1184610Salfred/* $FreeBSD$ */ 2184610Salfred/*- 3184610Salfred * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27184610Salfred/* 28184610Salfred * This file contains sub-routines to build up USB descriptors from 29184610Salfred * USB templates. 30184610Salfred */ 31184610Salfred 32194677Sthompsa#include <sys/stdint.h> 33194677Sthompsa#include <sys/stddef.h> 34194677Sthompsa#include <sys/param.h> 35194677Sthompsa#include <sys/queue.h> 36194677Sthompsa#include <sys/types.h> 37194677Sthompsa#include <sys/systm.h> 38194677Sthompsa#include <sys/kernel.h> 39194677Sthompsa#include <sys/bus.h> 40194677Sthompsa#include <sys/module.h> 41194677Sthompsa#include <sys/lock.h> 42194677Sthompsa#include <sys/mutex.h> 43194677Sthompsa#include <sys/condvar.h> 44194677Sthompsa#include <sys/sysctl.h> 45194677Sthompsa#include <sys/sx.h> 46194677Sthompsa#include <sys/unistd.h> 47194677Sthompsa#include <sys/callout.h> 48194677Sthompsa#include <sys/malloc.h> 49194677Sthompsa#include <sys/priv.h> 50194677Sthompsa 51188942Sthompsa#include <dev/usb/usb.h> 52223467Shselasky#include <dev/usb/usb_ioctl.h> 53194677Sthompsa#include <dev/usb/usbdi.h> 54194677Sthompsa#include <dev/usb/usbdi_util.h> 55194677Sthompsa#include "usbdevs.h" 56194677Sthompsa 57188942Sthompsa#include <dev/usb/usb_cdc.h> 58188942Sthompsa#include <dev/usb/usb_core.h> 59194677Sthompsa#include <dev/usb/usb_dynamic.h> 60188942Sthompsa#include <dev/usb/usb_busdma.h> 61188942Sthompsa#include <dev/usb/usb_process.h> 62188942Sthompsa#include <dev/usb/usb_device.h> 63184610Salfred 64194677Sthompsa#define USB_DEBUG_VAR usb_debug 65194677Sthompsa#include <dev/usb/usb_debug.h> 66194677Sthompsa 67188942Sthompsa#include <dev/usb/usb_controller.h> 68188942Sthompsa#include <dev/usb/usb_bus.h> 69188942Sthompsa#include <dev/usb/template/usb_template.h> 70184610Salfred 71188942SthompsaMODULE_DEPEND(usb_template, usb, 1, 1, 1); 72188942SthompsaMODULE_VERSION(usb_template, 1); 73184610Salfred 74184610Salfred/* function prototypes */ 75184610Salfred 76194228Sthompsastatic void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); 77194228Sthompsastatic void usb_make_endpoint_desc(struct usb_temp_setup *, 78192984Sthompsa const struct usb_temp_endpoint_desc *); 79194228Sthompsastatic void usb_make_interface_desc(struct usb_temp_setup *, 80192984Sthompsa const struct usb_temp_interface_desc *); 81194228Sthompsastatic void usb_make_config_desc(struct usb_temp_setup *, 82192984Sthompsa const struct usb_temp_config_desc *); 83194228Sthompsastatic void usb_make_device_desc(struct usb_temp_setup *, 84192984Sthompsa const struct usb_temp_device_desc *); 85194228Sthompsastatic uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, 86185948Sthompsa uint8_t); 87194228Sthompsastatic uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, 88192984Sthompsa struct usb_hw_ep_scratch_sub *, uint8_t); 89194228Sthompsastatic uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, 90185948Sthompsa uint8_t); 91194228Sthompsastatic usb_error_t usb_hw_ep_resolve(struct usb_device *, 92192984Sthompsa struct usb_descriptor *); 93194228Sthompsastatic const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); 94194228Sthompsastatic void *usb_temp_get_device_desc(struct usb_device *); 95194228Sthompsastatic void *usb_temp_get_qualifier_desc(struct usb_device *); 96194228Sthompsastatic void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, 97185948Sthompsa uint8_t); 98194228Sthompsastatic const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, 99185948Sthompsa uint8_t); 100194228Sthompsastatic const void *usb_temp_get_vendor_desc(struct usb_device *, 101205030Sthompsa const struct usb_device_request *, uint16_t *plen); 102194228Sthompsastatic const void *usb_temp_get_hub_desc(struct usb_device *); 103194228Sthompsastatic usb_error_t usb_temp_get_desc(struct usb_device *, 104192984Sthompsa struct usb_device_request *, const void **, uint16_t *); 105194228Sthompsastatic usb_error_t usb_temp_setup_by_index(struct usb_device *, 106185948Sthompsa uint16_t index); 107194228Sthompsastatic void usb_temp_init(void *); 108184610Salfred 109184610Salfred/*------------------------------------------------------------------------* 110194228Sthompsa * usb_make_raw_desc 111184610Salfred * 112184610Salfred * This function will insert a raw USB descriptor into the generated 113184610Salfred * USB configuration. 114184610Salfred *------------------------------------------------------------------------*/ 115184610Salfredstatic void 116194228Sthompsausb_make_raw_desc(struct usb_temp_setup *temp, 117184610Salfred const uint8_t *raw) 118184610Salfred{ 119184610Salfred void *dst; 120184610Salfred uint8_t len; 121184610Salfred 122184610Salfred /* 123184610Salfred * The first byte of any USB descriptor gives the length. 124184610Salfred */ 125184610Salfred if (raw) { 126184610Salfred len = raw[0]; 127184610Salfred if (temp->buf) { 128184610Salfred dst = USB_ADD_BYTES(temp->buf, temp->size); 129218475Shselasky memcpy(dst, raw, len); 130184610Salfred 131184610Salfred /* check if we have got a CDC union descriptor */ 132184610Salfred 133192984Sthompsa if ((raw[0] >= sizeof(struct usb_cdc_union_descriptor)) && 134184610Salfred (raw[1] == UDESC_CS_INTERFACE) && 135184610Salfred (raw[2] == UDESCSUB_CDC_UNION)) { 136192984Sthompsa struct usb_cdc_union_descriptor *ud = (void *)dst; 137184610Salfred 138184610Salfred /* update the interface numbers */ 139184610Salfred 140184610Salfred ud->bMasterInterface += 141184610Salfred temp->bInterfaceNumber; 142184610Salfred ud->bSlaveInterface[0] += 143184610Salfred temp->bInterfaceNumber; 144184610Salfred } 145223467Shselasky 146223467Shselasky /* check if we have got an interface association descriptor */ 147223467Shselasky 148223467Shselasky if ((raw[0] >= sizeof(struct usb_interface_assoc_descriptor)) && 149223467Shselasky (raw[1] == UDESC_IFACE_ASSOC)) { 150223467Shselasky struct usb_interface_assoc_descriptor *iad = (void *)dst; 151223467Shselasky 152223467Shselasky /* update the interface number */ 153223467Shselasky 154223467Shselasky iad->bFirstInterface += 155223467Shselasky temp->bInterfaceNumber; 156223467Shselasky } 157223467Shselasky 158223467Shselasky /* check if we have got a call management descriptor */ 159223467Shselasky 160223467Shselasky if ((raw[0] >= sizeof(struct usb_cdc_cm_descriptor)) && 161223467Shselasky (raw[1] == UDESC_CS_INTERFACE) && 162223467Shselasky (raw[2] == UDESCSUB_CDC_CM)) { 163223467Shselasky struct usb_cdc_cm_descriptor *ccd = (void *)dst; 164223467Shselasky 165223467Shselasky /* update the interface number */ 166223467Shselasky 167223467Shselasky ccd->bDataInterface += 168223467Shselasky temp->bInterfaceNumber; 169223467Shselasky } 170184610Salfred } 171184610Salfred temp->size += len; 172184610Salfred } 173184610Salfred} 174184610Salfred 175184610Salfred/*------------------------------------------------------------------------* 176194228Sthompsa * usb_make_endpoint_desc 177184610Salfred * 178184610Salfred * This function will generate an USB endpoint descriptor from the 179184610Salfred * given USB template endpoint descriptor, which will be inserted into 180184610Salfred * the USB configuration. 181184610Salfred *------------------------------------------------------------------------*/ 182184610Salfredstatic void 183194228Sthompsausb_make_endpoint_desc(struct usb_temp_setup *temp, 184192984Sthompsa const struct usb_temp_endpoint_desc *ted) 185184610Salfred{ 186192984Sthompsa struct usb_endpoint_descriptor *ed; 187184610Salfred const void **rd; 188184610Salfred uint16_t old_size; 189184610Salfred uint16_t mps; 190205033Sthompsa uint8_t ea; /* Endpoint Address */ 191205033Sthompsa uint8_t et; /* Endpiont Type */ 192184610Salfred 193184610Salfred /* Reserve memory */ 194184610Salfred old_size = temp->size; 195184610Salfred 196205033Sthompsa ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); 197205033Sthompsa et = (ted->bmAttributes & UE_XFERTYPE); 198205033Sthompsa 199205033Sthompsa if (et == UE_ISOCHRONOUS) { 200205033Sthompsa /* account for extra byte fields */ 201205033Sthompsa temp->size += sizeof(*ed) + 2; 202205033Sthompsa } else { 203205033Sthompsa temp->size += sizeof(*ed); 204205033Sthompsa } 205205033Sthompsa 206184610Salfred /* Scan all Raw Descriptors first */ 207184610Salfred rd = ted->ppRawDesc; 208184610Salfred if (rd) { 209184610Salfred while (*rd) { 210194228Sthompsa usb_make_raw_desc(temp, *rd); 211184610Salfred rd++; 212184610Salfred } 213184610Salfred } 214184610Salfred if (ted->pPacketSize == NULL) { 215184610Salfred /* not initialized */ 216184610Salfred temp->err = USB_ERR_INVAL; 217184610Salfred return; 218184610Salfred } 219192500Sthompsa mps = ted->pPacketSize->mps[temp->usb_speed]; 220184610Salfred if (mps == 0) { 221184610Salfred /* not initialized */ 222184610Salfred temp->err = USB_ERR_INVAL; 223184610Salfred return; 224184610Salfred } else if (mps == UE_ZERO_MPS) { 225184610Salfred /* escape for Zero Max Packet Size */ 226184610Salfred mps = 0; 227184610Salfred } 228184610Salfred 229184610Salfred /* 230184610Salfred * Fill out the real USB endpoint descriptor 231184610Salfred * in case there is a buffer present: 232184610Salfred */ 233184610Salfred if (temp->buf) { 234184610Salfred ed = USB_ADD_BYTES(temp->buf, old_size); 235205033Sthompsa if (et == UE_ISOCHRONOUS) 236205033Sthompsa ed->bLength = sizeof(*ed) + 2; 237205033Sthompsa else 238205033Sthompsa ed->bLength = sizeof(*ed); 239184610Salfred ed->bDescriptorType = UDESC_ENDPOINT; 240184610Salfred ed->bEndpointAddress = ea; 241184610Salfred ed->bmAttributes = ted->bmAttributes; 242184610Salfred USETW(ed->wMaxPacketSize, mps); 243184610Salfred 244184610Salfred /* setup bInterval parameter */ 245184610Salfred 246184610Salfred if (ted->pIntervals && 247192500Sthompsa ted->pIntervals->bInterval[temp->usb_speed]) { 248184610Salfred ed->bInterval = 249192500Sthompsa ted->pIntervals->bInterval[temp->usb_speed]; 250184610Salfred } else { 251184610Salfred switch (et) { 252184610Salfred case UE_BULK: 253184610Salfred case UE_CONTROL: 254184610Salfred ed->bInterval = 0; /* not used */ 255184610Salfred break; 256184610Salfred case UE_INTERRUPT: 257192500Sthompsa switch (temp->usb_speed) { 258184610Salfred case USB_SPEED_LOW: 259184610Salfred case USB_SPEED_FULL: 260184610Salfred ed->bInterval = 1; /* 1 ms */ 261184610Salfred break; 262184610Salfred default: 263229103Shselasky ed->bInterval = 4; /* 1 ms */ 264184610Salfred break; 265184610Salfred } 266184610Salfred break; 267184610Salfred default: /* UE_ISOCHRONOUS */ 268192500Sthompsa switch (temp->usb_speed) { 269184610Salfred case USB_SPEED_LOW: 270184610Salfred case USB_SPEED_FULL: 271184610Salfred ed->bInterval = 1; /* 1 ms */ 272184610Salfred break; 273184610Salfred default: 274184610Salfred ed->bInterval = 1; /* 125 us */ 275184610Salfred break; 276184610Salfred } 277184610Salfred break; 278184610Salfred } 279184610Salfred } 280184610Salfred } 281184610Salfred temp->bNumEndpoints++; 282184610Salfred} 283184610Salfred 284184610Salfred/*------------------------------------------------------------------------* 285194228Sthompsa * usb_make_interface_desc 286184610Salfred * 287184610Salfred * This function will generate an USB interface descriptor from the 288184610Salfred * given USB template interface descriptor, which will be inserted 289184610Salfred * into the USB configuration. 290184610Salfred *------------------------------------------------------------------------*/ 291184610Salfredstatic void 292194228Sthompsausb_make_interface_desc(struct usb_temp_setup *temp, 293192984Sthompsa const struct usb_temp_interface_desc *tid) 294184610Salfred{ 295192984Sthompsa struct usb_interface_descriptor *id; 296192984Sthompsa const struct usb_temp_endpoint_desc **ted; 297184610Salfred const void **rd; 298184610Salfred uint16_t old_size; 299184610Salfred 300184610Salfred /* Reserve memory */ 301184610Salfred 302184610Salfred old_size = temp->size; 303184610Salfred temp->size += sizeof(*id); 304184610Salfred 305184610Salfred /* Update interface and alternate interface numbers */ 306184610Salfred 307184610Salfred if (tid->isAltInterface == 0) { 308184610Salfred temp->bAlternateSetting = 0; 309184610Salfred temp->bInterfaceNumber++; 310184610Salfred } else { 311184610Salfred temp->bAlternateSetting++; 312184610Salfred } 313184610Salfred 314184610Salfred /* Scan all Raw Descriptors first */ 315184610Salfred 316184610Salfred rd = tid->ppRawDesc; 317184610Salfred 318184610Salfred if (rd) { 319184610Salfred while (*rd) { 320194228Sthompsa usb_make_raw_desc(temp, *rd); 321184610Salfred rd++; 322184610Salfred } 323184610Salfred } 324184610Salfred /* Reset some counters */ 325184610Salfred 326184610Salfred temp->bNumEndpoints = 0; 327184610Salfred 328184610Salfred /* Scan all Endpoint Descriptors second */ 329184610Salfred 330184610Salfred ted = tid->ppEndpoints; 331184610Salfred if (ted) { 332184610Salfred while (*ted) { 333194228Sthompsa usb_make_endpoint_desc(temp, *ted); 334184610Salfred ted++; 335184610Salfred } 336184610Salfred } 337184610Salfred /* 338184610Salfred * Fill out the real USB interface descriptor 339184610Salfred * in case there is a buffer present: 340184610Salfred */ 341184610Salfred if (temp->buf) { 342184610Salfred id = USB_ADD_BYTES(temp->buf, old_size); 343184610Salfred id->bLength = sizeof(*id); 344184610Salfred id->bDescriptorType = UDESC_INTERFACE; 345184610Salfred id->bInterfaceNumber = temp->bInterfaceNumber; 346184610Salfred id->bAlternateSetting = temp->bAlternateSetting; 347184610Salfred id->bNumEndpoints = temp->bNumEndpoints; 348184610Salfred id->bInterfaceClass = tid->bInterfaceClass; 349184610Salfred id->bInterfaceSubClass = tid->bInterfaceSubClass; 350184610Salfred id->bInterfaceProtocol = tid->bInterfaceProtocol; 351184610Salfred id->iInterface = tid->iInterface; 352184610Salfred } 353184610Salfred} 354184610Salfred 355184610Salfred/*------------------------------------------------------------------------* 356194228Sthompsa * usb_make_config_desc 357184610Salfred * 358184610Salfred * This function will generate an USB config descriptor from the given 359184610Salfred * USB template config descriptor, which will be inserted into the USB 360184610Salfred * configuration. 361184610Salfred *------------------------------------------------------------------------*/ 362184610Salfredstatic void 363194228Sthompsausb_make_config_desc(struct usb_temp_setup *temp, 364192984Sthompsa const struct usb_temp_config_desc *tcd) 365184610Salfred{ 366192984Sthompsa struct usb_config_descriptor *cd; 367192984Sthompsa const struct usb_temp_interface_desc **tid; 368184610Salfred uint16_t old_size; 369184610Salfred 370184610Salfred /* Reserve memory */ 371184610Salfred 372184610Salfred old_size = temp->size; 373184610Salfred temp->size += sizeof(*cd); 374184610Salfred 375184610Salfred /* Reset some counters */ 376184610Salfred 377235000Shselasky temp->bInterfaceNumber = 0xFF; 378184610Salfred temp->bAlternateSetting = 0; 379184610Salfred 380184610Salfred /* Scan all the USB interfaces */ 381184610Salfred 382184610Salfred tid = tcd->ppIfaceDesc; 383184610Salfred if (tid) { 384184610Salfred while (*tid) { 385194228Sthompsa usb_make_interface_desc(temp, *tid); 386184610Salfred tid++; 387184610Salfred } 388184610Salfred } 389184610Salfred /* 390184610Salfred * Fill out the real USB config descriptor 391184610Salfred * in case there is a buffer present: 392184610Salfred */ 393184610Salfred if (temp->buf) { 394184610Salfred cd = USB_ADD_BYTES(temp->buf, old_size); 395184610Salfred 396184610Salfred /* compute total size */ 397184610Salfred old_size = temp->size - old_size; 398184610Salfred 399184610Salfred cd->bLength = sizeof(*cd); 400184610Salfred cd->bDescriptorType = UDESC_CONFIG; 401184610Salfred USETW(cd->wTotalLength, old_size); 402184610Salfred cd->bNumInterface = temp->bInterfaceNumber + 1; 403184610Salfred cd->bConfigurationValue = temp->bConfigurationValue; 404184610Salfred cd->iConfiguration = tcd->iConfiguration; 405184610Salfred cd->bmAttributes = tcd->bmAttributes; 406184610Salfred cd->bMaxPower = tcd->bMaxPower; 407184610Salfred cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); 408184610Salfred 409184610Salfred if (temp->self_powered) { 410184610Salfred cd->bmAttributes |= UC_SELF_POWERED; 411184610Salfred } else { 412184610Salfred cd->bmAttributes &= ~UC_SELF_POWERED; 413184610Salfred } 414184610Salfred } 415184610Salfred} 416184610Salfred 417184610Salfred/*------------------------------------------------------------------------* 418194228Sthompsa * usb_make_device_desc 419184610Salfred * 420184610Salfred * This function will generate an USB device descriptor from the 421184610Salfred * given USB template device descriptor. 422184610Salfred *------------------------------------------------------------------------*/ 423184610Salfredstatic void 424194228Sthompsausb_make_device_desc(struct usb_temp_setup *temp, 425192984Sthompsa const struct usb_temp_device_desc *tdd) 426184610Salfred{ 427192984Sthompsa struct usb_temp_data *utd; 428192984Sthompsa const struct usb_temp_config_desc **tcd; 429184610Salfred uint16_t old_size; 430184610Salfred 431184610Salfred /* Reserve memory */ 432184610Salfred 433184610Salfred old_size = temp->size; 434184610Salfred temp->size += sizeof(*utd); 435184610Salfred 436184610Salfred /* Scan all the USB configs */ 437184610Salfred 438184610Salfred temp->bConfigurationValue = 1; 439184610Salfred tcd = tdd->ppConfigDesc; 440184610Salfred if (tcd) { 441184610Salfred while (*tcd) { 442194228Sthompsa usb_make_config_desc(temp, *tcd); 443184610Salfred temp->bConfigurationValue++; 444184610Salfred tcd++; 445184610Salfred } 446184610Salfred } 447184610Salfred /* 448184610Salfred * Fill out the real USB device descriptor 449184610Salfred * in case there is a buffer present: 450184610Salfred */ 451184610Salfred 452184610Salfred if (temp->buf) { 453184610Salfred utd = USB_ADD_BYTES(temp->buf, old_size); 454184610Salfred 455184610Salfred /* Store a pointer to our template device descriptor */ 456184610Salfred utd->tdd = tdd; 457184610Salfred 458184610Salfred /* Fill out USB device descriptor */ 459184610Salfred utd->udd.bLength = sizeof(utd->udd); 460184610Salfred utd->udd.bDescriptorType = UDESC_DEVICE; 461184610Salfred utd->udd.bDeviceClass = tdd->bDeviceClass; 462184610Salfred utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; 463184610Salfred utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; 464184610Salfred USETW(utd->udd.idVendor, tdd->idVendor); 465184610Salfred USETW(utd->udd.idProduct, tdd->idProduct); 466184610Salfred USETW(utd->udd.bcdDevice, tdd->bcdDevice); 467184610Salfred utd->udd.iManufacturer = tdd->iManufacturer; 468184610Salfred utd->udd.iProduct = tdd->iProduct; 469184610Salfred utd->udd.iSerialNumber = tdd->iSerialNumber; 470184610Salfred utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; 471184610Salfred 472184610Salfred /* 473184610Salfred * Fill out the USB device qualifier. Pretend that we 474184610Salfred * don't support any other speeds by setting 475184610Salfred * "bNumConfigurations" equal to zero. That saves us 476184610Salfred * generating an extra set of configuration 477184610Salfred * descriptors. 478184610Salfred */ 479184610Salfred utd->udq.bLength = sizeof(utd->udq); 480184610Salfred utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; 481184610Salfred utd->udq.bDeviceClass = tdd->bDeviceClass; 482184610Salfred utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; 483184610Salfred utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; 484184610Salfred utd->udq.bNumConfigurations = 0; 485184610Salfred USETW(utd->udq.bcdUSB, 0x0200); 486184610Salfred utd->udq.bMaxPacketSize0 = 0; 487184610Salfred 488192500Sthompsa switch (temp->usb_speed) { 489184610Salfred case USB_SPEED_LOW: 490184610Salfred USETW(utd->udd.bcdUSB, 0x0110); 491184610Salfred utd->udd.bMaxPacketSize = 8; 492184610Salfred break; 493184610Salfred case USB_SPEED_FULL: 494184610Salfred USETW(utd->udd.bcdUSB, 0x0110); 495184610Salfred utd->udd.bMaxPacketSize = 32; 496184610Salfred break; 497184610Salfred case USB_SPEED_HIGH: 498184610Salfred USETW(utd->udd.bcdUSB, 0x0200); 499184610Salfred utd->udd.bMaxPacketSize = 64; 500184610Salfred break; 501184610Salfred case USB_SPEED_VARIABLE: 502184610Salfred USETW(utd->udd.bcdUSB, 0x0250); 503184610Salfred utd->udd.bMaxPacketSize = 255; /* 512 bytes */ 504184610Salfred break; 505223467Shselasky case USB_SPEED_SUPER: 506223467Shselasky USETW(utd->udd.bcdUSB, 0x0300); 507223467Shselasky utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ 508223467Shselasky break; 509184610Salfred default: 510184610Salfred temp->err = USB_ERR_INVAL; 511184610Salfred break; 512184610Salfred } 513184610Salfred } 514184610Salfred} 515184610Salfred 516184610Salfred/*------------------------------------------------------------------------* 517194228Sthompsa * usb_hw_ep_match 518184610Salfred * 519184610Salfred * Return values: 520184610Salfred * 0: The endpoint profile does not match the criterias 521184610Salfred * Else: The endpoint profile matches the criterias 522184610Salfred *------------------------------------------------------------------------*/ 523184610Salfredstatic uint8_t 524194228Sthompsausb_hw_ep_match(const struct usb_hw_ep_profile *pf, 525184610Salfred uint8_t ep_type, uint8_t ep_dir_in) 526184610Salfred{ 527184610Salfred if (ep_type == UE_CONTROL) { 528184610Salfred /* special */ 529184610Salfred return (pf->support_control); 530184610Salfred } 531184610Salfred if ((pf->support_in && ep_dir_in) || 532184610Salfred (pf->support_out && !ep_dir_in)) { 533184610Salfred if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || 534184610Salfred (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || 535184610Salfred (pf->support_bulk && (ep_type == UE_BULK))) { 536184610Salfred return (1); 537184610Salfred } 538184610Salfred } 539184610Salfred return (0); 540184610Salfred} 541184610Salfred 542184610Salfred/*------------------------------------------------------------------------* 543194228Sthompsa * usb_hw_ep_find_match 544184610Salfred * 545184610Salfred * This function is used to find the best matching endpoint profile 546184610Salfred * for and endpoint belonging to an USB descriptor. 547184610Salfred * 548184610Salfred * Return values: 549184610Salfred * 0: Success. Got a match. 550184610Salfred * Else: Failure. No match. 551184610Salfred *------------------------------------------------------------------------*/ 552184610Salfredstatic uint8_t 553194228Sthompsausb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, 554192984Sthompsa struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) 555184610Salfred{ 556192984Sthompsa const struct usb_hw_ep_profile *pf; 557184610Salfred uint16_t distance; 558184610Salfred uint16_t temp; 559184610Salfred uint16_t max_frame_size; 560184610Salfred uint8_t n; 561184610Salfred uint8_t best_n; 562184610Salfred uint8_t dir_in; 563184610Salfred uint8_t dir_out; 564184610Salfred 565184610Salfred distance = 0xFFFF; 566184610Salfred best_n = 0; 567184610Salfred 568184610Salfred if ((!ep->needs_in) && (!ep->needs_out)) { 569184610Salfred return (0); /* we are done */ 570184610Salfred } 571184610Salfred if (ep->needs_ep_type == UE_CONTROL) { 572184610Salfred dir_in = 1; 573184610Salfred dir_out = 1; 574184610Salfred } else { 575184610Salfred if (ep->needs_in) { 576184610Salfred dir_in = 1; 577184610Salfred dir_out = 0; 578184610Salfred } else { 579184610Salfred dir_in = 0; 580184610Salfred dir_out = 1; 581184610Salfred } 582184610Salfred } 583184610Salfred 584184610Salfred for (n = 1; n != (USB_EP_MAX / 2); n++) { 585184610Salfred 586184610Salfred /* get HW endpoint profile */ 587184610Salfred (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); 588184610Salfred if (pf == NULL) { 589184610Salfred /* end of profiles */ 590184610Salfred break; 591184610Salfred } 592184610Salfred /* check if IN-endpoint is reserved */ 593184610Salfred if (dir_in || pf->is_simplex) { 594184610Salfred if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { 595184610Salfred /* mismatch */ 596184610Salfred continue; 597184610Salfred } 598184610Salfred } 599184610Salfred /* check if OUT-endpoint is reserved */ 600184610Salfred if (dir_out || pf->is_simplex) { 601184610Salfred if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { 602184610Salfred /* mismatch */ 603184610Salfred continue; 604184610Salfred } 605184610Salfred } 606184610Salfred /* check simplex */ 607184610Salfred if (pf->is_simplex == is_simplex) { 608184610Salfred /* mismatch */ 609184610Salfred continue; 610184610Salfred } 611184610Salfred /* check if HW endpoint matches */ 612194228Sthompsa if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { 613184610Salfred /* mismatch */ 614184610Salfred continue; 615184610Salfred } 616184610Salfred /* get maximum frame size */ 617184610Salfred if (dir_in) 618184610Salfred max_frame_size = pf->max_in_frame_size; 619184610Salfred else 620184610Salfred max_frame_size = pf->max_out_frame_size; 621184610Salfred 622184610Salfred /* check if we have a matching profile */ 623184610Salfred if (max_frame_size >= ep->max_frame_size) { 624184610Salfred temp = (max_frame_size - ep->max_frame_size); 625184610Salfred if (distance > temp) { 626184610Salfred distance = temp; 627184610Salfred best_n = n; 628184610Salfred ep->pf = pf; 629184610Salfred } 630184610Salfred } 631184610Salfred } 632184610Salfred 633184610Salfred /* see if we got a match */ 634184610Salfred if (best_n != 0) { 635184610Salfred /* get the correct profile */ 636184610Salfred pf = ep->pf; 637184610Salfred 638184610Salfred /* reserve IN-endpoint */ 639184610Salfred if (dir_in) { 640184610Salfred ues->bmInAlloc[best_n / 8] |= 641184610Salfred (1 << (best_n % 8)); 642184610Salfred ep->hw_endpoint_in = best_n | UE_DIR_IN; 643184610Salfred ep->needs_in = 0; 644184610Salfred } 645184610Salfred /* reserve OUT-endpoint */ 646184610Salfred if (dir_out) { 647184610Salfred ues->bmOutAlloc[best_n / 8] |= 648184610Salfred (1 << (best_n % 8)); 649184610Salfred ep->hw_endpoint_out = best_n | UE_DIR_OUT; 650184610Salfred ep->needs_out = 0; 651184610Salfred } 652184610Salfred return (0); /* got a match */ 653184610Salfred } 654184610Salfred return (1); /* failure */ 655184610Salfred} 656184610Salfred 657184610Salfred/*------------------------------------------------------------------------* 658194228Sthompsa * usb_hw_ep_get_needs 659184610Salfred * 660184610Salfred * This function will figure out the type and number of endpoints 661184610Salfred * which are needed for an USB configuration. 662184610Salfred * 663184610Salfred * Return values: 664184610Salfred * 0: Success. 665184610Salfred * Else: Failure. 666184610Salfred *------------------------------------------------------------------------*/ 667184610Salfredstatic uint8_t 668194228Sthompsausb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, 669184610Salfred uint8_t ep_type, uint8_t is_complete) 670184610Salfred{ 671192984Sthompsa const struct usb_hw_ep_profile *pf; 672192984Sthompsa struct usb_hw_ep_scratch_sub *ep_iface; 673192984Sthompsa struct usb_hw_ep_scratch_sub *ep_curr; 674192984Sthompsa struct usb_hw_ep_scratch_sub *ep_max; 675192984Sthompsa struct usb_hw_ep_scratch_sub *ep_end; 676192984Sthompsa struct usb_descriptor *desc; 677192984Sthompsa struct usb_interface_descriptor *id; 678192984Sthompsa struct usb_endpoint_descriptor *ed; 679192500Sthompsa enum usb_dev_speed speed; 680184610Salfred uint16_t wMaxPacketSize; 681184610Salfred uint16_t temp; 682184610Salfred uint8_t ep_no; 683184610Salfred 684184610Salfred ep_iface = ues->ep_max; 685184610Salfred ep_curr = ues->ep_max; 686184610Salfred ep_end = ues->ep + USB_EP_MAX; 687184610Salfred ep_max = ues->ep_max; 688184610Salfred desc = NULL; 689194228Sthompsa speed = usbd_get_speed(ues->udev); 690184610Salfred 691184610Salfredrepeat: 692184610Salfred 693194228Sthompsa while ((desc = usb_desc_foreach(ues->cd, desc))) { 694184610Salfred 695184610Salfred if ((desc->bDescriptorType == UDESC_INTERFACE) && 696184610Salfred (desc->bLength >= sizeof(*id))) { 697184610Salfred 698184610Salfred id = (void *)desc; 699184610Salfred 700184610Salfred if (id->bAlternateSetting == 0) { 701184610Salfred /* going forward */ 702184610Salfred ep_iface = ep_max; 703184610Salfred } else { 704184610Salfred /* reset */ 705184610Salfred ep_curr = ep_iface; 706184610Salfred } 707184610Salfred } 708184610Salfred if ((desc->bDescriptorType == UDESC_ENDPOINT) && 709184610Salfred (desc->bLength >= sizeof(*ed))) { 710184610Salfred 711184610Salfred ed = (void *)desc; 712184610Salfred 713184610Salfred goto handle_endpoint_desc; 714184610Salfred } 715184610Salfred } 716184610Salfred ues->ep_max = ep_max; 717184610Salfred return (0); 718184610Salfred 719184610Salfredhandle_endpoint_desc: 720184610Salfred temp = (ed->bmAttributes & UE_XFERTYPE); 721184610Salfred 722184610Salfred if (temp == ep_type) { 723184610Salfred 724184610Salfred if (ep_curr == ep_end) { 725184610Salfred /* too many endpoints */ 726184610Salfred return (1); /* failure */ 727184610Salfred } 728184610Salfred wMaxPacketSize = UGETW(ed->wMaxPacketSize); 729184610Salfred if ((wMaxPacketSize & 0xF800) && 730184610Salfred (speed == USB_SPEED_HIGH)) { 731184610Salfred /* handle packet multiplier */ 732184610Salfred temp = (wMaxPacketSize >> 11) & 3; 733184610Salfred wMaxPacketSize &= 0x7FF; 734184610Salfred if (temp == 1) { 735184610Salfred wMaxPacketSize *= 2; 736184610Salfred } else { 737184610Salfred wMaxPacketSize *= 3; 738184610Salfred } 739184610Salfred } 740184610Salfred /* 741184610Salfred * Check if we have a fixed endpoint number, else the 742184610Salfred * endpoint number is allocated dynamically: 743184610Salfred */ 744184610Salfred ep_no = (ed->bEndpointAddress & UE_ADDR); 745184610Salfred if (ep_no != 0) { 746184610Salfred 747184610Salfred /* get HW endpoint profile */ 748184610Salfred (ues->methods->get_hw_ep_profile) 749184610Salfred (ues->udev, &pf, ep_no); 750184610Salfred if (pf == NULL) { 751184610Salfred /* HW profile does not exist - failure */ 752184610Salfred DPRINTFN(0, "Endpoint profile %u " 753184610Salfred "does not exist\n", ep_no); 754184610Salfred return (1); 755184610Salfred } 756184610Salfred /* reserve fixed endpoint number */ 757184610Salfred if (ep_type == UE_CONTROL) { 758184610Salfred ues->bmInAlloc[ep_no / 8] |= 759184610Salfred (1 << (ep_no % 8)); 760184610Salfred ues->bmOutAlloc[ep_no / 8] |= 761184610Salfred (1 << (ep_no % 8)); 762184610Salfred if ((pf->max_in_frame_size < wMaxPacketSize) || 763184610Salfred (pf->max_out_frame_size < wMaxPacketSize)) { 764184610Salfred DPRINTFN(0, "Endpoint profile %u " 765199816Sthompsa "has too small buffer\n", ep_no); 766184610Salfred return (1); 767184610Salfred } 768184610Salfred } else if (ed->bEndpointAddress & UE_DIR_IN) { 769184610Salfred ues->bmInAlloc[ep_no / 8] |= 770184610Salfred (1 << (ep_no % 8)); 771184610Salfred if (pf->max_in_frame_size < wMaxPacketSize) { 772184610Salfred DPRINTFN(0, "Endpoint profile %u " 773199816Sthompsa "has too small buffer\n", ep_no); 774184610Salfred return (1); 775184610Salfred } 776184610Salfred } else { 777184610Salfred ues->bmOutAlloc[ep_no / 8] |= 778184610Salfred (1 << (ep_no % 8)); 779184610Salfred if (pf->max_out_frame_size < wMaxPacketSize) { 780184610Salfred DPRINTFN(0, "Endpoint profile %u " 781199816Sthompsa "has too small buffer\n", ep_no); 782184610Salfred return (1); 783184610Salfred } 784184610Salfred } 785184610Salfred } else if (is_complete) { 786184610Salfred 787184610Salfred /* check if we have enough buffer space */ 788184610Salfred if (wMaxPacketSize > 789184610Salfred ep_curr->max_frame_size) { 790184610Salfred return (1); /* failure */ 791184610Salfred } 792184610Salfred if (ed->bEndpointAddress & UE_DIR_IN) { 793184610Salfred ed->bEndpointAddress = 794184610Salfred ep_curr->hw_endpoint_in; 795184610Salfred } else { 796184610Salfred ed->bEndpointAddress = 797184610Salfred ep_curr->hw_endpoint_out; 798184610Salfred } 799184610Salfred 800184610Salfred } else { 801184610Salfred 802184610Salfred /* compute the maximum frame size */ 803184610Salfred if (ep_curr->max_frame_size < wMaxPacketSize) { 804184610Salfred ep_curr->max_frame_size = wMaxPacketSize; 805184610Salfred } 806184610Salfred if (temp == UE_CONTROL) { 807184610Salfred ep_curr->needs_in = 1; 808184610Salfred ep_curr->needs_out = 1; 809184610Salfred } else { 810184610Salfred if (ed->bEndpointAddress & UE_DIR_IN) { 811184610Salfred ep_curr->needs_in = 1; 812184610Salfred } else { 813184610Salfred ep_curr->needs_out = 1; 814184610Salfred } 815184610Salfred } 816184610Salfred ep_curr->needs_ep_type = ep_type; 817184610Salfred } 818184610Salfred 819184610Salfred ep_curr++; 820184610Salfred if (ep_max < ep_curr) { 821184610Salfred ep_max = ep_curr; 822184610Salfred } 823184610Salfred } 824184610Salfred goto repeat; 825184610Salfred} 826184610Salfred 827184610Salfred/*------------------------------------------------------------------------* 828194228Sthompsa * usb_hw_ep_resolve 829184610Salfred * 830184610Salfred * This function will try to resolve endpoint requirements by the 831184610Salfred * given endpoint profiles that the USB hardware reports. 832184610Salfred * 833184610Salfred * Return values: 834184610Salfred * 0: Success 835184610Salfred * Else: Failure 836184610Salfred *------------------------------------------------------------------------*/ 837193045Sthompsastatic usb_error_t 838194228Sthompsausb_hw_ep_resolve(struct usb_device *udev, 839192984Sthompsa struct usb_descriptor *desc) 840184610Salfred{ 841192984Sthompsa struct usb_hw_ep_scratch *ues; 842192984Sthompsa struct usb_hw_ep_scratch_sub *ep; 843192984Sthompsa const struct usb_hw_ep_profile *pf; 844192984Sthompsa struct usb_bus_methods *methods; 845192984Sthompsa struct usb_device_descriptor *dd; 846184610Salfred uint16_t mps; 847184610Salfred 848247090Shselasky if (desc == NULL) 849184610Salfred return (USB_ERR_INVAL); 850247090Shselasky 851184610Salfred /* get bus methods */ 852184610Salfred methods = udev->bus->methods; 853184610Salfred 854247090Shselasky if (methods->get_hw_ep_profile == NULL) 855184610Salfred return (USB_ERR_INVAL); 856247090Shselasky 857184610Salfred if (desc->bDescriptorType == UDESC_DEVICE) { 858184610Salfred 859247090Shselasky if (desc->bLength < sizeof(*dd)) 860184610Salfred return (USB_ERR_INVAL); 861247090Shselasky 862184610Salfred dd = (void *)desc; 863184610Salfred 864184610Salfred /* get HW control endpoint 0 profile */ 865184610Salfred (methods->get_hw_ep_profile) (udev, &pf, 0); 866184610Salfred if (pf == NULL) { 867184610Salfred return (USB_ERR_INVAL); 868184610Salfred } 869194228Sthompsa if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { 870184610Salfred DPRINTFN(0, "Endpoint 0 does not " 871184610Salfred "support control\n"); 872184610Salfred return (USB_ERR_INVAL); 873184610Salfred } 874184610Salfred mps = dd->bMaxPacketSize; 875184610Salfred 876184610Salfred if (udev->speed == USB_SPEED_FULL) { 877184610Salfred /* 878184610Salfred * We can optionally choose another packet size ! 879184610Salfred */ 880184610Salfred while (1) { 881184610Salfred /* check if "mps" is ok */ 882184610Salfred if (pf->max_in_frame_size >= mps) { 883184610Salfred break; 884184610Salfred } 885184610Salfred /* reduce maximum packet size */ 886184610Salfred mps /= 2; 887184610Salfred 888184610Salfred /* check if "mps" is too small */ 889184610Salfred if (mps < 8) { 890184610Salfred return (USB_ERR_INVAL); 891184610Salfred } 892184610Salfred } 893184610Salfred 894184610Salfred dd->bMaxPacketSize = mps; 895184610Salfred 896184610Salfred } else { 897184610Salfred /* We only have one choice */ 898184610Salfred if (mps == 255) { 899184610Salfred mps = 512; 900184610Salfred } 901184610Salfred /* Check if we support the specified wMaxPacketSize */ 902184610Salfred if (pf->max_in_frame_size < mps) { 903184610Salfred return (USB_ERR_INVAL); 904184610Salfred } 905184610Salfred } 906184610Salfred return (0); /* success */ 907184610Salfred } 908247090Shselasky if (desc->bDescriptorType != UDESC_CONFIG) 909184610Salfred return (USB_ERR_INVAL); 910247090Shselasky if (desc->bLength < sizeof(*(ues->cd))) 911184610Salfred return (USB_ERR_INVAL); 912184610Salfred 913247090Shselasky ues = udev->scratch.hw_ep_scratch; 914247090Shselasky 915229080Shselasky memset(ues, 0, sizeof(*ues)); 916184610Salfred 917184610Salfred ues->ep_max = ues->ep; 918184610Salfred ues->cd = (void *)desc; 919184610Salfred ues->methods = methods; 920184610Salfred ues->udev = udev; 921184610Salfred 922184610Salfred /* Get all the endpoints we need */ 923184610Salfred 924194228Sthompsa if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || 925194228Sthompsa usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || 926194228Sthompsa usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || 927194228Sthompsa usb_hw_ep_get_needs(ues, UE_BULK, 0)) { 928184610Salfred DPRINTFN(0, "Could not get needs\n"); 929184610Salfred return (USB_ERR_INVAL); 930184610Salfred } 931184610Salfred for (ep = ues->ep; ep != ues->ep_max; ep++) { 932184610Salfred 933184610Salfred while (ep->needs_in || ep->needs_out) { 934184610Salfred 935184610Salfred /* 936184610Salfred * First try to use a simplex endpoint. 937184610Salfred * Then try to use a duplex endpoint. 938184610Salfred */ 939194228Sthompsa if (usb_hw_ep_find_match(ues, ep, 1) && 940194228Sthompsa usb_hw_ep_find_match(ues, ep, 0)) { 941184610Salfred DPRINTFN(0, "Could not find match\n"); 942184610Salfred return (USB_ERR_INVAL); 943184610Salfred } 944184610Salfred } 945184610Salfred } 946184610Salfred 947184610Salfred ues->ep_max = ues->ep; 948184610Salfred 949184610Salfred /* Update all endpoint addresses */ 950184610Salfred 951194228Sthompsa if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || 952194228Sthompsa usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || 953194228Sthompsa usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || 954194228Sthompsa usb_hw_ep_get_needs(ues, UE_BULK, 1)) { 955184610Salfred DPRINTFN(0, "Could not update endpoint address\n"); 956184610Salfred return (USB_ERR_INVAL); 957184610Salfred } 958184610Salfred return (0); /* success */ 959184610Salfred} 960184610Salfred 961184610Salfred/*------------------------------------------------------------------------* 962194228Sthompsa * usb_temp_get_tdd 963184610Salfred * 964184610Salfred * Returns: 965184610Salfred * NULL: No USB template device descriptor found. 966184610Salfred * Else: Pointer to the USB template device descriptor. 967184610Salfred *------------------------------------------------------------------------*/ 968192984Sthompsastatic const struct usb_temp_device_desc * 969194228Sthompsausb_temp_get_tdd(struct usb_device *udev) 970184610Salfred{ 971194228Sthompsa if (udev->usb_template_ptr == NULL) { 972184610Salfred return (NULL); 973184610Salfred } 974194228Sthompsa return (udev->usb_template_ptr->tdd); 975184610Salfred} 976184610Salfred 977184610Salfred/*------------------------------------------------------------------------* 978194228Sthompsa * usb_temp_get_device_desc 979184610Salfred * 980184610Salfred * Returns: 981184610Salfred * NULL: No USB device descriptor found. 982184610Salfred * Else: Pointer to USB device descriptor. 983184610Salfred *------------------------------------------------------------------------*/ 984184610Salfredstatic void * 985194228Sthompsausb_temp_get_device_desc(struct usb_device *udev) 986184610Salfred{ 987192984Sthompsa struct usb_device_descriptor *dd; 988184610Salfred 989194228Sthompsa if (udev->usb_template_ptr == NULL) { 990184610Salfred return (NULL); 991184610Salfred } 992194228Sthompsa dd = &udev->usb_template_ptr->udd; 993184610Salfred if (dd->bDescriptorType != UDESC_DEVICE) { 994184610Salfred /* sanity check failed */ 995184610Salfred return (NULL); 996184610Salfred } 997184610Salfred return (dd); 998184610Salfred} 999184610Salfred 1000184610Salfred/*------------------------------------------------------------------------* 1001194228Sthompsa * usb_temp_get_qualifier_desc 1002184610Salfred * 1003184610Salfred * Returns: 1004184610Salfred * NULL: No USB device_qualifier descriptor found. 1005184610Salfred * Else: Pointer to USB device_qualifier descriptor. 1006184610Salfred *------------------------------------------------------------------------*/ 1007184610Salfredstatic void * 1008194228Sthompsausb_temp_get_qualifier_desc(struct usb_device *udev) 1009184610Salfred{ 1010192984Sthompsa struct usb_device_qualifier *dq; 1011184610Salfred 1012194228Sthompsa if (udev->usb_template_ptr == NULL) { 1013184610Salfred return (NULL); 1014184610Salfred } 1015194228Sthompsa dq = &udev->usb_template_ptr->udq; 1016184610Salfred if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { 1017184610Salfred /* sanity check failed */ 1018184610Salfred return (NULL); 1019184610Salfred } 1020184610Salfred return (dq); 1021184610Salfred} 1022184610Salfred 1023184610Salfred/*------------------------------------------------------------------------* 1024194228Sthompsa * usb_temp_get_config_desc 1025184610Salfred * 1026184610Salfred * Returns: 1027184610Salfred * NULL: No USB config descriptor found. 1028184610Salfred * Else: Pointer to USB config descriptor having index "index". 1029184610Salfred *------------------------------------------------------------------------*/ 1030184610Salfredstatic void * 1031194228Sthompsausb_temp_get_config_desc(struct usb_device *udev, 1032184610Salfred uint16_t *pLength, uint8_t index) 1033184610Salfred{ 1034192984Sthompsa struct usb_device_descriptor *dd; 1035192984Sthompsa struct usb_config_descriptor *cd; 1036184610Salfred uint16_t temp; 1037184610Salfred 1038194228Sthompsa if (udev->usb_template_ptr == NULL) { 1039184610Salfred return (NULL); 1040184610Salfred } 1041194228Sthompsa dd = &udev->usb_template_ptr->udd; 1042194228Sthompsa cd = (void *)(udev->usb_template_ptr + 1); 1043184610Salfred 1044184610Salfred if (index >= dd->bNumConfigurations) { 1045184610Salfred /* out of range */ 1046184610Salfred return (NULL); 1047184610Salfred } 1048184610Salfred while (index--) { 1049184610Salfred if (cd->bDescriptorType != UDESC_CONFIG) { 1050184610Salfred /* sanity check failed */ 1051184610Salfred return (NULL); 1052184610Salfred } 1053184610Salfred temp = UGETW(cd->wTotalLength); 1054184610Salfred cd = USB_ADD_BYTES(cd, temp); 1055184610Salfred } 1056184610Salfred 1057184610Salfred if (pLength) { 1058184610Salfred *pLength = UGETW(cd->wTotalLength); 1059184610Salfred } 1060184610Salfred return (cd); 1061184610Salfred} 1062184610Salfred 1063184610Salfred/*------------------------------------------------------------------------* 1064194228Sthompsa * usb_temp_get_vendor_desc 1065184610Salfred * 1066184610Salfred * Returns: 1067184610Salfred * NULL: No vendor descriptor found. 1068184610Salfred * Else: Pointer to a vendor descriptor. 1069184610Salfred *------------------------------------------------------------------------*/ 1070184610Salfredstatic const void * 1071194228Sthompsausb_temp_get_vendor_desc(struct usb_device *udev, 1072205030Sthompsa const struct usb_device_request *req, uint16_t *plen) 1073184610Salfred{ 1074192984Sthompsa const struct usb_temp_device_desc *tdd; 1075184610Salfred 1076194228Sthompsa tdd = usb_temp_get_tdd(udev); 1077184610Salfred if (tdd == NULL) { 1078184610Salfred return (NULL); 1079184610Salfred } 1080184610Salfred if (tdd->getVendorDesc == NULL) { 1081184610Salfred return (NULL); 1082184610Salfred } 1083205030Sthompsa return ((tdd->getVendorDesc) (req, plen)); 1084184610Salfred} 1085184610Salfred 1086184610Salfred/*------------------------------------------------------------------------* 1087194228Sthompsa * usb_temp_get_string_desc 1088184610Salfred * 1089184610Salfred * Returns: 1090184610Salfred * NULL: No string descriptor found. 1091184610Salfred * Else: Pointer to a string descriptor. 1092184610Salfred *------------------------------------------------------------------------*/ 1093184610Salfredstatic const void * 1094194228Sthompsausb_temp_get_string_desc(struct usb_device *udev, 1095184610Salfred uint16_t lang_id, uint8_t string_index) 1096184610Salfred{ 1097192984Sthompsa const struct usb_temp_device_desc *tdd; 1098184610Salfred 1099194228Sthompsa tdd = usb_temp_get_tdd(udev); 1100184610Salfred if (tdd == NULL) { 1101184610Salfred return (NULL); 1102184610Salfred } 1103184610Salfred if (tdd->getStringDesc == NULL) { 1104184610Salfred return (NULL); 1105184610Salfred } 1106184610Salfred return ((tdd->getStringDesc) (lang_id, string_index)); 1107184610Salfred} 1108184610Salfred 1109184610Salfred/*------------------------------------------------------------------------* 1110194228Sthompsa * usb_temp_get_hub_desc 1111184610Salfred * 1112184610Salfred * Returns: 1113184610Salfred * NULL: No USB HUB descriptor found. 1114184610Salfred * Else: Pointer to a USB HUB descriptor. 1115184610Salfred *------------------------------------------------------------------------*/ 1116184610Salfredstatic const void * 1117194228Sthompsausb_temp_get_hub_desc(struct usb_device *udev) 1118184610Salfred{ 1119184610Salfred return (NULL); /* needs to be implemented */ 1120184610Salfred} 1121184610Salfred 1122184610Salfred/*------------------------------------------------------------------------* 1123194228Sthompsa * usb_temp_get_desc 1124184610Salfred * 1125184610Salfred * This function is a demultiplexer for local USB device side control 1126184610Salfred * endpoint requests. 1127184610Salfred *------------------------------------------------------------------------*/ 1128193045Sthompsastatic usb_error_t 1129194228Sthompsausb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, 1130184610Salfred const void **pPtr, uint16_t *pLength) 1131184610Salfred{ 1132184610Salfred const uint8_t *buf; 1133184610Salfred uint16_t len; 1134184610Salfred 1135184610Salfred buf = NULL; 1136184610Salfred len = 0; 1137184610Salfred 1138184610Salfred switch (req->bmRequestType) { 1139184610Salfred case UT_READ_DEVICE: 1140184610Salfred switch (req->bRequest) { 1141184610Salfred case UR_GET_DESCRIPTOR: 1142184610Salfred goto tr_handle_get_descriptor; 1143184610Salfred default: 1144184610Salfred goto tr_stalled; 1145184610Salfred } 1146184610Salfred case UT_READ_CLASS_DEVICE: 1147184610Salfred switch (req->bRequest) { 1148184610Salfred case UR_GET_DESCRIPTOR: 1149184610Salfred goto tr_handle_get_class_descriptor; 1150184610Salfred default: 1151184610Salfred goto tr_stalled; 1152184610Salfred } 1153184610Salfred default: 1154184610Salfred goto tr_stalled; 1155184610Salfred } 1156184610Salfred 1157184610Salfredtr_handle_get_descriptor: 1158184610Salfred switch (req->wValue[1]) { 1159184610Salfred case UDESC_DEVICE: 1160184610Salfred if (req->wValue[0]) { 1161184610Salfred goto tr_stalled; 1162184610Salfred } 1163194228Sthompsa buf = usb_temp_get_device_desc(udev); 1164184610Salfred goto tr_valid; 1165184610Salfred case UDESC_DEVICE_QUALIFIER: 1166184610Salfred if (udev->speed != USB_SPEED_HIGH) { 1167184610Salfred goto tr_stalled; 1168184610Salfred } 1169184610Salfred if (req->wValue[0]) { 1170184610Salfred goto tr_stalled; 1171184610Salfred } 1172194228Sthompsa buf = usb_temp_get_qualifier_desc(udev); 1173184610Salfred goto tr_valid; 1174184610Salfred case UDESC_OTHER_SPEED_CONFIGURATION: 1175184610Salfred if (udev->speed != USB_SPEED_HIGH) { 1176184610Salfred goto tr_stalled; 1177184610Salfred } 1178184610Salfred case UDESC_CONFIG: 1179194228Sthompsa buf = usb_temp_get_config_desc(udev, 1180184610Salfred &len, req->wValue[0]); 1181184610Salfred goto tr_valid; 1182184610Salfred case UDESC_STRING: 1183194228Sthompsa buf = usb_temp_get_string_desc(udev, 1184184610Salfred UGETW(req->wIndex), req->wValue[0]); 1185184610Salfred goto tr_valid; 1186184610Salfred default: 1187184610Salfred goto tr_stalled; 1188184610Salfred } 1189184610Salfred 1190184610Salfredtr_handle_get_class_descriptor: 1191184610Salfred if (req->wValue[0]) { 1192184610Salfred goto tr_stalled; 1193184610Salfred } 1194194228Sthompsa buf = usb_temp_get_hub_desc(udev); 1195184610Salfred goto tr_valid; 1196184610Salfred 1197184610Salfredtr_valid: 1198205030Sthompsa if (buf == NULL) 1199184610Salfred goto tr_stalled; 1200205030Sthompsa if (len == 0) 1201184610Salfred len = buf[0]; 1202184610Salfred *pPtr = buf; 1203184610Salfred *pLength = len; 1204191402Sthompsa return (0); /* success */ 1205184610Salfred 1206184610Salfredtr_stalled: 1207205030Sthompsa /* try to get a vendor specific descriptor */ 1208205030Sthompsa len = 0; 1209205030Sthompsa buf = usb_temp_get_vendor_desc(udev, req, &len); 1210205030Sthompsa if (buf != NULL) 1211205030Sthompsa goto tr_valid; 1212184610Salfred *pPtr = NULL; 1213184610Salfred *pLength = 0; 1214191402Sthompsa return (0); /* we ignore failures */ 1215184610Salfred} 1216184610Salfred 1217184610Salfred/*------------------------------------------------------------------------* 1218192984Sthompsa * usb_temp_setup 1219184610Salfred * 1220184610Salfred * This function generates USB descriptors according to the given USB 1221184610Salfred * template device descriptor. It will also try to figure out the best 1222184610Salfred * matching endpoint addresses using the hardware endpoint profiles. 1223184610Salfred * 1224184610Salfred * Returns: 1225184610Salfred * 0: Success 1226184610Salfred * Else: Failure 1227184610Salfred *------------------------------------------------------------------------*/ 1228205030Sthompsausb_error_t 1229192984Sthompsausb_temp_setup(struct usb_device *udev, 1230192984Sthompsa const struct usb_temp_device_desc *tdd) 1231184610Salfred{ 1232192984Sthompsa struct usb_temp_setup *uts; 1233184610Salfred void *buf; 1234247090Shselasky usb_error_t error; 1235184610Salfred uint8_t n; 1236247090Shselasky uint8_t do_unlock; 1237184610Salfred 1238247090Shselasky /* be NULL safe */ 1239247090Shselasky if (tdd == NULL) 1240184610Salfred return (0); 1241184610Salfred 1242247090Shselasky /* Protect scratch area */ 1243247090Shselasky do_unlock = usbd_enum_lock(udev); 1244247090Shselasky 1245247090Shselasky uts = udev->scratch.temp_setup; 1246247090Shselasky 1247229080Shselasky memset(uts, 0, sizeof(*uts)); 1248184610Salfred 1249192500Sthompsa uts->usb_speed = udev->speed; 1250184610Salfred uts->self_powered = udev->flags.self_powered; 1251184610Salfred 1252184610Salfred /* first pass */ 1253184610Salfred 1254194228Sthompsa usb_make_device_desc(uts, tdd); 1255184610Salfred 1256184610Salfred if (uts->err) { 1257184610Salfred /* some error happened */ 1258247090Shselasky goto done; 1259184610Salfred } 1260184610Salfred /* sanity check */ 1261184610Salfred if (uts->size == 0) { 1262247090Shselasky uts->err = USB_ERR_INVAL; 1263247090Shselasky goto done; 1264184610Salfred } 1265184610Salfred /* allocate zeroed memory */ 1266184610Salfred uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO); 1267247090Shselasky /* 1268247090Shselasky * Allow malloc() to return NULL regardless of M_WAITOK flag. 1269247090Shselasky * This helps when porting the software to non-FreeBSD 1270247090Shselasky * systems. 1271247090Shselasky */ 1272184610Salfred if (uts->buf == NULL) { 1273184610Salfred /* could not allocate memory */ 1274247090Shselasky uts->err = USB_ERR_NOMEM; 1275247090Shselasky goto done; 1276184610Salfred } 1277184610Salfred /* second pass */ 1278184610Salfred 1279184610Salfred uts->size = 0; 1280184610Salfred 1281194228Sthompsa usb_make_device_desc(uts, tdd); 1282184610Salfred 1283184610Salfred /* 1284184610Salfred * Store a pointer to our descriptors: 1285184610Salfred */ 1286194228Sthompsa udev->usb_template_ptr = uts->buf; 1287184610Salfred 1288184610Salfred if (uts->err) { 1289184610Salfred /* some error happened during second pass */ 1290247090Shselasky goto done; 1291184610Salfred } 1292184610Salfred /* 1293184610Salfred * Resolve all endpoint addresses ! 1294184610Salfred */ 1295194228Sthompsa buf = usb_temp_get_device_desc(udev); 1296194228Sthompsa uts->err = usb_hw_ep_resolve(udev, buf); 1297184610Salfred if (uts->err) { 1298184610Salfred DPRINTFN(0, "Could not resolve endpoints for " 1299184610Salfred "Device Descriptor, error = %s\n", 1300194228Sthompsa usbd_errstr(uts->err)); 1301247090Shselasky goto done; 1302184610Salfred } 1303184610Salfred for (n = 0;; n++) { 1304184610Salfred 1305194228Sthompsa buf = usb_temp_get_config_desc(udev, NULL, n); 1306184610Salfred if (buf == NULL) { 1307184610Salfred break; 1308184610Salfred } 1309194228Sthompsa uts->err = usb_hw_ep_resolve(udev, buf); 1310184610Salfred if (uts->err) { 1311184610Salfred DPRINTFN(0, "Could not resolve endpoints for " 1312184610Salfred "Config Descriptor %u, error = %s\n", n, 1313194228Sthompsa usbd_errstr(uts->err)); 1314247090Shselasky goto done; 1315184610Salfred } 1316184610Salfred } 1317247090Shselaskydone: 1318247090Shselasky error = uts->err; 1319247090Shselasky if (error) 1320247090Shselasky usb_temp_unsetup(udev); 1321247090Shselasky if (do_unlock) 1322247090Shselasky usbd_enum_unlock(udev); 1323247090Shselasky return (error); 1324184610Salfred} 1325184610Salfred 1326184610Salfred/*------------------------------------------------------------------------* 1327194228Sthompsa * usb_temp_unsetup 1328184610Salfred * 1329184610Salfred * This function frees any memory associated with the currently 1330184610Salfred * setup template, if any. 1331184610Salfred *------------------------------------------------------------------------*/ 1332205030Sthompsavoid 1333194228Sthompsausb_temp_unsetup(struct usb_device *udev) 1334184610Salfred{ 1335194228Sthompsa if (udev->usb_template_ptr) { 1336184610Salfred 1337194228Sthompsa free(udev->usb_template_ptr, M_USB); 1338184610Salfred 1339194228Sthompsa udev->usb_template_ptr = NULL; 1340184610Salfred } 1341184610Salfred} 1342184610Salfred 1343193045Sthompsastatic usb_error_t 1344194228Sthompsausb_temp_setup_by_index(struct usb_device *udev, uint16_t index) 1345184610Salfred{ 1346193045Sthompsa usb_error_t err; 1347184610Salfred 1348184610Salfred switch (index) { 1349223467Shselasky case USB_TEMP_MSC: 1350194228Sthompsa err = usb_temp_setup(udev, &usb_template_msc); 1351184610Salfred break; 1352223467Shselasky case USB_TEMP_CDCE: 1353194228Sthompsa err = usb_temp_setup(udev, &usb_template_cdce); 1354184610Salfred break; 1355223467Shselasky case USB_TEMP_MTP: 1356194228Sthompsa err = usb_temp_setup(udev, &usb_template_mtp); 1357184610Salfred break; 1358223467Shselasky case USB_TEMP_MODEM: 1359223467Shselasky err = usb_temp_setup(udev, &usb_template_modem); 1360223467Shselasky break; 1361223467Shselasky case USB_TEMP_AUDIO: 1362223467Shselasky err = usb_temp_setup(udev, &usb_template_audio); 1363223467Shselasky break; 1364223467Shselasky case USB_TEMP_KBD: 1365223467Shselasky err = usb_temp_setup(udev, &usb_template_kbd); 1366223467Shselasky break; 1367223467Shselasky case USB_TEMP_MOUSE: 1368223467Shselasky err = usb_temp_setup(udev, &usb_template_mouse); 1369223467Shselasky break; 1370184610Salfred default: 1371184610Salfred return (USB_ERR_INVAL); 1372184610Salfred } 1373184610Salfred 1374184610Salfred return (err); 1375184610Salfred} 1376184610Salfred 1377184610Salfredstatic void 1378194228Sthompsausb_temp_init(void *arg) 1379184610Salfred{ 1380184610Salfred /* register our functions */ 1381194228Sthompsa usb_temp_get_desc_p = &usb_temp_get_desc; 1382194228Sthompsa usb_temp_setup_by_index_p = &usb_temp_setup_by_index; 1383194228Sthompsa usb_temp_unsetup_p = &usb_temp_unsetup; 1384184610Salfred} 1385184610Salfred 1386194228SthompsaSYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); 1387194228SthompsaSYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); 1388