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 29d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #else 32ed6d949aSAndrew Thompson #include <sys/stdint.h> 33ed6d949aSAndrew Thompson #include <sys/stddef.h> 34ed6d949aSAndrew Thompson #include <sys/param.h> 35ed6d949aSAndrew Thompson #include <sys/queue.h> 36ed6d949aSAndrew Thompson #include <sys/types.h> 37ed6d949aSAndrew Thompson #include <sys/systm.h> 38ed6d949aSAndrew Thompson #include <sys/kernel.h> 39ed6d949aSAndrew Thompson #include <sys/bus.h> 40ed6d949aSAndrew Thompson #include <sys/module.h> 41ed6d949aSAndrew Thompson #include <sys/lock.h> 42ed6d949aSAndrew Thompson #include <sys/mutex.h> 43ed6d949aSAndrew Thompson #include <sys/condvar.h> 44ed6d949aSAndrew Thompson #include <sys/sysctl.h> 45ed6d949aSAndrew Thompson #include <sys/sx.h> 46ed6d949aSAndrew Thompson #include <sys/unistd.h> 47ed6d949aSAndrew Thompson #include <sys/callout.h> 48ed6d949aSAndrew Thompson #include <sys/malloc.h> 49ed6d949aSAndrew Thompson #include <sys/priv.h> 50ed6d949aSAndrew Thompson 5102ac6454SAndrew Thompson #include <dev/usb/usb.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 53ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5402ac6454SAndrew Thompson #include <dev/usb/usbhid.h> 5502ac6454SAndrew Thompson 56a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5702ac6454SAndrew Thompson 5802ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6702ac6454SAndrew Thompson 6802ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6902ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7002ac6454SAndrew Thompson #include <sys/ctype.h> 71d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 7202ac6454SAndrew Thompson 739465dbebSHans Petter Selasky static int usb_no_cs_fail; 749465dbebSHans Petter Selasky 75ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN, 769465dbebSHans Petter Selasky &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); 779465dbebSHans Petter Selasky 7823de050bSHans Petter Selasky static int usb_full_ddesc; 7923de050bSHans Petter Selasky 80ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN, 8123de050bSHans Petter Selasky &usb_full_ddesc, 0, "USB always read complete device descriptor, if set"); 8223de050bSHans Petter Selasky 83b850ecc1SAndrew Thompson #ifdef USB_DEBUG 84f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 85f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */ 86f6980be8SAndrew Thompson struct usb_ctrl_debug { 87f6980be8SAndrew Thompson int bus_index; /* target bus */ 88f6980be8SAndrew Thompson int dev_index; /* target address */ 89f6980be8SAndrew Thompson int ds_fail; /* fail data stage */ 902a4e4c67SHans Petter Selasky int ss_fail; /* fail status stage */ 91f6980be8SAndrew Thompson int ds_delay; /* data stage delay in ms */ 92f6980be8SAndrew Thompson int ss_delay; /* status stage delay in ms */ 93f6980be8SAndrew Thompson int bmRequestType_value; 94f6980be8SAndrew Thompson int bRequest_value; 95f6980be8SAndrew Thompson }; 96f6980be8SAndrew Thompson 97f6980be8SAndrew Thompson struct usb_ctrl_debug_bits { 98f6980be8SAndrew Thompson uint16_t ds_delay; 99f6980be8SAndrew Thompson uint16_t ss_delay; 100f6980be8SAndrew Thompson uint8_t ds_fail:1; 101f6980be8SAndrew Thompson uint8_t ss_fail:1; 102f6980be8SAndrew Thompson uint8_t enabled:1; 103f6980be8SAndrew Thompson }; 104f6980be8SAndrew Thompson 105f6980be8SAndrew Thompson /* The default is to disable fault injection. */ 106f6980be8SAndrew Thompson 107f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = { 108f6980be8SAndrew Thompson .bus_index = -1, 109f6980be8SAndrew Thompson .dev_index = -1, 110f6980be8SAndrew Thompson .bmRequestType_value = -1, 111f6980be8SAndrew Thompson .bRequest_value = -1, 112f6980be8SAndrew Thompson }; 113f6980be8SAndrew Thompson 114ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN, 115f6980be8SAndrew Thompson &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); 116ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN, 117f6980be8SAndrew Thompson &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); 118ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN, 119f6980be8SAndrew Thompson &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); 120ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN, 121f6980be8SAndrew Thompson &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); 122ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN, 123f6980be8SAndrew Thompson &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); 124ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN, 125f6980be8SAndrew Thompson &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); 126ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN, 127f6980be8SAndrew Thompson &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); 128ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN, 129f6980be8SAndrew Thompson &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); 130f6980be8SAndrew Thompson 131f6980be8SAndrew Thompson /*------------------------------------------------------------------------* 132f6980be8SAndrew Thompson * usbd_get_debug_bits 133f6980be8SAndrew Thompson * 134f6980be8SAndrew Thompson * This function is only useful in USB host mode. 135f6980be8SAndrew Thompson *------------------------------------------------------------------------*/ 136f6980be8SAndrew Thompson static void 137f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, 138f6980be8SAndrew Thompson struct usb_ctrl_debug_bits *dbg) 139f6980be8SAndrew Thompson { 140f6980be8SAndrew Thompson int temp; 141f6980be8SAndrew Thompson 142f6980be8SAndrew Thompson memset(dbg, 0, sizeof(*dbg)); 143f6980be8SAndrew Thompson 144f6980be8SAndrew Thompson /* Compute data stage delay */ 145f6980be8SAndrew Thompson 146f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_delay; 147f6980be8SAndrew Thompson if (temp < 0) 148f6980be8SAndrew Thompson temp = 0; 149f6980be8SAndrew Thompson else if (temp > (16*1024)) 150f6980be8SAndrew Thompson temp = (16*1024); 151f6980be8SAndrew Thompson 152f6980be8SAndrew Thompson dbg->ds_delay = temp; 153f6980be8SAndrew Thompson 154f6980be8SAndrew Thompson /* Compute status stage delay */ 155f6980be8SAndrew Thompson 156f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_delay; 157f6980be8SAndrew Thompson if (temp < 0) 158f6980be8SAndrew Thompson temp = 0; 159f6980be8SAndrew Thompson else if (temp > (16*1024)) 160f6980be8SAndrew Thompson temp = (16*1024); 161f6980be8SAndrew Thompson 162f6980be8SAndrew Thompson dbg->ss_delay = temp; 163f6980be8SAndrew Thompson 164f6980be8SAndrew Thompson /* Check if this control request should be failed */ 165f6980be8SAndrew Thompson 166f6980be8SAndrew Thompson if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) 167f6980be8SAndrew Thompson return; 168f6980be8SAndrew Thompson 169f6980be8SAndrew Thompson if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) 170f6980be8SAndrew Thompson return; 171f6980be8SAndrew Thompson 172f6980be8SAndrew Thompson temp = usb_ctrl_debug.bmRequestType_value; 173f6980be8SAndrew Thompson 174f6980be8SAndrew Thompson if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) 175f6980be8SAndrew Thompson return; 176f6980be8SAndrew Thompson 177f6980be8SAndrew Thompson temp = usb_ctrl_debug.bRequest_value; 178f6980be8SAndrew Thompson 179f6980be8SAndrew Thompson if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) 180f6980be8SAndrew Thompson return; 181f6980be8SAndrew Thompson 182f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_fail; 183f6980be8SAndrew Thompson if (temp) 184f6980be8SAndrew Thompson dbg->ds_fail = 1; 185f6980be8SAndrew Thompson 186f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_fail; 187f6980be8SAndrew Thompson if (temp) 188f6980be8SAndrew Thompson dbg->ss_fail = 1; 189f6980be8SAndrew Thompson 190f6980be8SAndrew Thompson dbg->enabled = 1; 191f6980be8SAndrew Thompson } 192f6980be8SAndrew Thompson #endif /* USB_REQ_DEBUG */ 193f6980be8SAndrew Thompson #endif /* USB_DEBUG */ 19402ac6454SAndrew Thompson 19502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 196a593f6b8SAndrew Thompson * usbd_do_request_callback 19702ac6454SAndrew Thompson * 19802ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control 19902ac6454SAndrew Thompson * transfers. 20002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 20102ac6454SAndrew Thompson void 202ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) 20302ac6454SAndrew Thompson { 20402ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 20502ac6454SAndrew Thompson 20602ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer)); 20702ac6454SAndrew Thompson 20802ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 20902ac6454SAndrew Thompson case USB_ST_SETUP: 210a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 21102ac6454SAndrew Thompson break; 21202ac6454SAndrew Thompson default: 21391cd9240SAndrew Thompson cv_signal(&xfer->xroot->udev->ctrlreq_cv); 21402ac6454SAndrew Thompson break; 21502ac6454SAndrew Thompson } 21602ac6454SAndrew Thompson } 21702ac6454SAndrew Thompson 21802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 219a593f6b8SAndrew Thompson * usb_do_clear_stall_callback 22002ac6454SAndrew Thompson * 22102ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests. 22202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 22302ac6454SAndrew Thompson void 224ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 22502ac6454SAndrew Thompson { 226760bc48eSAndrew Thompson struct usb_device_request req; 227760bc48eSAndrew Thompson struct usb_device *udev; 228ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 229ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end; 230ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first; 231a5cf1aaaSHans Petter Selasky usb_stream_t x; 23263521bbcSAndrew Thompson uint8_t to; 23302ac6454SAndrew Thompson 23402ac6454SAndrew Thompson udev = xfer->xroot->udev; 23502ac6454SAndrew Thompson 23602ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 23702ac6454SAndrew Thompson 238ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */ 23902ac6454SAndrew Thompson 240ae60fdfbSAndrew Thompson ep = udev->ep_curr; 241ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max; 242ae60fdfbSAndrew Thompson ep_first = udev->endpoints; 243ae60fdfbSAndrew Thompson to = udev->endpoints_max; 244115df0b6SAndrew Thompson 24502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 24602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 2479465dbebSHans Petter Selasky tr_transferred: 2489eb0d702SHans Petter Selasky /* reset error counter */ 2499eb0d702SHans Petter Selasky udev->clear_stall_errors = 0; 2509eb0d702SHans Petter Selasky 251ae60fdfbSAndrew Thompson if (ep == NULL) 252115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */ 253ae60fdfbSAndrew Thompson if (ep->edesc && 254ae60fdfbSAndrew Thompson ep->is_stalled) { 255ae60fdfbSAndrew Thompson ep->toggle_next = 0; 256ae60fdfbSAndrew Thompson ep->is_stalled = 0; 257963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */ 258963169b4SHans Petter Selasky usbd_clear_stall_locked(udev, ep); 259a5cf1aaaSHans Petter Selasky for (x = 0; x != USB_MAX_EP_STREAMS; x++) { 260a5cf1aaaSHans Petter Selasky /* start the current or next transfer, if any */ 261a5cf1aaaSHans Petter Selasky usb_command_wrapper(&ep->endpoint_q[x], 262a5cf1aaaSHans Petter Selasky ep->endpoint_q[x].curr); 263a5cf1aaaSHans Petter Selasky } 26402ac6454SAndrew Thompson } 265ae60fdfbSAndrew Thompson ep++; 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson case USB_ST_SETUP: 26802ac6454SAndrew Thompson tr_setup: 269115df0b6SAndrew Thompson if (to == 0) 270ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */ 271ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end)) 272ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */ 273ae60fdfbSAndrew Thompson if (ep->edesc && 274ae60fdfbSAndrew Thompson ep->is_stalled) { 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson /* setup a clear-stall packet */ 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 27902ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 28002ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 281ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 28202ac6454SAndrew Thompson req.wIndex[1] = 0; 28302ac6454SAndrew Thompson USETW(req.wLength, 0); 28402ac6454SAndrew Thompson 28502ac6454SAndrew Thompson /* copy in the transfer */ 28602ac6454SAndrew Thompson 287a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 28802ac6454SAndrew Thompson 28902ac6454SAndrew Thompson /* set length */ 290ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 29102ac6454SAndrew Thompson xfer->nframes = 1; 29202ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 29302ac6454SAndrew Thompson 294a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 29502ac6454SAndrew Thompson 29602ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 29702ac6454SAndrew Thompson break; 29802ac6454SAndrew Thompson } 299ae60fdfbSAndrew Thompson ep++; 300115df0b6SAndrew Thompson to--; 30102ac6454SAndrew Thompson goto tr_setup; 30202ac6454SAndrew Thompson 30302ac6454SAndrew Thompson default: 3049eb0d702SHans Petter Selasky if (error == USB_ERR_CANCELLED) 30502ac6454SAndrew Thompson break; 3069eb0d702SHans Petter Selasky 3079eb0d702SHans Petter Selasky DPRINTF("Clear stall failed.\n"); 3089465dbebSHans Petter Selasky 3099465dbebSHans Petter Selasky /* 3109465dbebSHans Petter Selasky * Some VMs like VirtualBox always return failure on 3119465dbebSHans Petter Selasky * clear-stall which we sometimes should just ignore. 3129465dbebSHans Petter Selasky */ 3139465dbebSHans Petter Selasky if (usb_no_cs_fail) 3149465dbebSHans Petter Selasky goto tr_transferred; 3159eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) 3169eb0d702SHans Petter Selasky goto tr_setup; 3179eb0d702SHans Petter Selasky 3189eb0d702SHans Petter Selasky if (error == USB_ERR_TIMEOUT) { 3199eb0d702SHans Petter Selasky udev->clear_stall_errors = USB_CS_RESET_LIMIT; 3209eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3219eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3229eb0d702SHans Petter Selasky } else { 3239eb0d702SHans Petter Selasky udev->clear_stall_errors++; 3249eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { 3259eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3269eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3279eb0d702SHans Petter Selasky } 32802ac6454SAndrew Thompson } 32902ac6454SAndrew Thompson goto tr_setup; 33002ac6454SAndrew Thompson } 33102ac6454SAndrew Thompson 332ae60fdfbSAndrew Thompson /* store current endpoint */ 333ae60fdfbSAndrew Thompson udev->ep_curr = ep; 33402ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 33502ac6454SAndrew Thompson } 33602ac6454SAndrew Thompson 337e0a69b51SAndrew Thompson static usb_handle_req_t * 338a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev) 339459d369eSAndrew Thompson { 340459d369eSAndrew Thompson /* figure out if there is a Handle Request function */ 341f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE) 342a593f6b8SAndrew Thompson return (usb_temp_get_desc_p); 343459d369eSAndrew Thompson else if (udev->parent_hub == NULL) 344459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec); 345459d369eSAndrew Thompson else 346459d369eSAndrew Thompson return (NULL); 347459d369eSAndrew Thompson } 348459d369eSAndrew Thompson 34902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 350a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request 35102ac6454SAndrew Thompson * 35202ac6454SAndrew Thompson * Description of arguments passed to these functions: 35302ac6454SAndrew Thompson * 354760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the 35502ac6454SAndrew Thompson * request should be performed. It is possible to call this function 35602ac6454SAndrew Thompson * in both Host Side mode and Device Side mode. 35702ac6454SAndrew Thompson * 35802ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it 35902ac6454SAndrew Thompson * will get dropped and picked up during the execution of this 36002ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this 36102ac6454SAndrew Thompson * argument is NULL it has no effect. 36202ac6454SAndrew Thompson * 36302ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an 36402ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB 36502ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB 36602ac6454SAndrew Thompson * request, if it is a read or a write. 36702ac6454SAndrew Thompson * 36802ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req" 36902ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which 37002ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can 37102ac6454SAndrew Thompson * be NULL. 37202ac6454SAndrew Thompson * 37302ac6454SAndrew Thompson * "flags" - here is a list of valid flags: 37402ac6454SAndrew Thompson * 37502ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than 37602ac6454SAndrew Thompson * specified 37702ac6454SAndrew Thompson * 37802ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed 37902ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay" 38002ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging. 38102ac6454SAndrew Thompson * 38202ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland 38302ac6454SAndrew Thompson * pointer. 38402ac6454SAndrew Thompson * 38502ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in 38602ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This 38702ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is 38802ac6454SAndrew Thompson * used. 38902ac6454SAndrew Thompson * 39002ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in 39102ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is 39202ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater 39302ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack 39402ac6454SAndrew Thompson * does not allow control requests without a timeout. 39502ac6454SAndrew Thompson * 396a18a7a41SHans Petter Selasky * NOTE: This function is thread safe. All calls to "usbd_do_request_flags" 397a18a7a41SHans Petter Selasky * will be serialized by the use of the USB device enumeration lock. 39802ac6454SAndrew Thompson * 39902ac6454SAndrew Thompson * Returns: 40002ac6454SAndrew Thompson * 0: Success 40102ac6454SAndrew Thompson * Else: Failure 40202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 403e0a69b51SAndrew Thompson usb_error_t 404a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, 405760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 406e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 40702ac6454SAndrew Thompson { 408f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 409f6980be8SAndrew Thompson struct usb_ctrl_debug_bits dbg; 410f6980be8SAndrew Thompson #endif 411e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 412760bc48eSAndrew Thompson struct usb_xfer *xfer; 41302ac6454SAndrew Thompson const void *desc; 41402ac6454SAndrew Thompson int err = 0; 415e0a69b51SAndrew Thompson usb_ticks_t start_ticks; 416e0a69b51SAndrew Thompson usb_ticks_t delta_ticks; 417e0a69b51SAndrew Thompson usb_ticks_t max_ticks; 41802ac6454SAndrew Thompson uint16_t length; 41902ac6454SAndrew Thompson uint16_t temp; 420f6980be8SAndrew Thompson uint16_t acttemp; 421a18a7a41SHans Petter Selasky uint8_t do_unlock; 42202ac6454SAndrew Thompson 42302ac6454SAndrew Thompson if (timeout < 50) { 42402ac6454SAndrew Thompson /* timeout is too small */ 42502ac6454SAndrew Thompson timeout = 50; 42602ac6454SAndrew Thompson } 42702ac6454SAndrew Thompson if (timeout > 30000) { 42802ac6454SAndrew Thompson /* timeout is too big */ 42902ac6454SAndrew Thompson timeout = 30000; 43002ac6454SAndrew Thompson } 43102ac6454SAndrew Thompson length = UGETW(req->wLength); 43202ac6454SAndrew Thompson 43302ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " 43402ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", 43502ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest, 43602ac6454SAndrew Thompson req->wValue[1], req->wValue[0], 43702ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0], 43802ac6454SAndrew Thompson req->wLength[1], req->wLength[0]); 43902ac6454SAndrew Thompson 440bd216778SAndrew Thompson /* Check if the device is still alive */ 441bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) { 442bd216778SAndrew Thompson DPRINTF("usb device has gone\n"); 443bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED); 444bd216778SAndrew Thompson } 445bd216778SAndrew Thompson 44602ac6454SAndrew Thompson /* 44702ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not 44802ac6454SAndrew Thompson * check the return value: 44902ac6454SAndrew Thompson */ 45039307315SAndrew Thompson if (actlen) 45102ac6454SAndrew Thompson *actlen = 0; 45239307315SAndrew Thompson 453bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0) 454bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR) 455bdc081c6SAndrew Thompson return (USB_ERR_INVAL); 456bdc081c6SAndrew Thompson #endif 4572df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) { 45802ac6454SAndrew Thompson mtx_unlock(mtx); 45902ac6454SAndrew Thompson mtx_assert(mtx, MA_NOTOWNED); 46002ac6454SAndrew Thompson } 4612df1e9a6SAndrew Thompson 4622df1e9a6SAndrew Thompson /* 463*64cb5e2aSHans Petter Selasky * Serialize access to this function: 464a18a7a41SHans Petter Selasky */ 465*64cb5e2aSHans Petter Selasky do_unlock = usbd_ctrl_lock(udev); 4662df1e9a6SAndrew Thompson 467a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 46839307315SAndrew Thompson 469459d369eSAndrew Thompson if (hr_func != NULL) { 470459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n"); 47139307315SAndrew Thompson 472459d369eSAndrew Thompson desc = NULL; 473459d369eSAndrew Thompson temp = 0; 474459d369eSAndrew Thompson 475459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 47639307315SAndrew Thompson if (length != 0) { 477459d369eSAndrew Thompson DPRINTFN(1, "The handle request function " 478459d369eSAndrew Thompson "does not support writing data!\n"); 47939307315SAndrew Thompson err = USB_ERR_INVAL; 48039307315SAndrew Thompson goto done; 48139307315SAndrew Thompson } 48239307315SAndrew Thompson } 483459d369eSAndrew Thompson 484459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */ 48539307315SAndrew Thompson 48639307315SAndrew Thompson USB_BUS_LOCK(udev->bus); 487459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp); 48839307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 48939307315SAndrew Thompson 49039307315SAndrew Thompson if (err) 49139307315SAndrew Thompson goto done; 49239307315SAndrew Thompson 493459d369eSAndrew Thompson if (length > temp) { 49439307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) { 49539307315SAndrew Thompson err = USB_ERR_SHORT_XFER; 49639307315SAndrew Thompson goto done; 49739307315SAndrew Thompson } 498459d369eSAndrew Thompson length = temp; 49939307315SAndrew Thompson } 50039307315SAndrew Thompson if (actlen) 50139307315SAndrew Thompson *actlen = length; 50239307315SAndrew Thompson 50339307315SAndrew Thompson if (length > 0) { 50439307315SAndrew Thompson #if USB_HAVE_USER_IO 50539307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 506459d369eSAndrew Thompson if (copyout(desc, data, length)) { 50739307315SAndrew Thompson err = USB_ERR_INVAL; 50839307315SAndrew Thompson goto done; 50939307315SAndrew Thompson } 51039307315SAndrew Thompson } else 51139307315SAndrew Thompson #endif 512271ae033SHans Petter Selasky memcpy(data, desc, length); 51339307315SAndrew Thompson } 514459d369eSAndrew Thompson goto done; /* success */ 51539307315SAndrew Thompson } 51639307315SAndrew Thompson 51702ac6454SAndrew Thompson /* 51802ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any: 51902ac6454SAndrew Thompson */ 5205b3bb704SAndrew Thompson usbd_ctrl_transfer_setup(udev); 52102ac6454SAndrew Thompson 5225b3bb704SAndrew Thompson xfer = udev->ctrl_xfer[0]; 52302ac6454SAndrew Thompson if (xfer == NULL) { 52402ac6454SAndrew Thompson /* most likely out of memory */ 52502ac6454SAndrew Thompson err = USB_ERR_NOMEM; 52602ac6454SAndrew Thompson goto done; 52702ac6454SAndrew Thompson } 528f6980be8SAndrew Thompson 529f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 530f6980be8SAndrew Thompson /* Get debug bits */ 531f6980be8SAndrew Thompson usbd_get_debug_bits(udev, req, &dbg); 532f6980be8SAndrew Thompson 533f6980be8SAndrew Thompson /* Check for fault injection */ 534f6980be8SAndrew Thompson if (dbg.enabled) 535f6980be8SAndrew Thompson flags |= USB_DELAY_STATUS_STAGE; 536f6980be8SAndrew Thompson #endif 53702ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 53802ac6454SAndrew Thompson 5394eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE) 54002ac6454SAndrew Thompson xfer->flags.manual_status = 1; 5414eae601eSAndrew Thompson else 54202ac6454SAndrew Thompson xfer->flags.manual_status = 0; 5434eae601eSAndrew Thompson 5444eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK) 5454eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1; 5464eae601eSAndrew Thompson else 5474eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0; 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson xfer->timeout = timeout; 55002ac6454SAndrew Thompson 55102ac6454SAndrew Thompson start_ticks = ticks; 55202ac6454SAndrew Thompson 55302ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout); 55402ac6454SAndrew Thompson 555a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); 55602ac6454SAndrew Thompson 557ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); 55802ac6454SAndrew Thompson 55902ac6454SAndrew Thompson while (1) { 56002ac6454SAndrew Thompson temp = length; 561f6980be8SAndrew Thompson if (temp > usbd_xfer_max_len(xfer)) { 562ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer); 56302ac6454SAndrew Thompson } 564f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 565f6980be8SAndrew Thompson if (xfer->flags.manual_status) { 566f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) != 0) { 567f6980be8SAndrew Thompson /* Execute data stage separately */ 568f6980be8SAndrew Thompson temp = 0; 569f6980be8SAndrew Thompson } else if (temp > 0) { 570f6980be8SAndrew Thompson if (dbg.ds_fail) { 571f6980be8SAndrew Thompson err = USB_ERR_INVAL; 572f6980be8SAndrew Thompson break; 573f6980be8SAndrew Thompson } 574f6980be8SAndrew Thompson if (dbg.ds_delay > 0) { 575f6980be8SAndrew Thompson usb_pause_mtx( 576f6980be8SAndrew Thompson xfer->xroot->xfer_mtx, 577f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ds_delay)); 578f6980be8SAndrew Thompson /* make sure we don't time out */ 579f6980be8SAndrew Thompson start_ticks = ticks; 580f6980be8SAndrew Thompson } 581f6980be8SAndrew Thompson } 582f6980be8SAndrew Thompson } 583f6980be8SAndrew Thompson #endif 584ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp); 58502ac6454SAndrew Thompson 58602ac6454SAndrew Thompson if (temp > 0) { 58702ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 588bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 58902ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 59002ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 591a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1, 59202ac6454SAndrew Thompson 0, data, temp); 59302ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 59402ac6454SAndrew Thompson if (err) { 59502ac6454SAndrew Thompson err = USB_ERR_INVAL; 59602ac6454SAndrew Thompson break; 59702ac6454SAndrew Thompson } 598bdc081c6SAndrew Thompson } else 599bdc081c6SAndrew Thompson #endif 600a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 601bdc081c6SAndrew Thompson 0, data, temp); 60202ac6454SAndrew Thompson } 603f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 2); 60402ac6454SAndrew Thompson } else { 605f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) == 0) { 60602ac6454SAndrew Thompson if (xfer->flags.manual_status) { 607f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 608f6980be8SAndrew Thompson if (dbg.ss_fail) { 609f6980be8SAndrew Thompson err = USB_ERR_INVAL; 610f6980be8SAndrew Thompson break; 61102ac6454SAndrew Thompson } 612f6980be8SAndrew Thompson if (dbg.ss_delay > 0) { 613a593f6b8SAndrew Thompson usb_pause_mtx( 61402ac6454SAndrew Thompson xfer->xroot->xfer_mtx, 615f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ss_delay)); 616f6980be8SAndrew Thompson /* make sure we don't time out */ 617f6980be8SAndrew Thompson start_ticks = ticks; 61802ac6454SAndrew Thompson } 61902ac6454SAndrew Thompson #endif 62002ac6454SAndrew Thompson xfer->flags.manual_status = 0; 62102ac6454SAndrew Thompson } else { 62202ac6454SAndrew Thompson break; 62302ac6454SAndrew Thompson } 62402ac6454SAndrew Thompson } 625f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 1); 62602ac6454SAndrew Thompson } 62702ac6454SAndrew Thompson 628a593f6b8SAndrew Thompson usbd_transfer_start(xfer); 62902ac6454SAndrew Thompson 630a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) { 63191cd9240SAndrew Thompson cv_wait(&udev->ctrlreq_cv, 63202ac6454SAndrew Thompson xfer->xroot->xfer_mtx); 63302ac6454SAndrew Thompson } 63402ac6454SAndrew Thompson 63502ac6454SAndrew Thompson err = xfer->error; 63602ac6454SAndrew Thompson 63702ac6454SAndrew Thompson if (err) { 63802ac6454SAndrew Thompson break; 63902ac6454SAndrew Thompson } 64002ac6454SAndrew Thompson 641f6980be8SAndrew Thompson /* get actual length of DATA stage */ 642f6980be8SAndrew Thompson 643f6980be8SAndrew Thompson if (xfer->aframes < 2) { 644f6980be8SAndrew Thompson acttemp = 0; 64502ac6454SAndrew Thompson } else { 646f6980be8SAndrew Thompson acttemp = usbd_xfer_frame_len(xfer, 1); 64702ac6454SAndrew Thompson } 64802ac6454SAndrew Thompson 64902ac6454SAndrew Thompson /* check for short packet */ 65002ac6454SAndrew Thompson 651f6980be8SAndrew Thompson if (temp > acttemp) { 652f6980be8SAndrew Thompson temp = acttemp; 65302ac6454SAndrew Thompson length = temp; 65402ac6454SAndrew Thompson } 65502ac6454SAndrew Thompson if (temp > 0) { 65602ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) { 657bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 65802ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 65902ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 660a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1, 66102ac6454SAndrew Thompson 0, data, temp); 66202ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 66302ac6454SAndrew Thompson if (err) { 66402ac6454SAndrew Thompson err = USB_ERR_INVAL; 66502ac6454SAndrew Thompson break; 66602ac6454SAndrew Thompson } 667bdc081c6SAndrew Thompson } else 668bdc081c6SAndrew Thompson #endif 669a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1, 67002ac6454SAndrew Thompson 0, data, temp); 67102ac6454SAndrew Thompson } 67202ac6454SAndrew Thompson } 67302ac6454SAndrew Thompson /* 67402ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup 67502ac6454SAndrew Thompson * packet again: 67602ac6454SAndrew Thompson */ 677ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 67802ac6454SAndrew Thompson 67902ac6454SAndrew Thompson /* update length and data pointer */ 68002ac6454SAndrew Thompson length -= temp; 68102ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp); 68202ac6454SAndrew Thompson 68302ac6454SAndrew Thompson if (actlen) { 68402ac6454SAndrew Thompson (*actlen) += temp; 68502ac6454SAndrew Thompson } 68602ac6454SAndrew Thompson /* check for timeout */ 68702ac6454SAndrew Thompson 68802ac6454SAndrew Thompson delta_ticks = ticks - start_ticks; 68902ac6454SAndrew Thompson if (delta_ticks > max_ticks) { 69002ac6454SAndrew Thompson if (!err) { 69102ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 69202ac6454SAndrew Thompson } 69302ac6454SAndrew Thompson } 69402ac6454SAndrew Thompson if (err) { 69502ac6454SAndrew Thompson break; 69602ac6454SAndrew Thompson } 69702ac6454SAndrew Thompson } 69802ac6454SAndrew Thompson 69902ac6454SAndrew Thompson if (err) { 70002ac6454SAndrew Thompson /* 70102ac6454SAndrew Thompson * Make sure that the control endpoint is no longer 70202ac6454SAndrew Thompson * blocked in case of a non-transfer related error: 70302ac6454SAndrew Thompson */ 704a593f6b8SAndrew Thompson usbd_transfer_stop(xfer); 70502ac6454SAndrew Thompson } 70602ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 70702ac6454SAndrew Thompson 70802ac6454SAndrew Thompson done: 709a18a7a41SHans Petter Selasky if (do_unlock) 710*64cb5e2aSHans Petter Selasky usbd_ctrl_unlock(udev); 711a18a7a41SHans Petter Selasky 7122df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) 71302ac6454SAndrew Thompson mtx_lock(mtx); 7142df1e9a6SAndrew Thompson 715a0d53e0bSHans Petter Selasky switch (err) { 716a0d53e0bSHans Petter Selasky case USB_ERR_NORMAL_COMPLETION: 717a0d53e0bSHans Petter Selasky case USB_ERR_SHORT_XFER: 718a0d53e0bSHans Petter Selasky case USB_ERR_STALLED: 719a0d53e0bSHans Petter Selasky case USB_ERR_CANCELLED: 720a0d53e0bSHans Petter Selasky break; 721a0d53e0bSHans Petter Selasky default: 722a0d53e0bSHans Petter Selasky DPRINTF("I/O error - waiting a bit for TT cleanup\n"); 723a0d53e0bSHans Petter Selasky usb_pause_mtx(mtx, hz / 16); 724a0d53e0bSHans Petter Selasky break; 725a0d53e0bSHans Petter Selasky } 726e0a69b51SAndrew Thompson return ((usb_error_t)err); 72702ac6454SAndrew Thompson } 72802ac6454SAndrew Thompson 72902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 730a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code 73102ac6454SAndrew Thompson * 73202ac6454SAndrew Thompson * This function is factored out code. It does basically the same like 733a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the 73402ac6454SAndrew Thompson * passed process argument before doing the USB request. If the 73502ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It 73602ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked 73702ac6454SAndrew Thompson * when calling this function. 73802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 739e0a69b51SAndrew Thompson usb_error_t 740a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, 741760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 742e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 74302ac6454SAndrew Thompson { 744e0a69b51SAndrew Thompson usb_error_t err; 74502ac6454SAndrew Thompson uint16_t len; 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson /* get request data length */ 74802ac6454SAndrew Thompson len = UGETW(req->wLength); 74902ac6454SAndrew Thompson 75002ac6454SAndrew Thompson /* check if the device is being detached */ 751a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) { 75202ac6454SAndrew Thompson err = USB_ERR_IOERROR; 75302ac6454SAndrew Thompson goto done; 75402ac6454SAndrew Thompson } 75502ac6454SAndrew Thompson 75602ac6454SAndrew Thompson /* forward the USB request */ 757a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx, 75802ac6454SAndrew Thompson req, data, flags, actlen, timeout); 75902ac6454SAndrew Thompson 76002ac6454SAndrew Thompson done: 76102ac6454SAndrew Thompson /* on failure we zero the data */ 76202ac6454SAndrew Thompson /* on short packet we zero the unused data */ 76302ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { 76402ac6454SAndrew Thompson if (err) 76502ac6454SAndrew Thompson memset(data, 0, len); 76602ac6454SAndrew Thompson else if (actlen && *actlen != len) 76702ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen); 76802ac6454SAndrew Thompson } 76902ac6454SAndrew Thompson return (err); 77002ac6454SAndrew Thompson } 77102ac6454SAndrew Thompson 77202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 773a593f6b8SAndrew Thompson * usbd_req_reset_port 77402ac6454SAndrew Thompson * 775cbb75751SHans Petter Selasky * This function will instruct a USB HUB to perform a reset sequence 77602ac6454SAndrew Thompson * on the specified port number. 77702ac6454SAndrew Thompson * 77802ac6454SAndrew Thompson * Returns: 77902ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero. 78002ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be 78102ac6454SAndrew Thompson * disabled. 78202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 783e0a69b51SAndrew Thompson usb_error_t 784a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 78502ac6454SAndrew Thompson { 786760bc48eSAndrew Thompson struct usb_port_status ps; 787e0a69b51SAndrew Thompson usb_error_t err; 78802ac6454SAndrew Thompson uint16_t n; 78993ee6e85SHans Petter Selasky uint16_t status; 79093ee6e85SHans Petter Selasky uint16_t change; 79102ac6454SAndrew Thompson 79293ee6e85SHans Petter Selasky DPRINTF("\n"); 79393ee6e85SHans Petter Selasky 794f6bd103eSHans Petter Selasky /* clear any leftover port reset changes first */ 795f6bd103eSHans Petter Selasky usbd_req_clear_port_feature( 796f6bd103eSHans Petter Selasky udev, mtx, port, UHF_C_PORT_RESET); 797f6bd103eSHans Petter Selasky 798f6bd103eSHans Petter Selasky /* assert port reset on the given port */ 799f6bd103eSHans Petter Selasky err = usbd_req_set_port_feature( 800f6bd103eSHans Petter Selasky udev, mtx, port, UHF_PORT_RESET); 801f6bd103eSHans Petter Selasky 802f6bd103eSHans Petter Selasky /* check for errors */ 803f6bd103eSHans Petter Selasky if (err) 80402ac6454SAndrew Thompson goto done; 80502ac6454SAndrew Thompson n = 0; 80602ac6454SAndrew Thompson while (1) { 80702ac6454SAndrew Thompson /* wait for the device to recover from reset */ 80837506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); 80937506412SHans Petter Selasky n += usb_port_reset_delay; 810a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port); 81193ee6e85SHans Petter Selasky if (err) 81202ac6454SAndrew Thompson goto done; 81393ee6e85SHans Petter Selasky 814f6bd103eSHans Petter Selasky status = UGETW(ps.wPortStatus); 815f6bd103eSHans Petter Selasky change = UGETW(ps.wPortChange); 816f6bd103eSHans Petter Selasky 8170e777d84SHans Petter Selasky /* if the device disappeared, just give up */ 818f6bd103eSHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS)) 8190e777d84SHans Petter Selasky goto done; 820f6bd103eSHans Petter Selasky 82102ac6454SAndrew Thompson /* check if reset is complete */ 822f6bd103eSHans Petter Selasky if (change & UPS_C_PORT_RESET) 82302ac6454SAndrew Thompson break; 824f6bd103eSHans Petter Selasky 825f6bd103eSHans Petter Selasky /* 826f6bd103eSHans Petter Selasky * Some Virtual Machines like VirtualBox 4.x fail to 827f6bd103eSHans Petter Selasky * generate a port reset change event. Check if reset 828f6bd103eSHans Petter Selasky * is no longer asserted. 829f6bd103eSHans Petter Selasky */ 830f6bd103eSHans Petter Selasky if (!(status & UPS_RESET)) 831f6bd103eSHans Petter Selasky break; 832f6bd103eSHans Petter Selasky 83302ac6454SAndrew Thompson /* check for timeout */ 83402ac6454SAndrew Thompson if (n > 1000) { 83502ac6454SAndrew Thompson n = 0; 83602ac6454SAndrew Thompson break; 83702ac6454SAndrew Thompson } 83802ac6454SAndrew Thompson } 83902ac6454SAndrew Thompson 84002ac6454SAndrew Thompson /* clear port reset first */ 841a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature( 84202ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET); 84393ee6e85SHans Petter Selasky if (err) 84402ac6454SAndrew Thompson goto done; 84593ee6e85SHans Petter Selasky 84602ac6454SAndrew Thompson /* check for timeout */ 84702ac6454SAndrew Thompson if (n == 0) { 84802ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 84902ac6454SAndrew Thompson goto done; 85002ac6454SAndrew Thompson } 85102ac6454SAndrew Thompson /* wait for the device to recover from reset */ 85237506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); 85302ac6454SAndrew Thompson 85402ac6454SAndrew Thompson done: 85502ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n", 856a593f6b8SAndrew Thompson port, usbd_errstr(err)); 85702ac6454SAndrew Thompson return (err); 85802ac6454SAndrew Thompson } 85902ac6454SAndrew Thompson 86002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 861cbb75751SHans Petter Selasky * usbd_req_warm_reset_port 862cbb75751SHans Petter Selasky * 863cbb75751SHans Petter Selasky * This function will instruct an USB HUB to perform a warm reset 864cbb75751SHans Petter Selasky * sequence on the specified port number. This kind of reset is not 865cbb75751SHans Petter Selasky * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted 866cbb75751SHans Petter Selasky * for SUPER-speed USB HUBs. 867cbb75751SHans Petter Selasky * 868cbb75751SHans Petter Selasky * Returns: 869cbb75751SHans Petter Selasky * 0: Success. The USB device should now be available again. 870cbb75751SHans Petter Selasky * Else: Failure. No USB device is present and the USB port should be 871cbb75751SHans Petter Selasky * disabled. 872cbb75751SHans Petter Selasky *------------------------------------------------------------------------*/ 873cbb75751SHans Petter Selasky usb_error_t 87493ee6e85SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, 87593ee6e85SHans Petter Selasky uint8_t port) 876cbb75751SHans Petter Selasky { 877cbb75751SHans Petter Selasky struct usb_port_status ps; 878cbb75751SHans Petter Selasky usb_error_t err; 879cbb75751SHans Petter Selasky uint16_t n; 88093ee6e85SHans Petter Selasky uint16_t status; 88193ee6e85SHans Petter Selasky uint16_t change; 882cbb75751SHans Petter Selasky 88393ee6e85SHans Petter Selasky DPRINTF("\n"); 88493ee6e85SHans Petter Selasky 88593ee6e85SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 88693ee6e85SHans Petter Selasky if (err) 887cbb75751SHans Petter Selasky goto done; 88893ee6e85SHans Petter Selasky 88993ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus); 89093ee6e85SHans Petter Selasky 89193ee6e85SHans Petter Selasky switch (UPS_PORT_LINK_STATE_GET(status)) { 89293ee6e85SHans Petter Selasky case UPS_PORT_LS_U3: 89393ee6e85SHans Petter Selasky case UPS_PORT_LS_COMP_MODE: 89493ee6e85SHans Petter Selasky case UPS_PORT_LS_LOOPBACK: 89593ee6e85SHans Petter Selasky case UPS_PORT_LS_SS_INA: 89693ee6e85SHans Petter Selasky break; 89793ee6e85SHans Petter Selasky default: 89893ee6e85SHans Petter Selasky DPRINTF("Wrong state for warm reset\n"); 89993ee6e85SHans Petter Selasky return (0); 900cbb75751SHans Petter Selasky } 90193ee6e85SHans Petter Selasky 90293ee6e85SHans Petter Selasky /* clear any leftover warm port reset changes first */ 90393ee6e85SHans Petter Selasky usbd_req_clear_port_feature(udev, mtx, 90493ee6e85SHans Petter Selasky port, UHF_C_BH_PORT_RESET); 90593ee6e85SHans Petter Selasky 90693ee6e85SHans Petter Selasky /* set warm port reset */ 90793ee6e85SHans Petter Selasky err = usbd_req_set_port_feature(udev, mtx, 90893ee6e85SHans Petter Selasky port, UHF_BH_PORT_RESET); 90993ee6e85SHans Petter Selasky if (err) 91093ee6e85SHans Petter Selasky goto done; 91193ee6e85SHans Petter Selasky 912cbb75751SHans Petter Selasky n = 0; 913cbb75751SHans Petter Selasky while (1) { 914cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 91537506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); 91637506412SHans Petter Selasky n += usb_port_reset_delay; 917cbb75751SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 91893ee6e85SHans Petter Selasky if (err) 919cbb75751SHans Petter Selasky goto done; 92093ee6e85SHans Petter Selasky 92193ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus); 92293ee6e85SHans Petter Selasky change = UGETW(ps.wPortChange); 92393ee6e85SHans Petter Selasky 924cbb75751SHans Petter Selasky /* if the device disappeared, just give up */ 92593ee6e85SHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS)) 926cbb75751SHans Petter Selasky goto done; 92793ee6e85SHans Petter Selasky 928cbb75751SHans Petter Selasky /* check if reset is complete */ 92993ee6e85SHans Petter Selasky if (change & UPS_C_BH_PORT_RESET) 930cbb75751SHans Petter Selasky break; 93193ee6e85SHans Petter Selasky 932cbb75751SHans Petter Selasky /* check for timeout */ 933cbb75751SHans Petter Selasky if (n > 1000) { 934cbb75751SHans Petter Selasky n = 0; 935cbb75751SHans Petter Selasky break; 936cbb75751SHans Petter Selasky } 937cbb75751SHans Petter Selasky } 938cbb75751SHans Petter Selasky 939cbb75751SHans Petter Selasky /* clear port reset first */ 940cbb75751SHans Petter Selasky err = usbd_req_clear_port_feature( 941cbb75751SHans Petter Selasky udev, mtx, port, UHF_C_BH_PORT_RESET); 94293ee6e85SHans Petter Selasky if (err) 943cbb75751SHans Petter Selasky goto done; 94493ee6e85SHans Petter Selasky 945cbb75751SHans Petter Selasky /* check for timeout */ 946cbb75751SHans Petter Selasky if (n == 0) { 947cbb75751SHans Petter Selasky err = USB_ERR_TIMEOUT; 948cbb75751SHans Petter Selasky goto done; 949cbb75751SHans Petter Selasky } 950cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 95137506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); 952cbb75751SHans Petter Selasky 953cbb75751SHans Petter Selasky done: 954cbb75751SHans Petter Selasky DPRINTFN(2, "port %d warm reset returning error=%s\n", 955cbb75751SHans Petter Selasky port, usbd_errstr(err)); 956cbb75751SHans Petter Selasky return (err); 957cbb75751SHans Petter Selasky } 958cbb75751SHans Petter Selasky 959cbb75751SHans Petter Selasky /*------------------------------------------------------------------------* 960a593f6b8SAndrew Thompson * usbd_req_get_desc 96102ac6454SAndrew Thompson * 96202ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 96302ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 96402ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 96502ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 96602ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 96702ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 96816589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 96916589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 97016589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 97116589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 97216589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 97316589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 97416589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 97516589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 97616589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 97716589beaSAndrew Thompson * descriptor. 97802ac6454SAndrew Thompson * 97902ac6454SAndrew Thompson * Returns: 98002ac6454SAndrew Thompson * 0: Success 98102ac6454SAndrew Thompson * Else: Failure 98202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 983e0a69b51SAndrew Thompson usb_error_t 984a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 98516589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 98602ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 98702ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 98802ac6454SAndrew Thompson uint8_t retries) 98902ac6454SAndrew Thompson { 990760bc48eSAndrew Thompson struct usb_device_request req; 99102ac6454SAndrew Thompson uint8_t *buf; 992e0a69b51SAndrew Thompson usb_error_t err; 99302ac6454SAndrew Thompson 99402ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 99502ac6454SAndrew Thompson id, type, index, max_len); 99602ac6454SAndrew Thompson 99702ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 99802ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 99902ac6454SAndrew Thompson USETW2(req.wValue, type, index); 100002ac6454SAndrew Thompson USETW(req.wIndex, id); 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson while (1) { 100302ac6454SAndrew Thompson 100402ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 100502ac6454SAndrew Thompson err = USB_ERR_INVAL; 100602ac6454SAndrew Thompson goto done; 100702ac6454SAndrew Thompson } 100802ac6454SAndrew Thompson USETW(req.wLength, min_len); 100902ac6454SAndrew Thompson 1010a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 101123de050bSHans Petter Selasky desc, 0, NULL, 500 /* ms */); 101202ac6454SAndrew Thompson 101302ac6454SAndrew Thompson if (err) { 101402ac6454SAndrew Thompson if (!retries) { 101502ac6454SAndrew Thompson goto done; 101602ac6454SAndrew Thompson } 101702ac6454SAndrew Thompson retries--; 101802ac6454SAndrew Thompson 1019a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 102002ac6454SAndrew Thompson 102102ac6454SAndrew Thompson continue; 102202ac6454SAndrew Thompson } 102302ac6454SAndrew Thompson buf = desc; 102402ac6454SAndrew Thompson 102502ac6454SAndrew Thompson if (min_len == max_len) { 102602ac6454SAndrew Thompson 102716589beaSAndrew Thompson /* enforce correct length */ 102816589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 102902ac6454SAndrew Thompson buf[0] = min_len; 103016589beaSAndrew Thompson 103116589beaSAndrew Thompson /* enforce correct type */ 103202ac6454SAndrew Thompson buf[1] = type; 103302ac6454SAndrew Thompson 103402ac6454SAndrew Thompson goto done; 103502ac6454SAndrew Thompson } 103602ac6454SAndrew Thompson /* range check */ 103702ac6454SAndrew Thompson 103802ac6454SAndrew Thompson if (max_len > buf[0]) { 103902ac6454SAndrew Thompson max_len = buf[0]; 104002ac6454SAndrew Thompson } 104102ac6454SAndrew Thompson /* zero minimum data */ 104202ac6454SAndrew Thompson 104302ac6454SAndrew Thompson while (min_len > max_len) { 104402ac6454SAndrew Thompson min_len--; 104502ac6454SAndrew Thompson buf[min_len] = 0; 104602ac6454SAndrew Thompson } 104702ac6454SAndrew Thompson 104802ac6454SAndrew Thompson /* set new minimum length */ 104902ac6454SAndrew Thompson 105002ac6454SAndrew Thompson min_len = max_len; 105102ac6454SAndrew Thompson } 105202ac6454SAndrew Thompson done: 105316589beaSAndrew Thompson if (actlen != NULL) { 105416589beaSAndrew Thompson if (err) 105516589beaSAndrew Thompson *actlen = 0; 105616589beaSAndrew Thompson else 105716589beaSAndrew Thompson *actlen = min_len; 105816589beaSAndrew Thompson } 105902ac6454SAndrew Thompson return (err); 106002ac6454SAndrew Thompson } 106102ac6454SAndrew Thompson 106202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1063a593f6b8SAndrew Thompson * usbd_req_get_string_any 106402ac6454SAndrew Thompson * 106502ac6454SAndrew Thompson * This function will return the string given by "string_index" 106602ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 106702ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 106802ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 106902ac6454SAndrew Thompson * 107002ac6454SAndrew Thompson * Returns: 107102ac6454SAndrew Thompson * 0: Success 107202ac6454SAndrew Thompson * Else: Failure 107302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1074e0a69b51SAndrew Thompson usb_error_t 1075a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 107602ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 107702ac6454SAndrew Thompson { 107802ac6454SAndrew Thompson char *s; 107902ac6454SAndrew Thompson uint8_t *temp; 108002ac6454SAndrew Thompson uint16_t i; 108102ac6454SAndrew Thompson uint16_t n; 108202ac6454SAndrew Thompson uint16_t c; 108302ac6454SAndrew Thompson uint8_t swap; 1084e0a69b51SAndrew Thompson usb_error_t err; 108502ac6454SAndrew Thompson 108602ac6454SAndrew Thompson if (len == 0) { 108702ac6454SAndrew Thompson /* should not happen */ 108802ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 108902ac6454SAndrew Thompson } 109002ac6454SAndrew Thompson if (string_index == 0) { 109102ac6454SAndrew Thompson /* this is the language table */ 109202ac6454SAndrew Thompson buf[0] = 0; 109302ac6454SAndrew Thompson return (USB_ERR_INVAL); 109402ac6454SAndrew Thompson } 109502ac6454SAndrew Thompson if (udev->flags.no_strings) { 109602ac6454SAndrew Thompson buf[0] = 0; 109702ac6454SAndrew Thompson return (USB_ERR_STALLED); 109802ac6454SAndrew Thompson } 1099a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 110002ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 110102ac6454SAndrew Thompson if (err) { 110202ac6454SAndrew Thompson buf[0] = 0; 110302ac6454SAndrew Thompson return (err); 110402ac6454SAndrew Thompson } 110502ac6454SAndrew Thompson temp = (uint8_t *)buf; 110602ac6454SAndrew Thompson 110702ac6454SAndrew Thompson if (temp[0] < 2) { 110802ac6454SAndrew Thompson /* string length is too short */ 110902ac6454SAndrew Thompson buf[0] = 0; 111002ac6454SAndrew Thompson return (USB_ERR_INVAL); 111102ac6454SAndrew Thompson } 111202ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 111302ac6454SAndrew Thompson len--; 111402ac6454SAndrew Thompson 111502ac6454SAndrew Thompson /* find maximum length */ 111602ac6454SAndrew Thompson s = buf; 111702ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 111802ac6454SAndrew Thompson if (n > len) { 111902ac6454SAndrew Thompson n = len; 112002ac6454SAndrew Thompson } 112102ac6454SAndrew Thompson /* skip descriptor header */ 112202ac6454SAndrew Thompson temp += 2; 112302ac6454SAndrew Thompson 112402ac6454SAndrew Thompson /* reset swap state */ 112502ac6454SAndrew Thompson swap = 3; 112602ac6454SAndrew Thompson 112702ac6454SAndrew Thompson /* convert and filter */ 112802ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 112902ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 113002ac6454SAndrew Thompson 113102ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 113202ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 113302ac6454SAndrew Thompson /* Little Endian, default */ 113402ac6454SAndrew Thompson *s = c; 113502ac6454SAndrew Thompson swap = 1; 113602ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 113702ac6454SAndrew Thompson /* Big Endian */ 113802ac6454SAndrew Thompson *s = c >> 8; 113902ac6454SAndrew Thompson swap = 2; 114002ac6454SAndrew Thompson } else { 114102ac6454SAndrew Thompson /* silently skip bad character */ 114202ac6454SAndrew Thompson continue; 114302ac6454SAndrew Thompson } 114402ac6454SAndrew Thompson 114502ac6454SAndrew Thompson /* 1146b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical 1147b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts 1148b64cf89fSHans Petter Selasky * and daemons. 114902ac6454SAndrew Thompson */ 1150b64cf89fSHans Petter Selasky if (isalpha(*s) || 1151b64cf89fSHans Petter Selasky isdigit(*s) || 1152b64cf89fSHans Petter Selasky *s == '-' || 1153b64cf89fSHans Petter Selasky *s == '+' || 1154b64cf89fSHans Petter Selasky *s == ' ' || 1155b64cf89fSHans Petter Selasky *s == '.' || 1156b64cf89fSHans Petter Selasky *s == ',') { 1157b64cf89fSHans Petter Selasky /* allowed */ 115802ac6454SAndrew Thompson s++; 115902ac6454SAndrew Thompson } 1160b64cf89fSHans Petter Selasky /* silently skip bad character */ 1161b64cf89fSHans Petter Selasky } 116202ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 116302ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 116402ac6454SAndrew Thompson } 116502ac6454SAndrew Thompson 116602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1167a593f6b8SAndrew Thompson * usbd_req_get_string_desc 116802ac6454SAndrew Thompson * 116902ac6454SAndrew Thompson * If you don't know the language ID, consider using 1170a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 117102ac6454SAndrew Thompson * 117202ac6454SAndrew Thompson * Returns: 117302ac6454SAndrew Thompson * 0: Success 117402ac6454SAndrew Thompson * Else: Failure 117502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1176e0a69b51SAndrew Thompson usb_error_t 1177a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 117802ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 117902ac6454SAndrew Thompson uint8_t string_index) 118002ac6454SAndrew Thompson { 1181a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 118202ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 118302ac6454SAndrew Thompson } 118402ac6454SAndrew Thompson 118502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1186a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 11877efaaa9aSAndrew Thompson * 11887efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 11897efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 11907efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 11917efaaa9aSAndrew Thompson * 11927efaaa9aSAndrew Thompson * Returns: 11937efaaa9aSAndrew Thompson * 0: Success 11947efaaa9aSAndrew Thompson * Else: Failure 11957efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 1196e0a69b51SAndrew Thompson usb_error_t 1197a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 1198760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 11997efaaa9aSAndrew Thompson { 1200760bc48eSAndrew Thompson struct usb_device_request req; 1201e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 1202459d369eSAndrew Thompson const void *ptr; 1203459d369eSAndrew Thompson uint16_t len; 1204e0a69b51SAndrew Thompson usb_error_t err; 12057efaaa9aSAndrew Thompson 120663521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 12077efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 1208459d369eSAndrew Thompson USETW(req.wValue, wValue); 12097efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 12107efaaa9aSAndrew Thompson USETW(req.wLength, 0); 12117efaaa9aSAndrew Thompson 1212459d369eSAndrew Thompson ptr = NULL; 1213459d369eSAndrew Thompson len = 0; 12147efaaa9aSAndrew Thompson 1215a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 1216459d369eSAndrew Thompson 1217459d369eSAndrew Thompson if (hr_func == NULL) 1218459d369eSAndrew Thompson err = USB_ERR_INVAL; 1219459d369eSAndrew Thompson else { 1220459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 1221459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 1222459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 1223459d369eSAndrew Thompson } 1224459d369eSAndrew Thompson 1225459d369eSAndrew Thompson if (err) 1226459d369eSAndrew Thompson ptr = NULL; 1227459d369eSAndrew Thompson else if (ptr == NULL) 1228459d369eSAndrew Thompson err = USB_ERR_INVAL; 1229459d369eSAndrew Thompson 1230760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 1231459d369eSAndrew Thompson 1232459d369eSAndrew Thompson return (err); 12337efaaa9aSAndrew Thompson } 12347efaaa9aSAndrew Thompson 12357efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 1236a593f6b8SAndrew Thompson * usbd_req_get_config_desc 123702ac6454SAndrew Thompson * 123802ac6454SAndrew Thompson * Returns: 123902ac6454SAndrew Thompson * 0: Success 124002ac6454SAndrew Thompson * Else: Failure 124102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1242e0a69b51SAndrew Thompson usb_error_t 1243a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 1244760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 124502ac6454SAndrew Thompson { 1246e0a69b51SAndrew Thompson usb_error_t err; 124702ac6454SAndrew Thompson 124802ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 124902ac6454SAndrew Thompson 1250a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 125102ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 125202ac6454SAndrew Thompson if (err) { 125302ac6454SAndrew Thompson goto done; 125402ac6454SAndrew Thompson } 125502ac6454SAndrew Thompson /* Extra sanity checking */ 12566d917491SHans Petter Selasky if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { 125702ac6454SAndrew Thompson err = USB_ERR_INVAL; 125802ac6454SAndrew Thompson } 125902ac6454SAndrew Thompson done: 126002ac6454SAndrew Thompson return (err); 126102ac6454SAndrew Thompson } 126202ac6454SAndrew Thompson 126302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 12642c79a775SHans Petter Selasky * usbd_alloc_config_desc 12652c79a775SHans Petter Selasky * 12662c79a775SHans Petter Selasky * This function is used to allocate a zeroed configuration 12672c79a775SHans Petter Selasky * descriptor. 12682c79a775SHans Petter Selasky * 12692c79a775SHans Petter Selasky * Returns: 12702c79a775SHans Petter Selasky * NULL: Failure 12712c79a775SHans Petter Selasky * Else: Success 12722c79a775SHans Petter Selasky *------------------------------------------------------------------------*/ 12732c79a775SHans Petter Selasky void * 12742c79a775SHans Petter Selasky usbd_alloc_config_desc(struct usb_device *udev, uint32_t size) 12752c79a775SHans Petter Selasky { 12762c79a775SHans Petter Selasky if (size > USB_CONFIG_MAX) { 12772c79a775SHans Petter Selasky DPRINTF("Configuration descriptor too big\n"); 12782c79a775SHans Petter Selasky return (NULL); 12792c79a775SHans Petter Selasky } 12802c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0) 12812c79a775SHans Petter Selasky return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK)); 12822c79a775SHans Petter Selasky #else 12832c79a775SHans Petter Selasky memset(udev->config_data, 0, sizeof(udev->config_data)); 12842c79a775SHans Petter Selasky return (udev->config_data); 12852c79a775SHans Petter Selasky #endif 12862c79a775SHans Petter Selasky } 12872c79a775SHans Petter Selasky 12882c79a775SHans Petter Selasky /*------------------------------------------------------------------------* 12892c79a775SHans Petter Selasky * usbd_alloc_config_desc 12902c79a775SHans Petter Selasky * 12912c79a775SHans Petter Selasky * This function is used to free a configuration descriptor. 12922c79a775SHans Petter Selasky *------------------------------------------------------------------------*/ 12932c79a775SHans Petter Selasky void 12942c79a775SHans Petter Selasky usbd_free_config_desc(struct usb_device *udev, void *ptr) 12952c79a775SHans Petter Selasky { 12962c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0) 12972c79a775SHans Petter Selasky free(ptr, M_USBDEV); 12982c79a775SHans Petter Selasky #endif 12992c79a775SHans Petter Selasky } 13002c79a775SHans Petter Selasky 13012c79a775SHans Petter Selasky /*------------------------------------------------------------------------* 1302a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 130302ac6454SAndrew Thompson * 130402ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 13052c79a775SHans Petter Selasky * ensures that "wTotalLength" is correct. The returned configuration 13062c79a775SHans Petter Selasky * descriptor is freed by calling "usbd_free_config_desc()". 130702ac6454SAndrew Thompson * 130802ac6454SAndrew Thompson * Returns: 130902ac6454SAndrew Thompson * 0: Success 131002ac6454SAndrew Thompson * Else: Failure 131102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1312e0a69b51SAndrew Thompson usb_error_t 1313a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, 13142c79a775SHans Petter Selasky struct usb_config_descriptor **ppcd, uint8_t index) 131502ac6454SAndrew Thompson { 1316760bc48eSAndrew Thompson struct usb_config_descriptor cd; 1317760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 13185b0752bbSHans Petter Selasky uint32_t len; 1319e0a69b51SAndrew Thompson usb_error_t err; 132002ac6454SAndrew Thompson 132102ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 132202ac6454SAndrew Thompson 132302ac6454SAndrew Thompson *ppcd = NULL; 132402ac6454SAndrew Thompson 1325a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 13265b0752bbSHans Petter Selasky if (err) 132702ac6454SAndrew Thompson return (err); 13285b0752bbSHans Petter Selasky 132902ac6454SAndrew Thompson /* get full descriptor */ 133002ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 13315b0752bbSHans Petter Selasky if (len < (uint32_t)sizeof(*cdesc)) { 133202ac6454SAndrew Thompson /* corrupt descriptor */ 133302ac6454SAndrew Thompson return (USB_ERR_INVAL); 13345b0752bbSHans Petter Selasky } else if (len > USB_CONFIG_MAX) { 13355b0752bbSHans Petter Selasky DPRINTF("Configuration descriptor was truncated\n"); 13365b0752bbSHans Petter Selasky len = USB_CONFIG_MAX; 133702ac6454SAndrew Thompson } 13382c79a775SHans Petter Selasky cdesc = usbd_alloc_config_desc(udev, len); 13395b0752bbSHans Petter Selasky if (cdesc == NULL) 134002ac6454SAndrew Thompson return (USB_ERR_NOMEM); 1341a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 134202ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 134302ac6454SAndrew Thompson if (err) { 13442c79a775SHans Petter Selasky usbd_free_config_desc(udev, cdesc); 134502ac6454SAndrew Thompson return (err); 134602ac6454SAndrew Thompson } 134702ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 134802ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 134902ac6454SAndrew Thompson 135002ac6454SAndrew Thompson *ppcd = cdesc; 135102ac6454SAndrew Thompson 135202ac6454SAndrew Thompson return (0); /* success */ 135302ac6454SAndrew Thompson } 135402ac6454SAndrew Thompson 135502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1356a593f6b8SAndrew Thompson * usbd_req_get_device_desc 135702ac6454SAndrew Thompson * 135802ac6454SAndrew Thompson * Returns: 135902ac6454SAndrew Thompson * 0: Success 136002ac6454SAndrew Thompson * Else: Failure 136102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1362e0a69b51SAndrew Thompson usb_error_t 1363a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1364760bc48eSAndrew Thompson struct usb_device_descriptor *d) 136502ac6454SAndrew Thompson { 136602ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1367a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 136802ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 136902ac6454SAndrew Thompson } 137002ac6454SAndrew Thompson 137102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1372a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 137302ac6454SAndrew Thompson * 137402ac6454SAndrew Thompson * Returns: 137502ac6454SAndrew Thompson * 0: Success 137602ac6454SAndrew Thompson * Else: Failure 137702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1378e0a69b51SAndrew Thompson usb_error_t 1379a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 138002ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 138102ac6454SAndrew Thompson { 1382a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1383760bc48eSAndrew Thompson struct usb_device_request req; 138402ac6454SAndrew Thompson 1385bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 138602ac6454SAndrew Thompson return (USB_ERR_INVAL); 1387bd73b187SAlfred Perlstein 138802ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 138902ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 139002ac6454SAndrew Thompson USETW(req.wValue, 0); 139102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 139202ac6454SAndrew Thompson req.wIndex[1] = 0; 139302ac6454SAndrew Thompson USETW(req.wLength, 1); 1394a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 139502ac6454SAndrew Thompson } 139602ac6454SAndrew Thompson 139702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1398a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 139902ac6454SAndrew Thompson * 140002ac6454SAndrew Thompson * Returns: 140102ac6454SAndrew Thompson * 0: Success 140202ac6454SAndrew Thompson * Else: Failure 140302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1404e0a69b51SAndrew Thompson usb_error_t 1405a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 140602ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 140702ac6454SAndrew Thompson { 1408a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1409760bc48eSAndrew Thompson struct usb_device_request req; 141002ac6454SAndrew Thompson 1411bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 141202ac6454SAndrew Thompson return (USB_ERR_INVAL); 1413bd73b187SAlfred Perlstein 141402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 141502ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 141602ac6454SAndrew Thompson req.wValue[0] = alt_no; 141702ac6454SAndrew Thompson req.wValue[1] = 0; 141802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 141902ac6454SAndrew Thompson req.wIndex[1] = 0; 142002ac6454SAndrew Thompson USETW(req.wLength, 0); 1421a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 142202ac6454SAndrew Thompson } 142302ac6454SAndrew Thompson 142402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1425a593f6b8SAndrew Thompson * usbd_req_get_device_status 142602ac6454SAndrew Thompson * 142702ac6454SAndrew Thompson * Returns: 142802ac6454SAndrew Thompson * 0: Success 142902ac6454SAndrew Thompson * Else: Failure 143002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1431e0a69b51SAndrew Thompson usb_error_t 1432a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1433760bc48eSAndrew Thompson struct usb_status *st) 143402ac6454SAndrew Thompson { 1435760bc48eSAndrew Thompson struct usb_device_request req; 143602ac6454SAndrew Thompson 143702ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 143802ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 143902ac6454SAndrew Thompson USETW(req.wValue, 0); 144002ac6454SAndrew Thompson USETW(req.wIndex, 0); 144102ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1442a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 144302ac6454SAndrew Thompson } 144402ac6454SAndrew Thompson 144502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1446a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 144702ac6454SAndrew Thompson * 144802ac6454SAndrew Thompson * Returns: 144902ac6454SAndrew Thompson * 0: Success 145002ac6454SAndrew Thompson * Else: Failure 145102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1452e0a69b51SAndrew Thompson usb_error_t 1453a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1454760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 145502ac6454SAndrew Thompson { 1456760bc48eSAndrew Thompson struct usb_device_request req; 145702ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 145802ac6454SAndrew Thompson 145902ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 146002ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 146102ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 146202ac6454SAndrew Thompson USETW(req.wIndex, 0); 146302ac6454SAndrew Thompson USETW(req.wLength, len); 1464a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 146502ac6454SAndrew Thompson } 146602ac6454SAndrew Thompson 146702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1468963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor 1469963169b4SHans Petter Selasky * 1470963169b4SHans Petter Selasky * Returns: 1471963169b4SHans Petter Selasky * 0: Success 1472963169b4SHans Petter Selasky * Else: Failure 1473963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1474963169b4SHans Petter Selasky usb_error_t 1475963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1476963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports) 1477963169b4SHans Petter Selasky { 1478963169b4SHans Petter Selasky struct usb_device_request req; 1479963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); 1480963169b4SHans Petter Selasky 1481963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE; 1482963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR; 1483963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0); 1484963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1485963169b4SHans Petter Selasky USETW(req.wLength, len); 1486963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd)); 1487963169b4SHans Petter Selasky } 1488963169b4SHans Petter Selasky 1489963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1490a593f6b8SAndrew Thompson * usbd_req_get_hub_status 149102ac6454SAndrew Thompson * 149202ac6454SAndrew Thompson * Returns: 149302ac6454SAndrew Thompson * 0: Success 149402ac6454SAndrew Thompson * Else: Failure 149502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1496e0a69b51SAndrew Thompson usb_error_t 1497a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1498760bc48eSAndrew Thompson struct usb_hub_status *st) 149902ac6454SAndrew Thompson { 1500760bc48eSAndrew Thompson struct usb_device_request req; 150102ac6454SAndrew Thompson 150202ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 150302ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 150402ac6454SAndrew Thompson USETW(req.wValue, 0); 150502ac6454SAndrew Thompson USETW(req.wIndex, 0); 1506760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1507a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 150802ac6454SAndrew Thompson } 150902ac6454SAndrew Thompson 151002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1511a593f6b8SAndrew Thompson * usbd_req_set_address 151202ac6454SAndrew Thompson * 151302ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 151402ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 151502ac6454SAndrew Thompson * 151602ac6454SAndrew Thompson * Returns: 151702ac6454SAndrew Thompson * 0: Success 151802ac6454SAndrew Thompson * Else: Failure 151902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1520e0a69b51SAndrew Thompson usb_error_t 1521a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 152202ac6454SAndrew Thompson { 1523760bc48eSAndrew Thompson struct usb_device_request req; 1524963169b4SHans Petter Selasky usb_error_t err; 152502ac6454SAndrew Thompson 152602ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 152702ac6454SAndrew Thompson 152802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 152902ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 153002ac6454SAndrew Thompson USETW(req.wValue, addr); 153102ac6454SAndrew Thompson USETW(req.wIndex, 0); 153202ac6454SAndrew Thompson USETW(req.wLength, 0); 153302ac6454SAndrew Thompson 1534963169b4SHans Petter Selasky err = USB_ERR_INVAL; 1535963169b4SHans Petter Selasky 1536963169b4SHans Petter Selasky /* check if USB controller handles set address */ 1537963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL) 1538963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr); 1539963169b4SHans Petter Selasky 1540963169b4SHans Petter Selasky if (err != USB_ERR_INVAL) 1541963169b4SHans Petter Selasky goto done; 1542963169b4SHans Petter Selasky 154302ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1544963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL, 1545963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000); 1546963169b4SHans Petter Selasky 1547963169b4SHans Petter Selasky done: 1548963169b4SHans Petter Selasky /* allow device time to set new address */ 1549963169b4SHans Petter Selasky usb_pause_mtx(mtx, 155037506412SHans Petter Selasky USB_MS_TO_TICKS(usb_set_address_settle)); 1551963169b4SHans Petter Selasky 1552963169b4SHans Petter Selasky return (err); 155302ac6454SAndrew Thompson } 155402ac6454SAndrew Thompson 155502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1556a593f6b8SAndrew Thompson * usbd_req_get_port_status 155702ac6454SAndrew Thompson * 155802ac6454SAndrew Thompson * Returns: 155902ac6454SAndrew Thompson * 0: Success 156002ac6454SAndrew Thompson * Else: Failure 156102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1562e0a69b51SAndrew Thompson usb_error_t 1563a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1564760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 156502ac6454SAndrew Thompson { 1566760bc48eSAndrew Thompson struct usb_device_request req; 156702ac6454SAndrew Thompson 156802ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 156902ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 157002ac6454SAndrew Thompson USETW(req.wValue, 0); 157102ac6454SAndrew Thompson req.wIndex[0] = port; 157202ac6454SAndrew Thompson req.wIndex[1] = 0; 157302ac6454SAndrew Thompson USETW(req.wLength, sizeof *ps); 1574a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, ps)); 157502ac6454SAndrew Thompson } 157602ac6454SAndrew Thompson 157702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1578a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 157902ac6454SAndrew Thompson * 158002ac6454SAndrew Thompson * Returns: 158102ac6454SAndrew Thompson * 0: Success 158202ac6454SAndrew Thompson * Else: Failure 158302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1584e0a69b51SAndrew Thompson usb_error_t 1585a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 158602ac6454SAndrew Thompson uint16_t sel) 158702ac6454SAndrew Thompson { 1588760bc48eSAndrew Thompson struct usb_device_request req; 158902ac6454SAndrew Thompson 159002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 159102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 159202ac6454SAndrew Thompson USETW(req.wValue, sel); 159302ac6454SAndrew Thompson USETW(req.wIndex, 0); 159402ac6454SAndrew Thompson USETW(req.wLength, 0); 1595a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 159602ac6454SAndrew Thompson } 159702ac6454SAndrew Thompson 159802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1599a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 160002ac6454SAndrew Thompson * 160102ac6454SAndrew Thompson * Returns: 160202ac6454SAndrew Thompson * 0: Success 160302ac6454SAndrew Thompson * Else: Failure 160402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1605e0a69b51SAndrew Thompson usb_error_t 1606a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 160702ac6454SAndrew Thompson uint16_t sel) 160802ac6454SAndrew Thompson { 1609760bc48eSAndrew Thompson struct usb_device_request req; 161002ac6454SAndrew Thompson 161102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 161202ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 161302ac6454SAndrew Thompson USETW(req.wValue, sel); 161402ac6454SAndrew Thompson USETW(req.wIndex, 0); 161502ac6454SAndrew Thompson USETW(req.wLength, 0); 1616a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 161702ac6454SAndrew Thompson } 161802ac6454SAndrew Thompson 161902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1620963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout 1621963169b4SHans Petter Selasky * 1622963169b4SHans Petter Selasky * Returns: 1623963169b4SHans Petter Selasky * 0: Success 1624963169b4SHans Petter Selasky * Else: Failure 1625963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1626963169b4SHans Petter Selasky usb_error_t 1627963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, 1628963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1629963169b4SHans Petter Selasky { 1630963169b4SHans Petter Selasky struct usb_device_request req; 1631963169b4SHans Petter Selasky 1632963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1633963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1634963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT); 1635963169b4SHans Petter Selasky req.wIndex[0] = port; 1636963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1637963169b4SHans Petter Selasky USETW(req.wLength, 0); 1638963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1639963169b4SHans Petter Selasky } 1640963169b4SHans Petter Selasky 1641963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1642963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout 1643963169b4SHans Petter Selasky * 1644963169b4SHans Petter Selasky * Returns: 1645963169b4SHans Petter Selasky * 0: Success 1646963169b4SHans Petter Selasky * Else: Failure 1647963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1648963169b4SHans Petter Selasky usb_error_t 1649963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, 1650963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1651963169b4SHans Petter Selasky { 1652963169b4SHans Petter Selasky struct usb_device_request req; 1653963169b4SHans Petter Selasky 1654963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1655963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1656963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT); 1657963169b4SHans Petter Selasky req.wIndex[0] = port; 1658963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1659963169b4SHans Petter Selasky USETW(req.wLength, 0); 1660963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1661963169b4SHans Petter Selasky } 1662963169b4SHans Petter Selasky 1663963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1664963169b4SHans Petter Selasky * usbd_req_set_hub_depth 1665963169b4SHans Petter Selasky * 1666963169b4SHans Petter Selasky * Returns: 1667963169b4SHans Petter Selasky * 0: Success 1668963169b4SHans Petter Selasky * Else: Failure 1669963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1670963169b4SHans Petter Selasky usb_error_t 1671963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, 1672963169b4SHans Petter Selasky uint16_t depth) 1673963169b4SHans Petter Selasky { 1674963169b4SHans Petter Selasky struct usb_device_request req; 1675963169b4SHans Petter Selasky 1676963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE; 1677963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH; 1678963169b4SHans Petter Selasky USETW(req.wValue, depth); 1679963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1680963169b4SHans Petter Selasky USETW(req.wLength, 0); 1681963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1682963169b4SHans Petter Selasky } 1683963169b4SHans Petter Selasky 1684963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1685a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 168602ac6454SAndrew Thompson * 168702ac6454SAndrew Thompson * Returns: 168802ac6454SAndrew Thompson * 0: Success 168902ac6454SAndrew Thompson * Else: Failure 169002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1691e0a69b51SAndrew Thompson usb_error_t 1692a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 169302ac6454SAndrew Thompson uint8_t port, uint16_t sel) 169402ac6454SAndrew Thompson { 1695760bc48eSAndrew Thompson struct usb_device_request req; 169602ac6454SAndrew Thompson 169702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 169802ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 169902ac6454SAndrew Thompson USETW(req.wValue, sel); 170002ac6454SAndrew Thompson req.wIndex[0] = port; 170102ac6454SAndrew Thompson req.wIndex[1] = 0; 170202ac6454SAndrew Thompson USETW(req.wLength, 0); 1703a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 170402ac6454SAndrew Thompson } 170502ac6454SAndrew Thompson 170602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1707a593f6b8SAndrew Thompson * usbd_req_set_port_feature 170802ac6454SAndrew Thompson * 170902ac6454SAndrew Thompson * Returns: 171002ac6454SAndrew Thompson * 0: Success 171102ac6454SAndrew Thompson * Else: Failure 171202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1713e0a69b51SAndrew Thompson usb_error_t 1714a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, 171502ac6454SAndrew Thompson uint8_t port, uint16_t sel) 171602ac6454SAndrew Thompson { 1717760bc48eSAndrew Thompson struct usb_device_request req; 171802ac6454SAndrew Thompson 171902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 172002ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 172102ac6454SAndrew Thompson USETW(req.wValue, sel); 172202ac6454SAndrew Thompson req.wIndex[0] = port; 172302ac6454SAndrew Thompson req.wIndex[1] = 0; 172402ac6454SAndrew Thompson USETW(req.wLength, 0); 1725a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 172602ac6454SAndrew Thompson } 172702ac6454SAndrew Thompson 172802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1729a593f6b8SAndrew Thompson * usbd_req_set_protocol 173002ac6454SAndrew Thompson * 173102ac6454SAndrew Thompson * Returns: 173202ac6454SAndrew Thompson * 0: Success 173302ac6454SAndrew Thompson * Else: Failure 173402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1735e0a69b51SAndrew Thompson usb_error_t 1736a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 173702ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 173802ac6454SAndrew Thompson { 1739a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1740760bc48eSAndrew Thompson struct usb_device_request req; 174102ac6454SAndrew Thompson 174202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 174302ac6454SAndrew Thompson return (USB_ERR_INVAL); 174402ac6454SAndrew Thompson } 174502ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 174602ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 174702ac6454SAndrew Thompson 174802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 174902ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 175002ac6454SAndrew Thompson USETW(req.wValue, report); 175102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 175202ac6454SAndrew Thompson req.wIndex[1] = 0; 175302ac6454SAndrew Thompson USETW(req.wLength, 0); 1754a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 175502ac6454SAndrew Thompson } 175602ac6454SAndrew Thompson 175702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1758a593f6b8SAndrew Thompson * usbd_req_set_report 175902ac6454SAndrew Thompson * 176002ac6454SAndrew Thompson * Returns: 176102ac6454SAndrew Thompson * 0: Success 176202ac6454SAndrew Thompson * Else: Failure 176302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1764e0a69b51SAndrew Thompson usb_error_t 1765a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 176602ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 176702ac6454SAndrew Thompson { 1768a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1769760bc48eSAndrew Thompson struct usb_device_request req; 177002ac6454SAndrew Thompson 177102ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 177202ac6454SAndrew Thompson return (USB_ERR_INVAL); 177302ac6454SAndrew Thompson } 177402ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 177502ac6454SAndrew Thompson 177602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 177702ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 177802ac6454SAndrew Thompson USETW2(req.wValue, type, id); 177902ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 178002ac6454SAndrew Thompson req.wIndex[1] = 0; 178102ac6454SAndrew Thompson USETW(req.wLength, len); 1782a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 178302ac6454SAndrew Thompson } 178402ac6454SAndrew Thompson 178502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1786a593f6b8SAndrew Thompson * usbd_req_get_report 178702ac6454SAndrew Thompson * 178802ac6454SAndrew Thompson * Returns: 178902ac6454SAndrew Thompson * 0: Success 179002ac6454SAndrew Thompson * Else: Failure 179102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1792e0a69b51SAndrew Thompson usb_error_t 1793a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 179402ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 179502ac6454SAndrew Thompson { 1796a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1797760bc48eSAndrew Thompson struct usb_device_request req; 179802ac6454SAndrew Thompson 1799477a63a8SAlexander Motin if ((iface == NULL) || (iface->idesc == NULL)) { 180002ac6454SAndrew Thompson return (USB_ERR_INVAL); 180102ac6454SAndrew Thompson } 180202ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 180302ac6454SAndrew Thompson 180402ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 180502ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 180602ac6454SAndrew Thompson USETW2(req.wValue, type, id); 180702ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 180802ac6454SAndrew Thompson req.wIndex[1] = 0; 180902ac6454SAndrew Thompson USETW(req.wLength, len); 1810a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 181102ac6454SAndrew Thompson } 181202ac6454SAndrew Thompson 181302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1814a593f6b8SAndrew Thompson * usbd_req_set_idle 181502ac6454SAndrew Thompson * 181602ac6454SAndrew Thompson * Returns: 181702ac6454SAndrew Thompson * 0: Success 181802ac6454SAndrew Thompson * Else: Failure 181902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1820e0a69b51SAndrew Thompson usb_error_t 1821a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 182202ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 182302ac6454SAndrew Thompson { 1824a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1825760bc48eSAndrew Thompson struct usb_device_request req; 182602ac6454SAndrew Thompson 182702ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 182802ac6454SAndrew Thompson return (USB_ERR_INVAL); 182902ac6454SAndrew Thompson } 183002ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 183102ac6454SAndrew Thompson 183202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 183302ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 183402ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 183502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 183602ac6454SAndrew Thompson req.wIndex[1] = 0; 183702ac6454SAndrew Thompson USETW(req.wLength, 0); 1838a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 183902ac6454SAndrew Thompson } 184002ac6454SAndrew Thompson 184102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1842a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 184302ac6454SAndrew Thompson * 184402ac6454SAndrew Thompson * Returns: 184502ac6454SAndrew Thompson * 0: Success 184602ac6454SAndrew Thompson * Else: Failure 184702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1848e0a69b51SAndrew Thompson usb_error_t 1849a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 185002ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 185102ac6454SAndrew Thompson { 1852a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1853760bc48eSAndrew Thompson struct usb_device_request req; 185402ac6454SAndrew Thompson 185502ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 185602ac6454SAndrew Thompson return (USB_ERR_INVAL); 185702ac6454SAndrew Thompson } 185802ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 185902ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 186002ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 186102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 186202ac6454SAndrew Thompson req.wIndex[1] = 0; 186302ac6454SAndrew Thompson USETW(req.wLength, size); 1864a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 186502ac6454SAndrew Thompson } 186602ac6454SAndrew Thompson 186702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1868a593f6b8SAndrew Thompson * usbd_req_set_config 186902ac6454SAndrew Thompson * 187002ac6454SAndrew Thompson * This function is used to select the current configuration number in 187102ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 187202ac6454SAndrew Thompson * configuration the function of the interfaces can change. 187302ac6454SAndrew Thompson * 187402ac6454SAndrew Thompson * Returns: 187502ac6454SAndrew Thompson * 0: Success 187602ac6454SAndrew Thompson * Else: Failure 187702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1878e0a69b51SAndrew Thompson usb_error_t 1879a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 188002ac6454SAndrew Thompson { 1881760bc48eSAndrew Thompson struct usb_device_request req; 188202ac6454SAndrew Thompson 188302ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 188402ac6454SAndrew Thompson 188502ac6454SAndrew Thompson /* do "set configuration" request */ 188602ac6454SAndrew Thompson 188702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 188802ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 188902ac6454SAndrew Thompson req.wValue[0] = conf; 189002ac6454SAndrew Thompson req.wValue[1] = 0; 189102ac6454SAndrew Thompson USETW(req.wIndex, 0); 189202ac6454SAndrew Thompson USETW(req.wLength, 0); 1893a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 189402ac6454SAndrew Thompson } 189502ac6454SAndrew Thompson 189602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1897a593f6b8SAndrew Thompson * usbd_req_get_config 189802ac6454SAndrew Thompson * 189902ac6454SAndrew Thompson * Returns: 190002ac6454SAndrew Thompson * 0: Success 190102ac6454SAndrew Thompson * Else: Failure 190202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1903e0a69b51SAndrew Thompson usb_error_t 1904a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 190502ac6454SAndrew Thompson { 1906760bc48eSAndrew Thompson struct usb_device_request req; 190702ac6454SAndrew Thompson 190802ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 190902ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 191002ac6454SAndrew Thompson USETW(req.wValue, 0); 191102ac6454SAndrew Thompson USETW(req.wIndex, 0); 191202ac6454SAndrew Thompson USETW(req.wLength, 1); 1913a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 191402ac6454SAndrew Thompson } 191502ac6454SAndrew Thompson 191602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1917963169b4SHans Petter Selasky * usbd_setup_device_desc 1918963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1919963169b4SHans Petter Selasky usb_error_t 1920963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) 1921963169b4SHans Petter Selasky { 1922963169b4SHans Petter Selasky usb_error_t err; 1923963169b4SHans Petter Selasky 1924963169b4SHans Petter Selasky /* 1925963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor ! 1926963169b4SHans Petter Selasky * 1927963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor 1928963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size 1929963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor 1930963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint 1931963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new 1932963169b4SHans Petter Selasky * USB control request will be setup! 1933963169b4SHans Petter Selasky */ 1934963169b4SHans Petter Selasky switch (udev->speed) { 1935963169b4SHans Petter Selasky case USB_SPEED_FULL: 193623de050bSHans Petter Selasky if (usb_full_ddesc != 0) { 193723de050bSHans Petter Selasky /* get full device descriptor */ 193823de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 193923de050bSHans Petter Selasky if (err == 0) 194023de050bSHans Petter Selasky break; 194123de050bSHans Petter Selasky } 194223de050bSHans Petter Selasky 194323de050bSHans Petter Selasky /* get partial device descriptor, some devices crash on this */ 1944963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 1945963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 194623de050bSHans Petter Selasky if (err != 0) 1947963169b4SHans Petter Selasky break; 194823de050bSHans Petter Selasky 194923de050bSHans Petter Selasky /* get the full device descriptor */ 195023de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 195123de050bSHans Petter Selasky break; 195223de050bSHans Petter Selasky 1953963169b4SHans Petter Selasky default: 1954a1a10f53SHans Petter Selasky DPRINTF("Minimum bMaxPacketSize is large enough " 195523de050bSHans Petter Selasky "to hold the complete device descriptor or " 1956a1a10f53SHans Petter Selasky "only one bMaxPacketSize choice\n"); 1957963169b4SHans Petter Selasky 1958963169b4SHans Petter Selasky /* get the full device descriptor */ 1959963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 1960963169b4SHans Petter Selasky 1961963169b4SHans Petter Selasky /* try one more time, if error */ 196223de050bSHans Petter Selasky if (err != 0) 1963963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 196423de050bSHans Petter Selasky break; 196523de050bSHans Petter Selasky } 1966963169b4SHans Petter Selasky 196723de050bSHans Petter Selasky if (err != 0) { 196823de050bSHans Petter Selasky DPRINTFN(0, "getting device descriptor " 196923de050bSHans Petter Selasky "at addr %d failed, %s\n", udev->address, 197023de050bSHans Petter Selasky usbd_errstr(err)); 1971963169b4SHans Petter Selasky return (err); 1972963169b4SHans Petter Selasky } 1973963169b4SHans Petter Selasky 1974963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " 1975963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", 1976963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB), 1977963169b4SHans Petter Selasky udev->ddesc.bDeviceClass, 1978963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass, 1979963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol, 1980963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize, 1981963169b4SHans Petter Selasky udev->ddesc.bLength, 1982963169b4SHans Petter Selasky udev->speed); 1983963169b4SHans Petter Selasky 1984963169b4SHans Petter Selasky return (err); 1985963169b4SHans Petter Selasky } 1986963169b4SHans Petter Selasky 1987963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1988a593f6b8SAndrew Thompson * usbd_req_re_enumerate 198902ac6454SAndrew Thompson * 199002ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 199102ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 199202ac6454SAndrew Thompson * new configuration. 199302ac6454SAndrew Thompson * 199402ac6454SAndrew Thompson * Returns: 199502ac6454SAndrew Thompson * 0: Success 199602ac6454SAndrew Thompson * Else: Failure 199702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1998e0a69b51SAndrew Thompson usb_error_t 1999a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 200002ac6454SAndrew Thompson { 2001760bc48eSAndrew Thompson struct usb_device *parent_hub; 2002e0a69b51SAndrew Thompson usb_error_t err; 200302ac6454SAndrew Thompson uint8_t old_addr; 200402ac6454SAndrew Thompson uint8_t do_retry = 1; 200502ac6454SAndrew Thompson 2006f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 200702ac6454SAndrew Thompson return (USB_ERR_INVAL); 200802ac6454SAndrew Thompson } 200902ac6454SAndrew Thompson old_addr = udev->address; 201002ac6454SAndrew Thompson parent_hub = udev->parent_hub; 201102ac6454SAndrew Thompson if (parent_hub == NULL) { 201202ac6454SAndrew Thompson return (USB_ERR_INVAL); 201302ac6454SAndrew Thompson } 201402ac6454SAndrew Thompson retry: 2015a0d53e0bSHans Petter Selasky #if USB_HAVE_TT_SUPPORT 20169eb0d702SHans Petter Selasky /* 20179eb0d702SHans Petter Selasky * Try to reset the High Speed parent HUB of a LOW- or FULL- 20189eb0d702SHans Petter Selasky * speed device, if any. 20199eb0d702SHans Petter Selasky */ 20209eb0d702SHans Petter Selasky if (udev->parent_hs_hub != NULL && 20219eb0d702SHans Petter Selasky udev->speed != USB_SPEED_HIGH) { 20229eb0d702SHans Petter Selasky DPRINTF("Trying to reset parent High Speed TT.\n"); 2023a0d53e0bSHans Petter Selasky if (udev->parent_hs_hub == parent_hub && 2024a0d53e0bSHans Petter Selasky (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) + 2025a0d53e0bSHans Petter Selasky uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) { 2026a0d53e0bSHans Petter Selasky /* we can reset the whole TT */ 2027a0d53e0bSHans Petter Selasky err = usbd_req_reset_tt(parent_hub, NULL, 20289eb0d702SHans Petter Selasky udev->hs_port_no); 2029a0d53e0bSHans Petter Selasky } else { 2030a0d53e0bSHans Petter Selasky /* only reset a particular device and endpoint */ 2031a0d53e0bSHans Petter Selasky err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL, 2032a0d53e0bSHans Petter Selasky udev->hs_port_no, old_addr, UE_CONTROL, 0); 2033a0d53e0bSHans Petter Selasky } 20349eb0d702SHans Petter Selasky if (err) { 20359eb0d702SHans Petter Selasky DPRINTF("Resetting parent High " 20369eb0d702SHans Petter Selasky "Speed TT failed (%s).\n", 20379eb0d702SHans Petter Selasky usbd_errstr(err)); 20389eb0d702SHans Petter Selasky } 20399eb0d702SHans Petter Selasky } 2040a0d53e0bSHans Petter Selasky #endif 204193ee6e85SHans Petter Selasky /* Try to warm reset first */ 204293ee6e85SHans Petter Selasky if (parent_hub->speed == USB_SPEED_SUPER) 204393ee6e85SHans Petter Selasky usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); 204493ee6e85SHans Petter Selasky 20459eb0d702SHans Petter Selasky /* Try to reset the parent HUB port. */ 2046a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 204702ac6454SAndrew Thompson if (err) { 204803797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 2049a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 205002ac6454SAndrew Thompson goto done; 205102ac6454SAndrew Thompson } 2052963169b4SHans Petter Selasky 205302ac6454SAndrew Thompson /* 205402ac6454SAndrew Thompson * After that the port has been reset our device should be at 205502ac6454SAndrew Thompson * address zero: 205602ac6454SAndrew Thompson */ 205702ac6454SAndrew Thompson udev->address = USB_START_ADDR; 205802ac6454SAndrew Thompson 205902ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 206002ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 206102ac6454SAndrew Thompson 2062963169b4SHans Petter Selasky /* reset USB state */ 2063963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED); 2064963169b4SHans Petter Selasky 206502ac6454SAndrew Thompson /* 206602ac6454SAndrew Thompson * Restore device address: 206702ac6454SAndrew Thompson */ 2068a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 206902ac6454SAndrew Thompson if (err) { 207002ac6454SAndrew Thompson /* XXX ignore any errors! */ 207103797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 2072a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 207302ac6454SAndrew Thompson } 2074963169b4SHans Petter Selasky /* 2075963169b4SHans Petter Selasky * Restore device address, if the controller driver did not 2076963169b4SHans Petter Selasky * set a new one: 2077963169b4SHans Petter Selasky */ 2078963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 207902ac6454SAndrew Thompson udev->address = old_addr; 208002ac6454SAndrew Thompson 2081963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */ 2082963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx); 208302ac6454SAndrew Thompson 208402ac6454SAndrew Thompson done: 208502ac6454SAndrew Thompson if (err && do_retry) { 208602ac6454SAndrew Thompson /* give the USB firmware some time to load */ 2087a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 208802ac6454SAndrew Thompson /* no more retries after this retry */ 208902ac6454SAndrew Thompson do_retry = 0; 209002ac6454SAndrew Thompson /* try again */ 209102ac6454SAndrew Thompson goto retry; 209202ac6454SAndrew Thompson } 209302ac6454SAndrew Thompson /* restore address */ 2094963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 209502ac6454SAndrew Thompson udev->address = old_addr; 2096963169b4SHans Petter Selasky /* update state, if successful */ 2097963169b4SHans Petter Selasky if (err == 0) 2098963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED); 209902ac6454SAndrew Thompson return (err); 210002ac6454SAndrew Thompson } 210102ac6454SAndrew Thompson 210202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2103a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 210402ac6454SAndrew Thompson * 210502ac6454SAndrew Thompson * Returns: 210602ac6454SAndrew Thompson * 0: Success 210702ac6454SAndrew Thompson * Else: Failure 210802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2109e0a69b51SAndrew Thompson usb_error_t 2110a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 211102ac6454SAndrew Thompson uint16_t sel) 211202ac6454SAndrew Thompson { 2113760bc48eSAndrew Thompson struct usb_device_request req; 211402ac6454SAndrew Thompson 211502ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 211602ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 211702ac6454SAndrew Thompson USETW(req.wValue, sel); 211802ac6454SAndrew Thompson USETW(req.wIndex, 0); 211902ac6454SAndrew Thompson USETW(req.wLength, 0); 2120a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 212102ac6454SAndrew Thompson } 212202ac6454SAndrew Thompson 212302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2124a593f6b8SAndrew Thompson * usbd_req_set_device_feature 212502ac6454SAndrew Thompson * 212602ac6454SAndrew Thompson * Returns: 212702ac6454SAndrew Thompson * 0: Success 212802ac6454SAndrew Thompson * Else: Failure 212902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2130e0a69b51SAndrew Thompson usb_error_t 2131a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 213202ac6454SAndrew Thompson uint16_t sel) 213302ac6454SAndrew Thompson { 2134760bc48eSAndrew Thompson struct usb_device_request req; 213502ac6454SAndrew Thompson 213602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 213702ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 213802ac6454SAndrew Thompson USETW(req.wValue, sel); 213902ac6454SAndrew Thompson USETW(req.wIndex, 0); 214002ac6454SAndrew Thompson USETW(req.wLength, 0); 2141a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 214202ac6454SAndrew Thompson } 21439eb0d702SHans Petter Selasky 21449eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 21459eb0d702SHans Petter Selasky * usbd_req_reset_tt 21469eb0d702SHans Petter Selasky * 21479eb0d702SHans Petter Selasky * Returns: 21489eb0d702SHans Petter Selasky * 0: Success 21499eb0d702SHans Petter Selasky * Else: Failure 21509eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 21519eb0d702SHans Petter Selasky usb_error_t 21529eb0d702SHans Petter Selasky usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, 21539eb0d702SHans Petter Selasky uint8_t port) 21549eb0d702SHans Petter Selasky { 21559eb0d702SHans Petter Selasky struct usb_device_request req; 21569eb0d702SHans Petter Selasky 21579eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 21589eb0d702SHans Petter Selasky 21599eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 21609eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 21619eb0d702SHans Petter Selasky port = 1; 21629eb0d702SHans Petter Selasky 21639eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 21649eb0d702SHans Petter Selasky req.bRequest = UR_RESET_TT; 21659eb0d702SHans Petter Selasky USETW(req.wValue, 0); 21669eb0d702SHans Petter Selasky req.wIndex[0] = port; 21679eb0d702SHans Petter Selasky req.wIndex[1] = 0; 21689eb0d702SHans Petter Selasky USETW(req.wLength, 0); 21699eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 21709eb0d702SHans Petter Selasky } 21719eb0d702SHans Petter Selasky 21729eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 21739eb0d702SHans Petter Selasky * usbd_req_clear_tt_buffer 21749eb0d702SHans Petter Selasky * 21759eb0d702SHans Petter Selasky * For single TT HUBs the port should be 1. 21769eb0d702SHans Petter Selasky * 21779eb0d702SHans Petter Selasky * Returns: 21789eb0d702SHans Petter Selasky * 0: Success 21799eb0d702SHans Petter Selasky * Else: Failure 21809eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 21819eb0d702SHans Petter Selasky usb_error_t 21829eb0d702SHans Petter Selasky usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, 21839eb0d702SHans Petter Selasky uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) 21849eb0d702SHans Petter Selasky { 21859eb0d702SHans Petter Selasky struct usb_device_request req; 21869eb0d702SHans Petter Selasky uint16_t wValue; 21879eb0d702SHans Petter Selasky 21889eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 21899eb0d702SHans Petter Selasky 21909eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 21919eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 21929eb0d702SHans Petter Selasky port = 1; 21939eb0d702SHans Petter Selasky 21949eb0d702SHans Petter Selasky wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | 21959eb0d702SHans Petter Selasky ((endpoint & 0x80) << 8) | ((type & 3) << 12); 21969eb0d702SHans Petter Selasky 21979eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 21989eb0d702SHans Petter Selasky req.bRequest = UR_CLEAR_TT_BUFFER; 21999eb0d702SHans Petter Selasky USETW(req.wValue, wValue); 22009eb0d702SHans Petter Selasky req.wIndex[0] = port; 22019eb0d702SHans Petter Selasky req.wIndex[1] = 0; 22029eb0d702SHans Petter Selasky USETW(req.wLength, 0); 22039eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 22049eb0d702SHans Petter Selasky } 22054131f6fbSHans Petter Selasky 22064131f6fbSHans Petter Selasky /*------------------------------------------------------------------------* 22074131f6fbSHans Petter Selasky * usbd_req_set_port_link_state 22084131f6fbSHans Petter Selasky * 22094131f6fbSHans Petter Selasky * USB 3.0 specific request 22104131f6fbSHans Petter Selasky * 22114131f6fbSHans Petter Selasky * Returns: 22124131f6fbSHans Petter Selasky * 0: Success 22134131f6fbSHans Petter Selasky * Else: Failure 22144131f6fbSHans Petter Selasky *------------------------------------------------------------------------*/ 22154131f6fbSHans Petter Selasky usb_error_t 22164131f6fbSHans Petter Selasky usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, 22174131f6fbSHans Petter Selasky uint8_t port, uint8_t link_state) 22184131f6fbSHans Petter Selasky { 22194131f6fbSHans Petter Selasky struct usb_device_request req; 22204131f6fbSHans Petter Selasky 22214131f6fbSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 22224131f6fbSHans Petter Selasky req.bRequest = UR_SET_FEATURE; 22234131f6fbSHans Petter Selasky USETW(req.wValue, UHF_PORT_LINK_STATE); 22244131f6fbSHans Petter Selasky req.wIndex[0] = port; 22254131f6fbSHans Petter Selasky req.wIndex[1] = link_state; 22264131f6fbSHans Petter Selasky USETW(req.wLength, 0); 22274131f6fbSHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 22284131f6fbSHans Petter Selasky } 22294563ba7aSHans Petter Selasky 22304563ba7aSHans Petter Selasky /*------------------------------------------------------------------------* 22314563ba7aSHans Petter Selasky * usbd_req_set_lpm_info 22324563ba7aSHans Petter Selasky * 22334563ba7aSHans Petter Selasky * USB 2.0 specific request for Link Power Management. 22344563ba7aSHans Petter Selasky * 22354563ba7aSHans Petter Selasky * Returns: 22364563ba7aSHans Petter Selasky * 0: Success 22374563ba7aSHans Petter Selasky * USB_ERR_PENDING_REQUESTS: NYET 22384563ba7aSHans Petter Selasky * USB_ERR_TIMEOUT: TIMEOUT 22394563ba7aSHans Petter Selasky * USB_ERR_STALL: STALL 22404563ba7aSHans Petter Selasky * Else: Failure 22414563ba7aSHans Petter Selasky *------------------------------------------------------------------------*/ 22424563ba7aSHans Petter Selasky usb_error_t 22434563ba7aSHans Petter Selasky usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx, 22444563ba7aSHans Petter Selasky uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe) 22454563ba7aSHans Petter Selasky { 22464563ba7aSHans Petter Selasky struct usb_device_request req; 22474563ba7aSHans Petter Selasky usb_error_t err; 22484563ba7aSHans Petter Selasky uint8_t buf[1]; 22494563ba7aSHans Petter Selasky 22504563ba7aSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 22514563ba7aSHans Petter Selasky req.bRequest = UR_SET_AND_TEST; 22524563ba7aSHans Petter Selasky USETW(req.wValue, UHF_PORT_L1); 22534563ba7aSHans Petter Selasky req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4); 22544563ba7aSHans Petter Selasky req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00); 22554563ba7aSHans Petter Selasky USETW(req.wLength, sizeof(buf)); 22564563ba7aSHans Petter Selasky 22574563ba7aSHans Petter Selasky /* set default value in case of short transfer */ 22584563ba7aSHans Petter Selasky buf[0] = 0x00; 22594563ba7aSHans Petter Selasky 22604563ba7aSHans Petter Selasky err = usbd_do_request(udev, mtx, &req, buf); 22614563ba7aSHans Petter Selasky if (err) 22624563ba7aSHans Petter Selasky return (err); 22634563ba7aSHans Petter Selasky 22644563ba7aSHans Petter Selasky switch (buf[0]) { 22654563ba7aSHans Petter Selasky case 0x00: /* SUCCESS */ 22664563ba7aSHans Petter Selasky break; 22674563ba7aSHans Petter Selasky case 0x10: /* NYET */ 22684563ba7aSHans Petter Selasky err = USB_ERR_PENDING_REQUESTS; 22694563ba7aSHans Petter Selasky break; 22704563ba7aSHans Petter Selasky case 0x11: /* TIMEOUT */ 22714563ba7aSHans Petter Selasky err = USB_ERR_TIMEOUT; 22724563ba7aSHans Petter Selasky break; 22734563ba7aSHans Petter Selasky case 0x30: /* STALL */ 22744563ba7aSHans Petter Selasky err = USB_ERR_STALLED; 22754563ba7aSHans Petter Selasky break; 22764563ba7aSHans Petter Selasky default: /* reserved */ 22774563ba7aSHans Petter Selasky err = USB_ERR_IOERROR; 22784563ba7aSHans Petter Selasky break; 22794563ba7aSHans Petter Selasky } 22804563ba7aSHans Petter Selasky return (err); 22814563ba7aSHans Petter Selasky } 22824563ba7aSHans Petter Selasky 2283