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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
|
/*
* SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org)
* SPDX-License-Identifier: MIT
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
/* Since 2012 starting with LPC11uxx, NXP start to use common USB Device Controller with code name LPC IP3511
* for almost their new MCUs. Currently supported and tested families are
* - LPC11U68, LPC11U37
* - LPC1347
* - LPC51U68
* - LPC54114
* - LPC55s69
*/
#if CFG_TUD_ENABLED && defined(TUP_USBIP_IP3511)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
// LPCOpen
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#endif
#include "chip.h"
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#else
// SDK
#include "fsl_device_registers.h"
#define INCLUDE_FSL_DEVICE_REGISTERS
#endif
#include "device/dcd.h"
//--------------------------------------------------------------------+
// IP3511 Registers
//--------------------------------------------------------------------+
typedef struct {
__IO uint32_t DEVCMDSTAT; // Device Command/Status register, offset: 0x0
__I uint32_t INFO; // Info register, offset: 0x4
__IO uint32_t EPLISTSTART; // EP Command/Status List start address, offset: 0x8
__IO uint32_t DATABUFSTART; // Data buffer start address, offset: 0xC
__IO uint32_t LPM; // Link Power Management register, offset: 0x10
__IO uint32_t EPSKIP; // Endpoint skip, offset: 0x14
__IO uint32_t EPINUSE; // Endpoint Buffer in use, offset: 0x18
__IO uint32_t EPBUFCFG; // Endpoint Buffer Configuration register, offset: 0x1C
__IO uint32_t INTSTAT; // interrupt status register, offset: 0x20
__IO uint32_t INTEN; // interrupt enable register, offset: 0x24
__IO uint32_t INTSETSTAT; // set interrupt status register, offset: 0x28
uint8_t RESERVED_0[8];
__I uint32_t EPTOGGLE; // Endpoint toggle register, offset: 0x34
} dcd_registers_t;
// Max nbytes for each control/bulk/interrupt transfer
enum {
NBYTES_ISO_FS_MAX = 1023, // FS ISO
NBYTES_ISO_HS_MAX = 1024, // HS ISO
NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt. TODO some FS can do burst with higher size e.g 1024. Need to test
NBYTES_CBI_HS_MAX = 32767 // can be up to all 15-bit, but only tested with 4096
};
enum {
INT_SOF_MASK = TU_BIT(30),
INT_DEVICE_STATUS_MASK = TU_BIT(31)
};
enum {
DEVCMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
DEVCMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
DEVCMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
DEVCMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), // reflect the soft-connect only, does not reflect the actual attached state
DEVCMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
// 23-22 is link speed (only available for HighSpeed port)
DEVCMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
DEVCMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
DEVCMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
DEVCMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
// write-1-to-clear latches
DEVCMDSTAT_W1C_MASK = DEVCMDSTAT_SETUP_RECEIVED_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK |
DEVCMDSTAT_SUSPEND_CHANGE_MASK | DEVCMDSTAT_RESET_CHANGE_MASK,
};
enum {
DEVCMDSTAT_SPEED_SHIFT = 22
};
//--------------------------------------------------------------------+
// Endpoint Command/Status List
//--------------------------------------------------------------------+
// EP Command/Status field definition
enum {
EPCS_TYPE = TU_BIT(26),
EPCS_RF_TV = TU_BIT(27),
EPCS_TOGGLE_RESET = TU_BIT(28),
EPCS_STALL = TU_BIT(29),
EPCS_DISABLED = TU_BIT(30),
EPCS_ACTIVE = TU_BIT(31),
};
// Endpoint Command/Status
typedef union TU_ATTR_PACKED
{
// Full and High speed has different bit layout for buffer_offset and nbytes
// TODO FS/HS layout depends on the max speed of controller e.g
// lpc55s69 PORT0 is only FS but actually has the same layout as HS on port1
// Buffer (aligned 64) = DATABUFSTART [31:22] | buffer_offset [21:6]
volatile struct {
uint32_t offset : 16;
uint32_t nbytes : 10;
uint32_t TU_RESERVED : 6;
} buffer_fs;
// Buffer (aligned 64) = USB_RAM [31:17] | buffer_offset [16:6]
volatile struct {
uint32_t offset : 11 ;
uint32_t nbytes : 15 ;
uint32_t TU_RESERVED : 6 ;
} buffer_hs;
volatile struct {
uint32_t TU_RESERVED : 26;
uint32_t type : 1 ;
uint32_t rf_tv : 1 ; // rate feedback or toggle value
uint32_t toggle_reset : 1 ;
uint32_t stall : 1 ;
uint32_t disable : 1 ;
uint32_t active : 1 ;
} cmd_sts;
}ep_cmd_sts_t;
TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" );
// Software transfer management
typedef struct
{
uint16_t total_bytes;
uint16_t xferred_bytes;
uint16_t nbytes;
// prevent unaligned access on Highspeed port on USB_SRAM
uint16_t TU_RESERVED;
}xfer_dma_t;
// Absolute max of endpoints pairs for all port
// - 11 13 15 51 54 has 5x2 endpoints
// - 55 usb0 (FS) has 5x2 endpoints, usb1 (HS) has 6x2 endpoints
#define MAX_EP_PAIRS 6
// Bounded spin waiting for hardware to clear an EPSKIP bit when retiring a still-armed endpoint on
// reopen (dcd_edpt_open). Hardware clears it within a (micro)frame; the guard only avoids a hang.
#define IP3511_EPSKIP_SPIN 100000u
// NOTE data will be transferred as soon as dcd get request by dcd_pipe(_queue)_xfer using double buffering.
// current_td is used to keep track of number of remaining & xferred bytes of the current request.
typedef struct
{
// 256 byte aligned, 2 for double buffer (not used)
// Each cmd_sts can only transfer up to DMA_NBYTES_MAX bytes each
ep_cmd_sts_t ep[2*MAX_EP_PAIRS][2];
xfer_dma_t dma[2*MAX_EP_PAIRS];
// volatile: the controller DMAs a new setup packet into this buffer as soon as the SETUP
// latch is cleared, so reads of it must stay ordered against the register accesses around them
TU_ATTR_ALIGNED(64) volatile uint8_t setup_packet[8];
}dcd_data_t;
// EP list must be 256-byte aligned
// Some MCU controller may require this variable to be placed in specific SRAM region.
// For example: LPC55s69 port1 Highspeed must be USB_RAM (0x40100000)
// Use CFG_TUD_MEM_SECTION to place it accordingly.
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
// Dummy buffer to fix ZLPs overwriting the buffer: Errata LPC55S6x USB.5 / LPC55S2x USB.4 - the
// HS device controller always DMA-writes OUT data in 8-byte units, so up to 7 bytes land past the
// received length. This redirects the ZLP case; the general short-OUT case is unhandled here
// (TinyUSB's own endpoint buffers are sized/aligned so the spill stays inside them, but a tight
// caller buffer can be overrun by up to 7 bytes - the SDK's documented workaround is a bounce
// buffer). TODO find way to save memory
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
//--------------------------------------------------------------------+
// Multiple Controllers
//--------------------------------------------------------------------+
typedef struct
{
dcd_registers_t* regs; // registers
const bool is_highspeed; // max link speed
const IRQn_Type irqnum; // IRQ number
const uint8_t ep_pairs; // Max bi-directional Endpoints
}dcd_controller_t;
#ifdef INCLUDE_FSL_DEVICE_REGISTERS
static const dcd_controller_t _dcd_controller[] = {
{ .regs = (dcd_registers_t*) USB0_BASE , .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = FSL_FEATURE_USB_EP_NUM },
#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
{ .regs = (dcd_registers_t*) USBHSD_BASE, .is_highspeed = true, .irqnum = USB1_IRQn, .ep_pairs = FSL_FEATURE_USBHSD_EP_NUM }
#endif
};
#else
static const dcd_controller_t _dcd_controller[] = {
{ .regs = (dcd_registers_t*) LPC_USB0_BASE, .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = 5 },
};
#endif
#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
#define IP3511_HAS_HIGHSPEED
#endif
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint16_t get_buf_offset(void const volatile * buffer) {
uint32_t addr = (uint32_t) buffer;
TU_ASSERT( (addr & 0x3f) == 0, 0 );
return ( (addr >> 6) & 0xFFFFUL ) ;
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t ep_addr2id(uint8_t ep_addr) {
return (uint8_t)(2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0));
}
TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_highspeed) {
return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
}
TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
}
TU_ATTR_ALWAYS_INLINE static inline ep_cmd_sts_t* get_ep_cs(uint8_t ep_id) {
return _dcd.ep[ep_id];
}
TU_ATTR_ALWAYS_INLINE static inline bool rhport_is_highspeed(uint8_t rhport) {
return _dcd_controller[rhport].is_highspeed;
}
// DEVCMDSTAT mixes RW fields with write-1-to-clear latches (SETUP + the 3 change bits): a blind
// RMW writes a pending latch back as 1 and silently clears it (a SETUP eaten this way strands
// EP0). Mask the latches on every update; pass one in set_mask only to clear it.
TU_ATTR_ALWAYS_INLINE static inline void devcmdstat_update(dcd_registers_t* dcd_reg,
uint32_t clear_mask, uint32_t set_mask) {
const uint32_t v = dcd_reg->DEVCMDSTAT & ~(DEVCMDSTAT_W1C_MASK | clear_mask);
dcd_reg->DEVCMDSTAT = v | set_mask;
}
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
static void prepare_setup_packet(uint8_t rhport) {
uint16_t const buf_offset = get_buf_offset(_dcd.setup_packet);
if ( _dcd_controller[rhport].is_highspeed ) {
_dcd.ep[0][1].buffer_hs.offset = buf_offset;
} else {
_dcd.ep[0][1].buffer_fs.offset = buf_offset;
}
}
static void edpt_reset(uint8_t rhport, uint8_t ep_id)
{
(void) rhport;
tu_memclr(&_dcd.ep[ep_id], sizeof(_dcd.ep[ep_id]));
}
static void edpt_reset_all(uint8_t rhport)
{
for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id)
{
edpt_reset(rhport, ep_id);
}
prepare_setup_packet(rhport);
}
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rh_init;
edpt_reset_all(rhport);
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->EPLISTSTART = (uint32_t) _dcd.ep;
dcd_reg->DATABUFSTART = tu_align((uint32_t) &_dcd, TU_BIT(22)); // 22-bit alignment
dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK;
// deliberately clear every latch (incl. a SETUP left by a bootloader/warm start) for a
// deterministic init state
devcmdstat_update(dcd_reg, 0, DEVCMDSTAT_DEVICE_ENABLE_MASK | DEVCMDSTAT_DEVICE_CONNECT_MASK |
DEVCMDSTAT_W1C_MASK);
NVIC_ClearPendingIRQ(_dcd_controller[rhport].irqnum);
return true;
}
void dcd_int_enable(uint8_t rhport)
{
NVIC_EnableIRQ(_dcd_controller[rhport].irqnum);
}
void dcd_int_disable(uint8_t rhport)
{
NVIC_DisableIRQ(_dcd_controller[rhport].irqnum);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
// Response with status first before changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0, false);
devcmdstat_update(dcd_reg, DEVCMDSTAT_DEVICE_ADDR_MASK, dev_addr);
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
}
void dcd_connect(uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
devcmdstat_update(dcd_reg, 0, DEVCMDSTAT_DEVICE_CONNECT_MASK);
}
void dcd_disconnect(uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
devcmdstat_update(dcd_reg, DEVCMDSTAT_DEVICE_CONNECT_MASK, 0);
}
void dcd_sof_enable(uint8_t rhport, bool en)
{
(void) rhport;
(void) en;
// TODO implement later
}
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
// Retire a still-armed (Active) endpoint before reconfiguring it (reopen across SET_INTERFACE).
// UM11126 §41.7.6/§41.8.3: write EPSKIP and wait for hardware to clear the bit, then Active is
// safe to clear. EPSKIP raises the endpoint interrupt as it clears Active, delivered as a
// (partial) transfer completion. Here that is sanctioned — usbd_edpt_close() documents "in
// progress transfers may be delivered after this call", and that completion is what clears the
// stale usbd busy flag (ISO_ALLOC close is a no-op) so the class can re-arm the reopened
// endpoint. NOT for the stall/iso-activate paths: there the class re-arms from the completion
// callback and the endpoint ends up Active+Stall, which never sends a STALL handshake (usbtest
// case 13 regression on LPC11u37) — those paths must clear Active directly instead.
// Bounded: hardware clears EPSKIP within a (micro)frame.
static void edpt_skip_active(uint8_t rhport, uint8_t ep_id) {
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
if ( ep_cs[0].cmd_sts.active || ep_cs[1].cmd_sts.active ) {
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->EPSKIP |= TU_BIT(ep_id);
uint32_t guard = IP3511_EPSKIP_SPIN;
while ( (dcd_reg->EPSKIP & TU_BIT(ep_id)) && guard-- ) {}
}
ep_cs[0].cmd_sts.active = ep_cs[1].cmd_sts.active = 0;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
// TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
uint8_t const ep_id = ep_addr2id(ep_addr);
// Clear Active directly before setting Stall (no EPSKIP — see edpt_skip_active): the hardware
// services an armed buffer instead of returning STALL, so a halt requested while a transfer is
// queued would not actually stall the endpoint (usbtest case 13).
_dcd.ep[ep_id][0].cmd_sts.active = 0;
_dcd.ep[ep_id][0].cmd_sts.stall = 1;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const ep_id = ep_addr2id(ep_addr);
// Preserve rf_tv: for non-control endpoints it is a TYPE bit, not the toggle value (UM11126:
// T=1 + RF 1/0 = interrupt/iso). Zeroing it here turned HS periodic interrupt endpoints into
// isochronous - no handshake on OUT, dead IN (usbtest cases 25/26 on lpc55 HS port).
// TODO implement the Errata LPC546xx USB.13 work-around (same semantics in UM11126): with RF/TV preserved at 1, TR
// loads the toggle from TV, so an HS interrupt endpoint restarts on DATA1 after clear-halt and
// the host discards one packet as a retransmission. The documented workaround needs an
// interrupt-on-NAK state machine (park as generic TR=1/TV=0, wait for a NAKed token to latch
// toggle 0 via EPTOGGLE, restore the type) - deferred; one lost packet beats the fully broken
// endpoint the old rf_tv clear caused.
_dcd.ep[ep_id][0].cmd_sts.stall = 0;
_dcd.ep[ep_id][0].cmd_sts.toggle_reset = 1;
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
//------------- Prepare Queue Head -------------//
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
// usbd_edpt_close() is a no-op on ISO_ALLOC ports, so an endpoint a class closed then reopened
// across SET_INTERFACE (e.g. the video notification or audio streaming endpoint) is still armed
// here rather than disabled. Retire it (edpt_skip_active) before reconfiguring.
if ( !(ep_cs[0].cmd_sts.disable && ep_cs[1].cmd_sts.disable) ) {
edpt_skip_active(rhport, ep_id);
ep_cs[0].cmd_sts.disable = ep_cs[1].cmd_sts.disable = 1;
}
edpt_reset(rhport, ep_id);
switch (p_endpoint_desc->bmAttributes.xfer) {
case TUSB_XFER_ISOCHRONOUS:
ep_cs[0].cmd_sts.type = 1;
break;
case TUSB_XFER_INTERRUPT:
// What is interrupt endpoint in rate feedback mode ?
if ( rhport_is_highspeed(rhport) ) {
ep_cs[0].cmd_sts.type = 1;
ep_cs[0].cmd_sts.rf_tv = 1;
}
break;
case TUSB_XFER_BULK:
// nothing to do both type and rf_tv are 0
break;
default: break;
}
// Enable EP interrupt
dcd_reg->INTEN |= TU_BIT(ep_id);
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id)
{
_dcd.ep[ep_id][0].cmd_sts.active = _dcd.ep[ep_id][1].cmd_sts.active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
_dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void) largest_packet_size;
// Reserve the endpoint command/status entry once (persists across altsetting changes); the
// buffer pointer is filled per-transfer, so nothing to pre-allocate. Mirrors the ISO branch of
// dcd_edpt_open().
uint8_t ep_id = ep_addr2id(ep_addr);
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
TU_ASSERT( ep_cs[0].cmd_sts.disable && ep_cs[1].cmd_sts.disable );
edpt_reset(rhport, ep_id);
ep_cs[0].cmd_sts.type = 1; // ISO
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->INTEN |= TU_BIT(ep_id);
return true;
}
bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
// (Re)activate on altsetting selection: abort a transfer still armed from the previous
// altsetting (the hardware keeps servicing an Active buffer across SET_INTERFACE, fighting the
// fresh transfer the class queues), clear stall and reset the data toggle. Direct Active=0, not
// EPSKIP (see edpt_skip_active). The class re-arms via dcd_edpt_xfer().
uint8_t ep_id = ep_addr2id(desc_ep->bEndpointAddress);
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
ep_cs[0].cmd_sts.active = 0;
ep_cs[1].cmd_sts.active = 0;
dcd_edpt_clear_stall(rhport, desc_ep->bEndpointAddress);
return true;
}
static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) {
uint16_t nbytes;
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
const bool is_iso = ep_is_iso(ep_cs, _dcd_controller[rhport].is_highspeed);
if ( rhport_is_highspeed(rhport) ) {
nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_HS_MAX : NBYTES_CBI_HS_MAX);
#if TU_CHECK_MCU(OPT_MCU_LPC54)
// LPC54 Errata USB.1: In USB high-speed device mode, the NBytes field does not decrement after BULK OUT transfer.
// Suggested Work-around: Program the NByte to the max packet size (512)
// Actual Work-around: round up NByte to multiple of 4.
// Note: this can cause buffer overflowed and corrupt data if host send more data than total_bytes
if ( (ep_id > 1) && (ep_id & 0x01) == 0 && ep_is_bulk(ep_cs) ) {
if ( nbytes & 0x03 ) {
nbytes = tu_align4(nbytes) + 4;
}
}
#endif
ep_cs[0].buffer_hs.offset = buf_offset;
ep_cs[0].buffer_hs.nbytes = nbytes;
}else {
nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX);
ep_cs[0].buffer_fs.offset = buf_offset;
ep_cs[0].buffer_fs.nbytes = nbytes;
}
_dcd.dma[ep_id].nbytes = nbytes;
ep_cs[0].cmd_sts.active = 1;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr) {
(void) is_isr;
uint8_t const ep_id = ep_addr2id(ep_addr);
if (!buffer || total_bytes == 0) {
// Although having no data, ZLPs can cause buffer overwritten to zeroes. Probably due to USB/DMA controller side
// effect/bug. Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
buffer = (uint8_t *) (uint32_t) dummy;
}
tu_memclr(&_dcd.dma[ep_id], sizeof(xfer_dma_t));
_dcd.dma[ep_id].total_bytes = total_bytes;
prepare_ep_xfer(rhport, ep_id, get_buf_offset(buffer), total_bytes);
return true;
}
//--------------------------------------------------------------------+
// IRQ
//--------------------------------------------------------------------+
static void bus_reset(uint8_t rhport)
{
tu_memclr(&_dcd, sizeof(dcd_data_t));
edpt_reset_all(rhport);
// disable all endpoints as specified by LPC55S69 UM Table 778
for(uint8_t ep_id = 0; ep_id < 2*MAX_EP_PAIRS; ep_id++)
{
_dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
dcd_reg->EPINUSE = 0;
dcd_reg->EPBUFCFG = 0;
dcd_reg->EPSKIP = 0xFFFFFFFF;
dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt
devcmdstat_update(dcd_reg, 0, DEVCMDSTAT_SETUP_RECEIVED_MASK); // clear setup received interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints
}
static void process_xfer_isr(uint8_t rhport, uint32_t int_status) {
uint8_t const max_ep = 2*_dcd_controller[rhport].ep_pairs;
for(uint8_t ep_id = 0; ep_id < max_ep; ep_id++ ) {
if ( tu_bit_test(int_status, ep_id) ) {
ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0];
xfer_dma_t* xfer_dma = &_dcd.dma[ep_id];
if ( ep_id <= 1 ) {
// For control endpoint, we need to manually clear Active bit
ep_cs->cmd_sts.active = 0;
}
uint16_t buf_offset;
uint16_t buf_nbytes;
if ( rhport_is_highspeed(rhport) ) {
buf_offset = (uint16_t)ep_cs->buffer_hs.offset;
buf_nbytes = (uint16_t)ep_cs->buffer_hs.nbytes;
#if TU_CHECK_MCU(OPT_MCU_LPC54)
// LPC54 Errata USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer
// There is no work-around. For EP in transfer, the NByte value can be ignored after a packet is transmitted.
if ( (ep_id > 1) && (ep_id & 0x01) == 1 && ep_is_bulk(ep_cs) ) {
buf_nbytes = 0;
}
#endif
} else {
buf_offset = (uint16_t)ep_cs->buffer_fs.offset;
buf_nbytes = (uint16_t)ep_cs->buffer_fs.nbytes;
}
xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes;
if ( (buf_nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) ) {
// There is more data to transfer
// buff_offset has been already increased by hw to correct value for next transfer
prepare_ep_xfer(rhport, ep_id, buf_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes);
} else {
// for detecting ZLP
xfer_dma->total_bytes = xfer_dma->xferred_bytes;
uint8_t const ep_addr = tu_edpt_addr(ep_id / 2, ep_id & 0x01);
// TODO no way determine if the transfer is failed or not
dcd_event_xfer_complete(rhport, ep_addr, xfer_dma->xferred_bytes, XFER_RESULT_SUCCESS, true);
}
}
}
}
void dcd_int_handler(uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
uint32_t int_status = dcd_reg->INTSTAT;
int_status &= dcd_reg->INTEN;
dcd_reg->INTSTAT = int_status; // Acknowledge handled interrupt
if (int_status == 0) return;
// Snapshot after the INTSTAT ack: latch bits persist (RWC) so nothing is lost, while the reverse
// order could consume INTSTAT bit0 for a SETUP not yet visible in the snapshot - stranding the
// SETUP (INTSTAT is edge-latched) and feeding bit0 to process_xfer_isr as a bogus completion.
uint32_t const cmd_stat = dcd_reg->DEVCMDSTAT;
//------------- Device Status -------------//
if ( int_status & INT_DEVICE_STATUS_MASK )
{
// clear only the change latches observed in the snapshot: one latched by hardware between the
// snapshot and this write would be acknowledged unseen (its DEV_INT re-latches and dispatches
// next pass instead)
devcmdstat_update(dcd_reg, 0, cmd_stat &
(DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK));
if ( cmd_stat & DEVCMDSTAT_RESET_CHANGE_MASK) // bus reset
{
bus_reset(rhport);
tusb_speed_t speed = TUSB_SPEED_FULL;
if ( _dcd_controller[rhport].is_highspeed ) {
// 0 : reserved, 1 : full, 2 : high, 3: super
if ( 2 == ((cmd_stat >> DEVCMDSTAT_SPEED_SHIFT) & 0x3UL) ) {
speed= TUSB_SPEED_HIGH;
}
}
dcd_event_bus_reset(rhport, speed, true);
}
if (cmd_stat & DEVCMDSTAT_CONNECT_CHANGE_MASK)
{
// device disconnect
if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{
// debouncing as this can be set when device is powering
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
}
if (cmd_stat & DEVCMDSTAT_SUSPEND_CHANGE_MASK)
{
// suspend signal, bus idle for more than 3ms
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{
dcd_event_bus_signal(rhport, (cmd_stat & DEVCMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true);
}
}
}
// Setup Receive
if ( tu_bit_test(int_status, 0) && (cmd_stat & DEVCMDSTAT_SETUP_RECEIVED_MASK) )
{
// Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints
_dcd.ep[0][0].cmd_sts.active = _dcd.ep[1][0].cmd_sts.active = 0;
_dcd.ep[0][0].cmd_sts.stall = _dcd.ep[1][0].cmd_sts.stall = 0;
// UM flow: ack the latch FIRST, then read the payload. This IP has no setup lockout, so a
// back-to-back SETUP can overwrite _dcd.setup_packet at any time - but with the latch already
// released, any such overwrite re-latches SETUP_RECEIVED and is redelivered (worst case a
// superseded duplicate, absorbed by usbd's queued-setup counter). The reverse order can
// consume the newer SETUP's latch unseen and lose it.
devcmdstat_update(dcd_reg, 0, DEVCMDSTAT_SETUP_RECEIVED_MASK);
// UM11126 Fig 163 (control EP0 flowchart) requires clearing the EP0IN interrupt here: a
// control IN completion latched before this SETUP must not reach usbd after it, where it
// would be applied to the new request and arm its status stage early. EP0OUT goes with it -
// bit0 is set by SETUP reception too, and left set it would replay next pass as a phantom
// completion. Neither can discard live work: the SETUP latch NAKs all EP0 traffic until the
// update above, and both EP0 Active bits were cleared a few lines up.
dcd_reg->INTSTAT = TU_BIT(0) | TU_BIT(1);
// Copied a byte at a time rather than with memcpy: C orders volatile accesses only against
// each other, so a non-volatile copy of this buffer may be sunk below the guard read that
// follows - gcc does exactly that at -O2 and -O3, leaving only -Os correct.
uint8_t setup_copy[8];
for (uint8_t i = 0; i < sizeof(setup_copy); i++) {
setup_copy[i] = _dcd.setup_packet[i];
}
// a SETUP that raced in after the acks (its bit0 consumed above) makes this copy suspect:
// its latch is visible again, so re-raise the endpoint interrupt and let the next pass
// deliver the newer payload rather than passing up bytes that may be torn between the two
if (dcd_reg->DEVCMDSTAT & DEVCMDSTAT_SETUP_RECEIVED_MASK) {
dcd_reg->INTSETSTAT = TU_BIT(0);
} else {
dcd_event_setup_received(rhport, setup_copy, true);
}
// keep waiting for next setup
prepare_setup_packet(rhport);
// drop both EP0 bits: acked above, and neither belongs to the request this SETUP starts
int_status &= ~(TU_BIT(0) | TU_BIT(1));
}
// Endpoint transfer complete interrupt
process_xfer_isr(rhport, int_status);
}
#endif
|