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 7102ac6454SAndrew Thompson #if 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; 74a593f6b8SAndrew Thompson static int usb_ss_delay = 0; 7502ac6454SAndrew Thompson 769360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW, 77a593f6b8SAndrew Thompson &usb_pr_poll_delay, 0, "USB port reset poll delay in ms"); 789360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW, 79a593f6b8SAndrew Thompson &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms"); 809360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ss_delay, CTLFLAG_RW, 81a593f6b8SAndrew Thompson &usb_ss_delay, 0, "USB status stage delay in ms"); 8202ac6454SAndrew Thompson #endif 8302ac6454SAndrew Thompson 8402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 85a593f6b8SAndrew Thompson * usbd_do_request_callback 8602ac6454SAndrew Thompson * 8702ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control 8802ac6454SAndrew Thompson * transfers. 8902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 9002ac6454SAndrew Thompson void 91ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) 9202ac6454SAndrew Thompson { 9302ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer)); 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9802ac6454SAndrew Thompson case USB_ST_SETUP: 99a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 10002ac6454SAndrew Thompson break; 10102ac6454SAndrew Thompson default: 1028437751dSAndrew Thompson cv_signal(xfer->xroot->udev->default_cv); 10302ac6454SAndrew Thompson break; 10402ac6454SAndrew Thompson } 10502ac6454SAndrew Thompson } 10602ac6454SAndrew Thompson 10702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 108a593f6b8SAndrew Thompson * usb_do_clear_stall_callback 10902ac6454SAndrew Thompson * 11002ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests. 11102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 11202ac6454SAndrew Thompson void 113ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 11402ac6454SAndrew Thompson { 115760bc48eSAndrew Thompson struct usb_device_request req; 116760bc48eSAndrew Thompson struct usb_device *udev; 117ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 118ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end; 119ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first; 12063521bbcSAndrew Thompson uint8_t to; 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson udev = xfer->xroot->udev; 12302ac6454SAndrew Thompson 12402ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 12502ac6454SAndrew Thompson 126ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */ 12702ac6454SAndrew Thompson 128ae60fdfbSAndrew Thompson ep = udev->ep_curr; 129ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max; 130ae60fdfbSAndrew Thompson ep_first = udev->endpoints; 131ae60fdfbSAndrew Thompson to = udev->endpoints_max; 132115df0b6SAndrew Thompson 13302ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 13402ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 135ae60fdfbSAndrew Thompson if (ep == NULL) 136115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */ 137ae60fdfbSAndrew Thompson if (ep->edesc && 138ae60fdfbSAndrew Thompson ep->is_stalled) { 139ae60fdfbSAndrew Thompson ep->toggle_next = 0; 140ae60fdfbSAndrew Thompson ep->is_stalled = 0; 14102ac6454SAndrew Thompson /* start up the current or next transfer, if any */ 142a593f6b8SAndrew Thompson usb_command_wrapper(&ep->endpoint_q, 143ae60fdfbSAndrew Thompson ep->endpoint_q.curr); 14402ac6454SAndrew Thompson } 145ae60fdfbSAndrew Thompson ep++; 14602ac6454SAndrew Thompson 14702ac6454SAndrew Thompson case USB_ST_SETUP: 14802ac6454SAndrew Thompson tr_setup: 149115df0b6SAndrew Thompson if (to == 0) 150ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */ 151ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end)) 152ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */ 153ae60fdfbSAndrew Thompson if (ep->edesc && 154ae60fdfbSAndrew Thompson ep->is_stalled) { 15502ac6454SAndrew Thompson 15602ac6454SAndrew Thompson /* setup a clear-stall packet */ 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 15902ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 16002ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 161ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 16202ac6454SAndrew Thompson req.wIndex[1] = 0; 16302ac6454SAndrew Thompson USETW(req.wLength, 0); 16402ac6454SAndrew Thompson 16502ac6454SAndrew Thompson /* copy in the transfer */ 16602ac6454SAndrew Thompson 167a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 16802ac6454SAndrew Thompson 16902ac6454SAndrew Thompson /* set length */ 170ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 17102ac6454SAndrew Thompson xfer->nframes = 1; 17202ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17302ac6454SAndrew Thompson 174a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 17502ac6454SAndrew Thompson 17602ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 17702ac6454SAndrew Thompson break; 17802ac6454SAndrew Thompson } 179ae60fdfbSAndrew Thompson ep++; 180115df0b6SAndrew Thompson to--; 18102ac6454SAndrew Thompson goto tr_setup; 18202ac6454SAndrew Thompson 18302ac6454SAndrew Thompson default: 18402ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 18502ac6454SAndrew Thompson break; 18602ac6454SAndrew Thompson } 18702ac6454SAndrew Thompson goto tr_setup; 18802ac6454SAndrew Thompson } 18902ac6454SAndrew Thompson 190ae60fdfbSAndrew Thompson /* store current endpoint */ 191ae60fdfbSAndrew Thompson udev->ep_curr = ep; 19202ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 19302ac6454SAndrew Thompson } 19402ac6454SAndrew Thompson 195e0a69b51SAndrew Thompson static usb_handle_req_t * 196a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev) 197459d369eSAndrew Thompson { 198459d369eSAndrew Thompson /* figure out if there is a Handle Request function */ 199f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE) 200a593f6b8SAndrew Thompson return (usb_temp_get_desc_p); 201459d369eSAndrew Thompson else if (udev->parent_hub == NULL) 202459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec); 203459d369eSAndrew Thompson else 204459d369eSAndrew Thompson return (NULL); 205459d369eSAndrew Thompson } 206459d369eSAndrew Thompson 20702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 208a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request 20902ac6454SAndrew Thompson * 21002ac6454SAndrew Thompson * Description of arguments passed to these functions: 21102ac6454SAndrew Thompson * 212760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the 21302ac6454SAndrew Thompson * request should be performed. It is possible to call this function 21402ac6454SAndrew Thompson * in both Host Side mode and Device Side mode. 21502ac6454SAndrew Thompson * 21602ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it 21702ac6454SAndrew Thompson * will get dropped and picked up during the execution of this 21802ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this 21902ac6454SAndrew Thompson * argument is NULL it has no effect. 22002ac6454SAndrew Thompson * 22102ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an 22202ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB 22302ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB 22402ac6454SAndrew Thompson * request, if it is a read or a write. 22502ac6454SAndrew Thompson * 22602ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req" 22702ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which 22802ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can 22902ac6454SAndrew Thompson * be NULL. 23002ac6454SAndrew Thompson * 23102ac6454SAndrew Thompson * "flags" - here is a list of valid flags: 23202ac6454SAndrew Thompson * 23302ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than 23402ac6454SAndrew Thompson * specified 23502ac6454SAndrew Thompson * 23602ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed 23702ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay" 23802ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging. 23902ac6454SAndrew Thompson * 24002ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland 24102ac6454SAndrew Thompson * pointer. 24202ac6454SAndrew Thompson * 24302ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in 24402ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This 24502ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is 24602ac6454SAndrew Thompson * used. 24702ac6454SAndrew Thompson * 24802ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in 24902ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is 25002ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater 25102ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack 25202ac6454SAndrew Thompson * does not allow control requests without a timeout. 25302ac6454SAndrew Thompson * 25402ac6454SAndrew Thompson * NOTE: This function is thread safe. All calls to 255a593f6b8SAndrew Thompson * "usbd_do_request_flags" will be serialised by the use of an 25602ac6454SAndrew Thompson * internal "sx_lock". 25702ac6454SAndrew Thompson * 25802ac6454SAndrew Thompson * Returns: 25902ac6454SAndrew Thompson * 0: Success 26002ac6454SAndrew Thompson * Else: Failure 26102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 262e0a69b51SAndrew Thompson usb_error_t 263a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, 264760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 265e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 26602ac6454SAndrew Thompson { 267e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 268760bc48eSAndrew Thompson struct usb_xfer *xfer; 26902ac6454SAndrew Thompson const void *desc; 27002ac6454SAndrew Thompson int err = 0; 271e0a69b51SAndrew Thompson usb_ticks_t start_ticks; 272e0a69b51SAndrew Thompson usb_ticks_t delta_ticks; 273e0a69b51SAndrew Thompson usb_ticks_t max_ticks; 27402ac6454SAndrew Thompson uint16_t length; 27502ac6454SAndrew Thompson uint16_t temp; 27602ac6454SAndrew Thompson 27702ac6454SAndrew Thompson if (timeout < 50) { 27802ac6454SAndrew Thompson /* timeout is too small */ 27902ac6454SAndrew Thompson timeout = 50; 28002ac6454SAndrew Thompson } 28102ac6454SAndrew Thompson if (timeout > 30000) { 28202ac6454SAndrew Thompson /* timeout is too big */ 28302ac6454SAndrew Thompson timeout = 30000; 28402ac6454SAndrew Thompson } 28502ac6454SAndrew Thompson length = UGETW(req->wLength); 28602ac6454SAndrew Thompson 28702ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " 28802ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", 28902ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest, 29002ac6454SAndrew Thompson req->wValue[1], req->wValue[0], 29102ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0], 29202ac6454SAndrew Thompson req->wLength[1], req->wLength[0]); 29302ac6454SAndrew Thompson 294bd216778SAndrew Thompson /* Check if the device is still alive */ 295bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) { 296bd216778SAndrew Thompson DPRINTF("usb device has gone\n"); 297bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED); 298bd216778SAndrew Thompson } 299bd216778SAndrew Thompson 30002ac6454SAndrew Thompson /* 30102ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not 30202ac6454SAndrew Thompson * check the return value: 30302ac6454SAndrew Thompson */ 30439307315SAndrew Thompson if (actlen) 30502ac6454SAndrew Thompson *actlen = 0; 30639307315SAndrew Thompson 307bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0) 308bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR) 309bdc081c6SAndrew Thompson return (USB_ERR_INVAL); 310bdc081c6SAndrew Thompson #endif 31102ac6454SAndrew Thompson if (mtx) { 31202ac6454SAndrew Thompson mtx_unlock(mtx); 31302ac6454SAndrew Thompson if (mtx != &Giant) { 31402ac6454SAndrew Thompson mtx_assert(mtx, MA_NOTOWNED); 31502ac6454SAndrew Thompson } 31602ac6454SAndrew Thompson } 31702ac6454SAndrew Thompson /* 31802ac6454SAndrew Thompson * Grab the default sx-lock so that serialisation 31902ac6454SAndrew Thompson * is achieved when multiple threads are involved: 32002ac6454SAndrew Thompson */ 32102ac6454SAndrew Thompson 32202ac6454SAndrew Thompson sx_xlock(udev->default_sx); 32302ac6454SAndrew Thompson 324a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 32539307315SAndrew Thompson 326459d369eSAndrew Thompson if (hr_func != NULL) { 327459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n"); 32839307315SAndrew Thompson 329459d369eSAndrew Thompson desc = NULL; 330459d369eSAndrew Thompson temp = 0; 331459d369eSAndrew Thompson 332459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 33339307315SAndrew Thompson if (length != 0) { 334459d369eSAndrew Thompson DPRINTFN(1, "The handle request function " 335459d369eSAndrew Thompson "does not support writing data!\n"); 33639307315SAndrew Thompson err = USB_ERR_INVAL; 33739307315SAndrew Thompson goto done; 33839307315SAndrew Thompson } 33939307315SAndrew Thompson } 340459d369eSAndrew Thompson 341459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */ 34239307315SAndrew Thompson 34339307315SAndrew Thompson USB_BUS_LOCK(udev->bus); 344459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp); 34539307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 34639307315SAndrew Thompson 34739307315SAndrew Thompson if (err) 34839307315SAndrew Thompson goto done; 34939307315SAndrew Thompson 350459d369eSAndrew Thompson if (length > temp) { 35139307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) { 35239307315SAndrew Thompson err = USB_ERR_SHORT_XFER; 35339307315SAndrew Thompson goto done; 35439307315SAndrew Thompson } 355459d369eSAndrew Thompson length = temp; 35639307315SAndrew Thompson } 35739307315SAndrew Thompson if (actlen) 35839307315SAndrew Thompson *actlen = length; 35939307315SAndrew Thompson 36039307315SAndrew Thompson if (length > 0) { 36139307315SAndrew Thompson #if USB_HAVE_USER_IO 36239307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 363459d369eSAndrew Thompson if (copyout(desc, data, length)) { 36439307315SAndrew Thompson err = USB_ERR_INVAL; 36539307315SAndrew Thompson goto done; 36639307315SAndrew Thompson } 36739307315SAndrew Thompson } else 36839307315SAndrew Thompson #endif 369459d369eSAndrew Thompson bcopy(desc, data, length); 37039307315SAndrew Thompson } 371459d369eSAndrew Thompson goto done; /* success */ 37239307315SAndrew Thompson } 37339307315SAndrew Thompson 37402ac6454SAndrew Thompson /* 37502ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any: 37602ac6454SAndrew Thompson */ 377a593f6b8SAndrew Thompson usbd_default_transfer_setup(udev); 37802ac6454SAndrew Thompson 37902ac6454SAndrew Thompson xfer = udev->default_xfer[0]; 38002ac6454SAndrew Thompson if (xfer == NULL) { 38102ac6454SAndrew Thompson /* most likely out of memory */ 38202ac6454SAndrew Thompson err = USB_ERR_NOMEM; 38302ac6454SAndrew Thompson goto done; 38402ac6454SAndrew Thompson } 38502ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 38602ac6454SAndrew Thompson 3874eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE) 38802ac6454SAndrew Thompson xfer->flags.manual_status = 1; 3894eae601eSAndrew Thompson else 39002ac6454SAndrew Thompson xfer->flags.manual_status = 0; 3914eae601eSAndrew Thompson 3924eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK) 3934eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1; 3944eae601eSAndrew Thompson else 3954eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0; 39602ac6454SAndrew Thompson 39702ac6454SAndrew Thompson xfer->timeout = timeout; 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson start_ticks = ticks; 40002ac6454SAndrew Thompson 40102ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout); 40202ac6454SAndrew Thompson 403a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); 40402ac6454SAndrew Thompson 405ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); 40602ac6454SAndrew Thompson xfer->nframes = 2; 40702ac6454SAndrew Thompson 40802ac6454SAndrew Thompson while (1) { 40902ac6454SAndrew Thompson temp = length; 41002ac6454SAndrew Thompson if (temp > xfer->max_data_length) { 411ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer); 41202ac6454SAndrew Thompson } 413ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp); 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson if (temp > 0) { 41602ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 417bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 41802ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 41902ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 420a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1, 42102ac6454SAndrew Thompson 0, data, temp); 42202ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 42302ac6454SAndrew Thompson if (err) { 42402ac6454SAndrew Thompson err = USB_ERR_INVAL; 42502ac6454SAndrew Thompson break; 42602ac6454SAndrew Thompson } 427bdc081c6SAndrew Thompson } else 428bdc081c6SAndrew Thompson #endif 429a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 430bdc081c6SAndrew Thompson 0, data, temp); 43102ac6454SAndrew Thompson } 43202ac6454SAndrew Thompson xfer->nframes = 2; 43302ac6454SAndrew Thompson } else { 43402ac6454SAndrew Thompson if (xfer->frlengths[0] == 0) { 43502ac6454SAndrew Thompson if (xfer->flags.manual_status) { 43602ac6454SAndrew Thompson #if USB_DEBUG 43702ac6454SAndrew Thompson int temp; 43802ac6454SAndrew Thompson 439a593f6b8SAndrew Thompson temp = usb_ss_delay; 44002ac6454SAndrew Thompson if (temp > 5000) { 44102ac6454SAndrew Thompson temp = 5000; 44202ac6454SAndrew Thompson } 44302ac6454SAndrew Thompson if (temp > 0) { 444a593f6b8SAndrew Thompson usb_pause_mtx( 44502ac6454SAndrew Thompson xfer->xroot->xfer_mtx, 44602ac6454SAndrew Thompson USB_MS_TO_TICKS(temp)); 44702ac6454SAndrew Thompson } 44802ac6454SAndrew Thompson #endif 44902ac6454SAndrew Thompson xfer->flags.manual_status = 0; 45002ac6454SAndrew Thompson } else { 45102ac6454SAndrew Thompson break; 45202ac6454SAndrew Thompson } 45302ac6454SAndrew Thompson } 45402ac6454SAndrew Thompson xfer->nframes = 1; 45502ac6454SAndrew Thompson } 45602ac6454SAndrew Thompson 457a593f6b8SAndrew Thompson usbd_transfer_start(xfer); 45802ac6454SAndrew Thompson 459a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) { 4608437751dSAndrew Thompson cv_wait(udev->default_cv, 46102ac6454SAndrew Thompson xfer->xroot->xfer_mtx); 46202ac6454SAndrew Thompson } 46302ac6454SAndrew Thompson 46402ac6454SAndrew Thompson err = xfer->error; 46502ac6454SAndrew Thompson 46602ac6454SAndrew Thompson if (err) { 46702ac6454SAndrew Thompson break; 46802ac6454SAndrew Thompson } 46902ac6454SAndrew Thompson /* subtract length of SETUP packet, if any */ 47002ac6454SAndrew Thompson 47102ac6454SAndrew Thompson if (xfer->aframes > 0) { 47202ac6454SAndrew Thompson xfer->actlen -= xfer->frlengths[0]; 47302ac6454SAndrew Thompson } else { 47402ac6454SAndrew Thompson xfer->actlen = 0; 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson 47702ac6454SAndrew Thompson /* check for short packet */ 47802ac6454SAndrew Thompson 47902ac6454SAndrew Thompson if (temp > xfer->actlen) { 48002ac6454SAndrew Thompson temp = xfer->actlen; 48102ac6454SAndrew Thompson length = temp; 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson if (temp > 0) { 48402ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) { 485bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 48602ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 48702ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 488a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1, 48902ac6454SAndrew Thompson 0, data, temp); 49002ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 49102ac6454SAndrew Thompson if (err) { 49202ac6454SAndrew Thompson err = USB_ERR_INVAL; 49302ac6454SAndrew Thompson break; 49402ac6454SAndrew Thompson } 495bdc081c6SAndrew Thompson } else 496bdc081c6SAndrew Thompson #endif 497a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1, 49802ac6454SAndrew Thompson 0, data, temp); 49902ac6454SAndrew Thompson } 50002ac6454SAndrew Thompson } 50102ac6454SAndrew Thompson /* 50202ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup 50302ac6454SAndrew Thompson * packet again: 50402ac6454SAndrew Thompson */ 505ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 50602ac6454SAndrew Thompson 50702ac6454SAndrew Thompson /* update length and data pointer */ 50802ac6454SAndrew Thompson length -= temp; 50902ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp); 51002ac6454SAndrew Thompson 51102ac6454SAndrew Thompson if (actlen) { 51202ac6454SAndrew Thompson (*actlen) += temp; 51302ac6454SAndrew Thompson } 51402ac6454SAndrew Thompson /* check for timeout */ 51502ac6454SAndrew Thompson 51602ac6454SAndrew Thompson delta_ticks = ticks - start_ticks; 51702ac6454SAndrew Thompson if (delta_ticks > max_ticks) { 51802ac6454SAndrew Thompson if (!err) { 51902ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 52002ac6454SAndrew Thompson } 52102ac6454SAndrew Thompson } 52202ac6454SAndrew Thompson if (err) { 52302ac6454SAndrew Thompson break; 52402ac6454SAndrew Thompson } 52502ac6454SAndrew Thompson } 52602ac6454SAndrew Thompson 52702ac6454SAndrew Thompson if (err) { 52802ac6454SAndrew Thompson /* 52902ac6454SAndrew Thompson * Make sure that the control endpoint is no longer 53002ac6454SAndrew Thompson * blocked in case of a non-transfer related error: 53102ac6454SAndrew Thompson */ 532a593f6b8SAndrew Thompson usbd_transfer_stop(xfer); 53302ac6454SAndrew Thompson } 53402ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 53502ac6454SAndrew Thompson 53602ac6454SAndrew Thompson done: 53702ac6454SAndrew Thompson sx_xunlock(udev->default_sx); 53802ac6454SAndrew Thompson 53902ac6454SAndrew Thompson if (mtx) { 54002ac6454SAndrew Thompson mtx_lock(mtx); 54102ac6454SAndrew Thompson } 542e0a69b51SAndrew Thompson return ((usb_error_t)err); 54302ac6454SAndrew Thompson } 54402ac6454SAndrew Thompson 54502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 546a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code 54702ac6454SAndrew Thompson * 54802ac6454SAndrew Thompson * This function is factored out code. It does basically the same like 549a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the 55002ac6454SAndrew Thompson * passed process argument before doing the USB request. If the 55102ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It 55202ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked 55302ac6454SAndrew Thompson * when calling this function. 55402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 555e0a69b51SAndrew Thompson usb_error_t 556a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, 557760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 558e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 55902ac6454SAndrew Thompson { 560e0a69b51SAndrew Thompson usb_error_t err; 56102ac6454SAndrew Thompson uint16_t len; 56202ac6454SAndrew Thompson 56302ac6454SAndrew Thompson /* get request data length */ 56402ac6454SAndrew Thompson len = UGETW(req->wLength); 56502ac6454SAndrew Thompson 56602ac6454SAndrew Thompson /* check if the device is being detached */ 567a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) { 56802ac6454SAndrew Thompson err = USB_ERR_IOERROR; 56902ac6454SAndrew Thompson goto done; 57002ac6454SAndrew Thompson } 57102ac6454SAndrew Thompson 57202ac6454SAndrew Thompson /* forward the USB request */ 573a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx, 57402ac6454SAndrew Thompson req, data, flags, actlen, timeout); 57502ac6454SAndrew Thompson 57602ac6454SAndrew Thompson done: 57702ac6454SAndrew Thompson /* on failure we zero the data */ 57802ac6454SAndrew Thompson /* on short packet we zero the unused data */ 57902ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { 58002ac6454SAndrew Thompson if (err) 58102ac6454SAndrew Thompson memset(data, 0, len); 58202ac6454SAndrew Thompson else if (actlen && *actlen != len) 58302ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen); 58402ac6454SAndrew Thompson } 58502ac6454SAndrew Thompson return (err); 58602ac6454SAndrew Thompson } 58702ac6454SAndrew Thompson 58802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 589a593f6b8SAndrew Thompson * usbd_req_reset_port 59002ac6454SAndrew Thompson * 59102ac6454SAndrew Thompson * This function will instruct an USB HUB to perform a reset sequence 59202ac6454SAndrew Thompson * on the specified port number. 59302ac6454SAndrew Thompson * 59402ac6454SAndrew Thompson * Returns: 59502ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero. 59602ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be 59702ac6454SAndrew Thompson * disabled. 59802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 599e0a69b51SAndrew Thompson usb_error_t 600a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 60102ac6454SAndrew Thompson { 602760bc48eSAndrew Thompson struct usb_port_status ps; 603e0a69b51SAndrew Thompson usb_error_t err; 60402ac6454SAndrew Thompson uint16_t n; 60502ac6454SAndrew Thompson 60602ac6454SAndrew Thompson #if USB_DEBUG 60702ac6454SAndrew Thompson uint16_t pr_poll_delay; 60802ac6454SAndrew Thompson uint16_t pr_recovery_delay; 60902ac6454SAndrew Thompson 61002ac6454SAndrew Thompson #endif 611a593f6b8SAndrew Thompson err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET); 61202ac6454SAndrew Thompson if (err) { 61302ac6454SAndrew Thompson goto done; 61402ac6454SAndrew Thompson } 61502ac6454SAndrew Thompson #if USB_DEBUG 61602ac6454SAndrew Thompson /* range check input parameters */ 617a593f6b8SAndrew Thompson pr_poll_delay = usb_pr_poll_delay; 61802ac6454SAndrew Thompson if (pr_poll_delay < 1) { 61902ac6454SAndrew Thompson pr_poll_delay = 1; 62002ac6454SAndrew Thompson } else if (pr_poll_delay > 1000) { 62102ac6454SAndrew Thompson pr_poll_delay = 1000; 62202ac6454SAndrew Thompson } 623a593f6b8SAndrew Thompson pr_recovery_delay = usb_pr_recovery_delay; 62402ac6454SAndrew Thompson if (pr_recovery_delay > 1000) { 62502ac6454SAndrew Thompson pr_recovery_delay = 1000; 62602ac6454SAndrew Thompson } 62702ac6454SAndrew Thompson #endif 62802ac6454SAndrew Thompson n = 0; 62902ac6454SAndrew Thompson while (1) { 63002ac6454SAndrew Thompson #if USB_DEBUG 63102ac6454SAndrew Thompson /* wait for the device to recover from reset */ 632a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); 63302ac6454SAndrew Thompson n += pr_poll_delay; 63402ac6454SAndrew Thompson #else 63502ac6454SAndrew Thompson /* wait for the device to recover from reset */ 636a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); 63702ac6454SAndrew Thompson n += USB_PORT_RESET_DELAY; 63802ac6454SAndrew Thompson #endif 639a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port); 64002ac6454SAndrew Thompson if (err) { 64102ac6454SAndrew Thompson goto done; 64202ac6454SAndrew Thompson } 64302ac6454SAndrew Thompson /* if the device disappeared, just give up */ 64402ac6454SAndrew Thompson if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { 64502ac6454SAndrew Thompson goto done; 64602ac6454SAndrew Thompson } 64702ac6454SAndrew Thompson /* check if reset is complete */ 64802ac6454SAndrew Thompson if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) { 64902ac6454SAndrew Thompson break; 65002ac6454SAndrew Thompson } 65102ac6454SAndrew Thompson /* check for timeout */ 65202ac6454SAndrew Thompson if (n > 1000) { 65302ac6454SAndrew Thompson n = 0; 65402ac6454SAndrew Thompson break; 65502ac6454SAndrew Thompson } 65602ac6454SAndrew Thompson } 65702ac6454SAndrew Thompson 65802ac6454SAndrew Thompson /* clear port reset first */ 659a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature( 66002ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET); 66102ac6454SAndrew Thompson if (err) { 66202ac6454SAndrew Thompson goto done; 66302ac6454SAndrew Thompson } 66402ac6454SAndrew Thompson /* check for timeout */ 66502ac6454SAndrew Thompson if (n == 0) { 66602ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 66702ac6454SAndrew Thompson goto done; 66802ac6454SAndrew Thompson } 66902ac6454SAndrew Thompson #if USB_DEBUG 67002ac6454SAndrew Thompson /* wait for the device to recover from reset */ 671a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); 67202ac6454SAndrew Thompson #else 67302ac6454SAndrew Thompson /* wait for the device to recover from reset */ 674a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); 67502ac6454SAndrew Thompson #endif 67602ac6454SAndrew Thompson 67702ac6454SAndrew Thompson done: 67802ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n", 679a593f6b8SAndrew Thompson port, usbd_errstr(err)); 68002ac6454SAndrew Thompson return (err); 68102ac6454SAndrew Thompson } 68202ac6454SAndrew Thompson 68302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 684a593f6b8SAndrew Thompson * usbd_req_get_desc 68502ac6454SAndrew Thompson * 68602ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 68702ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 68802ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 68902ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 69002ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 69102ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 69216589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 69316589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 69416589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 69516589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 69616589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 69716589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 69816589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 69916589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 70016589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 70116589beaSAndrew Thompson * descriptor. 70202ac6454SAndrew Thompson * 70302ac6454SAndrew Thompson * Returns: 70402ac6454SAndrew Thompson * 0: Success 70502ac6454SAndrew Thompson * Else: Failure 70602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 707e0a69b51SAndrew Thompson usb_error_t 708a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 70916589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 71002ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 71102ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 71202ac6454SAndrew Thompson uint8_t retries) 71302ac6454SAndrew Thompson { 714760bc48eSAndrew Thompson struct usb_device_request req; 71502ac6454SAndrew Thompson uint8_t *buf; 716e0a69b51SAndrew Thompson usb_error_t err; 71702ac6454SAndrew Thompson 71802ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 71902ac6454SAndrew Thompson id, type, index, max_len); 72002ac6454SAndrew Thompson 72102ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 72202ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 72302ac6454SAndrew Thompson USETW2(req.wValue, type, index); 72402ac6454SAndrew Thompson USETW(req.wIndex, id); 72502ac6454SAndrew Thompson 72602ac6454SAndrew Thompson while (1) { 72702ac6454SAndrew Thompson 72802ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 72902ac6454SAndrew Thompson err = USB_ERR_INVAL; 73002ac6454SAndrew Thompson goto done; 73102ac6454SAndrew Thompson } 73202ac6454SAndrew Thompson USETW(req.wLength, min_len); 73302ac6454SAndrew Thompson 734a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 73502ac6454SAndrew Thompson desc, 0, NULL, 1000); 73602ac6454SAndrew Thompson 73702ac6454SAndrew Thompson if (err) { 73802ac6454SAndrew Thompson if (!retries) { 73902ac6454SAndrew Thompson goto done; 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson retries--; 74202ac6454SAndrew Thompson 743a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson continue; 74602ac6454SAndrew Thompson } 74702ac6454SAndrew Thompson buf = desc; 74802ac6454SAndrew Thompson 74902ac6454SAndrew Thompson if (min_len == max_len) { 75002ac6454SAndrew Thompson 75116589beaSAndrew Thompson /* enforce correct length */ 75216589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 75302ac6454SAndrew Thompson buf[0] = min_len; 75416589beaSAndrew Thompson 75516589beaSAndrew Thompson /* enforce correct type */ 75602ac6454SAndrew Thompson buf[1] = type; 75702ac6454SAndrew Thompson 75802ac6454SAndrew Thompson goto done; 75902ac6454SAndrew Thompson } 76002ac6454SAndrew Thompson /* range check */ 76102ac6454SAndrew Thompson 76202ac6454SAndrew Thompson if (max_len > buf[0]) { 76302ac6454SAndrew Thompson max_len = buf[0]; 76402ac6454SAndrew Thompson } 76502ac6454SAndrew Thompson /* zero minimum data */ 76602ac6454SAndrew Thompson 76702ac6454SAndrew Thompson while (min_len > max_len) { 76802ac6454SAndrew Thompson min_len--; 76902ac6454SAndrew Thompson buf[min_len] = 0; 77002ac6454SAndrew Thompson } 77102ac6454SAndrew Thompson 77202ac6454SAndrew Thompson /* set new minimum length */ 77302ac6454SAndrew Thompson 77402ac6454SAndrew Thompson min_len = max_len; 77502ac6454SAndrew Thompson } 77602ac6454SAndrew Thompson done: 77716589beaSAndrew Thompson if (actlen != NULL) { 77816589beaSAndrew Thompson if (err) 77916589beaSAndrew Thompson *actlen = 0; 78016589beaSAndrew Thompson else 78116589beaSAndrew Thompson *actlen = min_len; 78216589beaSAndrew Thompson } 78302ac6454SAndrew Thompson return (err); 78402ac6454SAndrew Thompson } 78502ac6454SAndrew Thompson 78602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 787a593f6b8SAndrew Thompson * usbd_req_get_string_any 78802ac6454SAndrew Thompson * 78902ac6454SAndrew Thompson * This function will return the string given by "string_index" 79002ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 79102ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 79202ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 79302ac6454SAndrew Thompson * 79402ac6454SAndrew Thompson * Returns: 79502ac6454SAndrew Thompson * 0: Success 79602ac6454SAndrew Thompson * Else: Failure 79702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 798e0a69b51SAndrew Thompson usb_error_t 799a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 80002ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 80102ac6454SAndrew Thompson { 80202ac6454SAndrew Thompson char *s; 80302ac6454SAndrew Thompson uint8_t *temp; 80402ac6454SAndrew Thompson uint16_t i; 80502ac6454SAndrew Thompson uint16_t n; 80602ac6454SAndrew Thompson uint16_t c; 80702ac6454SAndrew Thompson uint8_t swap; 808e0a69b51SAndrew Thompson usb_error_t err; 80902ac6454SAndrew Thompson 81002ac6454SAndrew Thompson if (len == 0) { 81102ac6454SAndrew Thompson /* should not happen */ 81202ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 81302ac6454SAndrew Thompson } 81402ac6454SAndrew Thompson if (string_index == 0) { 81502ac6454SAndrew Thompson /* this is the language table */ 81602ac6454SAndrew Thompson buf[0] = 0; 81702ac6454SAndrew Thompson return (USB_ERR_INVAL); 81802ac6454SAndrew Thompson } 81902ac6454SAndrew Thompson if (udev->flags.no_strings) { 82002ac6454SAndrew Thompson buf[0] = 0; 82102ac6454SAndrew Thompson return (USB_ERR_STALLED); 82202ac6454SAndrew Thompson } 823a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 82402ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 82502ac6454SAndrew Thompson if (err) { 82602ac6454SAndrew Thompson buf[0] = 0; 82702ac6454SAndrew Thompson return (err); 82802ac6454SAndrew Thompson } 82902ac6454SAndrew Thompson temp = (uint8_t *)buf; 83002ac6454SAndrew Thompson 83102ac6454SAndrew Thompson if (temp[0] < 2) { 83202ac6454SAndrew Thompson /* string length is too short */ 83302ac6454SAndrew Thompson buf[0] = 0; 83402ac6454SAndrew Thompson return (USB_ERR_INVAL); 83502ac6454SAndrew Thompson } 83602ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 83702ac6454SAndrew Thompson len--; 83802ac6454SAndrew Thompson 83902ac6454SAndrew Thompson /* find maximum length */ 84002ac6454SAndrew Thompson s = buf; 84102ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 84202ac6454SAndrew Thompson if (n > len) { 84302ac6454SAndrew Thompson n = len; 84402ac6454SAndrew Thompson } 84502ac6454SAndrew Thompson /* skip descriptor header */ 84602ac6454SAndrew Thompson temp += 2; 84702ac6454SAndrew Thompson 84802ac6454SAndrew Thompson /* reset swap state */ 84902ac6454SAndrew Thompson swap = 3; 85002ac6454SAndrew Thompson 85102ac6454SAndrew Thompson /* convert and filter */ 85202ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 85302ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 85402ac6454SAndrew Thompson 85502ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 85602ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 85702ac6454SAndrew Thompson /* Little Endian, default */ 85802ac6454SAndrew Thompson *s = c; 85902ac6454SAndrew Thompson swap = 1; 86002ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 86102ac6454SAndrew Thompson /* Big Endian */ 86202ac6454SAndrew Thompson *s = c >> 8; 86302ac6454SAndrew Thompson swap = 2; 86402ac6454SAndrew Thompson } else { 86502ac6454SAndrew Thompson /* silently skip bad character */ 86602ac6454SAndrew Thompson continue; 86702ac6454SAndrew Thompson } 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson /* 87002ac6454SAndrew Thompson * Filter by default - we don't allow greater and less than 87102ac6454SAndrew Thompson * signs because they might confuse the dmesg printouts! 87202ac6454SAndrew Thompson */ 87302ac6454SAndrew Thompson if ((*s == '<') || (*s == '>') || (!isprint(*s))) { 87402ac6454SAndrew Thompson /* silently skip bad character */ 87502ac6454SAndrew Thompson continue; 87602ac6454SAndrew Thompson } 87702ac6454SAndrew Thompson s++; 87802ac6454SAndrew Thompson } 87902ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 88002ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 88102ac6454SAndrew Thompson } 88202ac6454SAndrew Thompson 88302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 884a593f6b8SAndrew Thompson * usbd_req_get_string_desc 88502ac6454SAndrew Thompson * 88602ac6454SAndrew Thompson * If you don't know the language ID, consider using 887a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 88802ac6454SAndrew Thompson * 88902ac6454SAndrew Thompson * Returns: 89002ac6454SAndrew Thompson * 0: Success 89102ac6454SAndrew Thompson * Else: Failure 89202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 893e0a69b51SAndrew Thompson usb_error_t 894a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 89502ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 89602ac6454SAndrew Thompson uint8_t string_index) 89702ac6454SAndrew Thompson { 898a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 89902ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 90002ac6454SAndrew Thompson } 90102ac6454SAndrew Thompson 90202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 903a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 9047efaaa9aSAndrew Thompson * 9057efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 9067efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 9077efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 9087efaaa9aSAndrew Thompson * 9097efaaa9aSAndrew Thompson * Returns: 9107efaaa9aSAndrew Thompson * 0: Success 9117efaaa9aSAndrew Thompson * Else: Failure 9127efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 913e0a69b51SAndrew Thompson usb_error_t 914a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 915760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 9167efaaa9aSAndrew Thompson { 917760bc48eSAndrew Thompson struct usb_device_request req; 918e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 919459d369eSAndrew Thompson const void *ptr; 920459d369eSAndrew Thompson uint16_t len; 921e0a69b51SAndrew Thompson usb_error_t err; 9227efaaa9aSAndrew Thompson 92363521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 9247efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 925459d369eSAndrew Thompson USETW(req.wValue, wValue); 9267efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 9277efaaa9aSAndrew Thompson USETW(req.wLength, 0); 9287efaaa9aSAndrew Thompson 929459d369eSAndrew Thompson ptr = NULL; 930459d369eSAndrew Thompson len = 0; 9317efaaa9aSAndrew Thompson 932a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 933459d369eSAndrew Thompson 934459d369eSAndrew Thompson if (hr_func == NULL) 935459d369eSAndrew Thompson err = USB_ERR_INVAL; 936459d369eSAndrew Thompson else { 937459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 938459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 939459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 940459d369eSAndrew Thompson } 941459d369eSAndrew Thompson 942459d369eSAndrew Thompson if (err) 943459d369eSAndrew Thompson ptr = NULL; 944459d369eSAndrew Thompson else if (ptr == NULL) 945459d369eSAndrew Thompson err = USB_ERR_INVAL; 946459d369eSAndrew Thompson 947760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 948459d369eSAndrew Thompson 949459d369eSAndrew Thompson return (err); 9507efaaa9aSAndrew Thompson } 9517efaaa9aSAndrew Thompson 9527efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 953a593f6b8SAndrew Thompson * usbd_req_get_config_desc 95402ac6454SAndrew Thompson * 95502ac6454SAndrew Thompson * Returns: 95602ac6454SAndrew Thompson * 0: Success 95702ac6454SAndrew Thompson * Else: Failure 95802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 959e0a69b51SAndrew Thompson usb_error_t 960a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 961760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 96202ac6454SAndrew Thompson { 963e0a69b51SAndrew Thompson usb_error_t err; 96402ac6454SAndrew Thompson 96502ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 96602ac6454SAndrew Thompson 967a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 96802ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 96902ac6454SAndrew Thompson if (err) { 97002ac6454SAndrew Thompson goto done; 97102ac6454SAndrew Thompson } 97202ac6454SAndrew Thompson /* Extra sanity checking */ 97302ac6454SAndrew Thompson if (UGETW(d->wTotalLength) < sizeof(*d)) { 97402ac6454SAndrew Thompson err = USB_ERR_INVAL; 97502ac6454SAndrew Thompson } 97602ac6454SAndrew Thompson done: 97702ac6454SAndrew Thompson return (err); 97802ac6454SAndrew Thompson } 97902ac6454SAndrew Thompson 98002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 981a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 98202ac6454SAndrew Thompson * 98302ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 98402ac6454SAndrew Thompson * ensures that "wTotalLength" is correct. 98502ac6454SAndrew Thompson * 98602ac6454SAndrew Thompson * Returns: 98702ac6454SAndrew Thompson * 0: Success 98802ac6454SAndrew Thompson * Else: Failure 98902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 990e0a69b51SAndrew Thompson usb_error_t 991a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, 992760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, struct malloc_type *mtype, 99302ac6454SAndrew Thompson uint8_t index) 99402ac6454SAndrew Thompson { 995760bc48eSAndrew Thompson struct usb_config_descriptor cd; 996760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 99702ac6454SAndrew Thompson uint16_t len; 998e0a69b51SAndrew Thompson usb_error_t err; 99902ac6454SAndrew Thompson 100002ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson *ppcd = NULL; 100302ac6454SAndrew Thompson 1004a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 100502ac6454SAndrew Thompson if (err) { 100602ac6454SAndrew Thompson return (err); 100702ac6454SAndrew Thompson } 100802ac6454SAndrew Thompson /* get full descriptor */ 100902ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 101002ac6454SAndrew Thompson if (len < sizeof(*cdesc)) { 101102ac6454SAndrew Thompson /* corrupt descriptor */ 101202ac6454SAndrew Thompson return (USB_ERR_INVAL); 101302ac6454SAndrew Thompson } 101402ac6454SAndrew Thompson cdesc = malloc(len, mtype, M_WAITOK); 101502ac6454SAndrew Thompson if (cdesc == NULL) { 101602ac6454SAndrew Thompson return (USB_ERR_NOMEM); 101702ac6454SAndrew Thompson } 1018a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 101902ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 102002ac6454SAndrew Thompson if (err) { 102102ac6454SAndrew Thompson free(cdesc, mtype); 102202ac6454SAndrew Thompson return (err); 102302ac6454SAndrew Thompson } 102402ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 102502ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 102602ac6454SAndrew Thompson 102702ac6454SAndrew Thompson *ppcd = cdesc; 102802ac6454SAndrew Thompson 102902ac6454SAndrew Thompson return (0); /* success */ 103002ac6454SAndrew Thompson } 103102ac6454SAndrew Thompson 103202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1033a593f6b8SAndrew Thompson * usbd_req_get_device_desc 103402ac6454SAndrew Thompson * 103502ac6454SAndrew Thompson * Returns: 103602ac6454SAndrew Thompson * 0: Success 103702ac6454SAndrew Thompson * Else: Failure 103802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1039e0a69b51SAndrew Thompson usb_error_t 1040a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1041760bc48eSAndrew Thompson struct usb_device_descriptor *d) 104202ac6454SAndrew Thompson { 104302ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1044a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 104502ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 104602ac6454SAndrew Thompson } 104702ac6454SAndrew Thompson 104802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1049a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 105002ac6454SAndrew Thompson * 105102ac6454SAndrew Thompson * Returns: 105202ac6454SAndrew Thompson * 0: Success 105302ac6454SAndrew Thompson * Else: Failure 105402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1055e0a69b51SAndrew Thompson usb_error_t 1056a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 105702ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 105802ac6454SAndrew Thompson { 1059a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1060760bc48eSAndrew Thompson struct usb_device_request req; 106102ac6454SAndrew Thompson 1062bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 106302ac6454SAndrew Thompson return (USB_ERR_INVAL); 1064bd73b187SAlfred Perlstein 106502ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 106602ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 106702ac6454SAndrew Thompson USETW(req.wValue, 0); 106802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 106902ac6454SAndrew Thompson req.wIndex[1] = 0; 107002ac6454SAndrew Thompson USETW(req.wLength, 1); 1071a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 107202ac6454SAndrew Thompson } 107302ac6454SAndrew Thompson 107402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1075a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 107602ac6454SAndrew Thompson * 107702ac6454SAndrew Thompson * Returns: 107802ac6454SAndrew Thompson * 0: Success 107902ac6454SAndrew Thompson * Else: Failure 108002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1081e0a69b51SAndrew Thompson usb_error_t 1082a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 108302ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 108402ac6454SAndrew Thompson { 1085a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1086760bc48eSAndrew Thompson struct usb_device_request req; 108702ac6454SAndrew Thompson 1088bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 108902ac6454SAndrew Thompson return (USB_ERR_INVAL); 1090bd73b187SAlfred Perlstein 109102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 109202ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 109302ac6454SAndrew Thompson req.wValue[0] = alt_no; 109402ac6454SAndrew Thompson req.wValue[1] = 0; 109502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 109602ac6454SAndrew Thompson req.wIndex[1] = 0; 109702ac6454SAndrew Thompson USETW(req.wLength, 0); 1098a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 109902ac6454SAndrew Thompson } 110002ac6454SAndrew Thompson 110102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1102a593f6b8SAndrew Thompson * usbd_req_get_device_status 110302ac6454SAndrew Thompson * 110402ac6454SAndrew Thompson * Returns: 110502ac6454SAndrew Thompson * 0: Success 110602ac6454SAndrew Thompson * Else: Failure 110702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1108e0a69b51SAndrew Thompson usb_error_t 1109a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1110760bc48eSAndrew Thompson struct usb_status *st) 111102ac6454SAndrew Thompson { 1112760bc48eSAndrew Thompson struct usb_device_request req; 111302ac6454SAndrew Thompson 111402ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 111502ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 111602ac6454SAndrew Thompson USETW(req.wValue, 0); 111702ac6454SAndrew Thompson USETW(req.wIndex, 0); 111802ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1119a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 112002ac6454SAndrew Thompson } 112102ac6454SAndrew Thompson 112202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1123a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 112402ac6454SAndrew Thompson * 112502ac6454SAndrew Thompson * Returns: 112602ac6454SAndrew Thompson * 0: Success 112702ac6454SAndrew Thompson * Else: Failure 112802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1129e0a69b51SAndrew Thompson usb_error_t 1130a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1131760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 113202ac6454SAndrew Thompson { 1133760bc48eSAndrew Thompson struct usb_device_request req; 113402ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 113502ac6454SAndrew Thompson 113602ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 113702ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 113802ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 113902ac6454SAndrew Thompson USETW(req.wIndex, 0); 114002ac6454SAndrew Thompson USETW(req.wLength, len); 1141a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 114202ac6454SAndrew Thompson } 114302ac6454SAndrew Thompson 114402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1145a593f6b8SAndrew Thompson * usbd_req_get_hub_status 114602ac6454SAndrew Thompson * 114702ac6454SAndrew Thompson * Returns: 114802ac6454SAndrew Thompson * 0: Success 114902ac6454SAndrew Thompson * Else: Failure 115002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1151e0a69b51SAndrew Thompson usb_error_t 1152a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1153760bc48eSAndrew Thompson struct usb_hub_status *st) 115402ac6454SAndrew Thompson { 1155760bc48eSAndrew Thompson struct usb_device_request req; 115602ac6454SAndrew Thompson 115702ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 115802ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 115902ac6454SAndrew Thompson USETW(req.wValue, 0); 116002ac6454SAndrew Thompson USETW(req.wIndex, 0); 1161760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1162a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 116302ac6454SAndrew Thompson } 116402ac6454SAndrew Thompson 116502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1166a593f6b8SAndrew Thompson * usbd_req_set_address 116702ac6454SAndrew Thompson * 116802ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 116902ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 117002ac6454SAndrew Thompson * 117102ac6454SAndrew Thompson * Returns: 117202ac6454SAndrew Thompson * 0: Success 117302ac6454SAndrew Thompson * Else: Failure 117402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1175e0a69b51SAndrew Thompson usb_error_t 1176a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 117702ac6454SAndrew Thompson { 1178760bc48eSAndrew Thompson struct usb_device_request req; 117902ac6454SAndrew Thompson 118002ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 118102ac6454SAndrew Thompson 118202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 118302ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 118402ac6454SAndrew Thompson USETW(req.wValue, addr); 118502ac6454SAndrew Thompson USETW(req.wIndex, 0); 118602ac6454SAndrew Thompson USETW(req.wLength, 0); 118702ac6454SAndrew Thompson 118802ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1189a593f6b8SAndrew Thompson return (usbd_do_request_flags(udev, mtx, &req, NULL, 119002ac6454SAndrew Thompson USB_DELAY_STATUS_STAGE, NULL, 1000)); 119102ac6454SAndrew Thompson } 119202ac6454SAndrew Thompson 119302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1194a593f6b8SAndrew Thompson * usbd_req_get_port_status 119502ac6454SAndrew Thompson * 119602ac6454SAndrew Thompson * Returns: 119702ac6454SAndrew Thompson * 0: Success 119802ac6454SAndrew Thompson * Else: Failure 119902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1200e0a69b51SAndrew Thompson usb_error_t 1201a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1202760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 120302ac6454SAndrew Thompson { 1204760bc48eSAndrew Thompson struct usb_device_request req; 120502ac6454SAndrew Thompson 120602ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 120702ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 120802ac6454SAndrew Thompson USETW(req.wValue, 0); 120902ac6454SAndrew Thompson req.wIndex[0] = port; 121002ac6454SAndrew Thompson req.wIndex[1] = 0; 121102ac6454SAndrew Thompson USETW(req.wLength, sizeof *ps); 1212a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, ps)); 121302ac6454SAndrew Thompson } 121402ac6454SAndrew Thompson 121502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1216a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 121702ac6454SAndrew Thompson * 121802ac6454SAndrew Thompson * Returns: 121902ac6454SAndrew Thompson * 0: Success 122002ac6454SAndrew Thompson * Else: Failure 122102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1222e0a69b51SAndrew Thompson usb_error_t 1223a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 122402ac6454SAndrew Thompson uint16_t sel) 122502ac6454SAndrew Thompson { 1226760bc48eSAndrew Thompson struct usb_device_request req; 122702ac6454SAndrew Thompson 122802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 122902ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 123002ac6454SAndrew Thompson USETW(req.wValue, sel); 123102ac6454SAndrew Thompson USETW(req.wIndex, 0); 123202ac6454SAndrew Thompson USETW(req.wLength, 0); 1233a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 123402ac6454SAndrew Thompson } 123502ac6454SAndrew Thompson 123602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1237a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 123802ac6454SAndrew Thompson * 123902ac6454SAndrew Thompson * Returns: 124002ac6454SAndrew Thompson * 0: Success 124102ac6454SAndrew Thompson * Else: Failure 124202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1243e0a69b51SAndrew Thompson usb_error_t 1244a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 124502ac6454SAndrew Thompson uint16_t sel) 124602ac6454SAndrew Thompson { 1247760bc48eSAndrew Thompson struct usb_device_request req; 124802ac6454SAndrew Thompson 124902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 125002ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 125102ac6454SAndrew Thompson USETW(req.wValue, sel); 125202ac6454SAndrew Thompson USETW(req.wIndex, 0); 125302ac6454SAndrew Thompson USETW(req.wLength, 0); 1254a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 125502ac6454SAndrew Thompson } 125602ac6454SAndrew Thompson 125702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1258a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 125902ac6454SAndrew Thompson * 126002ac6454SAndrew Thompson * Returns: 126102ac6454SAndrew Thompson * 0: Success 126202ac6454SAndrew Thompson * Else: Failure 126302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1264e0a69b51SAndrew Thompson usb_error_t 1265a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 126602ac6454SAndrew Thompson uint8_t port, uint16_t sel) 126702ac6454SAndrew Thompson { 1268760bc48eSAndrew Thompson struct usb_device_request req; 126902ac6454SAndrew Thompson 127002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 127102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 127202ac6454SAndrew Thompson USETW(req.wValue, sel); 127302ac6454SAndrew Thompson req.wIndex[0] = port; 127402ac6454SAndrew Thompson req.wIndex[1] = 0; 127502ac6454SAndrew Thompson USETW(req.wLength, 0); 1276a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 127702ac6454SAndrew Thompson } 127802ac6454SAndrew Thompson 127902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1280a593f6b8SAndrew Thompson * usbd_req_set_port_feature 128102ac6454SAndrew Thompson * 128202ac6454SAndrew Thompson * Returns: 128302ac6454SAndrew Thompson * 0: Success 128402ac6454SAndrew Thompson * Else: Failure 128502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1286e0a69b51SAndrew Thompson usb_error_t 1287a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, 128802ac6454SAndrew Thompson uint8_t port, uint16_t sel) 128902ac6454SAndrew Thompson { 1290760bc48eSAndrew Thompson struct usb_device_request req; 129102ac6454SAndrew Thompson 129202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 129302ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 129402ac6454SAndrew Thompson USETW(req.wValue, sel); 129502ac6454SAndrew Thompson req.wIndex[0] = port; 129602ac6454SAndrew Thompson req.wIndex[1] = 0; 129702ac6454SAndrew Thompson USETW(req.wLength, 0); 1298a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 129902ac6454SAndrew Thompson } 130002ac6454SAndrew Thompson 130102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1302a593f6b8SAndrew Thompson * usbd_req_set_protocol 130302ac6454SAndrew Thompson * 130402ac6454SAndrew Thompson * Returns: 130502ac6454SAndrew Thompson * 0: Success 130602ac6454SAndrew Thompson * Else: Failure 130702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1308e0a69b51SAndrew Thompson usb_error_t 1309a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 131002ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 131102ac6454SAndrew Thompson { 1312a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1313760bc48eSAndrew Thompson struct usb_device_request req; 131402ac6454SAndrew Thompson 131502ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 131602ac6454SAndrew Thompson return (USB_ERR_INVAL); 131702ac6454SAndrew Thompson } 131802ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 131902ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 132002ac6454SAndrew Thompson 132102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 132202ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 132302ac6454SAndrew Thompson USETW(req.wValue, report); 132402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 132502ac6454SAndrew Thompson req.wIndex[1] = 0; 132602ac6454SAndrew Thompson USETW(req.wLength, 0); 1327a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 132802ac6454SAndrew Thompson } 132902ac6454SAndrew Thompson 133002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1331a593f6b8SAndrew Thompson * usbd_req_set_report 133202ac6454SAndrew Thompson * 133302ac6454SAndrew Thompson * Returns: 133402ac6454SAndrew Thompson * 0: Success 133502ac6454SAndrew Thompson * Else: Failure 133602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1337e0a69b51SAndrew Thompson usb_error_t 1338a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 133902ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 134002ac6454SAndrew Thompson { 1341a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1342760bc48eSAndrew Thompson struct usb_device_request req; 134302ac6454SAndrew Thompson 134402ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 134502ac6454SAndrew Thompson return (USB_ERR_INVAL); 134602ac6454SAndrew Thompson } 134702ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 134802ac6454SAndrew Thompson 134902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 135002ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 135102ac6454SAndrew Thompson USETW2(req.wValue, type, id); 135202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 135302ac6454SAndrew Thompson req.wIndex[1] = 0; 135402ac6454SAndrew Thompson USETW(req.wLength, len); 1355a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 135602ac6454SAndrew Thompson } 135702ac6454SAndrew Thompson 135802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1359a593f6b8SAndrew Thompson * usbd_req_get_report 136002ac6454SAndrew Thompson * 136102ac6454SAndrew Thompson * Returns: 136202ac6454SAndrew Thompson * 0: Success 136302ac6454SAndrew Thompson * Else: Failure 136402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1365e0a69b51SAndrew Thompson usb_error_t 1366a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 136702ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 136802ac6454SAndrew Thompson { 1369a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1370760bc48eSAndrew Thompson struct usb_device_request req; 137102ac6454SAndrew Thompson 137202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { 137302ac6454SAndrew Thompson return (USB_ERR_INVAL); 137402ac6454SAndrew Thompson } 137502ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 137602ac6454SAndrew Thompson 137702ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 137802ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 137902ac6454SAndrew Thompson USETW2(req.wValue, type, id); 138002ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 138102ac6454SAndrew Thompson req.wIndex[1] = 0; 138202ac6454SAndrew Thompson USETW(req.wLength, len); 1383a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 138402ac6454SAndrew Thompson } 138502ac6454SAndrew Thompson 138602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1387a593f6b8SAndrew Thompson * usbd_req_set_idle 138802ac6454SAndrew Thompson * 138902ac6454SAndrew Thompson * Returns: 139002ac6454SAndrew Thompson * 0: Success 139102ac6454SAndrew Thompson * Else: Failure 139202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1393e0a69b51SAndrew Thompson usb_error_t 1394a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 139502ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 139602ac6454SAndrew Thompson { 1397a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1398760bc48eSAndrew Thompson struct usb_device_request req; 139902ac6454SAndrew Thompson 140002ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 140102ac6454SAndrew Thompson return (USB_ERR_INVAL); 140202ac6454SAndrew Thompson } 140302ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 140402ac6454SAndrew Thompson 140502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 140602ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 140702ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 140802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 140902ac6454SAndrew Thompson req.wIndex[1] = 0; 141002ac6454SAndrew Thompson USETW(req.wLength, 0); 1411a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 141202ac6454SAndrew Thompson } 141302ac6454SAndrew Thompson 141402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1415a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 141602ac6454SAndrew Thompson * 141702ac6454SAndrew Thompson * Returns: 141802ac6454SAndrew Thompson * 0: Success 141902ac6454SAndrew Thompson * Else: Failure 142002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1421e0a69b51SAndrew Thompson usb_error_t 1422a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 142302ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 142402ac6454SAndrew Thompson { 1425a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1426760bc48eSAndrew Thompson struct usb_device_request req; 142702ac6454SAndrew Thompson 142802ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 142902ac6454SAndrew Thompson return (USB_ERR_INVAL); 143002ac6454SAndrew Thompson } 143102ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 143202ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 143302ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 143402ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 143502ac6454SAndrew Thompson req.wIndex[1] = 0; 143602ac6454SAndrew Thompson USETW(req.wLength, size); 1437a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 143802ac6454SAndrew Thompson } 143902ac6454SAndrew Thompson 144002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1441a593f6b8SAndrew Thompson * usbd_req_set_config 144202ac6454SAndrew Thompson * 144302ac6454SAndrew Thompson * This function is used to select the current configuration number in 144402ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 144502ac6454SAndrew Thompson * configuration the function of the interfaces can change. 144602ac6454SAndrew Thompson * 144702ac6454SAndrew Thompson * Returns: 144802ac6454SAndrew Thompson * 0: Success 144902ac6454SAndrew Thompson * Else: Failure 145002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1451e0a69b51SAndrew Thompson usb_error_t 1452a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 145302ac6454SAndrew Thompson { 1454760bc48eSAndrew Thompson struct usb_device_request req; 145502ac6454SAndrew Thompson 145602ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 145702ac6454SAndrew Thompson 145802ac6454SAndrew Thompson /* do "set configuration" request */ 145902ac6454SAndrew Thompson 146002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 146102ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 146202ac6454SAndrew Thompson req.wValue[0] = conf; 146302ac6454SAndrew Thompson req.wValue[1] = 0; 146402ac6454SAndrew Thompson USETW(req.wIndex, 0); 146502ac6454SAndrew Thompson USETW(req.wLength, 0); 1466a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 146702ac6454SAndrew Thompson } 146802ac6454SAndrew Thompson 146902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1470a593f6b8SAndrew Thompson * usbd_req_get_config 147102ac6454SAndrew Thompson * 147202ac6454SAndrew Thompson * Returns: 147302ac6454SAndrew Thompson * 0: Success 147402ac6454SAndrew Thompson * Else: Failure 147502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1476e0a69b51SAndrew Thompson usb_error_t 1477a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 147802ac6454SAndrew Thompson { 1479760bc48eSAndrew Thompson struct usb_device_request req; 148002ac6454SAndrew Thompson 148102ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 148202ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 148302ac6454SAndrew Thompson USETW(req.wValue, 0); 148402ac6454SAndrew Thompson USETW(req.wIndex, 0); 148502ac6454SAndrew Thompson USETW(req.wLength, 1); 1486a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 148702ac6454SAndrew Thompson } 148802ac6454SAndrew Thompson 148902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1490a593f6b8SAndrew Thompson * usbd_req_re_enumerate 149102ac6454SAndrew Thompson * 149202ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 149302ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 149402ac6454SAndrew Thompson * new configuration. 149502ac6454SAndrew Thompson * 149602ac6454SAndrew Thompson * Returns: 149702ac6454SAndrew Thompson * 0: Success 149802ac6454SAndrew Thompson * Else: Failure 149902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1500e0a69b51SAndrew Thompson usb_error_t 1501a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 150202ac6454SAndrew Thompson { 1503760bc48eSAndrew Thompson struct usb_device *parent_hub; 1504e0a69b51SAndrew Thompson usb_error_t err; 150502ac6454SAndrew Thompson uint8_t old_addr; 150602ac6454SAndrew Thompson uint8_t do_retry = 1; 150702ac6454SAndrew Thompson 1508f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 150902ac6454SAndrew Thompson return (USB_ERR_INVAL); 151002ac6454SAndrew Thompson } 151102ac6454SAndrew Thompson old_addr = udev->address; 151202ac6454SAndrew Thompson parent_hub = udev->parent_hub; 151302ac6454SAndrew Thompson if (parent_hub == NULL) { 151402ac6454SAndrew Thompson return (USB_ERR_INVAL); 151502ac6454SAndrew Thompson } 151602ac6454SAndrew Thompson retry: 1517a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 151802ac6454SAndrew Thompson if (err) { 151903797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 1520a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 152102ac6454SAndrew Thompson goto done; 152202ac6454SAndrew Thompson } 152302ac6454SAndrew Thompson /* 152402ac6454SAndrew Thompson * After that the port has been reset our device should be at 152502ac6454SAndrew Thompson * address zero: 152602ac6454SAndrew Thompson */ 152702ac6454SAndrew Thompson udev->address = USB_START_ADDR; 152802ac6454SAndrew Thompson 152902ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 153002ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 153102ac6454SAndrew Thompson 153202ac6454SAndrew Thompson /* 153302ac6454SAndrew Thompson * Restore device address: 153402ac6454SAndrew Thompson */ 1535a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 153602ac6454SAndrew Thompson if (err) { 153702ac6454SAndrew Thompson /* XXX ignore any errors! */ 153803797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 1539a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 154002ac6454SAndrew Thompson } 154102ac6454SAndrew Thompson /* restore device address */ 154202ac6454SAndrew Thompson udev->address = old_addr; 154302ac6454SAndrew Thompson 154402ac6454SAndrew Thompson /* allow device time to set new address */ 1545a593f6b8SAndrew Thompson usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); 154602ac6454SAndrew Thompson 154702ac6454SAndrew Thompson /* get the device descriptor */ 1548a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 154902ac6454SAndrew Thompson USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 155002ac6454SAndrew Thompson if (err) { 155102ac6454SAndrew Thompson DPRINTFN(0, "getting device descriptor " 155203797f33SAndrew Thompson "at addr %d failed, %s!\n", udev->address, 1553a593f6b8SAndrew Thompson usbd_errstr(err)); 155402ac6454SAndrew Thompson goto done; 155502ac6454SAndrew Thompson } 155602ac6454SAndrew Thompson /* get the full device descriptor */ 1557a593f6b8SAndrew Thompson err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 155802ac6454SAndrew Thompson if (err) { 155902ac6454SAndrew Thompson DPRINTFN(0, "addr=%d, getting device " 156003797f33SAndrew Thompson "descriptor failed, %s!\n", old_addr, 1561a593f6b8SAndrew Thompson usbd_errstr(err)); 156202ac6454SAndrew Thompson goto done; 156302ac6454SAndrew Thompson } 156402ac6454SAndrew Thompson done: 156502ac6454SAndrew Thompson if (err && do_retry) { 156602ac6454SAndrew Thompson /* give the USB firmware some time to load */ 1567a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 156802ac6454SAndrew Thompson /* no more retries after this retry */ 156902ac6454SAndrew Thompson do_retry = 0; 157002ac6454SAndrew Thompson /* try again */ 157102ac6454SAndrew Thompson goto retry; 157202ac6454SAndrew Thompson } 157302ac6454SAndrew Thompson /* restore address */ 157402ac6454SAndrew Thompson udev->address = old_addr; 157502ac6454SAndrew Thompson return (err); 157602ac6454SAndrew Thompson } 157702ac6454SAndrew Thompson 157802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1579a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 158002ac6454SAndrew Thompson * 158102ac6454SAndrew Thompson * Returns: 158202ac6454SAndrew Thompson * 0: Success 158302ac6454SAndrew Thompson * Else: Failure 158402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1585e0a69b51SAndrew Thompson usb_error_t 1586a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 158702ac6454SAndrew Thompson uint16_t sel) 158802ac6454SAndrew Thompson { 1589760bc48eSAndrew Thompson struct usb_device_request req; 159002ac6454SAndrew Thompson 159102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 159202ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 159302ac6454SAndrew Thompson USETW(req.wValue, sel); 159402ac6454SAndrew Thompson USETW(req.wIndex, 0); 159502ac6454SAndrew Thompson USETW(req.wLength, 0); 1596a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 159702ac6454SAndrew Thompson } 159802ac6454SAndrew Thompson 159902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1600a593f6b8SAndrew Thompson * usbd_req_set_device_feature 160102ac6454SAndrew Thompson * 160202ac6454SAndrew Thompson * Returns: 160302ac6454SAndrew Thompson * 0: Success 160402ac6454SAndrew Thompson * Else: Failure 160502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1606e0a69b51SAndrew Thompson usb_error_t 1607a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 160802ac6454SAndrew Thompson uint16_t sel) 160902ac6454SAndrew Thompson { 1610760bc48eSAndrew Thompson struct usb_device_request req; 161102ac6454SAndrew Thompson 161202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 161302ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 161402ac6454SAndrew Thompson USETW(req.wValue, sel); 161502ac6454SAndrew Thompson USETW(req.wIndex, 0); 161602ac6454SAndrew Thompson USETW(req.wLength, 0); 1617a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 161802ac6454SAndrew Thompson } 1619