Skip to content
Open
128 changes: 111 additions & 17 deletions examples/device/net_lwip_webserver/src/usb_descriptors.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,19 @@ enum {
CONFIG_ID_COUNT
};

#if CFG_TUD_NCM
#define USB_BCD 0x0201
#else
#define USB_BCD 0x0200
#endif

//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
static const tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
#if CFG_TUD_NCM
.bcdUSB = 0x0201,
#else
.bcdUSB = 0x0200,
#endif
.bcdUSB = USB_BCD,
// Use Interface Association Descriptor (IAD) device class
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
Expand Down Expand Up @@ -136,57 +138,149 @@ const uint8_t *tud_descriptor_device_cb(void) {

#if CFG_TUD_ECM_RNDIS

static uint8_t const rndis_configuration[] = {
// full speed configuration
static uint8_t const rndis_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 64),
};

static const uint8_t ecm_configuration[] = {
static const uint8_t ecm_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_ECM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
64, CFG_TUD_NET_MTU),
};

#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration

// high speed configuration
static uint8_t const rndis_hs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 512),
};

static const uint8_t ecm_hs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_ECM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
512, CFG_TUD_NET_MTU),
};
#endif // highspeed

#else

static uint8_t const ncm_configuration[] = {
// full speed configuration
static uint8_t const ncm_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval.
TUD_CDC_NCM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
64, CFG_TUD_NET_MTU, 50),
};

#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration

// high speed configuration
// bInterval: FS=50 means 50ms; HS encodes as 2^(n-1) * 125us, so 9 = 2^8 * 125us = 32ms
static uint8_t const ncm_hs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),

// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval.
TUD_CDC_NCM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
512, CFG_TUD_NET_MTU, 9),
};
#endif // highspeed

#endif

// Configuration array: RNDIS and CDC-ECM
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
static const uint8_t *const configuration_arr[CONFIG_ID_COUNT] = {
static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_fs_configuration,
[CONFIG_ID_ECM] = ecm_fs_configuration
#else
[CONFIG_ID_NCM] = ncm_fs_configuration
#endif
};

#if TUD_OPT_HIGH_SPEED
static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_configuration,
[CONFIG_ID_ECM] = ecm_configuration
[CONFIG_ID_RNDIS] = rndis_hs_configuration,
[CONFIG_ID_ECM] = ecm_hs_configuration
#else
[CONFIG_ID_NCM] = ncm_configuration
[CONFIG_ID_NCM] = ncm_hs_configuration
#endif
};

// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
static tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,

.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,

.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = CONFIG_ID_COUNT,
.bReserved = 0x00
};

// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
// device_qualifier descriptor describes information about a high-speed capable device that would
// change if the device were operating at the other speed. If not highspeed capable stall this request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *) &desc_device_qualifier;
}

// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
// if link speed is high return fullspeed config, and vice versa
const uint8_t *const *arr = (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_fs_arr : configuration_hs_arr;
return (index < CONFIG_ID_COUNT) ? arr[index] : NULL;
}

#endif // highspeed

// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
if (index >= CONFIG_ID_COUNT) return NULL;
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_hs_arr[index] : configuration_fs_arr[index];
#else
return configuration_fs_arr[index];
#endif
}

#if CFG_TUD_NCM
Expand Down
7 changes: 5 additions & 2 deletions src/class/net/ecm_rndis_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ typedef struct {
uint8_t ep_notif;
uint8_t ep_in;
uint8_t ep_out;
uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal)

bool ecm_mode;

Expand Down Expand Up @@ -176,6 +177,9 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);

// Save the actual bulk endpoint size (IN and OUT assumed equal)
_netd_itf.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc);

if (_netd_itf.ecm_mode) {
// ECM by default is in-active, save the endpoint attribute
// to open later when received setInterface
Expand Down Expand Up @@ -356,8 +360,7 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
/* data transmission finished */
if (ep_addr == _netd_itf.ep_in) {
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */

if (xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE))) {
if (xferred_bytes && (0 == (xferred_bytes % _netd_itf.ep_size))) {
do_in_xfer(NULL, 0); /* a ZLP is needed */
} else {
/* we're finally finished */
Expand Down
5 changes: 4 additions & 1 deletion src/class/net/ncm_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ typedef struct {
uint8_t itf_num; // interface number
uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
uint8_t rhport; // storage of \a rhport because some callbacks are done without it
uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal)

// recv handling
recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
Expand Down Expand Up @@ -340,7 +341,8 @@ static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);

if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
uint16_t const ep_size = ncm_interface.ep_size;
if (xferred_bytes == 0 || xferred_bytes % ep_size != 0) {
return false;
}

Expand Down Expand Up @@ -905,6 +907,7 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16
// a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
ncm_interface.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc);
drv_len += 2 * sizeof(tusb_desc_endpoint_t);

return drv_len;
Expand Down
3 changes: 0 additions & 3 deletions src/class/net/net_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
#endif

/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)

/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */
#ifndef CFG_TUD_NET_MTU
#define CFG_TUD_NET_MTU 1514
Expand Down
8 changes: 4 additions & 4 deletions src/device/usbd.h
Original file line number Diff line number Diff line change
Expand Up @@ -1026,9 +1026,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// Length of template descriptor
#define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7)

// CDC-ECM Descriptor Template
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \
// CDC-NCM Descriptor Template
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), size, max segment size, EP notification bInterval.
#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize, _ep_notif_interval) \
/* Interface Association */\
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\
/* CDC Control Interface */\
Expand All @@ -1042,7 +1042,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
/* CDC-NCM Functional Descriptor */\
6, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_NCM, U16_TO_U8S_LE(0x0100), 0, \
/* Endpoint Notification */\
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 50,\
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), _ep_notif_interval,\
/* CDC Data Interface (default inactive) */\
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\
/* CDC Data Interface (alternative active) */\
Expand Down
Loading