]>
Commit | Line | Data |
---|---|---|
816ac92e JG |
1 | /* |
2 | * xen paravirt usb device backend | |
3 | * | |
4 | * (c) Juergen Gross <[email protected]> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; under version 2 of the License. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
17 | * | |
18 | * Contributions after 2012-01-13 are licensed under the terms of the | |
19 | * GNU GPL, version 2 or (at your option) any later version. | |
20 | */ | |
21 | ||
b1b23e5b | 22 | #include "qemu/osdep.h" |
816ac92e | 23 | #include <libusb.h> |
a9c94277 | 24 | #include <sys/user.h> |
816ac92e | 25 | |
816ac92e | 26 | #include "qemu/config-file.h" |
db725815 | 27 | #include "qemu/main-loop.h" |
922a01a0 | 28 | #include "qemu/option.h" |
816ac92e JG |
29 | #include "hw/sysbus.h" |
30 | #include "hw/usb.h" | |
2d0ed5e6 | 31 | #include "hw/xen/xen-legacy-backend.h" |
816ac92e | 32 | #include "monitor/qdev.h" |
452fcdbc | 33 | #include "qapi/qmp/qdict.h" |
816ac92e | 34 | #include "qapi/qmp/qstring.h" |
816ac92e | 35 | |
a3434a2d | 36 | #include "hw/xen/interface/io/usbif.h" |
816ac92e JG |
37 | |
38 | /* | |
39 | * Check for required support of usbif.h: USBIF_SHORT_NOT_OK was the last | |
40 | * macro added we rely on. | |
41 | */ | |
42 | #ifdef USBIF_SHORT_NOT_OK | |
43 | ||
44 | #define TR(xendev, lvl, fmt, args...) \ | |
45 | { \ | |
46 | struct timeval tv; \ | |
47 | \ | |
48 | gettimeofday(&tv, NULL); \ | |
96c77dba | 49 | xen_pv_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \ |
816ac92e JG |
50 | tv.tv_sec, tv.tv_usec, __func__, ##args); \ |
51 | } | |
52 | #define TR_BUS(xendev, fmt, args...) TR(xendev, 2, fmt, ##args) | |
53 | #define TR_REQ(xendev, fmt, args...) TR(xendev, 3, fmt, ##args) | |
54 | ||
55 | #define USBBACK_MAXPORTS USBIF_PIPE_PORT_MASK | |
56 | #define USB_DEV_ADDR_SIZE (USBIF_PIPE_DEV_MASK + 1) | |
57 | ||
58 | /* USB wire protocol: structure describing control request parameter. */ | |
59 | struct usbif_ctrlrequest { | |
60 | uint8_t bRequestType; | |
61 | uint8_t bRequest; | |
62 | uint16_t wValue; | |
63 | uint16_t wIndex; | |
64 | uint16_t wLength; | |
65 | }; | |
66 | ||
67 | struct usbback_info; | |
68 | struct usbback_req; | |
69 | ||
70 | struct usbback_stub { | |
71 | USBDevice *dev; | |
72 | USBPort port; | |
73 | unsigned int speed; | |
74 | bool attached; | |
b58deb34 | 75 | QTAILQ_HEAD(, usbback_req) submit_q; |
816ac92e JG |
76 | }; |
77 | ||
78 | struct usbback_req { | |
79 | struct usbback_info *usbif; | |
80 | struct usbback_stub *stub; | |
81 | struct usbif_urb_request req; | |
82 | USBPacket packet; | |
83 | ||
84 | unsigned int nr_buffer_segs; /* # of transfer_buffer segments */ | |
85 | unsigned int nr_extra_segs; /* # of iso_frame_desc segments */ | |
86 | ||
87 | QTAILQ_ENTRY(usbback_req) q; | |
88 | ||
89 | void *buffer; | |
90 | void *isoc_buffer; | |
91 | struct libusb_transfer *xfer; | |
80440ea0 JG |
92 | |
93 | bool cancelled; | |
816ac92e JG |
94 | }; |
95 | ||
96 | struct usbback_hotplug { | |
97 | QSIMPLEQ_ENTRY(usbback_hotplug) q; | |
98 | unsigned port; | |
99 | }; | |
100 | ||
101 | struct usbback_info { | |
2d0ed5e6 | 102 | struct XenLegacyDevice xendev; /* must be first */ |
816ac92e JG |
103 | USBBus bus; |
104 | void *urb_sring; | |
105 | void *conn_sring; | |
106 | struct usbif_urb_back_ring urb_ring; | |
107 | struct usbif_conn_back_ring conn_ring; | |
108 | int num_ports; | |
109 | int usb_ver; | |
110 | bool ring_error; | |
b58deb34 PB |
111 | QTAILQ_HEAD(, usbback_req) req_free_q; |
112 | QSIMPLEQ_HEAD(, usbback_hotplug) hotplug_q; | |
816ac92e JG |
113 | struct usbback_stub ports[USBBACK_MAXPORTS]; |
114 | struct usbback_stub *addr_table[USB_DEV_ADDR_SIZE]; | |
115 | QEMUBH *bh; | |
116 | }; | |
117 | ||
118 | static struct usbback_req *usbback_get_req(struct usbback_info *usbif) | |
119 | { | |
120 | struct usbback_req *usbback_req; | |
121 | ||
122 | if (QTAILQ_EMPTY(&usbif->req_free_q)) { | |
123 | usbback_req = g_new0(struct usbback_req, 1); | |
124 | } else { | |
125 | usbback_req = QTAILQ_FIRST(&usbif->req_free_q); | |
126 | QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q); | |
127 | } | |
128 | return usbback_req; | |
129 | } | |
130 | ||
131 | static void usbback_put_req(struct usbback_req *usbback_req) | |
132 | { | |
133 | struct usbback_info *usbif; | |
134 | ||
135 | usbif = usbback_req->usbif; | |
136 | memset(usbback_req, 0, sizeof(*usbback_req)); | |
137 | QTAILQ_INSERT_HEAD(&usbif->req_free_q, usbback_req, q); | |
138 | } | |
139 | ||
140 | static int usbback_gnttab_map(struct usbback_req *usbback_req) | |
141 | { | |
142 | unsigned int nr_segs, i, prot; | |
143 | uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST]; | |
144 | struct usbback_info *usbif = usbback_req->usbif; | |
2d0ed5e6 | 145 | struct XenLegacyDevice *xendev = &usbif->xendev; |
816ac92e JG |
146 | struct usbif_request_segment *seg; |
147 | void *addr; | |
148 | ||
149 | nr_segs = usbback_req->nr_buffer_segs + usbback_req->nr_extra_segs; | |
150 | if (!nr_segs) { | |
151 | return 0; | |
152 | } | |
153 | ||
154 | if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) { | |
96c77dba | 155 | xen_pv_printf(xendev, 0, "bad number of segments in request (%d)\n", |
816ac92e JG |
156 | nr_segs); |
157 | return -EINVAL; | |
158 | } | |
159 | ||
160 | for (i = 0; i < nr_segs; i++) { | |
161 | if ((unsigned)usbback_req->req.seg[i].offset + | |
c6d25aa6 | 162 | (unsigned)usbback_req->req.seg[i].length > XC_PAGE_SIZE) { |
96c77dba | 163 | xen_pv_printf(xendev, 0, "segment crosses page boundary\n"); |
816ac92e JG |
164 | return -EINVAL; |
165 | } | |
166 | } | |
167 | ||
168 | if (usbback_req->nr_buffer_segs) { | |
169 | prot = PROT_READ; | |
170 | if (usbif_pipein(usbback_req->req.pipe)) { | |
171 | prot |= PROT_WRITE; | |
172 | } | |
173 | for (i = 0; i < usbback_req->nr_buffer_segs; i++) { | |
174 | ref[i] = usbback_req->req.seg[i].gref; | |
175 | } | |
58560f2a PD |
176 | usbback_req->buffer = |
177 | xen_be_map_grant_refs(xendev, ref, usbback_req->nr_buffer_segs, | |
178 | prot); | |
816ac92e JG |
179 | |
180 | if (!usbback_req->buffer) { | |
181 | return -ENOMEM; | |
182 | } | |
183 | ||
184 | for (i = 0; i < usbback_req->nr_buffer_segs; i++) { | |
185 | seg = usbback_req->req.seg + i; | |
c6d25aa6 | 186 | addr = usbback_req->buffer + i * XC_PAGE_SIZE + seg->offset; |
816ac92e JG |
187 | qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length); |
188 | } | |
189 | } | |
190 | ||
191 | if (!usbif_pipeisoc(usbback_req->req.pipe)) { | |
192 | return 0; | |
193 | } | |
194 | ||
195 | /* | |
196 | * Right now isoc requests are not supported. | |
197 | * Prepare supporting those by doing the work needed on the guest | |
198 | * interface side. | |
199 | */ | |
200 | ||
201 | if (!usbback_req->nr_extra_segs) { | |
96c77dba | 202 | xen_pv_printf(xendev, 0, "iso request without descriptor segments\n"); |
816ac92e JG |
203 | return -EINVAL; |
204 | } | |
205 | ||
206 | prot = PROT_READ | PROT_WRITE; | |
207 | for (i = 0; i < usbback_req->nr_extra_segs; i++) { | |
208 | ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; | |
209 | } | |
58560f2a PD |
210 | usbback_req->isoc_buffer = |
211 | xen_be_map_grant_refs(xendev, ref, usbback_req->nr_extra_segs, | |
212 | prot); | |
816ac92e JG |
213 | |
214 | if (!usbback_req->isoc_buffer) { | |
215 | return -ENOMEM; | |
216 | } | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | static int usbback_init_packet(struct usbback_req *usbback_req) | |
222 | { | |
2d0ed5e6 | 223 | struct XenLegacyDevice *xendev = &usbback_req->usbif->xendev; |
816ac92e JG |
224 | USBPacket *packet = &usbback_req->packet; |
225 | USBDevice *dev = usbback_req->stub->dev; | |
226 | USBEndpoint *ep; | |
227 | unsigned int pid, ep_nr; | |
228 | bool sok; | |
229 | int ret = 0; | |
230 | ||
231 | qemu_iovec_init(&packet->iov, USBIF_MAX_SEGMENTS_PER_REQUEST); | |
232 | pid = usbif_pipein(usbback_req->req.pipe) ? USB_TOKEN_IN : USB_TOKEN_OUT; | |
233 | ep_nr = usbif_pipeendpoint(usbback_req->req.pipe); | |
234 | sok = !!(usbback_req->req.transfer_flags & USBIF_SHORT_NOT_OK); | |
235 | if (usbif_pipectrl(usbback_req->req.pipe)) { | |
236 | ep_nr = 0; | |
237 | sok = false; | |
238 | } | |
239 | ep = usb_ep_get(dev, pid, ep_nr); | |
240 | usb_packet_setup(packet, pid, ep, 0, 1, sok, true); | |
241 | ||
242 | switch (usbif_pipetype(usbback_req->req.pipe)) { | |
243 | case USBIF_PIPE_TYPE_ISOC: | |
244 | TR_REQ(xendev, "iso transfer %s: buflen: %x, %d frames\n", | |
245 | (pid == USB_TOKEN_IN) ? "in" : "out", | |
246 | usbback_req->req.buffer_length, | |
247 | usbback_req->req.u.isoc.nr_frame_desc_segs); | |
248 | ret = -EINVAL; /* isoc not implemented yet */ | |
249 | break; | |
250 | ||
251 | case USBIF_PIPE_TYPE_INT: | |
252 | TR_REQ(xendev, "int transfer %s: buflen: %x\n", | |
253 | (pid == USB_TOKEN_IN) ? "in" : "out", | |
254 | usbback_req->req.buffer_length); | |
255 | break; | |
256 | ||
257 | case USBIF_PIPE_TYPE_CTRL: | |
258 | packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl; | |
042ec47e AP |
259 | TR_REQ(xendev, "ctrl parameter: %"PRIx64", buflen: %x\n", |
260 | packet->parameter, | |
816ac92e JG |
261 | usbback_req->req.buffer_length); |
262 | break; | |
263 | ||
264 | case USBIF_PIPE_TYPE_BULK: | |
265 | TR_REQ(xendev, "bulk transfer %s: buflen: %x\n", | |
266 | (pid == USB_TOKEN_IN) ? "in" : "out", | |
267 | usbback_req->req.buffer_length); | |
268 | break; | |
269 | default: | |
270 | ret = -EINVAL; | |
271 | break; | |
272 | } | |
273 | ||
274 | return ret; | |
275 | } | |
276 | ||
277 | static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, | |
278 | int32_t actual_length, int32_t error_count) | |
279 | { | |
280 | struct usbback_info *usbif; | |
281 | struct usbif_urb_response *res; | |
2d0ed5e6 | 282 | struct XenLegacyDevice *xendev; |
816ac92e JG |
283 | unsigned int notify; |
284 | ||
285 | usbif = usbback_req->usbif; | |
286 | xendev = &usbif->xendev; | |
287 | ||
288 | TR_REQ(xendev, "id %d, status %d, length %d, errcnt %d\n", | |
289 | usbback_req->req.id, status, actual_length, error_count); | |
290 | ||
291 | if (usbback_req->packet.iov.iov) { | |
292 | qemu_iovec_destroy(&usbback_req->packet.iov); | |
293 | } | |
294 | ||
295 | if (usbback_req->buffer) { | |
58560f2a PD |
296 | xen_be_unmap_grant_refs(xendev, usbback_req->buffer, |
297 | usbback_req->nr_buffer_segs); | |
816ac92e JG |
298 | usbback_req->buffer = NULL; |
299 | } | |
300 | ||
301 | if (usbback_req->isoc_buffer) { | |
58560f2a PD |
302 | xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, |
303 | usbback_req->nr_extra_segs); | |
816ac92e JG |
304 | usbback_req->isoc_buffer = NULL; |
305 | } | |
306 | ||
80440ea0 JG |
307 | if (usbif->urb_sring) { |
308 | res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt); | |
309 | res->id = usbback_req->req.id; | |
310 | res->status = status; | |
311 | res->actual_length = actual_length; | |
312 | res->error_count = error_count; | |
313 | res->start_frame = 0; | |
314 | usbif->urb_ring.rsp_prod_pvt++; | |
315 | RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify); | |
316 | ||
317 | if (notify) { | |
ba18fa2a | 318 | xen_pv_send_notify(xendev); |
80440ea0 | 319 | } |
816ac92e JG |
320 | } |
321 | ||
80440ea0 JG |
322 | if (!usbback_req->cancelled) |
323 | usbback_put_req(usbback_req); | |
816ac92e JG |
324 | } |
325 | ||
326 | static void usbback_do_response_ret(struct usbback_req *usbback_req, | |
327 | int32_t status) | |
328 | { | |
329 | usbback_do_response(usbback_req, status, 0, 0); | |
330 | } | |
331 | ||
332 | static int32_t usbback_xlat_status(int status) | |
333 | { | |
334 | switch (status) { | |
335 | case USB_RET_SUCCESS: | |
336 | return 0; | |
337 | case USB_RET_NODEV: | |
338 | return -ENODEV; | |
339 | case USB_RET_STALL: | |
340 | return -EPIPE; | |
341 | case USB_RET_BABBLE: | |
342 | return -EOVERFLOW; | |
343 | case USB_RET_IOERROR: | |
344 | return -EPROTO; | |
345 | } | |
346 | ||
347 | return -ESHUTDOWN; | |
348 | } | |
349 | ||
350 | static void usbback_packet_complete(USBPacket *packet) | |
351 | { | |
352 | struct usbback_req *usbback_req; | |
353 | int32_t status; | |
354 | ||
355 | usbback_req = container_of(packet, struct usbback_req, packet); | |
356 | ||
357 | QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); | |
358 | ||
359 | status = usbback_xlat_status(packet->status); | |
360 | usbback_do_response(usbback_req, status, packet->actual_length, 0); | |
361 | } | |
362 | ||
363 | static void usbback_set_address(struct usbback_info *usbif, | |
364 | struct usbback_stub *stub, | |
365 | unsigned int cur_addr, unsigned int new_addr) | |
366 | { | |
367 | if (cur_addr) { | |
368 | usbif->addr_table[cur_addr] = NULL; | |
369 | } | |
370 | if (new_addr) { | |
371 | usbif->addr_table[new_addr] = stub; | |
372 | } | |
373 | } | |
374 | ||
80440ea0 | 375 | static void usbback_cancel_req(struct usbback_req *usbback_req) |
816ac92e | 376 | { |
816ac92e JG |
377 | if (usb_packet_is_inflight(&usbback_req->packet)) { |
378 | usb_cancel_packet(&usbback_req->packet); | |
80440ea0 JG |
379 | QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); |
380 | usbback_req->cancelled = true; | |
381 | usbback_do_response_ret(usbback_req, -EPROTO); | |
816ac92e | 382 | } |
816ac92e JG |
383 | } |
384 | ||
385 | static void usbback_process_unlink_req(struct usbback_req *usbback_req) | |
386 | { | |
387 | struct usbback_info *usbif; | |
388 | struct usbback_req *unlink_req; | |
389 | unsigned int id, devnum; | |
390 | int ret; | |
391 | ||
392 | usbif = usbback_req->usbif; | |
393 | ret = 0; | |
394 | id = usbback_req->req.u.unlink.unlink_id; | |
395 | TR_REQ(&usbif->xendev, "unlink id %d\n", id); | |
396 | devnum = usbif_pipedevice(usbback_req->req.pipe); | |
397 | if (unlikely(devnum == 0)) { | |
398 | usbback_req->stub = usbif->ports + | |
80440ea0 | 399 | usbif_pipeportnum(usbback_req->req.pipe) - 1; |
816ac92e JG |
400 | if (unlikely(!usbback_req->stub)) { |
401 | ret = -ENODEV; | |
402 | goto fail_response; | |
403 | } | |
404 | } else { | |
405 | if (unlikely(!usbif->addr_table[devnum])) { | |
406 | ret = -ENODEV; | |
407 | goto fail_response; | |
408 | } | |
409 | usbback_req->stub = usbif->addr_table[devnum]; | |
410 | } | |
411 | ||
412 | QTAILQ_FOREACH(unlink_req, &usbback_req->stub->submit_q, q) { | |
413 | if (unlink_req->req.id == id) { | |
80440ea0 | 414 | usbback_cancel_req(unlink_req); |
816ac92e JG |
415 | break; |
416 | } | |
417 | } | |
418 | ||
419 | fail_response: | |
420 | usbback_do_response_ret(usbback_req, ret); | |
421 | } | |
422 | ||
423 | /* | |
424 | * Checks whether a request can be handled at once or should be forwarded | |
425 | * to the usb framework. | |
426 | * Return value is: | |
427 | * 0 in case of usb framework is needed | |
428 | * 1 in case of local handling (no error) | |
429 | * The request response has been queued already if return value not 0. | |
430 | */ | |
431 | static int usbback_check_and_submit(struct usbback_req *usbback_req) | |
432 | { | |
433 | struct usbback_info *usbif; | |
434 | unsigned int devnum; | |
435 | struct usbback_stub *stub; | |
436 | struct usbif_ctrlrequest *ctrl; | |
437 | int ret; | |
438 | uint16_t wValue; | |
439 | ||
440 | usbif = usbback_req->usbif; | |
441 | stub = NULL; | |
442 | devnum = usbif_pipedevice(usbback_req->req.pipe); | |
443 | ctrl = (struct usbif_ctrlrequest *)usbback_req->req.u.ctrl; | |
444 | wValue = le16_to_cpu(ctrl->wValue); | |
445 | ||
446 | /* | |
447 | * When the device is first connected or resetted, USB device has no | |
448 | * address. In this initial state, following requests are sent to device | |
449 | * address (#0), | |
450 | * | |
451 | * 1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is sent, | |
452 | * and OS knows what device is connected to. | |
453 | * | |
454 | * 2. SET_ADDRESS is sent, and then device has its address. | |
455 | * | |
456 | * In the next step, SET_CONFIGURATION is sent to addressed device, and | |
457 | * then the device is finally ready to use. | |
458 | */ | |
459 | if (unlikely(devnum == 0)) { | |
460 | stub = usbif->ports + usbif_pipeportnum(usbback_req->req.pipe) - 1; | |
461 | if (!stub->dev || !stub->attached) { | |
462 | ret = -ENODEV; | |
463 | goto do_response; | |
464 | } | |
465 | ||
466 | switch (ctrl->bRequest) { | |
467 | case USB_REQ_GET_DESCRIPTOR: | |
468 | /* | |
469 | * GET_DESCRIPTOR request to device #0. | |
470 | * through normal transfer. | |
471 | */ | |
472 | TR_REQ(&usbif->xendev, "devnum 0 GET_DESCRIPTOR\n"); | |
473 | usbback_req->stub = stub; | |
474 | return 0; | |
475 | case USB_REQ_SET_ADDRESS: | |
476 | /* | |
477 | * SET_ADDRESS request to device #0. | |
478 | * add attached device to addr_table. | |
479 | */ | |
480 | TR_REQ(&usbif->xendev, "devnum 0 SET_ADDRESS\n"); | |
481 | usbback_set_address(usbif, stub, 0, wValue); | |
482 | ret = 0; | |
483 | break; | |
484 | default: | |
485 | ret = -EINVAL; | |
486 | break; | |
487 | } | |
488 | goto do_response; | |
489 | } | |
490 | ||
491 | if (unlikely(!usbif->addr_table[devnum])) { | |
492 | ret = -ENODEV; | |
493 | goto do_response; | |
494 | } | |
495 | usbback_req->stub = usbif->addr_table[devnum]; | |
496 | ||
497 | /* | |
498 | * Check special request | |
499 | */ | |
500 | if (ctrl->bRequest != USB_REQ_SET_ADDRESS) { | |
501 | return 0; | |
502 | } | |
503 | ||
504 | /* | |
505 | * SET_ADDRESS request to addressed device. | |
506 | * change addr or remove from addr_table. | |
507 | */ | |
508 | usbback_set_address(usbif, usbback_req->stub, devnum, wValue); | |
509 | ret = 0; | |
510 | ||
511 | do_response: | |
512 | usbback_do_response_ret(usbback_req, ret); | |
513 | return 1; | |
514 | } | |
515 | ||
516 | static void usbback_dispatch(struct usbback_req *usbback_req) | |
517 | { | |
518 | int ret; | |
519 | unsigned int devnum; | |
520 | struct usbback_info *usbif; | |
521 | ||
522 | usbif = usbback_req->usbif; | |
523 | ||
524 | TR_REQ(&usbif->xendev, "start req_id %d pipe %08x\n", usbback_req->req.id, | |
525 | usbback_req->req.pipe); | |
526 | ||
527 | /* unlink request */ | |
528 | if (unlikely(usbif_pipeunlink(usbback_req->req.pipe))) { | |
529 | usbback_process_unlink_req(usbback_req); | |
530 | return; | |
531 | } | |
532 | ||
533 | if (usbif_pipectrl(usbback_req->req.pipe)) { | |
534 | if (usbback_check_and_submit(usbback_req)) { | |
535 | return; | |
536 | } | |
537 | } else { | |
538 | devnum = usbif_pipedevice(usbback_req->req.pipe); | |
539 | usbback_req->stub = usbif->addr_table[devnum]; | |
540 | ||
541 | if (!usbback_req->stub || !usbback_req->stub->attached) { | |
542 | ret = -ENODEV; | |
543 | goto fail_response; | |
544 | } | |
545 | } | |
546 | ||
547 | QTAILQ_INSERT_TAIL(&usbback_req->stub->submit_q, usbback_req, q); | |
548 | ||
549 | usbback_req->nr_buffer_segs = usbback_req->req.nr_buffer_segs; | |
550 | usbback_req->nr_extra_segs = usbif_pipeisoc(usbback_req->req.pipe) ? | |
551 | usbback_req->req.u.isoc.nr_frame_desc_segs : 0; | |
552 | ||
553 | ret = usbback_init_packet(usbback_req); | |
554 | if (ret) { | |
96c77dba | 555 | xen_pv_printf(&usbif->xendev, 0, "invalid request\n"); |
816ac92e JG |
556 | ret = -ESHUTDOWN; |
557 | goto fail_free_urb; | |
558 | } | |
559 | ||
560 | ret = usbback_gnttab_map(usbback_req); | |
561 | if (ret) { | |
96c77dba | 562 | xen_pv_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret); |
816ac92e JG |
563 | ret = -ESHUTDOWN; |
564 | goto fail_free_urb; | |
565 | } | |
566 | ||
567 | usb_handle_packet(usbback_req->stub->dev, &usbback_req->packet); | |
568 | if (usbback_req->packet.status != USB_RET_ASYNC) { | |
569 | usbback_packet_complete(&usbback_req->packet); | |
570 | } | |
571 | return; | |
572 | ||
573 | fail_free_urb: | |
574 | QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); | |
575 | ||
576 | fail_response: | |
577 | usbback_do_response_ret(usbback_req, ret); | |
578 | } | |
579 | ||
580 | static void usbback_hotplug_notify(struct usbback_info *usbif) | |
581 | { | |
582 | struct usbif_conn_back_ring *ring = &usbif->conn_ring; | |
583 | struct usbif_conn_request req; | |
584 | struct usbif_conn_response *res; | |
585 | struct usbback_hotplug *usb_hp; | |
586 | unsigned int notify; | |
587 | ||
588 | if (!usbif->conn_sring) { | |
589 | return; | |
590 | } | |
591 | ||
592 | /* Check for full ring. */ | |
593 | if ((RING_SIZE(ring) - ring->rsp_prod_pvt - ring->req_cons) == 0) { | |
ba18fa2a | 594 | xen_pv_send_notify(&usbif->xendev); |
816ac92e JG |
595 | return; |
596 | } | |
597 | ||
598 | usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q); | |
599 | QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q); | |
600 | ||
601 | RING_COPY_REQUEST(ring, ring->req_cons, &req); | |
602 | ring->req_cons++; | |
603 | ring->sring->req_event = ring->req_cons + 1; | |
604 | ||
605 | res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt); | |
606 | res->id = req.id; | |
607 | res->portnum = usb_hp->port; | |
608 | res->speed = usbif->ports[usb_hp->port - 1].speed; | |
609 | ring->rsp_prod_pvt++; | |
610 | RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify); | |
611 | ||
612 | if (notify) { | |
ba18fa2a | 613 | xen_pv_send_notify(&usbif->xendev); |
816ac92e JG |
614 | } |
615 | ||
616 | TR_BUS(&usbif->xendev, "hotplug port %d speed %d\n", usb_hp->port, | |
617 | res->speed); | |
618 | ||
619 | g_free(usb_hp); | |
620 | ||
621 | if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { | |
622 | qemu_bh_schedule(usbif->bh); | |
623 | } | |
624 | } | |
625 | ||
626 | static void usbback_bh(void *opaque) | |
627 | { | |
628 | struct usbback_info *usbif; | |
629 | struct usbif_urb_back_ring *urb_ring; | |
630 | struct usbback_req *usbback_req; | |
631 | RING_IDX rc, rp; | |
632 | unsigned int more_to_do; | |
633 | ||
634 | usbif = opaque; | |
635 | if (usbif->ring_error) { | |
636 | return; | |
637 | } | |
638 | ||
639 | if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { | |
640 | usbback_hotplug_notify(usbif); | |
641 | } | |
642 | ||
643 | urb_ring = &usbif->urb_ring; | |
644 | rc = urb_ring->req_cons; | |
645 | rp = urb_ring->sring->req_prod; | |
646 | xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ | |
647 | ||
648 | if (RING_REQUEST_PROD_OVERFLOW(urb_ring, rp)) { | |
649 | rc = urb_ring->rsp_prod_pvt; | |
96c77dba | 650 | xen_pv_printf(&usbif->xendev, 0, "domU provided bogus ring requests " |
816ac92e JG |
651 | "(%#x - %#x = %u). Halting ring processing.\n", |
652 | rp, rc, rp - rc); | |
653 | usbif->ring_error = true; | |
654 | return; | |
655 | } | |
656 | ||
657 | while (rc != rp) { | |
658 | if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) { | |
659 | break; | |
660 | } | |
661 | usbback_req = usbback_get_req(usbif); | |
662 | ||
663 | RING_COPY_REQUEST(urb_ring, rc, &usbback_req->req); | |
664 | usbback_req->usbif = usbif; | |
665 | ||
666 | usbback_dispatch(usbback_req); | |
667 | ||
668 | urb_ring->req_cons = ++rc; | |
669 | } | |
670 | ||
671 | RING_FINAL_CHECK_FOR_REQUESTS(urb_ring, more_to_do); | |
672 | if (more_to_do) { | |
673 | qemu_bh_schedule(usbif->bh); | |
674 | } | |
675 | } | |
676 | ||
677 | static void usbback_hotplug_enq(struct usbback_info *usbif, unsigned port) | |
678 | { | |
679 | struct usbback_hotplug *usb_hp; | |
680 | ||
681 | usb_hp = g_new0(struct usbback_hotplug, 1); | |
682 | usb_hp->port = port; | |
683 | QSIMPLEQ_INSERT_TAIL(&usbif->hotplug_q, usb_hp, q); | |
684 | usbback_hotplug_notify(usbif); | |
685 | } | |
686 | ||
80440ea0 JG |
687 | static void usbback_portid_drain(struct usbback_info *usbif, unsigned port) |
688 | { | |
689 | struct usbback_req *req, *tmp; | |
690 | bool sched = false; | |
691 | ||
692 | QTAILQ_FOREACH_SAFE(req, &usbif->ports[port - 1].submit_q, q, tmp) { | |
693 | usbback_cancel_req(req); | |
694 | sched = true; | |
695 | } | |
696 | ||
697 | if (sched) { | |
698 | qemu_bh_schedule(usbif->bh); | |
699 | } | |
700 | } | |
701 | ||
702 | static void usbback_portid_detach(struct usbback_info *usbif, unsigned port) | |
703 | { | |
704 | if (!usbif->ports[port - 1].attached) { | |
705 | return; | |
706 | } | |
707 | ||
708 | usbif->ports[port - 1].speed = USBIF_SPEED_NONE; | |
709 | usbif->ports[port - 1].attached = false; | |
710 | usbback_portid_drain(usbif, port); | |
711 | usbback_hotplug_enq(usbif, port); | |
712 | } | |
713 | ||
816ac92e JG |
714 | static void usbback_portid_remove(struct usbback_info *usbif, unsigned port) |
715 | { | |
816ac92e JG |
716 | if (!usbif->ports[port - 1].dev) { |
717 | return; | |
718 | } | |
719 | ||
816ac92e JG |
720 | object_unparent(OBJECT(usbif->ports[port - 1].dev)); |
721 | usbif->ports[port - 1].dev = NULL; | |
80440ea0 | 722 | usbback_portid_detach(usbif, port); |
816ac92e JG |
723 | |
724 | TR_BUS(&usbif->xendev, "port %d removed\n", port); | |
725 | } | |
726 | ||
727 | static void usbback_portid_add(struct usbback_info *usbif, unsigned port, | |
728 | char *busid) | |
729 | { | |
730 | unsigned speed; | |
731 | char *portname; | |
816ac92e JG |
732 | Error *local_err = NULL; |
733 | QDict *qdict; | |
734 | QemuOpts *opts; | |
f1784a22 | 735 | char *tmp; |
816ac92e JG |
736 | |
737 | if (usbif->ports[port - 1].dev) { | |
738 | return; | |
739 | } | |
740 | ||
741 | portname = strchr(busid, '-'); | |
742 | if (!portname) { | |
96c77dba | 743 | xen_pv_printf(&usbif->xendev, 0, "device %s illegal specification\n", |
816ac92e JG |
744 | busid); |
745 | return; | |
746 | } | |
747 | portname++; | |
816ac92e JG |
748 | |
749 | qdict = qdict_new(); | |
46f5ac20 | 750 | qdict_put_str(qdict, "driver", "usb-host"); |
f1784a22 | 751 | tmp = g_strdup_printf("%s.0", usbif->xendev.qdev.id); |
46f5ac20 | 752 | qdict_put_str(qdict, "bus", tmp); |
f1784a22 JG |
753 | g_free(tmp); |
754 | tmp = g_strdup_printf("%s-%u", usbif->xendev.qdev.id, port); | |
46f5ac20 | 755 | qdict_put_str(qdict, "id", tmp); |
f1784a22 | 756 | g_free(tmp); |
46f5ac20 EB |
757 | qdict_put_int(qdict, "port", port); |
758 | qdict_put_int(qdict, "hostbus", atoi(busid)); | |
759 | qdict_put_str(qdict, "hostport", portname); | |
816ac92e JG |
760 | opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); |
761 | if (local_err) { | |
762 | goto err; | |
763 | } | |
764 | usbif->ports[port - 1].dev = USB_DEVICE(qdev_device_add(opts, &local_err)); | |
765 | if (!usbif->ports[port - 1].dev) { | |
766 | goto err; | |
767 | } | |
cb3e7f08 | 768 | qobject_unref(qdict); |
816ac92e JG |
769 | speed = usbif->ports[port - 1].dev->speed; |
770 | switch (speed) { | |
771 | case USB_SPEED_LOW: | |
772 | speed = USBIF_SPEED_LOW; | |
773 | break; | |
774 | case USB_SPEED_FULL: | |
775 | speed = USBIF_SPEED_FULL; | |
776 | break; | |
777 | case USB_SPEED_HIGH: | |
778 | speed = (usbif->usb_ver < USB_VER_USB20) ? | |
779 | USBIF_SPEED_NONE : USBIF_SPEED_HIGH; | |
780 | break; | |
781 | default: | |
782 | speed = USBIF_SPEED_NONE; | |
783 | break; | |
784 | } | |
785 | if (speed == USBIF_SPEED_NONE) { | |
96c77dba | 786 | xen_pv_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid); |
816ac92e JG |
787 | object_unparent(OBJECT(usbif->ports[port - 1].dev)); |
788 | usbif->ports[port - 1].dev = NULL; | |
789 | return; | |
790 | } | |
791 | usb_device_reset(usbif->ports[port - 1].dev); | |
792 | usbif->ports[port - 1].speed = speed; | |
793 | usbif->ports[port - 1].attached = true; | |
794 | QTAILQ_INIT(&usbif->ports[port - 1].submit_q); | |
795 | usbback_hotplug_enq(usbif, port); | |
796 | ||
797 | TR_BUS(&usbif->xendev, "port %d attached\n", port); | |
798 | return; | |
799 | ||
800 | err: | |
cb3e7f08 | 801 | qobject_unref(qdict); |
96c77dba | 802 | xen_pv_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); |
816ac92e JG |
803 | } |
804 | ||
805 | static void usbback_process_port(struct usbback_info *usbif, unsigned port) | |
806 | { | |
807 | char node[8]; | |
808 | char *busid; | |
809 | ||
810 | snprintf(node, sizeof(node), "port/%d", port); | |
811 | busid = xenstore_read_be_str(&usbif->xendev, node); | |
812 | if (busid == NULL) { | |
96c77dba | 813 | xen_pv_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node); |
816ac92e JG |
814 | return; |
815 | } | |
816 | ||
817 | /* Remove portid, if the port is not connected. */ | |
818 | if (strlen(busid) == 0) { | |
819 | usbback_portid_remove(usbif, port); | |
820 | } else { | |
821 | usbback_portid_add(usbif, port, busid); | |
822 | } | |
823 | ||
824 | g_free(busid); | |
825 | } | |
826 | ||
2d0ed5e6 | 827 | static void usbback_disconnect(struct XenLegacyDevice *xendev) |
816ac92e JG |
828 | { |
829 | struct usbback_info *usbif; | |
816ac92e JG |
830 | unsigned int i; |
831 | ||
832 | TR_BUS(xendev, "start\n"); | |
833 | ||
834 | usbif = container_of(xendev, struct usbback_info, xendev); | |
835 | ||
65807f4b | 836 | xen_pv_unbind_evtchn(xendev); |
816ac92e JG |
837 | |
838 | if (usbif->urb_sring) { | |
58560f2a | 839 | xen_be_unmap_grant_ref(xendev, usbif->urb_sring); |
816ac92e JG |
840 | usbif->urb_sring = NULL; |
841 | } | |
842 | if (usbif->conn_sring) { | |
58560f2a | 843 | xen_be_unmap_grant_ref(xendev, usbif->conn_sring); |
816ac92e JG |
844 | usbif->conn_sring = NULL; |
845 | } | |
846 | ||
847 | for (i = 0; i < usbif->num_ports; i++) { | |
80440ea0 JG |
848 | if (usbif->ports[i].dev) { |
849 | usbback_portid_drain(usbif, i + 1); | |
816ac92e JG |
850 | } |
851 | } | |
852 | ||
853 | TR_BUS(xendev, "finished\n"); | |
854 | } | |
855 | ||
2d0ed5e6 | 856 | static int usbback_connect(struct XenLegacyDevice *xendev) |
816ac92e JG |
857 | { |
858 | struct usbback_info *usbif; | |
859 | struct usbif_urb_sring *urb_sring; | |
860 | struct usbif_conn_sring *conn_sring; | |
861 | int urb_ring_ref; | |
862 | int conn_ring_ref; | |
f8224fb0 | 863 | unsigned int i, max_grants; |
816ac92e JG |
864 | |
865 | TR_BUS(xendev, "start\n"); | |
866 | ||
f8224fb0 JG |
867 | /* max_grants: for each request and for the rings (request and connect). */ |
868 | max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; | |
869 | xen_be_set_max_grant_refs(xendev, max_grants); | |
870 | ||
816ac92e JG |
871 | usbif = container_of(xendev, struct usbback_info, xendev); |
872 | ||
873 | if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) { | |
96c77dba | 874 | xen_pv_printf(xendev, 0, "error reading urb-ring-ref\n"); |
816ac92e JG |
875 | return -1; |
876 | } | |
877 | if (xenstore_read_fe_int(xendev, "conn-ring-ref", &conn_ring_ref)) { | |
96c77dba | 878 | xen_pv_printf(xendev, 0, "error reading conn-ring-ref\n"); |
816ac92e JG |
879 | return -1; |
880 | } | |
881 | if (xenstore_read_fe_int(xendev, "event-channel", &xendev->remote_port)) { | |
96c77dba | 882 | xen_pv_printf(xendev, 0, "error reading event-channel\n"); |
816ac92e JG |
883 | return -1; |
884 | } | |
885 | ||
58560f2a PD |
886 | usbif->urb_sring = xen_be_map_grant_ref(xendev, urb_ring_ref, |
887 | PROT_READ | PROT_WRITE); | |
888 | usbif->conn_sring = xen_be_map_grant_ref(xendev, conn_ring_ref, | |
889 | PROT_READ | PROT_WRITE); | |
816ac92e | 890 | if (!usbif->urb_sring || !usbif->conn_sring) { |
96c77dba | 891 | xen_pv_printf(xendev, 0, "error mapping rings\n"); |
816ac92e JG |
892 | usbback_disconnect(xendev); |
893 | return -1; | |
894 | } | |
895 | ||
896 | urb_sring = usbif->urb_sring; | |
897 | conn_sring = usbif->conn_sring; | |
898 | BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE); | |
899 | BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE); | |
900 | ||
901 | xen_be_bind_evtchn(xendev); | |
902 | ||
96c77dba | 903 | xen_pv_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, " |
816ac92e JG |
904 | "remote port %d, local port %d\n", urb_ring_ref, |
905 | conn_ring_ref, xendev->remote_port, xendev->local_port); | |
906 | ||
907 | for (i = 1; i <= usbif->num_ports; i++) { | |
908 | if (usbif->ports[i - 1].dev) { | |
909 | usbback_hotplug_enq(usbif, i); | |
910 | } | |
911 | } | |
912 | ||
913 | return 0; | |
914 | } | |
915 | ||
2d0ed5e6 PD |
916 | static void usbback_backend_changed(struct XenLegacyDevice *xendev, |
917 | const char *node) | |
816ac92e JG |
918 | { |
919 | struct usbback_info *usbif; | |
920 | unsigned int i; | |
921 | ||
922 | TR_BUS(xendev, "path %s\n", node); | |
923 | ||
924 | usbif = container_of(xendev, struct usbback_info, xendev); | |
925 | for (i = 1; i <= usbif->num_ports; i++) { | |
926 | usbback_process_port(usbif, i); | |
927 | } | |
928 | } | |
929 | ||
2d0ed5e6 | 930 | static int usbback_init(struct XenLegacyDevice *xendev) |
816ac92e JG |
931 | { |
932 | struct usbback_info *usbif; | |
933 | ||
934 | TR_BUS(xendev, "start\n"); | |
935 | ||
936 | usbif = container_of(xendev, struct usbback_info, xendev); | |
937 | ||
938 | if (xenstore_read_be_int(xendev, "num-ports", &usbif->num_ports) || | |
939 | usbif->num_ports < 1 || usbif->num_ports > USBBACK_MAXPORTS) { | |
96c77dba | 940 | xen_pv_printf(xendev, 0, "num-ports not readable or out of bounds\n"); |
816ac92e JG |
941 | return -1; |
942 | } | |
943 | if (xenstore_read_be_int(xendev, "usb-ver", &usbif->usb_ver) || | |
944 | (usbif->usb_ver != USB_VER_USB11 && usbif->usb_ver != USB_VER_USB20)) { | |
96c77dba | 945 | xen_pv_printf(xendev, 0, "usb-ver not readable or out of bounds\n"); |
816ac92e JG |
946 | return -1; |
947 | } | |
948 | ||
949 | usbback_backend_changed(xendev, "port"); | |
950 | ||
951 | TR_BUS(xendev, "finished\n"); | |
952 | ||
953 | return 0; | |
954 | } | |
955 | ||
956 | static void xen_bus_attach(USBPort *port) | |
957 | { | |
958 | struct usbback_info *usbif; | |
959 | ||
960 | usbif = port->opaque; | |
961 | TR_BUS(&usbif->xendev, "\n"); | |
962 | usbif->ports[port->index].attached = true; | |
963 | usbback_hotplug_enq(usbif, port->index + 1); | |
964 | } | |
965 | ||
966 | static void xen_bus_detach(USBPort *port) | |
967 | { | |
968 | struct usbback_info *usbif; | |
969 | ||
970 | usbif = port->opaque; | |
971 | TR_BUS(&usbif->xendev, "\n"); | |
80440ea0 | 972 | usbback_portid_detach(usbif, port->index + 1); |
816ac92e JG |
973 | } |
974 | ||
975 | static void xen_bus_child_detach(USBPort *port, USBDevice *child) | |
976 | { | |
977 | struct usbback_info *usbif; | |
978 | ||
979 | usbif = port->opaque; | |
980 | TR_BUS(&usbif->xendev, "\n"); | |
981 | } | |
982 | ||
983 | static void xen_bus_complete(USBPort *port, USBPacket *packet) | |
984 | { | |
80440ea0 | 985 | struct usbback_req *usbback_req; |
816ac92e JG |
986 | struct usbback_info *usbif; |
987 | ||
80440ea0 JG |
988 | usbback_req = container_of(packet, struct usbback_req, packet); |
989 | if (usbback_req->cancelled) { | |
990 | g_free(usbback_req); | |
991 | return; | |
992 | } | |
993 | ||
994 | usbif = usbback_req->usbif; | |
816ac92e JG |
995 | TR_REQ(&usbif->xendev, "\n"); |
996 | usbback_packet_complete(packet); | |
997 | } | |
998 | ||
999 | static USBPortOps xen_usb_port_ops = { | |
1000 | .attach = xen_bus_attach, | |
1001 | .detach = xen_bus_detach, | |
1002 | .child_detach = xen_bus_child_detach, | |
1003 | .complete = xen_bus_complete, | |
1004 | }; | |
1005 | ||
1006 | static USBBusOps xen_usb_bus_ops = { | |
1007 | }; | |
1008 | ||
2d0ed5e6 | 1009 | static void usbback_alloc(struct XenLegacyDevice *xendev) |
816ac92e JG |
1010 | { |
1011 | struct usbback_info *usbif; | |
1012 | USBPort *p; | |
f8224fb0 | 1013 | unsigned int i; |
816ac92e JG |
1014 | |
1015 | usbif = container_of(xendev, struct usbback_info, xendev); | |
1016 | ||
f1784a22 JG |
1017 | usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, |
1018 | DEVICE(&xendev->qdev)); | |
816ac92e JG |
1019 | for (i = 0; i < USBBACK_MAXPORTS; i++) { |
1020 | p = &(usbif->ports[i].port); | |
1021 | usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops, | |
1022 | USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL | | |
1023 | USB_SPEED_MASK_HIGH); | |
816ac92e JG |
1024 | } |
1025 | ||
1026 | QTAILQ_INIT(&usbif->req_free_q); | |
1027 | QSIMPLEQ_INIT(&usbif->hotplug_q); | |
1028 | usbif->bh = qemu_bh_new(usbback_bh, usbif); | |
816ac92e JG |
1029 | } |
1030 | ||
2d0ed5e6 | 1031 | static int usbback_free(struct XenLegacyDevice *xendev) |
816ac92e JG |
1032 | { |
1033 | struct usbback_info *usbif; | |
1034 | struct usbback_req *usbback_req; | |
1035 | struct usbback_hotplug *usb_hp; | |
1036 | unsigned int i; | |
1037 | ||
1038 | TR_BUS(xendev, "start\n"); | |
1039 | ||
1040 | usbback_disconnect(xendev); | |
1041 | usbif = container_of(xendev, struct usbback_info, xendev); | |
1042 | for (i = 1; i <= usbif->num_ports; i++) { | |
1043 | usbback_portid_remove(usbif, i); | |
1044 | } | |
1045 | ||
1046 | while (!QTAILQ_EMPTY(&usbif->req_free_q)) { | |
1047 | usbback_req = QTAILQ_FIRST(&usbif->req_free_q); | |
1048 | QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q); | |
1049 | g_free(usbback_req); | |
1050 | } | |
1051 | while (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { | |
1052 | usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q); | |
1053 | QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q); | |
1054 | g_free(usb_hp); | |
1055 | } | |
1056 | ||
1057 | qemu_bh_delete(usbif->bh); | |
1058 | ||
1059 | for (i = 0; i < USBBACK_MAXPORTS; i++) { | |
1060 | usb_unregister_port(&usbif->bus, &(usbif->ports[i].port)); | |
1061 | } | |
1062 | ||
1063 | usb_bus_release(&usbif->bus); | |
1064 | ||
1065 | TR_BUS(xendev, "finished\n"); | |
1066 | ||
1067 | return 0; | |
1068 | } | |
1069 | ||
2d0ed5e6 | 1070 | static void usbback_event(struct XenLegacyDevice *xendev) |
816ac92e JG |
1071 | { |
1072 | struct usbback_info *usbif; | |
1073 | ||
1074 | usbif = container_of(xendev, struct usbback_info, xendev); | |
1075 | qemu_bh_schedule(usbif->bh); | |
1076 | } | |
1077 | ||
1078 | struct XenDevOps xen_usb_ops = { | |
1079 | .size = sizeof(struct usbback_info), | |
1080 | .flags = DEVOPS_FLAG_NEED_GNTDEV, | |
1081 | .init = usbback_init, | |
1082 | .alloc = usbback_alloc, | |
1083 | .free = usbback_free, | |
1084 | .backend_changed = usbback_backend_changed, | |
1085 | .initialise = usbback_connect, | |
1086 | .disconnect = usbback_disconnect, | |
1087 | .event = usbback_event, | |
1088 | }; | |
1089 | ||
1090 | #else /* USBIF_SHORT_NOT_OK */ | |
1091 | ||
1092 | static int usbback_not_supported(void) | |
1093 | { | |
1094 | return -EINVAL; | |
1095 | } | |
1096 | ||
1097 | struct XenDevOps xen_usb_ops = { | |
1098 | .backend_register = usbback_not_supported, | |
1099 | }; | |
1100 | ||
1101 | #endif |