root / hw / usb.c @ 37f32f0f
History | View | Annotate | Download (16.8 kB)
1 | bb36d470 | bellard | /*
|
---|---|---|---|
2 | bb36d470 | bellard | * QEMU USB emulation
|
3 | bb36d470 | bellard | *
|
4 | bb36d470 | bellard | * Copyright (c) 2005 Fabrice Bellard
|
5 | 5fafdf24 | ths | *
|
6 | 89b9b79f | aliguori | * 2008 Generic packet handler rewrite by Max Krasnyansky
|
7 | 89b9b79f | aliguori | *
|
8 | bb36d470 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
9 | bb36d470 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
10 | bb36d470 | bellard | * in the Software without restriction, including without limitation the rights
|
11 | bb36d470 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12 | bb36d470 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
13 | bb36d470 | bellard | * furnished to do so, subject to the following conditions:
|
14 | bb36d470 | bellard | *
|
15 | bb36d470 | bellard | * The above copyright notice and this permission notice shall be included in
|
16 | bb36d470 | bellard | * all copies or substantial portions of the Software.
|
17 | bb36d470 | bellard | *
|
18 | bb36d470 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19 | bb36d470 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20 | bb36d470 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
21 | bb36d470 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22 | bb36d470 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23 | bb36d470 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24 | bb36d470 | bellard | * THE SOFTWARE.
|
25 | bb36d470 | bellard | */
|
26 | 87ecb68b | pbrook | #include "qemu-common.h" |
27 | 87ecb68b | pbrook | #include "usb.h" |
28 | 4f4321c1 | Gerd Hoffmann | #include "iov.h" |
29 | bb36d470 | bellard | |
30 | 891fb2cd | Gerd Hoffmann | void usb_attach(USBPort *port)
|
31 | bb36d470 | bellard | { |
32 | 891fb2cd | Gerd Hoffmann | USBDevice *dev = port->dev; |
33 | 891fb2cd | Gerd Hoffmann | |
34 | 891fb2cd | Gerd Hoffmann | assert(dev != NULL);
|
35 | 891fb2cd | Gerd Hoffmann | assert(dev->attached); |
36 | e0b8e72d | Gerd Hoffmann | assert(dev->state == USB_STATE_NOTATTACHED); |
37 | 891fb2cd | Gerd Hoffmann | port->ops->attach(port); |
38 | d1f8b536 | Gerd Hoffmann | dev->state = USB_STATE_ATTACHED; |
39 | d1f8b536 | Gerd Hoffmann | usb_device_handle_attach(dev); |
40 | 891fb2cd | Gerd Hoffmann | } |
41 | 891fb2cd | Gerd Hoffmann | |
42 | 891fb2cd | Gerd Hoffmann | void usb_detach(USBPort *port)
|
43 | 891fb2cd | Gerd Hoffmann | { |
44 | 891fb2cd | Gerd Hoffmann | USBDevice *dev = port->dev; |
45 | 891fb2cd | Gerd Hoffmann | |
46 | 891fb2cd | Gerd Hoffmann | assert(dev != NULL);
|
47 | e0b8e72d | Gerd Hoffmann | assert(dev->state != USB_STATE_NOTATTACHED); |
48 | 891fb2cd | Gerd Hoffmann | port->ops->detach(port); |
49 | d1f8b536 | Gerd Hoffmann | dev->state = USB_STATE_NOTATTACHED; |
50 | bb36d470 | bellard | } |
51 | bb36d470 | bellard | |
52 | d28f4e2d | Gerd Hoffmann | void usb_port_reset(USBPort *port)
|
53 | e0b8e72d | Gerd Hoffmann | { |
54 | e0b8e72d | Gerd Hoffmann | USBDevice *dev = port->dev; |
55 | e0b8e72d | Gerd Hoffmann | |
56 | e0b8e72d | Gerd Hoffmann | assert(dev != NULL);
|
57 | e0b8e72d | Gerd Hoffmann | usb_detach(port); |
58 | e0b8e72d | Gerd Hoffmann | usb_attach(port); |
59 | d28f4e2d | Gerd Hoffmann | usb_device_reset(dev); |
60 | d28f4e2d | Gerd Hoffmann | } |
61 | d28f4e2d | Gerd Hoffmann | |
62 | d28f4e2d | Gerd Hoffmann | void usb_device_reset(USBDevice *dev)
|
63 | d28f4e2d | Gerd Hoffmann | { |
64 | d28f4e2d | Gerd Hoffmann | if (dev == NULL || !dev->attached) { |
65 | d28f4e2d | Gerd Hoffmann | return;
|
66 | d28f4e2d | Gerd Hoffmann | } |
67 | d28f4e2d | Gerd Hoffmann | dev->remote_wakeup = 0;
|
68 | d28f4e2d | Gerd Hoffmann | dev->addr = 0;
|
69 | d28f4e2d | Gerd Hoffmann | dev->state = USB_STATE_DEFAULT; |
70 | d28f4e2d | Gerd Hoffmann | usb_device_handle_reset(dev); |
71 | e0b8e72d | Gerd Hoffmann | } |
72 | e0b8e72d | Gerd Hoffmann | |
73 | 7567b51f | Gerd Hoffmann | void usb_wakeup(USBEndpoint *ep)
|
74 | 01eacab6 | Gerd Hoffmann | { |
75 | 7567b51f | Gerd Hoffmann | USBDevice *dev = ep->dev; |
76 | 37f32f0f | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
77 | 7567b51f | Gerd Hoffmann | |
78 | 01eacab6 | Gerd Hoffmann | if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
|
79 | d47e59b8 | Hans de Goede | dev->port->ops->wakeup(dev->port); |
80 | 01eacab6 | Gerd Hoffmann | } |
81 | 37f32f0f | Gerd Hoffmann | if (bus->ops->wakeup_endpoint) {
|
82 | 37f32f0f | Gerd Hoffmann | bus->ops->wakeup_endpoint(bus, ep); |
83 | 37f32f0f | Gerd Hoffmann | } |
84 | 01eacab6 | Gerd Hoffmann | } |
85 | 01eacab6 | Gerd Hoffmann | |
86 | bb36d470 | bellard | /**********************/
|
87 | 89b9b79f | aliguori | |
88 | bb36d470 | bellard | /* generic USB device helpers (you are not forced to use them when
|
89 | bb36d470 | bellard | writing your USB device driver, but they help handling the
|
90 | 5fafdf24 | ths | protocol)
|
91 | bb36d470 | bellard | */
|
92 | bb36d470 | bellard | |
93 | 50b7963e | Hans de Goede | #define SETUP_STATE_IDLE 0 |
94 | 50b7963e | Hans de Goede | #define SETUP_STATE_SETUP 1 |
95 | 50b7963e | Hans de Goede | #define SETUP_STATE_DATA 2 |
96 | 50b7963e | Hans de Goede | #define SETUP_STATE_ACK 3 |
97 | bb36d470 | bellard | |
98 | 89b9b79f | aliguori | static int do_token_setup(USBDevice *s, USBPacket *p) |
99 | 89b9b79f | aliguori | { |
100 | 89b9b79f | aliguori | int request, value, index;
|
101 | 89b9b79f | aliguori | int ret = 0; |
102 | 89b9b79f | aliguori | |
103 | 4f4321c1 | Gerd Hoffmann | if (p->iov.size != 8) { |
104 | 89b9b79f | aliguori | return USB_RET_STALL;
|
105 | 4f4321c1 | Gerd Hoffmann | } |
106 | 4f4321c1 | Gerd Hoffmann | |
107 | 4f4321c1 | Gerd Hoffmann | usb_packet_copy(p, s->setup_buf, p->iov.size); |
108 | 89b9b79f | aliguori | s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; |
109 | 89b9b79f | aliguori | s->setup_index = 0;
|
110 | 89b9b79f | aliguori | |
111 | 89b9b79f | aliguori | request = (s->setup_buf[0] << 8) | s->setup_buf[1]; |
112 | 89b9b79f | aliguori | value = (s->setup_buf[3] << 8) | s->setup_buf[2]; |
113 | 89b9b79f | aliguori | index = (s->setup_buf[5] << 8) | s->setup_buf[4]; |
114 | 007fd62f | Hans de Goede | |
115 | 89b9b79f | aliguori | if (s->setup_buf[0] & USB_DIR_IN) { |
116 | 62aed765 | Anthony Liguori | ret = usb_device_handle_control(s, p, request, value, index, |
117 | 62aed765 | Anthony Liguori | s->setup_len, s->data_buf); |
118 | 50b7963e | Hans de Goede | if (ret == USB_RET_ASYNC) {
|
119 | 50b7963e | Hans de Goede | s->setup_state = SETUP_STATE_SETUP; |
120 | 50b7963e | Hans de Goede | return USB_RET_ASYNC;
|
121 | 50b7963e | Hans de Goede | } |
122 | 89b9b79f | aliguori | if (ret < 0) |
123 | 89b9b79f | aliguori | return ret;
|
124 | 89b9b79f | aliguori | |
125 | 89b9b79f | aliguori | if (ret < s->setup_len)
|
126 | 89b9b79f | aliguori | s->setup_len = ret; |
127 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_DATA; |
128 | 89b9b79f | aliguori | } else {
|
129 | 19f33223 | Hans de Goede | if (s->setup_len > sizeof(s->data_buf)) { |
130 | 19f33223 | Hans de Goede | fprintf(stderr, |
131 | 19f33223 | Hans de Goede | "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
|
132 | 19f33223 | Hans de Goede | s->setup_len, sizeof(s->data_buf));
|
133 | 19f33223 | Hans de Goede | return USB_RET_STALL;
|
134 | 19f33223 | Hans de Goede | } |
135 | 89b9b79f | aliguori | if (s->setup_len == 0) |
136 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_ACK; |
137 | 89b9b79f | aliguori | else
|
138 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_DATA; |
139 | 89b9b79f | aliguori | } |
140 | 89b9b79f | aliguori | |
141 | 89b9b79f | aliguori | return ret;
|
142 | 89b9b79f | aliguori | } |
143 | 89b9b79f | aliguori | |
144 | 89b9b79f | aliguori | static int do_token_in(USBDevice *s, USBPacket *p) |
145 | bb36d470 | bellard | { |
146 | 89b9b79f | aliguori | int request, value, index;
|
147 | 89b9b79f | aliguori | int ret = 0; |
148 | 89b9b79f | aliguori | |
149 | 079d0b7f | Gerd Hoffmann | assert(p->ep->nr == 0);
|
150 | 89b9b79f | aliguori | |
151 | 89b9b79f | aliguori | request = (s->setup_buf[0] << 8) | s->setup_buf[1]; |
152 | 89b9b79f | aliguori | value = (s->setup_buf[3] << 8) | s->setup_buf[2]; |
153 | 89b9b79f | aliguori | index = (s->setup_buf[5] << 8) | s->setup_buf[4]; |
154 | 89b9b79f | aliguori | |
155 | 89b9b79f | aliguori | switch(s->setup_state) {
|
156 | 89b9b79f | aliguori | case SETUP_STATE_ACK:
|
157 | 89b9b79f | aliguori | if (!(s->setup_buf[0] & USB_DIR_IN)) { |
158 | 62aed765 | Anthony Liguori | ret = usb_device_handle_control(s, p, request, value, index, |
159 | 62aed765 | Anthony Liguori | s->setup_len, s->data_buf); |
160 | 007fd62f | Hans de Goede | if (ret == USB_RET_ASYNC) {
|
161 | 007fd62f | Hans de Goede | return USB_RET_ASYNC;
|
162 | 007fd62f | Hans de Goede | } |
163 | 007fd62f | Hans de Goede | s->setup_state = SETUP_STATE_IDLE; |
164 | 89b9b79f | aliguori | if (ret > 0) |
165 | 89b9b79f | aliguori | return 0; |
166 | 89b9b79f | aliguori | return ret;
|
167 | 89b9b79f | aliguori | } |
168 | 89b9b79f | aliguori | |
169 | 89b9b79f | aliguori | /* return 0 byte */
|
170 | 89b9b79f | aliguori | return 0; |
171 | 89b9b79f | aliguori | |
172 | 89b9b79f | aliguori | case SETUP_STATE_DATA:
|
173 | 89b9b79f | aliguori | if (s->setup_buf[0] & USB_DIR_IN) { |
174 | 89b9b79f | aliguori | int len = s->setup_len - s->setup_index;
|
175 | 4f4321c1 | Gerd Hoffmann | if (len > p->iov.size) {
|
176 | 4f4321c1 | Gerd Hoffmann | len = p->iov.size; |
177 | 4f4321c1 | Gerd Hoffmann | } |
178 | 4f4321c1 | Gerd Hoffmann | usb_packet_copy(p, s->data_buf + s->setup_index, len); |
179 | 89b9b79f | aliguori | s->setup_index += len; |
180 | 89b9b79f | aliguori | if (s->setup_index >= s->setup_len)
|
181 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_ACK; |
182 | 89b9b79f | aliguori | return len;
|
183 | 89b9b79f | aliguori | } |
184 | 89b9b79f | aliguori | |
185 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_IDLE; |
186 | 89b9b79f | aliguori | return USB_RET_STALL;
|
187 | 89b9b79f | aliguori | |
188 | 89b9b79f | aliguori | default:
|
189 | 89b9b79f | aliguori | return USB_RET_STALL;
|
190 | 89b9b79f | aliguori | } |
191 | 89b9b79f | aliguori | } |
192 | 89b9b79f | aliguori | |
193 | 89b9b79f | aliguori | static int do_token_out(USBDevice *s, USBPacket *p) |
194 | 89b9b79f | aliguori | { |
195 | 079d0b7f | Gerd Hoffmann | assert(p->ep->nr == 0);
|
196 | 89b9b79f | aliguori | |
197 | 89b9b79f | aliguori | switch(s->setup_state) {
|
198 | 89b9b79f | aliguori | case SETUP_STATE_ACK:
|
199 | 89b9b79f | aliguori | if (s->setup_buf[0] & USB_DIR_IN) { |
200 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_IDLE; |
201 | 89b9b79f | aliguori | /* transfer OK */
|
202 | 89b9b79f | aliguori | } else {
|
203 | 89b9b79f | aliguori | /* ignore additional output */
|
204 | 89b9b79f | aliguori | } |
205 | 89b9b79f | aliguori | return 0; |
206 | 89b9b79f | aliguori | |
207 | 89b9b79f | aliguori | case SETUP_STATE_DATA:
|
208 | 89b9b79f | aliguori | if (!(s->setup_buf[0] & USB_DIR_IN)) { |
209 | 89b9b79f | aliguori | int len = s->setup_len - s->setup_index;
|
210 | 4f4321c1 | Gerd Hoffmann | if (len > p->iov.size) {
|
211 | 4f4321c1 | Gerd Hoffmann | len = p->iov.size; |
212 | 4f4321c1 | Gerd Hoffmann | } |
213 | 4f4321c1 | Gerd Hoffmann | usb_packet_copy(p, s->data_buf + s->setup_index, len); |
214 | 89b9b79f | aliguori | s->setup_index += len; |
215 | 89b9b79f | aliguori | if (s->setup_index >= s->setup_len)
|
216 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_ACK; |
217 | 89b9b79f | aliguori | return len;
|
218 | 89b9b79f | aliguori | } |
219 | 89b9b79f | aliguori | |
220 | 89b9b79f | aliguori | s->setup_state = SETUP_STATE_IDLE; |
221 | 89b9b79f | aliguori | return USB_RET_STALL;
|
222 | 89b9b79f | aliguori | |
223 | 89b9b79f | aliguori | default:
|
224 | 89b9b79f | aliguori | return USB_RET_STALL;
|
225 | 89b9b79f | aliguori | } |
226 | 89b9b79f | aliguori | } |
227 | bb36d470 | bellard | |
228 | 50b7963e | Hans de Goede | /* ctrl complete function for devices which use usb_generic_handle_packet and
|
229 | 50b7963e | Hans de Goede | may return USB_RET_ASYNC from their handle_control callback. Device code
|
230 | 50b7963e | Hans de Goede | which does this *must* call this function instead of the normal
|
231 | 50b7963e | Hans de Goede | usb_packet_complete to complete their async control packets. */
|
232 | 50b7963e | Hans de Goede | void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
|
233 | 50b7963e | Hans de Goede | { |
234 | 4f4321c1 | Gerd Hoffmann | if (p->result < 0) { |
235 | 50b7963e | Hans de Goede | s->setup_state = SETUP_STATE_IDLE; |
236 | 50b7963e | Hans de Goede | } |
237 | 50b7963e | Hans de Goede | |
238 | 50b7963e | Hans de Goede | switch (s->setup_state) {
|
239 | 50b7963e | Hans de Goede | case SETUP_STATE_SETUP:
|
240 | 4f4321c1 | Gerd Hoffmann | if (p->result < s->setup_len) {
|
241 | 4f4321c1 | Gerd Hoffmann | s->setup_len = p->result; |
242 | 50b7963e | Hans de Goede | } |
243 | 50b7963e | Hans de Goede | s->setup_state = SETUP_STATE_DATA; |
244 | 4f4321c1 | Gerd Hoffmann | p->result = 8;
|
245 | 50b7963e | Hans de Goede | break;
|
246 | 50b7963e | Hans de Goede | |
247 | 50b7963e | Hans de Goede | case SETUP_STATE_ACK:
|
248 | 50b7963e | Hans de Goede | s->setup_state = SETUP_STATE_IDLE; |
249 | 4f4321c1 | Gerd Hoffmann | p->result = 0;
|
250 | 50b7963e | Hans de Goede | break;
|
251 | 50b7963e | Hans de Goede | |
252 | 50b7963e | Hans de Goede | default:
|
253 | 50b7963e | Hans de Goede | break;
|
254 | 50b7963e | Hans de Goede | } |
255 | 50b7963e | Hans de Goede | usb_packet_complete(s, p); |
256 | 50b7963e | Hans de Goede | } |
257 | 50b7963e | Hans de Goede | |
258 | bb36d470 | bellard | /* XXX: fix overflow */
|
259 | bb36d470 | bellard | int set_usb_string(uint8_t *buf, const char *str) |
260 | bb36d470 | bellard | { |
261 | bb36d470 | bellard | int len, i;
|
262 | bb36d470 | bellard | uint8_t *q; |
263 | bb36d470 | bellard | |
264 | bb36d470 | bellard | q = buf; |
265 | bb36d470 | bellard | len = strlen(str); |
266 | ce5c37c2 | pbrook | *q++ = 2 * len + 2; |
267 | bb36d470 | bellard | *q++ = 3;
|
268 | bb36d470 | bellard | for(i = 0; i < len; i++) { |
269 | bb36d470 | bellard | *q++ = str[i]; |
270 | bb36d470 | bellard | *q++ = 0;
|
271 | bb36d470 | bellard | } |
272 | bb36d470 | bellard | return q - buf;
|
273 | bb36d470 | bellard | } |
274 | 4d611c9a | pbrook | |
275 | 73796fe6 | Gerd Hoffmann | USBDevice *usb_find_device(USBPort *port, uint8_t addr) |
276 | 73796fe6 | Gerd Hoffmann | { |
277 | 73796fe6 | Gerd Hoffmann | USBDevice *dev = port->dev; |
278 | 73796fe6 | Gerd Hoffmann | |
279 | 73796fe6 | Gerd Hoffmann | if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { |
280 | 73796fe6 | Gerd Hoffmann | return NULL; |
281 | 73796fe6 | Gerd Hoffmann | } |
282 | 73796fe6 | Gerd Hoffmann | if (dev->addr == addr) {
|
283 | 73796fe6 | Gerd Hoffmann | return dev;
|
284 | 73796fe6 | Gerd Hoffmann | } |
285 | 73796fe6 | Gerd Hoffmann | return usb_device_find_device(dev, addr);
|
286 | 73796fe6 | Gerd Hoffmann | } |
287 | 73796fe6 | Gerd Hoffmann | |
288 | db4be873 | Gerd Hoffmann | static int usb_process_one(USBPacket *p) |
289 | db4be873 | Gerd Hoffmann | { |
290 | db4be873 | Gerd Hoffmann | USBDevice *dev = p->ep->dev; |
291 | db4be873 | Gerd Hoffmann | |
292 | db4be873 | Gerd Hoffmann | if (p->ep->nr == 0) { |
293 | db4be873 | Gerd Hoffmann | /* control pipe */
|
294 | db4be873 | Gerd Hoffmann | switch (p->pid) {
|
295 | db4be873 | Gerd Hoffmann | case USB_TOKEN_SETUP:
|
296 | db4be873 | Gerd Hoffmann | return do_token_setup(dev, p);
|
297 | db4be873 | Gerd Hoffmann | case USB_TOKEN_IN:
|
298 | db4be873 | Gerd Hoffmann | return do_token_in(dev, p);
|
299 | db4be873 | Gerd Hoffmann | case USB_TOKEN_OUT:
|
300 | db4be873 | Gerd Hoffmann | return do_token_out(dev, p);
|
301 | db4be873 | Gerd Hoffmann | default:
|
302 | db4be873 | Gerd Hoffmann | return USB_RET_STALL;
|
303 | db4be873 | Gerd Hoffmann | } |
304 | db4be873 | Gerd Hoffmann | } else {
|
305 | db4be873 | Gerd Hoffmann | /* data pipe */
|
306 | db4be873 | Gerd Hoffmann | return usb_device_handle_data(dev, p);
|
307 | db4be873 | Gerd Hoffmann | } |
308 | db4be873 | Gerd Hoffmann | } |
309 | db4be873 | Gerd Hoffmann | |
310 | 53aa8c0e | Gerd Hoffmann | /* Hand over a packet to a device for processing. Return value
|
311 | 53aa8c0e | Gerd Hoffmann | USB_RET_ASYNC indicates the processing isn't finished yet, the
|
312 | 53aa8c0e | Gerd Hoffmann | driver will call usb_packet_complete() when done processing it. */
|
313 | 53aa8c0e | Gerd Hoffmann | int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
314 | 53aa8c0e | Gerd Hoffmann | { |
315 | 53aa8c0e | Gerd Hoffmann | int ret;
|
316 | 53aa8c0e | Gerd Hoffmann | |
317 | 98861f51 | Gerd Hoffmann | if (dev == NULL) { |
318 | 98861f51 | Gerd Hoffmann | return USB_RET_NODEV;
|
319 | 98861f51 | Gerd Hoffmann | } |
320 | 079d0b7f | Gerd Hoffmann | assert(dev == p->ep->dev); |
321 | 1977f93d | Gerd Hoffmann | assert(dev->state == USB_STATE_DEFAULT); |
322 | f53c398a | Gerd Hoffmann | assert(p->state == USB_PACKET_SETUP); |
323 | db4be873 | Gerd Hoffmann | assert(p->ep != NULL);
|
324 | 1977f93d | Gerd Hoffmann | |
325 | db4be873 | Gerd Hoffmann | if (QTAILQ_EMPTY(&p->ep->queue)) {
|
326 | db4be873 | Gerd Hoffmann | ret = usb_process_one(p); |
327 | db4be873 | Gerd Hoffmann | if (ret == USB_RET_ASYNC) {
|
328 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_ASYNC); |
329 | db4be873 | Gerd Hoffmann | QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); |
330 | db4be873 | Gerd Hoffmann | } else {
|
331 | db4be873 | Gerd Hoffmann | p->result = ret; |
332 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_COMPLETE); |
333 | 1977f93d | Gerd Hoffmann | } |
334 | 1977f93d | Gerd Hoffmann | } else {
|
335 | db4be873 | Gerd Hoffmann | ret = USB_RET_ASYNC; |
336 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_QUEUED); |
337 | db4be873 | Gerd Hoffmann | QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); |
338 | 4ff658fb | Gerd Hoffmann | } |
339 | 53aa8c0e | Gerd Hoffmann | return ret;
|
340 | 89b9b79f | aliguori | } |
341 | 4ff658fb | Gerd Hoffmann | |
342 | 4ff658fb | Gerd Hoffmann | /* Notify the controller that an async packet is complete. This should only
|
343 | 4ff658fb | Gerd Hoffmann | be called for packets previously deferred by returning USB_RET_ASYNC from
|
344 | 4ff658fb | Gerd Hoffmann | handle_packet. */
|
345 | 4ff658fb | Gerd Hoffmann | void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
346 | 4ff658fb | Gerd Hoffmann | { |
347 | db4be873 | Gerd Hoffmann | USBEndpoint *ep = p->ep; |
348 | db4be873 | Gerd Hoffmann | int ret;
|
349 | db4be873 | Gerd Hoffmann | |
350 | f53c398a | Gerd Hoffmann | assert(p->state == USB_PACKET_ASYNC); |
351 | db4be873 | Gerd Hoffmann | assert(QTAILQ_FIRST(&ep->queue) == p); |
352 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_COMPLETE); |
353 | db4be873 | Gerd Hoffmann | QTAILQ_REMOVE(&ep->queue, p, queue); |
354 | 4d8debba | Gerd Hoffmann | dev->port->ops->complete(dev->port, p); |
355 | db4be873 | Gerd Hoffmann | |
356 | db4be873 | Gerd Hoffmann | while (!QTAILQ_EMPTY(&ep->queue)) {
|
357 | db4be873 | Gerd Hoffmann | p = QTAILQ_FIRST(&ep->queue); |
358 | db4be873 | Gerd Hoffmann | assert(p->state == USB_PACKET_QUEUED); |
359 | db4be873 | Gerd Hoffmann | ret = usb_process_one(p); |
360 | db4be873 | Gerd Hoffmann | if (ret == USB_RET_ASYNC) {
|
361 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_ASYNC); |
362 | db4be873 | Gerd Hoffmann | break;
|
363 | db4be873 | Gerd Hoffmann | } |
364 | db4be873 | Gerd Hoffmann | p->result = ret; |
365 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_COMPLETE); |
366 | db4be873 | Gerd Hoffmann | QTAILQ_REMOVE(&ep->queue, p, queue); |
367 | db4be873 | Gerd Hoffmann | dev->port->ops->complete(dev->port, p); |
368 | db4be873 | Gerd Hoffmann | } |
369 | 4ff658fb | Gerd Hoffmann | } |
370 | 4ff658fb | Gerd Hoffmann | |
371 | 4ff658fb | Gerd Hoffmann | /* Cancel an active packet. The packed must have been deferred by
|
372 | 4ff658fb | Gerd Hoffmann | returning USB_RET_ASYNC from handle_packet, and not yet
|
373 | 4ff658fb | Gerd Hoffmann | completed. */
|
374 | 4ff658fb | Gerd Hoffmann | void usb_cancel_packet(USBPacket * p)
|
375 | 4ff658fb | Gerd Hoffmann | { |
376 | db4be873 | Gerd Hoffmann | bool callback = (p->state == USB_PACKET_ASYNC);
|
377 | db4be873 | Gerd Hoffmann | assert(usb_packet_is_inflight(p)); |
378 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_CANCELED); |
379 | db4be873 | Gerd Hoffmann | QTAILQ_REMOVE(&p->ep->queue, p, queue); |
380 | db4be873 | Gerd Hoffmann | if (callback) {
|
381 | db4be873 | Gerd Hoffmann | usb_device_cancel_packet(p->ep->dev, p); |
382 | db4be873 | Gerd Hoffmann | } |
383 | 4ff658fb | Gerd Hoffmann | } |
384 | 4f4321c1 | Gerd Hoffmann | |
385 | 4f4321c1 | Gerd Hoffmann | |
386 | 4f4321c1 | Gerd Hoffmann | void usb_packet_init(USBPacket *p)
|
387 | 4f4321c1 | Gerd Hoffmann | { |
388 | 4f4321c1 | Gerd Hoffmann | qemu_iovec_init(&p->iov, 1);
|
389 | 4f4321c1 | Gerd Hoffmann | } |
390 | 4f4321c1 | Gerd Hoffmann | |
391 | db4be873 | Gerd Hoffmann | void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
392 | db4be873 | Gerd Hoffmann | { |
393 | db4be873 | Gerd Hoffmann | #ifdef DEBUG
|
394 | db4be873 | Gerd Hoffmann | static const char *name[] = { |
395 | db4be873 | Gerd Hoffmann | [USB_PACKET_UNDEFINED] = "undef",
|
396 | db4be873 | Gerd Hoffmann | [USB_PACKET_SETUP] = "setup",
|
397 | db4be873 | Gerd Hoffmann | [USB_PACKET_QUEUED] = "queued",
|
398 | db4be873 | Gerd Hoffmann | [USB_PACKET_ASYNC] = "async",
|
399 | db4be873 | Gerd Hoffmann | [USB_PACKET_COMPLETE] = "complete",
|
400 | db4be873 | Gerd Hoffmann | [USB_PACKET_CANCELED] = "canceled",
|
401 | db4be873 | Gerd Hoffmann | }; |
402 | db4be873 | Gerd Hoffmann | static const char *rets[] = { |
403 | db4be873 | Gerd Hoffmann | [-USB_RET_NODEV] = "NODEV",
|
404 | db4be873 | Gerd Hoffmann | [-USB_RET_NAK] = "NAK",
|
405 | db4be873 | Gerd Hoffmann | [-USB_RET_STALL] = "STALL",
|
406 | db4be873 | Gerd Hoffmann | [-USB_RET_BABBLE] = "BABBLE",
|
407 | db4be873 | Gerd Hoffmann | [-USB_RET_ASYNC] = "ASYNC",
|
408 | db4be873 | Gerd Hoffmann | }; |
409 | db4be873 | Gerd Hoffmann | char add[16] = ""; |
410 | db4be873 | Gerd Hoffmann | |
411 | db4be873 | Gerd Hoffmann | if (state == USB_PACKET_COMPLETE) {
|
412 | db4be873 | Gerd Hoffmann | if (p->result < 0) { |
413 | db4be873 | Gerd Hoffmann | snprintf(add, sizeof(add), " - %s", rets[-p->result]); |
414 | db4be873 | Gerd Hoffmann | } else {
|
415 | db4be873 | Gerd Hoffmann | snprintf(add, sizeof(add), " - %d", p->result); |
416 | db4be873 | Gerd Hoffmann | } |
417 | db4be873 | Gerd Hoffmann | } |
418 | db4be873 | Gerd Hoffmann | fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n",
|
419 | db4be873 | Gerd Hoffmann | p->ep->dev->qdev.parent_bus->name, |
420 | db4be873 | Gerd Hoffmann | p->ep->dev->port->path, |
421 | db4be873 | Gerd Hoffmann | p->ep->dev->addr, p->ep->nr, |
422 | db4be873 | Gerd Hoffmann | p, name[p->state], name[state], add); |
423 | db4be873 | Gerd Hoffmann | #endif
|
424 | db4be873 | Gerd Hoffmann | p->state = state; |
425 | db4be873 | Gerd Hoffmann | } |
426 | db4be873 | Gerd Hoffmann | |
427 | 079d0b7f | Gerd Hoffmann | void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) |
428 | 4f4321c1 | Gerd Hoffmann | { |
429 | f53c398a | Gerd Hoffmann | assert(!usb_packet_is_inflight(p)); |
430 | 4f4321c1 | Gerd Hoffmann | p->pid = pid; |
431 | 079d0b7f | Gerd Hoffmann | p->ep = ep; |
432 | 4f4321c1 | Gerd Hoffmann | p->result = 0;
|
433 | 4f4321c1 | Gerd Hoffmann | qemu_iovec_reset(&p->iov); |
434 | db4be873 | Gerd Hoffmann | usb_packet_set_state(p, USB_PACKET_SETUP); |
435 | 4f4321c1 | Gerd Hoffmann | } |
436 | 4f4321c1 | Gerd Hoffmann | |
437 | 4f4321c1 | Gerd Hoffmann | void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) |
438 | 4f4321c1 | Gerd Hoffmann | { |
439 | 4f4321c1 | Gerd Hoffmann | qemu_iovec_add(&p->iov, ptr, len); |
440 | 4f4321c1 | Gerd Hoffmann | } |
441 | 4f4321c1 | Gerd Hoffmann | |
442 | 4f4321c1 | Gerd Hoffmann | void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) |
443 | 4f4321c1 | Gerd Hoffmann | { |
444 | 4f4321c1 | Gerd Hoffmann | assert(p->result >= 0);
|
445 | 4f4321c1 | Gerd Hoffmann | assert(p->result + bytes <= p->iov.size); |
446 | 4f4321c1 | Gerd Hoffmann | switch (p->pid) {
|
447 | 4f4321c1 | Gerd Hoffmann | case USB_TOKEN_SETUP:
|
448 | 4f4321c1 | Gerd Hoffmann | case USB_TOKEN_OUT:
|
449 | 4f4321c1 | Gerd Hoffmann | iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); |
450 | 4f4321c1 | Gerd Hoffmann | break;
|
451 | 4f4321c1 | Gerd Hoffmann | case USB_TOKEN_IN:
|
452 | 4f4321c1 | Gerd Hoffmann | iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); |
453 | 4f4321c1 | Gerd Hoffmann | break;
|
454 | 4f4321c1 | Gerd Hoffmann | default:
|
455 | 4f4321c1 | Gerd Hoffmann | fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
|
456 | 4f4321c1 | Gerd Hoffmann | abort(); |
457 | 4f4321c1 | Gerd Hoffmann | } |
458 | 4f4321c1 | Gerd Hoffmann | p->result += bytes; |
459 | 4f4321c1 | Gerd Hoffmann | } |
460 | 4f4321c1 | Gerd Hoffmann | |
461 | 4f4321c1 | Gerd Hoffmann | void usb_packet_skip(USBPacket *p, size_t bytes)
|
462 | 4f4321c1 | Gerd Hoffmann | { |
463 | 4f4321c1 | Gerd Hoffmann | assert(p->result >= 0);
|
464 | 4f4321c1 | Gerd Hoffmann | assert(p->result + bytes <= p->iov.size); |
465 | 4f4321c1 | Gerd Hoffmann | if (p->pid == USB_TOKEN_IN) {
|
466 | 4f4321c1 | Gerd Hoffmann | iov_clear(p->iov.iov, p->iov.niov, p->result, bytes); |
467 | 4f4321c1 | Gerd Hoffmann | } |
468 | 4f4321c1 | Gerd Hoffmann | p->result += bytes; |
469 | 4f4321c1 | Gerd Hoffmann | } |
470 | 4f4321c1 | Gerd Hoffmann | |
471 | 4f4321c1 | Gerd Hoffmann | void usb_packet_cleanup(USBPacket *p)
|
472 | 4f4321c1 | Gerd Hoffmann | { |
473 | f53c398a | Gerd Hoffmann | assert(!usb_packet_is_inflight(p)); |
474 | 4f4321c1 | Gerd Hoffmann | qemu_iovec_destroy(&p->iov); |
475 | 4f4321c1 | Gerd Hoffmann | } |
476 | d8e17efd | Gerd Hoffmann | |
477 | d8e17efd | Gerd Hoffmann | void usb_ep_init(USBDevice *dev)
|
478 | d8e17efd | Gerd Hoffmann | { |
479 | d8e17efd | Gerd Hoffmann | int ep;
|
480 | d8e17efd | Gerd Hoffmann | |
481 | 63095ab5 | Gerd Hoffmann | dev->ep_ctl.nr = 0;
|
482 | 25d5de7d | Gerd Hoffmann | dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; |
483 | 25d5de7d | Gerd Hoffmann | dev->ep_ctl.ifnum = 0;
|
484 | 25d5de7d | Gerd Hoffmann | dev->ep_ctl.dev = dev; |
485 | db4be873 | Gerd Hoffmann | QTAILQ_INIT(&dev->ep_ctl.queue); |
486 | d8e17efd | Gerd Hoffmann | for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { |
487 | 63095ab5 | Gerd Hoffmann | dev->ep_in[ep].nr = ep + 1;
|
488 | 63095ab5 | Gerd Hoffmann | dev->ep_out[ep].nr = ep + 1;
|
489 | 63095ab5 | Gerd Hoffmann | dev->ep_in[ep].pid = USB_TOKEN_IN; |
490 | 63095ab5 | Gerd Hoffmann | dev->ep_out[ep].pid = USB_TOKEN_OUT; |
491 | d8e17efd | Gerd Hoffmann | dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; |
492 | d8e17efd | Gerd Hoffmann | dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; |
493 | 82f02fe9 | Gerd Hoffmann | dev->ep_in[ep].ifnum = 0;
|
494 | 82f02fe9 | Gerd Hoffmann | dev->ep_out[ep].ifnum = 0;
|
495 | 25d5de7d | Gerd Hoffmann | dev->ep_in[ep].dev = dev; |
496 | 25d5de7d | Gerd Hoffmann | dev->ep_out[ep].dev = dev; |
497 | db4be873 | Gerd Hoffmann | QTAILQ_INIT(&dev->ep_in[ep].queue); |
498 | db4be873 | Gerd Hoffmann | QTAILQ_INIT(&dev->ep_out[ep].queue); |
499 | d8e17efd | Gerd Hoffmann | } |
500 | d8e17efd | Gerd Hoffmann | } |
501 | d8e17efd | Gerd Hoffmann | |
502 | 5b6780d0 | Gerd Hoffmann | void usb_ep_dump(USBDevice *dev)
|
503 | 5b6780d0 | Gerd Hoffmann | { |
504 | 5b6780d0 | Gerd Hoffmann | static const char *tname[] = { |
505 | 5b6780d0 | Gerd Hoffmann | [USB_ENDPOINT_XFER_CONTROL] = "control",
|
506 | 5b6780d0 | Gerd Hoffmann | [USB_ENDPOINT_XFER_ISOC] = "isoc",
|
507 | 5b6780d0 | Gerd Hoffmann | [USB_ENDPOINT_XFER_BULK] = "bulk",
|
508 | 5b6780d0 | Gerd Hoffmann | [USB_ENDPOINT_XFER_INT] = "int",
|
509 | 5b6780d0 | Gerd Hoffmann | }; |
510 | 5b6780d0 | Gerd Hoffmann | int ifnum, ep, first;
|
511 | 5b6780d0 | Gerd Hoffmann | |
512 | 5b6780d0 | Gerd Hoffmann | fprintf(stderr, "Device \"%s\", config %d\n",
|
513 | 5b6780d0 | Gerd Hoffmann | dev->product_desc, dev->configuration); |
514 | 5b6780d0 | Gerd Hoffmann | for (ifnum = 0; ifnum < 16; ifnum++) { |
515 | 5b6780d0 | Gerd Hoffmann | first = 1;
|
516 | 5b6780d0 | Gerd Hoffmann | for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { |
517 | 5b6780d0 | Gerd Hoffmann | if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
|
518 | 5b6780d0 | Gerd Hoffmann | dev->ep_in[ep].ifnum == ifnum) { |
519 | 5b6780d0 | Gerd Hoffmann | if (first) {
|
520 | 5b6780d0 | Gerd Hoffmann | first = 0;
|
521 | 5b6780d0 | Gerd Hoffmann | fprintf(stderr, " Interface %d, alternative %d\n",
|
522 | 5b6780d0 | Gerd Hoffmann | ifnum, dev->altsetting[ifnum]); |
523 | 5b6780d0 | Gerd Hoffmann | } |
524 | f003397c | Gerd Hoffmann | fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep,
|
525 | f003397c | Gerd Hoffmann | tname[dev->ep_in[ep].type], |
526 | f003397c | Gerd Hoffmann | dev->ep_in[ep].max_packet_size); |
527 | 5b6780d0 | Gerd Hoffmann | } |
528 | 5b6780d0 | Gerd Hoffmann | if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
|
529 | 5b6780d0 | Gerd Hoffmann | dev->ep_out[ep].ifnum == ifnum) { |
530 | 5b6780d0 | Gerd Hoffmann | if (first) {
|
531 | 5b6780d0 | Gerd Hoffmann | first = 0;
|
532 | 5b6780d0 | Gerd Hoffmann | fprintf(stderr, " Interface %d, alternative %d\n",
|
533 | 5b6780d0 | Gerd Hoffmann | ifnum, dev->altsetting[ifnum]); |
534 | 5b6780d0 | Gerd Hoffmann | } |
535 | f003397c | Gerd Hoffmann | fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep,
|
536 | f003397c | Gerd Hoffmann | tname[dev->ep_out[ep].type], |
537 | f003397c | Gerd Hoffmann | dev->ep_out[ep].max_packet_size); |
538 | 5b6780d0 | Gerd Hoffmann | } |
539 | 5b6780d0 | Gerd Hoffmann | } |
540 | 5b6780d0 | Gerd Hoffmann | } |
541 | 5b6780d0 | Gerd Hoffmann | fprintf(stderr, "--\n");
|
542 | 5b6780d0 | Gerd Hoffmann | } |
543 | 5b6780d0 | Gerd Hoffmann | |
544 | d8e17efd | Gerd Hoffmann | struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) |
545 | d8e17efd | Gerd Hoffmann | { |
546 | 079d0b7f | Gerd Hoffmann | struct USBEndpoint *eps;
|
547 | 079d0b7f | Gerd Hoffmann | |
548 | 079d0b7f | Gerd Hoffmann | if (dev == NULL) { |
549 | 079d0b7f | Gerd Hoffmann | return NULL; |
550 | 079d0b7f | Gerd Hoffmann | } |
551 | 079d0b7f | Gerd Hoffmann | eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; |
552 | 25d5de7d | Gerd Hoffmann | if (ep == 0) { |
553 | 25d5de7d | Gerd Hoffmann | return &dev->ep_ctl;
|
554 | 25d5de7d | Gerd Hoffmann | } |
555 | d8e17efd | Gerd Hoffmann | assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); |
556 | d8e17efd | Gerd Hoffmann | assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
|
557 | d8e17efd | Gerd Hoffmann | return eps + ep - 1; |
558 | d8e17efd | Gerd Hoffmann | } |
559 | d8e17efd | Gerd Hoffmann | |
560 | d8e17efd | Gerd Hoffmann | uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep) |
561 | d8e17efd | Gerd Hoffmann | { |
562 | d8e17efd | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
563 | d8e17efd | Gerd Hoffmann | return uep->type;
|
564 | d8e17efd | Gerd Hoffmann | } |
565 | d8e17efd | Gerd Hoffmann | |
566 | d8e17efd | Gerd Hoffmann | void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type) |
567 | d8e17efd | Gerd Hoffmann | { |
568 | d8e17efd | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
569 | d8e17efd | Gerd Hoffmann | uep->type = type; |
570 | d8e17efd | Gerd Hoffmann | } |
571 | 82f02fe9 | Gerd Hoffmann | |
572 | 82f02fe9 | Gerd Hoffmann | uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep) |
573 | 82f02fe9 | Gerd Hoffmann | { |
574 | 82f02fe9 | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
575 | 82f02fe9 | Gerd Hoffmann | return uep->ifnum;
|
576 | 82f02fe9 | Gerd Hoffmann | } |
577 | 82f02fe9 | Gerd Hoffmann | |
578 | 82f02fe9 | Gerd Hoffmann | void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum) |
579 | 82f02fe9 | Gerd Hoffmann | { |
580 | 82f02fe9 | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
581 | 82f02fe9 | Gerd Hoffmann | uep->ifnum = ifnum; |
582 | 82f02fe9 | Gerd Hoffmann | } |
583 | f003397c | Gerd Hoffmann | |
584 | f003397c | Gerd Hoffmann | void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, |
585 | f003397c | Gerd Hoffmann | uint16_t raw) |
586 | f003397c | Gerd Hoffmann | { |
587 | f003397c | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
588 | f003397c | Gerd Hoffmann | int size, microframes;
|
589 | f003397c | Gerd Hoffmann | |
590 | f003397c | Gerd Hoffmann | size = raw & 0x7ff;
|
591 | f003397c | Gerd Hoffmann | switch ((raw >> 11) & 3) { |
592 | f003397c | Gerd Hoffmann | case 1: |
593 | f003397c | Gerd Hoffmann | microframes = 2;
|
594 | f003397c | Gerd Hoffmann | break;
|
595 | f003397c | Gerd Hoffmann | case 2: |
596 | f003397c | Gerd Hoffmann | microframes = 3;
|
597 | f003397c | Gerd Hoffmann | break;
|
598 | f003397c | Gerd Hoffmann | default:
|
599 | f003397c | Gerd Hoffmann | microframes = 1;
|
600 | f003397c | Gerd Hoffmann | break;
|
601 | f003397c | Gerd Hoffmann | } |
602 | f003397c | Gerd Hoffmann | uep->max_packet_size = size * microframes; |
603 | f003397c | Gerd Hoffmann | } |
604 | f003397c | Gerd Hoffmann | |
605 | f003397c | Gerd Hoffmann | int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) |
606 | f003397c | Gerd Hoffmann | { |
607 | f003397c | Gerd Hoffmann | struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
|
608 | f003397c | Gerd Hoffmann | return uep->max_packet_size;
|
609 | f003397c | Gerd Hoffmann | } |