102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 4718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 602ac6454SAndrew Thompson * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 7b8b3f4fdSHans Petter Selasky * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved. 802ac6454SAndrew Thompson * 902ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 1002ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 1102ac6454SAndrew Thompson * are met: 1202ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1402ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1502ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1602ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1702ac6454SAndrew Thompson * 1802ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1902ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2002ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2102ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2202ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2302ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2402ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2502ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2602ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2702ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2802ac6454SAndrew Thompson * SUCH DAMAGE. 2902ac6454SAndrew Thompson */ 3002ac6454SAndrew Thompson 31d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 32d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 33d2b99310SHans Petter Selasky #else 34ed6d949aSAndrew Thompson #include <sys/stdint.h> 35ed6d949aSAndrew Thompson #include <sys/stddef.h> 36ed6d949aSAndrew Thompson #include <sys/param.h> 37ed6d949aSAndrew Thompson #include <sys/queue.h> 38ed6d949aSAndrew Thompson #include <sys/types.h> 39ed6d949aSAndrew Thompson #include <sys/systm.h> 40ed6d949aSAndrew Thompson #include <sys/kernel.h> 41ed6d949aSAndrew Thompson #include <sys/bus.h> 42ed6d949aSAndrew Thompson #include <sys/module.h> 43ed6d949aSAndrew Thompson #include <sys/lock.h> 44ed6d949aSAndrew Thompson #include <sys/mutex.h> 45ed6d949aSAndrew Thompson #include <sys/condvar.h> 46ed6d949aSAndrew Thompson #include <sys/sysctl.h> 47ed6d949aSAndrew Thompson #include <sys/sx.h> 48ed6d949aSAndrew Thompson #include <sys/unistd.h> 49ed6d949aSAndrew Thompson #include <sys/callout.h> 50ed6d949aSAndrew Thompson #include <sys/malloc.h> 51ed6d949aSAndrew Thompson #include <sys/priv.h> 52ed6d949aSAndrew Thompson 5302ac6454SAndrew Thompson #include <dev/usb/usb.h> 54ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 55ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5602ac6454SAndrew Thompson #include <dev/usb/usbhid.h> 5702ac6454SAndrew Thompson 58a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5902ac6454SAndrew Thompson 6002ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6802ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6902ac6454SAndrew Thompson 7002ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 7102ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7202ac6454SAndrew Thompson #include <sys/ctype.h> 73d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 7402ac6454SAndrew Thompson 759465dbebSHans Petter Selasky static int usb_no_cs_fail; 769465dbebSHans Petter Selasky 77ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN, 789465dbebSHans Petter Selasky &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); 799465dbebSHans Petter Selasky 8023de050bSHans Petter Selasky static int usb_full_ddesc; 8123de050bSHans Petter Selasky 82ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN, 8323de050bSHans Petter Selasky &usb_full_ddesc, 0, "USB always read complete device descriptor, if set"); 8423de050bSHans Petter Selasky 85b850ecc1SAndrew Thompson #ifdef USB_DEBUG 86f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 87f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */ 88f6980be8SAndrew Thompson struct usb_ctrl_debug { 89f6980be8SAndrew Thompson int bus_index; /* target bus */ 90f6980be8SAndrew Thompson int dev_index; /* target address */ 91f6980be8SAndrew Thompson int ds_fail; /* fail data stage */ 922a4e4c67SHans Petter Selasky int ss_fail; /* fail status stage */ 93f6980be8SAndrew Thompson int ds_delay; /* data stage delay in ms */ 94f6980be8SAndrew Thompson int ss_delay; /* status stage delay in ms */ 95f6980be8SAndrew Thompson int bmRequestType_value; 96f6980be8SAndrew Thompson int bRequest_value; 97f6980be8SAndrew Thompson }; 98f6980be8SAndrew Thompson 99f6980be8SAndrew Thompson struct usb_ctrl_debug_bits { 100f6980be8SAndrew Thompson uint16_t ds_delay; 101f6980be8SAndrew Thompson uint16_t ss_delay; 102f6980be8SAndrew Thompson uint8_t ds_fail:1; 103f6980be8SAndrew Thompson uint8_t ss_fail:1; 104f6980be8SAndrew Thompson uint8_t enabled:1; 105f6980be8SAndrew Thompson }; 106f6980be8SAndrew Thompson 107f6980be8SAndrew Thompson /* The default is to disable fault injection. */ 108f6980be8SAndrew Thompson 109f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = { 110f6980be8SAndrew Thompson .bus_index = -1, 111f6980be8SAndrew Thompson .dev_index = -1, 112f6980be8SAndrew Thompson .bmRequestType_value = -1, 113f6980be8SAndrew Thompson .bRequest_value = -1, 114f6980be8SAndrew Thompson }; 115f6980be8SAndrew Thompson 116ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN, 117f6980be8SAndrew Thompson &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); 118ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN, 119f6980be8SAndrew Thompson &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); 120ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN, 121f6980be8SAndrew Thompson &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); 122ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN, 123f6980be8SAndrew Thompson &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); 124ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN, 125f6980be8SAndrew Thompson &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); 126ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN, 127f6980be8SAndrew Thompson &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); 128ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN, 129f6980be8SAndrew Thompson &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); 130ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN, 131f6980be8SAndrew Thompson &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); 132f6980be8SAndrew Thompson 133f6980be8SAndrew Thompson /*------------------------------------------------------------------------* 134f6980be8SAndrew Thompson * usbd_get_debug_bits 135f6980be8SAndrew Thompson * 136f6980be8SAndrew Thompson * This function is only useful in USB host mode. 137f6980be8SAndrew Thompson *------------------------------------------------------------------------*/ 138f6980be8SAndrew Thompson static void 139f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, 140f6980be8SAndrew Thompson struct usb_ctrl_debug_bits *dbg) 141f6980be8SAndrew Thompson { 142f6980be8SAndrew Thompson int temp; 143f6980be8SAndrew Thompson 144f6980be8SAndrew Thompson memset(dbg, 0, sizeof(*dbg)); 145f6980be8SAndrew Thompson 146f6980be8SAndrew Thompson /* Compute data stage delay */ 147f6980be8SAndrew Thompson 148f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_delay; 149f6980be8SAndrew Thompson if (temp < 0) 150f6980be8SAndrew Thompson temp = 0; 151f6980be8SAndrew Thompson else if (temp > (16*1024)) 152f6980be8SAndrew Thompson temp = (16*1024); 153f6980be8SAndrew Thompson 154f6980be8SAndrew Thompson dbg->ds_delay = temp; 155f6980be8SAndrew Thompson 156f6980be8SAndrew Thompson /* Compute status stage delay */ 157f6980be8SAndrew Thompson 158f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_delay; 159f6980be8SAndrew Thompson if (temp < 0) 160f6980be8SAndrew Thompson temp = 0; 161f6980be8SAndrew Thompson else if (temp > (16*1024)) 162f6980be8SAndrew Thompson temp = (16*1024); 163f6980be8SAndrew Thompson 164f6980be8SAndrew Thompson dbg->ss_delay = temp; 165f6980be8SAndrew Thompson 166f6980be8SAndrew Thompson /* Check if this control request should be failed */ 167f6980be8SAndrew Thompson 168f6980be8SAndrew Thompson if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) 169f6980be8SAndrew Thompson return; 170f6980be8SAndrew Thompson 171f6980be8SAndrew Thompson if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) 172f6980be8SAndrew Thompson return; 173f6980be8SAndrew Thompson 174f6980be8SAndrew Thompson temp = usb_ctrl_debug.bmRequestType_value; 175f6980be8SAndrew Thompson 176f6980be8SAndrew Thompson if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) 177f6980be8SAndrew Thompson return; 178f6980be8SAndrew Thompson 179f6980be8SAndrew Thompson temp = usb_ctrl_debug.bRequest_value; 180f6980be8SAndrew Thompson 181f6980be8SAndrew Thompson if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) 182f6980be8SAndrew Thompson return; 183f6980be8SAndrew Thompson 184f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_fail; 185f6980be8SAndrew Thompson if (temp) 186f6980be8SAndrew Thompson dbg->ds_fail = 1; 187f6980be8SAndrew Thompson 188f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_fail; 189f6980be8SAndrew Thompson if (temp) 190f6980be8SAndrew Thompson dbg->ss_fail = 1; 191f6980be8SAndrew Thompson 192f6980be8SAndrew Thompson dbg->enabled = 1; 193f6980be8SAndrew Thompson } 194f6980be8SAndrew Thompson #endif /* USB_REQ_DEBUG */ 195f6980be8SAndrew Thompson #endif /* USB_DEBUG */ 19602ac6454SAndrew Thompson 19702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 198a593f6b8SAndrew Thompson * usbd_do_request_callback 19902ac6454SAndrew Thompson * 20002ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control 20102ac6454SAndrew Thompson * transfers. 20202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 20302ac6454SAndrew Thompson void 204ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) 20502ac6454SAndrew Thompson { 20602ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 20702ac6454SAndrew Thompson 20802ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer)); 20902ac6454SAndrew Thompson 21002ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 21102ac6454SAndrew Thompson case USB_ST_SETUP: 212a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 21302ac6454SAndrew Thompson break; 21402ac6454SAndrew Thompson default: 21591cd9240SAndrew Thompson cv_signal(&xfer->xroot->udev->ctrlreq_cv); 21602ac6454SAndrew Thompson break; 21702ac6454SAndrew Thompson } 21802ac6454SAndrew Thompson } 21902ac6454SAndrew Thompson 22002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 221a593f6b8SAndrew Thompson * usb_do_clear_stall_callback 22202ac6454SAndrew Thompson * 22302ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests. 22402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 22502ac6454SAndrew Thompson void 226ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 22702ac6454SAndrew Thompson { 228760bc48eSAndrew Thompson struct usb_device_request req; 229760bc48eSAndrew Thompson struct usb_device *udev; 230ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 231ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end; 232ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first; 233a5cf1aaaSHans Petter Selasky usb_stream_t x; 23463521bbcSAndrew Thompson uint8_t to; 23502ac6454SAndrew Thompson 23602ac6454SAndrew Thompson udev = xfer->xroot->udev; 23702ac6454SAndrew Thompson 23802ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 23902ac6454SAndrew Thompson 240ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */ 24102ac6454SAndrew Thompson 242ae60fdfbSAndrew Thompson ep = udev->ep_curr; 243ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max; 244ae60fdfbSAndrew Thompson ep_first = udev->endpoints; 245ae60fdfbSAndrew Thompson to = udev->endpoints_max; 246115df0b6SAndrew Thompson 24702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 24802ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 2499465dbebSHans Petter Selasky tr_transferred: 2509eb0d702SHans Petter Selasky /* reset error counter */ 2519eb0d702SHans Petter Selasky udev->clear_stall_errors = 0; 2529eb0d702SHans Petter Selasky 253ae60fdfbSAndrew Thompson if (ep == NULL) 254115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */ 255ae60fdfbSAndrew Thompson if (ep->edesc && 256ae60fdfbSAndrew Thompson ep->is_stalled) { 257ae60fdfbSAndrew Thompson ep->toggle_next = 0; 258ae60fdfbSAndrew Thompson ep->is_stalled = 0; 259963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */ 260963169b4SHans Petter Selasky usbd_clear_stall_locked(udev, ep); 261a5cf1aaaSHans Petter Selasky for (x = 0; x != USB_MAX_EP_STREAMS; x++) { 262a5cf1aaaSHans Petter Selasky /* start the current or next transfer, if any */ 263a5cf1aaaSHans Petter Selasky usb_command_wrapper(&ep->endpoint_q[x], 264a5cf1aaaSHans Petter Selasky ep->endpoint_q[x].curr); 265a5cf1aaaSHans Petter Selasky } 26602ac6454SAndrew Thompson } 267ae60fdfbSAndrew Thompson ep++; 26802ac6454SAndrew Thompson 26902ac6454SAndrew Thompson case USB_ST_SETUP: 27002ac6454SAndrew Thompson tr_setup: 271115df0b6SAndrew Thompson if (to == 0) 272ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */ 273ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end)) 274ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */ 275ae60fdfbSAndrew Thompson if (ep->edesc && 276ae60fdfbSAndrew Thompson ep->is_stalled) { 27702ac6454SAndrew Thompson /* setup a clear-stall packet */ 27802ac6454SAndrew Thompson 27902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 28002ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 28102ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 282ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 28302ac6454SAndrew Thompson req.wIndex[1] = 0; 28402ac6454SAndrew Thompson USETW(req.wLength, 0); 28502ac6454SAndrew Thompson 28602ac6454SAndrew Thompson /* copy in the transfer */ 28702ac6454SAndrew Thompson 288a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 28902ac6454SAndrew Thompson 29002ac6454SAndrew Thompson /* set length */ 291ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 29202ac6454SAndrew Thompson xfer->nframes = 1; 29302ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 29402ac6454SAndrew Thompson 295a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 29602ac6454SAndrew Thompson 29702ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 29802ac6454SAndrew Thompson break; 29902ac6454SAndrew Thompson } 300ae60fdfbSAndrew Thompson ep++; 301115df0b6SAndrew Thompson to--; 30202ac6454SAndrew Thompson goto tr_setup; 30302ac6454SAndrew Thompson 30402ac6454SAndrew Thompson default: 3059eb0d702SHans Petter Selasky if (error == USB_ERR_CANCELLED) 30602ac6454SAndrew Thompson break; 3079eb0d702SHans Petter Selasky 3089eb0d702SHans Petter Selasky DPRINTF("Clear stall failed.\n"); 3099465dbebSHans Petter Selasky 3109465dbebSHans Petter Selasky /* 3119465dbebSHans Petter Selasky * Some VMs like VirtualBox always return failure on 3129465dbebSHans Petter Selasky * clear-stall which we sometimes should just ignore. 3139465dbebSHans Petter Selasky */ 3149465dbebSHans Petter Selasky if (usb_no_cs_fail) 3159465dbebSHans Petter Selasky goto tr_transferred; 3164e2d8cd3SHans Petter Selasky 3174e2d8cd3SHans Petter Selasky /* 3184e2d8cd3SHans Petter Selasky * Some non-compliant USB devices do not implement the 3194e2d8cd3SHans Petter Selasky * clear endpoint halt feature. Silently ignore such 3204e2d8cd3SHans Petter Selasky * devices, when they at least respond correctly 3214e2d8cd3SHans Petter Selasky * passing up a valid STALL PID packet. 3224e2d8cd3SHans Petter Selasky */ 3234e2d8cd3SHans Petter Selasky if (error == USB_ERR_STALLED) 3244e2d8cd3SHans Petter Selasky goto tr_transferred; 3254e2d8cd3SHans Petter Selasky 3269eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) 3279eb0d702SHans Petter Selasky goto tr_setup; 3289eb0d702SHans Petter Selasky 3299eb0d702SHans Petter Selasky if (error == USB_ERR_TIMEOUT) { 3309eb0d702SHans Petter Selasky udev->clear_stall_errors = USB_CS_RESET_LIMIT; 3319eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3329eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3339eb0d702SHans Petter Selasky } else { 3349eb0d702SHans Petter Selasky udev->clear_stall_errors++; 3359eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { 3369eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n"); 3379eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 3389eb0d702SHans Petter Selasky } 33902ac6454SAndrew Thompson } 34002ac6454SAndrew Thompson goto tr_setup; 34102ac6454SAndrew Thompson } 34202ac6454SAndrew Thompson 343ae60fdfbSAndrew Thompson /* store current endpoint */ 344ae60fdfbSAndrew Thompson udev->ep_curr = ep; 34502ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 34602ac6454SAndrew Thompson } 34702ac6454SAndrew Thompson 348e0a69b51SAndrew Thompson static usb_handle_req_t * 349a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev) 350459d369eSAndrew Thompson { 351459d369eSAndrew Thompson /* figure out if there is a Handle Request function */ 352f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE) 353a593f6b8SAndrew Thompson return (usb_temp_get_desc_p); 354459d369eSAndrew Thompson else if (udev->parent_hub == NULL) 355459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec); 356459d369eSAndrew Thompson else 357459d369eSAndrew Thompson return (NULL); 358459d369eSAndrew Thompson } 359459d369eSAndrew Thompson 36002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 361a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request 36202ac6454SAndrew Thompson * 36302ac6454SAndrew Thompson * Description of arguments passed to these functions: 36402ac6454SAndrew Thompson * 365760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the 36602ac6454SAndrew Thompson * request should be performed. It is possible to call this function 36702ac6454SAndrew Thompson * in both Host Side mode and Device Side mode. 36802ac6454SAndrew Thompson * 36902ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it 37002ac6454SAndrew Thompson * will get dropped and picked up during the execution of this 37102ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this 37202ac6454SAndrew Thompson * argument is NULL it has no effect. 37302ac6454SAndrew Thompson * 37402ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an 37502ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB 37602ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB 37702ac6454SAndrew Thompson * request, if it is a read or a write. 37802ac6454SAndrew Thompson * 37902ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req" 38002ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which 38102ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can 38202ac6454SAndrew Thompson * be NULL. 38302ac6454SAndrew Thompson * 38402ac6454SAndrew Thompson * "flags" - here is a list of valid flags: 38502ac6454SAndrew Thompson * 38602ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than 38702ac6454SAndrew Thompson * specified 38802ac6454SAndrew Thompson * 38902ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed 39002ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay" 39102ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging. 39202ac6454SAndrew Thompson * 39302ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland 39402ac6454SAndrew Thompson * pointer. 39502ac6454SAndrew Thompson * 39602ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in 39702ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This 39802ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is 39902ac6454SAndrew Thompson * used. 40002ac6454SAndrew Thompson * 40102ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in 40202ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is 40302ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater 40402ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack 40502ac6454SAndrew Thompson * does not allow control requests without a timeout. 40602ac6454SAndrew Thompson * 407a18a7a41SHans Petter Selasky * NOTE: This function is thread safe. All calls to "usbd_do_request_flags" 408a18a7a41SHans Petter Selasky * will be serialized by the use of the USB device enumeration lock. 40902ac6454SAndrew Thompson * 41002ac6454SAndrew Thompson * Returns: 41102ac6454SAndrew Thompson * 0: Success 41202ac6454SAndrew Thompson * Else: Failure 41302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 414e0a69b51SAndrew Thompson usb_error_t 415a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, 416760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 417e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 41802ac6454SAndrew Thompson { 419f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 420f6980be8SAndrew Thompson struct usb_ctrl_debug_bits dbg; 421f6980be8SAndrew Thompson #endif 422e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 423760bc48eSAndrew Thompson struct usb_xfer *xfer; 42402ac6454SAndrew Thompson const void *desc; 42502ac6454SAndrew Thompson int err = 0; 426e0a69b51SAndrew Thompson usb_ticks_t start_ticks; 427e0a69b51SAndrew Thompson usb_ticks_t delta_ticks; 428e0a69b51SAndrew Thompson usb_ticks_t max_ticks; 42902ac6454SAndrew Thompson uint16_t length; 43002ac6454SAndrew Thompson uint16_t temp; 431f6980be8SAndrew Thompson uint16_t acttemp; 432a18a7a41SHans Petter Selasky uint8_t do_unlock; 43302ac6454SAndrew Thompson 43402ac6454SAndrew Thompson if (timeout < 50) { 43502ac6454SAndrew Thompson /* timeout is too small */ 43602ac6454SAndrew Thompson timeout = 50; 43702ac6454SAndrew Thompson } 43802ac6454SAndrew Thompson if (timeout > 30000) { 43902ac6454SAndrew Thompson /* timeout is too big */ 44002ac6454SAndrew Thompson timeout = 30000; 44102ac6454SAndrew Thompson } 44202ac6454SAndrew Thompson length = UGETW(req->wLength); 44302ac6454SAndrew Thompson 44402ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " 44502ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", 44602ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest, 44702ac6454SAndrew Thompson req->wValue[1], req->wValue[0], 44802ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0], 44902ac6454SAndrew Thompson req->wLength[1], req->wLength[0]); 45002ac6454SAndrew Thompson 451bd216778SAndrew Thompson /* Check if the device is still alive */ 452bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) { 453bd216778SAndrew Thompson DPRINTF("usb device has gone\n"); 454bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED); 455bd216778SAndrew Thompson } 456bd216778SAndrew Thompson 45702ac6454SAndrew Thompson /* 45802ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not 45902ac6454SAndrew Thompson * check the return value: 46002ac6454SAndrew Thompson */ 46139307315SAndrew Thompson if (actlen) 46202ac6454SAndrew Thompson *actlen = 0; 46339307315SAndrew Thompson 464bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0) 465bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR) 466bdc081c6SAndrew Thompson return (USB_ERR_INVAL); 467bdc081c6SAndrew Thompson #endif 4682df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) { 4690eb8d462SHans Petter Selasky USB_MTX_UNLOCK(mtx); 4700eb8d462SHans Petter Selasky USB_MTX_ASSERT(mtx, MA_NOTOWNED); 47102ac6454SAndrew Thompson } 4722df1e9a6SAndrew Thompson 4732df1e9a6SAndrew Thompson /* 47464cb5e2aSHans Petter Selasky * Serialize access to this function: 475a18a7a41SHans Petter Selasky */ 47664cb5e2aSHans Petter Selasky do_unlock = usbd_ctrl_lock(udev); 4772df1e9a6SAndrew Thompson 478a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 47939307315SAndrew Thompson 480459d369eSAndrew Thompson if (hr_func != NULL) { 481459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n"); 48239307315SAndrew Thompson 483459d369eSAndrew Thompson desc = NULL; 484459d369eSAndrew Thompson temp = 0; 485459d369eSAndrew Thompson 486459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 48739307315SAndrew Thompson if (length != 0) { 488459d369eSAndrew Thompson DPRINTFN(1, "The handle request function " 489459d369eSAndrew Thompson "does not support writing data!\n"); 49039307315SAndrew Thompson err = USB_ERR_INVAL; 49139307315SAndrew Thompson goto done; 49239307315SAndrew Thompson } 49339307315SAndrew Thompson } 494459d369eSAndrew Thompson 495459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */ 49639307315SAndrew Thompson 49739307315SAndrew Thompson USB_BUS_LOCK(udev->bus); 498459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp); 49939307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 50039307315SAndrew Thompson 50139307315SAndrew Thompson if (err) 50239307315SAndrew Thompson goto done; 50339307315SAndrew Thompson 504459d369eSAndrew Thompson if (length > temp) { 50539307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) { 50639307315SAndrew Thompson err = USB_ERR_SHORT_XFER; 50739307315SAndrew Thompson goto done; 50839307315SAndrew Thompson } 509459d369eSAndrew Thompson length = temp; 51039307315SAndrew Thompson } 51139307315SAndrew Thompson if (actlen) 51239307315SAndrew Thompson *actlen = length; 51339307315SAndrew Thompson 51439307315SAndrew Thompson if (length > 0) { 51539307315SAndrew Thompson #if USB_HAVE_USER_IO 51639307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 517459d369eSAndrew Thompson if (copyout(desc, data, length)) { 51839307315SAndrew Thompson err = USB_ERR_INVAL; 51939307315SAndrew Thompson goto done; 52039307315SAndrew Thompson } 52139307315SAndrew Thompson } else 52239307315SAndrew Thompson #endif 523271ae033SHans Petter Selasky memcpy(data, desc, length); 52439307315SAndrew Thompson } 525459d369eSAndrew Thompson goto done; /* success */ 52639307315SAndrew Thompson } 52739307315SAndrew Thompson 52802ac6454SAndrew Thompson /* 52902ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any: 53002ac6454SAndrew Thompson */ 5315b3bb704SAndrew Thompson usbd_ctrl_transfer_setup(udev); 53202ac6454SAndrew Thompson 5335b3bb704SAndrew Thompson xfer = udev->ctrl_xfer[0]; 53402ac6454SAndrew Thompson if (xfer == NULL) { 53502ac6454SAndrew Thompson /* most likely out of memory */ 53602ac6454SAndrew Thompson err = USB_ERR_NOMEM; 53702ac6454SAndrew Thompson goto done; 53802ac6454SAndrew Thompson } 539f6980be8SAndrew Thompson 540f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 541f6980be8SAndrew Thompson /* Get debug bits */ 542f6980be8SAndrew Thompson usbd_get_debug_bits(udev, req, &dbg); 543f6980be8SAndrew Thompson 544f6980be8SAndrew Thompson /* Check for fault injection */ 545f6980be8SAndrew Thompson if (dbg.enabled) 546f6980be8SAndrew Thompson flags |= USB_DELAY_STATUS_STAGE; 547f6980be8SAndrew Thompson #endif 54802ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 54902ac6454SAndrew Thompson 5504eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE) 55102ac6454SAndrew Thompson xfer->flags.manual_status = 1; 5524eae601eSAndrew Thompson else 55302ac6454SAndrew Thompson xfer->flags.manual_status = 0; 5544eae601eSAndrew Thompson 5554eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK) 5564eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1; 5574eae601eSAndrew Thompson else 5584eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0; 55902ac6454SAndrew Thompson 56002ac6454SAndrew Thompson xfer->timeout = timeout; 56102ac6454SAndrew Thompson 56202ac6454SAndrew Thompson start_ticks = ticks; 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout); 56502ac6454SAndrew Thompson 566a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); 56702ac6454SAndrew Thompson 568ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); 56902ac6454SAndrew Thompson 57002ac6454SAndrew Thompson while (1) { 57102ac6454SAndrew Thompson temp = length; 572f6980be8SAndrew Thompson if (temp > usbd_xfer_max_len(xfer)) { 573ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer); 57402ac6454SAndrew Thompson } 575f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 576f6980be8SAndrew Thompson if (xfer->flags.manual_status) { 577f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) != 0) { 578f6980be8SAndrew Thompson /* Execute data stage separately */ 579f6980be8SAndrew Thompson temp = 0; 580f6980be8SAndrew Thompson } else if (temp > 0) { 581f6980be8SAndrew Thompson if (dbg.ds_fail) { 582f6980be8SAndrew Thompson err = USB_ERR_INVAL; 583f6980be8SAndrew Thompson break; 584f6980be8SAndrew Thompson } 585f6980be8SAndrew Thompson if (dbg.ds_delay > 0) { 586f6980be8SAndrew Thompson usb_pause_mtx( 587f6980be8SAndrew Thompson xfer->xroot->xfer_mtx, 588f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ds_delay)); 589f6980be8SAndrew Thompson /* make sure we don't time out */ 590f6980be8SAndrew Thompson start_ticks = ticks; 591f6980be8SAndrew Thompson } 592f6980be8SAndrew Thompson } 593f6980be8SAndrew Thompson } 594f6980be8SAndrew Thompson #endif 595ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp); 59602ac6454SAndrew Thompson 59702ac6454SAndrew Thompson if (temp > 0) { 59802ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) { 599bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 60002ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 60102ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 602a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1, 60302ac6454SAndrew Thompson 0, data, temp); 60402ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 60502ac6454SAndrew Thompson if (err) { 60602ac6454SAndrew Thompson err = USB_ERR_INVAL; 60702ac6454SAndrew Thompson break; 60802ac6454SAndrew Thompson } 609bdc081c6SAndrew Thompson } else 610bdc081c6SAndrew Thompson #endif 611a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 612bdc081c6SAndrew Thompson 0, data, temp); 61302ac6454SAndrew Thompson } 614f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 2); 61502ac6454SAndrew Thompson } else { 616f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) == 0) { 61702ac6454SAndrew Thompson if (xfer->flags.manual_status) { 618f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG 619f6980be8SAndrew Thompson if (dbg.ss_fail) { 620f6980be8SAndrew Thompson err = USB_ERR_INVAL; 621f6980be8SAndrew Thompson break; 62202ac6454SAndrew Thompson } 623f6980be8SAndrew Thompson if (dbg.ss_delay > 0) { 624a593f6b8SAndrew Thompson usb_pause_mtx( 62502ac6454SAndrew Thompson xfer->xroot->xfer_mtx, 626f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ss_delay)); 627f6980be8SAndrew Thompson /* make sure we don't time out */ 628f6980be8SAndrew Thompson start_ticks = ticks; 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson #endif 63102ac6454SAndrew Thompson xfer->flags.manual_status = 0; 63202ac6454SAndrew Thompson } else { 63302ac6454SAndrew Thompson break; 63402ac6454SAndrew Thompson } 63502ac6454SAndrew Thompson } 636f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 1); 63702ac6454SAndrew Thompson } 63802ac6454SAndrew Thompson 639a593f6b8SAndrew Thompson usbd_transfer_start(xfer); 64002ac6454SAndrew Thompson 641a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) { 64291cd9240SAndrew Thompson cv_wait(&udev->ctrlreq_cv, 64302ac6454SAndrew Thompson xfer->xroot->xfer_mtx); 64402ac6454SAndrew Thompson } 64502ac6454SAndrew Thompson 64602ac6454SAndrew Thompson err = xfer->error; 64702ac6454SAndrew Thompson 64802ac6454SAndrew Thompson if (err) { 64902ac6454SAndrew Thompson break; 65002ac6454SAndrew Thompson } 65102ac6454SAndrew Thompson 652f6980be8SAndrew Thompson /* get actual length of DATA stage */ 653f6980be8SAndrew Thompson 654f6980be8SAndrew Thompson if (xfer->aframes < 2) { 655f6980be8SAndrew Thompson acttemp = 0; 65602ac6454SAndrew Thompson } else { 657f6980be8SAndrew Thompson acttemp = usbd_xfer_frame_len(xfer, 1); 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson 66002ac6454SAndrew Thompson /* check for short packet */ 66102ac6454SAndrew Thompson 662f6980be8SAndrew Thompson if (temp > acttemp) { 663f6980be8SAndrew Thompson temp = acttemp; 66402ac6454SAndrew Thompson length = temp; 66502ac6454SAndrew Thompson } 66602ac6454SAndrew Thompson if (temp > 0) { 66702ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) { 668bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 66902ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) { 67002ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 671a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1, 67202ac6454SAndrew Thompson 0, data, temp); 67302ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 67402ac6454SAndrew Thompson if (err) { 67502ac6454SAndrew Thompson err = USB_ERR_INVAL; 67602ac6454SAndrew Thompson break; 67702ac6454SAndrew Thompson } 678bdc081c6SAndrew Thompson } else 679bdc081c6SAndrew Thompson #endif 680a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1, 68102ac6454SAndrew Thompson 0, data, temp); 68202ac6454SAndrew Thompson } 68302ac6454SAndrew Thompson } 68402ac6454SAndrew Thompson /* 68502ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup 68602ac6454SAndrew Thompson * packet again: 68702ac6454SAndrew Thompson */ 688ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 68902ac6454SAndrew Thompson 69002ac6454SAndrew Thompson /* update length and data pointer */ 69102ac6454SAndrew Thompson length -= temp; 69202ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp); 69302ac6454SAndrew Thompson 69402ac6454SAndrew Thompson if (actlen) { 69502ac6454SAndrew Thompson (*actlen) += temp; 69602ac6454SAndrew Thompson } 69702ac6454SAndrew Thompson /* check for timeout */ 69802ac6454SAndrew Thompson 69902ac6454SAndrew Thompson delta_ticks = ticks - start_ticks; 70002ac6454SAndrew Thompson if (delta_ticks > max_ticks) { 70102ac6454SAndrew Thompson if (!err) { 70202ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 70302ac6454SAndrew Thompson } 70402ac6454SAndrew Thompson } 70502ac6454SAndrew Thompson if (err) { 70602ac6454SAndrew Thompson break; 70702ac6454SAndrew Thompson } 70802ac6454SAndrew Thompson } 70902ac6454SAndrew Thompson 71002ac6454SAndrew Thompson if (err) { 71102ac6454SAndrew Thompson /* 71202ac6454SAndrew Thompson * Make sure that the control endpoint is no longer 71302ac6454SAndrew Thompson * blocked in case of a non-transfer related error: 71402ac6454SAndrew Thompson */ 715a593f6b8SAndrew Thompson usbd_transfer_stop(xfer); 71602ac6454SAndrew Thompson } 71702ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson done: 720a18a7a41SHans Petter Selasky if (do_unlock) 72164cb5e2aSHans Petter Selasky usbd_ctrl_unlock(udev); 722a18a7a41SHans Petter Selasky 7232df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) 7240eb8d462SHans Petter Selasky USB_MTX_LOCK(mtx); 7252df1e9a6SAndrew Thompson 726a0d53e0bSHans Petter Selasky switch (err) { 727a0d53e0bSHans Petter Selasky case USB_ERR_NORMAL_COMPLETION: 728a0d53e0bSHans Petter Selasky case USB_ERR_SHORT_XFER: 729a0d53e0bSHans Petter Selasky case USB_ERR_STALLED: 730a0d53e0bSHans Petter Selasky case USB_ERR_CANCELLED: 731a0d53e0bSHans Petter Selasky break; 732a0d53e0bSHans Petter Selasky default: 7332dea6902SHans Petter Selasky DPRINTF("error=%s - waiting a bit for TT cleanup\n", 7342dea6902SHans Petter Selasky usbd_errstr(err)); 735a0d53e0bSHans Petter Selasky usb_pause_mtx(mtx, hz / 16); 736a0d53e0bSHans Petter Selasky break; 737a0d53e0bSHans Petter Selasky } 738e0a69b51SAndrew Thompson return ((usb_error_t)err); 73902ac6454SAndrew Thompson } 74002ac6454SAndrew Thompson 74102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 742a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code 74302ac6454SAndrew Thompson * 74402ac6454SAndrew Thompson * This function is factored out code. It does basically the same like 745a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the 74602ac6454SAndrew Thompson * passed process argument before doing the USB request. If the 74702ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It 74802ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked 74902ac6454SAndrew Thompson * when calling this function. 75002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 751e0a69b51SAndrew Thompson usb_error_t 752a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, 753760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags, 754e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout) 75502ac6454SAndrew Thompson { 756e0a69b51SAndrew Thompson usb_error_t err; 75702ac6454SAndrew Thompson uint16_t len; 75802ac6454SAndrew Thompson 75902ac6454SAndrew Thompson /* get request data length */ 76002ac6454SAndrew Thompson len = UGETW(req->wLength); 76102ac6454SAndrew Thompson 76202ac6454SAndrew Thompson /* check if the device is being detached */ 763a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) { 76402ac6454SAndrew Thompson err = USB_ERR_IOERROR; 76502ac6454SAndrew Thompson goto done; 76602ac6454SAndrew Thompson } 76702ac6454SAndrew Thompson 76802ac6454SAndrew Thompson /* forward the USB request */ 769a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx, 77002ac6454SAndrew Thompson req, data, flags, actlen, timeout); 77102ac6454SAndrew Thompson 77202ac6454SAndrew Thompson done: 77302ac6454SAndrew Thompson /* on failure we zero the data */ 77402ac6454SAndrew Thompson /* on short packet we zero the unused data */ 77502ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { 77602ac6454SAndrew Thompson if (err) 77702ac6454SAndrew Thompson memset(data, 0, len); 77802ac6454SAndrew Thompson else if (actlen && *actlen != len) 77902ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen); 78002ac6454SAndrew Thompson } 78102ac6454SAndrew Thompson return (err); 78202ac6454SAndrew Thompson } 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 785a593f6b8SAndrew Thompson * usbd_req_reset_port 78602ac6454SAndrew Thompson * 787cbb75751SHans Petter Selasky * This function will instruct a USB HUB to perform a reset sequence 78802ac6454SAndrew Thompson * on the specified port number. 78902ac6454SAndrew Thompson * 79002ac6454SAndrew Thompson * Returns: 79102ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero. 79202ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be 79302ac6454SAndrew Thompson * disabled. 79402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 795e0a69b51SAndrew Thompson usb_error_t 796a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) 79702ac6454SAndrew Thompson { 798760bc48eSAndrew Thompson struct usb_port_status ps; 799e0a69b51SAndrew Thompson usb_error_t err; 80002ac6454SAndrew Thompson uint16_t n; 80193ee6e85SHans Petter Selasky uint16_t status; 80293ee6e85SHans Petter Selasky uint16_t change; 80302ac6454SAndrew Thompson 80493ee6e85SHans Petter Selasky DPRINTF("\n"); 80593ee6e85SHans Petter Selasky 806f6bd103eSHans Petter Selasky /* clear any leftover port reset changes first */ 807f6bd103eSHans Petter Selasky usbd_req_clear_port_feature( 808f6bd103eSHans Petter Selasky udev, mtx, port, UHF_C_PORT_RESET); 809f6bd103eSHans Petter Selasky 810f6bd103eSHans Petter Selasky /* assert port reset on the given port */ 811f6bd103eSHans Petter Selasky err = usbd_req_set_port_feature( 812f6bd103eSHans Petter Selasky udev, mtx, port, UHF_PORT_RESET); 813f6bd103eSHans Petter Selasky 814f6bd103eSHans Petter Selasky /* check for errors */ 815f6bd103eSHans Petter Selasky if (err) 81602ac6454SAndrew Thompson goto done; 81702ac6454SAndrew Thompson n = 0; 81802ac6454SAndrew Thompson while (1) { 81902ac6454SAndrew Thompson /* wait for the device to recover from reset */ 82037506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); 82137506412SHans Petter Selasky n += usb_port_reset_delay; 822a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port); 82393ee6e85SHans Petter Selasky if (err) 82402ac6454SAndrew Thompson goto done; 82593ee6e85SHans Petter Selasky 826f6bd103eSHans Petter Selasky status = UGETW(ps.wPortStatus); 827f6bd103eSHans Petter Selasky change = UGETW(ps.wPortChange); 828f6bd103eSHans Petter Selasky 8290e777d84SHans Petter Selasky /* if the device disappeared, just give up */ 830f6bd103eSHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS)) 8310e777d84SHans Petter Selasky goto done; 832f6bd103eSHans Petter Selasky 83302ac6454SAndrew Thompson /* check if reset is complete */ 834f6bd103eSHans Petter Selasky if (change & UPS_C_PORT_RESET) 83502ac6454SAndrew Thompson break; 836f6bd103eSHans Petter Selasky 837f6bd103eSHans Petter Selasky /* 838f6bd103eSHans Petter Selasky * Some Virtual Machines like VirtualBox 4.x fail to 839f6bd103eSHans Petter Selasky * generate a port reset change event. Check if reset 840f6bd103eSHans Petter Selasky * is no longer asserted. 841f6bd103eSHans Petter Selasky */ 842f6bd103eSHans Petter Selasky if (!(status & UPS_RESET)) 843f6bd103eSHans Petter Selasky break; 844f6bd103eSHans Petter Selasky 84502ac6454SAndrew Thompson /* check for timeout */ 84602ac6454SAndrew Thompson if (n > 1000) { 84702ac6454SAndrew Thompson n = 0; 84802ac6454SAndrew Thompson break; 84902ac6454SAndrew Thompson } 85002ac6454SAndrew Thompson } 85102ac6454SAndrew Thompson 85202ac6454SAndrew Thompson /* clear port reset first */ 853a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature( 85402ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET); 85593ee6e85SHans Petter Selasky if (err) 85602ac6454SAndrew Thompson goto done; 85793ee6e85SHans Petter Selasky 85802ac6454SAndrew Thompson /* check for timeout */ 85902ac6454SAndrew Thompson if (n == 0) { 86002ac6454SAndrew Thompson err = USB_ERR_TIMEOUT; 86102ac6454SAndrew Thompson goto done; 86202ac6454SAndrew Thompson } 86302ac6454SAndrew Thompson /* wait for the device to recover from reset */ 86437506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); 86502ac6454SAndrew Thompson 86602ac6454SAndrew Thompson done: 86702ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n", 868a593f6b8SAndrew Thompson port, usbd_errstr(err)); 86902ac6454SAndrew Thompson return (err); 87002ac6454SAndrew Thompson } 87102ac6454SAndrew Thompson 87202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 873cbb75751SHans Petter Selasky * usbd_req_warm_reset_port 874cbb75751SHans Petter Selasky * 875cbb75751SHans Petter Selasky * This function will instruct an USB HUB to perform a warm reset 876cbb75751SHans Petter Selasky * sequence on the specified port number. This kind of reset is not 877cbb75751SHans Petter Selasky * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted 878cbb75751SHans Petter Selasky * for SUPER-speed USB HUBs. 879cbb75751SHans Petter Selasky * 880cbb75751SHans Petter Selasky * Returns: 881cbb75751SHans Petter Selasky * 0: Success. The USB device should now be available again. 882cbb75751SHans Petter Selasky * Else: Failure. No USB device is present and the USB port should be 883cbb75751SHans Petter Selasky * disabled. 884cbb75751SHans Petter Selasky *------------------------------------------------------------------------*/ 885cbb75751SHans Petter Selasky usb_error_t 88693ee6e85SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, 88793ee6e85SHans Petter Selasky uint8_t port) 888cbb75751SHans Petter Selasky { 889cbb75751SHans Petter Selasky struct usb_port_status ps; 890cbb75751SHans Petter Selasky usb_error_t err; 891cbb75751SHans Petter Selasky uint16_t n; 89293ee6e85SHans Petter Selasky uint16_t status; 89393ee6e85SHans Petter Selasky uint16_t change; 894cbb75751SHans Petter Selasky 89593ee6e85SHans Petter Selasky DPRINTF("\n"); 89693ee6e85SHans Petter Selasky 89793ee6e85SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 89893ee6e85SHans Petter Selasky if (err) 899cbb75751SHans Petter Selasky goto done; 90093ee6e85SHans Petter Selasky 90193ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus); 90293ee6e85SHans Petter Selasky 90393ee6e85SHans Petter Selasky switch (UPS_PORT_LINK_STATE_GET(status)) { 90493ee6e85SHans Petter Selasky case UPS_PORT_LS_U3: 90593ee6e85SHans Petter Selasky case UPS_PORT_LS_COMP_MODE: 90693ee6e85SHans Petter Selasky case UPS_PORT_LS_LOOPBACK: 90793ee6e85SHans Petter Selasky case UPS_PORT_LS_SS_INA: 90893ee6e85SHans Petter Selasky break; 90993ee6e85SHans Petter Selasky default: 91093ee6e85SHans Petter Selasky DPRINTF("Wrong state for warm reset\n"); 91193ee6e85SHans Petter Selasky return (0); 912cbb75751SHans Petter Selasky } 91393ee6e85SHans Petter Selasky 91493ee6e85SHans Petter Selasky /* clear any leftover warm port reset changes first */ 91593ee6e85SHans Petter Selasky usbd_req_clear_port_feature(udev, mtx, 91693ee6e85SHans Petter Selasky port, UHF_C_BH_PORT_RESET); 91793ee6e85SHans Petter Selasky 91893ee6e85SHans Petter Selasky /* set warm port reset */ 91993ee6e85SHans Petter Selasky err = usbd_req_set_port_feature(udev, mtx, 92093ee6e85SHans Petter Selasky port, UHF_BH_PORT_RESET); 92193ee6e85SHans Petter Selasky if (err) 92293ee6e85SHans Petter Selasky goto done; 92393ee6e85SHans Petter Selasky 924cbb75751SHans Petter Selasky n = 0; 925cbb75751SHans Petter Selasky while (1) { 926cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 92737506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); 92837506412SHans Petter Selasky n += usb_port_reset_delay; 929cbb75751SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port); 93093ee6e85SHans Petter Selasky if (err) 931cbb75751SHans Petter Selasky goto done; 93293ee6e85SHans Petter Selasky 93393ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus); 93493ee6e85SHans Petter Selasky change = UGETW(ps.wPortChange); 93593ee6e85SHans Petter Selasky 936cbb75751SHans Petter Selasky /* if the device disappeared, just give up */ 93793ee6e85SHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS)) 938cbb75751SHans Petter Selasky goto done; 93993ee6e85SHans Petter Selasky 940cbb75751SHans Petter Selasky /* check if reset is complete */ 94193ee6e85SHans Petter Selasky if (change & UPS_C_BH_PORT_RESET) 942cbb75751SHans Petter Selasky break; 94393ee6e85SHans Petter Selasky 944cbb75751SHans Petter Selasky /* check for timeout */ 945cbb75751SHans Petter Selasky if (n > 1000) { 946cbb75751SHans Petter Selasky n = 0; 947cbb75751SHans Petter Selasky break; 948cbb75751SHans Petter Selasky } 949cbb75751SHans Petter Selasky } 950cbb75751SHans Petter Selasky 951cbb75751SHans Petter Selasky /* clear port reset first */ 952cbb75751SHans Petter Selasky err = usbd_req_clear_port_feature( 953cbb75751SHans Petter Selasky udev, mtx, port, UHF_C_BH_PORT_RESET); 95493ee6e85SHans Petter Selasky if (err) 955cbb75751SHans Petter Selasky goto done; 95693ee6e85SHans Petter Selasky 957cbb75751SHans Petter Selasky /* check for timeout */ 958cbb75751SHans Petter Selasky if (n == 0) { 959cbb75751SHans Petter Selasky err = USB_ERR_TIMEOUT; 960cbb75751SHans Petter Selasky goto done; 961cbb75751SHans Petter Selasky } 962cbb75751SHans Petter Selasky /* wait for the device to recover from reset */ 96337506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); 964cbb75751SHans Petter Selasky 965cbb75751SHans Petter Selasky done: 966cbb75751SHans Petter Selasky DPRINTFN(2, "port %d warm reset returning error=%s\n", 967cbb75751SHans Petter Selasky port, usbd_errstr(err)); 968cbb75751SHans Petter Selasky return (err); 969cbb75751SHans Petter Selasky } 970cbb75751SHans Petter Selasky 971cbb75751SHans Petter Selasky /*------------------------------------------------------------------------* 972a593f6b8SAndrew Thompson * usbd_req_get_desc 97302ac6454SAndrew Thompson * 97402ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains 97502ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and 97602ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len" 97702ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len" 97802ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real 97902ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing 98016589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB 98116589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen" 98216589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get 98316589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The 98416589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen" 98516589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated 98616589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to 98716589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of 98816589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the 98916589beaSAndrew Thompson * descriptor. 99002ac6454SAndrew Thompson * 99102ac6454SAndrew Thompson * Returns: 99202ac6454SAndrew Thompson * 0: Success 99302ac6454SAndrew Thompson * Else: Failure 99402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 995e0a69b51SAndrew Thompson usb_error_t 996a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev, 99716589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc, 99802ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len, 99902ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index, 100002ac6454SAndrew Thompson uint8_t retries) 100102ac6454SAndrew Thompson { 1002760bc48eSAndrew Thompson struct usb_device_request req; 1003b774480cSHans Petter Selasky uint8_t *buf = desc; 1004e0a69b51SAndrew Thompson usb_error_t err; 100502ac6454SAndrew Thompson 100602ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", 100702ac6454SAndrew Thompson id, type, index, max_len); 100802ac6454SAndrew Thompson 100902ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 101002ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 101102ac6454SAndrew Thompson USETW2(req.wValue, type, index); 101202ac6454SAndrew Thompson USETW(req.wIndex, id); 101302ac6454SAndrew Thompson 101402ac6454SAndrew Thompson while (1) { 101502ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) { 101602ac6454SAndrew Thompson err = USB_ERR_INVAL; 101702ac6454SAndrew Thompson goto done; 101802ac6454SAndrew Thompson } 101902ac6454SAndrew Thompson USETW(req.wLength, min_len); 102002ac6454SAndrew Thompson 1021a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req, 10222dea6902SHans Petter Selasky desc, 0, NULL, 1000 /* ms */); 102302ac6454SAndrew Thompson 1024b774480cSHans Petter Selasky if (err != 0 && err != USB_ERR_TIMEOUT && 1025b774480cSHans Petter Selasky min_len != max_len) { 1026b774480cSHans Petter Selasky /* clear descriptor data */ 1027b774480cSHans Petter Selasky memset(desc, 0, max_len); 1028b774480cSHans Petter Selasky 1029b774480cSHans Petter Selasky /* try to read full descriptor length */ 1030b774480cSHans Petter Selasky USETW(req.wLength, max_len); 1031b774480cSHans Petter Selasky 1032b774480cSHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, 10332dea6902SHans Petter Selasky desc, USB_SHORT_XFER_OK, NULL, 1000 /* ms */); 1034b774480cSHans Petter Selasky 1035b774480cSHans Petter Selasky if (err == 0) { 1036b774480cSHans Petter Selasky /* verify length */ 1037b774480cSHans Petter Selasky if (buf[0] > max_len) 1038b774480cSHans Petter Selasky buf[0] = max_len; 1039b774480cSHans Petter Selasky else if (buf[0] < 2) 1040b774480cSHans Petter Selasky err = USB_ERR_INVAL; 1041b774480cSHans Petter Selasky 1042b774480cSHans Petter Selasky min_len = buf[0]; 1043b774480cSHans Petter Selasky 1044b774480cSHans Petter Selasky /* enforce descriptor type */ 1045b774480cSHans Petter Selasky buf[1] = type; 1046b774480cSHans Petter Selasky goto done; 1047b774480cSHans Petter Selasky } 1048b774480cSHans Petter Selasky } 1049b774480cSHans Petter Selasky 105002ac6454SAndrew Thompson if (err) { 105102ac6454SAndrew Thompson if (!retries) { 105202ac6454SAndrew Thompson goto done; 105302ac6454SAndrew Thompson } 105402ac6454SAndrew Thompson retries--; 105502ac6454SAndrew Thompson 1056a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5); 105702ac6454SAndrew Thompson 105802ac6454SAndrew Thompson continue; 105902ac6454SAndrew Thompson } 106002ac6454SAndrew Thompson 106102ac6454SAndrew Thompson if (min_len == max_len) { 106216589beaSAndrew Thompson /* enforce correct length */ 106316589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL)) 106402ac6454SAndrew Thompson buf[0] = min_len; 106516589beaSAndrew Thompson 106616589beaSAndrew Thompson /* enforce correct type */ 106702ac6454SAndrew Thompson buf[1] = type; 106802ac6454SAndrew Thompson 106902ac6454SAndrew Thompson goto done; 107002ac6454SAndrew Thompson } 107102ac6454SAndrew Thompson /* range check */ 107202ac6454SAndrew Thompson 107302ac6454SAndrew Thompson if (max_len > buf[0]) { 107402ac6454SAndrew Thompson max_len = buf[0]; 107502ac6454SAndrew Thompson } 107602ac6454SAndrew Thompson /* zero minimum data */ 107702ac6454SAndrew Thompson 107802ac6454SAndrew Thompson while (min_len > max_len) { 107902ac6454SAndrew Thompson min_len--; 108002ac6454SAndrew Thompson buf[min_len] = 0; 108102ac6454SAndrew Thompson } 108202ac6454SAndrew Thompson 108302ac6454SAndrew Thompson /* set new minimum length */ 108402ac6454SAndrew Thompson 108502ac6454SAndrew Thompson min_len = max_len; 108602ac6454SAndrew Thompson } 108702ac6454SAndrew Thompson done: 108816589beaSAndrew Thompson if (actlen != NULL) { 108916589beaSAndrew Thompson if (err) 109016589beaSAndrew Thompson *actlen = 0; 109116589beaSAndrew Thompson else 109216589beaSAndrew Thompson *actlen = min_len; 109316589beaSAndrew Thompson } 109402ac6454SAndrew Thompson return (err); 109502ac6454SAndrew Thompson } 109602ac6454SAndrew Thompson 109702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1098a593f6b8SAndrew Thompson * usbd_req_get_string_any 109902ac6454SAndrew Thompson * 110002ac6454SAndrew Thompson * This function will return the string given by "string_index" 110102ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes 110202ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as 110302ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length ! 110402ac6454SAndrew Thompson * 110502ac6454SAndrew Thompson * Returns: 110602ac6454SAndrew Thompson * 0: Success 110702ac6454SAndrew Thompson * Else: Failure 110802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1109e0a69b51SAndrew Thompson usb_error_t 1110a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, 111102ac6454SAndrew Thompson uint16_t len, uint8_t string_index) 111202ac6454SAndrew Thompson { 111302ac6454SAndrew Thompson char *s; 111402ac6454SAndrew Thompson uint8_t *temp; 111502ac6454SAndrew Thompson uint16_t i; 111602ac6454SAndrew Thompson uint16_t n; 111702ac6454SAndrew Thompson uint16_t c; 111802ac6454SAndrew Thompson uint8_t swap; 1119e0a69b51SAndrew Thompson usb_error_t err; 112002ac6454SAndrew Thompson 112102ac6454SAndrew Thompson if (len == 0) { 112202ac6454SAndrew Thompson /* should not happen */ 112302ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 112402ac6454SAndrew Thompson } 112502ac6454SAndrew Thompson if (string_index == 0) { 112602ac6454SAndrew Thompson /* this is the language table */ 112702ac6454SAndrew Thompson buf[0] = 0; 112802ac6454SAndrew Thompson return (USB_ERR_INVAL); 112902ac6454SAndrew Thompson } 113002ac6454SAndrew Thompson if (udev->flags.no_strings) { 113102ac6454SAndrew Thompson buf[0] = 0; 113202ac6454SAndrew Thompson return (USB_ERR_STALLED); 113302ac6454SAndrew Thompson } 1134a593f6b8SAndrew Thompson err = usbd_req_get_string_desc 113502ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index); 113602ac6454SAndrew Thompson if (err) { 113702ac6454SAndrew Thompson buf[0] = 0; 113802ac6454SAndrew Thompson return (err); 113902ac6454SAndrew Thompson } 114002ac6454SAndrew Thompson temp = (uint8_t *)buf; 114102ac6454SAndrew Thompson 114202ac6454SAndrew Thompson if (temp[0] < 2) { 114302ac6454SAndrew Thompson /* string length is too short */ 114402ac6454SAndrew Thompson buf[0] = 0; 114502ac6454SAndrew Thompson return (USB_ERR_INVAL); 114602ac6454SAndrew Thompson } 114702ac6454SAndrew Thompson /* reserve one byte for terminating zero */ 114802ac6454SAndrew Thompson len--; 114902ac6454SAndrew Thompson 115002ac6454SAndrew Thompson /* find maximum length */ 115102ac6454SAndrew Thompson s = buf; 115202ac6454SAndrew Thompson n = (temp[0] / 2) - 1; 115302ac6454SAndrew Thompson if (n > len) { 115402ac6454SAndrew Thompson n = len; 115502ac6454SAndrew Thompson } 115602ac6454SAndrew Thompson /* skip descriptor header */ 115702ac6454SAndrew Thompson temp += 2; 115802ac6454SAndrew Thompson 115902ac6454SAndrew Thompson /* reset swap state */ 116002ac6454SAndrew Thompson swap = 3; 116102ac6454SAndrew Thompson 116202ac6454SAndrew Thompson /* convert and filter */ 116302ac6454SAndrew Thompson for (i = 0; (i != n); i++) { 116402ac6454SAndrew Thompson c = UGETW(temp + (2 * i)); 116502ac6454SAndrew Thompson 116602ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */ 116702ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 116802ac6454SAndrew Thompson /* Little Endian, default */ 116902ac6454SAndrew Thompson *s = c; 117002ac6454SAndrew Thompson swap = 1; 117102ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 117202ac6454SAndrew Thompson /* Big Endian */ 117302ac6454SAndrew Thompson *s = c >> 8; 117402ac6454SAndrew Thompson swap = 2; 117502ac6454SAndrew Thompson } else { 117602ac6454SAndrew Thompson /* silently skip bad character */ 117702ac6454SAndrew Thompson continue; 117802ac6454SAndrew Thompson } 117902ac6454SAndrew Thompson 118002ac6454SAndrew Thompson /* 1181b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical 1182b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts 1183b64cf89fSHans Petter Selasky * and daemons. 118402ac6454SAndrew Thompson */ 1185b64cf89fSHans Petter Selasky if (isalpha(*s) || 1186b64cf89fSHans Petter Selasky isdigit(*s) || 1187b64cf89fSHans Petter Selasky *s == '-' || 1188b64cf89fSHans Petter Selasky *s == '+' || 1189b64cf89fSHans Petter Selasky *s == ' ' || 1190b64cf89fSHans Petter Selasky *s == '.' || 119160737149SEdward Tomasz Napierala *s == ',' || 119260737149SEdward Tomasz Napierala *s == ':' || 119360737149SEdward Tomasz Napierala *s == '/' || 119460737149SEdward Tomasz Napierala *s == '(' || 119560737149SEdward Tomasz Napierala *s == ')') { 1196b64cf89fSHans Petter Selasky /* allowed */ 119702ac6454SAndrew Thompson s++; 119802ac6454SAndrew Thompson } 1199b64cf89fSHans Petter Selasky /* silently skip bad character */ 1200b64cf89fSHans Petter Selasky } 120102ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */ 120202ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 120302ac6454SAndrew Thompson } 120402ac6454SAndrew Thompson 120502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1206a593f6b8SAndrew Thompson * usbd_req_get_string_desc 120702ac6454SAndrew Thompson * 120802ac6454SAndrew Thompson * If you don't know the language ID, consider using 1209a593f6b8SAndrew Thompson * "usbd_req_get_string_any()". 121002ac6454SAndrew Thompson * 121102ac6454SAndrew Thompson * Returns: 121202ac6454SAndrew Thompson * 0: Success 121302ac6454SAndrew Thompson * Else: Failure 121402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1215e0a69b51SAndrew Thompson usb_error_t 1216a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, 121702ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id, 121802ac6454SAndrew Thompson uint8_t string_index) 121902ac6454SAndrew Thompson { 1220a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, 122102ac6454SAndrew Thompson UDESC_STRING, string_index, 0)); 122202ac6454SAndrew Thompson } 122302ac6454SAndrew Thompson 122402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1225a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr 12267efaaa9aSAndrew Thompson * 12277efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer 12287efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for 12297efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration. 12307efaaa9aSAndrew Thompson * 12317efaaa9aSAndrew Thompson * Returns: 12327efaaa9aSAndrew Thompson * 0: Success 12337efaaa9aSAndrew Thompson * Else: Failure 12347efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/ 1235e0a69b51SAndrew Thompson usb_error_t 1236a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev, 1237760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue) 12387efaaa9aSAndrew Thompson { 1239760bc48eSAndrew Thompson struct usb_device_request req; 1240e0a69b51SAndrew Thompson usb_handle_req_t *hr_func; 1241459d369eSAndrew Thompson const void *ptr; 1242459d369eSAndrew Thompson uint16_t len; 1243e0a69b51SAndrew Thompson usb_error_t err; 12447efaaa9aSAndrew Thompson 124563521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 12467efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 1247459d369eSAndrew Thompson USETW(req.wValue, wValue); 12487efaaa9aSAndrew Thompson USETW(req.wIndex, 0); 12497efaaa9aSAndrew Thompson USETW(req.wLength, 0); 12507efaaa9aSAndrew Thompson 1251459d369eSAndrew Thompson ptr = NULL; 1252459d369eSAndrew Thompson len = 0; 12537efaaa9aSAndrew Thompson 1254a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev); 1255459d369eSAndrew Thompson 1256459d369eSAndrew Thompson if (hr_func == NULL) 1257459d369eSAndrew Thompson err = USB_ERR_INVAL; 1258459d369eSAndrew Thompson else { 1259459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus); 1260459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len); 1261459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus); 1262459d369eSAndrew Thompson } 1263459d369eSAndrew Thompson 1264459d369eSAndrew Thompson if (err) 1265459d369eSAndrew Thompson ptr = NULL; 1266459d369eSAndrew Thompson else if (ptr == NULL) 1267459d369eSAndrew Thompson err = USB_ERR_INVAL; 1268459d369eSAndrew Thompson 1269760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr); 1270459d369eSAndrew Thompson 1271459d369eSAndrew Thompson return (err); 12727efaaa9aSAndrew Thompson } 12737efaaa9aSAndrew Thompson 12747efaaa9aSAndrew Thompson /*------------------------------------------------------------------------* 1275a593f6b8SAndrew Thompson * usbd_req_get_config_desc 127602ac6454SAndrew Thompson * 127702ac6454SAndrew Thompson * Returns: 127802ac6454SAndrew Thompson * 0: Success 127902ac6454SAndrew Thompson * Else: Failure 128002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1281e0a69b51SAndrew Thompson usb_error_t 1282a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, 1283760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index) 128402ac6454SAndrew Thompson { 1285e0a69b51SAndrew Thompson usb_error_t err; 128602ac6454SAndrew Thompson 128702ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index); 128802ac6454SAndrew Thompson 1289a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 129002ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); 129102ac6454SAndrew Thompson if (err) { 129202ac6454SAndrew Thompson goto done; 129302ac6454SAndrew Thompson } 129402ac6454SAndrew Thompson /* Extra sanity checking */ 12956d917491SHans Petter Selasky if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { 129602ac6454SAndrew Thompson err = USB_ERR_INVAL; 129702ac6454SAndrew Thompson } 129802ac6454SAndrew Thompson done: 129902ac6454SAndrew Thompson return (err); 130002ac6454SAndrew Thompson } 130102ac6454SAndrew Thompson 130202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 13032c79a775SHans Petter Selasky * usbd_alloc_config_desc 13042c79a775SHans Petter Selasky * 13052c79a775SHans Petter Selasky * This function is used to allocate a zeroed configuration 13062c79a775SHans Petter Selasky * descriptor. 13072c79a775SHans Petter Selasky * 13082c79a775SHans Petter Selasky * Returns: 13092c79a775SHans Petter Selasky * NULL: Failure 13102c79a775SHans Petter Selasky * Else: Success 13112c79a775SHans Petter Selasky *------------------------------------------------------------------------*/ 13122c79a775SHans Petter Selasky void * 13132c79a775SHans Petter Selasky usbd_alloc_config_desc(struct usb_device *udev, uint32_t size) 13142c79a775SHans Petter Selasky { 13152c79a775SHans Petter Selasky if (size > USB_CONFIG_MAX) { 13162c79a775SHans Petter Selasky DPRINTF("Configuration descriptor too big\n"); 13172c79a775SHans Petter Selasky return (NULL); 13182c79a775SHans Petter Selasky } 13192c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0) 13202c79a775SHans Petter Selasky return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK)); 13212c79a775SHans Petter Selasky #else 13222c79a775SHans Petter Selasky memset(udev->config_data, 0, sizeof(udev->config_data)); 13232c79a775SHans Petter Selasky return (udev->config_data); 13242c79a775SHans Petter Selasky #endif 13252c79a775SHans Petter Selasky } 13262c79a775SHans Petter Selasky 13272c79a775SHans Petter Selasky /*------------------------------------------------------------------------* 13282c79a775SHans Petter Selasky * usbd_alloc_config_desc 13292c79a775SHans Petter Selasky * 13302c79a775SHans Petter Selasky * This function is used to free a configuration descriptor. 13312c79a775SHans Petter Selasky *------------------------------------------------------------------------*/ 13322c79a775SHans Petter Selasky void 13332c79a775SHans Petter Selasky usbd_free_config_desc(struct usb_device *udev, void *ptr) 13342c79a775SHans Petter Selasky { 13352c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0) 13362c79a775SHans Petter Selasky free(ptr, M_USBDEV); 13372c79a775SHans Petter Selasky #endif 13382c79a775SHans Petter Selasky } 13392c79a775SHans Petter Selasky 13402c79a775SHans Petter Selasky /*------------------------------------------------------------------------* 1341a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full 134202ac6454SAndrew Thompson * 134302ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and 13442c79a775SHans Petter Selasky * ensures that "wTotalLength" is correct. The returned configuration 13452c79a775SHans Petter Selasky * descriptor is freed by calling "usbd_free_config_desc()". 134602ac6454SAndrew Thompson * 134702ac6454SAndrew Thompson * Returns: 134802ac6454SAndrew Thompson * 0: Success 134902ac6454SAndrew Thompson * Else: Failure 135002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1351e0a69b51SAndrew Thompson usb_error_t 1352a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, 13532c79a775SHans Petter Selasky struct usb_config_descriptor **ppcd, uint8_t index) 135402ac6454SAndrew Thompson { 1355760bc48eSAndrew Thompson struct usb_config_descriptor cd; 1356760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 13575b0752bbSHans Petter Selasky uint32_t len; 1358e0a69b51SAndrew Thompson usb_error_t err; 135902ac6454SAndrew Thompson 136002ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index); 136102ac6454SAndrew Thompson 136202ac6454SAndrew Thompson *ppcd = NULL; 136302ac6454SAndrew Thompson 1364a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index); 13655b0752bbSHans Petter Selasky if (err) 136602ac6454SAndrew Thompson return (err); 13675b0752bbSHans Petter Selasky 136802ac6454SAndrew Thompson /* get full descriptor */ 136902ac6454SAndrew Thompson len = UGETW(cd.wTotalLength); 13705b0752bbSHans Petter Selasky if (len < (uint32_t)sizeof(*cdesc)) { 137102ac6454SAndrew Thompson /* corrupt descriptor */ 137202ac6454SAndrew Thompson return (USB_ERR_INVAL); 13735b0752bbSHans Petter Selasky } else if (len > USB_CONFIG_MAX) { 13745b0752bbSHans Petter Selasky DPRINTF("Configuration descriptor was truncated\n"); 13755b0752bbSHans Petter Selasky len = USB_CONFIG_MAX; 137602ac6454SAndrew Thompson } 13772c79a775SHans Petter Selasky cdesc = usbd_alloc_config_desc(udev, len); 13785b0752bbSHans Petter Selasky if (cdesc == NULL) 137902ac6454SAndrew Thompson return (USB_ERR_NOMEM); 1380a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, 138102ac6454SAndrew Thompson UDESC_CONFIG, index, 3); 138202ac6454SAndrew Thompson if (err) { 13832c79a775SHans Petter Selasky usbd_free_config_desc(udev, cdesc); 138402ac6454SAndrew Thompson return (err); 138502ac6454SAndrew Thompson } 138602ac6454SAndrew Thompson /* make sure that the device is not fooling us: */ 138702ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len); 138802ac6454SAndrew Thompson 138902ac6454SAndrew Thompson *ppcd = cdesc; 139002ac6454SAndrew Thompson 139102ac6454SAndrew Thompson return (0); /* success */ 139202ac6454SAndrew Thompson } 139302ac6454SAndrew Thompson 139402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1395a593f6b8SAndrew Thompson * usbd_req_get_device_desc 139602ac6454SAndrew Thompson * 139702ac6454SAndrew Thompson * Returns: 139802ac6454SAndrew Thompson * 0: Success 139902ac6454SAndrew Thompson * Else: Failure 140002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1401e0a69b51SAndrew Thompson usb_error_t 1402a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, 1403760bc48eSAndrew Thompson struct usb_device_descriptor *d) 140402ac6454SAndrew Thompson { 140502ac6454SAndrew Thompson DPRINTFN(4, "\n"); 1406a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), 140702ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3)); 140802ac6454SAndrew Thompson } 140902ac6454SAndrew Thompson 141002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1411a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no 141202ac6454SAndrew Thompson * 141302ac6454SAndrew Thompson * Returns: 141402ac6454SAndrew Thompson * 0: Success 141502ac6454SAndrew Thompson * Else: Failure 141602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1417e0a69b51SAndrew Thompson usb_error_t 1418a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 141902ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index) 142002ac6454SAndrew Thompson { 1421a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1422760bc48eSAndrew Thompson struct usb_device_request req; 142302ac6454SAndrew Thompson 1424bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 142502ac6454SAndrew Thompson return (USB_ERR_INVAL); 1426bd73b187SAlfred Perlstein 142702ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 142802ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE; 142902ac6454SAndrew Thompson USETW(req.wValue, 0); 143002ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 143102ac6454SAndrew Thompson req.wIndex[1] = 0; 143202ac6454SAndrew Thompson USETW(req.wLength, 1); 1433a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no)); 143402ac6454SAndrew Thompson } 143502ac6454SAndrew Thompson 143602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1437a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no 143802ac6454SAndrew Thompson * 143902ac6454SAndrew Thompson * Returns: 144002ac6454SAndrew Thompson * 0: Success 144102ac6454SAndrew Thompson * Else: Failure 144202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1443e0a69b51SAndrew Thompson usb_error_t 1444a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, 144502ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no) 144602ac6454SAndrew Thompson { 1447a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1448760bc48eSAndrew Thompson struct usb_device_request req; 1449b8b3f4fdSHans Petter Selasky usb_error_t err; 145002ac6454SAndrew Thompson 1451bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL)) 145202ac6454SAndrew Thompson return (USB_ERR_INVAL); 1453bd73b187SAlfred Perlstein 145402ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE; 145502ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE; 145602ac6454SAndrew Thompson req.wValue[0] = alt_no; 145702ac6454SAndrew Thompson req.wValue[1] = 0; 145802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 145902ac6454SAndrew Thompson req.wIndex[1] = 0; 146002ac6454SAndrew Thompson USETW(req.wLength, 0); 1461b8b3f4fdSHans Petter Selasky err = usbd_do_request(udev, mtx, &req, 0); 1462b8b3f4fdSHans Petter Selasky if (err == USB_ERR_STALLED && iface->num_altsetting == 1) { 1463b8b3f4fdSHans Petter Selasky /* 1464b8b3f4fdSHans Petter Selasky * The USB specification chapter 9.4.10 says that USB 1465b8b3f4fdSHans Petter Selasky * devices having only one alternate setting are 1466b8b3f4fdSHans Petter Selasky * allowed to STALL this request. Ignore this failure. 1467b8b3f4fdSHans Petter Selasky */ 1468b8b3f4fdSHans Petter Selasky err = 0; 1469b8b3f4fdSHans Petter Selasky DPRINTF("Setting default alternate number failed. (ignored)\n"); 1470b8b3f4fdSHans Petter Selasky } 1471b8b3f4fdSHans Petter Selasky return (err); 147202ac6454SAndrew Thompson } 147302ac6454SAndrew Thompson 147402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1475a593f6b8SAndrew Thompson * usbd_req_get_device_status 147602ac6454SAndrew Thompson * 147702ac6454SAndrew Thompson * Returns: 147802ac6454SAndrew Thompson * 0: Success 147902ac6454SAndrew Thompson * Else: Failure 148002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1481e0a69b51SAndrew Thompson usb_error_t 1482a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, 1483760bc48eSAndrew Thompson struct usb_status *st) 148402ac6454SAndrew Thompson { 1485760bc48eSAndrew Thompson struct usb_device_request req; 148602ac6454SAndrew Thompson 148702ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 148802ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 148902ac6454SAndrew Thompson USETW(req.wValue, 0); 149002ac6454SAndrew Thompson USETW(req.wIndex, 0); 149102ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st)); 1492a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 149302ac6454SAndrew Thompson } 149402ac6454SAndrew Thompson 149502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1496a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor 149702ac6454SAndrew Thompson * 149802ac6454SAndrew Thompson * Returns: 149902ac6454SAndrew Thompson * 0: Success 150002ac6454SAndrew Thompson * Else: Failure 150102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1502e0a69b51SAndrew Thompson usb_error_t 1503a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1504760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports) 150502ac6454SAndrew Thompson { 1506760bc48eSAndrew Thompson struct usb_device_request req; 150702ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8; 150802ac6454SAndrew Thompson 150902ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 151002ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 151102ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0); 151202ac6454SAndrew Thompson USETW(req.wIndex, 0); 151302ac6454SAndrew Thompson USETW(req.wLength, len); 1514a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd)); 151502ac6454SAndrew Thompson } 151602ac6454SAndrew Thompson 151702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1518963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor 1519963169b4SHans Petter Selasky * 1520963169b4SHans Petter Selasky * Returns: 1521963169b4SHans Petter Selasky * 0: Success 1522963169b4SHans Petter Selasky * Else: Failure 1523963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1524963169b4SHans Petter Selasky usb_error_t 1525963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, 1526963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports) 1527963169b4SHans Petter Selasky { 1528963169b4SHans Petter Selasky struct usb_device_request req; 1529963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); 1530963169b4SHans Petter Selasky 1531963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE; 1532963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR; 1533963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0); 1534963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1535963169b4SHans Petter Selasky USETW(req.wLength, len); 1536963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd)); 1537963169b4SHans Petter Selasky } 1538963169b4SHans Petter Selasky 1539963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1540a593f6b8SAndrew Thompson * usbd_req_get_hub_status 154102ac6454SAndrew Thompson * 154202ac6454SAndrew Thompson * Returns: 154302ac6454SAndrew Thompson * 0: Success 154402ac6454SAndrew Thompson * Else: Failure 154502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1546e0a69b51SAndrew Thompson usb_error_t 1547a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, 1548760bc48eSAndrew Thompson struct usb_hub_status *st) 154902ac6454SAndrew Thompson { 1550760bc48eSAndrew Thompson struct usb_device_request req; 155102ac6454SAndrew Thompson 155202ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE; 155302ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 155402ac6454SAndrew Thompson USETW(req.wValue, 0); 155502ac6454SAndrew Thompson USETW(req.wIndex, 0); 1556760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status)); 1557a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st)); 155802ac6454SAndrew Thompson } 155902ac6454SAndrew Thompson 156002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1561a593f6b8SAndrew Thompson * usbd_req_set_address 156202ac6454SAndrew Thompson * 156302ac6454SAndrew Thompson * This function is used to set the address for an USB device. After 156402ac6454SAndrew Thompson * port reset the USB device will respond at address zero. 156502ac6454SAndrew Thompson * 156602ac6454SAndrew Thompson * Returns: 156702ac6454SAndrew Thompson * 0: Success 156802ac6454SAndrew Thompson * Else: Failure 156902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1570e0a69b51SAndrew Thompson usb_error_t 1571a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) 157202ac6454SAndrew Thompson { 1573760bc48eSAndrew Thompson struct usb_device_request req; 1574963169b4SHans Petter Selasky usb_error_t err; 157502ac6454SAndrew Thompson 157602ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr); 157702ac6454SAndrew Thompson 157802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 157902ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS; 158002ac6454SAndrew Thompson USETW(req.wValue, addr); 158102ac6454SAndrew Thompson USETW(req.wIndex, 0); 158202ac6454SAndrew Thompson USETW(req.wLength, 0); 158302ac6454SAndrew Thompson 1584963169b4SHans Petter Selasky err = USB_ERR_INVAL; 1585963169b4SHans Petter Selasky 1586963169b4SHans Petter Selasky /* check if USB controller handles set address */ 1587963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL) 1588963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr); 1589963169b4SHans Petter Selasky 1590963169b4SHans Petter Selasky if (err != USB_ERR_INVAL) 1591963169b4SHans Petter Selasky goto done; 1592963169b4SHans Petter Selasky 159302ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */ 1594963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL, 1595963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000); 1596963169b4SHans Petter Selasky 1597963169b4SHans Petter Selasky done: 1598963169b4SHans Petter Selasky /* allow device time to set new address */ 1599963169b4SHans Petter Selasky usb_pause_mtx(mtx, 160037506412SHans Petter Selasky USB_MS_TO_TICKS(usb_set_address_settle)); 1601963169b4SHans Petter Selasky 1602963169b4SHans Petter Selasky return (err); 160302ac6454SAndrew Thompson } 160402ac6454SAndrew Thompson 160502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1606a593f6b8SAndrew Thompson * usbd_req_get_port_status 160702ac6454SAndrew Thompson * 160802ac6454SAndrew Thompson * Returns: 160902ac6454SAndrew Thompson * 0: Success 161002ac6454SAndrew Thompson * Else: Failure 161102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1612e0a69b51SAndrew Thompson usb_error_t 1613a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, 1614760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port) 161502ac6454SAndrew Thompson { 1616760bc48eSAndrew Thompson struct usb_device_request req; 161702ac6454SAndrew Thompson 161802ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER; 161902ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS; 162002ac6454SAndrew Thompson USETW(req.wValue, 0); 162102ac6454SAndrew Thompson req.wIndex[0] = port; 162202ac6454SAndrew Thompson req.wIndex[1] = 0; 16234604b6a1SHans Petter Selasky USETW(req.wLength, sizeof(*ps)); 16244604b6a1SHans Petter Selasky 16254604b6a1SHans Petter Selasky return (usbd_do_request_flags(udev, mtx, &req, ps, 0, NULL, 1000)); 162602ac6454SAndrew Thompson } 162702ac6454SAndrew Thompson 162802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1629a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature 163002ac6454SAndrew Thompson * 163102ac6454SAndrew Thompson * Returns: 163202ac6454SAndrew Thompson * 0: Success 163302ac6454SAndrew Thompson * Else: Failure 163402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1635e0a69b51SAndrew Thompson usb_error_t 1636a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, 163702ac6454SAndrew Thompson uint16_t sel) 163802ac6454SAndrew Thompson { 1639760bc48eSAndrew Thompson struct usb_device_request req; 164002ac6454SAndrew Thompson 164102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 164202ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 164302ac6454SAndrew Thompson USETW(req.wValue, sel); 164402ac6454SAndrew Thompson USETW(req.wIndex, 0); 164502ac6454SAndrew Thompson USETW(req.wLength, 0); 1646a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 164702ac6454SAndrew Thompson } 164802ac6454SAndrew Thompson 164902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1650a593f6b8SAndrew Thompson * usbd_req_set_hub_feature 165102ac6454SAndrew Thompson * 165202ac6454SAndrew Thompson * Returns: 165302ac6454SAndrew Thompson * 0: Success 165402ac6454SAndrew Thompson * Else: Failure 165502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1656e0a69b51SAndrew Thompson usb_error_t 1657a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, 165802ac6454SAndrew Thompson uint16_t sel) 165902ac6454SAndrew Thompson { 1660760bc48eSAndrew Thompson struct usb_device_request req; 166102ac6454SAndrew Thompson 166202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE; 166302ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 166402ac6454SAndrew Thompson USETW(req.wValue, sel); 166502ac6454SAndrew Thompson USETW(req.wIndex, 0); 166602ac6454SAndrew Thompson USETW(req.wLength, 0); 1667a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 166802ac6454SAndrew Thompson } 166902ac6454SAndrew Thompson 167002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1671963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout 1672963169b4SHans Petter Selasky * 1673963169b4SHans Petter Selasky * Returns: 1674963169b4SHans Petter Selasky * 0: Success 1675963169b4SHans Petter Selasky * Else: Failure 1676963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1677963169b4SHans Petter Selasky usb_error_t 1678963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, 1679963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1680963169b4SHans Petter Selasky { 1681963169b4SHans Petter Selasky struct usb_device_request req; 1682963169b4SHans Petter Selasky 1683963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1684963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1685963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT); 1686963169b4SHans Petter Selasky req.wIndex[0] = port; 1687963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1688963169b4SHans Petter Selasky USETW(req.wLength, 0); 1689963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1690963169b4SHans Petter Selasky } 1691963169b4SHans Petter Selasky 1692963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1693963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout 1694963169b4SHans Petter Selasky * 1695963169b4SHans Petter Selasky * Returns: 1696963169b4SHans Petter Selasky * 0: Success 1697963169b4SHans Petter Selasky * Else: Failure 1698963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1699963169b4SHans Petter Selasky usb_error_t 1700963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, 1701963169b4SHans Petter Selasky uint8_t port, uint8_t timeout) 1702963169b4SHans Petter Selasky { 1703963169b4SHans Petter Selasky struct usb_device_request req; 1704963169b4SHans Petter Selasky 1705963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 1706963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE; 1707963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT); 1708963169b4SHans Petter Selasky req.wIndex[0] = port; 1709963169b4SHans Petter Selasky req.wIndex[1] = timeout; 1710963169b4SHans Petter Selasky USETW(req.wLength, 0); 1711963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1712963169b4SHans Petter Selasky } 1713963169b4SHans Petter Selasky 1714963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1715963169b4SHans Petter Selasky * usbd_req_set_hub_depth 1716963169b4SHans Petter Selasky * 1717963169b4SHans Petter Selasky * Returns: 1718963169b4SHans Petter Selasky * 0: Success 1719963169b4SHans Petter Selasky * Else: Failure 1720963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1721963169b4SHans Petter Selasky usb_error_t 1722963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, 1723963169b4SHans Petter Selasky uint16_t depth) 1724963169b4SHans Petter Selasky { 1725963169b4SHans Petter Selasky struct usb_device_request req; 1726963169b4SHans Petter Selasky 1727963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE; 1728963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH; 1729963169b4SHans Petter Selasky USETW(req.wValue, depth); 1730963169b4SHans Petter Selasky USETW(req.wIndex, 0); 1731963169b4SHans Petter Selasky USETW(req.wLength, 0); 1732963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 1733963169b4SHans Petter Selasky } 1734963169b4SHans Petter Selasky 1735963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 1736a593f6b8SAndrew Thompson * usbd_req_clear_port_feature 173702ac6454SAndrew Thompson * 173802ac6454SAndrew Thompson * Returns: 173902ac6454SAndrew Thompson * 0: Success 174002ac6454SAndrew Thompson * Else: Failure 174102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1742e0a69b51SAndrew Thompson usb_error_t 1743a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, 174402ac6454SAndrew Thompson uint8_t port, uint16_t sel) 174502ac6454SAndrew Thompson { 1746760bc48eSAndrew Thompson struct usb_device_request req; 174702ac6454SAndrew Thompson 174802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 174902ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 175002ac6454SAndrew Thompson USETW(req.wValue, sel); 175102ac6454SAndrew Thompson req.wIndex[0] = port; 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_port_feature 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_port_feature(struct usb_device *udev, struct mtx *mtx, 176602ac6454SAndrew Thompson uint8_t port, uint16_t sel) 176702ac6454SAndrew Thompson { 1768760bc48eSAndrew Thompson struct usb_device_request req; 176902ac6454SAndrew Thompson 177002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER; 177102ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 177202ac6454SAndrew Thompson USETW(req.wValue, sel); 177302ac6454SAndrew Thompson req.wIndex[0] = port; 177402ac6454SAndrew Thompson req.wIndex[1] = 0; 177502ac6454SAndrew Thompson USETW(req.wLength, 0); 1776a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 177702ac6454SAndrew Thompson } 177802ac6454SAndrew Thompson 177902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1780a593f6b8SAndrew Thompson * usbd_req_set_protocol 178102ac6454SAndrew Thompson * 178202ac6454SAndrew Thompson * Returns: 178302ac6454SAndrew Thompson * 0: Success 178402ac6454SAndrew Thompson * Else: Failure 178502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1786e0a69b51SAndrew Thompson usb_error_t 1787a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, 178802ac6454SAndrew Thompson uint8_t iface_index, uint16_t report) 178902ac6454SAndrew Thompson { 1790a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1791760bc48eSAndrew Thompson struct usb_device_request req; 179202ac6454SAndrew Thompson 179302ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 179402ac6454SAndrew Thompson return (USB_ERR_INVAL); 179502ac6454SAndrew Thompson } 179602ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", 179702ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber); 179802ac6454SAndrew Thompson 179902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 180002ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL; 180102ac6454SAndrew Thompson USETW(req.wValue, report); 180202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 180302ac6454SAndrew Thompson req.wIndex[1] = 0; 180402ac6454SAndrew Thompson USETW(req.wLength, 0); 1805a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 180602ac6454SAndrew Thompson } 180702ac6454SAndrew Thompson 180802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1809a593f6b8SAndrew Thompson * usbd_req_set_report 181002ac6454SAndrew Thompson * 181102ac6454SAndrew Thompson * Returns: 181202ac6454SAndrew Thompson * 0: Success 181302ac6454SAndrew Thompson * Else: Failure 181402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1815e0a69b51SAndrew Thompson usb_error_t 1816a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, 181702ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id) 181802ac6454SAndrew Thompson { 1819a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1820760bc48eSAndrew Thompson struct usb_device_request req; 182102ac6454SAndrew Thompson 182202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 182302ac6454SAndrew Thompson return (USB_ERR_INVAL); 182402ac6454SAndrew Thompson } 182502ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 182602ac6454SAndrew Thompson 182702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 182802ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT; 182902ac6454SAndrew Thompson USETW2(req.wValue, type, id); 183002ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 183102ac6454SAndrew Thompson req.wIndex[1] = 0; 183202ac6454SAndrew Thompson USETW(req.wLength, len); 1833a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 183402ac6454SAndrew Thompson } 183502ac6454SAndrew Thompson 183602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1837a593f6b8SAndrew Thompson * usbd_req_get_report 183802ac6454SAndrew Thompson * 183902ac6454SAndrew Thompson * Returns: 184002ac6454SAndrew Thompson * 0: Success 184102ac6454SAndrew Thompson * Else: Failure 184202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1843e0a69b51SAndrew Thompson usb_error_t 1844a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, 184502ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) 184602ac6454SAndrew Thompson { 1847a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1848760bc48eSAndrew Thompson struct usb_device_request req; 184902ac6454SAndrew Thompson 1850477a63a8SAlexander Motin if ((iface == NULL) || (iface->idesc == NULL)) { 185102ac6454SAndrew Thompson return (USB_ERR_INVAL); 185202ac6454SAndrew Thompson } 185302ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len); 185402ac6454SAndrew Thompson 185502ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE; 185602ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT; 185702ac6454SAndrew Thompson USETW2(req.wValue, type, id); 185802ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 185902ac6454SAndrew Thompson req.wIndex[1] = 0; 186002ac6454SAndrew Thompson USETW(req.wLength, len); 1861a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data)); 186202ac6454SAndrew Thompson } 186302ac6454SAndrew Thompson 186402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1865a593f6b8SAndrew Thompson * usbd_req_set_idle 186602ac6454SAndrew Thompson * 186702ac6454SAndrew Thompson * Returns: 186802ac6454SAndrew Thompson * 0: Success 186902ac6454SAndrew Thompson * Else: Failure 187002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1871e0a69b51SAndrew Thompson usb_error_t 1872a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, 187302ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id) 187402ac6454SAndrew Thompson { 1875a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1876760bc48eSAndrew Thompson struct usb_device_request req; 187702ac6454SAndrew Thompson 187802ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 187902ac6454SAndrew Thompson return (USB_ERR_INVAL); 188002ac6454SAndrew Thompson } 188102ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id); 188202ac6454SAndrew Thompson 188302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 188402ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE; 188502ac6454SAndrew Thompson USETW2(req.wValue, duration, id); 188602ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 188702ac6454SAndrew Thompson req.wIndex[1] = 0; 188802ac6454SAndrew Thompson USETW(req.wLength, 0); 1889a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 189002ac6454SAndrew Thompson } 189102ac6454SAndrew Thompson 189202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1893a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor 189402ac6454SAndrew Thompson * 189502ac6454SAndrew Thompson * Returns: 189602ac6454SAndrew Thompson * 0: Success 189702ac6454SAndrew Thompson * Else: Failure 189802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1899e0a69b51SAndrew Thompson usb_error_t 1900a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, 190102ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index) 190202ac6454SAndrew Thompson { 1903a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 1904760bc48eSAndrew Thompson struct usb_device_request req; 190502ac6454SAndrew Thompson 190602ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 190702ac6454SAndrew Thompson return (USB_ERR_INVAL); 190802ac6454SAndrew Thompson } 190902ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE; 191002ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR; 191102ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 191202ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber; 191302ac6454SAndrew Thompson req.wIndex[1] = 0; 191402ac6454SAndrew Thompson USETW(req.wLength, size); 1915a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d)); 191602ac6454SAndrew Thompson } 191702ac6454SAndrew Thompson 191802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1919a593f6b8SAndrew Thompson * usbd_req_set_config 192002ac6454SAndrew Thompson * 192102ac6454SAndrew Thompson * This function is used to select the current configuration number in 192202ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the 192302ac6454SAndrew Thompson * configuration the function of the interfaces can change. 192402ac6454SAndrew Thompson * 192502ac6454SAndrew Thompson * Returns: 192602ac6454SAndrew Thompson * 0: Success 192702ac6454SAndrew Thompson * Else: Failure 192802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1929e0a69b51SAndrew Thompson usb_error_t 1930a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) 193102ac6454SAndrew Thompson { 1932760bc48eSAndrew Thompson struct usb_device_request req; 193302ac6454SAndrew Thompson 193402ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf); 193502ac6454SAndrew Thompson 193602ac6454SAndrew Thompson /* do "set configuration" request */ 193702ac6454SAndrew Thompson 193802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 193902ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG; 194002ac6454SAndrew Thompson req.wValue[0] = conf; 194102ac6454SAndrew Thompson req.wValue[1] = 0; 194202ac6454SAndrew Thompson USETW(req.wIndex, 0); 194302ac6454SAndrew Thompson USETW(req.wLength, 0); 1944a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 194502ac6454SAndrew Thompson } 194602ac6454SAndrew Thompson 194702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1948a593f6b8SAndrew Thompson * usbd_req_get_config 194902ac6454SAndrew Thompson * 195002ac6454SAndrew Thompson * Returns: 195102ac6454SAndrew Thompson * 0: Success 195202ac6454SAndrew Thompson * Else: Failure 195302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1954e0a69b51SAndrew Thompson usb_error_t 1955a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) 195602ac6454SAndrew Thompson { 1957760bc48eSAndrew Thompson struct usb_device_request req; 195802ac6454SAndrew Thompson 195902ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE; 196002ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG; 196102ac6454SAndrew Thompson USETW(req.wValue, 0); 196202ac6454SAndrew Thompson USETW(req.wIndex, 0); 196302ac6454SAndrew Thompson USETW(req.wLength, 1); 1964a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf)); 196502ac6454SAndrew Thompson } 196602ac6454SAndrew Thompson 196702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1968963169b4SHans Petter Selasky * usbd_setup_device_desc 1969963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 1970963169b4SHans Petter Selasky usb_error_t 1971963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) 1972963169b4SHans Petter Selasky { 1973963169b4SHans Petter Selasky usb_error_t err; 1974963169b4SHans Petter Selasky 1975963169b4SHans Petter Selasky /* 1976963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor ! 1977963169b4SHans Petter Selasky * 1978963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor 1979963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size 1980963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor 1981963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint 1982963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new 1983963169b4SHans Petter Selasky * USB control request will be setup! 1984963169b4SHans Petter Selasky */ 1985963169b4SHans Petter Selasky switch (udev->speed) { 1986963169b4SHans Petter Selasky case USB_SPEED_FULL: 198723de050bSHans Petter Selasky if (usb_full_ddesc != 0) { 198823de050bSHans Petter Selasky /* get full device descriptor */ 198923de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 199023de050bSHans Petter Selasky if (err == 0) 199123de050bSHans Petter Selasky break; 199223de050bSHans Petter Selasky } 199323de050bSHans Petter Selasky 199423de050bSHans Petter Selasky /* get partial device descriptor, some devices crash on this */ 1995963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, 1996963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); 1997c3a38d6cSHans Petter Selasky if (err != 0) { 1998c3a38d6cSHans Petter Selasky DPRINTF("Trying fallback for getting the USB device descriptor\n"); 1999c3a38d6cSHans Petter Selasky /* try 8 bytes bMaxPacketSize */ 2000c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 8; 2001c3a38d6cSHans Petter Selasky /* get full device descriptor */ 2002c3a38d6cSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 2003c3a38d6cSHans Petter Selasky if (err == 0) 2004963169b4SHans Petter Selasky break; 2005c3a38d6cSHans Petter Selasky /* try 16 bytes bMaxPacketSize */ 2006c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 16; 2007c3a38d6cSHans Petter Selasky /* get full device descriptor */ 2008c3a38d6cSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 2009c3a38d6cSHans Petter Selasky if (err == 0) 2010c3a38d6cSHans Petter Selasky break; 2011c3a38d6cSHans Petter Selasky /* try 32/64 bytes bMaxPacketSize */ 2012c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 32; 2013c3a38d6cSHans Petter Selasky } 201423de050bSHans Petter Selasky /* get the full device descriptor */ 201523de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 201623de050bSHans Petter Selasky break; 201723de050bSHans Petter Selasky 2018963169b4SHans Petter Selasky default: 2019a1a10f53SHans Petter Selasky DPRINTF("Minimum bMaxPacketSize is large enough " 202023de050bSHans Petter Selasky "to hold the complete device descriptor or " 2021a1a10f53SHans Petter Selasky "only one bMaxPacketSize choice\n"); 2022963169b4SHans Petter Selasky 2023963169b4SHans Petter Selasky /* get the full device descriptor */ 2024963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 2025963169b4SHans Petter Selasky 2026963169b4SHans Petter Selasky /* try one more time, if error */ 202723de050bSHans Petter Selasky if (err != 0) 2028963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); 202923de050bSHans Petter Selasky break; 203023de050bSHans Petter Selasky } 2031963169b4SHans Petter Selasky 203223de050bSHans Petter Selasky if (err != 0) { 203323de050bSHans Petter Selasky DPRINTFN(0, "getting device descriptor " 203423de050bSHans Petter Selasky "at addr %d failed, %s\n", udev->address, 203523de050bSHans Petter Selasky usbd_errstr(err)); 2036963169b4SHans Petter Selasky return (err); 2037963169b4SHans Petter Selasky } 2038963169b4SHans Petter Selasky 2039963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " 2040963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", 2041963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB), 2042963169b4SHans Petter Selasky udev->ddesc.bDeviceClass, 2043963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass, 2044963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol, 2045963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize, 2046963169b4SHans Petter Selasky udev->ddesc.bLength, 2047963169b4SHans Petter Selasky udev->speed); 2048963169b4SHans Petter Selasky 2049963169b4SHans Petter Selasky return (err); 2050963169b4SHans Petter Selasky } 2051963169b4SHans Petter Selasky 2052963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 2053a593f6b8SAndrew Thompson * usbd_req_re_enumerate 205402ac6454SAndrew Thompson * 205502ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the 205602ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a 205702ac6454SAndrew Thompson * new configuration. 205802ac6454SAndrew Thompson * 205902ac6454SAndrew Thompson * Returns: 206002ac6454SAndrew Thompson * 0: Success 206102ac6454SAndrew Thompson * Else: Failure 206202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2063e0a69b51SAndrew Thompson usb_error_t 2064a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) 206502ac6454SAndrew Thompson { 2066760bc48eSAndrew Thompson struct usb_device *parent_hub; 2067e0a69b51SAndrew Thompson usb_error_t err; 206802ac6454SAndrew Thompson uint8_t old_addr; 206902ac6454SAndrew Thompson uint8_t do_retry = 1; 207002ac6454SAndrew Thompson 2071f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) { 207202ac6454SAndrew Thompson return (USB_ERR_INVAL); 207302ac6454SAndrew Thompson } 207402ac6454SAndrew Thompson old_addr = udev->address; 207502ac6454SAndrew Thompson parent_hub = udev->parent_hub; 207602ac6454SAndrew Thompson if (parent_hub == NULL) { 207702ac6454SAndrew Thompson return (USB_ERR_INVAL); 207802ac6454SAndrew Thompson } 207902ac6454SAndrew Thompson retry: 2080a0d53e0bSHans Petter Selasky #if USB_HAVE_TT_SUPPORT 20819eb0d702SHans Petter Selasky /* 20829eb0d702SHans Petter Selasky * Try to reset the High Speed parent HUB of a LOW- or FULL- 20839eb0d702SHans Petter Selasky * speed device, if any. 20849eb0d702SHans Petter Selasky */ 20859eb0d702SHans Petter Selasky if (udev->parent_hs_hub != NULL && 20869eb0d702SHans Petter Selasky udev->speed != USB_SPEED_HIGH) { 20879eb0d702SHans Petter Selasky DPRINTF("Trying to reset parent High Speed TT.\n"); 2088a0d53e0bSHans Petter Selasky if (udev->parent_hs_hub == parent_hub && 2089a0d53e0bSHans Petter Selasky (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) + 2090a0d53e0bSHans Petter Selasky uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) { 2091a0d53e0bSHans Petter Selasky /* we can reset the whole TT */ 2092a0d53e0bSHans Petter Selasky err = usbd_req_reset_tt(parent_hub, NULL, 20939eb0d702SHans Petter Selasky udev->hs_port_no); 2094a0d53e0bSHans Petter Selasky } else { 2095a0d53e0bSHans Petter Selasky /* only reset a particular device and endpoint */ 2096a0d53e0bSHans Petter Selasky err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL, 2097a0d53e0bSHans Petter Selasky udev->hs_port_no, old_addr, UE_CONTROL, 0); 2098a0d53e0bSHans Petter Selasky } 20999eb0d702SHans Petter Selasky if (err) { 21009eb0d702SHans Petter Selasky DPRINTF("Resetting parent High " 21019eb0d702SHans Petter Selasky "Speed TT failed (%s).\n", 21029eb0d702SHans Petter Selasky usbd_errstr(err)); 21039eb0d702SHans Petter Selasky } 21049eb0d702SHans Petter Selasky } 2105a0d53e0bSHans Petter Selasky #endif 210693ee6e85SHans Petter Selasky /* Try to warm reset first */ 210793ee6e85SHans Petter Selasky if (parent_hub->speed == USB_SPEED_SUPER) 210893ee6e85SHans Petter Selasky usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); 210993ee6e85SHans Petter Selasky 21109eb0d702SHans Petter Selasky /* Try to reset the parent HUB port. */ 2111a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); 211202ac6454SAndrew Thompson if (err) { 211303797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n", 2114a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 211502ac6454SAndrew Thompson goto done; 211602ac6454SAndrew Thompson } 2117963169b4SHans Petter Selasky 211802ac6454SAndrew Thompson /* 211902ac6454SAndrew Thompson * After that the port has been reset our device should be at 212002ac6454SAndrew Thompson * address zero: 212102ac6454SAndrew Thompson */ 212202ac6454SAndrew Thompson udev->address = USB_START_ADDR; 212302ac6454SAndrew Thompson 212402ac6454SAndrew Thompson /* reset "bMaxPacketSize" */ 212502ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; 212602ac6454SAndrew Thompson 2127963169b4SHans Petter Selasky /* reset USB state */ 2128963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED); 2129963169b4SHans Petter Selasky 213002ac6454SAndrew Thompson /* 213102ac6454SAndrew Thompson * Restore device address: 213202ac6454SAndrew Thompson */ 2133a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr); 213402ac6454SAndrew Thompson if (err) { 213502ac6454SAndrew Thompson /* XXX ignore any errors! */ 213603797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", 2137a593f6b8SAndrew Thompson old_addr, usbd_errstr(err)); 213802ac6454SAndrew Thompson } 2139963169b4SHans Petter Selasky /* 2140963169b4SHans Petter Selasky * Restore device address, if the controller driver did not 2141963169b4SHans Petter Selasky * set a new one: 2142963169b4SHans Petter Selasky */ 2143963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 214402ac6454SAndrew Thompson udev->address = old_addr; 214502ac6454SAndrew Thompson 2146963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */ 2147963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx); 214802ac6454SAndrew Thompson 214902ac6454SAndrew Thompson done: 215002ac6454SAndrew Thompson if (err && do_retry) { 215102ac6454SAndrew Thompson /* give the USB firmware some time to load */ 2152a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2); 215302ac6454SAndrew Thompson /* no more retries after this retry */ 215402ac6454SAndrew Thompson do_retry = 0; 215502ac6454SAndrew Thompson /* try again */ 215602ac6454SAndrew Thompson goto retry; 215702ac6454SAndrew Thompson } 215802ac6454SAndrew Thompson /* restore address */ 2159963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR) 216002ac6454SAndrew Thompson udev->address = old_addr; 2161963169b4SHans Petter Selasky /* update state, if successful */ 2162963169b4SHans Petter Selasky if (err == 0) 2163963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED); 216402ac6454SAndrew Thompson return (err); 216502ac6454SAndrew Thompson } 216602ac6454SAndrew Thompson 216702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2168a593f6b8SAndrew Thompson * usbd_req_clear_device_feature 216902ac6454SAndrew Thompson * 217002ac6454SAndrew Thompson * Returns: 217102ac6454SAndrew Thompson * 0: Success 217202ac6454SAndrew Thompson * Else: Failure 217302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2174e0a69b51SAndrew Thompson usb_error_t 2175a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, 217602ac6454SAndrew Thompson uint16_t sel) 217702ac6454SAndrew Thompson { 2178760bc48eSAndrew Thompson struct usb_device_request req; 217902ac6454SAndrew Thompson 218002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 218102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 218202ac6454SAndrew Thompson USETW(req.wValue, sel); 218302ac6454SAndrew Thompson USETW(req.wIndex, 0); 218402ac6454SAndrew Thompson USETW(req.wLength, 0); 2185a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 218602ac6454SAndrew Thompson } 218702ac6454SAndrew Thompson 218802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2189a593f6b8SAndrew Thompson * usbd_req_set_device_feature 219002ac6454SAndrew Thompson * 219102ac6454SAndrew Thompson * Returns: 219202ac6454SAndrew Thompson * 0: Success 219302ac6454SAndrew Thompson * Else: Failure 219402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2195e0a69b51SAndrew Thompson usb_error_t 2196a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, 219702ac6454SAndrew Thompson uint16_t sel) 219802ac6454SAndrew Thompson { 2199760bc48eSAndrew Thompson struct usb_device_request req; 220002ac6454SAndrew Thompson 220102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE; 220202ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE; 220302ac6454SAndrew Thompson USETW(req.wValue, sel); 220402ac6454SAndrew Thompson USETW(req.wIndex, 0); 220502ac6454SAndrew Thompson USETW(req.wLength, 0); 2206a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0)); 220702ac6454SAndrew Thompson } 22089eb0d702SHans Petter Selasky 22099eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 22109eb0d702SHans Petter Selasky * usbd_req_reset_tt 22119eb0d702SHans Petter Selasky * 22129eb0d702SHans Petter Selasky * Returns: 22139eb0d702SHans Petter Selasky * 0: Success 22149eb0d702SHans Petter Selasky * Else: Failure 22159eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 22169eb0d702SHans Petter Selasky usb_error_t 22179eb0d702SHans Petter Selasky usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, 22189eb0d702SHans Petter Selasky uint8_t port) 22199eb0d702SHans Petter Selasky { 22209eb0d702SHans Petter Selasky struct usb_device_request req; 22219eb0d702SHans Petter Selasky 22229eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 22239eb0d702SHans Petter Selasky 22249eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 22259eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 22269eb0d702SHans Petter Selasky port = 1; 22279eb0d702SHans Petter Selasky 22289eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 22299eb0d702SHans Petter Selasky req.bRequest = UR_RESET_TT; 22309eb0d702SHans Petter Selasky USETW(req.wValue, 0); 22319eb0d702SHans Petter Selasky req.wIndex[0] = port; 22329eb0d702SHans Petter Selasky req.wIndex[1] = 0; 22339eb0d702SHans Petter Selasky USETW(req.wLength, 0); 22349eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 22359eb0d702SHans Petter Selasky } 22369eb0d702SHans Petter Selasky 22379eb0d702SHans Petter Selasky /*------------------------------------------------------------------------* 22389eb0d702SHans Petter Selasky * usbd_req_clear_tt_buffer 22399eb0d702SHans Petter Selasky * 22409eb0d702SHans Petter Selasky * For single TT HUBs the port should be 1. 22419eb0d702SHans Petter Selasky * 22429eb0d702SHans Petter Selasky * Returns: 22439eb0d702SHans Petter Selasky * 0: Success 22449eb0d702SHans Petter Selasky * Else: Failure 22459eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/ 22469eb0d702SHans Petter Selasky usb_error_t 22479eb0d702SHans Petter Selasky usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, 22489eb0d702SHans Petter Selasky uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) 22499eb0d702SHans Petter Selasky { 22509eb0d702SHans Petter Selasky struct usb_device_request req; 22519eb0d702SHans Petter Selasky uint16_t wValue; 22529eb0d702SHans Petter Selasky 22539eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */ 22549eb0d702SHans Petter Selasky 22559eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB && 22569eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) 22579eb0d702SHans Petter Selasky port = 1; 22589eb0d702SHans Petter Selasky 22599eb0d702SHans Petter Selasky wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | 22609eb0d702SHans Petter Selasky ((endpoint & 0x80) << 8) | ((type & 3) << 12); 22619eb0d702SHans Petter Selasky 22629eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 22639eb0d702SHans Petter Selasky req.bRequest = UR_CLEAR_TT_BUFFER; 22649eb0d702SHans Petter Selasky USETW(req.wValue, wValue); 22659eb0d702SHans Petter Selasky req.wIndex[0] = port; 22669eb0d702SHans Petter Selasky req.wIndex[1] = 0; 22679eb0d702SHans Petter Selasky USETW(req.wLength, 0); 22689eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 22699eb0d702SHans Petter Selasky } 22704131f6fbSHans Petter Selasky 22714131f6fbSHans Petter Selasky /*------------------------------------------------------------------------* 22724131f6fbSHans Petter Selasky * usbd_req_set_port_link_state 22734131f6fbSHans Petter Selasky * 22744131f6fbSHans Petter Selasky * USB 3.0 specific request 22754131f6fbSHans Petter Selasky * 22764131f6fbSHans Petter Selasky * Returns: 22774131f6fbSHans Petter Selasky * 0: Success 22784131f6fbSHans Petter Selasky * Else: Failure 22794131f6fbSHans Petter Selasky *------------------------------------------------------------------------*/ 22804131f6fbSHans Petter Selasky usb_error_t 22814131f6fbSHans Petter Selasky usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, 22824131f6fbSHans Petter Selasky uint8_t port, uint8_t link_state) 22834131f6fbSHans Petter Selasky { 22844131f6fbSHans Petter Selasky struct usb_device_request req; 22854131f6fbSHans Petter Selasky 22864131f6fbSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 22874131f6fbSHans Petter Selasky req.bRequest = UR_SET_FEATURE; 22884131f6fbSHans Petter Selasky USETW(req.wValue, UHF_PORT_LINK_STATE); 22894131f6fbSHans Petter Selasky req.wIndex[0] = port; 22904131f6fbSHans Petter Selasky req.wIndex[1] = link_state; 22914131f6fbSHans Petter Selasky USETW(req.wLength, 0); 22924131f6fbSHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0)); 22934131f6fbSHans Petter Selasky } 22944563ba7aSHans Petter Selasky 22954563ba7aSHans Petter Selasky /*------------------------------------------------------------------------* 22964563ba7aSHans Petter Selasky * usbd_req_set_lpm_info 22974563ba7aSHans Petter Selasky * 22984563ba7aSHans Petter Selasky * USB 2.0 specific request for Link Power Management. 22994563ba7aSHans Petter Selasky * 23004563ba7aSHans Petter Selasky * Returns: 23014563ba7aSHans Petter Selasky * 0: Success 23024563ba7aSHans Petter Selasky * USB_ERR_PENDING_REQUESTS: NYET 23034563ba7aSHans Petter Selasky * USB_ERR_TIMEOUT: TIMEOUT 23044563ba7aSHans Petter Selasky * USB_ERR_STALL: STALL 23054563ba7aSHans Petter Selasky * Else: Failure 23064563ba7aSHans Petter Selasky *------------------------------------------------------------------------*/ 23074563ba7aSHans Petter Selasky usb_error_t 23084563ba7aSHans Petter Selasky usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx, 23094563ba7aSHans Petter Selasky uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe) 23104563ba7aSHans Petter Selasky { 23114563ba7aSHans Petter Selasky struct usb_device_request req; 23124563ba7aSHans Petter Selasky usb_error_t err; 23134563ba7aSHans Petter Selasky uint8_t buf[1]; 23144563ba7aSHans Petter Selasky 23154563ba7aSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER; 23164563ba7aSHans Petter Selasky req.bRequest = UR_SET_AND_TEST; 23174563ba7aSHans Petter Selasky USETW(req.wValue, UHF_PORT_L1); 23184563ba7aSHans Petter Selasky req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4); 23194563ba7aSHans Petter Selasky req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00); 23204563ba7aSHans Petter Selasky USETW(req.wLength, sizeof(buf)); 23214563ba7aSHans Petter Selasky 23224563ba7aSHans Petter Selasky /* set default value in case of short transfer */ 23234563ba7aSHans Petter Selasky buf[0] = 0x00; 23244563ba7aSHans Petter Selasky 23254563ba7aSHans Petter Selasky err = usbd_do_request(udev, mtx, &req, buf); 23264563ba7aSHans Petter Selasky if (err) 23274563ba7aSHans Petter Selasky return (err); 23284563ba7aSHans Petter Selasky 23294563ba7aSHans Petter Selasky switch (buf[0]) { 23304563ba7aSHans Petter Selasky case 0x00: /* SUCCESS */ 23314563ba7aSHans Petter Selasky break; 23324563ba7aSHans Petter Selasky case 0x10: /* NYET */ 23334563ba7aSHans Petter Selasky err = USB_ERR_PENDING_REQUESTS; 23344563ba7aSHans Petter Selasky break; 23354563ba7aSHans Petter Selasky case 0x11: /* TIMEOUT */ 23364563ba7aSHans Petter Selasky err = USB_ERR_TIMEOUT; 23374563ba7aSHans Petter Selasky break; 23384563ba7aSHans Petter Selasky case 0x30: /* STALL */ 23394563ba7aSHans Petter Selasky err = USB_ERR_STALLED; 23404563ba7aSHans Petter Selasky break; 23414563ba7aSHans Petter Selasky default: /* reserved */ 23424563ba7aSHans Petter Selasky err = USB_ERR_IOERROR; 23434563ba7aSHans Petter Selasky break; 23444563ba7aSHans Petter Selasky } 23454563ba7aSHans Petter Selasky return (err); 23464563ba7aSHans Petter Selasky } 2347