linux/drivers/staging/usbip/userspace/src/usbip_unbind.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
   3 *               2005-2007 Takahiro Hirofuchi
   4 *
   5 * This program is free software: you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation, either version 2 of the License, or
   8 * (at your option) any later version.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include <sysfs/libsysfs.h>
  20
  21#include <errno.h>
  22#include <stdio.h>
  23#include <string.h>
  24
  25#include <getopt.h>
  26
  27#include "usbip_common.h"
  28#include "utils.h"
  29#include "usbip.h"
  30
  31static const char usbip_unbind_usage_string[] =
  32        "usbip unbind <args>\n"
  33        "    -b, --busid=<busid>    Unbind " USBIP_HOST_DRV_NAME ".ko from "
  34        "device on <busid>\n";
  35
  36void usbip_unbind_usage(void)
  37{
  38        printf("usage: %s", usbip_unbind_usage_string);
  39}
  40
  41static int unbind_device(char *busid)
  42{
  43        char bus_type[] = "usb";
  44        struct sysfs_driver *usbip_host_drv;
  45        struct sysfs_device *dev;
  46        struct dlist *devlist;
  47        int verified = 0;
  48        int rc, ret = -1;
  49
  50        char attr_name[] = "bConfigurationValue";
  51        char sysfs_mntpath[SYSFS_PATH_MAX];
  52        char busid_attr_path[SYSFS_PATH_MAX];
  53        struct sysfs_attribute *busid_attr;
  54        char *val = NULL;
  55        int len;
  56
  57        /* verify the busid device is using usbip-host */
  58        usbip_host_drv = sysfs_open_driver(bus_type, USBIP_HOST_DRV_NAME);
  59        if (!usbip_host_drv) {
  60                err("could not open %s driver: %s", USBIP_HOST_DRV_NAME,
  61                    strerror(errno));
  62                return -1;
  63        }
  64
  65        devlist = sysfs_get_driver_devices(usbip_host_drv);
  66        if (!devlist) {
  67                err("%s is not in use by any devices", USBIP_HOST_DRV_NAME);
  68                goto err_close_usbip_host_drv;
  69        }
  70
  71        dlist_for_each_data(devlist, dev, struct sysfs_device) {
  72                if (!strncmp(busid, dev->name, strlen(busid)) &&
  73                    !strncmp(dev->driver_name, USBIP_HOST_DRV_NAME,
  74                             strlen(USBIP_HOST_DRV_NAME))) {
  75                        verified = 1;
  76                        break;
  77                }
  78        }
  79
  80        if (!verified) {
  81                err("device on busid %s is not using %s", busid,
  82                    USBIP_HOST_DRV_NAME);
  83                goto err_close_usbip_host_drv;
  84        }
  85
  86        /*
  87         * NOTE: A read and write of an attribute value of the device busid
  88         * refers to must be done to start probing. That way a rebind of the
  89         * default driver for the device occurs.
  90         *
  91         * This seems very hackish and adds a lot of pointless code. I think it
  92         * should be done in the kernel by the driver after del_match_busid is
  93         * finished!
  94         */
  95
  96        rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
  97        if (rc < 0) {
  98                err("sysfs must be mounted: %s", strerror(errno));
  99                return -1;
 100        }
 101
 102        snprintf(busid_attr_path, sizeof(busid_attr_path), "%s/%s/%s/%s/%s/%s",
 103                 sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DEVICES_NAME,
 104                 busid, attr_name);
 105
 106        /* read a device attribute */
 107        busid_attr = sysfs_open_attribute(busid_attr_path);
 108        if (!busid_attr) {
 109                err("could not open %s/%s: %s", busid, attr_name,
 110                    strerror(errno));
 111                return -1;
 112        }
 113
 114        if (sysfs_read_attribute(busid_attr) < 0) {
 115                err("problem reading attribute: %s", strerror(errno));
 116                goto err_out;
 117        }
 118
 119        len = busid_attr->len;
 120        val = malloc(len);
 121        *val = *busid_attr->value;
 122        sysfs_close_attribute(busid_attr);
 123
 124        /* notify driver of unbind */
 125        rc = modify_match_busid(busid, 0);
 126        if (rc < 0) {
 127                err("unable to unbind device on %s", busid);
 128                goto err_out;
 129        }
 130
 131        /* write the device attribute */
 132        busid_attr = sysfs_open_attribute(busid_attr_path);
 133        if (!busid_attr) {
 134                err("could not open %s/%s: %s", busid, attr_name,
 135                    strerror(errno));
 136                return -1;
 137        }
 138
 139        rc = sysfs_write_attribute(busid_attr, val, len);
 140        if (rc < 0) {
 141                err("problem writing attribute: %s", strerror(errno));
 142                goto err_out;
 143        }
 144        sysfs_close_attribute(busid_attr);
 145
 146        ret = 0;
 147        printf("unbind device on busid %s: complete\n", busid);
 148
 149err_out:
 150        free(val);
 151err_close_usbip_host_drv:
 152        sysfs_close_driver(usbip_host_drv);
 153
 154        return ret;
 155}
 156
 157int usbip_unbind(int argc, char *argv[])
 158{
 159        static const struct option opts[] = {
 160                { "busid", required_argument, NULL, 'b' },
 161                { NULL,    0,                 NULL,  0  }
 162        };
 163
 164        int opt;
 165        int ret = -1;
 166
 167        for (;;) {
 168                opt = getopt_long(argc, argv, "b:", opts, NULL);
 169
 170                if (opt == -1)
 171                        break;
 172
 173                switch (opt) {
 174                case 'b':
 175                        ret = unbind_device(optarg);
 176                        goto out;
 177                default:
 178                        goto err_out;
 179                }
 180        }
 181
 182err_out:
 183        usbip_unbind_usage();
 184out:
 185        return ret;
 186}
 187