ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_usb.c
Go to the documentation of this file.
1
21
22#include "board_usb.h"
23
24#include <stdio.h>
25#include <string.h>
26
27#include "board_console.h"
28#include "board_usb_internal.h"
30#include "ra8_elc_regs.h"
31#include "ra8_usb_regs.h"
32
36
37/* Set true when a real-firmware USB host (the emulated USBHS controller in
38 * board_periph_usbhs_host.c) drives THIS device model through the bridge below;
39 * the built-in virtual chapter-9 host then stands down (board_usb_tick returns
40 * early) so exactly one host drives the device -- the chip-internal self-loop. */
42
43/* Self-loop role polarity (see the role-routing section of board_usb.h).
44 * false: USBFS window = this device model, USBHS window = host model (Config
45 * A). true: swapped (Config B) -- the firmware declared the FS controller the
46 * host (SYSCFG.DCFM=1 written to the FS window) or the HS controller the
47 * device (SYSCFG.DPRPU=1 written to the HS window), so the FS window routes to
48 * the host model and the HS window routes here. Sticky until board_usb_init. */
50
51/* ICU event the device model pends for its interrupts: USBFS_INT while the
52 * device model sits behind the USBFS window (Config A), USBHS_USB_INT_RESUME
53 * once the roles swap and the DCD drives the USBHS controller (Config B). */
55
56/* DCP control-OUT "wire" holding buffer (bridge path). The polled host delivers
57 * a control-write data stage (e.g. a DFU_DNLOAD block) before the device -- off
58 * its own IRQ on the shared core -- has armed its DCP to receive it, and the
59 * device's arm-time CFIFO BCLR would discard data landed too early. So the
60 * bytes are held here as "in flight on the wire" and handed to the device DCP
61 * only once it has armed (PID=BUF + DCP BRDY enabled), exactly as the SIE would
62 * deliver an OUT packet the device is finally ready to ACK. */
66
67/* Virtual-host bookkeeping. */
68uint8_t s_host_phase;
69uint8_t s_host_step;
71uint32_t s_host_wait;
73uint32_t s_usb_irqs;
74
75/* Bulk-echo (secondary CDC check) bookkeeping. */
79uint32_t s_echo_in_got;
80
81/* Enumeration-step log (host SETUP -> device stage), shown in the report. */
83static uint32_t s_log_n;
84
85/* =============================================================================
86 * Small register / logging helpers.
87 * =============================================================================
88 */
89
91void priv_usb_log_line(const char* msg)
92{
93 /* Mirror this completed USB event (one SETUP step / state change / bulk
94 * buffer -- already coalesced by the caller, never per byte) onto the board
95 * view's USB console tab. This is an in-memory store only and does not touch
96 * injected output sink, so the smoke / golden output stays byte-identical. Pushed outside
97 * the s_log cap below so the live tab keeps flowing past the report cap. */
99 if (s_log_n < (uint32_t)k_usb_log_cap) {
100 (void)snprintf(s_log[s_log_n], sizeof(s_log[0]), "%s", msg);
101 if (s_trace) {
102 (void)priv_emu_io_errf(" [usb] %s\n", s_log[s_log_n]);
103 }
104 s_log_n++;
105 }
106}
107
109void priv_usb_log_count(const char* label, unsigned n)
110{
111 char line[k_usb_log_width];
112 (void)snprintf(line, sizeof(line), "%s: %u byte(s)", label, n);
113 priv_usb_log_line(line);
114}
115
116void board_usb_tick(uc_engine* uc)
117{
118 /* Latch the CONFIGURED marker as soon as the device state reaches it -- the
119 * host sets DVSQ, but record it here so a single source of truth drives both
120 * the success assertion and the report marker. */
121 if (s_usb.dvsq == (uint16_t)k_ra8_dvsq_configured) {
122 s_configured = true;
123 }
124 /* A real-firmware USB host is driving the device through the bridge (the
125 * chip-internal self-loop): the built-in virtual host must not also drive it,
126 * or two hosts would race the device's control endpoint. Stand down -- but
127 * still pump the device's level-triggered DCP-OUT receive so a control-write
128 * data stage the host delivered before the device armed still lands. */
129 if (s_external_host) {
131 return;
132 }
133 /* Self-loop bench (register-level USBHS host model, board_usb_host.c): the
134 * firmware's own host drives this device over the loop cable
135 * (board_usb_loop_*), so the built-in virtual host must not compete for the
136 * control pipe or drain the bulk-IN data. */
137 if (s_loop_latched) {
138 return;
139 }
141 case k_phase_idle:
143 break;
144 case k_phase_reset:
146 break;
147 case k_phase_setup:
149 break;
152 break;
153 case k_phase_done:
154 default:
155 priv_host_echo_read_in(uc); /* keep draining any late echo. */
156 break;
157 }
158}
159
160/* =============================================================================
161 * Public lifecycle / accessors / report.
162 * =============================================================================
163 */
164
165void board_usb_init(bool trace)
166{
167 s_usb = (usb_state_t){};
168 s_trace = trace;
169 s_host_phase = (uint8_t)k_phase_idle;
170 s_host_step = 0U;
172 s_host_wait = 0U;
173 s_configured = false;
174 s_usb_irqs = 0U;
175 s_echo_out_len = 0U;
176 s_echo_out_sent = 0U;
177 s_echo_in_got = 0U;
178 s_log_n = 0U;
179 s_dcp_hold_len = 0U;
180 s_dcp_hold_pending = false;
181 s_roles_swapped = false;
183 s_usb.dvsq = (uint16_t)k_ra8_dvsq_powered;
184}
185
187{
188 s_raise = raise;
189}
190
192{
193 return s_configured;
194}
195
196void board_usb_feed_bulk_in(const uint8_t* data, uint32_t len)
197{
198 if ((data == nullptr) || (len == 0U)) {
199 return;
200 }
201 const uint32_t n = (len > (uint32_t)k_usb_echo_cap) ? (uint32_t)k_usb_echo_cap : len;
202 (void)memcpy(s_echo_out, data, n);
203 s_echo_out_len = n;
204}
205
207{
208 return s_echo_in_got;
209}
210
212RA8_INTERNAL static const char* internal_usb_dvsq_name(uint16_t dvsq)
213{
214 switch (dvsq & (uint16_t)k_ra8_intsts0_mask_dvsq) {
215 case (uint16_t)k_ra8_dvsq_powered:
216 return "Powered";
217 case (uint16_t)k_ra8_dvsq_default:
218 return "Default";
219 case (uint16_t)k_ra8_dvsq_address:
220 return "Address";
221 case (uint16_t)k_ra8_dvsq_configured:
222 return "Configured";
223 default:
224 return "Suspended";
225 }
226}
227
228const char* board_usb_state_string(void)
229{
230 if (s_configured) {
231 switch ((usb_dev_class_t)s_dev_class) {
232 case k_usb_class_hid:
233 return "CONFIGURED (HID active)";
234 case k_usb_class_msc:
235 return "CONFIGURED (MSC active)";
236 case k_usb_class_cdc:
237 return "CONFIGURED (CDC-ACM active)";
239 return "CONFIGURED (Printer active)";
241 return "CONFIGURED (Vendor active)";
243 default:
244 return "CONFIGURED";
245 }
246 }
247 return internal_usb_dvsq_name(s_usb.dvsq);
248}
249
251{
252 if ((s_usb_irqs == 0U) && (s_log_n == 0U)) {
253 return; /* USB never came up in this run; stay silent. */
254 }
255 (void)priv_emu_io_errf(" USB-FS : %u IRQ(s), device state %s\n",
258 for (uint32_t i = 0U; i < s_log_n; i++) {
259 (void)priv_emu_io_errf(" usb: %s\n", s_log[i]);
260 }
261 if (s_configured) {
262 (void)priv_emu_io_errf(" USB: device CONFIGURED (%s)\n", priv_usb_class_active_str());
263 } else {
264 (void)priv_emu_io_errf(" USB: enumeration INCOMPLETE (device did not reach CONFIGURED)\n");
265 }
266 if (s_hid_reports > 0U) {
267 (void)priv_emu_io_errf(" USB HID : %u mouse report(s), cursor (%d,%d) buttons 0x%02X\n",
269 (int)s_hid_cx,
270 (int)s_hid_cy,
271 (unsigned)s_hid_buttons);
272 }
273 if ((s_msc_blocks > 0U) || (s_msc_read_ok > 0U)) {
274 (void)priv_emu_io_errf(
275 " USB MSC : capacity %u blocks x %uB, INQUIRY %s, sector read %u byte(s)\n",
278 s_msc_inquiry_ok ? "ok" : "--",
280 }
281 if (s_echo_out_len > 0U) {
282 (void)priv_emu_io_errf(" USB CDC echo : sent %u byte(s) OUT, read %u byte(s) IN\n",
285 }
286 if (s_loop_latched) {
287 (void)priv_emu_io_errf(
288 " USB self-loop : fw host drove %u SETUP(s), %u bulk-OUT, %u bulk-IN pkt(s)\n",
292 }
293}
Multi-channel console log store backing the board view's tabbed console.
void board_console_push(board_console_ch_t ch, const char *line)
Append one completed line to a channel ring and the ALL ring.
@ k_board_console_ch_usb
USBFS transfer / enumeration summaries.
static char s_log[k_usb_log_cap][k_usb_log_width]
Definition board_usb.c:82
void board_usb_feed_bulk_in(const uint8_t *data, uint32_t len)
Queue host->device bulk bytes for the CDC data OUT pipe (echo test).
Definition board_usb.c:196
void board_usb_report(void)
Print the USB section of the end-of-run summary.
Definition board_usb.c:250
uint32_t board_usb_echo_received(void)
Number of bulk bytes the host has read back as the device's echo.
Definition board_usb.c:206
void board_usb_tick(uc_engine *uc)
Advance the virtual USB host one emulation chunk.
Definition board_usb.c:116
void board_usb_init(bool trace)
Reset the USBFS controller model and the virtual host state machine.
Definition board_usb.c:165
const char * board_usb_state_string(void)
One-line, human-readable USB device state for the board view.
Definition board_usb.c:228
static const char * internal_usb_dvsq_name(uint16_t dvsq)
Print the device-state name for the report line.
Definition board_usb.c:212
bool board_usb_configured(void)
Report whether enumeration reached the CONFIGURED state.
Definition board_usb.c:191
static uint32_t s_log_n
Definition board_usb.c:83
void priv_usb_log_line(const char *msg)
Append one already-formatted line to the enumeration-step log.
Definition board_usb.c:91
void board_usb_set_irq_raiser(board_usb_irq_raiser_t raise)
Install the ICU event-raise hook used to pend the USBFS interrupt.
Definition board_usb.c:186
void priv_usb_log_count(const char *label, unsigned n)
Append a "<label>: <n> byte(s)"-style line built from one count.
Definition board_usb.c:109
USBFS controller model + a virtual USB host for the board emulator.
void(* board_usb_irq_raiser_t)(uc_engine *uc, uint16_t event)
Signature of the ICU event-raise hook board_periph installs.
Definition board_usb.h:68
Module-private state + interfaces shared by the board_usb TUs.
uint32_t s_msc_blocks
MSC capacity in blocks (vhost).
uint32_t s_loop_bulk_in_pkts
Bulk-IN packets (loop).
uint32_t s_loop_setups
SETUPs from the fw host (loop).
uint8_t s_dcp_hold[k_usb_in_cap]
Held control-OUT bytes (core).
Definition board_usb.c:63
@ k_sub_deliver
Latch the SETUP + raise CTRT.
bool s_external_host
Bridge host owns the bus (core).
Definition board_usb.c:41
usb_host_phase_t
Host state-machine phases.
@ k_phase_reset
Holding bus reset; device re-arms its DCP.
@ k_phase_done
Terminal idle.
@ k_phase_idle
Waiting for the device pull-up (DPRPU).
@ k_phase_configured
Device configured; optional bulk echo.
@ k_phase_setup
Walking the enumeration script.
usb_state_t s_usb
Controller + FIFO state (core).
Definition board_usb.c:33
uint32_t s_loop_bulk_out_pkts
Bulk-OUT packets (loop).
uint8_t s_host_substate
Per-step sub-state (core).
Definition board_usb.c:70
int32_t s_hid_cy
Accumulated HID mouse Y (vhost).
uint32_t s_echo_in_got
Echoed bytes read back (core).
Definition board_usb.c:79
uint8_t s_host_step
Enumeration-script index (core).
Definition board_usb.c:69
uint32_t s_host_wait
Sub-state wait ticks (core).
Definition board_usb.c:71
uint32_t s_msc_block_len
MSC block size (vhost).
uint8_t s_dev_class
Detected device class (vhost).
uint8_t s_echo_out[k_usb_echo_cap]
Host bulk-OUT payload (core).
Definition board_usb.c:76
uint8_t s_host_phase
Virtual-host phase (core).
Definition board_usb.c:68
void priv_host_run_configured_phase(uc_engine *uc)
Virtual-host phase driver: post-CONFIGURED class traffic (vhost).
void priv_bridge_pump_device(uc_engine *uc)
Pump the device's level-triggered DCP-OUT receive (bridge).
void priv_host_run_setup_phase(uc_engine *uc)
Virtual-host phase driver: walking the enumeration script (vhost).
@ k_usb_echo_cap
Bulk echo staging capacity.
@ k_usb_in_cap
DCP/bulk IN staging cap (multi-MPS).
uint32_t s_msc_read_ok
MSC read data bytes (vhost).
const char * priv_usb_class_active_str(void)
Human label of the detected class + live traffic totals (vhost).
int32_t s_hid_cx
Accumulated HID mouse X (vhost).
usb_dev_class_t
USB device class the host detected from the interface descriptor.
@ k_usb_class_printer
Printer (7/1/x), issue #265.
@ k_usb_class_cdc
CDC-ACM (virtual serial).
@ k_usb_class_hid
HID (boot mouse / keyboard).
@ k_usb_class_msc
Mass storage (BOT/SCSI).
@ k_usb_class_unknown
Not yet detected.
@ k_usb_class_vendor
Vendor specific (0xFF).
void priv_host_echo_read_in(uc_engine *uc)
Drain any device bulk-IN data the virtual host is owed (vhost).
uint32_t s_usb_irqs
USB interrupts raised (core).
Definition board_usb.c:73
bool s_dcp_hold_pending
Held bytes await the arm (core).
Definition board_usb.c:65
bool s_msc_inquiry_ok
MSC INQUIRY completed (vhost).
@ k_usb_log_width
Bytes per enumeration-step log line.
@ k_usb_log_cap
Enumeration-step log capacity.
void priv_host_run_idle_phase(uc_engine *uc)
Virtual-host phase driver: waiting for the device pull-up (vhost).
uint16_t s_dcp_hold_len
Held byte count (core).
Definition board_usb.c:64
uint32_t s_hid_reports
HID reports read (vhost).
uint32_t s_echo_out_len
Bytes queued by –usb-in (core).
Definition board_usb.c:77
bool s_loop_latched
Firmware host owns the bus (loop).
bool s_roles_swapped
Self-loop role polarity (core).
Definition board_usb.c:49
uint16_t g_dev_irq_event
Device ICU event number (core).
Definition board_usb.c:54
uint8_t s_hid_buttons
Last HID button bitmap (vhost).
board_usb_irq_raiser_t s_raise
ICU pend callback (core).
Definition board_usb.c:35
void priv_host_run_reset_phase(uc_engine *uc)
Virtual-host phase driver: holding bus reset (vhost).
uint32_t s_echo_out_sent
Bytes delivered to device (core).
Definition board_usb.c:78
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
static volatile bool s_configured
Latched true once the host drives the device to CONFIGURED.
Definition main.c:128
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Event Link Controller (ELC) register layout for the Renesas RA8D2.
@ k_ra8_elc_event_usbfs_int
USBFS controller interrupt.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_intsts0_mask_dvsq
Device state.
@ k_ra8_dvsq_address
Address assigned.
@ k_ra8_dvsq_default
Default (post bus reset).
@ k_ra8_dvsq_configured
Configured.
@ k_ra8_dvsq_powered
Powered (no reset yet).
Aggregate USBFS controller + virtual-host model state.
static volatile uint32_t s_trace[k_dcd_trace_entries]
JLink-readable ring of packed transfer/SETUP events.