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