-
Notifications
You must be signed in to change notification settings - Fork 907
/
Copy pathusb_descriptors.c
201 lines (160 loc) · 6.69 KB
/
usb_descriptors.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <tusb.h>
#include <bsp/board_api.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 (0x4000 | _PID_MAP(CDC, 0))
// set USB 2.0
#define CDC_EXAMPLE_BCD 0x0200
// 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 = 0x01, // Index of manufacturer string
.iProduct = 0x02, // Index of product string
.iSerialNumber = 0x03, // 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_TOTAL
};
// total length of configuration descriptor
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_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, 4, 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, 4, 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),
};
// 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);
// 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
};
// 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,
"RPiReset" // 6: Reset Interface
};
// 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;
}