• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
usbip_bind_usage(void)32 void usbip_bind_usage(void)
33 {
34 	printf("usage: %s", usbip_bind_usage_string);
35 }
36 
37 /* call at unbound state */
bind_usbip(char * busid)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 */
unbind_other(char * busid)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 
bind_device(char * busid)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 
usbip_bind(int argc,char * argv[])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 }
212