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