102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 2702ac6454SAndrew Thompson /* 2802ac6454SAndrew Thompson * This file contains sub-routines to build up USB descriptors from 2902ac6454SAndrew Thompson * USB templates. 3002ac6454SAndrew Thompson */ 3102ac6454SAndrew Thompson 3202ac6454SAndrew Thompson #include <dev/usb/usb.h> 3302ac6454SAndrew Thompson #include <dev/usb/usb_cdc.h> 3402ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h> 3502ac6454SAndrew Thompson #include <dev/usb/usb_error.h> 3602ac6454SAndrew Thompson 3702ac6454SAndrew Thompson #define USB_DEBUG_VAR usb2_debug 3802ac6454SAndrew Thompson 3902ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 4002ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 4102ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 4202ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 4302ac6454SAndrew Thompson #include <dev/usb/usb_parse.h> 4402ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 4502ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 4602ac6454SAndrew Thompson 4702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 4802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 4902ac6454SAndrew Thompson 5002ac6454SAndrew Thompson #include <dev/usb/template/usb_template.h> 5102ac6454SAndrew Thompson 5202ac6454SAndrew Thompson MODULE_DEPEND(usb_template, usb, 1, 1, 1); 5302ac6454SAndrew Thompson MODULE_VERSION(usb_template, 1); 5402ac6454SAndrew Thompson 5502ac6454SAndrew Thompson /* function prototypes */ 5602ac6454SAndrew Thompson 57760bc48eSAndrew Thompson static void usb2_make_raw_desc(struct usb_temp_setup *, const uint8_t *); 58760bc48eSAndrew Thompson static void usb2_make_endpoint_desc(struct usb_temp_setup *, 59760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *); 60760bc48eSAndrew Thompson static void usb2_make_interface_desc(struct usb_temp_setup *, 61760bc48eSAndrew Thompson const struct usb_temp_interface_desc *); 62760bc48eSAndrew Thompson static void usb2_make_config_desc(struct usb_temp_setup *, 63760bc48eSAndrew Thompson const struct usb_temp_config_desc *); 64760bc48eSAndrew Thompson static void usb2_make_device_desc(struct usb_temp_setup *, 65760bc48eSAndrew Thompson const struct usb_temp_device_desc *); 66760bc48eSAndrew Thompson static uint8_t usb2_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, 6702ac6454SAndrew Thompson uint8_t); 68760bc48eSAndrew Thompson static uint8_t usb2_hw_ep_find_match(struct usb_hw_ep_scratch *, 69760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *, uint8_t); 70760bc48eSAndrew Thompson static uint8_t usb2_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, 7102ac6454SAndrew Thompson uint8_t); 72e0a69b51SAndrew Thompson static usb_error_t usb2_hw_ep_resolve(struct usb_device *, 73760bc48eSAndrew Thompson struct usb_descriptor *); 74760bc48eSAndrew Thompson static const struct usb_temp_device_desc *usb2_temp_get_tdd(struct usb_device *); 75760bc48eSAndrew Thompson static void *usb2_temp_get_device_desc(struct usb_device *); 76760bc48eSAndrew Thompson static void *usb2_temp_get_qualifier_desc(struct usb_device *); 77760bc48eSAndrew Thompson static void *usb2_temp_get_config_desc(struct usb_device *, uint16_t *, 7802ac6454SAndrew Thompson uint8_t); 79760bc48eSAndrew Thompson static const void *usb2_temp_get_string_desc(struct usb_device *, uint16_t, 8002ac6454SAndrew Thompson uint8_t); 81760bc48eSAndrew Thompson static const void *usb2_temp_get_vendor_desc(struct usb_device *, 82760bc48eSAndrew Thompson const struct usb_device_request *); 83760bc48eSAndrew Thompson static const void *usb2_temp_get_hub_desc(struct usb_device *); 84e0a69b51SAndrew Thompson static usb_error_t usb2_temp_get_desc(struct usb_device *, 85760bc48eSAndrew Thompson struct usb_device_request *, const void **, uint16_t *); 86e0a69b51SAndrew Thompson static usb_error_t usb_temp_setup(struct usb_device *, 87760bc48eSAndrew Thompson const struct usb_temp_device_desc *); 88760bc48eSAndrew Thompson static void usb2_temp_unsetup(struct usb_device *); 89e0a69b51SAndrew Thompson static usb_error_t usb2_temp_setup_by_index(struct usb_device *, 9002ac6454SAndrew Thompson uint16_t index); 9102ac6454SAndrew Thompson static void usb2_temp_init(void *); 9202ac6454SAndrew Thompson 9302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 9402ac6454SAndrew Thompson * usb2_make_raw_desc 9502ac6454SAndrew Thompson * 9602ac6454SAndrew Thompson * This function will insert a raw USB descriptor into the generated 9702ac6454SAndrew Thompson * USB configuration. 9802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 9902ac6454SAndrew Thompson static void 100760bc48eSAndrew Thompson usb2_make_raw_desc(struct usb_temp_setup *temp, 10102ac6454SAndrew Thompson const uint8_t *raw) 10202ac6454SAndrew Thompson { 10302ac6454SAndrew Thompson void *dst; 10402ac6454SAndrew Thompson uint8_t len; 10502ac6454SAndrew Thompson 10602ac6454SAndrew Thompson /* 10702ac6454SAndrew Thompson * The first byte of any USB descriptor gives the length. 10802ac6454SAndrew Thompson */ 10902ac6454SAndrew Thompson if (raw) { 11002ac6454SAndrew Thompson len = raw[0]; 11102ac6454SAndrew Thompson if (temp->buf) { 11202ac6454SAndrew Thompson dst = USB_ADD_BYTES(temp->buf, temp->size); 11302ac6454SAndrew Thompson bcopy(raw, dst, len); 11402ac6454SAndrew Thompson 11502ac6454SAndrew Thompson /* check if we have got a CDC union descriptor */ 11602ac6454SAndrew Thompson 117760bc48eSAndrew Thompson if ((raw[0] >= sizeof(struct usb_cdc_union_descriptor)) && 11802ac6454SAndrew Thompson (raw[1] == UDESC_CS_INTERFACE) && 11902ac6454SAndrew Thompson (raw[2] == UDESCSUB_CDC_UNION)) { 120760bc48eSAndrew Thompson struct usb_cdc_union_descriptor *ud = (void *)dst; 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson /* update the interface numbers */ 12302ac6454SAndrew Thompson 12402ac6454SAndrew Thompson ud->bMasterInterface += 12502ac6454SAndrew Thompson temp->bInterfaceNumber; 12602ac6454SAndrew Thompson ud->bSlaveInterface[0] += 12702ac6454SAndrew Thompson temp->bInterfaceNumber; 12802ac6454SAndrew Thompson } 12902ac6454SAndrew Thompson } 13002ac6454SAndrew Thompson temp->size += len; 13102ac6454SAndrew Thompson } 13202ac6454SAndrew Thompson } 13302ac6454SAndrew Thompson 13402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 13502ac6454SAndrew Thompson * usb2_make_endpoint_desc 13602ac6454SAndrew Thompson * 13702ac6454SAndrew Thompson * This function will generate an USB endpoint descriptor from the 13802ac6454SAndrew Thompson * given USB template endpoint descriptor, which will be inserted into 13902ac6454SAndrew Thompson * the USB configuration. 14002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14102ac6454SAndrew Thompson static void 142760bc48eSAndrew Thompson usb2_make_endpoint_desc(struct usb_temp_setup *temp, 143760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc *ted) 14402ac6454SAndrew Thompson { 145760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 14602ac6454SAndrew Thompson const void **rd; 14702ac6454SAndrew Thompson uint16_t old_size; 14802ac6454SAndrew Thompson uint16_t mps; 14902ac6454SAndrew Thompson uint8_t ea = 0; /* Endpoint Address */ 15002ac6454SAndrew Thompson uint8_t et = 0; /* Endpiont Type */ 15102ac6454SAndrew Thompson 15202ac6454SAndrew Thompson /* Reserve memory */ 15302ac6454SAndrew Thompson old_size = temp->size; 15402ac6454SAndrew Thompson temp->size += sizeof(*ed); 15502ac6454SAndrew Thompson 15602ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson rd = ted->ppRawDesc; 15902ac6454SAndrew Thompson if (rd) { 16002ac6454SAndrew Thompson while (*rd) { 16102ac6454SAndrew Thompson usb2_make_raw_desc(temp, *rd); 16202ac6454SAndrew Thompson rd++; 16302ac6454SAndrew Thompson } 16402ac6454SAndrew Thompson } 16502ac6454SAndrew Thompson if (ted->pPacketSize == NULL) { 16602ac6454SAndrew Thompson /* not initialized */ 16702ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 16802ac6454SAndrew Thompson return; 16902ac6454SAndrew Thompson } 1708d2dd5ddSAndrew Thompson mps = ted->pPacketSize->mps[temp->usb_speed]; 17102ac6454SAndrew Thompson if (mps == 0) { 17202ac6454SAndrew Thompson /* not initialized */ 17302ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 17402ac6454SAndrew Thompson return; 17502ac6454SAndrew Thompson } else if (mps == UE_ZERO_MPS) { 17602ac6454SAndrew Thompson /* escape for Zero Max Packet Size */ 17702ac6454SAndrew Thompson mps = 0; 17802ac6454SAndrew Thompson } 17902ac6454SAndrew Thompson ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); 18002ac6454SAndrew Thompson et = (ted->bmAttributes & UE_XFERTYPE); 18102ac6454SAndrew Thompson 18202ac6454SAndrew Thompson /* 18302ac6454SAndrew Thompson * Fill out the real USB endpoint descriptor 18402ac6454SAndrew Thompson * in case there is a buffer present: 18502ac6454SAndrew Thompson */ 18602ac6454SAndrew Thompson if (temp->buf) { 18702ac6454SAndrew Thompson ed = USB_ADD_BYTES(temp->buf, old_size); 18802ac6454SAndrew Thompson ed->bLength = sizeof(*ed); 18902ac6454SAndrew Thompson ed->bDescriptorType = UDESC_ENDPOINT; 19002ac6454SAndrew Thompson ed->bEndpointAddress = ea; 19102ac6454SAndrew Thompson ed->bmAttributes = ted->bmAttributes; 19202ac6454SAndrew Thompson USETW(ed->wMaxPacketSize, mps); 19302ac6454SAndrew Thompson 19402ac6454SAndrew Thompson /* setup bInterval parameter */ 19502ac6454SAndrew Thompson 19602ac6454SAndrew Thompson if (ted->pIntervals && 1978d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]) { 19802ac6454SAndrew Thompson ed->bInterval = 1998d2dd5ddSAndrew Thompson ted->pIntervals->bInterval[temp->usb_speed]; 20002ac6454SAndrew Thompson } else { 20102ac6454SAndrew Thompson switch (et) { 20202ac6454SAndrew Thompson case UE_BULK: 20302ac6454SAndrew Thompson case UE_CONTROL: 20402ac6454SAndrew Thompson ed->bInterval = 0; /* not used */ 20502ac6454SAndrew Thompson break; 20602ac6454SAndrew Thompson case UE_INTERRUPT: 2078d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 20802ac6454SAndrew Thompson case USB_SPEED_LOW: 20902ac6454SAndrew Thompson case USB_SPEED_FULL: 21002ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 21102ac6454SAndrew Thompson break; 21202ac6454SAndrew Thompson default: 21302ac6454SAndrew Thompson ed->bInterval = 8; /* 8*125 us */ 21402ac6454SAndrew Thompson break; 21502ac6454SAndrew Thompson } 21602ac6454SAndrew Thompson break; 21702ac6454SAndrew Thompson default: /* UE_ISOCHRONOUS */ 2188d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 21902ac6454SAndrew Thompson case USB_SPEED_LOW: 22002ac6454SAndrew Thompson case USB_SPEED_FULL: 22102ac6454SAndrew Thompson ed->bInterval = 1; /* 1 ms */ 22202ac6454SAndrew Thompson break; 22302ac6454SAndrew Thompson default: 22402ac6454SAndrew Thompson ed->bInterval = 1; /* 125 us */ 22502ac6454SAndrew Thompson break; 22602ac6454SAndrew Thompson } 22702ac6454SAndrew Thompson break; 22802ac6454SAndrew Thompson } 22902ac6454SAndrew Thompson } 23002ac6454SAndrew Thompson } 23102ac6454SAndrew Thompson temp->bNumEndpoints++; 23202ac6454SAndrew Thompson } 23302ac6454SAndrew Thompson 23402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 23502ac6454SAndrew Thompson * usb2_make_interface_desc 23602ac6454SAndrew Thompson * 23702ac6454SAndrew Thompson * This function will generate an USB interface descriptor from the 23802ac6454SAndrew Thompson * given USB template interface descriptor, which will be inserted 23902ac6454SAndrew Thompson * into the USB configuration. 24002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 24102ac6454SAndrew Thompson static void 242760bc48eSAndrew Thompson usb2_make_interface_desc(struct usb_temp_setup *temp, 243760bc48eSAndrew Thompson const struct usb_temp_interface_desc *tid) 24402ac6454SAndrew Thompson { 245760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 246760bc48eSAndrew Thompson const struct usb_temp_endpoint_desc **ted; 24702ac6454SAndrew Thompson const void **rd; 24802ac6454SAndrew Thompson uint16_t old_size; 24902ac6454SAndrew Thompson 25002ac6454SAndrew Thompson /* Reserve memory */ 25102ac6454SAndrew Thompson 25202ac6454SAndrew Thompson old_size = temp->size; 25302ac6454SAndrew Thompson temp->size += sizeof(*id); 25402ac6454SAndrew Thompson 25502ac6454SAndrew Thompson /* Update interface and alternate interface numbers */ 25602ac6454SAndrew Thompson 25702ac6454SAndrew Thompson if (tid->isAltInterface == 0) { 25802ac6454SAndrew Thompson temp->bAlternateSetting = 0; 25902ac6454SAndrew Thompson temp->bInterfaceNumber++; 26002ac6454SAndrew Thompson } else { 26102ac6454SAndrew Thompson temp->bAlternateSetting++; 26202ac6454SAndrew Thompson } 26302ac6454SAndrew Thompson 26402ac6454SAndrew Thompson /* Scan all Raw Descriptors first */ 26502ac6454SAndrew Thompson 26602ac6454SAndrew Thompson rd = tid->ppRawDesc; 26702ac6454SAndrew Thompson 26802ac6454SAndrew Thompson if (rd) { 26902ac6454SAndrew Thompson while (*rd) { 27002ac6454SAndrew Thompson usb2_make_raw_desc(temp, *rd); 27102ac6454SAndrew Thompson rd++; 27202ac6454SAndrew Thompson } 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson /* Reset some counters */ 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson temp->bNumEndpoints = 0; 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson /* Scan all Endpoint Descriptors second */ 27902ac6454SAndrew Thompson 28002ac6454SAndrew Thompson ted = tid->ppEndpoints; 28102ac6454SAndrew Thompson if (ted) { 28202ac6454SAndrew Thompson while (*ted) { 28302ac6454SAndrew Thompson usb2_make_endpoint_desc(temp, *ted); 28402ac6454SAndrew Thompson ted++; 28502ac6454SAndrew Thompson } 28602ac6454SAndrew Thompson } 28702ac6454SAndrew Thompson /* 28802ac6454SAndrew Thompson * Fill out the real USB interface descriptor 28902ac6454SAndrew Thompson * in case there is a buffer present: 29002ac6454SAndrew Thompson */ 29102ac6454SAndrew Thompson if (temp->buf) { 29202ac6454SAndrew Thompson id = USB_ADD_BYTES(temp->buf, old_size); 29302ac6454SAndrew Thompson id->bLength = sizeof(*id); 29402ac6454SAndrew Thompson id->bDescriptorType = UDESC_INTERFACE; 29502ac6454SAndrew Thompson id->bInterfaceNumber = temp->bInterfaceNumber; 29602ac6454SAndrew Thompson id->bAlternateSetting = temp->bAlternateSetting; 29702ac6454SAndrew Thompson id->bNumEndpoints = temp->bNumEndpoints; 29802ac6454SAndrew Thompson id->bInterfaceClass = tid->bInterfaceClass; 29902ac6454SAndrew Thompson id->bInterfaceSubClass = tid->bInterfaceSubClass; 30002ac6454SAndrew Thompson id->bInterfaceProtocol = tid->bInterfaceProtocol; 30102ac6454SAndrew Thompson id->iInterface = tid->iInterface; 30202ac6454SAndrew Thompson } 30302ac6454SAndrew Thompson } 30402ac6454SAndrew Thompson 30502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 30602ac6454SAndrew Thompson * usb2_make_config_desc 30702ac6454SAndrew Thompson * 30802ac6454SAndrew Thompson * This function will generate an USB config descriptor from the given 30902ac6454SAndrew Thompson * USB template config descriptor, which will be inserted into the USB 31002ac6454SAndrew Thompson * configuration. 31102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 31202ac6454SAndrew Thompson static void 313760bc48eSAndrew Thompson usb2_make_config_desc(struct usb_temp_setup *temp, 314760bc48eSAndrew Thompson const struct usb_temp_config_desc *tcd) 31502ac6454SAndrew Thompson { 316760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 317760bc48eSAndrew Thompson const struct usb_temp_interface_desc **tid; 31802ac6454SAndrew Thompson uint16_t old_size; 31902ac6454SAndrew Thompson 32002ac6454SAndrew Thompson /* Reserve memory */ 32102ac6454SAndrew Thompson 32202ac6454SAndrew Thompson old_size = temp->size; 32302ac6454SAndrew Thompson temp->size += sizeof(*cd); 32402ac6454SAndrew Thompson 32502ac6454SAndrew Thompson /* Reset some counters */ 32602ac6454SAndrew Thompson 32702ac6454SAndrew Thompson temp->bInterfaceNumber = 0 - 1; 32802ac6454SAndrew Thompson temp->bAlternateSetting = 0; 32902ac6454SAndrew Thompson 33002ac6454SAndrew Thompson /* Scan all the USB interfaces */ 33102ac6454SAndrew Thompson 33202ac6454SAndrew Thompson tid = tcd->ppIfaceDesc; 33302ac6454SAndrew Thompson if (tid) { 33402ac6454SAndrew Thompson while (*tid) { 33502ac6454SAndrew Thompson usb2_make_interface_desc(temp, *tid); 33602ac6454SAndrew Thompson tid++; 33702ac6454SAndrew Thompson } 33802ac6454SAndrew Thompson } 33902ac6454SAndrew Thompson /* 34002ac6454SAndrew Thompson * Fill out the real USB config descriptor 34102ac6454SAndrew Thompson * in case there is a buffer present: 34202ac6454SAndrew Thompson */ 34302ac6454SAndrew Thompson if (temp->buf) { 34402ac6454SAndrew Thompson cd = USB_ADD_BYTES(temp->buf, old_size); 34502ac6454SAndrew Thompson 34602ac6454SAndrew Thompson /* compute total size */ 34702ac6454SAndrew Thompson old_size = temp->size - old_size; 34802ac6454SAndrew Thompson 34902ac6454SAndrew Thompson cd->bLength = sizeof(*cd); 35002ac6454SAndrew Thompson cd->bDescriptorType = UDESC_CONFIG; 35102ac6454SAndrew Thompson USETW(cd->wTotalLength, old_size); 35202ac6454SAndrew Thompson cd->bNumInterface = temp->bInterfaceNumber + 1; 35302ac6454SAndrew Thompson cd->bConfigurationValue = temp->bConfigurationValue; 35402ac6454SAndrew Thompson cd->iConfiguration = tcd->iConfiguration; 35502ac6454SAndrew Thompson cd->bmAttributes = tcd->bmAttributes; 35602ac6454SAndrew Thompson cd->bMaxPower = tcd->bMaxPower; 35702ac6454SAndrew Thompson cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); 35802ac6454SAndrew Thompson 35902ac6454SAndrew Thompson if (temp->self_powered) { 36002ac6454SAndrew Thompson cd->bmAttributes |= UC_SELF_POWERED; 36102ac6454SAndrew Thompson } else { 36202ac6454SAndrew Thompson cd->bmAttributes &= ~UC_SELF_POWERED; 36302ac6454SAndrew Thompson } 36402ac6454SAndrew Thompson } 36502ac6454SAndrew Thompson } 36602ac6454SAndrew Thompson 36702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 36802ac6454SAndrew Thompson * usb2_make_device_desc 36902ac6454SAndrew Thompson * 37002ac6454SAndrew Thompson * This function will generate an USB device descriptor from the 37102ac6454SAndrew Thompson * given USB template device descriptor. 37202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37302ac6454SAndrew Thompson static void 374760bc48eSAndrew Thompson usb2_make_device_desc(struct usb_temp_setup *temp, 375760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 37602ac6454SAndrew Thompson { 377760bc48eSAndrew Thompson struct usb_temp_data *utd; 378760bc48eSAndrew Thompson const struct usb_temp_config_desc **tcd; 37902ac6454SAndrew Thompson uint16_t old_size; 38002ac6454SAndrew Thompson 38102ac6454SAndrew Thompson /* Reserve memory */ 38202ac6454SAndrew Thompson 38302ac6454SAndrew Thompson old_size = temp->size; 38402ac6454SAndrew Thompson temp->size += sizeof(*utd); 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson /* Scan all the USB configs */ 38702ac6454SAndrew Thompson 38802ac6454SAndrew Thompson temp->bConfigurationValue = 1; 38902ac6454SAndrew Thompson tcd = tdd->ppConfigDesc; 39002ac6454SAndrew Thompson if (tcd) { 39102ac6454SAndrew Thompson while (*tcd) { 39202ac6454SAndrew Thompson usb2_make_config_desc(temp, *tcd); 39302ac6454SAndrew Thompson temp->bConfigurationValue++; 39402ac6454SAndrew Thompson tcd++; 39502ac6454SAndrew Thompson } 39602ac6454SAndrew Thompson } 39702ac6454SAndrew Thompson /* 39802ac6454SAndrew Thompson * Fill out the real USB device descriptor 39902ac6454SAndrew Thompson * in case there is a buffer present: 40002ac6454SAndrew Thompson */ 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson if (temp->buf) { 40302ac6454SAndrew Thompson utd = USB_ADD_BYTES(temp->buf, old_size); 40402ac6454SAndrew Thompson 40502ac6454SAndrew Thompson /* Store a pointer to our template device descriptor */ 40602ac6454SAndrew Thompson utd->tdd = tdd; 40702ac6454SAndrew Thompson 40802ac6454SAndrew Thompson /* Fill out USB device descriptor */ 40902ac6454SAndrew Thompson utd->udd.bLength = sizeof(utd->udd); 41002ac6454SAndrew Thompson utd->udd.bDescriptorType = UDESC_DEVICE; 41102ac6454SAndrew Thompson utd->udd.bDeviceClass = tdd->bDeviceClass; 41202ac6454SAndrew Thompson utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; 41302ac6454SAndrew Thompson utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; 41402ac6454SAndrew Thompson USETW(utd->udd.idVendor, tdd->idVendor); 41502ac6454SAndrew Thompson USETW(utd->udd.idProduct, tdd->idProduct); 41602ac6454SAndrew Thompson USETW(utd->udd.bcdDevice, tdd->bcdDevice); 41702ac6454SAndrew Thompson utd->udd.iManufacturer = tdd->iManufacturer; 41802ac6454SAndrew Thompson utd->udd.iProduct = tdd->iProduct; 41902ac6454SAndrew Thompson utd->udd.iSerialNumber = tdd->iSerialNumber; 42002ac6454SAndrew Thompson utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; 42102ac6454SAndrew Thompson 42202ac6454SAndrew Thompson /* 42302ac6454SAndrew Thompson * Fill out the USB device qualifier. Pretend that we 42402ac6454SAndrew Thompson * don't support any other speeds by setting 42502ac6454SAndrew Thompson * "bNumConfigurations" equal to zero. That saves us 42602ac6454SAndrew Thompson * generating an extra set of configuration 42702ac6454SAndrew Thompson * descriptors. 42802ac6454SAndrew Thompson */ 42902ac6454SAndrew Thompson utd->udq.bLength = sizeof(utd->udq); 43002ac6454SAndrew Thompson utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; 43102ac6454SAndrew Thompson utd->udq.bDeviceClass = tdd->bDeviceClass; 43202ac6454SAndrew Thompson utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; 43302ac6454SAndrew Thompson utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; 43402ac6454SAndrew Thompson utd->udq.bNumConfigurations = 0; 43502ac6454SAndrew Thompson USETW(utd->udq.bcdUSB, 0x0200); 43602ac6454SAndrew Thompson utd->udq.bMaxPacketSize0 = 0; 43702ac6454SAndrew Thompson 4388d2dd5ddSAndrew Thompson switch (temp->usb_speed) { 43902ac6454SAndrew Thompson case USB_SPEED_LOW: 44002ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 44102ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 8; 44202ac6454SAndrew Thompson break; 44302ac6454SAndrew Thompson case USB_SPEED_FULL: 44402ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0110); 44502ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 32; 44602ac6454SAndrew Thompson break; 44702ac6454SAndrew Thompson case USB_SPEED_HIGH: 44802ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0200); 44902ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 64; 45002ac6454SAndrew Thompson break; 45102ac6454SAndrew Thompson case USB_SPEED_VARIABLE: 45202ac6454SAndrew Thompson USETW(utd->udd.bcdUSB, 0x0250); 45302ac6454SAndrew Thompson utd->udd.bMaxPacketSize = 255; /* 512 bytes */ 45402ac6454SAndrew Thompson break; 45502ac6454SAndrew Thompson default: 45602ac6454SAndrew Thompson temp->err = USB_ERR_INVAL; 45702ac6454SAndrew Thompson break; 45802ac6454SAndrew Thompson } 45902ac6454SAndrew Thompson } 46002ac6454SAndrew Thompson } 46102ac6454SAndrew Thompson 46202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 46302ac6454SAndrew Thompson * usb2_hw_ep_match 46402ac6454SAndrew Thompson * 46502ac6454SAndrew Thompson * Return values: 46602ac6454SAndrew Thompson * 0: The endpoint profile does not match the criterias 46702ac6454SAndrew Thompson * Else: The endpoint profile matches the criterias 46802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 46902ac6454SAndrew Thompson static uint8_t 470760bc48eSAndrew Thompson usb2_hw_ep_match(const struct usb_hw_ep_profile *pf, 47102ac6454SAndrew Thompson uint8_t ep_type, uint8_t ep_dir_in) 47202ac6454SAndrew Thompson { 47302ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 47402ac6454SAndrew Thompson /* special */ 47502ac6454SAndrew Thompson return (pf->support_control); 47602ac6454SAndrew Thompson } 47702ac6454SAndrew Thompson if ((pf->support_in && ep_dir_in) || 47802ac6454SAndrew Thompson (pf->support_out && !ep_dir_in)) { 47902ac6454SAndrew Thompson if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || 48002ac6454SAndrew Thompson (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || 48102ac6454SAndrew Thompson (pf->support_bulk && (ep_type == UE_BULK))) { 48202ac6454SAndrew Thompson return (1); 48302ac6454SAndrew Thompson } 48402ac6454SAndrew Thompson } 48502ac6454SAndrew Thompson return (0); 48602ac6454SAndrew Thompson } 48702ac6454SAndrew Thompson 48802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 48902ac6454SAndrew Thompson * usb2_hw_ep_find_match 49002ac6454SAndrew Thompson * 49102ac6454SAndrew Thompson * This function is used to find the best matching endpoint profile 49202ac6454SAndrew Thompson * for and endpoint belonging to an USB descriptor. 49302ac6454SAndrew Thompson * 49402ac6454SAndrew Thompson * Return values: 49502ac6454SAndrew Thompson * 0: Success. Got a match. 49602ac6454SAndrew Thompson * Else: Failure. No match. 49702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 49802ac6454SAndrew Thompson static uint8_t 499760bc48eSAndrew Thompson usb2_hw_ep_find_match(struct usb_hw_ep_scratch *ues, 500760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) 50102ac6454SAndrew Thompson { 502760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 50302ac6454SAndrew Thompson uint16_t distance; 50402ac6454SAndrew Thompson uint16_t temp; 50502ac6454SAndrew Thompson uint16_t max_frame_size; 50602ac6454SAndrew Thompson uint8_t n; 50702ac6454SAndrew Thompson uint8_t best_n; 50802ac6454SAndrew Thompson uint8_t dir_in; 50902ac6454SAndrew Thompson uint8_t dir_out; 51002ac6454SAndrew Thompson 51102ac6454SAndrew Thompson distance = 0xFFFF; 51202ac6454SAndrew Thompson best_n = 0; 51302ac6454SAndrew Thompson 51402ac6454SAndrew Thompson if ((!ep->needs_in) && (!ep->needs_out)) { 51502ac6454SAndrew Thompson return (0); /* we are done */ 51602ac6454SAndrew Thompson } 51702ac6454SAndrew Thompson if (ep->needs_ep_type == UE_CONTROL) { 51802ac6454SAndrew Thompson dir_in = 1; 51902ac6454SAndrew Thompson dir_out = 1; 52002ac6454SAndrew Thompson } else { 52102ac6454SAndrew Thompson if (ep->needs_in) { 52202ac6454SAndrew Thompson dir_in = 1; 52302ac6454SAndrew Thompson dir_out = 0; 52402ac6454SAndrew Thompson } else { 52502ac6454SAndrew Thompson dir_in = 0; 52602ac6454SAndrew Thompson dir_out = 1; 52702ac6454SAndrew Thompson } 52802ac6454SAndrew Thompson } 52902ac6454SAndrew Thompson 53002ac6454SAndrew Thompson for (n = 1; n != (USB_EP_MAX / 2); n++) { 53102ac6454SAndrew Thompson 53202ac6454SAndrew Thompson /* get HW endpoint profile */ 53302ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); 53402ac6454SAndrew Thompson if (pf == NULL) { 53502ac6454SAndrew Thompson /* end of profiles */ 53602ac6454SAndrew Thompson break; 53702ac6454SAndrew Thompson } 53802ac6454SAndrew Thompson /* check if IN-endpoint is reserved */ 53902ac6454SAndrew Thompson if (dir_in || pf->is_simplex) { 54002ac6454SAndrew Thompson if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { 54102ac6454SAndrew Thompson /* mismatch */ 54202ac6454SAndrew Thompson continue; 54302ac6454SAndrew Thompson } 54402ac6454SAndrew Thompson } 54502ac6454SAndrew Thompson /* check if OUT-endpoint is reserved */ 54602ac6454SAndrew Thompson if (dir_out || pf->is_simplex) { 54702ac6454SAndrew Thompson if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { 54802ac6454SAndrew Thompson /* mismatch */ 54902ac6454SAndrew Thompson continue; 55002ac6454SAndrew Thompson } 55102ac6454SAndrew Thompson } 55202ac6454SAndrew Thompson /* check simplex */ 55302ac6454SAndrew Thompson if (pf->is_simplex == is_simplex) { 55402ac6454SAndrew Thompson /* mismatch */ 55502ac6454SAndrew Thompson continue; 55602ac6454SAndrew Thompson } 55702ac6454SAndrew Thompson /* check if HW endpoint matches */ 55802ac6454SAndrew Thompson if (!usb2_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { 55902ac6454SAndrew Thompson /* mismatch */ 56002ac6454SAndrew Thompson continue; 56102ac6454SAndrew Thompson } 56202ac6454SAndrew Thompson /* get maximum frame size */ 56302ac6454SAndrew Thompson if (dir_in) 56402ac6454SAndrew Thompson max_frame_size = pf->max_in_frame_size; 56502ac6454SAndrew Thompson else 56602ac6454SAndrew Thompson max_frame_size = pf->max_out_frame_size; 56702ac6454SAndrew Thompson 56802ac6454SAndrew Thompson /* check if we have a matching profile */ 56902ac6454SAndrew Thompson if (max_frame_size >= ep->max_frame_size) { 57002ac6454SAndrew Thompson temp = (max_frame_size - ep->max_frame_size); 57102ac6454SAndrew Thompson if (distance > temp) { 57202ac6454SAndrew Thompson distance = temp; 57302ac6454SAndrew Thompson best_n = n; 57402ac6454SAndrew Thompson ep->pf = pf; 57502ac6454SAndrew Thompson } 57602ac6454SAndrew Thompson } 57702ac6454SAndrew Thompson } 57802ac6454SAndrew Thompson 57902ac6454SAndrew Thompson /* see if we got a match */ 58002ac6454SAndrew Thompson if (best_n != 0) { 58102ac6454SAndrew Thompson /* get the correct profile */ 58202ac6454SAndrew Thompson pf = ep->pf; 58302ac6454SAndrew Thompson 58402ac6454SAndrew Thompson /* reserve IN-endpoint */ 58502ac6454SAndrew Thompson if (dir_in) { 58602ac6454SAndrew Thompson ues->bmInAlloc[best_n / 8] |= 58702ac6454SAndrew Thompson (1 << (best_n % 8)); 58802ac6454SAndrew Thompson ep->hw_endpoint_in = best_n | UE_DIR_IN; 58902ac6454SAndrew Thompson ep->needs_in = 0; 59002ac6454SAndrew Thompson } 59102ac6454SAndrew Thompson /* reserve OUT-endpoint */ 59202ac6454SAndrew Thompson if (dir_out) { 59302ac6454SAndrew Thompson ues->bmOutAlloc[best_n / 8] |= 59402ac6454SAndrew Thompson (1 << (best_n % 8)); 59502ac6454SAndrew Thompson ep->hw_endpoint_out = best_n | UE_DIR_OUT; 59602ac6454SAndrew Thompson ep->needs_out = 0; 59702ac6454SAndrew Thompson } 59802ac6454SAndrew Thompson return (0); /* got a match */ 59902ac6454SAndrew Thompson } 60002ac6454SAndrew Thompson return (1); /* failure */ 60102ac6454SAndrew Thompson } 60202ac6454SAndrew Thompson 60302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 60402ac6454SAndrew Thompson * usb2_hw_ep_get_needs 60502ac6454SAndrew Thompson * 60602ac6454SAndrew Thompson * This function will figure out the type and number of endpoints 60702ac6454SAndrew Thompson * which are needed for an USB configuration. 60802ac6454SAndrew Thompson * 60902ac6454SAndrew Thompson * Return values: 61002ac6454SAndrew Thompson * 0: Success. 61102ac6454SAndrew Thompson * Else: Failure. 61202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 61302ac6454SAndrew Thompson static uint8_t 614760bc48eSAndrew Thompson usb2_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, 61502ac6454SAndrew Thompson uint8_t ep_type, uint8_t is_complete) 61602ac6454SAndrew Thompson { 617760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 618760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_iface; 619760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_curr; 620760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_max; 621760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep_end; 622760bc48eSAndrew Thompson struct usb_descriptor *desc; 623760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 624760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 6258d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 62602ac6454SAndrew Thompson uint16_t wMaxPacketSize; 62702ac6454SAndrew Thompson uint16_t temp; 62802ac6454SAndrew Thompson uint8_t ep_no; 62902ac6454SAndrew Thompson 63002ac6454SAndrew Thompson ep_iface = ues->ep_max; 63102ac6454SAndrew Thompson ep_curr = ues->ep_max; 63202ac6454SAndrew Thompson ep_end = ues->ep + USB_EP_MAX; 63302ac6454SAndrew Thompson ep_max = ues->ep_max; 63402ac6454SAndrew Thompson desc = NULL; 63502ac6454SAndrew Thompson speed = usb2_get_speed(ues->udev); 63602ac6454SAndrew Thompson 63702ac6454SAndrew Thompson repeat: 63802ac6454SAndrew Thompson 63902ac6454SAndrew Thompson while ((desc = usb2_desc_foreach(ues->cd, desc))) { 64002ac6454SAndrew Thompson 64102ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 64202ac6454SAndrew Thompson (desc->bLength >= sizeof(*id))) { 64302ac6454SAndrew Thompson 64402ac6454SAndrew Thompson id = (void *)desc; 64502ac6454SAndrew Thompson 64602ac6454SAndrew Thompson if (id->bAlternateSetting == 0) { 64702ac6454SAndrew Thompson /* going forward */ 64802ac6454SAndrew Thompson ep_iface = ep_max; 64902ac6454SAndrew Thompson } else { 65002ac6454SAndrew Thompson /* reset */ 65102ac6454SAndrew Thompson ep_curr = ep_iface; 65202ac6454SAndrew Thompson } 65302ac6454SAndrew Thompson } 65402ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_ENDPOINT) && 65502ac6454SAndrew Thompson (desc->bLength >= sizeof(*ed))) { 65602ac6454SAndrew Thompson 65702ac6454SAndrew Thompson ed = (void *)desc; 65802ac6454SAndrew Thompson 65902ac6454SAndrew Thompson goto handle_endpoint_desc; 66002ac6454SAndrew Thompson } 66102ac6454SAndrew Thompson } 66202ac6454SAndrew Thompson ues->ep_max = ep_max; 66302ac6454SAndrew Thompson return (0); 66402ac6454SAndrew Thompson 66502ac6454SAndrew Thompson handle_endpoint_desc: 66602ac6454SAndrew Thompson temp = (ed->bmAttributes & UE_XFERTYPE); 66702ac6454SAndrew Thompson 66802ac6454SAndrew Thompson if (temp == ep_type) { 66902ac6454SAndrew Thompson 67002ac6454SAndrew Thompson if (ep_curr == ep_end) { 67102ac6454SAndrew Thompson /* too many endpoints */ 67202ac6454SAndrew Thompson return (1); /* failure */ 67302ac6454SAndrew Thompson } 67402ac6454SAndrew Thompson wMaxPacketSize = UGETW(ed->wMaxPacketSize); 67502ac6454SAndrew Thompson if ((wMaxPacketSize & 0xF800) && 67602ac6454SAndrew Thompson (speed == USB_SPEED_HIGH)) { 67702ac6454SAndrew Thompson /* handle packet multiplier */ 67802ac6454SAndrew Thompson temp = (wMaxPacketSize >> 11) & 3; 67902ac6454SAndrew Thompson wMaxPacketSize &= 0x7FF; 68002ac6454SAndrew Thompson if (temp == 1) { 68102ac6454SAndrew Thompson wMaxPacketSize *= 2; 68202ac6454SAndrew Thompson } else { 68302ac6454SAndrew Thompson wMaxPacketSize *= 3; 68402ac6454SAndrew Thompson } 68502ac6454SAndrew Thompson } 68602ac6454SAndrew Thompson /* 68702ac6454SAndrew Thompson * Check if we have a fixed endpoint number, else the 68802ac6454SAndrew Thompson * endpoint number is allocated dynamically: 68902ac6454SAndrew Thompson */ 69002ac6454SAndrew Thompson ep_no = (ed->bEndpointAddress & UE_ADDR); 69102ac6454SAndrew Thompson if (ep_no != 0) { 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson /* get HW endpoint profile */ 69402ac6454SAndrew Thompson (ues->methods->get_hw_ep_profile) 69502ac6454SAndrew Thompson (ues->udev, &pf, ep_no); 69602ac6454SAndrew Thompson if (pf == NULL) { 69702ac6454SAndrew Thompson /* HW profile does not exist - failure */ 69802ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 69902ac6454SAndrew Thompson "does not exist\n", ep_no); 70002ac6454SAndrew Thompson return (1); 70102ac6454SAndrew Thompson } 70202ac6454SAndrew Thompson /* reserve fixed endpoint number */ 70302ac6454SAndrew Thompson if (ep_type == UE_CONTROL) { 70402ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 70502ac6454SAndrew Thompson (1 << (ep_no % 8)); 70602ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 70702ac6454SAndrew Thompson (1 << (ep_no % 8)); 70802ac6454SAndrew Thompson if ((pf->max_in_frame_size < wMaxPacketSize) || 70902ac6454SAndrew Thompson (pf->max_out_frame_size < wMaxPacketSize)) { 71002ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 71102ac6454SAndrew Thompson "has too small buffer!\n", ep_no); 71202ac6454SAndrew Thompson return (1); 71302ac6454SAndrew Thompson } 71402ac6454SAndrew Thompson } else if (ed->bEndpointAddress & UE_DIR_IN) { 71502ac6454SAndrew Thompson ues->bmInAlloc[ep_no / 8] |= 71602ac6454SAndrew Thompson (1 << (ep_no % 8)); 71702ac6454SAndrew Thompson if (pf->max_in_frame_size < wMaxPacketSize) { 71802ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 71902ac6454SAndrew Thompson "has too small buffer!\n", ep_no); 72002ac6454SAndrew Thompson return (1); 72102ac6454SAndrew Thompson } 72202ac6454SAndrew Thompson } else { 72302ac6454SAndrew Thompson ues->bmOutAlloc[ep_no / 8] |= 72402ac6454SAndrew Thompson (1 << (ep_no % 8)); 72502ac6454SAndrew Thompson if (pf->max_out_frame_size < wMaxPacketSize) { 72602ac6454SAndrew Thompson DPRINTFN(0, "Endpoint profile %u " 72702ac6454SAndrew Thompson "has too small buffer!\n", ep_no); 72802ac6454SAndrew Thompson return (1); 72902ac6454SAndrew Thompson } 73002ac6454SAndrew Thompson } 73102ac6454SAndrew Thompson } else if (is_complete) { 73202ac6454SAndrew Thompson 73302ac6454SAndrew Thompson /* check if we have enough buffer space */ 73402ac6454SAndrew Thompson if (wMaxPacketSize > 73502ac6454SAndrew Thompson ep_curr->max_frame_size) { 73602ac6454SAndrew Thompson return (1); /* failure */ 73702ac6454SAndrew Thompson } 73802ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 73902ac6454SAndrew Thompson ed->bEndpointAddress = 74002ac6454SAndrew Thompson ep_curr->hw_endpoint_in; 74102ac6454SAndrew Thompson } else { 74202ac6454SAndrew Thompson ed->bEndpointAddress = 74302ac6454SAndrew Thompson ep_curr->hw_endpoint_out; 74402ac6454SAndrew Thompson } 74502ac6454SAndrew Thompson 74602ac6454SAndrew Thompson } else { 74702ac6454SAndrew Thompson 74802ac6454SAndrew Thompson /* compute the maximum frame size */ 74902ac6454SAndrew Thompson if (ep_curr->max_frame_size < wMaxPacketSize) { 75002ac6454SAndrew Thompson ep_curr->max_frame_size = wMaxPacketSize; 75102ac6454SAndrew Thompson } 75202ac6454SAndrew Thompson if (temp == UE_CONTROL) { 75302ac6454SAndrew Thompson ep_curr->needs_in = 1; 75402ac6454SAndrew Thompson ep_curr->needs_out = 1; 75502ac6454SAndrew Thompson } else { 75602ac6454SAndrew Thompson if (ed->bEndpointAddress & UE_DIR_IN) { 75702ac6454SAndrew Thompson ep_curr->needs_in = 1; 75802ac6454SAndrew Thompson } else { 75902ac6454SAndrew Thompson ep_curr->needs_out = 1; 76002ac6454SAndrew Thompson } 76102ac6454SAndrew Thompson } 76202ac6454SAndrew Thompson ep_curr->needs_ep_type = ep_type; 76302ac6454SAndrew Thompson } 76402ac6454SAndrew Thompson 76502ac6454SAndrew Thompson ep_curr++; 76602ac6454SAndrew Thompson if (ep_max < ep_curr) { 76702ac6454SAndrew Thompson ep_max = ep_curr; 76802ac6454SAndrew Thompson } 76902ac6454SAndrew Thompson } 77002ac6454SAndrew Thompson goto repeat; 77102ac6454SAndrew Thompson } 77202ac6454SAndrew Thompson 77302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 77402ac6454SAndrew Thompson * usb2_hw_ep_resolve 77502ac6454SAndrew Thompson * 77602ac6454SAndrew Thompson * This function will try to resolve endpoint requirements by the 77702ac6454SAndrew Thompson * given endpoint profiles that the USB hardware reports. 77802ac6454SAndrew Thompson * 77902ac6454SAndrew Thompson * Return values: 78002ac6454SAndrew Thompson * 0: Success 78102ac6454SAndrew Thompson * Else: Failure 78202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 783e0a69b51SAndrew Thompson static usb_error_t 784760bc48eSAndrew Thompson usb2_hw_ep_resolve(struct usb_device *udev, 785760bc48eSAndrew Thompson struct usb_descriptor *desc) 78602ac6454SAndrew Thompson { 787760bc48eSAndrew Thompson struct usb_hw_ep_scratch *ues; 788760bc48eSAndrew Thompson struct usb_hw_ep_scratch_sub *ep; 789760bc48eSAndrew Thompson const struct usb_hw_ep_profile *pf; 790760bc48eSAndrew Thompson struct usb_bus_methods *methods; 791760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 79202ac6454SAndrew Thompson uint16_t mps; 79302ac6454SAndrew Thompson 79402ac6454SAndrew Thompson if (desc == NULL) { 79502ac6454SAndrew Thompson return (USB_ERR_INVAL); 79602ac6454SAndrew Thompson } 79702ac6454SAndrew Thompson /* get bus methods */ 79802ac6454SAndrew Thompson methods = udev->bus->methods; 79902ac6454SAndrew Thompson 80002ac6454SAndrew Thompson if (methods->get_hw_ep_profile == NULL) { 80102ac6454SAndrew Thompson return (USB_ERR_INVAL); 80202ac6454SAndrew Thompson } 80302ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_DEVICE) { 80402ac6454SAndrew Thompson 80502ac6454SAndrew Thompson if (desc->bLength < sizeof(*dd)) { 80602ac6454SAndrew Thompson return (USB_ERR_INVAL); 80702ac6454SAndrew Thompson } 80802ac6454SAndrew Thompson dd = (void *)desc; 80902ac6454SAndrew Thompson 81002ac6454SAndrew Thompson /* get HW control endpoint 0 profile */ 81102ac6454SAndrew Thompson (methods->get_hw_ep_profile) (udev, &pf, 0); 81202ac6454SAndrew Thompson if (pf == NULL) { 81302ac6454SAndrew Thompson return (USB_ERR_INVAL); 81402ac6454SAndrew Thompson } 81502ac6454SAndrew Thompson if (!usb2_hw_ep_match(pf, UE_CONTROL, 0)) { 81602ac6454SAndrew Thompson DPRINTFN(0, "Endpoint 0 does not " 81702ac6454SAndrew Thompson "support control\n"); 81802ac6454SAndrew Thompson return (USB_ERR_INVAL); 81902ac6454SAndrew Thompson } 82002ac6454SAndrew Thompson mps = dd->bMaxPacketSize; 82102ac6454SAndrew Thompson 82202ac6454SAndrew Thompson if (udev->speed == USB_SPEED_FULL) { 82302ac6454SAndrew Thompson /* 82402ac6454SAndrew Thompson * We can optionally choose another packet size ! 82502ac6454SAndrew Thompson */ 82602ac6454SAndrew Thompson while (1) { 82702ac6454SAndrew Thompson /* check if "mps" is ok */ 82802ac6454SAndrew Thompson if (pf->max_in_frame_size >= mps) { 82902ac6454SAndrew Thompson break; 83002ac6454SAndrew Thompson } 83102ac6454SAndrew Thompson /* reduce maximum packet size */ 83202ac6454SAndrew Thompson mps /= 2; 83302ac6454SAndrew Thompson 83402ac6454SAndrew Thompson /* check if "mps" is too small */ 83502ac6454SAndrew Thompson if (mps < 8) { 83602ac6454SAndrew Thompson return (USB_ERR_INVAL); 83702ac6454SAndrew Thompson } 83802ac6454SAndrew Thompson } 83902ac6454SAndrew Thompson 84002ac6454SAndrew Thompson dd->bMaxPacketSize = mps; 84102ac6454SAndrew Thompson 84202ac6454SAndrew Thompson } else { 84302ac6454SAndrew Thompson /* We only have one choice */ 84402ac6454SAndrew Thompson if (mps == 255) { 84502ac6454SAndrew Thompson mps = 512; 84602ac6454SAndrew Thompson } 84702ac6454SAndrew Thompson /* Check if we support the specified wMaxPacketSize */ 84802ac6454SAndrew Thompson if (pf->max_in_frame_size < mps) { 84902ac6454SAndrew Thompson return (USB_ERR_INVAL); 85002ac6454SAndrew Thompson } 85102ac6454SAndrew Thompson } 85202ac6454SAndrew Thompson return (0); /* success */ 85302ac6454SAndrew Thompson } 85402ac6454SAndrew Thompson if (desc->bDescriptorType != UDESC_CONFIG) { 85502ac6454SAndrew Thompson return (USB_ERR_INVAL); 85602ac6454SAndrew Thompson } 85702ac6454SAndrew Thompson if (desc->bLength < sizeof(*(ues->cd))) { 85802ac6454SAndrew Thompson return (USB_ERR_INVAL); 85902ac6454SAndrew Thompson } 86002ac6454SAndrew Thompson ues = udev->bus->scratch[0].hw_ep_scratch; 86102ac6454SAndrew Thompson 86202ac6454SAndrew Thompson bzero(ues, sizeof(*ues)); 86302ac6454SAndrew Thompson 86402ac6454SAndrew Thompson ues->ep_max = ues->ep; 86502ac6454SAndrew Thompson ues->cd = (void *)desc; 86602ac6454SAndrew Thompson ues->methods = methods; 86702ac6454SAndrew Thompson ues->udev = udev; 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson /* Get all the endpoints we need */ 87002ac6454SAndrew Thompson 87102ac6454SAndrew Thompson if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || 87202ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || 87302ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_CONTROL, 0) || 87402ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_BULK, 0)) { 87502ac6454SAndrew Thompson DPRINTFN(0, "Could not get needs\n"); 87602ac6454SAndrew Thompson return (USB_ERR_INVAL); 87702ac6454SAndrew Thompson } 87802ac6454SAndrew Thompson for (ep = ues->ep; ep != ues->ep_max; ep++) { 87902ac6454SAndrew Thompson 88002ac6454SAndrew Thompson while (ep->needs_in || ep->needs_out) { 88102ac6454SAndrew Thompson 88202ac6454SAndrew Thompson /* 88302ac6454SAndrew Thompson * First try to use a simplex endpoint. 88402ac6454SAndrew Thompson * Then try to use a duplex endpoint. 88502ac6454SAndrew Thompson */ 88602ac6454SAndrew Thompson if (usb2_hw_ep_find_match(ues, ep, 1) && 88702ac6454SAndrew Thompson usb2_hw_ep_find_match(ues, ep, 0)) { 88802ac6454SAndrew Thompson DPRINTFN(0, "Could not find match\n"); 88902ac6454SAndrew Thompson return (USB_ERR_INVAL); 89002ac6454SAndrew Thompson } 89102ac6454SAndrew Thompson } 89202ac6454SAndrew Thompson } 89302ac6454SAndrew Thompson 89402ac6454SAndrew Thompson ues->ep_max = ues->ep; 89502ac6454SAndrew Thompson 89602ac6454SAndrew Thompson /* Update all endpoint addresses */ 89702ac6454SAndrew Thompson 89802ac6454SAndrew Thompson if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || 89902ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || 90002ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_CONTROL, 1) || 90102ac6454SAndrew Thompson usb2_hw_ep_get_needs(ues, UE_BULK, 1)) { 90202ac6454SAndrew Thompson DPRINTFN(0, "Could not update endpoint address\n"); 90302ac6454SAndrew Thompson return (USB_ERR_INVAL); 90402ac6454SAndrew Thompson } 90502ac6454SAndrew Thompson return (0); /* success */ 90602ac6454SAndrew Thompson } 90702ac6454SAndrew Thompson 90802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 90902ac6454SAndrew Thompson * usb2_temp_get_tdd 91002ac6454SAndrew Thompson * 91102ac6454SAndrew Thompson * Returns: 91202ac6454SAndrew Thompson * NULL: No USB template device descriptor found. 91302ac6454SAndrew Thompson * Else: Pointer to the USB template device descriptor. 91402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 915760bc48eSAndrew Thompson static const struct usb_temp_device_desc * 916760bc48eSAndrew Thompson usb2_temp_get_tdd(struct usb_device *udev) 91702ac6454SAndrew Thompson { 91802ac6454SAndrew Thompson if (udev->usb2_template_ptr == NULL) { 91902ac6454SAndrew Thompson return (NULL); 92002ac6454SAndrew Thompson } 92102ac6454SAndrew Thompson return (udev->usb2_template_ptr->tdd); 92202ac6454SAndrew Thompson } 92302ac6454SAndrew Thompson 92402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 92502ac6454SAndrew Thompson * usb2_temp_get_device_desc 92602ac6454SAndrew Thompson * 92702ac6454SAndrew Thompson * Returns: 92802ac6454SAndrew Thompson * NULL: No USB device descriptor found. 92902ac6454SAndrew Thompson * Else: Pointer to USB device descriptor. 93002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 93102ac6454SAndrew Thompson static void * 932760bc48eSAndrew Thompson usb2_temp_get_device_desc(struct usb_device *udev) 93302ac6454SAndrew Thompson { 934760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 93502ac6454SAndrew Thompson 93602ac6454SAndrew Thompson if (udev->usb2_template_ptr == NULL) { 93702ac6454SAndrew Thompson return (NULL); 93802ac6454SAndrew Thompson } 93902ac6454SAndrew Thompson dd = &udev->usb2_template_ptr->udd; 94002ac6454SAndrew Thompson if (dd->bDescriptorType != UDESC_DEVICE) { 94102ac6454SAndrew Thompson /* sanity check failed */ 94202ac6454SAndrew Thompson return (NULL); 94302ac6454SAndrew Thompson } 94402ac6454SAndrew Thompson return (dd); 94502ac6454SAndrew Thompson } 94602ac6454SAndrew Thompson 94702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 94802ac6454SAndrew Thompson * usb2_temp_get_qualifier_desc 94902ac6454SAndrew Thompson * 95002ac6454SAndrew Thompson * Returns: 95102ac6454SAndrew Thompson * NULL: No USB device_qualifier descriptor found. 95202ac6454SAndrew Thompson * Else: Pointer to USB device_qualifier descriptor. 95302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 95402ac6454SAndrew Thompson static void * 955760bc48eSAndrew Thompson usb2_temp_get_qualifier_desc(struct usb_device *udev) 95602ac6454SAndrew Thompson { 957760bc48eSAndrew Thompson struct usb_device_qualifier *dq; 95802ac6454SAndrew Thompson 95902ac6454SAndrew Thompson if (udev->usb2_template_ptr == NULL) { 96002ac6454SAndrew Thompson return (NULL); 96102ac6454SAndrew Thompson } 96202ac6454SAndrew Thompson dq = &udev->usb2_template_ptr->udq; 96302ac6454SAndrew Thompson if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { 96402ac6454SAndrew Thompson /* sanity check failed */ 96502ac6454SAndrew Thompson return (NULL); 96602ac6454SAndrew Thompson } 96702ac6454SAndrew Thompson return (dq); 96802ac6454SAndrew Thompson } 96902ac6454SAndrew Thompson 97002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 97102ac6454SAndrew Thompson * usb2_temp_get_config_desc 97202ac6454SAndrew Thompson * 97302ac6454SAndrew Thompson * Returns: 97402ac6454SAndrew Thompson * NULL: No USB config descriptor found. 97502ac6454SAndrew Thompson * Else: Pointer to USB config descriptor having index "index". 97602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 97702ac6454SAndrew Thompson static void * 978760bc48eSAndrew Thompson usb2_temp_get_config_desc(struct usb_device *udev, 97902ac6454SAndrew Thompson uint16_t *pLength, uint8_t index) 98002ac6454SAndrew Thompson { 981760bc48eSAndrew Thompson struct usb_device_descriptor *dd; 982760bc48eSAndrew Thompson struct usb_config_descriptor *cd; 98302ac6454SAndrew Thompson uint16_t temp; 98402ac6454SAndrew Thompson 98502ac6454SAndrew Thompson if (udev->usb2_template_ptr == NULL) { 98602ac6454SAndrew Thompson return (NULL); 98702ac6454SAndrew Thompson } 98802ac6454SAndrew Thompson dd = &udev->usb2_template_ptr->udd; 98902ac6454SAndrew Thompson cd = (void *)(udev->usb2_template_ptr + 1); 99002ac6454SAndrew Thompson 99102ac6454SAndrew Thompson if (index >= dd->bNumConfigurations) { 99202ac6454SAndrew Thompson /* out of range */ 99302ac6454SAndrew Thompson return (NULL); 99402ac6454SAndrew Thompson } 99502ac6454SAndrew Thompson while (index--) { 99602ac6454SAndrew Thompson if (cd->bDescriptorType != UDESC_CONFIG) { 99702ac6454SAndrew Thompson /* sanity check failed */ 99802ac6454SAndrew Thompson return (NULL); 99902ac6454SAndrew Thompson } 100002ac6454SAndrew Thompson temp = UGETW(cd->wTotalLength); 100102ac6454SAndrew Thompson cd = USB_ADD_BYTES(cd, temp); 100202ac6454SAndrew Thompson } 100302ac6454SAndrew Thompson 100402ac6454SAndrew Thompson if (pLength) { 100502ac6454SAndrew Thompson *pLength = UGETW(cd->wTotalLength); 100602ac6454SAndrew Thompson } 100702ac6454SAndrew Thompson return (cd); 100802ac6454SAndrew Thompson } 100902ac6454SAndrew Thompson 101002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 101102ac6454SAndrew Thompson * usb2_temp_get_vendor_desc 101202ac6454SAndrew Thompson * 101302ac6454SAndrew Thompson * Returns: 101402ac6454SAndrew Thompson * NULL: No vendor descriptor found. 101502ac6454SAndrew Thompson * Else: Pointer to a vendor descriptor. 101602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 101702ac6454SAndrew Thompson static const void * 1018760bc48eSAndrew Thompson usb2_temp_get_vendor_desc(struct usb_device *udev, 1019760bc48eSAndrew Thompson const struct usb_device_request *req) 102002ac6454SAndrew Thompson { 1021760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 102202ac6454SAndrew Thompson 102302ac6454SAndrew Thompson tdd = usb2_temp_get_tdd(udev); 102402ac6454SAndrew Thompson if (tdd == NULL) { 102502ac6454SAndrew Thompson return (NULL); 102602ac6454SAndrew Thompson } 102702ac6454SAndrew Thompson if (tdd->getVendorDesc == NULL) { 102802ac6454SAndrew Thompson return (NULL); 102902ac6454SAndrew Thompson } 103002ac6454SAndrew Thompson return ((tdd->getVendorDesc) (req)); 103102ac6454SAndrew Thompson } 103202ac6454SAndrew Thompson 103302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 103402ac6454SAndrew Thompson * usb2_temp_get_string_desc 103502ac6454SAndrew Thompson * 103602ac6454SAndrew Thompson * Returns: 103702ac6454SAndrew Thompson * NULL: No string descriptor found. 103802ac6454SAndrew Thompson * Else: Pointer to a string descriptor. 103902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 104002ac6454SAndrew Thompson static const void * 1041760bc48eSAndrew Thompson usb2_temp_get_string_desc(struct usb_device *udev, 104202ac6454SAndrew Thompson uint16_t lang_id, uint8_t string_index) 104302ac6454SAndrew Thompson { 1044760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd; 104502ac6454SAndrew Thompson 104602ac6454SAndrew Thompson tdd = usb2_temp_get_tdd(udev); 104702ac6454SAndrew Thompson if (tdd == NULL) { 104802ac6454SAndrew Thompson return (NULL); 104902ac6454SAndrew Thompson } 105002ac6454SAndrew Thompson if (tdd->getStringDesc == NULL) { 105102ac6454SAndrew Thompson return (NULL); 105202ac6454SAndrew Thompson } 105302ac6454SAndrew Thompson return ((tdd->getStringDesc) (lang_id, string_index)); 105402ac6454SAndrew Thompson } 105502ac6454SAndrew Thompson 105602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 105702ac6454SAndrew Thompson * usb2_temp_get_hub_desc 105802ac6454SAndrew Thompson * 105902ac6454SAndrew Thompson * Returns: 106002ac6454SAndrew Thompson * NULL: No USB HUB descriptor found. 106102ac6454SAndrew Thompson * Else: Pointer to a USB HUB descriptor. 106202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 106302ac6454SAndrew Thompson static const void * 1064760bc48eSAndrew Thompson usb2_temp_get_hub_desc(struct usb_device *udev) 106502ac6454SAndrew Thompson { 106602ac6454SAndrew Thompson return (NULL); /* needs to be implemented */ 106702ac6454SAndrew Thompson } 106802ac6454SAndrew Thompson 106902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 107002ac6454SAndrew Thompson * usb2_temp_get_desc 107102ac6454SAndrew Thompson * 107202ac6454SAndrew Thompson * This function is a demultiplexer for local USB device side control 107302ac6454SAndrew Thompson * endpoint requests. 107402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1075e0a69b51SAndrew Thompson static usb_error_t 1076760bc48eSAndrew Thompson usb2_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, 107702ac6454SAndrew Thompson const void **pPtr, uint16_t *pLength) 107802ac6454SAndrew Thompson { 107902ac6454SAndrew Thompson const uint8_t *buf; 108002ac6454SAndrew Thompson uint16_t len; 108102ac6454SAndrew Thompson 108202ac6454SAndrew Thompson buf = NULL; 108302ac6454SAndrew Thompson len = 0; 108402ac6454SAndrew Thompson 108502ac6454SAndrew Thompson switch (req->bmRequestType) { 108602ac6454SAndrew Thompson case UT_READ_DEVICE: 108702ac6454SAndrew Thompson switch (req->bRequest) { 108802ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 108902ac6454SAndrew Thompson goto tr_handle_get_descriptor; 109002ac6454SAndrew Thompson default: 109102ac6454SAndrew Thompson goto tr_stalled; 109202ac6454SAndrew Thompson } 109302ac6454SAndrew Thompson break; 109402ac6454SAndrew Thompson case UT_READ_CLASS_DEVICE: 109502ac6454SAndrew Thompson switch (req->bRequest) { 109602ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 109702ac6454SAndrew Thompson goto tr_handle_get_class_descriptor; 109802ac6454SAndrew Thompson default: 109902ac6454SAndrew Thompson goto tr_stalled; 110002ac6454SAndrew Thompson } 110102ac6454SAndrew Thompson break; 110202ac6454SAndrew Thompson case UT_READ_VENDOR_DEVICE: 110302ac6454SAndrew Thompson case UT_READ_VENDOR_OTHER: 110402ac6454SAndrew Thompson buf = usb2_temp_get_vendor_desc(udev, req); 110502ac6454SAndrew Thompson goto tr_valid; 110602ac6454SAndrew Thompson default: 110702ac6454SAndrew Thompson goto tr_stalled; 110802ac6454SAndrew Thompson } 110902ac6454SAndrew Thompson 111002ac6454SAndrew Thompson tr_handle_get_descriptor: 111102ac6454SAndrew Thompson switch (req->wValue[1]) { 111202ac6454SAndrew Thompson case UDESC_DEVICE: 111302ac6454SAndrew Thompson if (req->wValue[0]) { 111402ac6454SAndrew Thompson goto tr_stalled; 111502ac6454SAndrew Thompson } 111602ac6454SAndrew Thompson buf = usb2_temp_get_device_desc(udev); 111702ac6454SAndrew Thompson goto tr_valid; 111802ac6454SAndrew Thompson case UDESC_DEVICE_QUALIFIER: 111902ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 112002ac6454SAndrew Thompson goto tr_stalled; 112102ac6454SAndrew Thompson } 112202ac6454SAndrew Thompson if (req->wValue[0]) { 112302ac6454SAndrew Thompson goto tr_stalled; 112402ac6454SAndrew Thompson } 112502ac6454SAndrew Thompson buf = usb2_temp_get_qualifier_desc(udev); 112602ac6454SAndrew Thompson goto tr_valid; 112702ac6454SAndrew Thompson case UDESC_OTHER_SPEED_CONFIGURATION: 112802ac6454SAndrew Thompson if (udev->speed != USB_SPEED_HIGH) { 112902ac6454SAndrew Thompson goto tr_stalled; 113002ac6454SAndrew Thompson } 113102ac6454SAndrew Thompson case UDESC_CONFIG: 113202ac6454SAndrew Thompson buf = usb2_temp_get_config_desc(udev, 113302ac6454SAndrew Thompson &len, req->wValue[0]); 113402ac6454SAndrew Thompson goto tr_valid; 113502ac6454SAndrew Thompson case UDESC_STRING: 113602ac6454SAndrew Thompson buf = usb2_temp_get_string_desc(udev, 113702ac6454SAndrew Thompson UGETW(req->wIndex), req->wValue[0]); 113802ac6454SAndrew Thompson goto tr_valid; 113902ac6454SAndrew Thompson default: 114002ac6454SAndrew Thompson goto tr_stalled; 114102ac6454SAndrew Thompson } 114202ac6454SAndrew Thompson goto tr_stalled; 114302ac6454SAndrew Thompson 114402ac6454SAndrew Thompson tr_handle_get_class_descriptor: 114502ac6454SAndrew Thompson if (req->wValue[0]) { 114602ac6454SAndrew Thompson goto tr_stalled; 114702ac6454SAndrew Thompson } 114802ac6454SAndrew Thompson buf = usb2_temp_get_hub_desc(udev); 114902ac6454SAndrew Thompson goto tr_valid; 115002ac6454SAndrew Thompson 115102ac6454SAndrew Thompson tr_valid: 115202ac6454SAndrew Thompson if (buf == NULL) { 115302ac6454SAndrew Thompson goto tr_stalled; 115402ac6454SAndrew Thompson } 115502ac6454SAndrew Thompson if (len == 0) { 115602ac6454SAndrew Thompson len = buf[0]; 115702ac6454SAndrew Thompson } 115802ac6454SAndrew Thompson *pPtr = buf; 115902ac6454SAndrew Thompson *pLength = len; 1160459d369eSAndrew Thompson return (0); /* success */ 116102ac6454SAndrew Thompson 116202ac6454SAndrew Thompson tr_stalled: 116302ac6454SAndrew Thompson *pPtr = NULL; 116402ac6454SAndrew Thompson *pLength = 0; 1165459d369eSAndrew Thompson return (0); /* we ignore failures */ 116602ac6454SAndrew Thompson } 116702ac6454SAndrew Thompson 116802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1169760bc48eSAndrew Thompson * usb_temp_setup 117002ac6454SAndrew Thompson * 117102ac6454SAndrew Thompson * This function generates USB descriptors according to the given USB 117202ac6454SAndrew Thompson * template device descriptor. It will also try to figure out the best 117302ac6454SAndrew Thompson * matching endpoint addresses using the hardware endpoint profiles. 117402ac6454SAndrew Thompson * 117502ac6454SAndrew Thompson * Returns: 117602ac6454SAndrew Thompson * 0: Success 117702ac6454SAndrew Thompson * Else: Failure 117802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1179e0a69b51SAndrew Thompson static usb_error_t 1180760bc48eSAndrew Thompson usb_temp_setup(struct usb_device *udev, 1181760bc48eSAndrew Thompson const struct usb_temp_device_desc *tdd) 118202ac6454SAndrew Thompson { 1183760bc48eSAndrew Thompson struct usb_temp_setup *uts; 118402ac6454SAndrew Thompson void *buf; 118502ac6454SAndrew Thompson uint8_t n; 118602ac6454SAndrew Thompson 118702ac6454SAndrew Thompson if (tdd == NULL) { 118802ac6454SAndrew Thompson /* be NULL safe */ 118902ac6454SAndrew Thompson return (0); 119002ac6454SAndrew Thompson } 119102ac6454SAndrew Thompson uts = udev->bus->scratch[0].temp_setup; 119202ac6454SAndrew Thompson 119302ac6454SAndrew Thompson bzero(uts, sizeof(*uts)); 119402ac6454SAndrew Thompson 11958d2dd5ddSAndrew Thompson uts->usb_speed = udev->speed; 119602ac6454SAndrew Thompson uts->self_powered = udev->flags.self_powered; 119702ac6454SAndrew Thompson 119802ac6454SAndrew Thompson /* first pass */ 119902ac6454SAndrew Thompson 120002ac6454SAndrew Thompson usb2_make_device_desc(uts, tdd); 120102ac6454SAndrew Thompson 120202ac6454SAndrew Thompson if (uts->err) { 120302ac6454SAndrew Thompson /* some error happened */ 120402ac6454SAndrew Thompson return (uts->err); 120502ac6454SAndrew Thompson } 120602ac6454SAndrew Thompson /* sanity check */ 120702ac6454SAndrew Thompson if (uts->size == 0) { 120802ac6454SAndrew Thompson return (USB_ERR_INVAL); 120902ac6454SAndrew Thompson } 121002ac6454SAndrew Thompson /* allocate zeroed memory */ 121102ac6454SAndrew Thompson uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO); 121202ac6454SAndrew Thompson if (uts->buf == NULL) { 121302ac6454SAndrew Thompson /* could not allocate memory */ 121402ac6454SAndrew Thompson return (USB_ERR_NOMEM); 121502ac6454SAndrew Thompson } 121602ac6454SAndrew Thompson /* second pass */ 121702ac6454SAndrew Thompson 121802ac6454SAndrew Thompson uts->size = 0; 121902ac6454SAndrew Thompson 122002ac6454SAndrew Thompson usb2_make_device_desc(uts, tdd); 122102ac6454SAndrew Thompson 122202ac6454SAndrew Thompson /* 122302ac6454SAndrew Thompson * Store a pointer to our descriptors: 122402ac6454SAndrew Thompson */ 122502ac6454SAndrew Thompson udev->usb2_template_ptr = uts->buf; 122602ac6454SAndrew Thompson 122702ac6454SAndrew Thompson if (uts->err) { 122802ac6454SAndrew Thompson /* some error happened during second pass */ 122902ac6454SAndrew Thompson goto error; 123002ac6454SAndrew Thompson } 123102ac6454SAndrew Thompson /* 123202ac6454SAndrew Thompson * Resolve all endpoint addresses ! 123302ac6454SAndrew Thompson */ 123402ac6454SAndrew Thompson buf = usb2_temp_get_device_desc(udev); 123502ac6454SAndrew Thompson uts->err = usb2_hw_ep_resolve(udev, buf); 123602ac6454SAndrew Thompson if (uts->err) { 123702ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 123802ac6454SAndrew Thompson "Device Descriptor, error = %s\n", 123902ac6454SAndrew Thompson usb2_errstr(uts->err)); 124002ac6454SAndrew Thompson goto error; 124102ac6454SAndrew Thompson } 124202ac6454SAndrew Thompson for (n = 0;; n++) { 124302ac6454SAndrew Thompson 124402ac6454SAndrew Thompson buf = usb2_temp_get_config_desc(udev, NULL, n); 124502ac6454SAndrew Thompson if (buf == NULL) { 124602ac6454SAndrew Thompson break; 124702ac6454SAndrew Thompson } 124802ac6454SAndrew Thompson uts->err = usb2_hw_ep_resolve(udev, buf); 124902ac6454SAndrew Thompson if (uts->err) { 125002ac6454SAndrew Thompson DPRINTFN(0, "Could not resolve endpoints for " 125102ac6454SAndrew Thompson "Config Descriptor %u, error = %s\n", n, 125202ac6454SAndrew Thompson usb2_errstr(uts->err)); 125302ac6454SAndrew Thompson goto error; 125402ac6454SAndrew Thompson } 125502ac6454SAndrew Thompson } 125602ac6454SAndrew Thompson return (uts->err); 125702ac6454SAndrew Thompson 125802ac6454SAndrew Thompson error: 125902ac6454SAndrew Thompson usb2_temp_unsetup(udev); 126002ac6454SAndrew Thompson return (uts->err); 126102ac6454SAndrew Thompson } 126202ac6454SAndrew Thompson 126302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 126402ac6454SAndrew Thompson * usb2_temp_unsetup 126502ac6454SAndrew Thompson * 126602ac6454SAndrew Thompson * This function frees any memory associated with the currently 126702ac6454SAndrew Thompson * setup template, if any. 126802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 126902ac6454SAndrew Thompson static void 1270760bc48eSAndrew Thompson usb2_temp_unsetup(struct usb_device *udev) 127102ac6454SAndrew Thompson { 127202ac6454SAndrew Thompson if (udev->usb2_template_ptr) { 127302ac6454SAndrew Thompson 127402ac6454SAndrew Thompson free(udev->usb2_template_ptr, M_USB); 127502ac6454SAndrew Thompson 127602ac6454SAndrew Thompson udev->usb2_template_ptr = NULL; 127702ac6454SAndrew Thompson } 127802ac6454SAndrew Thompson } 127902ac6454SAndrew Thompson 1280e0a69b51SAndrew Thompson static usb_error_t 1281760bc48eSAndrew Thompson usb2_temp_setup_by_index(struct usb_device *udev, uint16_t index) 128202ac6454SAndrew Thompson { 1283e0a69b51SAndrew Thompson usb_error_t err; 128402ac6454SAndrew Thompson 128502ac6454SAndrew Thompson switch (index) { 128602ac6454SAndrew Thompson case 0: 1287760bc48eSAndrew Thompson err = usb_temp_setup(udev, &usb2_template_msc); 128802ac6454SAndrew Thompson break; 128902ac6454SAndrew Thompson case 1: 1290760bc48eSAndrew Thompson err = usb_temp_setup(udev, &usb2_template_cdce); 129102ac6454SAndrew Thompson break; 129202ac6454SAndrew Thompson case 2: 1293760bc48eSAndrew Thompson err = usb_temp_setup(udev, &usb2_template_mtp); 129402ac6454SAndrew Thompson break; 129502ac6454SAndrew Thompson default: 129602ac6454SAndrew Thompson return (USB_ERR_INVAL); 129702ac6454SAndrew Thompson } 129802ac6454SAndrew Thompson 129902ac6454SAndrew Thompson return (err); 130002ac6454SAndrew Thompson } 130102ac6454SAndrew Thompson 130202ac6454SAndrew Thompson static void 130302ac6454SAndrew Thompson usb2_temp_init(void *arg) 130402ac6454SAndrew Thompson { 130502ac6454SAndrew Thompson /* register our functions */ 130602ac6454SAndrew Thompson usb2_temp_get_desc_p = &usb2_temp_get_desc; 130702ac6454SAndrew Thompson usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index; 130802ac6454SAndrew Thompson usb2_temp_unsetup_p = &usb2_temp_unsetup; 130902ac6454SAndrew Thompson } 131002ac6454SAndrew Thompson 131102ac6454SAndrew Thompson SYSINIT(usb2_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_temp_init, NULL); 131202ac6454SAndrew Thompson SYSUNINIT(usb2_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb2_temp_unload, NULL); 1313