/* * 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. * */ #include "tusb.h" #include "pico/unique_id.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: * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB] */ #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) | \ _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(NET, 5) ) // String Descriptor Index enum { STRID_LANGID = 0, STRID_MANUFACTURER, STRID_PRODUCT, STRID_SERIAL, STRID_INTERFACE, STRID_MAC }; enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; enum { CONFIG_ID_RNDIS = 0, CONFIG_ID_ECM = 1, CONFIG_ID_COUNT }; //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, // Use Interface Association Descriptor (IAD) device class .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = 0xCafe, .idProduct = USB_PID, .bcdDevice = 0x0101, .iManufacturer = STRID_MANUFACTURER, .iProduct = STRID_PRODUCT, .iSerialNumber = STRID_SERIAL, .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations }; // 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 //--------------------------------------------------------------------+ #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) #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 ... #define EPNUM_NET_NOTIF 0x81 #define EPNUM_NET_OUT 0x02 #define EPNUM_NET_IN 0x82 #elif CFG_TUSB_MCU == OPT_MCU_SAMG // SAMG doesn't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_NET_NOTIF 0x81 #define EPNUM_NET_OUT 0x02 #define EPNUM_NET_IN 0x83 #else #define EPNUM_NET_NOTIF 0x81 #define EPNUM_NET_OUT 0x02 #define EPNUM_NET_IN 0x82 #endif static uint8_t const rndis_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), }; static uint8_t const ecm_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), }; // Configuration array: RNDIS and CDC-ECM // - Windows only works with RNDIS // - MacOS only works with CDC-ECM // - Linux will work on both // Note index is Num-1x static uint8_t const * const configuration_arr[2] = { [CONFIG_ID_RNDIS] = rndis_configuration, [CONFIG_ID_ECM ] = ecm_configuration }; // 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) { return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; } //--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ // array of pointer to string descriptors static char const* string_desc_arr [] = { [STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409) [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer [STRID_PRODUCT] = "Go to http://192.168.7.1/", // Product //[STRID_SERIAL] = "123456", // Serial [STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description // STRID_MAC index is handled separately // STRID_SERIAL index is handled seperately }; static uint16_t _desc_str[32]; // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { (void) langid; unsigned int chr_count = 0; if (STRID_LANGID == index) { memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2); chr_count = 1; } else if (STRID_SERIAL == index) { pico_unique_board_id_t id; pico_get_unique_board_id(&id); for (unsigned i=0; i> 4) & 0xf]; _desc_str[1+chr_count++] = "0123456789ABCDEF"[(id.id[i] >> 0) & 0xf]; } } else if (STRID_MAC == index) { // Convert MAC address into UTF-16 for (unsigned i=0; i> 4) & 0xf]; _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf]; } } else { // 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); if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1; // Convert ASCII string into UTF-16 for (unsigned int i=0; i