1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /* $FreeBSD$ */ |
---|
4 | /*- |
---|
5 | * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. |
---|
6 | * |
---|
7 | * Redistribution and use in source and binary forms, with or without |
---|
8 | * modification, are permitted provided that the following conditions |
---|
9 | * are met: |
---|
10 | * 1. Redistributions of source code must retain the above copyright |
---|
11 | * notice, this list of conditions and the following disclaimer. |
---|
12 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
13 | * notice, this list of conditions and the following disclaimer in the |
---|
14 | * documentation and/or other materials provided with the distribution. |
---|
15 | * |
---|
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
26 | * SUCH DAMAGE. |
---|
27 | */ |
---|
28 | |
---|
29 | #include <sys/stdint.h> |
---|
30 | #include <sys/stddef.h> |
---|
31 | #include <rtems/bsd/sys/param.h> |
---|
32 | #include <sys/queue.h> |
---|
33 | #include <rtems/bsd/sys/types.h> |
---|
34 | #include <sys/systm.h> |
---|
35 | #include <sys/kernel.h> |
---|
36 | #include <sys/bus.h> |
---|
37 | #include <sys/module.h> |
---|
38 | #include <rtems/bsd/sys/lock.h> |
---|
39 | #include <sys/mutex.h> |
---|
40 | #include <sys/condvar.h> |
---|
41 | #include <sys/sysctl.h> |
---|
42 | #include <sys/sx.h> |
---|
43 | #include <rtems/bsd/sys/unistd.h> |
---|
44 | #include <sys/callout.h> |
---|
45 | #include <sys/malloc.h> |
---|
46 | #include <sys/priv.h> |
---|
47 | |
---|
48 | #include <dev/usb/usb.h> |
---|
49 | #include <dev/usb/usbdi.h> |
---|
50 | #include <dev/usb/usbdi_util.h> |
---|
51 | #include <rtems/bsd/local/usb_if.h> |
---|
52 | |
---|
53 | #define USB_DEBUG_VAR usb_debug |
---|
54 | |
---|
55 | #include <dev/usb/usb_core.h> |
---|
56 | #include <dev/usb/usb_process.h> |
---|
57 | #include <dev/usb/usb_busdma.h> |
---|
58 | #include <dev/usb/usb_transfer.h> |
---|
59 | #include <dev/usb/usb_device.h> |
---|
60 | #include <dev/usb/usb_debug.h> |
---|
61 | #include <dev/usb/usb_dynamic.h> |
---|
62 | #include <dev/usb/usb_hub.h> |
---|
63 | |
---|
64 | #include <dev/usb/usb_controller.h> |
---|
65 | #include <dev/usb/usb_bus.h> |
---|
66 | |
---|
67 | /* function prototypes */ |
---|
68 | |
---|
69 | static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); |
---|
70 | static usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); |
---|
71 | static usb_error_t usb_handle_request(struct usb_xfer *); |
---|
72 | static usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); |
---|
73 | static usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, |
---|
74 | uint8_t); |
---|
75 | static usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, |
---|
76 | uint16_t *, struct usb_device_request, uint16_t, |
---|
77 | uint8_t); |
---|
78 | |
---|
79 | /*------------------------------------------------------------------------* |
---|
80 | * usb_handle_request_callback |
---|
81 | * |
---|
82 | * This function is the USB callback for generic USB Device control |
---|
83 | * transfers. |
---|
84 | *------------------------------------------------------------------------*/ |
---|
85 | void |
---|
86 | usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) |
---|
87 | { |
---|
88 | usb_error_t err; |
---|
89 | |
---|
90 | /* check the current transfer state */ |
---|
91 | |
---|
92 | switch (USB_GET_STATE(xfer)) { |
---|
93 | case USB_ST_SETUP: |
---|
94 | case USB_ST_TRANSFERRED: |
---|
95 | |
---|
96 | /* handle the request */ |
---|
97 | err = usb_handle_request(xfer); |
---|
98 | |
---|
99 | if (err) { |
---|
100 | |
---|
101 | if (err == USB_ERR_BAD_CONTEXT) { |
---|
102 | /* we need to re-setup the control transfer */ |
---|
103 | usb_needs_explore(xfer->xroot->bus, 0); |
---|
104 | break; |
---|
105 | } |
---|
106 | goto tr_restart; |
---|
107 | } |
---|
108 | usbd_transfer_submit(xfer); |
---|
109 | break; |
---|
110 | |
---|
111 | default: |
---|
112 | /* check if a control transfer is active */ |
---|
113 | if (xfer->flags_int.control_rem != 0xFFFF) { |
---|
114 | /* handle the request */ |
---|
115 | err = usb_handle_request(xfer); |
---|
116 | } |
---|
117 | if (xfer->error != USB_ERR_CANCELLED) { |
---|
118 | /* should not happen - try stalling */ |
---|
119 | goto tr_restart; |
---|
120 | } |
---|
121 | break; |
---|
122 | } |
---|
123 | return; |
---|
124 | |
---|
125 | tr_restart: |
---|
126 | /* |
---|
127 | * If a control transfer is active, stall it, and wait for the |
---|
128 | * next control transfer. |
---|
129 | */ |
---|
130 | usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); |
---|
131 | xfer->nframes = 1; |
---|
132 | xfer->flags.manual_status = 1; |
---|
133 | xfer->flags.force_short_xfer = 0; |
---|
134 | usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ |
---|
135 | usbd_transfer_submit(xfer); |
---|
136 | } |
---|
137 | |
---|
138 | /*------------------------------------------------------------------------* |
---|
139 | * usb_handle_set_config |
---|
140 | * |
---|
141 | * Returns: |
---|
142 | * 0: Success |
---|
143 | * Else: Failure |
---|
144 | *------------------------------------------------------------------------*/ |
---|
145 | static usb_error_t |
---|
146 | usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) |
---|
147 | { |
---|
148 | struct usb_device *udev = xfer->xroot->udev; |
---|
149 | usb_error_t err = 0; |
---|
150 | uint8_t do_unlock; |
---|
151 | |
---|
152 | /* |
---|
153 | * We need to protect against other threads doing probe and |
---|
154 | * attach: |
---|
155 | */ |
---|
156 | USB_XFER_UNLOCK(xfer); |
---|
157 | |
---|
158 | /* Prevent re-enumeration */ |
---|
159 | do_unlock = usbd_enum_lock(udev); |
---|
160 | |
---|
161 | if (conf_no == USB_UNCONFIG_NO) { |
---|
162 | conf_no = USB_UNCONFIG_INDEX; |
---|
163 | } else { |
---|
164 | /* |
---|
165 | * The relationship between config number and config index |
---|
166 | * is very simple in our case: |
---|
167 | */ |
---|
168 | conf_no--; |
---|
169 | } |
---|
170 | |
---|
171 | if (usbd_set_config_index(udev, conf_no)) { |
---|
172 | DPRINTF("set config %d failed\n", conf_no); |
---|
173 | err = USB_ERR_STALLED; |
---|
174 | goto done; |
---|
175 | } |
---|
176 | if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { |
---|
177 | DPRINTF("probe and attach failed\n"); |
---|
178 | err = USB_ERR_STALLED; |
---|
179 | goto done; |
---|
180 | } |
---|
181 | done: |
---|
182 | if (do_unlock) |
---|
183 | usbd_enum_unlock(udev); |
---|
184 | USB_XFER_LOCK(xfer); |
---|
185 | return (err); |
---|
186 | } |
---|
187 | |
---|
188 | static usb_error_t |
---|
189 | usb_check_alt_setting(struct usb_device *udev, |
---|
190 | struct usb_interface *iface, uint8_t alt_index) |
---|
191 | { |
---|
192 | uint8_t do_unlock; |
---|
193 | usb_error_t err = 0; |
---|
194 | |
---|
195 | /* Prevent re-enumeration */ |
---|
196 | do_unlock = usbd_enum_lock(udev); |
---|
197 | |
---|
198 | if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) |
---|
199 | err = USB_ERR_INVAL; |
---|
200 | |
---|
201 | if (do_unlock) |
---|
202 | usbd_enum_unlock(udev); |
---|
203 | |
---|
204 | return (err); |
---|
205 | } |
---|
206 | |
---|
207 | /*------------------------------------------------------------------------* |
---|
208 | * usb_handle_iface_request |
---|
209 | * |
---|
210 | * Returns: |
---|
211 | * 0: Success |
---|
212 | * Else: Failure |
---|
213 | *------------------------------------------------------------------------*/ |
---|
214 | static usb_error_t |
---|
215 | usb_handle_iface_request(struct usb_xfer *xfer, |
---|
216 | void **ppdata, uint16_t *plen, |
---|
217 | struct usb_device_request req, uint16_t off, uint8_t state) |
---|
218 | { |
---|
219 | struct usb_interface *iface; |
---|
220 | struct usb_interface *iface_parent; /* parent interface */ |
---|
221 | struct usb_device *udev = xfer->xroot->udev; |
---|
222 | int error; |
---|
223 | uint8_t iface_index; |
---|
224 | uint8_t temp_state; |
---|
225 | uint8_t do_unlock; |
---|
226 | |
---|
227 | if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { |
---|
228 | iface_index = req.wIndex[0]; /* unicast */ |
---|
229 | } else { |
---|
230 | iface_index = 0; /* broadcast */ |
---|
231 | } |
---|
232 | |
---|
233 | /* |
---|
234 | * We need to protect against other threads doing probe and |
---|
235 | * attach: |
---|
236 | */ |
---|
237 | USB_XFER_UNLOCK(xfer); |
---|
238 | |
---|
239 | /* Prevent re-enumeration */ |
---|
240 | do_unlock = usbd_enum_lock(udev); |
---|
241 | |
---|
242 | error = ENXIO; |
---|
243 | |
---|
244 | tr_repeat: |
---|
245 | iface = usbd_get_iface(udev, iface_index); |
---|
246 | if ((iface == NULL) || |
---|
247 | (iface->idesc == NULL)) { |
---|
248 | /* end of interfaces non-existing interface */ |
---|
249 | goto tr_stalled; |
---|
250 | } |
---|
251 | /* set initial state */ |
---|
252 | |
---|
253 | temp_state = state; |
---|
254 | |
---|
255 | /* forward request to interface, if any */ |
---|
256 | |
---|
257 | if ((error != 0) && |
---|
258 | (error != ENOTTY) && |
---|
259 | (iface->subdev != NULL) && |
---|
260 | device_is_attached(iface->subdev)) { |
---|
261 | #if 0 |
---|
262 | DEVMETHOD(usb_handle_request, NULL); /* dummy */ |
---|
263 | #endif |
---|
264 | error = USB_HANDLE_REQUEST(iface->subdev, |
---|
265 | &req, ppdata, plen, |
---|
266 | off, &temp_state); |
---|
267 | } |
---|
268 | iface_parent = usbd_get_iface(udev, iface->parent_iface_index); |
---|
269 | |
---|
270 | if ((iface_parent == NULL) || |
---|
271 | (iface_parent->idesc == NULL)) { |
---|
272 | /* non-existing interface */ |
---|
273 | iface_parent = NULL; |
---|
274 | } |
---|
275 | /* forward request to parent interface, if any */ |
---|
276 | |
---|
277 | if ((error != 0) && |
---|
278 | (error != ENOTTY) && |
---|
279 | (iface_parent != NULL) && |
---|
280 | (iface_parent->subdev != NULL) && |
---|
281 | ((req.bmRequestType & 0x1F) == UT_INTERFACE) && |
---|
282 | (iface_parent->subdev != iface->subdev) && |
---|
283 | device_is_attached(iface_parent->subdev)) { |
---|
284 | error = USB_HANDLE_REQUEST(iface_parent->subdev, |
---|
285 | &req, ppdata, plen, off, &temp_state); |
---|
286 | } |
---|
287 | if (error == 0) { |
---|
288 | /* negativly adjust pointer and length */ |
---|
289 | *ppdata = ((uint8_t *)(*ppdata)) - off; |
---|
290 | *plen += off; |
---|
291 | |
---|
292 | if ((state == USB_HR_NOT_COMPLETE) && |
---|
293 | (temp_state == USB_HR_COMPLETE_OK)) |
---|
294 | goto tr_short; |
---|
295 | else |
---|
296 | goto tr_valid; |
---|
297 | } else if (error == ENOTTY) { |
---|
298 | goto tr_stalled; |
---|
299 | } |
---|
300 | if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { |
---|
301 | iface_index++; /* iterate */ |
---|
302 | goto tr_repeat; |
---|
303 | } |
---|
304 | if (state != USB_HR_NOT_COMPLETE) { |
---|
305 | /* we are complete */ |
---|
306 | goto tr_valid; |
---|
307 | } |
---|
308 | switch (req.bmRequestType) { |
---|
309 | case UT_WRITE_INTERFACE: |
---|
310 | switch (req.bRequest) { |
---|
311 | case UR_SET_INTERFACE: |
---|
312 | /* |
---|
313 | * We assume that the endpoints are the same |
---|
314 | * accross the alternate settings. |
---|
315 | * |
---|
316 | * Reset the endpoints, because re-attaching |
---|
317 | * only a part of the device is not possible. |
---|
318 | */ |
---|
319 | error = usb_check_alt_setting(udev, |
---|
320 | iface, req.wValue[0]); |
---|
321 | if (error) { |
---|
322 | DPRINTF("alt setting does not exist %s\n", |
---|
323 | usbd_errstr(error)); |
---|
324 | goto tr_stalled; |
---|
325 | } |
---|
326 | error = usb_reset_iface_endpoints(udev, iface_index); |
---|
327 | if (error) { |
---|
328 | DPRINTF("alt setting failed %s\n", |
---|
329 | usbd_errstr(error)); |
---|
330 | goto tr_stalled; |
---|
331 | } |
---|
332 | /* update the current alternate setting */ |
---|
333 | iface->alt_index = req.wValue[0]; |
---|
334 | break; |
---|
335 | |
---|
336 | default: |
---|
337 | goto tr_stalled; |
---|
338 | } |
---|
339 | break; |
---|
340 | |
---|
341 | case UT_READ_INTERFACE: |
---|
342 | switch (req.bRequest) { |
---|
343 | case UR_GET_INTERFACE: |
---|
344 | *ppdata = &iface->alt_index; |
---|
345 | *plen = 1; |
---|
346 | break; |
---|
347 | |
---|
348 | default: |
---|
349 | goto tr_stalled; |
---|
350 | } |
---|
351 | break; |
---|
352 | default: |
---|
353 | goto tr_stalled; |
---|
354 | } |
---|
355 | tr_valid: |
---|
356 | if (do_unlock) |
---|
357 | usbd_enum_unlock(udev); |
---|
358 | USB_XFER_LOCK(xfer); |
---|
359 | return (0); |
---|
360 | |
---|
361 | tr_short: |
---|
362 | if (do_unlock) |
---|
363 | usbd_enum_unlock(udev); |
---|
364 | USB_XFER_LOCK(xfer); |
---|
365 | return (USB_ERR_SHORT_XFER); |
---|
366 | |
---|
367 | tr_stalled: |
---|
368 | if (do_unlock) |
---|
369 | usbd_enum_unlock(udev); |
---|
370 | USB_XFER_LOCK(xfer); |
---|
371 | return (USB_ERR_STALLED); |
---|
372 | } |
---|
373 | |
---|
374 | /*------------------------------------------------------------------------* |
---|
375 | * usb_handle_stall |
---|
376 | * |
---|
377 | * Returns: |
---|
378 | * 0: Success |
---|
379 | * Else: Failure |
---|
380 | *------------------------------------------------------------------------*/ |
---|
381 | static usb_error_t |
---|
382 | usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) |
---|
383 | { |
---|
384 | struct usb_device *udev = xfer->xroot->udev; |
---|
385 | usb_error_t err; |
---|
386 | |
---|
387 | USB_XFER_UNLOCK(xfer); |
---|
388 | err = usbd_set_endpoint_stall(udev, |
---|
389 | usbd_get_ep_by_addr(udev, ep), do_stall); |
---|
390 | USB_XFER_LOCK(xfer); |
---|
391 | return (err); |
---|
392 | } |
---|
393 | |
---|
394 | /*------------------------------------------------------------------------* |
---|
395 | * usb_handle_get_stall |
---|
396 | * |
---|
397 | * Returns: |
---|
398 | * 0: Success |
---|
399 | * Else: Failure |
---|
400 | *------------------------------------------------------------------------*/ |
---|
401 | static uint8_t |
---|
402 | usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) |
---|
403 | { |
---|
404 | struct usb_endpoint *ep; |
---|
405 | uint8_t halted; |
---|
406 | |
---|
407 | ep = usbd_get_ep_by_addr(udev, ea_val); |
---|
408 | if (ep == NULL) { |
---|
409 | /* nothing to do */ |
---|
410 | return (0); |
---|
411 | } |
---|
412 | USB_BUS_LOCK(udev->bus); |
---|
413 | halted = ep->is_stalled; |
---|
414 | USB_BUS_UNLOCK(udev->bus); |
---|
415 | |
---|
416 | return (halted); |
---|
417 | } |
---|
418 | |
---|
419 | /*------------------------------------------------------------------------* |
---|
420 | * usb_handle_remote_wakeup |
---|
421 | * |
---|
422 | * Returns: |
---|
423 | * 0: Success |
---|
424 | * Else: Failure |
---|
425 | *------------------------------------------------------------------------*/ |
---|
426 | static usb_error_t |
---|
427 | usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) |
---|
428 | { |
---|
429 | struct usb_device *udev; |
---|
430 | struct usb_bus *bus; |
---|
431 | |
---|
432 | udev = xfer->xroot->udev; |
---|
433 | bus = udev->bus; |
---|
434 | |
---|
435 | USB_BUS_LOCK(bus); |
---|
436 | |
---|
437 | if (is_on) { |
---|
438 | udev->flags.remote_wakeup = 1; |
---|
439 | } else { |
---|
440 | udev->flags.remote_wakeup = 0; |
---|
441 | } |
---|
442 | |
---|
443 | USB_BUS_UNLOCK(bus); |
---|
444 | |
---|
445 | #if USB_HAVE_POWERD |
---|
446 | /* In case we are out of sync, update the power state. */ |
---|
447 | usb_bus_power_update(udev->bus); |
---|
448 | #endif |
---|
449 | return (0); /* success */ |
---|
450 | } |
---|
451 | |
---|
452 | /*------------------------------------------------------------------------* |
---|
453 | * usb_handle_request |
---|
454 | * |
---|
455 | * Internal state sequence: |
---|
456 | * |
---|
457 | * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR |
---|
458 | * |
---|
459 | * Returns: |
---|
460 | * 0: Ready to start hardware |
---|
461 | * Else: Stall current transfer, if any |
---|
462 | *------------------------------------------------------------------------*/ |
---|
463 | static usb_error_t |
---|
464 | usb_handle_request(struct usb_xfer *xfer) |
---|
465 | { |
---|
466 | struct usb_device_request req; |
---|
467 | struct usb_device *udev; |
---|
468 | const void *src_zcopy; /* zero-copy source pointer */ |
---|
469 | const void *src_mcopy; /* non zero-copy source pointer */ |
---|
470 | uint16_t off; /* data offset */ |
---|
471 | uint16_t rem; /* data remainder */ |
---|
472 | uint16_t max_len; /* max fragment length */ |
---|
473 | uint16_t wValue; |
---|
474 | uint8_t state; |
---|
475 | uint8_t is_complete = 1; |
---|
476 | usb_error_t err; |
---|
477 | union { |
---|
478 | uWord wStatus; |
---|
479 | uint8_t buf[2]; |
---|
480 | } temp; |
---|
481 | |
---|
482 | /* |
---|
483 | * Filter the USB transfer state into |
---|
484 | * something which we understand: |
---|
485 | */ |
---|
486 | |
---|
487 | switch (USB_GET_STATE(xfer)) { |
---|
488 | case USB_ST_SETUP: |
---|
489 | state = USB_HR_NOT_COMPLETE; |
---|
490 | |
---|
491 | if (!xfer->flags_int.control_act) { |
---|
492 | /* nothing to do */ |
---|
493 | goto tr_stalled; |
---|
494 | } |
---|
495 | break; |
---|
496 | case USB_ST_TRANSFERRED: |
---|
497 | if (!xfer->flags_int.control_act) { |
---|
498 | state = USB_HR_COMPLETE_OK; |
---|
499 | } else { |
---|
500 | state = USB_HR_NOT_COMPLETE; |
---|
501 | } |
---|
502 | break; |
---|
503 | default: |
---|
504 | state = USB_HR_COMPLETE_ERR; |
---|
505 | break; |
---|
506 | } |
---|
507 | |
---|
508 | /* reset frame stuff */ |
---|
509 | |
---|
510 | usbd_xfer_set_frame_len(xfer, 0, 0); |
---|
511 | |
---|
512 | usbd_xfer_set_frame_offset(xfer, 0, 0); |
---|
513 | usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); |
---|
514 | |
---|
515 | /* get the current request, if any */ |
---|
516 | |
---|
517 | usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); |
---|
518 | |
---|
519 | if (xfer->flags_int.control_rem == 0xFFFF) { |
---|
520 | /* first time - not initialised */ |
---|
521 | rem = UGETW(req.wLength); |
---|
522 | off = 0; |
---|
523 | } else { |
---|
524 | /* not first time - initialised */ |
---|
525 | rem = xfer->flags_int.control_rem; |
---|
526 | off = UGETW(req.wLength) - rem; |
---|
527 | } |
---|
528 | |
---|
529 | /* set some defaults */ |
---|
530 | |
---|
531 | max_len = 0; |
---|
532 | src_zcopy = NULL; |
---|
533 | src_mcopy = NULL; |
---|
534 | udev = xfer->xroot->udev; |
---|
535 | |
---|
536 | /* get some request fields decoded */ |
---|
537 | |
---|
538 | wValue = UGETW(req.wValue); |
---|
539 | |
---|
540 | DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " |
---|
541 | "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, |
---|
542 | req.bRequest, wValue, UGETW(req.wIndex), off, rem, state); |
---|
543 | |
---|
544 | /* demultiplex the control request */ |
---|
545 | |
---|
546 | switch (req.bmRequestType) { |
---|
547 | case UT_READ_DEVICE: |
---|
548 | if (state != USB_HR_NOT_COMPLETE) { |
---|
549 | break; |
---|
550 | } |
---|
551 | switch (req.bRequest) { |
---|
552 | case UR_GET_DESCRIPTOR: |
---|
553 | goto tr_handle_get_descriptor; |
---|
554 | case UR_GET_CONFIG: |
---|
555 | goto tr_handle_get_config; |
---|
556 | case UR_GET_STATUS: |
---|
557 | goto tr_handle_get_status; |
---|
558 | default: |
---|
559 | goto tr_stalled; |
---|
560 | } |
---|
561 | break; |
---|
562 | |
---|
563 | case UT_WRITE_DEVICE: |
---|
564 | switch (req.bRequest) { |
---|
565 | case UR_SET_ADDRESS: |
---|
566 | goto tr_handle_set_address; |
---|
567 | case UR_SET_CONFIG: |
---|
568 | goto tr_handle_set_config; |
---|
569 | case UR_CLEAR_FEATURE: |
---|
570 | switch (wValue) { |
---|
571 | case UF_DEVICE_REMOTE_WAKEUP: |
---|
572 | goto tr_handle_clear_wakeup; |
---|
573 | default: |
---|
574 | goto tr_stalled; |
---|
575 | } |
---|
576 | break; |
---|
577 | case UR_SET_FEATURE: |
---|
578 | switch (wValue) { |
---|
579 | case UF_DEVICE_REMOTE_WAKEUP: |
---|
580 | goto tr_handle_set_wakeup; |
---|
581 | default: |
---|
582 | goto tr_stalled; |
---|
583 | } |
---|
584 | break; |
---|
585 | default: |
---|
586 | goto tr_stalled; |
---|
587 | } |
---|
588 | break; |
---|
589 | |
---|
590 | case UT_WRITE_ENDPOINT: |
---|
591 | switch (req.bRequest) { |
---|
592 | case UR_CLEAR_FEATURE: |
---|
593 | switch (wValue) { |
---|
594 | case UF_ENDPOINT_HALT: |
---|
595 | goto tr_handle_clear_halt; |
---|
596 | default: |
---|
597 | goto tr_stalled; |
---|
598 | } |
---|
599 | break; |
---|
600 | case UR_SET_FEATURE: |
---|
601 | switch (wValue) { |
---|
602 | case UF_ENDPOINT_HALT: |
---|
603 | goto tr_handle_set_halt; |
---|
604 | default: |
---|
605 | goto tr_stalled; |
---|
606 | } |
---|
607 | break; |
---|
608 | default: |
---|
609 | goto tr_stalled; |
---|
610 | } |
---|
611 | break; |
---|
612 | |
---|
613 | case UT_READ_ENDPOINT: |
---|
614 | switch (req.bRequest) { |
---|
615 | case UR_GET_STATUS: |
---|
616 | goto tr_handle_get_ep_status; |
---|
617 | default: |
---|
618 | goto tr_stalled; |
---|
619 | } |
---|
620 | break; |
---|
621 | default: |
---|
622 | /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ |
---|
623 | err = usb_handle_iface_request(xfer, |
---|
624 | USB_ADD_BYTES(&src_zcopy, 0), |
---|
625 | &max_len, req, off, state); |
---|
626 | if (err == 0) { |
---|
627 | is_complete = 0; |
---|
628 | goto tr_valid; |
---|
629 | } else if (err == USB_ERR_SHORT_XFER) { |
---|
630 | goto tr_valid; |
---|
631 | } |
---|
632 | /* |
---|
633 | * Reset zero-copy pointer and max length |
---|
634 | * variable in case they were unintentionally |
---|
635 | * set: |
---|
636 | */ |
---|
637 | src_zcopy = NULL; |
---|
638 | max_len = 0; |
---|
639 | |
---|
640 | /* |
---|
641 | * Check if we have a vendor specific |
---|
642 | * descriptor: |
---|
643 | */ |
---|
644 | goto tr_handle_get_descriptor; |
---|
645 | } |
---|
646 | goto tr_valid; |
---|
647 | |
---|
648 | tr_handle_get_descriptor: |
---|
649 | err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); |
---|
650 | if (err) |
---|
651 | goto tr_stalled; |
---|
652 | if (src_zcopy == NULL) |
---|
653 | goto tr_stalled; |
---|
654 | goto tr_valid; |
---|
655 | |
---|
656 | tr_handle_get_config: |
---|
657 | temp.buf[0] = udev->curr_config_no; |
---|
658 | src_mcopy = temp.buf; |
---|
659 | max_len = 1; |
---|
660 | goto tr_valid; |
---|
661 | |
---|
662 | tr_handle_get_status: |
---|
663 | |
---|
664 | wValue = 0; |
---|
665 | |
---|
666 | USB_BUS_LOCK(udev->bus); |
---|
667 | if (udev->flags.remote_wakeup) { |
---|
668 | wValue |= UDS_REMOTE_WAKEUP; |
---|
669 | } |
---|
670 | if (udev->flags.self_powered) { |
---|
671 | wValue |= UDS_SELF_POWERED; |
---|
672 | } |
---|
673 | USB_BUS_UNLOCK(udev->bus); |
---|
674 | |
---|
675 | USETW(temp.wStatus, wValue); |
---|
676 | src_mcopy = temp.wStatus; |
---|
677 | max_len = sizeof(temp.wStatus); |
---|
678 | goto tr_valid; |
---|
679 | |
---|
680 | tr_handle_set_address: |
---|
681 | if (state == USB_HR_NOT_COMPLETE) { |
---|
682 | if (wValue >= 0x80) { |
---|
683 | /* invalid value */ |
---|
684 | goto tr_stalled; |
---|
685 | } else if (udev->curr_config_no != 0) { |
---|
686 | /* we are configured ! */ |
---|
687 | goto tr_stalled; |
---|
688 | } |
---|
689 | } else if (state != USB_HR_NOT_COMPLETE) { |
---|
690 | udev->address = (wValue & 0x7F); |
---|
691 | goto tr_bad_context; |
---|
692 | } |
---|
693 | goto tr_valid; |
---|
694 | |
---|
695 | tr_handle_set_config: |
---|
696 | if (state == USB_HR_NOT_COMPLETE) { |
---|
697 | if (usb_handle_set_config(xfer, req.wValue[0])) { |
---|
698 | goto tr_stalled; |
---|
699 | } |
---|
700 | } |
---|
701 | goto tr_valid; |
---|
702 | |
---|
703 | tr_handle_clear_halt: |
---|
704 | if (state == USB_HR_NOT_COMPLETE) { |
---|
705 | if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { |
---|
706 | goto tr_stalled; |
---|
707 | } |
---|
708 | } |
---|
709 | goto tr_valid; |
---|
710 | |
---|
711 | tr_handle_clear_wakeup: |
---|
712 | if (state == USB_HR_NOT_COMPLETE) { |
---|
713 | if (usb_handle_remote_wakeup(xfer, 0)) { |
---|
714 | goto tr_stalled; |
---|
715 | } |
---|
716 | } |
---|
717 | goto tr_valid; |
---|
718 | |
---|
719 | tr_handle_set_halt: |
---|
720 | if (state == USB_HR_NOT_COMPLETE) { |
---|
721 | if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { |
---|
722 | goto tr_stalled; |
---|
723 | } |
---|
724 | } |
---|
725 | goto tr_valid; |
---|
726 | |
---|
727 | tr_handle_set_wakeup: |
---|
728 | if (state == USB_HR_NOT_COMPLETE) { |
---|
729 | if (usb_handle_remote_wakeup(xfer, 1)) { |
---|
730 | goto tr_stalled; |
---|
731 | } |
---|
732 | } |
---|
733 | goto tr_valid; |
---|
734 | |
---|
735 | tr_handle_get_ep_status: |
---|
736 | if (state == USB_HR_NOT_COMPLETE) { |
---|
737 | temp.wStatus[0] = |
---|
738 | usb_handle_get_stall(udev, req.wIndex[0]); |
---|
739 | temp.wStatus[1] = 0; |
---|
740 | src_mcopy = temp.wStatus; |
---|
741 | max_len = sizeof(temp.wStatus); |
---|
742 | } |
---|
743 | goto tr_valid; |
---|
744 | |
---|
745 | tr_valid: |
---|
746 | if (state != USB_HR_NOT_COMPLETE) { |
---|
747 | goto tr_stalled; |
---|
748 | } |
---|
749 | /* subtract offset from length */ |
---|
750 | |
---|
751 | max_len -= off; |
---|
752 | |
---|
753 | /* Compute the real maximum data length */ |
---|
754 | |
---|
755 | if (max_len > xfer->max_data_length) { |
---|
756 | max_len = usbd_xfer_max_len(xfer); |
---|
757 | } |
---|
758 | if (max_len > rem) { |
---|
759 | max_len = rem; |
---|
760 | } |
---|
761 | /* |
---|
762 | * If the remainder is greater than the maximum data length, |
---|
763 | * we need to truncate the value for the sake of the |
---|
764 | * comparison below: |
---|
765 | */ |
---|
766 | if (rem > xfer->max_data_length) { |
---|
767 | rem = usbd_xfer_max_len(xfer); |
---|
768 | } |
---|
769 | if ((rem != max_len) && (is_complete != 0)) { |
---|
770 | /* |
---|
771 | * If we don't transfer the data we can transfer, then |
---|
772 | * the transfer is short ! |
---|
773 | */ |
---|
774 | xfer->flags.force_short_xfer = 1; |
---|
775 | xfer->nframes = 2; |
---|
776 | } else { |
---|
777 | /* |
---|
778 | * Default case |
---|
779 | */ |
---|
780 | xfer->flags.force_short_xfer = 0; |
---|
781 | xfer->nframes = max_len ? 2 : 1; |
---|
782 | } |
---|
783 | if (max_len > 0) { |
---|
784 | if (src_mcopy) { |
---|
785 | src_mcopy = USB_ADD_BYTES(src_mcopy, off); |
---|
786 | usbd_copy_in(xfer->frbuffers + 1, 0, |
---|
787 | src_mcopy, max_len); |
---|
788 | usbd_xfer_set_frame_len(xfer, 1, max_len); |
---|
789 | } else { |
---|
790 | usbd_xfer_set_frame_data(xfer, 1, |
---|
791 | USB_ADD_BYTES(src_zcopy, off), max_len); |
---|
792 | } |
---|
793 | } else { |
---|
794 | /* the end is reached, send status */ |
---|
795 | xfer->flags.manual_status = 0; |
---|
796 | usbd_xfer_set_frame_len(xfer, 1, 0); |
---|
797 | } |
---|
798 | DPRINTF("success\n"); |
---|
799 | return (0); /* success */ |
---|
800 | |
---|
801 | tr_stalled: |
---|
802 | DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? |
---|
803 | "complete" : "stalled"); |
---|
804 | return (USB_ERR_STALLED); |
---|
805 | |
---|
806 | tr_bad_context: |
---|
807 | DPRINTF("bad context\n"); |
---|
808 | return (USB_ERR_BAD_CONTEXT); |
---|
809 | } |
---|