211 lines
7.2 KiB
C
211 lines
7.2 KiB
C
/**
|
|
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <tusb.h>
|
|
#include <bsp/board_api.h>
|
|
#include <assert.h>
|
|
|
|
#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;
|
|
} |