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