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; 248*963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */ 249*963169b4SHans 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 * 74402ac6454SAndrew Thompson * This function will instruct an 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 } 79602ac6454SAndrew Thompson /* if the device disappeared, just give up */ 79702ac6454SAndrew Thompson if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { 79802ac6454SAndrew Thompson goto done; 79902ac6454SAndrew Thompson } 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 /*------------------------------------------------------------------------* 837a593f6b8SAndrew Thompson * usbd_req_get_desc 83802ac6454SAndrew Thompson * 83902ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 84002ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 84102ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 84202ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 84302ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 84402ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 84516589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 84616589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 84716589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 84816589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 84916589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 85016589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 85116589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 85216589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 85316589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 85416589beaSAndrew Thompson * descriptor. 85502ac6454SAndrew Thompson * 85602ac6454SAndrew Thompson * Returns: 85702ac6454SAndrew Thompson * 0: Success 85802ac6454SAndrew Thompson * Else: Failure 85902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 860e0a69b51SAndrew Thompson usb_error_t 861a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 86216589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 86302ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 86402ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 86502ac6454SAndrew Thompson uint8_t retries) 86602ac6454SAndrew Thompson { 867760bc48eSAndrew Thompson struct usb_device_request req; 86802ac6454SAndrew Thompson uint8_t *buf; 869e0a69b51SAndrew Thompson usb_error_t err; 87002ac6454SAndrew Thompson 87102ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 87202ac6454SAndrew Thompson id, type, index, max_len); 87302ac6454SAndrew Thompson 87402ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 87502ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 87602ac6454SAndrew Thompson USETW2(req.wValue, type, index); 87702ac6454SAndrew Thompson USETW(req.wIndex, id); 87802ac6454SAndrew Thompson 87902ac6454SAndrew Thompson while (1) { 88002ac6454SAndrew Thompson 88102ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 88202ac6454SAndrew Thompson err = USB_ERR_INVAL; 88302ac6454SAndrew Thompson goto done; 88402ac6454SAndrew Thompson } 88502ac6454SAndrew Thompson USETW(req.wLength, min_len); 88602ac6454SAndrew Thompson 887a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 88802ac6454SAndrew Thompson desc, 0, NULL, 1000); 88902ac6454SAndrew Thompson 89002ac6454SAndrew Thompson if (err) { 89102ac6454SAndrew Thompson if (!retries) { 89202ac6454SAndrew Thompson goto done; 89302ac6454SAndrew Thompson } 89402ac6454SAndrew Thompson retries--; 89502ac6454SAndrew Thompson 896a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 89702ac6454SAndrew Thompson 89802ac6454SAndrew Thompson continue; 89902ac6454SAndrew Thompson } 90002ac6454SAndrew Thompson buf = desc; 90102ac6454SAndrew Thompson 90202ac6454SAndrew Thompson if (min_len == max_len) { 90302ac6454SAndrew Thompson 90416589beaSAndrew Thompson /* enforce correct length */ 90516589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 90602ac6454SAndrew Thompson buf[0] = min_len; 90716589beaSAndrew Thompson 90816589beaSAndrew Thompson /* enforce correct type */ 90902ac6454SAndrew Thompson buf[1] = type; 91002ac6454SAndrew Thompson 91102ac6454SAndrew Thompson goto done; 91202ac6454SAndrew Thompson } 91302ac6454SAndrew Thompson /* range check */ 91402ac6454SAndrew Thompson 91502ac6454SAndrew Thompson if (max_len > buf[0]) { 91602ac6454SAndrew Thompson max_len = buf[0]; 91702ac6454SAndrew Thompson } 91802ac6454SAndrew Thompson /* zero minimum data */ 91902ac6454SAndrew Thompson 92002ac6454SAndrew Thompson while (min_len > max_len) { 92102ac6454SAndrew Thompson min_len--; 92202ac6454SAndrew Thompson buf[min_len] = 0; 92302ac6454SAndrew Thompson } 92402ac6454SAndrew Thompson 92502ac6454SAndrew Thompson /* set new minimum length */ 92602ac6454SAndrew Thompson 92702ac6454SAndrew Thompson min_len = max_len; 92802ac6454SAndrew Thompson } 92902ac6454SAndrew Thompson done: 93016589beaSAndrew Thompson if (actlen != NULL) { 93116589beaSAndrew Thompson if (err) 93216589beaSAndrew Thompson *actlen = 0; 93316589beaSAndrew Thompson else 93416589beaSAndrew Thompson *actlen = min_len; 93516589beaSAndrew Thompson } 93602ac6454SAndrew Thompson return (err); 93702ac6454SAndrew Thompson } 93802ac6454SAndrew Thompson 93902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 940a593f6b8SAndrew Thompson * usbd_req_get_string_any 94102ac6454SAndrew Thompson * 94202ac6454SAndrew Thompson * This function will return the string given by "string_index" 94302ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 94402ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 94502ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 94602ac6454SAndrew Thompson * 94702ac6454SAndrew Thompson * Returns: 94802ac6454SAndrew Thompson * 0: Success 94902ac6454SAndrew Thompson * Else: Failure 95002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 951e0a69b51SAndrew Thompson usb_error_t 952a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 95302ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 95402ac6454SAndrew Thompson { 95502ac6454SAndrew Thompson char *s; 95602ac6454SAndrew Thompson uint8_t *temp; 95702ac6454SAndrew Thompson uint16_t i; 95802ac6454SAndrew Thompson uint16_t n; 95902ac6454SAndrew Thompson uint16_t c; 96002ac6454SAndrew Thompson uint8_t swap; 961e0a69b51SAndrew Thompson usb_error_t err; 96202ac6454SAndrew Thompson 96302ac6454SAndrew Thompson if (len == 0) { 96402ac6454SAndrew Thompson /* should not happen */ 96502ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 96602ac6454SAndrew Thompson } 96702ac6454SAndrew Thompson if (string_index == 0) { 96802ac6454SAndrew Thompson /* this is the language table */ 96902ac6454SAndrew Thompson buf[0] = 0; 97002ac6454SAndrew Thompson return (USB_ERR_INVAL); 97102ac6454SAndrew Thompson } 97202ac6454SAndrew Thompson if (udev->flags.no_strings) { 97302ac6454SAndrew Thompson buf[0] = 0; 97402ac6454SAndrew Thompson return (USB_ERR_STALLED); 97502ac6454SAndrew Thompson } 976a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 97702ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 97802ac6454SAndrew Thompson if (err) { 97902ac6454SAndrew Thompson buf[0] = 0; 98002ac6454SAndrew Thompson return (err); 98102ac6454SAndrew Thompson } 98202ac6454SAndrew Thompson temp = (uint8_t *)buf; 98302ac6454SAndrew Thompson 98402ac6454SAndrew Thompson if (temp[0] < 2) { 98502ac6454SAndrew Thompson /* string length is too short */ 98602ac6454SAndrew Thompson buf[0] = 0; 98702ac6454SAndrew Thompson return (USB_ERR_INVAL); 98802ac6454SAndrew Thompson } 98902ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 99002ac6454SAndrew Thompson len--; 99102ac6454SAndrew Thompson 99202ac6454SAndrew Thompson /* find maximum length */ 99302ac6454SAndrew Thompson s = buf; 99402ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 99502ac6454SAndrew Thompson if (n > len) { 99602ac6454SAndrew Thompson n = len; 99702ac6454SAndrew Thompson } 99802ac6454SAndrew Thompson /* skip descriptor header */ 99902ac6454SAndrew Thompson temp += 2; 100002ac6454SAndrew Thompson 100102ac6454SAndrew Thompson /* reset swap state */ 100202ac6454SAndrew Thompson swap = 3; 100302ac6454SAndrew Thompson 100402ac6454SAndrew Thompson /* convert and filter */ 100502ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 100602ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 100702ac6454SAndrew Thompson 100802ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 100902ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 101002ac6454SAndrew Thompson /* Little Endian, default */ 101102ac6454SAndrew Thompson *s = c; 101202ac6454SAndrew Thompson swap = 1; 101302ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 101402ac6454SAndrew Thompson /* Big Endian */ 101502ac6454SAndrew Thompson *s = c >> 8; 101602ac6454SAndrew Thompson swap = 2; 101702ac6454SAndrew Thompson } else { 101802ac6454SAndrew Thompson /* silently skip bad character */ 101902ac6454SAndrew Thompson continue; 102002ac6454SAndrew Thompson } 102102ac6454SAndrew Thompson 102202ac6454SAndrew Thompson /* 1023b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical 1024b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts 1025b64cf89fSHans Petter Selasky * and daemons. 102602ac6454SAndrew Thompson */ 1027b64cf89fSHans Petter Selasky if (isalpha(*s) || 1028b64cf89fSHans Petter Selasky isdigit(*s) || 1029b64cf89fSHans Petter Selasky *s == '-' || 1030b64cf89fSHans Petter Selasky *s == '+' || 1031b64cf89fSHans Petter Selasky *s == ' ' || 1032b64cf89fSHans Petter Selasky *s == '.' || 1033b64cf89fSHans Petter Selasky *s == ',') { 1034b64cf89fSHans Petter Selasky /* allowed */ 103502ac6454SAndrew Thompson s++; 103602ac6454SAndrew Thompson } 1037b64cf89fSHans Petter Selasky /* silently skip bad character */ 1038b64cf89fSHans Petter Selasky } 103902ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 104002ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 104102ac6454SAndrew Thompson } 104202ac6454SAndrew Thompson 104302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1044a593f6b8SAndrew Thompson * usbd_req_get_string_desc 104502ac6454SAndrew Thompson * 104602ac6454SAndrew Thompson * If you don't know the language ID, consider using 1047a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 104802ac6454SAndrew Thompson * 104902ac6454SAndrew Thompson * Returns: 105002ac6454SAndrew Thompson * 0: Success 105102ac6454SAndrew Thompson * Else: Failure 105202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1053e0a69b51SAndrew Thompson usb_error_t 1054a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 105502ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 105602ac6454SAndrew Thompson uint8_t string_index) 105702ac6454SAndrew Thompson { 1058a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 105902ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 106002ac6454SAndrew Thompson } 106102ac6454SAndrew Thompson 106202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1063a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 10647efaaa9aSAndrew Thompson * 10657efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 10667efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 10677efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 10687efaaa9aSAndrew Thompson * 10697efaaa9aSAndrew Thompson * Returns: 10707efaaa9aSAndrew Thompson * 0: Success 10717efaaa9aSAndrew Thompson * Else: Failure 10727efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 1073e0a69b51SAndrew Thompson usb_error_t 1074a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 1075760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 10767efaaa9aSAndrew Thompson { 1077760bc48eSAndrew Thompson struct usb_device_request req; 1078e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 1079459d369eSAndrew Thompson const void *ptr; 1080459d369eSAndrew Thompson uint16_t len; 1081e0a69b51SAndrew Thompson usb_error_t err; 10827efaaa9aSAndrew Thompson 108363521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 10847efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 1085459d369eSAndrew Thompson USETW(req.wValue, wValue); 10867efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 10877efaaa9aSAndrew Thompson USETW(req.wLength, 0); 10887efaaa9aSAndrew Thompson 1089459d369eSAndrew Thompson ptr = NULL; 1090459d369eSAndrew Thompson len = 0; 10917efaaa9aSAndrew Thompson 1092a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 1093459d369eSAndrew Thompson 1094459d369eSAndrew Thompson if (hr_func == NULL) 1095459d369eSAndrew Thompson err = USB_ERR_INVAL; 1096459d369eSAndrew Thompson else { 1097459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 1098459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 1099459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 1100459d369eSAndrew Thompson } 1101459d369eSAndrew Thompson 1102459d369eSAndrew Thompson if (err) 1103459d369eSAndrew Thompson ptr = NULL; 1104459d369eSAndrew Thompson else if (ptr == NULL) 1105459d369eSAndrew Thompson err = USB_ERR_INVAL; 1106459d369eSAndrew Thompson 1107760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 1108459d369eSAndrew Thompson 1109459d369eSAndrew Thompson return (err); 11107efaaa9aSAndrew Thompson } 11117efaaa9aSAndrew Thompson 11127efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 1113a593f6b8SAndrew Thompson * usbd_req_get_config_desc 111402ac6454SAndrew Thompson * 111502ac6454SAndrew Thompson * Returns: 111602ac6454SAndrew Thompson * 0: Success 111702ac6454SAndrew Thompson * Else: Failure 111802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1119e0a69b51SAndrew Thompson usb_error_t 1120a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 1121760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 112202ac6454SAndrew Thompson { 1123e0a69b51SAndrew Thompson usb_error_t err; 112402ac6454SAndrew Thompson 112502ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 112602ac6454SAndrew Thompson 1127a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 112802ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 112902ac6454SAndrew Thompson if (err) { 113002ac6454SAndrew Thompson goto done; 113102ac6454SAndrew Thompson } 113202ac6454SAndrew Thompson /* Extra sanity checking */ 113302ac6454SAndrew Thompson if (UGETW(d->wTotalLength) < sizeof(*d)) { 113402ac6454SAndrew Thompson err = USB_ERR_INVAL; 113502ac6454SAndrew Thompson } 113602ac6454SAndrew Thompson done: 113702ac6454SAndrew Thompson return (err); 113802ac6454SAndrew Thompson } 113902ac6454SAndrew Thompson 114002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1141a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 114202ac6454SAndrew Thompson * 114302ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 114402ac6454SAndrew Thompson * ensures that "wTotalLength" is correct. 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_config_desc_full(struct usb_device *udev, struct mtx *mtx, 1152760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, struct malloc_type *mtype, 115302ac6454SAndrew Thompson uint8_t index) 115402ac6454SAndrew Thompson { 1155760bc48eSAndrew Thompson struct usb_config_descriptor cd; 1156760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 115702ac6454SAndrew Thompson uint16_t len; 1158e0a69b51SAndrew Thompson usb_error_t err; 115902ac6454SAndrew Thompson 116002ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 116102ac6454SAndrew Thompson 116202ac6454SAndrew Thompson *ppcd = NULL; 116302ac6454SAndrew Thompson 1164a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 116502ac6454SAndrew Thompson if (err) { 116602ac6454SAndrew Thompson return (err); 116702ac6454SAndrew Thompson } 116802ac6454SAndrew Thompson /* get full descriptor */ 116902ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 117002ac6454SAndrew Thompson if (len < sizeof(*cdesc)) { 117102ac6454SAndrew Thompson /* corrupt descriptor */ 117202ac6454SAndrew Thompson return (USB_ERR_INVAL); 117302ac6454SAndrew Thompson } 117402ac6454SAndrew Thompson cdesc = malloc(len, mtype, M_WAITOK); 117502ac6454SAndrew Thompson if (cdesc == NULL) { 117602ac6454SAndrew Thompson return (USB_ERR_NOMEM); 117702ac6454SAndrew Thompson } 1178a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 117902ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 118002ac6454SAndrew Thompson if (err) { 118102ac6454SAndrew Thompson free(cdesc, mtype); 118202ac6454SAndrew Thompson return (err); 118302ac6454SAndrew Thompson } 118402ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 118502ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 118602ac6454SAndrew Thompson 118702ac6454SAndrew Thompson *ppcd = cdesc; 118802ac6454SAndrew Thompson 118902ac6454SAndrew Thompson return (0); /* success */ 119002ac6454SAndrew Thompson } 119102ac6454SAndrew Thompson 119202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1193a593f6b8SAndrew Thompson * usbd_req_get_device_desc 119402ac6454SAndrew Thompson * 119502ac6454SAndrew Thompson * Returns: 119602ac6454SAndrew Thompson * 0: Success 119702ac6454SAndrew Thompson * Else: Failure 119802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1199e0a69b51SAndrew Thompson usb_error_t 1200a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1201760bc48eSAndrew Thompson struct usb_device_descriptor *d) 120202ac6454SAndrew Thompson { 120302ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1204a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 120502ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 120602ac6454SAndrew Thompson } 120702ac6454SAndrew Thompson 120802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1209a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 121002ac6454SAndrew Thompson * 121102ac6454SAndrew Thompson * Returns: 121202ac6454SAndrew Thompson * 0: Success 121302ac6454SAndrew Thompson * Else: Failure 121402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1215e0a69b51SAndrew Thompson usb_error_t 1216a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 121702ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 121802ac6454SAndrew Thompson { 1219a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1220760bc48eSAndrew Thompson struct usb_device_request req; 122102ac6454SAndrew Thompson 1222bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 122302ac6454SAndrew Thompson return (USB_ERR_INVAL); 1224bd73b187SAlfred Perlstein 122502ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 122602ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 122702ac6454SAndrew Thompson USETW(req.wValue, 0); 122802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 122902ac6454SAndrew Thompson req.wIndex[1] = 0; 123002ac6454SAndrew Thompson USETW(req.wLength, 1); 1231a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 123202ac6454SAndrew Thompson } 123302ac6454SAndrew Thompson 123402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1235a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 123602ac6454SAndrew Thompson * 123702ac6454SAndrew Thompson * Returns: 123802ac6454SAndrew Thompson * 0: Success 123902ac6454SAndrew Thompson * Else: Failure 124002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1241e0a69b51SAndrew Thompson usb_error_t 1242a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 124302ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 124402ac6454SAndrew Thompson { 1245a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1246760bc48eSAndrew Thompson struct usb_device_request req; 124702ac6454SAndrew Thompson 1248bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 124902ac6454SAndrew Thompson return (USB_ERR_INVAL); 1250bd73b187SAlfred Perlstein 125102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 125202ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 125302ac6454SAndrew Thompson req.wValue[0] = alt_no; 125402ac6454SAndrew Thompson req.wValue[1] = 0; 125502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 125602ac6454SAndrew Thompson req.wIndex[1] = 0; 125702ac6454SAndrew Thompson USETW(req.wLength, 0); 1258a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 125902ac6454SAndrew Thompson } 126002ac6454SAndrew Thompson 126102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1262a593f6b8SAndrew Thompson * usbd_req_get_device_status 126302ac6454SAndrew Thompson * 126402ac6454SAndrew Thompson * Returns: 126502ac6454SAndrew Thompson * 0: Success 126602ac6454SAndrew Thompson * Else: Failure 126702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1268e0a69b51SAndrew Thompson usb_error_t 1269a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1270760bc48eSAndrew Thompson struct usb_status *st) 127102ac6454SAndrew Thompson { 1272760bc48eSAndrew Thompson struct usb_device_request req; 127302ac6454SAndrew Thompson 127402ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 127502ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 127602ac6454SAndrew Thompson USETW(req.wValue, 0); 127702ac6454SAndrew Thompson USETW(req.wIndex, 0); 127802ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1279a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 128002ac6454SAndrew Thompson } 128102ac6454SAndrew Thompson 128202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1283a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 128402ac6454SAndrew Thompson * 128502ac6454SAndrew Thompson * Returns: 128602ac6454SAndrew Thompson * 0: Success 128702ac6454SAndrew Thompson * Else: Failure 128802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1289e0a69b51SAndrew Thompson usb_error_t 1290a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1291760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 129202ac6454SAndrew Thompson { 1293760bc48eSAndrew Thompson struct usb_device_request req; 129402ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 129502ac6454SAndrew Thompson 129602ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 129702ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 129802ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 129902ac6454SAndrew Thompson USETW(req.wIndex, 0); 130002ac6454SAndrew Thompson USETW(req.wLength, len); 1301a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 130202ac6454SAndrew Thompson } 130302ac6454SAndrew Thompson 130402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1305*963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor 1306*963169b4SHans Petter Selasky * 1307*963169b4SHans Petter Selasky * Returns: 1308*963169b4SHans Petter Selasky * 0: Success 1309*963169b4SHans Petter Selasky * Else: Failure 1310*963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1311*963169b4SHans Petter Selasky usb_error_t 1312*963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1313*963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports) 1314*963169b4SHans Petter Selasky { 1315*963169b4SHans Petter Selasky struct usb_device_request req; 1316*963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); 1317*963169b4SHans Petter Selasky 1318*963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE; 1319*963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR; 1320*963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0); 1321*963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1322*963169b4SHans Petter Selasky USETW(req.wLength, len); 1323*963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd)); 1324*963169b4SHans Petter Selasky } 1325*963169b4SHans Petter Selasky 1326*963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1327a593f6b8SAndrew Thompson * usbd_req_get_hub_status 132802ac6454SAndrew Thompson * 132902ac6454SAndrew Thompson * Returns: 133002ac6454SAndrew Thompson * 0: Success 133102ac6454SAndrew Thompson * Else: Failure 133202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1333e0a69b51SAndrew Thompson usb_error_t 1334a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1335760bc48eSAndrew Thompson struct usb_hub_status *st) 133602ac6454SAndrew Thompson { 1337760bc48eSAndrew Thompson struct usb_device_request req; 133802ac6454SAndrew Thompson 133902ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 134002ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 134102ac6454SAndrew Thompson USETW(req.wValue, 0); 134202ac6454SAndrew Thompson USETW(req.wIndex, 0); 1343760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1344a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 134502ac6454SAndrew Thompson } 134602ac6454SAndrew Thompson 134702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1348a593f6b8SAndrew Thompson * usbd_req_set_address 134902ac6454SAndrew Thompson * 135002ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 135102ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 135202ac6454SAndrew Thompson * 135302ac6454SAndrew Thompson * Returns: 135402ac6454SAndrew Thompson * 0: Success 135502ac6454SAndrew Thompson * Else: Failure 135602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1357e0a69b51SAndrew Thompson usb_error_t 1358a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 135902ac6454SAndrew Thompson { 1360760bc48eSAndrew Thompson struct usb_device_request req; 1361*963169b4SHans Petter Selasky usb_error_t err; 136202ac6454SAndrew Thompson 136302ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 136402ac6454SAndrew Thompson 136502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 136602ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 136702ac6454SAndrew Thompson USETW(req.wValue, addr); 136802ac6454SAndrew Thompson USETW(req.wIndex, 0); 136902ac6454SAndrew Thompson USETW(req.wLength, 0); 137002ac6454SAndrew Thompson 1371*963169b4SHans Petter Selasky err = USB_ERR_INVAL; 1372*963169b4SHans Petter Selasky 1373*963169b4SHans Petter Selasky /* check if USB controller handles set address */ 1374*963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL) 1375*963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr); 1376*963169b4SHans Petter Selasky 1377*963169b4SHans Petter Selasky if (err != USB_ERR_INVAL) 1378*963169b4SHans Petter Selasky goto done; 1379*963169b4SHans Petter Selasky 138002ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1381*963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL, 1382*963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000); 1383*963169b4SHans Petter Selasky 1384*963169b4SHans Petter Selasky done: 1385*963169b4SHans Petter Selasky /* allow device time to set new address */ 1386*963169b4SHans Petter Selasky usb_pause_mtx(mtx, 1387*963169b4SHans Petter Selasky USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); 1388*963169b4SHans Petter Selasky 1389*963169b4SHans Petter Selasky return (err); 139002ac6454SAndrew Thompson } 139102ac6454SAndrew Thompson 139202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1393a593f6b8SAndrew Thompson * usbd_req_get_port_status 139402ac6454SAndrew Thompson * 139502ac6454SAndrew Thompson * Returns: 139602ac6454SAndrew Thompson * 0: Success 139702ac6454SAndrew Thompson * Else: Failure 139802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1399e0a69b51SAndrew Thompson usb_error_t 1400a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1401760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 140202ac6454SAndrew Thompson { 1403760bc48eSAndrew Thompson struct usb_device_request req; 140402ac6454SAndrew Thompson 140502ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 140602ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 140702ac6454SAndrew Thompson USETW(req.wValue, 0); 140802ac6454SAndrew Thompson req.wIndex[0] = port; 140902ac6454SAndrew Thompson req.wIndex[1] = 0; 141002ac6454SAndrew Thompson USETW(req.wLength, sizeof *ps); 1411a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, ps)); 141202ac6454SAndrew Thompson } 141302ac6454SAndrew Thompson 141402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1415a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 141602ac6454SAndrew Thompson * 141702ac6454SAndrew Thompson * Returns: 141802ac6454SAndrew Thompson * 0: Success 141902ac6454SAndrew Thompson * Else: Failure 142002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1421e0a69b51SAndrew Thompson usb_error_t 1422a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 142302ac6454SAndrew Thompson uint16_t sel) 142402ac6454SAndrew Thompson { 1425760bc48eSAndrew Thompson struct usb_device_request req; 142602ac6454SAndrew Thompson 142702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 142802ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 142902ac6454SAndrew Thompson USETW(req.wValue, sel); 143002ac6454SAndrew Thompson USETW(req.wIndex, 0); 143102ac6454SAndrew Thompson USETW(req.wLength, 0); 1432a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 143302ac6454SAndrew Thompson } 143402ac6454SAndrew Thompson 143502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1436a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 143702ac6454SAndrew Thompson * 143802ac6454SAndrew Thompson * Returns: 143902ac6454SAndrew Thompson * 0: Success 144002ac6454SAndrew Thompson * Else: Failure 144102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1442e0a69b51SAndrew Thompson usb_error_t 1443a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 144402ac6454SAndrew Thompson uint16_t sel) 144502ac6454SAndrew Thompson { 1446760bc48eSAndrew Thompson struct usb_device_request req; 144702ac6454SAndrew Thompson 144802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 144902ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 145002ac6454SAndrew Thompson USETW(req.wValue, sel); 145102ac6454SAndrew Thompson USETW(req.wIndex, 0); 145202ac6454SAndrew Thompson USETW(req.wLength, 0); 1453a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 145402ac6454SAndrew Thompson } 145502ac6454SAndrew Thompson 145602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1457*963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout 1458*963169b4SHans Petter Selasky * 1459*963169b4SHans Petter Selasky * Returns: 1460*963169b4SHans Petter Selasky * 0: Success 1461*963169b4SHans Petter Selasky * Else: Failure 1462*963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1463*963169b4SHans Petter Selasky usb_error_t 1464*963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, 1465*963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1466*963169b4SHans Petter Selasky { 1467*963169b4SHans Petter Selasky struct usb_device_request req; 1468*963169b4SHans Petter Selasky 1469*963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1470*963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1471*963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT); 1472*963169b4SHans Petter Selasky req.wIndex[0] = port; 1473*963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1474*963169b4SHans Petter Selasky USETW(req.wLength, 0); 1475*963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1476*963169b4SHans Petter Selasky } 1477*963169b4SHans Petter Selasky 1478*963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1479*963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout 1480*963169b4SHans Petter Selasky * 1481*963169b4SHans Petter Selasky * Returns: 1482*963169b4SHans Petter Selasky * 0: Success 1483*963169b4SHans Petter Selasky * Else: Failure 1484*963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1485*963169b4SHans Petter Selasky usb_error_t 1486*963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, 1487*963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1488*963169b4SHans Petter Selasky { 1489*963169b4SHans Petter Selasky struct usb_device_request req; 1490*963169b4SHans Petter Selasky 1491*963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1492*963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1493*963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT); 1494*963169b4SHans Petter Selasky req.wIndex[0] = port; 1495*963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1496*963169b4SHans Petter Selasky USETW(req.wLength, 0); 1497*963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1498*963169b4SHans Petter Selasky } 1499*963169b4SHans Petter Selasky 1500*963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1501*963169b4SHans Petter Selasky * usbd_req_set_hub_depth 1502*963169b4SHans Petter Selasky * 1503*963169b4SHans Petter Selasky * Returns: 1504*963169b4SHans Petter Selasky * 0: Success 1505*963169b4SHans Petter Selasky * Else: Failure 1506*963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1507*963169b4SHans Petter Selasky usb_error_t 1508*963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, 1509*963169b4SHans Petter Selasky uint16_t depth) 1510*963169b4SHans Petter Selasky { 1511*963169b4SHans Petter Selasky struct usb_device_request req; 1512*963169b4SHans Petter Selasky 1513*963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE; 1514*963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH; 1515*963169b4SHans Petter Selasky USETW(req.wValue, depth); 1516*963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1517*963169b4SHans Petter Selasky USETW(req.wLength, 0); 1518*963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1519*963169b4SHans Petter Selasky } 1520*963169b4SHans Petter Selasky 1521*963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1522a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 152302ac6454SAndrew Thompson * 152402ac6454SAndrew Thompson * Returns: 152502ac6454SAndrew Thompson * 0: Success 152602ac6454SAndrew Thompson * Else: Failure 152702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1528e0a69b51SAndrew Thompson usb_error_t 1529a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 153002ac6454SAndrew Thompson uint8_t port, uint16_t sel) 153102ac6454SAndrew Thompson { 1532760bc48eSAndrew Thompson struct usb_device_request req; 153302ac6454SAndrew Thompson 153402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 153502ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 153602ac6454SAndrew Thompson USETW(req.wValue, sel); 153702ac6454SAndrew Thompson req.wIndex[0] = port; 153802ac6454SAndrew Thompson req.wIndex[1] = 0; 153902ac6454SAndrew Thompson USETW(req.wLength, 0); 1540a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 154102ac6454SAndrew Thompson } 154202ac6454SAndrew Thompson 154302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1544a593f6b8SAndrew Thompson * usbd_req_set_port_feature 154502ac6454SAndrew Thompson * 154602ac6454SAndrew Thompson * Returns: 154702ac6454SAndrew Thompson * 0: Success 154802ac6454SAndrew Thompson * Else: Failure 154902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1550e0a69b51SAndrew Thompson usb_error_t 1551a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, 155202ac6454SAndrew Thompson uint8_t port, uint16_t sel) 155302ac6454SAndrew Thompson { 1554760bc48eSAndrew Thompson struct usb_device_request req; 155502ac6454SAndrew Thompson 155602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 155702ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 155802ac6454SAndrew Thompson USETW(req.wValue, sel); 155902ac6454SAndrew Thompson req.wIndex[0] = port; 156002ac6454SAndrew Thompson req.wIndex[1] = 0; 156102ac6454SAndrew Thompson USETW(req.wLength, 0); 1562a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 156302ac6454SAndrew Thompson } 156402ac6454SAndrew Thompson 156502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1566a593f6b8SAndrew Thompson * usbd_req_set_protocol 156702ac6454SAndrew Thompson * 156802ac6454SAndrew Thompson * Returns: 156902ac6454SAndrew Thompson * 0: Success 157002ac6454SAndrew Thompson * Else: Failure 157102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1572e0a69b51SAndrew Thompson usb_error_t 1573a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 157402ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 157502ac6454SAndrew Thompson { 1576a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1577760bc48eSAndrew Thompson struct usb_device_request req; 157802ac6454SAndrew Thompson 157902ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 158002ac6454SAndrew Thompson return (USB_ERR_INVAL); 158102ac6454SAndrew Thompson } 158202ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 158302ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 158402ac6454SAndrew Thompson 158502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 158602ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 158702ac6454SAndrew Thompson USETW(req.wValue, report); 158802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 158902ac6454SAndrew Thompson req.wIndex[1] = 0; 159002ac6454SAndrew Thompson USETW(req.wLength, 0); 1591a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 159202ac6454SAndrew Thompson } 159302ac6454SAndrew Thompson 159402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1595a593f6b8SAndrew Thompson * usbd_req_set_report 159602ac6454SAndrew Thompson * 159702ac6454SAndrew Thompson * Returns: 159802ac6454SAndrew Thompson * 0: Success 159902ac6454SAndrew Thompson * Else: Failure 160002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1601e0a69b51SAndrew Thompson usb_error_t 1602a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 160302ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 160402ac6454SAndrew Thompson { 1605a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1606760bc48eSAndrew Thompson struct usb_device_request req; 160702ac6454SAndrew Thompson 160802ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 160902ac6454SAndrew Thompson return (USB_ERR_INVAL); 161002ac6454SAndrew Thompson } 161102ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 161202ac6454SAndrew Thompson 161302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 161402ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 161502ac6454SAndrew Thompson USETW2(req.wValue, type, id); 161602ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 161702ac6454SAndrew Thompson req.wIndex[1] = 0; 161802ac6454SAndrew Thompson USETW(req.wLength, len); 1619a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 162002ac6454SAndrew Thompson } 162102ac6454SAndrew Thompson 162202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1623a593f6b8SAndrew Thompson * usbd_req_get_report 162402ac6454SAndrew Thompson * 162502ac6454SAndrew Thompson * Returns: 162602ac6454SAndrew Thompson * 0: Success 162702ac6454SAndrew Thompson * Else: Failure 162802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1629e0a69b51SAndrew Thompson usb_error_t 1630a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 163102ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 163202ac6454SAndrew Thompson { 1633a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1634760bc48eSAndrew Thompson struct usb_device_request req; 163502ac6454SAndrew Thompson 163602ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { 163702ac6454SAndrew Thompson return (USB_ERR_INVAL); 163802ac6454SAndrew Thompson } 163902ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 164002ac6454SAndrew Thompson 164102ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 164202ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 164302ac6454SAndrew Thompson USETW2(req.wValue, type, id); 164402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 164502ac6454SAndrew Thompson req.wIndex[1] = 0; 164602ac6454SAndrew Thompson USETW(req.wLength, len); 1647a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 164802ac6454SAndrew Thompson } 164902ac6454SAndrew Thompson 165002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1651a593f6b8SAndrew Thompson * usbd_req_set_idle 165202ac6454SAndrew Thompson * 165302ac6454SAndrew Thompson * Returns: 165402ac6454SAndrew Thompson * 0: Success 165502ac6454SAndrew Thompson * Else: Failure 165602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1657e0a69b51SAndrew Thompson usb_error_t 1658a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 165902ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 166002ac6454SAndrew Thompson { 1661a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1662760bc48eSAndrew Thompson struct usb_device_request req; 166302ac6454SAndrew Thompson 166402ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 166502ac6454SAndrew Thompson return (USB_ERR_INVAL); 166602ac6454SAndrew Thompson } 166702ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 166802ac6454SAndrew Thompson 166902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 167002ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 167102ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 167202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 167302ac6454SAndrew Thompson req.wIndex[1] = 0; 167402ac6454SAndrew Thompson USETW(req.wLength, 0); 1675a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 167602ac6454SAndrew Thompson } 167702ac6454SAndrew Thompson 167802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1679a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 168002ac6454SAndrew Thompson * 168102ac6454SAndrew Thompson * Returns: 168202ac6454SAndrew Thompson * 0: Success 168302ac6454SAndrew Thompson * Else: Failure 168402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1685e0a69b51SAndrew Thompson usb_error_t 1686a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 168702ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 168802ac6454SAndrew Thompson { 1689a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1690760bc48eSAndrew Thompson struct usb_device_request req; 169102ac6454SAndrew Thompson 169202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 169302ac6454SAndrew Thompson return (USB_ERR_INVAL); 169402ac6454SAndrew Thompson } 169502ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 169602ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 169702ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 169802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 169902ac6454SAndrew Thompson req.wIndex[1] = 0; 170002ac6454SAndrew Thompson USETW(req.wLength, size); 1701a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 170202ac6454SAndrew Thompson } 170302ac6454SAndrew Thompson 170402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1705a593f6b8SAndrew Thompson * usbd_req_set_config 170602ac6454SAndrew Thompson * 170702ac6454SAndrew Thompson * This function is used to select the current configuration number in 170802ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 170902ac6454SAndrew Thompson * configuration the function of the interfaces can change. 171002ac6454SAndrew Thompson * 171102ac6454SAndrew Thompson * Returns: 171202ac6454SAndrew Thompson * 0: Success 171302ac6454SAndrew Thompson * Else: Failure 171402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1715e0a69b51SAndrew Thompson usb_error_t 1716a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 171702ac6454SAndrew Thompson { 1718760bc48eSAndrew Thompson struct usb_device_request req; 171902ac6454SAndrew Thompson 172002ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 172102ac6454SAndrew Thompson 172202ac6454SAndrew Thompson /* do "set configuration" request */ 172302ac6454SAndrew Thompson 172402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 172502ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 172602ac6454SAndrew Thompson req.wValue[0] = conf; 172702ac6454SAndrew Thompson req.wValue[1] = 0; 172802ac6454SAndrew Thompson USETW(req.wIndex, 0); 172902ac6454SAndrew Thompson USETW(req.wLength, 0); 1730a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 173102ac6454SAndrew Thompson } 173202ac6454SAndrew Thompson 173302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1734a593f6b8SAndrew Thompson * usbd_req_get_config 173502ac6454SAndrew Thompson * 173602ac6454SAndrew Thompson * Returns: 173702ac6454SAndrew Thompson * 0: Success 173802ac6454SAndrew Thompson * Else: Failure 173902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1740e0a69b51SAndrew Thompson usb_error_t 1741a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 174202ac6454SAndrew Thompson { 1743760bc48eSAndrew Thompson struct usb_device_request req; 174402ac6454SAndrew Thompson 174502ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 174602ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 174702ac6454SAndrew Thompson USETW(req.wValue, 0); 174802ac6454SAndrew Thompson USETW(req.wIndex, 0); 174902ac6454SAndrew Thompson USETW(req.wLength, 1); 1750a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 175102ac6454SAndrew Thompson } 175202ac6454SAndrew Thompson 175302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1754*963169b4SHans Petter Selasky * usbd_setup_device_desc 1755*963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1756*963169b4SHans Petter Selasky usb_error_t 1757*963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) 1758*963169b4SHans Petter Selasky { 1759*963169b4SHans Petter Selasky usb_error_t err; 1760*963169b4SHans Petter Selasky 1761*963169b4SHans Petter Selasky /* 1762*963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor ! 1763*963169b4SHans Petter Selasky * 1764*963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor 1765*963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size 1766*963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor 1767*963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint 1768*963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new 1769*963169b4SHans Petter Selasky * USB control request will be setup! 1770*963169b4SHans Petter Selasky */ 1771*963169b4SHans Petter Selasky switch (udev->speed) { 1772*963169b4SHans Petter Selasky case USB_SPEED_FULL: 1773*963169b4SHans Petter Selasky case USB_SPEED_LOW: 1774*963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 1775*963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 1776*963169b4SHans Petter Selasky if (err != 0) { 1777*963169b4SHans Petter Selasky DPRINTFN(0, "getting device descriptor " 1778*963169b4SHans Petter Selasky "at addr %d failed, %s\n", udev->address, 1779*963169b4SHans Petter Selasky usbd_errstr(err)); 1780*963169b4SHans Petter Selasky return (err); 1781*963169b4SHans Petter Selasky } 1782*963169b4SHans Petter Selasky break; 1783*963169b4SHans Petter Selasky default: 1784*963169b4SHans Petter Selasky DPRINTF("Minimum MaxPacketSize is large enough " 1785*963169b4SHans Petter Selasky "to hold the complete device descriptor\n"); 1786*963169b4SHans Petter Selasky break; 1787*963169b4SHans Petter Selasky } 1788*963169b4SHans Petter Selasky 1789*963169b4SHans Petter Selasky /* get the full device descriptor */ 1790*963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1791*963169b4SHans Petter Selasky 1792*963169b4SHans Petter Selasky /* try one more time, if error */ 1793*963169b4SHans Petter Selasky if (err) 1794*963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1795*963169b4SHans Petter Selasky 1796*963169b4SHans Petter Selasky if (err) { 1797*963169b4SHans Petter Selasky DPRINTF("addr=%d, getting full desc failed\n", 1798*963169b4SHans Petter Selasky udev->address); 1799*963169b4SHans Petter Selasky return (err); 1800*963169b4SHans Petter Selasky } 1801*963169b4SHans Petter Selasky 1802*963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " 1803*963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", 1804*963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB), 1805*963169b4SHans Petter Selasky udev->ddesc.bDeviceClass, 1806*963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass, 1807*963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol, 1808*963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize, 1809*963169b4SHans Petter Selasky udev->ddesc.bLength, 1810*963169b4SHans Petter Selasky udev->speed); 1811*963169b4SHans Petter Selasky 1812*963169b4SHans Petter Selasky return (err); 1813*963169b4SHans Petter Selasky } 1814*963169b4SHans Petter Selasky 1815*963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1816a593f6b8SAndrew Thompson * usbd_req_re_enumerate 181702ac6454SAndrew Thompson * 181802ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 181902ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 182002ac6454SAndrew Thompson * new configuration. 182102ac6454SAndrew Thompson * 182202ac6454SAndrew Thompson * Returns: 182302ac6454SAndrew Thompson * 0: Success 182402ac6454SAndrew Thompson * Else: Failure 182502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1826e0a69b51SAndrew Thompson usb_error_t 1827a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 182802ac6454SAndrew Thompson { 1829760bc48eSAndrew Thompson struct usb_device *parent_hub; 1830e0a69b51SAndrew Thompson usb_error_t err; 183102ac6454SAndrew Thompson uint8_t old_addr; 183202ac6454SAndrew Thompson uint8_t do_retry = 1; 183302ac6454SAndrew Thompson 1834f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 183502ac6454SAndrew Thompson return (USB_ERR_INVAL); 183602ac6454SAndrew Thompson } 183702ac6454SAndrew Thompson old_addr = udev->address; 183802ac6454SAndrew Thompson parent_hub = udev->parent_hub; 183902ac6454SAndrew Thompson if (parent_hub == NULL) { 184002ac6454SAndrew Thompson return (USB_ERR_INVAL); 184102ac6454SAndrew Thompson } 184202ac6454SAndrew Thompson retry: 1843a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 184402ac6454SAndrew Thompson if (err) { 184503797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 1846a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 184702ac6454SAndrew Thompson goto done; 184802ac6454SAndrew Thompson } 1849*963169b4SHans Petter Selasky 185002ac6454SAndrew Thompson /* 185102ac6454SAndrew Thompson * After that the port has been reset our device should be at 185202ac6454SAndrew Thompson * address zero: 185302ac6454SAndrew Thompson */ 185402ac6454SAndrew Thompson udev->address = USB_START_ADDR; 185502ac6454SAndrew Thompson 185602ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 185702ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 185802ac6454SAndrew Thompson 1859*963169b4SHans Petter Selasky /* reset USB state */ 1860*963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED); 1861*963169b4SHans Petter Selasky 186202ac6454SAndrew Thompson /* 186302ac6454SAndrew Thompson * Restore device address: 186402ac6454SAndrew Thompson */ 1865a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 186602ac6454SAndrew Thompson if (err) { 186702ac6454SAndrew Thompson /* XXX ignore any errors! */ 186803797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 1869a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 187002ac6454SAndrew Thompson } 1871*963169b4SHans Petter Selasky /* 1872*963169b4SHans Petter Selasky * Restore device address, if the controller driver did not 1873*963169b4SHans Petter Selasky * set a new one: 1874*963169b4SHans Petter Selasky */ 1875*963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 187602ac6454SAndrew Thompson udev->address = old_addr; 187702ac6454SAndrew Thompson 1878*963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */ 1879*963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx); 188002ac6454SAndrew Thompson 188102ac6454SAndrew Thompson done: 188202ac6454SAndrew Thompson if (err && do_retry) { 188302ac6454SAndrew Thompson /* give the USB firmware some time to load */ 1884a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 188502ac6454SAndrew Thompson /* no more retries after this retry */ 188602ac6454SAndrew Thompson do_retry = 0; 188702ac6454SAndrew Thompson /* try again */ 188802ac6454SAndrew Thompson goto retry; 188902ac6454SAndrew Thompson } 189002ac6454SAndrew Thompson /* restore address */ 1891*963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 189202ac6454SAndrew Thompson udev->address = old_addr; 1893*963169b4SHans Petter Selasky /* update state, if successful */ 1894*963169b4SHans Petter Selasky if (err == 0) 1895*963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED); 189602ac6454SAndrew Thompson return (err); 189702ac6454SAndrew Thompson } 189802ac6454SAndrew Thompson 189902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1900a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 190102ac6454SAndrew Thompson * 190202ac6454SAndrew Thompson * Returns: 190302ac6454SAndrew Thompson * 0: Success 190402ac6454SAndrew Thompson * Else: Failure 190502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1906e0a69b51SAndrew Thompson usb_error_t 1907a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 190802ac6454SAndrew Thompson uint16_t sel) 190902ac6454SAndrew Thompson { 1910760bc48eSAndrew Thompson struct usb_device_request req; 191102ac6454SAndrew Thompson 191202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 191302ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 191402ac6454SAndrew Thompson USETW(req.wValue, sel); 191502ac6454SAndrew Thompson USETW(req.wIndex, 0); 191602ac6454SAndrew Thompson USETW(req.wLength, 0); 1917a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 191802ac6454SAndrew Thompson } 191902ac6454SAndrew Thompson 192002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1921a593f6b8SAndrew Thompson * usbd_req_set_device_feature 192202ac6454SAndrew Thompson * 192302ac6454SAndrew Thompson * Returns: 192402ac6454SAndrew Thompson * 0: Success 192502ac6454SAndrew Thompson * Else: Failure 192602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1927e0a69b51SAndrew Thompson usb_error_t 1928a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 192902ac6454SAndrew Thompson uint16_t sel) 193002ac6454SAndrew Thompson { 1931760bc48eSAndrew Thompson struct usb_device_request req; 193202ac6454SAndrew Thompson 193302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 193402ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 193502ac6454SAndrew Thompson USETW(req.wValue, sel); 193602ac6454SAndrew Thompson USETW(req.wIndex, 0); 193702ac6454SAndrew Thompson USETW(req.wLength, 0); 1938a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 193902ac6454SAndrew Thompson } 1940