102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 2007 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 2902ac6454SAndrew Thompson /* 3002ac6454SAndrew Thompson * This file contains sub-routines to build up USB descriptors from 3102ac6454SAndrew Thompson * USB templates. 3202ac6454SAndrew Thompson */ 3302ac6454SAndrew Thompson 34d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 35d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 36d2b99310SHans Petter Selasky #else 37ed6d949aSAndrew Thompson #include <sys/stdint.h> 38ed6d949aSAndrew Thompson #include <sys/stddef.h> 39ed6d949aSAndrew Thompson #include <sys/param.h> 40ed6d949aSAndrew Thompson #include <sys/queue.h> 41ed6d949aSAndrew Thompson #include <sys/types.h> 42ed6d949aSAndrew Thompson #include <sys/systm.h> 43ed6d949aSAndrew Thompson #include <sys/kernel.h> 44ed6d949aSAndrew Thompson #include <sys/bus.h> 45ed6d949aSAndrew Thompson #include <sys/module.h> 46ed6d949aSAndrew Thompson #include <sys/lock.h> 47ed6d949aSAndrew Thompson #include <sys/mutex.h> 48ed6d949aSAndrew Thompson #include <sys/condvar.h> 49ed6d949aSAndrew Thompson #include <sys/sysctl.h> 50ed6d949aSAndrew Thompson #include <sys/sx.h> 51ed6d949aSAndrew Thompson #include <sys/unistd.h> 52ed6d949aSAndrew Thompson #include <sys/callout.h> 53ed6d949aSAndrew Thompson #include <sys/malloc.h> 54ed6d949aSAndrew Thompson #include <sys/priv.h> 55ed6d949aSAndrew Thompson 5602ac6454SAndrew Thompson #include <dev/usb/usb.h> 57399e6543SHans Petter Selasky #include <dev/usb/usb_ioctl.h> 58ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 59ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 60ed6d949aSAndrew Thompson #include "usbdevs.h" 61ed6d949aSAndrew Thompson 6202ac6454SAndrew Thompson #include <dev/usb/usb_cdc.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 64ed6d949aSAndrew Thompson #include <dev/usb/usb_dynamic.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 688e06491aSEdward Tomasz Napierala #include <dev/usb/usb_util.h> 69ed6d949aSAndrew Thompson 70ed6d949aSAndrew Thompson #define USB_DEBUG_VAR usb_debug 71ed6d949aSAndrew Thompson #include <dev/usb/usb_debug.h> 7202ac6454SAndrew Thompson 7302ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 7402ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 752c79a775SHans Petter Selasky #include <dev/usb/usb_request.h> 7602ac6454SAndrew Thompson #include <dev/usb/template/usb_template.h> 77d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 7802ac6454SAndrew Thompson 798e06491aSEdward Tomasz Napierala SYSCTL_NODE(_hw_usb, OID_AUTO, templates, CTLFLAG_RW, 0, 808e06491aSEdward Tomasz Napierala "USB device side templates"); 818e06491aSEdward Tomasz Napierala 8202ac6454SAndrew Thompson MODULE_DEPEND(usb_template, usb, 1, 1, 1); 8302ac6454SAndrew Thompson MODULE_VERSION(usb_template, 1); 8402ac6454SAndrew Thompson 8502ac6454SAndrew Thompson /* function prototypes */ 8602ac6454SAndrew Thompson 87a593f6b8SAndrew Thompson static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); 88a593f6b8SAndrew Thompson static void usb_make_endpoint_desc(struct usb_temp_setup *, 89760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *); 90a593f6b8SAndrew Thompson static void usb_make_interface_desc(struct usb_temp_setup *, 91760bc48eSAndrew Thompson const struct usb_temp_interface_desc *); 92a593f6b8SAndrew Thompson static void usb_make_config_desc(struct usb_temp_setup *, 93760bc48eSAndrew Thompson const struct usb_temp_config_desc *); 94a593f6b8SAndrew Thompson static void usb_make_device_desc(struct usb_temp_setup *, 95760bc48eSAndrew Thompson const struct usb_temp_device_desc *); 96a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, 9702ac6454SAndrew Thompson uint8_t); 98a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, 99760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *, uint8_t); 100a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, 10102ac6454SAndrew Thompson uint8_t); 102a593f6b8SAndrew Thompson static usb_error_t usb_hw_ep_resolve(struct usb_device *, 103760bc48eSAndrew Thompson struct usb_descriptor *); 104a593f6b8SAndrew Thompson static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); 105a593f6b8SAndrew Thompson static void *usb_temp_get_device_desc(struct usb_device *); 106a593f6b8SAndrew Thompson static void *usb_temp_get_qualifier_desc(struct usb_device *); 107a593f6b8SAndrew Thompson static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, 10802ac6454SAndrew Thompson uint8_t); 109a593f6b8SAndrew Thompson static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, 11002ac6454SAndrew Thompson uint8_t); 111a593f6b8SAndrew Thompson static const void *usb_temp_get_vendor_desc(struct usb_device *, 1129a8e0122SAndrew Thompson const struct usb_device_request *, uint16_t *plen); 113a593f6b8SAndrew Thompson static const void *usb_temp_get_hub_desc(struct usb_device *); 114a593f6b8SAndrew Thompson static usb_error_t usb_temp_get_desc(struct usb_device *, 115760bc48eSAndrew Thompson struct usb_device_request *, const void **, uint16_t *); 116a593f6b8SAndrew Thompson static usb_error_t usb_temp_setup_by_index(struct usb_device *, 11702ac6454SAndrew Thompson uint16_t index); 118a593f6b8SAndrew Thompson static void usb_temp_init(void *); 11902ac6454SAndrew Thompson 12002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1218e06491aSEdward Tomasz Napierala * usb_decode_str_desc 1228e06491aSEdward Tomasz Napierala * 1238e06491aSEdward Tomasz Napierala * Helper function to decode string descriptors into a C string. 1248e06491aSEdward Tomasz Napierala *------------------------------------------------------------------------*/ 1258e06491aSEdward Tomasz Napierala void 1268e06491aSEdward Tomasz Napierala usb_decode_str_desc(struct usb_string_descriptor *sd, char *buf, size_t buflen) 1278e06491aSEdward Tomasz Napierala { 1288e06491aSEdward Tomasz Napierala size_t i; 1298e06491aSEdward Tomasz Napierala 1302d1a7697SEdward Tomasz Napierala if (sd->bLength < 2) { 1312d1a7697SEdward Tomasz Napierala buf[0] = '\0'; 1322d1a7697SEdward Tomasz Napierala return; 1332d1a7697SEdward Tomasz Napierala } 1342d1a7697SEdward Tomasz Napierala 1352d1a7697SEdward Tomasz Napierala for (i = 0; i < buflen - 1 && i < (sd->bLength / 2) - 1; i++) 1368e06491aSEdward Tomasz Napierala buf[i] = UGETW(sd->bString[i]); 1378e06491aSEdward Tomasz Napierala 1388e06491aSEdward Tomasz Napierala buf[i] = '\0'; 1398e06491aSEdward Tomasz Napierala } 1408e06491aSEdward Tomasz Napierala 1418e06491aSEdward Tomasz Napierala /*------------------------------------------------------------------------* 1428e06491aSEdward Tomasz Napierala * usb_temp_sysctl 1438e06491aSEdward Tomasz Napierala * 1448e06491aSEdward Tomasz Napierala * Callback for SYSCTL_PROC(9), to set and retrieve template string 1458e06491aSEdward Tomasz Napierala * descriptors. 1468e06491aSEdward Tomasz Napierala *------------------------------------------------------------------------*/ 1478e06491aSEdward Tomasz Napierala int 1488e06491aSEdward Tomasz Napierala usb_temp_sysctl(SYSCTL_HANDLER_ARGS) 1498e06491aSEdward Tomasz Napierala { 1508e06491aSEdward Tomasz Napierala char buf[128]; 1518e06491aSEdward Tomasz Napierala struct usb_string_descriptor *sd = arg1; 1528e06491aSEdward Tomasz Napierala size_t len, sdlen = arg2; 1538e06491aSEdward Tomasz Napierala int error; 1548e06491aSEdward Tomasz Napierala 1558e06491aSEdward Tomasz Napierala usb_decode_str_desc(sd, buf, sizeof(buf)); 1568e06491aSEdward Tomasz Napierala 1578e06491aSEdward Tomasz Napierala error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 1588e06491aSEdward Tomasz Napierala if (error != 0 || req->newptr == NULL) 1598e06491aSEdward Tomasz Napierala return (error); 1608e06491aSEdward Tomasz Napierala 1618e06491aSEdward Tomasz Napierala len = usb_make_str_desc(sd, sdlen, buf); 1628e06491aSEdward Tomasz Napierala if (len == 0) 1638e06491aSEdward Tomasz Napierala return (EINVAL); 1648e06491aSEdward Tomasz Napierala 1658e06491aSEdward Tomasz Napierala return (0); 1668e06491aSEdward Tomasz Napierala } 1678e06491aSEdward Tomasz Napierala 1688e06491aSEdward Tomasz Napierala 1698e06491aSEdward Tomasz Napierala /*------------------------------------------------------------------------* 170a593f6b8SAndrew Thompson * usb_make_raw_desc 17102ac6454SAndrew Thompson * 17202ac6454SAndrew Thompson * This function will insert a raw USB descriptor into the generated 17302ac6454SAndrew Thompson * USB configuration. 17402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 17502ac6454SAndrew Thompson static void 176a593f6b8SAndrew Thompson usb_make_raw_desc(struct usb_temp_setup *temp, 17702ac6454SAndrew Thompson const uint8_t *raw) 17802ac6454SAndrew Thompson { 17902ac6454SAndrew Thompson void *dst; 18002ac6454SAndrew Thompson uint8_t len; 18102ac6454SAndrew Thompson 18202ac6454SAndrew Thompson /* 18302ac6454SAndrew Thompson * The first byte of any USB descriptor gives the length. 18402ac6454SAndrew Thompson */ 18502ac6454SAndrew Thompson if (raw) { 18602ac6454SAndrew Thompson len = raw[0]; 18702ac6454SAndrew Thompson if (temp->buf) { 18802ac6454SAndrew Thompson dst = USB_ADD_BYTES(temp->buf, temp->size); 18988334428SHans Petter Selasky memcpy(dst, raw, len); 19002ac6454SAndrew Thompson 19102ac6454SAndrew Thompson /* check if we have got a CDC union descriptor */ 19202ac6454SAndrew Thompson 193f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) && 19402ac6454SAndrew Thompson (raw[1] == UDESC_CS_INTERFACE) && 19502ac6454SAndrew Thompson (raw[2] == UDESCSUB_CDC_UNION)) { 196760bc48eSAndrew Thompson struct usb_cdc_union_descriptor *ud = (void *)dst; 19702ac6454SAndrew Thompson 19802ac6454SAndrew Thompson /* update the interface numbers */ 19902ac6454SAndrew Thompson 20002ac6454SAndrew Thompson ud->bMasterInterface += 20102ac6454SAndrew Thompson temp->bInterfaceNumber; 20202ac6454SAndrew Thompson ud->bSlaveInterface[0] += 20302ac6454SAndrew Thompson temp->bInterfaceNumber; 20402ac6454SAndrew Thompson } 205399e6543SHans Petter Selasky 206399e6543SHans Petter Selasky /* check if we have got an interface association descriptor */ 207399e6543SHans Petter Selasky 208f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) && 209399e6543SHans Petter Selasky (raw[1] == UDESC_IFACE_ASSOC)) { 210399e6543SHans Petter Selasky struct usb_interface_assoc_descriptor *iad = (void *)dst; 211399e6543SHans Petter Selasky 212399e6543SHans Petter Selasky /* update the interface number */ 213399e6543SHans Petter Selasky 214399e6543SHans Petter Selasky iad->bFirstInterface += 215399e6543SHans Petter Selasky temp->bInterfaceNumber; 216399e6543SHans Petter Selasky } 217399e6543SHans Petter Selasky 218399e6543SHans Petter Selasky /* check if we have got a call management descriptor */ 219399e6543SHans Petter Selasky 220f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) && 221399e6543SHans Petter Selasky (raw[1] == UDESC_CS_INTERFACE) && 222399e6543SHans Petter Selasky (raw[2] == UDESCSUB_CDC_CM)) { 223399e6543SHans Petter Selasky struct usb_cdc_cm_descriptor *ccd = (void *)dst; 224399e6543SHans Petter Selasky 225399e6543SHans Petter Selasky /* update the interface number */ 226399e6543SHans Petter Selasky 227399e6543SHans Petter Selasky ccd->bDataInterface += 228399e6543SHans Petter Selasky temp->bInterfaceNumber; 229399e6543SHans Petter Selasky } 23002ac6454SAndrew Thompson } 23102ac6454SAndrew Thompson temp->size += len; 23202ac6454SAndrew Thompson } 23302ac6454SAndrew Thompson } 23402ac6454SAndrew Thompson 23502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 236a593f6b8SAndrew Thompson * usb_make_endpoint_desc 23702ac6454SAndrew Thompson * 23802ac6454SAndrew Thompson * This function will generate an USB endpoint descriptor from the 23902ac6454SAndrew Thompson * given USB template endpoint descriptor, which will be inserted into 24002ac6454SAndrew Thompson * the USB configuration. 24102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 24202ac6454SAndrew Thompson static void 243a593f6b8SAndrew Thompson usb_make_endpoint_desc(struct usb_temp_setup *temp, 244760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *ted) 24502ac6454SAndrew Thompson { 246760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 24702ac6454SAndrew Thompson const void **rd; 24802ac6454SAndrew Thompson uint16_t old_size; 24902ac6454SAndrew Thompson uint16_t mps; 2506a268418SAndrew Thompson uint8_t ea; /* Endpoint Address */ 2516a268418SAndrew Thompson uint8_t et; /* Endpiont Type */ 25202ac6454SAndrew Thompson 25302ac6454SAndrew Thompson /* Reserve memory */ 25402ac6454SAndrew Thompson old_size = temp->size; 2556a268418SAndrew Thompson 2566a268418SAndrew Thompson ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); 2576a268418SAndrew Thompson et = (ted->bmAttributes & UE_XFERTYPE); 2586a268418SAndrew Thompson 2596a268418SAndrew Thompson if (et == UE_ISOCHRONOUS) { 2606a268418SAndrew Thompson /* account for extra byte fields */ 2616a268418SAndrew Thompson temp->size += sizeof(*ed) + 2; 2626a268418SAndrew Thompson } else { 26302ac6454SAndrew Thompson temp->size += sizeof(*ed); 2646a268418SAndrew Thompson } 26502ac6454SAndrew Thompson 26602ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 26702ac6454SAndrew Thompson rd = ted->ppRawDesc; 26802ac6454SAndrew Thompson if (rd) { 26902ac6454SAndrew Thompson while (*rd) { 270a593f6b8SAndrew Thompson usb_make_raw_desc(temp, *rd); 27102ac6454SAndrew Thompson rd++; 27202ac6454SAndrew Thompson } 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson if (ted->pPacketSize == NULL) { 27502ac6454SAndrew Thompson /* not initialized */ 27602ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 27702ac6454SAndrew Thompson return; 27802ac6454SAndrew Thompson } 2798d2dd5ddSAndrew Thompson mps = ted->pPacketSize->mps[temp->usb_speed]; 28002ac6454SAndrew Thompson if (mps == 0) { 28102ac6454SAndrew Thompson /* not initialized */ 28202ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 28302ac6454SAndrew Thompson return; 28402ac6454SAndrew Thompson } else if (mps == UE_ZERO_MPS) { 28502ac6454SAndrew Thompson /* escape for Zero Max Packet Size */ 28602ac6454SAndrew Thompson mps = 0; 28702ac6454SAndrew Thompson } 28802ac6454SAndrew Thompson 28902ac6454SAndrew Thompson /* 29002ac6454SAndrew Thompson * Fill out the real USB endpoint descriptor 29102ac6454SAndrew Thompson * in case there is a buffer present: 29202ac6454SAndrew Thompson */ 29302ac6454SAndrew Thompson if (temp->buf) { 29402ac6454SAndrew Thompson ed = USB_ADD_BYTES(temp->buf, old_size); 2956a268418SAndrew Thompson if (et == UE_ISOCHRONOUS) 2966a268418SAndrew Thompson ed->bLength = sizeof(*ed) + 2; 2976a268418SAndrew Thompson else 29802ac6454SAndrew Thompson ed->bLength = sizeof(*ed); 29902ac6454SAndrew Thompson ed->bDescriptorType = UDESC_ENDPOINT; 30002ac6454SAndrew Thompson ed->bEndpointAddress = ea; 30102ac6454SAndrew Thompson ed->bmAttributes = ted->bmAttributes; 30202ac6454SAndrew Thompson USETW(ed->wMaxPacketSize, mps); 30302ac6454SAndrew Thompson 30402ac6454SAndrew Thompson /* setup bInterval parameter */ 30502ac6454SAndrew Thompson 30602ac6454SAndrew Thompson if (ted->pIntervals && 3078d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]) { 30802ac6454SAndrew Thompson ed->bInterval = 3098d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]; 31002ac6454SAndrew Thompson } else { 31102ac6454SAndrew Thompson switch (et) { 31202ac6454SAndrew Thompson case UE_BULK: 31302ac6454SAndrew Thompson case UE_CONTROL: 31402ac6454SAndrew Thompson ed->bInterval = 0; /* not used */ 31502ac6454SAndrew Thompson break; 31602ac6454SAndrew Thompson case UE_INTERRUPT: 3178d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 31802ac6454SAndrew Thompson case USB_SPEED_LOW: 31902ac6454SAndrew Thompson case USB_SPEED_FULL: 32002ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 32102ac6454SAndrew Thompson break; 32202ac6454SAndrew Thompson default: 323e6ee4f7dSHans Petter Selasky ed->bInterval = 4; /* 1 ms */ 32402ac6454SAndrew Thompson break; 32502ac6454SAndrew Thompson } 32602ac6454SAndrew Thompson break; 32702ac6454SAndrew Thompson default: /* UE_ISOCHRONOUS */ 3288d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 32902ac6454SAndrew Thompson case USB_SPEED_LOW: 33002ac6454SAndrew Thompson case USB_SPEED_FULL: 33102ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 33202ac6454SAndrew Thompson break; 33302ac6454SAndrew Thompson default: 33402ac6454SAndrew Thompson ed->bInterval = 1; /* 125 us */ 33502ac6454SAndrew Thompson break; 33602ac6454SAndrew Thompson } 33702ac6454SAndrew Thompson break; 33802ac6454SAndrew Thompson } 33902ac6454SAndrew Thompson } 34002ac6454SAndrew Thompson } 34102ac6454SAndrew Thompson temp->bNumEndpoints++; 34202ac6454SAndrew Thompson } 34302ac6454SAndrew Thompson 34402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 345a593f6b8SAndrew Thompson * usb_make_interface_desc 34602ac6454SAndrew Thompson * 34702ac6454SAndrew Thompson * This function will generate an USB interface descriptor from the 34802ac6454SAndrew Thompson * given USB template interface descriptor, which will be inserted 34902ac6454SAndrew Thompson * into the USB configuration. 35002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 35102ac6454SAndrew Thompson static void 352a593f6b8SAndrew Thompson usb_make_interface_desc(struct usb_temp_setup *temp, 353760bc48eSAndrew Thompson const struct usb_temp_interface_desc *tid) 35402ac6454SAndrew Thompson { 355760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 356760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc **ted; 35702ac6454SAndrew Thompson const void **rd; 35802ac6454SAndrew Thompson uint16_t old_size; 35902ac6454SAndrew Thompson 36002ac6454SAndrew Thompson /* Reserve memory */ 36102ac6454SAndrew Thompson 36202ac6454SAndrew Thompson old_size = temp->size; 36302ac6454SAndrew Thompson temp->size += sizeof(*id); 36402ac6454SAndrew Thompson 36502ac6454SAndrew Thompson /* Update interface and alternate interface numbers */ 36602ac6454SAndrew Thompson 36702ac6454SAndrew Thompson if (tid->isAltInterface == 0) { 36802ac6454SAndrew Thompson temp->bAlternateSetting = 0; 36902ac6454SAndrew Thompson temp->bInterfaceNumber++; 37002ac6454SAndrew Thompson } else { 37102ac6454SAndrew Thompson temp->bAlternateSetting++; 37202ac6454SAndrew Thompson } 37302ac6454SAndrew Thompson 37402ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 37502ac6454SAndrew Thompson 37602ac6454SAndrew Thompson rd = tid->ppRawDesc; 37702ac6454SAndrew Thompson 37802ac6454SAndrew Thompson if (rd) { 37902ac6454SAndrew Thompson while (*rd) { 380a593f6b8SAndrew Thompson usb_make_raw_desc(temp, *rd); 38102ac6454SAndrew Thompson rd++; 38202ac6454SAndrew Thompson } 38302ac6454SAndrew Thompson } 38402ac6454SAndrew Thompson /* Reset some counters */ 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson temp->bNumEndpoints = 0; 38702ac6454SAndrew Thompson 38802ac6454SAndrew Thompson /* Scan all Endpoint Descriptors second */ 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson ted = tid->ppEndpoints; 39102ac6454SAndrew Thompson if (ted) { 39202ac6454SAndrew Thompson while (*ted) { 393a593f6b8SAndrew Thompson usb_make_endpoint_desc(temp, *ted); 39402ac6454SAndrew Thompson ted++; 39502ac6454SAndrew Thompson } 39602ac6454SAndrew Thompson } 39702ac6454SAndrew Thompson /* 39802ac6454SAndrew Thompson * Fill out the real USB interface descriptor 39902ac6454SAndrew Thompson * in case there is a buffer present: 40002ac6454SAndrew Thompson */ 40102ac6454SAndrew Thompson if (temp->buf) { 40202ac6454SAndrew Thompson id = USB_ADD_BYTES(temp->buf, old_size); 40302ac6454SAndrew Thompson id->bLength = sizeof(*id); 40402ac6454SAndrew Thompson id->bDescriptorType = UDESC_INTERFACE; 40502ac6454SAndrew Thompson id->bInterfaceNumber = temp->bInterfaceNumber; 40602ac6454SAndrew Thompson id->bAlternateSetting = temp->bAlternateSetting; 40702ac6454SAndrew Thompson id->bNumEndpoints = temp->bNumEndpoints; 40802ac6454SAndrew Thompson id->bInterfaceClass = tid->bInterfaceClass; 40902ac6454SAndrew Thompson id->bInterfaceSubClass = tid->bInterfaceSubClass; 41002ac6454SAndrew Thompson id->bInterfaceProtocol = tid->bInterfaceProtocol; 41102ac6454SAndrew Thompson id->iInterface = tid->iInterface; 41202ac6454SAndrew Thompson } 41302ac6454SAndrew Thompson } 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 416a593f6b8SAndrew Thompson * usb_make_config_desc 41702ac6454SAndrew Thompson * 41802ac6454SAndrew Thompson * This function will generate an USB config descriptor from the given 41902ac6454SAndrew Thompson * USB template config descriptor, which will be inserted into the USB 42002ac6454SAndrew Thompson * configuration. 42102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 42202ac6454SAndrew Thompson static void 423a593f6b8SAndrew Thompson usb_make_config_desc(struct usb_temp_setup *temp, 424760bc48eSAndrew Thompson const struct usb_temp_config_desc *tcd) 42502ac6454SAndrew Thompson { 426760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 427760bc48eSAndrew Thompson const struct usb_temp_interface_desc **tid; 42802ac6454SAndrew Thompson uint16_t old_size; 42902ac6454SAndrew Thompson 43002ac6454SAndrew Thompson /* Reserve memory */ 43102ac6454SAndrew Thompson 43202ac6454SAndrew Thompson old_size = temp->size; 43302ac6454SAndrew Thompson temp->size += sizeof(*cd); 43402ac6454SAndrew Thompson 43502ac6454SAndrew Thompson /* Reset some counters */ 43602ac6454SAndrew Thompson 4376d917491SHans Petter Selasky temp->bInterfaceNumber = 0xFF; 43802ac6454SAndrew Thompson temp->bAlternateSetting = 0; 43902ac6454SAndrew Thompson 44002ac6454SAndrew Thompson /* Scan all the USB interfaces */ 44102ac6454SAndrew Thompson 44202ac6454SAndrew Thompson tid = tcd->ppIfaceDesc; 44302ac6454SAndrew Thompson if (tid) { 44402ac6454SAndrew Thompson while (*tid) { 445a593f6b8SAndrew Thompson usb_make_interface_desc(temp, *tid); 44602ac6454SAndrew Thompson tid++; 44702ac6454SAndrew Thompson } 44802ac6454SAndrew Thompson } 44902ac6454SAndrew Thompson /* 45002ac6454SAndrew Thompson * Fill out the real USB config descriptor 45102ac6454SAndrew Thompson * in case there is a buffer present: 45202ac6454SAndrew Thompson */ 45302ac6454SAndrew Thompson if (temp->buf) { 45402ac6454SAndrew Thompson cd = USB_ADD_BYTES(temp->buf, old_size); 45502ac6454SAndrew Thompson 45602ac6454SAndrew Thompson /* compute total size */ 45702ac6454SAndrew Thompson old_size = temp->size - old_size; 45802ac6454SAndrew Thompson 45902ac6454SAndrew Thompson cd->bLength = sizeof(*cd); 46002ac6454SAndrew Thompson cd->bDescriptorType = UDESC_CONFIG; 46102ac6454SAndrew Thompson USETW(cd->wTotalLength, old_size); 46202ac6454SAndrew Thompson cd->bNumInterface = temp->bInterfaceNumber + 1; 46302ac6454SAndrew Thompson cd->bConfigurationValue = temp->bConfigurationValue; 46402ac6454SAndrew Thompson cd->iConfiguration = tcd->iConfiguration; 46502ac6454SAndrew Thompson cd->bmAttributes = tcd->bmAttributes; 46602ac6454SAndrew Thompson cd->bMaxPower = tcd->bMaxPower; 46702ac6454SAndrew Thompson cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); 46802ac6454SAndrew Thompson 46902ac6454SAndrew Thompson if (temp->self_powered) { 47002ac6454SAndrew Thompson cd->bmAttributes |= UC_SELF_POWERED; 47102ac6454SAndrew Thompson } else { 47202ac6454SAndrew Thompson cd->bmAttributes &= ~UC_SELF_POWERED; 47302ac6454SAndrew Thompson } 47402ac6454SAndrew Thompson } 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson 47702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 478a593f6b8SAndrew Thompson * usb_make_device_desc 47902ac6454SAndrew Thompson * 48002ac6454SAndrew Thompson * This function will generate an USB device descriptor from the 48102ac6454SAndrew Thompson * given USB template device descriptor. 48202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 48302ac6454SAndrew Thompson static void 484a593f6b8SAndrew Thompson usb_make_device_desc(struct usb_temp_setup *temp, 485760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 48602ac6454SAndrew Thompson { 487760bc48eSAndrew Thompson struct usb_temp_data *utd; 488760bc48eSAndrew Thompson const struct usb_temp_config_desc **tcd; 48902ac6454SAndrew Thompson uint16_t old_size; 49002ac6454SAndrew Thompson 49102ac6454SAndrew Thompson /* Reserve memory */ 49202ac6454SAndrew Thompson 49302ac6454SAndrew Thompson old_size = temp->size; 49402ac6454SAndrew Thompson temp->size += sizeof(*utd); 49502ac6454SAndrew Thompson 49602ac6454SAndrew Thompson /* Scan all the USB configs */ 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson temp->bConfigurationValue = 1; 49902ac6454SAndrew Thompson tcd = tdd->ppConfigDesc; 50002ac6454SAndrew Thompson if (tcd) { 50102ac6454SAndrew Thompson while (*tcd) { 502a593f6b8SAndrew Thompson usb_make_config_desc(temp, *tcd); 50302ac6454SAndrew Thompson temp->bConfigurationValue++; 50402ac6454SAndrew Thompson tcd++; 50502ac6454SAndrew Thompson } 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson /* 50802ac6454SAndrew Thompson * Fill out the real USB device descriptor 50902ac6454SAndrew Thompson * in case there is a buffer present: 51002ac6454SAndrew Thompson */ 51102ac6454SAndrew Thompson 51202ac6454SAndrew Thompson if (temp->buf) { 51302ac6454SAndrew Thompson utd = USB_ADD_BYTES(temp->buf, old_size); 51402ac6454SAndrew Thompson 51502ac6454SAndrew Thompson /* Store a pointer to our template device descriptor */ 51602ac6454SAndrew Thompson utd->tdd = tdd; 51702ac6454SAndrew Thompson 51802ac6454SAndrew Thompson /* Fill out USB device descriptor */ 51902ac6454SAndrew Thompson utd->udd.bLength = sizeof(utd->udd); 52002ac6454SAndrew Thompson utd->udd.bDescriptorType = UDESC_DEVICE; 52102ac6454SAndrew Thompson utd->udd.bDeviceClass = tdd->bDeviceClass; 52202ac6454SAndrew Thompson utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; 52302ac6454SAndrew Thompson utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; 52402ac6454SAndrew Thompson USETW(utd->udd.idVendor, tdd->idVendor); 52502ac6454SAndrew Thompson USETW(utd->udd.idProduct, tdd->idProduct); 52602ac6454SAndrew Thompson USETW(utd->udd.bcdDevice, tdd->bcdDevice); 52702ac6454SAndrew Thompson utd->udd.iManufacturer = tdd->iManufacturer; 52802ac6454SAndrew Thompson utd->udd.iProduct = tdd->iProduct; 52902ac6454SAndrew Thompson utd->udd.iSerialNumber = tdd->iSerialNumber; 53002ac6454SAndrew Thompson utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; 53102ac6454SAndrew Thompson 53202ac6454SAndrew Thompson /* 53302ac6454SAndrew Thompson * Fill out the USB device qualifier. Pretend that we 53402ac6454SAndrew Thompson * don't support any other speeds by setting 53502ac6454SAndrew Thompson * "bNumConfigurations" equal to zero. That saves us 53602ac6454SAndrew Thompson * generating an extra set of configuration 53702ac6454SAndrew Thompson * descriptors. 53802ac6454SAndrew Thompson */ 53902ac6454SAndrew Thompson utd->udq.bLength = sizeof(utd->udq); 54002ac6454SAndrew Thompson utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; 54102ac6454SAndrew Thompson utd->udq.bDeviceClass = tdd->bDeviceClass; 54202ac6454SAndrew Thompson utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; 54302ac6454SAndrew Thompson utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; 54402ac6454SAndrew Thompson utd->udq.bNumConfigurations = 0; 54502ac6454SAndrew Thompson USETW(utd->udq.bcdUSB, 0x0200); 54602ac6454SAndrew Thompson utd->udq.bMaxPacketSize0 = 0; 54702ac6454SAndrew Thompson 5488d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 54902ac6454SAndrew Thompson case USB_SPEED_LOW: 55002ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 55102ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 8; 55202ac6454SAndrew Thompson break; 55302ac6454SAndrew Thompson case USB_SPEED_FULL: 55402ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 55502ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 32; 55602ac6454SAndrew Thompson break; 55702ac6454SAndrew Thompson case USB_SPEED_HIGH: 55802ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0200); 55902ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 64; 56002ac6454SAndrew Thompson break; 56102ac6454SAndrew Thompson case USB_SPEED_VARIABLE: 56202ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0250); 56302ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 255; /* 512 bytes */ 56402ac6454SAndrew Thompson break; 565399e6543SHans Petter Selasky case USB_SPEED_SUPER: 566399e6543SHans Petter Selasky USETW(utd->udd.bcdUSB, 0x0300); 567399e6543SHans Petter Selasky utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ 568399e6543SHans Petter Selasky break; 56902ac6454SAndrew Thompson default: 57002ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 57102ac6454SAndrew Thompson break; 57202ac6454SAndrew Thompson } 57302ac6454SAndrew Thompson } 57402ac6454SAndrew Thompson } 57502ac6454SAndrew Thompson 57602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 577a593f6b8SAndrew Thompson * usb_hw_ep_match 57802ac6454SAndrew Thompson * 57902ac6454SAndrew Thompson * Return values: 58020733245SPedro F. Giffuni * 0: The endpoint profile does not match the criteria 58120733245SPedro F. Giffuni * Else: The endpoint profile matches the criteria 58202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 58302ac6454SAndrew Thompson static uint8_t 584a593f6b8SAndrew Thompson usb_hw_ep_match(const struct usb_hw_ep_profile *pf, 58502ac6454SAndrew Thompson uint8_t ep_type, uint8_t ep_dir_in) 58602ac6454SAndrew Thompson { 58702ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 58802ac6454SAndrew Thompson /* special */ 58902ac6454SAndrew Thompson return (pf->support_control); 59002ac6454SAndrew Thompson } 59102ac6454SAndrew Thompson if ((pf->support_in && ep_dir_in) || 59202ac6454SAndrew Thompson (pf->support_out && !ep_dir_in)) { 59302ac6454SAndrew Thompson if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || 59402ac6454SAndrew Thompson (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || 59502ac6454SAndrew Thompson (pf->support_bulk && (ep_type == UE_BULK))) { 59602ac6454SAndrew Thompson return (1); 59702ac6454SAndrew Thompson } 59802ac6454SAndrew Thompson } 59902ac6454SAndrew Thompson return (0); 60002ac6454SAndrew Thompson } 60102ac6454SAndrew Thompson 60202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 603a593f6b8SAndrew Thompson * usb_hw_ep_find_match 60402ac6454SAndrew Thompson * 60502ac6454SAndrew Thompson * This function is used to find the best matching endpoint profile 60602ac6454SAndrew Thompson * for and endpoint belonging to an USB descriptor. 60702ac6454SAndrew Thompson * 60802ac6454SAndrew Thompson * Return values: 60902ac6454SAndrew Thompson * 0: Success. Got a match. 61002ac6454SAndrew Thompson * Else: Failure. No match. 61102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 61202ac6454SAndrew Thompson static uint8_t 613a593f6b8SAndrew Thompson usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, 614760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) 61502ac6454SAndrew Thompson { 616760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 61702ac6454SAndrew Thompson uint16_t distance; 61802ac6454SAndrew Thompson uint16_t temp; 61902ac6454SAndrew Thompson uint16_t max_frame_size; 62002ac6454SAndrew Thompson uint8_t n; 62102ac6454SAndrew Thompson uint8_t best_n; 62202ac6454SAndrew Thompson uint8_t dir_in; 62302ac6454SAndrew Thompson uint8_t dir_out; 62402ac6454SAndrew Thompson 62502ac6454SAndrew Thompson distance = 0xFFFF; 62602ac6454SAndrew Thompson best_n = 0; 62702ac6454SAndrew Thompson 62802ac6454SAndrew Thompson if ((!ep->needs_in) && (!ep->needs_out)) { 62902ac6454SAndrew Thompson return (0); /* we are done */ 63002ac6454SAndrew Thompson } 63102ac6454SAndrew Thompson if (ep->needs_ep_type == UE_CONTROL) { 63202ac6454SAndrew Thompson dir_in = 1; 63302ac6454SAndrew Thompson dir_out = 1; 63402ac6454SAndrew Thompson } else { 63502ac6454SAndrew Thompson if (ep->needs_in) { 63602ac6454SAndrew Thompson dir_in = 1; 63702ac6454SAndrew Thompson dir_out = 0; 63802ac6454SAndrew Thompson } else { 63902ac6454SAndrew Thompson dir_in = 0; 64002ac6454SAndrew Thompson dir_out = 1; 64102ac6454SAndrew Thompson } 64202ac6454SAndrew Thompson } 64302ac6454SAndrew Thompson 64402ac6454SAndrew Thompson for (n = 1; n != (USB_EP_MAX / 2); n++) { 64502ac6454SAndrew Thompson 64602ac6454SAndrew Thompson /* get HW endpoint profile */ 64702ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); 64802ac6454SAndrew Thompson if (pf == NULL) { 64902ac6454SAndrew Thompson /* end of profiles */ 65002ac6454SAndrew Thompson break; 65102ac6454SAndrew Thompson } 65202ac6454SAndrew Thompson /* check if IN-endpoint is reserved */ 65302ac6454SAndrew Thompson if (dir_in || pf->is_simplex) { 65402ac6454SAndrew Thompson if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { 65502ac6454SAndrew Thompson /* mismatch */ 65602ac6454SAndrew Thompson continue; 65702ac6454SAndrew Thompson } 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson /* check if OUT-endpoint is reserved */ 66002ac6454SAndrew Thompson if (dir_out || pf->is_simplex) { 66102ac6454SAndrew Thompson if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { 66202ac6454SAndrew Thompson /* mismatch */ 66302ac6454SAndrew Thompson continue; 66402ac6454SAndrew Thompson } 66502ac6454SAndrew Thompson } 66602ac6454SAndrew Thompson /* check simplex */ 66702ac6454SAndrew Thompson if (pf->is_simplex == is_simplex) { 66802ac6454SAndrew Thompson /* mismatch */ 66902ac6454SAndrew Thompson continue; 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson /* check if HW endpoint matches */ 672a593f6b8SAndrew Thompson if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { 67302ac6454SAndrew Thompson /* mismatch */ 67402ac6454SAndrew Thompson continue; 67502ac6454SAndrew Thompson } 67602ac6454SAndrew Thompson /* get maximum frame size */ 67702ac6454SAndrew Thompson if (dir_in) 67802ac6454SAndrew Thompson max_frame_size = pf->max_in_frame_size; 67902ac6454SAndrew Thompson else 68002ac6454SAndrew Thompson max_frame_size = pf->max_out_frame_size; 68102ac6454SAndrew Thompson 68202ac6454SAndrew Thompson /* check if we have a matching profile */ 68302ac6454SAndrew Thompson if (max_frame_size >= ep->max_frame_size) { 68402ac6454SAndrew Thompson temp = (max_frame_size - ep->max_frame_size); 68502ac6454SAndrew Thompson if (distance > temp) { 68602ac6454SAndrew Thompson distance = temp; 68702ac6454SAndrew Thompson best_n = n; 68802ac6454SAndrew Thompson ep->pf = pf; 68902ac6454SAndrew Thompson } 69002ac6454SAndrew Thompson } 69102ac6454SAndrew Thompson } 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson /* see if we got a match */ 69402ac6454SAndrew Thompson if (best_n != 0) { 69502ac6454SAndrew Thompson /* get the correct profile */ 69602ac6454SAndrew Thompson pf = ep->pf; 69702ac6454SAndrew Thompson 69802ac6454SAndrew Thompson /* reserve IN-endpoint */ 69902ac6454SAndrew Thompson if (dir_in) { 70002ac6454SAndrew Thompson ues->bmInAlloc[best_n / 8] |= 70102ac6454SAndrew Thompson (1 << (best_n % 8)); 70202ac6454SAndrew Thompson ep->hw_endpoint_in = best_n | UE_DIR_IN; 70302ac6454SAndrew Thompson ep->needs_in = 0; 70402ac6454SAndrew Thompson } 70502ac6454SAndrew Thompson /* reserve OUT-endpoint */ 70602ac6454SAndrew Thompson if (dir_out) { 70702ac6454SAndrew Thompson ues->bmOutAlloc[best_n / 8] |= 70802ac6454SAndrew Thompson (1 << (best_n % 8)); 70902ac6454SAndrew Thompson ep->hw_endpoint_out = best_n | UE_DIR_OUT; 71002ac6454SAndrew Thompson ep->needs_out = 0; 71102ac6454SAndrew Thompson } 71202ac6454SAndrew Thompson return (0); /* got a match */ 71302ac6454SAndrew Thompson } 71402ac6454SAndrew Thompson return (1); /* failure */ 71502ac6454SAndrew Thompson } 71602ac6454SAndrew Thompson 71702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 718a593f6b8SAndrew Thompson * usb_hw_ep_get_needs 71902ac6454SAndrew Thompson * 72002ac6454SAndrew Thompson * This function will figure out the type and number of endpoints 72102ac6454SAndrew Thompson * which are needed for an USB configuration. 72202ac6454SAndrew Thompson * 72302ac6454SAndrew Thompson * Return values: 72402ac6454SAndrew Thompson * 0: Success. 72502ac6454SAndrew Thompson * Else: Failure. 72602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 72702ac6454SAndrew Thompson static uint8_t 728a593f6b8SAndrew Thompson usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, 72902ac6454SAndrew Thompson uint8_t ep_type, uint8_t is_complete) 73002ac6454SAndrew Thompson { 731760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 732760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_iface; 733760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_curr; 734760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_max; 735760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_end; 736760bc48eSAndrew Thompson struct usb_descriptor *desc; 737760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 738760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 7398d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 74002ac6454SAndrew Thompson uint16_t wMaxPacketSize; 74102ac6454SAndrew Thompson uint16_t temp; 74202ac6454SAndrew Thompson uint8_t ep_no; 74302ac6454SAndrew Thompson 74402ac6454SAndrew Thompson ep_iface = ues->ep_max; 74502ac6454SAndrew Thompson ep_curr = ues->ep_max; 74602ac6454SAndrew Thompson ep_end = ues->ep + USB_EP_MAX; 74702ac6454SAndrew Thompson ep_max = ues->ep_max; 74802ac6454SAndrew Thompson desc = NULL; 749a593f6b8SAndrew Thompson speed = usbd_get_speed(ues->udev); 75002ac6454SAndrew Thompson 75102ac6454SAndrew Thompson repeat: 75202ac6454SAndrew Thompson 753a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(ues->cd, desc))) { 75402ac6454SAndrew Thompson 75502ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 75602ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) { 75702ac6454SAndrew Thompson 75802ac6454SAndrew Thompson id = (void *)desc; 75902ac6454SAndrew Thompson 76002ac6454SAndrew Thompson if (id->bAlternateSetting == 0) { 76102ac6454SAndrew Thompson /* going forward */ 76202ac6454SAndrew Thompson ep_iface = ep_max; 76302ac6454SAndrew Thompson } else { 76402ac6454SAndrew Thompson /* reset */ 76502ac6454SAndrew Thompson ep_curr = ep_iface; 76602ac6454SAndrew Thompson } 76702ac6454SAndrew Thompson } 76802ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_ENDPOINT) && 76902ac6454SAndrew Thompson (desc->bLength >= sizeof(*ed))) { 77002ac6454SAndrew Thompson 77102ac6454SAndrew Thompson ed = (void *)desc; 77202ac6454SAndrew Thompson 77302ac6454SAndrew Thompson goto handle_endpoint_desc; 77402ac6454SAndrew Thompson } 77502ac6454SAndrew Thompson } 77602ac6454SAndrew Thompson ues->ep_max = ep_max; 77702ac6454SAndrew Thompson return (0); 77802ac6454SAndrew Thompson 77902ac6454SAndrew Thompson handle_endpoint_desc: 78002ac6454SAndrew Thompson temp = (ed->bmAttributes & UE_XFERTYPE); 78102ac6454SAndrew Thompson 78202ac6454SAndrew Thompson if (temp == ep_type) { 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson if (ep_curr == ep_end) { 78502ac6454SAndrew Thompson /* too many endpoints */ 78602ac6454SAndrew Thompson return (1); /* failure */ 78702ac6454SAndrew Thompson } 78802ac6454SAndrew Thompson wMaxPacketSize = UGETW(ed->wMaxPacketSize); 78902ac6454SAndrew Thompson if ((wMaxPacketSize & 0xF800) && 79002ac6454SAndrew Thompson (speed == USB_SPEED_HIGH)) { 79102ac6454SAndrew Thompson /* handle packet multiplier */ 79202ac6454SAndrew Thompson temp = (wMaxPacketSize >> 11) & 3; 79302ac6454SAndrew Thompson wMaxPacketSize &= 0x7FF; 79402ac6454SAndrew Thompson if (temp == 1) { 79502ac6454SAndrew Thompson wMaxPacketSize *= 2; 79602ac6454SAndrew Thompson } else { 79702ac6454SAndrew Thompson wMaxPacketSize *= 3; 79802ac6454SAndrew Thompson } 79902ac6454SAndrew Thompson } 80002ac6454SAndrew Thompson /* 80102ac6454SAndrew Thompson * Check if we have a fixed endpoint number, else the 80202ac6454SAndrew Thompson * endpoint number is allocated dynamically: 80302ac6454SAndrew Thompson */ 80402ac6454SAndrew Thompson ep_no = (ed->bEndpointAddress & UE_ADDR); 80502ac6454SAndrew Thompson if (ep_no != 0) { 80602ac6454SAndrew Thompson 80702ac6454SAndrew Thompson /* get HW endpoint profile */ 80802ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) 80902ac6454SAndrew Thompson (ues->udev, &pf, ep_no); 81002ac6454SAndrew Thompson if (pf == NULL) { 81102ac6454SAndrew Thompson /* HW profile does not exist - failure */ 81202ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 81302ac6454SAndrew Thompson "does not exist\n", ep_no); 81402ac6454SAndrew Thompson return (1); 81502ac6454SAndrew Thompson } 81602ac6454SAndrew Thompson /* reserve fixed endpoint number */ 81702ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 81802ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 81902ac6454SAndrew Thompson (1 << (ep_no % 8)); 82002ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 82102ac6454SAndrew Thompson (1 << (ep_no % 8)); 82202ac6454SAndrew Thompson if ((pf->max_in_frame_size < wMaxPacketSize) || 82302ac6454SAndrew Thompson (pf->max_out_frame_size < wMaxPacketSize)) { 82402ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 825767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 82602ac6454SAndrew Thompson return (1); 82702ac6454SAndrew Thompson } 82802ac6454SAndrew Thompson } else if (ed->bEndpointAddress & UE_DIR_IN) { 82902ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 83002ac6454SAndrew Thompson (1 << (ep_no % 8)); 83102ac6454SAndrew Thompson if (pf->max_in_frame_size < wMaxPacketSize) { 83202ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 833767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 83402ac6454SAndrew Thompson return (1); 83502ac6454SAndrew Thompson } 83602ac6454SAndrew Thompson } else { 83702ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 83802ac6454SAndrew Thompson (1 << (ep_no % 8)); 83902ac6454SAndrew Thompson if (pf->max_out_frame_size < wMaxPacketSize) { 84002ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 841767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 84202ac6454SAndrew Thompson return (1); 84302ac6454SAndrew Thompson } 84402ac6454SAndrew Thompson } 84502ac6454SAndrew Thompson } else if (is_complete) { 84602ac6454SAndrew Thompson 84702ac6454SAndrew Thompson /* check if we have enough buffer space */ 84802ac6454SAndrew Thompson if (wMaxPacketSize > 84902ac6454SAndrew Thompson ep_curr->max_frame_size) { 85002ac6454SAndrew Thompson return (1); /* failure */ 85102ac6454SAndrew Thompson } 85202ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 85302ac6454SAndrew Thompson ed->bEndpointAddress = 85402ac6454SAndrew Thompson ep_curr->hw_endpoint_in; 85502ac6454SAndrew Thompson } else { 85602ac6454SAndrew Thompson ed->bEndpointAddress = 85702ac6454SAndrew Thompson ep_curr->hw_endpoint_out; 85802ac6454SAndrew Thompson } 85902ac6454SAndrew Thompson 86002ac6454SAndrew Thompson } else { 86102ac6454SAndrew Thompson 86202ac6454SAndrew Thompson /* compute the maximum frame size */ 86302ac6454SAndrew Thompson if (ep_curr->max_frame_size < wMaxPacketSize) { 86402ac6454SAndrew Thompson ep_curr->max_frame_size = wMaxPacketSize; 86502ac6454SAndrew Thompson } 86602ac6454SAndrew Thompson if (temp == UE_CONTROL) { 86702ac6454SAndrew Thompson ep_curr->needs_in = 1; 86802ac6454SAndrew Thompson ep_curr->needs_out = 1; 86902ac6454SAndrew Thompson } else { 87002ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 87102ac6454SAndrew Thompson ep_curr->needs_in = 1; 87202ac6454SAndrew Thompson } else { 87302ac6454SAndrew Thompson ep_curr->needs_out = 1; 87402ac6454SAndrew Thompson } 87502ac6454SAndrew Thompson } 87602ac6454SAndrew Thompson ep_curr->needs_ep_type = ep_type; 87702ac6454SAndrew Thompson } 87802ac6454SAndrew Thompson 87902ac6454SAndrew Thompson ep_curr++; 88002ac6454SAndrew Thompson if (ep_max < ep_curr) { 88102ac6454SAndrew Thompson ep_max = ep_curr; 88202ac6454SAndrew Thompson } 88302ac6454SAndrew Thompson } 88402ac6454SAndrew Thompson goto repeat; 88502ac6454SAndrew Thompson } 88602ac6454SAndrew Thompson 88702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 888a593f6b8SAndrew Thompson * usb_hw_ep_resolve 88902ac6454SAndrew Thompson * 89002ac6454SAndrew Thompson * This function will try to resolve endpoint requirements by the 89102ac6454SAndrew Thompson * given endpoint profiles that the USB hardware reports. 89202ac6454SAndrew Thompson * 89302ac6454SAndrew Thompson * Return values: 89402ac6454SAndrew Thompson * 0: Success 89502ac6454SAndrew Thompson * Else: Failure 89602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 897e0a69b51SAndrew Thompson static usb_error_t 898a593f6b8SAndrew Thompson usb_hw_ep_resolve(struct usb_device *udev, 899760bc48eSAndrew Thompson struct usb_descriptor *desc) 90002ac6454SAndrew Thompson { 901760bc48eSAndrew Thompson struct usb_hw_ep_scratch *ues; 902760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep; 903760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 904e892b3feSHans Petter Selasky const struct usb_bus_methods *methods; 905760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 90602ac6454SAndrew Thompson uint16_t mps; 90702ac6454SAndrew Thompson 9086950c75fSHans Petter Selasky if (desc == NULL) 90902ac6454SAndrew Thompson return (USB_ERR_INVAL); 9106950c75fSHans Petter Selasky 91102ac6454SAndrew Thompson /* get bus methods */ 91202ac6454SAndrew Thompson methods = udev->bus->methods; 91302ac6454SAndrew Thompson 9146950c75fSHans Petter Selasky if (methods->get_hw_ep_profile == NULL) 91502ac6454SAndrew Thompson return (USB_ERR_INVAL); 9166950c75fSHans Petter Selasky 91702ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_DEVICE) { 91802ac6454SAndrew Thompson 9196950c75fSHans Petter Selasky if (desc->bLength < sizeof(*dd)) 92002ac6454SAndrew Thompson return (USB_ERR_INVAL); 9216950c75fSHans Petter Selasky 92202ac6454SAndrew Thompson dd = (void *)desc; 92302ac6454SAndrew Thompson 92402ac6454SAndrew Thompson /* get HW control endpoint 0 profile */ 92502ac6454SAndrew Thompson (methods->get_hw_ep_profile) (udev, &pf, 0); 92602ac6454SAndrew Thompson if (pf == NULL) { 92702ac6454SAndrew Thompson return (USB_ERR_INVAL); 92802ac6454SAndrew Thompson } 929a593f6b8SAndrew Thompson if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { 93002ac6454SAndrew Thompson DPRINTFN(0, "Endpoint 0 does not " 93102ac6454SAndrew Thompson "support control\n"); 93202ac6454SAndrew Thompson return (USB_ERR_INVAL); 93302ac6454SAndrew Thompson } 93402ac6454SAndrew Thompson mps = dd->bMaxPacketSize; 93502ac6454SAndrew Thompson 93602ac6454SAndrew Thompson if (udev->speed == USB_SPEED_FULL) { 93702ac6454SAndrew Thompson /* 93802ac6454SAndrew Thompson * We can optionally choose another packet size ! 93902ac6454SAndrew Thompson */ 94002ac6454SAndrew Thompson while (1) { 94102ac6454SAndrew Thompson /* check if "mps" is ok */ 94202ac6454SAndrew Thompson if (pf->max_in_frame_size >= mps) { 94302ac6454SAndrew Thompson break; 94402ac6454SAndrew Thompson } 94502ac6454SAndrew Thompson /* reduce maximum packet size */ 94602ac6454SAndrew Thompson mps /= 2; 94702ac6454SAndrew Thompson 94802ac6454SAndrew Thompson /* check if "mps" is too small */ 94902ac6454SAndrew Thompson if (mps < 8) { 95002ac6454SAndrew Thompson return (USB_ERR_INVAL); 95102ac6454SAndrew Thompson } 95202ac6454SAndrew Thompson } 95302ac6454SAndrew Thompson 95402ac6454SAndrew Thompson dd->bMaxPacketSize = mps; 95502ac6454SAndrew Thompson 95602ac6454SAndrew Thompson } else { 95702ac6454SAndrew Thompson /* We only have one choice */ 95802ac6454SAndrew Thompson if (mps == 255) { 95902ac6454SAndrew Thompson mps = 512; 96002ac6454SAndrew Thompson } 96102ac6454SAndrew Thompson /* Check if we support the specified wMaxPacketSize */ 96202ac6454SAndrew Thompson if (pf->max_in_frame_size < mps) { 96302ac6454SAndrew Thompson return (USB_ERR_INVAL); 96402ac6454SAndrew Thompson } 96502ac6454SAndrew Thompson } 96602ac6454SAndrew Thompson return (0); /* success */ 96702ac6454SAndrew Thompson } 9686950c75fSHans Petter Selasky if (desc->bDescriptorType != UDESC_CONFIG) 96902ac6454SAndrew Thompson return (USB_ERR_INVAL); 9706950c75fSHans Petter Selasky if (desc->bLength < sizeof(*(ues->cd))) 97102ac6454SAndrew Thompson return (USB_ERR_INVAL); 9726950c75fSHans Petter Selasky 9736950c75fSHans Petter Selasky ues = udev->scratch.hw_ep_scratch; 97402ac6454SAndrew Thompson 975271ae033SHans Petter Selasky memset(ues, 0, sizeof(*ues)); 97602ac6454SAndrew Thompson 97702ac6454SAndrew Thompson ues->ep_max = ues->ep; 97802ac6454SAndrew Thompson ues->cd = (void *)desc; 97902ac6454SAndrew Thompson ues->methods = methods; 98002ac6454SAndrew Thompson ues->udev = udev; 98102ac6454SAndrew Thompson 98202ac6454SAndrew Thompson /* Get all the endpoints we need */ 98302ac6454SAndrew Thompson 984a593f6b8SAndrew Thompson if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || 985a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || 986a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || 987a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_BULK, 0)) { 98802ac6454SAndrew Thompson DPRINTFN(0, "Could not get needs\n"); 98902ac6454SAndrew Thompson return (USB_ERR_INVAL); 99002ac6454SAndrew Thompson } 99102ac6454SAndrew Thompson for (ep = ues->ep; ep != ues->ep_max; ep++) { 99202ac6454SAndrew Thompson 99302ac6454SAndrew Thompson while (ep->needs_in || ep->needs_out) { 99402ac6454SAndrew Thompson 99502ac6454SAndrew Thompson /* 99602ac6454SAndrew Thompson * First try to use a simplex endpoint. 99702ac6454SAndrew Thompson * Then try to use a duplex endpoint. 99802ac6454SAndrew Thompson */ 999a593f6b8SAndrew Thompson if (usb_hw_ep_find_match(ues, ep, 1) && 1000a593f6b8SAndrew Thompson usb_hw_ep_find_match(ues, ep, 0)) { 100102ac6454SAndrew Thompson DPRINTFN(0, "Could not find match\n"); 100202ac6454SAndrew Thompson return (USB_ERR_INVAL); 100302ac6454SAndrew Thompson } 100402ac6454SAndrew Thompson } 100502ac6454SAndrew Thompson } 100602ac6454SAndrew Thompson 100702ac6454SAndrew Thompson ues->ep_max = ues->ep; 100802ac6454SAndrew Thompson 100902ac6454SAndrew Thompson /* Update all endpoint addresses */ 101002ac6454SAndrew Thompson 1011a593f6b8SAndrew Thompson if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || 1012a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || 1013a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || 1014a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_BULK, 1)) { 101502ac6454SAndrew Thompson DPRINTFN(0, "Could not update endpoint address\n"); 101602ac6454SAndrew Thompson return (USB_ERR_INVAL); 101702ac6454SAndrew Thompson } 101802ac6454SAndrew Thompson return (0); /* success */ 101902ac6454SAndrew Thompson } 102002ac6454SAndrew Thompson 102102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1022a593f6b8SAndrew Thompson * usb_temp_get_tdd 102302ac6454SAndrew Thompson * 102402ac6454SAndrew Thompson * Returns: 102502ac6454SAndrew Thompson * NULL: No USB template device descriptor found. 102602ac6454SAndrew Thompson * Else: Pointer to the USB template device descriptor. 102702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1028760bc48eSAndrew Thompson static const struct usb_temp_device_desc * 1029a593f6b8SAndrew Thompson usb_temp_get_tdd(struct usb_device *udev) 103002ac6454SAndrew Thompson { 1031a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 103202ac6454SAndrew Thompson return (NULL); 103302ac6454SAndrew Thompson } 1034a593f6b8SAndrew Thompson return (udev->usb_template_ptr->tdd); 103502ac6454SAndrew Thompson } 103602ac6454SAndrew Thompson 103702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1038a593f6b8SAndrew Thompson * usb_temp_get_device_desc 103902ac6454SAndrew Thompson * 104002ac6454SAndrew Thompson * Returns: 104102ac6454SAndrew Thompson * NULL: No USB device descriptor found. 104202ac6454SAndrew Thompson * Else: Pointer to USB device descriptor. 104302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 104402ac6454SAndrew Thompson static void * 1045a593f6b8SAndrew Thompson usb_temp_get_device_desc(struct usb_device *udev) 104602ac6454SAndrew Thompson { 1047760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 104802ac6454SAndrew Thompson 1049a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 105002ac6454SAndrew Thompson return (NULL); 105102ac6454SAndrew Thompson } 1052a593f6b8SAndrew Thompson dd = &udev->usb_template_ptr->udd; 105302ac6454SAndrew Thompson if (dd->bDescriptorType != UDESC_DEVICE) { 105402ac6454SAndrew Thompson /* sanity check failed */ 105502ac6454SAndrew Thompson return (NULL); 105602ac6454SAndrew Thompson } 105702ac6454SAndrew Thompson return (dd); 105802ac6454SAndrew Thompson } 105902ac6454SAndrew Thompson 106002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1061a593f6b8SAndrew Thompson * usb_temp_get_qualifier_desc 106202ac6454SAndrew Thompson * 106302ac6454SAndrew Thompson * Returns: 106402ac6454SAndrew Thompson * NULL: No USB device_qualifier descriptor found. 106502ac6454SAndrew Thompson * Else: Pointer to USB device_qualifier descriptor. 106602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 106702ac6454SAndrew Thompson static void * 1068a593f6b8SAndrew Thompson usb_temp_get_qualifier_desc(struct usb_device *udev) 106902ac6454SAndrew Thompson { 1070760bc48eSAndrew Thompson struct usb_device_qualifier *dq; 107102ac6454SAndrew Thompson 1072a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 107302ac6454SAndrew Thompson return (NULL); 107402ac6454SAndrew Thompson } 1075a593f6b8SAndrew Thompson dq = &udev->usb_template_ptr->udq; 107602ac6454SAndrew Thompson if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { 107702ac6454SAndrew Thompson /* sanity check failed */ 107802ac6454SAndrew Thompson return (NULL); 107902ac6454SAndrew Thompson } 108002ac6454SAndrew Thompson return (dq); 108102ac6454SAndrew Thompson } 108202ac6454SAndrew Thompson 108302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1084a593f6b8SAndrew Thompson * usb_temp_get_config_desc 108502ac6454SAndrew Thompson * 108602ac6454SAndrew Thompson * Returns: 108702ac6454SAndrew Thompson * NULL: No USB config descriptor found. 108802ac6454SAndrew Thompson * Else: Pointer to USB config descriptor having index "index". 108902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 109002ac6454SAndrew Thompson static void * 1091a593f6b8SAndrew Thompson usb_temp_get_config_desc(struct usb_device *udev, 109202ac6454SAndrew Thompson uint16_t *pLength, uint8_t index) 109302ac6454SAndrew Thompson { 1094760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 1095760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 109602ac6454SAndrew Thompson uint16_t temp; 109702ac6454SAndrew Thompson 1098a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 109902ac6454SAndrew Thompson return (NULL); 110002ac6454SAndrew Thompson } 1101a593f6b8SAndrew Thompson dd = &udev->usb_template_ptr->udd; 1102a593f6b8SAndrew Thompson cd = (void *)(udev->usb_template_ptr + 1); 110302ac6454SAndrew Thompson 110402ac6454SAndrew Thompson if (index >= dd->bNumConfigurations) { 110502ac6454SAndrew Thompson /* out of range */ 110602ac6454SAndrew Thompson return (NULL); 110702ac6454SAndrew Thompson } 110802ac6454SAndrew Thompson while (index--) { 110902ac6454SAndrew Thompson if (cd->bDescriptorType != UDESC_CONFIG) { 111002ac6454SAndrew Thompson /* sanity check failed */ 111102ac6454SAndrew Thompson return (NULL); 111202ac6454SAndrew Thompson } 111302ac6454SAndrew Thompson temp = UGETW(cd->wTotalLength); 111402ac6454SAndrew Thompson cd = USB_ADD_BYTES(cd, temp); 111502ac6454SAndrew Thompson } 111602ac6454SAndrew Thompson 111702ac6454SAndrew Thompson if (pLength) { 111802ac6454SAndrew Thompson *pLength = UGETW(cd->wTotalLength); 111902ac6454SAndrew Thompson } 112002ac6454SAndrew Thompson return (cd); 112102ac6454SAndrew Thompson } 112202ac6454SAndrew Thompson 112302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1124a593f6b8SAndrew Thompson * usb_temp_get_vendor_desc 112502ac6454SAndrew Thompson * 112602ac6454SAndrew Thompson * Returns: 112702ac6454SAndrew Thompson * NULL: No vendor descriptor found. 112802ac6454SAndrew Thompson * Else: Pointer to a vendor descriptor. 112902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 113002ac6454SAndrew Thompson static const void * 1131a593f6b8SAndrew Thompson usb_temp_get_vendor_desc(struct usb_device *udev, 11329a8e0122SAndrew Thompson const struct usb_device_request *req, uint16_t *plen) 113302ac6454SAndrew Thompson { 1134760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 113502ac6454SAndrew Thompson 1136a593f6b8SAndrew Thompson tdd = usb_temp_get_tdd(udev); 113702ac6454SAndrew Thompson if (tdd == NULL) { 113802ac6454SAndrew Thompson return (NULL); 113902ac6454SAndrew Thompson } 114002ac6454SAndrew Thompson if (tdd->getVendorDesc == NULL) { 114102ac6454SAndrew Thompson return (NULL); 114202ac6454SAndrew Thompson } 11439a8e0122SAndrew Thompson return ((tdd->getVendorDesc) (req, plen)); 114402ac6454SAndrew Thompson } 114502ac6454SAndrew Thompson 114602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1147a593f6b8SAndrew Thompson * usb_temp_get_string_desc 114802ac6454SAndrew Thompson * 114902ac6454SAndrew Thompson * Returns: 115002ac6454SAndrew Thompson * NULL: No string descriptor found. 115102ac6454SAndrew Thompson * Else: Pointer to a string descriptor. 115202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 115302ac6454SAndrew Thompson static const void * 1154a593f6b8SAndrew Thompson usb_temp_get_string_desc(struct usb_device *udev, 115502ac6454SAndrew Thompson uint16_t lang_id, uint8_t string_index) 115602ac6454SAndrew Thompson { 1157760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 115802ac6454SAndrew Thompson 1159a593f6b8SAndrew Thompson tdd = usb_temp_get_tdd(udev); 116002ac6454SAndrew Thompson if (tdd == NULL) { 116102ac6454SAndrew Thompson return (NULL); 116202ac6454SAndrew Thompson } 116302ac6454SAndrew Thompson if (tdd->getStringDesc == NULL) { 116402ac6454SAndrew Thompson return (NULL); 116502ac6454SAndrew Thompson } 116602ac6454SAndrew Thompson return ((tdd->getStringDesc) (lang_id, string_index)); 116702ac6454SAndrew Thompson } 116802ac6454SAndrew Thompson 116902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1170a593f6b8SAndrew Thompson * usb_temp_get_hub_desc 117102ac6454SAndrew Thompson * 117202ac6454SAndrew Thompson * Returns: 117302ac6454SAndrew Thompson * NULL: No USB HUB descriptor found. 117402ac6454SAndrew Thompson * Else: Pointer to a USB HUB descriptor. 117502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 117602ac6454SAndrew Thompson static const void * 1177a593f6b8SAndrew Thompson usb_temp_get_hub_desc(struct usb_device *udev) 117802ac6454SAndrew Thompson { 117902ac6454SAndrew Thompson return (NULL); /* needs to be implemented */ 118002ac6454SAndrew Thompson } 118102ac6454SAndrew Thompson 118202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1183a593f6b8SAndrew Thompson * usb_temp_get_desc 118402ac6454SAndrew Thompson * 118502ac6454SAndrew Thompson * This function is a demultiplexer for local USB device side control 118602ac6454SAndrew Thompson * endpoint requests. 118702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1188e0a69b51SAndrew Thompson static usb_error_t 1189a593f6b8SAndrew Thompson usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, 119002ac6454SAndrew Thompson const void **pPtr, uint16_t *pLength) 119102ac6454SAndrew Thompson { 119202ac6454SAndrew Thompson const uint8_t *buf; 119302ac6454SAndrew Thompson uint16_t len; 119402ac6454SAndrew Thompson 119502ac6454SAndrew Thompson buf = NULL; 119602ac6454SAndrew Thompson len = 0; 119702ac6454SAndrew Thompson 119802ac6454SAndrew Thompson switch (req->bmRequestType) { 119902ac6454SAndrew Thompson case UT_READ_DEVICE: 120002ac6454SAndrew Thompson switch (req->bRequest) { 120102ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 120202ac6454SAndrew Thompson goto tr_handle_get_descriptor; 120302ac6454SAndrew Thompson default: 120402ac6454SAndrew Thompson goto tr_stalled; 120502ac6454SAndrew Thompson } 120602ac6454SAndrew Thompson case UT_READ_CLASS_DEVICE: 120702ac6454SAndrew Thompson switch (req->bRequest) { 120802ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 120902ac6454SAndrew Thompson goto tr_handle_get_class_descriptor; 121002ac6454SAndrew Thompson default: 121102ac6454SAndrew Thompson goto tr_stalled; 121202ac6454SAndrew Thompson } 121302ac6454SAndrew Thompson default: 121402ac6454SAndrew Thompson goto tr_stalled; 121502ac6454SAndrew Thompson } 121602ac6454SAndrew Thompson 121702ac6454SAndrew Thompson tr_handle_get_descriptor: 121802ac6454SAndrew Thompson switch (req->wValue[1]) { 121902ac6454SAndrew Thompson case UDESC_DEVICE: 122002ac6454SAndrew Thompson if (req->wValue[0]) { 122102ac6454SAndrew Thompson goto tr_stalled; 122202ac6454SAndrew Thompson } 1223a593f6b8SAndrew Thompson buf = usb_temp_get_device_desc(udev); 122402ac6454SAndrew Thompson goto tr_valid; 122502ac6454SAndrew Thompson case UDESC_DEVICE_QUALIFIER: 122602ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 122702ac6454SAndrew Thompson goto tr_stalled; 122802ac6454SAndrew Thompson } 122902ac6454SAndrew Thompson if (req->wValue[0]) { 123002ac6454SAndrew Thompson goto tr_stalled; 123102ac6454SAndrew Thompson } 1232a593f6b8SAndrew Thompson buf = usb_temp_get_qualifier_desc(udev); 123302ac6454SAndrew Thompson goto tr_valid; 123402ac6454SAndrew Thompson case UDESC_OTHER_SPEED_CONFIGURATION: 123502ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 123602ac6454SAndrew Thompson goto tr_stalled; 123702ac6454SAndrew Thompson } 123802ac6454SAndrew Thompson case UDESC_CONFIG: 1239a593f6b8SAndrew Thompson buf = usb_temp_get_config_desc(udev, 124002ac6454SAndrew Thompson &len, req->wValue[0]); 124102ac6454SAndrew Thompson goto tr_valid; 124202ac6454SAndrew Thompson case UDESC_STRING: 1243a593f6b8SAndrew Thompson buf = usb_temp_get_string_desc(udev, 124402ac6454SAndrew Thompson UGETW(req->wIndex), req->wValue[0]); 124502ac6454SAndrew Thompson goto tr_valid; 124602ac6454SAndrew Thompson default: 124702ac6454SAndrew Thompson goto tr_stalled; 124802ac6454SAndrew Thompson } 124902ac6454SAndrew Thompson 125002ac6454SAndrew Thompson tr_handle_get_class_descriptor: 125102ac6454SAndrew Thompson if (req->wValue[0]) { 125202ac6454SAndrew Thompson goto tr_stalled; 125302ac6454SAndrew Thompson } 1254a593f6b8SAndrew Thompson buf = usb_temp_get_hub_desc(udev); 125502ac6454SAndrew Thompson goto tr_valid; 125602ac6454SAndrew Thompson 125702ac6454SAndrew Thompson tr_valid: 12589a8e0122SAndrew Thompson if (buf == NULL) 125902ac6454SAndrew Thompson goto tr_stalled; 12609a8e0122SAndrew Thompson if (len == 0) 126102ac6454SAndrew Thompson len = buf[0]; 126202ac6454SAndrew Thompson *pPtr = buf; 126302ac6454SAndrew Thompson *pLength = len; 1264459d369eSAndrew Thompson return (0); /* success */ 126502ac6454SAndrew Thompson 126602ac6454SAndrew Thompson tr_stalled: 12679a8e0122SAndrew Thompson /* try to get a vendor specific descriptor */ 12689a8e0122SAndrew Thompson len = 0; 12699a8e0122SAndrew Thompson buf = usb_temp_get_vendor_desc(udev, req, &len); 12709a8e0122SAndrew Thompson if (buf != NULL) 12719a8e0122SAndrew Thompson goto tr_valid; 127202ac6454SAndrew Thompson *pPtr = NULL; 127302ac6454SAndrew Thompson *pLength = 0; 1274459d369eSAndrew Thompson return (0); /* we ignore failures */ 127502ac6454SAndrew Thompson } 127602ac6454SAndrew Thompson 127702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1278760bc48eSAndrew Thompson * usb_temp_setup 127902ac6454SAndrew Thompson * 128002ac6454SAndrew Thompson * This function generates USB descriptors according to the given USB 128102ac6454SAndrew Thompson * template device descriptor. It will also try to figure out the best 128202ac6454SAndrew Thompson * matching endpoint addresses using the hardware endpoint profiles. 128302ac6454SAndrew Thompson * 128402ac6454SAndrew Thompson * Returns: 128502ac6454SAndrew Thompson * 0: Success 128602ac6454SAndrew Thompson * Else: Failure 128702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 12889a8e0122SAndrew Thompson usb_error_t 1289760bc48eSAndrew Thompson usb_temp_setup(struct usb_device *udev, 1290760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 129102ac6454SAndrew Thompson { 1292760bc48eSAndrew Thompson struct usb_temp_setup *uts; 129302ac6454SAndrew Thompson void *buf; 12946950c75fSHans Petter Selasky usb_error_t error; 129502ac6454SAndrew Thompson uint8_t n; 12966950c75fSHans Petter Selasky uint8_t do_unlock; 129702ac6454SAndrew Thompson 129802ac6454SAndrew Thompson /* be NULL safe */ 12996950c75fSHans Petter Selasky if (tdd == NULL) 130002ac6454SAndrew Thompson return (0); 13016950c75fSHans Petter Selasky 13026950c75fSHans Petter Selasky /* Protect scratch area */ 130364cb5e2aSHans Petter Selasky do_unlock = usbd_ctrl_lock(udev); 13046950c75fSHans Petter Selasky 13056950c75fSHans Petter Selasky uts = udev->scratch.temp_setup; 130602ac6454SAndrew Thompson 1307271ae033SHans Petter Selasky memset(uts, 0, sizeof(*uts)); 130802ac6454SAndrew Thompson 13098d2dd5ddSAndrew Thompson uts->usb_speed = udev->speed; 131002ac6454SAndrew Thompson uts->self_powered = udev->flags.self_powered; 131102ac6454SAndrew Thompson 131202ac6454SAndrew Thompson /* first pass */ 131302ac6454SAndrew Thompson 1314a593f6b8SAndrew Thompson usb_make_device_desc(uts, tdd); 131502ac6454SAndrew Thompson 131602ac6454SAndrew Thompson if (uts->err) { 131702ac6454SAndrew Thompson /* some error happened */ 13186950c75fSHans Petter Selasky goto done; 131902ac6454SAndrew Thompson } 132002ac6454SAndrew Thompson /* sanity check */ 132102ac6454SAndrew Thompson if (uts->size == 0) { 13226950c75fSHans Petter Selasky uts->err = USB_ERR_INVAL; 13236950c75fSHans Petter Selasky goto done; 132402ac6454SAndrew Thompson } 132502ac6454SAndrew Thompson /* allocate zeroed memory */ 13262c79a775SHans Petter Selasky uts->buf = usbd_alloc_config_desc(udev, uts->size); 13276950c75fSHans Petter Selasky /* 13286950c75fSHans Petter Selasky * Allow malloc() to return NULL regardless of M_WAITOK flag. 13296950c75fSHans Petter Selasky * This helps when porting the software to non-FreeBSD 13306950c75fSHans Petter Selasky * systems. 13316950c75fSHans Petter Selasky */ 133202ac6454SAndrew Thompson if (uts->buf == NULL) { 133302ac6454SAndrew Thompson /* could not allocate memory */ 13346950c75fSHans Petter Selasky uts->err = USB_ERR_NOMEM; 13356950c75fSHans Petter Selasky goto done; 133602ac6454SAndrew Thompson } 133702ac6454SAndrew Thompson /* second pass */ 133802ac6454SAndrew Thompson 133902ac6454SAndrew Thompson uts->size = 0; 134002ac6454SAndrew Thompson 1341a593f6b8SAndrew Thompson usb_make_device_desc(uts, tdd); 134202ac6454SAndrew Thompson 134302ac6454SAndrew Thompson /* 134402ac6454SAndrew Thompson * Store a pointer to our descriptors: 134502ac6454SAndrew Thompson */ 1346a593f6b8SAndrew Thompson udev->usb_template_ptr = uts->buf; 134702ac6454SAndrew Thompson 134802ac6454SAndrew Thompson if (uts->err) { 134902ac6454SAndrew Thompson /* some error happened during second pass */ 13506950c75fSHans Petter Selasky goto done; 135102ac6454SAndrew Thompson } 135202ac6454SAndrew Thompson /* 135302ac6454SAndrew Thompson * Resolve all endpoint addresses ! 135402ac6454SAndrew Thompson */ 1355a593f6b8SAndrew Thompson buf = usb_temp_get_device_desc(udev); 1356a593f6b8SAndrew Thompson uts->err = usb_hw_ep_resolve(udev, buf); 135702ac6454SAndrew Thompson if (uts->err) { 135802ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 135902ac6454SAndrew Thompson "Device Descriptor, error = %s\n", 1360a593f6b8SAndrew Thompson usbd_errstr(uts->err)); 13616950c75fSHans Petter Selasky goto done; 136202ac6454SAndrew Thompson } 136302ac6454SAndrew Thompson for (n = 0;; n++) { 136402ac6454SAndrew Thompson 1365a593f6b8SAndrew Thompson buf = usb_temp_get_config_desc(udev, NULL, n); 136602ac6454SAndrew Thompson if (buf == NULL) { 136702ac6454SAndrew Thompson break; 136802ac6454SAndrew Thompson } 1369a593f6b8SAndrew Thompson uts->err = usb_hw_ep_resolve(udev, buf); 137002ac6454SAndrew Thompson if (uts->err) { 137102ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 137202ac6454SAndrew Thompson "Config Descriptor %u, error = %s\n", n, 1373a593f6b8SAndrew Thompson usbd_errstr(uts->err)); 13746950c75fSHans Petter Selasky goto done; 137502ac6454SAndrew Thompson } 137602ac6454SAndrew Thompson } 13776950c75fSHans Petter Selasky done: 13786950c75fSHans Petter Selasky error = uts->err; 13796950c75fSHans Petter Selasky if (error) 1380a593f6b8SAndrew Thompson usb_temp_unsetup(udev); 13816950c75fSHans Petter Selasky if (do_unlock) 138264cb5e2aSHans Petter Selasky usbd_ctrl_unlock(udev); 13836950c75fSHans Petter Selasky return (error); 138402ac6454SAndrew Thompson } 138502ac6454SAndrew Thompson 138602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1387a593f6b8SAndrew Thompson * usb_temp_unsetup 138802ac6454SAndrew Thompson * 138902ac6454SAndrew Thompson * This function frees any memory associated with the currently 139002ac6454SAndrew Thompson * setup template, if any. 139102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 13929a8e0122SAndrew Thompson void 1393a593f6b8SAndrew Thompson usb_temp_unsetup(struct usb_device *udev) 139402ac6454SAndrew Thompson { 13952c79a775SHans Petter Selasky usbd_free_config_desc(udev, udev->usb_template_ptr); 1396a593f6b8SAndrew Thompson udev->usb_template_ptr = NULL; 139702ac6454SAndrew Thompson } 139802ac6454SAndrew Thompson 1399e0a69b51SAndrew Thompson static usb_error_t 1400a593f6b8SAndrew Thompson usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) 140102ac6454SAndrew Thompson { 1402e0a69b51SAndrew Thompson usb_error_t err; 140302ac6454SAndrew Thompson 140402ac6454SAndrew Thompson switch (index) { 1405399e6543SHans Petter Selasky case USB_TEMP_MSC: 1406a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_msc); 140702ac6454SAndrew Thompson break; 1408399e6543SHans Petter Selasky case USB_TEMP_CDCE: 1409a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_cdce); 141002ac6454SAndrew Thompson break; 1411399e6543SHans Petter Selasky case USB_TEMP_MTP: 1412a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_mtp); 141302ac6454SAndrew Thompson break; 1414399e6543SHans Petter Selasky case USB_TEMP_MODEM: 1415399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_modem); 1416399e6543SHans Petter Selasky break; 1417399e6543SHans Petter Selasky case USB_TEMP_AUDIO: 1418399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_audio); 1419399e6543SHans Petter Selasky break; 1420399e6543SHans Petter Selasky case USB_TEMP_KBD: 1421399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_kbd); 1422399e6543SHans Petter Selasky break; 1423399e6543SHans Petter Selasky case USB_TEMP_MOUSE: 1424399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_mouse); 1425399e6543SHans Petter Selasky break; 1426f9478f91SHans Petter Selasky case USB_TEMP_PHONE: 1427f9478f91SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_phone); 1428f9478f91SHans Petter Selasky break; 14293e420a3eSRuslan Bukin case USB_TEMP_SERIALNET: 14303e420a3eSRuslan Bukin err = usb_temp_setup(udev, &usb_template_serialnet); 14313e420a3eSRuslan Bukin break; 14320a76fe7cSHans Petter Selasky case USB_TEMP_MIDI: 14330a76fe7cSHans Petter Selasky err = usb_temp_setup(udev, &usb_template_midi); 14340a76fe7cSHans Petter Selasky break; 1435*3dc87e52SEdward Tomasz Napierala case USB_TEMP_MULTI: 1436*3dc87e52SEdward Tomasz Napierala err = usb_temp_setup(udev, &usb_template_multi); 1437*3dc87e52SEdward Tomasz Napierala break; 143802ac6454SAndrew Thompson default: 143902ac6454SAndrew Thompson return (USB_ERR_INVAL); 144002ac6454SAndrew Thompson } 144102ac6454SAndrew Thompson 144202ac6454SAndrew Thompson return (err); 144302ac6454SAndrew Thompson } 144402ac6454SAndrew Thompson 144502ac6454SAndrew Thompson static void 1446a593f6b8SAndrew Thompson usb_temp_init(void *arg) 144702ac6454SAndrew Thompson { 144802ac6454SAndrew Thompson /* register our functions */ 1449a593f6b8SAndrew Thompson usb_temp_get_desc_p = &usb_temp_get_desc; 1450a593f6b8SAndrew Thompson usb_temp_setup_by_index_p = &usb_temp_setup_by_index; 1451a593f6b8SAndrew Thompson usb_temp_unsetup_p = &usb_temp_unsetup; 145202ac6454SAndrew Thompson } 145302ac6454SAndrew Thompson 1454a593f6b8SAndrew Thompson SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); 1455a593f6b8SAndrew Thompson SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); 1456