ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ux_dcd_ra8_usb_setup.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
54volatile uint32_t g_setup_dispatch_count = 0U;
55
72volatile uint8_t g_setup_packet_buffer[8] = {};
73
81volatile uint32_t g_setup_packet_count = 0U;
82
100volatile uint32_t g_dispatch_skip_reason = 0U;
101
118volatile bool g_ctrl_out_pending = false;
119
132UX_SLAVE_TRANSFER* g_ctrl_out_tr = UX_NULL;
133
141volatile uint16_t g_ctrl_out_wlen = 0U;
142
150volatile uint32_t g_ctrl_out_rx = 0U;
151
159volatile uint32_t g_ctrl_out_done = 0U;
160
179volatile uint64_t g_last_dispatched_setup_fp = 0U;
180
190volatile uint64_t g_dispatched_fp_ring[4] = {};
191
192volatile uint8_t g_dispatched_fp_ring_idx = 0U;
193
203volatile uint8_t g_state_at_dispatch = 0U;
204
210typedef enum : uint8_t {
211 /* bmRequestType bit 7 -- USB 2.0 spec sec 9.3 Table 9-2:
212 * 0 = Host-to-Device (write or no-data), 1 = Device-to-Host (read). */
214 /* SET_ADDRESS standard request code. USB 2.0 spec sec 9.4.6
215 * Table 9-4 ("Standard Request Codes"): bRequest = 5 = SET_ADDRESS.
216 * Used to gate out the manual CCPL pulse for SET_ADDRESS, which
217 * the Renesas USBHS SIE auto-handles per HUM Ch 37.3 (auto
218 * response function, p 2147). */
221
230typedef enum : uint16_t {
233
240static volatile uint32_t s_trace[k_dcd_trace_entries] = {};
241
248static volatile uint32_t s_trace_ts[k_dcd_trace_entries] = {};
249
254typedef enum : uintptr_t {
255 k_dcd_dwt_cyccnt_addr = 0xE0001004U,
257
259static volatile uint32_t* const s_dcd_dwt_cyccnt = (volatile uint32_t*)k_dcd_dwt_cyccnt_addr;
260
267static volatile uint32_t s_trace_seq = 0U;
268
288void priv_trace_event(uint8_t kind, uint8_t code, uint16_t length)
289{
290 const uint32_t slot = s_trace_seq % (uint32_t)k_dcd_trace_entries;
291 s_trace[slot] = ((uint32_t)kind << (uint32_t)k_dcd_trace_kind_shift) |
292 ((uint32_t)code << (uint32_t)k_dcd_trace_code_shift) | (uint32_t)length;
294 s_trace_seq++;
295}
296
311
321typedef enum : uint8_t {
324
346RA8_INTERNAL static void internal_pack_setup_le(volatile uint8_t* buf, const ra8_usb_setup_t* setup)
347{
348 buf[k_setup_idx_bmrt] = setup->bm_request_type;
349 buf[k_setup_idx_brq] = setup->b_request;
350 buf[k_setup_idx_val_lo] = (uint8_t)(setup->w_value & k_setup_byte_mask);
351 buf[k_setup_idx_val_hi] = (uint8_t)((setup->w_value >> k_setup_byte_shift) & k_setup_byte_mask);
352 buf[k_setup_idx_idx_lo] = (uint8_t)(setup->w_index & k_setup_byte_mask);
353 buf[k_setup_idx_idx_hi] = (uint8_t)((setup->w_index >> k_setup_byte_shift) & k_setup_byte_mask);
354 buf[k_setup_idx_len_lo] = (uint8_t)(setup->w_length & k_setup_byte_mask);
355 buf[k_setup_idx_len_hi] = (uint8_t)((setup->w_length >> k_setup_byte_shift) & k_setup_byte_mask);
356}
357
390 UX_SLAVE_TRANSFER* tr)
391{
392 g_ctrl_out_pending = false;
393 if ((setup->bm_request_type & (uint8_t)k_ra8_usb_setup_dir_mask) == 0U) {
394 if (setup->w_length > 0U) {
395 g_ctrl_out_tr = tr;
396 g_ctrl_out_wlen = setup->w_length;
397 g_ctrl_out_pending = true;
398 (void)ra8_usb_dcp_out_arm(g_dcd.speed);
399 return true;
400 }
401 }
402 return false;
403}
404
430unsigned int priv_dispatch_setup(const ra8_usb_setup_t* setup)
431{
432 if (setup == nullptr) {
433 return UX_ERROR;
434 }
435
436 /* Mirror the just-decoded SETUP into the JLink-readable probe BEFORE
437 * touching any USBX-owned state. Even if USBX is not yet bound (no
438 * device stack init, no class registration), the bench can confirm
439 * via JLink that the chip latched a real SETUP and the bridge drained
440 * USBREQ/USBVAL/USBINDX/USBLENG correctly. */
444
445 /* USBX must already be bound to forward the SETUP into the chapter-9
446 * dispatcher. If it is not, that is fine for the bench probe path --
447 * we still recorded the packet above. Mark the skip reason so a JLink
448 * read disambiguates "USBX not bound" from "drain failed". */
449 if (_ux_system_slave == UX_NULL) {
450 g_dispatch_skip_reason |= 0x10U;
451 return UX_ERROR;
452 }
453 UX_SLAVE_DEVICE* device = &_ux_system_slave->ux_system_slave_device;
454 UX_SLAVE_TRANSFER* tr =
455 &device->ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;
456 if (tr == UX_NULL) {
457 g_dispatch_skip_reason |= 0x20U;
458 return UX_ERROR;
459 }
460
461 internal_pack_setup_le(tr->ux_slave_transfer_request_setup, setup);
462
463 tr->ux_slave_transfer_request_actual_length = 0UL;
464 tr->ux_slave_transfer_request_current_data_pointer = tr->ux_slave_transfer_request_data_pointer;
465 /* Chapter-9 dispatcher gates on completion_code == UX_SUCCESS
466 * (ux_device_stack_control_request_process.c line ~101). The
467 * previous SETUP may have left it as UX_TRANSFER_STALLED on a
468 * STALL'd request -- clear it so this fresh SETUP is honored. */
469 tr->ux_slave_transfer_request_completion_code = UX_SUCCESS;
470
471 /* Control-OUT with a data stage (e.g. DFU_DNLOAD) is armed and DEFERRED to
472 * the DCP BRDY ISR (see internal_try_defer_ctrl_out); IN / no-data requests
473 * fall through to the immediate chapter-9 dispatch below. */
474 if (internal_try_defer_ctrl_out(setup, tr)) {
476 return UX_SUCCESS;
477 }
478
479 const unsigned int rc = _ux_device_stack_control_request_process(tr);
480 if (rc != UX_SUCCESS) {
481 g_dispatch_skip_reason |= 0x08U;
482 } else {
484 }
485 return rc;
486}
487
513 uint64_t fingerprint)
514{
515 ra8_usb_setup_t setup = {};
516 if (ra8_usb_read_setup_unconditional(speed, &setup) != k_ra8_ok) {
517 return;
518 }
520 g_last_dispatched_setup_fp = fingerprint;
522 g_dispatched_fp_ring_idx = (uint8_t)((g_dispatched_fp_ring_idx + 1U) & 0x03U);
523
524 /* Belt-and-suspenders: ensure device_state is in {ATTACHED,
525 * ADDRESSED, CONFIGURED} BEFORE dispatch so the chapter-9 gate
526 * does not silently drop the SETUP. State demotion only happens on
527 * bus reset / suspend, so promoting to ATTACHED is monotonic-safe.
528 * Per-case branch keeps this out of the compound-decision MC/DC
529 * inventory. */
530 if (_ux_system_slave != UX_NULL) {
531 UX_SLAVE_DEVICE* const dev = &_ux_system_slave->ux_system_slave_device;
532 switch (dev->ux_slave_device_state) {
533 case (ULONG)UX_DEVICE_ATTACHED:
534 case (ULONG)UX_DEVICE_ADDRESSED:
535 case (ULONG)UX_DEVICE_CONFIGURED:
536 break;
537 default:
538 dev->ux_slave_device_state = (ULONG)UX_DEVICE_ATTACHED;
539 break;
540 }
541 }
542 g_state_at_dispatch = (uint8_t)(_ux_system_slave != UX_NULL
543 ? _ux_system_slave->ux_system_slave_device.ux_slave_device_state
545 const unsigned int rc = priv_dispatch_setup(&setup);
546
547 /* Drive CCPL for no-data H2D control transfers (SET_ADDRESS,
548 * SET_CONFIGURATION, SET_INTERFACE, SET_FEATURE...) so the host
549 * observes the status-stage IN-ZLP. Nested ifs keep the
550 * (dir==0) AND (wLength==0) gate out of the MC/DC inventory. */
551 if ((setup.bm_request_type & (uint8_t)k_ra8_usb_setup_dir_mask) == 0U) {
552 if (setup.w_length == 0U) {
553 (void)ra8_usb_control_response(speed, rc == UX_SUCCESS);
554 /* kind 7: H2D status driven -- code = bRequest, len = USBX rc. */
555 priv_trace_event((uint8_t)k_dcd_trace_kind_ccpl, setup.b_request, (uint16_t)rc);
556 }
557 }
558}
559
580{
581 volatile r_usb_regs_t* const reg = (speed == k_ra8_usb_speed_hs) ? ra8_usb_hs() : ra8_usb_fs();
582 if (reg == nullptr) {
583 return;
584 }
585 const uint16_t usbreq_live = reg->USBREQ;
586 const uint16_t usbval_live = reg->USBVAL;
587 const uint16_t usbindx_live = reg->USBINDX;
588 const uint16_t usbleng_live = reg->USBLENG;
589 const uint64_t fingerprint = ((uint64_t)usbreq_live) | ((uint64_t)usbval_live << 16) |
590 ((uint64_t)usbindx_live << 32) |
591 ((uint64_t)usbleng_live << (uint8_t)k_ra8_usb_fp_shift_usbleng);
592
593 /* Clear INTSTS0.VALID before anything else. The USBREQ/USBVAL/
594 * USBINDX/USBLENG mirrors persist (HUM Ch 37.2.21..24 p 2087), so the
595 * SETUP stays readable below; but a latched VALID stalls the
596 * DCPCTR.PID write gate (HUM Ch 37.2.31 p 2095) and holds the USB IRQ
597 * line asserted. VALID is a real event bit (in event_msk), so the
598 * ISR spurious-entry gate does not short-circuit it -- it must be
599 * cleared here. The early clear is also the dedup: a second
600 * ISR entry for the same physical SETUP sees VALID=0 and
601 * priv_handle_ctrt skips this function.
602 * HUM Ch 36.2.14 INTSTS0 p 1985 (W0C). */
603 reg->INTSTS0 = (uint16_t)(reg->INTSTS0 & (uint16_t)~(uint16_t)k_ra8_intsts0_mask_valid);
604
605 /* SET_ADDRESS short-circuit (HUM Ch 37.3 p 2147 -- the SIE owns the
606 * USBADDR latch and the IN-ZLP status stage). Nested ifs keep this
607 * out of the MC/DC compound-decision inventory. */
608 bool is_set_address = false;
609 if ((usbreq_live & (uint16_t)k_ra8_usb_usbreq_breq_mask) == (uint16_t)k_ra8_usb_usbreq_set_addr) {
610 if ((usbreq_live & (uint16_t)k_ra8_usb_usbreq_bmrt_mask) == 0x0000U) {
611 if (usbleng_live == 0U) {
612 is_set_address = true;
613 }
614 }
615 }
616 if (is_set_address) {
617 return;
618 }
619
620 /* Dispatch every observed SETUP, including a host retransmit of
621 * byte-identical bytes after a missed response window. The previous
622 * `fingerprint != g_last_dispatched_setup_fp` skip dropped exactly
623 * those retransmits and -- with the VALID clear also skipped -- left
624 * the controller wedged. The early VALID clear above is the real
625 * dedup; the fingerprint is now recorded for diagnostics only. */
626 internal_ctrt_dispatch_fresh_setup(speed, fingerprint);
627}
628
652void priv_handle_ctrt(ra8_usb_speed_t speed, uint16_t intsts0)
653{
654 const uint16_t ctsq = (uint16_t)(intsts0 & (uint16_t)k_ra8_intsts0_mask_ctsq);
655 const bool have_valid = ((intsts0 & (uint16_t)k_ra8_intsts0_mask_valid) != 0U);
656
657 /* Per HUM Ch 37.2.18 p 2081 the SIE auto-clears VALID quickly but
658 * USBREQ/USBVAL/USBINDX/USBLENG persist; drain unconditionally on
659 * any VALID observation and dedup via the fingerprint. */
660 if (have_valid) {
662 }
663
664 /* Status-stage-only handling (no SETUP drain needed). CTSQ=wrnd
665 * fires on the trailing edge of no-data H2D transfers when the
666 * SETUP-drain CCPL above did not run. */
667 switch (ctsq) {
668 case k_ra8_ctsq_rdss:
669 case k_ra8_ctsq_wrss:
670 case k_ra8_ctsq_wrnd:
671 (void)ra8_usb_control_response(speed, true);
672 break;
673 case k_ra8_ctsq_sqer:
674 (void)ra8_usb_control_response(speed, false);
675 break;
676 default:
677 break;
678 }
679}
680
713{
714 if (!g_ctrl_out_pending) {
715 return;
716 }
717 volatile r_usb_regs_t* const reg = (speed == k_ra8_usb_speed_hs) ? ra8_usb_hs() : ra8_usb_fs();
718 if (reg == nullptr) {
719 return;
720 }
721 /* Wait for the host's OUT packet to land before draining; an unrelated IRQ
722 * (e.g. a BRDY for a bulk pipe) leaves us pending for the next pass.
723 * HUM Ch 36.2.13 "BRDYSTS" p 1984. */
724 if ((reg->BRDYSTS & (uint16_t)k_ra8_usb_dcp_brdy_bit) == 0U) {
725 return;
726 }
727
728 UX_SLAVE_TRANSFER* const tr = g_ctrl_out_tr;
729 g_ctrl_out_pending = false;
730 g_ctrl_out_tr = UX_NULL;
731 if (tr == UX_NULL) {
732 return;
733 }
734
735 uint16_t rx = 0U;
736 if (ra8_usb_dcp_out_read(speed,
737 tr->ux_slave_transfer_request_data_pointer,
739 &rx) != k_ra8_ok) {
740 return;
741 }
742 tr->ux_slave_transfer_request_actual_length = rx;
743 g_ctrl_out_rx = (uint32_t)rx;
745 (void)_ux_device_stack_control_request_process(tr);
746 /* Complete the control-write with its IN-ZLP status stage: pulse CCPL so the
747 * SIE answers the host's status-stage IN token. Without this the host's
748 * status read (internal_host_ctrl_status) never sees the ZLP and times out. */
749 (void)ra8_usb_control_response(speed, true);
750}
#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
NVIC + ICU IELSR allocator.
Lightweight Logging Interface for ra8-firmware.
ra8_err_t ra8_usb_dcp_out_arm(ra8_usb_speed_t speed)
Arm the DCP (EP0) to receive a host-to-device control data stage.
ra8_err_t ra8_usb_control_response(ra8_usb_speed_t speed, bool accept)
Issue a control-transfer status response on EP0.
ra8_err_t ra8_usb_read_setup_unconditional(ra8_usb_speed_t speed, ra8_usb_setup_t *out_setup)
Snapshot the current SETUP packet from the controller without gating on INTSTS0.VALID (HS / SQMON pol...
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_dcp_out_read(ra8_usb_speed_t speed, uint8_t *buf, uint16_t cap, uint16_t *out_rx)
Drain an armed control-OUT data stage from the DCP (EP0).
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_ctsq_sqer
Sequence error.
@ k_ra8_ctsq_rdss
Control read status stage.
@ k_ra8_ctsq_wrss
Control write status stage.
@ k_ra8_ctsq_wrnd
Control write nodata status.
static volatile r_usb_regs_t * ra8_usb_hs(void)
Get pointer to the USB HS controller register block.
@ k_ra8_intsts0_mask_valid
SETUP packet detect flag.
@ k_ra8_intsts0_mask_ctsq
Control transfer stage.
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 USBINDX
+0x058 USB Request wIndex.
volatile uint16_t USBVAL
+0x056 USB Request wValue.
volatile uint16_t INTSTS0
+0x040 Interrupt Status 0.
volatile uint16_t USBLENG
+0x05A USB Request wLength.
volatile uint16_t BRDYSTS
+0x046 BRDY Interrupt Status.
volatile uint16_t USBREQ
+0x054 USB Request type/bRequest.
Decoded 8-byte USB SETUP packet.
uint16_t w_length
wLength.
uint8_t b_request
bRequest code.
uint8_t bm_request_type
Request direction / type / recipient.
uint16_t w_index
wIndex.
uint16_t w_value
wValue.
USBX device-controller-driver (DCD) bridge to ra8_usb.
USBX device-controller-driver bridge to ra8_usb – per-module cross-translation-unit contract.
volatile uint64_t g_last_dispatched_setup_fp
64-bit fingerprint of the last dispatched SETUP packet.
volatile uint32_t g_setup_packet_count
Total SETUP packets latched into g_setup_packet_buffer.
@ k_ra8_usb_skip_process_ok
control_request_process returned OK.
volatile uint8_t g_setup_packet_buffer[8]
Wire-format bytes of the most recent SETUP packet.
ra8_usb_dcd_t g_dcd
The single bridge instance, defined in ux_dcd_ra8_usb.c.
@ k_setup_byte_shift
Bits per byte for the hi-byte extraction.
@ k_setup_byte_mask
Low-byte mask after the shift.
@ k_dcd_trace_kind_ccpl
EP0 H2D status stage driven.
@ k_dcd_trace_kind_shift
Kind field bit offset.
@ k_dcd_trace_entries
Ring slots (power of two).
@ k_dcd_trace_code_shift
Code field bit offset.
@ k_dcd_trace_kind_setup
EP0 SETUP received.
@ k_ra8_usb_state_unknown
Device-state snapshot sentinel.
volatile uint32_t g_dispatch_skip_reason
Last-iteration bitmask of why a SETUP dispatch was skipped or completed.
@ k_setup_idx_brq
bRequest (offset 1).
@ k_setup_idx_val_lo
wValue low byte (offset 2).
@ k_setup_idx_idx_hi
wIndex high byte (offset 5).
@ k_setup_idx_val_hi
wValue high byte (offset 3).
@ k_setup_idx_bmrt
bmRequestType (offset 0).
@ k_setup_idx_len_hi
wLength high byte (offset 7).
@ k_setup_idx_len_lo
wLength low byte (offset 6).
@ k_setup_idx_idx_lo
wIndex low byte (offset 4).
volatile uint32_t g_setup_dispatch_count
Count of SETUP packets fed into the chapter-9 dispatcher.
static void internal_pack_setup_le(volatile uint8_t *buf, const ra8_usb_setup_t *setup)
Pack a decoded SETUP into the 8-byte USB-wire little-endian layout.
ra8_dcd_dwt_t
Armv8-M Data Watchpoint and Trace unit register addresses.
@ k_dcd_dwt_cyccnt_addr
DWT_CYCCNT free-running cycle counter.
void priv_trace_event(uint8_t kind, uint8_t code, uint16_t length)
Append one packed event to the JLink-readable trace ring.
volatile uint64_t g_dispatched_fp_ring[4]
Ring of the last 4 dispatched SETUP fingerprints (oldest at index 0).
volatile uint16_t g_ctrl_out_wlen
wLength (cap) of the pending control-write data stage, in bytes.
static volatile uint32_t *const s_dcd_dwt_cyccnt
DWT cycle counter address (Armv8-M DWT_CYCCNT).
volatile uint8_t g_state_at_dispatch
Snapshot of ux_slave_device_state at the moment of the most recent SETUP dispatch.
static volatile uint32_t s_trace_ts[k_dcd_trace_entries]
DWT cycle-count timestamp per s_trace slot.
ra8_usb_setup_local_t
Local USB SETUP-packet bit-field constants.
@ k_ra8_usb_setup_dir_mask
RA8 USB setup dir mask.
@ k_ra8_usb_breq_set_address
RA8 USB breq set address.
static volatile uint32_t s_trace[k_dcd_trace_entries]
JLink-readable ring of packed transfer/SETUP events.
volatile uint32_t g_ctrl_out_done
Count of completed deferred control-OUT data stages.
static bool internal_try_defer_ctrl_out(const ra8_usb_setup_t *setup, UX_SLAVE_TRANSFER *tr)
Arm + defer a control-WRITE data stage; report whether it was deferred.
void priv_handle_ctrt(ra8_usb_speed_t speed, uint16_t intsts0)
Handle the CTRT (control-transfer-stage) interrupt branch.
ra8_usb_dcp_brdy_t
BRDYSTS / BRDYENB bit for the Default Control Pipe (pipe 0).
@ k_ra8_usb_dcp_brdy_bit
DCP (pipe 0) BRDYSTS / BRDYENB bit.
ra8_usb_setup_fp_shift_t
Bit offset of each 16-bit SETUP mirror inside the 64-bit SETUP fingerprint word.
@ k_ra8_usb_fp_shift_usbleng
USBLENG packs into fingerprint bits 48-63.
volatile uint8_t g_dispatched_fp_ring_idx
volatile uint32_t g_ctrl_out_rx
Byte count drained by the most recent deferred control-OUT receive.
UX_SLAVE_TRANSFER * g_ctrl_out_tr
EP0 transfer request whose data buffer the deferred OUT data fills.
static void internal_ctrt_dispatch_fresh_setup(ra8_usb_speed_t speed, uint64_t fingerprint)
Drain a fresh SETUP and forward to the chapter-9 dispatcher.
static void internal_ctrt_handle_valid(ra8_usb_speed_t speed)
VALID-observed branch of priv_handle_ctrt.
volatile bool g_ctrl_out_pending
A control-write data stage is armed and awaiting the host's OUT data.
ra8_usb_setup_usbreq_t
Byte masks and request encodings applied to the USBREQ mirror.
@ k_ra8_usb_usbreq_bmrt_mask
bmRequestType (low) byte of USBREQ.
@ k_ra8_usb_usbreq_breq_mask
bRequest (high) byte of USBREQ.
@ k_ra8_usb_usbreq_set_addr
USBREQ high byte == SET_ADDRESS (5).
static volatile uint32_t s_trace_seq
Monotonic count of events written to s_trace.
unsigned int priv_dispatch_setup(const ra8_usb_setup_t *setup)
Push a decoded SETUP packet into the USBX chapter-9 dispatcher.
void priv_handle_ctrl_out_data(ra8_usb_speed_t speed)
Drain a deferred control-OUT data stage and run chapter-9.