linux/tools/usb/usbip/src/usbip_bind.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
   4 *               2005-2007 Takahiro Hirofuchi
   5 */
   6
   7#include <libudev.h>
   8
   9#include <errno.h>
  10#include <stdio.h>
  11#include <stdlib.h>
  12#include <string.h>
  13
  14#include <getopt.h>
  15
  16#include "usbip_common.h"
  17#include "utils.h"
  18#include "usbip.h"
  19#include "sysfs_utils.h"
  20
  21enum unbind_status {
  22        UNBIND_ST_OK,
  23        UNBIND_ST_USBIP_HOST,
  24        UNBIND_ST_FAILED
  25};
  26
  27static const char usbip_bind_usage_string[] =
  28        "usbip bind <args>\n"
  29        "    -b, --busid=<busid>    Bind " USBIP_HOST_DRV_NAME ".ko to device "
  30        "on <busid>\n";
  31
  32void usbip_bind_usage(void)
  33{
  34        printf("usage: %s", usbip_bind_usage_string);
  35}
  36
  37/* call at unbound state */
  38static int bind_usbip(char *busid)
  39{
  40        char attr_name[] = "bind";
  41        char bind_attr_path[SYSFS_PATH_MAX];
  42        int rc = -1;
  43
  44        snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
  45                 SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
  46                 SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
  47
  48        rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
  49        if (rc < 0) {
  50                err("error binding device %s to driver: %s", busid,
  51                    strerror(errno));
  52                return -1;
  53        }
  54
  55        return 0;
  56}
  57
  58/* buggy driver may cause dead lock */
  59static int unbind_other(char *busid)
  60{
  61        enum unbind_status status = UNBIND_ST_OK;
  62
  63        char attr_name[] = "unbind";
  64        char unbind_attr_path[SYSFS_PATH_MAX];
  65        int rc = -1;
  66
  67        struct udev *udev;
  68        struct udev_device *dev;
  69        const char *driver;
  70        const char *bDevClass;
  71
  72        /* Create libudev context. */
  73        udev = udev_new();
  74
  75        /* Get the device. */
  76        dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
  77        if (!dev) {
  78                dbg("unable to find device with bus ID %s", busid);
  79                goto err_close_busid_dev;
  80        }
  81
  82        /* Check what kind of device it is. */
  83        bDevClass  = udev_device_get_sysattr_value(dev, "bDeviceClass");
  84        if (!bDevClass) {
  85                dbg("unable to get bDevClass device attribute");
  86                goto err_close_busid_dev;
  87        }
  88
  89        if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
  90                dbg("skip unbinding of hub");
  91                goto err_close_busid_dev;
  92        }
  93
  94        /* Get the device driver. */
  95        driver = udev_device_get_driver(dev);
  96        if (!driver) {
  97                /* No driver bound to this device. */
  98                goto out;
  99        }
 100
 101        if (!strncmp(USBIP_HOST_DRV_NAME, driver,
 102                                strlen(USBIP_HOST_DRV_NAME))) {
 103                /* Already bound to usbip-host. */
 104                status = UNBIND_ST_USBIP_HOST;
 105                goto out;
 106        }
 107
 108        /* Unbind device from driver. */
 109        snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
 110                 SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
 111                 SYSFS_DRIVERS_NAME, driver, attr_name);
 112
 113        rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
 114        if (rc < 0) {
 115                err("error unbinding device %s from driver", busid);
 116                goto err_close_busid_dev;
 117        }
 118
 119        goto out;
 120
 121err_close_busid_dev:
 122        status = UNBIND_ST_FAILED;
 123out:
 124        udev_device_unref(dev);
 125        udev_unref(udev);
 126
 127        return status;
 128}
 129
 130static int bind_device(char *busid)
 131{
 132        int rc;
 133        struct udev *udev;
 134        struct udev_device *dev;
 135        const char *devpath;
 136
 137        /* Check whether the device with this bus ID exists. */
 138        udev = udev_new();
 139        dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
 140        if (!dev) {
 141                err("device with the specified bus ID does not exist");
 142                return -1;
 143        }
 144        devpath = udev_device_get_devpath(dev);
 145        udev_unref(udev);
 146
 147        /* If the device is already attached to vhci_hcd - bail out */
 148        if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
 149                err("bind loop detected: device: %s is attached to %s\n",
 150                    devpath, USBIP_VHCI_DRV_NAME);
 151                return -1;
 152        }
 153
 154        rc = unbind_other(busid);
 155        if (rc == UNBIND_ST_FAILED) {
 156                err("could not unbind driver from device on busid %s", busid);
 157                return -1;
 158        } else if (rc == UNBIND_ST_USBIP_HOST) {
 159                err("device on busid %s is already bound to %s", busid,
 160                    USBIP_HOST_DRV_NAME);
 161                return -1;
 162        }
 163
 164        rc = modify_match_busid(busid, 1);
 165        if (rc < 0) {
 166                err("unable to bind device on %s", busid);
 167                return -1;
 168        }
 169
 170        rc = bind_usbip(busid);
 171        if (rc < 0) {
 172                err("could not bind device to %s", USBIP_HOST_DRV_NAME);
 173                modify_match_busid(busid, 0);
 174                return -1;
 175        }
 176
 177        info("bind device on busid %s: complete", busid);
 178
 179        return 0;
 180}
 181
 182int usbip_bind(int argc, char *argv[])
 183{
 184        static const struct option opts[] = {
 185                { "busid", required_argument, NULL, 'b' },
 186                { NULL,    0,                 NULL,  0  }
 187        };
 188
 189        int opt;
 190        int ret = -1;
 191
 192        for (;;) {
 193                opt = getopt_long(argc, argv, "b:", opts, NULL);
 194
 195                if (opt == -1)
 196                        break;
 197
 198                switch (opt) {
 199                case 'b':
 200                        ret = bind_device(optarg);
 201                        goto out;
 202                default:
 203                        goto err_out;
 204                }
 205        }
 206
 207err_out:
 208        usbip_bind_usage();
 209out:
 210        return ret;
 211}
 212