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