102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 2702ac6454SAndrew Thompson #include <dev/usb/usb.h> 2802ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h> 2902ac6454SAndrew Thompson 3002ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 3102ac6454SAndrew Thompson #include <dev/usb/usb_parse.h> 3202ac6454SAndrew Thompson 3302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 3402ac6454SAndrew Thompson * usb2_desc_foreach 3502ac6454SAndrew Thompson * 3602ac6454SAndrew Thompson * This function is the safe way to iterate across the USB config 3702ac6454SAndrew Thompson * descriptor. It contains several checks against invalid 3802ac6454SAndrew Thompson * descriptors. If the "desc" argument passed to this function is 3902ac6454SAndrew Thompson * "NULL" the first descriptor, if any, will be returned. 4002ac6454SAndrew Thompson * 4102ac6454SAndrew Thompson * Return values: 4202ac6454SAndrew Thompson * NULL: End of descriptors 4302ac6454SAndrew Thompson * Else: Next descriptor after "desc" 4402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 45760bc48eSAndrew Thompson struct usb_descriptor * 46760bc48eSAndrew Thompson usb2_desc_foreach(struct usb_config_descriptor *cd, 47760bc48eSAndrew Thompson struct usb_descriptor *_desc) 4802ac6454SAndrew Thompson { 4902ac6454SAndrew Thompson uint8_t *desc_next; 5002ac6454SAndrew Thompson uint8_t *start; 5102ac6454SAndrew Thompson uint8_t *end; 5202ac6454SAndrew Thompson uint8_t *desc; 5302ac6454SAndrew Thompson 5402ac6454SAndrew Thompson /* be NULL safe */ 5502ac6454SAndrew Thompson if (cd == NULL) 5602ac6454SAndrew Thompson return (NULL); 5702ac6454SAndrew Thompson 5802ac6454SAndrew Thompson /* We assume that the "wTotalLength" has been checked. */ 5902ac6454SAndrew Thompson start = (uint8_t *)cd; 6002ac6454SAndrew Thompson end = start + UGETW(cd->wTotalLength); 6102ac6454SAndrew Thompson desc = (uint8_t *)_desc; 6202ac6454SAndrew Thompson 6302ac6454SAndrew Thompson /* Get start of next USB descriptor. */ 6402ac6454SAndrew Thompson if (desc == NULL) 6502ac6454SAndrew Thompson desc = start; 6602ac6454SAndrew Thompson else 6702ac6454SAndrew Thompson desc = desc + desc[0]; 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson /* Check that the next USB descriptor is within the range. */ 7002ac6454SAndrew Thompson if ((desc < start) || (desc >= end)) 7102ac6454SAndrew Thompson return (NULL); /* out of range, or EOD */ 7202ac6454SAndrew Thompson 7302ac6454SAndrew Thompson /* Check that the second next USB descriptor is within range. */ 7402ac6454SAndrew Thompson desc_next = desc + desc[0]; 7502ac6454SAndrew Thompson if ((desc_next < start) || (desc_next > end)) 7602ac6454SAndrew Thompson return (NULL); /* out of range */ 7702ac6454SAndrew Thompson 7802ac6454SAndrew Thompson /* Check minimum descriptor length. */ 7902ac6454SAndrew Thompson if (desc[0] < 3) 8002ac6454SAndrew Thompson return (NULL); /* too short descriptor */ 8102ac6454SAndrew Thompson 8202ac6454SAndrew Thompson /* Return start of next descriptor. */ 83760bc48eSAndrew Thompson return ((struct usb_descriptor *)desc); 8402ac6454SAndrew Thompson } 8502ac6454SAndrew Thompson 8602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 87bdd41206SAndrew Thompson * usb2_idesc_foreach 8802ac6454SAndrew Thompson * 89bdd41206SAndrew Thompson * This function will iterate the interface descriptors in the config 90bdd41206SAndrew Thompson * descriptor. The parse state structure should be zeroed before 91bdd41206SAndrew Thompson * calling this function the first time. 9202ac6454SAndrew Thompson * 9302ac6454SAndrew Thompson * Return values: 9402ac6454SAndrew Thompson * NULL: End of descriptors 9502ac6454SAndrew Thompson * Else: A valid interface descriptor 9602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 97760bc48eSAndrew Thompson struct usb_interface_descriptor * 98760bc48eSAndrew Thompson usb2_idesc_foreach(struct usb_config_descriptor *cd, 99760bc48eSAndrew Thompson struct usb_idesc_parse_state *ps) 10002ac6454SAndrew Thompson { 101760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 102bdd41206SAndrew Thompson uint8_t new_iface; 10302ac6454SAndrew Thompson 104bdd41206SAndrew Thompson /* retrieve current descriptor */ 105760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ps->desc; 106bdd41206SAndrew Thompson /* default is to start a new interface */ 107bdd41206SAndrew Thompson new_iface = 1; 10802ac6454SAndrew Thompson 109bdd41206SAndrew Thompson while (1) { 110760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *) 111760bc48eSAndrew Thompson usb2_desc_foreach(cd, (struct usb_descriptor *)id); 112bdd41206SAndrew Thompson if (id == NULL) 113bdd41206SAndrew Thompson break; 114bdd41206SAndrew Thompson if ((id->bDescriptorType == UDESC_INTERFACE) && 115bdd41206SAndrew Thompson (id->bLength >= sizeof(*id))) { 116bdd41206SAndrew Thompson if (ps->iface_no_last == id->bInterfaceNumber) 117bdd41206SAndrew Thompson new_iface = 0; 118bdd41206SAndrew Thompson ps->iface_no_last = id->bInterfaceNumber; 119bdd41206SAndrew Thompson break; 120bdd41206SAndrew Thompson } 121bdd41206SAndrew Thompson } 12202ac6454SAndrew Thompson 123bdd41206SAndrew Thompson if (ps->desc == NULL) { 124bdd41206SAndrew Thompson /* first time */ 125bdd41206SAndrew Thompson } else if (new_iface) { 126bdd41206SAndrew Thompson /* new interface */ 127bdd41206SAndrew Thompson ps->iface_index ++; 128bdd41206SAndrew Thompson ps->iface_index_alt = 0; 12902ac6454SAndrew Thompson } else { 130bdd41206SAndrew Thompson /* new alternate interface */ 131bdd41206SAndrew Thompson ps->iface_index_alt ++; 13202ac6454SAndrew Thompson } 13302ac6454SAndrew Thompson 134bdd41206SAndrew Thompson /* store and return current descriptor */ 135760bc48eSAndrew Thompson ps->desc = (struct usb_descriptor *)id; 13602ac6454SAndrew Thompson return (id); 13702ac6454SAndrew Thompson } 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 140bdd41206SAndrew Thompson * usb2_edesc_foreach 14102ac6454SAndrew Thompson * 142bdd41206SAndrew Thompson * This function will iterate all the endpoint descriptors within an 143bdd41206SAndrew Thompson * interface descriptor. Starting value for the "ped" argument should 144bdd41206SAndrew Thompson * be a valid interface descriptor. 14502ac6454SAndrew Thompson * 14602ac6454SAndrew Thompson * Return values: 14702ac6454SAndrew Thompson * NULL: End of descriptors 14802ac6454SAndrew Thompson * Else: A valid endpoint descriptor 14902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 150760bc48eSAndrew Thompson struct usb_endpoint_descriptor * 151760bc48eSAndrew Thompson usb2_edesc_foreach(struct usb_config_descriptor *cd, 152760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ped) 15302ac6454SAndrew Thompson { 154760bc48eSAndrew Thompson struct usb_descriptor *desc; 15502ac6454SAndrew Thompson 156760bc48eSAndrew Thompson desc = ((struct usb_descriptor *)ped); 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson while ((desc = usb2_desc_foreach(cd, desc))) { 15902ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_INTERFACE) { 16002ac6454SAndrew Thompson break; 16102ac6454SAndrew Thompson } 16202ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_ENDPOINT) { 163bdd41206SAndrew Thompson if (desc->bLength < sizeof(*ped)) { 16402ac6454SAndrew Thompson /* endpoint index is invalid */ 16502ac6454SAndrew Thompson break; 16602ac6454SAndrew Thompson } 167760bc48eSAndrew Thompson return ((struct usb_endpoint_descriptor *)desc); 16802ac6454SAndrew Thompson } 16902ac6454SAndrew Thompson } 17002ac6454SAndrew Thompson return (NULL); 17102ac6454SAndrew Thompson } 17202ac6454SAndrew Thompson 17302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 174bdd41206SAndrew Thompson * usb2_get_no_descriptors 17502ac6454SAndrew Thompson * 176bdd41206SAndrew Thompson * This function will count the total number of descriptors in the 177bdd41206SAndrew Thompson * configuration descriptor of type "type". 17802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 179bdd41206SAndrew Thompson uint8_t 180760bc48eSAndrew Thompson usb2_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type) 18102ac6454SAndrew Thompson { 182760bc48eSAndrew Thompson struct usb_descriptor *desc = NULL; 183bdd41206SAndrew Thompson uint8_t count = 0; 18402ac6454SAndrew Thompson 18502ac6454SAndrew Thompson while ((desc = usb2_desc_foreach(cd, desc))) { 186bdd41206SAndrew Thompson if (desc->bDescriptorType == type) { 18702ac6454SAndrew Thompson count++; 188bdd41206SAndrew Thompson if (count == 0xFF) 189bdd41206SAndrew Thompson break; /* crazy */ 19002ac6454SAndrew Thompson } 19102ac6454SAndrew Thompson } 19202ac6454SAndrew Thompson return (count); 19302ac6454SAndrew Thompson } 19402ac6454SAndrew Thompson 19502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 19602ac6454SAndrew Thompson * usb2_get_no_alts 19702ac6454SAndrew Thompson * 19802ac6454SAndrew Thompson * Return value: 199bdd41206SAndrew Thompson * Number of alternate settings for the given interface descriptor pointer. 20002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 201bdd41206SAndrew Thompson uint8_t 202760bc48eSAndrew Thompson usb2_get_no_alts(struct usb_config_descriptor *cd, 203760bc48eSAndrew Thompson struct usb_interface_descriptor *id) 20402ac6454SAndrew Thompson { 205760bc48eSAndrew Thompson struct usb_descriptor *desc; 206bdd41206SAndrew Thompson uint8_t n = 0; 207bdd41206SAndrew Thompson uint8_t ifaceno; 208bdd41206SAndrew Thompson 209bdd41206SAndrew Thompson ifaceno = id->bInterfaceNumber; 210bdd41206SAndrew Thompson 211760bc48eSAndrew Thompson desc = (struct usb_descriptor *)id; 21202ac6454SAndrew Thompson 21302ac6454SAndrew Thompson while ((desc = usb2_desc_foreach(cd, desc))) { 21402ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 21502ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) { 216760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)desc; 21702ac6454SAndrew Thompson if (id->bInterfaceNumber == ifaceno) { 21802ac6454SAndrew Thompson n++; 219bdd41206SAndrew Thompson if (n == 0xFF) 220bdd41206SAndrew Thompson break; /* crazy */ 221bdd41206SAndrew Thompson } else 222bdd41206SAndrew Thompson break; /* end */ 22302ac6454SAndrew Thompson } 22402ac6454SAndrew Thompson } 22502ac6454SAndrew Thompson return (n); 22602ac6454SAndrew Thompson } 227