ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_xfer.c
Go to the documentation of this file.
1
24
25#include <stdint.h>
26
27#include "ra8_attributes.h"
28#include "ra8_check.h"
29#include "ra8_err.h"
30#include "ra8_log.h"
31#include "ra8_usb.h"
32#include "ra8_usb_internal.h"
33#include "ra8_usb_regs.h"
34
35static const char* s_tag = "USB";
36
37/* =============================================================================
38 * Device-mode data path (pipe queue + DCP control data)
39 * =============================================================================
40 */
41
59ra8_usb_queue_in(ra8_usb_speed_t speed, uint8_t pipe_num, const uint8_t* data, uint16_t len)
60{
61 /* THROUGHPUT NOTE.
62 * queue_in writes ONE packet (up to MPS bytes) into the IN pipe's
63 * CFIFO bank and asserts BVAL. The hardware transmits the bank on
64 * the next IN token from the host. Bulk pipes run single-buffered
65 * (PIPECFG.DBLB clear), so the next packet cannot be staged until
66 * the current bank drains. Our measured ceiling (2.66 MB/s on HS)
67 * is bounded by the Linux cdc_acm read-URB completion path on the
68 * host, not by this code. See the matching note in
69 * port/usbx/src/ux_dcd_ra8_usb.c (auto-echo block) for the full
70 * chain-of-causality + what it would take to lift the ceiling. */
71 volatile r_usb_regs_t* reg = priv_pick(speed);
72 if (reg == nullptr) {
74 }
75 if ((pipe_num == 0U) || (pipe_num > k_ra8_usb_max_pipe_num)) {
77 }
78 if ((len > k_ra8_usb_pipe_max_packet) || ((data == nullptr) && (len != 0U))) {
80 }
81
82 priv_select_cfifo(reg, pipe_num, true);
83 const ra8_err_t ready = priv_wait_frdy(reg);
84 RA8_RETURN_ON_ERROR(ready, s_tag, "queue_in: FRDY timeout");
85
86 if (len > 0U) {
87 priv_fifo_write(reg, data, len);
88 }
89
90 /* HUM Ch 36.2.8 "CFIFOCTR : CFIFO Port Control Register", p 1979 */
92 priv_pipe_pid(reg, pipe_num, k_ra8_pid_buf);
93 return k_ra8_ok;
94}
95
123static ra8_err_t
124internal_dcp_in_payload(volatile r_usb_regs_t* reg, const uint8_t* data, uint16_t len)
125{
126 uint16_t remaining = len;
127 uint16_t offset = 0U;
128 bool pid_raised = false;
129 while (remaining > 0U) {
130 const uint16_t chunk =
131 (remaining > k_ra8_usb_dcp_max_packet) ? (uint16_t)k_ra8_usb_dcp_max_packet : remaining;
132 const ra8_err_t pushed = priv_dcp_push_chunk(reg, &data[offset], chunk);
133 RA8_RETURN_ON_ERROR(pushed, s_tag, "dcp_in_data: chunk push failed");
134 if (!pid_raised) {
135 /* HUM Ch 36.2.21 "DCPCTR : DCP Control Register", p 1999 */
137 pid_raised = true;
138 }
139 offset = (uint16_t)(offset + chunk);
140 remaining = (uint16_t)(remaining - chunk);
141 }
142 return k_ra8_ok;
143}
144
170{
171 const ra8_err_t ready = priv_wait_frdy(reg);
172 RA8_RETURN_ON_ERROR(ready, s_tag, "dcp_in_data: FRDY timeout (zlp)");
173 /* HUM Ch 36.2.8 "CFIFOCTR : CFIFO Port Control Register", p 1979 */
175 /* HUM Ch 36.2.21 "DCPCTR : DCP Control Register", p 1999 */
177 return k_ra8_ok;
178}
179
180/* Diagnostic probes for the DCP IN data push (read via JLink). */
181volatile uint32_t g_dcp_push_count = 0U;
182volatile uint16_t g_dcp_dcpctr_pre_push = 0U;
183volatile uint16_t g_dcp_dcpctr_post_push = 0U;
184volatile uint16_t g_dcp_cfifoctr_pre = 0U;
185volatile uint16_t g_dcp_cfifoctr_post = 0U;
186volatile uint16_t g_dcp_last_len = 0U;
187volatile uint8_t g_dcp_last_err = 0U; /* 0=ok, 1=frdy timeout, 2=null arg */
188
214ra8_err_t ra8_usb_dcp_in_data(ra8_usb_speed_t speed, const uint8_t* data, uint16_t len)
215{
216 volatile r_usb_regs_t* reg = priv_pick(speed);
217 if (reg == nullptr) {
218 g_dcp_last_err = 2U;
220 }
221 if ((data == nullptr) && (len != 0U)) {
222 g_dcp_last_err = 2U;
224 }
226 g_dcp_last_len = len;
228
229 /* Select DCP (CURPIPE = 0) on CFIFO in IN direction. Done once; the
230 * selection persists across chunks.
231 * HUM Ch 36.2.7 "CFIFOSEL : CFIFO Port Select Register", p 1976 */
232 priv_select_cfifo(reg, 0U, true);
234
235 /* Discard any stale, unconsumed buffer content before writing the
236 * fresh response. If a previous control-IN payload was never pulled
237 * by the host (a missed response window followed by a SETUP
238 * retransmit), the CFIFO is left non-empty, FRDY write-ready never
239 * re-asserts, and priv_wait_frdy below times out. Mirrors the
240 * hw_usb_set_bclr in FSP usb_pstd_ctrl_read.
241 * HUM Ch 36.2.8 "CFIFOCTR : CFIFO Port Control Register", p 1979 */
242 reg->CFIFOCTR = (uint16_t)k_ra8_fifoctr_bclr;
243
244 ra8_err_t err;
245 if (len == 0U) {
246 err = internal_dcp_in_zlp(reg);
247 } else {
248 err = internal_dcp_in_payload(reg, data, len);
249 }
252 g_dcp_last_err = (uint8_t)((err == k_ra8_ok) ? 0U : 1U);
253 return err;
254}
255
276static ra8_err_t
277internal_check_queue_out_args(uint8_t pipe_num, const uint8_t* out_buf, const uint16_t* inout_len)
278{
279 if ((out_buf == nullptr) || (inout_len == nullptr)) {
280 return k_ra8_err_null_ptr;
281 }
282 if ((pipe_num == 0U) || (pipe_num > k_ra8_usb_max_pipe_num)) {
284 }
285 if ((*inout_len == 0U) || (*inout_len > k_ra8_usb_pipe_max_packet)) {
287 }
288 return k_ra8_ok;
289}
290
309 uint8_t pipe_num,
310 uint8_t* out_buf,
311 uint16_t* inout_len,
312 bool rearm)
313{
314 volatile r_usb_regs_t* reg = priv_pick(speed);
315 if (reg == nullptr) {
317 }
318 const ra8_err_t arg_err = internal_check_queue_out_args(pipe_num, out_buf, inout_len);
319 if (arg_err != k_ra8_ok) {
320 return arg_err;
321 }
322
323 /* Fast path: BRDYSTS bit `pipe_num` is set by hardware when an OUT
324 * packet has landed in this pipe's FIFO buffer. If it's clear, no
325 * data is waiting -- return k_ra8_err_no_data WITHOUT entering the
326 * 10ms FRDY spin.
327 * HUM Ch 36.2.12 "BRDYSTS : BRDY Interrupt Status Register", p 1983. */
328 const uint16_t pipe_bit = (uint16_t)(1U << pipe_num);
329 if ((reg->BRDYSTS & pipe_bit) == 0U) {
330 *inout_len = 0U;
331 return k_ra8_err_no_data;
332 }
333
334 /* W0C BRDYSTS for this pipe BEFORE draining (FSP pattern, mirrors
335 * `r_usb_pinthandler_usbip0.c` order). Critical for PIPECFG.DBLB:
336 * if bank B raises a fresh BRDY edge while we are still draining
337 * bank A, the BRDYSTS bit will be re-set by hardware and survive a
338 * future read; clearing AFTER the drain would wipe that edge and
339 * lose every other packet. */
340 reg->BRDYSTS = (uint16_t)(~pipe_bit);
341
342 priv_select_cfifo(reg, pipe_num, false);
343 const ra8_err_t ready = priv_wait_frdy(reg);
344 RA8_RETURN_ON_ERROR(ready, s_tag, "queue_out: FRDY timeout");
345
346 /* HUM Ch 36.2.8 "CFIFOCTR : CFIFO Port Control Register", p 1979 */
347 const uint16_t available = (uint16_t)(reg->CFIFOCTR & k_ra8_fifoctr_dtln);
348 if (available == 0U) {
349 /* Zero-length packet (ZLP). FSP releases the bank explicitly via
350 * BCLR in this case (`r_usb_plibusbip.c` usb_pstd_read_data line
351 * 700: "0 length packet -> Clear BVAL"). For a non-zero drain the
352 * FIFO empties naturally as we read DTLN bytes; BCLR is NOT used
353 * and would confuse the bank pointer in DBLB mode. */
355 *inout_len = 0U;
356 return k_ra8_err_no_data;
357 }
358 const uint16_t take = (available < *inout_len) ? available : *inout_len;
359 priv_fifo_read(reg, out_buf, take);
360 *inout_len = take;
361 /* No BCLR for non-zero drain (FSP semantics). The FIFO read above
362 * drained `take` bytes; the remainder (if take < available) is lost
363 * by design -- our caller passed a buffer large enough for one MPS
364 * packet, which is what one bank holds. */
365 if (rearm) {
366 /* Re-arm PID=BUF; rearm == false callers own the arm/park decision. */
367 priv_pipe_pid(reg, pipe_num, k_ra8_pid_buf);
368 }
369 return k_ra8_ok;
370}
371
399{
400 volatile r_usb_regs_t* reg = priv_pick(speed);
401 if (reg == nullptr) {
403 }
404 if ((pipe_num == 0U) || (pipe_num > k_ra8_usb_max_pipe_num)) {
406 }
407 /* HUM Ch 36.2.13 "NRDYSTS : NRDY Interrupt Status Register", p 1992.
408 * W0C semantics: write 0 to clear the target bit, 1 to preserve the
409 * rest. */
410 const uint16_t pipe_bit = (uint16_t)(1U << pipe_num);
411 reg->NRDYSTS = (uint16_t)(~pipe_bit);
412 /* HUM Ch 36.2.27 "PIPEnCTR : PIPE n Control Register", p 2005. Force
413 * PID=BUF so the next host OUT token is ACKed. */
414 priv_pipe_pid(reg, pipe_num, k_ra8_pid_buf);
415 return k_ra8_ok;
416}
417
442{
443 volatile r_usb_regs_t* reg = priv_pick(speed);
444 if (reg == nullptr) {
446 }
447 if ((pipe_num == 0U) || (pipe_num > k_ra8_usb_max_pipe_num)) {
449 }
450 /* HUM Ch 36.2.27 "PIPEnCTR : PIPE n Control Register" p 2005 */
451 priv_pipe_pid(reg, pipe_num, k_ra8_pid_nak);
452 return k_ra8_ok;
453}
454
455/* =============================================================================
456 * Control transfers
457 * =============================================================================
458 */
459
480{
481 RA8_CHECK_NULL_PTR(out_setup, s_tag, "read_setup_if_valid: out_setup");
482 volatile r_usb_regs_t* reg = priv_pick(speed);
483 if (reg == nullptr) {
485 }
486 /* HUM Ch 36.2.14 "INTSTS0 : Interrupt Status Register 0", p 1986 */
487 if ((reg->INTSTS0 & k_ra8_intsts0_mask_valid) == 0U) {
488 return k_ra8_err_no_data;
489 }
490
491 /* HUM Ch 36.2.17 "USBREQ : USB Request Type Register", p 1995 */
492 const uint16_t req = reg->USBREQ;
493 out_setup->bm_request_type = (uint8_t)(req & k_ra8_usb_byte_mask);
494 out_setup->b_request = (uint8_t)((req >> k_ra8_usb_byte_bits) & k_ra8_usb_byte_mask);
495 out_setup->w_value = reg->USBVAL;
496 out_setup->w_index = reg->USBINDX;
497 out_setup->w_length = reg->USBLENG;
498
499 /* Clear VALID by writing zero to the bit (W0C semantics). */
500 reg->INTSTS0 = (uint16_t)(reg->INTSTS0 & (uint16_t)~k_ra8_intsts0_mask_valid);
501 return k_ra8_ok;
502}
503
530{
531 RA8_CHECK_NULL_PTR(out_setup, s_tag, "read_setup_unconditional: out_setup");
532 volatile r_usb_regs_t* reg = priv_pick(speed);
533 if (reg == nullptr) {
535 }
536
537 /* HUM Ch 36.2.17 / 37.2.21 "USBREQ : USB Request Type Register",
538 * p 1989 / 2087. The SETUP latch survives VALID being auto-cleared
539 * by the SIE; only a fresh SETUP token can overwrite it. */
540 const uint16_t req = reg->USBREQ;
541 out_setup->bm_request_type = (uint8_t)(req & k_ra8_usb_byte_mask);
542 out_setup->b_request = (uint8_t)((req >> k_ra8_usb_byte_bits) & k_ra8_usb_byte_mask);
543 out_setup->w_value = reg->USBVAL;
544 out_setup->w_index = reg->USBINDX;
545 out_setup->w_length = reg->USBLENG;
546
547 /* HUM Ch 36.2.14 INTSTS0 p 1985: defensively W0C-clear VALID in case
548 * the SIE has re-asserted it (no-op when already 0). */
549 reg->INTSTS0 = (uint16_t)(reg->INTSTS0 & (uint16_t)~k_ra8_intsts0_mask_valid);
550 return k_ra8_ok;
551}
552
568{
569 volatile r_usb_regs_t* reg = priv_pick(speed);
570 if (reg == nullptr) {
572 }
573 if (!accept) {
574 /* HUM Ch 36.2.21 "DCPCTR : DCP Control Register", p 1999 */
576 return k_ra8_ok;
577 }
579 /* HUM Ch 36.2.21 "DCPCTR : DCP Control Register", p 1999 */
580 priv_rmw16(&reg->DCPCTR, (uint16_t)(1U << k_ra8_dcpctr_bit_ccpl), 0U);
581 return k_ra8_ok;
582}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Lightweight Logging Interface for ra8-firmware.
void priv_fifo_write(volatile r_usb_regs_t *reg, const uint8_t *data, uint16_t len)
Push a byte buffer into the CFIFO data port.
Definition ra8_usb.c:592
void priv_dcp_pid(volatile r_usb_regs_t *reg, ra8_usb_pid_t pid)
Set DCPCTR PID field to a specific value while preserving the rest of the register.
Definition ra8_usb.c:288
void priv_rmw16(volatile uint16_t *reg, uint16_t set_mask, uint16_t clr_mask)
Apply a generic read-modify-write to a 16-bit register.
Definition ra8_usb.c:154
void priv_select_cfifo(volatile r_usb_regs_t *reg, uint16_t pipe_num, bool is_in_dir)
Set CFIFOSEL.MBW + CURPIPE + ISEL for the given pipe / direction.
Definition ra8_usb.c:199
ra8_err_t priv_wait_frdy(volatile r_usb_regs_t *reg)
Spin until CFIFOCTR.FRDY asserts or a deadline elapses.
Definition ra8_usb.c:251
volatile r_usb_regs_t * priv_pick(ra8_usb_speed_t speed)
Resolve the per-speed register pointer.
Definition ra8_usb.c:110
ra8_err_t priv_dcp_push_chunk(volatile r_usb_regs_t *reg, const uint8_t *p, uint16_t n)
Push a single DCP IN chunk: wait for FRDY, write FIFO, pulse BVAL.
Definition ra8_usb.c:770
void priv_pipe_pid(volatile r_usb_regs_t *reg, uint8_t pipe_num, ra8_usb_pid_t pid)
Set PIPECTR[idx] PID field.
Definition ra8_usb.c:308
void priv_fifo_read(volatile r_usb_regs_t *reg, uint8_t *data, uint16_t len)
Drain the CFIFO data port into a buffer.
Definition ra8_usb.c:712
Native USB controller driver public API (device + host modes).
ra8_usb_speed_t
Selects which controller instance the call targets.
Cross-TU surface for the ra8_usb driver split.
@ k_ra8_usb_byte_mask
Low byte of a 16-bit FIFO word.
@ k_ra8_usb_byte_bits
Bits per byte (shift constant).
@ k_ra8_usb_pipe_max_packet
Max packet ceiling for pipes.
@ k_ra8_usb_max_pipe_num
PIPE1..PIPE9 + DCP at 0.
@ k_ra8_usb_dcp_max_packet
EP0 default packet size.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_pid_buf
BUF response (transmit/receive).
@ k_ra8_pid_nak
NAK response.
@ k_ra8_pid_stall
STALL response.
@ k_ra8_fifoctr_bclr
Buffer clear.
@ k_ra8_fifoctr_dtln
Data length (write-bytes-rem).
@ k_ra8_fifoctr_bval
Buffer valid.
@ k_ra8_dcpctr_bit_ccpl
Control transfer end enable.
@ k_ra8_intsts0_mask_valid
SETUP packet detect flag.
ra8_err_t ra8_usb_park_out_pipe(ra8_usb_speed_t speed, uint8_t pipe_num)
Implementation of ra8_usb_park_out_pipe().
volatile uint16_t g_dcp_cfifoctr_post
ra8_err_t ra8_usb_read_setup_if_valid(ra8_usb_speed_t speed, ra8_usb_setup_t *out_setup)
Implementation of ra8_usb_read_setup_if_valid().
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)
Implementation of ra8_usb_queue_out().
volatile uint32_t g_dcp_push_count
volatile uint16_t g_dcp_cfifoctr_pre
volatile uint8_t g_dcp_last_err
ra8_err_t ra8_usb_control_response(ra8_usb_speed_t speed, bool accept)
Implementation of ra8_usb_control_response().
static ra8_err_t internal_dcp_in_zlp(volatile r_usb_regs_t *reg)
Send a zero-length data stage on the DCP IN endpoint.
static ra8_err_t internal_dcp_in_payload(volatile r_usb_regs_t *reg, const uint8_t *data, uint16_t len)
Send the EP0 IN data-stage payload as one or more DCP chunks.
ra8_err_t ra8_usb_rearm_out_pipe(ra8_usb_speed_t speed, uint8_t pipe_num)
Implementation of ra8_usb_rearm_out_pipe().
volatile uint16_t g_dcp_dcpctr_post_push
volatile uint16_t g_dcp_dcpctr_pre_push
volatile uint16_t g_dcp_last_len
static ra8_err_t internal_check_queue_out_args(uint8_t pipe_num, const uint8_t *out_buf, const uint16_t *inout_len)
Argument validation helper for ra8_usb_queue_out.
ra8_err_t ra8_usb_read_setup_unconditional(ra8_usb_speed_t speed, ra8_usb_setup_t *out_setup)
Implementation of ra8_usb_read_setup_unconditional (see header).
ra8_err_t ra8_usb_queue_in(ra8_usb_speed_t speed, uint8_t pipe_num, const uint8_t *data, uint16_t len)
Implementation of ra8_usb_queue_in().
ra8_err_t ra8_usb_dcp_in_data(ra8_usb_speed_t speed, const uint8_t *data, uint16_t len)
Implementation of ra8_usb_dcp_in_data().
USB FS / HS register window (device-mode subset).
volatile uint16_t USBINDX
+0x058 USB Request wIndex.
volatile uint16_t DCPCTR
+0x060 DCP Control.
volatile uint16_t USBVAL
+0x056 USB Request wValue.
volatile uint16_t CFIFOCTR
+0x022 Control FIFO control.
volatile uint16_t INTSTS0
+0x040 Interrupt Status 0.
volatile uint16_t USBLENG
+0x05A USB Request wLength.
volatile uint16_t NRDYSTS
+0x048 NRDY Interrupt Status.
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.