ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ux_dcd_ra8_usb_irq.c
Go to the documentation of this file.
1
21
22#define UX_SOURCE_CODE
23
24#include <stdint.h>
25#include <string.h>
26
27#include "ra8_check.h"
28#include "ra8_elc_regs.h"
29#include "ra8_isr.h"
30#include "ra8_log.h"
31#include "ra8_usb_regs.h"
32#include "tx_api.h"
33#include "ux_api.h"
34#include "ux_dcd_ra8_usb.h"
36#include "ux_device_stack.h"
37#include "ux_system.h"
38#include "ux_utility.h"
39
51volatile uint32_t g_ctrt_irq_count = 0U;
52
64volatile uint16_t g_intsts0_last_dispatch = 0U;
65
73
86volatile uint32_t g_intsts0_valid_count = 0U;
87
100volatile uint32_t g_intsts0_ctrt_count = 0U;
101
115volatile uint16_t g_intsts0_snapshot[(uint32_t)k_ra8_usb_dcd_intsts0_hist_n] = {};
116
125volatile uint32_t g_intsts0_snapshot_count = 0U;
126
138volatile uint8_t g_ctsq_history[(uint32_t)k_ra8_usb_dcd_intsts0_hist_n] = {};
139
154volatile uint16_t g_intsts0_observed_or = 0U;
155
168volatile uint8_t g_dvsq_history[(uint32_t)k_ra8_usb_dcd_intsts0_hist_n] = {};
169
170/* Auto-echo support: lets the bridge mirror data from one OUT pipe to one
171 * IN pipe directly inside the ISR, without involving USBX worker threads.
172 * Diagnostic counters expose what the path is doing; auto_echo_buf is
173 * static and reused across BRDY events. Set ::g_dcd_auto_echo_enable to 1
174 * via ::ux_dcd_ra8_usb_auto_echo_enable() to opt in. */
178
179volatile uint32_t g_dcd_auto_echo_drain_ok = 0U;
180
181volatile uint32_t g_dcd_auto_echo_drain_skip = 0U;
182
183volatile uint32_t g_dcd_auto_echo_tx_ok = 0U;
184
185volatile uint32_t g_dcd_auto_echo_tx_err = 0U;
186
187volatile uint16_t g_dcd_auto_echo_last_len = 0U;
188
190
211RA8_INTERNAL static void internal_irq_record_snapshot(uint16_t intsts0)
212{
213 const uint16_t ctrt_bit = (uint16_t)(1U << (uint8_t)k_ra8_int0_bit_ctrt);
214 const uint16_t dvst_bit = (uint16_t)(1U << (uint8_t)k_ra8_int0_bit_dvst);
215 const uint16_t valid_bit = (uint16_t)k_ra8_intsts0_mask_valid;
216
217 if ((intsts0 & valid_bit) != 0U) {
219 /* VALID = "USB Request Reception Flag" (HUM Ch 37.2.18 p 2081);
220 * fold into the canonical SETUP-arrival counter. */
222 }
223 if ((intsts0 & ctrt_bit) != 0U) {
225 }
226 const uint16_t interesting_mask = (uint16_t)(ctrt_bit | valid_bit | dvst_bit);
227 if ((intsts0 & interesting_mask) != 0U) {
228 const uint8_t slot =
230 g_intsts0_snapshot[slot] = intsts0;
231 g_ctsq_history[slot] = (uint8_t)(intsts0 & (uint16_t)k_ra8_intsts0_mask_ctsq);
232 /* DVSQ field is bits 6:4 (HUM Ch 36.2.16 p 1986). */
233 g_dvsq_history[slot] =
234 (uint8_t)((intsts0 & (uint16_t)k_ra8_intsts0_mask_dvsq) >> (uint8_t)k_ra8_int0_dvsq_shift);
236 }
237}
238
272{
274 g_dcd.pipes[i].dir_in == 0U) {
275 uint16_t len = (uint16_t)k_ux_dcd_ra8_usb_auto_echo_max;
276 const ra8_err_t qo =
277 ra8_usb_queue_out(g_dcd.speed, i, s_dcd_auto_echo_buf, &len, /* rearm */ true);
278 if (qo == k_ra8_ok && len > 0U) {
281 const uint16_t out_mps = g_dcd.pipes[i].max_pkt;
283 k_ra8_ok) {
285 /* If the data packet was exactly MPS, follow with a ZLP so the
286 * host URB completes; otherwise the host waits for a short
287 * packet that never arrives. */
288 if (out_mps != 0U && (len % out_mps) == 0U) {
290 }
291 } else {
293 }
294 } else {
296 (void)ra8_usb_rearm_out_pipe(g_dcd.speed, i);
297 }
298 }
299}
300
324RA8_INTERNAL static bool internal_irq_stage_next_in(UX_SLAVE_TRANSFER* tr, uint8_t i)
325{
326 const uint16_t total = (uint16_t)tr->ux_slave_transfer_request_requested_length;
327 const uint16_t sent = (uint16_t)tr->ux_slave_transfer_request_actual_length;
328 const uint16_t mps = g_dcd.pipes[i].max_pkt;
329 const uint16_t rem = (uint16_t)(total - sent);
330 const uint16_t chunk = (rem > mps) ? mps : rem;
331 if (ra8_usb_queue_in(g_dcd.speed, i, &tr->ux_slave_transfer_request_data_pointer[sent], chunk) !=
332 k_ra8_ok) {
333 g_diag.in_stage_fail++;
334 return false;
335 }
336 tr->ux_slave_transfer_request_actual_length = (ULONG)((uint32_t)sent + (uint32_t)chunk);
337 return true;
338}
339
366RA8_INTERNAL static bool
367internal_irq_recover_in_nak(UX_SLAVE_TRANSFER* tr, uint8_t i, volatile r_usb_regs_t* reg)
368{
369 const uint16_t pipe_bit = (uint16_t)(1U << i);
370 const uint16_t total = (uint16_t)tr->ux_slave_transfer_request_requested_length;
371 const uint16_t sent = (uint16_t)tr->ux_slave_transfer_request_actual_length;
372 const uint16_t ctr = reg->PIPECTR[i - 1U];
373 if ((ctr & (uint16_t)k_ra8_pipectr_pid_mask) != (uint16_t)k_ra8_pid_nak) {
374 return false;
375 }
376 if ((ctr & (uint16_t)k_ra8_pipectr_inbufm) != 0U) {
377 g_diag.in_rearm_nak++;
378 (void)ra8_usb_rearm_out_pipe(g_dcd.speed, i);
379 return true; /* BEMP follows once the re-armed bank drains */
380 }
381 if ((reg->BEMPSTS & pipe_bit) == 0U) {
382 if (sent < total) {
383 g_diag.in_restage_nak++;
384 (void)internal_irq_stage_next_in(tr, i);
385 return true;
386 }
387 }
388 return false;
389}
390
413RA8_INTERNAL static void internal_irq_complete_in(UX_SLAVE_TRANSFER* tr, uint8_t i)
414{
415 volatile r_usb_regs_t* reg = (g_dcd.speed == k_ra8_usb_speed_hs) ? ra8_usb_hs() : ra8_usb_fs();
416 if (reg == nullptr) { /* GCOVR_EXCL_BR_LINE -- speed always maps */
417 return; /* GCOVR_EXCL_LINE -- valid speed proves reg */
418 }
419 const uint16_t pipe_bit = (uint16_t)(1U << i);
420 const uint16_t total = (uint16_t)tr->ux_slave_transfer_request_requested_length;
421 const uint16_t sent = (uint16_t)tr->ux_slave_transfer_request_actual_length;
422
423 if (internal_irq_recover_in_nak(tr, i, reg)) {
424 return;
425 }
426
427 /* Wait for BEMPSTS: it confirms the host has drained the bank just
428 * transmitted. Acting before that would let the next BOT stage (or
429 * the next data packet) be pushed over data the host has not read --
430 * a race that corrupts the transport and surfaces host-side as
431 * DID_ERROR. If BEMP has not fired, leave the transfer stashed and
432 * retry on the next IRQ walk. HUM Ch 36.2.15 BEMPSTS (W0C). */
433 if ((reg->BEMPSTS & pipe_bit) == 0U) {
434 return;
435 }
436
437 /* Multi-packet streaming. ra8_usb_queue_in moves at most one MPS
438 * bank, so a transfer longer than MPS is sent one bank per host
439 * drain: push the next bank until the whole requested length is
440 * out, only then post completion. ``actual_length`` is the running
441 * queued-byte count seeded by internal_submit_pipe. The BEMPSTS ack
442 * is transactional: it lands only after the next chunk is staged, so
443 * a staging failure keeps the event pending and retries on the next
444 * walk instead of stranding the transfer. */
445 if (sent < total) {
446 if (internal_irq_stage_next_in(tr, i)) {
447 reg->BEMPSTS = (uint16_t)(~pipe_bit);
448 }
449 return;
450 }
451 reg->BEMPSTS = (uint16_t)(~pipe_bit);
452
453 /* Multi-packet trailing ZLP: stage it only when USBX's force_zlp
454 * flag asks for one (host expects more than the MPS-aligned
455 * payload), and complete the transfer on the ZLP's own BEMP. BOT
456 * data phases never set the flag (the class sends exactly the
457 * host-requested length), so the CSW always follows the data. */
458 if (tr->ux_slave_transfer_request_force_zlp == (ULONG)UX_TRUE) {
459 tr->ux_slave_transfer_request_force_zlp = (ULONG)UX_FALSE;
460 (void)ra8_usb_queue_in(g_dcd.speed, i, tr->ux_slave_transfer_request_data_pointer, 0U);
461 return;
462 }
464 tr->ux_slave_transfer_request_completion_code = UX_SUCCESS;
465 g_dcd.pipes[i].xfer = nullptr;
466#ifndef UX_DEVICE_STANDALONE
467 (void)tx_semaphore_put(&tr->ux_slave_transfer_request_semaphore);
468#endif
469}
470
493RA8_INTERNAL static void internal_irq_finish_out(UX_SLAVE_TRANSFER* tr, uint8_t i, uint16_t now)
494{
495 (void)ra8_usb_park_out_pipe(g_dcd.speed, i);
496 uint8_t trace_op = (uint8_t)k_dcd_trace_no_code;
497 uint16_t trace_len = now;
498 if (now == (uint16_t)k_dcd_trace_cbw_len) {
499 const uint8_t* cdb = &tr->ux_slave_transfer_request_data_pointer[k_dcd_trace_cbw_op_off];
500 trace_op = cdb[0];
501 /* For CBWs, carry CDB bytes 1..2 instead of the length so VPD
502 * INQUIRY requests (EVPD flag + page code) are visible. For
503 * READ(10)/WRITE(10) carry the low 16 LBA bits instead. */
504 uint8_t hi = cdb[1];
505 uint8_t lo = cdb[2];
506 if (cdb[0] == (uint8_t)k_dcd_trace_op_read10) {
507 hi = cdb[k_dcd_trace_cdb_lba_hi];
508 lo = cdb[k_dcd_trace_cdb_lba_lo];
509 }
510 if (cdb[0] == (uint8_t)k_dcd_trace_op_write10) {
511 hi = cdb[k_dcd_trace_cdb_lba_hi];
512 lo = cdb[k_dcd_trace_cdb_lba_lo];
513 }
514 trace_len = (uint16_t)(((uint16_t)hi << (uint16_t)k_dcd_trace_byte_shift) | (uint16_t)lo);
515 }
516 priv_trace_event((uint8_t)k_dcd_trace_kind_out, trace_op, trace_len);
517 tr->ux_slave_transfer_request_completion_code = UX_SUCCESS;
518 g_dcd.pipes[i].xfer = nullptr;
519#ifndef UX_DEVICE_STANDALONE
520 (void)tx_semaphore_put(&tr->ux_slave_transfer_request_semaphore);
521#endif
522}
523
543RA8_INTERNAL static void internal_irq_complete_out(UX_SLAVE_TRANSFER* tr, uint8_t i)
544{
545 /* Multi-packet streaming. ra8_usb_queue_out drains at most one MPS
546 * bank, so an OUT transfer longer than MPS (e.g. the 512-byte SCSI
547 * WRITE data phase) is received one bank per host packet:
548 * accumulate into the caller buffer at the running offset, re-arm
549 * the pipe for the next bank, and complete only once the whole
550 * requested length is in -- or the host ends early with a short
551 * (< MPS) packet, which terminates the OUT data phase (this is also
552 * how a 31-byte CBW into a 64-byte request completes). */
553 const uint16_t total = (uint16_t)tr->ux_slave_transfer_request_requested_length;
554 const uint16_t mps = g_dcd.pipes[i].max_pkt;
555 /* Drain every OUT bank the controller currently holds (DBLB presents
556 * up to two back-to-back) before re-arming, so a single BRDY services
557 * as many banks as are ready. One MPS bank per ra8_usb_queue_out call;
558 * stop on completion, a short packet, or when no bank is ready. */
559 for (uint32_t pass = 0U; pass < (uint32_t)k_dcd_out_drain_max; pass++) {
560 const uint16_t got = (uint16_t)tr->ux_slave_transfer_request_actual_length;
561 uint16_t want = (uint16_t)(total - got);
562 if (want > mps) {
563 want = mps;
564 }
565 uint16_t len = want;
566 const ra8_err_t qo_err = ra8_usb_queue_out(g_dcd.speed,
567 i,
568 &tr->ux_slave_transfer_request_data_pointer[got],
569 &len,
570 /* rearm */ false);
571 if (qo_err != k_ra8_ok) {
572 if (i == 2U) {
573 g_diag.irq_walk_pipe2_no_data++;
574 }
575 break;
576 }
577 if (i == 2U) {
578 g_diag.irq_walk_pipe2_complete++;
579 }
580 const uint16_t now = (uint16_t)(got + len);
581 tr->ux_slave_transfer_request_actual_length = (ULONG)now;
582 if (now >= total) {
583 internal_irq_finish_out(tr, i, now);
584 return;
585 }
586 if (len < mps) {
587 internal_irq_finish_out(tr, i, now); /* short packet ends the phase */
588 return;
589 }
590 }
591 /* More packets expected -- re-arm for the next host OUT token(s). */
592 (void)ra8_usb_rearm_out_pipe(g_dcd.speed, i);
593}
594
620{
621 if (g_dcd.pipes[i].dir_in != 0U) {
622 return; /* IN pipe -- the host does not write to it */
623 }
624 if (g_dcd.pipes[i].ep_addr == 0U) {
625 return; /* pipe not configured by the class layer */
626 }
627 if (g_dcd_auto_echo_enable != 0U) {
628 if (i == g_dcd_auto_echo_out_pipe) {
629 return; /* CDC auto-echo drains this pipe itself */
630 }
631 }
632 if (g_orphan_len != 0U) {
633 return; /* one-deep holding buffer already occupied */
634 }
635 uint16_t len = (uint16_t)k_ra8_usb_orphan_bytes;
636 if (ra8_usb_queue_out(g_dcd.speed, i, g_orphan_buf, &len, /* rearm */ false) == k_ra8_ok) {
637 if (len != 0U) {
638 g_orphan_pipe = i;
639 g_orphan_len = len;
641 /* queue_out W0C-cleared BRDYSTS and re-armed PID=BUF; pull it
642 * back to NAK so a second packet cannot land before the next
643 * transfer is submitted. A full-speed bulk packet takes ~45 us
644 * on the wire -- far longer than this queue_out -> park gap --
645 * so the controller cannot accept one in between. */
646 (void)ra8_usb_park_out_pipe(g_dcd.speed, i);
647 }
648 }
649}
650
674{
675 g_diag.irq_walk_total++;
676 UX_SLAVE_TRANSFER* tr = g_dcd.pipes[i].xfer;
677 if (tr == nullptr) {
680 return;
681 }
682 if (i == 2U) {
683 g_diag.irq_walk_pipe2_seen++;
684 }
685 if (g_dcd.pipes[i].dir_in != 0U) {
687 return;
688 }
690}
691
709void ux_dcd_ra8_usb_irq(ra8_usb_speed_t speed, uint16_t intsts0)
710{
712 return;
713 }
714
715 g_intsts0_last_dispatch = intsts0;
716 g_intsts0_observed_or = (uint16_t)(g_intsts0_observed_or | intsts0);
717
719
720 const uint16_t ctrt_bit = (uint16_t)(1U << (uint8_t)k_ra8_int0_bit_ctrt);
721 const uint16_t dvst_bit = (uint16_t)(1U << (uint8_t)k_ra8_int0_bit_dvst);
722 const uint16_t valid_bit = (uint16_t)k_ra8_intsts0_mask_valid;
723 const bool have_ctrt = (intsts0 & ctrt_bit) != 0U;
724 const bool have_valid = (intsts0 & valid_bit) != 0U;
725 const bool have_dvst = (intsts0 & dvst_bit) != 0U;
726
727 /* DVST FIRST -- bus reset / suspend / resume must be processed
728 * BEFORE the SETUP drain. internal_handle_dvst calls
729 * ra8_usb_device_busreset_rearm on Default-state which RESETS
730 * DCPCFG/DCPMAXP/DCPCTR. Draining first would have the response
731 * wiped, leaving the chip NAK'ing host IN tokens. */
732 if (have_dvst) {
733 priv_irq_dvst_prelude(speed, intsts0);
734 }
735
736 /* Deferred control-OUT data stage (e.g. DFU_DNLOAD) BEFORE the CTRT block:
737 * if the host's OUT data has landed in the DCP bank, drain it and run
738 * chapter-9 now so the payload is consumed before the CTSQ=wrss edge in
739 * priv_handle_ctrt drives the status-stage CCPL. */
741
742 /* SETUP / chapter-9 path runs AFTER DVST so the busreset_rearm
743 * has already restored DCP defaults. */
744 if (have_ctrt) {
746 priv_handle_ctrt(speed, intsts0);
747 }
748 /* VALID-without-CTRT fallback (HUM Ch 36.2.14 p 1985): the polled
749 * worker can race the controller between the VALID and CTRT edges;
750 * ra8_usb_dispatch preserves VALID on W0C ack so we can still drain. */
751 else if (have_valid) {
752 priv_handle_ctrt(speed, intsts0);
753 }
754
755 /* Walk every pipe with a queued OUT transfer. ra8_usb_queue_out
756 * returns k_ra8_err_no_data if BRDY hasn't fired for that pipe;
757 * we just retry on the next IRQ in that case. */
758 for (uint8_t i = 1U; i < (uint8_t)k_ux_dcd_ra8_usb_max_pipes; i++) {
760 }
761}
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
Event Link Controller (ELC) register layout for the Renesas RA8D2.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
NVIC + ICU IELSR allocator.
Lightweight Logging Interface for ra8-firmware.
ra8_err_t ra8_usb_park_out_pipe(ra8_usb_speed_t speed, uint8_t pipe_num)
Park an OUT pipe at PID=NAK so an idle pipe stays quiescent.
ra8_err_t ra8_usb_queue_out(ra8_usb_speed_t speed, uint8_t pipe_num, uint8_t *out_buf, uint16_t *inout_len, bool rearm)
Drain an OUT transfer (host -> device) from pipe_num.
ra8_err_t ra8_usb_rearm_out_pipe(ra8_usb_speed_t speed, uint8_t pipe_num)
Re-arm an OUT pipe that the controller has parked at PID=NAK.
ra8_usb_speed_t
Selects which controller instance the call targets.
@ k_ra8_usb_speed_hs
High-Speed controller (USBHS @ 0x40351000).
ra8_err_t ra8_usb_queue_in(ra8_usb_speed_t speed, uint8_t pipe_num, const uint8_t *data, uint16_t len)
Queue an IN transfer (device -> host) on pipe_num.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_pipectr_inbufm
IN buffer monitor.
@ k_ra8_pipectr_pid_mask
PID[1:0] response field.
@ k_ra8_pid_nak
NAK response.
static volatile r_usb_regs_t * ra8_usb_hs(void)
Get pointer to the USB HS controller register block.
@ k_ra8_intsts0_mask_dvsq
Device state.
@ k_ra8_intsts0_mask_valid
SETUP packet detect flag.
@ k_ra8_intsts0_mask_ctsq
Control transfer stage.
@ k_ra8_int0_bit_dvst
Device-state transition.
@ k_ra8_int0_bit_ctrt
Control-transfer-stage transition.
static volatile r_usb_regs_t * ra8_usb_fs(void)
Compile-time offset and size insurance for r_usb_regs_t.
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
USB FS / HS register window (device-mode subset).
volatile uint16_t PIPECTR[k_ra8_usb_pipectr_count]
+0x070 PIPE1..9 ctrl.
volatile uint16_t BEMPSTS
+0x04A BEMP Interrupt Status.
USBX device-controller-driver (DCD) bridge to ra8_usb.
@ k_ux_dcd_ra8_usb_max_pipes
DCP + 9 PIPE entries (HUM 36.1).
@ k_ux_dcd_ra8_usb_state_uninit
Bridge not yet initialized.
USBX device-controller-driver bridge to ra8_usb – per-module cross-translation-unit contract.
volatile uint32_t g_dcd_auto_echo_enable
Non-zero once ISR-side auto-echo is armed.
void priv_trace_event(uint8_t kind, uint8_t code, uint16_t length)
Append one packed event to the JLink-readable trace ring.
@ k_ra8_int0_dvsq_shift
DVSQ[2:0] field starts at bit 4.
uint8_t g_dcd_auto_echo_out_pipe
Pipe drained by the in-ISR auto-echo path.
uint16_t g_orphan_len
Held orphan byte count; 0 = empty.
uint8_t g_dcd_auto_echo_in_pipe
Pipe the auto-echo path re-queues onto.
ra8_usb_dcd_t g_dcd
The single bridge instance, defined in ux_dcd_ra8_usb.c.
@ k_dcd_trace_no_code
Code when none applies.
@ k_dcd_trace_cdb_lba_lo
CDB byte: LBA bits 7..0.
@ k_dcd_trace_cbw_op_off
CDB opcode offset inside a CBW.
@ k_dcd_trace_kind_out
Bulk-OUT transfer completed.
@ k_dcd_trace_cbw_len
BOT CBW wire length.
@ k_dcd_out_drain_max
Max OUT banks drained per ISR pass.
@ k_dcd_trace_cdb_lba_hi
CDB byte: LBA bits 15..8.
@ k_dcd_trace_op_read10
SCSI READ(10) opcode.
@ k_dcd_trace_op_write10
SCSI WRITE(10) opcode.
@ k_dcd_trace_byte_shift
CDB byte-pair packing shift.
@ k_dcd_trace_kind_ocap
Orphan OUT packet captured.
@ k_dcd_trace_kind_in
Bulk-IN transfer completed.
ra8_usb_dcd_diag_t g_diag
Bridge diagnostic counter block, defined in ux_dcd_ra8_usb.c.
void priv_handle_ctrt(ra8_usb_speed_t speed, uint16_t intsts0)
Handle the CTRT (control-transfer-stage) interrupt branch.
volatile uint32_t g_setup_token_observed
Counter of proven device-side SETUP-token latches.
void priv_irq_dvst_prelude(ra8_usb_speed_t speed, uint16_t intsts0)
Capture DVSTCTR0 / RHST history, mirror speed, dispatch DVST.
uint8_t g_orphan_buf[k_ra8_usb_orphan_bytes]
One held no-receiver bulk-OUT packet.
uint8_t g_orphan_pipe
Pipe the held orphan packet is on.
@ k_ra8_usb_orphan_bytes
One full-speed bulk MPS packet.
void priv_handle_ctrl_out_data(ra8_usb_speed_t speed)
Drain a deferred control-OUT data stage and run chapter-9.
static void internal_irq_drain_orphan_out(uint8_t i)
Capture a no-receiver bulk-OUT packet into the orphan buffer.
static void internal_irq_complete_out(UX_SLAVE_TRANSFER *tr, uint8_t i)
Complete a stashed OUT-pipe transfer (or rearm on no-data).
volatile uint32_t g_dcd_auto_echo_tx_err
static uint8_t s_dcd_auto_echo_buf[k_ux_dcd_ra8_usb_auto_echo_max]
static void internal_irq_complete_in(UX_SLAVE_TRANSFER *tr, uint8_t i)
Complete a stashed IN-pipe transfer.
ux_dcd_ra8_usb_auto_echo_cfg_t
@ k_ux_dcd_ra8_usb_auto_echo_max
Ux dcd RA8 USB auto echo maximum.
volatile uint32_t g_intsts0_snapshot_count
Total interesting INTSTS0 snapshots seen; modulo k_ra8_usb_dcd_intsts0_hist_n is the next write slot.
static void internal_irq_walk_pipe(uint8_t i)
Per-pipe finaliser for the BRDY/BEMP walk in ux_dcd_ra8_usb_irq.
volatile uint32_t g_intsts0_valid_count
Number of dispatch ticks where INTSTS0.VALID was observed.
volatile uint32_t g_intsts0_ctrt_count
Number of dispatch ticks where INTSTS0.CTRT was observed.
void ux_dcd_ra8_usb_irq(ra8_usb_speed_t speed, uint16_t intsts0)
Ux dcd ra usb irq.
static bool internal_irq_stage_next_in(UX_SLAVE_TRANSFER *tr, uint8_t i)
Stage the next IN chunk of a stashed transfer onto the pipe.
volatile uint16_t g_dcd_auto_echo_last_len
volatile uint8_t g_ctsq_history[(uint32_t) k_ra8_usb_dcd_intsts0_hist_n]
Ring buffer of the last 8 INTSTS0.CTSQ[2:0] values observed with CTRT or VALID asserted.
ra8_usb_dcd_intsts0_hist_t
Sizing for the INTSTS0 / CTSQ history rings.
@ k_ra8_usb_dcd_intsts0_hist_n
Slots in g_intsts0_snapshot/g_ctsq_history.
volatile uint16_t g_intsts0_observed_or
Bitwise OR of every INTSTS0 value ever fed to ux_dcd_ra8_usb_irq.
volatile uint8_t g_dvsq_history[(uint32_t) k_ra8_usb_dcd_intsts0_hist_n]
Per-snapshot DVSQ[2:0] field, pre-decoded for JLink readers.
static void internal_irq_record_snapshot(uint16_t intsts0)
Record per-bit IRQ counters and the JLink-visible event ring.
volatile uint16_t g_intsts0_last_dispatch
Most-recent INTSTS0 snapshot fed to ux_dcd_ra8_usb_irq.
static void internal_irq_finish_out(UX_SLAVE_TRANSFER *tr, uint8_t i, uint16_t now)
Finish a completed OUT transfer: park, trace, wake the waiter.
static void internal_irq_auto_echo(uint8_t i)
No-waiter auto-echo drain on the configured OUT pipe.
volatile uint16_t g_intsts0_snapshot[(uint32_t) k_ra8_usb_dcd_intsts0_hist_n]
Ring buffer of the last 8 INTSTS0 values seen by the worker on ticks where any of CTRT/VALID/DVST wer...
volatile uint32_t g_ctrt_irq_count
Counter of INTSTS0.CTRT events seen by the dispatcher.
volatile uint32_t g_dcd_auto_echo_tx_ok
static bool internal_irq_recover_in_nak(UX_SLAVE_TRANSFER *tr, uint8_t i, volatile r_usb_regs_t *reg)
Recover a stashed IN transfer whose pipe fell to PID=NAK.
volatile uint32_t g_dcd_auto_echo_drain_ok
volatile uint32_t g_dcd_auto_echo_drain_skip