Merge branch 'for-linus' of git://android.git.kernel.org/kernel/tegra
[pandora-kernel.git] / drivers / staging / usbip / stub_main.c
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/slab.h>
21
22 #include "usbip_common.h"
23 #include "stub.h"
24
25 /* Version Information */
26 #define DRIVER_VERSION "1.0"
27 #define DRIVER_AUTHOR "Takahiro Hirofuchi"
28 #define DRIVER_DESC "Stub Driver for USB/IP"
29
30 /* stub_priv is allocated from stub_priv_cache */
31 struct kmem_cache *stub_priv_cache;
32
33 /*-------------------------------------------------------------------------*/
34
35 /* Define sysfs entries for the usbip driver */
36
37
38 /*
39  * busid_tables defines matching busids that usbip can grab. A user can change
40  * dynamically what device is locally used and what device is exported to a
41  * remote host.
42  */
43 #define MAX_BUSID 16
44 static struct bus_id_priv busid_table[MAX_BUSID];
45 static spinlock_t busid_table_lock;
46
47
48 int match_busid(const char *busid)
49 {
50         int i;
51
52         spin_lock(&busid_table_lock);
53
54         for (i = 0; i < MAX_BUSID; i++)
55                 if (busid_table[i].name[0])
56                         if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
57                                 /* already registerd */
58                                 spin_unlock(&busid_table_lock);
59                                 return 0;
60                         }
61
62         spin_unlock(&busid_table_lock);
63
64         return 1;
65 }
66
67 struct bus_id_priv *get_busid_priv(const char *busid)
68 {
69         int i;
70
71         spin_lock(&busid_table_lock);
72
73         for (i = 0; i < MAX_BUSID; i++)
74                 if (busid_table[i].name[0])
75                         if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
76                                 /* already registerd */
77                                 spin_unlock(&busid_table_lock);
78                                 return &(busid_table[i]);
79                         }
80
81         spin_unlock(&busid_table_lock);
82
83         return NULL;
84 }
85
86 static ssize_t show_match_busid(struct device_driver *drv, char *buf)
87 {
88         int i;
89         char *out = buf;
90
91         spin_lock(&busid_table_lock);
92
93         for (i = 0; i < MAX_BUSID; i++)
94                 if (busid_table[i].name[0])
95                         out += sprintf(out, "%s ", busid_table[i].name);
96
97         spin_unlock(&busid_table_lock);
98
99         out += sprintf(out, "\n");
100
101         return out - buf;
102 }
103
104 static int add_match_busid(char *busid)
105 {
106         int i;
107
108         if (!match_busid(busid))
109                 return 0;
110
111         spin_lock(&busid_table_lock);
112
113         for (i = 0; i < MAX_BUSID; i++)
114                 if (!busid_table[i].name[0]) {
115                         strncpy(busid_table[i].name, busid, BUSID_SIZE);
116                         if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
117                             (busid_table[i].status != STUB_BUSID_REMOV))
118                                 busid_table[i].status = STUB_BUSID_ADDED;
119                         spin_unlock(&busid_table_lock);
120                         return 0;
121                 }
122
123         spin_unlock(&busid_table_lock);
124
125         return -1;
126 }
127
128 int del_match_busid(char *busid)
129 {
130         int i;
131
132         spin_lock(&busid_table_lock);
133
134         for (i = 0; i < MAX_BUSID; i++)
135                 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
136                         /* found */
137                         if (busid_table[i].status == STUB_BUSID_OTHER)
138                                 memset(busid_table[i].name, 0, BUSID_SIZE);
139                         if ((busid_table[i].status != STUB_BUSID_OTHER) &&
140                             (busid_table[i].status != STUB_BUSID_ADDED)) {
141                                 busid_table[i].status = STUB_BUSID_REMOV;
142                         }
143                         spin_unlock(&busid_table_lock);
144                         return 0;
145                 }
146
147         spin_unlock(&busid_table_lock);
148
149         return -1;
150 }
151 static void init_busid_table(void)
152 {
153         int i;
154
155
156         for (i = 0; i < MAX_BUSID; i++) {
157                 memset(busid_table[i].name, 0, BUSID_SIZE);
158                 busid_table[i].status = STUB_BUSID_OTHER;
159                 busid_table[i].interf_count = 0;
160                 busid_table[i].sdev = NULL;
161                 busid_table[i].shutdown_busid = 0;
162         }
163         spin_lock_init(&busid_table_lock);
164 }
165
166 static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
167                 size_t count)
168 {
169         int len;
170         char busid[BUSID_SIZE];
171
172         if (count < 5)
173                 return -EINVAL;
174
175         /* strnlen() does not include \0 */
176         len = strnlen(buf + 4, BUSID_SIZE);
177
178         /* busid needs to include \0 termination */
179         if (!(len < BUSID_SIZE))
180                 return -EINVAL;
181
182         strncpy(busid, buf + 4, BUSID_SIZE);
183
184
185         if (!strncmp(buf, "add ", 4)) {
186                 if (add_match_busid(busid) < 0)
187                         return -ENOMEM;
188                 else {
189                         usbip_udbg("add busid %s\n", busid);
190                         return count;
191                 }
192         } else if (!strncmp(buf, "del ", 4)) {
193                 if (del_match_busid(busid) < 0)
194                         return -ENODEV;
195                 else {
196                         usbip_udbg("del busid %s\n", busid);
197                         return count;
198                 }
199         } else
200                 return -EINVAL;
201 }
202
203 static DRIVER_ATTR(match_busid, S_IRUSR|S_IWUSR, show_match_busid,
204                                                         store_match_busid);
205
206
207
208 /*-------------------------------------------------------------------------*/
209
210 /* Cleanup functions used to free private data */
211
212 static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
213 {
214         struct stub_priv *priv, *tmp;
215
216         list_for_each_entry_safe(priv, tmp, listhead, list) {
217                 list_del(&priv->list);
218                 return priv;
219         }
220
221         return NULL;
222 }
223
224 static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
225 {
226         unsigned long flags;
227         struct stub_priv *priv;
228
229         spin_lock_irqsave(&sdev->priv_lock, flags);
230
231         priv = stub_priv_pop_from_listhead(&sdev->priv_init);
232         if (priv) {
233                 spin_unlock_irqrestore(&sdev->priv_lock, flags);
234                 return priv;
235         }
236
237         priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
238         if (priv) {
239                 spin_unlock_irqrestore(&sdev->priv_lock, flags);
240                 return priv;
241         }
242
243         priv = stub_priv_pop_from_listhead(&sdev->priv_free);
244         if (priv) {
245                 spin_unlock_irqrestore(&sdev->priv_lock, flags);
246                 return priv;
247         }
248
249         spin_unlock_irqrestore(&sdev->priv_lock, flags);
250         return NULL;
251 }
252
253 void stub_device_cleanup_urbs(struct stub_device *sdev)
254 {
255         struct stub_priv *priv;
256
257         usbip_udbg("free sdev %p\n", sdev);
258
259         while ((priv = stub_priv_pop(sdev))) {
260                 struct urb *urb = priv->urb;
261
262                 usbip_udbg("   free urb %p\n", urb);
263                 usb_kill_urb(urb);
264
265                 kmem_cache_free(stub_priv_cache, priv);
266
267                 kfree(urb->transfer_buffer);
268
269                 kfree(urb->setup_packet);
270
271                 usb_free_urb(urb);
272         }
273 }
274
275
276 /*-------------------------------------------------------------------------*/
277
278 static int __init usb_stub_init(void)
279 {
280         int ret;
281
282         stub_priv_cache = kmem_cache_create("stub_priv",
283                                             sizeof(struct stub_priv), 0,
284                                             SLAB_HWCACHE_ALIGN, NULL);
285
286         if (!stub_priv_cache) {
287                 printk(KERN_ERR KBUILD_MODNAME
288                        ": create stub_priv_cache error\n");
289                 return -ENOMEM;
290         }
291
292         ret = usb_register(&stub_driver);
293         if (ret) {
294                 printk(KERN_ERR KBUILD_MODNAME ": usb_register failed %d\n",
295                        ret);
296                 goto error_usb_register;
297         }
298
299         printk(KERN_INFO KBUILD_MODNAME ":"
300                DRIVER_DESC ":" DRIVER_VERSION "\n");
301
302         init_busid_table();
303
304         ret = driver_create_file(&stub_driver.drvwrap.driver,
305                                  &driver_attr_match_busid);
306
307         if (ret) {
308                 printk(KERN_ERR KBUILD_MODNAME ": create driver sysfs\n");
309                 goto error_create_file;
310         }
311
312         return ret;
313 error_create_file:
314         usb_deregister(&stub_driver);
315 error_usb_register:
316         kmem_cache_destroy(stub_priv_cache);
317         return ret;
318 }
319
320 static void __exit usb_stub_exit(void)
321 {
322         driver_remove_file(&stub_driver.drvwrap.driver,
323                            &driver_attr_match_busid);
324
325         /*
326          * deregister() calls stub_disconnect() for all devices. Device
327          * specific data is cleared in stub_disconnect().
328          */
329         usb_deregister(&stub_driver);
330
331         kmem_cache_destroy(stub_priv_cache);
332 }
333
334 module_init(usb_stub_init);
335 module_exit(usb_stub_exit);
336
337 MODULE_AUTHOR(DRIVER_AUTHOR);
338 MODULE_DESCRIPTION(DRIVER_DESC);
339 MODULE_LICENSE("GPL");