Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,13 @@ Release
BrowseInfo
.cmake_build
README_processed.rst
# Dependencies fetched by tools/get_deps.py (should not be committed)
hw/mcu/raspberry_pi/Pico-PIO-USB/
hw/mcu/st/cmsis_device_f4/
hw/mcu/st/stm32f4xx_hal_driver/
lib/CMSIS_5/
lib/FreeRTOS-Kernel/
lib/lwip/
lib/threadx/
tools/linkermap/
tools/uf2/
153 changes: 136 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,174 @@ 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_hs_configuration,
[CONFIG_ID_ECM] = ecm_hs_configuration
#else
[CONFIG_ID_NCM] = ncm_hs_configuration
#endif
};

// Size array for each configuration
static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = MAIN_CONFIG_TOTAL_LEN,
[CONFIG_ID_ECM] = ALT_CONFIG_TOTAL_LEN
#else
[CONFIG_ID_NCM] = NCM_CONFIG_TOTAL_LEN
#endif
};

// Scratch buffer for other speed configuration (sized to hold the largest config)
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_configuration,
[CONFIG_ID_ECM] = ecm_configuration
#define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN)
#else
[CONFIG_ID_NCM] = ncm_configuration
#define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN
#endif
static uint8_t desc_other_speed_config[MAX_CONFIG_TOTAL_LEN];

// device qualifier: device descriptor fields that differ at other 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 SPEED 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 (index >= CONFIG_ID_COUNT) return NULL;

// 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;

// Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
memcpy(desc_other_speed_config, arr[index], configuration_sz_arr[index]);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;

return desc_other_speed_config;
}

#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