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