Open FFBoard
Open source force feedback firmware
dcd_msp430x5xx.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019-2020 William D. Jones
5 * Copyright (c) 2019-2020 Ha Thach (tinyusb.org)
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * This file is part of the TinyUSB stack.
26 */
27
28#include "tusb_option.h"
29
30#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MSP430x5xx )
31
32#include "msp430.h"
33#include "device/dcd.h"
34
35/*------------------------------------------------------------------*/
36/* MACRO TYPEDEF CONSTANT ENUM
37 *------------------------------------------------------------------*/
38// usbpllir_mirror and usbmaintl_mirror can be added later if needed.
39static volatile uint16_t usbiepie_mirror = 0;
40static volatile uint16_t usboepie_mirror = 0;
41static volatile uint8_t usbie_mirror = 0;
42static volatile uint16_t usbpwrctl_mirror = 0;
43static bool in_isr = false;
44
45uint8_t _setup_packet[8];
46
47// Xfer control
48typedef struct
49{
50 uint8_t * buffer;
51 // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
52 uint16_t total_len;
53 uint16_t queued_len;
54 uint16_t max_size;
55 bool short_packet;
57
59#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
60
61// Accessing endpoint regs
62typedef volatile uint8_t * ep_regs_t;
63
64typedef enum
65{
66 CNF = 0,
67 BBAX = 1,
68 BCTX = 2,
69 BBAY = 5,
70 BCTY = 6,
71 SIZXY = 7
73
74#define EP_REGS(epnum, dir) ((ep_regs_t) ((uintptr_t)&USBOEPCNF_1 + 64*dir + 8*(epnum - 1)))
75
76static void bus_reset(void)
77{
78 // Hardcoded into the USB core.
81
82 USBKEYPID = USBKEY;
83
84 // Enable the control EP 0. Also enable Indication Enable- a guard flag
85 // separate from the Interrupt Enable mask.
86 USBOEPCNF_0 |= (UBME | USBIIE);
87 USBIEPCNF_0 |= (UBME | USBIIE);
88
89 // Enable interrupts for this endpoint.
90 USBOEPIE |= BIT0;
91 USBIEPIE |= BIT0;
92
93 // Clear NAK until a setup packet is received.
94 USBOEPCNT_0 &= ~NAK;
95 USBIEPCNT_0 &= ~NAK;
96
97 // Enable responding to packets.
98 USBCTL |= FEN;
99
100 // Dedicated buffers in hardware for SETUP and EP0, no setup needed.
101 // Now safe to respond to SETUP packets.
102 USBIE |= SETUPIE;
103
104 USBKEYPID = 0;
105}
106
107// Controls reset behavior of the USB module on receipt of a bus reset event.
108// - enable: When true, bus reset events will cause a reset the USB module.
109static void enable_functional_reset(const bool enable)
110{
111 // Check whether or not the USB configuration registers were
112 // locked prior to this function being called so that, if
113 // necessary, the lock state can be restored on exit.
114 bool unlocked = (USBKEYPID == 0xA528) ? true : false;
115
116 if(!unlocked) USBKEYPID = USBKEY;
117
118 if(enable)
119 {
120 USBCTL |= FRSTE;
121 }
122 else
123 {
124 USBCTL &= ~FRSTE;
125 }
126
127 if(!unlocked) USBKEYPID = 0;
128}
129
130/*------------------------------------------------------------------*/
131/* Controller API
132 *------------------------------------------------------------------*/
133bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
134 (void) rhport; (void) rh_init;
135
136 USBKEYPID = USBKEY;
137
138 // Enable the module (required to write config regs)!
139 USBCNF |= USB_EN;
140
141 // Reset used interrupts
142 USBOEPIE = 0;
143 USBIEPIE = 0;
144 USBIE = 0;
145 USBOEPIFG = 0;
146 USBIEPIFG = 0;
147 USBIFG = 0;
148 USBPWRCTL &= ~(VUOVLIE | VBONIE | VBOFFIE | VUOVLIFG | VBONIFG | VBOFFIFG);
149 usboepie_mirror = 0;
150 usbiepie_mirror = 0;
151 usbie_mirror = 0;
153
154 USBVECINT = 0;
155
156 if(USBPWRCTL & USBBGVBV) {// Bus power detected?
157 USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
158 USBIE |= RSTRIE; // Enable reset and wait for it before continuing.
159 USBCNF |= PUR_EN; // Enable pullup.
160 } else {
161 USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
162 USBCNF &= ~USB_EN; // Disable USB module until bus power is detected.
163 }
164
165 USBKEYPID = 0;
166
167 return true;
168}
169
170// There is no "USB peripheral interrupt disable" bit on MSP430, so we have
171// to save the relevant registers individually.
172// WARNING: Unlike the ARM/NVIC routines, these functions are _not_ idempotent
173// if you modified the registers saved in between calls so they don't match
174// the mirrors; mirrors will be updated to reflect most recent register
175// contents.
176void dcd_int_enable (uint8_t rhport)
177{
178 (void) rhport;
179
180 __bic_SR_register(GIE); // Unlikely to be called in ISR, but let's be safe.
181 // Also, this cleanly disables all USB interrupts
182 // atomically from application's POV.
183
184 // This guard is required because tinyusb can enable interrupts without
185 // having disabled them first.
186 if(in_isr)
187 {
188 USBOEPIE = usboepie_mirror;
189 USBIEPIE = usbiepie_mirror;
190 USBIE = usbie_mirror;
191 USBPWRCTL |= usbpwrctl_mirror;
192 }
193
194 in_isr = false;
195 __bis_SR_register(GIE);
196}
197
198void dcd_int_disable (uint8_t rhport)
199{
200 (void) rhport;
201
202 __bic_SR_register(GIE);
203 usboepie_mirror = USBOEPIE;
204 usbiepie_mirror = USBIEPIE;
205 usbie_mirror = USBIE;
206 usbpwrctl_mirror = (USBPWRCTL & (VUOVLIE | VBONIE | VBOFFIE));
207 USBOEPIE = 0;
208 USBIEPIE = 0;
209 USBIE = 0;
210 USBPWRCTL &= ~(VUOVLIE | VBONIE | VBOFFIE);
211 in_isr = true;
212 __bis_SR_register(GIE);
213}
214
215void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
216{
217 (void) rhport;
218
219 USBFUNADR = dev_addr;
220
221 // Response with status after changing device address
222 dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
223}
224
225void dcd_remote_wakeup(uint8_t rhport)
226{
227 (void) rhport;
228}
229
230void dcd_connect(uint8_t rhport)
231{
232 dcd_int_disable(rhport);
233
234 USBKEYPID = USBKEY;
235 USBCNF |= PUR_EN; // Enable pullup.
236 USBKEYPID = 0;
237
238 dcd_int_enable(rhport);
239}
240
241void dcd_disconnect(uint8_t rhport)
242{
243 dcd_int_disable(rhport);
244
245 USBKEYPID = USBKEY;
246 USBCNF &= ~PUR_EN; // Disable pullup.
247 USBKEYPID = 0;
248
249 dcd_int_enable(rhport);
250}
251
252void dcd_sof_enable(uint8_t rhport, bool en)
253{
254 (void) rhport;
255 (void) en;
256
257 // TODO implement later
258}
259
260/*------------------------------------------------------------------*/
261/* DCD Endpoint port
262 *------------------------------------------------------------------*/
263
264bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
265{
266 (void) rhport;
267
268 uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
269 uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
270
271 // Unsupported endpoint numbers or type (Iso not supported. Control
272 // not supported on nonzero endpoints).
273 if( (epnum > 7) || \
274 (desc_edpt->bmAttributes.xfer == 0) || \
275 (desc_edpt->bmAttributes.xfer == 1)) {
276 return false;
277 }
278
279 xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
280 xfer->max_size = tu_edpt_packet_size(desc_edpt);
281
282 // Buffer allocation scheme:
283 // For simplicity, only single buffer for now, since tinyusb currently waits
284 // for an xfer to complete before scheduling another one. This means only
285 // the X buffer is used.
286 //
287 // 1904 bytes are available, the max endpoint size supported on msp430 is
288 // 64 bytes. This is enough RAM for all 14 endpoints enabled _with_ double
289 // bufferring (64*14*2 = 1792 bytes). Extra RAM exists for triple and higher
290 // order bufferring, which must be maintained in software.
291 //
292 // For simplicity, each endpoint gets a hardcoded 64 byte chunk (regardless
293 // of actual wMaxPacketSize) whose start address is the following:
294 // addr = 128 * (epnum - 1) + 64 * dir.
295 //
296 // Double buffering equation:
297 // x_addr = 256 * (epnum - 1) + 128 * dir
298 // y_addr = x_addr + 64
299 // Address is right-shifted by 3 to fit into 8 bits.
300
301 uint8_t buf_base = (128 * (epnum - 1) + 64 * dir) >> 3;
302
303 // IN and OUT EP registers have the same structure.
304 ep_regs_t ep_regs = EP_REGS(epnum, dir);
305
306 // FIXME: I was able to get into a situation where OUT EP 3 would stall
307 // while debugging, despite stall code never being called. It appears
308 // these registers don't get cleared on reset, being part of RAM.
309 // Investigate and see if I can duplicate.
310 // Also, DBUF got set on OUT EP 2 while debugging. Only OUT EPs seem to be
311 // affected at this time. USB RAM directly precedes main RAM; perhaps I'm
312 // overwriting registers via buffer overflow w/ my debugging code?
313 ep_regs[SIZXY] = tu_edpt_packet_size(desc_edpt);
314 ep_regs[BCTX] |= NAK;
315 ep_regs[BBAX] = buf_base;
316 ep_regs[CNF] &= ~(TOGGLE | STALL | DBUF); // ISO xfers not supported on
317 // MSP430, so no need to gate DATA0/1 and frame
318 // behavior. Clear stall and double buffer bit as
319 // well- see above comment.
320 ep_regs[CNF] |= (UBME | USBIIE);
321
322 USBKEYPID = USBKEY;
323 if(dir == TUSB_DIR_OUT)
324 {
325 USBOEPIE |= (1 << epnum);
326 }
327 else
328 {
329 USBIEPIE |= (1 << epnum);
330 }
331 USBKEYPID = 0;
332
333 return true;
334}
335
336void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
337 (void) rhport; (void) ep_addr;
338 // TODO implement dcd_edpt_close()
339}
340
341void dcd_edpt_close_all (uint8_t rhport)
342{
343 (void) rhport;
344 // TODO implement dcd_edpt_close_all()
345}
346
347bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
348{
349 (void) rhport;
350
351 uint8_t const epnum = tu_edpt_number(ep_addr);
352 uint8_t const dir = tu_edpt_dir(ep_addr);
353
354 xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
355 xfer->buffer = buffer;
356 // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
358 xfer->queued_len = 0;
359 xfer->short_packet = false;
360
361 if(epnum == 0)
362 {
363 if(dir == TUSB_DIR_OUT)
364 {
365 // Interrupt will notify us when data was received.
366 USBCTL &= ~DIR;
367 USBOEPCNT_0 &= ~NAK;
368 }
369 else
370 {
371 // Kickstart the IN packet handler by queuing initial data and calling
372 // the ISR to transmit the first packet.
373 // Interrupt only fires on completed xfer.
374 USBCTL |= DIR;
375 USBIEPIFG |= BIT0;
376 }
377 }
378 else
379 {
380 ep_regs_t ep_regs = EP_REGS(epnum, dir);
381
382 if(dir == TUSB_DIR_OUT)
383 {
384 ep_regs[BCTX] &= ~NAK;
385 }
386 else
387 {
388 USBIEPIFG |= (1 << epnum);
389 }
390 }
391
392 return true;
393}
394
395#if 0 // TODO support dcd_edpt_xfer_fifo API
396bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
397{
398 (void) rhport;
399
400 uint8_t const epnum = tu_edpt_number(ep_addr);
401 uint8_t const dir = tu_edpt_dir(ep_addr);
402
403 xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
404 xfer->buffer = NULL;
405 xfer->ff = ff;
407 xfer->queued_len = 0;
408 xfer->short_packet = false;
409
410 ep_regs_t ep_regs = EP_REGS(epnum, dir);
411
412 if(dir == TUSB_DIR_OUT)
413 {
414 ep_regs[BCTX] &= ~NAK;
415 }
416 else
417 {
418 USBIEPIFG |= (1 << epnum);
419 }
420
421 return true;
422}
423#endif
424
425void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
426{
427 (void) rhport;
428
429 uint8_t const epnum = tu_edpt_number(ep_addr);
430 uint8_t const dir = tu_edpt_dir(ep_addr);
431
432 if(epnum == 0)
433 {
434 if(dir == TUSB_DIR_OUT)
435 {
436 USBOEPCNT_0 |= NAK;
437 USBOEPCNF_0 |= STALL;
438 }
439 else
440 {
441 USBIEPCNT_0 |= NAK;
442 USBIEPCNF_0 |= STALL;
443 }
444 }
445 else
446 {
447 ep_regs_t ep_regs = EP_REGS(epnum, dir);
448 ep_regs[CNF] |= STALL;
449 }
450}
451
452void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
453{
454 (void) rhport;
455
456 uint8_t const epnum = tu_edpt_number(ep_addr);
457 uint8_t const dir = tu_edpt_dir(ep_addr);
458
459 if(epnum == 0)
460 {
461 if(dir == TUSB_DIR_OUT)
462 {
463 USBOEPCNF_0 &= ~STALL;
464 }
465 else
466 {
467 USBIEPCNF_0 &= ~STALL;
468 }
469 }
470 else
471 {
472 ep_regs_t ep_regs = EP_REGS(epnum, dir);
473 // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
474 // and bulk endpoints.
475 ep_regs[CNF] &= ~(STALL + TOGGLE);
476 }
477}
478
480{
481 (void) rhport;
482 (void) request;
483
484 // FIXME: Per manual, we should be clearing the NAK bits of EP0 after the
485 // Status Phase of a control xfer is done, in preparation of another possible
486 // SETUP packet. However, from my own testing, SETUP packets _are_ correctly
487 // handled by the USB core without clearing the NAKs.
488 //
489 // Right now, clearing NAKs in this callbacks causes a direction mismatch
490 // between host and device on EP0. Figure out why and come back to this.
491 // USBOEPCNT_0 &= ~NAK;
492 // USBIEPCNT_0 &= ~NAK;
493}
494
495/*------------------------------------------------------------------*/
496
497static void receive_packet(uint8_t ep_num)
498{
499 xfer_ctl_t * xfer = XFER_CTL_BASE(ep_num, TUSB_DIR_OUT);
500 ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_OUT);
501 uint8_t xfer_size;
502
503 if(ep_num == 0)
504 {
505 xfer_size = USBOEPCNT_0 & 0x0F;
506 }
507 else
508 {
509 xfer_size = ep_regs[BCTX] & 0x7F;
510 }
511
512 uint16_t remaining = xfer->total_len - xfer->queued_len;
513 uint16_t to_recv_size;
514
515 if(remaining <= xfer->max_size) {
516 // Avoid buffer overflow.
517 to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
518 } else {
519 // Room for full packet, choose recv_size based on what the microcontroller
520 // claims.
521 to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
522 }
523
524#if 0 // TODO support dcd_edpt_xfer_fifo API
525 if (xfer->ff)
526 {
527 volatile uint8_t * ep_buf = (ep_num == 0) ? &USBOEP0BUF : (&USBSTABUFF + (ep_regs[BBAX] << 3));
528 tu_fifo_write_n(xfer->ff, (const void *) ep_buf, to_recv_size);
529 }
530 else
531#endif
532 {
533 uint8_t * base = (xfer->buffer + xfer->queued_len);
534
535 if(ep_num == 0)
536 {
537 volatile uint8_t * ep0out_buf = &USBOEP0BUF;
538 for(uint16_t i = 0; i < to_recv_size; i++)
539 {
540 base[i] = ep0out_buf[i];
541 }
542 }
543 else
544 {
545 volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
546 for(uint16_t i = 0; i < to_recv_size ; i++)
547 {
548 base[i] = ep_buf[i];
549 }
550 }
551 }
552
553 xfer->queued_len += xfer_size;
554
555 xfer->short_packet = (xfer_size < xfer->max_size);
556 if((xfer->total_len == xfer->queued_len) || xfer->short_packet)
557 {
558 dcd_event_xfer_complete(0, ep_num, xfer->queued_len, XFER_RESULT_SUCCESS, true);
559 }
560 else
561 {
562 // Schedule to receive another packet.
563 if(ep_num == 0)
564 {
565 USBOEPCNT_0 &= ~NAK;
566 }
567 else
568 {
569 ep_regs[BCTX] &= ~NAK;
570 }
571 }
572}
573
574static void transmit_packet(uint8_t ep_num)
575{
576 xfer_ctl_t * xfer = XFER_CTL_BASE(ep_num, TUSB_DIR_IN);
577
578 // First, determine whether we should even send a packet or finish
579 // up the xfer.
580 bool zlp = (xfer->total_len == 0); // By necessity, xfer->total_len will
581 // equal xfer->queued_len for ZLPs.
582 // Of course a ZLP is a short packet.
583 if((!zlp && (xfer->total_len == xfer->queued_len)) || xfer->short_packet)
584 {
585 dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true);
586 return;
587 }
588
589 // Then actually commit to transmit a packet.
590 uint8_t * base = (xfer->buffer + xfer->queued_len);
591 uint16_t remaining = xfer->total_len - xfer->queued_len;
592 uint8_t xfer_size = (xfer->max_size < xfer->total_len) ? xfer->max_size : remaining;
593
594 xfer->queued_len += xfer_size;
595 if(xfer_size < xfer->max_size)
596 {
597 // Next "xfer complete interrupt", the transfer will end.
598 xfer->short_packet = true;
599 }
600
601 if(ep_num == 0)
602 {
603 volatile uint8_t * ep0in_buf = &USBIEP0BUF;
604 for(uint16_t i = 0; i < xfer_size; i++)
605 {
606 ep0in_buf[i] = base[i];
607 }
608
609 USBIEPCNT_0 = (USBIEPCNT_0 & 0xF0) + xfer_size;
610 USBIEPCNT_0 &= ~NAK;
611 }
612 else
613 {
614 ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_IN);
615 volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
616
617#if 0 // TODO support dcd_edpt_xfer_fifo API
618 if (xfer->ff)
619 {
620 tu_fifo_read_n(xfer->ff, (void *) ep_buf, xfer_size);
621 }
622 else
623#endif
624 {
625 for(int i = 0; i < xfer_size; i++)
626 {
627 ep_buf[i] = base[i];
628 }
629 }
630
631 ep_regs[BCTX] = (ep_regs[BCTX] & 0x80) + (xfer_size & 0x7F);
632 ep_regs[BCTX] &= ~NAK;
633 }
634}
635
636static void handle_setup_packet(void)
637{
638 volatile uint8_t * setup_buf = &USBSUBLK;
639
640 for(int i = 0; i < 8; i++)
641 {
642 _setup_packet[i] = setup_buf[i];
643 }
644
645 // Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer.
646 USBIEPCNT_0 |= NAK;
647 USBOEPCNT_0 |= NAK;
648
649 // Clear SETUPIFG to avoid handling in the USBVECINT switch statement.
650 // When handled there the NAKs applied to the endpoints above are
651 // cleared by hardware and the host will receive stale/duplicate data.
652 //
653 // Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide:
654 //
655 // "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on
656 // input endpoint 0 and output endpoint 0 is also cleared."
657 USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround.
658 USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround.
659 USBIFG &= ~SETUPIFG;
660 USBIEPCNF_0 |= UBME; // Errata USB10 workaround.
661 USBOEPCNF_0 |= UBME; // Errata USB10 workaround.
662 dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
663}
664
665#if CFG_TUSB_OS == OPT_OS_NONE
666TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) {
667 // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles
668 // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop
669 volatile uint32_t cycles = (25000 * ms) >> 2;
670 while (cycles > 0) {
671 cycles--;
672 asm("nop");
673 }
674}
675#else
676#define tu_delay(ms) osal_task_delay(ms)
677#endif
678
679static void handle_bus_power_event(void *param) {
680 (void) param;
681
682 tu_delay(5); // Bus power settling delay.
683
684 USBKEYPID = USBKEY;
685
686 if(USBPWRCTL & USBBGVBV) { // Event caused by application of bus power.
687 USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
688 USBPLLDIVB = USBPLLDIVB; // For some reason the PLL will *NOT* lock unless the divider
689 // register is re-written. The assumption here is that this
690 // register was already properly configured during board-level
691 // initialization.
692 USBPLLCTL |= (UPLLEN | UPFDEN); // Enable the PLL.
693
694 uint16_t attempts = 0;
695 do { // Poll the PLL, checking for a successful lock.
696 USBPLLIR = 0;
697 tu_delay(1);
698 attempts++;
699 } while ((attempts < 10) && (USBPLLIR != 0));
700
701 // A successful lock is indicated by all PLL-related interrupt flags being cleared.
702 if(!USBPLLIR) {
703 const tusb_rhport_init_t rhport_init = {
704 .role = TUSB_ROLE_DEVICE,
705 .speed = TUSB_SPEED_FULL
706 };
707 dcd_init(0, &rhport_init); // Re-initialize the USB module.
708 }
709 } else { // Event caused by removal of bus power.
710 USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
711 USBPLLCTL &= ~(UPLLEN | UPFDEN); // Disable the PLL.
712 USBCNF = 0; // Disable the USB module.
713 dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
714 }
715
716 USBKEYPID = 0;
717}
718
719void dcd_int_handler(uint8_t rhport)
720{
721 (void) rhport;
722
723 // Setup is special- reading USBVECINT to handle setup packets is done to
724 // stop hardware-generated NAKs on EP0.
725 uint8_t setup_status = USBIFG & SETUPIFG;
726
727 if(setup_status)
728 {
731 }
732
733 // Workaround possible bug in MSP430 GCC 9.3.0 where volatile variable
734 // USBVECINT is read from twice when only once is intended. The second
735 // (garbage) read seems to be triggered by certain switch statement
736 // configurations.
737 uint16_t curr_vector;
738 #if __GNUC__ > 9 || (__GNUC__ == 9 && __GNUC_MINOR__ > 2)
739 asm volatile ("mov %1, %0"
740 : "=r" (curr_vector)
741 : "m" (USBVECINT));
742 #else
743 curr_vector = USBVECINT;
744 #endif
745
746 switch(curr_vector)
747 {
748 case USBVECINT_NONE:
749 break;
750
751 case USBVECINT_RSTR:
752 enable_functional_reset(false); // Errata USB4 workaround.
753 bus_reset();
755 break;
756
757 case USBVECINT_PWR_VBUSOn:
758 case USBVECINT_PWR_VBUSOff: {
759 USBKEYPID = USBKEY;
760 // Prevent (possibly) unstable power from generating spurious interrupts.
761 USBPWRCTL &= ~(VBONIE | VBOFFIE);
762 USBKEYPID = 0;
763
764 dcd_event_t event;
765
766 event.rhport = 0;
767 event.event_id = USBD_EVENT_FUNC_CALL;
768 event.func_call.func = handle_bus_power_event;
769
770 dcd_event_handler(&event, true);
771 }
772 break;
773
774 // Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
775 // is received. At this point, even though the hardware is no longer
776 // forcing NAKs, the EP0 NAK bits should still be set to avoid
777 // sending/receiving data before tinyusb is ready.
778 //
779 // Furthermore, it's possible for the hardware to STALL in the middle of
780 // a control xfer if the EP0 NAK bits aren't set properly.
781 // See: https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/845259
782 // From my testing, if all of the following hold:
783 // * OUT EP0 NAK is cleared.
784 // * IN EP0 NAK is set.
785 // * DIR bit in USBCTL is clear.
786 // and an IN packet is received on EP0, the USB core will STALL. Setting
787 // both EP0 NAKs manually when a SETUP packet is received, as is done
788 // in handle_setup_packet(), avoids meeting STALL conditions.
789 //
790 // TODO: Figure out/explain why the STALL condition can be reached in the
791 // first place. When I first noticed the STALL, the only two places I
792 // touched the NAK bits were in dcd_edpt_xfer() and to _set_ (sic) them in
793 // bus_reset(). SETUP packet handling should've been unaffected.
794 case USBVECINT_SETUP_PACKET_RECEIVED:
795 break;
796
797 case USBVECINT_INPUT_ENDPOINT0:
800 break;
801
802 case USBVECINT_OUTPUT_ENDPOINT0:
805 break;
806
807 case USBVECINT_INPUT_ENDPOINT1:
808 case USBVECINT_INPUT_ENDPOINT2:
809 case USBVECINT_INPUT_ENDPOINT3:
810 case USBVECINT_INPUT_ENDPOINT4:
811 case USBVECINT_INPUT_ENDPOINT5:
812 case USBVECINT_INPUT_ENDPOINT6:
813 case USBVECINT_INPUT_ENDPOINT7:
814 {
815 uint8_t ep = ((curr_vector - USBVECINT_INPUT_ENDPOINT1) >> 1) + 1;
816 transmit_packet(ep);
817 }
818 break;
819
820 case USBVECINT_OUTPUT_ENDPOINT1:
821 case USBVECINT_OUTPUT_ENDPOINT2:
822 case USBVECINT_OUTPUT_ENDPOINT3:
823 case USBVECINT_OUTPUT_ENDPOINT4:
824 case USBVECINT_OUTPUT_ENDPOINT5:
825 case USBVECINT_OUTPUT_ENDPOINT6:
826 case USBVECINT_OUTPUT_ENDPOINT7:
827 {
828 uint8_t ep = ((curr_vector - USBVECINT_OUTPUT_ENDPOINT1) >> 1) + 1;
829 receive_packet(ep);
830 }
831 break;
832
833 default:
834 while(true);
835 }
836
837}
838
839#endif
static TU_ATTR_ALWAYS_INLINE void dcd_event_bus_signal(uint8_t rhport, dcd_eventid_t eid, bool in_isr)
Definition: dcd.h:196
static TU_ATTR_ALWAYS_INLINE void dcd_event_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
Definition: dcd.h:222
static TU_ATTR_ALWAYS_INLINE void dcd_event_setup_received(uint8_t rhport, uint8_t const *setup, bool in_isr)
Definition: dcd.h:213
dcd_event_t
Definition: dcd.h:86
void dcd_event_handler(dcd_event_t const *event, bool in_isr)
Definition: usbd.c:1153
static TU_ATTR_ALWAYS_INLINE void dcd_event_bus_reset(uint8_t rhport, tusb_speed_t speed, bool in_isr)
Definition: dcd.h:204
static EPx_REGS *const ep_regs[EP_MAX]
Definition: dcd_da146xx.c:203
xfer_ctl_t
Definition: dcd_dwc2.c:52
static void receive_packet(uint8_t ep_num)
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes)
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
uint8_t _setup_packet[8]
static void bus_reset(void)
static volatile uint16_t usbpwrctl_mirror
static void handle_setup_packet(void)
void dcd_int_handler(uint8_t rhport)
void dcd_disconnect(uint8_t rhport)
xfer_ctl_t xfer_status[8][2]
static volatile uint8_t usbie_mirror
static void handle_bus_power_event(void *param)
volatile uint8_t * ep_regs_t
void dcd_edpt_close_all(uint8_t rhport)
static bool in_isr
void dcd_int_disable(uint8_t rhport)
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
static volatile uint16_t usboepie_mirror
void dcd_connect(uint8_t rhport)
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
static TU_ATTR_ALWAYS_INLINE void tu_delay(uint32_t ms)
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request)
static void transmit_packet(uint8_t ep_num)
ep_regs_index_t
@ BBAX
@ BBAY
@ BCTX
@ SIZXY
@ BCTY
@ CNF
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
static volatile uint16_t usbiepie_mirror
static void enable_functional_reset(const bool enable)
void dcd_int_enable(uint8_t rhport)
void dcd_remote_wakeup(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
xfer_td_t xfer[EP_CBI_COUNT+1][2]
Definition: dcd_nrf5x.c:119
uint16_t total_bytes
Definition: dcd_nuc505.c:113
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
uint8_t const * buffer
Definition: midi_device.h:100
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
uint8_t bEndpointAddress
Definition: video.h:306
tusb_role_t role
Definition: tusb_types.h:281
uint16_t max_size
Definition: dcd_esp32sx.c:71
uint16_t total_len
Definition: dcd_nrf5x.c:100
uint8_t * buffer
Definition: dcd_nrf5x.c:99
uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n)
This function will write n elements into the array index specified by the write pointer and increment...
Definition: tusb_fifo.c:861
uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n)
This function will read n elements from the array index specified by the read pointer and increment t...
Definition: tusb_fifo.c:730
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_DIR_OUT
Definition: tusb_types.h:66
@ TUSB_DIR_IN_MASK
Definition: tusb_types.h:69
@ TUSB_SPEED_FULL
Definition: tusb_types.h:50
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
static TU_ATTR_ALWAYS_INLINE uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const *desc_ep)
Definition: tusb_types.h:515
TU_ATTR_PACKED_END TU_ATTR_BIT_FIELD_ORDER_END static TU_ATTR_ALWAYS_INLINE tusb_dir_t tu_edpt_dir(uint8_t addr)
Definition: tusb_types.h:502
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
Definition: tusb_types.h:511
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259