linux/tools/usb/usbip/libsrc/vhci_driver.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005-2007 Takahiro Hirofuchi
   3 */
   4
   5#include "usbip_common.h"
   6#include "vhci_driver.h"
   7#include <limits.h>
   8#include <netdb.h>
   9#include <libudev.h>
  10#include <dirent.h>
  11#include "sysfs_utils.h"
  12
  13#undef  PROGNAME
  14#define PROGNAME "libusbip"
  15
  16struct usbip_vhci_driver *vhci_driver;
  17struct udev *udev_context;
  18
  19static struct usbip_imported_device *
  20imported_device_init(struct usbip_imported_device *idev, char *busid)
  21{
  22        struct udev_device *sudev;
  23
  24        sudev = udev_device_new_from_subsystem_sysname(udev_context,
  25                                                       "usb", busid);
  26        if (!sudev) {
  27                dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
  28                goto err;
  29        }
  30        read_usb_device(sudev, &idev->udev);
  31        udev_device_unref(sudev);
  32
  33        return idev;
  34
  35err:
  36        return NULL;
  37}
  38
  39static int parse_status(const char *value)
  40{
  41        int ret = 0;
  42        char *c;
  43
  44        /* skip a header line */
  45        c = strchr(value, '\n');
  46        if (!c)
  47                return -1;
  48        c++;
  49
  50        while (*c != '\0') {
  51                int port, status, speed, devid;
  52                unsigned long socket;
  53                char lbusid[SYSFS_BUS_ID_SIZE];
  54                struct usbip_imported_device *idev;
  55                char hub[3];
  56
  57                ret = sscanf(c, "%2s  %d %d %d %x %lx %31s\n",
  58                                hub, &port, &status, &speed,
  59                                &devid, &socket, lbusid);
  60
  61                if (ret < 5) {
  62                        dbg("sscanf failed: %d", ret);
  63                        BUG();
  64                }
  65
  66                dbg("hub %s port %d status %d speed %d devid %x",
  67                                hub, port, status, speed, devid);
  68                dbg("socket %lx lbusid %s", socket, lbusid);
  69
  70                /* if a device is connected, look at it */
  71                idev = &vhci_driver->idev[port];
  72                memset(idev, 0, sizeof(*idev));
  73
  74                if (strncmp("hs", hub, 2) == 0)
  75                        idev->hub = HUB_SPEED_HIGH;
  76                else /* strncmp("ss", hub, 2) == 0 */
  77                        idev->hub = HUB_SPEED_SUPER;
  78
  79                idev->port      = port;
  80                idev->status    = status;
  81
  82                idev->devid     = devid;
  83
  84                idev->busnum    = (devid >> 16);
  85                idev->devnum    = (devid & 0x0000ffff);
  86
  87                if (idev->status != VDEV_ST_NULL
  88                    && idev->status != VDEV_ST_NOTASSIGNED) {
  89                        idev = imported_device_init(idev, lbusid);
  90                        if (!idev) {
  91                                dbg("imported_device_init failed");
  92                                return -1;
  93                        }
  94                }
  95
  96                /* go to the next line */
  97                c = strchr(c, '\n');
  98                if (!c)
  99                        break;
 100                c++;
 101        }
 102
 103        dbg("exit");
 104
 105        return 0;
 106}
 107
 108#define MAX_STATUS_NAME 16
 109
 110static int refresh_imported_device_list(void)
 111{
 112        const char *attr_status;
 113        char status[MAX_STATUS_NAME+1] = "status";
 114        int i, ret;
 115
 116        for (i = 0; i < vhci_driver->ncontrollers; i++) {
 117                if (i > 0)
 118                        snprintf(status, sizeof(status), "status.%d", i);
 119
 120                attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
 121                                                            status);
 122                if (!attr_status) {
 123                        err("udev_device_get_sysattr_value failed");
 124                        return -1;
 125                }
 126
 127                dbg("controller %d", i);
 128
 129                ret = parse_status(attr_status);
 130                if (ret != 0)
 131                        return ret;
 132        }
 133
 134        return 0;
 135}
 136
 137static int get_nports(void)
 138{
 139        const char *attr_nports;
 140
 141        attr_nports = udev_device_get_sysattr_value(vhci_driver->hc_device, "nports");
 142        if (!attr_nports) {
 143                err("udev_device_get_sysattr_value nports failed");
 144                return -1;
 145        }
 146
 147        return (int)strtoul(attr_nports, NULL, 10);
 148}
 149
 150static int vhci_hcd_filter(const struct dirent *dirent)
 151{
 152        return strcmp(dirent->d_name, "vhci_hcd") >= 0;
 153}
 154
 155static int get_ncontrollers(void)
 156{
 157        struct dirent **namelist;
 158        struct udev_device *platform;
 159        int n;
 160
 161        platform = udev_device_get_parent(vhci_driver->hc_device);
 162        if (platform == NULL)
 163                return -1;
 164
 165        n = scandir(udev_device_get_syspath(platform), &namelist, vhci_hcd_filter, NULL);
 166        if (n < 0)
 167                err("scandir failed");
 168        else {
 169                for (int i = 0; i < n; i++)
 170                        free(namelist[i]);
 171                free(namelist);
 172        }
 173
 174        return n;
 175}
 176
 177/*
 178 * Read the given port's record.
 179 *
 180 * To avoid buffer overflow we will read the entire line and
 181 * validate each part's size. The initial buffer is padded by 4 to
 182 * accommodate the 2 spaces, 1 newline and an additional character
 183 * which is needed to properly validate the 3rd part without it being
 184 * truncated to an acceptable length.
 185 */
 186static int read_record(int rhport, char *host, unsigned long host_len,
 187                char *port, unsigned long port_len, char *busid)
 188{
 189        int part;
 190        FILE *file;
 191        char path[PATH_MAX+1];
 192        char *buffer, *start, *end;
 193        char delim[] = {' ', ' ', '\n'};
 194        int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
 195        size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
 196
 197        buffer = malloc(buffer_len);
 198        if (!buffer)
 199                return -1;
 200
 201        snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
 202
 203        file = fopen(path, "r");
 204        if (!file) {
 205                err("fopen");
 206                free(buffer);
 207                return -1;
 208        }
 209
 210        if (fgets(buffer, buffer_len, file) == NULL) {
 211                err("fgets");
 212                free(buffer);
 213                fclose(file);
 214                return -1;
 215        }
 216        fclose(file);
 217
 218        /* validate the length of each of the 3 parts */
 219        start = buffer;
 220        for (part = 0; part < 3; part++) {
 221                end = strchr(start, delim[part]);
 222                if (end == NULL || (end - start) > max_len[part]) {
 223                        free(buffer);
 224                        return -1;
 225                }
 226                start = end + 1;
 227        }
 228
 229        if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
 230                err("sscanf");
 231                free(buffer);
 232                return -1;
 233        }
 234
 235        free(buffer);
 236
 237        return 0;
 238}
 239
 240/* ---------------------------------------------------------------------- */
 241
 242int usbip_vhci_driver_open(void)
 243{
 244        udev_context = udev_new();
 245        if (!udev_context) {
 246                err("udev_new failed");
 247                return -1;
 248        }
 249
 250        vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
 251
 252        /* will be freed in usbip_driver_close() */
 253        vhci_driver->hc_device =
 254                udev_device_new_from_subsystem_sysname(udev_context,
 255                                                       USBIP_VHCI_BUS_TYPE,
 256                                                       USBIP_VHCI_DEVICE_NAME);
 257        if (!vhci_driver->hc_device) {
 258                err("udev_device_new_from_subsystem_sysname failed");
 259                goto err;
 260        }
 261
 262        vhci_driver->nports = get_nports();
 263        dbg("available ports: %d", vhci_driver->nports);
 264
 265        if (vhci_driver->nports <= 0) {
 266                err("no available ports");
 267                goto err;
 268        } else if (vhci_driver->nports > MAXNPORT) {
 269                err("port number exceeds %d", MAXNPORT);
 270                goto err;
 271        }
 272
 273        vhci_driver->ncontrollers = get_ncontrollers();
 274        dbg("available controllers: %d", vhci_driver->ncontrollers);
 275
 276        if (vhci_driver->ncontrollers <=0) {
 277                err("no available usb controllers");
 278                goto err;
 279        }
 280
 281        if (refresh_imported_device_list())
 282                goto err;
 283
 284        return 0;
 285
 286err:
 287        udev_device_unref(vhci_driver->hc_device);
 288
 289        if (vhci_driver)
 290                free(vhci_driver);
 291
 292        vhci_driver = NULL;
 293
 294        udev_unref(udev_context);
 295
 296        return -1;
 297}
 298
 299
 300void usbip_vhci_driver_close(void)
 301{
 302        if (!vhci_driver)
 303                return;
 304
 305        udev_device_unref(vhci_driver->hc_device);
 306
 307        free(vhci_driver);
 308
 309        vhci_driver = NULL;
 310
 311        udev_unref(udev_context);
 312}
 313
 314
 315int usbip_vhci_refresh_device_list(void)
 316{
 317
 318        if (refresh_imported_device_list())
 319                goto err;
 320
 321        return 0;
 322err:
 323        dbg("failed to refresh device list");
 324        return -1;
 325}
 326
 327
 328int usbip_vhci_get_free_port(uint32_t speed)
 329{
 330        for (int i = 0; i < vhci_driver->nports; i++) {
 331                if (speed == USB_SPEED_SUPER &&
 332                    vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
 333                        continue;
 334
 335                if (vhci_driver->idev[i].status == VDEV_ST_NULL)
 336                        return vhci_driver->idev[i].port;
 337        }
 338
 339        return -1;
 340}
 341
 342int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 343                uint32_t speed) {
 344        char buff[200]; /* what size should be ? */
 345        char attach_attr_path[SYSFS_PATH_MAX];
 346        char attr_attach[] = "attach";
 347        const char *path;
 348        int ret;
 349
 350        snprintf(buff, sizeof(buff), "%u %d %u %u",
 351                        port, sockfd, devid, speed);
 352        dbg("writing: %s", buff);
 353
 354        path = udev_device_get_syspath(vhci_driver->hc_device);
 355        snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
 356                 path, attr_attach);
 357        dbg("attach attribute path: %s", attach_attr_path);
 358
 359        ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
 360        if (ret < 0) {
 361                dbg("write_sysfs_attribute failed");
 362                return -1;
 363        }
 364
 365        dbg("attached port: %d", port);
 366
 367        return 0;
 368}
 369
 370static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
 371{
 372        return (busnum << 16) | devnum;
 373}
 374
 375/* will be removed */
 376int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
 377                uint8_t devnum, uint32_t speed)
 378{
 379        int devid = get_devid(busnum, devnum);
 380
 381        return usbip_vhci_attach_device2(port, sockfd, devid, speed);
 382}
 383
 384int usbip_vhci_detach_device(uint8_t port)
 385{
 386        char detach_attr_path[SYSFS_PATH_MAX];
 387        char attr_detach[] = "detach";
 388        char buff[200]; /* what size should be ? */
 389        const char *path;
 390        int ret;
 391
 392        snprintf(buff, sizeof(buff), "%u", port);
 393        dbg("writing: %s", buff);
 394
 395        path = udev_device_get_syspath(vhci_driver->hc_device);
 396        snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
 397                 path, attr_detach);
 398        dbg("detach attribute path: %s", detach_attr_path);
 399
 400        ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
 401        if (ret < 0) {
 402                dbg("write_sysfs_attribute failed");
 403                return -1;
 404        }
 405
 406        dbg("detached port: %d", port);
 407
 408        return 0;
 409}
 410
 411int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
 412{
 413        char product_name[100];
 414        char host[NI_MAXHOST] = "unknown host";
 415        char serv[NI_MAXSERV] = "unknown port";
 416        char remote_busid[SYSFS_BUS_ID_SIZE];
 417        int ret;
 418        int read_record_error = 0;
 419
 420        if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
 421                return 0;
 422
 423        ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
 424                          remote_busid);
 425        if (ret) {
 426                err("read_record");
 427                read_record_error = 1;
 428        }
 429
 430        printf("Port %02d: <%s> at %s\n", idev->port,
 431               usbip_status_string(idev->status),
 432               usbip_speed_string(idev->udev.speed));
 433
 434        usbip_names_get_product(product_name, sizeof(product_name),
 435                                idev->udev.idVendor, idev->udev.idProduct);
 436
 437        printf("       %s\n",  product_name);
 438
 439        if (!read_record_error) {
 440                printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
 441                       host, serv, remote_busid);
 442                printf("%10s -> remote bus/dev %03d/%03d\n", " ",
 443                       idev->busnum, idev->devnum);
 444        } else {
 445                printf("%10s -> unknown host, remote port and remote busid\n",
 446                       idev->udev.busid);
 447                printf("%10s -> remote bus/dev %03d/%03d\n", " ",
 448                       idev->busnum, idev->devnum);
 449        }
 450
 451        return 0;
 452}
 453