102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4*718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 1602ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1702ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1802ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1902ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2002ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2102ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2202ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2302ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2402ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2502ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2602ac6454SAndrew Thompson * SUCH DAMAGE. 2702ac6454SAndrew Thompson */ 2802ac6454SAndrew Thompson 29d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #else 32ed6d949aSAndrew Thompson #include <sys/stdint.h> 33ed6d949aSAndrew Thompson #include <sys/stddef.h> 34ed6d949aSAndrew Thompson #include <sys/param.h> 35ed6d949aSAndrew Thompson #include <sys/queue.h> 36ed6d949aSAndrew Thompson #include <sys/types.h> 37ed6d949aSAndrew Thompson #include <sys/systm.h> 38ed6d949aSAndrew Thompson #include <sys/kernel.h> 39ed6d949aSAndrew Thompson #include <sys/bus.h> 40ed6d949aSAndrew Thompson #include <sys/module.h> 41ed6d949aSAndrew Thompson #include <sys/lock.h> 42ed6d949aSAndrew Thompson #include <sys/mutex.h> 43ed6d949aSAndrew Thompson #include <sys/condvar.h> 44ed6d949aSAndrew Thompson #include <sys/sysctl.h> 45ed6d949aSAndrew Thompson #include <sys/sx.h> 46ed6d949aSAndrew Thompson #include <sys/unistd.h> 47ed6d949aSAndrew Thompson #include <sys/callout.h> 48ed6d949aSAndrew Thompson #include <sys/malloc.h> 49ed6d949aSAndrew Thompson #include <sys/priv.h> 5002ac6454SAndrew Thompson 51ed6d949aSAndrew Thompson #include <dev/usb/usb.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 53ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 545b0752bbSHans Petter Selasky 555b0752bbSHans Petter Selasky #define USB_DEBUG_VAR usb_debug 565b0752bbSHans Petter Selasky 575b0752bbSHans Petter Selasky #include <dev/usb/usb_core.h> 585b0752bbSHans Petter Selasky #include <dev/usb/usb_debug.h> 59d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6002ac6454SAndrew Thompson 6102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 62a593f6b8SAndrew Thompson * usb_desc_foreach 6302ac6454SAndrew Thompson * 6402ac6454SAndrew Thompson * This function is the safe way to iterate across the USB config 6502ac6454SAndrew Thompson * descriptor. It contains several checks against invalid 6602ac6454SAndrew Thompson * descriptors. If the "desc" argument passed to this function is 6702ac6454SAndrew Thompson * "NULL" the first descriptor, if any, will be returned. 6802ac6454SAndrew Thompson * 6902ac6454SAndrew Thompson * Return values: 7002ac6454SAndrew Thompson * NULL: End of descriptors 7102ac6454SAndrew Thompson * Else: Next descriptor after "desc" 7202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 73760bc48eSAndrew Thompson struct usb_descriptor * 74a593f6b8SAndrew Thompson usb_desc_foreach(struct usb_config_descriptor *cd, 75760bc48eSAndrew Thompson struct usb_descriptor *_desc) 7602ac6454SAndrew Thompson { 7702ac6454SAndrew Thompson uint8_t *desc_next; 7802ac6454SAndrew Thompson uint8_t *start; 7902ac6454SAndrew Thompson uint8_t *end; 8002ac6454SAndrew Thompson uint8_t *desc; 8102ac6454SAndrew Thompson 8202ac6454SAndrew Thompson /* be NULL safe */ 8302ac6454SAndrew Thompson if (cd == NULL) 8402ac6454SAndrew Thompson return (NULL); 8502ac6454SAndrew Thompson 8602ac6454SAndrew Thompson /* We assume that the "wTotalLength" has been checked. */ 8702ac6454SAndrew Thompson start = (uint8_t *)cd; 8802ac6454SAndrew Thompson end = start + UGETW(cd->wTotalLength); 8902ac6454SAndrew Thompson desc = (uint8_t *)_desc; 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson /* Get start of next USB descriptor. */ 9202ac6454SAndrew Thompson if (desc == NULL) 9302ac6454SAndrew Thompson desc = start; 9402ac6454SAndrew Thompson else 9502ac6454SAndrew Thompson desc = desc + desc[0]; 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson /* Check that the next USB descriptor is within the range. */ 9802ac6454SAndrew Thompson if ((desc < start) || (desc >= end)) 9902ac6454SAndrew Thompson return (NULL); /* out of range, or EOD */ 10002ac6454SAndrew Thompson 10102ac6454SAndrew Thompson /* Check that the second next USB descriptor is within range. */ 10202ac6454SAndrew Thompson desc_next = desc + desc[0]; 10302ac6454SAndrew Thompson if ((desc_next < start) || (desc_next > end)) 10402ac6454SAndrew Thompson return (NULL); /* out of range */ 10502ac6454SAndrew Thompson 10602ac6454SAndrew Thompson /* Check minimum descriptor length. */ 10702ac6454SAndrew Thompson if (desc[0] < 3) 10802ac6454SAndrew Thompson return (NULL); /* too short descriptor */ 10902ac6454SAndrew Thompson 11002ac6454SAndrew Thompson /* Return start of next descriptor. */ 111760bc48eSAndrew Thompson return ((struct usb_descriptor *)desc); 11202ac6454SAndrew Thompson } 11302ac6454SAndrew Thompson 11402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 115a593f6b8SAndrew Thompson * usb_idesc_foreach 11602ac6454SAndrew Thompson * 117bdd41206SAndrew Thompson * This function will iterate the interface descriptors in the config 118bdd41206SAndrew Thompson * descriptor. The parse state structure should be zeroed before 119bdd41206SAndrew Thompson * calling this function the first time. 12002ac6454SAndrew Thompson * 12102ac6454SAndrew Thompson * Return values: 12202ac6454SAndrew Thompson * NULL: End of descriptors 12302ac6454SAndrew Thompson * Else: A valid interface descriptor 12402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 125760bc48eSAndrew Thompson struct usb_interface_descriptor * 126a593f6b8SAndrew Thompson usb_idesc_foreach(struct usb_config_descriptor *cd, 127760bc48eSAndrew Thompson struct usb_idesc_parse_state *ps) 12802ac6454SAndrew Thompson { 129760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 130bdd41206SAndrew Thompson uint8_t new_iface; 13102ac6454SAndrew Thompson 132bdd41206SAndrew Thompson /* retrieve current descriptor */ 133760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ps->desc; 134bdd41206SAndrew Thompson /* default is to start a new interface */ 135bdd41206SAndrew Thompson new_iface = 1; 13602ac6454SAndrew Thompson 137bdd41206SAndrew Thompson while (1) { 138760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *) 139a593f6b8SAndrew Thompson usb_desc_foreach(cd, (struct usb_descriptor *)id); 140bdd41206SAndrew Thompson if (id == NULL) 141bdd41206SAndrew Thompson break; 142bdd41206SAndrew Thompson if ((id->bDescriptorType == UDESC_INTERFACE) && 143bdd41206SAndrew Thompson (id->bLength >= sizeof(*id))) { 144bdd41206SAndrew Thompson if (ps->iface_no_last == id->bInterfaceNumber) 145bdd41206SAndrew Thompson new_iface = 0; 146bdd41206SAndrew Thompson ps->iface_no_last = id->bInterfaceNumber; 147bdd41206SAndrew Thompson break; 148bdd41206SAndrew Thompson } 149bdd41206SAndrew Thompson } 15002ac6454SAndrew Thompson 151bdd41206SAndrew Thompson if (ps->desc == NULL) { 1525b0752bbSHans Petter Selasky /* first time or zero descriptors */ 153bdd41206SAndrew Thompson } else if (new_iface) { 154bdd41206SAndrew Thompson /* new interface */ 155bdd41206SAndrew Thompson ps->iface_index ++; 156bdd41206SAndrew Thompson ps->iface_index_alt = 0; 15702ac6454SAndrew Thompson } else { 158bdd41206SAndrew Thompson /* new alternate interface */ 159bdd41206SAndrew Thompson ps->iface_index_alt ++; 16002ac6454SAndrew Thompson } 1615b0752bbSHans Petter Selasky #if (USB_IFACE_MAX <= 0) 1625b0752bbSHans Petter Selasky #error "USB_IFACE_MAX must be defined greater than zero" 1635b0752bbSHans Petter Selasky #endif 1645b0752bbSHans Petter Selasky /* check for too many interfaces */ 1655b0752bbSHans Petter Selasky if (ps->iface_index >= USB_IFACE_MAX) { 1665b0752bbSHans Petter Selasky DPRINTF("Interface limit reached\n"); 1675b0752bbSHans Petter Selasky id = NULL; 1685b0752bbSHans Petter Selasky } 16902ac6454SAndrew Thompson 170bdd41206SAndrew Thompson /* store and return current descriptor */ 171760bc48eSAndrew Thompson ps->desc = (struct usb_descriptor *)id; 17202ac6454SAndrew Thompson return (id); 17302ac6454SAndrew Thompson } 17402ac6454SAndrew Thompson 17502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 176a593f6b8SAndrew Thompson * usb_edesc_foreach 17702ac6454SAndrew Thompson * 178bdd41206SAndrew Thompson * This function will iterate all the endpoint descriptors within an 179bdd41206SAndrew Thompson * interface descriptor. Starting value for the "ped" argument should 180bdd41206SAndrew Thompson * be a valid interface descriptor. 18102ac6454SAndrew Thompson * 18202ac6454SAndrew Thompson * Return values: 18302ac6454SAndrew Thompson * NULL: End of descriptors 18402ac6454SAndrew Thompson * Else: A valid endpoint descriptor 18502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 186760bc48eSAndrew Thompson struct usb_endpoint_descriptor * 187a593f6b8SAndrew Thompson usb_edesc_foreach(struct usb_config_descriptor *cd, 188760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ped) 18902ac6454SAndrew Thompson { 190760bc48eSAndrew Thompson struct usb_descriptor *desc; 19102ac6454SAndrew Thompson 192760bc48eSAndrew Thompson desc = ((struct usb_descriptor *)ped); 19302ac6454SAndrew Thompson 194a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 19502ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_INTERFACE) { 19602ac6454SAndrew Thompson break; 19702ac6454SAndrew Thompson } 19802ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_ENDPOINT) { 199bdd41206SAndrew Thompson if (desc->bLength < sizeof(*ped)) { 200963169b4SHans Petter Selasky /* endpoint descriptor is invalid */ 20102ac6454SAndrew Thompson break; 20202ac6454SAndrew Thompson } 203760bc48eSAndrew Thompson return ((struct usb_endpoint_descriptor *)desc); 20402ac6454SAndrew Thompson } 20502ac6454SAndrew Thompson } 20602ac6454SAndrew Thompson return (NULL); 20702ac6454SAndrew Thompson } 20802ac6454SAndrew Thompson 20902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 210963169b4SHans Petter Selasky * usb_ed_comp_foreach 211963169b4SHans Petter Selasky * 212963169b4SHans Petter Selasky * This function will iterate all the endpoint companion descriptors 213963169b4SHans Petter Selasky * within an endpoint descriptor in an interface descriptor. Starting 214963169b4SHans Petter Selasky * value for the "ped" argument should be a valid endpoint companion 215963169b4SHans Petter Selasky * descriptor. 216963169b4SHans Petter Selasky * 217963169b4SHans Petter Selasky * Return values: 218963169b4SHans Petter Selasky * NULL: End of descriptors 219963169b4SHans Petter Selasky * Else: A valid endpoint companion descriptor 220963169b4SHans Petter Selasky *------------------------------------------------------------------------*/ 221963169b4SHans Petter Selasky struct usb_endpoint_ss_comp_descriptor * 222963169b4SHans Petter Selasky usb_ed_comp_foreach(struct usb_config_descriptor *cd, 223963169b4SHans Petter Selasky struct usb_endpoint_ss_comp_descriptor *ped) 224963169b4SHans Petter Selasky { 225963169b4SHans Petter Selasky struct usb_descriptor *desc; 226963169b4SHans Petter Selasky 227963169b4SHans Petter Selasky desc = ((struct usb_descriptor *)ped); 228963169b4SHans Petter Selasky 229963169b4SHans Petter Selasky while ((desc = usb_desc_foreach(cd, desc))) { 230963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_INTERFACE) 231963169b4SHans Petter Selasky break; 232963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_ENDPOINT) 233963169b4SHans Petter Selasky break; 234963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) { 235963169b4SHans Petter Selasky if (desc->bLength < sizeof(*ped)) { 236963169b4SHans Petter Selasky /* endpoint companion descriptor is invalid */ 237963169b4SHans Petter Selasky break; 238963169b4SHans Petter Selasky } 239963169b4SHans Petter Selasky return ((struct usb_endpoint_ss_comp_descriptor *)desc); 240963169b4SHans Petter Selasky } 241963169b4SHans Petter Selasky } 242963169b4SHans Petter Selasky return (NULL); 243963169b4SHans Petter Selasky } 244963169b4SHans Petter Selasky 245963169b4SHans Petter Selasky /*------------------------------------------------------------------------* 246a593f6b8SAndrew Thompson * usbd_get_no_descriptors 24702ac6454SAndrew Thompson * 248bdd41206SAndrew Thompson * This function will count the total number of descriptors in the 249bdd41206SAndrew Thompson * configuration descriptor of type "type". 25002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 251bdd41206SAndrew Thompson uint8_t 252a593f6b8SAndrew Thompson usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type) 25302ac6454SAndrew Thompson { 254760bc48eSAndrew Thompson struct usb_descriptor *desc = NULL; 255bdd41206SAndrew Thompson uint8_t count = 0; 25602ac6454SAndrew Thompson 257a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 258bdd41206SAndrew Thompson if (desc->bDescriptorType == type) { 25902ac6454SAndrew Thompson count++; 260bdd41206SAndrew Thompson if (count == 0xFF) 261bdd41206SAndrew Thompson break; /* crazy */ 26202ac6454SAndrew Thompson } 26302ac6454SAndrew Thompson } 26402ac6454SAndrew Thompson return (count); 26502ac6454SAndrew Thompson } 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 268a593f6b8SAndrew Thompson * usbd_get_no_alts 26902ac6454SAndrew Thompson * 27002ac6454SAndrew Thompson * Return value: 271bd73b187SAlfred Perlstein * Number of alternate settings for the given interface descriptor 272bd73b187SAlfred Perlstein * pointer. If the USB descriptor is corrupt, the returned value can 273bd73b187SAlfred Perlstein * be greater than the actual number of alternate settings. 27402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 275bdd41206SAndrew Thompson uint8_t 276a593f6b8SAndrew Thompson usbd_get_no_alts(struct usb_config_descriptor *cd, 277760bc48eSAndrew Thompson struct usb_interface_descriptor *id) 27802ac6454SAndrew Thompson { 279760bc48eSAndrew Thompson struct usb_descriptor *desc; 280bd73b187SAlfred Perlstein uint8_t n; 281bdd41206SAndrew Thompson uint8_t ifaceno; 282bdd41206SAndrew Thompson 283bd73b187SAlfred Perlstein /* Reset interface count */ 284bd73b187SAlfred Perlstein 285bd73b187SAlfred Perlstein n = 0; 286bd73b187SAlfred Perlstein 287bd73b187SAlfred Perlstein /* Get the interface number */ 288bd73b187SAlfred Perlstein 289bdd41206SAndrew Thompson ifaceno = id->bInterfaceNumber; 290bdd41206SAndrew Thompson 291bd73b187SAlfred Perlstein /* Iterate all the USB descriptors */ 29202ac6454SAndrew Thompson 293bd73b187SAlfred Perlstein desc = NULL; 294a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 29502ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 29602ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) { 297760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)desc; 29802ac6454SAndrew Thompson if (id->bInterfaceNumber == ifaceno) { 29902ac6454SAndrew Thompson n++; 300bdd41206SAndrew Thompson if (n == 0xFF) 301bdd41206SAndrew Thompson break; /* crazy */ 302bd73b187SAlfred Perlstein } 30302ac6454SAndrew Thompson } 30402ac6454SAndrew Thompson } 30502ac6454SAndrew Thompson return (n); 30602ac6454SAndrew Thompson } 307