ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_usb_bridge.c
Go to the documentation of this file.
1
15
16#include <stdio.h>
17#include <string.h>
18
19#include "board_usb_internal.h"
21#include "ra8_elc_regs.h"
22#include "ra8_usb_regs.h"
23
24/* =============================================================================
25 * Chip-internal USBHS-host <-> USBFS-device bridge (see board_usb.h).
26 *
27 * The emulated USBHS host controller (board_periph_usbhs_host.c) calls these to
28 * drive THIS device model from the real firmware's host-side register accesses,
29 * reusing the same device-poking primitives the virtual host uses. This closes
30 * the RA8D2's on-chip HS-host -> FS-device loop entirely in-process.
31 * =============================================================================
32 */
33
35{
36 s_external_host = present;
37}
38
40{
41 return s_roles_swapped;
42}
43
44void board_usb_roles_swap(uc_engine* uc)
45{
46 (void)uc;
47 if (s_roles_swapped) {
48 return; /* one-shot: both triggers (FS DCFM=1, HS DPRPU=1) fire in Config B. */
49 }
50 /* The device firmware's PHY + module-init writes landed in the HS window's
51 * shadow while its role was still unknowable (the HS host bring-up drives the
52 * very same device-polarity PHY sequence first). Adopt that accumulated state
53 * as the device model's register file -- the models swap windows, the
54 * firmware-visible register contents must not change -- and reset the host
55 * model for its fresh life behind the FS window (which is still virgin: the
56 * FS host driver's first window write is the SYSCFG.DCFM=1 that triggers
57 * this swap, and the HS DPRPU=1 trigger fires before any FS host activity). */
59 s_roles_swapped = true;
61 priv_usb_log_line("role swap: FS window = host model, HS window = device model (Config B)");
62}
63
64uint64_t board_usb_dev_reg_read(uc_engine* uc, uint64_t off, unsigned size)
65{
66 (void)uc;
67 return (uint64_t)priv_usb_reg_read(off, size);
68}
69
70void board_usb_dev_reg_write(uc_engine* uc, uint64_t off, unsigned size, uint64_t value)
71{
72 (void)uc;
73 priv_usb_reg_write(off, (uint32_t)value, size);
74}
75
77{
79}
80
81void board_usb_bridge_bus_reset(uc_engine* uc)
82{
83 s_usb.dvsq = (uint16_t)k_ra8_dvsq_default;
85 priv_usb_log_line("bridge: HS host bus reset -> device Default");
87}
88
89void board_usb_bridge_deliver_setup(uc_engine* uc, const uint8_t* setup)
90{
91 if (setup == nullptr) {
92 return;
93 }
94 const usb_setup_step_t s = {
95 .bm_request_type = setup[0],
96 .b_request = setup[1],
97 .w_value = (uint16_t)((uint16_t)setup[2] | (uint16_t)((uint16_t)setup[3] << 8)),
98 .w_index = (uint16_t)((uint16_t)setup[4] | (uint16_t)((uint16_t)setup[5] << 8)),
99 .w_length = (uint16_t)((uint16_t)setup[6] | (uint16_t)((uint16_t)setup[7] << 8)),
100 .name = "bridge SETUP",
101 };
103 /* SET_ADDRESS is latched by the SIE on hardware; mirror that so the device
104 * advances to the Address state (priv_host_apply_no_data self-guards on bRequest). */
106}
107
109{
110 return s_usb.dcp_in.valid && priv_host_dcp_pid_buf();
111}
112
113uint16_t board_usb_bridge_dcp_in_take(uint8_t* buf, uint16_t cap)
114{
115 if (buf == nullptr) {
116 return 0U;
117 }
118 uint16_t n = s_usb.dcp_in.len;
119 if (n > cap) {
120 n = cap;
121 }
122 (void)memcpy(buf, s_usb.dcp_in.data, n);
123 priv_usb_detect_class(s_usb.dcp_in.data, s_usb.dcp_in.len);
124 priv_usb_log_count("bridge control-IN: device returned", (unsigned)s_usb.dcp_in.len);
125 if (s_trace) {
126 /* Payload head, --trace only: enough to identify a descriptor / status. */
127 char line[k_usb_log_width];
128 int w = 0;
129 const uint16_t limit =
130 (n < (uint16_t)k_usb_trace_dump_max) ? n : (uint16_t)k_usb_trace_dump_max;
131 for (uint16_t i = 0U; (i < limit) && (w >= 0) && ((size_t)w < sizeof(line)); i++) {
132 w += snprintf(&line[w], sizeof(line) - (size_t)w, "%02X ", (unsigned)s_usb.dcp_in.data[i]);
133 }
134 (void)priv_emu_io_errf(" [usb] bridge control-IN bytes: %s\n", line);
135 }
136 s_usb.dcp_in.len = 0U;
137 s_usb.dcp_in.valid = false;
138 return n;
139}
140
142{
143 s_usb.ctsq = (uint16_t)k_ra8_ctsq_rdss;
144 s_usb.setup_valid = false;
147}
148
150{
151 return priv_host_take_ccpl();
152}
153
155{
157}
158
159void board_usb_bridge_bulk_out(uc_engine* uc, uint8_t dev_pipe, const uint8_t* data, uint16_t len)
160{
161 if (data == nullptr) {
162 return;
163 }
164 usb_out_buf_t* b = &s_usb.pipe_out[dev_pipe % k_usb_pipe_count];
165 uint16_t n = len;
166 if (n > (uint16_t)sizeof(b->data)) {
167 n = (uint16_t)sizeof(b->data);
168 }
169 (void)memcpy(b->data, data, n);
170 b->len = n;
171 b->rd = 0U;
172 b->ready = true;
173 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
174 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << dev_pipe));
177}
178
180{
181 const usb_out_buf_t* b = &s_usb.pipe_out[dev_pipe % k_usb_pipe_count];
182 /* The device firmware drains the staged OUT packet through its CFIFO, which
183 * advances @c rd to @c len. Until then the packet is still in flight, so the
184 * host must not push the next one (it would overwrite this bank -- the exact
185 * failure that wedged a multi-packet WRITE(10) data stage). */
186 return b->ready && (b->rd >= b->len);
187}
188
189bool board_usb_bridge_bulk_in_ready(uint8_t dev_pipe)
190{
191 const usb_in_buf_t* b = &s_usb.pipe_in[dev_pipe % k_usb_pipe_count];
192 return b->valid && (b->len > 0U);
193}
194
195uint16_t board_usb_bridge_bulk_in_take(uc_engine* uc, uint8_t dev_pipe, uint8_t* buf, uint16_t cap)
196{
197 if (buf == nullptr) {
198 return 0U;
199 }
200 usb_in_buf_t* b = &s_usb.pipe_in[dev_pipe % k_usb_pipe_count];
201 uint16_t n = b->len;
202 if (n > cap) {
203 n = cap;
204 }
205 (void)memcpy(buf, b->data, n);
206 b->len = 0U;
207 b->valid = false;
208 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_bempsts);
209 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << dev_pipe));
212 return n;
213}
214
215void board_usb_bridge_dcp_out(uc_engine* uc, const uint8_t* data, uint16_t len)
216{
217 (void)uc;
218 if (data == nullptr) {
219 return;
220 }
221 uint16_t n = len;
222 if (n > (uint16_t)sizeof(s_dcp_hold)) {
223 n = (uint16_t)sizeof(s_dcp_hold);
224 }
225 /* Hold the bytes as "in flight on the wire": the device has almost certainly
226 * not armed its DCP for OUT yet, and its arm-time BCLR would discard them.
227 * priv_bridge_pump_device() lands them once the device is ready. */
228 (void)memcpy(s_dcp_hold, data, n);
229 s_dcp_hold_len = n;
230 s_dcp_hold_pending = true;
231}
232
234{
235 /* Still on the wire (not yet handed to the device): not consumed. */
236 if (s_dcp_hold_pending) {
237 return false;
238 }
239 return s_usb.dcp_out.ready && (s_usb.dcp_out.rd >= s_usb.dcp_out.len);
240}
241
258void priv_bridge_pump_device(uc_engine* uc)
259{
260 if (!s_dcp_hold_pending) {
261 return;
262 }
263 const uint16_t dcpctr = s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_dcpctr)];
264 const uint16_t brdyenb = s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_brdyenb)];
265 const bool armed = ((dcpctr & (uint16_t)k_ra8_pid_mask) == (uint16_t)k_ra8_pid_buf) &&
266 ((brdyenb & (uint16_t)k_usb_dcp_pipe_bit) != 0U);
267 if (!armed) {
268 return; /* device has not armed its DCP for OUT yet; keep the bytes on the wire. */
269 }
270 /* The device is ready (its arm-time BCLR already ran on an empty bank): land
271 * the held control-OUT packet into the DCP and raise its BRDY so the device's
272 * receive ISR drains it, exactly as an OUT token the device finally ACKs. */
273 (void)memcpy(s_usb.dcp_out.data, s_dcp_hold, s_dcp_hold_len);
274 s_usb.dcp_out.len = s_dcp_hold_len;
275 s_usb.dcp_out.rd = 0U;
276 s_usb.dcp_out.ready = true;
277 s_dcp_hold_pending = false;
278 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
279 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)k_usb_dcp_pipe_bit);
282}
uint32_t board_usbhs_host_shadow_handoff(uint16_t *dst_words, uint32_t word_capacity)
Hand the host model's register shadow off and reset the host model.
void board_usb_bridge_mark_configured(uc_engine *uc)
Mark the device configured after a host SET_CONFIGURATION.
bool board_usb_bridge_bulk_out_consumed(uint8_t dev_pipe)
Whether the device has drained the last host bulk-OUT packet.
bool board_usb_bridge_dcp_out_consumed(void)
Whether the device has drained the last host DCP control-OUT packet.
void board_usb_bridge_ctrl_status(uc_engine *uc)
Drive the read-status stage of a host control-read (raise CTRT).
bool board_usb_roles_swapped(void)
Whether the self-loop window roles are swapped (Config B).
bool board_usb_bridge_dcp_in_ready(void)
Whether the device has queued a control-IN response on the DCP.
void board_usb_bridge_deliver_setup(uc_engine *uc, const uint8_t *setup)
Deliver an 8-byte host SETUP packet to the device (raise its CTRT).
uint16_t board_usb_bridge_bulk_in_take(uc_engine *uc, uint8_t dev_pipe, uint8_t *buf, uint16_t cap)
Consume a device bulk-IN packet from dev_pipe into buf.
void board_usb_dev_reg_write(uc_engine *uc, uint64_t off, unsigned size, uint64_t value)
Device-model register write by window byte offset (role routing).
void priv_bridge_pump_device(uc_engine *uc)
Re-assert a device level-triggered condition the polled host awaits.
void board_usb_roles_swap(uc_engine *uc)
Swap the self-loop window<->model bindings (enter Config B).
void board_usb_set_external_host(bool present)
Declare (or withdraw) a real-firmware USB host driving the device.
uint64_t board_usb_dev_reg_read(uc_engine *uc, uint64_t off, unsigned size)
Device-model register read by window byte offset (role routing).
void board_usb_bridge_bus_reset(uc_engine *uc)
Bridge a host bus reset onto the device (advance DVSQ to Default).
bool board_usb_dev_attached(void)
Whether the modelled USBFS device has asserted its D+ pull-up (DPRPU).
bool board_usb_bridge_bulk_in_ready(uint8_t dev_pipe)
Whether the device has queued a bulk-IN packet on dev_pipe.
void board_usb_bridge_dcp_out(uc_engine *uc, const uint8_t *data, uint16_t len)
Deliver a host control-write data-stage packet to the device DCP.
uint16_t board_usb_bridge_dcp_in_take(uint8_t *buf, uint16_t cap)
Consume the device's queued control-IN response into buf.
void board_usb_bridge_bulk_out(uc_engine *uc, uint8_t dev_pipe, const uint8_t *data, uint16_t len)
Deliver a host bulk-OUT packet to a device endpoint pipe.
bool board_usb_bridge_dev_took_ccpl(void)
Take (consume) the device's control-transfer completion (DCPCTR.CCPL).
Module-private state + interfaces shared by the board_usb TUs.
void priv_usb_reg_write(uint64_t off, uint32_t value, unsigned size)
USBFS window register write dispatch (dev).
void priv_usb_raise_irq(uc_engine *uc)
Pend the device's ICU interrupt via the registered raiser (dev).
uint8_t s_dcp_hold[k_usb_in_cap]
Held control-OUT bytes (core).
Definition board_usb.c:63
bool s_external_host
Bridge host owns the bus (core).
Definition board_usb.c:41
usb_state_t s_usb
Controller + FIFO state (core).
Definition board_usb.c:33
void priv_host_mark_configured(uc_engine *uc)
Advance the device state to CONFIGURED (vhost).
void priv_usb_intsts0_set(uint8_t bit)
Set one INTSTS0 event bit in the register shadow (dev).
bool priv_host_take_ccpl(void)
Consume the device's CCPL (control transfer complete) (vhost).
@ k_usb_pipe_count
DCP (0) + PIPE1..PIPE9.
@ k_usb_reg_words
16-bit register shadow word count.
void priv_host_apply_no_data(uc_engine *uc, const usb_setup_step_t *s)
Apply a no-data control write's device-state side effects (vhost).
bool priv_host_device_attached(void)
True when SYSCFG.DPRPU is set: the device attached its pull-up (vhost).
void priv_usb_log_line(const char *msg)
Append one already-formatted line to the enumeration-step log (core).
Definition board_usb.c:91
bool s_dcp_hold_pending
Held bytes await the arm (core).
Definition board_usb.c:65
@ k_usb_log_width
Bytes per enumeration-step log line.
@ k_usb_trace_dump_max
Control-IN payload bytes shown in –trace.
@ k_usb_dcp_pipe_bit
DCP (pipe 0) BRDYSTS/BRDYENB bit mask.
static uint32_t internal_usb_word(uint64_t off)
Word index into the 16-bit register shadow for a window offset.
uint16_t s_dcp_hold_len
Held byte count (core).
Definition board_usb.c:64
void priv_usb_log_count(const char *label, unsigned n)
Log a labelled count once (label + n) into the step log (core).
Definition board_usb.c:109
void priv_usb_detect_class(const uint8_t *d, uint16_t len)
Detect the device class from the enumerated config descriptor (vhost).
bool priv_host_dcp_pid_buf(void)
True when the device armed its DCP to BUF (ready for OUT) (vhost).
bool s_roles_swapped
Self-loop role polarity (core).
Definition board_usb.c:49
uint32_t priv_usb_reg_read(uint64_t off, unsigned size)
USBFS window register read dispatch (dev).
uint16_t g_dev_irq_event
Device ICU event number (core).
Definition board_usb.c:54
void priv_host_deliver_setup(uc_engine *uc, const usb_setup_step_t *s)
Latch a SETUP packet into USBREQ..USBLENG + raise CTRT (vhost).
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.
Event Link Controller (ELC) register layout for the Renesas RA8D2.
@ k_ra8_elc_event_usbhs_int_resume
USBHS combined int + resume.
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_pid_buf
BUF response (transmit/receive).
@ k_ra8_pid_mask
PID[1:0] field mask.
@ k_ra8_usb_off_brdyenb
BRDYENB : BRDY Enable.
@ k_ra8_usb_off_bempsts
BEMPSTS : BEMP Status.
@ k_ra8_usb_off_dcpctr
DCPCTR : DCP control.
@ k_ra8_usb_off_brdysts
BRDYSTS : BRDY Status.
@ k_ra8_ctsq_rdss
Control read status stage.
@ k_ra8_int0_bit_bemp
Buffer-empty interrupt.
@ k_ra8_int0_bit_dvst
Device-state transition.
@ k_ra8_int0_bit_brdy
Buffer-ready interrupt.
@ k_ra8_int0_bit_ctrt
Control-transfer-stage transition.
@ k_ra8_dvsq_default
Default (post bus reset).
One pipe's device-visible IN staging buffer (device -> host payload).
One pipe's host-visible OUT staging buffer (host -> device payload).
One SETUP step in the host enumeration script (USB 2.0 sec 9.4).
static volatile uint32_t s_trace[k_dcd_trace_entries]
JLink-readable ring of packed transfer/SETUP events.