Pico_USB_Tests/src_device/usb_descriptor.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;
}