bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 1 | /* |
| 2 | * QEMU USB emulation |
| 3 | * |
| 4 | * Copyright (c) 2005 Fabrice Bellard |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 5 | * |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 6 | * 2008 Generic packet handler rewrite by Max Krasnyansky |
| 7 | * |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | * of this software and associated documentation files (the "Software"), to deal |
| 10 | * in the Software without restriction, including without limitation the rights |
| 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | * copies of the Software, and to permit persons to whom the Software is |
| 13 | * furnished to do so, subject to the following conditions: |
| 14 | * |
| 15 | * The above copyright notice and this permission notice shall be included in |
| 16 | * all copies or substantial portions of the Software. |
| 17 | * |
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | * THE SOFTWARE. |
| 25 | */ |
pbrook | 87ecb68 | 2007-11-17 17:14:51 +0000 | [diff] [blame] | 26 | #include "qemu-common.h" |
Gerd Hoffmann | f1ae32a | 2012-03-07 14:55:18 +0100 | [diff] [blame] | 27 | #include "hw/usb.h" |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 28 | #include "iov.h" |
Gerd Hoffmann | 808aeb9 | 2012-02-24 11:03:27 +0100 | [diff] [blame] | 29 | #include "trace.h" |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 30 | |
Gerd Hoffmann | 891fb2c | 2011-09-01 13:56:37 +0200 | [diff] [blame] | 31 | void usb_attach(USBPort *port) |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 32 | { |
Gerd Hoffmann | 891fb2c | 2011-09-01 13:56:37 +0200 | [diff] [blame] | 33 | USBDevice *dev = port->dev; |
| 34 | |
| 35 | assert(dev != NULL); |
| 36 | assert(dev->attached); |
Gerd Hoffmann | e0b8e72 | 2011-09-15 12:10:21 +0200 | [diff] [blame] | 37 | assert(dev->state == USB_STATE_NOTATTACHED); |
Gerd Hoffmann | 891fb2c | 2011-09-01 13:56:37 +0200 | [diff] [blame] | 38 | port->ops->attach(port); |
Gerd Hoffmann | d1f8b53 | 2012-01-06 15:13:34 +0100 | [diff] [blame] | 39 | dev->state = USB_STATE_ATTACHED; |
| 40 | usb_device_handle_attach(dev); |
Gerd Hoffmann | 891fb2c | 2011-09-01 13:56:37 +0200 | [diff] [blame] | 41 | } |
| 42 | |
| 43 | void usb_detach(USBPort *port) |
| 44 | { |
| 45 | USBDevice *dev = port->dev; |
| 46 | |
| 47 | assert(dev != NULL); |
Gerd Hoffmann | e0b8e72 | 2011-09-15 12:10:21 +0200 | [diff] [blame] | 48 | assert(dev->state != USB_STATE_NOTATTACHED); |
Gerd Hoffmann | 891fb2c | 2011-09-01 13:56:37 +0200 | [diff] [blame] | 49 | port->ops->detach(port); |
Gerd Hoffmann | d1f8b53 | 2012-01-06 15:13:34 +0100 | [diff] [blame] | 50 | dev->state = USB_STATE_NOTATTACHED; |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 51 | } |
| 52 | |
Gerd Hoffmann | d28f4e2 | 2012-01-06 15:23:10 +0100 | [diff] [blame] | 53 | void usb_port_reset(USBPort *port) |
Gerd Hoffmann | e0b8e72 | 2011-09-15 12:10:21 +0200 | [diff] [blame] | 54 | { |
| 55 | USBDevice *dev = port->dev; |
| 56 | |
| 57 | assert(dev != NULL); |
| 58 | usb_detach(port); |
| 59 | usb_attach(port); |
Gerd Hoffmann | d28f4e2 | 2012-01-06 15:23:10 +0100 | [diff] [blame] | 60 | usb_device_reset(dev); |
| 61 | } |
| 62 | |
| 63 | void usb_device_reset(USBDevice *dev) |
| 64 | { |
| 65 | if (dev == NULL || !dev->attached) { |
| 66 | return; |
| 67 | } |
| 68 | dev->remote_wakeup = 0; |
| 69 | dev->addr = 0; |
| 70 | dev->state = USB_STATE_DEFAULT; |
| 71 | usb_device_handle_reset(dev); |
Gerd Hoffmann | e0b8e72 | 2011-09-15 12:10:21 +0200 | [diff] [blame] | 72 | } |
| 73 | |
Gerd Hoffmann | 7567b51 | 2012-01-17 13:25:46 +0100 | [diff] [blame] | 74 | void usb_wakeup(USBEndpoint *ep) |
Gerd Hoffmann | 01eacab | 2010-12-01 11:32:45 +0100 | [diff] [blame] | 75 | { |
Gerd Hoffmann | 7567b51 | 2012-01-17 13:25:46 +0100 | [diff] [blame] | 76 | USBDevice *dev = ep->dev; |
Gerd Hoffmann | 37f32f0 | 2012-01-20 13:29:53 +0100 | [diff] [blame] | 77 | USBBus *bus = usb_bus_from_device(dev); |
Gerd Hoffmann | 7567b51 | 2012-01-17 13:25:46 +0100 | [diff] [blame] | 78 | |
Gerd Hoffmann | 01eacab | 2010-12-01 11:32:45 +0100 | [diff] [blame] | 79 | if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { |
Hans de Goede | d47e59b | 2011-06-21 11:52:28 +0200 | [diff] [blame] | 80 | dev->port->ops->wakeup(dev->port); |
Gerd Hoffmann | 01eacab | 2010-12-01 11:32:45 +0100 | [diff] [blame] | 81 | } |
Gerd Hoffmann | 37f32f0 | 2012-01-20 13:29:53 +0100 | [diff] [blame] | 82 | if (bus->ops->wakeup_endpoint) { |
| 83 | bus->ops->wakeup_endpoint(bus, ep); |
| 84 | } |
Gerd Hoffmann | 01eacab | 2010-12-01 11:32:45 +0100 | [diff] [blame] | 85 | } |
| 86 | |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 87 | /**********************/ |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 88 | |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 89 | /* generic USB device helpers (you are not forced to use them when |
| 90 | writing your USB device driver, but they help handling the |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 91 | protocol) |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 92 | */ |
| 93 | |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 94 | #define SETUP_STATE_IDLE 0 |
| 95 | #define SETUP_STATE_SETUP 1 |
| 96 | #define SETUP_STATE_DATA 2 |
| 97 | #define SETUP_STATE_ACK 3 |
Gerd Hoffmann | 1b4b29a | 2012-03-02 13:22:29 +0100 | [diff] [blame] | 98 | #define SETUP_STATE_PARAM 4 |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 99 | |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 100 | static int do_token_setup(USBDevice *s, USBPacket *p) |
| 101 | { |
| 102 | int request, value, index; |
| 103 | int ret = 0; |
| 104 | |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 105 | if (p->iov.size != 8) { |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 106 | return USB_RET_STALL; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | usb_packet_copy(p, s->setup_buf, p->iov.size); |
Gerd Hoffmann | c19537a | 2012-08-09 10:57:32 +0200 | [diff] [blame] | 110 | p->result = 0; |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 111 | s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; |
| 112 | s->setup_index = 0; |
| 113 | |
| 114 | request = (s->setup_buf[0] << 8) | s->setup_buf[1]; |
| 115 | value = (s->setup_buf[3] << 8) | s->setup_buf[2]; |
| 116 | index = (s->setup_buf[5] << 8) | s->setup_buf[4]; |
Hans de Goede | 007fd62 | 2011-02-02 16:33:13 +0100 | [diff] [blame] | 117 | |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 118 | if (s->setup_buf[0] & USB_DIR_IN) { |
Anthony Liguori | 62aed76 | 2011-12-15 14:53:10 -0600 | [diff] [blame] | 119 | ret = usb_device_handle_control(s, p, request, value, index, |
| 120 | s->setup_len, s->data_buf); |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 121 | if (ret == USB_RET_ASYNC) { |
| 122 | s->setup_state = SETUP_STATE_SETUP; |
| 123 | return USB_RET_ASYNC; |
| 124 | } |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 125 | if (ret < 0) |
| 126 | return ret; |
| 127 | |
| 128 | if (ret < s->setup_len) |
| 129 | s->setup_len = ret; |
| 130 | s->setup_state = SETUP_STATE_DATA; |
| 131 | } else { |
Hans de Goede | 19f3322 | 2011-02-02 17:46:00 +0100 | [diff] [blame] | 132 | if (s->setup_len > sizeof(s->data_buf)) { |
| 133 | fprintf(stderr, |
| 134 | "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", |
| 135 | s->setup_len, sizeof(s->data_buf)); |
| 136 | return USB_RET_STALL; |
| 137 | } |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 138 | if (s->setup_len == 0) |
| 139 | s->setup_state = SETUP_STATE_ACK; |
| 140 | else |
| 141 | s->setup_state = SETUP_STATE_DATA; |
| 142 | } |
| 143 | |
| 144 | return ret; |
| 145 | } |
| 146 | |
| 147 | static int do_token_in(USBDevice *s, USBPacket *p) |
| 148 | { |
| 149 | int request, value, index; |
| 150 | int ret = 0; |
| 151 | |
Gerd Hoffmann | 079d0b7 | 2012-01-12 13:23:01 +0100 | [diff] [blame] | 152 | assert(p->ep->nr == 0); |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 153 | |
| 154 | request = (s->setup_buf[0] << 8) | s->setup_buf[1]; |
| 155 | value = (s->setup_buf[3] << 8) | s->setup_buf[2]; |
| 156 | index = (s->setup_buf[5] << 8) | s->setup_buf[4]; |
| 157 | |
| 158 | switch(s->setup_state) { |
| 159 | case SETUP_STATE_ACK: |
| 160 | if (!(s->setup_buf[0] & USB_DIR_IN)) { |
Anthony Liguori | 62aed76 | 2011-12-15 14:53:10 -0600 | [diff] [blame] | 161 | ret = usb_device_handle_control(s, p, request, value, index, |
| 162 | s->setup_len, s->data_buf); |
Hans de Goede | 007fd62 | 2011-02-02 16:33:13 +0100 | [diff] [blame] | 163 | if (ret == USB_RET_ASYNC) { |
| 164 | return USB_RET_ASYNC; |
| 165 | } |
| 166 | s->setup_state = SETUP_STATE_IDLE; |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 167 | if (ret > 0) |
| 168 | return 0; |
| 169 | return ret; |
| 170 | } |
| 171 | |
| 172 | /* return 0 byte */ |
| 173 | return 0; |
| 174 | |
| 175 | case SETUP_STATE_DATA: |
| 176 | if (s->setup_buf[0] & USB_DIR_IN) { |
| 177 | int len = s->setup_len - s->setup_index; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 178 | if (len > p->iov.size) { |
| 179 | len = p->iov.size; |
| 180 | } |
| 181 | usb_packet_copy(p, s->data_buf + s->setup_index, len); |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 182 | s->setup_index += len; |
| 183 | if (s->setup_index >= s->setup_len) |
| 184 | s->setup_state = SETUP_STATE_ACK; |
| 185 | return len; |
| 186 | } |
| 187 | |
| 188 | s->setup_state = SETUP_STATE_IDLE; |
| 189 | return USB_RET_STALL; |
| 190 | |
| 191 | default: |
| 192 | return USB_RET_STALL; |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | static int do_token_out(USBDevice *s, USBPacket *p) |
| 197 | { |
Gerd Hoffmann | 079d0b7 | 2012-01-12 13:23:01 +0100 | [diff] [blame] | 198 | assert(p->ep->nr == 0); |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 199 | |
| 200 | switch(s->setup_state) { |
| 201 | case SETUP_STATE_ACK: |
| 202 | if (s->setup_buf[0] & USB_DIR_IN) { |
| 203 | s->setup_state = SETUP_STATE_IDLE; |
| 204 | /* transfer OK */ |
| 205 | } else { |
| 206 | /* ignore additional output */ |
| 207 | } |
| 208 | return 0; |
| 209 | |
| 210 | case SETUP_STATE_DATA: |
| 211 | if (!(s->setup_buf[0] & USB_DIR_IN)) { |
| 212 | int len = s->setup_len - s->setup_index; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 213 | if (len > p->iov.size) { |
| 214 | len = p->iov.size; |
| 215 | } |
| 216 | usb_packet_copy(p, s->data_buf + s->setup_index, len); |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 217 | s->setup_index += len; |
| 218 | if (s->setup_index >= s->setup_len) |
| 219 | s->setup_state = SETUP_STATE_ACK; |
| 220 | return len; |
| 221 | } |
| 222 | |
| 223 | s->setup_state = SETUP_STATE_IDLE; |
| 224 | return USB_RET_STALL; |
| 225 | |
| 226 | default: |
| 227 | return USB_RET_STALL; |
| 228 | } |
| 229 | } |
| 230 | |
Gerd Hoffmann | 1b4b29a | 2012-03-02 13:22:29 +0100 | [diff] [blame] | 231 | static int do_parameter(USBDevice *s, USBPacket *p) |
| 232 | { |
| 233 | int request, value, index; |
| 234 | int i, ret = 0; |
| 235 | |
| 236 | for (i = 0; i < 8; i++) { |
| 237 | s->setup_buf[i] = p->parameter >> (i*8); |
| 238 | } |
| 239 | |
| 240 | s->setup_state = SETUP_STATE_PARAM; |
| 241 | s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; |
| 242 | s->setup_index = 0; |
| 243 | |
| 244 | request = (s->setup_buf[0] << 8) | s->setup_buf[1]; |
| 245 | value = (s->setup_buf[3] << 8) | s->setup_buf[2]; |
| 246 | index = (s->setup_buf[5] << 8) | s->setup_buf[4]; |
| 247 | |
| 248 | if (s->setup_len > sizeof(s->data_buf)) { |
| 249 | fprintf(stderr, |
| 250 | "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", |
| 251 | s->setup_len, sizeof(s->data_buf)); |
| 252 | return USB_RET_STALL; |
| 253 | } |
| 254 | |
| 255 | if (p->pid == USB_TOKEN_OUT) { |
| 256 | usb_packet_copy(p, s->data_buf, s->setup_len); |
| 257 | } |
| 258 | |
| 259 | ret = usb_device_handle_control(s, p, request, value, index, |
| 260 | s->setup_len, s->data_buf); |
| 261 | if (ret < 0) { |
| 262 | return ret; |
| 263 | } |
| 264 | |
| 265 | if (ret < s->setup_len) { |
| 266 | s->setup_len = ret; |
| 267 | } |
| 268 | if (p->pid == USB_TOKEN_IN) { |
| 269 | usb_packet_copy(p, s->data_buf, s->setup_len); |
| 270 | } |
| 271 | |
| 272 | return ret; |
| 273 | } |
| 274 | |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 275 | /* ctrl complete function for devices which use usb_generic_handle_packet and |
| 276 | may return USB_RET_ASYNC from their handle_control callback. Device code |
| 277 | which does this *must* call this function instead of the normal |
| 278 | usb_packet_complete to complete their async control packets. */ |
| 279 | void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) |
| 280 | { |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 281 | if (p->result < 0) { |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 282 | s->setup_state = SETUP_STATE_IDLE; |
| 283 | } |
| 284 | |
| 285 | switch (s->setup_state) { |
| 286 | case SETUP_STATE_SETUP: |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 287 | if (p->result < s->setup_len) { |
| 288 | s->setup_len = p->result; |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 289 | } |
| 290 | s->setup_state = SETUP_STATE_DATA; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 291 | p->result = 8; |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 292 | break; |
| 293 | |
| 294 | case SETUP_STATE_ACK: |
| 295 | s->setup_state = SETUP_STATE_IDLE; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 296 | p->result = 0; |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 297 | break; |
| 298 | |
Gerd Hoffmann | 1b4b29a | 2012-03-02 13:22:29 +0100 | [diff] [blame] | 299 | case SETUP_STATE_PARAM: |
| 300 | if (p->result < s->setup_len) { |
| 301 | s->setup_len = p->result; |
| 302 | } |
| 303 | if (p->pid == USB_TOKEN_IN) { |
| 304 | p->result = 0; |
| 305 | usb_packet_copy(p, s->data_buf, s->setup_len); |
| 306 | } |
| 307 | break; |
| 308 | |
Hans de Goede | 50b7963 | 2011-02-02 17:36:29 +0100 | [diff] [blame] | 309 | default: |
| 310 | break; |
| 311 | } |
| 312 | usb_packet_complete(s, p); |
| 313 | } |
| 314 | |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 315 | /* XXX: fix overflow */ |
| 316 | int set_usb_string(uint8_t *buf, const char *str) |
| 317 | { |
| 318 | int len, i; |
| 319 | uint8_t *q; |
| 320 | |
| 321 | q = buf; |
| 322 | len = strlen(str); |
pbrook | ce5c37c | 2006-03-11 20:37:58 +0000 | [diff] [blame] | 323 | *q++ = 2 * len + 2; |
bellard | bb36d47 | 2005-11-05 14:22:28 +0000 | [diff] [blame] | 324 | *q++ = 3; |
| 325 | for(i = 0; i < len; i++) { |
| 326 | *q++ = str[i]; |
| 327 | *q++ = 0; |
| 328 | } |
| 329 | return q - buf; |
| 330 | } |
pbrook | 4d611c9 | 2006-08-12 01:04:27 +0000 | [diff] [blame] | 331 | |
Gerd Hoffmann | 73796fe | 2012-01-10 16:59:28 +0100 | [diff] [blame] | 332 | USBDevice *usb_find_device(USBPort *port, uint8_t addr) |
| 333 | { |
| 334 | USBDevice *dev = port->dev; |
| 335 | |
| 336 | if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { |
| 337 | return NULL; |
| 338 | } |
| 339 | if (dev->addr == addr) { |
| 340 | return dev; |
| 341 | } |
| 342 | return usb_device_find_device(dev, addr); |
| 343 | } |
| 344 | |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 345 | static int usb_process_one(USBPacket *p) |
| 346 | { |
| 347 | USBDevice *dev = p->ep->dev; |
| 348 | |
| 349 | if (p->ep->nr == 0) { |
| 350 | /* control pipe */ |
Gerd Hoffmann | 1b4b29a | 2012-03-02 13:22:29 +0100 | [diff] [blame] | 351 | if (p->parameter) { |
| 352 | return do_parameter(dev, p); |
| 353 | } |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 354 | switch (p->pid) { |
| 355 | case USB_TOKEN_SETUP: |
| 356 | return do_token_setup(dev, p); |
| 357 | case USB_TOKEN_IN: |
| 358 | return do_token_in(dev, p); |
| 359 | case USB_TOKEN_OUT: |
| 360 | return do_token_out(dev, p); |
| 361 | default: |
| 362 | return USB_RET_STALL; |
| 363 | } |
| 364 | } else { |
| 365 | /* data pipe */ |
| 366 | return usb_device_handle_data(dev, p); |
| 367 | } |
| 368 | } |
| 369 | |
Gerd Hoffmann | 53aa8c0 | 2011-05-12 13:20:39 +0200 | [diff] [blame] | 370 | /* Hand over a packet to a device for processing. Return value |
| 371 | USB_RET_ASYNC indicates the processing isn't finished yet, the |
| 372 | driver will call usb_packet_complete() when done processing it. */ |
| 373 | int usb_handle_packet(USBDevice *dev, USBPacket *p) |
| 374 | { |
| 375 | int ret; |
| 376 | |
Gerd Hoffmann | 98861f5 | 2012-01-10 17:33:02 +0100 | [diff] [blame] | 377 | if (dev == NULL) { |
| 378 | return USB_RET_NODEV; |
| 379 | } |
Gerd Hoffmann | 079d0b7 | 2012-01-12 13:23:01 +0100 | [diff] [blame] | 380 | assert(dev == p->ep->dev); |
Gerd Hoffmann | 1977f93 | 2012-01-11 12:14:02 +0100 | [diff] [blame] | 381 | assert(dev->state == USB_STATE_DEFAULT); |
Gerd Hoffmann | 5ac2731 | 2012-03-08 12:27:47 +0100 | [diff] [blame] | 382 | usb_packet_check_state(p, USB_PACKET_SETUP); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 383 | assert(p->ep != NULL); |
Gerd Hoffmann | 1977f93 | 2012-01-11 12:14:02 +0100 | [diff] [blame] | 384 | |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 385 | /* Submitting a new packet clears halt */ |
| 386 | if (p->ep->halted) { |
| 387 | assert(QTAILQ_EMPTY(&p->ep->queue)); |
| 388 | p->ep->halted = false; |
| 389 | } |
| 390 | |
Gerd Hoffmann | 7936e0f | 2012-03-01 14:39:28 +0100 | [diff] [blame] | 391 | if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 392 | ret = usb_process_one(p); |
| 393 | if (ret == USB_RET_ASYNC) { |
| 394 | usb_packet_set_state(p, USB_PACKET_ASYNC); |
| 395 | QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); |
| 396 | } else { |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 397 | /* |
| 398 | * When pipelining is enabled usb-devices must always return async, |
| 399 | * otherwise packets can complete out of order! |
| 400 | */ |
Hans de Goede | 9c1f676 | 2012-09-03 12:48:49 +0200 | [diff] [blame] | 401 | assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue)); |
Hans de Goede | cc40997 | 2012-09-03 12:33:44 +0200 | [diff] [blame] | 402 | if (ret != USB_RET_NAK) { |
| 403 | p->result = ret; |
| 404 | usb_packet_set_state(p, USB_PACKET_COMPLETE); |
| 405 | } |
Gerd Hoffmann | 1977f93 | 2012-01-11 12:14:02 +0100 | [diff] [blame] | 406 | } |
| 407 | } else { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 408 | ret = USB_RET_ASYNC; |
| 409 | usb_packet_set_state(p, USB_PACKET_QUEUED); |
| 410 | QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); |
Gerd Hoffmann | 4ff658f | 2011-05-12 13:48:13 +0200 | [diff] [blame] | 411 | } |
Gerd Hoffmann | 53aa8c0 | 2011-05-12 13:20:39 +0200 | [diff] [blame] | 412 | return ret; |
aliguori | 89b9b79 | 2008-08-21 19:29:38 +0000 | [diff] [blame] | 413 | } |
Gerd Hoffmann | 4ff658f | 2011-05-12 13:48:13 +0200 | [diff] [blame] | 414 | |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 415 | static void __usb_packet_complete(USBDevice *dev, USBPacket *p) |
| 416 | { |
| 417 | USBEndpoint *ep = p->ep; |
| 418 | |
| 419 | assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK); |
| 420 | |
| 421 | if (p->result < 0) { |
| 422 | ep->halted = true; |
| 423 | } |
| 424 | usb_packet_set_state(p, USB_PACKET_COMPLETE); |
| 425 | QTAILQ_REMOVE(&ep->queue, p, queue); |
| 426 | dev->port->ops->complete(dev->port, p); |
| 427 | } |
| 428 | |
Gerd Hoffmann | 4ff658f | 2011-05-12 13:48:13 +0200 | [diff] [blame] | 429 | /* Notify the controller that an async packet is complete. This should only |
| 430 | be called for packets previously deferred by returning USB_RET_ASYNC from |
| 431 | handle_packet. */ |
| 432 | void usb_packet_complete(USBDevice *dev, USBPacket *p) |
| 433 | { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 434 | USBEndpoint *ep = p->ep; |
| 435 | int ret; |
| 436 | |
Gerd Hoffmann | 5ac2731 | 2012-03-08 12:27:47 +0100 | [diff] [blame] | 437 | usb_packet_check_state(p, USB_PACKET_ASYNC); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 438 | assert(QTAILQ_FIRST(&ep->queue) == p); |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 439 | __usb_packet_complete(dev, p); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 440 | |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 441 | while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 442 | p = QTAILQ_FIRST(&ep->queue); |
Gerd Hoffmann | eb9d467 | 2012-02-28 15:36:06 +0100 | [diff] [blame] | 443 | if (p->state == USB_PACKET_ASYNC) { |
| 444 | break; |
| 445 | } |
Gerd Hoffmann | 5ac2731 | 2012-03-08 12:27:47 +0100 | [diff] [blame] | 446 | usb_packet_check_state(p, USB_PACKET_QUEUED); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 447 | ret = usb_process_one(p); |
| 448 | if (ret == USB_RET_ASYNC) { |
| 449 | usb_packet_set_state(p, USB_PACKET_ASYNC); |
| 450 | break; |
| 451 | } |
| 452 | p->result = ret; |
Hans de Goede | 0132b4b | 2012-08-17 15:24:49 +0200 | [diff] [blame] | 453 | __usb_packet_complete(ep->dev, p); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 454 | } |
Gerd Hoffmann | 4ff658f | 2011-05-12 13:48:13 +0200 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | /* Cancel an active packet. The packed must have been deferred by |
| 458 | returning USB_RET_ASYNC from handle_packet, and not yet |
| 459 | completed. */ |
| 460 | void usb_cancel_packet(USBPacket * p) |
| 461 | { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 462 | bool callback = (p->state == USB_PACKET_ASYNC); |
| 463 | assert(usb_packet_is_inflight(p)); |
| 464 | usb_packet_set_state(p, USB_PACKET_CANCELED); |
| 465 | QTAILQ_REMOVE(&p->ep->queue, p, queue); |
| 466 | if (callback) { |
| 467 | usb_device_cancel_packet(p->ep->dev, p); |
| 468 | } |
Gerd Hoffmann | 4ff658f | 2011-05-12 13:48:13 +0200 | [diff] [blame] | 469 | } |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 470 | |
| 471 | |
| 472 | void usb_packet_init(USBPacket *p) |
| 473 | { |
| 474 | qemu_iovec_init(&p->iov, 1); |
| 475 | } |
| 476 | |
Gerd Hoffmann | 5ac2731 | 2012-03-08 12:27:47 +0100 | [diff] [blame] | 477 | static const char *usb_packet_state_name(USBPacketState state) |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 478 | { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 479 | static const char *name[] = { |
| 480 | [USB_PACKET_UNDEFINED] = "undef", |
| 481 | [USB_PACKET_SETUP] = "setup", |
| 482 | [USB_PACKET_QUEUED] = "queued", |
| 483 | [USB_PACKET_ASYNC] = "async", |
| 484 | [USB_PACKET_COMPLETE] = "complete", |
| 485 | [USB_PACKET_CANCELED] = "canceled", |
| 486 | }; |
Gerd Hoffmann | 5ac2731 | 2012-03-08 12:27:47 +0100 | [diff] [blame] | 487 | if (state < ARRAY_SIZE(name)) { |
| 488 | return name[state]; |
| 489 | } |
| 490 | return "INVALID"; |
| 491 | } |
| 492 | |
| 493 | void usb_packet_check_state(USBPacket *p, USBPacketState expected) |
| 494 | { |
| 495 | USBDevice *dev; |
| 496 | USBBus *bus; |
| 497 | |
| 498 | if (p->state == expected) { |
| 499 | return; |
| 500 | } |
| 501 | dev = p->ep->dev; |
| 502 | bus = usb_bus_from_device(dev); |
| 503 | trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p, |
| 504 | usb_packet_state_name(p->state), |
| 505 | usb_packet_state_name(expected)); |
| 506 | assert(!"usb packet state check failed"); |
| 507 | } |
| 508 | |
| 509 | void usb_packet_set_state(USBPacket *p, USBPacketState state) |
| 510 | { |
Gerd Hoffmann | f5bf14b | 2012-03-23 13:34:50 +0100 | [diff] [blame] | 511 | if (p->ep) { |
| 512 | USBDevice *dev = p->ep->dev; |
| 513 | USBBus *bus = usb_bus_from_device(dev); |
| 514 | trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p, |
| 515 | usb_packet_state_name(p->state), |
| 516 | usb_packet_state_name(state)); |
| 517 | } else { |
| 518 | trace_usb_packet_state_change(-1, "", -1, p, |
| 519 | usb_packet_state_name(p->state), |
| 520 | usb_packet_state_name(state)); |
| 521 | } |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 522 | p->state = state; |
| 523 | } |
| 524 | |
Gerd Hoffmann | e983395 | 2012-08-23 13:30:13 +0200 | [diff] [blame] | 525 | void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id) |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 526 | { |
Gerd Hoffmann | f53c398 | 2012-01-12 12:51:48 +0100 | [diff] [blame] | 527 | assert(!usb_packet_is_inflight(p)); |
Gerd Hoffmann | 0cc6a0f | 2012-04-19 13:07:54 +0200 | [diff] [blame] | 528 | assert(p->iov.iov != NULL); |
Gerd Hoffmann | e983395 | 2012-08-23 13:30:13 +0200 | [diff] [blame] | 529 | p->id = id; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 530 | p->pid = pid; |
Gerd Hoffmann | 079d0b7 | 2012-01-12 13:23:01 +0100 | [diff] [blame] | 531 | p->ep = ep; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 532 | p->result = 0; |
Gerd Hoffmann | 1b4b29a | 2012-03-02 13:22:29 +0100 | [diff] [blame] | 533 | p->parameter = 0; |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 534 | qemu_iovec_reset(&p->iov); |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 535 | usb_packet_set_state(p, USB_PACKET_SETUP); |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 536 | } |
| 537 | |
| 538 | void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) |
| 539 | { |
| 540 | qemu_iovec_add(&p->iov, ptr, len); |
| 541 | } |
| 542 | |
| 543 | void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) |
| 544 | { |
| 545 | assert(p->result >= 0); |
| 546 | assert(p->result + bytes <= p->iov.size); |
| 547 | switch (p->pid) { |
| 548 | case USB_TOKEN_SETUP: |
| 549 | case USB_TOKEN_OUT: |
Michael Tokarev | dcf6f5e | 2012-03-11 18:05:12 +0400 | [diff] [blame] | 550 | iov_to_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 551 | break; |
| 552 | case USB_TOKEN_IN: |
Michael Tokarev | dcf6f5e | 2012-03-11 18:05:12 +0400 | [diff] [blame] | 553 | iov_from_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 554 | break; |
| 555 | default: |
| 556 | fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); |
| 557 | abort(); |
| 558 | } |
| 559 | p->result += bytes; |
| 560 | } |
| 561 | |
| 562 | void usb_packet_skip(USBPacket *p, size_t bytes) |
| 563 | { |
| 564 | assert(p->result >= 0); |
| 565 | assert(p->result + bytes <= p->iov.size); |
| 566 | if (p->pid == USB_TOKEN_IN) { |
Michael Tokarev | dcf6f5e | 2012-03-11 18:05:12 +0400 | [diff] [blame] | 567 | iov_memset(p->iov.iov, p->iov.niov, p->result, 0, bytes); |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 568 | } |
| 569 | p->result += bytes; |
| 570 | } |
| 571 | |
| 572 | void usb_packet_cleanup(USBPacket *p) |
| 573 | { |
Gerd Hoffmann | f53c398 | 2012-01-12 12:51:48 +0100 | [diff] [blame] | 574 | assert(!usb_packet_is_inflight(p)); |
Gerd Hoffmann | 4f4321c | 2011-07-12 15:22:25 +0200 | [diff] [blame] | 575 | qemu_iovec_destroy(&p->iov); |
| 576 | } |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 577 | |
Gerd Hoffmann | 19deaa0 | 2012-07-03 10:11:21 +0200 | [diff] [blame] | 578 | void usb_ep_reset(USBDevice *dev) |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 579 | { |
| 580 | int ep; |
| 581 | |
Gerd Hoffmann | 63095ab | 2012-01-12 13:24:22 +0100 | [diff] [blame] | 582 | dev->ep_ctl.nr = 0; |
Gerd Hoffmann | 25d5de7 | 2011-12-13 15:58:19 +0100 | [diff] [blame] | 583 | dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; |
| 584 | dev->ep_ctl.ifnum = 0; |
| 585 | dev->ep_ctl.dev = dev; |
Gerd Hoffmann | 7936e0f | 2012-03-01 14:39:28 +0100 | [diff] [blame] | 586 | dev->ep_ctl.pipeline = false; |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 587 | for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { |
Gerd Hoffmann | 63095ab | 2012-01-12 13:24:22 +0100 | [diff] [blame] | 588 | dev->ep_in[ep].nr = ep + 1; |
| 589 | dev->ep_out[ep].nr = ep + 1; |
| 590 | dev->ep_in[ep].pid = USB_TOKEN_IN; |
| 591 | dev->ep_out[ep].pid = USB_TOKEN_OUT; |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 592 | dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; |
| 593 | dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; |
Gerd Hoffmann | 7c37e6a | 2012-07-03 10:15:08 +0200 | [diff] [blame] | 594 | dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID; |
| 595 | dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; |
Gerd Hoffmann | 25d5de7 | 2011-12-13 15:58:19 +0100 | [diff] [blame] | 596 | dev->ep_in[ep].dev = dev; |
| 597 | dev->ep_out[ep].dev = dev; |
Gerd Hoffmann | 7936e0f | 2012-03-01 14:39:28 +0100 | [diff] [blame] | 598 | dev->ep_in[ep].pipeline = false; |
| 599 | dev->ep_out[ep].pipeline = false; |
Gerd Hoffmann | 19deaa0 | 2012-07-03 10:11:21 +0200 | [diff] [blame] | 600 | } |
| 601 | } |
| 602 | |
| 603 | void usb_ep_init(USBDevice *dev) |
| 604 | { |
| 605 | int ep; |
| 606 | |
| 607 | usb_ep_reset(dev); |
| 608 | QTAILQ_INIT(&dev->ep_ctl.queue); |
| 609 | for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { |
Gerd Hoffmann | db4be87 | 2012-01-12 14:26:13 +0100 | [diff] [blame] | 610 | QTAILQ_INIT(&dev->ep_in[ep].queue); |
| 611 | QTAILQ_INIT(&dev->ep_out[ep].queue); |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 612 | } |
| 613 | } |
| 614 | |
Gerd Hoffmann | 5b6780d | 2011-08-29 13:45:25 +0200 | [diff] [blame] | 615 | void usb_ep_dump(USBDevice *dev) |
| 616 | { |
| 617 | static const char *tname[] = { |
| 618 | [USB_ENDPOINT_XFER_CONTROL] = "control", |
| 619 | [USB_ENDPOINT_XFER_ISOC] = "isoc", |
| 620 | [USB_ENDPOINT_XFER_BULK] = "bulk", |
| 621 | [USB_ENDPOINT_XFER_INT] = "int", |
| 622 | }; |
| 623 | int ifnum, ep, first; |
| 624 | |
| 625 | fprintf(stderr, "Device \"%s\", config %d\n", |
| 626 | dev->product_desc, dev->configuration); |
| 627 | for (ifnum = 0; ifnum < 16; ifnum++) { |
| 628 | first = 1; |
| 629 | for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { |
| 630 | if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID && |
| 631 | dev->ep_in[ep].ifnum == ifnum) { |
| 632 | if (first) { |
| 633 | first = 0; |
| 634 | fprintf(stderr, " Interface %d, alternative %d\n", |
| 635 | ifnum, dev->altsetting[ifnum]); |
| 636 | } |
Gerd Hoffmann | f003397 | 2011-08-31 16:09:27 +0200 | [diff] [blame] | 637 | fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep, |
| 638 | tname[dev->ep_in[ep].type], |
| 639 | dev->ep_in[ep].max_packet_size); |
Gerd Hoffmann | 5b6780d | 2011-08-29 13:45:25 +0200 | [diff] [blame] | 640 | } |
| 641 | if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID && |
| 642 | dev->ep_out[ep].ifnum == ifnum) { |
| 643 | if (first) { |
| 644 | first = 0; |
| 645 | fprintf(stderr, " Interface %d, alternative %d\n", |
| 646 | ifnum, dev->altsetting[ifnum]); |
| 647 | } |
Gerd Hoffmann | f003397 | 2011-08-31 16:09:27 +0200 | [diff] [blame] | 648 | fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep, |
| 649 | tname[dev->ep_out[ep].type], |
| 650 | dev->ep_out[ep].max_packet_size); |
Gerd Hoffmann | 5b6780d | 2011-08-29 13:45:25 +0200 | [diff] [blame] | 651 | } |
| 652 | } |
| 653 | } |
| 654 | fprintf(stderr, "--\n"); |
| 655 | } |
| 656 | |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 657 | struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) |
| 658 | { |
Gerd Hoffmann | 079d0b7 | 2012-01-12 13:23:01 +0100 | [diff] [blame] | 659 | struct USBEndpoint *eps; |
| 660 | |
| 661 | if (dev == NULL) { |
| 662 | return NULL; |
| 663 | } |
| 664 | eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; |
Gerd Hoffmann | 25d5de7 | 2011-12-13 15:58:19 +0100 | [diff] [blame] | 665 | if (ep == 0) { |
| 666 | return &dev->ep_ctl; |
| 667 | } |
Gerd Hoffmann | d8e17ef | 2011-08-29 12:49:46 +0200 | [diff] [blame] | 668 | assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); |
| 669 | assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); |
| 670 | return eps + ep - 1; |
| 671 | } |
| 672 | |
| 673 | uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep) |
| 674 | { |
| 675 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 676 | return uep->type; |
| 677 | } |
| 678 | |
| 679 | void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type) |
| 680 | { |
| 681 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 682 | uep->type = type; |
| 683 | } |
Gerd Hoffmann | 82f02fe | 2011-08-29 12:57:48 +0200 | [diff] [blame] | 684 | |
| 685 | uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep) |
| 686 | { |
| 687 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 688 | return uep->ifnum; |
| 689 | } |
| 690 | |
| 691 | void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum) |
| 692 | { |
| 693 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 694 | uep->ifnum = ifnum; |
| 695 | } |
Gerd Hoffmann | f003397 | 2011-08-31 16:09:27 +0200 | [diff] [blame] | 696 | |
| 697 | void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, |
| 698 | uint16_t raw) |
| 699 | { |
| 700 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 701 | int size, microframes; |
| 702 | |
| 703 | size = raw & 0x7ff; |
| 704 | switch ((raw >> 11) & 3) { |
| 705 | case 1: |
| 706 | microframes = 2; |
| 707 | break; |
| 708 | case 2: |
| 709 | microframes = 3; |
| 710 | break; |
| 711 | default: |
| 712 | microframes = 1; |
| 713 | break; |
| 714 | } |
| 715 | uep->max_packet_size = size * microframes; |
| 716 | } |
| 717 | |
| 718 | int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) |
| 719 | { |
| 720 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 721 | return uep->max_packet_size; |
| 722 | } |
Gerd Hoffmann | 7936e0f | 2012-03-01 14:39:28 +0100 | [diff] [blame] | 723 | |
| 724 | void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) |
| 725 | { |
| 726 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 727 | uep->pipeline = enabled; |
| 728 | } |
Hans de Goede | c13a9e6 | 2012-08-28 09:43:18 +0200 | [diff] [blame] | 729 | |
| 730 | USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, |
| 731 | uint64_t id) |
| 732 | { |
| 733 | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); |
| 734 | USBPacket *p; |
| 735 | |
| 736 | while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) { |
| 737 | if (p->id == id) { |
| 738 | return p; |
| 739 | } |
| 740 | } |
| 741 | |
| 742 | return NULL; |
| 743 | } |