Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TinyUsbCdcImpl.hxx
Go to the documentation of this file.
1
34// This file does not compile standalone, instead it has to be included in the
35// board-speciofic Cdc driver implementation cxx file.
36#ifndef _FREERTOS_DRIVERS_TINYUSB_TINYUSBCDCIMPL_HXX_
37#define _FREERTOS_DRIVERS_TINYUSB_TINYUSBCDCIMPL_HXX_
38
40
42#include "os/OS.hxx"
43#include <fcntl.h>
44
45#include "tusb.h"
46
47#ifndef USBD_STACK_SIZE
48#define USBD_STACK_SIZE 768
49#endif
50
51#ifndef USBD_TASK_PRIO
52#define USBD_TASK_PRIO 3
53#endif
54
55TinyUsbCdc::~TinyUsbCdc()
56{
57}
58
59void TinyUsbCdc::hw_postinit()
60{
61 usbdThread_.start("tinyusb_device", USBD_TASK_PRIO, USBD_STACK_SIZE);
62}
63
65{
66 // init device stack on configured roothub port
67 // This should be called after scheduler/kernel is started.
68 // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS
69 // queue API.
70 tud_init(BOARD_TUD_RHPORT);
71
72 // RTOS forever loop
73 while (1)
74 {
75 // put this thread to waiting state until there is new events
76 tud_task();
77
78 // following code only run if tud_task() process at least 1 event
79 tud_cdc_write_flush();
80 }
81}
82
83ssize_t TinyUsbCdc::read(File *file, void *buf, size_t count)
84{
85 unsigned char *data = (unsigned char *)buf;
86 ssize_t result = 0;
87
88 while (count)
89 {
90 portENTER_CRITICAL();
91 /* We limit the amount of bytes we read with each iteration in order
92 * to limit the amount of time that interrupts are disabled and
93 * preserve our real-time performance.
94 */
95 size_t bytes_read = tud_cdc_read(data, count < 64 ? count : 64);
96 portEXIT_CRITICAL();
97
98 if (bytes_read == 0)
99 {
100 /* no more data to receive */
101 if ((file->flags & O_NONBLOCK) || result > 0)
102 {
103 break;
104 }
105 else
106 {
107 /* wait for data to come in, this call will release the
108 * critical section lock.
109 */
110 DeviceBufferBase::block_until_condition(file, true);
111 }
112 }
113
114 count -= bytes_read;
115 result += bytes_read;
116 data += bytes_read;
117 }
118
119 if (!result && (file->flags & O_NONBLOCK))
120 {
121 return -EAGAIN;
122 }
123
124 return result;
125}
126
127ssize_t TinyUsbCdc::write(File *file, const void *buf, size_t count)
128{
129 const unsigned char *data = (const unsigned char *)buf;
130 ssize_t result = 0;
131
132 while (count)
133 {
134 portENTER_CRITICAL();
135 /* We limit the amount of bytes we write with each iteration in order
136 * to limit the amount of time that interrupts are disabled and
137 * preserve our real-time performance.
138 */
139 size_t bytes_written = tud_cdc_write(data, count < 64 ? count : 64);
140 portEXIT_CRITICAL();
141
142 if (bytes_written == 0)
143 {
144 /* no more space to send data */
145 if ((file->flags & O_NONBLOCK) || result > 0)
146 {
147 break;
148 }
149 else
150 {
151 /* wait for space to be available, this call will release the
152 * critical section lock.
153 */
154 DeviceBufferBase::block_until_condition(file, false);
155 }
156 }
157
158 count -= bytes_written;
159 result += bytes_written;
160 data += bytes_written;
161 }
162
163 if (!result && (file->flags & O_NONBLOCK))
164 {
165 return -EAGAIN;
166 }
167
168 if (result)
169 {
170 tud_cdc_write_flush();
171 }
172
173 return result;
174}
175
182bool TinyUsbCdc::select(File *file, int mode)
183{
184 bool retval = false;
185 switch (mode)
186 {
187 case FREAD:
188 portENTER_CRITICAL();
189 if (tud_cdc_available() > 0)
190 {
191 retval = true;
192 }
193 else
194 {
196 }
197 portEXIT_CRITICAL();
198 break;
199 case FWRITE:
200 portENTER_CRITICAL();
201 if (tud_cdc_write_available() > 0)
202 {
203 retval = true;
204 }
205 else
206 {
208 }
209 portEXIT_CRITICAL();
210 break;
211 default:
212 case 0:
213 /* we don't support any exceptions */
214 break;
215 }
216 return retval;
217}
218
219inline void TinyUsbCdc::rx_available()
220{
222}
223
224inline void TinyUsbCdc::tx_complete()
225{
227}
228
229extern "C" {
230
231// Invoked when CDC interface received data from host
232void tud_cdc_rx_cb(uint8_t itf)
233{
234 (void)itf;
235 Singleton<TinyUsbCdc>::instance()->rx_available();
236}
237
238// Invoked when a TX is complete and therefore space becomes available in TX
239// buffer
240void tud_cdc_tx_complete_cb(uint8_t itf)
241{
242 (void)itf;
243 Singleton<TinyUsbCdc>::instance()->tx_complete();
244}
245
246// ===================== USB DESCRIPTORS =============================
247
248/* A combination of interfaces must have a unique product id, since PC will save
249 * device driver after the first plug. Same VID/PID with different interface e.g
250 * MSC (first), then CDC (later) will possibly cause system error on PC.
251 *
252 * Auto ProductID layout's Bitmap:
253 * [MSB] HID | MSC | CDC [LSB]
254 */
255#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
256#define USB_PID \
257 (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
258 _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
259
260#define USB_VID 0xCAFE
261#define USB_BCD 0x0200
262
263// Invoked when received GET DEVICE DESCRIPTOR
264// Application return pointer to descriptor
265uint8_t const *tud_descriptor_device_cb(void)
266{
267 static tusb_desc_device_t const desc_device = {
268 .bLength = sizeof(tusb_desc_device_t),
269 .bDescriptorType = TUSB_DESC_DEVICE,
270 .bcdUSB = USB_BCD,
271
272 // Use Interface Association Descriptor (IAD) for CDC
273 // As required by USB Specs IAD's subclass must be common class (2) and
274 // protocol must be IAD (1)
275 .bDeviceClass = TUSB_CLASS_MISC,
276 .bDeviceSubClass = MISC_SUBCLASS_COMMON,
277 .bDeviceProtocol = MISC_PROTOCOL_IAD,
278
279 .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
280
281 .idVendor = USB_VID,
282 .idProduct = USB_PID,
283 .bcdDevice = 0x0100,
284
285 .iManufacturer = 0x01,
286 .iProduct = 0x02,
287 .iSerialNumber = 0x03,
288
289 .bNumConfigurations = 0x01};
290
291 return (uint8_t const *)&desc_device;
292}
293
294enum
295{
296 ITF_NUM_CDC = 0,
297 ITF_NUM_CDC_DATA,
298 ITF_NUM_TOTAL
299};
300
301#define EPNUM_CDC_NOTIF 0x81
302#define EPNUM_CDC_OUT 0x02
303#define EPNUM_CDC_IN 0x82
304
305#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
306
307// full speed configuration
308uint8_t const desc_fs_configuration[] = {
309 // Config number, interface count, string index, total length, attribute,
310 // power in mA
311 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
312
313 // Interface number, string index, EP notification address and size, EP data
314 // address (out, in) and size.
315 TUD_CDC_DESCRIPTOR(
316 ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
317};
318
319#if TUD_OPT_HIGH_SPEED
320// Per USB specs: high speed capable device must report device_qualifier and
321// other_speed_configuration
322
323// high speed configuration
324uint8_t const desc_hs_configuration[] = {
325 // Config number, interface count, string index, total length, attribute,
326 // power in mA
327 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
328
329 // Interface number, string index, EP notification address and size, EP data
330 // address (out, in) and size.
331 TUD_CDC_DESCRIPTOR(
332 ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
333};
334
335// other speed configuration
336uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
337
338// device qualifier is mostly similar to device descriptor since we don't change
339// configuration based on speed
340tusb_desc_device_qualifier_t const desc_device_qualifier = {
341 .bLength = sizeof(tusb_desc_device_qualifier_t),
342 .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
343 .bcdUSB = USB_BCD,
344
345 .bDeviceClass = TUSB_CLASS_MISC,
346 .bDeviceSubClass = MISC_SUBCLASS_COMMON,
347 .bDeviceProtocol = MISC_PROTOCOL_IAD,
348
349 .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
350 .bNumConfigurations = 0x01,
351 .bReserved = 0x00};
352
353// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
354// Application return pointer to descriptor, whose contents must exist long
355// enough for transfer to complete. device_qualifier descriptor describes
356// information about a high-speed capable device that would change if the device
357// were operating at the other speed. If not highspeed capable stall this
358// request.
359uint8_t const *tud_descriptor_device_qualifier_cb(void)
360{
361 return (uint8_t const *)&desc_device_qualifier;
362}
363
364// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
365// Application return pointer to descriptor, whose contents must exist long
366// enough for transfer to complete Configuration descriptor in the other speed
367// e.g if high speed then this is for full speed and vice versa
368uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index)
369{
370 (void)index; // for multiple configurations
371
372 // if link speed is high return fullspeed config, and vice versa
373 // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG
374 memcpy(desc_other_speed_config,
375 (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration
376 : desc_hs_configuration,
377 CONFIG_TOTAL_LEN);
378
379 desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
380
381 return desc_other_speed_config;
382}
383
384#endif // highspeed
385
386// Invoked when received GET CONFIGURATION DESCRIPTOR
387// Application return pointer to descriptor
388// Descriptor contents must exist long enough for transfer to complete
389uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
390{
391 (void)index; // for multiple configurations
392
393#if TUD_OPT_HIGH_SPEED
394 // Although we are highspeed, host may be fullspeed.
395 return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration
396 : desc_fs_configuration;
397#else
398 return desc_fs_configuration;
399#endif
400}
401
402// array of pointer to string descriptors
403char const *string_desc_arr[] = {
404 (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
405 "TinyUSB", // 1: Manufacturer
406 "TinyUSB Device", // 2: Product
407 "123456789012", // 3: Serials, should use chip ID
408 "TinyUSB CDC", // 4: CDC Interface
409};
410
411static uint16_t _desc_str[32];
412
413// Invoked when received GET STRING DESCRIPTOR request
414// Application return pointer to descriptor, whose contents must exist long
415// enough for transfer to complete
416uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
417{
418 (void)langid;
419
420 uint8_t chr_count;
421
422 if (index == 0)
423 {
424 memcpy(&_desc_str[1], string_desc_arr[0], 2);
425 chr_count = 1;
426 }
427 else
428 {
429 // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
430 // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
431
432 if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
433 return NULL;
434
435 const char *str = string_desc_arr[index];
436
437 // Cap at max char
438 chr_count = (uint8_t)strlen(str);
439 if (chr_count > 31)
440 chr_count = 31;
441
442 // Convert ASCII string into UTF-16
443 for (uint8_t i = 0; i < chr_count; i++)
444 {
445 _desc_str[1 + i] = str[i];
446 }
447 }
448
449 // first byte is length (including header), second byte is string type
450 _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
451
452 return _desc_str;
453}
454
455} // extern "C"
456
457#endif // _FREERTOS_DRIVERS_TINYUSB_TINYUSBCDCIMPL_HXX_
void start(const char *name, int priority, size_t stack_size)
Starts the thread.
Definition OS.hxx:78
static T * instance()
Definition Singleton.hxx:77
void * entry() override
User entry point for the created thread.
ssize_t write(File *file, const void *buf, size_t count) override
Write to a file or device.
bool select(File *file, int mode) override
Device select method.
Device::SelectInfo selectInfoWrite_
Handles the select for outgoing data (write).
ssize_t read(File *file, void *buf, size_t count) override
Read from a file or device.
Device::SelectInfo selectInfoRead_
Handles the select for incoming data (read).
#define FWRITE
Workaround for missing header defines on some newlib versions.
Definition fcntl.h:58
#define FREAD
Workaround for missing header defines on some newlib versions.
Definition fcntl.h:53
static void select_insert(SelectInfo *info)
Add client to list of clients needing woken.
Definition Select.cxx:197
static void select_wakeup(SelectInfo *info)
Wakeup the list of clients needing woken.
Definition Select.cxx:210
File information.
Definition Devtab.hxx:52
int flags
open flags
Definition Devtab.hxx:63