Logo Search packages:      
Sourcecode: usbip version File versions

vhci_attach.c

/*
 * $Id: vhci_attach.c 42 2007-09-07 12:07:51Z hirofuchi $
 *
 * 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 <fcntl.h>
#include <glib.h>

static const char version[] = PACKAGE_STRING
      " ($Id: vhci_attach.c 42 2007-09-07 12:07:51Z hirofuchi $)";


/* /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("/tmp/vhci_hcd", 0700);

      snprintf(path, PATH_MAX, "/tmp/vhci_hcd/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, "/tmp/vhci_hcd/port%d", rhport);

      file = fopen(path, "r");
      if (!file) {
            err("fopen");
            return -1;
      }

      fscanf(file, "%s %s %s\n", host, port, busid);

      fclose(file);

      return 0;
}


void 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;
      }

      ret = read_record(idev->port, host, serv, remote_busid);
      if (ret)
            err("red_record");

      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);
                  }
            }
      }
}




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;
      }

      memcpy(&request.busid, busid, SYSFS_BUS_ID_SIZE);

      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 void 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;
      }

      rhport = query_import_device(sockfd, busid);
      if (rhport < 0) {
            err("query");
            return;
      }

      close(sockfd);

      ret = record_connection(host, USBIP_PORT_STRING,
                  busid, rhport);
      if (ret < 0)
            err("record connection");

      return;
}

static void 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;
            }

      /* check max port */

      portnum = atoi(port);

      ret = usbip_vhci_driver_open();
      if (ret < 0) {
            err("open vhci_driver");
            return;
      }

      usbip_vhci_detach_device(portnum);

      usbip_vhci_driver_close();
}

static void show_exported_devices(char *host)
{
      int ret;
      int sockfd;

      sockfd = tcp_connect(host, USBIP_PORT_STRING);
      if (sockfd < 0) {
            info("- %s failed", host);
            return;
      }

      info("- %s", host);

      ret = query_exported_devices(sockfd);
      if (ret < 0) {
            err("query");
      }

      close(sockfd);
}

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);
            attach_device(host, udev.busid);
      }

      return rep.ndev;
}

static void attach_devices_all(char *host)
{
      int ret;
      int sockfd;

      sockfd = tcp_connect(host, USBIP_PORT_STRING);
      if(sockfd < 0) {
            info("- %s failed", host);
            return;
      }

      info("- %s", host);

      ret = attach_exported_devices(host, sockfd);
      if(ret < 0) {
            err("query");
      }

      close(sockfd);
}


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 void show_port_status(void)
{
      int ret;
      struct usbip_imported_device *idev;

      ret = usbip_vhci_driver_open();
      if (ret < 0)
            return;


      for (int i = 0; i < vhci_driver->nports; i++) {
            idev = &vhci_driver->idev[i];

            usbip_vhci_imported_device_dump(idev);
      }

      usbip_vhci_driver_close();
}

#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)
            err("open usb.ids");

      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");
            }
      }


      switch(cmd) {
            case cmd_attach:
                  if (optind == argc - 2)
                        attach_device(argv[optind], argv[optind+1]);
                  else
                        show_help();
                  break;
            case cmd_detach:
                  while (optind < argc)
                        detach_port(argv[optind++]);
                  break;
            case cmd_port:
                  show_port_status();
                  break;
            case cmd_list:
                  while (optind < argc)
                        show_exported_devices(argv[optind++]);
                  break;
            case cmd_attachall:
                  while(optind < argc)
                        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();

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index