Open FFBoard
Open source force feedback firmware
hcd_max3421.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2023 Ha Thach (tinyusb.org)
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 && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
30
31#include <stdatomic.h>
32#include "host/hcd.h"
33#include "host/usbh.h"
34
35//--------------------------------------------------------------------+
36//
37//--------------------------------------------------------------------+
38
39// Command format is
40// Reg [7:3] | 0 [2] | Dir [1] | Ack [0]
41
42enum {
44};
45
46enum {
47 RCVVFIFO_ADDR = 1u << 3, // 0x08
48 SNDFIFO_ADDR = 2u << 3, // 0x10
49 SUDFIFO_ADDR = 4u << 3, // 0x20
50 RCVBC_ADDR = 6u << 3, // 0x30
51 SNDBC_ADDR = 7u << 3, // 0x38
52 USBIRQ_ADDR = 13u << 3, // 0x68
53 USBIEN_ADDR = 14u << 3, // 0x70
54 USBCTL_ADDR = 15u << 3, // 0x78
55 CPUCTL_ADDR = 16u << 3, // 0x80
56 PINCTL_ADDR = 17u << 3, // 0x88
57 REVISION_ADDR = 18u << 3, // 0x90
58 // 19 is not used
59 IOPINS1_ADDR = 20u << 3, // 0xA0
60 IOPINS2_ADDR = 21u << 3, // 0xA8
61 GPINIRQ_ADDR = 22u << 3, // 0xB0
62 GPINIEN_ADDR = 23u << 3, // 0xB8
63 GPINPOL_ADDR = 24u << 3, // 0xC0
64 HIRQ_ADDR = 25u << 3, // 0xC8
65 HIEN_ADDR = 26u << 3, // 0xD0
66 MODE_ADDR = 27u << 3, // 0xD8
67 PERADDR_ADDR = 28u << 3, // 0xE0
68 HCTL_ADDR = 29u << 3, // 0xE8
69 HXFR_ADDR = 30u << 3, // 0xF0
70 HRSL_ADDR = 31u << 3, // 0xF8
71};
72
73enum {
76 USBIRQ_VBUS_IRQ = 1u << 6,
77};
78
79enum {
80 USBCTL_PWRDOWN = 1u << 4,
81 USBCTL_CHIPRES = 1u << 5,
82};
83
84enum {
85 CPUCTL_IE = 1u << 0,
88};
89
90enum {
91 PINCTL_GPXA = 1u << 0,
92 PINCTL_GPXB = 1u << 1,
93 PINCTL_POSINT = 1u << 2,
94 PINCTL_INTLEVEL = 1u << 3,
95 PINCTL_FDUPSPI = 1u << 4,
96};
97
98enum {
100 HIRQ_RWU_IRQ = 1u << 1,
103 HIRQ_SUSDN_IRQ = 1u << 4,
105 HIRQ_FRAME_IRQ = 1u << 6,
107};
108
109enum {
110 MODE_HOST = 1u << 0,
111 MODE_LOWSPEED = 1u << 1,
112 MODE_HUBPRE = 1u << 2,
113 MODE_SOFKAENAB = 1u << 3,
114 MODE_SEPIRQ = 1u << 4,
115 MODE_DELAYISO = 1u << 5,
116 MODE_DMPULLDN = 1u << 6,
117 MODE_DPPULLDN = 1u << 7,
118};
119
120enum {
121 HCTL_BUSRST = 1u << 0,
122 HCTL_FRMRST = 1u << 1,
123 HCTL_SAMPLEBUS = 1u << 2,
124 HCTL_SIGRSM = 1u << 3,
125 HCTL_RCVTOG0 = 1u << 4,
126 HCTL_RCVTOG1 = 1u << 5,
127 HCTL_SNDTOG0 = 1u << 6,
128 HCTL_SNDTOG1 = 1u << 7,
129};
130
131enum {
133 HXFR_SETUP = 1u << 4,
134 HXFR_OUT_NIN = 1u << 5,
135 HXFR_ISO = 1u << 6,
136 HXFR_HS = 1u << 7,
137};
138
139enum {
141 HRSL_RCVTOGRD = 1u << 4,
142 HRSL_SNDTOGRD = 1u << 5,
143 HRSL_KSTATUS = 1u << 6,
144 HRSL_JSTATUS = 1u << 7,
145};
146
147enum {
164};
165
166enum {
169
170enum {
171 MAX_NAK_DEFAULT = 1 // Number of NAK per endpoint per usb frame to save CPU/SPI bus usage
173
174enum {
178 EP_STATE_ATTEMPT_1 = 3, // Number of attempts to transfer in a frame. Incremented after each NAK
181
182//--------------------------------------------------------------------+
183//
184//--------------------------------------------------------------------+
185
186typedef struct TU_ATTR_PACKED {
187 uint8_t ep_num : 4;
188 uint8_t is_setup : 1;
189 uint8_t is_out : 1;
190 uint8_t is_iso : 1;
192
193TU_VERIFY_STATIC(sizeof(hxfr_bm_t) == 1, "size is not correct");
194
195typedef struct {
196 uint8_t daddr;
197
198 union {
200 uint8_t hxfr;
201 };
202
204 uint8_t state : 4;
205 uint8_t data_toggle : 1;
207 };
208
209 uint16_t total_len;
210 uint16_t xferred_len;
211 uint8_t* buf;
213
214TU_VERIFY_STATIC(sizeof(max3421_ep_t) == 12, "size is not correct");
215
216typedef struct {
217 volatile uint16_t frame_count;
218
219 // cached register
220 uint8_t sndbc;
221 uint8_t hirq;
222 uint8_t hien;
223 uint8_t mode;
224 uint8_t peraddr;
225 union {
227 uint8_t hxfr;
228 };
229
230 // owner of data in SNDFIFO, for retrying NAKed without re-writing to FIFO
231 struct {
232 uint8_t daddr;
233 uint8_t hxfr;
234 }sndfifo_owner;
235
236 atomic_flag busy; // busy transferring
237
238#if OSAL_MUTEX_REQUIRED
239 OSAL_MUTEX_DEF(spi_mutexdef);
241#endif
242
243 max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0
245
247
248// max NAK before giving up in a frame. 0 means infinite NAKs
251 .cpuctl = 0, // default: INT pulse width = 10.6 us
252 .pinctl = 0, // default: negative edge interrupt
253};
254
255//--------------------------------------------------------------------+
256// API: SPI transfer with MAX3421E
257// - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application
258// - reg_read(), reg_write(): is implemented by this driver, can be used by application
259//--------------------------------------------------------------------+
260
261// API to control MAX3421 SPI CS
262extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
263
264// API to transfer data with MAX3421 SPI
265// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
266extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
267
268// API to enable/disable MAX3421 INTR pin interrupt
269extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
270
271// API to read MAX3421's register. Implemented by TinyUSB
272uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
273
274// API to write MAX3421's register. Implemented by TinyUSB
275bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
276
277//--------------------------------------------------------------------+
278// SPI Commands and Helper
279//--------------------------------------------------------------------+
280
281#define reg_read tuh_max3421_reg_read
282#define reg_write tuh_max3421_reg_write
283
284static void max3421_spi_lock(uint8_t rhport, bool in_isr) {
285 // disable interrupt and mutex lock (for pre-emptive RTOS) if not in_isr
286 if (!in_isr) {
287 (void) osal_mutex_lock(_hcd_data.spi_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
288 tuh_max3421_int_api(rhport, false);
289 }
290
291 // assert CS
292 tuh_max3421_spi_cs_api(rhport, true);
293}
294
295static void max3421_spi_unlock(uint8_t rhport, bool in_isr) {
296 // de-assert CS
297 tuh_max3421_spi_cs_api(rhport, false);
298
299 // mutex unlock and re-enable interrupt
300 if (!in_isr) {
301 tuh_max3421_int_api(rhport, true);
303 }
304}
305
306uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr) {
307 uint8_t tx_buf[2] = {reg, 0};
308 uint8_t rx_buf[2] = {0, 0};
309
310 max3421_spi_lock(rhport, in_isr);
311 bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
312 max3421_spi_unlock(rhport, in_isr);
313
314 _hcd_data.hirq = rx_buf[0];
315 return ret ? rx_buf[1] : 0;
316}
317
318bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr) {
319 uint8_t tx_buf[2] = {reg | CMDBYTE_WRITE, data};
320 uint8_t rx_buf[2] = {0, 0};
321
322 max3421_spi_lock(rhport, in_isr);
323 bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
324 max3421_spi_unlock(rhport, in_isr);
325
326 // HIRQ register since we are in full-duplex mode
327 _hcd_data.hirq = rx_buf[0];
328
329 return ret;
330}
331
332//--------------------------------------------------------------------
333// Register helper
334//--------------------------------------------------------------------
335TU_ATTR_ALWAYS_INLINE static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) {
336 reg_write(rhport, HIRQ_ADDR, data, in_isr);
337 // HIRQ write 1 is clear
338 _hcd_data.hirq &= (uint8_t) ~data;
339}
340
341TU_ATTR_ALWAYS_INLINE static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) {
343 reg_write(rhport, HIEN_ADDR, data, in_isr);
344}
345
346TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) {
348 reg_write(rhport, MODE_ADDR, data, in_isr);
349}
350
351TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
352 if ( _hcd_data.peraddr == data ) return; // no need to change address
353
355 reg_write(rhport, PERADDR_ADDR, data, in_isr);
356}
357
358TU_ATTR_ALWAYS_INLINE static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) {
360 reg_write(rhport, HXFR_ADDR, data, in_isr);
361}
362
363TU_ATTR_ALWAYS_INLINE static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
365 reg_write(rhport, SNDBC_ADDR, data, in_isr);
366}
367
368//--------------------------------------------------------------------
369// FIFO access (receive, send, setup)
370//--------------------------------------------------------------------
371static void hwfifo_write(uint8_t rhport, uint8_t reg, const uint8_t* buffer, uint8_t len, bool in_isr) {
372 uint8_t hirq;
373 reg |= CMDBYTE_WRITE;
374
375 max3421_spi_lock(rhport, in_isr);
376
377 tuh_max3421_spi_xfer_api(rhport, &reg, &hirq, 1);
378 _hcd_data.hirq = hirq;
379 tuh_max3421_spi_xfer_api(rhport, buffer, NULL, len);
380
381 max3421_spi_unlock(rhport, in_isr);
382}
383
384// Write to SNDFIFO if len > 0 and update SNDBC
385TU_ATTR_ALWAYS_INLINE static inline void hwfifo_send(uint8_t rhport, const uint8_t* buffer, uint8_t len, bool in_isr) {
386 if (len) {
387 hwfifo_write(rhport, SNDFIFO_ADDR, buffer, len, in_isr);
388 }
389 sndbc_write(rhport, len, in_isr);
390}
391
392TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint8_t* buffer, bool in_isr) {
394}
395
396static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
397 uint8_t hirq;
398 uint8_t const reg = RCVVFIFO_ADDR;
399
400 max3421_spi_lock(rhport, in_isr);
401
402 tuh_max3421_spi_xfer_api(rhport, &reg, &hirq, 1);
403 _hcd_data.hirq = hirq;
404 tuh_max3421_spi_xfer_api(rhport, NULL, buffer, len);
405
406 max3421_spi_unlock(rhport, in_isr);
407}
408
409//--------------------------------------------------------------------+
410// Endpoint helper
411//--------------------------------------------------------------------+
412
413static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
414 uint8_t const is_out = 1-ep_dir;
415 for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
416 max3421_ep_t* ep = &_hcd_data.ep[i];
417 // control endpoint is bi-direction (skip check)
418 if (daddr == ep->daddr && ep_num == ep->hxfr_bm.ep_num && (ep_num == 0 || is_out == ep->hxfr_bm.is_out)) {
419 return ep;
420 }
421 }
422
423 return NULL;
424}
425
426// daddr = 0 and ep_num = 0 means find a free (allocate) endpoint
427TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * allocate_ep(void) {
428 return find_ep_not_addr0(0, 0, 0);
429}
430
431TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * find_opened_ep(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
432 if (daddr == 0 && ep_num == 0) {
433 return &_hcd_data.ep[0];
434 }else{
435 return find_ep_not_addr0(daddr, ep_num, ep_dir);
436 }
437}
438
439// free all endpoints belong to device address
440static void free_ep(uint8_t daddr) {
441 for (size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
442 max3421_ep_t* ep = &_hcd_data.ep[i];
443 if (ep->daddr == daddr) {
444 tu_memclr(ep, sizeof(max3421_ep_t));
445 }
446 }
447}
448
449// Check if endpoint has a queued transfer and not reach max NAK in this frame
450TU_ATTR_ALWAYS_INLINE static inline bool is_ep_pending(max3421_ep_t const * ep) {
451 uint8_t const state = ep->state;
452 return ep->packet_size && (state >= EP_STATE_ATTEMPT_1) &&
454}
455
456// Find the next pending endpoint using round-robin scheduling, starting from next endpoint.
457// return NULL if not found
458// TODO respect interrupt endpoint's interval
460 size_t const idx = (size_t) (cur_ep - _hcd_data.ep);
461
462 // starting from next endpoint
463 for (size_t i = idx + 1; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
464 max3421_ep_t* ep = &_hcd_data.ep[i];
465 if (is_ep_pending(ep)) {
466 return ep;
467 }
468 }
469
470 // wrap around including current endpoint
471 for (size_t i = 0; i <= idx; i++) {
472 max3421_ep_t* ep = &_hcd_data.ep[i];
473 if (is_ep_pending(ep)) {
474 return ep;
475 }
476 }
477
478 return NULL;
479}
480
481//--------------------------------------------------------------------+
482// Controller API
483//--------------------------------------------------------------------+
484
485// optional hcd configuration, called by tuh_configure()
486bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
487 (void) rhport;
488 TU_VERIFY(cfg_id == TUH_CFGID_MAX3421 && cfg_param != NULL);
489
490 tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
491 _tuh_cfg = cfg->max3421;
493 return true;
494}
495
496// Initialize controller to host mode
497bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
498 (void) rh_init;
499
500 tuh_max3421_int_api(rhport, false);
501
502 TU_LOG2_INT(sizeof(max3421_ep_t));
503 TU_LOG2_INT(sizeof(max3421_data_t));
504 TU_LOG2_INT(offsetof(max3421_data_t, ep));
505
506 tu_memclr(&_hcd_data, sizeof(_hcd_data));
507 _hcd_data.peraddr = 0xff; // invalid
508
509#if OSAL_MUTEX_REQUIRED
511#endif
512
513 // NOTE: driver does not seem to work without nRST pin signal
514
515 // full duplex, interrupt negative edge
516 reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false);
517
518 // v1 is 0x01, v2 is 0x12, v3 is 0x13
519 // Note: v1 and v2 has host OUT errata whose workaround is not implemented in this driver
520 uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
521 TU_LOG2_HEX(revision);
522 TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
523
524 // reset
525 reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
526 reg_write(rhport, USBCTL_ADDR, 0, false);
527 while( !(reg_read(rhport, USBIRQ_ADDR, false) & USBIRQ_OSCOK_IRQ) ) {
528 // wait for oscillator to stabilize
529 }
530
531 // Mode: Host and DP/DM pull down
533
534 // frame reset & bus reset, this will trigger CONDET IRQ if device is already connected
535 reg_write(rhport, HCTL_ADDR, HCTL_BUSRST | HCTL_FRMRST, false);
536
537 // clear all previously pending IRQ
538 hirq_write(rhport, 0xff, false);
539
540 // Enable IRQ
541 hien_write(rhport, DEFAULT_HIEN, false);
542
543 tuh_max3421_int_api(rhport, true);
544
545 // Enable Interrupt pin
546 reg_write(rhport, CPUCTL_ADDR, _tuh_cfg.cpuctl | CPUCTL_IE, false);
547
548 return true;
549}
550
551bool hcd_deinit(uint8_t rhport) {
552 (void) rhport;
553
554 // disable interrupt
555 tuh_max3421_int_api(rhport, false);
556
557 // reset max3421 and power down
558 reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
559 reg_write(rhport, USBCTL_ADDR, USBCTL_PWRDOWN, false);
560
561 #if OSAL_MUTEX_REQUIRED
563 _hcd_data.spi_mutex = NULL;
564 #endif
565
566 return true;
567}
568
569// Enable USB interrupt
570// Not actually enable GPIO interrupt, just set variable to prevent handler to process
571void hcd_int_enable (uint8_t rhport) {
572 tuh_max3421_int_api(rhport, true);
573}
574
575// Disable USB interrupt
576// Not actually disable GPIO interrupt, just set variable to prevent handler to process
577void hcd_int_disable(uint8_t rhport) {
578 tuh_max3421_int_api(rhport, false);
579}
580
581// Get frame number (1ms)
582uint32_t hcd_frame_number(uint8_t rhport) {
583 (void) rhport;
584 return (uint32_t ) _hcd_data.frame_count;
585}
586
587//--------------------------------------------------------------------+
588// Port API
589//--------------------------------------------------------------------+
590
591// Get the current connect status of roothub port
592bool hcd_port_connect_status(uint8_t rhport) {
593 (void) rhport;
594 return (_hcd_data.mode & MODE_SOFKAENAB) ? true : false;
595}
596
597// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
598// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
599void hcd_port_reset(uint8_t rhport) {
600 reg_write(rhport, HCTL_ADDR, HCTL_BUSRST, false);
601}
602
603// Complete bus reset sequence, may be required by some controllers
604void hcd_port_reset_end(uint8_t rhport) {
605 reg_write(rhport, HCTL_ADDR, 0, false);
606}
607
608// Get port link speed
610 (void) rhport;
612}
613
614// HCD closes all opened endpoints belong to this device
615void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
616 (void) rhport;
617 (void) dev_addr;
618}
619
620//--------------------------------------------------------------------+
621// Endpoints API
622//--------------------------------------------------------------------+
623
624// Open an endpoint
625bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc) {
626 (void) rhport;
627
628 uint8_t const ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
629 tusb_dir_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
630
631 max3421_ep_t * ep;
632 if (daddr == 0 && ep_num == 0) {
633 ep = &_hcd_data.ep[0];
634 }else {
635 ep = allocate_ep();
636 TU_ASSERT(ep);
637 ep->daddr = daddr;
638 ep->hxfr_bm.ep_num = (uint8_t) (ep_num & 0x0f);
639 ep->hxfr_bm.is_out = (ep_dir == TUSB_DIR_OUT) ? 1 : 0;
640 ep->hxfr_bm.is_iso = (TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer) ? 1 : 0;
641 }
642
643 ep->packet_size = (uint16_t) (tu_edpt_packet_size(ep_desc) & 0x7ff);
644
645 return true;
646}
647
648/* The microcontroller repeatedly writes the SNDFIFO register R2 to load the FIFO with up to 64 data bytes.
649 * Then the microcontroller writes the SNDBC register, which this does three things:
650 * 1. Tells the MAX3421E SIE (Serial Interface Engine) how many bytes in the FIFO to send.
651 * 2. Connects the SNDFIFO and SNDBC register to the USB logic for USB transmission.
652 * 3. Clears the SNDBAVIRQ interrupt flag. If the second FIFO is available for µC loading, the SNDBAVIRQ immediately re-asserts.
653
654 +-----------+
655 --->| SNDBC-A |
656 / | SNDFIFO-A |
657 / +-----------+
658 +------+ +-------------+ / +----------+
659 | MCU |------>| R2: SNDFIFO |---- << Write R7 Flip >> ---| MAX3241E |
660 |(hcd) | | R7: SNDBC | / | SIE |
661 +------+ +-------------+ / +----------+
662 +-----------+ /
663 | SNDBC-B | /
664 | SNDFIFO-B |<---
665 +-----------+
666 Note: xact_out() is called when starting a new transfer, continue a transfer (isr) or retry a transfer (NAK)
667 For NAK retry, we do not need to write to FIFO or SNDBC register again.
668*/
669static void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
670 // Page 12: Programming BULK-OUT Transfers
671 // TODO: double buffering for ISO transfer
672 if (switch_ep) {
673 peraddr_write(rhport, ep->daddr, in_isr);
674 const uint8_t hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0);
675 reg_write(rhport, HCTL_ADDR, hctl, in_isr);
676 }
677
678 // Only write to sndfifo and sdnbc register if it is not a NAKed retry
680 // skip SNDBAV IRQ check, overwrite sndfifo if needed
681 const uint8_t xact_len = (uint8_t) tu_min16(ep->total_len - ep->xferred_len, ep->packet_size);
682 hwfifo_send(rhport, ep->buf, xact_len, in_isr);
683 }
686
687 hxfr_write(rhport, ep->hxfr, in_isr);
688}
689
690static void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
691 // Page 13: Programming BULK-IN Transfers
692 if (switch_ep) {
693 peraddr_write(rhport, ep->daddr, in_isr);
694
695 uint8_t const hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0);
696 reg_write(rhport, HCTL_ADDR, hctl, in_isr);
697 }
698
699 hxfr_write(rhport, ep->hxfr, in_isr);
700}
701
702static void xact_setup(uint8_t rhport, max3421_ep_t *ep, bool in_isr) {
703 peraddr_write(rhport, ep->daddr, in_isr);
704 hwfifo_setup(rhport, ep->buf, in_isr);
705 hxfr_write(rhport, HXFR_SETUP, in_isr);
706}
707
708static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
709 if (ep->hxfr_bm.ep_num == 0 ) {
710 // setup
711 if (ep->hxfr_bm.is_setup) {
712 xact_setup(rhport, ep, in_isr);
713 return;
714 }
715
716 // status
717 if (ep->buf == NULL || ep->total_len == 0) {
718 const uint8_t hxfr = (uint8_t) (HXFR_HS | (ep->hxfr & HXFR_OUT_NIN));
719 peraddr_write(rhport, ep->daddr, in_isr);
720 hxfr_write(rhport, hxfr, in_isr);
721 return;
722 }
723 }
724
725 if (ep->hxfr_bm.is_out) {
726 xact_out(rhport, ep, switch_ep, in_isr);
727 }else {
728 xact_in(rhport, ep, switch_ep, in_isr);
729 }
730}
731
732// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
733bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
734 uint8_t const ep_num = tu_edpt_number(ep_addr);
735 uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
736 max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
737 TU_VERIFY(ep);
738
739 if (ep_num == 0) {
740 // control transfer can switch direction
741 ep->hxfr_bm.is_out = ep_dir ? 0 : 1;
742 ep->hxfr_bm.is_setup = 0;
743 ep->data_toggle = 1;
744 }
745
746 ep->buf = buffer;
747 ep->total_len = buflen;
748 ep->xferred_len = 0;
749 ep->state = EP_STATE_ATTEMPT_1;
750
751 // carry out transfer if not busy
752 if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
753 xact_generic(rhport, ep, true, false);
754 }
755
756 return true;
757}
758
759bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
760 uint8_t const ep_num = tu_edpt_number(ep_addr);
761 uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
762 max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
763 TU_VERIFY(ep);
764
765 if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
766 hcd_int_disable(rhport);
767 ep->state = EP_STATE_ABORTING;
768 hcd_int_enable(rhport);
769 }
770
771 return true;
772}
773
774// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
775bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]) {
776 (void) rhport;
777
778 max3421_ep_t* ep = find_opened_ep(daddr, 0, 0);
779 TU_ASSERT(ep);
780
781 ep->hxfr_bm.is_out = 1;
782 ep->hxfr_bm.is_setup = 1;
783 ep->buf = (uint8_t*)(uintptr_t) setup_packet;
784 ep->total_len = 8;
785 ep->xferred_len = 0;
786 ep->state = EP_STATE_ATTEMPT_1;
787
788 // carry out transfer if not busy
789 if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
790 xact_setup(rhport, ep, false);
791 }
792
793 return true;
794}
795
796// clear stall, data toggle is also reset to DATA0
797bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
798 (void) rhport;
799 (void) dev_addr;
800 (void) ep_addr;
801
802 return false;
803}
804
805//--------------------------------------------------------------------+
806// Interrupt Handler
807//--------------------------------------------------------------------+
808
809static void handle_connect_irq(uint8_t rhport, bool in_isr) {
810 uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
811 uint8_t const jk = hrsl & (HRSL_JSTATUS | HRSL_KSTATUS);
812
813 uint8_t new_mode = MODE_DPPULLDN | MODE_DMPULLDN | MODE_HOST;
814 TU_LOG2_HEX(jk);
815
816 switch(jk) {
817 case 0x00: // SEO is disconnected
818 case (HRSL_JSTATUS | HRSL_KSTATUS): // SE1 is illegal
819 mode_write(rhport, new_mode, in_isr);
820
821 // port reset anyway, this will help to stable bus signal for next connection
822 reg_write(rhport, HCTL_ADDR, HCTL_BUSRST, in_isr);
824 reg_write(rhport, HCTL_ADDR, 0, in_isr);
825 break;
826
827 default: {
828 // Bus Reset also cause CONDET IRQ, skip if we are already connected and doing bus reset
830 break;
831 }
832
833 // Low speed if (LS = 1 and J-state) or (LS = 0 and K-State)
834 // However, since we are always in full speed mode, we can just check J-state
835 if (jk == HRSL_KSTATUS) {
836 new_mode |= MODE_LOWSPEED;
837 TU_LOG3("Low speed\r\n");
838 }else {
839 TU_LOG3("Full speed\r\n");
840 }
841 new_mode |= MODE_SOFKAENAB;
842 mode_write(rhport, new_mode, in_isr);
843
844 // FIXME multiple MAX3421 rootdevice address is not 1
845 uint8_t const daddr = 1;
846 free_ep(daddr);
847
849 break;
850 }
851 }
852}
853
854static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
855 uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
856 uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
857
858 // save data toggle
859 if (ep_dir) {
860 ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1u : 0u;
861 }else {
862 ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1u : 0u;
863 }
864
865 ep->state = EP_STATE_IDLE;
866 hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, in_isr);
867
868 // Find next pending endpoint
869 max3421_ep_t * next_ep = find_next_pending_ep(ep);
870 if (next_ep) {
871 xact_generic(rhport, next_ep, true, in_isr);
872 }else {
873 // no more pending
874 atomic_flag_clear(&_hcd_data.busy);
875 }
876}
877
878static void handle_xfer_done(uint8_t rhport, bool in_isr) {
879 const uint8_t hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
880 const uint8_t hresult = hrsl & HRSL_RESULT_MASK;
881 const uint8_t ep_num = _hcd_data.hxfr_bm.ep_num;
882 const uint8_t hxfr_type = _hcd_data.hxfr & 0xf0;
883 const uint8_t ep_dir = ((hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN)) ? 0 : 1;
884
885 max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, ep_dir);
886 TU_VERIFY(ep, );
887
888 xfer_result_t xfer_result;
889 switch(hresult) {
890 case HRSL_NAK:
891 if (ep->state == EP_STATE_ABORTING) {
892 ep->state = EP_STATE_IDLE;
893 } else {
894 if (ep_num == 0) {
895 // control endpoint -> retry immediately and return
897 return;
898 }
899 if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
900 ep->state++;
901 }
902 }
903
904 max3421_ep_t * next_ep = find_next_pending_ep(ep);
905 if (ep == next_ep) {
906 // this endpoint is only one pending -> retry immediately
908 } else if (next_ep) {
909 // switch to next pending endpoint
910 xact_generic(rhport, next_ep, true, in_isr);
911 } else {
912 // no more pending in this frame -> clear busy
913 atomic_flag_clear(&_hcd_data.busy);
914 }
915 return;
916
917 case HRSL_BAD_REQ:
918 // occurred when initialized without any pending transfer. Skip for now
919 return;
920
921 case HRSL_SUCCESS:
922 xfer_result = XFER_RESULT_SUCCESS;
923 break;
924
925 case HRSL_STALL:
926 xfer_result = XFER_RESULT_STALLED;
927 break;
928
929 default:
930 TU_LOG3("HRSL: %02X\r\n", hrsl);
931 xfer_result = XFER_RESULT_FAILED;
932 break;
933 }
934
935 if (xfer_result != XFER_RESULT_SUCCESS) {
936 xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
937 return;
938 }
939
940 if (ep_dir) {
941 // IN transfer: fifo data is already received in RCVDAV IRQ
942
943 // mark control handshake as complete
944 if (hxfr_type & HXFR_HS) {
945 ep->state = EP_STATE_COMPLETE;
946 }
947
948 // short packet or all bytes transferred
949 if (ep->state == EP_STATE_COMPLETE) {
950 xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
951 }else {
952 hxfr_write(rhport, _hcd_data.hxfr, in_isr); // more to transfer
953 }
954 } else {
955 // SETUP or OUT transfer
956
957 // clear sndfifo owner since data is sent
960
961 uint8_t xact_len;
962
963 if (hxfr_type & HXFR_SETUP) {
964 xact_len = 8;
965 } else if (hxfr_type & HXFR_HS) {
966 xact_len = 0;
967 } else {
968 xact_len = _hcd_data.sndbc;
969 }
970
971 ep->xferred_len += xact_len;
972 ep->buf += xact_len;
973
974 if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
975 xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
976 } else {
977 xact_out(rhport, ep, false, in_isr); // more to transfer
978 }
979 }
980}
981
982#if CFG_TUSB_DEBUG >= 3
983void print_hirq(uint8_t hirq) {
984 TU_LOG3_HEX(hirq);
985
986 if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN");
987 if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME");
988 if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET");
989 if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN");
990 if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV");
991 if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV");
992 if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU");
993 if (hirq & HIRQ_BUSEVENT_IRQ) TU_LOG3(" BUSEVENT");
994
995 TU_LOG3("\r\n");
996}
997#else
998 #define print_hirq(hirq)
999#endif
1000
1001// Interrupt handler
1002void hcd_int_handler(uint8_t rhport, bool in_isr) {
1003 uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
1004 if (!hirq) return;
1005// print_hirq(hirq);
1006
1007 if (hirq & HIRQ_FRAME_IRQ) {
1009
1010 // reset all endpoints nak counter, retry with 1st pending ep.
1011 max3421_ep_t* ep_retry = NULL;
1012 for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
1013 max3421_ep_t* ep = &_hcd_data.ep[i];
1014 if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) {
1015 ep->state = EP_STATE_ATTEMPT_1;
1016
1017 if (ep_retry == NULL) {
1018 ep_retry = ep;
1019 }
1020 }
1021 }
1022
1023 // start usb transfer if not busy
1024 if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
1025 xact_generic(rhport, ep_retry, true, in_isr);
1026 }
1027 }
1028
1029 if (hirq & HIRQ_CONDET_IRQ) {
1030 handle_connect_irq(rhport, in_isr);
1031 }
1032
1033 // queue more transfer in handle_xfer_done() can cause hirq to be set again while external IRQ may not catch and/or
1034 // not call this handler again. So we need to loop until all IRQ are cleared
1035 while (hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ)) {
1036 if (hirq & HIRQ_RCVDAV_IRQ) {
1037 const uint8_t ep_num = _hcd_data.hxfr_bm.ep_num;
1038 max3421_ep_t* ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
1039 uint8_t xact_len = 0;
1040
1041 // RCVDAV_IRQ can trigger 2 times (dual buffered)
1042 while (hirq & HIRQ_RCVDAV_IRQ) {
1043 const uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, in_isr);
1044 xact_len = (uint8_t) tu_min16(rcvbc, ep->total_len - ep->xferred_len);
1045 if (xact_len) {
1046 hwfifo_receive(rhport, ep->buf, xact_len, in_isr);
1047 ep->buf += xact_len;
1048 ep->xferred_len += xact_len;
1049 }
1050
1051 // ack RCVDVAV IRQ
1053 hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
1054 }
1055
1056 if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
1057 ep->state = EP_STATE_COMPLETE;
1058 }
1059 }
1060
1061 if (hirq & HIRQ_HXFRDN_IRQ) {
1063 handle_xfer_done(rhport, in_isr);
1064 }
1065
1066 hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
1067 }
1068
1069 // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
1070 hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ;
1071 if (hirq) {
1072 hirq_write(rhport, hirq, in_isr);
1073 }
1074}
1075
1076#endif
static struct @612 data
static bool in_isr
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
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
@ CPUCTL_PULSEWID1
Definition: hcd_max3421.c:87
@ CPUCTL_IE
Definition: hcd_max3421.c:85
@ CPUCTL_PULSEWID0
Definition: hcd_max3421.c:86
struct TU_ATTR_PACKED hxfr_bm_t
@ HCTL_SIGRSM
Definition: hcd_max3421.c:124
@ HCTL_FRMRST
Definition: hcd_max3421.c:122
@ HCTL_SAMPLEBUS
Definition: hcd_max3421.c:123
@ HCTL_SNDTOG0
Definition: hcd_max3421.c:127
@ HCTL_RCVTOG0
Definition: hcd_max3421.c:125
@ HCTL_BUSRST
Definition: hcd_max3421.c:121
@ HCTL_RCVTOG1
Definition: hcd_max3421.c:126
@ HCTL_SNDTOG1
Definition: hcd_max3421.c:128
static void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr)
Definition: hcd_max3421.c:690
@ EP_STATE_ATTEMPT_MAX
Definition: hcd_max3421.c:179
@ EP_STATE_IDLE
Definition: hcd_max3421.c:175
@ EP_STATE_COMPLETE
Definition: hcd_max3421.c:176
@ EP_STATE_ATTEMPT_1
Definition: hcd_max3421.c:178
@ EP_STATE_ABORTING
Definition: hcd_max3421.c:177
@ USBCTL_PWRDOWN
Definition: hcd_max3421.c:80
@ USBCTL_CHIPRES
Definition: hcd_max3421.c:81
static TU_ATTR_ALWAYS_INLINE void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:358
@ GPINPOL_ADDR
Definition: hcd_max3421.c:63
@ USBIEN_ADDR
Definition: hcd_max3421.c:53
@ PERADDR_ADDR
Definition: hcd_max3421.c:67
@ HIEN_ADDR
Definition: hcd_max3421.c:65
@ IOPINS1_ADDR
Definition: hcd_max3421.c:59
@ HXFR_ADDR
Definition: hcd_max3421.c:69
@ SNDFIFO_ADDR
Definition: hcd_max3421.c:48
@ IOPINS2_ADDR
Definition: hcd_max3421.c:60
@ PINCTL_ADDR
Definition: hcd_max3421.c:56
@ HCTL_ADDR
Definition: hcd_max3421.c:68
@ CPUCTL_ADDR
Definition: hcd_max3421.c:55
@ MODE_ADDR
Definition: hcd_max3421.c:66
@ SUDFIFO_ADDR
Definition: hcd_max3421.c:49
@ USBCTL_ADDR
Definition: hcd_max3421.c:54
@ HIRQ_ADDR
Definition: hcd_max3421.c:64
@ SNDBC_ADDR
Definition: hcd_max3421.c:51
@ GPINIRQ_ADDR
Definition: hcd_max3421.c:61
@ RCVVFIFO_ADDR
Definition: hcd_max3421.c:47
@ GPINIEN_ADDR
Definition: hcd_max3421.c:62
@ RCVBC_ADDR
Definition: hcd_max3421.c:50
@ USBIRQ_ADDR
Definition: hcd_max3421.c:52
@ REVISION_ADDR
Definition: hcd_max3421.c:57
@ HRSL_ADDR
Definition: hcd_max3421.c:70
static TU_ATTR_ALWAYS_INLINE void hien_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:341
void tuh_max3421_spi_cs_api(uint8_t rhport, bool active)
static TU_ATTR_ALWAYS_INLINE void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:351
void hcd_int_disable(uint8_t rhport)
Definition: hcd_max3421.c:577
@ MODE_DMPULLDN
Definition: hcd_max3421.c:116
@ MODE_SOFKAENAB
Definition: hcd_max3421.c:113
@ MODE_DPPULLDN
Definition: hcd_max3421.c:117
@ MODE_LOWSPEED
Definition: hcd_max3421.c:111
@ MODE_HUBPRE
Definition: hcd_max3421.c:112
@ MODE_SEPIRQ
Definition: hcd_max3421.c:114
@ MODE_HOST
Definition: hcd_max3421.c:110
@ MODE_DELAYISO
Definition: hcd_max3421.c:115
@ USBIRQ_VBUS_IRQ
Definition: hcd_max3421.c:76
@ USBIRQ_NOVBUS_IRQ
Definition: hcd_max3421.c:75
@ USBIRQ_OSCOK_IRQ
Definition: hcd_max3421.c:74
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param)
Definition: hcd_max3421.c:486
@ HIRQ_FRAME_IRQ
Definition: hcd_max3421.c:105
@ HIRQ_BUSEVENT_IRQ
Definition: hcd_max3421.c:99
@ HIRQ_RWU_IRQ
Definition: hcd_max3421.c:100
@ HIRQ_SUSDN_IRQ
Definition: hcd_max3421.c:103
@ HIRQ_SNDBAV_IRQ
Definition: hcd_max3421.c:102
@ HIRQ_CONDET_IRQ
Definition: hcd_max3421.c:104
@ HIRQ_RCVDAV_IRQ
Definition: hcd_max3421.c:101
@ HIRQ_HXFRDN_IRQ
Definition: hcd_max3421.c:106
void tuh_max3421_int_api(uint8_t rhport, bool enabled)
@ HRSL_RCVTOGRD
Definition: hcd_max3421.c:141
@ HRSL_JSTATUS
Definition: hcd_max3421.c:144
@ HRSL_KSTATUS
Definition: hcd_max3421.c:143
@ HRSL_RESULT_MASK
Definition: hcd_max3421.c:140
@ HRSL_SNDTOGRD
Definition: hcd_max3421.c:142
@ DEFAULT_HIEN
Definition: hcd_max3421.c:167
static max3421_data_t _hcd_data
Definition: hcd_max3421.c:246
bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:318
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr)
Definition: hcd_max3421.c:759
static TU_ATTR_ALWAYS_INLINE void hwfifo_send(uint8_t rhport, const uint8_t *buffer, uint8_t len, bool in_isr)
Definition: hcd_max3421.c:385
static max3421_ep_t * find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir)
Definition: hcd_max3421.c:413
bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const *ep_desc)
Definition: hcd_max3421.c:625
static void max3421_spi_lock(uint8_t rhport, bool in_isr)
Definition: hcd_max3421.c:284
static TU_ATTR_ALWAYS_INLINE void hwfifo_setup(uint8_t rhport, const uint8_t *buffer, bool in_isr)
Definition: hcd_max3421.c:392
static void max3421_spi_unlock(uint8_t rhport, bool in_isr)
Definition: hcd_max3421.c:295
static TU_ATTR_ALWAYS_INLINE max3421_ep_t * find_opened_ep(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir)
Definition: hcd_max3421.c:431
static TU_ATTR_ALWAYS_INLINE bool is_ep_pending(max3421_ep_t const *ep)
Definition: hcd_max3421.c:450
bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
Definition: hcd_max3421.c:797
bool hcd_deinit(uint8_t rhport)
Definition: hcd_max3421.c:551
void hcd_int_enable(uint8_t rhport)
Definition: hcd_max3421.c:571
bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, uint8_t *rx_buf, size_t xfer_bytes)
@ PINCTL_GPXA
Definition: hcd_max3421.c:91
@ PINCTL_POSINT
Definition: hcd_max3421.c:93
@ PINCTL_FDUPSPI
Definition: hcd_max3421.c:95
@ PINCTL_GPXB
Definition: hcd_max3421.c:92
@ PINCTL_INTLEVEL
Definition: hcd_max3421.c:94
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
Definition: hcd_max3421.c:615
static void handle_xfer_done(uint8_t rhport, bool in_isr)
Definition: hcd_max3421.c:878
static void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr)
Definition: hcd_max3421.c:669
void hcd_port_reset_end(uint8_t rhport)
Definition: hcd_max3421.c:604
static void hwfifo_write(uint8_t rhport, uint8_t reg, const uint8_t *buffer, uint8_t len, bool in_isr)
Definition: hcd_max3421.c:371
@ HXFR_HS
Definition: hcd_max3421.c:136
@ HXFR_SETUP
Definition: hcd_max3421.c:133
@ HXFR_OUT_NIN
Definition: hcd_max3421.c:134
@ HXFR_EPNUM_MASK
Definition: hcd_max3421.c:132
@ HXFR_ISO
Definition: hcd_max3421.c:135
bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8])
Definition: hcd_max3421.c:775
static TU_ATTR_ALWAYS_INLINE void hirq_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:335
static void xact_setup(uint8_t rhport, max3421_ep_t *ep, bool in_isr)
Definition: hcd_max3421.c:702
void hcd_port_reset(uint8_t rhport)
Definition: hcd_max3421.c:599
bool hcd_port_connect_status(uint8_t rhport)
Definition: hcd_max3421.c:592
@ HRSL_UNDEF
Definition: hcd_max3421.c:151
@ HRSL_PKT_ERR
Definition: hcd_max3421.c:158
@ HRSL_BUSY
Definition: hcd_max3421.c:149
@ HRSL_K_ERR
Definition: hcd_max3421.c:160
@ HRSL_STALL
Definition: hcd_max3421.c:153
@ HRSL_CRC_ERR
Definition: hcd_max3421.c:159
@ HRSL_TIMEOUT
Definition: hcd_max3421.c:162
@ HRSL_WRONG_PID
Definition: hcd_max3421.c:155
@ HRSL_BAD_BYTECOUNT
Definition: hcd_max3421.c:156
@ HRSL_SUCCESS
Definition: hcd_max3421.c:148
@ HRSL_TOG_ERR
Definition: hcd_max3421.c:154
@ HRSL_BAD_REQ
Definition: hcd_max3421.c:150
@ HRSL_J_ERR
Definition: hcd_max3421.c:161
@ HRSL_BABBLE
Definition: hcd_max3421.c:163
@ HRSL_PID_ERR
Definition: hcd_max3421.c:157
@ HRSL_NAK
Definition: hcd_max3421.c:152
static TU_ATTR_ALWAYS_INLINE void mode_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:346
TU_VERIFY_STATIC(sizeof(hxfr_bm_t)==1, "size is not correct")
uint32_t hcd_frame_number(uint8_t rhport)
Definition: hcd_max3421.c:582
uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr)
Definition: hcd_max3421.c:306
@ CMDBYTE_WRITE
Definition: hcd_max3421.c:43
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
Definition: hcd_max3421.c:609
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: hcd_max3421.c:733
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr)
Definition: hcd_max3421.c:854
void hcd_int_handler(uint8_t rhport, bool in_isr)
Definition: hcd_max3421.c:1002
static void free_ep(uint8_t daddr)
Definition: hcd_max3421.c:440
void print_hirq(uint8_t hirq)
Definition: hcd_max3421.c:983
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
Definition: hcd_max3421.c:497
static max3421_ep_t * find_next_pending_ep(max3421_ep_t *cur_ep)
Definition: hcd_max3421.c:459
@ MAX_NAK_DEFAULT
Definition: hcd_max3421.c:171
static TU_ATTR_ALWAYS_INLINE max3421_ep_t * allocate_ep(void)
Definition: hcd_max3421.c:427
static TU_ATTR_ALWAYS_INLINE void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr)
Definition: hcd_max3421.c:363
static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr)
Definition: hcd_max3421.c:708
static tuh_configure_max3421_t _tuh_cfg
Definition: hcd_max3421.c:249
static void handle_connect_irq(uint8_t rhport, bool in_isr)
Definition: hcd_max3421.c:809
static void hwfifo_receive(uint8_t rhport, uint8_t *buffer, uint16_t len, bool in_isr)
Definition: hcd_max3421.c:396
uint8_t const * buffer
Definition: midi_device.h:100
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_delete(osal_mutex_t mutex_hdl)
SemaphoreHandle_t osal_mutex_t
Definition: osal_freertos.h:54
static TU_ATTR_ALWAYS_INLINE osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef)
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t ep_num
Definition: hcd_max3421.c:187
uint8_t is_iso
Definition: hcd_max3421.c:190
uint8_t is_setup
Definition: hcd_max3421.c:188
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
volatile uint16_t
Definition: hcd_rusb2.c:58
uint8_t bEndpointAddress
Definition: video.h:306
uint8_t is_out
Definition: hcd_max3421.c:189
max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]
Definition: hcd_max3421.c:243
hxfr_bm_t hxfr_bm
Definition: hcd_max3421.c:226
atomic_flag busy
Definition: hcd_max3421.c:236
OSAL_MUTEX_DEF(spi_mutexdef)
osal_mutex_t spi_mutex
Definition: hcd_max3421.c:240
volatile uint16_t frame_count
Definition: hcd_max3421.c:217
struct max3421_data_t::@148 sndfifo_owner
uint8_t peraddr
Definition: hcd_max3421.c:224
uint8_t daddr
Definition: hcd_max3421.c:196
uint16_t xferred_len
Definition: hcd_max3421.c:210
uint16_t total_len
Definition: hcd_max3421.c:209
uint8_t hxfr
Definition: hcd_max3421.c:200
uint8_t * buf
Definition: hcd_max3421.c:211
hxfr_bm_t hxfr_bm
Definition: hcd_max3421.c:199
static TU_ATTR_ALWAYS_INLINE uint16_t tu_min16(uint16_t x, uint16_t y)
Definition: tusb_common.h:155
static TU_ATTR_ALWAYS_INLINE uint8_t tu_min8(uint8_t x, uint8_t y)
Definition: tusb_common.h:154
tusb_dir_t
Definition: tusb_types.h:65
@ 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_FULL
Definition: tusb_types.h:50
@ TUSB_SPEED_LOW
Definition: tusb_types.h:51
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
xfer_result_t
Definition: tusb_types.h:236
@ 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
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
tuh_configure_max3421_t max3421
Definition: usbh.h:92
uint8_t daddr
Definition: usbh.c:264
@ TUH_CFGID_MAX3421
Definition: usbh.h:80