[PATCH rtems-libbsd v2 1/5] usb_template:Import CDC Ethernet
Husni Faiz
ahamedhusni73 at gmail.com
Wed Jul 28 11:56:31 UTC 2021
Signed-off-by: Husni Faiz <ahamedhusni73 at 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 <hselasky at 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 <hselasky at 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
More information about the devel
mailing list