ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_usb_loop.c
Go to the documentation of this file.
1
14
15#include <stdio.h>
16#include <string.h>
17
18#include "board_usb_internal.h"
19#include "ra8_elc_regs.h"
20#include "ra8_usb_regs.h"
21
22/* Loop-cable (self-loop) state -- see the board_usb_loop_* block below. */
24static bool s_loop_pending_cfg;
25static uint16_t s_loop_dcp_rd;
27uint32_t s_loop_setups;
30
31/* =============================================================================
32 * Loop-cable transport -- the USBHS HOST-mode model (board_usb_host.c) drives
33 * this device through these calls when the firmware itself is the bus host
34 * (self-loop bench: USBHS J7 host cabled to USBFS J11 device). Each helper
35 * mirrors the corresponding built-in virtual-host step so the device-side
36 * firmware sees identical register semantics either way.
37 * =============================================================================
38 */
39
41typedef enum : uint16_t {
45
55RA8_INTERNAL static void internal_loop_reset_pipe(uint8_t pipe)
56{
57 s_usb.pipe_in[pipe].len = 0U;
58 s_usb.pipe_in[pipe].valid = false;
59 s_usb.pipe_out[pipe].len = 0U;
60 s_usb.pipe_out[pipe].rd = 0U;
61 s_usb.pipe_out[pipe].ready = false;
62 s_loop_pipe_rd[pipe] = 0U;
63}
64
74{
75 s_usb.dcp_in.len = 0U;
76 s_usb.dcp_in.valid = false;
77 s_usb.dcp_out.len = 0U;
78 s_usb.dcp_out.rd = 0U;
79 s_usb.dcp_out.ready = false;
80 s_loop_dcp_rd = 0U;
81}
82
84{
85 if (s_loop_latched) {
86 return;
87 }
88 s_loop_latched = true;
89 priv_usb_log_line("self-loop latched: firmware host owns the bus (virtual host parked)");
90}
91
93{
95}
96
97void board_usb_loop_bus_reset(uc_engine* uc)
98{
100 for (uint32_t p = 0U; p < (uint32_t)k_usb_pipe_count; p++) {
101 internal_loop_reset_pipe((uint8_t)p);
102 }
103 s_usb.setup_valid = false;
104 s_usb.ctsq = (uint16_t)k_ra8_ctsq_idle;
105 s_usb.dvsq = (uint16_t)k_ra8_dvsq_default;
107 priv_usb_log_line("loop: bus reset -> DVSQ Default");
109}
110
111bool board_usb_loop_setup(uc_engine* uc, uint16_t req, uint16_t val, uint16_t indx, uint16_t leng)
112{
113 const uint8_t bm = (uint8_t)(req & (uint16_t)k_loop_req_bm_mask);
114 const uint8_t breq = (uint8_t)(req >> k_usb_byte_bits);
115 /* A new SETUP supersedes whatever control transfer preceded it: drop stale
116 * DCP staging and consume a stale CCPL (the fw host is strictly serial, so
117 * any completion still latched here belongs to the finished transfer). */
119 (void)priv_host_take_ccpl();
120 s_loop_pending_cfg = false;
121 s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_usbreq)] = req;
122 s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_usbval)] = val;
123 s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_usbindx)] = indx;
124 s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_usbleng)] = leng;
126 char line[k_usb_log_width];
127 (void)snprintf(line,
128 sizeof(line),
129 "loop: SETUP bm=0x%02X bReq=0x%02X wVal=0x%04X wLen=%u",
130 (unsigned)bm,
131 (unsigned)breq,
132 (unsigned)val,
133 (unsigned)leng);
134 priv_usb_log_line(line);
135 if ((bm == (uint8_t)k_usb_dir_host_to_device) && (breq == (uint8_t)k_usb_req_set_address)) {
136 /* The device SIE latches USBADDR and runs the whole status stage itself
137 * (mirrors ::priv_host_apply_no_data): the host needs no device CCPL. */
139 (uint16_t)(val & (uint16_t)k_ra8_usbaddr_addr_mask);
140 s_usb.dvsq = (uint16_t)k_ra8_dvsq_address;
143 return true;
144 }
145 if ((bm == (uint8_t)k_usb_dir_host_to_device) && (breq == (uint8_t)k_usb_req_set_config)) {
146 s_loop_pending_cfg = true; /* configured-state advance rides the CCPL. */
147 }
148 /* For an OUT-data request the payload arrives via board_usb_loop_ctrl_out. */
149 s_usb.setup_valid = true;
150 if ((bm & (uint8_t)k_usb_dir_device_to_host) != 0U) {
151 s_usb.ctsq = (uint16_t)k_ra8_ctsq_rdds;
152 } else if (leng != 0U) {
153 s_usb.ctsq = (uint16_t)k_ra8_ctsq_wrds;
154 } else {
155 s_usb.ctsq = (uint16_t)k_ra8_ctsq_wrnd;
156 }
159 return false;
160}
161
162bool board_usb_loop_take_ccpl(uc_engine* uc)
163{
164 if (!priv_host_take_ccpl()) {
165 return false;
166 }
167 if (s_loop_pending_cfg) {
168 s_loop_pending_cfg = false;
170 }
171 return true;
172}
173
175{
176 if (!s_usb.dcp_in.valid) {
177 return 0U;
178 }
179 return (uint16_t)(s_usb.dcp_in.len - s_loop_dcp_rd);
180}
181
182uint16_t board_usb_loop_ctrl_in_read(uint8_t* dst, uint16_t cap)
183{
184 const uint16_t avail = board_usb_loop_ctrl_in_avail();
185 const uint16_t n = (avail < cap) ? avail : cap;
186 for (uint16_t i = 0U; i < n; i++) {
187 dst[i] = s_usb.dcp_in.data[s_loop_dcp_rd + i];
188 }
189 s_loop_dcp_rd = (uint16_t)(s_loop_dcp_rd + n);
190 if ((n > 0U) && (s_loop_dcp_rd >= s_usb.dcp_in.len)) {
191 priv_usb_detect_class(s_usb.dcp_in.data, s_usb.dcp_in.len);
192 s_usb.dcp_in.len = 0U;
193 s_usb.dcp_in.valid = false;
194 s_loop_dcp_rd = 0U;
195 }
196 return n;
197}
198
200{
201 s_usb.dcp_in.len = 0U;
202 s_usb.dcp_in.valid = false;
203 s_loop_dcp_rd = 0U;
204}
205
206void board_usb_loop_ctrl_out(uc_engine* uc, const uint8_t* data, uint16_t len)
207{
208 uint16_t n = len;
209 if (n > (uint16_t)sizeof(s_usb.dcp_out.data)) {
210 n = (uint16_t)sizeof(s_usb.dcp_out.data);
211 }
212 (void)memcpy(s_usb.dcp_out.data, data, n);
213 s_usb.dcp_out.len = n;
214 s_usb.dcp_out.rd = 0U;
215 s_usb.dcp_out.ready = true;
216 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
217 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)k_ra8_usb_dcp_brdy_bit);
220}
221
223{
224 s_usb.ctsq = (uint16_t)k_ra8_ctsq_rdss;
225 s_usb.setup_valid = false;
228}
229
230void board_usb_loop_bulk_out(uc_engine* uc, uint8_t ep, const uint8_t* data, uint16_t len)
231{
232 usb_out_buf_t* b = &s_usb.pipe_out[ep % k_usb_pipe_count];
233 uint16_t n = len;
234 if (n > (uint16_t)sizeof(b->data)) {
235 n = (uint16_t)sizeof(b->data);
236 }
237 (void)memcpy(b->data, data, n);
238 b->len = n;
239 b->rd = 0U;
240 b->ready = true;
241 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
242 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << (ep % k_usb_pipe_count)));
246}
247
249{
250 const usb_in_buf_t* b = &s_usb.pipe_in[ep % k_usb_pipe_count];
251 if (!b->valid) {
252 return 0U;
253 }
254 return (uint16_t)(b->len - s_loop_pipe_rd[ep % k_usb_pipe_count]);
255}
256
257uint16_t board_usb_loop_bulk_in_read(uc_engine* uc, uint8_t ep, uint8_t* dst, uint16_t cap)
258{
259 const uint8_t pipe = (uint8_t)(ep % k_usb_pipe_count);
260 usb_in_buf_t* b = &s_usb.pipe_in[pipe];
261 const uint16_t avail = board_usb_loop_bulk_in_avail(ep);
262 const uint16_t n = (avail < cap) ? avail : cap;
263 for (uint16_t i = 0U; i < n; i++) {
264 dst[i] = b->data[s_loop_pipe_rd[pipe] + i];
265 }
266 s_loop_pipe_rd[pipe] = (uint16_t)(s_loop_pipe_rd[pipe] + n);
267 if ((n > 0U) && (s_loop_pipe_rd[pipe] >= b->len)) {
268 b->len = 0U;
269 b->valid = false;
270 s_loop_pipe_rd[pipe] = 0U;
272 /* Transmit buffer drained: raise the device's BEMP for this pipe so its
273 * firmware can queue the next packet (mirrors ::priv_host_echo_read_in). */
274 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_bempsts);
275 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << pipe));
278 }
279 return n;
280}
281
283{
284 const uint8_t pipe = (uint8_t)(ep % k_usb_pipe_count);
285 s_usb.pipe_in[pipe].len = 0U;
286 s_usb.pipe_in[pipe].valid = false;
287 s_loop_pipe_rd[pipe] = 0U;
288}
Module-private state + interfaces shared by the board_usb TUs.
@ k_usb_dir_host_to_device
OUT: host -> device (control write).
@ k_usb_dir_device_to_host
IN: device -> host (control read).
void priv_usb_raise_irq(uc_engine *uc)
Pend the device's ICU interrupt via the registered raiser (dev).
uint32_t s_loop_bulk_in_pkts
Bulk-IN packets (loop).
uint32_t s_loop_setups
SETUPs from the fw host (loop).
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).
void priv_host_mark_configured(uc_engine *uc)
Advance the device state to CONFIGURED (vhost).
@ k_usb_req_set_config
SET_CONFIGURATION.
@ k_usb_req_set_address
SET_ADDRESS.
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.
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
@ k_usb_log_width
Bytes per enumeration-step log line.
@ k_usb_byte_bits
Bits per byte.
static uint32_t internal_usb_word(uint64_t off)
Word index into the 16-bit register shadow for a window offset.
void priv_usb_detect_class(const uint8_t *d, uint16_t len)
Detect the device class from the enumerated config descriptor (vhost).
bool s_loop_latched
Firmware host owns the bus (loop).
void board_usb_loop_bulk_in_flush(uint8_t ep)
Drop whatever remains of a device pipe's bulk-IN staging.
static void internal_loop_reset_dcp(void)
Reset the DCP staging (both directions) plus the host read cursor.
static uint16_t s_loop_pipe_rd[k_usb_pipe_count]
Host cursors into pipe_in.
static bool s_loop_pending_cfg
SET_CONFIGURATION in flight.
bool board_usb_loop_take_ccpl(uc_engine *uc)
Poll the device's control-transfer completion (DCPCTR.CCPL).
static uint16_t s_loop_dcp_rd
Host cursor into dcp_in.
void board_usb_loop_ctrl_out(uc_engine *uc, const uint8_t *data, uint16_t len)
Deliver a control-OUT data-stage packet to the device's DCP.
bool board_usb_loop_attached(void)
Whether the modelled USBFS device presents its D+ pull-up.
void board_usb_loop_bulk_out(uc_engine *uc, uint8_t ep, const uint8_t *data, uint16_t len)
Deliver one bulk-OUT packet from the firmware host to a device pipe.
uint16_t board_usb_loop_ctrl_in_avail(void)
Control-IN bytes the device has queued and the host not yet drained.
uint16_t board_usb_loop_ctrl_in_read(uint8_t *dst, uint16_t cap)
Drain up to cap control-IN bytes from the device's DCP staging.
void board_usb_loop_latch(void)
Latch the self-loop: the firmware brought a controller up as HOST.
uint16_t board_usb_loop_bulk_in_read(uc_engine *uc, uint8_t ep, uint8_t *dst, uint16_t cap)
Drain up to cap bulk-IN bytes from a device pipe's staging.
static void internal_loop_reset_pipe(uint8_t pipe)
Reset one pipe's IN/OUT staging plus the host-side read cursor.
void board_usb_loop_status_out_zlp(uc_engine *uc)
Run the control-read status stage against the device (host OUT ZLP).
bool board_usb_loop_setup(uc_engine *uc, uint16_t req, uint16_t val, uint16_t indx, uint16_t leng)
Deliver one SETUP packet from the firmware host to the device.
void board_usb_loop_bus_reset(uc_engine *uc)
Deliver a bus reset to the modelled USBFS device (host released RST).
void board_usb_loop_ctrl_in_flush(void)
Drop whatever remains of the device's control-IN staging.
loop_req_mask_t
SETUP-word decode helpers + the DCP status bit for the loop path.
@ k_loop_req_bm_mask
bmRequestType byte of USBREQ.
uint16_t board_usb_loop_bulk_in_avail(uint8_t ep)
Bulk-IN bytes a device pipe has queued and the host not yet drained.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Event Link Controller (ELC) register layout for the Renesas RA8D2.
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_usb_off_usbleng
USBLENG : USB request wLength.
@ k_ra8_usb_off_usbindx
USBINDX : USB request wIndex.
@ k_ra8_usb_off_usbaddr
USBADDR : USB address.
@ k_ra8_usb_off_usbval
USBVAL : USB request wValue.
@ k_ra8_usb_off_bempsts
BEMPSTS : BEMP Status.
@ k_ra8_usb_off_usbreq
USBREQ : USB request type.
@ k_ra8_usb_off_brdysts
BRDYSTS : BRDY Status.
@ k_ra8_usbaddr_addr_mask
7-bit USB address [6:0].
@ k_ra8_ctsq_rdss
Control read status stage.
@ k_ra8_ctsq_rdds
Control read data stage.
@ k_ra8_ctsq_idle
Idle / setup stage end.
@ k_ra8_ctsq_wrds
Control write data stage.
@ k_ra8_ctsq_wrnd
Control write nodata status.
@ 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_address
Address assigned.
@ 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).
@ k_ra8_usb_dcp_brdy_bit
DCP (pipe 0) BRDYSTS / BRDYENB bit.