Open FFBoard
Open source force feedback firmware
hcd_musb.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2021 Koji KITAYAMA
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if CFG_TUH_ENABLED && \
30 TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
31
32#if __GNUC__ > 8 && defined(__ARM_FEATURE_UNALIGNED)
33/* GCC warns that an address may be unaligned, even though
34 * the target CPU has the capability for unaligned memory access. */
35_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
36#endif
37
38#include "host/hcd.h"
39
40#include "musb_type.h"
41
42#if TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
43 #include "musb_ti.h"
44#else
45 #error "Unsupported MCUs"
46#endif
47
48#ifndef HCD_ATTR_ENDPOINT_MAX
49# define HCD_ATTR_ENDPOINT_MAX 8
50#endif
51
52/*------------------------------------------------------------------
53 * MACRO TYPEDEF CONSTANT ENUM DECLARATION
54 *------------------------------------------------------------------*/
55#define REQUEST_TYPE_INVALID (0xFFu)
56
57typedef struct {
58 uint_fast16_t beg; /* offset of including first element */
59 uint_fast16_t end; /* offset of excluding the last element */
61
62typedef struct TU_ATTR_PACKED {
63 uint8_t TXFUNCADDR;
64 uint8_t RESERVED0;
65 uint8_t TXHUBADDR;
66 uint8_t TXHUBPORT;
67 uint8_t RXFUNCADDR;
68 uint8_t RESERVED1;
69 uint8_t RXHUBADDR;
70 uint8_t RXHUBPORT;
72
73typedef struct TU_ATTR_PACKED {
75 uint8_t TXCSRL;
76 uint8_t TXCSRH;
78 uint8_t RXCSRL;
79 uint8_t RXCSRH;
81 uint8_t TXTYPE;
82 uint8_t TXINTERVAL;
83 uint8_t RXTYPE;
84 uint8_t RXINTERVAL;
87
88typedef union {
89 uint8_t u8;
90 uint16_t u16;
91 uint32_t u32;
92} hw_fifo_t;
93
94typedef struct TU_ATTR_PACKED
95{
96 void *buf; /* the start address of a transfer data buffer */
97 uint16_t length; /* the number of bytes in the buffer */
98 uint16_t remaining; /* the number of bytes remaining in the buffer */
100
101typedef struct TU_ATTR_PACKED
102{
103 uint8_t dev;
104 uint8_t ep;
106
107typedef struct
108{
109 bool need_reset; /* The device has not been reset after connection. */
111 uint8_t ctl_mps[7]; /* EP0 max packet size for each device */
113 pipe_state_t pipe[7][2]; /* pipe[pipe number - 1][direction 0:RX 1:TX] */
114 pipe_addr_t addr[7][2]; /* addr[pipe number - 1][direction 0:RX 1:TX] */
115} hcd_data_t;
116
117/*------------------------------------------------------------------
118 * INTERNAL OBJECT & FUNCTION DECLARATION
119 *------------------------------------------------------------------*/
121
122static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
123{
124 free_block_t *cur = beg;
125 for (; cur < end && ((addr < cur->beg) || (cur->end <= addr)); ++cur) ;
126 return cur;
127}
128
129static inline int update_free_block_list(free_block_t *blks, unsigned num, uint_fast16_t addr, uint_fast16_t size)
130{
131 free_block_t *p = find_containing_block(blks, blks + num, addr);
132 TU_ASSERT(p != blks + num, -2);
133 if (p->beg == addr) {
134 /* Shrink block */
135 p->beg = addr + size;
136 if (p->beg != p->end) return 0;
137 /* remove block */
138 free_block_t *end = blks + num;
139 while (p + 1 < end) {
140 *p = *(p + 1);
141 ++p;
142 }
143 return -1;
144 } else {
145 /* Split into 2 blocks */
146 free_block_t tmp = {
147 .beg = addr + size,
148 .end = p->end
149 };
150 p->end = addr;
151 if (p->beg == p->end) {
152 if (tmp.beg != tmp.end) {
153 *p = tmp;
154 return 0;
155 }
156 /* remove block */
157 free_block_t *end = blks + num;
158 while (p + 1 < end) {
159 *p = *(p + 1);
160 ++p;
161 }
162 return -1;
163 }
164 if (tmp.beg == tmp.end) return 0;
165 blks[num] = tmp;
166 return 1;
167 }
168}
169
170static inline unsigned free_block_size(free_block_t const *blk)
171{
172 return blk->end - blk->beg;
173}
174
175static unsigned find_free_memory(uint_fast16_t size_in_log2_minus3)
176{
177 free_block_t free_blocks[2 * (HCD_ATTR_ENDPOINT_MAX - 1)];
178 unsigned num_blocks = 1;
179
180 /* Initialize free memory block list */
181 free_blocks[0].beg = 64 / 8;
182 free_blocks[0].end = (4 << 10) / 8; /* 4KiB / 8 bytes */
183 for (int i = 1; i < HCD_ATTR_ENDPOINT_MAX; ++i) {
184 uint_fast16_t addr;
185 int num;
186 USB0->EPIDX = i;
187 addr = USB0->TXFIFOADD;
188 if (addr) {
189 unsigned sz = USB0->TXFIFOSZ;
190 unsigned sft = (sz & USB_TXFIFOSZ_SIZE_M) + ((sz & USB_TXFIFOSZ_DPB) ? 1: 0);
191 num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
192 TU_ASSERT(-2 < num, 0);
193 num_blocks += num;
194 }
195 addr = USB0->RXFIFOADD;
196 if (addr) {
197 unsigned sz = USB0->RXFIFOSZ;
198 unsigned sft = (sz & USB_RXFIFOSZ_SIZE_M) + ((sz & USB_RXFIFOSZ_DPB) ? 1: 0);
199 num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
200 TU_ASSERT(-2 < num, 0);
201 num_blocks += num;
202 }
203 }
204
205 /* Find the best fit memory block */
206 uint_fast16_t size_in_8byte_unit = 1 << size_in_log2_minus3;
207 free_block_t const *min = NULL;
208 uint_fast16_t min_sz = 0xFFFFu;
209 free_block_t const *end = &free_blocks[num_blocks];
210 for (free_block_t const *cur = &free_blocks[0]; cur < end; ++cur) {
211 uint_fast16_t sz = free_block_size(cur);
212 if (sz < size_in_8byte_unit) continue;
213 if (size_in_8byte_unit == sz) return cur->beg;
214 if (sz < min_sz) min = cur;
215 }
216 TU_ASSERT(min, 0);
217 return min->beg;
218}
219
220static inline volatile hw_endpoint_t* edpt_regs(unsigned epnum_minus1)
221{
222 volatile hw_endpoint_t *regs = (volatile hw_endpoint_t*)((uintptr_t)&USB0->TXMAXP1);
223 return regs + epnum_minus1;
224}
225
226static unsigned find_pipe(uint_fast8_t dev_addr, uint_fast8_t ep_addr)
227{
228 unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
229 pipe_addr_t const *p = &_hcd.addr[0][dir_tx];
230 for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i, p += 2) {
231 if ((dev_addr == p->dev) && (ep_addr == p->ep))
232 return i + 1;
233 }
234 return 0;
235}
236
237static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
238{
239 volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
240 uintptr_t addr = (uintptr_t)buf;
241 while (len >= 4) {
242 reg->u32 = *(uint32_t const *)addr;
243 addr += 4;
244 len -= 4;
245 }
246 if (len >= 2) {
247 reg->u16 = *(uint16_t const *)addr;
248 addr += 2;
249 len -= 2;
250 }
251 if (len) {
252 reg->u8 = *(uint8_t const *)addr;
253 }
254}
255
256static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
257{
258 volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
259 uintptr_t addr = (uintptr_t)buf;
260 while (len >= 4) {
261 *(uint32_t *)addr = reg->u32;
262 addr += 4;
263 len -= 4;
264 }
265 if (len >= 2) {
266 *(uint16_t *)addr = reg->u16;
267 addr += 2;
268 len -= 2;
269 }
270 if (len) {
271 *(uint8_t *)addr = reg->u8;
272 }
273}
274
275static bool edpt0_xfer_out(void)
276{
277 pipe_state_t *pipe = &_hcd.pipe0;
278 unsigned const rem = pipe->remaining;
279 if (!rem) {
280 pipe->buf = NULL;
281 return true;
282 }
283 unsigned const dev_addr = USB0->TXFUNCADDR0;
284 unsigned const mps = _hcd.ctl_mps[dev_addr];
285 unsigned const len = TU_MIN(rem, mps);
286 void *buf = pipe->buf;
287 if (len) {
288 pipe_write_packet(buf, &USB0->FIFO0_WORD, len);
289 pipe->buf = (uint8_t*)buf + len;
290 }
291 pipe->remaining = rem - len;
292 USB0->CSRL0 = USB_CSRL0_TXRDY;
293 return false;
294}
295
296static bool edpt0_xfer_in(void)
297{
298 pipe_state_t *pipe = &_hcd.pipe0;
299 unsigned const rem = pipe->remaining;
300 unsigned const dev_addr = USB0->TXFUNCADDR0;
301 unsigned const mps = _hcd.ctl_mps[dev_addr];
302 unsigned const vld = USB0->COUNT0;
303 unsigned const len = TU_MIN(TU_MIN(rem, mps), vld);
304 void *buf = pipe->buf;
305 if (len) {
306 pipe_read_packet(buf, &USB0->FIFO0_WORD, len);
307 pipe->buf = (uint8_t*)buf + len;
308 }
309 pipe->remaining = rem - len;
310 if ((len < mps) || (rem == len)) {
311 pipe->buf = NULL;
312 return true;
313 }
314 USB0->CSRL0 = USB_CSRL0_REQPKT;
315 return false;
316}
317
318static bool edpt0_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
319{
320 (void)rhport;
321
322 unsigned const req = _hcd.bmRequestType;
323 TU_ASSERT(req != REQUEST_TYPE_INVALID);
324 TU_ASSERT(dev_addr < sizeof(_hcd.ctl_mps));
325
326 USB0->TXFUNCADDR0 = dev_addr;
327 const unsigned dir_in = tu_edpt_dir(ep_addr);
328 if (tu_edpt_dir(req) == dir_in) { /* DATA stage */
329 TU_ASSERT(buffer);
331 _hcd.pipe0.length = buflen;
332 _hcd.pipe0.remaining = buflen;
333 if (dir_in)
334 USB0->CSRL0 = USB_CSRL0_REQPKT;
335 else
337 } else { /* STATUS stage */
338 _hcd.pipe0.buf = NULL;
339 _hcd.pipe0.length = 0;
340 _hcd.pipe0.remaining = 0;
341 USB0->CSRL0 = USB_CSRL0_STATUS | (dir_in ? USB_CSRL0_REQPKT: USB_CSRL0_TXRDY);
342 }
343 return true;
344}
345
346static bool pipe_xfer_out(uint_fast8_t pipenum)
347{
348 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][1];
349 unsigned const rem = pipe->remaining;
350 if (!rem) {
351 pipe->buf = NULL;
352 return true;
353 }
354 hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
355 unsigned const mps = regs->TXMAXP;
356 unsigned const len = TU_MIN(rem, mps);
357 void *buf = pipe->buf;
358 if (len) {
359 pipe_write_packet(buf, &USB0->FIFO0_WORD + pipenum, len);
360 pipe->buf = (uint8_t*)buf + len;
361 }
362 pipe->remaining = rem - len;
363 regs->TXCSRL = USB_TXCSRL1_TXRDY;
364 return false;
365}
366
367static bool pipe_xfer_in(uint_fast8_t pipenum)
368{
369 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][0];
370 volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
371
372 TU_ASSERT(regs->RXCSRL & USB_RXCSRL1_RXRDY);
373
374 const unsigned mps = regs->RXMAXP;
375 const unsigned rem = pipe->remaining;
376 const unsigned vld = regs->RXCOUNT;
377 const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
378 void *buf = pipe->buf;
379 if (len) {
380 pipe_read_packet(buf, &USB0->FIFO0_WORD + pipenum, len);
381 pipe->buf = buf + len;
382 pipe->remaining = rem - len;
383 }
384 if ((len < mps) || (rem == len)) {
385 pipe->buf = NULL;
386 return NULL != buf;
387 }
388 regs->RXCSRL = USB_RXCSRL1_REQPKT;
389 return false;
390}
391
392static bool edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
393{
394 (void)rhport;
395 unsigned const pipenum = find_pipe(dev_addr, ep_addr);
396 unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
397 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][dir_tx];
398 pipe->buf = buffer;
399 pipe->length = buflen;
400 pipe->remaining = buflen;
401 if (dir_tx) {
402 pipe_xfer_out(pipenum);
403 } else {
404 volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
405 regs->RXCSRL = USB_RXCSRL1_REQPKT;
406 }
407 return true;
408}
409
410static void process_ep0(uint8_t rhport)
411{
412 (void)rhport;
413
414 uint_fast8_t csrl = USB0->CSRL0;
415 // TU_LOG1(" EP0 CSRL = %x\r\n", csrl);
416
417 unsigned const dev_addr = USB0->TXFUNCADDR0;
418 unsigned const req = _hcd.bmRequestType;
419 if (csrl & (USB_CSRL0_ERROR | USB_CSRL0_NAKTO | USB_CSRL0_STALLED)) {
420 /* No response / NAK timed out / Stall received */
421 if (csrl & (USB_CSRL0_RXRDY | USB_CSRL0_TXRDY))
422 USB0->CSRH0 = USB_CSRH0_FLUSH;
423 USB0->CSRL0 = 0;
424 _hcd.bmRequestType = REQUEST_TYPE_INVALID;
425 uint8_t result = (csrl & USB_CSRL0_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
426 if (REQUEST_TYPE_INVALID == req) { /* SETUP */
427 uint8_t const ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
430 result, true);
431 } else if (csrl & USB_CSRL0_STATUS) { /* STATUS */
432 uint8_t const ep_addr = tu_edpt_dir(req) ?
436 result, true);
437 } else { /* DATA */
438 uint8_t const ep_addr = tu_edpt_dir(req) ?
442 result, true);
443 }
444 return;
445 }
446 if (csrl & USB_CSRL0_STATUS) {
447 /* STATUS IN */
448 TU_ASSERT(USB_CSRL0_RXRDY == (csrl & USB_CSRL0_RXRDY),);
449 TU_ASSERT(0 == USB0->COUNT0,);
450 USB0->CSRH0 = USB_CSRH0_FLUSH;
451 USB0->CSRL0 = 0;
452 _hcd.bmRequestType = REQUEST_TYPE_INVALID;
454 0, XFER_RESULT_SUCCESS, true);
455 return;
456 }
457 if (csrl & USB_CSRL0_RXRDY) {
458 /* DATA IN */
459 TU_ASSERT(REQUEST_TYPE_INVALID != req,);
460 TU_ASSERT(_hcd.pipe0.buf,);
461 if (edpt0_xfer_in()) {
464 XFER_RESULT_SUCCESS, true);
465 }
466 return;
467 }
468
469 /* When CSRL0 is zero, it means that completion of sending a any length packet. */
470 if (!_hcd.pipe0.buf) {
471 /* STATUS OUT */
472 TU_ASSERT(REQUEST_TYPE_INVALID != req,);
473 _hcd.bmRequestType = REQUEST_TYPE_INVALID;
474 /* EP address is the reverse direction of DATA stage */
475 uint8_t const ep_addr = tu_edpt_dir(req) ?
478 return;
479 }
480 if (REQUEST_TYPE_INVALID == req) {
481 /* SETUP */
482 _hcd.bmRequestType = *(uint8_t*)_hcd.pipe0.buf;
483 _hcd.pipe0.buf = NULL;
485 8, XFER_RESULT_SUCCESS, true);
486 return;
487 }
488
489 /* DATA OUT */
490 if (edpt0_xfer_out()) {
493 XFER_RESULT_SUCCESS, true);
494 }
495}
496
497static void process_pipe_tx(uint8_t rhport, uint_fast8_t pipenum)
498{
499 (void)rhport;
500 bool completed;
501 uint8_t result;
502
503 volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
504 unsigned const csrl = regs->TXCSRL;
505 // TU_LOG1(" TXCSRL%d = %x\r\n", pipenum, csrl);
506 if (csrl & (USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) {
507 if (csrl & USB_TXCSRL1_TXRDY)
508 regs->TXCSRL = (csrl & ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) | USB_TXCSRL1_FLUSH;
509 else
510 regs->TXCSRL = csrl & ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR);
511 completed = true;
512 result = (csrl & USB_TXCSRL1_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
513 } else {
514 completed = pipe_xfer_out(pipenum);
515 result = XFER_RESULT_SUCCESS;
516 }
517 if (completed) {
518 pipe_addr_t *addr = &_hcd.addr[pipenum - 1][1];
519 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][1];
520 hcd_event_xfer_complete(addr->dev, addr->ep,
521 pipe->length - pipe->remaining,
522 result, true);
523 }
524}
525
526static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
527{
528 (void)rhport;
529 bool completed;
530 uint8_t result;
531
532 volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
533 unsigned const csrl = regs->RXCSRL;
534 // TU_LOG1(" RXCSRL%d = %x\r\n", pipenum, csrl);
535 if (csrl & (USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) {
536 if (csrl & USB_RXCSRL1_RXRDY)
537 regs->RXCSRL = (csrl & ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) | USB_RXCSRL1_FLUSH;
538 else
539 regs->RXCSRL = csrl & ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR);
540 completed = true;
541 result = (csrl & USB_RXCSRL1_STALLED) ? XFER_RESULT_STALLED: XFER_RESULT_FAILED;
542 } else {
543 completed = pipe_xfer_in(pipenum);
544 result = XFER_RESULT_SUCCESS;
545 }
546 if (completed) {
547 pipe_addr_t *addr = &_hcd.addr[pipenum - 1][0];
548 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][0];
549 hcd_event_xfer_complete(addr->dev, addr->ep,
550 pipe->length - pipe->remaining,
551 result, true);
552 }
553}
554
555/*------------------------------------------------------------------
556 * Host API
557 *------------------------------------------------------------------*/
558
559bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
560 (void) rhport;
561 (void) rh_init;
562
563 NVIC_ClearPendingIRQ(USB0_IRQn);
564 _hcd.bmRequestType = REQUEST_TYPE_INVALID;
565 USB0->DEVCTL |= USB_DEVCTL_SESSION;
566 USB0->IE = USB_IE_DISCON | USB_IE_CONN | USB_IE_BABBLE | USB_IE_RESUME;
567 return true;
568}
569
570void hcd_int_enable(uint8_t rhport)
571{
572 (void)rhport;
573 NVIC_EnableIRQ(USB0_IRQn);
574}
575
576void hcd_int_disable(uint8_t rhport)
577{
578 (void)rhport;
579 NVIC_DisableIRQ(USB0_IRQn);
580}
581
582uint32_t hcd_frame_number(uint8_t rhport)
583{
584 (void)rhport;
585 /* The device must be reset at least once after connection
586 * in order to start the frame counter. */
587 if (_hcd.need_reset) hcd_port_reset(rhport);
588 return USB0->FRAME;
589}
590
591//--------------------------------------------------------------------+
592// Port API
593//--------------------------------------------------------------------+
594
595bool hcd_port_connect_status(uint8_t rhport)
596{
597 (void)rhport;
598 unsigned devctl = USB0->DEVCTL;
599 if (!(devctl & USB_DEVCTL_HOST)) return false;
600 if (devctl & (USB_DEVCTL_LSDEV | USB_DEVCTL_FSDEV)) return true;
601 return false;
602}
603
604void hcd_port_reset(uint8_t rhport)
605{
606 (void)rhport;
607 USB0->POWER |= USB_POWER_HSENAB | USB_POWER_RESET;
608 unsigned cnt = SystemCoreClock / 1000 * 20;
609 while (cnt--) __NOP();
610 USB0->POWER &= ~USB_POWER_RESET;
611 _hcd.need_reset = false;
612}
613
614void hcd_port_reset_end(uint8_t rhport)
615{
616 (void) rhport;
617}
618
620{
621 (void)rhport;
622 unsigned devctl = USB0->DEVCTL;
623 if (devctl & USB_DEVCTL_LSDEV) return TUSB_SPEED_LOW;
624 if (!(devctl & USB_DEVCTL_FSDEV)) return TUSB_SPEED_INVALID;
625 if (USB0->POWER & USB_POWER_HSMODE) return TUSB_SPEED_HIGH;
626 return TUSB_SPEED_FULL;
627}
628
629void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
630{
631 (void)rhport;
632 if (sizeof(_hcd.ctl_mps) <= dev_addr) return;
633
634 unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
635 NVIC_DisableIRQ(USB0_IRQn);
636 _hcd.ctl_mps[dev_addr] = 0;
637 if (!dev_addr) return;
638
639 pipe_addr_t *p = &_hcd.addr[0][0];
640 for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i) {
641 for (unsigned j = 0; j < 2; ++j, ++p) {
642 if (dev_addr != p->dev) continue;
643 hw_addr_t volatile *fadr = (hw_addr_t volatile*)&USB0->TXFUNCADDR0 + i + 1;
644 hw_endpoint_t volatile *regs = edpt_regs(i);
645 USB0->EPIDX = i + 1;
646 if (j) {
647 USB0->TXIE &= ~TU_BIT(i + 1);
648 if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
649 regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
650 else
651 regs->TXCSRL = USB_TXCSRL1_CLRDT;
652 regs->TXMAXP = 0;
653 regs->TXTYPE = 0;
654 regs->TXINTERVAL = 0;
655 fadr->TXFUNCADDR = 0;
656 fadr->TXHUBADDR = 0;
657 fadr->TXHUBPORT = 0;
658 USB0->TXFIFOADD = 0;
659 USB0->TXFIFOSZ = 0;
660 } else {
661 USB0->RXIE &= ~TU_BIT(i + 1);
662 if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
663 regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
664 else
665 regs->RXCSRL = USB_RXCSRL1_CLRDT;
666 regs->RXMAXP = 0;
667 regs->RXTYPE = 0;
668 regs->RXINTERVAL = 0;
669 fadr->RXFUNCADDR = 0;
670 fadr->RXHUBADDR = 0;
671 fadr->RXHUBPORT = 0;
672 USB0->RXFIFOADD = 0;
673 USB0->RXFIFOSZ = 0;
674 }
675 p->dev = 0;
676 p->ep = 0;
677 pipe_state_t *pipe = &_hcd.pipe[i][j];
678 pipe->buf = NULL;
679 pipe->length = 0;
680 pipe->remaining = 0;
681 }
682 }
683 if (ie) NVIC_EnableIRQ(USB0_IRQn);
684}
685
686//--------------------------------------------------------------------+
687// Endpoints API
688//--------------------------------------------------------------------+
689
690bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
691{
692 (void)rhport;
693 pipe_write_packet((void*)(uintptr_t)setup_packet, &USB0->FIFO0_WORD, 8);
694 _hcd.pipe0.buf = (void*)(uintptr_t)setup_packet;
695 _hcd.pipe0.length = 8;
696 _hcd.pipe0.remaining = 0;
697
698 hcd_devtree_info_t devtree;
700 switch (devtree.speed) {
701 default: return false;
702 case TUSB_SPEED_LOW: USB0->TYPE0 = USB_TYPE0_SPEED_LOW; break;
703 case TUSB_SPEED_FULL: USB0->TYPE0 = USB_TYPE0_SPEED_FULL; break;
704 case TUSB_SPEED_HIGH: USB0->TYPE0 = USB_TYPE0_SPEED_HIGH; break;
705 }
706 USB0->TXHUBADDR0 = devtree.hub_addr;
707 USB0->TXHUBPORT0 = devtree.hub_port;
708 USB0->TXFUNCADDR0 = dev_addr;
709 USB0->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_SETUP;
710 return true;
711}
712
713bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
714{
715 (void)rhport;
716 if (sizeof(_hcd.ctl_mps) <= dev_addr) return false;
717 unsigned const ep_addr = ep_desc->bEndpointAddress;
718 unsigned const epn = tu_edpt_number(ep_addr);
719 if (0 == epn) {
721 return true;
722 }
723
724 unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
725 /* Find a free pipe */
726 unsigned pipenum = 0;
727 pipe_addr_t *p = &_hcd.addr[0][dir_tx];
728 for (unsigned i = 0; i < sizeof(_hcd.addr)/sizeof(_hcd.addr[0]); ++i, p += 2) {
729 if (0 == p->ep) {
730 p->dev = dev_addr;
731 p->ep = ep_addr;
732 pipenum = i + 1;
733 break;
734 }
735 }
736 if (!pipenum) return false;
737
738 unsigned const xfer = ep_desc->bmAttributes.xfer;
739 unsigned const mps = tu_edpt_packet_size(ep_desc);
740
741 pipe_state_t *pipe = &_hcd.pipe[pipenum - 1][dir_tx];
742 pipe->buf = NULL;
743 pipe->length = 0;
744 pipe->remaining = 0;
745
746 uint8_t pipe_type = 0;
747 hcd_devtree_info_t devtree;
749 switch (devtree.speed) {
750 default: return false;
751 case TUSB_SPEED_LOW: pipe_type |= USB_TXTYPE1_SPEED_LOW; break;
752 case TUSB_SPEED_FULL: pipe_type |= USB_TXTYPE1_SPEED_FULL; break;
753 case TUSB_SPEED_HIGH: pipe_type |= USB_TXTYPE1_SPEED_HIGH; break;
754 }
755 switch (xfer) {
756 default: return false;
757 case TUSB_XFER_BULK: pipe_type |= USB_TXTYPE1_PROTO_BULK; break;
758 case TUSB_XFER_INTERRUPT: pipe_type |= USB_TXTYPE1_PROTO_INT; break;
759 case TUSB_XFER_ISOCHRONOUS: pipe_type |= USB_TXTYPE1_PROTO_ISOC; break;
760 }
761
762 hw_addr_t volatile *fadr = (hw_addr_t volatile*)&USB0->TXFUNCADDR0 + pipenum;
763 hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
764 if (dir_tx) {
765 fadr->TXFUNCADDR = dev_addr;
766 fadr->TXHUBADDR = devtree.hub_addr;
767 fadr->TXHUBPORT = devtree.hub_port;
768 regs->TXMAXP = mps;
769 regs->TXTYPE = pipe_type | epn;
770 regs->TXINTERVAL = ep_desc->bInterval;
771 if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
772 regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
773 else
774 regs->TXCSRL = USB_TXCSRL1_CLRDT;
775 USB0->TXIE |= TU_BIT(pipenum);
776 } else {
777 fadr->RXFUNCADDR = dev_addr;
778 fadr->RXHUBADDR = devtree.hub_addr;
779 fadr->RXHUBPORT = devtree.hub_port;
780 regs->RXMAXP = mps;
781 regs->RXTYPE = pipe_type | epn;
782 regs->RXINTERVAL = ep_desc->bInterval;
783 if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
784 regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
785 else
786 regs->RXCSRL = USB_RXCSRL1_CLRDT;
787 USB0->RXIE |= TU_BIT(pipenum);
788 }
789
790 /* Setup FIFO */
791 int size_in_log2_minus3 = 28 - TU_MIN(28, __CLZ((uint32_t)mps));
792 if ((8u << size_in_log2_minus3) < mps) ++size_in_log2_minus3;
793 unsigned addr = find_free_memory(size_in_log2_minus3);
794 TU_ASSERT(addr);
795
796 USB0->EPIDX = pipenum;
797 if (dir_tx) {
798 USB0->TXFIFOADD = addr;
799 USB0->TXFIFOSZ = size_in_log2_minus3;
800 } else {
801 USB0->RXFIFOADD = addr;
802 USB0->RXFIFOSZ = size_in_log2_minus3;
803 }
804 return true;
805}
806
807bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
808{
809 (void)rhport;
810 bool ret = false;
811 if (0 == tu_edpt_number(ep_addr)) {
812 ret = edpt0_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
813 } else {
814 ret = edpt_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
815 }
816 return ret;
817}
818
819bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
820 (void) rhport;
821 (void) dev_addr;
822 (void) ep_addr;
823 // TODO not implemented yet
824 return false;
825}
826
827// clear stall, data toggle is also reset to DATA0
828bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
829 (void) rhport;
830 unsigned const pipenum = find_pipe(dev_addr, ep_addr);
831 if (!pipenum) return false;
832 hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
833 unsigned const dir_tx = tu_edpt_dir(ep_addr) ? 0: 1;
834 if (dir_tx)
835 regs->TXCSRL = USB_TXCSRL1_CLRDT;
836 else
837 regs->RXCSRL = USB_RXCSRL1_CLRDT;
838 return true;
839}
840
841/*-------------------------------------------------------------------
842 * ISR
843 *-------------------------------------------------------------------*/
844void hcd_int_handler(uint8_t rhport, bool in_isr)
845{
846 (void) in_isr;
847
848 uint_fast8_t is, txis, rxis;
849
850 is = USB0->IS; /* read and clear interrupt status */
851 txis = USB0->TXIS; /* read and clear interrupt status */
852 rxis = USB0->RXIS; /* read and clear interrupt status */
853 // TU_LOG1("D%2x T%2x R%2x\r\n", is, txis, rxis);
854
855 is &= USB0->IE; /* Clear disabled interrupts */
856 if (is & USB_IS_RESUME) {
857 }
858 if (is & USB_IS_CONN) {
859 _hcd.need_reset = true;
860 hcd_event_device_attach(rhport, true);
861 }
862 if (is & USB_IS_DISCON) {
863 hcd_event_device_remove(rhport, true);
864 }
865 if (is & USB_IS_BABBLE) {
866 }
867 txis &= USB0->TXIE; /* Clear disabled interrupts */
868 if (txis & USB_TXIE_EP0) {
869 process_ep0(rhport);
870 txis &= ~TU_BIT(0);
871 }
872 while (txis) {
873 unsigned const num = __builtin_ctz(txis);
874 process_pipe_tx(rhport, num);
875 txis &= ~TU_BIT(num);
876 }
877 rxis &= USB0->RXIE; /* Clear disabled interrupts */
878 while (rxis) {
879 unsigned const num = __builtin_ctz(rxis);
880 process_pipe_rx(rhport, num);
881 rxis &= ~TU_BIT(num);
882 }
883}
884
885#endif
u32 SystemCoreClock
static bool in_isr
xfer_td_t xfer[EP_CBI_COUNT+1][2]
Definition: dcd_nrf5x.c:119
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
static TU_ATTR_ALWAYS_INLINE unsigned __builtin_ctz(unsigned int value)
Definition: dcd_rusb2.c:923
static TU_ATTR_ALWAYS_INLINE void hcd_event_device_remove(uint8_t rhport, bool in_isr)
Definition: hcd.h:209
static TU_ATTR_ALWAYS_INLINE void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
Definition: hcd.h:221
static TU_ATTR_ALWAYS_INLINE void hcd_event_device_attach(uint8_t rhport, bool in_isr)
Definition: hcd.h:197
void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t *devtree_info)
Definition: usbh.c:949
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
Definition: hcd_musb.c:690
static bool pipe_xfer_in(uint_fast8_t pipenum)
Definition: hcd_musb.c:367
struct TU_ATTR_PACKED pipe_state_t
void hcd_int_disable(uint8_t rhport)
Definition: hcd_musb.c:576
static unsigned find_pipe(uint_fast8_t dev_addr, uint_fast8_t ep_addr)
Definition: hcd_musb.c:226
static void process_pipe_tx(uint8_t rhport, uint_fast8_t pipenum)
Definition: hcd_musb.c:497
static int update_free_block_list(free_block_t *blks, unsigned num, uint_fast16_t addr, uint_fast16_t size)
Definition: hcd_musb.c:129
static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
Definition: hcd_musb.c:237
static free_block_t * find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
Definition: hcd_musb.c:122
bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
Definition: hcd_musb.c:828
void hcd_int_enable(uint8_t rhport)
Definition: hcd_musb.c:570
static volatile hw_endpoint_t * edpt_regs(unsigned epnum_minus1)
Definition: hcd_musb.c:220
static bool edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: hcd_musb.c:392
struct TU_ATTR_PACKED pipe_addr_t
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: hcd_musb.c:807
struct TU_ATTR_PACKED hw_addr_t
_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"")
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
Definition: hcd_musb.c:629
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
Definition: hcd_musb.c:819
static void process_ep0(uint8_t rhport)
Definition: hcd_musb.c:410
void hcd_port_reset_end(uint8_t rhport)
Definition: hcd_musb.c:614
static bool edpt0_xfer_out(void)
Definition: hcd_musb.c:275
void hcd_port_reset(uint8_t rhport)
Definition: hcd_musb.c:604
static bool edpt0_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: hcd_musb.c:318
static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
Definition: hcd_musb.c:256
bool hcd_port_connect_status(uint8_t rhport)
Definition: hcd_musb.c:595
static unsigned find_free_memory(uint_fast16_t size_in_log2_minus3)
Definition: hcd_musb.c:175
uint32_t hcd_frame_number(uint8_t rhport)
Definition: hcd_musb.c:582
static bool pipe_xfer_out(uint_fast8_t pipenum)
Definition: hcd_musb.c:346
static hcd_data_t _hcd
Definition: hcd_musb.c:120
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
Definition: hcd_musb.c:619
static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
Definition: hcd_musb.c:526
struct TU_ATTR_PACKED hw_endpoint_t
void hcd_int_handler(uint8_t rhport, bool in_isr)
Definition: hcd_musb.c:844
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
Definition: hcd_musb.c:559
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc)
Definition: hcd_musb.c:713
static bool edpt0_xfer_in(void)
Definition: hcd_musb.c:296
static unsigned free_block_size(free_block_t const *blk)
Definition: hcd_musb.c:170
TU_ATTR_PACKED_BEGIN TU_ATTR_BIT_FIELD_ORDER_BEGIN union TU_ATTR_PACKED hw_fifo_t
uint8_t const * buffer
Definition: midi_device.h:100
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t RXHUBADDR
Definition: hcd_musb.c:69
uint16_t TXMAXP
Definition: hcd_musb.c:74
uint8_t TXHUBPORT
Definition: hcd_musb.c:66
uint16_t RXMAXP
Definition: hcd_musb.c:77
uint8_t TXFUNCADDR
Definition: hcd_musb.c:63
uint8_t TXCSRH
Definition: hcd_musb.c:76
uint8_t RXTYPE
Definition: hcd_musb.c:83
uint16_t wMaxPacketSize
Definition: tusb_types.h:371
uint16_t RXCOUNT
Definition: hcd_musb.c:80
uint8_t bInterval
Definition: tusb_types.h:372
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
uint8_t RXINTERVAL
Definition: hcd_musb.c:84
uint8_t TXHUBADDR
Definition: hcd_musb.c:65
uint8_t TXINTERVAL
Definition: hcd_musb.c:82
uint16_t length
Definition: dcd_ci_fs.c:94
uint8_t ep
Definition: hcd_musb.c:104
uint8_t RXFUNCADDR
Definition: hcd_musb.c:67
uint8_t dev
Definition: hcd_musb.c:103
volatile uint16_t
Definition: hcd_rusb2.c:58
uint8_t RXHUBPORT
Definition: hcd_musb.c:70
uint16_t RESERVED
Definition: hcd_musb.c:85
void * buf
Definition: dcd_musb.c:69
uint16_t remaining
Definition: dcd_ci_fs.c:95
uint8_t TXTYPE
Definition: hcd_musb.c:81
uint8_t RXCSRL
Definition: hcd_musb.c:78
uint8_t bEndpointAddress
Definition: video.h:306
uint8_t RESERVED0
Definition: hcd_musb.c:64
uint8_t RESERVED1
Definition: hcd_musb.c:68
uint8_t RXCSRH
Definition: hcd_musb.c:79
uint16_t ep[2]
Definition: video_device.c:109
uint8_t TXCSRL
Definition: hcd_musb.c:75
uint_fast16_t end
Definition: hcd_musb.c:59
uint_fast16_t beg
Definition: hcd_musb.c:58
pipe_addr_t addr[7][2]
Definition: hcd_musb.c:114
pipe_state_t pipe0
Definition: hcd_musb.c:112
uint8_t ctl_mps[7]
Definition: hcd_musb.c:111
bool need_reset
Definition: hcd_musb.c:109
uint8_t bmRequestType
Definition: hcd_musb.c:110
pipe_state_t pipe[7][2]
Definition: hcd_musb.c:113
uint8_t hub_port
Definition: hcd.h:96
uint8_t hub_addr
Definition: hcd.h:95
uint8_t speed
Definition: hcd.h:97
uint16_t length
Definition: dcd_rusb2.c:62
void * buf
Definition: dcd_rusb2.c:61
uint16_t remaining
Definition: dcd_rusb2.c:63
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_DIR_OUT
Definition: tusb_types.h:66
tusb_speed_t
defined base on EHCI specs value for Endpoint Speed
Definition: tusb_types.h:49
@ TUSB_SPEED_INVALID
Definition: tusb_types.h:54
@ TUSB_SPEED_FULL
Definition: tusb_types.h:50
@ TUSB_SPEED_LOW
Definition: tusb_types.h:51
@ TUSB_SPEED_HIGH
Definition: tusb_types.h:52
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
@ XFER_RESULT_FAILED
Definition: tusb_types.h:238
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ XFER_RESULT_STALLED
Definition: tusb_types.h:239
static TU_ATTR_ALWAYS_INLINE uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const *desc_ep)
Definition: tusb_types.h:515
@ TUSB_XFER_ISOCHRONOUS
Definition: tusb_types.h:60
@ TUSB_XFER_INTERRUPT
Definition: tusb_types.h:62
@ TUSB_XFER_BULK
Definition: tusb_types.h:61
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
uint16_t u16
Definition: hcd_musb.c:90
uint32_t u32
Definition: hcd_musb.c:91
volatile uint16_t u16
Definition: dcd_musb.c:63
volatile uint32_t u32
Definition: dcd_musb.c:64
volatile uint8_t u8
Definition: dcd_musb.c:62
uint8_t u8
Definition: hcd_musb.c:89