ia64/linux-2.6.18-xen.hg

view drivers/xen/usbback/usbstub.c @ 829:f799db0570f2

PVUSB: backend driver

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 18 11:43:24 2009 +0000 (2009-03-18)
parents
children 4c7eb2e71e9d
line source
1 /*
2 * usbstub.c
3 *
4 * USB stub driver - grabbing and managing USB devices.
5 *
6 * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
7 * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 *
22 * or,
23 *
24 * When distributed separately from the Linux kernel or incorporated into
25 * other software packages, subject to the following license:
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to
29 * deal in the Software without restriction, including without limitation the
30 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
31 * sell copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
42 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
43 * DEALINGS IN THE SOFTWARE.
44 */
46 #include "usbback.h"
48 static LIST_HEAD(usbstub_ids);
49 static DEFINE_SPINLOCK(usbstub_ids_lock);
50 static LIST_HEAD(grabbed_devices);
51 static DEFINE_SPINLOCK(grabbed_devices_lock);
53 struct usbstub *find_grabbed_device(int dom_id, int dev_id, int portnum)
54 {
55 struct usbstub *stub;
56 int found = 0;
57 unsigned long flags;
59 spin_lock_irqsave(&grabbed_devices_lock, flags);
60 list_for_each_entry(stub, &grabbed_devices, grabbed_list) {
61 if (stub->id->dom_id == dom_id
62 && stub->id->dev_id == dev_id
63 && stub->id->portnum == portnum) {
64 found = 1;
65 break;
66 }
67 }
68 spin_unlock_irqrestore(&grabbed_devices_lock, flags);
70 if (found)
71 return stub;
73 return NULL;
74 }
76 static struct usbstub *usbstub_alloc(struct usb_interface *interface,
77 struct usbstub_id *stub_id)
78 {
79 struct usbstub *stub;
81 stub = kzalloc(sizeof(*stub), GFP_KERNEL);
82 if (!stub) {
83 printk(KERN_ERR "no memory for alloc usbstub\n");
84 return NULL;
85 }
87 stub->udev = usb_get_dev(interface_to_usbdev(interface));
88 stub->interface = interface;
89 stub->id = stub_id;
90 spin_lock_init(&stub->submitting_lock);
91 INIT_LIST_HEAD(&stub->submitting_list);
93 return stub;
94 }
96 static int usbstub_free(struct usbstub *stub)
97 {
98 if (!stub)
99 return -EINVAL;
101 usb_put_dev(stub->udev);
102 stub->interface = NULL;
103 stub->udev = NULL;
104 stub->id = NULL;
105 kfree(stub);
107 return 0;
108 }
110 static int usbstub_match_one(struct usb_interface *interface,
111 struct usbstub_id *stub_id)
112 {
113 char *udev_busid = interface->dev.parent->bus_id;
115 if (!(strncmp(stub_id->bus_id, udev_busid, BUS_ID_SIZE))) {
116 return 1;
117 }
119 return 0;
120 }
122 static struct usbstub_id *usbstub_match(struct usb_interface *interface)
123 {
124 struct usb_device *udev = interface_to_usbdev(interface);
125 struct usbstub_id *stub_id;
126 unsigned long flags;
127 int found = 0;
129 /* hub currently not supported, so skip. */
130 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
131 return NULL;
133 spin_lock_irqsave(&usbstub_ids_lock, flags);
134 list_for_each_entry(stub_id, &usbstub_ids, id_list) {
135 if (usbstub_match_one(interface, stub_id)) {
136 found = 1;
137 break;
138 }
139 }
140 spin_unlock_irqrestore(&usbstub_ids_lock, flags);
142 if (found)
143 return stub_id;
145 return NULL;
146 }
148 static void add_to_grabbed_devices(struct usbstub *stub)
149 {
150 unsigned long flags;
152 spin_lock_irqsave(&grabbed_devices_lock, flags);
153 list_add(&stub->grabbed_list, &grabbed_devices);
154 spin_unlock_irqrestore(&grabbed_devices_lock, flags);
155 }
157 static void remove_from_grabbed_devices(struct usbstub *stub)
158 {
159 unsigned long flags;
161 spin_lock_irqsave(&grabbed_devices_lock, flags);
162 list_del(&stub->grabbed_list);
163 spin_unlock_irqrestore(&grabbed_devices_lock, flags);
164 }
166 static int usbstub_probe(struct usb_interface *interface,
167 const struct usb_device_id *id)
168 {
169 struct usbstub_id *stub_id = NULL;
170 struct usbstub *stub = NULL;
171 usbif_t *usbif = NULL;
172 int retval = 0;
174 if ((stub_id = usbstub_match(interface))) {
175 stub = usbstub_alloc(interface, stub_id);
176 if (!stub)
177 return -ENOMEM;
179 usb_set_intfdata(interface, stub);
180 add_to_grabbed_devices(stub);
181 usbif = find_usbif(stub_id->dom_id, stub_id->dev_id);
182 if (usbif) {
183 usbbk_plug_device(usbif, stub);
184 usbback_reconfigure(usbif);
185 }
187 } else
188 retval = -ENODEV;
190 return retval;
191 }
193 static void usbstub_disconnect(struct usb_interface *interface)
194 {
195 struct usbstub *stub
196 = (struct usbstub *) usb_get_intfdata(interface);
198 usb_set_intfdata(interface, NULL);
200 if (!stub)
201 return;
203 if (stub->usbif) {
204 usbback_reconfigure(stub->usbif);
205 usbbk_unplug_device(stub->usbif, stub);
206 }
208 usbbk_unlink_urbs(stub);
210 remove_from_grabbed_devices(stub);
212 usbstub_free(stub);
214 return;
215 }
217 static inline int str_to_vport(const char *buf,
218 char *phys_bus,
219 int *dom_id,
220 int *dev_id,
221 int *port)
222 {
223 char *p;
224 int len;
225 int err;
227 /* no physical bus */
228 if (!(p = strchr(buf, ':')))
229 return -EINVAL;
231 len = p - buf;
233 /* bad physical bus */
234 if (len + 1 > BUS_ID_SIZE)
235 return -EINVAL;
237 strlcpy(phys_bus, buf, len + 1);
238 err = sscanf(p + 1, "%d:%d:%d", dom_id, dev_id, port);
239 if (err == 3)
240 return 0;
241 else
242 return -EINVAL;
243 }
245 static int usbstub_id_add(const char *bus_id,
246 const int dom_id,
247 const int dev_id,
248 const int portnum)
249 {
250 struct usbstub_id *stub_id;
251 unsigned long flags;
253 stub_id = kzalloc(sizeof(*stub_id), GFP_KERNEL);
254 if (!stub_id)
255 return -ENOMEM;
257 stub_id->dom_id = dom_id;
258 stub_id->dev_id = dev_id;
259 stub_id->portnum = portnum;
261 strncpy(stub_id->bus_id, bus_id, BUS_ID_SIZE);
263 spin_lock_irqsave(&usbstub_ids_lock, flags);
264 list_add(&stub_id->id_list, &usbstub_ids);
265 spin_unlock_irqrestore(&usbstub_ids_lock, flags);
267 return 0;
268 }
270 static int usbstub_id_remove(const char *phys_bus,
271 const int dom_id,
272 const int dev_id,
273 const int portnum)
274 {
275 struct usbstub_id *stub_id, *tmp;
276 int err = -ENOENT;
277 unsigned long flags;
279 spin_lock_irqsave(&usbstub_ids_lock, flags);
280 list_for_each_entry_safe(stub_id, tmp, &usbstub_ids, id_list) {
281 if (stub_id->dom_id == dom_id
282 && stub_id->dev_id == dev_id
283 && stub_id->portnum == portnum) {
284 list_del(&stub_id->id_list);
285 kfree(stub_id);
287 err = 0;
288 }
289 }
290 spin_unlock_irqrestore(&usbstub_ids_lock, flags);
292 return err;
293 }
295 static ssize_t usbstub_vport_add(struct device_driver *driver,
296 const char *buf, size_t count)
297 {
298 int err = 0;
300 char bus_id[BUS_ID_SIZE];
301 int dom_id;
302 int dev_id;
303 int portnum;
305 err = str_to_vport(buf, &bus_id[0], &dom_id, &dev_id, &portnum);
306 if (err)
307 goto out;
309 err = usbstub_id_add(&bus_id[0], dom_id, dev_id, portnum);
311 out:
312 if (!err)
313 err = count;
314 return err;
315 }
317 DRIVER_ATTR(new_vport, S_IWUSR, NULL, usbstub_vport_add);
319 static ssize_t usbstub_vport_remove(struct device_driver *driver,
320 const char *buf, size_t count)
321 {
322 int err = 0;
324 char bus_id[BUS_ID_SIZE];
325 int dom_id;
326 int dev_id;
327 int portnum;
329 err = str_to_vport(buf, &bus_id[0], &dom_id, &dev_id, &portnum);
330 if (err)
331 goto out;
333 err = usbstub_id_remove(&bus_id[0], dom_id, dev_id, portnum);
335 out:
336 if (!err)
337 err = count;
338 return err;
339 }
341 DRIVER_ATTR(remove_vport, S_IWUSR, NULL, usbstub_vport_remove);
343 static ssize_t usbstub_vport_show(struct device_driver *driver,
344 char *buf)
345 {
346 struct usbstub_id *stub_id;
347 size_t count = 0;
348 unsigned long flags;
350 spin_lock_irqsave(&usbstub_ids_lock, flags);
351 list_for_each_entry(stub_id, &usbstub_ids, id_list) {
352 if (count >= PAGE_SIZE)
353 break;
354 count += scnprintf((char *)buf + count, PAGE_SIZE - count,
355 "%s:%d:%d:%d\n",
356 &stub_id->bus_id[0],
357 stub_id->dom_id,
358 stub_id->dev_id,
359 stub_id->portnum);
360 }
361 spin_unlock_irqrestore(&usbstub_ids_lock, flags);
363 return count;
364 }
366 DRIVER_ATTR(vports, S_IRUSR, usbstub_vport_show, NULL);
368 static ssize_t usbstub_devices_show(struct device_driver *driver,
369 char *buf)
370 {
371 struct usbstub *stub;
372 size_t count = 0;
373 unsigned long flags;
375 spin_lock_irqsave(&grabbed_devices_lock, flags);
376 list_for_each_entry(stub, &grabbed_devices, grabbed_list) {
377 if (count >= PAGE_SIZE)
378 break;
380 count += scnprintf((char *)buf + count, PAGE_SIZE - count,
381 "%u-%s:%u.%u\n",
382 stub->udev->bus->busnum,
383 stub->udev->devpath,
384 stub->udev->config->desc.bConfigurationValue,
385 stub->interface->cur_altsetting->desc.bInterfaceNumber);
387 }
388 spin_unlock_irqrestore(&grabbed_devices_lock, flags);
390 return count;
391 }
393 DRIVER_ATTR(grabbed_devices, S_IRUSR, usbstub_devices_show, NULL);
395 /* table of devices that matches any usbdevice */
396 static struct usb_device_id usbstub_table[] = {
397 { .driver_info = 1 }, /* wildcard, see usb_match_id() */
398 { } /* Terminating entry */
399 };
400 MODULE_DEVICE_TABLE(usb, usbstub_table);
402 static struct usb_driver usbback_usb_driver = {
403 .name = "usbback",
404 .probe = usbstub_probe,
405 .disconnect = usbstub_disconnect,
406 .id_table = usbstub_table,
407 };
409 int __init usbstub_init(void)
410 {
411 int err;
413 err = usb_register(&usbback_usb_driver);
414 if (err < 0)
415 goto out;
416 if (!err)
417 err = driver_create_file(&usbback_usb_driver.driver,
418 &driver_attr_new_vport);
419 if (!err)
420 err = driver_create_file(&usbback_usb_driver.driver,
421 &driver_attr_remove_vport);
422 if (!err)
423 err = driver_create_file(&usbback_usb_driver.driver,
424 &driver_attr_vports);
425 if (!err)
426 err = driver_create_file(&usbback_usb_driver.driver,
427 &driver_attr_grabbed_devices);
428 if (err)
429 usbstub_exit();
431 out:
432 return err;
433 }
435 void __exit usbstub_exit(void)
436 {
437 driver_remove_file(&usbback_usb_driver.driver,
438 &driver_attr_new_vport);
439 driver_remove_file(&usbback_usb_driver.driver,
440 &driver_attr_remove_vport);
441 driver_remove_file(&usbback_usb_driver.driver,
442 &driver_attr_vports);
443 driver_remove_file(&usbback_usb_driver.driver,
444 &driver_attr_grabbed_devices);
446 usb_deregister(&usbback_usb_driver);
447 }