1184610Salfred/* $FreeBSD$ */ 2184610Salfred/*- 3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27194677Sthompsa#include <sys/stdint.h> 28194677Sthompsa#include <sys/stddef.h> 29194677Sthompsa#include <sys/param.h> 30194677Sthompsa#include <sys/queue.h> 31194677Sthompsa#include <sys/types.h> 32194677Sthompsa#include <sys/systm.h> 33194677Sthompsa#include <sys/kernel.h> 34194677Sthompsa#include <sys/bus.h> 35194677Sthompsa#include <sys/module.h> 36194677Sthompsa#include <sys/lock.h> 37194677Sthompsa#include <sys/mutex.h> 38194677Sthompsa#include <sys/condvar.h> 39194677Sthompsa#include <sys/sysctl.h> 40194677Sthompsa#include <sys/sx.h> 41194677Sthompsa#include <sys/unistd.h> 42194677Sthompsa#include <sys/callout.h> 43194677Sthompsa#include <sys/malloc.h> 44194677Sthompsa#include <sys/priv.h> 45194677Sthompsa 46188942Sthompsa#include <dev/usb/usb.h> 47194677Sthompsa#include <dev/usb/usbdi.h> 48194677Sthompsa#include <dev/usb/usbdi_util.h> 49184610Salfred 50184610Salfred 51184610Salfred/*------------------------------------------------------------------------* 52194228Sthompsa * usb_desc_foreach 53184610Salfred * 54184610Salfred * This function is the safe way to iterate across the USB config 55184610Salfred * descriptor. It contains several checks against invalid 56184610Salfred * descriptors. If the "desc" argument passed to this function is 57184610Salfred * "NULL" the first descriptor, if any, will be returned. 58184610Salfred * 59184610Salfred * Return values: 60184610Salfred * NULL: End of descriptors 61184610Salfred * Else: Next descriptor after "desc" 62184610Salfred *------------------------------------------------------------------------*/ 63192984Sthompsastruct usb_descriptor * 64194228Sthompsausb_desc_foreach(struct usb_config_descriptor *cd, 65192984Sthompsa struct usb_descriptor *_desc) 66184610Salfred{ 67187178Sthompsa uint8_t *desc_next; 68187178Sthompsa uint8_t *start; 69187178Sthompsa uint8_t *end; 70187178Sthompsa uint8_t *desc; 71184610Salfred 72187178Sthompsa /* be NULL safe */ 73187178Sthompsa if (cd == NULL) 74184610Salfred return (NULL); 75184610Salfred 76187178Sthompsa /* We assume that the "wTotalLength" has been checked. */ 77187178Sthompsa start = (uint8_t *)cd; 78187178Sthompsa end = start + UGETW(cd->wTotalLength); 79187178Sthompsa desc = (uint8_t *)_desc; 80187178Sthompsa 81187178Sthompsa /* Get start of next USB descriptor. */ 82187178Sthompsa if (desc == NULL) 83187178Sthompsa desc = start; 84187178Sthompsa else 85187178Sthompsa desc = desc + desc[0]; 86187178Sthompsa 87187178Sthompsa /* Check that the next USB descriptor is within the range. */ 88187178Sthompsa if ((desc < start) || (desc >= end)) 89187178Sthompsa return (NULL); /* out of range, or EOD */ 90187178Sthompsa 91187178Sthompsa /* Check that the second next USB descriptor is within range. */ 92187178Sthompsa desc_next = desc + desc[0]; 93187178Sthompsa if ((desc_next < start) || (desc_next > end)) 94187178Sthompsa return (NULL); /* out of range */ 95187178Sthompsa 96187178Sthompsa /* Check minimum descriptor length. */ 97187178Sthompsa if (desc[0] < 3) 98187178Sthompsa return (NULL); /* too short descriptor */ 99187178Sthompsa 100187178Sthompsa /* Return start of next descriptor. */ 101192984Sthompsa return ((struct usb_descriptor *)desc); 102184610Salfred} 103184610Salfred 104184610Salfred/*------------------------------------------------------------------------* 105194228Sthompsa * usb_idesc_foreach 106184610Salfred * 107190730Sthompsa * This function will iterate the interface descriptors in the config 108190730Sthompsa * descriptor. The parse state structure should be zeroed before 109190730Sthompsa * calling this function the first time. 110184610Salfred * 111184610Salfred * Return values: 112184610Salfred * NULL: End of descriptors 113184610Salfred * Else: A valid interface descriptor 114184610Salfred *------------------------------------------------------------------------*/ 115192984Sthompsastruct usb_interface_descriptor * 116194228Sthompsausb_idesc_foreach(struct usb_config_descriptor *cd, 117192984Sthompsa struct usb_idesc_parse_state *ps) 118184610Salfred{ 119192984Sthompsa struct usb_interface_descriptor *id; 120190730Sthompsa uint8_t new_iface; 121184610Salfred 122190730Sthompsa /* retrieve current descriptor */ 123192984Sthompsa id = (struct usb_interface_descriptor *)ps->desc; 124190730Sthompsa /* default is to start a new interface */ 125190730Sthompsa new_iface = 1; 126184610Salfred 127190730Sthompsa while (1) { 128192984Sthompsa id = (struct usb_interface_descriptor *) 129194228Sthompsa usb_desc_foreach(cd, (struct usb_descriptor *)id); 130190730Sthompsa if (id == NULL) 131190730Sthompsa break; 132190730Sthompsa if ((id->bDescriptorType == UDESC_INTERFACE) && 133190730Sthompsa (id->bLength >= sizeof(*id))) { 134190730Sthompsa if (ps->iface_no_last == id->bInterfaceNumber) 135190730Sthompsa new_iface = 0; 136190730Sthompsa ps->iface_no_last = id->bInterfaceNumber; 137190730Sthompsa break; 138190730Sthompsa } 139190730Sthompsa } 140184610Salfred 141190730Sthompsa if (ps->desc == NULL) { 142190730Sthompsa /* first time */ 143190730Sthompsa } else if (new_iface) { 144190730Sthompsa /* new interface */ 145190730Sthompsa ps->iface_index ++; 146190730Sthompsa ps->iface_index_alt = 0; 147190730Sthompsa } else { 148190730Sthompsa /* new alternate interface */ 149190730Sthompsa ps->iface_index_alt ++; 150190730Sthompsa } 151184610Salfred 152190730Sthompsa /* store and return current descriptor */ 153192984Sthompsa ps->desc = (struct usb_descriptor *)id; 154190730Sthompsa return (id); 155184610Salfred} 156184610Salfred 157184610Salfred/*------------------------------------------------------------------------* 158194228Sthompsa * usb_edesc_foreach 159184610Salfred * 160190730Sthompsa * This function will iterate all the endpoint descriptors within an 161190730Sthompsa * interface descriptor. Starting value for the "ped" argument should 162190730Sthompsa * be a valid interface descriptor. 163184610Salfred * 164184610Salfred * Return values: 165184610Salfred * NULL: End of descriptors 166184610Salfred * Else: A valid endpoint descriptor 167184610Salfred *------------------------------------------------------------------------*/ 168192984Sthompsastruct usb_endpoint_descriptor * 169194228Sthompsausb_edesc_foreach(struct usb_config_descriptor *cd, 170192984Sthompsa struct usb_endpoint_descriptor *ped) 171184610Salfred{ 172192984Sthompsa struct usb_descriptor *desc; 173184610Salfred 174192984Sthompsa desc = ((struct usb_descriptor *)ped); 175184610Salfred 176194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 177184610Salfred if (desc->bDescriptorType == UDESC_INTERFACE) { 178184610Salfred break; 179184610Salfred } 180184610Salfred if (desc->bDescriptorType == UDESC_ENDPOINT) { 181190730Sthompsa if (desc->bLength < sizeof(*ped)) { 182213435Shselasky /* endpoint descriptor is invalid */ 183190730Sthompsa break; 184184610Salfred } 185192984Sthompsa return ((struct usb_endpoint_descriptor *)desc); 186184610Salfred } 187184610Salfred } 188184610Salfred return (NULL); 189184610Salfred} 190184610Salfred 191184610Salfred/*------------------------------------------------------------------------* 192213435Shselasky * usb_ed_comp_foreach 193213435Shselasky * 194213435Shselasky * This function will iterate all the endpoint companion descriptors 195213435Shselasky * within an endpoint descriptor in an interface descriptor. Starting 196213435Shselasky * value for the "ped" argument should be a valid endpoint companion 197213435Shselasky * descriptor. 198213435Shselasky * 199213435Shselasky * Return values: 200213435Shselasky * NULL: End of descriptors 201213435Shselasky * Else: A valid endpoint companion descriptor 202213435Shselasky *------------------------------------------------------------------------*/ 203213435Shselaskystruct usb_endpoint_ss_comp_descriptor * 204213435Shselaskyusb_ed_comp_foreach(struct usb_config_descriptor *cd, 205213435Shselasky struct usb_endpoint_ss_comp_descriptor *ped) 206213435Shselasky{ 207213435Shselasky struct usb_descriptor *desc; 208213435Shselasky 209213435Shselasky desc = ((struct usb_descriptor *)ped); 210213435Shselasky 211213435Shselasky while ((desc = usb_desc_foreach(cd, desc))) { 212213435Shselasky if (desc->bDescriptorType == UDESC_INTERFACE) 213213435Shselasky break; 214213435Shselasky if (desc->bDescriptorType == UDESC_ENDPOINT) 215213435Shselasky break; 216213435Shselasky if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) { 217213435Shselasky if (desc->bLength < sizeof(*ped)) { 218213435Shselasky /* endpoint companion descriptor is invalid */ 219213435Shselasky break; 220213435Shselasky } 221213435Shselasky return ((struct usb_endpoint_ss_comp_descriptor *)desc); 222213435Shselasky } 223213435Shselasky } 224213435Shselasky return (NULL); 225213435Shselasky} 226213435Shselasky 227213435Shselasky/*------------------------------------------------------------------------* 228194228Sthompsa * usbd_get_no_descriptors 229184610Salfred * 230190730Sthompsa * This function will count the total number of descriptors in the 231190730Sthompsa * configuration descriptor of type "type". 232184610Salfred *------------------------------------------------------------------------*/ 233190730Sthompsauint8_t 234194228Sthompsausbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type) 235184610Salfred{ 236192984Sthompsa struct usb_descriptor *desc = NULL; 237190730Sthompsa uint8_t count = 0; 238184610Salfred 239194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 240190730Sthompsa if (desc->bDescriptorType == type) { 241184610Salfred count++; 242190730Sthompsa if (count == 0xFF) 243190730Sthompsa break; /* crazy */ 244184610Salfred } 245184610Salfred } 246184610Salfred return (count); 247184610Salfred} 248184610Salfred 249184610Salfred/*------------------------------------------------------------------------* 250194228Sthompsa * usbd_get_no_alts 251184610Salfred * 252184610Salfred * Return value: 253195963Salfred * Number of alternate settings for the given interface descriptor 254195963Salfred * pointer. If the USB descriptor is corrupt, the returned value can 255195963Salfred * be greater than the actual number of alternate settings. 256184610Salfred *------------------------------------------------------------------------*/ 257190730Sthompsauint8_t 258194228Sthompsausbd_get_no_alts(struct usb_config_descriptor *cd, 259192984Sthompsa struct usb_interface_descriptor *id) 260184610Salfred{ 261192984Sthompsa struct usb_descriptor *desc; 262195963Salfred uint8_t n; 263190730Sthompsa uint8_t ifaceno; 264184610Salfred 265195963Salfred /* Reset interface count */ 266195963Salfred 267195963Salfred n = 0; 268195963Salfred 269195963Salfred /* Get the interface number */ 270195963Salfred 271190730Sthompsa ifaceno = id->bInterfaceNumber; 272190730Sthompsa 273195963Salfred /* Iterate all the USB descriptors */ 274190730Sthompsa 275195963Salfred desc = NULL; 276194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 277184610Salfred if ((desc->bDescriptorType == UDESC_INTERFACE) && 278184610Salfred (desc->bLength >= sizeof(*id))) { 279192984Sthompsa id = (struct usb_interface_descriptor *)desc; 280184610Salfred if (id->bInterfaceNumber == ifaceno) { 281184610Salfred n++; 282190730Sthompsa if (n == 0xFF) 283190730Sthompsa break; /* crazy */ 284195963Salfred } 285184610Salfred } 286184610Salfred } 287184610Salfred return (n); 288184610Salfred} 289