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