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/linker_set.h> 38ed6d949aSAndrew Thompson #include <sys/module.h> 39ed6d949aSAndrew Thompson #include <sys/lock.h> 40ed6d949aSAndrew Thompson #include <sys/mutex.h> 41ed6d949aSAndrew Thompson #include <sys/condvar.h> 42ed6d949aSAndrew Thompson #include <sys/sysctl.h> 43ed6d949aSAndrew Thompson #include <sys/sx.h> 44ed6d949aSAndrew Thompson #include <sys/unistd.h> 45ed6d949aSAndrew Thompson #include <sys/callout.h> 46ed6d949aSAndrew Thompson #include <sys/malloc.h> 47ed6d949aSAndrew Thompson #include <sys/priv.h> 48ed6d949aSAndrew Thompson 4902ac6454SAndrew Thompson #include <dev/usb/usb.h> 50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 51ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5202ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 5302ac6454SAndrew Thompson #include <dev/usb/usbhid.h> 5402ac6454SAndrew Thompson 55a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5602ac6454SAndrew Thompson 5702ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6602ac6454SAndrew Thompson 6702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6902ac6454SAndrew Thompson #include <sys/ctype.h> 7002ac6454SAndrew Thompson 71b850ecc1SAndrew Thompson #ifdef USB_DEBUG 72a593f6b8SAndrew Thompson static int usb_pr_poll_delay = USB_PORT_RESET_DELAY; 73a593f6b8SAndrew Thompson static int usb_pr_recovery_delay = USB_PORT_RESET_RECOVERY; 7402ac6454SAndrew Thompson 759360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW, 76a593f6b8SAndrew Thompson &usb_pr_poll_delay, 0, "USB port reset poll delay in ms"); 779360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW, 78a593f6b8SAndrew Thompson &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms"); 79f6980be8SAndrew Thompson 80f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 81f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */ 82f6980be8SAndrew Thompson struct usb_ctrl_debug { 83f6980be8SAndrew Thompson int bus_index; /* target bus */ 84f6980be8SAndrew Thompson int dev_index; /* target address */ 85f6980be8SAndrew Thompson int ds_fail; /* fail data stage */ 86f6980be8SAndrew Thompson int ss_fail; /* fail data stage */ 87f6980be8SAndrew Thompson int ds_delay; /* data stage delay in ms */ 88f6980be8SAndrew Thompson int ss_delay; /* status stage delay in ms */ 89f6980be8SAndrew Thompson int bmRequestType_value; 90f6980be8SAndrew Thompson int bRequest_value; 91f6980be8SAndrew Thompson }; 92f6980be8SAndrew Thompson 93f6980be8SAndrew Thompson struct usb_ctrl_debug_bits { 94f6980be8SAndrew Thompson uint16_t ds_delay; 95f6980be8SAndrew Thompson uint16_t ss_delay; 96f6980be8SAndrew Thompson uint8_t ds_fail:1; 97f6980be8SAndrew Thompson uint8_t ss_fail:1; 98f6980be8SAndrew Thompson uint8_t enabled:1; 99f6980be8SAndrew Thompson }; 100f6980be8SAndrew Thompson 101f6980be8SAndrew Thompson /* The default is to disable fault injection. */ 102f6980be8SAndrew Thompson 103f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = { 104f6980be8SAndrew Thompson .bus_index = -1, 105f6980be8SAndrew Thompson .dev_index = -1, 106f6980be8SAndrew Thompson .bmRequestType_value = -1, 107f6980be8SAndrew Thompson .bRequest_value = -1, 108f6980be8SAndrew Thompson }; 109f6980be8SAndrew Thompson 110f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RW, 111f6980be8SAndrew Thompson &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); 112f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RW, 113f6980be8SAndrew Thompson &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); 114f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RW, 115f6980be8SAndrew Thompson &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); 116f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RW, 117f6980be8SAndrew Thompson &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); 118f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RW, 119f6980be8SAndrew Thompson &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); 120f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RW, 121f6980be8SAndrew Thompson &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); 122f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RW, 123f6980be8SAndrew Thompson &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); 124f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RW, 125f6980be8SAndrew Thompson &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); 126f6980be8SAndrew Thompson 127f6980be8SAndrew Thompson /*------------------------------------------------------------------------* 128f6980be8SAndrew Thompson * usbd_get_debug_bits 129f6980be8SAndrew Thompson * 130f6980be8SAndrew Thompson * This function is only useful in USB host mode. 131f6980be8SAndrew Thompson *------------------------------------------------------------------------*/ 132f6980be8SAndrew Thompson static void 133f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, 134f6980be8SAndrew Thompson struct usb_ctrl_debug_bits *dbg) 135f6980be8SAndrew Thompson { 136f6980be8SAndrew Thompson int temp; 137f6980be8SAndrew Thompson 138f6980be8SAndrew Thompson memset(dbg, 0, sizeof(*dbg)); 139f6980be8SAndrew Thompson 140f6980be8SAndrew Thompson /* Compute data stage delay */ 141f6980be8SAndrew Thompson 142f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_delay; 143f6980be8SAndrew Thompson if (temp < 0) 144f6980be8SAndrew Thompson temp = 0; 145f6980be8SAndrew Thompson else if (temp > (16*1024)) 146f6980be8SAndrew Thompson temp = (16*1024); 147f6980be8SAndrew Thompson 148f6980be8SAndrew Thompson dbg->ds_delay = temp; 149f6980be8SAndrew Thompson 150f6980be8SAndrew Thompson /* Compute status stage delay */ 151f6980be8SAndrew Thompson 152f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_delay; 153f6980be8SAndrew Thompson if (temp < 0) 154f6980be8SAndrew Thompson temp = 0; 155f6980be8SAndrew Thompson else if (temp > (16*1024)) 156f6980be8SAndrew Thompson temp = (16*1024); 157f6980be8SAndrew Thompson 158f6980be8SAndrew Thompson dbg->ss_delay = temp; 159f6980be8SAndrew Thompson 160f6980be8SAndrew Thompson /* Check if this control request should be failed */ 161f6980be8SAndrew Thompson 162f6980be8SAndrew Thompson if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) 163f6980be8SAndrew Thompson return; 164f6980be8SAndrew Thompson 165f6980be8SAndrew Thompson if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) 166f6980be8SAndrew Thompson return; 167f6980be8SAndrew Thompson 168f6980be8SAndrew Thompson temp = usb_ctrl_debug.bmRequestType_value; 169f6980be8SAndrew Thompson 170f6980be8SAndrew Thompson if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) 171f6980be8SAndrew Thompson return; 172f6980be8SAndrew Thompson 173f6980be8SAndrew Thompson temp = usb_ctrl_debug.bRequest_value; 174f6980be8SAndrew Thompson 175f6980be8SAndrew Thompson if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) 176f6980be8SAndrew Thompson return; 177f6980be8SAndrew Thompson 178f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_fail; 179f6980be8SAndrew Thompson if (temp) 180f6980be8SAndrew Thompson dbg->ds_fail = 1; 181f6980be8SAndrew Thompson 182f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_fail; 183f6980be8SAndrew Thompson if (temp) 184f6980be8SAndrew Thompson dbg->ss_fail = 1; 185f6980be8SAndrew Thompson 186f6980be8SAndrew Thompson dbg->enabled = 1; 187f6980be8SAndrew Thompson } 188f6980be8SAndrew Thompson #endif /* USB_REQ_DEBUG */ 189f6980be8SAndrew Thompson #endif /* USB_DEBUG */ 19002ac6454SAndrew Thompson 19102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 192a593f6b8SAndrew Thompson * usbd_do_request_callback 19302ac6454SAndrew Thompson * 19402ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control 19502ac6454SAndrew Thompson * transfers. 19602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19702ac6454SAndrew Thompson void 198ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) 19902ac6454SAndrew Thompson { 20002ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 20102ac6454SAndrew Thompson 20202ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer)); 20302ac6454SAndrew Thompson 20402ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 20502ac6454SAndrew Thompson case USB_ST_SETUP: 206a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 20702ac6454SAndrew Thompson break; 20802ac6454SAndrew Thompson default: 20991cd9240SAndrew Thompson cv_signal(&xfer->xroot->udev->ctrlreq_cv); 21002ac6454SAndrew Thompson break; 21102ac6454SAndrew Thompson } 21202ac6454SAndrew Thompson } 21302ac6454SAndrew Thompson 21402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 215a593f6b8SAndrew Thompson * usb_do_clear_stall_callback 21602ac6454SAndrew Thompson * 21702ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests. 21802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 21902ac6454SAndrew Thompson void 220ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 22102ac6454SAndrew Thompson { 222760bc48eSAndrew Thompson struct usb_device_request req; 223760bc48eSAndrew Thompson struct usb_device *udev; 224ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 225ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end; 226ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first; 22763521bbcSAndrew Thompson uint8_t to; 22802ac6454SAndrew Thompson 22902ac6454SAndrew Thompson udev = xfer->xroot->udev; 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 23202ac6454SAndrew Thompson 233ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */ 23402ac6454SAndrew Thompson 235ae60fdfbSAndrew Thompson ep = udev->ep_curr; 236ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max; 237ae60fdfbSAndrew Thompson ep_first = udev->endpoints; 238ae60fdfbSAndrew Thompson to = udev->endpoints_max; 239115df0b6SAndrew Thompson 24002ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 24102ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 242ae60fdfbSAndrew Thompson if (ep == NULL) 243115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */ 244ae60fdfbSAndrew Thompson if (ep->edesc && 245ae60fdfbSAndrew Thompson ep->is_stalled) { 246ae60fdfbSAndrew Thompson ep->toggle_next = 0; 247ae60fdfbSAndrew Thompson ep->is_stalled = 0; 248963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */ 249963169b4SHans Petter Selasky usbd_clear_stall_locked(udev, ep); 25002ac6454SAndrew Thompson /* start up the current or next transfer, if any */ 251a593f6b8SAndrew Thompson usb_command_wrapper(&ep->endpoint_q, 252ae60fdfbSAndrew Thompson ep->endpoint_q.curr); 25302ac6454SAndrew Thompson } 254ae60fdfbSAndrew Thompson ep++; 25502ac6454SAndrew Thompson 25602ac6454SAndrew Thompson case USB_ST_SETUP: 25702ac6454SAndrew Thompson tr_setup: 258115df0b6SAndrew Thompson if (to == 0) 259ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */ 260ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end)) 261ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */ 262ae60fdfbSAndrew Thompson if (ep->edesc && 263ae60fdfbSAndrew Thompson ep->is_stalled) { 26402ac6454SAndrew Thompson 26502ac6454SAndrew Thompson /* setup a clear-stall packet */ 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 26802ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 26902ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 270ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 27102ac6454SAndrew Thompson req.wIndex[1] = 0; 27202ac6454SAndrew Thompson USETW(req.wLength, 0); 27302ac6454SAndrew Thompson 27402ac6454SAndrew Thompson /* copy in the transfer */ 27502ac6454SAndrew Thompson 276a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson /* set length */ 279ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 28002ac6454SAndrew Thompson xfer->nframes = 1; 28102ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 28202ac6454SAndrew Thompson 283a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 28402ac6454SAndrew Thompson 28502ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 28602ac6454SAndrew Thompson break; 28702ac6454SAndrew Thompson } 288ae60fdfbSAndrew Thompson ep++; 289115df0b6SAndrew Thompson to--; 29002ac6454SAndrew Thompson goto tr_setup; 29102ac6454SAndrew Thompson 29202ac6454SAndrew Thompson default: 29302ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 29402ac6454SAndrew Thompson break; 29502ac6454SAndrew Thompson } 29602ac6454SAndrew Thompson goto tr_setup; 29702ac6454SAndrew Thompson } 29802ac6454SAndrew Thompson 299ae60fdfbSAndrew Thompson /* store current endpoint */ 300ae60fdfbSAndrew Thompson udev->ep_curr = ep; 30102ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 30202ac6454SAndrew Thompson } 30302ac6454SAndrew Thompson 304e0a69b51SAndrew Thompson static usb_handle_req_t * 305a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev) 306459d369eSAndrew Thompson { 307459d369eSAndrew Thompson /* figure out if there is a Handle Request function */ 308f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE) 309a593f6b8SAndrew Thompson return (usb_temp_get_desc_p); 310459d369eSAndrew Thompson else if (udev->parent_hub == NULL) 311459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec); 312459d369eSAndrew Thompson else 313459d369eSAndrew Thompson return (NULL); 314459d369eSAndrew Thompson } 315459d369eSAndrew Thompson 31602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 317a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request 31802ac6454SAndrew Thompson * 31902ac6454SAndrew Thompson * Description of arguments passed to these functions: 32002ac6454SAndrew Thompson * 321760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the 32202ac6454SAndrew Thompson * request should be performed. It is possible to call this function 32302ac6454SAndrew Thompson * in both Host Side mode and Device Side mode. 32402ac6454SAndrew Thompson * 32502ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it 32602ac6454SAndrew Thompson * will get dropped and picked up during the execution of this 32702ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this 32802ac6454SAndrew Thompson * argument is NULL it has no effect. 32902ac6454SAndrew Thompson * 33002ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an 33102ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB 33202ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB 33302ac6454SAndrew Thompson * request, if it is a read or a write. 33402ac6454SAndrew Thompson * 33502ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req" 33602ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which 33702ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can 33802ac6454SAndrew Thompson * be NULL. 33902ac6454SAndrew Thompson * 34002ac6454SAndrew Thompson * "flags" - here is a list of valid flags: 34102ac6454SAndrew Thompson * 34202ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than 34302ac6454SAndrew Thompson * specified 34402ac6454SAndrew Thompson * 34502ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed 34602ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay" 34702ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging. 34802ac6454SAndrew Thompson * 34902ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland 35002ac6454SAndrew Thompson * pointer. 35102ac6454SAndrew Thompson * 35202ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in 35302ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This 35402ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is 35502ac6454SAndrew Thompson * used. 35602ac6454SAndrew Thompson * 35702ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in 35802ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is 35902ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater 36002ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack 36102ac6454SAndrew Thompson * does not allow control requests without a timeout. 36202ac6454SAndrew Thompson * 36302ac6454SAndrew Thompson * NOTE: This function is thread safe. All calls to 364a593f6b8SAndrew Thompson * "usbd_do_request_flags" will be serialised by the use of an 36502ac6454SAndrew Thompson * internal "sx_lock". 36602ac6454SAndrew Thompson * 36702ac6454SAndrew Thompson * Returns: 36802ac6454SAndrew Thompson * 0: Success 36902ac6454SAndrew Thompson * Else: Failure 37002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 371e0a69b51SAndrew Thompson usb_error_t 372a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, 373760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 374e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 37502ac6454SAndrew Thompson { 376f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 377f6980be8SAndrew Thompson struct usb_ctrl_debug_bits dbg; 378f6980be8SAndrew Thompson #endif 379e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 380760bc48eSAndrew Thompson struct usb_xfer *xfer; 38102ac6454SAndrew Thompson const void *desc; 38202ac6454SAndrew Thompson int err = 0; 383e0a69b51SAndrew Thompson usb_ticks_t start_ticks; 384e0a69b51SAndrew Thompson usb_ticks_t delta_ticks; 385e0a69b51SAndrew Thompson usb_ticks_t max_ticks; 38602ac6454SAndrew Thompson uint16_t length; 38702ac6454SAndrew Thompson uint16_t temp; 388f6980be8SAndrew Thompson uint16_t acttemp; 3892df1e9a6SAndrew Thompson uint8_t enum_locked; 39002ac6454SAndrew Thompson 39102ac6454SAndrew Thompson if (timeout < 50) { 39202ac6454SAndrew Thompson /* timeout is too small */ 39302ac6454SAndrew Thompson timeout = 50; 39402ac6454SAndrew Thompson } 39502ac6454SAndrew Thompson if (timeout > 30000) { 39602ac6454SAndrew Thompson /* timeout is too big */ 39702ac6454SAndrew Thompson timeout = 30000; 39802ac6454SAndrew Thompson } 39902ac6454SAndrew Thompson length = UGETW(req->wLength); 40002ac6454SAndrew Thompson 4012df1e9a6SAndrew Thompson enum_locked = usbd_enum_is_locked(udev); 4022df1e9a6SAndrew Thompson 40302ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " 40402ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", 40502ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest, 40602ac6454SAndrew Thompson req->wValue[1], req->wValue[0], 40702ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0], 40802ac6454SAndrew Thompson req->wLength[1], req->wLength[0]); 40902ac6454SAndrew Thompson 410bd216778SAndrew Thompson /* Check if the device is still alive */ 411bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) { 412bd216778SAndrew Thompson DPRINTF("usb device has gone\n"); 413bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED); 414bd216778SAndrew Thompson } 415bd216778SAndrew Thompson 41602ac6454SAndrew Thompson /* 41702ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not 41802ac6454SAndrew Thompson * check the return value: 41902ac6454SAndrew Thompson */ 42039307315SAndrew Thompson if (actlen) 42102ac6454SAndrew Thompson *actlen = 0; 42239307315SAndrew Thompson 423bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0) 424bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR) 425bdc081c6SAndrew Thompson return (USB_ERR_INVAL); 426bdc081c6SAndrew Thompson #endif 4272df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) { 42802ac6454SAndrew Thompson mtx_unlock(mtx); 42902ac6454SAndrew Thompson mtx_assert(mtx, MA_NOTOWNED); 43002ac6454SAndrew Thompson } 4312df1e9a6SAndrew Thompson 4322df1e9a6SAndrew Thompson /* 4332df1e9a6SAndrew Thompson * We need to allow suspend and resume at this point, else the 4342df1e9a6SAndrew Thompson * control transfer will timeout if the device is suspended! 4352df1e9a6SAndrew Thompson */ 4362df1e9a6SAndrew Thompson if (enum_locked) 4372df1e9a6SAndrew Thompson usbd_sr_unlock(udev); 4382df1e9a6SAndrew Thompson 43902ac6454SAndrew Thompson /* 44002ac6454SAndrew Thompson * Grab the default sx-lock so that serialisation 44102ac6454SAndrew Thompson * is achieved when multiple threads are involved: 44202ac6454SAndrew Thompson */ 44391cd9240SAndrew Thompson sx_xlock(&udev->ctrl_sx); 44402ac6454SAndrew Thompson 445a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 44639307315SAndrew Thompson 447459d369eSAndrew Thompson if (hr_func != NULL) { 448459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n"); 44939307315SAndrew Thompson 450459d369eSAndrew Thompson desc = NULL; 451459d369eSAndrew Thompson temp = 0; 452459d369eSAndrew Thompson 453459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 45439307315SAndrew Thompson if (length != 0) { 455459d369eSAndrew Thompson DPRINTFN(1, "The handle request function " 456459d369eSAndrew Thompson "does not support writing data!\n"); 45739307315SAndrew Thompson err = USB_ERR_INVAL; 45839307315SAndrew Thompson goto done; 45939307315SAndrew Thompson } 46039307315SAndrew Thompson } 461459d369eSAndrew Thompson 462459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */ 46339307315SAndrew Thompson 46439307315SAndrew Thompson USB_BUS_LOCK(udev->bus); 465459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp); 46639307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 46739307315SAndrew Thompson 46839307315SAndrew Thompson if (err) 46939307315SAndrew Thompson goto done; 47039307315SAndrew Thompson 471459d369eSAndrew Thompson if (length > temp) { 47239307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) { 47339307315SAndrew Thompson err = USB_ERR_SHORT_XFER; 47439307315SAndrew Thompson goto done; 47539307315SAndrew Thompson } 476459d369eSAndrew Thompson length = temp; 47739307315SAndrew Thompson } 47839307315SAndrew Thompson if (actlen) 47939307315SAndrew Thompson *actlen = length; 48039307315SAndrew Thompson 48139307315SAndrew Thompson if (length > 0) { 48239307315SAndrew Thompson #if USB_HAVE_USER_IO 48339307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 484459d369eSAndrew Thompson if (copyout(desc, data, length)) { 48539307315SAndrew Thompson err = USB_ERR_INVAL; 48639307315SAndrew Thompson goto done; 48739307315SAndrew Thompson } 48839307315SAndrew Thompson } else 48939307315SAndrew Thompson #endif 490459d369eSAndrew Thompson bcopy(desc, data, length); 49139307315SAndrew Thompson } 492459d369eSAndrew Thompson goto done; /* success */ 49339307315SAndrew Thompson } 49439307315SAndrew Thompson 49502ac6454SAndrew Thompson /* 49602ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any: 49702ac6454SAndrew Thompson */ 4985b3bb704SAndrew Thompson usbd_ctrl_transfer_setup(udev); 49902ac6454SAndrew Thompson 5005b3bb704SAndrew Thompson xfer = udev->ctrl_xfer[0]; 50102ac6454SAndrew Thompson if (xfer == NULL) { 50202ac6454SAndrew Thompson /* most likely out of memory */ 50302ac6454SAndrew Thompson err = USB_ERR_NOMEM; 50402ac6454SAndrew Thompson goto done; 50502ac6454SAndrew Thompson } 506f6980be8SAndrew Thompson 507f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 508f6980be8SAndrew Thompson /* Get debug bits */ 509f6980be8SAndrew Thompson usbd_get_debug_bits(udev, req, &dbg); 510f6980be8SAndrew Thompson 511f6980be8SAndrew Thompson /* Check for fault injection */ 512f6980be8SAndrew Thompson if (dbg.enabled) 513f6980be8SAndrew Thompson flags |= USB_DELAY_STATUS_STAGE; 514f6980be8SAndrew Thompson #endif 51502ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 51602ac6454SAndrew Thompson 5174eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE) 51802ac6454SAndrew Thompson xfer->flags.manual_status = 1; 5194eae601eSAndrew Thompson else 52002ac6454SAndrew Thompson xfer->flags.manual_status = 0; 5214eae601eSAndrew Thompson 5224eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK) 5234eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1; 5244eae601eSAndrew Thompson else 5254eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0; 52602ac6454SAndrew Thompson 52702ac6454SAndrew Thompson xfer->timeout = timeout; 52802ac6454SAndrew Thompson 52902ac6454SAndrew Thompson start_ticks = ticks; 53002ac6454SAndrew Thompson 53102ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout); 53202ac6454SAndrew Thompson 533a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); 53402ac6454SAndrew Thompson 535ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); 53602ac6454SAndrew Thompson 53702ac6454SAndrew Thompson while (1) { 53802ac6454SAndrew Thompson temp = length; 539f6980be8SAndrew Thompson if (temp > usbd_xfer_max_len(xfer)) { 540ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer); 54102ac6454SAndrew Thompson } 542f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 543f6980be8SAndrew Thompson if (xfer->flags.manual_status) { 544f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) != 0) { 545f6980be8SAndrew Thompson /* Execute data stage separately */ 546f6980be8SAndrew Thompson temp = 0; 547f6980be8SAndrew Thompson } else if (temp > 0) { 548f6980be8SAndrew Thompson if (dbg.ds_fail) { 549f6980be8SAndrew Thompson err = USB_ERR_INVAL; 550f6980be8SAndrew Thompson break; 551f6980be8SAndrew Thompson } 552f6980be8SAndrew Thompson if (dbg.ds_delay > 0) { 553f6980be8SAndrew Thompson usb_pause_mtx( 554f6980be8SAndrew Thompson xfer->xroot->xfer_mtx, 555f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ds_delay)); 556f6980be8SAndrew Thompson /* make sure we don't time out */ 557f6980be8SAndrew Thompson start_ticks = ticks; 558f6980be8SAndrew Thompson } 559f6980be8SAndrew Thompson } 560f6980be8SAndrew Thompson } 561f6980be8SAndrew Thompson #endif 562ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp); 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson if (temp > 0) { 56502ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 566bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 56702ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 56802ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 569a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1, 57002ac6454SAndrew Thompson 0, data, temp); 57102ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 57202ac6454SAndrew Thompson if (err) { 57302ac6454SAndrew Thompson err = USB_ERR_INVAL; 57402ac6454SAndrew Thompson break; 57502ac6454SAndrew Thompson } 576bdc081c6SAndrew Thompson } else 577bdc081c6SAndrew Thompson #endif 578a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 579bdc081c6SAndrew Thompson 0, data, temp); 58002ac6454SAndrew Thompson } 581f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 2); 58202ac6454SAndrew Thompson } else { 583f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) == 0) { 58402ac6454SAndrew Thompson if (xfer->flags.manual_status) { 585f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 586f6980be8SAndrew Thompson if (dbg.ss_fail) { 587f6980be8SAndrew Thompson err = USB_ERR_INVAL; 588f6980be8SAndrew Thompson break; 58902ac6454SAndrew Thompson } 590f6980be8SAndrew Thompson if (dbg.ss_delay > 0) { 591a593f6b8SAndrew Thompson usb_pause_mtx( 59202ac6454SAndrew Thompson xfer->xroot->xfer_mtx, 593f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ss_delay)); 594f6980be8SAndrew Thompson /* make sure we don't time out */ 595f6980be8SAndrew Thompson start_ticks = ticks; 59602ac6454SAndrew Thompson } 59702ac6454SAndrew Thompson #endif 59802ac6454SAndrew Thompson xfer->flags.manual_status = 0; 59902ac6454SAndrew Thompson } else { 60002ac6454SAndrew Thompson break; 60102ac6454SAndrew Thompson } 60202ac6454SAndrew Thompson } 603f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 1); 60402ac6454SAndrew Thompson } 60502ac6454SAndrew Thompson 606a593f6b8SAndrew Thompson usbd_transfer_start(xfer); 60702ac6454SAndrew Thompson 608a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) { 60991cd9240SAndrew Thompson cv_wait(&udev->ctrlreq_cv, 61002ac6454SAndrew Thompson xfer->xroot->xfer_mtx); 61102ac6454SAndrew Thompson } 61202ac6454SAndrew Thompson 61302ac6454SAndrew Thompson err = xfer->error; 61402ac6454SAndrew Thompson 61502ac6454SAndrew Thompson if (err) { 61602ac6454SAndrew Thompson break; 61702ac6454SAndrew Thompson } 61802ac6454SAndrew Thompson 619f6980be8SAndrew Thompson /* get actual length of DATA stage */ 620f6980be8SAndrew Thompson 621f6980be8SAndrew Thompson if (xfer->aframes < 2) { 622f6980be8SAndrew Thompson acttemp = 0; 62302ac6454SAndrew Thompson } else { 624f6980be8SAndrew Thompson acttemp = usbd_xfer_frame_len(xfer, 1); 62502ac6454SAndrew Thompson } 62602ac6454SAndrew Thompson 62702ac6454SAndrew Thompson /* check for short packet */ 62802ac6454SAndrew Thompson 629f6980be8SAndrew Thompson if (temp > acttemp) { 630f6980be8SAndrew Thompson temp = acttemp; 63102ac6454SAndrew Thompson length = temp; 63202ac6454SAndrew Thompson } 63302ac6454SAndrew Thompson if (temp > 0) { 63402ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) { 635bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 63602ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 63702ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 638a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1, 63902ac6454SAndrew Thompson 0, data, temp); 64002ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 64102ac6454SAndrew Thompson if (err) { 64202ac6454SAndrew Thompson err = USB_ERR_INVAL; 64302ac6454SAndrew Thompson break; 64402ac6454SAndrew Thompson } 645bdc081c6SAndrew Thompson } else 646bdc081c6SAndrew Thompson #endif 647a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1, 64802ac6454SAndrew Thompson 0, data, temp); 64902ac6454SAndrew Thompson } 65002ac6454SAndrew Thompson } 65102ac6454SAndrew Thompson /* 65202ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup 65302ac6454SAndrew Thompson * packet again: 65402ac6454SAndrew Thompson */ 655ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 65602ac6454SAndrew Thompson 65702ac6454SAndrew Thompson /* update length and data pointer */ 65802ac6454SAndrew Thompson length -= temp; 65902ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp); 66002ac6454SAndrew Thompson 66102ac6454SAndrew Thompson if (actlen) { 66202ac6454SAndrew Thompson (*actlen) += temp; 66302ac6454SAndrew Thompson } 66402ac6454SAndrew Thompson /* check for timeout */ 66502ac6454SAndrew Thompson 66602ac6454SAndrew Thompson delta_ticks = ticks - start_ticks; 66702ac6454SAndrew Thompson if (delta_ticks > max_ticks) { 66802ac6454SAndrew Thompson if (!err) { 66902ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson } 67202ac6454SAndrew Thompson if (err) { 67302ac6454SAndrew Thompson break; 67402ac6454SAndrew Thompson } 67502ac6454SAndrew Thompson } 67602ac6454SAndrew Thompson 67702ac6454SAndrew Thompson if (err) { 67802ac6454SAndrew Thompson /* 67902ac6454SAndrew Thompson * Make sure that the control endpoint is no longer 68002ac6454SAndrew Thompson * blocked in case of a non-transfer related error: 68102ac6454SAndrew Thompson */ 682a593f6b8SAndrew Thompson usbd_transfer_stop(xfer); 68302ac6454SAndrew Thompson } 68402ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 68502ac6454SAndrew Thompson 68602ac6454SAndrew Thompson done: 68791cd9240SAndrew Thompson sx_xunlock(&udev->ctrl_sx); 68802ac6454SAndrew Thompson 6892df1e9a6SAndrew Thompson if (enum_locked) 6902df1e9a6SAndrew Thompson usbd_sr_lock(udev); 6912df1e9a6SAndrew Thompson 6922df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) 69302ac6454SAndrew Thompson mtx_lock(mtx); 6942df1e9a6SAndrew Thompson 695e0a69b51SAndrew Thompson return ((usb_error_t)err); 69602ac6454SAndrew Thompson } 69702ac6454SAndrew Thompson 69802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 699a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code 70002ac6454SAndrew Thompson * 70102ac6454SAndrew Thompson * This function is factored out code. It does basically the same like 702a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the 70302ac6454SAndrew Thompson * passed process argument before doing the USB request. If the 70402ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It 70502ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked 70602ac6454SAndrew Thompson * when calling this function. 70702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 708e0a69b51SAndrew Thompson usb_error_t 709a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, 710760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 711e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 71202ac6454SAndrew Thompson { 713e0a69b51SAndrew Thompson usb_error_t err; 71402ac6454SAndrew Thompson uint16_t len; 71502ac6454SAndrew Thompson 71602ac6454SAndrew Thompson /* get request data length */ 71702ac6454SAndrew Thompson len = UGETW(req->wLength); 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson /* check if the device is being detached */ 720a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) { 72102ac6454SAndrew Thompson err = USB_ERR_IOERROR; 72202ac6454SAndrew Thompson goto done; 72302ac6454SAndrew Thompson } 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson /* forward the USB request */ 726a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx, 72702ac6454SAndrew Thompson req, data, flags, actlen, timeout); 72802ac6454SAndrew Thompson 72902ac6454SAndrew Thompson done: 73002ac6454SAndrew Thompson /* on failure we zero the data */ 73102ac6454SAndrew Thompson /* on short packet we zero the unused data */ 73202ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { 73302ac6454SAndrew Thompson if (err) 73402ac6454SAndrew Thompson memset(data, 0, len); 73502ac6454SAndrew Thompson else if (actlen && *actlen != len) 73602ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen); 73702ac6454SAndrew Thompson } 73802ac6454SAndrew Thompson return (err); 73902ac6454SAndrew Thompson } 74002ac6454SAndrew Thompson 74102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 742a593f6b8SAndrew Thompson * usbd_req_reset_port 74302ac6454SAndrew Thompson * 744cbb75751SHans Petter Selasky * This function will instruct a USB HUB to perform a reset sequence 74502ac6454SAndrew Thompson * on the specified port number. 74602ac6454SAndrew Thompson * 74702ac6454SAndrew Thompson * Returns: 74802ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero. 74902ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be 75002ac6454SAndrew Thompson * disabled. 75102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 752e0a69b51SAndrew Thompson usb_error_t 753a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 75402ac6454SAndrew Thompson { 755760bc48eSAndrew Thompson struct usb_port_status ps; 756e0a69b51SAndrew Thompson usb_error_t err; 75702ac6454SAndrew Thompson uint16_t n; 75802ac6454SAndrew Thompson 759b850ecc1SAndrew Thompson #ifdef USB_DEBUG 76002ac6454SAndrew Thompson uint16_t pr_poll_delay; 76102ac6454SAndrew Thompson uint16_t pr_recovery_delay; 76202ac6454SAndrew Thompson 76302ac6454SAndrew Thompson #endif 764a593f6b8SAndrew Thompson err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET); 76502ac6454SAndrew Thompson if (err) { 76602ac6454SAndrew Thompson goto done; 76702ac6454SAndrew Thompson } 768b850ecc1SAndrew Thompson #ifdef USB_DEBUG 76902ac6454SAndrew Thompson /* range check input parameters */ 770a593f6b8SAndrew Thompson pr_poll_delay = usb_pr_poll_delay; 77102ac6454SAndrew Thompson if (pr_poll_delay < 1) { 77202ac6454SAndrew Thompson pr_poll_delay = 1; 77302ac6454SAndrew Thompson } else if (pr_poll_delay > 1000) { 77402ac6454SAndrew Thompson pr_poll_delay = 1000; 77502ac6454SAndrew Thompson } 776a593f6b8SAndrew Thompson pr_recovery_delay = usb_pr_recovery_delay; 77702ac6454SAndrew Thompson if (pr_recovery_delay > 1000) { 77802ac6454SAndrew Thompson pr_recovery_delay = 1000; 77902ac6454SAndrew Thompson } 78002ac6454SAndrew Thompson #endif 78102ac6454SAndrew Thompson n = 0; 78202ac6454SAndrew Thompson while (1) { 783b850ecc1SAndrew Thompson #ifdef USB_DEBUG 78402ac6454SAndrew Thompson /* wait for the device to recover from reset */ 785a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); 78602ac6454SAndrew Thompson n += pr_poll_delay; 78702ac6454SAndrew Thompson #else 78802ac6454SAndrew Thompson /* wait for the device to recover from reset */ 789a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); 79002ac6454SAndrew Thompson n += USB_PORT_RESET_DELAY; 79102ac6454SAndrew Thompson #endif 792a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port); 79302ac6454SAndrew Thompson if (err) { 79402ac6454SAndrew Thompson goto done; 79502ac6454SAndrew Thompson } 796*0e777d84SHans Petter Selasky /* if the device disappeared, just give up */ 797*0e777d84SHans Petter Selasky if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { 798*0e777d84SHans Petter Selasky goto done; 799*0e777d84SHans Petter Selasky } 80002ac6454SAndrew Thompson /* check if reset is complete */ 80102ac6454SAndrew Thompson if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) { 80202ac6454SAndrew Thompson break; 80302ac6454SAndrew Thompson } 80402ac6454SAndrew Thompson /* check for timeout */ 80502ac6454SAndrew Thompson if (n > 1000) { 80602ac6454SAndrew Thompson n = 0; 80702ac6454SAndrew Thompson break; 80802ac6454SAndrew Thompson } 80902ac6454SAndrew Thompson } 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson /* clear port reset first */ 812a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature( 81302ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET); 81402ac6454SAndrew Thompson if (err) { 81502ac6454SAndrew Thompson goto done; 81602ac6454SAndrew Thompson } 81702ac6454SAndrew Thompson /* check for timeout */ 81802ac6454SAndrew Thompson if (n == 0) { 81902ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 82002ac6454SAndrew Thompson goto done; 82102ac6454SAndrew Thompson } 822b850ecc1SAndrew Thompson #ifdef USB_DEBUG 82302ac6454SAndrew Thompson /* wait for the device to recover from reset */ 824a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); 82502ac6454SAndrew Thompson #else 82602ac6454SAndrew Thompson /* wait for the device to recover from reset */ 827a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); 82802ac6454SAndrew Thompson #endif 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson done: 83102ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n", 832a593f6b8SAndrew Thompson port, usbd_errstr(err)); 83302ac6454SAndrew Thompson return (err); 83402ac6454SAndrew Thompson } 83502ac6454SAndrew Thompson 83602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 837cbb75751SHans Petter Selasky * usbd_req_warm_reset_port 838cbb75751SHans Petter Selasky * 839cbb75751SHans Petter Selasky * This function will instruct an USB HUB to perform a warm reset 840cbb75751SHans Petter Selasky * sequence on the specified port number. This kind of reset is not 841cbb75751SHans Petter Selasky * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted 842cbb75751SHans Petter Selasky * for SUPER-speed USB HUBs. 843cbb75751SHans Petter Selasky * 844cbb75751SHans Petter Selasky * Returns: 845cbb75751SHans Petter Selasky * 0: Success. The USB device should now be available again. 846cbb75751SHans Petter Selasky * Else: Failure. No USB device is present and the USB port should be 847cbb75751SHans Petter Selasky * disabled. 848cbb75751SHans Petter Selasky *------------------------------------------------------------------------*/ 849cbb75751SHans Petter Selasky usb_error_t 850cbb75751SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 851cbb75751SHans Petter Selasky { 852cbb75751SHans Petter Selasky struct usb_port_status ps; 853cbb75751SHans Petter Selasky usb_error_t err; 854cbb75751SHans Petter Selasky uint16_t n; 855cbb75751SHans Petter Selasky 856cbb75751SHans Petter Selasky #ifdef USB_DEBUG 857cbb75751SHans Petter Selasky uint16_t pr_poll_delay; 858cbb75751SHans Petter Selasky uint16_t pr_recovery_delay; 859cbb75751SHans Petter Selasky 860cbb75751SHans Petter Selasky #endif 861cbb75751SHans Petter Selasky err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); 862cbb75751SHans Petter Selasky if (err) { 863cbb75751SHans Petter Selasky goto done; 864cbb75751SHans Petter Selasky } 865cbb75751SHans Petter Selasky #ifdef USB_DEBUG 866cbb75751SHans Petter Selasky /* range check input parameters */ 867cbb75751SHans Petter Selasky pr_poll_delay = usb_pr_poll_delay; 868cbb75751SHans Petter Selasky if (pr_poll_delay < 1) { 869cbb75751SHans Petter Selasky pr_poll_delay = 1; 870cbb75751SHans Petter Selasky } else if (pr_poll_delay > 1000) { 871cbb75751SHans Petter Selasky pr_poll_delay = 1000; 872cbb75751SHans Petter Selasky } 873cbb75751SHans Petter Selasky pr_recovery_delay = usb_pr_recovery_delay; 874cbb75751SHans Petter Selasky if (pr_recovery_delay > 1000) { 875cbb75751SHans Petter Selasky pr_recovery_delay = 1000; 876cbb75751SHans Petter Selasky } 877cbb75751SHans Petter Selasky #endif 878cbb75751SHans Petter Selasky n = 0; 879cbb75751SHans Petter Selasky while (1) { 880cbb75751SHans Petter Selasky #ifdef USB_DEBUG 881cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 882cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); 883cbb75751SHans Petter Selasky n += pr_poll_delay; 884cbb75751SHans Petter Selasky #else 885cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 886cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); 887cbb75751SHans Petter Selasky n += USB_PORT_RESET_DELAY; 888cbb75751SHans Petter Selasky #endif 889cbb75751SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 890cbb75751SHans Petter Selasky if (err) { 891cbb75751SHans Petter Selasky goto done; 892cbb75751SHans Petter Selasky } 893cbb75751SHans Petter Selasky /* if the device disappeared, just give up */ 894cbb75751SHans Petter Selasky if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { 895cbb75751SHans Petter Selasky goto done; 896cbb75751SHans Petter Selasky } 897cbb75751SHans Petter Selasky /* check if reset is complete */ 898cbb75751SHans Petter Selasky if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) { 899cbb75751SHans Petter Selasky break; 900cbb75751SHans Petter Selasky } 901cbb75751SHans Petter Selasky /* check for timeout */ 902cbb75751SHans Petter Selasky if (n > 1000) { 903cbb75751SHans Petter Selasky n = 0; 904cbb75751SHans Petter Selasky break; 905cbb75751SHans Petter Selasky } 906cbb75751SHans Petter Selasky } 907cbb75751SHans Petter Selasky 908cbb75751SHans Petter Selasky /* clear port reset first */ 909cbb75751SHans Petter Selasky err = usbd_req_clear_port_feature( 910cbb75751SHans Petter Selasky udev, mtx, port, UHF_C_BH_PORT_RESET); 911cbb75751SHans Petter Selasky if (err) { 912cbb75751SHans Petter Selasky goto done; 913cbb75751SHans Petter Selasky } 914cbb75751SHans Petter Selasky /* check for timeout */ 915cbb75751SHans Petter Selasky if (n == 0) { 916cbb75751SHans Petter Selasky err = USB_ERR_TIMEOUT; 917cbb75751SHans Petter Selasky goto done; 918cbb75751SHans Petter Selasky } 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_recovery_delay)); 922cbb75751SHans Petter Selasky #else 923cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 924cbb75751SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); 925cbb75751SHans Petter Selasky #endif 926cbb75751SHans Petter Selasky 927cbb75751SHans Petter Selasky done: 928cbb75751SHans Petter Selasky DPRINTFN(2, "port %d warm reset returning error=%s\n", 929cbb75751SHans Petter Selasky port, usbd_errstr(err)); 930cbb75751SHans Petter Selasky return (err); 931cbb75751SHans Petter Selasky } 932cbb75751SHans Petter Selasky 933cbb75751SHans Petter Selasky /*------------------------------------------------------------------------* 934a593f6b8SAndrew Thompson * usbd_req_get_desc 93502ac6454SAndrew Thompson * 93602ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 93702ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 93802ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 93902ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 94002ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 94102ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 94216589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 94316589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 94416589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 94516589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 94616589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 94716589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 94816589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 94916589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 95016589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 95116589beaSAndrew Thompson * descriptor. 95202ac6454SAndrew Thompson * 95302ac6454SAndrew Thompson * Returns: 95402ac6454SAndrew Thompson * 0: Success 95502ac6454SAndrew Thompson * Else: Failure 95602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 957e0a69b51SAndrew Thompson usb_error_t 958a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 95916589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 96002ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 96102ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 96202ac6454SAndrew Thompson uint8_t retries) 96302ac6454SAndrew Thompson { 964760bc48eSAndrew Thompson struct usb_device_request req; 96502ac6454SAndrew Thompson uint8_t *buf; 966e0a69b51SAndrew Thompson usb_error_t err; 96702ac6454SAndrew Thompson 96802ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 96902ac6454SAndrew Thompson id, type, index, max_len); 97002ac6454SAndrew Thompson 97102ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 97202ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 97302ac6454SAndrew Thompson USETW2(req.wValue, type, index); 97402ac6454SAndrew Thompson USETW(req.wIndex, id); 97502ac6454SAndrew Thompson 97602ac6454SAndrew Thompson while (1) { 97702ac6454SAndrew Thompson 97802ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 97902ac6454SAndrew Thompson err = USB_ERR_INVAL; 98002ac6454SAndrew Thompson goto done; 98102ac6454SAndrew Thompson } 98202ac6454SAndrew Thompson USETW(req.wLength, min_len); 98302ac6454SAndrew Thompson 984a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 98502ac6454SAndrew Thompson desc, 0, NULL, 1000); 98602ac6454SAndrew Thompson 98702ac6454SAndrew Thompson if (err) { 98802ac6454SAndrew Thompson if (!retries) { 98902ac6454SAndrew Thompson goto done; 99002ac6454SAndrew Thompson } 99102ac6454SAndrew Thompson retries--; 99202ac6454SAndrew Thompson 993a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 99402ac6454SAndrew Thompson 99502ac6454SAndrew Thompson continue; 99602ac6454SAndrew Thompson } 99702ac6454SAndrew Thompson buf = desc; 99802ac6454SAndrew Thompson 99902ac6454SAndrew Thompson if (min_len == max_len) { 100002ac6454SAndrew Thompson 100116589beaSAndrew Thompson /* enforce correct length */ 100216589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 100302ac6454SAndrew Thompson buf[0] = min_len; 100416589beaSAndrew Thompson 100516589beaSAndrew Thompson /* enforce correct type */ 100602ac6454SAndrew Thompson buf[1] = type; 100702ac6454SAndrew Thompson 100802ac6454SAndrew Thompson goto done; 100902ac6454SAndrew Thompson } 101002ac6454SAndrew Thompson /* range check */ 101102ac6454SAndrew Thompson 101202ac6454SAndrew Thompson if (max_len > buf[0]) { 101302ac6454SAndrew Thompson max_len = buf[0]; 101402ac6454SAndrew Thompson } 101502ac6454SAndrew Thompson /* zero minimum data */ 101602ac6454SAndrew Thompson 101702ac6454SAndrew Thompson while (min_len > max_len) { 101802ac6454SAndrew Thompson min_len--; 101902ac6454SAndrew Thompson buf[min_len] = 0; 102002ac6454SAndrew Thompson } 102102ac6454SAndrew Thompson 102202ac6454SAndrew Thompson /* set new minimum length */ 102302ac6454SAndrew Thompson 102402ac6454SAndrew Thompson min_len = max_len; 102502ac6454SAndrew Thompson } 102602ac6454SAndrew Thompson done: 102716589beaSAndrew Thompson if (actlen != NULL) { 102816589beaSAndrew Thompson if (err) 102916589beaSAndrew Thompson *actlen = 0; 103016589beaSAndrew Thompson else 103116589beaSAndrew Thompson *actlen = min_len; 103216589beaSAndrew Thompson } 103302ac6454SAndrew Thompson return (err); 103402ac6454SAndrew Thompson } 103502ac6454SAndrew Thompson 103602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1037a593f6b8SAndrew Thompson * usbd_req_get_string_any 103802ac6454SAndrew Thompson * 103902ac6454SAndrew Thompson * This function will return the string given by "string_index" 104002ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 104102ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 104202ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 104302ac6454SAndrew Thompson * 104402ac6454SAndrew Thompson * Returns: 104502ac6454SAndrew Thompson * 0: Success 104602ac6454SAndrew Thompson * Else: Failure 104702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1048e0a69b51SAndrew Thompson usb_error_t 1049a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 105002ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 105102ac6454SAndrew Thompson { 105202ac6454SAndrew Thompson char *s; 105302ac6454SAndrew Thompson uint8_t *temp; 105402ac6454SAndrew Thompson uint16_t i; 105502ac6454SAndrew Thompson uint16_t n; 105602ac6454SAndrew Thompson uint16_t c; 105702ac6454SAndrew Thompson uint8_t swap; 1058e0a69b51SAndrew Thompson usb_error_t err; 105902ac6454SAndrew Thompson 106002ac6454SAndrew Thompson if (len == 0) { 106102ac6454SAndrew Thompson /* should not happen */ 106202ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 106302ac6454SAndrew Thompson } 106402ac6454SAndrew Thompson if (string_index == 0) { 106502ac6454SAndrew Thompson /* this is the language table */ 106602ac6454SAndrew Thompson buf[0] = 0; 106702ac6454SAndrew Thompson return (USB_ERR_INVAL); 106802ac6454SAndrew Thompson } 106902ac6454SAndrew Thompson if (udev->flags.no_strings) { 107002ac6454SAndrew Thompson buf[0] = 0; 107102ac6454SAndrew Thompson return (USB_ERR_STALLED); 107202ac6454SAndrew Thompson } 1073a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 107402ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 107502ac6454SAndrew Thompson if (err) { 107602ac6454SAndrew Thompson buf[0] = 0; 107702ac6454SAndrew Thompson return (err); 107802ac6454SAndrew Thompson } 107902ac6454SAndrew Thompson temp = (uint8_t *)buf; 108002ac6454SAndrew Thompson 108102ac6454SAndrew Thompson if (temp[0] < 2) { 108202ac6454SAndrew Thompson /* string length is too short */ 108302ac6454SAndrew Thompson buf[0] = 0; 108402ac6454SAndrew Thompson return (USB_ERR_INVAL); 108502ac6454SAndrew Thompson } 108602ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 108702ac6454SAndrew Thompson len--; 108802ac6454SAndrew Thompson 108902ac6454SAndrew Thompson /* find maximum length */ 109002ac6454SAndrew Thompson s = buf; 109102ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 109202ac6454SAndrew Thompson if (n > len) { 109302ac6454SAndrew Thompson n = len; 109402ac6454SAndrew Thompson } 109502ac6454SAndrew Thompson /* skip descriptor header */ 109602ac6454SAndrew Thompson temp += 2; 109702ac6454SAndrew Thompson 109802ac6454SAndrew Thompson /* reset swap state */ 109902ac6454SAndrew Thompson swap = 3; 110002ac6454SAndrew Thompson 110102ac6454SAndrew Thompson /* convert and filter */ 110202ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 110302ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 110402ac6454SAndrew Thompson 110502ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 110602ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 110702ac6454SAndrew Thompson /* Little Endian, default */ 110802ac6454SAndrew Thompson *s = c; 110902ac6454SAndrew Thompson swap = 1; 111002ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 111102ac6454SAndrew Thompson /* Big Endian */ 111202ac6454SAndrew Thompson *s = c >> 8; 111302ac6454SAndrew Thompson swap = 2; 111402ac6454SAndrew Thompson } else { 111502ac6454SAndrew Thompson /* silently skip bad character */ 111602ac6454SAndrew Thompson continue; 111702ac6454SAndrew Thompson } 111802ac6454SAndrew Thompson 111902ac6454SAndrew Thompson /* 1120b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical 1121b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts 1122b64cf89fSHans Petter Selasky * and daemons. 112302ac6454SAndrew Thompson */ 1124b64cf89fSHans Petter Selasky if (isalpha(*s) || 1125b64cf89fSHans Petter Selasky isdigit(*s) || 1126b64cf89fSHans Petter Selasky *s == '-' || 1127b64cf89fSHans Petter Selasky *s == '+' || 1128b64cf89fSHans Petter Selasky *s == ' ' || 1129b64cf89fSHans Petter Selasky *s == '.' || 1130b64cf89fSHans Petter Selasky *s == ',') { 1131b64cf89fSHans Petter Selasky /* allowed */ 113202ac6454SAndrew Thompson s++; 113302ac6454SAndrew Thompson } 1134b64cf89fSHans Petter Selasky /* silently skip bad character */ 1135b64cf89fSHans Petter Selasky } 113602ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 113702ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 113802ac6454SAndrew Thompson } 113902ac6454SAndrew Thompson 114002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1141a593f6b8SAndrew Thompson * usbd_req_get_string_desc 114202ac6454SAndrew Thompson * 114302ac6454SAndrew Thompson * If you don't know the language ID, consider using 1144a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 114502ac6454SAndrew Thompson * 114602ac6454SAndrew Thompson * Returns: 114702ac6454SAndrew Thompson * 0: Success 114802ac6454SAndrew Thompson * Else: Failure 114902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1150e0a69b51SAndrew Thompson usb_error_t 1151a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 115202ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 115302ac6454SAndrew Thompson uint8_t string_index) 115402ac6454SAndrew Thompson { 1155a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 115602ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 115702ac6454SAndrew Thompson } 115802ac6454SAndrew Thompson 115902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1160a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 11617efaaa9aSAndrew Thompson * 11627efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 11637efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 11647efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 11657efaaa9aSAndrew Thompson * 11667efaaa9aSAndrew Thompson * Returns: 11677efaaa9aSAndrew Thompson * 0: Success 11687efaaa9aSAndrew Thompson * Else: Failure 11697efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 1170e0a69b51SAndrew Thompson usb_error_t 1171a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 1172760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 11737efaaa9aSAndrew Thompson { 1174760bc48eSAndrew Thompson struct usb_device_request req; 1175e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 1176459d369eSAndrew Thompson const void *ptr; 1177459d369eSAndrew Thompson uint16_t len; 1178e0a69b51SAndrew Thompson usb_error_t err; 11797efaaa9aSAndrew Thompson 118063521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 11817efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 1182459d369eSAndrew Thompson USETW(req.wValue, wValue); 11837efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 11847efaaa9aSAndrew Thompson USETW(req.wLength, 0); 11857efaaa9aSAndrew Thompson 1186459d369eSAndrew Thompson ptr = NULL; 1187459d369eSAndrew Thompson len = 0; 11887efaaa9aSAndrew Thompson 1189a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 1190459d369eSAndrew Thompson 1191459d369eSAndrew Thompson if (hr_func == NULL) 1192459d369eSAndrew Thompson err = USB_ERR_INVAL; 1193459d369eSAndrew Thompson else { 1194459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 1195459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 1196459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 1197459d369eSAndrew Thompson } 1198459d369eSAndrew Thompson 1199459d369eSAndrew Thompson if (err) 1200459d369eSAndrew Thompson ptr = NULL; 1201459d369eSAndrew Thompson else if (ptr == NULL) 1202459d369eSAndrew Thompson err = USB_ERR_INVAL; 1203459d369eSAndrew Thompson 1204760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 1205459d369eSAndrew Thompson 1206459d369eSAndrew Thompson return (err); 12077efaaa9aSAndrew Thompson } 12087efaaa9aSAndrew Thompson 12097efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 1210a593f6b8SAndrew Thompson * usbd_req_get_config_desc 121102ac6454SAndrew Thompson * 121202ac6454SAndrew Thompson * Returns: 121302ac6454SAndrew Thompson * 0: Success 121402ac6454SAndrew Thompson * Else: Failure 121502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1216e0a69b51SAndrew Thompson usb_error_t 1217a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 1218760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 121902ac6454SAndrew Thompson { 1220e0a69b51SAndrew Thompson usb_error_t err; 122102ac6454SAndrew Thompson 122202ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 122302ac6454SAndrew Thompson 1224a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 122502ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 122602ac6454SAndrew Thompson if (err) { 122702ac6454SAndrew Thompson goto done; 122802ac6454SAndrew Thompson } 122902ac6454SAndrew Thompson /* Extra sanity checking */ 123002ac6454SAndrew Thompson if (UGETW(d->wTotalLength) < sizeof(*d)) { 123102ac6454SAndrew Thompson err = USB_ERR_INVAL; 123202ac6454SAndrew Thompson } 123302ac6454SAndrew Thompson done: 123402ac6454SAndrew Thompson return (err); 123502ac6454SAndrew Thompson } 123602ac6454SAndrew Thompson 123702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1238a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 123902ac6454SAndrew Thompson * 124002ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 124102ac6454SAndrew Thompson * ensures that "wTotalLength" is correct. 124202ac6454SAndrew Thompson * 124302ac6454SAndrew Thompson * Returns: 124402ac6454SAndrew Thompson * 0: Success 124502ac6454SAndrew Thompson * Else: Failure 124602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1247e0a69b51SAndrew Thompson usb_error_t 1248a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, 1249760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, struct malloc_type *mtype, 125002ac6454SAndrew Thompson uint8_t index) 125102ac6454SAndrew Thompson { 1252760bc48eSAndrew Thompson struct usb_config_descriptor cd; 1253760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 125402ac6454SAndrew Thompson uint16_t len; 1255e0a69b51SAndrew Thompson usb_error_t err; 125602ac6454SAndrew Thompson 125702ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 125802ac6454SAndrew Thompson 125902ac6454SAndrew Thompson *ppcd = NULL; 126002ac6454SAndrew Thompson 1261a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 126202ac6454SAndrew Thompson if (err) { 126302ac6454SAndrew Thompson return (err); 126402ac6454SAndrew Thompson } 126502ac6454SAndrew Thompson /* get full descriptor */ 126602ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 126702ac6454SAndrew Thompson if (len < sizeof(*cdesc)) { 126802ac6454SAndrew Thompson /* corrupt descriptor */ 126902ac6454SAndrew Thompson return (USB_ERR_INVAL); 127002ac6454SAndrew Thompson } 127102ac6454SAndrew Thompson cdesc = malloc(len, mtype, M_WAITOK); 127202ac6454SAndrew Thompson if (cdesc == NULL) { 127302ac6454SAndrew Thompson return (USB_ERR_NOMEM); 127402ac6454SAndrew Thompson } 1275a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 127602ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 127702ac6454SAndrew Thompson if (err) { 127802ac6454SAndrew Thompson free(cdesc, mtype); 127902ac6454SAndrew Thompson return (err); 128002ac6454SAndrew Thompson } 128102ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 128202ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 128302ac6454SAndrew Thompson 128402ac6454SAndrew Thompson *ppcd = cdesc; 128502ac6454SAndrew Thompson 128602ac6454SAndrew Thompson return (0); /* success */ 128702ac6454SAndrew Thompson } 128802ac6454SAndrew Thompson 128902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1290a593f6b8SAndrew Thompson * usbd_req_get_device_desc 129102ac6454SAndrew Thompson * 129202ac6454SAndrew Thompson * Returns: 129302ac6454SAndrew Thompson * 0: Success 129402ac6454SAndrew Thompson * Else: Failure 129502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1296e0a69b51SAndrew Thompson usb_error_t 1297a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1298760bc48eSAndrew Thompson struct usb_device_descriptor *d) 129902ac6454SAndrew Thompson { 130002ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1301a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 130202ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 130302ac6454SAndrew Thompson } 130402ac6454SAndrew Thompson 130502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1306a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 130702ac6454SAndrew Thompson * 130802ac6454SAndrew Thompson * Returns: 130902ac6454SAndrew Thompson * 0: Success 131002ac6454SAndrew Thompson * Else: Failure 131102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1312e0a69b51SAndrew Thompson usb_error_t 1313a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 131402ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 131502ac6454SAndrew Thompson { 1316a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1317760bc48eSAndrew Thompson struct usb_device_request req; 131802ac6454SAndrew Thompson 1319bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 132002ac6454SAndrew Thompson return (USB_ERR_INVAL); 1321bd73b187SAlfred Perlstein 132202ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 132302ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 132402ac6454SAndrew Thompson USETW(req.wValue, 0); 132502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 132602ac6454SAndrew Thompson req.wIndex[1] = 0; 132702ac6454SAndrew Thompson USETW(req.wLength, 1); 1328a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 132902ac6454SAndrew Thompson } 133002ac6454SAndrew Thompson 133102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1332a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 133302ac6454SAndrew Thompson * 133402ac6454SAndrew Thompson * Returns: 133502ac6454SAndrew Thompson * 0: Success 133602ac6454SAndrew Thompson * Else: Failure 133702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1338e0a69b51SAndrew Thompson usb_error_t 1339a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 134002ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 134102ac6454SAndrew Thompson { 1342a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1343760bc48eSAndrew Thompson struct usb_device_request req; 134402ac6454SAndrew Thompson 1345bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 134602ac6454SAndrew Thompson return (USB_ERR_INVAL); 1347bd73b187SAlfred Perlstein 134802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 134902ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 135002ac6454SAndrew Thompson req.wValue[0] = alt_no; 135102ac6454SAndrew Thompson req.wValue[1] = 0; 135202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 135302ac6454SAndrew Thompson req.wIndex[1] = 0; 135402ac6454SAndrew Thompson USETW(req.wLength, 0); 1355a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 135602ac6454SAndrew Thompson } 135702ac6454SAndrew Thompson 135802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1359a593f6b8SAndrew Thompson * usbd_req_get_device_status 136002ac6454SAndrew Thompson * 136102ac6454SAndrew Thompson * Returns: 136202ac6454SAndrew Thompson * 0: Success 136302ac6454SAndrew Thompson * Else: Failure 136402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1365e0a69b51SAndrew Thompson usb_error_t 1366a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1367760bc48eSAndrew Thompson struct usb_status *st) 136802ac6454SAndrew Thompson { 1369760bc48eSAndrew Thompson struct usb_device_request req; 137002ac6454SAndrew Thompson 137102ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 137202ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 137302ac6454SAndrew Thompson USETW(req.wValue, 0); 137402ac6454SAndrew Thompson USETW(req.wIndex, 0); 137502ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1376a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 137702ac6454SAndrew Thompson } 137802ac6454SAndrew Thompson 137902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1380a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 138102ac6454SAndrew Thompson * 138202ac6454SAndrew Thompson * Returns: 138302ac6454SAndrew Thompson * 0: Success 138402ac6454SAndrew Thompson * Else: Failure 138502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1386e0a69b51SAndrew Thompson usb_error_t 1387a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1388760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 138902ac6454SAndrew Thompson { 1390760bc48eSAndrew Thompson struct usb_device_request req; 139102ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 139202ac6454SAndrew Thompson 139302ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 139402ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 139502ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 139602ac6454SAndrew Thompson USETW(req.wIndex, 0); 139702ac6454SAndrew Thompson USETW(req.wLength, len); 1398a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 139902ac6454SAndrew Thompson } 140002ac6454SAndrew Thompson 140102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1402963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor 1403963169b4SHans Petter Selasky * 1404963169b4SHans Petter Selasky * Returns: 1405963169b4SHans Petter Selasky * 0: Success 1406963169b4SHans Petter Selasky * Else: Failure 1407963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1408963169b4SHans Petter Selasky usb_error_t 1409963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1410963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports) 1411963169b4SHans Petter Selasky { 1412963169b4SHans Petter Selasky struct usb_device_request req; 1413963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); 1414963169b4SHans Petter Selasky 1415963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE; 1416963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR; 1417963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0); 1418963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1419963169b4SHans Petter Selasky USETW(req.wLength, len); 1420963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd)); 1421963169b4SHans Petter Selasky } 1422963169b4SHans Petter Selasky 1423963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1424a593f6b8SAndrew Thompson * usbd_req_get_hub_status 142502ac6454SAndrew Thompson * 142602ac6454SAndrew Thompson * Returns: 142702ac6454SAndrew Thompson * 0: Success 142802ac6454SAndrew Thompson * Else: Failure 142902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1430e0a69b51SAndrew Thompson usb_error_t 1431a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1432760bc48eSAndrew Thompson struct usb_hub_status *st) 143302ac6454SAndrew Thompson { 1434760bc48eSAndrew Thompson struct usb_device_request req; 143502ac6454SAndrew Thompson 143602ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 143702ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 143802ac6454SAndrew Thompson USETW(req.wValue, 0); 143902ac6454SAndrew Thompson USETW(req.wIndex, 0); 1440760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1441a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 144202ac6454SAndrew Thompson } 144302ac6454SAndrew Thompson 144402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1445a593f6b8SAndrew Thompson * usbd_req_set_address 144602ac6454SAndrew Thompson * 144702ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 144802ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 144902ac6454SAndrew Thompson * 145002ac6454SAndrew Thompson * Returns: 145102ac6454SAndrew Thompson * 0: Success 145202ac6454SAndrew Thompson * Else: Failure 145302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1454e0a69b51SAndrew Thompson usb_error_t 1455a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 145602ac6454SAndrew Thompson { 1457760bc48eSAndrew Thompson struct usb_device_request req; 1458963169b4SHans Petter Selasky usb_error_t err; 145902ac6454SAndrew Thompson 146002ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 146102ac6454SAndrew Thompson 146202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 146302ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 146402ac6454SAndrew Thompson USETW(req.wValue, addr); 146502ac6454SAndrew Thompson USETW(req.wIndex, 0); 146602ac6454SAndrew Thompson USETW(req.wLength, 0); 146702ac6454SAndrew Thompson 1468963169b4SHans Petter Selasky err = USB_ERR_INVAL; 1469963169b4SHans Petter Selasky 1470963169b4SHans Petter Selasky /* check if USB controller handles set address */ 1471963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL) 1472963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr); 1473963169b4SHans Petter Selasky 1474963169b4SHans Petter Selasky if (err != USB_ERR_INVAL) 1475963169b4SHans Petter Selasky goto done; 1476963169b4SHans Petter Selasky 147702ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1478963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL, 1479963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000); 1480963169b4SHans Petter Selasky 1481963169b4SHans Petter Selasky done: 1482963169b4SHans Petter Selasky /* allow device time to set new address */ 1483963169b4SHans Petter Selasky usb_pause_mtx(mtx, 1484963169b4SHans Petter Selasky USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); 1485963169b4SHans Petter Selasky 1486963169b4SHans Petter Selasky return (err); 148702ac6454SAndrew Thompson } 148802ac6454SAndrew Thompson 148902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1490a593f6b8SAndrew Thompson * usbd_req_get_port_status 149102ac6454SAndrew Thompson * 149202ac6454SAndrew Thompson * Returns: 149302ac6454SAndrew Thompson * 0: Success 149402ac6454SAndrew Thompson * Else: Failure 149502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1496e0a69b51SAndrew Thompson usb_error_t 1497a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1498760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 149902ac6454SAndrew Thompson { 1500760bc48eSAndrew Thompson struct usb_device_request req; 150102ac6454SAndrew Thompson 150202ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 150302ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 150402ac6454SAndrew Thompson USETW(req.wValue, 0); 150502ac6454SAndrew Thompson req.wIndex[0] = port; 150602ac6454SAndrew Thompson req.wIndex[1] = 0; 150702ac6454SAndrew Thompson USETW(req.wLength, sizeof *ps); 1508a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, ps)); 150902ac6454SAndrew Thompson } 151002ac6454SAndrew Thompson 151102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1512a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 151302ac6454SAndrew Thompson * 151402ac6454SAndrew Thompson * Returns: 151502ac6454SAndrew Thompson * 0: Success 151602ac6454SAndrew Thompson * Else: Failure 151702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1518e0a69b51SAndrew Thompson usb_error_t 1519a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 152002ac6454SAndrew Thompson uint16_t sel) 152102ac6454SAndrew Thompson { 1522760bc48eSAndrew Thompson struct usb_device_request req; 152302ac6454SAndrew Thompson 152402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 152502ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 152602ac6454SAndrew Thompson USETW(req.wValue, sel); 152702ac6454SAndrew Thompson USETW(req.wIndex, 0); 152802ac6454SAndrew Thompson USETW(req.wLength, 0); 1529a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 153002ac6454SAndrew Thompson } 153102ac6454SAndrew Thompson 153202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1533a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 153402ac6454SAndrew Thompson * 153502ac6454SAndrew Thompson * Returns: 153602ac6454SAndrew Thompson * 0: Success 153702ac6454SAndrew Thompson * Else: Failure 153802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1539e0a69b51SAndrew Thompson usb_error_t 1540a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 154102ac6454SAndrew Thompson uint16_t sel) 154202ac6454SAndrew Thompson { 1543760bc48eSAndrew Thompson struct usb_device_request req; 154402ac6454SAndrew Thompson 154502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 154602ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 154702ac6454SAndrew Thompson USETW(req.wValue, sel); 154802ac6454SAndrew Thompson USETW(req.wIndex, 0); 154902ac6454SAndrew Thompson USETW(req.wLength, 0); 1550a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 155102ac6454SAndrew Thompson } 155202ac6454SAndrew Thompson 155302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1554963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout 1555963169b4SHans Petter Selasky * 1556963169b4SHans Petter Selasky * Returns: 1557963169b4SHans Petter Selasky * 0: Success 1558963169b4SHans Petter Selasky * Else: Failure 1559963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1560963169b4SHans Petter Selasky usb_error_t 1561963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, 1562963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1563963169b4SHans Petter Selasky { 1564963169b4SHans Petter Selasky struct usb_device_request req; 1565963169b4SHans Petter Selasky 1566963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1567963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1568963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT); 1569963169b4SHans Petter Selasky req.wIndex[0] = port; 1570963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1571963169b4SHans Petter Selasky USETW(req.wLength, 0); 1572963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1573963169b4SHans Petter Selasky } 1574963169b4SHans Petter Selasky 1575963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1576963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout 1577963169b4SHans Petter Selasky * 1578963169b4SHans Petter Selasky * Returns: 1579963169b4SHans Petter Selasky * 0: Success 1580963169b4SHans Petter Selasky * Else: Failure 1581963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1582963169b4SHans Petter Selasky usb_error_t 1583963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, 1584963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1585963169b4SHans Petter Selasky { 1586963169b4SHans Petter Selasky struct usb_device_request req; 1587963169b4SHans Petter Selasky 1588963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1589963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1590963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT); 1591963169b4SHans Petter Selasky req.wIndex[0] = port; 1592963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1593963169b4SHans Petter Selasky USETW(req.wLength, 0); 1594963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1595963169b4SHans Petter Selasky } 1596963169b4SHans Petter Selasky 1597963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1598963169b4SHans Petter Selasky * usbd_req_set_hub_depth 1599963169b4SHans Petter Selasky * 1600963169b4SHans Petter Selasky * Returns: 1601963169b4SHans Petter Selasky * 0: Success 1602963169b4SHans Petter Selasky * Else: Failure 1603963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1604963169b4SHans Petter Selasky usb_error_t 1605963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, 1606963169b4SHans Petter Selasky uint16_t depth) 1607963169b4SHans Petter Selasky { 1608963169b4SHans Petter Selasky struct usb_device_request req; 1609963169b4SHans Petter Selasky 1610963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE; 1611963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH; 1612963169b4SHans Petter Selasky USETW(req.wValue, depth); 1613963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1614963169b4SHans Petter Selasky USETW(req.wLength, 0); 1615963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1616963169b4SHans Petter Selasky } 1617963169b4SHans Petter Selasky 1618963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1619a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 162002ac6454SAndrew Thompson * 162102ac6454SAndrew Thompson * Returns: 162202ac6454SAndrew Thompson * 0: Success 162302ac6454SAndrew Thompson * Else: Failure 162402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1625e0a69b51SAndrew Thompson usb_error_t 1626a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 162702ac6454SAndrew Thompson uint8_t port, uint16_t sel) 162802ac6454SAndrew Thompson { 1629760bc48eSAndrew Thompson struct usb_device_request req; 163002ac6454SAndrew Thompson 163102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 163202ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 163302ac6454SAndrew Thompson USETW(req.wValue, sel); 163402ac6454SAndrew Thompson req.wIndex[0] = port; 163502ac6454SAndrew Thompson req.wIndex[1] = 0; 163602ac6454SAndrew Thompson USETW(req.wLength, 0); 1637a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 163802ac6454SAndrew Thompson } 163902ac6454SAndrew Thompson 164002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1641a593f6b8SAndrew Thompson * usbd_req_set_port_feature 164202ac6454SAndrew Thompson * 164302ac6454SAndrew Thompson * Returns: 164402ac6454SAndrew Thompson * 0: Success 164502ac6454SAndrew Thompson * Else: Failure 164602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1647e0a69b51SAndrew Thompson usb_error_t 1648a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, 164902ac6454SAndrew Thompson uint8_t port, uint16_t sel) 165002ac6454SAndrew Thompson { 1651760bc48eSAndrew Thompson struct usb_device_request req; 165202ac6454SAndrew Thompson 165302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 165402ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 165502ac6454SAndrew Thompson USETW(req.wValue, sel); 165602ac6454SAndrew Thompson req.wIndex[0] = port; 165702ac6454SAndrew Thompson req.wIndex[1] = 0; 165802ac6454SAndrew Thompson USETW(req.wLength, 0); 1659a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 166002ac6454SAndrew Thompson } 166102ac6454SAndrew Thompson 166202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1663a593f6b8SAndrew Thompson * usbd_req_set_protocol 166402ac6454SAndrew Thompson * 166502ac6454SAndrew Thompson * Returns: 166602ac6454SAndrew Thompson * 0: Success 166702ac6454SAndrew Thompson * Else: Failure 166802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1669e0a69b51SAndrew Thompson usb_error_t 1670a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 167102ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 167202ac6454SAndrew Thompson { 1673a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1674760bc48eSAndrew Thompson struct usb_device_request req; 167502ac6454SAndrew Thompson 167602ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 167702ac6454SAndrew Thompson return (USB_ERR_INVAL); 167802ac6454SAndrew Thompson } 167902ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 168002ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 168102ac6454SAndrew Thompson 168202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 168302ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 168402ac6454SAndrew Thompson USETW(req.wValue, report); 168502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 168602ac6454SAndrew Thompson req.wIndex[1] = 0; 168702ac6454SAndrew Thompson USETW(req.wLength, 0); 1688a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 168902ac6454SAndrew Thompson } 169002ac6454SAndrew Thompson 169102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1692a593f6b8SAndrew Thompson * usbd_req_set_report 169302ac6454SAndrew Thompson * 169402ac6454SAndrew Thompson * Returns: 169502ac6454SAndrew Thompson * 0: Success 169602ac6454SAndrew Thompson * Else: Failure 169702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1698e0a69b51SAndrew Thompson usb_error_t 1699a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 170002ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 170102ac6454SAndrew Thompson { 1702a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1703760bc48eSAndrew Thompson struct usb_device_request req; 170402ac6454SAndrew Thompson 170502ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 170602ac6454SAndrew Thompson return (USB_ERR_INVAL); 170702ac6454SAndrew Thompson } 170802ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 170902ac6454SAndrew Thompson 171002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 171102ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 171202ac6454SAndrew Thompson USETW2(req.wValue, type, id); 171302ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 171402ac6454SAndrew Thompson req.wIndex[1] = 0; 171502ac6454SAndrew Thompson USETW(req.wLength, len); 1716a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 171702ac6454SAndrew Thompson } 171802ac6454SAndrew Thompson 171902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1720a593f6b8SAndrew Thompson * usbd_req_get_report 172102ac6454SAndrew Thompson * 172202ac6454SAndrew Thompson * Returns: 172302ac6454SAndrew Thompson * 0: Success 172402ac6454SAndrew Thompson * Else: Failure 172502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1726e0a69b51SAndrew Thompson usb_error_t 1727a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 172802ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 172902ac6454SAndrew Thompson { 1730a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1731760bc48eSAndrew Thompson struct usb_device_request req; 173202ac6454SAndrew Thompson 173302ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { 173402ac6454SAndrew Thompson return (USB_ERR_INVAL); 173502ac6454SAndrew Thompson } 173602ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 173702ac6454SAndrew Thompson 173802ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 173902ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 174002ac6454SAndrew Thompson USETW2(req.wValue, type, id); 174102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 174202ac6454SAndrew Thompson req.wIndex[1] = 0; 174302ac6454SAndrew Thompson USETW(req.wLength, len); 1744a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 174502ac6454SAndrew Thompson } 174602ac6454SAndrew Thompson 174702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1748a593f6b8SAndrew Thompson * usbd_req_set_idle 174902ac6454SAndrew Thompson * 175002ac6454SAndrew Thompson * Returns: 175102ac6454SAndrew Thompson * 0: Success 175202ac6454SAndrew Thompson * Else: Failure 175302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1754e0a69b51SAndrew Thompson usb_error_t 1755a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 175602ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 175702ac6454SAndrew Thompson { 1758a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1759760bc48eSAndrew Thompson struct usb_device_request req; 176002ac6454SAndrew Thompson 176102ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 176202ac6454SAndrew Thompson return (USB_ERR_INVAL); 176302ac6454SAndrew Thompson } 176402ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 176502ac6454SAndrew Thompson 176602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 176702ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 176802ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 176902ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 177002ac6454SAndrew Thompson req.wIndex[1] = 0; 177102ac6454SAndrew Thompson USETW(req.wLength, 0); 1772a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 177302ac6454SAndrew Thompson } 177402ac6454SAndrew Thompson 177502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1776a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 177702ac6454SAndrew Thompson * 177802ac6454SAndrew Thompson * Returns: 177902ac6454SAndrew Thompson * 0: Success 178002ac6454SAndrew Thompson * Else: Failure 178102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1782e0a69b51SAndrew Thompson usb_error_t 1783a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 178402ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 178502ac6454SAndrew Thompson { 1786a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1787760bc48eSAndrew Thompson struct usb_device_request req; 178802ac6454SAndrew Thompson 178902ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 179002ac6454SAndrew Thompson return (USB_ERR_INVAL); 179102ac6454SAndrew Thompson } 179202ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 179302ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 179402ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 179502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 179602ac6454SAndrew Thompson req.wIndex[1] = 0; 179702ac6454SAndrew Thompson USETW(req.wLength, size); 1798a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 179902ac6454SAndrew Thompson } 180002ac6454SAndrew Thompson 180102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1802a593f6b8SAndrew Thompson * usbd_req_set_config 180302ac6454SAndrew Thompson * 180402ac6454SAndrew Thompson * This function is used to select the current configuration number in 180502ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 180602ac6454SAndrew Thompson * configuration the function of the interfaces can change. 180702ac6454SAndrew Thompson * 180802ac6454SAndrew Thompson * Returns: 180902ac6454SAndrew Thompson * 0: Success 181002ac6454SAndrew Thompson * Else: Failure 181102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1812e0a69b51SAndrew Thompson usb_error_t 1813a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 181402ac6454SAndrew Thompson { 1815760bc48eSAndrew Thompson struct usb_device_request req; 181602ac6454SAndrew Thompson 181702ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 181802ac6454SAndrew Thompson 181902ac6454SAndrew Thompson /* do "set configuration" request */ 182002ac6454SAndrew Thompson 182102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 182202ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 182302ac6454SAndrew Thompson req.wValue[0] = conf; 182402ac6454SAndrew Thompson req.wValue[1] = 0; 182502ac6454SAndrew Thompson USETW(req.wIndex, 0); 182602ac6454SAndrew Thompson USETW(req.wLength, 0); 1827a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 182802ac6454SAndrew Thompson } 182902ac6454SAndrew Thompson 183002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1831a593f6b8SAndrew Thompson * usbd_req_get_config 183202ac6454SAndrew Thompson * 183302ac6454SAndrew Thompson * Returns: 183402ac6454SAndrew Thompson * 0: Success 183502ac6454SAndrew Thompson * Else: Failure 183602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1837e0a69b51SAndrew Thompson usb_error_t 1838a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 183902ac6454SAndrew Thompson { 1840760bc48eSAndrew Thompson struct usb_device_request req; 184102ac6454SAndrew Thompson 184202ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 184302ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 184402ac6454SAndrew Thompson USETW(req.wValue, 0); 184502ac6454SAndrew Thompson USETW(req.wIndex, 0); 184602ac6454SAndrew Thompson USETW(req.wLength, 1); 1847a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 184802ac6454SAndrew Thompson } 184902ac6454SAndrew Thompson 185002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1851963169b4SHans Petter Selasky * usbd_setup_device_desc 1852963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1853963169b4SHans Petter Selasky usb_error_t 1854963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) 1855963169b4SHans Petter Selasky { 1856963169b4SHans Petter Selasky usb_error_t err; 1857963169b4SHans Petter Selasky 1858963169b4SHans Petter Selasky /* 1859963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor ! 1860963169b4SHans Petter Selasky * 1861963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor 1862963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size 1863963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor 1864963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint 1865963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new 1866963169b4SHans Petter Selasky * USB control request will be setup! 1867963169b4SHans Petter Selasky */ 1868963169b4SHans Petter Selasky switch (udev->speed) { 1869963169b4SHans Petter Selasky case USB_SPEED_FULL: 1870963169b4SHans Petter Selasky case USB_SPEED_LOW: 1871963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 1872963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 1873963169b4SHans Petter Selasky if (err != 0) { 1874963169b4SHans Petter Selasky DPRINTFN(0, "getting device descriptor " 1875963169b4SHans Petter Selasky "at addr %d failed, %s\n", udev->address, 1876963169b4SHans Petter Selasky usbd_errstr(err)); 1877963169b4SHans Petter Selasky return (err); 1878963169b4SHans Petter Selasky } 1879963169b4SHans Petter Selasky break; 1880963169b4SHans Petter Selasky default: 1881963169b4SHans Petter Selasky DPRINTF("Minimum MaxPacketSize is large enough " 1882963169b4SHans Petter Selasky "to hold the complete device descriptor\n"); 1883963169b4SHans Petter Selasky break; 1884963169b4SHans Petter Selasky } 1885963169b4SHans Petter Selasky 1886963169b4SHans Petter Selasky /* get the full device descriptor */ 1887963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1888963169b4SHans Petter Selasky 1889963169b4SHans Petter Selasky /* try one more time, if error */ 1890963169b4SHans Petter Selasky if (err) 1891963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1892963169b4SHans Petter Selasky 1893963169b4SHans Petter Selasky if (err) { 1894963169b4SHans Petter Selasky DPRINTF("addr=%d, getting full desc failed\n", 1895963169b4SHans Petter Selasky udev->address); 1896963169b4SHans Petter Selasky return (err); 1897963169b4SHans Petter Selasky } 1898963169b4SHans Petter Selasky 1899963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " 1900963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", 1901963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB), 1902963169b4SHans Petter Selasky udev->ddesc.bDeviceClass, 1903963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass, 1904963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol, 1905963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize, 1906963169b4SHans Petter Selasky udev->ddesc.bLength, 1907963169b4SHans Petter Selasky udev->speed); 1908963169b4SHans Petter Selasky 1909963169b4SHans Petter Selasky return (err); 1910963169b4SHans Petter Selasky } 1911963169b4SHans Petter Selasky 1912963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1913a593f6b8SAndrew Thompson * usbd_req_re_enumerate 191402ac6454SAndrew Thompson * 191502ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 191602ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 191702ac6454SAndrew Thompson * new configuration. 191802ac6454SAndrew Thompson * 191902ac6454SAndrew Thompson * Returns: 192002ac6454SAndrew Thompson * 0: Success 192102ac6454SAndrew Thompson * Else: Failure 192202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1923e0a69b51SAndrew Thompson usb_error_t 1924a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 192502ac6454SAndrew Thompson { 1926760bc48eSAndrew Thompson struct usb_device *parent_hub; 1927e0a69b51SAndrew Thompson usb_error_t err; 192802ac6454SAndrew Thompson uint8_t old_addr; 192902ac6454SAndrew Thompson uint8_t do_retry = 1; 193002ac6454SAndrew Thompson 1931f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 193202ac6454SAndrew Thompson return (USB_ERR_INVAL); 193302ac6454SAndrew Thompson } 193402ac6454SAndrew Thompson old_addr = udev->address; 193502ac6454SAndrew Thompson parent_hub = udev->parent_hub; 193602ac6454SAndrew Thompson if (parent_hub == NULL) { 193702ac6454SAndrew Thompson return (USB_ERR_INVAL); 193802ac6454SAndrew Thompson } 193902ac6454SAndrew Thompson retry: 1940a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 194102ac6454SAndrew Thompson if (err) { 194203797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 1943a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 194402ac6454SAndrew Thompson goto done; 194502ac6454SAndrew Thompson } 1946963169b4SHans Petter Selasky 194702ac6454SAndrew Thompson /* 194802ac6454SAndrew Thompson * After that the port has been reset our device should be at 194902ac6454SAndrew Thompson * address zero: 195002ac6454SAndrew Thompson */ 195102ac6454SAndrew Thompson udev->address = USB_START_ADDR; 195202ac6454SAndrew Thompson 195302ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 195402ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 195502ac6454SAndrew Thompson 1956963169b4SHans Petter Selasky /* reset USB state */ 1957963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED); 1958963169b4SHans Petter Selasky 195902ac6454SAndrew Thompson /* 196002ac6454SAndrew Thompson * Restore device address: 196102ac6454SAndrew Thompson */ 1962a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 196302ac6454SAndrew Thompson if (err) { 196402ac6454SAndrew Thompson /* XXX ignore any errors! */ 196503797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 1966a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 196702ac6454SAndrew Thompson } 1968963169b4SHans Petter Selasky /* 1969963169b4SHans Petter Selasky * Restore device address, if the controller driver did not 1970963169b4SHans Petter Selasky * set a new one: 1971963169b4SHans Petter Selasky */ 1972963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 197302ac6454SAndrew Thompson udev->address = old_addr; 197402ac6454SAndrew Thompson 1975963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */ 1976963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx); 197702ac6454SAndrew Thompson 197802ac6454SAndrew Thompson done: 197902ac6454SAndrew Thompson if (err && do_retry) { 198002ac6454SAndrew Thompson /* give the USB firmware some time to load */ 1981a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 198202ac6454SAndrew Thompson /* no more retries after this retry */ 198302ac6454SAndrew Thompson do_retry = 0; 198402ac6454SAndrew Thompson /* try again */ 198502ac6454SAndrew Thompson goto retry; 198602ac6454SAndrew Thompson } 198702ac6454SAndrew Thompson /* restore address */ 1988963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 198902ac6454SAndrew Thompson udev->address = old_addr; 1990963169b4SHans Petter Selasky /* update state, if successful */ 1991963169b4SHans Petter Selasky if (err == 0) 1992963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED); 199302ac6454SAndrew Thompson return (err); 199402ac6454SAndrew Thompson } 199502ac6454SAndrew Thompson 199602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1997a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 199802ac6454SAndrew Thompson * 199902ac6454SAndrew Thompson * Returns: 200002ac6454SAndrew Thompson * 0: Success 200102ac6454SAndrew Thompson * Else: Failure 200202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2003e0a69b51SAndrew Thompson usb_error_t 2004a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 200502ac6454SAndrew Thompson uint16_t sel) 200602ac6454SAndrew Thompson { 2007760bc48eSAndrew Thompson struct usb_device_request req; 200802ac6454SAndrew Thompson 200902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 201002ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 201102ac6454SAndrew Thompson USETW(req.wValue, sel); 201202ac6454SAndrew Thompson USETW(req.wIndex, 0); 201302ac6454SAndrew Thompson USETW(req.wLength, 0); 2014a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 201502ac6454SAndrew Thompson } 201602ac6454SAndrew Thompson 201702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2018a593f6b8SAndrew Thompson * usbd_req_set_device_feature 201902ac6454SAndrew Thompson * 202002ac6454SAndrew Thompson * Returns: 202102ac6454SAndrew Thompson * 0: Success 202202ac6454SAndrew Thompson * Else: Failure 202302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2024e0a69b51SAndrew Thompson usb_error_t 2025a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 202602ac6454SAndrew Thompson uint16_t sel) 202702ac6454SAndrew Thompson { 2028760bc48eSAndrew Thompson struct usb_device_request req; 202902ac6454SAndrew Thompson 203002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 203102ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 203202ac6454SAndrew Thompson USETW(req.wValue, sel); 203302ac6454SAndrew Thompson USETW(req.wIndex, 0); 203402ac6454SAndrew Thompson USETW(req.wLength, 0); 2035a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 203602ac6454SAndrew Thompson } 2037