102ac6454SAndrew Thompson /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
46da5df47SHans Petter Selasky * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved.
502ac6454SAndrew Thompson *
602ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson * are met:
902ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson *
1502ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson * SUCH DAMAGE.
2602ac6454SAndrew Thompson */
2702ac6454SAndrew Thompson
28d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
29d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
30d2b99310SHans Petter Selasky #else
31ed6d949aSAndrew Thompson #include <sys/stdint.h>
32ed6d949aSAndrew Thompson #include <sys/stddef.h>
33ed6d949aSAndrew Thompson #include <sys/param.h>
34ed6d949aSAndrew Thompson #include <sys/queue.h>
35ed6d949aSAndrew Thompson #include <sys/types.h>
36ed6d949aSAndrew Thompson #include <sys/systm.h>
37ed6d949aSAndrew Thompson #include <sys/kernel.h>
38ed6d949aSAndrew Thompson #include <sys/bus.h>
39ed6d949aSAndrew Thompson #include <sys/module.h>
40ed6d949aSAndrew Thompson #include <sys/lock.h>
41ed6d949aSAndrew Thompson #include <sys/mutex.h>
42ed6d949aSAndrew Thompson #include <sys/condvar.h>
43ed6d949aSAndrew Thompson #include <sys/sysctl.h>
44ed6d949aSAndrew Thompson #include <sys/sx.h>
45ed6d949aSAndrew Thompson #include <sys/unistd.h>
46ed6d949aSAndrew Thompson #include <sys/callout.h>
47ed6d949aSAndrew Thompson #include <sys/malloc.h>
48ed6d949aSAndrew Thompson #include <sys/priv.h>
4902ac6454SAndrew Thompson
50ed6d949aSAndrew Thompson #include <dev/usb/usb.h>
51ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
52ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
535b0752bbSHans Petter Selasky
545b0752bbSHans Petter Selasky #define USB_DEBUG_VAR usb_debug
555b0752bbSHans Petter Selasky
565b0752bbSHans Petter Selasky #include <dev/usb/usb_core.h>
575b0752bbSHans Petter Selasky #include <dev/usb/usb_debug.h>
58d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */
5902ac6454SAndrew Thompson
6002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
61a593f6b8SAndrew Thompson * usb_desc_foreach
6202ac6454SAndrew Thompson *
6302ac6454SAndrew Thompson * This function is the safe way to iterate across the USB config
6402ac6454SAndrew Thompson * descriptor. It contains several checks against invalid
6502ac6454SAndrew Thompson * descriptors. If the "desc" argument passed to this function is
6602ac6454SAndrew Thompson * "NULL" the first descriptor, if any, will be returned.
6702ac6454SAndrew Thompson *
6802ac6454SAndrew Thompson * Return values:
6902ac6454SAndrew Thompson * NULL: End of descriptors
7002ac6454SAndrew Thompson * Else: Next descriptor after "desc"
7102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
72760bc48eSAndrew Thompson struct usb_descriptor *
usb_desc_foreach(struct usb_config_descriptor * cd,struct usb_descriptor * _desc)73a593f6b8SAndrew Thompson usb_desc_foreach(struct usb_config_descriptor *cd,
74760bc48eSAndrew Thompson struct usb_descriptor *_desc)
7502ac6454SAndrew Thompson {
7602ac6454SAndrew Thompson uint8_t *desc_next;
7702ac6454SAndrew Thompson uint8_t *start;
7802ac6454SAndrew Thompson uint8_t *end;
7902ac6454SAndrew Thompson uint8_t *desc;
8002ac6454SAndrew Thompson
8102ac6454SAndrew Thompson /* be NULL safe */
8202ac6454SAndrew Thompson if (cd == NULL)
8302ac6454SAndrew Thompson return (NULL);
8402ac6454SAndrew Thompson
8502ac6454SAndrew Thompson /* We assume that the "wTotalLength" has been checked. */
8602ac6454SAndrew Thompson start = (uint8_t *)cd;
8702ac6454SAndrew Thompson end = start + UGETW(cd->wTotalLength);
8802ac6454SAndrew Thompson desc = (uint8_t *)_desc;
8902ac6454SAndrew Thompson
9002ac6454SAndrew Thompson /* Get start of next USB descriptor. */
9102ac6454SAndrew Thompson if (desc == NULL)
9202ac6454SAndrew Thompson desc = start;
9302ac6454SAndrew Thompson else
9402ac6454SAndrew Thompson desc = desc + desc[0];
9502ac6454SAndrew Thompson
9602ac6454SAndrew Thompson /* Check that the next USB descriptor is within the range. */
9702ac6454SAndrew Thompson if ((desc < start) || (desc >= end))
9802ac6454SAndrew Thompson return (NULL); /* out of range, or EOD */
9902ac6454SAndrew Thompson
10002ac6454SAndrew Thompson /* Check that the second next USB descriptor is within range. */
10102ac6454SAndrew Thompson desc_next = desc + desc[0];
10202ac6454SAndrew Thompson if ((desc_next < start) || (desc_next > end))
10302ac6454SAndrew Thompson return (NULL); /* out of range */
10402ac6454SAndrew Thompson
10502ac6454SAndrew Thompson /* Check minimum descriptor length. */
10602ac6454SAndrew Thompson if (desc[0] < 3)
10702ac6454SAndrew Thompson return (NULL); /* too short descriptor */
10802ac6454SAndrew Thompson
10902ac6454SAndrew Thompson /* Return start of next descriptor. */
110760bc48eSAndrew Thompson return ((struct usb_descriptor *)desc);
11102ac6454SAndrew Thompson }
11202ac6454SAndrew Thompson
11302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
114a593f6b8SAndrew Thompson * usb_idesc_foreach
11502ac6454SAndrew Thompson *
116bdd41206SAndrew Thompson * This function will iterate the interface descriptors in the config
117bdd41206SAndrew Thompson * descriptor. The parse state structure should be zeroed before
118bdd41206SAndrew Thompson * calling this function the first time.
11902ac6454SAndrew Thompson *
12002ac6454SAndrew Thompson * Return values:
12102ac6454SAndrew Thompson * NULL: End of descriptors
12202ac6454SAndrew Thompson * Else: A valid interface descriptor
12302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
124760bc48eSAndrew Thompson struct usb_interface_descriptor *
usb_idesc_foreach(struct usb_config_descriptor * cd,struct usb_idesc_parse_state * ps)125a593f6b8SAndrew Thompson usb_idesc_foreach(struct usb_config_descriptor *cd,
126760bc48eSAndrew Thompson struct usb_idesc_parse_state *ps)
12702ac6454SAndrew Thompson {
128760bc48eSAndrew Thompson struct usb_interface_descriptor *id;
129bdd41206SAndrew Thompson uint8_t new_iface;
13002ac6454SAndrew Thompson
131bdd41206SAndrew Thompson /* retrieve current descriptor */
132760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)ps->desc;
133bdd41206SAndrew Thompson /* default is to start a new interface */
134bdd41206SAndrew Thompson new_iface = 1;
13502ac6454SAndrew Thompson
136bdd41206SAndrew Thompson while (1) {
137760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)
138a593f6b8SAndrew Thompson usb_desc_foreach(cd, (struct usb_descriptor *)id);
139bdd41206SAndrew Thompson if (id == NULL)
140bdd41206SAndrew Thompson break;
141bdd41206SAndrew Thompson if ((id->bDescriptorType == UDESC_INTERFACE) &&
142bdd41206SAndrew Thompson (id->bLength >= sizeof(*id))) {
1436da5df47SHans Petter Selasky if (ps->iface_no_last == id->bInterfaceNumber) {
1446da5df47SHans Petter Selasky /*
1456da5df47SHans Petter Selasky * Don't allow more than 256 alternate
1466da5df47SHans Petter Selasky * settings to avoid overflowing the
1476da5df47SHans Petter Selasky * alternate index which is a 8-bit
1486da5df47SHans Petter Selasky * variable.
1496da5df47SHans Petter Selasky */
1506da5df47SHans Petter Selasky if (ps->iface_index_alt == 255) {
1516da5df47SHans Petter Selasky DPRINTF("Interface(%u) has more than 256 alternate settings\n",
1526da5df47SHans Petter Selasky id->bInterfaceNumber);
1536da5df47SHans Petter Selasky continue;
1546da5df47SHans Petter Selasky }
155bdd41206SAndrew Thompson new_iface = 0;
1566da5df47SHans Petter Selasky }
157bdd41206SAndrew Thompson ps->iface_no_last = id->bInterfaceNumber;
158bdd41206SAndrew Thompson break;
159bdd41206SAndrew Thompson }
160bdd41206SAndrew Thompson }
16102ac6454SAndrew Thompson
162bdd41206SAndrew Thompson if (ps->desc == NULL) {
1635b0752bbSHans Petter Selasky /* first time or zero descriptors */
164bdd41206SAndrew Thompson } else if (new_iface) {
165bdd41206SAndrew Thompson /* new interface */
166bdd41206SAndrew Thompson ps->iface_index ++;
167bdd41206SAndrew Thompson ps->iface_index_alt = 0;
16802ac6454SAndrew Thompson } else {
169bdd41206SAndrew Thompson /* new alternate interface */
170bdd41206SAndrew Thompson ps->iface_index_alt ++;
17102ac6454SAndrew Thompson }
1725b0752bbSHans Petter Selasky #if (USB_IFACE_MAX <= 0)
1735b0752bbSHans Petter Selasky #error "USB_IFACE_MAX must be defined greater than zero"
1745b0752bbSHans Petter Selasky #endif
1755b0752bbSHans Petter Selasky /* check for too many interfaces */
1765b0752bbSHans Petter Selasky if (ps->iface_index >= USB_IFACE_MAX) {
1775b0752bbSHans Petter Selasky DPRINTF("Interface limit reached\n");
1785b0752bbSHans Petter Selasky id = NULL;
1795b0752bbSHans Petter Selasky }
18002ac6454SAndrew Thompson
181bdd41206SAndrew Thompson /* store and return current descriptor */
182760bc48eSAndrew Thompson ps->desc = (struct usb_descriptor *)id;
18302ac6454SAndrew Thompson return (id);
18402ac6454SAndrew Thompson }
18502ac6454SAndrew Thompson
18602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
187a593f6b8SAndrew Thompson * usb_edesc_foreach
18802ac6454SAndrew Thompson *
189bdd41206SAndrew Thompson * This function will iterate all the endpoint descriptors within an
190bdd41206SAndrew Thompson * interface descriptor. Starting value for the "ped" argument should
191bdd41206SAndrew Thompson * be a valid interface descriptor.
19202ac6454SAndrew Thompson *
19302ac6454SAndrew Thompson * Return values:
19402ac6454SAndrew Thompson * NULL: End of descriptors
19502ac6454SAndrew Thompson * Else: A valid endpoint descriptor
19602ac6454SAndrew Thompson *------------------------------------------------------------------------*/
197760bc48eSAndrew Thompson struct usb_endpoint_descriptor *
usb_edesc_foreach(struct usb_config_descriptor * cd,struct usb_endpoint_descriptor * ped)198a593f6b8SAndrew Thompson usb_edesc_foreach(struct usb_config_descriptor *cd,
199760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ped)
20002ac6454SAndrew Thompson {
201760bc48eSAndrew Thompson struct usb_descriptor *desc;
20202ac6454SAndrew Thompson
203760bc48eSAndrew Thompson desc = ((struct usb_descriptor *)ped);
20402ac6454SAndrew Thompson
205a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) {
20602ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_INTERFACE) {
20702ac6454SAndrew Thompson break;
20802ac6454SAndrew Thompson }
20902ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_ENDPOINT) {
210bdd41206SAndrew Thompson if (desc->bLength < sizeof(*ped)) {
211963169b4SHans Petter Selasky /* endpoint descriptor is invalid */
21202ac6454SAndrew Thompson break;
21302ac6454SAndrew Thompson }
214760bc48eSAndrew Thompson return ((struct usb_endpoint_descriptor *)desc);
21502ac6454SAndrew Thompson }
21602ac6454SAndrew Thompson }
21702ac6454SAndrew Thompson return (NULL);
21802ac6454SAndrew Thompson }
21902ac6454SAndrew Thompson
22002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
221963169b4SHans Petter Selasky * usb_ed_comp_foreach
222963169b4SHans Petter Selasky *
223963169b4SHans Petter Selasky * This function will iterate all the endpoint companion descriptors
224963169b4SHans Petter Selasky * within an endpoint descriptor in an interface descriptor. Starting
225963169b4SHans Petter Selasky * value for the "ped" argument should be a valid endpoint companion
226963169b4SHans Petter Selasky * descriptor.
227963169b4SHans Petter Selasky *
228963169b4SHans Petter Selasky * Return values:
229963169b4SHans Petter Selasky * NULL: End of descriptors
230963169b4SHans Petter Selasky * Else: A valid endpoint companion descriptor
231963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
232963169b4SHans Petter Selasky struct usb_endpoint_ss_comp_descriptor *
usb_ed_comp_foreach(struct usb_config_descriptor * cd,struct usb_endpoint_ss_comp_descriptor * ped)233963169b4SHans Petter Selasky usb_ed_comp_foreach(struct usb_config_descriptor *cd,
234963169b4SHans Petter Selasky struct usb_endpoint_ss_comp_descriptor *ped)
235963169b4SHans Petter Selasky {
236963169b4SHans Petter Selasky struct usb_descriptor *desc;
237963169b4SHans Petter Selasky
238963169b4SHans Petter Selasky desc = ((struct usb_descriptor *)ped);
239963169b4SHans Petter Selasky
240963169b4SHans Petter Selasky while ((desc = usb_desc_foreach(cd, desc))) {
241963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_INTERFACE)
242963169b4SHans Petter Selasky break;
243963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_ENDPOINT)
244963169b4SHans Petter Selasky break;
245963169b4SHans Petter Selasky if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
246963169b4SHans Petter Selasky if (desc->bLength < sizeof(*ped)) {
247963169b4SHans Petter Selasky /* endpoint companion descriptor is invalid */
248963169b4SHans Petter Selasky break;
249963169b4SHans Petter Selasky }
250963169b4SHans Petter Selasky return ((struct usb_endpoint_ss_comp_descriptor *)desc);
251963169b4SHans Petter Selasky }
252963169b4SHans Petter Selasky }
253963169b4SHans Petter Selasky return (NULL);
254963169b4SHans Petter Selasky }
255963169b4SHans Petter Selasky
256963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
257a593f6b8SAndrew Thompson * usbd_get_no_descriptors
25802ac6454SAndrew Thompson *
259bdd41206SAndrew Thompson * This function will count the total number of descriptors in the
260bdd41206SAndrew Thompson * configuration descriptor of type "type".
26102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
262bdd41206SAndrew Thompson uint8_t
usbd_get_no_descriptors(struct usb_config_descriptor * cd,uint8_t type)263a593f6b8SAndrew Thompson usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
26402ac6454SAndrew Thompson {
265760bc48eSAndrew Thompson struct usb_descriptor *desc = NULL;
266bdd41206SAndrew Thompson uint8_t count = 0;
26702ac6454SAndrew Thompson
268a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) {
269bdd41206SAndrew Thompson if (desc->bDescriptorType == type) {
27002ac6454SAndrew Thompson count++;
271bdd41206SAndrew Thompson if (count == 0xFF)
272bdd41206SAndrew Thompson break; /* crazy */
27302ac6454SAndrew Thompson }
27402ac6454SAndrew Thompson }
27502ac6454SAndrew Thompson return (count);
27602ac6454SAndrew Thompson }
27702ac6454SAndrew Thompson
27802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
279a593f6b8SAndrew Thompson * usbd_get_no_alts
28002ac6454SAndrew Thompson *
28102ac6454SAndrew Thompson * Return value:
282bd73b187SAlfred Perlstein * Number of alternate settings for the given interface descriptor
283bd73b187SAlfred Perlstein * pointer. If the USB descriptor is corrupt, the returned value can
284bd73b187SAlfred Perlstein * be greater than the actual number of alternate settings.
28502ac6454SAndrew Thompson *------------------------------------------------------------------------*/
286bdd41206SAndrew Thompson uint8_t
usbd_get_no_alts(struct usb_config_descriptor * cd,struct usb_interface_descriptor * id)287a593f6b8SAndrew Thompson usbd_get_no_alts(struct usb_config_descriptor *cd,
288760bc48eSAndrew Thompson struct usb_interface_descriptor *id)
28902ac6454SAndrew Thompson {
290760bc48eSAndrew Thompson struct usb_descriptor *desc;
291bd73b187SAlfred Perlstein uint8_t n;
292bdd41206SAndrew Thompson uint8_t ifaceno;
293bdd41206SAndrew Thompson
294bd73b187SAlfred Perlstein /* Reset interface count */
295bd73b187SAlfred Perlstein
296bd73b187SAlfred Perlstein n = 0;
297bd73b187SAlfred Perlstein
298bd73b187SAlfred Perlstein /* Get the interface number */
299bd73b187SAlfred Perlstein
300bdd41206SAndrew Thompson ifaceno = id->bInterfaceNumber;
301bdd41206SAndrew Thompson
302bd73b187SAlfred Perlstein /* Iterate all the USB descriptors */
30302ac6454SAndrew Thompson
304bd73b187SAlfred Perlstein desc = NULL;
305a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) {
30602ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) &&
30702ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) {
308760bc48eSAndrew Thompson id = (struct usb_interface_descriptor *)desc;
30902ac6454SAndrew Thompson if (id->bInterfaceNumber == ifaceno) {
31002ac6454SAndrew Thompson n++;
311bdd41206SAndrew Thompson if (n == 0xFF)
312bdd41206SAndrew Thompson break; /* crazy */
313bd73b187SAlfred Perlstein }
31402ac6454SAndrew Thompson }
31502ac6454SAndrew Thompson }
31602ac6454SAndrew Thompson return (n);
31702ac6454SAndrew Thompson }
318