aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/usbip/userspace/src/usbip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/usbip/userspace/src/usbip.c')
-rw-r--r--drivers/staging/usbip/userspace/src/usbip.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/drivers/staging/usbip/userspace/src/usbip.c b/drivers/staging/usbip/userspace/src/usbip.c
new file mode 100644
index 000000000000..01a562866b5d
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip.c
@@ -0,0 +1,723 @@
+/*
+ *
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include "usbip.h"
+#include "usbip_network.h"
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <glib.h>
+
+static const char version[] = PACKAGE_STRING;
+
+
+/* /sys/devices/platform/vhci_hcd/usb6/6-1/6-1:1.1 -> 1 */
+static int get_interface_number(char *path)
+{
+ char *c;
+
+ c = strstr(path, vhci_driver->hc_device->bus_id);
+ if (!c)
+ return -1; /* hc exist? */
+ c++;
+ /* -> usb6/6-1/6-1:1.1 */
+
+ c = strchr(c, '/');
+ if (!c)
+ return -1; /* hc exist? */
+ c++;
+ /* -> 6-1/6-1:1.1 */
+
+ c = strchr(c, '/');
+ if (!c)
+ return -1; /* no interface path */
+ c++;
+ /* -> 6-1:1.1 */
+
+ c = strchr(c, ':');
+ if (!c)
+ return -1; /* no configuration? */
+ c++;
+ /* -> 1.1 */
+
+ c = strchr(c, '.');
+ if (!c)
+ return -1; /* no interface? */
+ c++;
+ /* -> 1 */
+
+
+ return atoi(c);
+}
+
+
+static struct sysfs_device *open_usb_interface(struct usb_device *udev, int i)
+{
+ struct sysfs_device *suinf;
+ char busid[SYSFS_BUS_ID_SIZE];
+
+ snprintf(busid, SYSFS_BUS_ID_SIZE, "%s:%d.%d",
+ udev->busid, udev->bConfigurationValue, i);
+
+ suinf = sysfs_open_device("usb", busid);
+ if (!suinf)
+ err("sysfs_open_device %s", busid);
+
+ return suinf;
+}
+
+
+#define MAX_BUFF 100
+static int record_connection(char *host, char *port, char *busid, int rhport)
+{
+ int fd;
+ char path[PATH_MAX+1];
+ char buff[MAX_BUFF+1];
+ int ret;
+
+ mkdir(VHCI_STATE_PATH, 0700);
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
+ if (fd < 0)
+ return -1;
+
+ snprintf(buff, MAX_BUFF, "%s %s %s\n",
+ host, port, busid);
+
+ ret = write(fd, buff, strlen(buff));
+ if (ret != (ssize_t) strlen(buff)) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int read_record(int rhport, char *host, char *port, char *busid)
+{
+ FILE *file;
+ char path[PATH_MAX+1];
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ file = fopen(path, "r");
+ if (!file) {
+ err("fopen");
+ return -1;
+ }
+
+ if (fscanf(file, "%s %s %s\n", host, port, busid) != 3) {
+ err("fscanf");
+ fclose(file);
+ return -1;
+ }
+
+ fclose(file);
+
+ return 0;
+}
+
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
+{
+ char product_name[100];
+ char host[NI_MAXHOST] = "unknown host";
+ char serv[NI_MAXSERV] = "unknown port";
+ char remote_busid[SYSFS_BUS_ID_SIZE];
+ int ret;
+
+ if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) {
+ info("Port %02d: <%s>", idev->port, usbip_status_string(idev->status));
+ return 0;
+ }
+
+ ret = read_record(idev->port, host, serv, remote_busid);
+ if (ret) {
+ err("read_record");
+ return -1;
+ }
+
+ info("Port %02d: <%s> at %s", idev->port,
+ usbip_status_string(idev->status), usbip_speed_string(idev->udev.speed));
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ idev->udev.idVendor, idev->udev.idProduct);
+
+ info(" %s", product_name);
+
+ info("%10s -> usbip://%s:%s/%s (remote devid %08x (bus/dev %03d/%03d))",
+ idev->udev.busid, host, serv, remote_busid,
+ idev->devid,
+ idev->busnum, idev->devnum);
+
+ for (int i=0; i < idev->udev.bNumInterfaces; i++) {
+ /* show interface information */
+ struct sysfs_device *suinf;
+
+ suinf = open_usb_interface(&idev->udev, i);
+ if (!suinf)
+ continue;
+
+ info(" %6s used by %-17s", suinf->bus_id, suinf->driver_name);
+ sysfs_close_device(suinf);
+
+ /* show class device information */
+ struct class_device *cdev;
+
+ dlist_for_each_data(idev->cdev_list, cdev, struct class_device) {
+ int ifnum = get_interface_number(cdev->devpath);
+ if (ifnum == i) {
+ info(" %s", cdev->clspath);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+static int query_exported_devices(int sockfd)
+{
+ int ret;
+ struct op_devlist_reply rep;
+ uint16_t code = OP_REP_DEVLIST;
+
+ bzero(&rep, sizeof(rep));
+
+ ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+ if (ret < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ ret = usbip_recv_op_common(sockfd, &code);
+ if (ret < 0) {
+ err("recv op_common");
+ return -1;
+ }
+
+ ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
+ if (ret < 0) {
+ err("recv op_devlist");
+ return -1;
+ }
+
+ PACK_OP_DEVLIST_REPLY(0, &rep);
+ dbg("exportable %d devices", rep.ndev);
+
+ for (unsigned int i=0; i < rep.ndev; i++) {
+ char product_name[100];
+ char class_name[100];
+ struct usb_device udev;
+
+ bzero(&udev, sizeof(udev));
+
+ ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
+ if (ret < 0) {
+ err("recv usb_device[%d]", i);
+ return -1;
+ }
+ pack_usb_device(0, &udev);
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ udev.idVendor, udev.idProduct);
+ usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass,
+ udev.bDeviceSubClass, udev.bDeviceProtocol);
+
+ info("%8s: %s", udev.busid, product_name);
+ info("%8s: %s", " ", udev.path);
+ info("%8s: %s", " ", class_name);
+
+ for (int j=0; j < udev.bNumInterfaces; j++) {
+ struct usb_interface uinf;
+
+ ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
+ if (ret < 0) {
+ err("recv usb_interface[%d]", j);
+ return -1;
+ }
+
+ pack_usb_interface(0, &uinf);
+ usbip_names_get_class(class_name, sizeof(class_name), uinf.bInterfaceClass,
+ uinf.bInterfaceSubClass, uinf.bInterfaceProtocol);
+
+ info("%8s: %2d - %s", " ", j, class_name);
+ }
+
+ info(" ");
+ }
+
+ return rep.ndev;
+}
+
+static int import_device(int sockfd, struct usb_device *udev)
+{
+ int ret;
+ int port;
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ port = usbip_vhci_get_free_port();
+ if (port < 0) {
+ err("no free port");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ ret = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+ udev->devnum, udev->speed);
+ if (ret < 0) {
+ err("import device");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return port;
+}
+
+
+static int query_import_device(int sockfd, char *busid)
+{
+ int ret;
+ struct op_import_request request;
+ struct op_import_reply reply;
+ uint16_t code = OP_REP_IMPORT;
+
+ bzero(&request, sizeof(request));
+ bzero(&reply, sizeof(reply));
+
+
+ /* send a request */
+ ret = usbip_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+ if (ret < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
+
+ PACK_OP_IMPORT_REQUEST(0, &request);
+
+ ret = usbip_send(sockfd, (void *) &request, sizeof(request));
+ if (ret < 0) {
+ err("send op_import_request");
+ return -1;
+ }
+
+
+ /* recieve a reply */
+ ret = usbip_recv_op_common(sockfd, &code);
+ if (ret < 0) {
+ err("recv op_common");
+ return -1;
+ }
+
+ ret = usbip_recv(sockfd, (void *) &reply, sizeof(reply));
+ if (ret < 0) {
+ err("recv op_import_reply");
+ return -1;
+ }
+
+ PACK_OP_IMPORT_REPLY(0, &reply);
+
+
+ /* check the reply */
+ if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
+ err("recv different busid %s", reply.udev.busid);
+ return -1;
+ }
+
+
+ /* import a device */
+ return import_device(sockfd, &reply.udev);
+}
+
+static int attach_device(char *host, char *busid)
+{
+ int sockfd;
+ int ret;
+ int rhport;
+
+ sockfd = tcp_connect(host, USBIP_PORT_STRING);
+ if (sockfd < 0) {
+ err("tcp connect");
+ return -1;
+ }
+
+ rhport = query_import_device(sockfd, busid);
+ if (rhport < 0) {
+ err("query");
+ return -1;
+ }
+
+ close(sockfd);
+
+ ret = record_connection(host, USBIP_PORT_STRING,
+ busid, rhport);
+ if (ret < 0) {
+ err("record connection");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int detach_port(char *port)
+{
+ int ret;
+ uint8_t portnum;
+
+ for (unsigned int i=0; i < strlen(port); i++)
+ if (!isdigit(port[i])) {
+ err("invalid port %s", port);
+ return -1;
+ }
+
+ /* check max port */
+
+ portnum = atoi(port);
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ ret = usbip_vhci_detach_device(portnum);
+ if (ret < 0)
+ return -1;
+
+ usbip_vhci_driver_close();
+
+ return ret;
+}
+
+static int show_exported_devices(char *host)
+{
+ int ret;
+ int sockfd;
+
+ sockfd = tcp_connect(host, USBIP_PORT_STRING);
+ if (sockfd < 0) {
+ err("- %s failed", host);
+ return -1;
+ }
+
+ info("- %s", host);
+
+ ret = query_exported_devices(sockfd);
+ if (ret < 0) {
+ err("query");
+ return -1;
+ }
+
+ close(sockfd);
+ return 0;
+}
+
+static int attach_exported_devices(char *host, int sockfd)
+{
+ int ret;
+ struct op_devlist_reply rep;
+ uint16_t code = OP_REP_DEVLIST;
+
+ bzero(&rep, sizeof(rep));
+
+ ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+ if(ret < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ ret = usbip_recv_op_common(sockfd, &code);
+ if(ret < 0) {
+ err("recv op_common");
+ return -1;
+ }
+
+ ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
+ if(ret < 0) {
+ err("recv op_devlist");
+ return -1;
+ }
+
+ PACK_OP_DEVLIST_REPLY(0, &rep);
+ dbg("exportable %d devices", rep.ndev);
+
+ for(unsigned int i=0; i < rep.ndev; i++) {
+ char product_name[100];
+ char class_name[100];
+ struct usb_device udev;
+
+ bzero(&udev, sizeof(udev));
+
+ ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
+ if(ret < 0) {
+ err("recv usb_device[%d]", i);
+ return -1;
+ }
+ pack_usb_device(0, &udev);
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ udev.idVendor, udev.idProduct);
+ usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass,
+ udev.bDeviceSubClass, udev.bDeviceProtocol);
+
+ dbg("Attaching usb port %s from host %s on usbip, with deviceid: %s", udev.busid, host, product_name);
+
+ for (int j=0; j < udev.bNumInterfaces; j++) {
+ struct usb_interface uinf;
+
+ ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
+ if (ret < 0) {
+ err("recv usb_interface[%d]", j);
+ return -1;
+ }
+
+ pack_usb_interface(0, &uinf);
+ usbip_names_get_class(class_name, sizeof(class_name), uinf.bInterfaceClass,
+ uinf.bInterfaceSubClass, uinf.bInterfaceProtocol);
+
+ dbg("interface %2d - %s", j, class_name);
+ }
+
+ attach_device(host, udev.busid);
+ }
+
+ return rep.ndev;
+}
+
+static int attach_devices_all(char *host)
+{
+ int ret;
+ int sockfd;
+
+ sockfd = tcp_connect(host, USBIP_PORT_STRING);
+ if(sockfd < 0) {
+ err("- %s failed", host);
+ return -1;
+ }
+
+ info("- %s", host);
+
+ ret = attach_exported_devices(host, sockfd);
+ if(ret < 0) {
+ err("query");
+ return -1;
+ }
+
+ close(sockfd);
+ return 0;
+}
+
+
+const char help_message[] = "\
+Usage: usbip [options] \n\
+ -a, --attach [host] [bus_id] \n\
+ Attach a remote USB device. \n\
+ \n\
+ -x, --attachall [host] \n\
+ Attach all remote USB devices on the specific host. \n\
+ \n\
+ -d, --detach [ports] \n\
+ Detach an imported USB device. \n\
+ \n\
+ -l, --list [hosts] \n\
+ List exported USB devices. \n\
+ \n\
+ -p, --port \n\
+ List virtual USB port status. \n\
+ \n\
+ -D, --debug \n\
+ Print debugging information. \n\
+ \n\
+ -v, --version \n\
+ Show version. \n\
+ \n\
+ -h, --help \n\
+ Print this help. \n";
+
+static void show_help(void)
+{
+ printf("%s", help_message);
+}
+
+static int show_port_status(void)
+{
+ int ret;
+ struct usbip_imported_device *idev;
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0)
+ return ret;
+
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ idev = &vhci_driver->idev[i];
+
+ if (usbip_vhci_imported_device_dump(idev) < 0)
+ ret = -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return ret;
+}
+
+#define _GNU_SOURCE
+#include <getopt.h>
+static const struct option longopts[] = {
+ {"attach", no_argument, NULL, 'a'},
+ {"attachall", no_argument, NULL, 'x'},
+ {"detach", no_argument, NULL, 'd'},
+ {"port", no_argument, NULL, 'p'},
+ {"list", no_argument, NULL, 'l'},
+ {"version", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"debug", no_argument, NULL, 'D'},
+ {"syslog", no_argument, NULL, 'S'},
+ {NULL, 0, NULL, 0}
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ enum {
+ cmd_attach = 1,
+ cmd_attachall,
+ cmd_detach,
+ cmd_port,
+ cmd_list,
+ cmd_help,
+ cmd_version
+ } cmd = 0;
+
+ usbip_use_stderr = 1;
+
+ if (geteuid() != 0)
+ g_warning("running non-root?");
+
+ ret = usbip_names_init(USBIDS_FILE);
+ if (ret)
+ notice("failed to open %s", USBIDS_FILE);
+
+ for (;;) {
+ int c;
+ int index = 0;
+
+ c = getopt_long(argc, argv, "adplvhDSx", longopts, &index);
+
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'a':
+ if (!cmd)
+ cmd = cmd_attach;
+ else
+ cmd = cmd_help;
+ break;
+ case 'd':
+ if (!cmd)
+ cmd = cmd_detach;
+ else
+ cmd = cmd_help;
+ break;
+ case 'p':
+ if (!cmd)
+ cmd = cmd_port;
+ else cmd = cmd_help;
+ break;
+ case 'l':
+ if (!cmd)
+ cmd = cmd_list;
+ else
+ cmd = cmd_help;
+ break;
+ case 'v':
+ if (!cmd)
+ cmd = cmd_version;
+ else
+ cmd = cmd_help;
+ break;
+ case 'x':
+ if(!cmd)
+ cmd = cmd_attachall;
+ else
+ cmd = cmd_help;
+ break;
+ case 'h':
+ cmd = cmd_help;
+ break;
+ case 'D':
+ usbip_use_debug = 1;
+ break;
+ case 'S':
+ usbip_use_syslog = 1;
+ break;
+ case '?':
+ break;
+
+ default:
+ err("getopt");
+ }
+ }
+
+ ret = 0;
+ switch(cmd) {
+ case cmd_attach:
+ if (optind == argc - 2)
+ ret = attach_device(argv[optind], argv[optind+1]);
+ else
+ show_help();
+ break;
+ case cmd_detach:
+ while (optind < argc)
+ ret = detach_port(argv[optind++]);
+ break;
+ case cmd_port:
+ ret = show_port_status();
+ break;
+ case cmd_list:
+ while (optind < argc)
+ ret = show_exported_devices(argv[optind++]);
+ break;
+ case cmd_attachall:
+ while(optind < argc)
+ ret = attach_devices_all(argv[optind++]);
+ break;
+ case cmd_version:
+ printf("%s\n", version);
+ break;
+ case cmd_help:
+ show_help();
+ break;
+ default:
+ show_help();
+ }
+
+
+ usbip_names_free();
+
+ exit((ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
+}