Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / usbback / usbstub.c
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, by your choice,
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  */
45
46 #include "usbback.h"
47
48 static LIST_HEAD(port_list);
49 static DEFINE_SPINLOCK(port_list_lock);
50
51 struct vusb_port_id *find_portid_by_busid(const char *busid)
52 {
53         struct vusb_port_id *portid;
54         int found = 0;
55         unsigned long flags;
56
57         spin_lock_irqsave(&port_list_lock, flags);
58         list_for_each_entry(portid, &port_list, id_list) {
59                 if (!(strncmp(portid->phys_bus, busid, USBBACK_BUS_ID_SIZE))) {
60                         found = 1;
61                         break;
62                 }
63         }
64         spin_unlock_irqrestore(&port_list_lock, flags);
65
66         if (found)
67                 return portid;
68
69         return NULL;
70 }
71
72 struct vusb_port_id *find_portid(const domid_t domid,
73                                                 const unsigned int handle,
74                                                 const int portnum)
75 {
76         struct vusb_port_id *portid;
77         int found = 0;
78         unsigned long flags;
79
80         spin_lock_irqsave(&port_list_lock, flags);
81         list_for_each_entry(portid, &port_list, id_list) {
82                 if ((portid->domid == domid)
83                                 && (portid->handle == handle)
84                                 && (portid->portnum == portnum)) {
85                                 found = 1;
86                                 break;
87                 }
88         }
89         spin_unlock_irqrestore(&port_list_lock, flags);
90
91         if (found)
92                 return portid;
93
94         return NULL;
95 }
96
97 int portid_add(const char *busid,
98                                         const domid_t domid,
99                                         const unsigned int handle,
100                                         const int portnum)
101 {
102         struct vusb_port_id *portid;
103         unsigned long flags;
104
105         portid = kzalloc(sizeof(*portid), GFP_KERNEL);
106         if (!portid)
107                 return -ENOMEM;
108
109         portid->domid = domid;
110         portid->handle = handle;
111         portid->portnum = portnum;
112
113         strlcpy(portid->phys_bus, busid, USBBACK_BUS_ID_SIZE);
114
115         spin_lock_irqsave(&port_list_lock, flags);
116         list_add(&portid->id_list, &port_list);
117         spin_unlock_irqrestore(&port_list_lock, flags);
118
119         return 0;
120 }
121
122 int portid_remove(const domid_t domid,
123                                         const unsigned int handle,
124                                         const int portnum)
125 {
126         struct vusb_port_id *portid, *tmp;
127         int err = -ENOENT;
128         unsigned long flags;
129
130         spin_lock_irqsave(&port_list_lock, flags);
131         list_for_each_entry_safe(portid, tmp, &port_list, id_list) {
132                 if (portid->domid == domid
133                                 && portid->handle == handle
134                                 && portid->portnum == portnum) {
135                         list_del(&portid->id_list);
136                         kfree(portid);
137
138                         err = 0;
139                 }
140         }
141         spin_unlock_irqrestore(&port_list_lock, flags);
142
143         return err;
144 }
145
146 static struct usbstub *usbstub_alloc(struct usb_device *udev,
147                                                 struct vusb_port_id *portid)
148 {
149         struct usbstub *stub;
150
151         stub = kzalloc(sizeof(*stub), GFP_KERNEL);
152         if (!stub) {
153                 pr_err("no memory for usbstub\n");
154                 return NULL;
155         }
156         kref_init(&stub->kref);
157         stub->udev = usb_get_dev(udev);
158         stub->portid = portid;
159         spin_lock_init(&stub->submitting_lock);
160         INIT_LIST_HEAD(&stub->submitting_list);
161
162         return stub;
163 }
164
165 static void usbstub_release(struct kref *kref)
166 {
167         struct usbstub *stub;
168
169         stub = container_of(kref, struct usbstub, kref);
170
171         usb_put_dev(stub->udev);
172         stub->udev = NULL;
173         stub->portid = NULL;
174         kfree(stub);
175 }
176
177 static inline void usbstub_get(struct usbstub *stub)
178 {
179         kref_get(&stub->kref);
180 }
181
182 static inline void usbstub_put(struct usbstub *stub)
183 {
184         kref_put(&stub->kref, usbstub_release);
185 }
186
187 static int usbstub_probe(struct usb_interface *intf,
188                 const struct usb_device_id *id)
189 {
190         struct usb_device *udev = interface_to_usbdev(intf);
191         const char *busid = dev_name(intf->dev.parent);
192         struct vusb_port_id *portid = NULL;
193         struct usbstub *stub = NULL;
194         usbif_t *usbif = NULL;
195         int retval = -ENODEV;
196
197         /* hub currently not supported, so skip. */
198         if (udev->descriptor.bDeviceClass ==  USB_CLASS_HUB)
199                 goto out;
200
201         portid = find_portid_by_busid(busid);
202         if (!portid)
203                 goto out;
204
205         usbif = find_usbif(portid->domid, portid->handle);
206         if (!usbif)
207                 goto out;
208
209         switch (udev->speed) {
210         case USB_SPEED_LOW:
211         case USB_SPEED_FULL:
212                 break;
213         case USB_SPEED_HIGH:
214                 if (usbif->usb_ver >= USB_VER_USB20)
215                         break;
216                 /* fall through */
217         default:
218                 goto out;
219         }
220
221         stub = find_attached_device(usbif, portid->portnum);
222         if (!stub) {
223                 /* new connection */
224                 stub = usbstub_alloc(udev, portid);
225                 if (!stub)
226                         return -ENOMEM;
227                 usbbk_attach_device(usbif, stub);
228                 usbbk_hotplug_notify(usbif, portid->portnum, udev->speed);
229         } else {
230                 /* maybe already called and connected by other intf */
231                 if (strncmp(stub->portid->phys_bus, busid, USBBACK_BUS_ID_SIZE))
232                         goto out; /* invalid call */
233         }
234
235         usbstub_get(stub);
236         usb_set_intfdata(intf, stub);
237         retval = 0;
238
239 out:
240         return retval;
241 }
242
243 static void usbstub_disconnect(struct usb_interface *intf)
244 {
245         struct usbstub *stub
246                 = (struct usbstub *) usb_get_intfdata(intf);
247
248         usb_set_intfdata(intf, NULL);
249
250         if (!stub)
251                 return;
252
253         if (stub->usbif) {
254                 usbbk_hotplug_notify(stub->usbif, stub->portid->portnum, 0);
255                 usbbk_detach_device(stub->usbif, stub);
256         }
257         usbbk_unlink_urbs(stub);
258         usbstub_put(stub);
259 }
260
261 static ssize_t usbstub_show_portids(struct device_driver *driver,
262                 char *buf)
263 {
264         struct vusb_port_id *portid;
265         size_t count = 0;
266         unsigned long flags;
267
268         spin_lock_irqsave(&port_list_lock, flags);
269         list_for_each_entry(portid, &port_list, id_list) {
270                 if (count >= PAGE_SIZE)
271                         break;
272                 count += scnprintf((char *)buf + count, PAGE_SIZE - count,
273                                 "%s:%d:%d:%d\n",
274                                 &portid->phys_bus[0],
275                                 portid->domid,
276                                 portid->handle,
277                                 portid->portnum);
278         }
279         spin_unlock_irqrestore(&port_list_lock, flags);
280
281         return count;
282 }
283 static DRIVER_ATTR(port_ids, S_IRUSR, usbstub_show_portids, NULL);
284
285 /* table of devices that matches any usbdevice */
286 static const struct usb_device_id usbstub_table[] = {
287                 { .driver_info = 1 }, /* wildcard, see usb_match_id() */
288                 { } /* Terminating entry */
289 };
290 MODULE_DEVICE_TABLE(usb, usbstub_table);
291
292 static struct usb_driver usbback_usb_driver = {
293                 .name = "usbback",
294                 .probe = usbstub_probe,
295                 .disconnect = usbstub_disconnect,
296                 .id_table = usbstub_table,
297                 .no_dynamic_id = 1,
298 };
299
300 int __init usbstub_init(void)
301 {
302         int err;
303
304         err = usb_register(&usbback_usb_driver);
305         if (err < 0) {
306                 pr_err("usbback: usb_register failed (%d)\n", err);
307                 goto out;
308         }
309
310         err = driver_create_file(&usbback_usb_driver.drvwrap.driver,
311                                 &driver_attr_port_ids);
312         if (err)
313                 usb_deregister(&usbback_usb_driver);
314
315 out:
316         return err;
317 }
318
319 void usbstub_exit(void)
320 {
321         driver_remove_file(&usbback_usb_driver.drvwrap.driver,
322                                 &driver_attr_port_ids);
323         usb_deregister(&usbback_usb_driver);
324 }