Signed-off-by: Husni Faiz <ahamedhusn...@gmail.com> --- freebsd/sys/dev/usb/template/usb_template.c | 1489 +++++++++++++++++ freebsd/sys/dev/usb/template/usb_template.h | 130 ++ .../sys/dev/usb/template/usb_template_cdce.c | 355 ++++ 3 files changed, 1974 insertions(+) create mode 100644 freebsd/sys/dev/usb/template/usb_template.c create mode 100644 freebsd/sys/dev/usb/template/usb_template.h create mode 100644 freebsd/sys/dev/usb/template/usb_template_cdce.c
diff --git a/freebsd/sys/dev/usb/template/usb_template.c b/freebsd/sys/dev/usb/template/usb_template.c new file mode 100644 index 00000000..0650da15 --- /dev/null +++ b/freebsd/sys/dev/usb/template/usb_template.c @@ -0,0 +1,1489 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $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 <rtems/bsd/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 <rtems/bsd/local/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); diff --git a/freebsd/sys/dev/usb/template/usb_template.h b/freebsd/sys/dev/usb/template/usb_template.h new file mode 100644 index 00000000..1bb2424c --- /dev/null +++ b/freebsd/sys/dev/usb/template/usb_template.h @@ -0,0 +1,130 @@ +/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2007 Hans Petter Selasky <hsela...@freebsd.org> + * 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. + */ + +/* USB templates are used to build up real USB descriptors */ + +#ifndef _USB_TEMPLATE_H_ +#define _USB_TEMPLATE_H_ + +#ifndef USB_TEMPLATE_VENDOR +/* + * https://github.com/obdev/v-usb/blob/master/usbdrv/USB-IDs-for-free.txt + */ +#define USB_TEMPLATE_VENDOR 0x16c0 +#define USB_TEMPLATE_MANUFACTURER \ + "The FreeBSD Project (https://www.FreeBSD.org)" +#endif + +typedef const void *(usb_temp_get_string_desc_t)(uint16_t lang_id, uint8_t string_index); +typedef const void *(usb_temp_get_vendor_desc_t)(const struct usb_device_request *req, uint16_t *plen); + +struct usb_temp_packet_size { + uint16_t mps[USB_SPEED_MAX]; +}; + +struct usb_temp_interval { + uint8_t bInterval[USB_SPEED_MAX]; +}; + +struct usb_temp_endpoint_desc { + const void **ppRawDesc; + const struct usb_temp_packet_size *pPacketSize; + const struct usb_temp_interval *pIntervals; + /* + * If (bEndpointAddress & UE_ADDR) is non-zero the endpoint number + * is pre-selected for this endpoint descriptor. Else an endpoint + * number is automatically chosen. + */ + uint8_t bEndpointAddress; /* UE_DIR_IN or UE_DIR_OUT */ + uint8_t bmAttributes; +}; + +struct usb_temp_interface_desc { + const void **ppRawDesc; + const struct usb_temp_endpoint_desc **ppEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + uint8_t isAltInterface; +}; + +struct usb_temp_config_desc { + const struct usb_temp_interface_desc **ppIfaceDesc; + uint8_t bmAttributes; + uint8_t bMaxPower; + uint8_t iConfiguration; +}; + +struct usb_temp_device_desc { + usb_temp_get_string_desc_t *getStringDesc; + usb_temp_get_vendor_desc_t *getVendorDesc; + const struct usb_temp_config_desc **ppConfigDesc; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; +}; + +struct usb_temp_data { + const struct usb_temp_device_desc *tdd; + struct usb_device_descriptor udd; /* device descriptor */ + struct usb_device_qualifier udq; /* device qualifier */ +}; + +/* prototypes */ + +extern struct usb_temp_device_desc usb_template_audio; +extern struct usb_temp_device_desc usb_template_cdce; +extern struct usb_temp_device_desc usb_template_kbd; +extern struct usb_temp_device_desc usb_template_modem; +extern struct usb_temp_device_desc usb_template_mouse; +extern struct usb_temp_device_desc usb_template_msc; +extern struct usb_temp_device_desc usb_template_mtp; +extern struct usb_temp_device_desc usb_template_phone; +extern struct usb_temp_device_desc usb_template_serialnet; +extern struct usb_temp_device_desc usb_template_midi; +extern struct usb_temp_device_desc usb_template_multi; +extern struct usb_temp_device_desc usb_template_cdceem; + +void usb_decode_str_desc(struct usb_string_descriptor *sd, + char *buf, size_t buflen); +usb_error_t usb_temp_setup(struct usb_device *, + const struct usb_temp_device_desc *); +void usb_temp_unsetup(struct usb_device *); +int usb_temp_sysctl(SYSCTL_HANDLER_ARGS); + +SYSCTL_DECL(_hw_usb_templates); + +#endif /* _USB_TEMPLATE_H_ */ diff --git a/freebsd/sys/dev/usb/template/usb_template_cdce.c b/freebsd/sys/dev/usb/template/usb_template_cdce.c new file mode 100644 index 00000000..18568fc2 --- /dev/null +++ b/freebsd/sys/dev/usb/template/usb_template_cdce.c @@ -0,0 +1,355 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $FreeBSD$ */ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2007 Hans Petter Selasky <hsela...@freebsd.org> + * Copyright (c) 2018 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Edward Tomasz Napierala + * under sponsorship from the FreeBSD Foundation. + * + * 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 the USB templates for a CDC USB ethernet device. + */ + +#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 <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_cdc.h> +#include <dev/usb/usb_ioctl.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/template/usb_template.h> +#endif /* USB_GLOBAL_INCLUDE_FILE */ + +enum { + ETH_LANG_INDEX, + ETH_MAC_INDEX, + ETH_CONTROL_INDEX, + ETH_DATA_INDEX, + ETH_CONFIGURATION_INDEX, + ETH_MANUFACTURER_INDEX, + ETH_PRODUCT_INDEX, + ETH_SERIAL_NUMBER_INDEX, + ETH_MAX_INDEX, +}; + +#define ETH_DEFAULT_VENDOR_ID USB_TEMPLATE_VENDOR +#define ETH_DEFAULT_PRODUCT_ID 0x27e1 +#define ETH_DEFAULT_MAC "2A02030405060789AB" +#define ETH_DEFAULT_CONTROL "USB Ethernet Comm Interface" +#define ETH_DEFAULT_DATA "USB Ethernet Data Interface" +#define ETH_DEFAULT_CONFIG "Default Config" +#define ETH_DEFAULT_MANUFACTURER USB_TEMPLATE_MANUFACTURER +#define ETH_DEFAULT_PRODUCT "USB Ethernet Adapter" +#define ETH_DEFAULT_SERIAL_NUMBER "December 2007" + +static struct usb_string_descriptor eth_mac; +static struct usb_string_descriptor eth_control; +static struct usb_string_descriptor eth_data; +static struct usb_string_descriptor eth_configuration; +static struct usb_string_descriptor eth_manufacturer; +static struct usb_string_descriptor eth_product; +static struct usb_string_descriptor eth_serial_number; + +static struct sysctl_ctx_list eth_ctx_list; + +/* prototypes */ + +static usb_temp_get_string_desc_t eth_get_string_desc; + +static const struct usb_cdc_union_descriptor eth_union_desc = { + .bLength = sizeof(eth_union_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_UNION, + .bMasterInterface = 0, /* this is automatically updated */ + .bSlaveInterface[0] = 1, /* this is automatically updated */ +}; + +static const struct usb_cdc_header_descriptor eth_header_desc = { + .bLength = sizeof(eth_header_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_HEADER, + .bcdCDC[0] = 0x10, + .bcdCDC[1] = 0x01, +}; + +static const struct usb_cdc_ethernet_descriptor eth_enf_desc = { + .bLength = sizeof(eth_enf_desc), + .bDescriptorType = UDESC_CS_INTERFACE, + .bDescriptorSubtype = UDESCSUB_CDC_ENF, + .iMacAddress = ETH_MAC_INDEX, + .bmEthernetStatistics = {0, 0, 0, 0}, + .wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */ + .wNumberMCFilters = {0, 0}, + .bNumberPowerFilters = 0, +}; + +static const void *eth_control_if_desc[] = { + ð_union_desc, + ð_header_desc, + ð_enf_desc, + NULL, +}; + +static const struct usb_temp_packet_size bulk_mps = { + .mps[USB_SPEED_FULL] = 64, + .mps[USB_SPEED_HIGH] = 512, +}; + +static const struct usb_temp_packet_size intr_mps = { + .mps[USB_SPEED_FULL] = 8, + .mps[USB_SPEED_HIGH] = 8, +}; + +static const struct usb_temp_endpoint_desc bulk_in_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_IN_EP_0 + .bEndpointAddress = USB_HIP_IN_EP_0, +#else + .bEndpointAddress = UE_DIR_IN, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb_temp_endpoint_desc bulk_out_ep = { + .pPacketSize = &bulk_mps, +#ifdef USB_HIP_OUT_EP_0 + .bEndpointAddress = USB_HIP_OUT_EP_0, +#else + .bEndpointAddress = UE_DIR_OUT, +#endif + .bmAttributes = UE_BULK, +}; + +static const struct usb_temp_endpoint_desc intr_in_ep = { + .pPacketSize = &intr_mps, + .bEndpointAddress = UE_DIR_IN, + .bmAttributes = UE_INTERRUPT, +}; + +static const struct usb_temp_endpoint_desc *eth_intr_endpoints[] = { + &intr_in_ep, + NULL, +}; + +static const struct usb_temp_interface_desc eth_control_interface = { + .ppEndpoints = eth_intr_endpoints, + .ppRawDesc = eth_control_if_desc, + .bInterfaceClass = UICLASS_CDC, + .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, + .bInterfaceProtocol = 0, + .iInterface = ETH_CONTROL_INDEX, +}; + +static const struct usb_temp_endpoint_desc *eth_data_endpoints[] = { + &bulk_in_ep, + &bulk_out_ep, + NULL, +}; + +static const struct usb_temp_interface_desc eth_data_null_interface = { + .ppEndpoints = NULL, /* no endpoints */ + .bInterfaceClass = UICLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = ETH_DATA_INDEX, +}; + +static const struct usb_temp_interface_desc eth_data_interface = { + .ppEndpoints = eth_data_endpoints, + .bInterfaceClass = UICLASS_CDC_DATA, + .bInterfaceSubClass = UISUBCLASS_DATA, + .bInterfaceProtocol = 0, + .iInterface = ETH_DATA_INDEX, + .isAltInterface = 1, /* this is an alternate setting */ +}; + +static const struct usb_temp_interface_desc *eth_interfaces[] = { + ð_control_interface, + ð_data_null_interface, + ð_data_interface, + NULL, +}; + +static const struct usb_temp_config_desc eth_config_desc = { + .ppIfaceDesc = eth_interfaces, + .bmAttributes = 0, + .bMaxPower = 0, + .iConfiguration = ETH_CONFIGURATION_INDEX, +}; + +static const struct usb_temp_config_desc *eth_configs[] = { + ð_config_desc, + NULL, +}; + +struct usb_temp_device_desc usb_template_cdce = { + .getStringDesc = ð_get_string_desc, + .ppConfigDesc = eth_configs, + .idVendor = ETH_DEFAULT_VENDOR_ID, + .idProduct = ETH_DEFAULT_PRODUCT_ID, + .bcdDevice = 0x0100, + .bDeviceClass = UDCLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .iManufacturer = ETH_MANUFACTURER_INDEX, + .iProduct = ETH_PRODUCT_INDEX, + .iSerialNumber = ETH_SERIAL_NUMBER_INDEX, +}; + +/*------------------------------------------------------------------------* + * eth_get_string_desc + * + * Return values: + * NULL: Failure. No such string. + * Else: Success. Pointer to string descriptor is returned. + *------------------------------------------------------------------------*/ +static const void * +eth_get_string_desc(uint16_t lang_id, uint8_t string_index) +{ + static const void *ptr[ETH_MAX_INDEX] = { + [ETH_LANG_INDEX] = &usb_string_lang_en, + [ETH_MAC_INDEX] = ð_mac, + [ETH_CONTROL_INDEX] = ð_control, + [ETH_DATA_INDEX] = ð_data, + [ETH_CONFIGURATION_INDEX] = ð_configuration, + [ETH_MANUFACTURER_INDEX] = ð_manufacturer, + [ETH_PRODUCT_INDEX] = ð_product, + [ETH_SERIAL_NUMBER_INDEX] = ð_serial_number, + }; + + if (string_index == 0) { + return (&usb_string_lang_en); + } + if (lang_id != 0x0409) { + return (NULL); + } + if (string_index < ETH_MAX_INDEX) { + return (ptr[string_index]); + } + return (NULL); +} + +static void +eth_init(void *arg __unused) +{ + struct sysctl_oid *parent; + char parent_name[3]; + + usb_make_str_desc(ð_mac, sizeof(eth_mac), + ETH_DEFAULT_MAC); + usb_make_str_desc(ð_control, sizeof(eth_control), + ETH_DEFAULT_CONTROL); + usb_make_str_desc(ð_data, sizeof(eth_data), + ETH_DEFAULT_DATA); + usb_make_str_desc(ð_configuration, sizeof(eth_configuration), + ETH_DEFAULT_CONFIG); + usb_make_str_desc(ð_manufacturer, sizeof(eth_manufacturer), + ETH_DEFAULT_MANUFACTURER); + usb_make_str_desc(ð_product, sizeof(eth_product), + ETH_DEFAULT_PRODUCT); + usb_make_str_desc(ð_serial_number, sizeof(eth_serial_number), + ETH_DEFAULT_SERIAL_NUMBER); + + snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_CDCE); + sysctl_ctx_init(ð_ctx_list); + + parent = SYSCTL_ADD_NODE(ð_ctx_list, + SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO, + parent_name, CTLFLAG_RW, + 0, "USB CDC Ethernet device side template"); + SYSCTL_ADD_U16(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "vendor_id", CTLFLAG_RWTUN, + &usb_template_cdce.idVendor, 1, "Vendor identifier"); + SYSCTL_ADD_U16(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "product_id", CTLFLAG_RWTUN, + &usb_template_cdce.idProduct, 1, "Product identifier"); + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "mac", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_mac, sizeof(eth_mac), usb_temp_sysctl, + "A", "MAC address string"); +#if 0 + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "control", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_control, sizeof(eth_control), usb_temp_sysctl, + "A", "Control interface string"); + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "data", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_data, sizeof(eth_data), usb_temp_sysctl, + "A", "Data interface string"); + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "configuration", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_configuration, sizeof(eth_configuration), usb_temp_sysctl, + "A", "Configuration string"); +#endif + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "manufacturer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_manufacturer, sizeof(eth_manufacturer), usb_temp_sysctl, + "A", "Manufacturer string"); + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_product, sizeof(eth_product), usb_temp_sysctl, + "A", "Product string"); + SYSCTL_ADD_PROC(ð_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, + "serial_number", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, + ð_serial_number, sizeof(eth_serial_number), usb_temp_sysctl, + "A", "Serial number string"); +} + +static void +eth_uninit(void *arg __unused) +{ + + sysctl_ctx_free(ð_ctx_list); +} + +SYSINIT(eth_init, SI_SUB_LOCK, SI_ORDER_FIRST, eth_init, NULL); +SYSUNINIT(eth_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, eth_uninit, NULL); -- 2.25.1 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel