102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 402ac6454SAndrew Thompson * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 502ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 1602ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1702ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1802ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1902ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2002ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2102ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2202ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2302ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2402ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2502ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2602ac6454SAndrew Thompson * SUCH DAMAGE. 2702ac6454SAndrew Thompson */ 2802ac6454SAndrew Thompson 29ed6d949aSAndrew Thompson #include <sys/stdint.h> 30ed6d949aSAndrew Thompson #include <sys/stddef.h> 31ed6d949aSAndrew Thompson #include <sys/param.h> 32ed6d949aSAndrew Thompson #include <sys/queue.h> 33ed6d949aSAndrew Thompson #include <sys/types.h> 34ed6d949aSAndrew Thompson #include <sys/systm.h> 35ed6d949aSAndrew Thompson #include <sys/kernel.h> 36ed6d949aSAndrew Thompson #include <sys/bus.h> 37ed6d949aSAndrew Thompson #include <sys/module.h> 38ed6d949aSAndrew Thompson #include <sys/lock.h> 39ed6d949aSAndrew Thompson #include <sys/mutex.h> 40ed6d949aSAndrew Thompson #include <sys/condvar.h> 41ed6d949aSAndrew Thompson #include <sys/sysctl.h> 42ed6d949aSAndrew Thompson #include <sys/sx.h> 43ed6d949aSAndrew Thompson #include <sys/unistd.h> 44ed6d949aSAndrew Thompson #include <sys/callout.h> 45ed6d949aSAndrew Thompson #include <sys/malloc.h> 46ed6d949aSAndrew Thompson #include <sys/priv.h> 47ed6d949aSAndrew Thompson 4802ac6454SAndrew Thompson #include <dev/usb/usb.h> 49ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 50ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5102ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 5202ac6454SAndrew Thompson #include <dev/usb/usbhid.h> 5302ac6454SAndrew Thompson 54a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5502ac6454SAndrew Thompson 5602ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6502ac6454SAndrew Thompson 6602ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6802ac6454SAndrew Thompson #include <sys/ctype.h> 6902ac6454SAndrew Thompson 70b850ecc1SAndrew Thompson #ifdef USB_DEBUG 71a593f6b8SAndrew Thompson static int usb_pr_poll_delay = USB_PORT_RESET_DELAY; 72a593f6b8SAndrew Thompson static int usb_pr_recovery_delay = USB_PORT_RESET_RECOVERY; 7302ac6454SAndrew Thompson 749360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW, 75a593f6b8SAndrew Thompson &usb_pr_poll_delay, 0, "USB port reset poll delay in ms"); 769360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW, 77a593f6b8SAndrew Thompson &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms"); 78f6980be8SAndrew Thompson 79f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 80f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */ 81f6980be8SAndrew Thompson struct usb_ctrl_debug { 82f6980be8SAndrew Thompson int bus_index; /* target bus */ 83f6980be8SAndrew Thompson int dev_index; /* target address */ 84f6980be8SAndrew Thompson int ds_fail; /* fail data stage */ 85f6980be8SAndrew Thompson int ss_fail; /* fail data stage */ 86f6980be8SAndrew Thompson int ds_delay; /* data stage delay in ms */ 87f6980be8SAndrew Thompson int ss_delay; /* status stage delay in ms */ 88f6980be8SAndrew Thompson int bmRequestType_value; 89f6980be8SAndrew Thompson int bRequest_value; 90f6980be8SAndrew Thompson }; 91f6980be8SAndrew Thompson 92f6980be8SAndrew Thompson struct usb_ctrl_debug_bits { 93f6980be8SAndrew Thompson uint16_t ds_delay; 94f6980be8SAndrew Thompson uint16_t ss_delay; 95f6980be8SAndrew Thompson uint8_t ds_fail:1; 96f6980be8SAndrew Thompson uint8_t ss_fail:1; 97f6980be8SAndrew Thompson uint8_t enabled:1; 98f6980be8SAndrew Thompson }; 99f6980be8SAndrew Thompson 100f6980be8SAndrew Thompson /* The default is to disable fault injection. */ 101f6980be8SAndrew Thompson 102f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = { 103f6980be8SAndrew Thompson .bus_index = -1, 104f6980be8SAndrew Thompson .dev_index = -1, 105f6980be8SAndrew Thompson .bmRequestType_value = -1, 106f6980be8SAndrew Thompson .bRequest_value = -1, 107f6980be8SAndrew Thompson }; 108f6980be8SAndrew Thompson 109f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RW, 110f6980be8SAndrew Thompson &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); 111f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RW, 112f6980be8SAndrew Thompson &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); 113f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RW, 114f6980be8SAndrew Thompson &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); 115f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RW, 116f6980be8SAndrew Thompson &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); 117f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RW, 118f6980be8SAndrew Thompson &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); 119f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RW, 120f6980be8SAndrew Thompson &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); 121f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RW, 122f6980be8SAndrew Thompson &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); 123f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RW, 124f6980be8SAndrew Thompson &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); 125f6980be8SAndrew Thompson 126f6980be8SAndrew Thompson /*------------------------------------------------------------------------* 127f6980be8SAndrew Thompson * usbd_get_debug_bits 128f6980be8SAndrew Thompson * 129f6980be8SAndrew Thompson * This function is only useful in USB host mode. 130f6980be8SAndrew Thompson *------------------------------------------------------------------------*/ 131f6980be8SAndrew Thompson static void 132f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, 133f6980be8SAndrew Thompson struct usb_ctrl_debug_bits *dbg) 134f6980be8SAndrew Thompson { 135f6980be8SAndrew Thompson int temp; 136f6980be8SAndrew Thompson 137f6980be8SAndrew Thompson memset(dbg, 0, sizeof(*dbg)); 138f6980be8SAndrew Thompson 139f6980be8SAndrew Thompson /* Compute data stage delay */ 140f6980be8SAndrew Thompson 141f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_delay; 142f6980be8SAndrew Thompson if (temp < 0) 143f6980be8SAndrew Thompson temp = 0; 144f6980be8SAndrew Thompson else if (temp > (16*1024)) 145f6980be8SAndrew Thompson temp = (16*1024); 146f6980be8SAndrew Thompson 147f6980be8SAndrew Thompson dbg->ds_delay = temp; 148f6980be8SAndrew Thompson 149f6980be8SAndrew Thompson /* Compute status stage delay */ 150f6980be8SAndrew Thompson 151f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_delay; 152f6980be8SAndrew Thompson if (temp < 0) 153f6980be8SAndrew Thompson temp = 0; 154f6980be8SAndrew Thompson else if (temp > (16*1024)) 155f6980be8SAndrew Thompson temp = (16*1024); 156f6980be8SAndrew Thompson 157f6980be8SAndrew Thompson dbg->ss_delay = temp; 158f6980be8SAndrew Thompson 159f6980be8SAndrew Thompson /* Check if this control request should be failed */ 160f6980be8SAndrew Thompson 161f6980be8SAndrew Thompson if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) 162f6980be8SAndrew Thompson return; 163f6980be8SAndrew Thompson 164f6980be8SAndrew Thompson if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) 165f6980be8SAndrew Thompson return; 166f6980be8SAndrew Thompson 167f6980be8SAndrew Thompson temp = usb_ctrl_debug.bmRequestType_value; 168f6980be8SAndrew Thompson 169f6980be8SAndrew Thompson if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) 170f6980be8SAndrew Thompson return; 171f6980be8SAndrew Thompson 172f6980be8SAndrew Thompson temp = usb_ctrl_debug.bRequest_value; 173f6980be8SAndrew Thompson 174f6980be8SAndrew Thompson if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) 175f6980be8SAndrew Thompson return; 176f6980be8SAndrew Thompson 177f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_fail; 178f6980be8SAndrew Thompson if (temp) 179f6980be8SAndrew Thompson dbg->ds_fail = 1; 180f6980be8SAndrew Thompson 181f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_fail; 182f6980be8SAndrew Thompson if (temp) 183f6980be8SAndrew Thompson dbg->ss_fail = 1; 184f6980be8SAndrew Thompson 185f6980be8SAndrew Thompson dbg->enabled = 1; 186f6980be8SAndrew Thompson } 187f6980be8SAndrew Thompson #endif /* USB_REQ_DEBUG */ 188f6980be8SAndrew Thompson #endif /* USB_DEBUG */ 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 191a593f6b8SAndrew Thompson * usbd_do_request_callback 19202ac6454SAndrew Thompson * 19302ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control 19402ac6454SAndrew Thompson * transfers. 19502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19602ac6454SAndrew Thompson void 197ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) 19802ac6454SAndrew Thompson { 19902ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 20002ac6454SAndrew Thompson 20102ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer)); 20202ac6454SAndrew Thompson 20302ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 20402ac6454SAndrew Thompson case USB_ST_SETUP: 205a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 20602ac6454SAndrew Thompson break; 20702ac6454SAndrew Thompson default: 20891cd9240SAndrew Thompson cv_signal(&xfer->xroot->udev->ctrlreq_cv); 20902ac6454SAndrew Thompson break; 21002ac6454SAndrew Thompson } 21102ac6454SAndrew Thompson } 21202ac6454SAndrew Thompson 21302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 214a593f6b8SAndrew Thompson * usb_do_clear_stall_callback 21502ac6454SAndrew Thompson * 21602ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests. 21702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 21802ac6454SAndrew Thompson void 219ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 22002ac6454SAndrew Thompson { 221760bc48eSAndrew Thompson struct usb_device_request req; 222760bc48eSAndrew Thompson struct usb_device *udev; 223ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 224ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end; 225ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first; 22663521bbcSAndrew Thompson uint8_t to; 22702ac6454SAndrew Thompson 22802ac6454SAndrew Thompson udev = xfer->xroot->udev; 22902ac6454SAndrew Thompson 23002ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 23102ac6454SAndrew Thompson 232ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */ 23302ac6454SAndrew Thompson 234ae60fdfbSAndrew Thompson ep = udev->ep_curr; 235ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max; 236ae60fdfbSAndrew Thompson ep_first = udev->endpoints; 237ae60fdfbSAndrew Thompson to = udev->endpoints_max; 238115df0b6SAndrew Thompson 23902ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 24002ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 2419eb0d702SHans Petter Selasky 2429eb0d702SHans Petter Selasky /* reset error counter */ 2439eb0d702SHans Petter Selasky udev->clear_stall_errors = 0; 2449eb0d702SHans Petter Selasky 245ae60fdfbSAndrew Thompson if (ep == NULL) 246115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */ 247ae60fdfbSAndrew Thompson if (ep->edesc && 248ae60fdfbSAndrew Thompson ep->is_stalled) { 249ae60fdfbSAndrew Thompson ep->toggle_next = 0; 250ae60fdfbSAndrew Thompson ep->is_stalled = 0; 251963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */ 252963169b4SHans Petter Selasky usbd_clear_stall_locked(udev, ep); 25302ac6454SAndrew Thompson /* start up the current or next transfer, if any */ 254a593f6b8SAndrew Thompson usb_command_wrapper(&ep->endpoint_q, 255ae60fdfbSAndrew Thompson ep->endpoint_q.curr); 25602ac6454SAndrew Thompson } 257ae60fdfbSAndrew Thompson ep++; 25802ac6454SAndrew Thompson 25902ac6454SAndrew Thompson case USB_ST_SETUP: 26002ac6454SAndrew Thompson tr_setup: 261115df0b6SAndrew Thompson if (to == 0) 262ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */ 263ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end)) 264ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */ 265ae60fdfbSAndrew Thompson if (ep->edesc && 266ae60fdfbSAndrew Thompson ep->is_stalled) { 26702ac6454SAndrew Thompson 26802ac6454SAndrew Thompson /* setup a clear-stall packet */ 26902ac6454SAndrew Thompson 27002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 27102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 27202ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 273ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 27402ac6454SAndrew Thompson req.wIndex[1] = 0; 27502ac6454SAndrew Thompson USETW(req.wLength, 0); 27602ac6454SAndrew Thompson 27702ac6454SAndrew Thompson /* copy in the transfer */ 27802ac6454SAndrew Thompson 279a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 28002ac6454SAndrew Thompson 28102ac6454SAndrew Thompson /* set length */ 282ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 28302ac6454SAndrew Thompson xfer->nframes = 1; 28402ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 28502ac6454SAndrew Thompson 286a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 28702ac6454SAndrew Thompson 28802ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 28902ac6454SAndrew Thompson break; 29002ac6454SAndrew Thompson } 291ae60fdfbSAndrew Thompson ep++; 292115df0b6SAndrew Thompson to--; 29302ac6454SAndrew Thompson goto tr_setup; 29402ac6454SAndrew Thompson 29502ac6454SAndrew Thompson default: 2969eb0d702SHans Petter Selasky if (error == USB_ERR_CANCELLED) 29702ac6454SAndrew Thompson break; 2989eb0d702SHans Petter Selasky 2999eb0d702SHans Petter Selasky DPRINTF("Clear stall failed.\n"); 3009eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) 3019eb0d702SHans Petter Selasky goto tr_setup; 3029eb0d702SHans Petter Selasky 3039eb0d702SHans Petter Selasky if (error == USB_ERR_TIMEOUT) { 3049eb0d702SHans Petter Selasky udev->clear_stall_errors = USB_CS_RESET_LIMIT; 3059eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3069eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3079eb0d702SHans Petter Selasky } else { 3089eb0d702SHans Petter Selasky udev->clear_stall_errors++; 3099eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { 3109eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3119eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3129eb0d702SHans Petter Selasky } 31302ac6454SAndrew Thompson } 31402ac6454SAndrew Thompson goto tr_setup; 31502ac6454SAndrew Thompson } 31602ac6454SAndrew Thompson 317ae60fdfbSAndrew Thompson /* store current endpoint */ 318ae60fdfbSAndrew Thompson udev->ep_curr = ep; 31902ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 32002ac6454SAndrew Thompson } 32102ac6454SAndrew Thompson 322e0a69b51SAndrew Thompson static usb_handle_req_t * 323a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev) 324459d369eSAndrew Thompson { 325459d369eSAndrew Thompson /* figure out if there is a Handle Request function */ 326f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE) 327a593f6b8SAndrew Thompson return (usb_temp_get_desc_p); 328459d369eSAndrew Thompson else if (udev->parent_hub == NULL) 329459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec); 330459d369eSAndrew Thompson else 331459d369eSAndrew Thompson return (NULL); 332459d369eSAndrew Thompson } 333459d369eSAndrew Thompson 33402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 335a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request 33602ac6454SAndrew Thompson * 33702ac6454SAndrew Thompson * Description of arguments passed to these functions: 33802ac6454SAndrew Thompson * 339760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the 34002ac6454SAndrew Thompson * request should be performed. It is possible to call this function 34102ac6454SAndrew Thompson * in both Host Side mode and Device Side mode. 34202ac6454SAndrew Thompson * 34302ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it 34402ac6454SAndrew Thompson * will get dropped and picked up during the execution of this 34502ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this 34602ac6454SAndrew Thompson * argument is NULL it has no effect. 34702ac6454SAndrew Thompson * 34802ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an 34902ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB 35002ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB 35102ac6454SAndrew Thompson * request, if it is a read or a write. 35202ac6454SAndrew Thompson * 35302ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req" 35402ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which 35502ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can 35602ac6454SAndrew Thompson * be NULL. 35702ac6454SAndrew Thompson * 35802ac6454SAndrew Thompson * "flags" - here is a list of valid flags: 35902ac6454SAndrew Thompson * 36002ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than 36102ac6454SAndrew Thompson * specified 36202ac6454SAndrew Thompson * 36302ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed 36402ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay" 36502ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging. 36602ac6454SAndrew Thompson * 36702ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland 36802ac6454SAndrew Thompson * pointer. 36902ac6454SAndrew Thompson * 37002ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in 37102ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This 37202ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is 37302ac6454SAndrew Thompson * used. 37402ac6454SAndrew Thompson * 37502ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in 37602ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is 37702ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater 37802ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack 37902ac6454SAndrew Thompson * does not allow control requests without a timeout. 38002ac6454SAndrew Thompson * 38102ac6454SAndrew Thompson * NOTE: This function is thread safe. All calls to 382a593f6b8SAndrew Thompson * "usbd_do_request_flags" will be serialised by the use of an 38302ac6454SAndrew Thompson * internal "sx_lock". 38402ac6454SAndrew Thompson * 38502ac6454SAndrew Thompson * Returns: 38602ac6454SAndrew Thompson * 0: Success 38702ac6454SAndrew Thompson * Else: Failure 38802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 389e0a69b51SAndrew Thompson usb_error_t 390a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, 391760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 392e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 39302ac6454SAndrew Thompson { 394f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 395f6980be8SAndrew Thompson struct usb_ctrl_debug_bits dbg; 396f6980be8SAndrew Thompson #endif 397e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 398760bc48eSAndrew Thompson struct usb_xfer *xfer; 39902ac6454SAndrew Thompson const void *desc; 40002ac6454SAndrew Thompson int err = 0; 401e0a69b51SAndrew Thompson usb_ticks_t start_ticks; 402e0a69b51SAndrew Thompson usb_ticks_t delta_ticks; 403e0a69b51SAndrew Thompson usb_ticks_t max_ticks; 40402ac6454SAndrew Thompson uint16_t length; 40502ac6454SAndrew Thompson uint16_t temp; 406f6980be8SAndrew Thompson uint16_t acttemp; 4072df1e9a6SAndrew Thompson uint8_t enum_locked; 40802ac6454SAndrew Thompson 40902ac6454SAndrew Thompson if (timeout < 50) { 41002ac6454SAndrew Thompson /* timeout is too small */ 41102ac6454SAndrew Thompson timeout = 50; 41202ac6454SAndrew Thompson } 41302ac6454SAndrew Thompson if (timeout > 30000) { 41402ac6454SAndrew Thompson /* timeout is too big */ 41502ac6454SAndrew Thompson timeout = 30000; 41602ac6454SAndrew Thompson } 41702ac6454SAndrew Thompson length = UGETW(req->wLength); 41802ac6454SAndrew Thompson 4192df1e9a6SAndrew Thompson enum_locked = usbd_enum_is_locked(udev); 4202df1e9a6SAndrew Thompson 42102ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " 42202ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", 42302ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest, 42402ac6454SAndrew Thompson req->wValue[1], req->wValue[0], 42502ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0], 42602ac6454SAndrew Thompson req->wLength[1], req->wLength[0]); 42702ac6454SAndrew Thompson 428bd216778SAndrew Thompson /* Check if the device is still alive */ 429bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) { 430bd216778SAndrew Thompson DPRINTF("usb device has gone\n"); 431bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED); 432bd216778SAndrew Thompson } 433bd216778SAndrew Thompson 43402ac6454SAndrew Thompson /* 43502ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not 43602ac6454SAndrew Thompson * check the return value: 43702ac6454SAndrew Thompson */ 43839307315SAndrew Thompson if (actlen) 43902ac6454SAndrew Thompson *actlen = 0; 44039307315SAndrew Thompson 441bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0) 442bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR) 443bdc081c6SAndrew Thompson return (USB_ERR_INVAL); 444bdc081c6SAndrew Thompson #endif 4452df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) { 44602ac6454SAndrew Thompson mtx_unlock(mtx); 44702ac6454SAndrew Thompson mtx_assert(mtx, MA_NOTOWNED); 44802ac6454SAndrew Thompson } 4492df1e9a6SAndrew Thompson 4502df1e9a6SAndrew Thompson /* 4512df1e9a6SAndrew Thompson * We need to allow suspend and resume at this point, else the 4522df1e9a6SAndrew Thompson * control transfer will timeout if the device is suspended! 4532df1e9a6SAndrew Thompson */ 4542df1e9a6SAndrew Thompson if (enum_locked) 4552df1e9a6SAndrew Thompson usbd_sr_unlock(udev); 4562df1e9a6SAndrew Thompson 45702ac6454SAndrew Thompson /* 45802ac6454SAndrew Thompson * Grab the default sx-lock so that serialisation 45902ac6454SAndrew Thompson * is achieved when multiple threads are involved: 46002ac6454SAndrew Thompson */ 46191cd9240SAndrew Thompson sx_xlock(&udev->ctrl_sx); 46202ac6454SAndrew Thompson 463a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 46439307315SAndrew Thompson 465459d369eSAndrew Thompson if (hr_func != NULL) { 466459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n"); 46739307315SAndrew Thompson 468459d369eSAndrew Thompson desc = NULL; 469459d369eSAndrew Thompson temp = 0; 470459d369eSAndrew Thompson 471459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 47239307315SAndrew Thompson if (length != 0) { 473459d369eSAndrew Thompson DPRINTFN(1, "The handle request function " 474459d369eSAndrew Thompson "does not support writing data!\n"); 47539307315SAndrew Thompson err = USB_ERR_INVAL; 47639307315SAndrew Thompson goto done; 47739307315SAndrew Thompson } 47839307315SAndrew Thompson } 479459d369eSAndrew Thompson 480459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */ 48139307315SAndrew Thompson 48239307315SAndrew Thompson USB_BUS_LOCK(udev->bus); 483459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp); 48439307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 48539307315SAndrew Thompson 48639307315SAndrew Thompson if (err) 48739307315SAndrew Thompson goto done; 48839307315SAndrew Thompson 489459d369eSAndrew Thompson if (length > temp) { 49039307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) { 49139307315SAndrew Thompson err = USB_ERR_SHORT_XFER; 49239307315SAndrew Thompson goto done; 49339307315SAndrew Thompson } 494459d369eSAndrew Thompson length = temp; 49539307315SAndrew Thompson } 49639307315SAndrew Thompson if (actlen) 49739307315SAndrew Thompson *actlen = length; 49839307315SAndrew Thompson 49939307315SAndrew Thompson if (length > 0) { 50039307315SAndrew Thompson #if USB_HAVE_USER_IO 50139307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 502459d369eSAndrew Thompson if (copyout(desc, data, length)) { 50339307315SAndrew Thompson err = USB_ERR_INVAL; 50439307315SAndrew Thompson goto done; 50539307315SAndrew Thompson } 50639307315SAndrew Thompson } else 50739307315SAndrew Thompson #endif 508459d369eSAndrew Thompson bcopy(desc, data, length); 50939307315SAndrew Thompson } 510459d369eSAndrew Thompson goto done; /* success */ 51139307315SAndrew Thompson } 51239307315SAndrew Thompson 51302ac6454SAndrew Thompson /* 51402ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any: 51502ac6454SAndrew Thompson */ 5165b3bb704SAndrew Thompson usbd_ctrl_transfer_setup(udev); 51702ac6454SAndrew Thompson 5185b3bb704SAndrew Thompson xfer = udev->ctrl_xfer[0]; 51902ac6454SAndrew Thompson if (xfer == NULL) { 52002ac6454SAndrew Thompson /* most likely out of memory */ 52102ac6454SAndrew Thompson err = USB_ERR_NOMEM; 52202ac6454SAndrew Thompson goto done; 52302ac6454SAndrew Thompson } 524f6980be8SAndrew Thompson 525f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 526f6980be8SAndrew Thompson /* Get debug bits */ 527f6980be8SAndrew Thompson usbd_get_debug_bits(udev, req, &dbg); 528f6980be8SAndrew Thompson 529f6980be8SAndrew Thompson /* Check for fault injection */ 530f6980be8SAndrew Thompson if (dbg.enabled) 531f6980be8SAndrew Thompson flags |= USB_DELAY_STATUS_STAGE; 532f6980be8SAndrew Thompson #endif 53302ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 53402ac6454SAndrew Thompson 5354eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE) 53602ac6454SAndrew Thompson xfer->flags.manual_status = 1; 5374eae601eSAndrew Thompson else 53802ac6454SAndrew Thompson xfer->flags.manual_status = 0; 5394eae601eSAndrew Thompson 5404eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK) 5414eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1; 5424eae601eSAndrew Thompson else 5434eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0; 54402ac6454SAndrew Thompson 54502ac6454SAndrew Thompson xfer->timeout = timeout; 54602ac6454SAndrew Thompson 54702ac6454SAndrew Thompson start_ticks = ticks; 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout); 55002ac6454SAndrew Thompson 551a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); 55202ac6454SAndrew Thompson 553ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); 55402ac6454SAndrew Thompson 55502ac6454SAndrew Thompson while (1) { 55602ac6454SAndrew Thompson temp = length; 557f6980be8SAndrew Thompson if (temp > usbd_xfer_max_len(xfer)) { 558ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer); 55902ac6454SAndrew Thompson } 560f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 561f6980be8SAndrew Thompson if (xfer->flags.manual_status) { 562f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) != 0) { 563f6980be8SAndrew Thompson /* Execute data stage separately */ 564f6980be8SAndrew Thompson temp = 0; 565f6980be8SAndrew Thompson } else if (temp > 0) { 566f6980be8SAndrew Thompson if (dbg.ds_fail) { 567f6980be8SAndrew Thompson err = USB_ERR_INVAL; 568f6980be8SAndrew Thompson break; 569f6980be8SAndrew Thompson } 570f6980be8SAndrew Thompson if (dbg.ds_delay > 0) { 571f6980be8SAndrew Thompson usb_pause_mtx( 572f6980be8SAndrew Thompson xfer->xroot->xfer_mtx, 573f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ds_delay)); 574f6980be8SAndrew Thompson /* make sure we don't time out */ 575f6980be8SAndrew Thompson start_ticks = ticks; 576f6980be8SAndrew Thompson } 577f6980be8SAndrew Thompson } 578f6980be8SAndrew Thompson } 579f6980be8SAndrew Thompson #endif 580ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp); 58102ac6454SAndrew Thompson 58202ac6454SAndrew Thompson if (temp > 0) { 58302ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 584bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 58502ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 58602ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 587a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1, 58802ac6454SAndrew Thompson 0, data, temp); 58902ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 59002ac6454SAndrew Thompson if (err) { 59102ac6454SAndrew Thompson err = USB_ERR_INVAL; 59202ac6454SAndrew Thompson break; 59302ac6454SAndrew Thompson } 594bdc081c6SAndrew Thompson } else 595bdc081c6SAndrew Thompson #endif 596a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 597bdc081c6SAndrew Thompson 0, data, temp); 59802ac6454SAndrew Thompson } 599f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 2); 60002ac6454SAndrew Thompson } else { 601f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) == 0) { 60202ac6454SAndrew Thompson if (xfer->flags.manual_status) { 603f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 604f6980be8SAndrew Thompson if (dbg.ss_fail) { 605f6980be8SAndrew Thompson err = USB_ERR_INVAL; 606f6980be8SAndrew Thompson break; 60702ac6454SAndrew Thompson } 608f6980be8SAndrew Thompson if (dbg.ss_delay > 0) { 609a593f6b8SAndrew Thompson usb_pause_mtx( 61002ac6454SAndrew Thompson xfer->xroot->xfer_mtx, 611f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ss_delay)); 612f6980be8SAndrew Thompson /* make sure we don't time out */ 613f6980be8SAndrew Thompson start_ticks = ticks; 61402ac6454SAndrew Thompson } 61502ac6454SAndrew Thompson #endif 61602ac6454SAndrew Thompson xfer->flags.manual_status = 0; 61702ac6454SAndrew Thompson } else { 61802ac6454SAndrew Thompson break; 61902ac6454SAndrew Thompson } 62002ac6454SAndrew Thompson } 621f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 1); 62202ac6454SAndrew Thompson } 62302ac6454SAndrew Thompson 624a593f6b8SAndrew Thompson usbd_transfer_start(xfer); 62502ac6454SAndrew Thompson 626a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) { 62791cd9240SAndrew Thompson cv_wait(&udev->ctrlreq_cv, 62802ac6454SAndrew Thompson xfer->xroot->xfer_mtx); 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson 63102ac6454SAndrew Thompson err = xfer->error; 63202ac6454SAndrew Thompson 63302ac6454SAndrew Thompson if (err) { 63402ac6454SAndrew Thompson break; 63502ac6454SAndrew Thompson } 63602ac6454SAndrew Thompson 637f6980be8SAndrew Thompson /* get actual length of DATA stage */ 638f6980be8SAndrew Thompson 639f6980be8SAndrew Thompson if (xfer->aframes < 2) { 640f6980be8SAndrew Thompson acttemp = 0; 64102ac6454SAndrew Thompson } else { 642f6980be8SAndrew Thompson acttemp = usbd_xfer_frame_len(xfer, 1); 64302ac6454SAndrew Thompson } 64402ac6454SAndrew Thompson 64502ac6454SAndrew Thompson /* check for short packet */ 64602ac6454SAndrew Thompson 647f6980be8SAndrew Thompson if (temp > acttemp) { 648f6980be8SAndrew Thompson temp = acttemp; 64902ac6454SAndrew Thompson length = temp; 65002ac6454SAndrew Thompson } 65102ac6454SAndrew Thompson if (temp > 0) { 65202ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) { 653bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 65402ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 65502ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 656a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1, 65702ac6454SAndrew Thompson 0, data, temp); 65802ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 65902ac6454SAndrew Thompson if (err) { 66002ac6454SAndrew Thompson err = USB_ERR_INVAL; 66102ac6454SAndrew Thompson break; 66202ac6454SAndrew Thompson } 663bdc081c6SAndrew Thompson } else 664bdc081c6SAndrew Thompson #endif 665a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1, 66602ac6454SAndrew Thompson 0, data, temp); 66702ac6454SAndrew Thompson } 66802ac6454SAndrew Thompson } 66902ac6454SAndrew Thompson /* 67002ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup 67102ac6454SAndrew Thompson * packet again: 67202ac6454SAndrew Thompson */ 673ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 67402ac6454SAndrew Thompson 67502ac6454SAndrew Thompson /* update length and data pointer */ 67602ac6454SAndrew Thompson length -= temp; 67702ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp); 67802ac6454SAndrew Thompson 67902ac6454SAndrew Thompson if (actlen) { 68002ac6454SAndrew Thompson (*actlen) += temp; 68102ac6454SAndrew Thompson } 68202ac6454SAndrew Thompson /* check for timeout */ 68302ac6454SAndrew Thompson 68402ac6454SAndrew Thompson delta_ticks = ticks - start_ticks; 68502ac6454SAndrew Thompson if (delta_ticks > max_ticks) { 68602ac6454SAndrew Thompson if (!err) { 68702ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 68802ac6454SAndrew Thompson } 68902ac6454SAndrew Thompson } 69002ac6454SAndrew Thompson if (err) { 69102ac6454SAndrew Thompson break; 69202ac6454SAndrew Thompson } 69302ac6454SAndrew Thompson } 69402ac6454SAndrew Thompson 69502ac6454SAndrew Thompson if (err) { 69602ac6454SAndrew Thompson /* 69702ac6454SAndrew Thompson * Make sure that the control endpoint is no longer 69802ac6454SAndrew Thompson * blocked in case of a non-transfer related error: 69902ac6454SAndrew Thompson */ 700a593f6b8SAndrew Thompson usbd_transfer_stop(xfer); 70102ac6454SAndrew Thompson } 70202ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 70302ac6454SAndrew Thompson 70402ac6454SAndrew Thompson done: 70591cd9240SAndrew Thompson sx_xunlock(&udev->ctrl_sx); 70602ac6454SAndrew Thompson 7072df1e9a6SAndrew Thompson if (enum_locked) 7082df1e9a6SAndrew Thompson usbd_sr_lock(udev); 7092df1e9a6SAndrew Thompson 7102df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) 71102ac6454SAndrew Thompson mtx_lock(mtx); 7122df1e9a6SAndrew Thompson 713e0a69b51SAndrew Thompson return ((usb_error_t)err); 71402ac6454SAndrew Thompson } 71502ac6454SAndrew Thompson 71602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 717a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code 71802ac6454SAndrew Thompson * 71902ac6454SAndrew Thompson * This function is factored out code. It does basically the same like 720a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the 72102ac6454SAndrew Thompson * passed process argument before doing the USB request. If the 72202ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It 72302ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked 72402ac6454SAndrew Thompson * when calling this function. 72502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 726e0a69b51SAndrew Thompson usb_error_t 727a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, 728760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 729e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 73002ac6454SAndrew Thompson { 731e0a69b51SAndrew Thompson usb_error_t err; 73202ac6454SAndrew Thompson uint16_t len; 73302ac6454SAndrew Thompson 73402ac6454SAndrew Thompson /* get request data length */ 73502ac6454SAndrew Thompson len = UGETW(req->wLength); 73602ac6454SAndrew Thompson 73702ac6454SAndrew Thompson /* check if the device is being detached */ 738a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) { 73902ac6454SAndrew Thompson err = USB_ERR_IOERROR; 74002ac6454SAndrew Thompson goto done; 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson 74302ac6454SAndrew Thompson /* forward the USB request */ 744a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx, 74502ac6454SAndrew Thompson req, data, flags, actlen, timeout); 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson done: 74802ac6454SAndrew Thompson /* on failure we zero the data */ 74902ac6454SAndrew Thompson /* on short packet we zero the unused data */ 75002ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { 75102ac6454SAndrew Thompson if (err) 75202ac6454SAndrew Thompson memset(data, 0, len); 75302ac6454SAndrew Thompson else if (actlen && *actlen != len) 75402ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen); 75502ac6454SAndrew Thompson } 75602ac6454SAndrew Thompson return (err); 75702ac6454SAndrew Thompson } 75802ac6454SAndrew Thompson 75902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 760a593f6b8SAndrew Thompson * usbd_req_reset_port 76102ac6454SAndrew Thompson * 762cbb75751SHans Petter Selasky * This function will instruct a USB HUB to perform a reset sequence 76302ac6454SAndrew Thompson * on the specified port number. 76402ac6454SAndrew Thompson * 76502ac6454SAndrew Thompson * Returns: 76602ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero. 76702ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be 76802ac6454SAndrew Thompson * disabled. 76902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 770e0a69b51SAndrew Thompson usb_error_t 771a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 77202ac6454SAndrew Thompson { 773760bc48eSAndrew Thompson struct usb_port_status ps; 774e0a69b51SAndrew Thompson usb_error_t err; 77502ac6454SAndrew Thompson uint16_t n; 77602ac6454SAndrew Thompson 777b850ecc1SAndrew Thompson #ifdef USB_DEBUG 77802ac6454SAndrew Thompson uint16_t pr_poll_delay; 77902ac6454SAndrew Thompson uint16_t pr_recovery_delay; 78002ac6454SAndrew Thompson 78102ac6454SAndrew Thompson #endif 782*f6bd103eSHans Petter Selasky /* clear any leftover port reset changes first */ 783*f6bd103eSHans Petter Selasky usbd_req_clear_port_feature( 784*f6bd103eSHans Petter Selasky udev, mtx, port, UHF_C_PORT_RESET); 785*f6bd103eSHans Petter Selasky 786*f6bd103eSHans Petter Selasky /* assert port reset on the given port */ 787*f6bd103eSHans Petter Selasky err = usbd_req_set_port_feature( 788*f6bd103eSHans Petter Selasky udev, mtx, port, UHF_PORT_RESET); 789*f6bd103eSHans Petter Selasky 790*f6bd103eSHans Petter Selasky /* check for errors */ 791*f6bd103eSHans Petter Selasky if (err) 79202ac6454SAndrew Thompson goto done; 793b850ecc1SAndrew Thompson #ifdef USB_DEBUG 79402ac6454SAndrew Thompson /* range check input parameters */ 795a593f6b8SAndrew Thompson pr_poll_delay = usb_pr_poll_delay; 79602ac6454SAndrew Thompson if (pr_poll_delay < 1) { 79702ac6454SAndrew Thompson pr_poll_delay = 1; 79802ac6454SAndrew Thompson } else if (pr_poll_delay > 1000) { 79902ac6454SAndrew Thompson pr_poll_delay = 1000; 80002ac6454SAndrew Thompson } 801a593f6b8SAndrew Thompson pr_recovery_delay = usb_pr_recovery_delay; 80202ac6454SAndrew Thompson if (pr_recovery_delay > 1000) { 80302ac6454SAndrew Thompson pr_recovery_delay = 1000; 80402ac6454SAndrew Thompson } 80502ac6454SAndrew Thompson #endif 80602ac6454SAndrew Thompson n = 0; 80702ac6454SAndrew Thompson while (1) { 808*f6bd103eSHans Petter Selasky uint16_t status; 809*f6bd103eSHans Petter Selasky uint16_t change; 810*f6bd103eSHans Petter Selasky 811b850ecc1SAndrew Thompson #ifdef USB_DEBUG 81202ac6454SAndrew Thompson /* wait for the device to recover from reset */ 813a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); 81402ac6454SAndrew Thompson n += pr_poll_delay; 81502ac6454SAndrew Thompson #else 81602ac6454SAndrew Thompson /* wait for the device to recover from reset */ 817a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); 81802ac6454SAndrew Thompson n += USB_PORT_RESET_DELAY; 81902ac6454SAndrew Thompson #endif 820a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port); 82102ac6454SAndrew Thompson if (err) { 82202ac6454SAndrew Thompson goto done; 82302ac6454SAndrew Thompson } 824*f6bd103eSHans Petter Selasky status = UGETW(ps.wPortStatus); 825*f6bd103eSHans Petter Selasky change = UGETW(ps.wPortChange); 826*f6bd103eSHans Petter Selasky 8270e777d84SHans Petter Selasky /* if the device disappeared, just give up */ 828*f6bd103eSHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS)) 8290e777d84SHans Petter Selasky goto done; 830*f6bd103eSHans Petter Selasky 83102ac6454SAndrew Thompson /* check if reset is complete */ 832*f6bd103eSHans Petter Selasky if (change & UPS_C_PORT_RESET) 83302ac6454SAndrew Thompson break; 834*f6bd103eSHans Petter Selasky 835*f6bd103eSHans Petter Selasky /* 836*f6bd103eSHans Petter Selasky * Some Virtual Machines like VirtualBox 4.x fail to 837*f6bd103eSHans Petter Selasky * generate a port reset change event. Check if reset 838*f6bd103eSHans Petter Selasky * is no longer asserted. 839*f6bd103eSHans Petter Selasky */ 840*f6bd103eSHans Petter Selasky if (!(status & UPS_RESET)) 841*f6bd103eSHans Petter Selasky break; 842*f6bd103eSHans Petter Selasky 84302ac6454SAndrew Thompson /* check for timeout */ 84402ac6454SAndrew Thompson if (n > 1000) { 84502ac6454SAndrew Thompson n = 0; 84602ac6454SAndrew Thompson break; 84702ac6454SAndrew Thompson } 84802ac6454SAndrew Thompson } 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson /* clear port reset first */ 851a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature( 85202ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET); 85302ac6454SAndrew Thompson if (err) { 85402ac6454SAndrew Thompson goto done; 85502ac6454SAndrew Thompson } 85602ac6454SAndrew Thompson /* check for timeout */ 85702ac6454SAndrew Thompson if (n == 0) { 85802ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 85902ac6454SAndrew Thompson goto done; 86002ac6454SAndrew Thompson } 861b850ecc1SAndrew Thompson #ifdef USB_DEBUG 86202ac6454SAndrew Thompson /* wait for the device to recover from reset */ 863a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); 86402ac6454SAndrew Thompson #else 86502ac6454SAndrew Thompson /* wait for the device to recover from reset */ 866a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); 86702ac6454SAndrew Thompson #endif 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson done: 87002ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n", 871a593f6b8SAndrew Thompson port, usbd_errstr(err)); 87202ac6454SAndrew Thompson return (err); 87302ac6454SAndrew Thompson } 87402ac6454SAndrew Thompson 87502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 876cbb75751SHans Petter Selasky * usbd_req_warm_reset_port 877cbb75751SHans Petter Selasky * 878cbb75751SHans Petter Selasky * This function will instruct an USB HUB to perform a warm reset 879cbb75751SHans Petter Selasky * sequence on the specified port number. This kind of reset is not 880cbb75751SHans Petter Selasky * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted 881cbb75751SHans Petter Selasky * for SUPER-speed USB HUBs. 882cbb75751SHans Petter Selasky * 883cbb75751SHans Petter Selasky * Returns: 884cbb75751SHans Petter Selasky * 0: Success. The USB device should now be available again. 885cbb75751SHans Petter Selasky * Else: Failure. No USB device is present and the USB port should be 886cbb75751SHans Petter Selasky * disabled. 887cbb75751SHans Petter Selasky *------------------------------------------------------------------------*/ 888cbb75751SHans Petter Selasky usb_error_t 889cbb75751SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 890cbb75751SHans Petter Selasky { 891cbb75751SHans Petter Selasky struct usb_port_status ps; 892cbb75751SHans Petter Selasky usb_error_t err; 893cbb75751SHans Petter Selasky uint16_t n; 894cbb75751SHans Petter Selasky 895cbb75751SHans Petter Selasky #ifdef USB_DEBUG 896cbb75751SHans Petter Selasky uint16_t pr_poll_delay; 897cbb75751SHans Petter Selasky uint16_t pr_recovery_delay; 898cbb75751SHans Petter Selasky 899cbb75751SHans Petter Selasky #endif 900cbb75751SHans Petter Selasky err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); 901cbb75751SHans Petter Selasky if (err) { 902cbb75751SHans Petter Selasky goto done; 903cbb75751SHans Petter Selasky } 904cbb75751SHans Petter Selasky #ifdef USB_DEBUG 905cbb75751SHans Petter Selasky /* range check input parameters */ 906cbb75751SHans Petter Selasky pr_poll_delay = usb_pr_poll_delay; 907cbb75751SHans Petter Selasky if (pr_poll_delay < 1) { 908cbb75751SHans Petter Selasky pr_poll_delay = 1; 909cbb75751SHans Petter Selasky } else if (pr_poll_delay > 1000) { 910cbb75751SHans Petter Selasky pr_poll_delay = 1000; 911cbb75751SHans Petter Selasky } 912cbb75751SHans Petter Selasky pr_recovery_delay = usb_pr_recovery_delay; 913cbb75751SHans Petter Selasky if (pr_recovery_delay > 1000) { 914cbb75751SHans Petter Selasky pr_recovery_delay = 1000; 915cbb75751SHans Petter Selasky } 916cbb75751SHans Petter Selasky #endif 917cbb75751SHans Petter Selasky n = 0; 918cbb75751SHans Petter Selasky while (1) { 919cbb75751SHans Petter Selasky #ifdef USB_DEBUG 920cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 921cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); 922cbb75751SHans Petter Selasky n += pr_poll_delay; 923cbb75751SHans Petter Selasky #else 924cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 925cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); 926cbb75751SHans Petter Selasky n += USB_PORT_RESET_DELAY; 927cbb75751SHans Petter Selasky #endif 928cbb75751SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 929cbb75751SHans Petter Selasky if (err) { 930cbb75751SHans Petter Selasky goto done; 931cbb75751SHans Petter Selasky } 932cbb75751SHans Petter Selasky /* if the device disappeared, just give up */ 933cbb75751SHans Petter Selasky if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { 934cbb75751SHans Petter Selasky goto done; 935cbb75751SHans Petter Selasky } 936cbb75751SHans Petter Selasky /* check if reset is complete */ 937cbb75751SHans Petter Selasky if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) { 938cbb75751SHans Petter Selasky break; 939cbb75751SHans Petter Selasky } 940cbb75751SHans Petter Selasky /* check for timeout */ 941cbb75751SHans Petter Selasky if (n > 1000) { 942cbb75751SHans Petter Selasky n = 0; 943cbb75751SHans Petter Selasky break; 944cbb75751SHans Petter Selasky } 945cbb75751SHans Petter Selasky } 946cbb75751SHans Petter Selasky 947cbb75751SHans Petter Selasky /* clear port reset first */ 948cbb75751SHans Petter Selasky err = usbd_req_clear_port_feature( 949cbb75751SHans Petter Selasky udev, mtx, port, UHF_C_BH_PORT_RESET); 950cbb75751SHans Petter Selasky if (err) { 951cbb75751SHans Petter Selasky goto done; 952cbb75751SHans Petter Selasky } 953cbb75751SHans Petter Selasky /* check for timeout */ 954cbb75751SHans Petter Selasky if (n == 0) { 955cbb75751SHans Petter Selasky err = USB_ERR_TIMEOUT; 956cbb75751SHans Petter Selasky goto done; 957cbb75751SHans Petter Selasky } 958cbb75751SHans Petter Selasky #ifdef USB_DEBUG 959cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 960cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); 961cbb75751SHans Petter Selasky #else 962cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 963cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); 964cbb75751SHans Petter Selasky #endif 965cbb75751SHans Petter Selasky 966cbb75751SHans Petter Selasky done: 967cbb75751SHans Petter Selasky DPRINTFN(2, "port %d warm reset returning error=%s\n", 968cbb75751SHans Petter Selasky port, usbd_errstr(err)); 969cbb75751SHans Petter Selasky return (err); 970cbb75751SHans Petter Selasky } 971cbb75751SHans Petter Selasky 972cbb75751SHans Petter Selasky /*------------------------------------------------------------------------* 973a593f6b8SAndrew Thompson * usbd_req_get_desc 97402ac6454SAndrew Thompson * 97502ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 97602ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 97702ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 97802ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 97902ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 98002ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 98116589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 98216589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 98316589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 98416589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 98516589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 98616589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 98716589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 98816589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 98916589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 99016589beaSAndrew Thompson * descriptor. 99102ac6454SAndrew Thompson * 99202ac6454SAndrew Thompson * Returns: 99302ac6454SAndrew Thompson * 0: Success 99402ac6454SAndrew Thompson * Else: Failure 99502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 996e0a69b51SAndrew Thompson usb_error_t 997a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 99816589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 99902ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 100002ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 100102ac6454SAndrew Thompson uint8_t retries) 100202ac6454SAndrew Thompson { 1003760bc48eSAndrew Thompson struct usb_device_request req; 100402ac6454SAndrew Thompson uint8_t *buf; 1005e0a69b51SAndrew Thompson usb_error_t err; 100602ac6454SAndrew Thompson 100702ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 100802ac6454SAndrew Thompson id, type, index, max_len); 100902ac6454SAndrew Thompson 101002ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 101102ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 101202ac6454SAndrew Thompson USETW2(req.wValue, type, index); 101302ac6454SAndrew Thompson USETW(req.wIndex, id); 101402ac6454SAndrew Thompson 101502ac6454SAndrew Thompson while (1) { 101602ac6454SAndrew Thompson 101702ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 101802ac6454SAndrew Thompson err = USB_ERR_INVAL; 101902ac6454SAndrew Thompson goto done; 102002ac6454SAndrew Thompson } 102102ac6454SAndrew Thompson USETW(req.wLength, min_len); 102202ac6454SAndrew Thompson 1023a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 102402ac6454SAndrew Thompson desc, 0, NULL, 1000); 102502ac6454SAndrew Thompson 102602ac6454SAndrew Thompson if (err) { 102702ac6454SAndrew Thompson if (!retries) { 102802ac6454SAndrew Thompson goto done; 102902ac6454SAndrew Thompson } 103002ac6454SAndrew Thompson retries--; 103102ac6454SAndrew Thompson 1032a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 103302ac6454SAndrew Thompson 103402ac6454SAndrew Thompson continue; 103502ac6454SAndrew Thompson } 103602ac6454SAndrew Thompson buf = desc; 103702ac6454SAndrew Thompson 103802ac6454SAndrew Thompson if (min_len == max_len) { 103902ac6454SAndrew Thompson 104016589beaSAndrew Thompson /* enforce correct length */ 104116589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 104202ac6454SAndrew Thompson buf[0] = min_len; 104316589beaSAndrew Thompson 104416589beaSAndrew Thompson /* enforce correct type */ 104502ac6454SAndrew Thompson buf[1] = type; 104602ac6454SAndrew Thompson 104702ac6454SAndrew Thompson goto done; 104802ac6454SAndrew Thompson } 104902ac6454SAndrew Thompson /* range check */ 105002ac6454SAndrew Thompson 105102ac6454SAndrew Thompson if (max_len > buf[0]) { 105202ac6454SAndrew Thompson max_len = buf[0]; 105302ac6454SAndrew Thompson } 105402ac6454SAndrew Thompson /* zero minimum data */ 105502ac6454SAndrew Thompson 105602ac6454SAndrew Thompson while (min_len > max_len) { 105702ac6454SAndrew Thompson min_len--; 105802ac6454SAndrew Thompson buf[min_len] = 0; 105902ac6454SAndrew Thompson } 106002ac6454SAndrew Thompson 106102ac6454SAndrew Thompson /* set new minimum length */ 106202ac6454SAndrew Thompson 106302ac6454SAndrew Thompson min_len = max_len; 106402ac6454SAndrew Thompson } 106502ac6454SAndrew Thompson done: 106616589beaSAndrew Thompson if (actlen != NULL) { 106716589beaSAndrew Thompson if (err) 106816589beaSAndrew Thompson *actlen = 0; 106916589beaSAndrew Thompson else 107016589beaSAndrew Thompson *actlen = min_len; 107116589beaSAndrew Thompson } 107202ac6454SAndrew Thompson return (err); 107302ac6454SAndrew Thompson } 107402ac6454SAndrew Thompson 107502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1076a593f6b8SAndrew Thompson * usbd_req_get_string_any 107702ac6454SAndrew Thompson * 107802ac6454SAndrew Thompson * This function will return the string given by "string_index" 107902ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 108002ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 108102ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 108202ac6454SAndrew Thompson * 108302ac6454SAndrew Thompson * Returns: 108402ac6454SAndrew Thompson * 0: Success 108502ac6454SAndrew Thompson * Else: Failure 108602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1087e0a69b51SAndrew Thompson usb_error_t 1088a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 108902ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 109002ac6454SAndrew Thompson { 109102ac6454SAndrew Thompson char *s; 109202ac6454SAndrew Thompson uint8_t *temp; 109302ac6454SAndrew Thompson uint16_t i; 109402ac6454SAndrew Thompson uint16_t n; 109502ac6454SAndrew Thompson uint16_t c; 109602ac6454SAndrew Thompson uint8_t swap; 1097e0a69b51SAndrew Thompson usb_error_t err; 109802ac6454SAndrew Thompson 109902ac6454SAndrew Thompson if (len == 0) { 110002ac6454SAndrew Thompson /* should not happen */ 110102ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 110202ac6454SAndrew Thompson } 110302ac6454SAndrew Thompson if (string_index == 0) { 110402ac6454SAndrew Thompson /* this is the language table */ 110502ac6454SAndrew Thompson buf[0] = 0; 110602ac6454SAndrew Thompson return (USB_ERR_INVAL); 110702ac6454SAndrew Thompson } 110802ac6454SAndrew Thompson if (udev->flags.no_strings) { 110902ac6454SAndrew Thompson buf[0] = 0; 111002ac6454SAndrew Thompson return (USB_ERR_STALLED); 111102ac6454SAndrew Thompson } 1112a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 111302ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 111402ac6454SAndrew Thompson if (err) { 111502ac6454SAndrew Thompson buf[0] = 0; 111602ac6454SAndrew Thompson return (err); 111702ac6454SAndrew Thompson } 111802ac6454SAndrew Thompson temp = (uint8_t *)buf; 111902ac6454SAndrew Thompson 112002ac6454SAndrew Thompson if (temp[0] < 2) { 112102ac6454SAndrew Thompson /* string length is too short */ 112202ac6454SAndrew Thompson buf[0] = 0; 112302ac6454SAndrew Thompson return (USB_ERR_INVAL); 112402ac6454SAndrew Thompson } 112502ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 112602ac6454SAndrew Thompson len--; 112702ac6454SAndrew Thompson 112802ac6454SAndrew Thompson /* find maximum length */ 112902ac6454SAndrew Thompson s = buf; 113002ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 113102ac6454SAndrew Thompson if (n > len) { 113202ac6454SAndrew Thompson n = len; 113302ac6454SAndrew Thompson } 113402ac6454SAndrew Thompson /* skip descriptor header */ 113502ac6454SAndrew Thompson temp += 2; 113602ac6454SAndrew Thompson 113702ac6454SAndrew Thompson /* reset swap state */ 113802ac6454SAndrew Thompson swap = 3; 113902ac6454SAndrew Thompson 114002ac6454SAndrew Thompson /* convert and filter */ 114102ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 114202ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 114302ac6454SAndrew Thompson 114402ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 114502ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 114602ac6454SAndrew Thompson /* Little Endian, default */ 114702ac6454SAndrew Thompson *s = c; 114802ac6454SAndrew Thompson swap = 1; 114902ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 115002ac6454SAndrew Thompson /* Big Endian */ 115102ac6454SAndrew Thompson *s = c >> 8; 115202ac6454SAndrew Thompson swap = 2; 115302ac6454SAndrew Thompson } else { 115402ac6454SAndrew Thompson /* silently skip bad character */ 115502ac6454SAndrew Thompson continue; 115602ac6454SAndrew Thompson } 115702ac6454SAndrew Thompson 115802ac6454SAndrew Thompson /* 1159b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical 1160b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts 1161b64cf89fSHans Petter Selasky * and daemons. 116202ac6454SAndrew Thompson */ 1163b64cf89fSHans Petter Selasky if (isalpha(*s) || 1164b64cf89fSHans Petter Selasky isdigit(*s) || 1165b64cf89fSHans Petter Selasky *s == '-' || 1166b64cf89fSHans Petter Selasky *s == '+' || 1167b64cf89fSHans Petter Selasky *s == ' ' || 1168b64cf89fSHans Petter Selasky *s == '.' || 1169b64cf89fSHans Petter Selasky *s == ',') { 1170b64cf89fSHans Petter Selasky /* allowed */ 117102ac6454SAndrew Thompson s++; 117202ac6454SAndrew Thompson } 1173b64cf89fSHans Petter Selasky /* silently skip bad character */ 1174b64cf89fSHans Petter Selasky } 117502ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 117602ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 117702ac6454SAndrew Thompson } 117802ac6454SAndrew Thompson 117902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1180a593f6b8SAndrew Thompson * usbd_req_get_string_desc 118102ac6454SAndrew Thompson * 118202ac6454SAndrew Thompson * If you don't know the language ID, consider using 1183a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 118402ac6454SAndrew Thompson * 118502ac6454SAndrew Thompson * Returns: 118602ac6454SAndrew Thompson * 0: Success 118702ac6454SAndrew Thompson * Else: Failure 118802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1189e0a69b51SAndrew Thompson usb_error_t 1190a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 119102ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 119202ac6454SAndrew Thompson uint8_t string_index) 119302ac6454SAndrew Thompson { 1194a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 119502ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 119602ac6454SAndrew Thompson } 119702ac6454SAndrew Thompson 119802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1199a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 12007efaaa9aSAndrew Thompson * 12017efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 12027efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 12037efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 12047efaaa9aSAndrew Thompson * 12057efaaa9aSAndrew Thompson * Returns: 12067efaaa9aSAndrew Thompson * 0: Success 12077efaaa9aSAndrew Thompson * Else: Failure 12087efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 1209e0a69b51SAndrew Thompson usb_error_t 1210a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 1211760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 12127efaaa9aSAndrew Thompson { 1213760bc48eSAndrew Thompson struct usb_device_request req; 1214e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 1215459d369eSAndrew Thompson const void *ptr; 1216459d369eSAndrew Thompson uint16_t len; 1217e0a69b51SAndrew Thompson usb_error_t err; 12187efaaa9aSAndrew Thompson 121963521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 12207efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 1221459d369eSAndrew Thompson USETW(req.wValue, wValue); 12227efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 12237efaaa9aSAndrew Thompson USETW(req.wLength, 0); 12247efaaa9aSAndrew Thompson 1225459d369eSAndrew Thompson ptr = NULL; 1226459d369eSAndrew Thompson len = 0; 12277efaaa9aSAndrew Thompson 1228a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 1229459d369eSAndrew Thompson 1230459d369eSAndrew Thompson if (hr_func == NULL) 1231459d369eSAndrew Thompson err = USB_ERR_INVAL; 1232459d369eSAndrew Thompson else { 1233459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 1234459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 1235459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 1236459d369eSAndrew Thompson } 1237459d369eSAndrew Thompson 1238459d369eSAndrew Thompson if (err) 1239459d369eSAndrew Thompson ptr = NULL; 1240459d369eSAndrew Thompson else if (ptr == NULL) 1241459d369eSAndrew Thompson err = USB_ERR_INVAL; 1242459d369eSAndrew Thompson 1243760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 1244459d369eSAndrew Thompson 1245459d369eSAndrew Thompson return (err); 12467efaaa9aSAndrew Thompson } 12477efaaa9aSAndrew Thompson 12487efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 1249a593f6b8SAndrew Thompson * usbd_req_get_config_desc 125002ac6454SAndrew Thompson * 125102ac6454SAndrew Thompson * Returns: 125202ac6454SAndrew Thompson * 0: Success 125302ac6454SAndrew Thompson * Else: Failure 125402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1255e0a69b51SAndrew Thompson usb_error_t 1256a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 1257760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 125802ac6454SAndrew Thompson { 1259e0a69b51SAndrew Thompson usb_error_t err; 126002ac6454SAndrew Thompson 126102ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 126202ac6454SAndrew Thompson 1263a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 126402ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 126502ac6454SAndrew Thompson if (err) { 126602ac6454SAndrew Thompson goto done; 126702ac6454SAndrew Thompson } 126802ac6454SAndrew Thompson /* Extra sanity checking */ 126902ac6454SAndrew Thompson if (UGETW(d->wTotalLength) < sizeof(*d)) { 127002ac6454SAndrew Thompson err = USB_ERR_INVAL; 127102ac6454SAndrew Thompson } 127202ac6454SAndrew Thompson done: 127302ac6454SAndrew Thompson return (err); 127402ac6454SAndrew Thompson } 127502ac6454SAndrew Thompson 127602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1277a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 127802ac6454SAndrew Thompson * 127902ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 128002ac6454SAndrew Thompson * ensures that "wTotalLength" is correct. 128102ac6454SAndrew Thompson * 128202ac6454SAndrew Thompson * Returns: 128302ac6454SAndrew Thompson * 0: Success 128402ac6454SAndrew Thompson * Else: Failure 128502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1286e0a69b51SAndrew Thompson usb_error_t 1287a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, 1288760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, struct malloc_type *mtype, 128902ac6454SAndrew Thompson uint8_t index) 129002ac6454SAndrew Thompson { 1291760bc48eSAndrew Thompson struct usb_config_descriptor cd; 1292760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 129302ac6454SAndrew Thompson uint16_t len; 1294e0a69b51SAndrew Thompson usb_error_t err; 129502ac6454SAndrew Thompson 129602ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 129702ac6454SAndrew Thompson 129802ac6454SAndrew Thompson *ppcd = NULL; 129902ac6454SAndrew Thompson 1300a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 130102ac6454SAndrew Thompson if (err) { 130202ac6454SAndrew Thompson return (err); 130302ac6454SAndrew Thompson } 130402ac6454SAndrew Thompson /* get full descriptor */ 130502ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 130602ac6454SAndrew Thompson if (len < sizeof(*cdesc)) { 130702ac6454SAndrew Thompson /* corrupt descriptor */ 130802ac6454SAndrew Thompson return (USB_ERR_INVAL); 130902ac6454SAndrew Thompson } 131002ac6454SAndrew Thompson cdesc = malloc(len, mtype, M_WAITOK); 131102ac6454SAndrew Thompson if (cdesc == NULL) { 131202ac6454SAndrew Thompson return (USB_ERR_NOMEM); 131302ac6454SAndrew Thompson } 1314a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 131502ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 131602ac6454SAndrew Thompson if (err) { 131702ac6454SAndrew Thompson free(cdesc, mtype); 131802ac6454SAndrew Thompson return (err); 131902ac6454SAndrew Thompson } 132002ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 132102ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 132202ac6454SAndrew Thompson 132302ac6454SAndrew Thompson *ppcd = cdesc; 132402ac6454SAndrew Thompson 132502ac6454SAndrew Thompson return (0); /* success */ 132602ac6454SAndrew Thompson } 132702ac6454SAndrew Thompson 132802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1329a593f6b8SAndrew Thompson * usbd_req_get_device_desc 133002ac6454SAndrew Thompson * 133102ac6454SAndrew Thompson * Returns: 133202ac6454SAndrew Thompson * 0: Success 133302ac6454SAndrew Thompson * Else: Failure 133402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1335e0a69b51SAndrew Thompson usb_error_t 1336a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1337760bc48eSAndrew Thompson struct usb_device_descriptor *d) 133802ac6454SAndrew Thompson { 133902ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1340a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 134102ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 134202ac6454SAndrew Thompson } 134302ac6454SAndrew Thompson 134402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1345a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 134602ac6454SAndrew Thompson * 134702ac6454SAndrew Thompson * Returns: 134802ac6454SAndrew Thompson * 0: Success 134902ac6454SAndrew Thompson * Else: Failure 135002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1351e0a69b51SAndrew Thompson usb_error_t 1352a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 135302ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 135402ac6454SAndrew Thompson { 1355a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1356760bc48eSAndrew Thompson struct usb_device_request req; 135702ac6454SAndrew Thompson 1358bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 135902ac6454SAndrew Thompson return (USB_ERR_INVAL); 1360bd73b187SAlfred Perlstein 136102ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 136202ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 136302ac6454SAndrew Thompson USETW(req.wValue, 0); 136402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 136502ac6454SAndrew Thompson req.wIndex[1] = 0; 136602ac6454SAndrew Thompson USETW(req.wLength, 1); 1367a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 136802ac6454SAndrew Thompson } 136902ac6454SAndrew Thompson 137002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1371a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 137202ac6454SAndrew Thompson * 137302ac6454SAndrew Thompson * Returns: 137402ac6454SAndrew Thompson * 0: Success 137502ac6454SAndrew Thompson * Else: Failure 137602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1377e0a69b51SAndrew Thompson usb_error_t 1378a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 137902ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 138002ac6454SAndrew Thompson { 1381a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1382760bc48eSAndrew Thompson struct usb_device_request req; 138302ac6454SAndrew Thompson 1384bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 138502ac6454SAndrew Thompson return (USB_ERR_INVAL); 1386bd73b187SAlfred Perlstein 138702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 138802ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 138902ac6454SAndrew Thompson req.wValue[0] = alt_no; 139002ac6454SAndrew Thompson req.wValue[1] = 0; 139102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 139202ac6454SAndrew Thompson req.wIndex[1] = 0; 139302ac6454SAndrew Thompson USETW(req.wLength, 0); 1394a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 139502ac6454SAndrew Thompson } 139602ac6454SAndrew Thompson 139702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1398a593f6b8SAndrew Thompson * usbd_req_get_device_status 139902ac6454SAndrew Thompson * 140002ac6454SAndrew Thompson * Returns: 140102ac6454SAndrew Thompson * 0: Success 140202ac6454SAndrew Thompson * Else: Failure 140302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1404e0a69b51SAndrew Thompson usb_error_t 1405a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1406760bc48eSAndrew Thompson struct usb_status *st) 140702ac6454SAndrew Thompson { 1408760bc48eSAndrew Thompson struct usb_device_request req; 140902ac6454SAndrew Thompson 141002ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 141102ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 141202ac6454SAndrew Thompson USETW(req.wValue, 0); 141302ac6454SAndrew Thompson USETW(req.wIndex, 0); 141402ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1415a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 141602ac6454SAndrew Thompson } 141702ac6454SAndrew Thompson 141802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1419a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 142002ac6454SAndrew Thompson * 142102ac6454SAndrew Thompson * Returns: 142202ac6454SAndrew Thompson * 0: Success 142302ac6454SAndrew Thompson * Else: Failure 142402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1425e0a69b51SAndrew Thompson usb_error_t 1426a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1427760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 142802ac6454SAndrew Thompson { 1429760bc48eSAndrew Thompson struct usb_device_request req; 143002ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 143102ac6454SAndrew Thompson 143202ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 143302ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 143402ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 143502ac6454SAndrew Thompson USETW(req.wIndex, 0); 143602ac6454SAndrew Thompson USETW(req.wLength, len); 1437a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 143802ac6454SAndrew Thompson } 143902ac6454SAndrew Thompson 144002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1441963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor 1442963169b4SHans Petter Selasky * 1443963169b4SHans Petter Selasky * Returns: 1444963169b4SHans Petter Selasky * 0: Success 1445963169b4SHans Petter Selasky * Else: Failure 1446963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1447963169b4SHans Petter Selasky usb_error_t 1448963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1449963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports) 1450963169b4SHans Petter Selasky { 1451963169b4SHans Petter Selasky struct usb_device_request req; 1452963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); 1453963169b4SHans Petter Selasky 1454963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE; 1455963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR; 1456963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0); 1457963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1458963169b4SHans Petter Selasky USETW(req.wLength, len); 1459963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd)); 1460963169b4SHans Petter Selasky } 1461963169b4SHans Petter Selasky 1462963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1463a593f6b8SAndrew Thompson * usbd_req_get_hub_status 146402ac6454SAndrew Thompson * 146502ac6454SAndrew Thompson * Returns: 146602ac6454SAndrew Thompson * 0: Success 146702ac6454SAndrew Thompson * Else: Failure 146802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1469e0a69b51SAndrew Thompson usb_error_t 1470a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1471760bc48eSAndrew Thompson struct usb_hub_status *st) 147202ac6454SAndrew Thompson { 1473760bc48eSAndrew Thompson struct usb_device_request req; 147402ac6454SAndrew Thompson 147502ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 147602ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 147702ac6454SAndrew Thompson USETW(req.wValue, 0); 147802ac6454SAndrew Thompson USETW(req.wIndex, 0); 1479760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1480a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 148102ac6454SAndrew Thompson } 148202ac6454SAndrew Thompson 148302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1484a593f6b8SAndrew Thompson * usbd_req_set_address 148502ac6454SAndrew Thompson * 148602ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 148702ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 148802ac6454SAndrew Thompson * 148902ac6454SAndrew Thompson * Returns: 149002ac6454SAndrew Thompson * 0: Success 149102ac6454SAndrew Thompson * Else: Failure 149202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1493e0a69b51SAndrew Thompson usb_error_t 1494a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 149502ac6454SAndrew Thompson { 1496760bc48eSAndrew Thompson struct usb_device_request req; 1497963169b4SHans Petter Selasky usb_error_t err; 149802ac6454SAndrew Thompson 149902ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 150002ac6454SAndrew Thompson 150102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 150202ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 150302ac6454SAndrew Thompson USETW(req.wValue, addr); 150402ac6454SAndrew Thompson USETW(req.wIndex, 0); 150502ac6454SAndrew Thompson USETW(req.wLength, 0); 150602ac6454SAndrew Thompson 1507963169b4SHans Petter Selasky err = USB_ERR_INVAL; 1508963169b4SHans Petter Selasky 1509963169b4SHans Petter Selasky /* check if USB controller handles set address */ 1510963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL) 1511963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr); 1512963169b4SHans Petter Selasky 1513963169b4SHans Petter Selasky if (err != USB_ERR_INVAL) 1514963169b4SHans Petter Selasky goto done; 1515963169b4SHans Petter Selasky 151602ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1517963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL, 1518963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000); 1519963169b4SHans Petter Selasky 1520963169b4SHans Petter Selasky done: 1521963169b4SHans Petter Selasky /* allow device time to set new address */ 1522963169b4SHans Petter Selasky usb_pause_mtx(mtx, 1523963169b4SHans Petter Selasky USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); 1524963169b4SHans Petter Selasky 1525963169b4SHans Petter Selasky return (err); 152602ac6454SAndrew Thompson } 152702ac6454SAndrew Thompson 152802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1529a593f6b8SAndrew Thompson * usbd_req_get_port_status 153002ac6454SAndrew Thompson * 153102ac6454SAndrew Thompson * Returns: 153202ac6454SAndrew Thompson * 0: Success 153302ac6454SAndrew Thompson * Else: Failure 153402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1535e0a69b51SAndrew Thompson usb_error_t 1536a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1537760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 153802ac6454SAndrew Thompson { 1539760bc48eSAndrew Thompson struct usb_device_request req; 154002ac6454SAndrew Thompson 154102ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 154202ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 154302ac6454SAndrew Thompson USETW(req.wValue, 0); 154402ac6454SAndrew Thompson req.wIndex[0] = port; 154502ac6454SAndrew Thompson req.wIndex[1] = 0; 154602ac6454SAndrew Thompson USETW(req.wLength, sizeof *ps); 1547a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, ps)); 154802ac6454SAndrew Thompson } 154902ac6454SAndrew Thompson 155002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1551a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 155202ac6454SAndrew Thompson * 155302ac6454SAndrew Thompson * Returns: 155402ac6454SAndrew Thompson * 0: Success 155502ac6454SAndrew Thompson * Else: Failure 155602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1557e0a69b51SAndrew Thompson usb_error_t 1558a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 155902ac6454SAndrew Thompson uint16_t sel) 156002ac6454SAndrew Thompson { 1561760bc48eSAndrew Thompson struct usb_device_request req; 156202ac6454SAndrew Thompson 156302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 156402ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 156502ac6454SAndrew Thompson USETW(req.wValue, sel); 156602ac6454SAndrew Thompson USETW(req.wIndex, 0); 156702ac6454SAndrew Thompson USETW(req.wLength, 0); 1568a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 156902ac6454SAndrew Thompson } 157002ac6454SAndrew Thompson 157102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1572a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 157302ac6454SAndrew Thompson * 157402ac6454SAndrew Thompson * Returns: 157502ac6454SAndrew Thompson * 0: Success 157602ac6454SAndrew Thompson * Else: Failure 157702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1578e0a69b51SAndrew Thompson usb_error_t 1579a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 158002ac6454SAndrew Thompson uint16_t sel) 158102ac6454SAndrew Thompson { 1582760bc48eSAndrew Thompson struct usb_device_request req; 158302ac6454SAndrew Thompson 158402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 158502ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 158602ac6454SAndrew Thompson USETW(req.wValue, sel); 158702ac6454SAndrew Thompson USETW(req.wIndex, 0); 158802ac6454SAndrew Thompson USETW(req.wLength, 0); 1589a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 159002ac6454SAndrew Thompson } 159102ac6454SAndrew Thompson 159202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1593963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout 1594963169b4SHans Petter Selasky * 1595963169b4SHans Petter Selasky * Returns: 1596963169b4SHans Petter Selasky * 0: Success 1597963169b4SHans Petter Selasky * Else: Failure 1598963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1599963169b4SHans Petter Selasky usb_error_t 1600963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, 1601963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1602963169b4SHans Petter Selasky { 1603963169b4SHans Petter Selasky struct usb_device_request req; 1604963169b4SHans Petter Selasky 1605963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1606963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1607963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT); 1608963169b4SHans Petter Selasky req.wIndex[0] = port; 1609963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1610963169b4SHans Petter Selasky USETW(req.wLength, 0); 1611963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1612963169b4SHans Petter Selasky } 1613963169b4SHans Petter Selasky 1614963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1615963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout 1616963169b4SHans Petter Selasky * 1617963169b4SHans Petter Selasky * Returns: 1618963169b4SHans Petter Selasky * 0: Success 1619963169b4SHans Petter Selasky * Else: Failure 1620963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1621963169b4SHans Petter Selasky usb_error_t 1622963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, 1623963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1624963169b4SHans Petter Selasky { 1625963169b4SHans Petter Selasky struct usb_device_request req; 1626963169b4SHans Petter Selasky 1627963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1628963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1629963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT); 1630963169b4SHans Petter Selasky req.wIndex[0] = port; 1631963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1632963169b4SHans Petter Selasky USETW(req.wLength, 0); 1633963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1634963169b4SHans Petter Selasky } 1635963169b4SHans Petter Selasky 1636963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1637963169b4SHans Petter Selasky * usbd_req_set_hub_depth 1638963169b4SHans Petter Selasky * 1639963169b4SHans Petter Selasky * Returns: 1640963169b4SHans Petter Selasky * 0: Success 1641963169b4SHans Petter Selasky * Else: Failure 1642963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1643963169b4SHans Petter Selasky usb_error_t 1644963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, 1645963169b4SHans Petter Selasky uint16_t depth) 1646963169b4SHans Petter Selasky { 1647963169b4SHans Petter Selasky struct usb_device_request req; 1648963169b4SHans Petter Selasky 1649963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE; 1650963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH; 1651963169b4SHans Petter Selasky USETW(req.wValue, depth); 1652963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1653963169b4SHans Petter Selasky USETW(req.wLength, 0); 1654963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1655963169b4SHans Petter Selasky } 1656963169b4SHans Petter Selasky 1657963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1658a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 165902ac6454SAndrew Thompson * 166002ac6454SAndrew Thompson * Returns: 166102ac6454SAndrew Thompson * 0: Success 166202ac6454SAndrew Thompson * Else: Failure 166302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1664e0a69b51SAndrew Thompson usb_error_t 1665a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 166602ac6454SAndrew Thompson uint8_t port, uint16_t sel) 166702ac6454SAndrew Thompson { 1668760bc48eSAndrew Thompson struct usb_device_request req; 166902ac6454SAndrew Thompson 167002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 167102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 167202ac6454SAndrew Thompson USETW(req.wValue, sel); 167302ac6454SAndrew Thompson req.wIndex[0] = port; 167402ac6454SAndrew Thompson req.wIndex[1] = 0; 167502ac6454SAndrew Thompson USETW(req.wLength, 0); 1676a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 167702ac6454SAndrew Thompson } 167802ac6454SAndrew Thompson 167902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1680a593f6b8SAndrew Thompson * usbd_req_set_port_feature 168102ac6454SAndrew Thompson * 168202ac6454SAndrew Thompson * Returns: 168302ac6454SAndrew Thompson * 0: Success 168402ac6454SAndrew Thompson * Else: Failure 168502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1686e0a69b51SAndrew Thompson usb_error_t 1687a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, 168802ac6454SAndrew Thompson uint8_t port, uint16_t sel) 168902ac6454SAndrew Thompson { 1690760bc48eSAndrew Thompson struct usb_device_request req; 169102ac6454SAndrew Thompson 169202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 169302ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 169402ac6454SAndrew Thompson USETW(req.wValue, sel); 169502ac6454SAndrew Thompson req.wIndex[0] = port; 169602ac6454SAndrew Thompson req.wIndex[1] = 0; 169702ac6454SAndrew Thompson USETW(req.wLength, 0); 1698a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 169902ac6454SAndrew Thompson } 170002ac6454SAndrew Thompson 170102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1702a593f6b8SAndrew Thompson * usbd_req_set_protocol 170302ac6454SAndrew Thompson * 170402ac6454SAndrew Thompson * Returns: 170502ac6454SAndrew Thompson * 0: Success 170602ac6454SAndrew Thompson * Else: Failure 170702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1708e0a69b51SAndrew Thompson usb_error_t 1709a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 171002ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 171102ac6454SAndrew Thompson { 1712a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1713760bc48eSAndrew Thompson struct usb_device_request req; 171402ac6454SAndrew Thompson 171502ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 171602ac6454SAndrew Thompson return (USB_ERR_INVAL); 171702ac6454SAndrew Thompson } 171802ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 171902ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 172002ac6454SAndrew Thompson 172102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 172202ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 172302ac6454SAndrew Thompson USETW(req.wValue, report); 172402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 172502ac6454SAndrew Thompson req.wIndex[1] = 0; 172602ac6454SAndrew Thompson USETW(req.wLength, 0); 1727a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 172802ac6454SAndrew Thompson } 172902ac6454SAndrew Thompson 173002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1731a593f6b8SAndrew Thompson * usbd_req_set_report 173202ac6454SAndrew Thompson * 173302ac6454SAndrew Thompson * Returns: 173402ac6454SAndrew Thompson * 0: Success 173502ac6454SAndrew Thompson * Else: Failure 173602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1737e0a69b51SAndrew Thompson usb_error_t 1738a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 173902ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 174002ac6454SAndrew Thompson { 1741a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1742760bc48eSAndrew Thompson struct usb_device_request req; 174302ac6454SAndrew Thompson 174402ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 174502ac6454SAndrew Thompson return (USB_ERR_INVAL); 174602ac6454SAndrew Thompson } 174702ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 174802ac6454SAndrew Thompson 174902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 175002ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 175102ac6454SAndrew Thompson USETW2(req.wValue, type, id); 175202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 175302ac6454SAndrew Thompson req.wIndex[1] = 0; 175402ac6454SAndrew Thompson USETW(req.wLength, len); 1755a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 175602ac6454SAndrew Thompson } 175702ac6454SAndrew Thompson 175802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1759a593f6b8SAndrew Thompson * usbd_req_get_report 176002ac6454SAndrew Thompson * 176102ac6454SAndrew Thompson * Returns: 176202ac6454SAndrew Thompson * 0: Success 176302ac6454SAndrew Thompson * Else: Failure 176402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1765e0a69b51SAndrew Thompson usb_error_t 1766a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 176702ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 176802ac6454SAndrew Thompson { 1769a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1770760bc48eSAndrew Thompson struct usb_device_request req; 177102ac6454SAndrew Thompson 177202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { 177302ac6454SAndrew Thompson return (USB_ERR_INVAL); 177402ac6454SAndrew Thompson } 177502ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 177602ac6454SAndrew Thompson 177702ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 177802ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 177902ac6454SAndrew Thompson USETW2(req.wValue, type, id); 178002ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 178102ac6454SAndrew Thompson req.wIndex[1] = 0; 178202ac6454SAndrew Thompson USETW(req.wLength, len); 1783a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 178402ac6454SAndrew Thompson } 178502ac6454SAndrew Thompson 178602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1787a593f6b8SAndrew Thompson * usbd_req_set_idle 178802ac6454SAndrew Thompson * 178902ac6454SAndrew Thompson * Returns: 179002ac6454SAndrew Thompson * 0: Success 179102ac6454SAndrew Thompson * Else: Failure 179202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1793e0a69b51SAndrew Thompson usb_error_t 1794a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 179502ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 179602ac6454SAndrew Thompson { 1797a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1798760bc48eSAndrew Thompson struct usb_device_request req; 179902ac6454SAndrew Thompson 180002ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 180102ac6454SAndrew Thompson return (USB_ERR_INVAL); 180202ac6454SAndrew Thompson } 180302ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 180402ac6454SAndrew Thompson 180502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 180602ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 180702ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 180802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 180902ac6454SAndrew Thompson req.wIndex[1] = 0; 181002ac6454SAndrew Thompson USETW(req.wLength, 0); 1811a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 181202ac6454SAndrew Thompson } 181302ac6454SAndrew Thompson 181402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1815a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 181602ac6454SAndrew Thompson * 181702ac6454SAndrew Thompson * Returns: 181802ac6454SAndrew Thompson * 0: Success 181902ac6454SAndrew Thompson * Else: Failure 182002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1821e0a69b51SAndrew Thompson usb_error_t 1822a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 182302ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 182402ac6454SAndrew Thompson { 1825a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1826760bc48eSAndrew Thompson struct usb_device_request req; 182702ac6454SAndrew Thompson 182802ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 182902ac6454SAndrew Thompson return (USB_ERR_INVAL); 183002ac6454SAndrew Thompson } 183102ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 183202ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 183302ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 183402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 183502ac6454SAndrew Thompson req.wIndex[1] = 0; 183602ac6454SAndrew Thompson USETW(req.wLength, size); 1837a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 183802ac6454SAndrew Thompson } 183902ac6454SAndrew Thompson 184002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1841a593f6b8SAndrew Thompson * usbd_req_set_config 184202ac6454SAndrew Thompson * 184302ac6454SAndrew Thompson * This function is used to select the current configuration number in 184402ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 184502ac6454SAndrew Thompson * configuration the function of the interfaces can change. 184602ac6454SAndrew Thompson * 184702ac6454SAndrew Thompson * Returns: 184802ac6454SAndrew Thompson * 0: Success 184902ac6454SAndrew Thompson * Else: Failure 185002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1851e0a69b51SAndrew Thompson usb_error_t 1852a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 185302ac6454SAndrew Thompson { 1854760bc48eSAndrew Thompson struct usb_device_request req; 185502ac6454SAndrew Thompson 185602ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 185702ac6454SAndrew Thompson 185802ac6454SAndrew Thompson /* do "set configuration" request */ 185902ac6454SAndrew Thompson 186002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 186102ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 186202ac6454SAndrew Thompson req.wValue[0] = conf; 186302ac6454SAndrew Thompson req.wValue[1] = 0; 186402ac6454SAndrew Thompson USETW(req.wIndex, 0); 186502ac6454SAndrew Thompson USETW(req.wLength, 0); 1866a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 186702ac6454SAndrew Thompson } 186802ac6454SAndrew Thompson 186902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1870a593f6b8SAndrew Thompson * usbd_req_get_config 187102ac6454SAndrew Thompson * 187202ac6454SAndrew Thompson * Returns: 187302ac6454SAndrew Thompson * 0: Success 187402ac6454SAndrew Thompson * Else: Failure 187502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1876e0a69b51SAndrew Thompson usb_error_t 1877a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 187802ac6454SAndrew Thompson { 1879760bc48eSAndrew Thompson struct usb_device_request req; 188002ac6454SAndrew Thompson 188102ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 188202ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 188302ac6454SAndrew Thompson USETW(req.wValue, 0); 188402ac6454SAndrew Thompson USETW(req.wIndex, 0); 188502ac6454SAndrew Thompson USETW(req.wLength, 1); 1886a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 188702ac6454SAndrew Thompson } 188802ac6454SAndrew Thompson 188902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1890963169b4SHans Petter Selasky * usbd_setup_device_desc 1891963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1892963169b4SHans Petter Selasky usb_error_t 1893963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) 1894963169b4SHans Petter Selasky { 1895963169b4SHans Petter Selasky usb_error_t err; 1896963169b4SHans Petter Selasky 1897963169b4SHans Petter Selasky /* 1898963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor ! 1899963169b4SHans Petter Selasky * 1900963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor 1901963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size 1902963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor 1903963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint 1904963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new 1905963169b4SHans Petter Selasky * USB control request will be setup! 1906963169b4SHans Petter Selasky */ 1907963169b4SHans Petter Selasky switch (udev->speed) { 1908963169b4SHans Petter Selasky case USB_SPEED_FULL: 1909963169b4SHans Petter Selasky case USB_SPEED_LOW: 1910963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 1911963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 1912963169b4SHans Petter Selasky if (err != 0) { 1913963169b4SHans Petter Selasky DPRINTFN(0, "getting device descriptor " 1914963169b4SHans Petter Selasky "at addr %d failed, %s\n", udev->address, 1915963169b4SHans Petter Selasky usbd_errstr(err)); 1916963169b4SHans Petter Selasky return (err); 1917963169b4SHans Petter Selasky } 1918963169b4SHans Petter Selasky break; 1919963169b4SHans Petter Selasky default: 1920963169b4SHans Petter Selasky DPRINTF("Minimum MaxPacketSize is large enough " 1921963169b4SHans Petter Selasky "to hold the complete device descriptor\n"); 1922963169b4SHans Petter Selasky break; 1923963169b4SHans Petter Selasky } 1924963169b4SHans Petter Selasky 1925963169b4SHans Petter Selasky /* get the full device descriptor */ 1926963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1927963169b4SHans Petter Selasky 1928963169b4SHans Petter Selasky /* try one more time, if error */ 1929963169b4SHans Petter Selasky if (err) 1930963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1931963169b4SHans Petter Selasky 1932963169b4SHans Petter Selasky if (err) { 1933963169b4SHans Petter Selasky DPRINTF("addr=%d, getting full desc failed\n", 1934963169b4SHans Petter Selasky udev->address); 1935963169b4SHans Petter Selasky return (err); 1936963169b4SHans Petter Selasky } 1937963169b4SHans Petter Selasky 1938963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " 1939963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", 1940963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB), 1941963169b4SHans Petter Selasky udev->ddesc.bDeviceClass, 1942963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass, 1943963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol, 1944963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize, 1945963169b4SHans Petter Selasky udev->ddesc.bLength, 1946963169b4SHans Petter Selasky udev->speed); 1947963169b4SHans Petter Selasky 1948963169b4SHans Petter Selasky return (err); 1949963169b4SHans Petter Selasky } 1950963169b4SHans Petter Selasky 1951963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1952a593f6b8SAndrew Thompson * usbd_req_re_enumerate 195302ac6454SAndrew Thompson * 195402ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 195502ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 195602ac6454SAndrew Thompson * new configuration. 195702ac6454SAndrew Thompson * 195802ac6454SAndrew Thompson * Returns: 195902ac6454SAndrew Thompson * 0: Success 196002ac6454SAndrew Thompson * Else: Failure 196102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1962e0a69b51SAndrew Thompson usb_error_t 1963a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 196402ac6454SAndrew Thompson { 1965760bc48eSAndrew Thompson struct usb_device *parent_hub; 1966e0a69b51SAndrew Thompson usb_error_t err; 196702ac6454SAndrew Thompson uint8_t old_addr; 196802ac6454SAndrew Thompson uint8_t do_retry = 1; 196902ac6454SAndrew Thompson 1970f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 197102ac6454SAndrew Thompson return (USB_ERR_INVAL); 197202ac6454SAndrew Thompson } 197302ac6454SAndrew Thompson old_addr = udev->address; 197402ac6454SAndrew Thompson parent_hub = udev->parent_hub; 197502ac6454SAndrew Thompson if (parent_hub == NULL) { 197602ac6454SAndrew Thompson return (USB_ERR_INVAL); 197702ac6454SAndrew Thompson } 197802ac6454SAndrew Thompson retry: 19799eb0d702SHans Petter Selasky /* 19809eb0d702SHans Petter Selasky * Try to reset the High Speed parent HUB of a LOW- or FULL- 19819eb0d702SHans Petter Selasky * speed device, if any. 19829eb0d702SHans Petter Selasky */ 19839eb0d702SHans Petter Selasky if (udev->parent_hs_hub != NULL && 19849eb0d702SHans Petter Selasky udev->speed != USB_SPEED_HIGH) { 19859eb0d702SHans Petter Selasky DPRINTF("Trying to reset parent High Speed TT.\n"); 19869eb0d702SHans Petter Selasky err = usbd_req_reset_tt(udev->parent_hs_hub, NULL, 19879eb0d702SHans Petter Selasky udev->hs_port_no); 19889eb0d702SHans Petter Selasky if (err) { 19899eb0d702SHans Petter Selasky DPRINTF("Resetting parent High " 19909eb0d702SHans Petter Selasky "Speed TT failed (%s).\n", 19919eb0d702SHans Petter Selasky usbd_errstr(err)); 19929eb0d702SHans Petter Selasky } 19939eb0d702SHans Petter Selasky } 19949eb0d702SHans Petter Selasky 19959eb0d702SHans Petter Selasky /* Try to reset the parent HUB port. */ 1996a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 199702ac6454SAndrew Thompson if (err) { 199803797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 1999a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 200002ac6454SAndrew Thompson goto done; 200102ac6454SAndrew Thompson } 2002963169b4SHans Petter Selasky 200302ac6454SAndrew Thompson /* 200402ac6454SAndrew Thompson * After that the port has been reset our device should be at 200502ac6454SAndrew Thompson * address zero: 200602ac6454SAndrew Thompson */ 200702ac6454SAndrew Thompson udev->address = USB_START_ADDR; 200802ac6454SAndrew Thompson 200902ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 201002ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 201102ac6454SAndrew Thompson 2012963169b4SHans Petter Selasky /* reset USB state */ 2013963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED); 2014963169b4SHans Petter Selasky 201502ac6454SAndrew Thompson /* 201602ac6454SAndrew Thompson * Restore device address: 201702ac6454SAndrew Thompson */ 2018a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 201902ac6454SAndrew Thompson if (err) { 202002ac6454SAndrew Thompson /* XXX ignore any errors! */ 202103797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 2022a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 202302ac6454SAndrew Thompson } 2024963169b4SHans Petter Selasky /* 2025963169b4SHans Petter Selasky * Restore device address, if the controller driver did not 2026963169b4SHans Petter Selasky * set a new one: 2027963169b4SHans Petter Selasky */ 2028963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 202902ac6454SAndrew Thompson udev->address = old_addr; 203002ac6454SAndrew Thompson 2031963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */ 2032963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx); 203302ac6454SAndrew Thompson 203402ac6454SAndrew Thompson done: 203502ac6454SAndrew Thompson if (err && do_retry) { 203602ac6454SAndrew Thompson /* give the USB firmware some time to load */ 2037a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 203802ac6454SAndrew Thompson /* no more retries after this retry */ 203902ac6454SAndrew Thompson do_retry = 0; 204002ac6454SAndrew Thompson /* try again */ 204102ac6454SAndrew Thompson goto retry; 204202ac6454SAndrew Thompson } 204302ac6454SAndrew Thompson /* restore address */ 2044963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 204502ac6454SAndrew Thompson udev->address = old_addr; 2046963169b4SHans Petter Selasky /* update state, if successful */ 2047963169b4SHans Petter Selasky if (err == 0) 2048963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED); 204902ac6454SAndrew Thompson return (err); 205002ac6454SAndrew Thompson } 205102ac6454SAndrew Thompson 205202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2053a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 205402ac6454SAndrew Thompson * 205502ac6454SAndrew Thompson * Returns: 205602ac6454SAndrew Thompson * 0: Success 205702ac6454SAndrew Thompson * Else: Failure 205802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2059e0a69b51SAndrew Thompson usb_error_t 2060a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 206102ac6454SAndrew Thompson uint16_t sel) 206202ac6454SAndrew Thompson { 2063760bc48eSAndrew Thompson struct usb_device_request req; 206402ac6454SAndrew Thompson 206502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 206602ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 206702ac6454SAndrew Thompson USETW(req.wValue, sel); 206802ac6454SAndrew Thompson USETW(req.wIndex, 0); 206902ac6454SAndrew Thompson USETW(req.wLength, 0); 2070a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 207102ac6454SAndrew Thompson } 207202ac6454SAndrew Thompson 207302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2074a593f6b8SAndrew Thompson * usbd_req_set_device_feature 207502ac6454SAndrew Thompson * 207602ac6454SAndrew Thompson * Returns: 207702ac6454SAndrew Thompson * 0: Success 207802ac6454SAndrew Thompson * Else: Failure 207902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2080e0a69b51SAndrew Thompson usb_error_t 2081a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 208202ac6454SAndrew Thompson uint16_t sel) 208302ac6454SAndrew Thompson { 2084760bc48eSAndrew Thompson struct usb_device_request req; 208502ac6454SAndrew Thompson 208602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 208702ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 208802ac6454SAndrew Thompson USETW(req.wValue, sel); 208902ac6454SAndrew Thompson USETW(req.wIndex, 0); 209002ac6454SAndrew Thompson USETW(req.wLength, 0); 2091a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 209202ac6454SAndrew Thompson } 20939eb0d702SHans Petter Selasky 20949eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 20959eb0d702SHans Petter Selasky * usbd_req_reset_tt 20969eb0d702SHans Petter Selasky * 20979eb0d702SHans Petter Selasky * Returns: 20989eb0d702SHans Petter Selasky * 0: Success 20999eb0d702SHans Petter Selasky * Else: Failure 21009eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 21019eb0d702SHans Petter Selasky usb_error_t 21029eb0d702SHans Petter Selasky usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, 21039eb0d702SHans Petter Selasky uint8_t port) 21049eb0d702SHans Petter Selasky { 21059eb0d702SHans Petter Selasky struct usb_device_request req; 21069eb0d702SHans Petter Selasky 21079eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 21089eb0d702SHans Petter Selasky 21099eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 21109eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 21119eb0d702SHans Petter Selasky port = 1; 21129eb0d702SHans Petter Selasky 21139eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 21149eb0d702SHans Petter Selasky req.bRequest = UR_RESET_TT; 21159eb0d702SHans Petter Selasky USETW(req.wValue, 0); 21169eb0d702SHans Petter Selasky req.wIndex[0] = port; 21179eb0d702SHans Petter Selasky req.wIndex[1] = 0; 21189eb0d702SHans Petter Selasky USETW(req.wLength, 0); 21199eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 21209eb0d702SHans Petter Selasky } 21219eb0d702SHans Petter Selasky 21229eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 21239eb0d702SHans Petter Selasky * usbd_req_clear_tt_buffer 21249eb0d702SHans Petter Selasky * 21259eb0d702SHans Petter Selasky * For single TT HUBs the port should be 1. 21269eb0d702SHans Petter Selasky * 21279eb0d702SHans Petter Selasky * Returns: 21289eb0d702SHans Petter Selasky * 0: Success 21299eb0d702SHans Petter Selasky * Else: Failure 21309eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 21319eb0d702SHans Petter Selasky usb_error_t 21329eb0d702SHans Petter Selasky usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, 21339eb0d702SHans Petter Selasky uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) 21349eb0d702SHans Petter Selasky { 21359eb0d702SHans Petter Selasky struct usb_device_request req; 21369eb0d702SHans Petter Selasky uint16_t wValue; 21379eb0d702SHans Petter Selasky 21389eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 21399eb0d702SHans Petter Selasky 21409eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 21419eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 21429eb0d702SHans Petter Selasky port = 1; 21439eb0d702SHans Petter Selasky 21449eb0d702SHans Petter Selasky wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | 21459eb0d702SHans Petter Selasky ((endpoint & 0x80) << 8) | ((type & 3) << 12); 21469eb0d702SHans Petter Selasky 21479eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 21489eb0d702SHans Petter Selasky req.bRequest = UR_CLEAR_TT_BUFFER; 21499eb0d702SHans Petter Selasky USETW(req.wValue, wValue); 21509eb0d702SHans Petter Selasky req.wIndex[0] = port; 21519eb0d702SHans Petter Selasky req.wIndex[1] = 0; 21529eb0d702SHans Petter Selasky USETW(req.wLength, 0); 21539eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 21549eb0d702SHans Petter Selasky } 2155