Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / usb / usbip / src / usbip_bind.c
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
21 enum unbind_status {
22         UNBIND_ST_OK,
23         UNBIND_ST_USBIP_HOST,
24         UNBIND_ST_FAILED
25 };
26
27 static 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
32 void usbip_bind_usage(void)
33 {
34         printf("usage: %s", usbip_bind_usage_string);
35 }
36
37 /* call at unbound state */
38 static 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 */
59 static 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
121 err_close_busid_dev:
122         status = UNBIND_ST_FAILED;
123 out:
124         udev_device_unref(dev);
125         udev_unref(udev);
126
127         return status;
128 }
129
130 static 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
182 int 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
207 err_out:
208         usbip_bind_usage();
209 out:
210         return ret;
211 }