Add CDC NCM driver

This commit is contained in:
Jacob Potter 2020-10-31 11:03:55 -06:00
parent 075334af80
commit bb0df2740e
6 changed files with 635 additions and 2 deletions

View File

@ -69,7 +69,8 @@ typedef enum
CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1]
CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1]
CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1]
CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0]
CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL , ///< Ethernet Emulation Model [USBEEM1.0]
CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL ///< Network Control Model [USBNCM1.0]
} cdc_comm_sublcass_type_t;
/// Communication Interface Protocol Codes
@ -114,7 +115,8 @@ typedef enum
CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor
CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor
CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor
CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor
CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 , ///< OBEX Service Identifier Functional Descriptor
CDC_FUNC_DESC_NCM = 0x1A , ///< NCM Functional Descriptor
}cdc_func_desc_type_t;
//--------------------------------------------------------------------+

View File

@ -0,0 +1,493 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jacob Berg Potter
* Copyright (c) 2020 Peter Lawrence
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NCM )
#include "device/usbd_pvt.h"
#include "cdc_device.h"
#include "cdc_ncm_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#define NTH16_SIGNATURE 0x484D434E
#define NDP16_SIGNATURE_NCM0 0x304D434E
#define NDP16_SIGNATURE_NCM1 0x314D434E
typedef struct
{
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
uint8_t ep_notif;
uint8_t ep_in;
uint8_t ep_out;
// Endpoint descriptor use to open/close when receving SetInterface
// TODO since configuration descriptor may not be long-lived memory, we should
// keep a copy of endpoint attribute instead
uint8_t const * ecm_desc_epdata;
enum {
REPORT_SPEED,
REPORT_CONNECTED,
REPORT_DONE
} report_state;
bool report_pending;
uint8_t current_ntb; // Index in transmit_ntb[] that is currently being filled with datagrams
uint8_t datagram_count; // Number of datagrams in transmit_ntb[current_ntb]
uint16_t next_datagram_offset; // Offset in transmit_ntb[current_ntb].data to place the next datagram
uint16_t ntb_in_size; // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
uint8_t max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
uint16_t nth_sequence; // Sequence number counter for transmitted NTBs
bool transferring;
} ncm_interface_t;
typedef struct TU_ATTR_PACKED
{
uint16_t wLength;
uint16_t bmNtbFormatsSupported;
uint32_t dwNtbInMaxSize;
uint16_t wNdbInDivisor;
uint16_t wNdbInPayloadRemainder;
uint16_t wNdbInAlignment;
uint16_t wReserved;
uint32_t dwNtbOutMaxSize;
uint16_t wNdbOutDivisor;
uint16_t wNdbOutPayloadRemainder;
uint16_t wNdbOutAlignment;
uint16_t wNtbOutMaxDatagrams;
} ntb_parameters_t;
typedef struct TU_ATTR_PACKED
{
uint32_t dwSignature;
uint16_t wHeaderLength;
uint16_t wSequence;
uint16_t wBlockLength;
uint16_t wNdpIndex;
} nth16_t;
typedef struct TU_ATTR_PACKED
{
uint16_t wDatagramIndex;
uint16_t wDatagramLength;
} ndp16_datagram_t;
typedef struct TU_ATTR_PACKED
{
uint32_t dwSignature;
uint16_t wLength;
uint16_t wNextNdpIndex;
ndp16_datagram_t datagram[];
} ndp16_t;
typedef union TU_ATTR_PACKED {
struct {
nth16_t nth;
ndp16_t ndp;
};
uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
} transmit_ntb_t;
struct ecm_notify_struct
{
tusb_control_request_t header;
uint32_t downlink, uplink;
};
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static const ntb_parameters_t ntb_parameters = {
.wLength = sizeof(ntb_parameters_t),
.bmNtbFormatsSupported = 0x01,
.dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
.wNdbInDivisor = 4,
.wNdbInPayloadRemainder = 0,
.wNdbInAlignment = CFG_TUD_NCM_ALIGNMENT,
.wReserved = 0,
.dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
.wNdbOutDivisor = 4,
.wNdbOutPayloadRemainder = 0,
.wNdbOutAlignment = CFG_TUD_NCM_ALIGNMENT,
.wNtbOutMaxDatagrams = 0
};
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static transmit_ntb_t transmit_ntb[2];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
static ncm_interface_t ncm_interface;
/*
* Set up the NTB state in ncm_interface to be ready to add datagrams.
*/
static void ncm_prepare_for_tx() {
ncm_interface.datagram_count = 0;
// datagrams start after all the headers
ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
+ ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
}
/*
* If not already transmitting, start sending the current NTB to the host and swap buffers
* to start filling the other one with datagrams.
*/
static void ncm_start_tx() {
if (ncm_interface.transferring) {
return;
}
transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
size_t ntb_length = ncm_interface.next_datagram_offset;
// Fill in NTB header
ntb->nth.dwSignature = NTH16_SIGNATURE;
ntb->nth.wHeaderLength = sizeof(nth16_t);
ntb->nth.wSequence = ncm_interface.nth_sequence++;
ntb->nth.wBlockLength = ntb_length;
ntb->nth.wNdpIndex = sizeof(nth16_t);
// Fill in NDP16 header and terminator
ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
ntb->ndp.wNextNdpIndex = 0;
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
// Kick off an endpoint transfer
usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_in, ntb->data, ntb_length);
ncm_interface.transferring = true;
// Swap to the other NTB and clear it out
ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
ncm_prepare_for_tx();
}
static struct ecm_notify_struct ncm_notify_connected =
{
.header = {
.bmRequestType = 0xA1,
.bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
.wValue = 1 /* Connected */,
.wLength = 0,
},
};
static struct ecm_notify_struct ncm_notify_speed_change =
{
.header = {
.bmRequestType = 0xA1,
.bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
.wLength = 8,
},
.downlink = 10000000,
.uplink = 10000000,
};
void ncm_receive_renew(void)
{
usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void ncmd_init(void)
{
tu_memclr(&ncm_interface, sizeof(ncm_interface));
ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
ncm_prepare_for_tx();
}
void ncmd_reset(uint8_t rhport)
{
(void) rhport;
ncmd_init();
}
uint16_t ncmd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// confirm interface hasn't already been allocated
TU_ASSERT(0 == ncm_interface.ep_notif, 0);
//------------- Management Interface -------------//
ncm_interface.itf_num = itf_desc->bInterfaceNumber;
uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const * p_desc = tu_desc_next( itf_desc );
// Communication Functional Descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// notification endpoint (if any)
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface -------------//
// - CDC-NCM data interface has 2 alternate settings
// - 0 : zero endpoints for inactive (default)
// - 1 : IN & OUT endpoints for transfer of NTBs
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
do
{
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
} while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
// ECM by default is in-active, save the endpoint attribute
// to open later when receive_ntb setInterface
ncm_interface.ecm_desc_epdata = p_desc;
drv_len += 2*sizeof(tusb_desc_endpoint_t);
return drv_len;
}
// Invoked when class request DATA stage is finished.
bool ncmd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
{
(void) rhport;
(void) request;
return true;
}
static void ncm_report()
{
if (ncm_interface.report_state == REPORT_SPEED) {
ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
ncm_interface.report_state = REPORT_CONNECTED;
ncm_interface.report_pending = true;
} else if (ncm_interface.report_state == REPORT_CONNECTED) {
ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
ncm_interface.report_state = REPORT_DONE;
ncm_interface.report_pending = true;
}
}
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool ncmd_control_request(uint8_t rhport, tusb_control_request_t const * request)
{
switch ( request->bmRequestType_bit.type )
{
case TUSB_REQ_TYPE_STANDARD:
switch ( request->bRequest )
{
case TUSB_REQ_GET_INTERFACE:
{
uint8_t const req_itfnum = (uint8_t) request->wIndex;
TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
}
break;
case TUSB_REQ_SET_INTERFACE:
{
uint8_t const req_itfnum = (uint8_t) request->wIndex;
uint8_t const req_alt = (uint8_t) request->wValue;
// Only valid for Data Interface with Alternate is either 0 or 1
TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
ncm_interface.itf_data_alt = req_alt;
if ( ncm_interface.itf_data_alt )
{
// TODO since we don't actually close endpoint
// hack here to not re-open it
if (ncm_interface.ep_in == 0 && ncm_interface.ep_out == 0)
{
TU_ASSERT(ncm_interface.ecm_desc_epdata);
TU_ASSERT( usbd_open_edpt_pair(rhport, ncm_interface.ecm_desc_epdata, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
// TODO set flag indicating tx allowed?
ncm_receive_renew(); // prepare for incoming datagrams
tud_ncm_link_state_cb(true);
}
} else {
// TODO close the endpoint pair
// For now pretend that we did, this should have no harm since host won't try to
// communicate with the endpoints again
// ncm_interface.ep_in = ncm_interface.ep_out = 0
// ncm_interface.report_pending = 0;
}
tud_control_status(rhport, request);
if (!ncm_interface.report_pending) ncm_report();
}
break;
// unsupported request
default: return false;
}
break;
case TUSB_REQ_TYPE_CLASS:
TU_VERIFY (ncm_interface.itf_num == request->wIndex);
if (0x80 /* GET_NTB_PARAMETERS */ == request->bRequest)
{
tud_control_xfer(rhport, request, (void*)&ntb_parameters, sizeof(ntb_parameters));
}
break;
// unsupported request
default: return false;
}
return true;
}
static void handle_incoming_datagram(uint32_t len)
{
uint32_t size = len;
if (len == 0) {
return;
}
TU_ASSERT(size >= sizeof(nth16_t), );
const nth16_t *hdr = (const nth16_t *)receive_ntb;
TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
int num_datagrams = (ndp->wLength - 12) / 4;
for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++) {
tud_ncm_receive_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
}
ncm_receive_renew();
}
bool ncmd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) rhport;
(void) result;
/* new datagram receive_ntb */
if (ep_addr == ncm_interface.ep_out )
{
handle_incoming_datagram(xferred_bytes);
}
/* data transmission finished */
if (ep_addr == ncm_interface.ep_in )
{
if (ncm_interface.transferring) {
ncm_interface.transferring = false;
}
// If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
if (ncm_interface.datagram_count) {
ncm_start_tx();
}
}
if (ep_addr == ncm_interface.ep_notif )
{
ncm_interface.report_pending = false;
ncm_report();
}
return true;
}
bool tud_ncm_xmit(void *arg, uint16_t size, void (*flatten)(void *, uint8_t *, uint16_t)) {
transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
return false;
}
size_t next_datagram_offset = ncm_interface.next_datagram_offset;
if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
return false;
}
flatten(arg, ntb->data + next_datagram_offset, size);
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
ncm_interface.datagram_count++;
next_datagram_offset += size;
// round up so the next datagram is aligned correctly
next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
ncm_interface.next_datagram_offset = next_datagram_offset;
ncm_start_tx();
return true;
}
#endif

View File

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_CDC_NCM_DEVICE_H_
#define _TUSB_CDC_NCM_DEVICE_H_
#include "common/tusb_common.h"
#include "device/usbd.h"
#include "cdc.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CFG_TUD_NCM_EP_BUFSIZE
#define CFG_TUD_NCM_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
#endif
#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
#endif
#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
#endif
#ifndef CFG_TUD_NCM_ALIGNMENT
#define CFG_TUD_NCM_ALIGNMENT 4
#endif
/**
* Transmit data. Returns true if successful, false if the packet was rejected (transmit buffer full,
* interface not up).
*
* This is structured with a callback to avoid the need for multiple copies; some network stacks,
* including LwIP, allow for non-contiguous buffers and provide a function to flatten them. In the
* LwIP case, a thin wrapper around pbuf_copy_partial() can be passed here.
*/
bool tud_ncm_xmit(void *arg, uint16_t size, void (*flatten)(void *arg, uint8_t *buffer, uint16_t size));
//--------------------------------------------------------------------+
// Application Callback API
//--------------------------------------------------------------------+
void tud_ncm_receive_cb(uint8_t *data, size_t length);
void tud_ncm_link_state_cb(bool state);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void ncmd_init (void);
void ncmd_reset (uint8_t rhport);
uint16_t ncmd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool ncmd_control_request (uint8_t rhport, tusb_control_request_t const * request);
bool ncmd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
bool ncmd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CDC_NCM_DEVICE_H_ */

View File

@ -220,6 +220,19 @@ static usbd_class_driver_t const _usbd_driver[] =
.sof = NULL
},
#endif
#if CFG_TUD_NCM
{
DRIVER_NAME("NCM")
.init = ncmd_init,
.reset = ncmd_reset,
.open = ncmd_open,
.control_request = ncmd_control_request,
.control_complete = ncmd_control_complete,
.xfer_cb = ncmd_xfer_cb,
.sof = NULL
},
#endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };

View File

@ -671,6 +671,39 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__)
//------------- CDC-NCM -------------//
// 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) \
/* Interface Association */\
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\
/* CDC Control Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, _desc_stridx,\
/* CDC-NCM Header */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\
/* CDC-NCM Union */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
/* CDC-ECM Functional Descriptor */\
13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0, \
/* CDC-ECM 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), 1,\
/* CDC Data Interface (default inactive) */\
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, 1, 0,\
/* CDC Data Interface (alternative active) */\
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, 1, 0,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
#ifdef __cplusplus
}
#endif

View File

@ -103,6 +103,10 @@
#if CFG_TUD_BTH
#include "class/bth/bth_device.h"
#endif
#if CFG_TUD_NCM
#include "class/cdc/cdc_ncm_device.h"
#endif
#endif