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 7902ac6454SAndrew Thompson MODULE_DEPEND(usb_template, usb, 1, 1, 1); 8002ac6454SAndrew Thompson MODULE_VERSION(usb_template, 1); 8102ac6454SAndrew Thompson 8202ac6454SAndrew Thompson /* function prototypes */ 8302ac6454SAndrew Thompson 84*d008c0d7SEdward Tomasz Napierala static int sysctl_hw_usb_template_power(SYSCTL_HANDLER_ARGS); 85a593f6b8SAndrew Thompson static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); 86a593f6b8SAndrew Thompson static void usb_make_endpoint_desc(struct usb_temp_setup *, 87760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *); 88a593f6b8SAndrew Thompson static void usb_make_interface_desc(struct usb_temp_setup *, 89760bc48eSAndrew Thompson const struct usb_temp_interface_desc *); 90a593f6b8SAndrew Thompson static void usb_make_config_desc(struct usb_temp_setup *, 91760bc48eSAndrew Thompson const struct usb_temp_config_desc *); 92a593f6b8SAndrew Thompson static void usb_make_device_desc(struct usb_temp_setup *, 93760bc48eSAndrew Thompson const struct usb_temp_device_desc *); 94a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, 9502ac6454SAndrew Thompson uint8_t); 96a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, 97760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *, uint8_t); 98a593f6b8SAndrew Thompson static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, 9902ac6454SAndrew Thompson uint8_t); 100a593f6b8SAndrew Thompson static usb_error_t usb_hw_ep_resolve(struct usb_device *, 101760bc48eSAndrew Thompson struct usb_descriptor *); 102a593f6b8SAndrew Thompson static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); 103a593f6b8SAndrew Thompson static void *usb_temp_get_device_desc(struct usb_device *); 104a593f6b8SAndrew Thompson static void *usb_temp_get_qualifier_desc(struct usb_device *); 105a593f6b8SAndrew Thompson static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, 10602ac6454SAndrew Thompson uint8_t); 107a593f6b8SAndrew Thompson static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, 10802ac6454SAndrew Thompson uint8_t); 109a593f6b8SAndrew Thompson static const void *usb_temp_get_vendor_desc(struct usb_device *, 1109a8e0122SAndrew Thompson const struct usb_device_request *, uint16_t *plen); 111a593f6b8SAndrew Thompson static const void *usb_temp_get_hub_desc(struct usb_device *); 112a593f6b8SAndrew Thompson static usb_error_t usb_temp_get_desc(struct usb_device *, 113760bc48eSAndrew Thompson struct usb_device_request *, const void **, uint16_t *); 114a593f6b8SAndrew Thompson static usb_error_t usb_temp_setup_by_index(struct usb_device *, 11502ac6454SAndrew Thompson uint16_t index); 116a593f6b8SAndrew Thompson static void usb_temp_init(void *); 11702ac6454SAndrew Thompson 118*d008c0d7SEdward Tomasz Napierala SYSCTL_NODE(_hw_usb, OID_AUTO, templates, CTLFLAG_RW, 0, 119*d008c0d7SEdward Tomasz Napierala "USB device side templates"); 120*d008c0d7SEdward Tomasz Napierala SYSCTL_PROC(_hw_usb, OID_AUTO, template_power, 121*d008c0d7SEdward Tomasz Napierala CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 122*d008c0d7SEdward Tomasz Napierala NULL, 0, sysctl_hw_usb_template_power, 123*d008c0d7SEdward Tomasz Napierala "I", "USB bus power consumption in mA"); 124*d008c0d7SEdward Tomasz Napierala 125*d008c0d7SEdward Tomasz Napierala static int usb_template_power = 500; /* 500mA */ 126*d008c0d7SEdward Tomasz Napierala 127*d008c0d7SEdward Tomasz Napierala static int 128*d008c0d7SEdward Tomasz Napierala sysctl_hw_usb_template_power(SYSCTL_HANDLER_ARGS) 129*d008c0d7SEdward Tomasz Napierala { 130*d008c0d7SEdward Tomasz Napierala int error, val; 131*d008c0d7SEdward Tomasz Napierala 132*d008c0d7SEdward Tomasz Napierala val = usb_template_power; 133*d008c0d7SEdward Tomasz Napierala error = sysctl_handle_int(oidp, &val, 0, req); 134*d008c0d7SEdward Tomasz Napierala if (error != 0 || req->newptr == NULL) 135*d008c0d7SEdward Tomasz Napierala return (error); 136*d008c0d7SEdward Tomasz Napierala 137*d008c0d7SEdward Tomasz Napierala if (val < 0 || val > 500) 138*d008c0d7SEdward Tomasz Napierala return (EINVAL); 139*d008c0d7SEdward Tomasz Napierala 140*d008c0d7SEdward Tomasz Napierala usb_template_power = val; 141*d008c0d7SEdward Tomasz Napierala 142*d008c0d7SEdward Tomasz Napierala return (0); 143*d008c0d7SEdward Tomasz Napierala } 144*d008c0d7SEdward Tomasz Napierala 14502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1468e06491aSEdward Tomasz Napierala * usb_decode_str_desc 1478e06491aSEdward Tomasz Napierala * 1488e06491aSEdward Tomasz Napierala * Helper function to decode string descriptors into a C string. 1498e06491aSEdward Tomasz Napierala *------------------------------------------------------------------------*/ 1508e06491aSEdward Tomasz Napierala void 1518e06491aSEdward Tomasz Napierala usb_decode_str_desc(struct usb_string_descriptor *sd, char *buf, size_t buflen) 1528e06491aSEdward Tomasz Napierala { 1538e06491aSEdward Tomasz Napierala size_t i; 1548e06491aSEdward Tomasz Napierala 1552d1a7697SEdward Tomasz Napierala if (sd->bLength < 2) { 1562d1a7697SEdward Tomasz Napierala buf[0] = '\0'; 1572d1a7697SEdward Tomasz Napierala return; 1582d1a7697SEdward Tomasz Napierala } 1592d1a7697SEdward Tomasz Napierala 1602d1a7697SEdward Tomasz Napierala for (i = 0; i < buflen - 1 && i < (sd->bLength / 2) - 1; i++) 1618e06491aSEdward Tomasz Napierala buf[i] = UGETW(sd->bString[i]); 1628e06491aSEdward Tomasz Napierala 1638e06491aSEdward Tomasz Napierala buf[i] = '\0'; 1648e06491aSEdward Tomasz Napierala } 1658e06491aSEdward Tomasz Napierala 1668e06491aSEdward Tomasz Napierala /*------------------------------------------------------------------------* 1678e06491aSEdward Tomasz Napierala * usb_temp_sysctl 1688e06491aSEdward Tomasz Napierala * 1698e06491aSEdward Tomasz Napierala * Callback for SYSCTL_PROC(9), to set and retrieve template string 1708e06491aSEdward Tomasz Napierala * descriptors. 1718e06491aSEdward Tomasz Napierala *------------------------------------------------------------------------*/ 1728e06491aSEdward Tomasz Napierala int 1738e06491aSEdward Tomasz Napierala usb_temp_sysctl(SYSCTL_HANDLER_ARGS) 1748e06491aSEdward Tomasz Napierala { 1758e06491aSEdward Tomasz Napierala char buf[128]; 1768e06491aSEdward Tomasz Napierala struct usb_string_descriptor *sd = arg1; 1778e06491aSEdward Tomasz Napierala size_t len, sdlen = arg2; 1788e06491aSEdward Tomasz Napierala int error; 1798e06491aSEdward Tomasz Napierala 1808e06491aSEdward Tomasz Napierala usb_decode_str_desc(sd, buf, sizeof(buf)); 1818e06491aSEdward Tomasz Napierala 1828e06491aSEdward Tomasz Napierala error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 1838e06491aSEdward Tomasz Napierala if (error != 0 || req->newptr == NULL) 1848e06491aSEdward Tomasz Napierala return (error); 1858e06491aSEdward Tomasz Napierala 1868e06491aSEdward Tomasz Napierala len = usb_make_str_desc(sd, sdlen, buf); 1878e06491aSEdward Tomasz Napierala if (len == 0) 1888e06491aSEdward Tomasz Napierala return (EINVAL); 1898e06491aSEdward Tomasz Napierala 1908e06491aSEdward Tomasz Napierala return (0); 1918e06491aSEdward Tomasz Napierala } 1928e06491aSEdward Tomasz Napierala 1938e06491aSEdward Tomasz Napierala 1948e06491aSEdward Tomasz Napierala /*------------------------------------------------------------------------* 195a593f6b8SAndrew Thompson * usb_make_raw_desc 19602ac6454SAndrew Thompson * 19702ac6454SAndrew Thompson * This function will insert a raw USB descriptor into the generated 19802ac6454SAndrew Thompson * USB configuration. 19902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 20002ac6454SAndrew Thompson static void 201a593f6b8SAndrew Thompson usb_make_raw_desc(struct usb_temp_setup *temp, 20202ac6454SAndrew Thompson const uint8_t *raw) 20302ac6454SAndrew Thompson { 20402ac6454SAndrew Thompson void *dst; 20502ac6454SAndrew Thompson uint8_t len; 20602ac6454SAndrew Thompson 20702ac6454SAndrew Thompson /* 20802ac6454SAndrew Thompson * The first byte of any USB descriptor gives the length. 20902ac6454SAndrew Thompson */ 21002ac6454SAndrew Thompson if (raw) { 21102ac6454SAndrew Thompson len = raw[0]; 21202ac6454SAndrew Thompson if (temp->buf) { 21302ac6454SAndrew Thompson dst = USB_ADD_BYTES(temp->buf, temp->size); 21488334428SHans Petter Selasky memcpy(dst, raw, len); 21502ac6454SAndrew Thompson 21602ac6454SAndrew Thompson /* check if we have got a CDC union descriptor */ 21702ac6454SAndrew Thompson 218f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) && 21902ac6454SAndrew Thompson (raw[1] == UDESC_CS_INTERFACE) && 22002ac6454SAndrew Thompson (raw[2] == UDESCSUB_CDC_UNION)) { 221760bc48eSAndrew Thompson struct usb_cdc_union_descriptor *ud = (void *)dst; 22202ac6454SAndrew Thompson 22302ac6454SAndrew Thompson /* update the interface numbers */ 22402ac6454SAndrew Thompson 22502ac6454SAndrew Thompson ud->bMasterInterface += 22602ac6454SAndrew Thompson temp->bInterfaceNumber; 22702ac6454SAndrew Thompson ud->bSlaveInterface[0] += 22802ac6454SAndrew Thompson temp->bInterfaceNumber; 22902ac6454SAndrew Thompson } 230399e6543SHans Petter Selasky 231399e6543SHans Petter Selasky /* check if we have got an interface association descriptor */ 232399e6543SHans Petter Selasky 233f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) && 234399e6543SHans Petter Selasky (raw[1] == UDESC_IFACE_ASSOC)) { 235399e6543SHans Petter Selasky struct usb_interface_assoc_descriptor *iad = (void *)dst; 236399e6543SHans Petter Selasky 237399e6543SHans Petter Selasky /* update the interface number */ 238399e6543SHans Petter Selasky 239399e6543SHans Petter Selasky iad->bFirstInterface += 240399e6543SHans Petter Selasky temp->bInterfaceNumber; 241399e6543SHans Petter Selasky } 242399e6543SHans Petter Selasky 243399e6543SHans Petter Selasky /* check if we have got a call management descriptor */ 244399e6543SHans Petter Selasky 245f9478f91SHans Petter Selasky if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) && 246399e6543SHans Petter Selasky (raw[1] == UDESC_CS_INTERFACE) && 247399e6543SHans Petter Selasky (raw[2] == UDESCSUB_CDC_CM)) { 248399e6543SHans Petter Selasky struct usb_cdc_cm_descriptor *ccd = (void *)dst; 249399e6543SHans Petter Selasky 250399e6543SHans Petter Selasky /* update the interface number */ 251399e6543SHans Petter Selasky 252399e6543SHans Petter Selasky ccd->bDataInterface += 253399e6543SHans Petter Selasky temp->bInterfaceNumber; 254399e6543SHans Petter Selasky } 25502ac6454SAndrew Thompson } 25602ac6454SAndrew Thompson temp->size += len; 25702ac6454SAndrew Thompson } 25802ac6454SAndrew Thompson } 25902ac6454SAndrew Thompson 26002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 261a593f6b8SAndrew Thompson * usb_make_endpoint_desc 26202ac6454SAndrew Thompson * 26302ac6454SAndrew Thompson * This function will generate an USB endpoint descriptor from the 26402ac6454SAndrew Thompson * given USB template endpoint descriptor, which will be inserted into 26502ac6454SAndrew Thompson * the USB configuration. 26602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 26702ac6454SAndrew Thompson static void 268a593f6b8SAndrew Thompson usb_make_endpoint_desc(struct usb_temp_setup *temp, 269760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *ted) 27002ac6454SAndrew Thompson { 271760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 27202ac6454SAndrew Thompson const void **rd; 27302ac6454SAndrew Thompson uint16_t old_size; 27402ac6454SAndrew Thompson uint16_t mps; 2756a268418SAndrew Thompson uint8_t ea; /* Endpoint Address */ 2766a268418SAndrew Thompson uint8_t et; /* Endpiont Type */ 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson /* Reserve memory */ 27902ac6454SAndrew Thompson old_size = temp->size; 2806a268418SAndrew Thompson 2816a268418SAndrew Thompson ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); 2826a268418SAndrew Thompson et = (ted->bmAttributes & UE_XFERTYPE); 2836a268418SAndrew Thompson 2846a268418SAndrew Thompson if (et == UE_ISOCHRONOUS) { 2856a268418SAndrew Thompson /* account for extra byte fields */ 2866a268418SAndrew Thompson temp->size += sizeof(*ed) + 2; 2876a268418SAndrew Thompson } else { 28802ac6454SAndrew Thompson temp->size += sizeof(*ed); 2896a268418SAndrew Thompson } 29002ac6454SAndrew Thompson 29102ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 29202ac6454SAndrew Thompson rd = ted->ppRawDesc; 29302ac6454SAndrew Thompson if (rd) { 29402ac6454SAndrew Thompson while (*rd) { 295a593f6b8SAndrew Thompson usb_make_raw_desc(temp, *rd); 29602ac6454SAndrew Thompson rd++; 29702ac6454SAndrew Thompson } 29802ac6454SAndrew Thompson } 29902ac6454SAndrew Thompson if (ted->pPacketSize == NULL) { 30002ac6454SAndrew Thompson /* not initialized */ 30102ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 30202ac6454SAndrew Thompson return; 30302ac6454SAndrew Thompson } 3048d2dd5ddSAndrew Thompson mps = ted->pPacketSize->mps[temp->usb_speed]; 30502ac6454SAndrew Thompson if (mps == 0) { 30602ac6454SAndrew Thompson /* not initialized */ 30702ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 30802ac6454SAndrew Thompson return; 30902ac6454SAndrew Thompson } else if (mps == UE_ZERO_MPS) { 31002ac6454SAndrew Thompson /* escape for Zero Max Packet Size */ 31102ac6454SAndrew Thompson mps = 0; 31202ac6454SAndrew Thompson } 31302ac6454SAndrew Thompson 31402ac6454SAndrew Thompson /* 31502ac6454SAndrew Thompson * Fill out the real USB endpoint descriptor 31602ac6454SAndrew Thompson * in case there is a buffer present: 31702ac6454SAndrew Thompson */ 31802ac6454SAndrew Thompson if (temp->buf) { 31902ac6454SAndrew Thompson ed = USB_ADD_BYTES(temp->buf, old_size); 3206a268418SAndrew Thompson if (et == UE_ISOCHRONOUS) 3216a268418SAndrew Thompson ed->bLength = sizeof(*ed) + 2; 3226a268418SAndrew Thompson else 32302ac6454SAndrew Thompson ed->bLength = sizeof(*ed); 32402ac6454SAndrew Thompson ed->bDescriptorType = UDESC_ENDPOINT; 32502ac6454SAndrew Thompson ed->bEndpointAddress = ea; 32602ac6454SAndrew Thompson ed->bmAttributes = ted->bmAttributes; 32702ac6454SAndrew Thompson USETW(ed->wMaxPacketSize, mps); 32802ac6454SAndrew Thompson 32902ac6454SAndrew Thompson /* setup bInterval parameter */ 33002ac6454SAndrew Thompson 33102ac6454SAndrew Thompson if (ted->pIntervals && 3328d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]) { 33302ac6454SAndrew Thompson ed->bInterval = 3348d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]; 33502ac6454SAndrew Thompson } else { 33602ac6454SAndrew Thompson switch (et) { 33702ac6454SAndrew Thompson case UE_BULK: 33802ac6454SAndrew Thompson case UE_CONTROL: 33902ac6454SAndrew Thompson ed->bInterval = 0; /* not used */ 34002ac6454SAndrew Thompson break; 34102ac6454SAndrew Thompson case UE_INTERRUPT: 3428d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 34302ac6454SAndrew Thompson case USB_SPEED_LOW: 34402ac6454SAndrew Thompson case USB_SPEED_FULL: 34502ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 34602ac6454SAndrew Thompson break; 34702ac6454SAndrew Thompson default: 348e6ee4f7dSHans Petter Selasky ed->bInterval = 4; /* 1 ms */ 34902ac6454SAndrew Thompson break; 35002ac6454SAndrew Thompson } 35102ac6454SAndrew Thompson break; 35202ac6454SAndrew Thompson default: /* UE_ISOCHRONOUS */ 3538d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 35402ac6454SAndrew Thompson case USB_SPEED_LOW: 35502ac6454SAndrew Thompson case USB_SPEED_FULL: 35602ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 35702ac6454SAndrew Thompson break; 35802ac6454SAndrew Thompson default: 35902ac6454SAndrew Thompson ed->bInterval = 1; /* 125 us */ 36002ac6454SAndrew Thompson break; 36102ac6454SAndrew Thompson } 36202ac6454SAndrew Thompson break; 36302ac6454SAndrew Thompson } 36402ac6454SAndrew Thompson } 36502ac6454SAndrew Thompson } 36602ac6454SAndrew Thompson temp->bNumEndpoints++; 36702ac6454SAndrew Thompson } 36802ac6454SAndrew Thompson 36902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 370a593f6b8SAndrew Thompson * usb_make_interface_desc 37102ac6454SAndrew Thompson * 37202ac6454SAndrew Thompson * This function will generate an USB interface descriptor from the 37302ac6454SAndrew Thompson * given USB template interface descriptor, which will be inserted 37402ac6454SAndrew Thompson * into the USB configuration. 37502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37602ac6454SAndrew Thompson static void 377a593f6b8SAndrew Thompson usb_make_interface_desc(struct usb_temp_setup *temp, 378760bc48eSAndrew Thompson const struct usb_temp_interface_desc *tid) 37902ac6454SAndrew Thompson { 380760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 381760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc **ted; 38202ac6454SAndrew Thompson const void **rd; 38302ac6454SAndrew Thompson uint16_t old_size; 38402ac6454SAndrew Thompson 38502ac6454SAndrew Thompson /* Reserve memory */ 38602ac6454SAndrew Thompson 38702ac6454SAndrew Thompson old_size = temp->size; 38802ac6454SAndrew Thompson temp->size += sizeof(*id); 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson /* Update interface and alternate interface numbers */ 39102ac6454SAndrew Thompson 39202ac6454SAndrew Thompson if (tid->isAltInterface == 0) { 39302ac6454SAndrew Thompson temp->bAlternateSetting = 0; 39402ac6454SAndrew Thompson temp->bInterfaceNumber++; 39502ac6454SAndrew Thompson } else { 39602ac6454SAndrew Thompson temp->bAlternateSetting++; 39702ac6454SAndrew Thompson } 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 40002ac6454SAndrew Thompson 40102ac6454SAndrew Thompson rd = tid->ppRawDesc; 40202ac6454SAndrew Thompson 40302ac6454SAndrew Thompson if (rd) { 40402ac6454SAndrew Thompson while (*rd) { 405a593f6b8SAndrew Thompson usb_make_raw_desc(temp, *rd); 40602ac6454SAndrew Thompson rd++; 40702ac6454SAndrew Thompson } 40802ac6454SAndrew Thompson } 40902ac6454SAndrew Thompson /* Reset some counters */ 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson temp->bNumEndpoints = 0; 41202ac6454SAndrew Thompson 41302ac6454SAndrew Thompson /* Scan all Endpoint Descriptors second */ 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson ted = tid->ppEndpoints; 41602ac6454SAndrew Thompson if (ted) { 41702ac6454SAndrew Thompson while (*ted) { 418a593f6b8SAndrew Thompson usb_make_endpoint_desc(temp, *ted); 41902ac6454SAndrew Thompson ted++; 42002ac6454SAndrew Thompson } 42102ac6454SAndrew Thompson } 42202ac6454SAndrew Thompson /* 42302ac6454SAndrew Thompson * Fill out the real USB interface descriptor 42402ac6454SAndrew Thompson * in case there is a buffer present: 42502ac6454SAndrew Thompson */ 42602ac6454SAndrew Thompson if (temp->buf) { 42702ac6454SAndrew Thompson id = USB_ADD_BYTES(temp->buf, old_size); 42802ac6454SAndrew Thompson id->bLength = sizeof(*id); 42902ac6454SAndrew Thompson id->bDescriptorType = UDESC_INTERFACE; 43002ac6454SAndrew Thompson id->bInterfaceNumber = temp->bInterfaceNumber; 43102ac6454SAndrew Thompson id->bAlternateSetting = temp->bAlternateSetting; 43202ac6454SAndrew Thompson id->bNumEndpoints = temp->bNumEndpoints; 43302ac6454SAndrew Thompson id->bInterfaceClass = tid->bInterfaceClass; 43402ac6454SAndrew Thompson id->bInterfaceSubClass = tid->bInterfaceSubClass; 43502ac6454SAndrew Thompson id->bInterfaceProtocol = tid->bInterfaceProtocol; 43602ac6454SAndrew Thompson id->iInterface = tid->iInterface; 43702ac6454SAndrew Thompson } 43802ac6454SAndrew Thompson } 43902ac6454SAndrew Thompson 44002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 441a593f6b8SAndrew Thompson * usb_make_config_desc 44202ac6454SAndrew Thompson * 44302ac6454SAndrew Thompson * This function will generate an USB config descriptor from the given 44402ac6454SAndrew Thompson * USB template config descriptor, which will be inserted into the USB 44502ac6454SAndrew Thompson * configuration. 44602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 44702ac6454SAndrew Thompson static void 448a593f6b8SAndrew Thompson usb_make_config_desc(struct usb_temp_setup *temp, 449760bc48eSAndrew Thompson const struct usb_temp_config_desc *tcd) 45002ac6454SAndrew Thompson { 451760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 452760bc48eSAndrew Thompson const struct usb_temp_interface_desc **tid; 45302ac6454SAndrew Thompson uint16_t old_size; 454*d008c0d7SEdward Tomasz Napierala int power; 45502ac6454SAndrew Thompson 45602ac6454SAndrew Thompson /* Reserve memory */ 45702ac6454SAndrew Thompson 45802ac6454SAndrew Thompson old_size = temp->size; 45902ac6454SAndrew Thompson temp->size += sizeof(*cd); 46002ac6454SAndrew Thompson 46102ac6454SAndrew Thompson /* Reset some counters */ 46202ac6454SAndrew Thompson 4636d917491SHans Petter Selasky temp->bInterfaceNumber = 0xFF; 46402ac6454SAndrew Thompson temp->bAlternateSetting = 0; 46502ac6454SAndrew Thompson 46602ac6454SAndrew Thompson /* Scan all the USB interfaces */ 46702ac6454SAndrew Thompson 46802ac6454SAndrew Thompson tid = tcd->ppIfaceDesc; 46902ac6454SAndrew Thompson if (tid) { 47002ac6454SAndrew Thompson while (*tid) { 471a593f6b8SAndrew Thompson usb_make_interface_desc(temp, *tid); 47202ac6454SAndrew Thompson tid++; 47302ac6454SAndrew Thompson } 47402ac6454SAndrew Thompson } 47502ac6454SAndrew Thompson /* 47602ac6454SAndrew Thompson * Fill out the real USB config descriptor 47702ac6454SAndrew Thompson * in case there is a buffer present: 47802ac6454SAndrew Thompson */ 47902ac6454SAndrew Thompson if (temp->buf) { 48002ac6454SAndrew Thompson cd = USB_ADD_BYTES(temp->buf, old_size); 48102ac6454SAndrew Thompson 48202ac6454SAndrew Thompson /* compute total size */ 48302ac6454SAndrew Thompson old_size = temp->size - old_size; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson cd->bLength = sizeof(*cd); 48602ac6454SAndrew Thompson cd->bDescriptorType = UDESC_CONFIG; 48702ac6454SAndrew Thompson USETW(cd->wTotalLength, old_size); 48802ac6454SAndrew Thompson cd->bNumInterface = temp->bInterfaceNumber + 1; 48902ac6454SAndrew Thompson cd->bConfigurationValue = temp->bConfigurationValue; 49002ac6454SAndrew Thompson cd->iConfiguration = tcd->iConfiguration; 49102ac6454SAndrew Thompson cd->bmAttributes = tcd->bmAttributes; 49202ac6454SAndrew Thompson 493*d008c0d7SEdward Tomasz Napierala power = usb_template_power; 494*d008c0d7SEdward Tomasz Napierala cd->bMaxPower = power / 2; /* 2 mA units */ 495*d008c0d7SEdward Tomasz Napierala cd->bmAttributes |= UC_REMOTE_WAKEUP; 496*d008c0d7SEdward Tomasz Napierala if (power > 0) { 497*d008c0d7SEdward Tomasz Napierala cd->bmAttributes |= UC_BUS_POWERED; 49802ac6454SAndrew Thompson cd->bmAttributes &= ~UC_SELF_POWERED; 499*d008c0d7SEdward Tomasz Napierala } else { 500*d008c0d7SEdward Tomasz Napierala cd->bmAttributes &= ~UC_BUS_POWERED; 501*d008c0d7SEdward Tomasz Napierala cd->bmAttributes |= UC_SELF_POWERED; 50202ac6454SAndrew Thompson } 50302ac6454SAndrew Thompson } 50402ac6454SAndrew Thompson } 50502ac6454SAndrew Thompson 50602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 507a593f6b8SAndrew Thompson * usb_make_device_desc 50802ac6454SAndrew Thompson * 50902ac6454SAndrew Thompson * This function will generate an USB device descriptor from the 51002ac6454SAndrew Thompson * given USB template device descriptor. 51102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 51202ac6454SAndrew Thompson static void 513a593f6b8SAndrew Thompson usb_make_device_desc(struct usb_temp_setup *temp, 514760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 51502ac6454SAndrew Thompson { 516760bc48eSAndrew Thompson struct usb_temp_data *utd; 517760bc48eSAndrew Thompson const struct usb_temp_config_desc **tcd; 51802ac6454SAndrew Thompson uint16_t old_size; 51902ac6454SAndrew Thompson 52002ac6454SAndrew Thompson /* Reserve memory */ 52102ac6454SAndrew Thompson 52202ac6454SAndrew Thompson old_size = temp->size; 52302ac6454SAndrew Thompson temp->size += sizeof(*utd); 52402ac6454SAndrew Thompson 52502ac6454SAndrew Thompson /* Scan all the USB configs */ 52602ac6454SAndrew Thompson 52702ac6454SAndrew Thompson temp->bConfigurationValue = 1; 52802ac6454SAndrew Thompson tcd = tdd->ppConfigDesc; 52902ac6454SAndrew Thompson if (tcd) { 53002ac6454SAndrew Thompson while (*tcd) { 531a593f6b8SAndrew Thompson usb_make_config_desc(temp, *tcd); 53202ac6454SAndrew Thompson temp->bConfigurationValue++; 53302ac6454SAndrew Thompson tcd++; 53402ac6454SAndrew Thompson } 53502ac6454SAndrew Thompson } 53602ac6454SAndrew Thompson /* 53702ac6454SAndrew Thompson * Fill out the real USB device descriptor 53802ac6454SAndrew Thompson * in case there is a buffer present: 53902ac6454SAndrew Thompson */ 54002ac6454SAndrew Thompson 54102ac6454SAndrew Thompson if (temp->buf) { 54202ac6454SAndrew Thompson utd = USB_ADD_BYTES(temp->buf, old_size); 54302ac6454SAndrew Thompson 54402ac6454SAndrew Thompson /* Store a pointer to our template device descriptor */ 54502ac6454SAndrew Thompson utd->tdd = tdd; 54602ac6454SAndrew Thompson 54702ac6454SAndrew Thompson /* Fill out USB device descriptor */ 54802ac6454SAndrew Thompson utd->udd.bLength = sizeof(utd->udd); 54902ac6454SAndrew Thompson utd->udd.bDescriptorType = UDESC_DEVICE; 55002ac6454SAndrew Thompson utd->udd.bDeviceClass = tdd->bDeviceClass; 55102ac6454SAndrew Thompson utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; 55202ac6454SAndrew Thompson utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; 55302ac6454SAndrew Thompson USETW(utd->udd.idVendor, tdd->idVendor); 55402ac6454SAndrew Thompson USETW(utd->udd.idProduct, tdd->idProduct); 55502ac6454SAndrew Thompson USETW(utd->udd.bcdDevice, tdd->bcdDevice); 55602ac6454SAndrew Thompson utd->udd.iManufacturer = tdd->iManufacturer; 55702ac6454SAndrew Thompson utd->udd.iProduct = tdd->iProduct; 55802ac6454SAndrew Thompson utd->udd.iSerialNumber = tdd->iSerialNumber; 55902ac6454SAndrew Thompson utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; 56002ac6454SAndrew Thompson 56102ac6454SAndrew Thompson /* 56202ac6454SAndrew Thompson * Fill out the USB device qualifier. Pretend that we 56302ac6454SAndrew Thompson * don't support any other speeds by setting 56402ac6454SAndrew Thompson * "bNumConfigurations" equal to zero. That saves us 56502ac6454SAndrew Thompson * generating an extra set of configuration 56602ac6454SAndrew Thompson * descriptors. 56702ac6454SAndrew Thompson */ 56802ac6454SAndrew Thompson utd->udq.bLength = sizeof(utd->udq); 56902ac6454SAndrew Thompson utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; 57002ac6454SAndrew Thompson utd->udq.bDeviceClass = tdd->bDeviceClass; 57102ac6454SAndrew Thompson utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; 57202ac6454SAndrew Thompson utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; 57302ac6454SAndrew Thompson utd->udq.bNumConfigurations = 0; 57402ac6454SAndrew Thompson USETW(utd->udq.bcdUSB, 0x0200); 57502ac6454SAndrew Thompson utd->udq.bMaxPacketSize0 = 0; 57602ac6454SAndrew Thompson 5778d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 57802ac6454SAndrew Thompson case USB_SPEED_LOW: 57902ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 58002ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 8; 58102ac6454SAndrew Thompson break; 58202ac6454SAndrew Thompson case USB_SPEED_FULL: 58302ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 58402ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 32; 58502ac6454SAndrew Thompson break; 58602ac6454SAndrew Thompson case USB_SPEED_HIGH: 58702ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0200); 58802ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 64; 58902ac6454SAndrew Thompson break; 59002ac6454SAndrew Thompson case USB_SPEED_VARIABLE: 59102ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0250); 59202ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 255; /* 512 bytes */ 59302ac6454SAndrew Thompson break; 594399e6543SHans Petter Selasky case USB_SPEED_SUPER: 595399e6543SHans Petter Selasky USETW(utd->udd.bcdUSB, 0x0300); 596399e6543SHans Petter Selasky utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ 597399e6543SHans Petter Selasky break; 59802ac6454SAndrew Thompson default: 59902ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 60002ac6454SAndrew Thompson break; 60102ac6454SAndrew Thompson } 60202ac6454SAndrew Thompson } 60302ac6454SAndrew Thompson } 60402ac6454SAndrew Thompson 60502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 606a593f6b8SAndrew Thompson * usb_hw_ep_match 60702ac6454SAndrew Thompson * 60802ac6454SAndrew Thompson * Return values: 60920733245SPedro F. Giffuni * 0: The endpoint profile does not match the criteria 61020733245SPedro F. Giffuni * Else: The endpoint profile matches the criteria 61102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 61202ac6454SAndrew Thompson static uint8_t 613a593f6b8SAndrew Thompson usb_hw_ep_match(const struct usb_hw_ep_profile *pf, 61402ac6454SAndrew Thompson uint8_t ep_type, uint8_t ep_dir_in) 61502ac6454SAndrew Thompson { 61602ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 61702ac6454SAndrew Thompson /* special */ 61802ac6454SAndrew Thompson return (pf->support_control); 61902ac6454SAndrew Thompson } 62002ac6454SAndrew Thompson if ((pf->support_in && ep_dir_in) || 62102ac6454SAndrew Thompson (pf->support_out && !ep_dir_in)) { 62202ac6454SAndrew Thompson if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || 62302ac6454SAndrew Thompson (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || 62402ac6454SAndrew Thompson (pf->support_bulk && (ep_type == UE_BULK))) { 62502ac6454SAndrew Thompson return (1); 62602ac6454SAndrew Thompson } 62702ac6454SAndrew Thompson } 62802ac6454SAndrew Thompson return (0); 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson 63102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 632a593f6b8SAndrew Thompson * usb_hw_ep_find_match 63302ac6454SAndrew Thompson * 63402ac6454SAndrew Thompson * This function is used to find the best matching endpoint profile 63502ac6454SAndrew Thompson * for and endpoint belonging to an USB descriptor. 63602ac6454SAndrew Thompson * 63702ac6454SAndrew Thompson * Return values: 63802ac6454SAndrew Thompson * 0: Success. Got a match. 63902ac6454SAndrew Thompson * Else: Failure. No match. 64002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 64102ac6454SAndrew Thompson static uint8_t 642a593f6b8SAndrew Thompson usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, 643760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) 64402ac6454SAndrew Thompson { 645760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 64602ac6454SAndrew Thompson uint16_t distance; 64702ac6454SAndrew Thompson uint16_t temp; 64802ac6454SAndrew Thompson uint16_t max_frame_size; 64902ac6454SAndrew Thompson uint8_t n; 65002ac6454SAndrew Thompson uint8_t best_n; 65102ac6454SAndrew Thompson uint8_t dir_in; 65202ac6454SAndrew Thompson uint8_t dir_out; 65302ac6454SAndrew Thompson 65402ac6454SAndrew Thompson distance = 0xFFFF; 65502ac6454SAndrew Thompson best_n = 0; 65602ac6454SAndrew Thompson 65702ac6454SAndrew Thompson if ((!ep->needs_in) && (!ep->needs_out)) { 65802ac6454SAndrew Thompson return (0); /* we are done */ 65902ac6454SAndrew Thompson } 66002ac6454SAndrew Thompson if (ep->needs_ep_type == UE_CONTROL) { 66102ac6454SAndrew Thompson dir_in = 1; 66202ac6454SAndrew Thompson dir_out = 1; 66302ac6454SAndrew Thompson } else { 66402ac6454SAndrew Thompson if (ep->needs_in) { 66502ac6454SAndrew Thompson dir_in = 1; 66602ac6454SAndrew Thompson dir_out = 0; 66702ac6454SAndrew Thompson } else { 66802ac6454SAndrew Thompson dir_in = 0; 66902ac6454SAndrew Thompson dir_out = 1; 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson } 67202ac6454SAndrew Thompson 67302ac6454SAndrew Thompson for (n = 1; n != (USB_EP_MAX / 2); n++) { 67402ac6454SAndrew Thompson 67502ac6454SAndrew Thompson /* get HW endpoint profile */ 67602ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); 67702ac6454SAndrew Thompson if (pf == NULL) { 67802ac6454SAndrew Thompson /* end of profiles */ 67902ac6454SAndrew Thompson break; 68002ac6454SAndrew Thompson } 68102ac6454SAndrew Thompson /* check if IN-endpoint is reserved */ 68202ac6454SAndrew Thompson if (dir_in || pf->is_simplex) { 68302ac6454SAndrew Thompson if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { 68402ac6454SAndrew Thompson /* mismatch */ 68502ac6454SAndrew Thompson continue; 68602ac6454SAndrew Thompson } 68702ac6454SAndrew Thompson } 68802ac6454SAndrew Thompson /* check if OUT-endpoint is reserved */ 68902ac6454SAndrew Thompson if (dir_out || pf->is_simplex) { 69002ac6454SAndrew Thompson if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { 69102ac6454SAndrew Thompson /* mismatch */ 69202ac6454SAndrew Thompson continue; 69302ac6454SAndrew Thompson } 69402ac6454SAndrew Thompson } 69502ac6454SAndrew Thompson /* check simplex */ 69602ac6454SAndrew Thompson if (pf->is_simplex == is_simplex) { 69702ac6454SAndrew Thompson /* mismatch */ 69802ac6454SAndrew Thompson continue; 69902ac6454SAndrew Thompson } 70002ac6454SAndrew Thompson /* check if HW endpoint matches */ 701a593f6b8SAndrew Thompson if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { 70202ac6454SAndrew Thompson /* mismatch */ 70302ac6454SAndrew Thompson continue; 70402ac6454SAndrew Thompson } 70502ac6454SAndrew Thompson /* get maximum frame size */ 70602ac6454SAndrew Thompson if (dir_in) 70702ac6454SAndrew Thompson max_frame_size = pf->max_in_frame_size; 70802ac6454SAndrew Thompson else 70902ac6454SAndrew Thompson max_frame_size = pf->max_out_frame_size; 71002ac6454SAndrew Thompson 71102ac6454SAndrew Thompson /* check if we have a matching profile */ 71202ac6454SAndrew Thompson if (max_frame_size >= ep->max_frame_size) { 71302ac6454SAndrew Thompson temp = (max_frame_size - ep->max_frame_size); 71402ac6454SAndrew Thompson if (distance > temp) { 71502ac6454SAndrew Thompson distance = temp; 71602ac6454SAndrew Thompson best_n = n; 71702ac6454SAndrew Thompson ep->pf = pf; 71802ac6454SAndrew Thompson } 71902ac6454SAndrew Thompson } 72002ac6454SAndrew Thompson } 72102ac6454SAndrew Thompson 72202ac6454SAndrew Thompson /* see if we got a match */ 72302ac6454SAndrew Thompson if (best_n != 0) { 72402ac6454SAndrew Thompson /* get the correct profile */ 72502ac6454SAndrew Thompson pf = ep->pf; 72602ac6454SAndrew Thompson 72702ac6454SAndrew Thompson /* reserve IN-endpoint */ 72802ac6454SAndrew Thompson if (dir_in) { 72902ac6454SAndrew Thompson ues->bmInAlloc[best_n / 8] |= 73002ac6454SAndrew Thompson (1 << (best_n % 8)); 73102ac6454SAndrew Thompson ep->hw_endpoint_in = best_n | UE_DIR_IN; 73202ac6454SAndrew Thompson ep->needs_in = 0; 73302ac6454SAndrew Thompson } 73402ac6454SAndrew Thompson /* reserve OUT-endpoint */ 73502ac6454SAndrew Thompson if (dir_out) { 73602ac6454SAndrew Thompson ues->bmOutAlloc[best_n / 8] |= 73702ac6454SAndrew Thompson (1 << (best_n % 8)); 73802ac6454SAndrew Thompson ep->hw_endpoint_out = best_n | UE_DIR_OUT; 73902ac6454SAndrew Thompson ep->needs_out = 0; 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson return (0); /* got a match */ 74202ac6454SAndrew Thompson } 74302ac6454SAndrew Thompson return (1); /* failure */ 74402ac6454SAndrew Thompson } 74502ac6454SAndrew Thompson 74602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 747a593f6b8SAndrew Thompson * usb_hw_ep_get_needs 74802ac6454SAndrew Thompson * 74902ac6454SAndrew Thompson * This function will figure out the type and number of endpoints 75002ac6454SAndrew Thompson * which are needed for an USB configuration. 75102ac6454SAndrew Thompson * 75202ac6454SAndrew Thompson * Return values: 75302ac6454SAndrew Thompson * 0: Success. 75402ac6454SAndrew Thompson * Else: Failure. 75502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75602ac6454SAndrew Thompson static uint8_t 757a593f6b8SAndrew Thompson usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, 75802ac6454SAndrew Thompson uint8_t ep_type, uint8_t is_complete) 75902ac6454SAndrew Thompson { 760760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 761760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_iface; 762760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_curr; 763760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_max; 764760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_end; 765760bc48eSAndrew Thompson struct usb_descriptor *desc; 766760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 767760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 7688d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 76902ac6454SAndrew Thompson uint16_t wMaxPacketSize; 77002ac6454SAndrew Thompson uint16_t temp; 77102ac6454SAndrew Thompson uint8_t ep_no; 77202ac6454SAndrew Thompson 77302ac6454SAndrew Thompson ep_iface = ues->ep_max; 77402ac6454SAndrew Thompson ep_curr = ues->ep_max; 77502ac6454SAndrew Thompson ep_end = ues->ep + USB_EP_MAX; 77602ac6454SAndrew Thompson ep_max = ues->ep_max; 77702ac6454SAndrew Thompson desc = NULL; 778a593f6b8SAndrew Thompson speed = usbd_get_speed(ues->udev); 77902ac6454SAndrew Thompson 78002ac6454SAndrew Thompson repeat: 78102ac6454SAndrew Thompson 782a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(ues->cd, desc))) { 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 78502ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) { 78602ac6454SAndrew Thompson 78702ac6454SAndrew Thompson id = (void *)desc; 78802ac6454SAndrew Thompson 78902ac6454SAndrew Thompson if (id->bAlternateSetting == 0) { 79002ac6454SAndrew Thompson /* going forward */ 79102ac6454SAndrew Thompson ep_iface = ep_max; 79202ac6454SAndrew Thompson } else { 79302ac6454SAndrew Thompson /* reset */ 79402ac6454SAndrew Thompson ep_curr = ep_iface; 79502ac6454SAndrew Thompson } 79602ac6454SAndrew Thompson } 79702ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_ENDPOINT) && 79802ac6454SAndrew Thompson (desc->bLength >= sizeof(*ed))) { 79902ac6454SAndrew Thompson 80002ac6454SAndrew Thompson ed = (void *)desc; 80102ac6454SAndrew Thompson 80202ac6454SAndrew Thompson goto handle_endpoint_desc; 80302ac6454SAndrew Thompson } 80402ac6454SAndrew Thompson } 80502ac6454SAndrew Thompson ues->ep_max = ep_max; 80602ac6454SAndrew Thompson return (0); 80702ac6454SAndrew Thompson 80802ac6454SAndrew Thompson handle_endpoint_desc: 80902ac6454SAndrew Thompson temp = (ed->bmAttributes & UE_XFERTYPE); 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson if (temp == ep_type) { 81202ac6454SAndrew Thompson 81302ac6454SAndrew Thompson if (ep_curr == ep_end) { 81402ac6454SAndrew Thompson /* too many endpoints */ 81502ac6454SAndrew Thompson return (1); /* failure */ 81602ac6454SAndrew Thompson } 81702ac6454SAndrew Thompson wMaxPacketSize = UGETW(ed->wMaxPacketSize); 81802ac6454SAndrew Thompson if ((wMaxPacketSize & 0xF800) && 81902ac6454SAndrew Thompson (speed == USB_SPEED_HIGH)) { 82002ac6454SAndrew Thompson /* handle packet multiplier */ 82102ac6454SAndrew Thompson temp = (wMaxPacketSize >> 11) & 3; 82202ac6454SAndrew Thompson wMaxPacketSize &= 0x7FF; 82302ac6454SAndrew Thompson if (temp == 1) { 82402ac6454SAndrew Thompson wMaxPacketSize *= 2; 82502ac6454SAndrew Thompson } else { 82602ac6454SAndrew Thompson wMaxPacketSize *= 3; 82702ac6454SAndrew Thompson } 82802ac6454SAndrew Thompson } 82902ac6454SAndrew Thompson /* 83002ac6454SAndrew Thompson * Check if we have a fixed endpoint number, else the 83102ac6454SAndrew Thompson * endpoint number is allocated dynamically: 83202ac6454SAndrew Thompson */ 83302ac6454SAndrew Thompson ep_no = (ed->bEndpointAddress & UE_ADDR); 83402ac6454SAndrew Thompson if (ep_no != 0) { 83502ac6454SAndrew Thompson 83602ac6454SAndrew Thompson /* get HW endpoint profile */ 83702ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) 83802ac6454SAndrew Thompson (ues->udev, &pf, ep_no); 83902ac6454SAndrew Thompson if (pf == NULL) { 84002ac6454SAndrew Thompson /* HW profile does not exist - failure */ 84102ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 84202ac6454SAndrew Thompson "does not exist\n", ep_no); 84302ac6454SAndrew Thompson return (1); 84402ac6454SAndrew Thompson } 84502ac6454SAndrew Thompson /* reserve fixed endpoint number */ 84602ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 84702ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 84802ac6454SAndrew Thompson (1 << (ep_no % 8)); 84902ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 85002ac6454SAndrew Thompson (1 << (ep_no % 8)); 85102ac6454SAndrew Thompson if ((pf->max_in_frame_size < wMaxPacketSize) || 85202ac6454SAndrew Thompson (pf->max_out_frame_size < wMaxPacketSize)) { 85302ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 854767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 85502ac6454SAndrew Thompson return (1); 85602ac6454SAndrew Thompson } 85702ac6454SAndrew Thompson } else if (ed->bEndpointAddress & UE_DIR_IN) { 85802ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 85902ac6454SAndrew Thompson (1 << (ep_no % 8)); 86002ac6454SAndrew Thompson if (pf->max_in_frame_size < wMaxPacketSize) { 86102ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 862767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 86302ac6454SAndrew Thompson return (1); 86402ac6454SAndrew Thompson } 86502ac6454SAndrew Thompson } else { 86602ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 86702ac6454SAndrew Thompson (1 << (ep_no % 8)); 86802ac6454SAndrew Thompson if (pf->max_out_frame_size < wMaxPacketSize) { 86902ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 870767cb2e2SAndrew Thompson "has too small buffer\n", ep_no); 87102ac6454SAndrew Thompson return (1); 87202ac6454SAndrew Thompson } 87302ac6454SAndrew Thompson } 87402ac6454SAndrew Thompson } else if (is_complete) { 87502ac6454SAndrew Thompson 87602ac6454SAndrew Thompson /* check if we have enough buffer space */ 87702ac6454SAndrew Thompson if (wMaxPacketSize > 87802ac6454SAndrew Thompson ep_curr->max_frame_size) { 87902ac6454SAndrew Thompson return (1); /* failure */ 88002ac6454SAndrew Thompson } 88102ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 88202ac6454SAndrew Thompson ed->bEndpointAddress = 88302ac6454SAndrew Thompson ep_curr->hw_endpoint_in; 88402ac6454SAndrew Thompson } else { 88502ac6454SAndrew Thompson ed->bEndpointAddress = 88602ac6454SAndrew Thompson ep_curr->hw_endpoint_out; 88702ac6454SAndrew Thompson } 88802ac6454SAndrew Thompson 88902ac6454SAndrew Thompson } else { 89002ac6454SAndrew Thompson 89102ac6454SAndrew Thompson /* compute the maximum frame size */ 89202ac6454SAndrew Thompson if (ep_curr->max_frame_size < wMaxPacketSize) { 89302ac6454SAndrew Thompson ep_curr->max_frame_size = wMaxPacketSize; 89402ac6454SAndrew Thompson } 89502ac6454SAndrew Thompson if (temp == UE_CONTROL) { 89602ac6454SAndrew Thompson ep_curr->needs_in = 1; 89702ac6454SAndrew Thompson ep_curr->needs_out = 1; 89802ac6454SAndrew Thompson } else { 89902ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 90002ac6454SAndrew Thompson ep_curr->needs_in = 1; 90102ac6454SAndrew Thompson } else { 90202ac6454SAndrew Thompson ep_curr->needs_out = 1; 90302ac6454SAndrew Thompson } 90402ac6454SAndrew Thompson } 90502ac6454SAndrew Thompson ep_curr->needs_ep_type = ep_type; 90602ac6454SAndrew Thompson } 90702ac6454SAndrew Thompson 90802ac6454SAndrew Thompson ep_curr++; 90902ac6454SAndrew Thompson if (ep_max < ep_curr) { 91002ac6454SAndrew Thompson ep_max = ep_curr; 91102ac6454SAndrew Thompson } 91202ac6454SAndrew Thompson } 91302ac6454SAndrew Thompson goto repeat; 91402ac6454SAndrew Thompson } 91502ac6454SAndrew Thompson 91602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 917a593f6b8SAndrew Thompson * usb_hw_ep_resolve 91802ac6454SAndrew Thompson * 91902ac6454SAndrew Thompson * This function will try to resolve endpoint requirements by the 92002ac6454SAndrew Thompson * given endpoint profiles that the USB hardware reports. 92102ac6454SAndrew Thompson * 92202ac6454SAndrew Thompson * Return values: 92302ac6454SAndrew Thompson * 0: Success 92402ac6454SAndrew Thompson * Else: Failure 92502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 926e0a69b51SAndrew Thompson static usb_error_t 927a593f6b8SAndrew Thompson usb_hw_ep_resolve(struct usb_device *udev, 928760bc48eSAndrew Thompson struct usb_descriptor *desc) 92902ac6454SAndrew Thompson { 930760bc48eSAndrew Thompson struct usb_hw_ep_scratch *ues; 931760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep; 932760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 933e892b3feSHans Petter Selasky const struct usb_bus_methods *methods; 934760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 93502ac6454SAndrew Thompson uint16_t mps; 93602ac6454SAndrew Thompson 9376950c75fSHans Petter Selasky if (desc == NULL) 93802ac6454SAndrew Thompson return (USB_ERR_INVAL); 9396950c75fSHans Petter Selasky 94002ac6454SAndrew Thompson /* get bus methods */ 94102ac6454SAndrew Thompson methods = udev->bus->methods; 94202ac6454SAndrew Thompson 9436950c75fSHans Petter Selasky if (methods->get_hw_ep_profile == NULL) 94402ac6454SAndrew Thompson return (USB_ERR_INVAL); 9456950c75fSHans Petter Selasky 94602ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_DEVICE) { 94702ac6454SAndrew Thompson 9486950c75fSHans Petter Selasky if (desc->bLength < sizeof(*dd)) 94902ac6454SAndrew Thompson return (USB_ERR_INVAL); 9506950c75fSHans Petter Selasky 95102ac6454SAndrew Thompson dd = (void *)desc; 95202ac6454SAndrew Thompson 95302ac6454SAndrew Thompson /* get HW control endpoint 0 profile */ 95402ac6454SAndrew Thompson (methods->get_hw_ep_profile) (udev, &pf, 0); 95502ac6454SAndrew Thompson if (pf == NULL) { 95602ac6454SAndrew Thompson return (USB_ERR_INVAL); 95702ac6454SAndrew Thompson } 958a593f6b8SAndrew Thompson if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { 95902ac6454SAndrew Thompson DPRINTFN(0, "Endpoint 0 does not " 96002ac6454SAndrew Thompson "support control\n"); 96102ac6454SAndrew Thompson return (USB_ERR_INVAL); 96202ac6454SAndrew Thompson } 96302ac6454SAndrew Thompson mps = dd->bMaxPacketSize; 96402ac6454SAndrew Thompson 96502ac6454SAndrew Thompson if (udev->speed == USB_SPEED_FULL) { 96602ac6454SAndrew Thompson /* 96702ac6454SAndrew Thompson * We can optionally choose another packet size ! 96802ac6454SAndrew Thompson */ 96902ac6454SAndrew Thompson while (1) { 97002ac6454SAndrew Thompson /* check if "mps" is ok */ 97102ac6454SAndrew Thompson if (pf->max_in_frame_size >= mps) { 97202ac6454SAndrew Thompson break; 97302ac6454SAndrew Thompson } 97402ac6454SAndrew Thompson /* reduce maximum packet size */ 97502ac6454SAndrew Thompson mps /= 2; 97602ac6454SAndrew Thompson 97702ac6454SAndrew Thompson /* check if "mps" is too small */ 97802ac6454SAndrew Thompson if (mps < 8) { 97902ac6454SAndrew Thompson return (USB_ERR_INVAL); 98002ac6454SAndrew Thompson } 98102ac6454SAndrew Thompson } 98202ac6454SAndrew Thompson 98302ac6454SAndrew Thompson dd->bMaxPacketSize = mps; 98402ac6454SAndrew Thompson 98502ac6454SAndrew Thompson } else { 98602ac6454SAndrew Thompson /* We only have one choice */ 98702ac6454SAndrew Thompson if (mps == 255) { 98802ac6454SAndrew Thompson mps = 512; 98902ac6454SAndrew Thompson } 99002ac6454SAndrew Thompson /* Check if we support the specified wMaxPacketSize */ 99102ac6454SAndrew Thompson if (pf->max_in_frame_size < mps) { 99202ac6454SAndrew Thompson return (USB_ERR_INVAL); 99302ac6454SAndrew Thompson } 99402ac6454SAndrew Thompson } 99502ac6454SAndrew Thompson return (0); /* success */ 99602ac6454SAndrew Thompson } 9976950c75fSHans Petter Selasky if (desc->bDescriptorType != UDESC_CONFIG) 99802ac6454SAndrew Thompson return (USB_ERR_INVAL); 9996950c75fSHans Petter Selasky if (desc->bLength < sizeof(*(ues->cd))) 100002ac6454SAndrew Thompson return (USB_ERR_INVAL); 10016950c75fSHans Petter Selasky 10026950c75fSHans Petter Selasky ues = udev->scratch.hw_ep_scratch; 100302ac6454SAndrew Thompson 1004271ae033SHans Petter Selasky memset(ues, 0, sizeof(*ues)); 100502ac6454SAndrew Thompson 100602ac6454SAndrew Thompson ues->ep_max = ues->ep; 100702ac6454SAndrew Thompson ues->cd = (void *)desc; 100802ac6454SAndrew Thompson ues->methods = methods; 100902ac6454SAndrew Thompson ues->udev = udev; 101002ac6454SAndrew Thompson 101102ac6454SAndrew Thompson /* Get all the endpoints we need */ 101202ac6454SAndrew Thompson 1013a593f6b8SAndrew Thompson if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || 1014a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || 1015a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || 1016a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_BULK, 0)) { 101702ac6454SAndrew Thompson DPRINTFN(0, "Could not get needs\n"); 101802ac6454SAndrew Thompson return (USB_ERR_INVAL); 101902ac6454SAndrew Thompson } 102002ac6454SAndrew Thompson for (ep = ues->ep; ep != ues->ep_max; ep++) { 102102ac6454SAndrew Thompson 102202ac6454SAndrew Thompson while (ep->needs_in || ep->needs_out) { 102302ac6454SAndrew Thompson 102402ac6454SAndrew Thompson /* 102502ac6454SAndrew Thompson * First try to use a simplex endpoint. 102602ac6454SAndrew Thompson * Then try to use a duplex endpoint. 102702ac6454SAndrew Thompson */ 1028a593f6b8SAndrew Thompson if (usb_hw_ep_find_match(ues, ep, 1) && 1029a593f6b8SAndrew Thompson usb_hw_ep_find_match(ues, ep, 0)) { 103002ac6454SAndrew Thompson DPRINTFN(0, "Could not find match\n"); 103102ac6454SAndrew Thompson return (USB_ERR_INVAL); 103202ac6454SAndrew Thompson } 103302ac6454SAndrew Thompson } 103402ac6454SAndrew Thompson } 103502ac6454SAndrew Thompson 103602ac6454SAndrew Thompson ues->ep_max = ues->ep; 103702ac6454SAndrew Thompson 103802ac6454SAndrew Thompson /* Update all endpoint addresses */ 103902ac6454SAndrew Thompson 1040a593f6b8SAndrew Thompson if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || 1041a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || 1042a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || 1043a593f6b8SAndrew Thompson usb_hw_ep_get_needs(ues, UE_BULK, 1)) { 104402ac6454SAndrew Thompson DPRINTFN(0, "Could not update endpoint address\n"); 104502ac6454SAndrew Thompson return (USB_ERR_INVAL); 104602ac6454SAndrew Thompson } 104702ac6454SAndrew Thompson return (0); /* success */ 104802ac6454SAndrew Thompson } 104902ac6454SAndrew Thompson 105002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1051a593f6b8SAndrew Thompson * usb_temp_get_tdd 105202ac6454SAndrew Thompson * 105302ac6454SAndrew Thompson * Returns: 105402ac6454SAndrew Thompson * NULL: No USB template device descriptor found. 105502ac6454SAndrew Thompson * Else: Pointer to the USB template device descriptor. 105602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1057760bc48eSAndrew Thompson static const struct usb_temp_device_desc * 1058a593f6b8SAndrew Thompson usb_temp_get_tdd(struct usb_device *udev) 105902ac6454SAndrew Thompson { 1060a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 106102ac6454SAndrew Thompson return (NULL); 106202ac6454SAndrew Thompson } 1063a593f6b8SAndrew Thompson return (udev->usb_template_ptr->tdd); 106402ac6454SAndrew Thompson } 106502ac6454SAndrew Thompson 106602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1067a593f6b8SAndrew Thompson * usb_temp_get_device_desc 106802ac6454SAndrew Thompson * 106902ac6454SAndrew Thompson * Returns: 107002ac6454SAndrew Thompson * NULL: No USB device descriptor found. 107102ac6454SAndrew Thompson * Else: Pointer to USB device descriptor. 107202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 107302ac6454SAndrew Thompson static void * 1074a593f6b8SAndrew Thompson usb_temp_get_device_desc(struct usb_device *udev) 107502ac6454SAndrew Thompson { 1076760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 107702ac6454SAndrew Thompson 1078a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 107902ac6454SAndrew Thompson return (NULL); 108002ac6454SAndrew Thompson } 1081a593f6b8SAndrew Thompson dd = &udev->usb_template_ptr->udd; 108202ac6454SAndrew Thompson if (dd->bDescriptorType != UDESC_DEVICE) { 108302ac6454SAndrew Thompson /* sanity check failed */ 108402ac6454SAndrew Thompson return (NULL); 108502ac6454SAndrew Thompson } 108602ac6454SAndrew Thompson return (dd); 108702ac6454SAndrew Thompson } 108802ac6454SAndrew Thompson 108902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1090a593f6b8SAndrew Thompson * usb_temp_get_qualifier_desc 109102ac6454SAndrew Thompson * 109202ac6454SAndrew Thompson * Returns: 109302ac6454SAndrew Thompson * NULL: No USB device_qualifier descriptor found. 109402ac6454SAndrew Thompson * Else: Pointer to USB device_qualifier descriptor. 109502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 109602ac6454SAndrew Thompson static void * 1097a593f6b8SAndrew Thompson usb_temp_get_qualifier_desc(struct usb_device *udev) 109802ac6454SAndrew Thompson { 1099760bc48eSAndrew Thompson struct usb_device_qualifier *dq; 110002ac6454SAndrew Thompson 1101a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 110202ac6454SAndrew Thompson return (NULL); 110302ac6454SAndrew Thompson } 1104a593f6b8SAndrew Thompson dq = &udev->usb_template_ptr->udq; 110502ac6454SAndrew Thompson if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { 110602ac6454SAndrew Thompson /* sanity check failed */ 110702ac6454SAndrew Thompson return (NULL); 110802ac6454SAndrew Thompson } 110902ac6454SAndrew Thompson return (dq); 111002ac6454SAndrew Thompson } 111102ac6454SAndrew Thompson 111202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1113a593f6b8SAndrew Thompson * usb_temp_get_config_desc 111402ac6454SAndrew Thompson * 111502ac6454SAndrew Thompson * Returns: 111602ac6454SAndrew Thompson * NULL: No USB config descriptor found. 111702ac6454SAndrew Thompson * Else: Pointer to USB config descriptor having index "index". 111802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 111902ac6454SAndrew Thompson static void * 1120a593f6b8SAndrew Thompson usb_temp_get_config_desc(struct usb_device *udev, 112102ac6454SAndrew Thompson uint16_t *pLength, uint8_t index) 112202ac6454SAndrew Thompson { 1123760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 1124760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 112502ac6454SAndrew Thompson uint16_t temp; 112602ac6454SAndrew Thompson 1127a593f6b8SAndrew Thompson if (udev->usb_template_ptr == NULL) { 112802ac6454SAndrew Thompson return (NULL); 112902ac6454SAndrew Thompson } 1130a593f6b8SAndrew Thompson dd = &udev->usb_template_ptr->udd; 1131a593f6b8SAndrew Thompson cd = (void *)(udev->usb_template_ptr + 1); 113202ac6454SAndrew Thompson 113302ac6454SAndrew Thompson if (index >= dd->bNumConfigurations) { 113402ac6454SAndrew Thompson /* out of range */ 113502ac6454SAndrew Thompson return (NULL); 113602ac6454SAndrew Thompson } 113702ac6454SAndrew Thompson while (index--) { 113802ac6454SAndrew Thompson if (cd->bDescriptorType != UDESC_CONFIG) { 113902ac6454SAndrew Thompson /* sanity check failed */ 114002ac6454SAndrew Thompson return (NULL); 114102ac6454SAndrew Thompson } 114202ac6454SAndrew Thompson temp = UGETW(cd->wTotalLength); 114302ac6454SAndrew Thompson cd = USB_ADD_BYTES(cd, temp); 114402ac6454SAndrew Thompson } 114502ac6454SAndrew Thompson 114602ac6454SAndrew Thompson if (pLength) { 114702ac6454SAndrew Thompson *pLength = UGETW(cd->wTotalLength); 114802ac6454SAndrew Thompson } 114902ac6454SAndrew Thompson return (cd); 115002ac6454SAndrew Thompson } 115102ac6454SAndrew Thompson 115202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1153a593f6b8SAndrew Thompson * usb_temp_get_vendor_desc 115402ac6454SAndrew Thompson * 115502ac6454SAndrew Thompson * Returns: 115602ac6454SAndrew Thompson * NULL: No vendor descriptor found. 115702ac6454SAndrew Thompson * Else: Pointer to a vendor descriptor. 115802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 115902ac6454SAndrew Thompson static const void * 1160a593f6b8SAndrew Thompson usb_temp_get_vendor_desc(struct usb_device *udev, 11619a8e0122SAndrew Thompson const struct usb_device_request *req, uint16_t *plen) 116202ac6454SAndrew Thompson { 1163760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 116402ac6454SAndrew Thompson 1165a593f6b8SAndrew Thompson tdd = usb_temp_get_tdd(udev); 116602ac6454SAndrew Thompson if (tdd == NULL) { 116702ac6454SAndrew Thompson return (NULL); 116802ac6454SAndrew Thompson } 116902ac6454SAndrew Thompson if (tdd->getVendorDesc == NULL) { 117002ac6454SAndrew Thompson return (NULL); 117102ac6454SAndrew Thompson } 11729a8e0122SAndrew Thompson return ((tdd->getVendorDesc) (req, plen)); 117302ac6454SAndrew Thompson } 117402ac6454SAndrew Thompson 117502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1176a593f6b8SAndrew Thompson * usb_temp_get_string_desc 117702ac6454SAndrew Thompson * 117802ac6454SAndrew Thompson * Returns: 117902ac6454SAndrew Thompson * NULL: No string descriptor found. 118002ac6454SAndrew Thompson * Else: Pointer to a string descriptor. 118102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 118202ac6454SAndrew Thompson static const void * 1183a593f6b8SAndrew Thompson usb_temp_get_string_desc(struct usb_device *udev, 118402ac6454SAndrew Thompson uint16_t lang_id, uint8_t string_index) 118502ac6454SAndrew Thompson { 1186760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 118702ac6454SAndrew Thompson 1188a593f6b8SAndrew Thompson tdd = usb_temp_get_tdd(udev); 118902ac6454SAndrew Thompson if (tdd == NULL) { 119002ac6454SAndrew Thompson return (NULL); 119102ac6454SAndrew Thompson } 119202ac6454SAndrew Thompson if (tdd->getStringDesc == NULL) { 119302ac6454SAndrew Thompson return (NULL); 119402ac6454SAndrew Thompson } 119502ac6454SAndrew Thompson return ((tdd->getStringDesc) (lang_id, string_index)); 119602ac6454SAndrew Thompson } 119702ac6454SAndrew Thompson 119802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1199a593f6b8SAndrew Thompson * usb_temp_get_hub_desc 120002ac6454SAndrew Thompson * 120102ac6454SAndrew Thompson * Returns: 120202ac6454SAndrew Thompson * NULL: No USB HUB descriptor found. 120302ac6454SAndrew Thompson * Else: Pointer to a USB HUB descriptor. 120402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 120502ac6454SAndrew Thompson static const void * 1206a593f6b8SAndrew Thompson usb_temp_get_hub_desc(struct usb_device *udev) 120702ac6454SAndrew Thompson { 120802ac6454SAndrew Thompson return (NULL); /* needs to be implemented */ 120902ac6454SAndrew Thompson } 121002ac6454SAndrew Thompson 121102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1212a593f6b8SAndrew Thompson * usb_temp_get_desc 121302ac6454SAndrew Thompson * 121402ac6454SAndrew Thompson * This function is a demultiplexer for local USB device side control 121502ac6454SAndrew Thompson * endpoint requests. 121602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1217e0a69b51SAndrew Thompson static usb_error_t 1218a593f6b8SAndrew Thompson usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, 121902ac6454SAndrew Thompson const void **pPtr, uint16_t *pLength) 122002ac6454SAndrew Thompson { 122102ac6454SAndrew Thompson const uint8_t *buf; 122202ac6454SAndrew Thompson uint16_t len; 122302ac6454SAndrew Thompson 122402ac6454SAndrew Thompson buf = NULL; 122502ac6454SAndrew Thompson len = 0; 122602ac6454SAndrew Thompson 122702ac6454SAndrew Thompson switch (req->bmRequestType) { 122802ac6454SAndrew Thompson case UT_READ_DEVICE: 122902ac6454SAndrew Thompson switch (req->bRequest) { 123002ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 123102ac6454SAndrew Thompson goto tr_handle_get_descriptor; 123202ac6454SAndrew Thompson default: 123302ac6454SAndrew Thompson goto tr_stalled; 123402ac6454SAndrew Thompson } 123502ac6454SAndrew Thompson case UT_READ_CLASS_DEVICE: 123602ac6454SAndrew Thompson switch (req->bRequest) { 123702ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 123802ac6454SAndrew Thompson goto tr_handle_get_class_descriptor; 123902ac6454SAndrew Thompson default: 124002ac6454SAndrew Thompson goto tr_stalled; 124102ac6454SAndrew Thompson } 124202ac6454SAndrew Thompson default: 124302ac6454SAndrew Thompson goto tr_stalled; 124402ac6454SAndrew Thompson } 124502ac6454SAndrew Thompson 124602ac6454SAndrew Thompson tr_handle_get_descriptor: 124702ac6454SAndrew Thompson switch (req->wValue[1]) { 124802ac6454SAndrew Thompson case UDESC_DEVICE: 124902ac6454SAndrew Thompson if (req->wValue[0]) { 125002ac6454SAndrew Thompson goto tr_stalled; 125102ac6454SAndrew Thompson } 1252a593f6b8SAndrew Thompson buf = usb_temp_get_device_desc(udev); 125302ac6454SAndrew Thompson goto tr_valid; 125402ac6454SAndrew Thompson case UDESC_DEVICE_QUALIFIER: 125502ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 125602ac6454SAndrew Thompson goto tr_stalled; 125702ac6454SAndrew Thompson } 125802ac6454SAndrew Thompson if (req->wValue[0]) { 125902ac6454SAndrew Thompson goto tr_stalled; 126002ac6454SAndrew Thompson } 1261a593f6b8SAndrew Thompson buf = usb_temp_get_qualifier_desc(udev); 126202ac6454SAndrew Thompson goto tr_valid; 126302ac6454SAndrew Thompson case UDESC_OTHER_SPEED_CONFIGURATION: 126402ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 126502ac6454SAndrew Thompson goto tr_stalled; 126602ac6454SAndrew Thompson } 126702ac6454SAndrew Thompson case UDESC_CONFIG: 1268a593f6b8SAndrew Thompson buf = usb_temp_get_config_desc(udev, 126902ac6454SAndrew Thompson &len, req->wValue[0]); 127002ac6454SAndrew Thompson goto tr_valid; 127102ac6454SAndrew Thompson case UDESC_STRING: 1272a593f6b8SAndrew Thompson buf = usb_temp_get_string_desc(udev, 127302ac6454SAndrew Thompson UGETW(req->wIndex), req->wValue[0]); 127402ac6454SAndrew Thompson goto tr_valid; 127502ac6454SAndrew Thompson default: 127602ac6454SAndrew Thompson goto tr_stalled; 127702ac6454SAndrew Thompson } 127802ac6454SAndrew Thompson 127902ac6454SAndrew Thompson tr_handle_get_class_descriptor: 128002ac6454SAndrew Thompson if (req->wValue[0]) { 128102ac6454SAndrew Thompson goto tr_stalled; 128202ac6454SAndrew Thompson } 1283a593f6b8SAndrew Thompson buf = usb_temp_get_hub_desc(udev); 128402ac6454SAndrew Thompson goto tr_valid; 128502ac6454SAndrew Thompson 128602ac6454SAndrew Thompson tr_valid: 12879a8e0122SAndrew Thompson if (buf == NULL) 128802ac6454SAndrew Thompson goto tr_stalled; 12899a8e0122SAndrew Thompson if (len == 0) 129002ac6454SAndrew Thompson len = buf[0]; 129102ac6454SAndrew Thompson *pPtr = buf; 129202ac6454SAndrew Thompson *pLength = len; 1293459d369eSAndrew Thompson return (0); /* success */ 129402ac6454SAndrew Thompson 129502ac6454SAndrew Thompson tr_stalled: 12969a8e0122SAndrew Thompson /* try to get a vendor specific descriptor */ 12979a8e0122SAndrew Thompson len = 0; 12989a8e0122SAndrew Thompson buf = usb_temp_get_vendor_desc(udev, req, &len); 12999a8e0122SAndrew Thompson if (buf != NULL) 13009a8e0122SAndrew Thompson goto tr_valid; 130102ac6454SAndrew Thompson *pPtr = NULL; 130202ac6454SAndrew Thompson *pLength = 0; 1303459d369eSAndrew Thompson return (0); /* we ignore failures */ 130402ac6454SAndrew Thompson } 130502ac6454SAndrew Thompson 130602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1307760bc48eSAndrew Thompson * usb_temp_setup 130802ac6454SAndrew Thompson * 130902ac6454SAndrew Thompson * This function generates USB descriptors according to the given USB 131002ac6454SAndrew Thompson * template device descriptor. It will also try to figure out the best 131102ac6454SAndrew Thompson * matching endpoint addresses using the hardware endpoint profiles. 131202ac6454SAndrew Thompson * 131302ac6454SAndrew Thompson * Returns: 131402ac6454SAndrew Thompson * 0: Success 131502ac6454SAndrew Thompson * Else: Failure 131602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 13179a8e0122SAndrew Thompson usb_error_t 1318760bc48eSAndrew Thompson usb_temp_setup(struct usb_device *udev, 1319760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 132002ac6454SAndrew Thompson { 1321760bc48eSAndrew Thompson struct usb_temp_setup *uts; 132202ac6454SAndrew Thompson void *buf; 13236950c75fSHans Petter Selasky usb_error_t error; 132402ac6454SAndrew Thompson uint8_t n; 13256950c75fSHans Petter Selasky uint8_t do_unlock; 132602ac6454SAndrew Thompson 132702ac6454SAndrew Thompson /* be NULL safe */ 13286950c75fSHans Petter Selasky if (tdd == NULL) 132902ac6454SAndrew Thompson return (0); 13306950c75fSHans Petter Selasky 13316950c75fSHans Petter Selasky /* Protect scratch area */ 133264cb5e2aSHans Petter Selasky do_unlock = usbd_ctrl_lock(udev); 13336950c75fSHans Petter Selasky 13346950c75fSHans Petter Selasky uts = udev->scratch.temp_setup; 133502ac6454SAndrew Thompson 1336271ae033SHans Petter Selasky memset(uts, 0, sizeof(*uts)); 133702ac6454SAndrew Thompson 13388d2dd5ddSAndrew Thompson uts->usb_speed = udev->speed; 133902ac6454SAndrew Thompson uts->self_powered = udev->flags.self_powered; 134002ac6454SAndrew Thompson 134102ac6454SAndrew Thompson /* first pass */ 134202ac6454SAndrew Thompson 1343a593f6b8SAndrew Thompson usb_make_device_desc(uts, tdd); 134402ac6454SAndrew Thompson 134502ac6454SAndrew Thompson if (uts->err) { 134602ac6454SAndrew Thompson /* some error happened */ 13476950c75fSHans Petter Selasky goto done; 134802ac6454SAndrew Thompson } 134902ac6454SAndrew Thompson /* sanity check */ 135002ac6454SAndrew Thompson if (uts->size == 0) { 13516950c75fSHans Petter Selasky uts->err = USB_ERR_INVAL; 13526950c75fSHans Petter Selasky goto done; 135302ac6454SAndrew Thompson } 135402ac6454SAndrew Thompson /* allocate zeroed memory */ 13552c79a775SHans Petter Selasky uts->buf = usbd_alloc_config_desc(udev, uts->size); 13566950c75fSHans Petter Selasky /* 13576950c75fSHans Petter Selasky * Allow malloc() to return NULL regardless of M_WAITOK flag. 13586950c75fSHans Petter Selasky * This helps when porting the software to non-FreeBSD 13596950c75fSHans Petter Selasky * systems. 13606950c75fSHans Petter Selasky */ 136102ac6454SAndrew Thompson if (uts->buf == NULL) { 136202ac6454SAndrew Thompson /* could not allocate memory */ 13636950c75fSHans Petter Selasky uts->err = USB_ERR_NOMEM; 13646950c75fSHans Petter Selasky goto done; 136502ac6454SAndrew Thompson } 136602ac6454SAndrew Thompson /* second pass */ 136702ac6454SAndrew Thompson 136802ac6454SAndrew Thompson uts->size = 0; 136902ac6454SAndrew Thompson 1370a593f6b8SAndrew Thompson usb_make_device_desc(uts, tdd); 137102ac6454SAndrew Thompson 137202ac6454SAndrew Thompson /* 137302ac6454SAndrew Thompson * Store a pointer to our descriptors: 137402ac6454SAndrew Thompson */ 1375a593f6b8SAndrew Thompson udev->usb_template_ptr = uts->buf; 137602ac6454SAndrew Thompson 137702ac6454SAndrew Thompson if (uts->err) { 137802ac6454SAndrew Thompson /* some error happened during second pass */ 13796950c75fSHans Petter Selasky goto done; 138002ac6454SAndrew Thompson } 138102ac6454SAndrew Thompson /* 138202ac6454SAndrew Thompson * Resolve all endpoint addresses ! 138302ac6454SAndrew Thompson */ 1384a593f6b8SAndrew Thompson buf = usb_temp_get_device_desc(udev); 1385a593f6b8SAndrew Thompson uts->err = usb_hw_ep_resolve(udev, buf); 138602ac6454SAndrew Thompson if (uts->err) { 138702ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 138802ac6454SAndrew Thompson "Device Descriptor, error = %s\n", 1389a593f6b8SAndrew Thompson usbd_errstr(uts->err)); 13906950c75fSHans Petter Selasky goto done; 139102ac6454SAndrew Thompson } 139202ac6454SAndrew Thompson for (n = 0;; n++) { 139302ac6454SAndrew Thompson 1394a593f6b8SAndrew Thompson buf = usb_temp_get_config_desc(udev, NULL, n); 139502ac6454SAndrew Thompson if (buf == NULL) { 139602ac6454SAndrew Thompson break; 139702ac6454SAndrew Thompson } 1398a593f6b8SAndrew Thompson uts->err = usb_hw_ep_resolve(udev, buf); 139902ac6454SAndrew Thompson if (uts->err) { 140002ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 140102ac6454SAndrew Thompson "Config Descriptor %u, error = %s\n", n, 1402a593f6b8SAndrew Thompson usbd_errstr(uts->err)); 14036950c75fSHans Petter Selasky goto done; 140402ac6454SAndrew Thompson } 140502ac6454SAndrew Thompson } 14066950c75fSHans Petter Selasky done: 14076950c75fSHans Petter Selasky error = uts->err; 14086950c75fSHans Petter Selasky if (error) 1409a593f6b8SAndrew Thompson usb_temp_unsetup(udev); 14106950c75fSHans Petter Selasky if (do_unlock) 141164cb5e2aSHans Petter Selasky usbd_ctrl_unlock(udev); 14126950c75fSHans Petter Selasky return (error); 141302ac6454SAndrew Thompson } 141402ac6454SAndrew Thompson 141502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1416a593f6b8SAndrew Thompson * usb_temp_unsetup 141702ac6454SAndrew Thompson * 141802ac6454SAndrew Thompson * This function frees any memory associated with the currently 141902ac6454SAndrew Thompson * setup template, if any. 142002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14219a8e0122SAndrew Thompson void 1422a593f6b8SAndrew Thompson usb_temp_unsetup(struct usb_device *udev) 142302ac6454SAndrew Thompson { 14242c79a775SHans Petter Selasky usbd_free_config_desc(udev, udev->usb_template_ptr); 1425a593f6b8SAndrew Thompson udev->usb_template_ptr = NULL; 142602ac6454SAndrew Thompson } 142702ac6454SAndrew Thompson 1428e0a69b51SAndrew Thompson static usb_error_t 1429a593f6b8SAndrew Thompson usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) 143002ac6454SAndrew Thompson { 1431e0a69b51SAndrew Thompson usb_error_t err; 143202ac6454SAndrew Thompson 143302ac6454SAndrew Thompson switch (index) { 1434399e6543SHans Petter Selasky case USB_TEMP_MSC: 1435a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_msc); 143602ac6454SAndrew Thompson break; 1437399e6543SHans Petter Selasky case USB_TEMP_CDCE: 1438a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_cdce); 143902ac6454SAndrew Thompson break; 1440399e6543SHans Petter Selasky case USB_TEMP_MTP: 1441a593f6b8SAndrew Thompson err = usb_temp_setup(udev, &usb_template_mtp); 144202ac6454SAndrew Thompson break; 1443399e6543SHans Petter Selasky case USB_TEMP_MODEM: 1444399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_modem); 1445399e6543SHans Petter Selasky break; 1446399e6543SHans Petter Selasky case USB_TEMP_AUDIO: 1447399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_audio); 1448399e6543SHans Petter Selasky break; 1449399e6543SHans Petter Selasky case USB_TEMP_KBD: 1450399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_kbd); 1451399e6543SHans Petter Selasky break; 1452399e6543SHans Petter Selasky case USB_TEMP_MOUSE: 1453399e6543SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_mouse); 1454399e6543SHans Petter Selasky break; 1455f9478f91SHans Petter Selasky case USB_TEMP_PHONE: 1456f9478f91SHans Petter Selasky err = usb_temp_setup(udev, &usb_template_phone); 1457f9478f91SHans Petter Selasky break; 14583e420a3eSRuslan Bukin case USB_TEMP_SERIALNET: 14593e420a3eSRuslan Bukin err = usb_temp_setup(udev, &usb_template_serialnet); 14603e420a3eSRuslan Bukin break; 14610a76fe7cSHans Petter Selasky case USB_TEMP_MIDI: 14620a76fe7cSHans Petter Selasky err = usb_temp_setup(udev, &usb_template_midi); 14630a76fe7cSHans Petter Selasky break; 14643dc87e52SEdward Tomasz Napierala case USB_TEMP_MULTI: 14653dc87e52SEdward Tomasz Napierala err = usb_temp_setup(udev, &usb_template_multi); 14663dc87e52SEdward Tomasz Napierala break; 146702ac6454SAndrew Thompson default: 146802ac6454SAndrew Thompson return (USB_ERR_INVAL); 146902ac6454SAndrew Thompson } 147002ac6454SAndrew Thompson 147102ac6454SAndrew Thompson return (err); 147202ac6454SAndrew Thompson } 147302ac6454SAndrew Thompson 147402ac6454SAndrew Thompson static void 1475a593f6b8SAndrew Thompson usb_temp_init(void *arg) 147602ac6454SAndrew Thompson { 147702ac6454SAndrew Thompson /* register our functions */ 1478a593f6b8SAndrew Thompson usb_temp_get_desc_p = &usb_temp_get_desc; 1479a593f6b8SAndrew Thompson usb_temp_setup_by_index_p = &usb_temp_setup_by_index; 1480a593f6b8SAndrew Thompson usb_temp_unsetup_p = &usb_temp_unsetup; 148102ac6454SAndrew Thompson } 148202ac6454SAndrew Thompson 1483a593f6b8SAndrew Thompson SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); 1484a593f6b8SAndrew Thompson SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); 1485