/** * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include "pico/usb_reset.h" // set some example Vendor and Product ID // the board will use to identify at the host #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) #define CDC_EXAMPLE_VID 0xCafe // use _PID_MAP to generate unique PID for each interface #define CDC_EXAMPLE_PID (0x4100 | _PID_MAP(CDC, 0)) // set to 2.10 so the MS_OS_20_DESCRIPTOR works #define CDC_EXAMPLE_BCD 0x0210 // String descriptors referenced with .i... in the descriptor tables enum { STRID_LANGID = 0, // 0: supported language ID STRID_MANUFACTURER, // 1: Manufacturer STRID_PRODUCT, // 2: Product STRID_SERIAL, // 3: Serials STRID_CDC_0, // 4: CDC Interface 0 STRID_CDC_1, // 5: CDC Interface 1 STRID_RPI_RESET, // 6: Reset Interface }; // array of pointer to string descriptors char const *string_desc_arr[] = { // switched because board is little endian (const char[]) { 0x09, 0x04 }, // 0: supported language is English (0x0409) "Raspberry Pi", // 1: Manufacturer "Pico (2)", // 2: Product NULL, // 3: Serials (null so it uses unique ID if available) "Pico SDK stdio", // 4: CDC Interface 0 "Custom CDC", // 5: CDC Interface 1, "Reset" // 6: Reset Interface }; // defines a descriptor that will be communicated to the host tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = CDC_EXAMPLE_BCD, .bDeviceClass = TUSB_CLASS_MISC, // CDC is a subclass of misc .bDeviceSubClass = MISC_SUBCLASS_COMMON, // CDC uses common subclass .bDeviceProtocol = MISC_PROTOCOL_IAD, // CDC uses IAD .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, // 64 bytes .idVendor = CDC_EXAMPLE_VID, .idProduct = CDC_EXAMPLE_PID, .bcdDevice = 0x0100, // Device release number .iManufacturer = STRID_MANUFACTURER, // Index of manufacturer string .iProduct = STRID_PRODUCT, // Index of product string .iSerialNumber = STRID_SERIAL, // Index of serial number string .bNumConfigurations = 0x01 // 1 configuration }; // called when host requests to get device descriptor uint8_t const *tud_descriptor_device_cb(void); enum { ITF_NUM_CDC_0 = 0, ITF_NUM_CDC_0_DATA, ITF_NUM_CDC_1, ITF_NUM_CDC_1_DATA, ITF_NUM_RPI_RESET, ITF_NUM_TOTAL }; static_assert(ITF_NUM_RPI_RESET == PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF, "ITF_NUM_RPI_RESET must be equal to the PICO_USB_RESET_MS_OS_20_DESCRIPTOR_ITF set in CMakeLists.txt"); // total length of configuration descriptor #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN) // define endpoint numbers #define EPNUM_CDC_0_NOTIF 0x81 // notification endpoint for CDC 0 #define EPNUM_CDC_0_OUT 0x02 // out endpoint for CDC 0 #define EPNUM_CDC_0_IN 0x82 // in endpoint for CDC 0 #define EPNUM_CDC_1_NOTIF 0x84 // notification endpoint for CDC 1 #define EPNUM_CDC_1_OUT 0x05 // out endpoint for CDC 1 #define EPNUM_CDC_1_IN 0x85 // in endpoint for CDC 1 // configure descriptor (for 2 CDC interfaces) uint8_t const desc_configuration[] = { // config descriptor | how much power in mA, count of interfaces, ... TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100), // CDC 0: Communication Interface - TODO: get 64 from tusb_config.h TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, STRID_CDC_0, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64), // CDC 0: Data Interface //TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0_DATA, 4, 0x01, 0x02), // CDC 1: Communication Interface - TODO: get 64 from tusb_config.h TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, STRID_CDC_1, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64), // CDC 1: Data Interface //TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1_DATA, 4, 0x03, 0x04), // RPi Reset Interface TUD_RPI_RESET_DESCRIPTOR(ITF_NUM_RPI_RESET, STRID_RPI_RESET), }; // called when host requests to get configuration descriptor uint8_t const * tud_descriptor_configuration_cb(uint8_t index); // more device descriptor this time the qualifier tusb_desc_device_qualifier_t const desc_device_qualifier = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = CDC_EXAMPLE_BCD, .bDeviceClass = TUSB_CLASS_CDC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .bNumConfigurations = 0x01, .bReserved = 0x00 }; // called when host requests to get device qualifier descriptor uint8_t const* tud_descriptor_device_qualifier_cb(void); // buffer to hold the string descriptor during the request | plus 1 for the null terminator static uint16_t _desc_str[32 + 1]; // called when host request to get string descriptor uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid); // --------------------------------------------------------------------+ // IMPLEMENTATION // --------------------------------------------------------------------+ uint8_t const *tud_descriptor_device_cb(void) { return (uint8_t const *)&desc_device; } uint8_t const* tud_descriptor_device_qualifier_cb(void) { return (uint8_t const *)&desc_device_qualifier; } uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { // avoid unused parameter warning and keep function signature consistent (void)index; return desc_configuration; } uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // TODO: check lang id (void) langid; size_t char_count; // Determine which string descriptor to return switch (index) { case STRID_LANGID: memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2); char_count = 1; break; case STRID_SERIAL: // try to read the serial from the board char_count = board_usb_get_serial(_desc_str + 1, 32); break; default: // COPYRIGHT NOTE: Based on TinyUSB example // Windows wants utf16le // Determine which string descriptor to return if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) { return NULL; } // Copy string descriptor into _desc_str const char *str = string_desc_arr[index]; char_count = strlen(str); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type // Cap at max char if (char_count > max_count) { char_count = max_count; } // Convert ASCII string into UTF-16 for (size_t i = 0; i < char_count; i++) { _desc_str[1 + i] = str[i]; } break; } // First byte is the length (including header), second byte is string type _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (char_count * 2 + 2)); return _desc_str; }