2023-03-17 16:12:49 +07:00
/*
2020-03-02 21:15:01 -06:00
* 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 .
*
*/
2023-08-04 10:28:24 +07:00
# include "bsp/board_api.h"
2020-03-02 21:15:01 -06:00
# include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID / PID with different interface e . g MSC ( first ) , then CDC ( later ) will possibly cause system error on PC .
*
* Auto ProductID layout ' s Bitmap :
2020-04-14 21:10:43 -05:00
* [ MSB ] NET | VENDOR | MIDI | HID | MSC | CDC [ LSB ]
2020-03-02 21:15:01 -06:00
*/
# define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
# define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
2021-10-05 12:32:07 +07:00
_PID_MAP ( MIDI , 3 ) | _PID_MAP ( VENDOR , 4 ) | _PID_MAP ( ECM_RNDIS , 5 ) | _PID_MAP ( NCM , 5 ) )
2020-03-02 21:15:01 -06:00
2020-03-23 23:46:12 +07:00
// String Descriptor Index
enum
{
STRID_LANGID = 0 ,
STRID_MANUFACTURER ,
STRID_PRODUCT ,
STRID_SERIAL ,
STRID_INTERFACE ,
STRID_MAC
} ;
2020-04-15 22:28:13 +07:00
enum
{
ITF_NUM_CDC = 0 ,
ITF_NUM_CDC_DATA ,
ITF_NUM_TOTAL
} ;
enum
{
2021-10-05 12:32:07 +07:00
# if CFG_TUD_ECM_RNDIS
2020-04-15 22:28:13 +07:00
CONFIG_ID_RNDIS = 0 ,
CONFIG_ID_ECM = 1 ,
2021-08-03 11:28:44 -05:00
# else
CONFIG_ID_NCM = 0 ,
# endif
2020-04-15 22:28:13 +07:00
CONFIG_ID_COUNT
} ;
2020-03-02 21:15:01 -06:00
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
. bLength = sizeof ( tusb_desc_device_t ) ,
. bDescriptorType = TUSB_DESC_DEVICE ,
2024-10-05 15:43:59 +02:00
# if CFG_TUD_NCM
. bcdUSB = 0x0201 ,
# else
2020-03-02 21:15:01 -06:00
. bcdUSB = 0x0200 ,
2024-10-05 15:43:59 +02:00
# endif
2020-04-15 16:01:32 -04:00
// Use Interface Association Descriptor (IAD) device class
. bDeviceClass = TUSB_CLASS_MISC ,
. bDeviceSubClass = MISC_SUBCLASS_COMMON ,
. bDeviceProtocol = MISC_PROTOCOL_IAD ,
2023-03-17 16:12:49 +07:00
2020-03-02 21:15:01 -06:00
. bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE ,
. idVendor = 0xCafe ,
. idProduct = USB_PID ,
2020-04-12 15:41:18 -05:00
. bcdDevice = 0x0101 ,
2020-03-02 21:15:01 -06:00
2020-03-23 23:46:12 +07:00
. iManufacturer = STRID_MANUFACTURER ,
. iProduct = STRID_PRODUCT ,
. iSerialNumber = STRID_SERIAL ,
2020-03-02 21:15:01 -06:00
2020-04-15 22:28:13 +07:00
. bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
2020-03-02 21:15:01 -06:00
} ;
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb ( void )
{
return ( uint8_t const * ) & desc_device ;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
2020-04-14 21:10:43 -05:00
# define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
# define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
2021-08-03 11:28:44 -05:00
# define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
2020-03-02 21:15:01 -06:00
# if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
2020-05-02 18:35:46 +07:00
# define EPNUM_NET_NOTIF 0x81
# define EPNUM_NET_OUT 0x02
# define EPNUM_NET_IN 0x82
2024-08-17 18:07:36 +07:00
# elif CFG_TUSB_MCU == OPT_MCU_CXD56
// CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
// 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
# define EPNUM_NET_NOTIF 0x83
2024-06-28 16:55:27 -04:00
# define EPNUM_NET_OUT 0x02
2024-08-17 18:07:36 +07:00
# define EPNUM_NET_IN 0x81
2024-06-28 16:55:27 -04:00
2024-08-19 19:05:33 +07:00
# elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
2024-08-17 18:07:36 +07:00
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
2020-05-02 18:35:46 +07:00
// e.g EP1 OUT & EP1 IN cannot exist together
# define EPNUM_NET_NOTIF 0x81
# define EPNUM_NET_OUT 0x02
# define EPNUM_NET_IN 0x83
2020-03-02 21:15:01 -06:00
# else
2020-05-02 18:35:46 +07:00
# define EPNUM_NET_NOTIF 0x81
# define EPNUM_NET_OUT 0x02
# define EPNUM_NET_IN 0x82
2020-03-02 21:15:01 -06:00
# endif
2021-10-05 12:32:07 +07:00
# if CFG_TUD_ECM_RNDIS
2020-04-15 18:06:12 +07:00
static uint8_t const rndis_configuration [ ] =
2020-03-02 21:15:01 -06:00
{
2020-04-15 22:28:13 +07:00
// 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 ) ,
2020-03-02 21:15:01 -06:00
2020-04-12 15:41:18 -05:00
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
2020-05-02 18:35:46 +07:00
TUD_RNDIS_DESCRIPTOR ( ITF_NUM_CDC , STRID_INTERFACE , EPNUM_NET_NOTIF , 8 , EPNUM_NET_OUT , EPNUM_NET_IN , CFG_TUD_NET_ENDPOINT_SIZE ) ,
2020-03-02 21:15:01 -06:00
} ;
2020-04-15 18:06:12 +07:00
static uint8_t const ecm_configuration [ ] =
2020-04-12 15:41:18 -05:00
{
2020-04-15 22:28:13 +07:00
// 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 ) ,
2020-04-12 15:41:18 -05:00
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
2020-05-02 18:35:46 +07:00
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 ) ,
2020-04-12 15:41:18 -05:00
} ;
2021-10-05 12:32:07 +07:00
2021-08-03 11:28:44 -05:00
# else
2021-10-05 12:32:07 +07:00
2021-08-03 11:28:44 -05:00
static uint8_t const ncm_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.
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 ) ,
} ;
2021-10-05 12:32:07 +07:00
2021-08-03 11:28:44 -05:00
# endif
2020-04-12 15:41:18 -05:00
2020-04-15 18:06:12 +07:00
// Configuration array: RNDIS and CDC-ECM
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
static uint8_t const * const configuration_arr [ 2 ] =
{
2021-10-05 12:32:07 +07:00
# if CFG_TUD_ECM_RNDIS
2020-04-15 22:28:13 +07:00
[ CONFIG_ID_RNDIS ] = rndis_configuration ,
[ CONFIG_ID_ECM ] = ecm_configuration
2021-08-03 11:28:44 -05:00
# else
[ CONFIG_ID_NCM ] = ncm_configuration
# endif
2020-04-15 18:06:12 +07:00
} ;
2020-03-02 21:15:01 -06:00
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb ( uint8_t index )
{
2020-04-15 22:28:13 +07:00
return ( index < CONFIG_ID_COUNT ) ? configuration_arr [ index ] : NULL ;
2020-03-02 21:15:01 -06:00
}
2024-10-05 15:43:59 +02:00
# if CFG_TUD_NCM
//--------------------------------------------------------------------+
// BOS Descriptor
//--------------------------------------------------------------------+
2024-10-15 21:49:02 +02:00
/* Used to automatically load the NCM driver on Windows 10, otherwise manual driver install is needed.
Associate NCM interface with WINNCM driver . */
2024-10-05 15:43:59 +02:00
/* Microsoft OS 2.0 registry property descriptor
Per MS requirements https : //msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
device should create DeviceInterfaceGUIDs . It can be done by driver and
in case of real PnP solution device should expose MS " Microsoft OS 2.0
registry property descriptor " . Such descriptor can insert any record
into Windows registry per device / configuration / interface . In our case it
will insert " DeviceInterfaceGUIDs " multistring property .
GUID is freshly generated and should be OK to use .
https : //developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
( Section Microsoft OS compatibility descriptors )
*/
# define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
# define MS_OS_20_DESC_LEN 0xB2
// BOS Descriptor is required for webUSB
uint8_t const desc_bos [ ] =
{
// total length, number of device caps
TUD_BOS_DESCRIPTOR ( BOS_TOTAL_LEN , 1 ) ,
// Microsoft OS 2.0 descriptor
TUD_BOS_MS_OS_20_DESCRIPTOR ( MS_OS_20_DESC_LEN , 1 )
} ;
uint8_t const * tud_descriptor_bos_cb ( void )
{
return desc_bos ;
}
uint8_t const desc_ms_os_20 [ ] =
{
// Set header: length, type, windows version, total length
U16_TO_U8S_LE ( 0x000A ) , U16_TO_U8S_LE ( MS_OS_20_SET_HEADER_DESCRIPTOR ) , U32_TO_U8S_LE ( 0x06030000 ) , U16_TO_U8S_LE ( MS_OS_20_DESC_LEN ) ,
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE ( 0x0008 ) , U16_TO_U8S_LE ( MS_OS_20_SUBSET_HEADER_CONFIGURATION ) , 0 , 0 , U16_TO_U8S_LE ( MS_OS_20_DESC_LEN - 0x0A ) ,
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE ( 0x0008 ) , U16_TO_U8S_LE ( MS_OS_20_SUBSET_HEADER_FUNCTION ) , ITF_NUM_CDC , 0 , U16_TO_U8S_LE ( MS_OS_20_DESC_LEN - 0x0A - 0x08 ) ,
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE ( 0x0014 ) , U16_TO_U8S_LE ( MS_OS_20_FEATURE_COMPATBLE_ID ) , ' W ' , ' I ' , ' N ' , ' N ' , ' C ' , ' M ' , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE ( MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14 ) , U16_TO_U8S_LE ( MS_OS_20_FEATURE_REG_PROPERTY ) ,
U16_TO_U8S_LE ( 0x0007 ) , U16_TO_U8S_LE ( 0x002A ) , // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
' D ' , 0x00 , ' e ' , 0x00 , ' v ' , 0x00 , ' i ' , 0x00 , ' c ' , 0x00 , ' e ' , 0x00 , ' I ' , 0x00 , ' n ' , 0x00 , ' t ' , 0x00 , ' e ' , 0x00 ,
' r ' , 0x00 , ' f ' , 0x00 , ' a ' , 0x00 , ' c ' , 0x00 , ' e ' , 0x00 , ' G ' , 0x00 , ' U ' , 0x00 , ' I ' , 0x00 , ' D ' , 0x00 , ' s ' , 0x00 , 0x00 , 0x00 ,
U16_TO_U8S_LE ( 0x0050 ) , // wPropertyDataLength
//bPropertyData: {12345678-0D08-43FD-8B3E-127CA8AFFF9D}
' { ' , 0x00 , ' 1 ' , 0x00 , ' 2 ' , 0x00 , ' 3 ' , 0x00 , ' 4 ' , 0x00 , ' 5 ' , 0x00 , ' 6 ' , 0x00 , ' 7 ' , 0x00 , ' 8 ' , 0x00 , ' - ' , 0x00 ,
' 0 ' , 0x00 , ' D ' , 0x00 , ' 0 ' , 0x00 , ' 8 ' , 0x00 , ' - ' , 0x00 , ' 4 ' , 0x00 , ' 3 ' , 0x00 , ' F ' , 0x00 , ' D ' , 0x00 , ' - ' , 0x00 ,
' 8 ' , 0x00 , ' B ' , 0x00 , ' 3 ' , 0x00 , ' E ' , 0x00 , ' - ' , 0x00 , ' 1 ' , 0x00 , ' 2 ' , 0x00 , ' 7 ' , 0x00 , ' C ' , 0x00 , ' A ' , 0x00 ,
' 8 ' , 0x00 , ' A ' , 0x00 , ' F ' , 0x00 , ' F ' , 0x00 , ' F ' , 0x00 , ' 9 ' , 0x00 , ' D ' , 0x00 , ' } ' , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
TU_VERIFY_STATIC ( sizeof ( desc_ms_os_20 ) = = MS_OS_20_DESC_LEN , " Incorrect size " ) ;
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb ( uint8_t rhport , uint8_t stage , tusb_control_request_t const * request ) {
// nothing to with DATA & ACK stage
if ( stage ! = CONTROL_STAGE_SETUP ) return true ;
switch ( request - > bmRequestType_bit . type ) {
case TUSB_REQ_TYPE_VENDOR :
switch ( request - > bRequest ) {
case 1 :
if ( request - > wIndex = = 7 ) {
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len ;
memcpy ( & total_len , desc_ms_os_20 + 8 , 2 ) ;
return tud_control_xfer ( rhport , request , ( void * ) ( uintptr_t ) desc_ms_os_20 , total_len ) ;
} else {
return false ;
}
default : break ;
}
break ;
default : break ;
}
// stall unknown request
return false ;
}
# endif
2020-03-02 21:15:01 -06:00
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
2020-03-24 00:07:34 +07:00
static char const * string_desc_arr [ ] =
2020-03-02 21:15:01 -06:00
{
2020-03-23 23:46:12 +07:00
[ STRID_LANGID ] = ( const char [ ] ) { 0x09 , 0x04 } , // supported language is English (0x0409)
[ STRID_MANUFACTURER ] = " TinyUSB " , // Manufacturer
[ STRID_PRODUCT ] = " TinyUSB Device " , // Product
2023-08-04 10:28:24 +07:00
[ STRID_SERIAL ] = NULL , // Serials will use unique ID if possible
2020-04-12 15:41:18 -05:00
[ STRID_INTERFACE ] = " TinyUSB Network Interface " // Interface Description
2020-03-23 23:46:12 +07:00
// STRID_MAC index is handled separately
2020-03-02 21:15:01 -06:00
} ;
2023-08-04 10:28:24 +07:00
static uint16_t _desc_str [ 32 + 1 ] ;
2020-03-02 21:15:01 -06:00
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
2023-08-04 10:28:24 +07:00
uint16_t const * tud_descriptor_string_cb ( uint8_t index , uint16_t langid ) {
2020-03-23 15:21:22 +07:00
( void ) langid ;
2020-03-23 23:22:52 +07:00
unsigned int chr_count = 0 ;
2020-03-02 21:15:01 -06:00
2023-08-04 10:28:24 +07:00
switch ( index ) {
case STRID_LANGID :
memcpy ( & _desc_str [ 1 ] , string_desc_arr [ 0 ] , 2 ) ;
chr_count = 1 ;
break ;
case STRID_SERIAL :
chr_count = board_usb_get_serial ( _desc_str + 1 , 32 ) ;
break ;
case STRID_MAC :
// Convert MAC address into UTF-16
for ( unsigned i = 0 ; i < sizeof ( tud_network_mac_address ) ; i + + ) {
_desc_str [ 1 + chr_count + + ] = " 0123456789ABCDEF " [ ( tud_network_mac_address [ i ] > > 4 ) & 0xf ] ;
_desc_str [ 1 + chr_count + + ] = " 0123456789ABCDEF " [ ( tud_network_mac_address [ i ] > > 0 ) & 0xf ] ;
}
break ;
default :
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( ! ( index < sizeof ( string_desc_arr ) / sizeof ( string_desc_arr [ 0 ] ) ) ) return NULL ;
const char * str = string_desc_arr [ index ] ;
// Cap at max char
chr_count = strlen ( str ) ;
size_t const max_count = sizeof ( _desc_str ) / sizeof ( _desc_str [ 0 ] ) - 1 ; // -1 for string type
if ( chr_count > max_count ) chr_count = max_count ;
// Convert ASCII string into UTF-16
for ( size_t i = 0 ; i < chr_count ; i + + ) {
_desc_str [ 1 + i ] = str [ i ] ;
}
break ;
2020-03-02 21:15:01 -06:00
}
// first byte is length (including header), second byte is string type
2022-06-27 11:50:17 +07:00
_desc_str [ 0 ] = ( uint16_t ) ( ( TUSB_DESC_STRING < < 8 ) | ( 2 * chr_count + 2 ) ) ;
2020-03-02 21:15:01 -06:00
return _desc_str ;
}