1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2003-2008 Takahiro Hirofuchi |
4 | */ |
5 | |
6 | #include <linux/string.h> |
7 | #include <linux/module.h> |
8 | #include <linux/device.h> |
9 | #include <linux/scatterlist.h> |
10 | |
11 | #include "usbip_common.h" |
12 | #include "stub.h" |
13 | |
14 | #define DRIVER_AUTHOR "Takahiro Hirofuchi" |
15 | #define DRIVER_DESC "USB/IP Host Driver" |
16 | |
17 | struct kmem_cache *stub_priv_cache; |
18 | |
19 | /* |
20 | * busid_tables defines matching busids that usbip can grab. A user can change |
21 | * dynamically what device is locally used and what device is exported to a |
22 | * remote host. |
23 | */ |
24 | #define MAX_BUSID 16 |
25 | static struct bus_id_priv busid_table[MAX_BUSID]; |
26 | static DEFINE_SPINLOCK(busid_table_lock); |
27 | |
28 | static void init_busid_table(void) |
29 | { |
30 | int i; |
31 | |
32 | /* |
33 | * This also sets the bus_table[i].status to |
34 | * STUB_BUSID_OTHER, which is 0. |
35 | */ |
36 | memset(busid_table, 0, sizeof(busid_table)); |
37 | |
38 | for (i = 0; i < MAX_BUSID; i++) |
39 | spin_lock_init(&busid_table[i].busid_lock); |
40 | } |
41 | |
42 | /* |
43 | * Find the index of the busid by name. |
44 | * Must be called with busid_table_lock held. |
45 | */ |
46 | static int get_busid_idx(const char *busid) |
47 | { |
48 | int i; |
49 | int idx = -1; |
50 | |
51 | for (i = 0; i < MAX_BUSID; i++) { |
52 | spin_lock(lock: &busid_table[i].busid_lock); |
53 | if (busid_table[i].name[0]) |
54 | if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { |
55 | idx = i; |
56 | spin_unlock(lock: &busid_table[i].busid_lock); |
57 | break; |
58 | } |
59 | spin_unlock(lock: &busid_table[i].busid_lock); |
60 | } |
61 | return idx; |
62 | } |
63 | |
64 | /* Returns holding busid_lock. Should call put_busid_priv() to unlock */ |
65 | struct bus_id_priv *get_busid_priv(const char *busid) |
66 | { |
67 | int idx; |
68 | struct bus_id_priv *bid = NULL; |
69 | |
70 | spin_lock(lock: &busid_table_lock); |
71 | idx = get_busid_idx(busid); |
72 | if (idx >= 0) { |
73 | bid = &(busid_table[idx]); |
74 | /* get busid_lock before returning */ |
75 | spin_lock(lock: &bid->busid_lock); |
76 | } |
77 | spin_unlock(lock: &busid_table_lock); |
78 | |
79 | return bid; |
80 | } |
81 | |
82 | void put_busid_priv(struct bus_id_priv *bid) |
83 | { |
84 | if (bid) |
85 | spin_unlock(lock: &bid->busid_lock); |
86 | } |
87 | |
88 | static int add_match_busid(char *busid) |
89 | { |
90 | int i; |
91 | int ret = -1; |
92 | |
93 | spin_lock(lock: &busid_table_lock); |
94 | /* already registered? */ |
95 | if (get_busid_idx(busid) >= 0) { |
96 | ret = 0; |
97 | goto out; |
98 | } |
99 | |
100 | for (i = 0; i < MAX_BUSID; i++) { |
101 | spin_lock(lock: &busid_table[i].busid_lock); |
102 | if (!busid_table[i].name[0]) { |
103 | strscpy(p: busid_table[i].name, q: busid, BUSID_SIZE); |
104 | if ((busid_table[i].status != STUB_BUSID_ALLOC) && |
105 | (busid_table[i].status != STUB_BUSID_REMOV)) |
106 | busid_table[i].status = STUB_BUSID_ADDED; |
107 | ret = 0; |
108 | spin_unlock(lock: &busid_table[i].busid_lock); |
109 | break; |
110 | } |
111 | spin_unlock(lock: &busid_table[i].busid_lock); |
112 | } |
113 | |
114 | out: |
115 | spin_unlock(lock: &busid_table_lock); |
116 | |
117 | return ret; |
118 | } |
119 | |
120 | int del_match_busid(char *busid) |
121 | { |
122 | int idx; |
123 | int ret = -1; |
124 | |
125 | spin_lock(lock: &busid_table_lock); |
126 | idx = get_busid_idx(busid); |
127 | if (idx < 0) |
128 | goto out; |
129 | |
130 | /* found */ |
131 | ret = 0; |
132 | |
133 | spin_lock(lock: &busid_table[idx].busid_lock); |
134 | |
135 | if (busid_table[idx].status == STUB_BUSID_OTHER) |
136 | memset(busid_table[idx].name, 0, BUSID_SIZE); |
137 | |
138 | if ((busid_table[idx].status != STUB_BUSID_OTHER) && |
139 | (busid_table[idx].status != STUB_BUSID_ADDED)) |
140 | busid_table[idx].status = STUB_BUSID_REMOV; |
141 | |
142 | spin_unlock(lock: &busid_table[idx].busid_lock); |
143 | out: |
144 | spin_unlock(lock: &busid_table_lock); |
145 | |
146 | return ret; |
147 | } |
148 | |
149 | static ssize_t match_busid_show(struct device_driver *drv, char *buf) |
150 | { |
151 | int i; |
152 | char *out = buf; |
153 | |
154 | spin_lock(lock: &busid_table_lock); |
155 | for (i = 0; i < MAX_BUSID; i++) { |
156 | spin_lock(lock: &busid_table[i].busid_lock); |
157 | if (busid_table[i].name[0]) |
158 | out += sprintf(buf: out, fmt: "%s " , busid_table[i].name); |
159 | spin_unlock(lock: &busid_table[i].busid_lock); |
160 | } |
161 | spin_unlock(lock: &busid_table_lock); |
162 | out += sprintf(buf: out, fmt: "\n" ); |
163 | |
164 | return out - buf; |
165 | } |
166 | |
167 | static ssize_t match_busid_store(struct device_driver *dev, const char *buf, |
168 | size_t count) |
169 | { |
170 | char busid[BUSID_SIZE]; |
171 | |
172 | if (count < 5) |
173 | return -EINVAL; |
174 | |
175 | /* busid needs to include \0 termination */ |
176 | if (strscpy(p: busid, q: buf + 4, BUSID_SIZE) < 0) |
177 | return -EINVAL; |
178 | |
179 | if (!strncmp(buf, "add " , 4)) { |
180 | if (add_match_busid(busid) < 0) |
181 | return -ENOMEM; |
182 | |
183 | pr_debug("add busid %s\n" , busid); |
184 | return count; |
185 | } |
186 | |
187 | if (!strncmp(buf, "del " , 4)) { |
188 | if (del_match_busid(busid) < 0) |
189 | return -ENODEV; |
190 | |
191 | pr_debug("del busid %s\n" , busid); |
192 | return count; |
193 | } |
194 | |
195 | return -EINVAL; |
196 | } |
197 | static DRIVER_ATTR_RW(match_busid); |
198 | |
199 | static int do_rebind(char *busid, struct bus_id_priv *busid_priv) |
200 | { |
201 | int ret = 0; |
202 | |
203 | /* device_attach() callers should hold parent lock for USB */ |
204 | if (busid_priv->udev->dev.parent) |
205 | device_lock(dev: busid_priv->udev->dev.parent); |
206 | ret = device_attach(dev: &busid_priv->udev->dev); |
207 | if (busid_priv->udev->dev.parent) |
208 | device_unlock(dev: busid_priv->udev->dev.parent); |
209 | if (ret < 0) |
210 | dev_err(&busid_priv->udev->dev, "rebind failed\n" ); |
211 | return ret; |
212 | } |
213 | |
214 | static void stub_device_rebind(void) |
215 | { |
216 | #if IS_MODULE(CONFIG_USBIP_HOST) |
217 | struct bus_id_priv *busid_priv; |
218 | int i; |
219 | |
220 | /* update status to STUB_BUSID_OTHER so probe ignores the device */ |
221 | spin_lock(&busid_table_lock); |
222 | for (i = 0; i < MAX_BUSID; i++) { |
223 | if (busid_table[i].name[0] && |
224 | busid_table[i].shutdown_busid) { |
225 | busid_priv = &(busid_table[i]); |
226 | busid_priv->status = STUB_BUSID_OTHER; |
227 | } |
228 | } |
229 | spin_unlock(&busid_table_lock); |
230 | |
231 | /* now run rebind - no need to hold locks. driver files are removed */ |
232 | for (i = 0; i < MAX_BUSID; i++) { |
233 | if (busid_table[i].name[0] && |
234 | busid_table[i].shutdown_busid) { |
235 | busid_priv = &(busid_table[i]); |
236 | do_rebind(busid_table[i].name, busid_priv); |
237 | } |
238 | } |
239 | #endif |
240 | } |
241 | |
242 | static ssize_t rebind_store(struct device_driver *dev, const char *buf, |
243 | size_t count) |
244 | { |
245 | int ret; |
246 | int len; |
247 | struct bus_id_priv *bid; |
248 | |
249 | /* buf length should be less that BUSID_SIZE */ |
250 | len = strnlen(p: buf, BUSID_SIZE); |
251 | |
252 | if (!(len < BUSID_SIZE)) |
253 | return -EINVAL; |
254 | |
255 | bid = get_busid_priv(busid: buf); |
256 | if (!bid) |
257 | return -ENODEV; |
258 | |
259 | /* mark the device for deletion so probe ignores it during rescan */ |
260 | bid->status = STUB_BUSID_OTHER; |
261 | /* release the busid lock */ |
262 | put_busid_priv(bid); |
263 | |
264 | ret = do_rebind(busid: (char *) buf, busid_priv: bid); |
265 | if (ret < 0) |
266 | return ret; |
267 | |
268 | /* delete device from busid_table */ |
269 | del_match_busid(busid: (char *) buf); |
270 | |
271 | return count; |
272 | } |
273 | |
274 | static DRIVER_ATTR_WO(rebind); |
275 | |
276 | static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) |
277 | { |
278 | struct stub_priv *priv, *tmp; |
279 | |
280 | list_for_each_entry_safe(priv, tmp, listhead, list) { |
281 | list_del_init(entry: &priv->list); |
282 | return priv; |
283 | } |
284 | |
285 | return NULL; |
286 | } |
287 | |
288 | void stub_free_priv_and_urb(struct stub_priv *priv) |
289 | { |
290 | struct urb *urb; |
291 | int i; |
292 | |
293 | for (i = 0; i < priv->num_urbs; i++) { |
294 | urb = priv->urbs[i]; |
295 | |
296 | if (!urb) |
297 | return; |
298 | |
299 | kfree(objp: urb->setup_packet); |
300 | urb->setup_packet = NULL; |
301 | |
302 | |
303 | if (urb->transfer_buffer && !priv->sgl) { |
304 | kfree(objp: urb->transfer_buffer); |
305 | urb->transfer_buffer = NULL; |
306 | } |
307 | |
308 | if (urb->num_sgs) { |
309 | sgl_free(sgl: urb->sg); |
310 | urb->sg = NULL; |
311 | urb->num_sgs = 0; |
312 | } |
313 | |
314 | usb_free_urb(urb); |
315 | } |
316 | if (!list_empty(head: &priv->list)) |
317 | list_del(entry: &priv->list); |
318 | if (priv->sgl) |
319 | sgl_free(sgl: priv->sgl); |
320 | kfree(objp: priv->urbs); |
321 | kmem_cache_free(s: stub_priv_cache, objp: priv); |
322 | } |
323 | |
324 | static struct stub_priv *stub_priv_pop(struct stub_device *sdev) |
325 | { |
326 | unsigned long flags; |
327 | struct stub_priv *priv; |
328 | |
329 | spin_lock_irqsave(&sdev->priv_lock, flags); |
330 | |
331 | priv = stub_priv_pop_from_listhead(listhead: &sdev->priv_init); |
332 | if (priv) |
333 | goto done; |
334 | |
335 | priv = stub_priv_pop_from_listhead(listhead: &sdev->priv_tx); |
336 | if (priv) |
337 | goto done; |
338 | |
339 | priv = stub_priv_pop_from_listhead(listhead: &sdev->priv_free); |
340 | |
341 | done: |
342 | spin_unlock_irqrestore(lock: &sdev->priv_lock, flags); |
343 | |
344 | return priv; |
345 | } |
346 | |
347 | void stub_device_cleanup_urbs(struct stub_device *sdev) |
348 | { |
349 | struct stub_priv *priv; |
350 | int i; |
351 | |
352 | dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n" ); |
353 | |
354 | while ((priv = stub_priv_pop(sdev))) { |
355 | for (i = 0; i < priv->num_urbs; i++) |
356 | usb_kill_urb(urb: priv->urbs[i]); |
357 | |
358 | stub_free_priv_and_urb(priv); |
359 | } |
360 | } |
361 | |
362 | static int __init usbip_host_init(void) |
363 | { |
364 | int ret; |
365 | |
366 | init_busid_table(); |
367 | |
368 | stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); |
369 | if (!stub_priv_cache) { |
370 | pr_err("kmem_cache_create failed\n" ); |
371 | return -ENOMEM; |
372 | } |
373 | |
374 | ret = usb_register_device_driver(&stub_driver, THIS_MODULE); |
375 | if (ret) { |
376 | pr_err("usb_register failed %d\n" , ret); |
377 | goto err_usb_register; |
378 | } |
379 | |
380 | ret = driver_create_file(driver: &stub_driver.drvwrap.driver, |
381 | attr: &driver_attr_match_busid); |
382 | if (ret) { |
383 | pr_err("driver_create_file failed\n" ); |
384 | goto err_create_file; |
385 | } |
386 | |
387 | ret = driver_create_file(driver: &stub_driver.drvwrap.driver, |
388 | attr: &driver_attr_rebind); |
389 | if (ret) { |
390 | pr_err("driver_create_file failed\n" ); |
391 | goto err_create_file; |
392 | } |
393 | |
394 | return ret; |
395 | |
396 | err_create_file: |
397 | usb_deregister_device_driver(&stub_driver); |
398 | err_usb_register: |
399 | kmem_cache_destroy(s: stub_priv_cache); |
400 | return ret; |
401 | } |
402 | |
403 | static void __exit usbip_host_exit(void) |
404 | { |
405 | driver_remove_file(driver: &stub_driver.drvwrap.driver, |
406 | attr: &driver_attr_match_busid); |
407 | |
408 | driver_remove_file(driver: &stub_driver.drvwrap.driver, |
409 | attr: &driver_attr_rebind); |
410 | |
411 | /* |
412 | * deregister() calls stub_disconnect() for all devices. Device |
413 | * specific data is cleared in stub_disconnect(). |
414 | */ |
415 | usb_deregister_device_driver(&stub_driver); |
416 | |
417 | /* initiate scan to attach devices */ |
418 | stub_device_rebind(); |
419 | |
420 | kmem_cache_destroy(s: stub_priv_cache); |
421 | } |
422 | |
423 | module_init(usbip_host_init); |
424 | module_exit(usbip_host_exit); |
425 | |
426 | MODULE_AUTHOR(DRIVER_AUTHOR); |
427 | MODULE_DESCRIPTION(DRIVER_DESC); |
428 | MODULE_LICENSE("GPL" ); |
429 | |