Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/usb/template/usb_template_cdce.c
/* $FreeBSD$ */ | /* $FreeBSD$ */ | ||||
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* | * | ||||
* Copyright (c) 2007 Hans Petter Selasky <hselasky@FreeBSD.org> | * Copyright (c) 2007 Hans Petter Selasky <hselasky@FreeBSD.org> | ||||
* Copyright (c) 2018 The FreeBSD Foundation | |||||
* All rights reserved. | * 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 | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
Show All 36 Lines | |||||
#include <sys/callout.h> | #include <sys/callout.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <dev/usb/usb.h> | #include <dev/usb/usb.h> | ||||
#include <dev/usb/usbdi.h> | #include <dev/usb/usbdi.h> | ||||
#include <dev/usb/usb_core.h> | #include <dev/usb/usb_core.h> | ||||
#include <dev/usb/usb_cdc.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> | #include <dev/usb/template/usb_template.h> | ||||
#endif /* USB_GLOBAL_INCLUDE_FILE */ | #endif /* USB_GLOBAL_INCLUDE_FILE */ | ||||
enum { | enum { | ||||
STRING_LANG_INDEX, | ETH_LANG_INDEX, | ||||
STRING_MAC_INDEX, | ETH_MAC_INDEX, | ||||
STRING_ETH_CONTROL_INDEX, | ETH_CONTROL_INDEX, | ||||
STRING_ETH_DATA_INDEX, | ETH_DATA_INDEX, | ||||
STRING_ETH_CONFIG_INDEX, | ETH_CONFIGURATION_INDEX, | ||||
STRING_ETH_VENDOR_INDEX, | ETH_MANUFACTURER_INDEX, | ||||
STRING_ETH_PRODUCT_INDEX, | ETH_PRODUCT_INDEX, | ||||
STRING_ETH_SERIAL_INDEX, | ETH_SERIAL_NUMBER_INDEX, | ||||
STRING_ETH_MAX, | ETH_MAX_INDEX, | ||||
}; | }; | ||||
#define STRING_MAC \ | #define ETH_DEFAULT_MAC "2A02030405060789AB" | ||||
"2\0A\0002\0003\0004\0005\0006\0007\08\09\0A\0B" | #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 "FreeBSD foundation" | |||||
#define ETH_DEFAULT_PRODUCT "USB Ethernet Adapter" | |||||
#define ETH_DEFAULT_SERIAL_NUMBER "December 2007" | |||||
#define STRING_ETH_CONTROL \ | static struct usb_string_descriptor eth_mac; | ||||
"U\0S\0B\0 \0E\0t\0h\0e\0r\0n\0e\0t\0 " \ | static struct usb_string_descriptor eth_control; | ||||
"\0C\0o\0m\0m\0 \0I\0n\0t\0e\0r\0f\0a\0c\0e" | 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; | |||||
#define STRING_ETH_DATA \ | static struct sysctl_ctx_list eth_ctx_list; | ||||
"U\0S\0B\0 \0E\0t\0h\0e\0r\0n\0e\0t\0 \0D\0a\0t\0a\0 " \ | |||||
"\0I\0n\0t\0e\0r\0f\0a\0c\0e" | |||||
#define STRING_ETH_CONFIG \ | |||||
"D\0e\0f\0a\0u\0l\0t\0 \0c\0o\0n\0f\0i\0g" | |||||
#define STRING_ETH_VENDOR \ | |||||
"F\0r\0e\0e\0B\0S\0D\0 \0f\0o\0u\0n\0d\0a\0t\0i\0o\0n" | |||||
#define STRING_ETH_PRODUCT \ | |||||
"U\0S\0B\0 \0E\0t\0h\0e\0r\0n\0e\0t\0 \0A\0d\0a\0p\0t\0e\0r" | |||||
#define STRING_ETH_SERIAL \ | |||||
"D\0e\0c\0e\0m\0b\0e\0r\0 \0002\0000\0000\0007" | |||||
/* make the real string descriptors */ | |||||
USB_MAKE_STRING_DESC(STRING_MAC, string_mac); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_CONTROL, string_eth_control); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_DATA, string_eth_data); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_CONFIG, string_eth_config); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_VENDOR, string_eth_vendor); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_PRODUCT, string_eth_product); | |||||
USB_MAKE_STRING_DESC(STRING_ETH_SERIAL, string_eth_serial); | |||||
/* prototypes */ | /* prototypes */ | ||||
static usb_temp_get_string_desc_t eth_get_string_desc; | static usb_temp_get_string_desc_t eth_get_string_desc; | ||||
static const struct usb_cdc_union_descriptor eth_union_desc = { | static const struct usb_cdc_union_descriptor eth_union_desc = { | ||||
.bLength = sizeof(eth_union_desc), | .bLength = sizeof(eth_union_desc), | ||||
.bDescriptorType = UDESC_CS_INTERFACE, | .bDescriptorType = UDESC_CS_INTERFACE, | ||||
.bDescriptorSubtype = UDESCSUB_CDC_UNION, | .bDescriptorSubtype = UDESCSUB_CDC_UNION, | ||||
.bMasterInterface = 0, /* this is automatically updated */ | .bMasterInterface = 0, /* this is automatically updated */ | ||||
.bSlaveInterface[0] = 1, /* this is automatically updated */ | .bSlaveInterface[0] = 1, /* this is automatically updated */ | ||||
}; | }; | ||||
static const struct usb_cdc_header_descriptor eth_header_desc = { | static const struct usb_cdc_header_descriptor eth_header_desc = { | ||||
.bLength = sizeof(eth_header_desc), | .bLength = sizeof(eth_header_desc), | ||||
.bDescriptorType = UDESC_CS_INTERFACE, | .bDescriptorType = UDESC_CS_INTERFACE, | ||||
.bDescriptorSubtype = UDESCSUB_CDC_HEADER, | .bDescriptorSubtype = UDESCSUB_CDC_HEADER, | ||||
.bcdCDC[0] = 0x10, | .bcdCDC[0] = 0x10, | ||||
.bcdCDC[1] = 0x01, | .bcdCDC[1] = 0x01, | ||||
}; | }; | ||||
static const struct usb_cdc_ethernet_descriptor eth_enf_desc = { | static const struct usb_cdc_ethernet_descriptor eth_enf_desc = { | ||||
.bLength = sizeof(eth_enf_desc), | .bLength = sizeof(eth_enf_desc), | ||||
.bDescriptorType = UDESC_CS_INTERFACE, | .bDescriptorType = UDESC_CS_INTERFACE, | ||||
.bDescriptorSubtype = UDESCSUB_CDC_ENF, | .bDescriptorSubtype = UDESCSUB_CDC_ENF, | ||||
.iMacAddress = STRING_MAC_INDEX, | .iMacAddress = ETH_MAC_INDEX, | ||||
.bmEthernetStatistics = {0, 0, 0, 0}, | .bmEthernetStatistics = {0, 0, 0, 0}, | ||||
.wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */ | .wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */ | ||||
.wNumberMCFilters = {0, 0}, | .wNumberMCFilters = {0, 0}, | ||||
.bNumberPowerFilters = 0, | .bNumberPowerFilters = 0, | ||||
}; | }; | ||||
static const void *eth_control_if_desc[] = { | static const void *eth_control_if_desc[] = { | ||||
ð_union_desc, | ð_union_desc, | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
static const struct usb_temp_interface_desc eth_control_interface = { | static const struct usb_temp_interface_desc eth_control_interface = { | ||||
.ppEndpoints = eth_intr_endpoints, | .ppEndpoints = eth_intr_endpoints, | ||||
.ppRawDesc = eth_control_if_desc, | .ppRawDesc = eth_control_if_desc, | ||||
.bInterfaceClass = UICLASS_CDC, | .bInterfaceClass = UICLASS_CDC, | ||||
.bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, | .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = STRING_ETH_CONTROL_INDEX, | .iInterface = ETH_CONTROL_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_endpoint_desc *eth_data_endpoints[] = { | static const struct usb_temp_endpoint_desc *eth_data_endpoints[] = { | ||||
&bulk_in_ep, | &bulk_in_ep, | ||||
&bulk_out_ep, | &bulk_out_ep, | ||||
NULL, | NULL, | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc eth_data_null_interface = { | static const struct usb_temp_interface_desc eth_data_null_interface = { | ||||
.ppEndpoints = NULL, /* no endpoints */ | .ppEndpoints = NULL, /* no endpoints */ | ||||
.bInterfaceClass = UICLASS_CDC_DATA, | .bInterfaceClass = UICLASS_CDC_DATA, | ||||
.bInterfaceSubClass = 0, | .bInterfaceSubClass = 0, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = STRING_ETH_DATA_INDEX, | .iInterface = ETH_DATA_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc eth_data_interface = { | static const struct usb_temp_interface_desc eth_data_interface = { | ||||
.ppEndpoints = eth_data_endpoints, | .ppEndpoints = eth_data_endpoints, | ||||
.bInterfaceClass = UICLASS_CDC_DATA, | .bInterfaceClass = UICLASS_CDC_DATA, | ||||
.bInterfaceSubClass = UISUBCLASS_DATA, | .bInterfaceSubClass = UISUBCLASS_DATA, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = STRING_ETH_DATA_INDEX, | .iInterface = ETH_DATA_INDEX, | ||||
.isAltInterface = 1, /* this is an alternate setting */ | .isAltInterface = 1, /* this is an alternate setting */ | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc *eth_interfaces[] = { | static const struct usb_temp_interface_desc *eth_interfaces[] = { | ||||
ð_control_interface, | ð_control_interface, | ||||
ð_data_null_interface, | ð_data_null_interface, | ||||
ð_data_interface, | ð_data_interface, | ||||
NULL, | NULL, | ||||
}; | }; | ||||
static const struct usb_temp_config_desc eth_config_desc = { | static const struct usb_temp_config_desc eth_config_desc = { | ||||
.ppIfaceDesc = eth_interfaces, | .ppIfaceDesc = eth_interfaces, | ||||
.bmAttributes = UC_BUS_POWERED, | .bmAttributes = UC_BUS_POWERED, | ||||
.bMaxPower = 25, /* 50 mA */ | .bMaxPower = 25, /* 50 mA */ | ||||
.iConfiguration = STRING_ETH_CONFIG_INDEX, | .iConfiguration = ETH_CONFIGURATION_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_config_desc *eth_configs[] = { | static const struct usb_temp_config_desc *eth_configs[] = { | ||||
ð_config_desc, | ð_config_desc, | ||||
NULL, | NULL, | ||||
}; | }; | ||||
const struct usb_temp_device_desc usb_template_cdce = { | struct usb_temp_device_desc usb_template_cdce = { | ||||
.getStringDesc = ð_get_string_desc, | .getStringDesc = ð_get_string_desc, | ||||
.ppConfigDesc = eth_configs, | .ppConfigDesc = eth_configs, | ||||
.idVendor = USB_TEMPLATE_VENDOR, | .idVendor = USB_TEMPLATE_VENDOR, | ||||
.idProduct = 0x0001, | .idProduct = 0x0001, | ||||
.bcdDevice = 0x0100, | .bcdDevice = 0x0100, | ||||
.bDeviceClass = UDCLASS_COMM, | .bDeviceClass = UDCLASS_COMM, | ||||
.bDeviceSubClass = 0, | .bDeviceSubClass = 0, | ||||
.bDeviceProtocol = 0, | .bDeviceProtocol = 0, | ||||
.iManufacturer = STRING_ETH_VENDOR_INDEX, | .iManufacturer = ETH_MANUFACTURER_INDEX, | ||||
.iProduct = STRING_ETH_PRODUCT_INDEX, | .iProduct = ETH_PRODUCT_INDEX, | ||||
.iSerialNumber = STRING_ETH_SERIAL_INDEX, | .iSerialNumber = ETH_SERIAL_NUMBER_INDEX, | ||||
}; | }; | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* eth_get_string_desc | * eth_get_string_desc | ||||
* | * | ||||
* Return values: | * Return values: | ||||
* NULL: Failure. No such string. | * NULL: Failure. No such string. | ||||
* Else: Success. Pointer to string descriptor is returned. | * Else: Success. Pointer to string descriptor is returned. | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
static const void * | static const void * | ||||
eth_get_string_desc(uint16_t lang_id, uint8_t string_index) | eth_get_string_desc(uint16_t lang_id, uint8_t string_index) | ||||
{ | { | ||||
static const void *ptr[STRING_ETH_MAX] = { | static const void *ptr[ETH_MAX_INDEX] = { | ||||
[STRING_LANG_INDEX] = &usb_string_lang_en, | [ETH_LANG_INDEX] = &usb_string_lang_en, | ||||
[STRING_MAC_INDEX] = &string_mac, | [ETH_MAC_INDEX] = ð_mac, | ||||
[STRING_ETH_CONTROL_INDEX] = &string_eth_control, | [ETH_CONTROL_INDEX] = ð_control, | ||||
[STRING_ETH_DATA_INDEX] = &string_eth_data, | [ETH_DATA_INDEX] = ð_data, | ||||
[STRING_ETH_CONFIG_INDEX] = &string_eth_config, | [ETH_CONFIGURATION_INDEX] = ð_configuration, | ||||
[STRING_ETH_VENDOR_INDEX] = &string_eth_vendor, | [ETH_MANUFACTURER_INDEX] = ð_manufacturer, | ||||
[STRING_ETH_PRODUCT_INDEX] = &string_eth_product, | [ETH_PRODUCT_INDEX] = ð_product, | ||||
[STRING_ETH_SERIAL_INDEX] = &string_eth_serial, | [ETH_SERIAL_NUMBER_INDEX] = ð_serial_number, | ||||
}; | }; | ||||
if (string_index == 0) { | if (string_index == 0) { | ||||
return (&usb_string_lang_en); | return (&usb_string_lang_en); | ||||
} | } | ||||
if (lang_id != 0x0409) { | if (lang_id != 0x0409) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (string_index < STRING_ETH_MAX) { | if (string_index < ETH_MAX_INDEX) { | ||||
return (ptr[string_index]); | return (ptr[string_index]); | ||||
} | } | ||||
return (NULL); | 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_init, SI_SUB_LOCK, SI_ORDER_FIRST, eth_uninit, NULL); |