63722e5212
with Communication Device Class Ethernet Emulation Model (CDC EEM). The driver supports both the device, and host side operation; there is a new USB template (#11) for the former. This enables communication with virtual USB NIC provided by iLO 5, as found in new HPE Proliant servers. Reviewed by: hselasky MFC after: 2 weeks Relnotes: yes Sponsored by: Hewlett Packard Enterprise
1488 lines
38 KiB
C
1488 lines
38 KiB
C
/* $FreeBSD$ */
|
|
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2007 Hans Petter Selasky. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* This file contains sub-routines to build up USB descriptors from
|
|
* USB templates.
|
|
*/
|
|
|
|
#ifdef USB_GLOBAL_INCLUDE_FILE
|
|
#include USB_GLOBAL_INCLUDE_FILE
|
|
#else
|
|
#include <sys/stdint.h>
|
|
#include <sys/stddef.h>
|
|
#include <sys/param.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/types.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/module.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/condvar.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/sx.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/priv.h>
|
|
|
|
#include <dev/usb/usb.h>
|
|
#include <dev/usb/usb_ioctl.h>
|
|
#include <dev/usb/usbdi.h>
|
|
#include <dev/usb/usbdi_util.h>
|
|
#include "usbdevs.h"
|
|
|
|
#include <dev/usb/usb_cdc.h>
|
|
#include <dev/usb/usb_core.h>
|
|
#include <dev/usb/usb_dynamic.h>
|
|
#include <dev/usb/usb_busdma.h>
|
|
#include <dev/usb/usb_process.h>
|
|
#include <dev/usb/usb_device.h>
|
|
#include <dev/usb/usb_util.h>
|
|
|
|
#define USB_DEBUG_VAR usb_debug
|
|
#include <dev/usb/usb_debug.h>
|
|
|
|
#include <dev/usb/usb_controller.h>
|
|
#include <dev/usb/usb_bus.h>
|
|
#include <dev/usb/usb_request.h>
|
|
#include <dev/usb/template/usb_template.h>
|
|
#endif /* USB_GLOBAL_INCLUDE_FILE */
|
|
|
|
MODULE_DEPEND(usb_template, usb, 1, 1, 1);
|
|
MODULE_VERSION(usb_template, 1);
|
|
|
|
/* function prototypes */
|
|
|
|
static int sysctl_hw_usb_template_power(SYSCTL_HANDLER_ARGS);
|
|
static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *);
|
|
static void usb_make_endpoint_desc(struct usb_temp_setup *,
|
|
const struct usb_temp_endpoint_desc *);
|
|
static void usb_make_interface_desc(struct usb_temp_setup *,
|
|
const struct usb_temp_interface_desc *);
|
|
static void usb_make_config_desc(struct usb_temp_setup *,
|
|
const struct usb_temp_config_desc *);
|
|
static void usb_make_device_desc(struct usb_temp_setup *,
|
|
const struct usb_temp_device_desc *);
|
|
static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t,
|
|
uint8_t);
|
|
static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *,
|
|
struct usb_hw_ep_scratch_sub *, uint8_t);
|
|
static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t,
|
|
uint8_t);
|
|
static usb_error_t usb_hw_ep_resolve(struct usb_device *,
|
|
struct usb_descriptor *);
|
|
static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *);
|
|
static void *usb_temp_get_device_desc(struct usb_device *);
|
|
static void *usb_temp_get_qualifier_desc(struct usb_device *);
|
|
static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *,
|
|
uint8_t);
|
|
static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t,
|
|
uint8_t);
|
|
static const void *usb_temp_get_vendor_desc(struct usb_device *,
|
|
const struct usb_device_request *, uint16_t *plen);
|
|
static const void *usb_temp_get_hub_desc(struct usb_device *);
|
|
static usb_error_t usb_temp_get_desc(struct usb_device *,
|
|
struct usb_device_request *, const void **, uint16_t *);
|
|
static usb_error_t usb_temp_setup_by_index(struct usb_device *,
|
|
uint16_t index);
|
|
static void usb_temp_init(void *);
|
|
|
|
SYSCTL_NODE(_hw_usb, OID_AUTO, templates, CTLFLAG_RW, 0,
|
|
"USB device side templates");
|
|
SYSCTL_PROC(_hw_usb, OID_AUTO, template_power,
|
|
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
|
NULL, 0, sysctl_hw_usb_template_power,
|
|
"I", "USB bus power consumption in mA at 5V");
|
|
|
|
static int usb_template_power = 500; /* 500mA */
|
|
|
|
static int
|
|
sysctl_hw_usb_template_power(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
int error, val;
|
|
|
|
val = usb_template_power;
|
|
error = sysctl_handle_int(oidp, &val, 0, req);
|
|
if (error != 0 || req->newptr == NULL)
|
|
return (error);
|
|
|
|
if (val < 0 || val > 500)
|
|
return (EINVAL);
|
|
|
|
usb_template_power = val;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_decode_str_desc
|
|
*
|
|
* Helper function to decode string descriptors into a C string.
|
|
*------------------------------------------------------------------------*/
|
|
void
|
|
usb_decode_str_desc(struct usb_string_descriptor *sd, char *buf, size_t buflen)
|
|
{
|
|
size_t i;
|
|
|
|
if (sd->bLength < 2) {
|
|
buf[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < buflen - 1 && i < (sd->bLength / 2) - 1; i++)
|
|
buf[i] = UGETW(sd->bString[i]);
|
|
|
|
buf[i] = '\0';
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_sysctl
|
|
*
|
|
* Callback for SYSCTL_PROC(9), to set and retrieve template string
|
|
* descriptors.
|
|
*------------------------------------------------------------------------*/
|
|
int
|
|
usb_temp_sysctl(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
char buf[128];
|
|
struct usb_string_descriptor *sd = arg1;
|
|
size_t len, sdlen = arg2;
|
|
int error;
|
|
|
|
usb_decode_str_desc(sd, buf, sizeof(buf));
|
|
|
|
error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
|
|
if (error != 0 || req->newptr == NULL)
|
|
return (error);
|
|
|
|
len = usb_make_str_desc(sd, sdlen, buf);
|
|
if (len == 0)
|
|
return (EINVAL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_make_raw_desc
|
|
*
|
|
* This function will insert a raw USB descriptor into the generated
|
|
* USB configuration.
|
|
*------------------------------------------------------------------------*/
|
|
static void
|
|
usb_make_raw_desc(struct usb_temp_setup *temp,
|
|
const uint8_t *raw)
|
|
{
|
|
void *dst;
|
|
uint8_t len;
|
|
|
|
/*
|
|
* The first byte of any USB descriptor gives the length.
|
|
*/
|
|
if (raw) {
|
|
len = raw[0];
|
|
if (temp->buf) {
|
|
dst = USB_ADD_BYTES(temp->buf, temp->size);
|
|
memcpy(dst, raw, len);
|
|
|
|
/* check if we have got a CDC union descriptor */
|
|
|
|
if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) &&
|
|
(raw[1] == UDESC_CS_INTERFACE) &&
|
|
(raw[2] == UDESCSUB_CDC_UNION)) {
|
|
struct usb_cdc_union_descriptor *ud = (void *)dst;
|
|
|
|
/* update the interface numbers */
|
|
|
|
ud->bMasterInterface +=
|
|
temp->bInterfaceNumber;
|
|
ud->bSlaveInterface[0] +=
|
|
temp->bInterfaceNumber;
|
|
}
|
|
|
|
/* check if we have got an interface association descriptor */
|
|
|
|
if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) &&
|
|
(raw[1] == UDESC_IFACE_ASSOC)) {
|
|
struct usb_interface_assoc_descriptor *iad = (void *)dst;
|
|
|
|
/* update the interface number */
|
|
|
|
iad->bFirstInterface +=
|
|
temp->bInterfaceNumber;
|
|
}
|
|
|
|
/* check if we have got a call management descriptor */
|
|
|
|
if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) &&
|
|
(raw[1] == UDESC_CS_INTERFACE) &&
|
|
(raw[2] == UDESCSUB_CDC_CM)) {
|
|
struct usb_cdc_cm_descriptor *ccd = (void *)dst;
|
|
|
|
/* update the interface number */
|
|
|
|
ccd->bDataInterface +=
|
|
temp->bInterfaceNumber;
|
|
}
|
|
}
|
|
temp->size += len;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_make_endpoint_desc
|
|
*
|
|
* This function will generate an USB endpoint descriptor from the
|
|
* given USB template endpoint descriptor, which will be inserted into
|
|
* the USB configuration.
|
|
*------------------------------------------------------------------------*/
|
|
static void
|
|
usb_make_endpoint_desc(struct usb_temp_setup *temp,
|
|
const struct usb_temp_endpoint_desc *ted)
|
|
{
|
|
struct usb_endpoint_descriptor *ed;
|
|
const void **rd;
|
|
uint16_t old_size;
|
|
uint16_t mps;
|
|
uint8_t ea; /* Endpoint Address */
|
|
uint8_t et; /* Endpiont Type */
|
|
|
|
/* Reserve memory */
|
|
old_size = temp->size;
|
|
|
|
ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT));
|
|
et = (ted->bmAttributes & UE_XFERTYPE);
|
|
|
|
if (et == UE_ISOCHRONOUS) {
|
|
/* account for extra byte fields */
|
|
temp->size += sizeof(*ed) + 2;
|
|
} else {
|
|
temp->size += sizeof(*ed);
|
|
}
|
|
|
|
/* Scan all Raw Descriptors first */
|
|
rd = ted->ppRawDesc;
|
|
if (rd) {
|
|
while (*rd) {
|
|
usb_make_raw_desc(temp, *rd);
|
|
rd++;
|
|
}
|
|
}
|
|
if (ted->pPacketSize == NULL) {
|
|
/* not initialized */
|
|
temp->err = USB_ERR_INVAL;
|
|
return;
|
|
}
|
|
mps = ted->pPacketSize->mps[temp->usb_speed];
|
|
if (mps == 0) {
|
|
/* not initialized */
|
|
temp->err = USB_ERR_INVAL;
|
|
return;
|
|
} else if (mps == UE_ZERO_MPS) {
|
|
/* escape for Zero Max Packet Size */
|
|
mps = 0;
|
|
}
|
|
|
|
/*
|
|
* Fill out the real USB endpoint descriptor
|
|
* in case there is a buffer present:
|
|
*/
|
|
if (temp->buf) {
|
|
ed = USB_ADD_BYTES(temp->buf, old_size);
|
|
if (et == UE_ISOCHRONOUS)
|
|
ed->bLength = sizeof(*ed) + 2;
|
|
else
|
|
ed->bLength = sizeof(*ed);
|
|
ed->bDescriptorType = UDESC_ENDPOINT;
|
|
ed->bEndpointAddress = ea;
|
|
ed->bmAttributes = ted->bmAttributes;
|
|
USETW(ed->wMaxPacketSize, mps);
|
|
|
|
/* setup bInterval parameter */
|
|
|
|
if (ted->pIntervals &&
|
|
ted->pIntervals->bInterval[temp->usb_speed]) {
|
|
ed->bInterval =
|
|
ted->pIntervals->bInterval[temp->usb_speed];
|
|
} else {
|
|
switch (et) {
|
|
case UE_BULK:
|
|
case UE_CONTROL:
|
|
ed->bInterval = 0; /* not used */
|
|
break;
|
|
case UE_INTERRUPT:
|
|
switch (temp->usb_speed) {
|
|
case USB_SPEED_LOW:
|
|
case USB_SPEED_FULL:
|
|
ed->bInterval = 1; /* 1 ms */
|
|
break;
|
|
default:
|
|
ed->bInterval = 4; /* 1 ms */
|
|
break;
|
|
}
|
|
break;
|
|
default: /* UE_ISOCHRONOUS */
|
|
switch (temp->usb_speed) {
|
|
case USB_SPEED_LOW:
|
|
case USB_SPEED_FULL:
|
|
ed->bInterval = 1; /* 1 ms */
|
|
break;
|
|
default:
|
|
ed->bInterval = 1; /* 125 us */
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
temp->bNumEndpoints++;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_make_interface_desc
|
|
*
|
|
* This function will generate an USB interface descriptor from the
|
|
* given USB template interface descriptor, which will be inserted
|
|
* into the USB configuration.
|
|
*------------------------------------------------------------------------*/
|
|
static void
|
|
usb_make_interface_desc(struct usb_temp_setup *temp,
|
|
const struct usb_temp_interface_desc *tid)
|
|
{
|
|
struct usb_interface_descriptor *id;
|
|
const struct usb_temp_endpoint_desc **ted;
|
|
const void **rd;
|
|
uint16_t old_size;
|
|
|
|
/* Reserve memory */
|
|
|
|
old_size = temp->size;
|
|
temp->size += sizeof(*id);
|
|
|
|
/* Update interface and alternate interface numbers */
|
|
|
|
if (tid->isAltInterface == 0) {
|
|
temp->bAlternateSetting = 0;
|
|
temp->bInterfaceNumber++;
|
|
} else {
|
|
temp->bAlternateSetting++;
|
|
}
|
|
|
|
/* Scan all Raw Descriptors first */
|
|
|
|
rd = tid->ppRawDesc;
|
|
|
|
if (rd) {
|
|
while (*rd) {
|
|
usb_make_raw_desc(temp, *rd);
|
|
rd++;
|
|
}
|
|
}
|
|
/* Reset some counters */
|
|
|
|
temp->bNumEndpoints = 0;
|
|
|
|
/* Scan all Endpoint Descriptors second */
|
|
|
|
ted = tid->ppEndpoints;
|
|
if (ted) {
|
|
while (*ted) {
|
|
usb_make_endpoint_desc(temp, *ted);
|
|
ted++;
|
|
}
|
|
}
|
|
/*
|
|
* Fill out the real USB interface descriptor
|
|
* in case there is a buffer present:
|
|
*/
|
|
if (temp->buf) {
|
|
id = USB_ADD_BYTES(temp->buf, old_size);
|
|
id->bLength = sizeof(*id);
|
|
id->bDescriptorType = UDESC_INTERFACE;
|
|
id->bInterfaceNumber = temp->bInterfaceNumber;
|
|
id->bAlternateSetting = temp->bAlternateSetting;
|
|
id->bNumEndpoints = temp->bNumEndpoints;
|
|
id->bInterfaceClass = tid->bInterfaceClass;
|
|
id->bInterfaceSubClass = tid->bInterfaceSubClass;
|
|
id->bInterfaceProtocol = tid->bInterfaceProtocol;
|
|
id->iInterface = tid->iInterface;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_make_config_desc
|
|
*
|
|
* This function will generate an USB config descriptor from the given
|
|
* USB template config descriptor, which will be inserted into the USB
|
|
* configuration.
|
|
*------------------------------------------------------------------------*/
|
|
static void
|
|
usb_make_config_desc(struct usb_temp_setup *temp,
|
|
const struct usb_temp_config_desc *tcd)
|
|
{
|
|
struct usb_config_descriptor *cd;
|
|
const struct usb_temp_interface_desc **tid;
|
|
uint16_t old_size;
|
|
int power;
|
|
|
|
/* Reserve memory */
|
|
|
|
old_size = temp->size;
|
|
temp->size += sizeof(*cd);
|
|
|
|
/* Reset some counters */
|
|
|
|
temp->bInterfaceNumber = 0xFF;
|
|
temp->bAlternateSetting = 0;
|
|
|
|
/* Scan all the USB interfaces */
|
|
|
|
tid = tcd->ppIfaceDesc;
|
|
if (tid) {
|
|
while (*tid) {
|
|
usb_make_interface_desc(temp, *tid);
|
|
tid++;
|
|
}
|
|
}
|
|
/*
|
|
* Fill out the real USB config descriptor
|
|
* in case there is a buffer present:
|
|
*/
|
|
if (temp->buf) {
|
|
cd = USB_ADD_BYTES(temp->buf, old_size);
|
|
|
|
/* compute total size */
|
|
old_size = temp->size - old_size;
|
|
|
|
cd->bLength = sizeof(*cd);
|
|
cd->bDescriptorType = UDESC_CONFIG;
|
|
USETW(cd->wTotalLength, old_size);
|
|
cd->bNumInterface = temp->bInterfaceNumber + 1;
|
|
cd->bConfigurationValue = temp->bConfigurationValue;
|
|
cd->iConfiguration = tcd->iConfiguration;
|
|
cd->bmAttributes = tcd->bmAttributes;
|
|
|
|
power = usb_template_power;
|
|
cd->bMaxPower = power / 2; /* 2 mA units */
|
|
cd->bmAttributes |= UC_REMOTE_WAKEUP;
|
|
if (power > 0) {
|
|
cd->bmAttributes |= UC_BUS_POWERED;
|
|
cd->bmAttributes &= ~UC_SELF_POWERED;
|
|
} else {
|
|
cd->bmAttributes &= ~UC_BUS_POWERED;
|
|
cd->bmAttributes |= UC_SELF_POWERED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_make_device_desc
|
|
*
|
|
* This function will generate an USB device descriptor from the
|
|
* given USB template device descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static void
|
|
usb_make_device_desc(struct usb_temp_setup *temp,
|
|
const struct usb_temp_device_desc *tdd)
|
|
{
|
|
struct usb_temp_data *utd;
|
|
const struct usb_temp_config_desc **tcd;
|
|
uint16_t old_size;
|
|
|
|
/* Reserve memory */
|
|
|
|
old_size = temp->size;
|
|
temp->size += sizeof(*utd);
|
|
|
|
/* Scan all the USB configs */
|
|
|
|
temp->bConfigurationValue = 1;
|
|
tcd = tdd->ppConfigDesc;
|
|
if (tcd) {
|
|
while (*tcd) {
|
|
usb_make_config_desc(temp, *tcd);
|
|
temp->bConfigurationValue++;
|
|
tcd++;
|
|
}
|
|
}
|
|
/*
|
|
* Fill out the real USB device descriptor
|
|
* in case there is a buffer present:
|
|
*/
|
|
|
|
if (temp->buf) {
|
|
utd = USB_ADD_BYTES(temp->buf, old_size);
|
|
|
|
/* Store a pointer to our template device descriptor */
|
|
utd->tdd = tdd;
|
|
|
|
/* Fill out USB device descriptor */
|
|
utd->udd.bLength = sizeof(utd->udd);
|
|
utd->udd.bDescriptorType = UDESC_DEVICE;
|
|
utd->udd.bDeviceClass = tdd->bDeviceClass;
|
|
utd->udd.bDeviceSubClass = tdd->bDeviceSubClass;
|
|
utd->udd.bDeviceProtocol = tdd->bDeviceProtocol;
|
|
USETW(utd->udd.idVendor, tdd->idVendor);
|
|
USETW(utd->udd.idProduct, tdd->idProduct);
|
|
USETW(utd->udd.bcdDevice, tdd->bcdDevice);
|
|
utd->udd.iManufacturer = tdd->iManufacturer;
|
|
utd->udd.iProduct = tdd->iProduct;
|
|
utd->udd.iSerialNumber = tdd->iSerialNumber;
|
|
utd->udd.bNumConfigurations = temp->bConfigurationValue - 1;
|
|
|
|
/*
|
|
* Fill out the USB device qualifier. Pretend that we
|
|
* don't support any other speeds by setting
|
|
* "bNumConfigurations" equal to zero. That saves us
|
|
* generating an extra set of configuration
|
|
* descriptors.
|
|
*/
|
|
utd->udq.bLength = sizeof(utd->udq);
|
|
utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER;
|
|
utd->udq.bDeviceClass = tdd->bDeviceClass;
|
|
utd->udq.bDeviceSubClass = tdd->bDeviceSubClass;
|
|
utd->udq.bDeviceProtocol = tdd->bDeviceProtocol;
|
|
utd->udq.bNumConfigurations = 0;
|
|
USETW(utd->udq.bcdUSB, 0x0200);
|
|
utd->udq.bMaxPacketSize0 = 0;
|
|
|
|
switch (temp->usb_speed) {
|
|
case USB_SPEED_LOW:
|
|
USETW(utd->udd.bcdUSB, 0x0110);
|
|
utd->udd.bMaxPacketSize = 8;
|
|
break;
|
|
case USB_SPEED_FULL:
|
|
USETW(utd->udd.bcdUSB, 0x0110);
|
|
utd->udd.bMaxPacketSize = 32;
|
|
break;
|
|
case USB_SPEED_HIGH:
|
|
USETW(utd->udd.bcdUSB, 0x0200);
|
|
utd->udd.bMaxPacketSize = 64;
|
|
break;
|
|
case USB_SPEED_VARIABLE:
|
|
USETW(utd->udd.bcdUSB, 0x0250);
|
|
utd->udd.bMaxPacketSize = 255; /* 512 bytes */
|
|
break;
|
|
case USB_SPEED_SUPER:
|
|
USETW(utd->udd.bcdUSB, 0x0300);
|
|
utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */
|
|
break;
|
|
default:
|
|
temp->err = USB_ERR_INVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_hw_ep_match
|
|
*
|
|
* Return values:
|
|
* 0: The endpoint profile does not match the criteria
|
|
* Else: The endpoint profile matches the criteria
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
usb_hw_ep_match(const struct usb_hw_ep_profile *pf,
|
|
uint8_t ep_type, uint8_t ep_dir_in)
|
|
{
|
|
if (ep_type == UE_CONTROL) {
|
|
/* special */
|
|
return (pf->support_control);
|
|
}
|
|
if ((pf->support_in && ep_dir_in) ||
|
|
(pf->support_out && !ep_dir_in)) {
|
|
if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) ||
|
|
(pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) ||
|
|
(pf->support_bulk && (ep_type == UE_BULK))) {
|
|
return (1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_hw_ep_find_match
|
|
*
|
|
* This function is used to find the best matching endpoint profile
|
|
* for and endpoint belonging to an USB descriptor.
|
|
*
|
|
* Return values:
|
|
* 0: Success. Got a match.
|
|
* Else: Failure. No match.
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues,
|
|
struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex)
|
|
{
|
|
const struct usb_hw_ep_profile *pf;
|
|
uint16_t distance;
|
|
uint16_t temp;
|
|
uint16_t max_frame_size;
|
|
uint8_t n;
|
|
uint8_t best_n;
|
|
uint8_t dir_in;
|
|
uint8_t dir_out;
|
|
|
|
distance = 0xFFFF;
|
|
best_n = 0;
|
|
|
|
if ((!ep->needs_in) && (!ep->needs_out)) {
|
|
return (0); /* we are done */
|
|
}
|
|
if (ep->needs_ep_type == UE_CONTROL) {
|
|
dir_in = 1;
|
|
dir_out = 1;
|
|
} else {
|
|
if (ep->needs_in) {
|
|
dir_in = 1;
|
|
dir_out = 0;
|
|
} else {
|
|
dir_in = 0;
|
|
dir_out = 1;
|
|
}
|
|
}
|
|
|
|
for (n = 1; n != (USB_EP_MAX / 2); n++) {
|
|
|
|
/* get HW endpoint profile */
|
|
(ues->methods->get_hw_ep_profile) (ues->udev, &pf, n);
|
|
if (pf == NULL) {
|
|
/* end of profiles */
|
|
break;
|
|
}
|
|
/* check if IN-endpoint is reserved */
|
|
if (dir_in || pf->is_simplex) {
|
|
if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) {
|
|
/* mismatch */
|
|
continue;
|
|
}
|
|
}
|
|
/* check if OUT-endpoint is reserved */
|
|
if (dir_out || pf->is_simplex) {
|
|
if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) {
|
|
/* mismatch */
|
|
continue;
|
|
}
|
|
}
|
|
/* check simplex */
|
|
if (pf->is_simplex == is_simplex) {
|
|
/* mismatch */
|
|
continue;
|
|
}
|
|
/* check if HW endpoint matches */
|
|
if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) {
|
|
/* mismatch */
|
|
continue;
|
|
}
|
|
/* get maximum frame size */
|
|
if (dir_in)
|
|
max_frame_size = pf->max_in_frame_size;
|
|
else
|
|
max_frame_size = pf->max_out_frame_size;
|
|
|
|
/* check if we have a matching profile */
|
|
if (max_frame_size >= ep->max_frame_size) {
|
|
temp = (max_frame_size - ep->max_frame_size);
|
|
if (distance > temp) {
|
|
distance = temp;
|
|
best_n = n;
|
|
ep->pf = pf;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* see if we got a match */
|
|
if (best_n != 0) {
|
|
/* get the correct profile */
|
|
pf = ep->pf;
|
|
|
|
/* reserve IN-endpoint */
|
|
if (dir_in) {
|
|
ues->bmInAlloc[best_n / 8] |=
|
|
(1 << (best_n % 8));
|
|
ep->hw_endpoint_in = best_n | UE_DIR_IN;
|
|
ep->needs_in = 0;
|
|
}
|
|
/* reserve OUT-endpoint */
|
|
if (dir_out) {
|
|
ues->bmOutAlloc[best_n / 8] |=
|
|
(1 << (best_n % 8));
|
|
ep->hw_endpoint_out = best_n | UE_DIR_OUT;
|
|
ep->needs_out = 0;
|
|
}
|
|
return (0); /* got a match */
|
|
}
|
|
return (1); /* failure */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_hw_ep_get_needs
|
|
*
|
|
* This function will figure out the type and number of endpoints
|
|
* which are needed for an USB configuration.
|
|
*
|
|
* Return values:
|
|
* 0: Success.
|
|
* Else: Failure.
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues,
|
|
uint8_t ep_type, uint8_t is_complete)
|
|
{
|
|
const struct usb_hw_ep_profile *pf;
|
|
struct usb_hw_ep_scratch_sub *ep_iface;
|
|
struct usb_hw_ep_scratch_sub *ep_curr;
|
|
struct usb_hw_ep_scratch_sub *ep_max;
|
|
struct usb_hw_ep_scratch_sub *ep_end;
|
|
struct usb_descriptor *desc;
|
|
struct usb_interface_descriptor *id;
|
|
struct usb_endpoint_descriptor *ed;
|
|
enum usb_dev_speed speed;
|
|
uint16_t wMaxPacketSize;
|
|
uint16_t temp;
|
|
uint8_t ep_no;
|
|
|
|
ep_iface = ues->ep_max;
|
|
ep_curr = ues->ep_max;
|
|
ep_end = ues->ep + USB_EP_MAX;
|
|
ep_max = ues->ep_max;
|
|
desc = NULL;
|
|
speed = usbd_get_speed(ues->udev);
|
|
|
|
repeat:
|
|
|
|
while ((desc = usb_desc_foreach(ues->cd, desc))) {
|
|
|
|
if ((desc->bDescriptorType == UDESC_INTERFACE) &&
|
|
(desc->bLength >= sizeof(*id))) {
|
|
|
|
id = (void *)desc;
|
|
|
|
if (id->bAlternateSetting == 0) {
|
|
/* going forward */
|
|
ep_iface = ep_max;
|
|
} else {
|
|
/* reset */
|
|
ep_curr = ep_iface;
|
|
}
|
|
}
|
|
if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
|
|
(desc->bLength >= sizeof(*ed))) {
|
|
|
|
ed = (void *)desc;
|
|
|
|
goto handle_endpoint_desc;
|
|
}
|
|
}
|
|
ues->ep_max = ep_max;
|
|
return (0);
|
|
|
|
handle_endpoint_desc:
|
|
temp = (ed->bmAttributes & UE_XFERTYPE);
|
|
|
|
if (temp == ep_type) {
|
|
|
|
if (ep_curr == ep_end) {
|
|
/* too many endpoints */
|
|
return (1); /* failure */
|
|
}
|
|
wMaxPacketSize = UGETW(ed->wMaxPacketSize);
|
|
if ((wMaxPacketSize & 0xF800) &&
|
|
(speed == USB_SPEED_HIGH)) {
|
|
/* handle packet multiplier */
|
|
temp = (wMaxPacketSize >> 11) & 3;
|
|
wMaxPacketSize &= 0x7FF;
|
|
if (temp == 1) {
|
|
wMaxPacketSize *= 2;
|
|
} else {
|
|
wMaxPacketSize *= 3;
|
|
}
|
|
}
|
|
/*
|
|
* Check if we have a fixed endpoint number, else the
|
|
* endpoint number is allocated dynamically:
|
|
*/
|
|
ep_no = (ed->bEndpointAddress & UE_ADDR);
|
|
if (ep_no != 0) {
|
|
|
|
/* get HW endpoint profile */
|
|
(ues->methods->get_hw_ep_profile)
|
|
(ues->udev, &pf, ep_no);
|
|
if (pf == NULL) {
|
|
/* HW profile does not exist - failure */
|
|
DPRINTFN(0, "Endpoint profile %u "
|
|
"does not exist\n", ep_no);
|
|
return (1);
|
|
}
|
|
/* reserve fixed endpoint number */
|
|
if (ep_type == UE_CONTROL) {
|
|
ues->bmInAlloc[ep_no / 8] |=
|
|
(1 << (ep_no % 8));
|
|
ues->bmOutAlloc[ep_no / 8] |=
|
|
(1 << (ep_no % 8));
|
|
if ((pf->max_in_frame_size < wMaxPacketSize) ||
|
|
(pf->max_out_frame_size < wMaxPacketSize)) {
|
|
DPRINTFN(0, "Endpoint profile %u "
|
|
"has too small buffer\n", ep_no);
|
|
return (1);
|
|
}
|
|
} else if (ed->bEndpointAddress & UE_DIR_IN) {
|
|
ues->bmInAlloc[ep_no / 8] |=
|
|
(1 << (ep_no % 8));
|
|
if (pf->max_in_frame_size < wMaxPacketSize) {
|
|
DPRINTFN(0, "Endpoint profile %u "
|
|
"has too small buffer\n", ep_no);
|
|
return (1);
|
|
}
|
|
} else {
|
|
ues->bmOutAlloc[ep_no / 8] |=
|
|
(1 << (ep_no % 8));
|
|
if (pf->max_out_frame_size < wMaxPacketSize) {
|
|
DPRINTFN(0, "Endpoint profile %u "
|
|
"has too small buffer\n", ep_no);
|
|
return (1);
|
|
}
|
|
}
|
|
} else if (is_complete) {
|
|
|
|
/* check if we have enough buffer space */
|
|
if (wMaxPacketSize >
|
|
ep_curr->max_frame_size) {
|
|
return (1); /* failure */
|
|
}
|
|
if (ed->bEndpointAddress & UE_DIR_IN) {
|
|
ed->bEndpointAddress =
|
|
ep_curr->hw_endpoint_in;
|
|
} else {
|
|
ed->bEndpointAddress =
|
|
ep_curr->hw_endpoint_out;
|
|
}
|
|
|
|
} else {
|
|
|
|
/* compute the maximum frame size */
|
|
if (ep_curr->max_frame_size < wMaxPacketSize) {
|
|
ep_curr->max_frame_size = wMaxPacketSize;
|
|
}
|
|
if (temp == UE_CONTROL) {
|
|
ep_curr->needs_in = 1;
|
|
ep_curr->needs_out = 1;
|
|
} else {
|
|
if (ed->bEndpointAddress & UE_DIR_IN) {
|
|
ep_curr->needs_in = 1;
|
|
} else {
|
|
ep_curr->needs_out = 1;
|
|
}
|
|
}
|
|
ep_curr->needs_ep_type = ep_type;
|
|
}
|
|
|
|
ep_curr++;
|
|
if (ep_max < ep_curr) {
|
|
ep_max = ep_curr;
|
|
}
|
|
}
|
|
goto repeat;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_hw_ep_resolve
|
|
*
|
|
* This function will try to resolve endpoint requirements by the
|
|
* given endpoint profiles that the USB hardware reports.
|
|
*
|
|
* Return values:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static usb_error_t
|
|
usb_hw_ep_resolve(struct usb_device *udev,
|
|
struct usb_descriptor *desc)
|
|
{
|
|
struct usb_hw_ep_scratch *ues;
|
|
struct usb_hw_ep_scratch_sub *ep;
|
|
const struct usb_hw_ep_profile *pf;
|
|
const struct usb_bus_methods *methods;
|
|
struct usb_device_descriptor *dd;
|
|
uint16_t mps;
|
|
|
|
if (desc == NULL)
|
|
return (USB_ERR_INVAL);
|
|
|
|
/* get bus methods */
|
|
methods = udev->bus->methods;
|
|
|
|
if (methods->get_hw_ep_profile == NULL)
|
|
return (USB_ERR_INVAL);
|
|
|
|
if (desc->bDescriptorType == UDESC_DEVICE) {
|
|
|
|
if (desc->bLength < sizeof(*dd))
|
|
return (USB_ERR_INVAL);
|
|
|
|
dd = (void *)desc;
|
|
|
|
/* get HW control endpoint 0 profile */
|
|
(methods->get_hw_ep_profile) (udev, &pf, 0);
|
|
if (pf == NULL) {
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) {
|
|
DPRINTFN(0, "Endpoint 0 does not "
|
|
"support control\n");
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
mps = dd->bMaxPacketSize;
|
|
|
|
if (udev->speed == USB_SPEED_FULL) {
|
|
/*
|
|
* We can optionally choose another packet size !
|
|
*/
|
|
while (1) {
|
|
/* check if "mps" is ok */
|
|
if (pf->max_in_frame_size >= mps) {
|
|
break;
|
|
}
|
|
/* reduce maximum packet size */
|
|
mps /= 2;
|
|
|
|
/* check if "mps" is too small */
|
|
if (mps < 8) {
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
}
|
|
|
|
dd->bMaxPacketSize = mps;
|
|
|
|
} else {
|
|
/* We only have one choice */
|
|
if (mps == 255) {
|
|
mps = 512;
|
|
}
|
|
/* Check if we support the specified wMaxPacketSize */
|
|
if (pf->max_in_frame_size < mps) {
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
}
|
|
return (0); /* success */
|
|
}
|
|
if (desc->bDescriptorType != UDESC_CONFIG)
|
|
return (USB_ERR_INVAL);
|
|
if (desc->bLength < sizeof(*(ues->cd)))
|
|
return (USB_ERR_INVAL);
|
|
|
|
ues = udev->scratch.hw_ep_scratch;
|
|
|
|
memset(ues, 0, sizeof(*ues));
|
|
|
|
ues->ep_max = ues->ep;
|
|
ues->cd = (void *)desc;
|
|
ues->methods = methods;
|
|
ues->udev = udev;
|
|
|
|
/* Get all the endpoints we need */
|
|
|
|
if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) ||
|
|
usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) ||
|
|
usb_hw_ep_get_needs(ues, UE_CONTROL, 0) ||
|
|
usb_hw_ep_get_needs(ues, UE_BULK, 0)) {
|
|
DPRINTFN(0, "Could not get needs\n");
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
for (ep = ues->ep; ep != ues->ep_max; ep++) {
|
|
|
|
while (ep->needs_in || ep->needs_out) {
|
|
|
|
/*
|
|
* First try to use a simplex endpoint.
|
|
* Then try to use a duplex endpoint.
|
|
*/
|
|
if (usb_hw_ep_find_match(ues, ep, 1) &&
|
|
usb_hw_ep_find_match(ues, ep, 0)) {
|
|
DPRINTFN(0, "Could not find match\n");
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
ues->ep_max = ues->ep;
|
|
|
|
/* Update all endpoint addresses */
|
|
|
|
if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) ||
|
|
usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) ||
|
|
usb_hw_ep_get_needs(ues, UE_CONTROL, 1) ||
|
|
usb_hw_ep_get_needs(ues, UE_BULK, 1)) {
|
|
DPRINTFN(0, "Could not update endpoint address\n");
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_tdd
|
|
*
|
|
* Returns:
|
|
* NULL: No USB template device descriptor found.
|
|
* Else: Pointer to the USB template device descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static const struct usb_temp_device_desc *
|
|
usb_temp_get_tdd(struct usb_device *udev)
|
|
{
|
|
if (udev->usb_template_ptr == NULL) {
|
|
return (NULL);
|
|
}
|
|
return (udev->usb_template_ptr->tdd);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_device_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No USB device descriptor found.
|
|
* Else: Pointer to USB device descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static void *
|
|
usb_temp_get_device_desc(struct usb_device *udev)
|
|
{
|
|
struct usb_device_descriptor *dd;
|
|
|
|
if (udev->usb_template_ptr == NULL) {
|
|
return (NULL);
|
|
}
|
|
dd = &udev->usb_template_ptr->udd;
|
|
if (dd->bDescriptorType != UDESC_DEVICE) {
|
|
/* sanity check failed */
|
|
return (NULL);
|
|
}
|
|
return (dd);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_qualifier_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No USB device_qualifier descriptor found.
|
|
* Else: Pointer to USB device_qualifier descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static void *
|
|
usb_temp_get_qualifier_desc(struct usb_device *udev)
|
|
{
|
|
struct usb_device_qualifier *dq;
|
|
|
|
if (udev->usb_template_ptr == NULL) {
|
|
return (NULL);
|
|
}
|
|
dq = &udev->usb_template_ptr->udq;
|
|
if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) {
|
|
/* sanity check failed */
|
|
return (NULL);
|
|
}
|
|
return (dq);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_config_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No USB config descriptor found.
|
|
* Else: Pointer to USB config descriptor having index "index".
|
|
*------------------------------------------------------------------------*/
|
|
static void *
|
|
usb_temp_get_config_desc(struct usb_device *udev,
|
|
uint16_t *pLength, uint8_t index)
|
|
{
|
|
struct usb_device_descriptor *dd;
|
|
struct usb_config_descriptor *cd;
|
|
uint16_t temp;
|
|
|
|
if (udev->usb_template_ptr == NULL) {
|
|
return (NULL);
|
|
}
|
|
dd = &udev->usb_template_ptr->udd;
|
|
cd = (void *)(udev->usb_template_ptr + 1);
|
|
|
|
if (index >= dd->bNumConfigurations) {
|
|
/* out of range */
|
|
return (NULL);
|
|
}
|
|
while (index--) {
|
|
if (cd->bDescriptorType != UDESC_CONFIG) {
|
|
/* sanity check failed */
|
|
return (NULL);
|
|
}
|
|
temp = UGETW(cd->wTotalLength);
|
|
cd = USB_ADD_BYTES(cd, temp);
|
|
}
|
|
|
|
if (pLength) {
|
|
*pLength = UGETW(cd->wTotalLength);
|
|
}
|
|
return (cd);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_vendor_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No vendor descriptor found.
|
|
* Else: Pointer to a vendor descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static const void *
|
|
usb_temp_get_vendor_desc(struct usb_device *udev,
|
|
const struct usb_device_request *req, uint16_t *plen)
|
|
{
|
|
const struct usb_temp_device_desc *tdd;
|
|
|
|
tdd = usb_temp_get_tdd(udev);
|
|
if (tdd == NULL) {
|
|
return (NULL);
|
|
}
|
|
if (tdd->getVendorDesc == NULL) {
|
|
return (NULL);
|
|
}
|
|
return ((tdd->getVendorDesc) (req, plen));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_string_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No string descriptor found.
|
|
* Else: Pointer to a string descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static const void *
|
|
usb_temp_get_string_desc(struct usb_device *udev,
|
|
uint16_t lang_id, uint8_t string_index)
|
|
{
|
|
const struct usb_temp_device_desc *tdd;
|
|
|
|
tdd = usb_temp_get_tdd(udev);
|
|
if (tdd == NULL) {
|
|
return (NULL);
|
|
}
|
|
if (tdd->getStringDesc == NULL) {
|
|
return (NULL);
|
|
}
|
|
return ((tdd->getStringDesc) (lang_id, string_index));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_hub_desc
|
|
*
|
|
* Returns:
|
|
* NULL: No USB HUB descriptor found.
|
|
* Else: Pointer to a USB HUB descriptor.
|
|
*------------------------------------------------------------------------*/
|
|
static const void *
|
|
usb_temp_get_hub_desc(struct usb_device *udev)
|
|
{
|
|
return (NULL); /* needs to be implemented */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_get_desc
|
|
*
|
|
* This function is a demultiplexer for local USB device side control
|
|
* endpoint requests.
|
|
*------------------------------------------------------------------------*/
|
|
static usb_error_t
|
|
usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req,
|
|
const void **pPtr, uint16_t *pLength)
|
|
{
|
|
const uint8_t *buf;
|
|
uint16_t len;
|
|
|
|
buf = NULL;
|
|
len = 0;
|
|
|
|
switch (req->bmRequestType) {
|
|
case UT_READ_DEVICE:
|
|
switch (req->bRequest) {
|
|
case UR_GET_DESCRIPTOR:
|
|
goto tr_handle_get_descriptor;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
case UT_READ_CLASS_DEVICE:
|
|
switch (req->bRequest) {
|
|
case UR_GET_DESCRIPTOR:
|
|
goto tr_handle_get_class_descriptor;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
|
|
tr_handle_get_descriptor:
|
|
switch (req->wValue[1]) {
|
|
case UDESC_DEVICE:
|
|
if (req->wValue[0]) {
|
|
goto tr_stalled;
|
|
}
|
|
buf = usb_temp_get_device_desc(udev);
|
|
goto tr_valid;
|
|
case UDESC_DEVICE_QUALIFIER:
|
|
if (udev->speed != USB_SPEED_HIGH) {
|
|
goto tr_stalled;
|
|
}
|
|
if (req->wValue[0]) {
|
|
goto tr_stalled;
|
|
}
|
|
buf = usb_temp_get_qualifier_desc(udev);
|
|
goto tr_valid;
|
|
case UDESC_OTHER_SPEED_CONFIGURATION:
|
|
if (udev->speed != USB_SPEED_HIGH) {
|
|
goto tr_stalled;
|
|
}
|
|
case UDESC_CONFIG:
|
|
buf = usb_temp_get_config_desc(udev,
|
|
&len, req->wValue[0]);
|
|
goto tr_valid;
|
|
case UDESC_STRING:
|
|
buf = usb_temp_get_string_desc(udev,
|
|
UGETW(req->wIndex), req->wValue[0]);
|
|
goto tr_valid;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
|
|
tr_handle_get_class_descriptor:
|
|
if (req->wValue[0]) {
|
|
goto tr_stalled;
|
|
}
|
|
buf = usb_temp_get_hub_desc(udev);
|
|
goto tr_valid;
|
|
|
|
tr_valid:
|
|
if (buf == NULL)
|
|
goto tr_stalled;
|
|
if (len == 0)
|
|
len = buf[0];
|
|
*pPtr = buf;
|
|
*pLength = len;
|
|
return (0); /* success */
|
|
|
|
tr_stalled:
|
|
/* try to get a vendor specific descriptor */
|
|
len = 0;
|
|
buf = usb_temp_get_vendor_desc(udev, req, &len);
|
|
if (buf != NULL)
|
|
goto tr_valid;
|
|
*pPtr = NULL;
|
|
*pLength = 0;
|
|
return (0); /* we ignore failures */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_setup
|
|
*
|
|
* This function generates USB descriptors according to the given USB
|
|
* template device descriptor. It will also try to figure out the best
|
|
* matching endpoint addresses using the hardware endpoint profiles.
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
usb_error_t
|
|
usb_temp_setup(struct usb_device *udev,
|
|
const struct usb_temp_device_desc *tdd)
|
|
{
|
|
struct usb_temp_setup *uts;
|
|
void *buf;
|
|
usb_error_t error;
|
|
uint8_t n;
|
|
uint8_t do_unlock;
|
|
|
|
/* be NULL safe */
|
|
if (tdd == NULL)
|
|
return (0);
|
|
|
|
/* Protect scratch area */
|
|
do_unlock = usbd_ctrl_lock(udev);
|
|
|
|
uts = udev->scratch.temp_setup;
|
|
|
|
memset(uts, 0, sizeof(*uts));
|
|
|
|
uts->usb_speed = udev->speed;
|
|
uts->self_powered = udev->flags.self_powered;
|
|
|
|
/* first pass */
|
|
|
|
usb_make_device_desc(uts, tdd);
|
|
|
|
if (uts->err) {
|
|
/* some error happened */
|
|
goto done;
|
|
}
|
|
/* sanity check */
|
|
if (uts->size == 0) {
|
|
uts->err = USB_ERR_INVAL;
|
|
goto done;
|
|
}
|
|
/* allocate zeroed memory */
|
|
uts->buf = usbd_alloc_config_desc(udev, uts->size);
|
|
/*
|
|
* Allow malloc() to return NULL regardless of M_WAITOK flag.
|
|
* This helps when porting the software to non-FreeBSD
|
|
* systems.
|
|
*/
|
|
if (uts->buf == NULL) {
|
|
/* could not allocate memory */
|
|
uts->err = USB_ERR_NOMEM;
|
|
goto done;
|
|
}
|
|
/* second pass */
|
|
|
|
uts->size = 0;
|
|
|
|
usb_make_device_desc(uts, tdd);
|
|
|
|
/*
|
|
* Store a pointer to our descriptors:
|
|
*/
|
|
udev->usb_template_ptr = uts->buf;
|
|
|
|
if (uts->err) {
|
|
/* some error happened during second pass */
|
|
goto done;
|
|
}
|
|
/*
|
|
* Resolve all endpoint addresses !
|
|
*/
|
|
buf = usb_temp_get_device_desc(udev);
|
|
uts->err = usb_hw_ep_resolve(udev, buf);
|
|
if (uts->err) {
|
|
DPRINTFN(0, "Could not resolve endpoints for "
|
|
"Device Descriptor, error = %s\n",
|
|
usbd_errstr(uts->err));
|
|
goto done;
|
|
}
|
|
for (n = 0;; n++) {
|
|
|
|
buf = usb_temp_get_config_desc(udev, NULL, n);
|
|
if (buf == NULL) {
|
|
break;
|
|
}
|
|
uts->err = usb_hw_ep_resolve(udev, buf);
|
|
if (uts->err) {
|
|
DPRINTFN(0, "Could not resolve endpoints for "
|
|
"Config Descriptor %u, error = %s\n", n,
|
|
usbd_errstr(uts->err));
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
error = uts->err;
|
|
if (error)
|
|
usb_temp_unsetup(udev);
|
|
if (do_unlock)
|
|
usbd_ctrl_unlock(udev);
|
|
return (error);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb_temp_unsetup
|
|
*
|
|
* This function frees any memory associated with the currently
|
|
* setup template, if any.
|
|
*------------------------------------------------------------------------*/
|
|
void
|
|
usb_temp_unsetup(struct usb_device *udev)
|
|
{
|
|
usbd_free_config_desc(udev, udev->usb_template_ptr);
|
|
udev->usb_template_ptr = NULL;
|
|
}
|
|
|
|
static usb_error_t
|
|
usb_temp_setup_by_index(struct usb_device *udev, uint16_t index)
|
|
{
|
|
usb_error_t err;
|
|
|
|
switch (index) {
|
|
case USB_TEMP_MSC:
|
|
err = usb_temp_setup(udev, &usb_template_msc);
|
|
break;
|
|
case USB_TEMP_CDCE:
|
|
err = usb_temp_setup(udev, &usb_template_cdce);
|
|
break;
|
|
case USB_TEMP_MTP:
|
|
err = usb_temp_setup(udev, &usb_template_mtp);
|
|
break;
|
|
case USB_TEMP_MODEM:
|
|
err = usb_temp_setup(udev, &usb_template_modem);
|
|
break;
|
|
case USB_TEMP_AUDIO:
|
|
err = usb_temp_setup(udev, &usb_template_audio);
|
|
break;
|
|
case USB_TEMP_KBD:
|
|
err = usb_temp_setup(udev, &usb_template_kbd);
|
|
break;
|
|
case USB_TEMP_MOUSE:
|
|
err = usb_temp_setup(udev, &usb_template_mouse);
|
|
break;
|
|
case USB_TEMP_PHONE:
|
|
err = usb_temp_setup(udev, &usb_template_phone);
|
|
break;
|
|
case USB_TEMP_SERIALNET:
|
|
err = usb_temp_setup(udev, &usb_template_serialnet);
|
|
break;
|
|
case USB_TEMP_MIDI:
|
|
err = usb_temp_setup(udev, &usb_template_midi);
|
|
break;
|
|
case USB_TEMP_MULTI:
|
|
err = usb_temp_setup(udev, &usb_template_multi);
|
|
break;
|
|
case USB_TEMP_CDCEEM:
|
|
err = usb_temp_setup(udev, &usb_template_cdceem);
|
|
break;
|
|
default:
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
usb_temp_init(void *arg)
|
|
{
|
|
/* register our functions */
|
|
usb_temp_get_desc_p = &usb_temp_get_desc;
|
|
usb_temp_setup_by_index_p = &usb_temp_setup_by_index;
|
|
usb_temp_unsetup_p = &usb_temp_unsetup;
|
|
}
|
|
|
|
SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL);
|
|
SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL);
|