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(struct udev_device *hc_device)
 139{
 140        const char *attr_nports;
 141
 142        attr_nports = udev_device_get_sysattr_value(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 !strncmp(dirent->d_name, "vhci_hcd.", 9);
 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        int nports;
 246        struct udev_device *hc_device;
 247
 248        udev_context = udev_new();
 249        if (!udev_context) {
 250                err("udev_new failed");
 251                return -1;
 252        }
 253
 254        /* will be freed in usbip_driver_close() */
 255        hc_device =
 256                udev_device_new_from_subsystem_sysname(udev_context,
 257                                                       USBIP_VHCI_BUS_TYPE,
 258                                                       USBIP_VHCI_DEVICE_NAME);
 259        if (!hc_device) {
 260                err("udev_device_new_from_subsystem_sysname failed");
 261                goto err;
 262        }
 263
 264        nports = get_nports(hc_device);
 265        if (nports <= 0) {
 266                err("no available ports");
 267                goto err;
 268        }
 269        dbg("available ports: %d", nports);
 270
 271        vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver) +
 272                        nports * sizeof(struct usbip_imported_device));
 273        if (!vhci_driver) {
 274                err("vhci_driver allocation failed");
 275                goto err;
 276        }
 277
 278        vhci_driver->nports = nports;
 279        vhci_driver->hc_device = hc_device;
 280        vhci_driver->ncontrollers = get_ncontrollers();
 281        dbg("available controllers: %d", vhci_driver->ncontrollers);
 282
 283        if (vhci_driver->ncontrollers <=0) {
 284                err("no available usb controllers");
 285                goto err;
 286        }
 287
 288        if (refresh_imported_device_list())
 289                goto err;
 290
 291        return 0;
 292
 293err:
 294        udev_device_unref(hc_device);
 295
 296        if (vhci_driver)
 297                free(vhci_driver);
 298
 299        vhci_driver = NULL;
 300
 301        udev_unref(udev_context);
 302
 303        return -1;
 304}
 305
 306
 307void usbip_vhci_driver_close(void)
 308{
 309        if (!vhci_driver)
 310                return;
 311
 312        udev_device_unref(vhci_driver->hc_device);
 313
 314        free(vhci_driver);
 315
 316        vhci_driver = NULL;
 317
 318        udev_unref(udev_context);
 319}
 320
 321
 322int usbip_vhci_refresh_device_list(void)
 323{
 324
 325        if (refresh_imported_device_list())
 326                goto err;
 327
 328        return 0;
 329err:
 330        dbg("failed to refresh device list");
 331        return -1;
 332}
 333
 334
 335int usbip_vhci_get_free_port(uint32_t speed)
 336{
 337        for (int i = 0; i < vhci_driver->nports; i++) {
 338
 339                switch (speed) {
 340                case    USB_SPEED_SUPER:
 341                        if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
 342                                continue;
 343                break;
 344                default:
 345                        if (vhci_driver->idev[i].hub != HUB_SPEED_HIGH)
 346                                continue;
 347                break;
 348                }
 349
 350                if (vhci_driver->idev[i].status == VDEV_ST_NULL)
 351                        return vhci_driver->idev[i].port;
 352        }
 353
 354        return -1;
 355}
 356
 357int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 358                uint32_t speed) {
 359        char buff[200]; /* what size should be ? */
 360        char attach_attr_path[SYSFS_PATH_MAX];
 361        char attr_attach[] = "attach";
 362        const char *path;
 363        int ret;
 364
 365        snprintf(buff, sizeof(buff), "%u %d %u %u",
 366                        port, sockfd, devid, speed);
 367        dbg("writing: %s", buff);
 368
 369        path = udev_device_get_syspath(vhci_driver->hc_device);
 370        snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
 371                 path, attr_attach);
 372        dbg("attach attribute path: %s", attach_attr_path);
 373
 374        ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
 375        if (ret < 0) {
 376                dbg("write_sysfs_attribute failed");
 377                return -1;
 378        }
 379
 380        dbg("attached port: %d", port);
 381
 382        return 0;
 383}
 384
 385static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
 386{
 387        return (busnum << 16) | devnum;
 388}
 389
 390/* will be removed */
 391int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
 392                uint8_t devnum, uint32_t speed)
 393{
 394        int devid = get_devid(busnum, devnum);
 395
 396        return usbip_vhci_attach_device2(port, sockfd, devid, speed);
 397}
 398
 399int usbip_vhci_detach_device(uint8_t port)
 400{
 401        char detach_attr_path[SYSFS_PATH_MAX];
 402        char attr_detach[] = "detach";
 403        char buff[200]; /* what size should be ? */
 404        const char *path;
 405        int ret;
 406
 407        snprintf(buff, sizeof(buff), "%u", port);
 408        dbg("writing: %s", buff);
 409
 410        path = udev_device_get_syspath(vhci_driver->hc_device);
 411        snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
 412                 path, attr_detach);
 413        dbg("detach attribute path: %s", detach_attr_path);
 414
 415        ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
 416        if (ret < 0) {
 417                dbg("write_sysfs_attribute failed");
 418                return -1;
 419        }
 420
 421        dbg("detached port: %d", port);
 422
 423        return 0;
 424}
 425
 426int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
 427{
 428        char product_name[100];
 429        char host[NI_MAXHOST] = "unknown host";
 430        char serv[NI_MAXSERV] = "unknown port";
 431        char remote_busid[SYSFS_BUS_ID_SIZE];
 432        int ret;
 433        int read_record_error = 0;
 434
 435        if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
 436                return 0;
 437
 438        ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
 439                          remote_busid);
 440        if (ret) {
 441                err("read_record");
 442                read_record_error = 1;
 443        }
 444
 445        printf("Port %02d: <%s> at %s\n", idev->port,
 446               usbip_status_string(idev->status),
 447               usbip_speed_string(idev->udev.speed));
 448
 449        usbip_names_get_product(product_name, sizeof(product_name),
 450                                idev->udev.idVendor, idev->udev.idProduct);
 451
 452        printf("       %s\n",  product_name);
 453
 454        if (!read_record_error) {
 455                printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
 456                       host, serv, remote_busid);
 457                printf("%10s -> remote bus/dev %03d/%03d\n", " ",
 458                       idev->busnum, idev->devnum);
 459        } else {
 460                printf("%10s -> unknown host, remote port and remote busid\n",
 461                       idev->udev.busid);
 462                printf("%10s -> remote bus/dev %03d/%03d\n", " ",
 463                       idev->busnum, idev->devnum);
 464        }
 465
 466        return 0;
 467}
 468