diff options
Diffstat (limited to 'drivers/usb/usbip/stub_main.c')
-rw-r--r-- | drivers/usb/usbip/stub_main.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c new file mode 100644 index 000000000000..44ab43fc4fcc --- /dev/null +++ b/drivers/usb/usbip/stub_main.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003-2008 Takahiro Hirofuchi | ||
3 | * | ||
4 | * This is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | ||
17 | * USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/string.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/device.h> | ||
23 | |||
24 | #include "usbip_common.h" | ||
25 | #include "stub.h" | ||
26 | |||
27 | #define DRIVER_AUTHOR "Takahiro Hirofuchi" | ||
28 | #define DRIVER_DESC "USB/IP Host Driver" | ||
29 | |||
30 | struct kmem_cache *stub_priv_cache; | ||
31 | /* | ||
32 | * busid_tables defines matching busids that usbip can grab. A user can change | ||
33 | * dynamically what device is locally used and what device is exported to a | ||
34 | * remote host. | ||
35 | */ | ||
36 | #define MAX_BUSID 16 | ||
37 | static struct bus_id_priv busid_table[MAX_BUSID]; | ||
38 | static spinlock_t busid_table_lock; | ||
39 | |||
40 | static void init_busid_table(void) | ||
41 | { | ||
42 | /* | ||
43 | * This also sets the bus_table[i].status to | ||
44 | * STUB_BUSID_OTHER, which is 0. | ||
45 | */ | ||
46 | memset(busid_table, 0, sizeof(busid_table)); | ||
47 | |||
48 | spin_lock_init(&busid_table_lock); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Find the index of the busid by name. | ||
53 | * Must be called with busid_table_lock held. | ||
54 | */ | ||
55 | static int get_busid_idx(const char *busid) | ||
56 | { | ||
57 | int i; | ||
58 | int idx = -1; | ||
59 | |||
60 | for (i = 0; i < MAX_BUSID; i++) | ||
61 | if (busid_table[i].name[0]) | ||
62 | if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { | ||
63 | idx = i; | ||
64 | break; | ||
65 | } | ||
66 | return idx; | ||
67 | } | ||
68 | |||
69 | struct bus_id_priv *get_busid_priv(const char *busid) | ||
70 | { | ||
71 | int idx; | ||
72 | struct bus_id_priv *bid = NULL; | ||
73 | |||
74 | spin_lock(&busid_table_lock); | ||
75 | idx = get_busid_idx(busid); | ||
76 | if (idx >= 0) | ||
77 | bid = &(busid_table[idx]); | ||
78 | spin_unlock(&busid_table_lock); | ||
79 | |||
80 | return bid; | ||
81 | } | ||
82 | |||
83 | static int add_match_busid(char *busid) | ||
84 | { | ||
85 | int i; | ||
86 | int ret = -1; | ||
87 | |||
88 | spin_lock(&busid_table_lock); | ||
89 | /* already registered? */ | ||
90 | if (get_busid_idx(busid) >= 0) { | ||
91 | ret = 0; | ||
92 | goto out; | ||
93 | } | ||
94 | |||
95 | for (i = 0; i < MAX_BUSID; i++) | ||
96 | if (!busid_table[i].name[0]) { | ||
97 | strlcpy(busid_table[i].name, busid, BUSID_SIZE); | ||
98 | if ((busid_table[i].status != STUB_BUSID_ALLOC) && | ||
99 | (busid_table[i].status != STUB_BUSID_REMOV)) | ||
100 | busid_table[i].status = STUB_BUSID_ADDED; | ||
101 | ret = 0; | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | out: | ||
106 | spin_unlock(&busid_table_lock); | ||
107 | |||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | int del_match_busid(char *busid) | ||
112 | { | ||
113 | int idx; | ||
114 | int ret = -1; | ||
115 | |||
116 | spin_lock(&busid_table_lock); | ||
117 | idx = get_busid_idx(busid); | ||
118 | if (idx < 0) | ||
119 | goto out; | ||
120 | |||
121 | /* found */ | ||
122 | ret = 0; | ||
123 | |||
124 | if (busid_table[idx].status == STUB_BUSID_OTHER) | ||
125 | memset(busid_table[idx].name, 0, BUSID_SIZE); | ||
126 | |||
127 | if ((busid_table[idx].status != STUB_BUSID_OTHER) && | ||
128 | (busid_table[idx].status != STUB_BUSID_ADDED)) | ||
129 | busid_table[idx].status = STUB_BUSID_REMOV; | ||
130 | |||
131 | out: | ||
132 | spin_unlock(&busid_table_lock); | ||
133 | |||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | static ssize_t show_match_busid(struct device_driver *drv, char *buf) | ||
138 | { | ||
139 | int i; | ||
140 | char *out = buf; | ||
141 | |||
142 | spin_lock(&busid_table_lock); | ||
143 | for (i = 0; i < MAX_BUSID; i++) | ||
144 | if (busid_table[i].name[0]) | ||
145 | out += sprintf(out, "%s ", busid_table[i].name); | ||
146 | spin_unlock(&busid_table_lock); | ||
147 | out += sprintf(out, "\n"); | ||
148 | |||
149 | return out - buf; | ||
150 | } | ||
151 | |||
152 | static ssize_t store_match_busid(struct device_driver *dev, const char *buf, | ||
153 | size_t count) | ||
154 | { | ||
155 | int len; | ||
156 | char busid[BUSID_SIZE]; | ||
157 | |||
158 | if (count < 5) | ||
159 | return -EINVAL; | ||
160 | |||
161 | /* busid needs to include \0 termination */ | ||
162 | len = strlcpy(busid, buf + 4, BUSID_SIZE); | ||
163 | if (sizeof(busid) <= len) | ||
164 | return -EINVAL; | ||
165 | |||
166 | if (!strncmp(buf, "add ", 4)) { | ||
167 | if (add_match_busid(busid) < 0) | ||
168 | return -ENOMEM; | ||
169 | |||
170 | pr_debug("add busid %s\n", busid); | ||
171 | return count; | ||
172 | } | ||
173 | |||
174 | if (!strncmp(buf, "del ", 4)) { | ||
175 | if (del_match_busid(busid) < 0) | ||
176 | return -ENODEV; | ||
177 | |||
178 | pr_debug("del busid %s\n", busid); | ||
179 | return count; | ||
180 | } | ||
181 | |||
182 | return -EINVAL; | ||
183 | } | ||
184 | static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, | ||
185 | store_match_busid); | ||
186 | |||
187 | static ssize_t rebind_store(struct device_driver *dev, const char *buf, | ||
188 | size_t count) | ||
189 | { | ||
190 | int ret; | ||
191 | int len; | ||
192 | struct bus_id_priv *bid; | ||
193 | |||
194 | /* buf length should be less that BUSID_SIZE */ | ||
195 | len = strnlen(buf, BUSID_SIZE); | ||
196 | |||
197 | if (!(len < BUSID_SIZE)) | ||
198 | return -EINVAL; | ||
199 | |||
200 | bid = get_busid_priv(buf); | ||
201 | if (!bid) | ||
202 | return -ENODEV; | ||
203 | |||
204 | ret = device_attach(&bid->udev->dev); | ||
205 | if (ret < 0) { | ||
206 | dev_err(&bid->udev->dev, "rebind failed\n"); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | return count; | ||
211 | } | ||
212 | |||
213 | static DRIVER_ATTR_WO(rebind); | ||
214 | |||
215 | static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) | ||
216 | { | ||
217 | struct stub_priv *priv, *tmp; | ||
218 | |||
219 | list_for_each_entry_safe(priv, tmp, listhead, list) { | ||
220 | list_del(&priv->list); | ||
221 | return priv; | ||
222 | } | ||
223 | |||
224 | return NULL; | ||
225 | } | ||
226 | |||
227 | static struct stub_priv *stub_priv_pop(struct stub_device *sdev) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | struct stub_priv *priv; | ||
231 | |||
232 | spin_lock_irqsave(&sdev->priv_lock, flags); | ||
233 | |||
234 | priv = stub_priv_pop_from_listhead(&sdev->priv_init); | ||
235 | if (priv) | ||
236 | goto done; | ||
237 | |||
238 | priv = stub_priv_pop_from_listhead(&sdev->priv_tx); | ||
239 | if (priv) | ||
240 | goto done; | ||
241 | |||
242 | priv = stub_priv_pop_from_listhead(&sdev->priv_free); | ||
243 | |||
244 | done: | ||
245 | spin_unlock_irqrestore(&sdev->priv_lock, flags); | ||
246 | |||
247 | return priv; | ||
248 | } | ||
249 | |||
250 | void stub_device_cleanup_urbs(struct stub_device *sdev) | ||
251 | { | ||
252 | struct stub_priv *priv; | ||
253 | struct urb *urb; | ||
254 | |||
255 | dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); | ||
256 | |||
257 | while ((priv = stub_priv_pop(sdev))) { | ||
258 | urb = priv->urb; | ||
259 | dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); | ||
260 | usb_kill_urb(urb); | ||
261 | |||
262 | kmem_cache_free(stub_priv_cache, priv); | ||
263 | |||
264 | kfree(urb->transfer_buffer); | ||
265 | kfree(urb->setup_packet); | ||
266 | usb_free_urb(urb); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static int __init usbip_host_init(void) | ||
271 | { | ||
272 | int ret; | ||
273 | |||
274 | init_busid_table(); | ||
275 | |||
276 | stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); | ||
277 | if (!stub_priv_cache) { | ||
278 | pr_err("kmem_cache_create failed\n"); | ||
279 | return -ENOMEM; | ||
280 | } | ||
281 | |||
282 | ret = usb_register_device_driver(&stub_driver, THIS_MODULE); | ||
283 | if (ret) { | ||
284 | pr_err("usb_register failed %d\n", ret); | ||
285 | goto err_usb_register; | ||
286 | } | ||
287 | |||
288 | ret = driver_create_file(&stub_driver.drvwrap.driver, | ||
289 | &driver_attr_match_busid); | ||
290 | if (ret) { | ||
291 | pr_err("driver_create_file failed\n"); | ||
292 | goto err_create_file; | ||
293 | } | ||
294 | |||
295 | ret = driver_create_file(&stub_driver.drvwrap.driver, | ||
296 | &driver_attr_rebind); | ||
297 | if (ret) { | ||
298 | pr_err("driver_create_file failed\n"); | ||
299 | goto err_create_file; | ||
300 | } | ||
301 | |||
302 | pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); | ||
303 | return ret; | ||
304 | |||
305 | err_create_file: | ||
306 | usb_deregister_device_driver(&stub_driver); | ||
307 | err_usb_register: | ||
308 | kmem_cache_destroy(stub_priv_cache); | ||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | static void __exit usbip_host_exit(void) | ||
313 | { | ||
314 | driver_remove_file(&stub_driver.drvwrap.driver, | ||
315 | &driver_attr_match_busid); | ||
316 | |||
317 | driver_remove_file(&stub_driver.drvwrap.driver, | ||
318 | &driver_attr_rebind); | ||
319 | |||
320 | /* | ||
321 | * deregister() calls stub_disconnect() for all devices. Device | ||
322 | * specific data is cleared in stub_disconnect(). | ||
323 | */ | ||
324 | usb_deregister_device_driver(&stub_driver); | ||
325 | |||
326 | kmem_cache_destroy(stub_priv_cache); | ||
327 | } | ||
328 | |||
329 | module_init(usbip_host_init); | ||
330 | module_exit(usbip_host_exit); | ||
331 | |||
332 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
333 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
334 | MODULE_LICENSE("GPL"); | ||
335 | MODULE_VERSION(USBIP_VERSION); | ||