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 <libudev.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 #include "sysfs_utils.h" 31 32 static const char usbip_unbind_usage_string[] = 33 "usbip unbind <args>\n" 34 " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from " 35 "device on <busid>\n"; 36 37 void usbip_unbind_usage(void) 38 { 39 printf("usage: %s", usbip_unbind_usage_string); 40 } 41 42 static int unbind_device(char *busid) 43 { 44 char bus_type[] = "usb"; 45 int rc, ret = -1; 46 47 char unbind_attr_name[] = "unbind"; 48 char unbind_attr_path[SYSFS_PATH_MAX]; 49 char rebind_attr_name[] = "rebind"; 50 char rebind_attr_path[SYSFS_PATH_MAX]; 51 52 struct udev *udev; 53 struct udev_device *dev; 54 const char *driver; 55 56 /* Create libudev context. */ 57 udev = udev_new(); 58 59 /* Check whether the device with this bus ID exists. */ 60 dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid); 61 if (!dev) { 62 err("device with the specified bus ID does not exist"); 63 goto err_close_udev; 64 } 65 66 /* Check whether the device is using usbip-host driver. */ 67 driver = udev_device_get_driver(dev); 68 if (!driver || strcmp(driver, "usbip-host")) { 69 err("device is not bound to usbip-host driver"); 70 goto err_close_udev; 71 } 72 73 /* Unbind device from driver. */ 74 snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", 75 SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, 76 USBIP_HOST_DRV_NAME, unbind_attr_name); 77 78 rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid)); 79 if (rc < 0) { 80 err("error unbinding device %s from driver", busid); 81 goto err_close_udev; 82 } 83 84 /* Notify driver of unbind. */ 85 rc = modify_match_busid(busid, 0); 86 if (rc < 0) { 87 err("unable to unbind device on %s", busid); 88 goto err_close_udev; 89 } 90 91 /* Trigger new probing. */ 92 snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", 93 SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, 94 USBIP_HOST_DRV_NAME, rebind_attr_name); 95 96 rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid)); 97 if (rc < 0) { 98 err("error rebinding"); 99 goto err_close_udev; 100 } 101 102 ret = 0; 103 info("unbind device on busid %s: complete", busid); 104 105 err_close_udev: 106 udev_device_unref(dev); 107 udev_unref(udev); 108 109 return ret; 110 } 111 112 int usbip_unbind(int argc, char *argv[]) 113 { 114 static const struct option opts[] = { 115 { "busid", required_argument, NULL, 'b' }, 116 { NULL, 0, NULL, 0 } 117 }; 118 119 int opt; 120 int ret = -1; 121 122 for (;;) { 123 opt = getopt_long(argc, argv, "b:", opts, NULL); 124 125 if (opt == -1) 126 break; 127 128 switch (opt) { 129 case 'b': 130 ret = unbind_device(optarg); 131 goto out; 132 default: 133 goto err_out; 134 } 135 } 136 137 err_out: 138 usbip_unbind_usage(); 139 out: 140 return ret; 141 } 142