ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ux_dcd_ra8_usb_xfer.c File Reference

USBX device-controller-driver bridge to ra8_usb – transfer dispatch. More...

#include <stdint.h>
#include <string.h>
#include "ra8_check.h"
#include "ra8_elc_regs.h"
#include "ra8_isr.h"
#include "ra8_log.h"
#include "ra8_usb_regs.h"
#include "tx_api.h"
#include "ux_api.h"
#include "ux_dcd_ra8_usb.h"
#include "ux_dcd_ra8_usb_internal.h"
#include "ux_device_stack.h"
#include "ux_system.h"
#include "ux_utility.h"
Include dependency graph for ux_dcd_ra8_usb_xfer.c:

Go to the source code of this file.

Macros

#define UX_SOURCE_CODE

Functions

uint8_t priv_ep_to_pipe (uint8_t ep_addr)
 Map a USB EP number (1..9) into our PIPE table index.
static unsigned int internal_ep0_transfer (struct UX_SLAVE_TRANSFER_STRUCT *tr)
 Drive an EP0 / DCP control transfer (data or status stage).
static unsigned int internal_wait_completion (struct UX_SLAVE_TRANSFER_STRUCT *tr, uint8_t pipe)
 Block on the USBX transfer semaphore until the IRQ posts completion.
static unsigned int internal_submit_consume_orphan (struct UX_SLAVE_TRANSFER_STRUCT *tr, uint8_t pipe)
 Deliver a held orphan OUT packet to a freshly submitted transfer.
static uint32_t internal_fifo_lock (void)
 Enter a CFIFO critical section: mask interrupts.
static void internal_fifo_unlock (uint32_t primask)
 Leave a CFIFO critical section: restore the interrupt mask.
static unsigned int internal_submit_in_pipe (struct UX_SLAVE_TRANSFER_STRUCT *tr, uint8_t pipe)
 Stage the first chunk of an IN transfer (plus single-packet ZLP).
static unsigned int internal_submit_pipe (struct UX_SLAVE_TRANSFER_STRUCT *tr, uint8_t pipe, uint8_t ep_addr)
 Submit the stashed IN/OUT transfer on a non-EP0 pipe.
unsigned int priv_transfer_request (struct UX_SLAVE_TRANSFER_STRUCT *tr)
 USBX UX_DCD_TRANSFER_REQUEST dispatcher entry point.

Variables

uint8_t g_orphan_buf [k_ra8_usb_orphan_bytes]
 One held OUT packet.
uint16_t g_orphan_len = 0U
 Held byte count; 0 = empty.
uint8_t g_orphan_pipe = 0U
 Pipe the held packet is on.

Detailed Description

USBX device-controller-driver bridge to ra8_usb – transfer dispatch.

Tag
[Ring 5 / PORT] {World: S}

Task-context transfer submission: EP0 control transfers, bulk/interrupt IN/OUT staging, the orphan-OUT consume path, and the CFIFO critical section helpers.

Split out of ux_dcd_ra8_usb.c to keep each translation unit under the maintainability line cap; the cross-translation-unit contract lives in ux_dcd_ra8_usb_internal.h.

Since
0.1.0

Definition in file ux_dcd_ra8_usb_xfer.c.

Macro Definition Documentation

◆ UX_SOURCE_CODE

#define UX_SOURCE_CODE

Definition at line 22 of file ux_dcd_ra8_usb_xfer.c.

Function Documentation

◆ internal_ep0_transfer()

unsigned int internal_ep0_transfer ( struct UX_SLAVE_TRANSFER_STRUCT * tr)
static

Drive an EP0 / DCP control transfer (data or status stage).

Split the IN-data and zero-length status paths apart so the outer dispatcher's MC/DC inventory stays small.

  • With payload (in_transfer_length != 0): push bytes via ra8_usb_dcp_in_data (PID=BUF, no CCPL). CCPL is asserted later on the CTSQ status-stage edge by priv_handle_ctrt.
  • Zero-length (SET_ADDRESS, SET_CONFIGURATION...): call ra8_usb_control_response(true) which sets PID=BUF and pulses CCPL, completing the status stage.
Parameters
[in,out]trUSBX EP0 transfer request.
Returns
UX_SUCCESS or UX_TRANSFER_ERROR.
Return values
UX_SUCCESSBytes / CCPL accepted by ra8_usb.
UX_TRANSFER_ERRORUnderlying DCP call failed.
Precondition
tr non-null and bound to the EP0 endpoint.
Bridge is past ux_dcd_ra8_usb_initialize.
Postcondition
actual_length reflects bytes pushed on the data path.
No wire-side state mutated on error.
Note
Runs on the USBX device task context.
Since
0.1.0

Definition at line 105 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, k_ra8_ok, RA8_INTERNAL, ra8_usb_control_response(), and ra8_usb_dcp_in_data().

Referenced by priv_transfer_request().

◆ internal_fifo_lock()

uint32_t internal_fifo_lock ( void )
static

Enter a CFIFO critical section: mask interrupts.

The CFIFO port (CFIFOSEL.CURPIPE + the FIFO window) is a single shared resource. The class thread stages the first chunk of every bulk-IN transfer through it while the USB ISR walk drains bulk-OUT packets and stages IN continuations through the same port. An ISR preempting the thread mid-FIFO-write retargets CURPIPE under it and the remaining bytes land in the wrong pipe – framing stays intact, the payload is garbage (observed against macOS as FAT sectors reading back zeroed during sustained sequential reads). Masking interrupts for the short stage (FRDY wait on an empty bank plus a <= MPS FIFO write, microseconds) closes the race.

Returns
The PRIMASK value to pass to internal_fifo_unlock.
Return values
0Interrupts were enabled on entry.
Precondition
Thread (non-ISR) context.
The section being protected is bounded (no unbounded spins).
Postcondition
Interrupts are masked.
No other state changes.
Note
Pair every call with internal_fifo_unlock.
Since
0.1.0

Definition at line 289 of file ux_dcd_ra8_usb_xfer.c.

References RA8_INTERNAL.

Referenced by internal_submit_in_pipe().

◆ internal_fifo_unlock()

void internal_fifo_unlock ( uint32_t primask)
static

Leave a CFIFO critical section: restore the interrupt mask.

Re-enables interrupts only when they were enabled at the matching internal_fifo_lock, so nesting inside an already-masked region stays masked.

Parameters
[in]primaskPRIMASK snapshot from internal_fifo_lock.
Precondition
primask came from the matching internal_fifo_lock call.
The protected FIFO operation has completed.
Postcondition
Interrupts are enabled again when they were on entry.
No other state changes.
Note
Pair of internal_fifo_lock.
Since
0.1.0

Definition at line 314 of file ux_dcd_ra8_usb_xfer.c.

References RA8_INTERNAL.

Referenced by internal_submit_in_pipe().

◆ internal_submit_consume_orphan()

unsigned int internal_submit_consume_orphan ( struct UX_SLAVE_TRANSFER_STRUCT * tr,
uint8_t pipe )
static

Deliver a held orphan OUT packet to a freshly submitted transfer.

Called by internal_submit_pipe when g_orphan_len is non-zero for the target pipe. Copies the held packet (captured by internal_irq_drain_orphan_out) into the transfer buffer as packet one. If the held packet fills the request, or is itself a short (< MPS) packet, it ends the transfer – completed synchronously here, with the semaphore posted so the task-side wait returns at once. Otherwise the pipe is armed to BUF and internal_irq_complete_out streams the remainder from the held byte offset.

Parameters
[in,out]trUSBX transfer request, already stashed on the pipe.
[in]pipePipe index (1..max_pipes-1); equals g_orphan_pipe.
Returns
USBX result code.
Return values
UX_SUCCESSHeld packet delivered (transfer complete or armed).
UX_TRANSFER_ERRORRe-arm of the OUT pipe failed.
Precondition
g_orphan_len != 0 and g_orphan_pipe == pipe.
Task context; the caller stashed tr at g_dcd.pipes[pipe].xfer.
Postcondition
g_orphan_len == 0 – the held packet was consumed.
On a completed transfer the semaphore is posted and the stash cleared.
Note
Task-context only; not ISR-safe.
Since
0.1.0

Definition at line 234 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, g_orphan_buf, g_orphan_len, k_dcd_trace_kind_ouse, k_ra8_ok, memcpy(), priv_trace_event(), RA8_INTERNAL, and ra8_usb_rearm_out_pipe().

Referenced by internal_submit_pipe().

◆ internal_submit_in_pipe()

unsigned int internal_submit_in_pipe ( struct UX_SLAVE_TRANSFER_STRUCT * tr,
uint8_t pipe )
static

Stage the first chunk of an IN transfer (plus single-packet ZLP).

IN endpoint path of internal_submit_pipe: USBX hands the DCD the whole transfer, but ra8_usb_queue_in moves at most one max-packet bank – so a transfer longer than MPS is streamed: this pushes the first packet (atomically against the ISR walk's shared CFIFO use, see internal_fifo_lock) and internal_irq_complete_in pushes each subsequent packet as the host drains the previous one (BEMP). A single-packet MPS-exact transfer gets its trailing ZLP staged into the second bank.

Parameters
[in,out]trUSBX transfer request, already stashed.
[in]pipePipe index the transfer is bound to.
Returns
UX_SUCCESS or UX_TRANSFER_ERROR.
Return values
UX_SUCCESSFirst chunk (and any ZLP) queued.
UX_TRANSFER_ERRORBridge rejected the queue; stash cleared.
Precondition
g_dcd.pipes[pipe].xfer == tr.
Task (non-ISR) context.
Postcondition
On success actual_length holds the queued byte count.
On error g_dcd.pipes[pipe].xfer is cleared.
Note
Task-context only; masks interrupts for the FIFO stage.
Since
0.1.0

Definition at line 348 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, internal_fifo_lock(), internal_fifo_unlock(), k_ra8_ok, RA8_INTERNAL, and ra8_usb_queue_in().

Referenced by internal_submit_pipe().

◆ internal_submit_pipe()

unsigned int internal_submit_pipe ( struct UX_SLAVE_TRANSFER_STRUCT * tr,
uint8_t pipe,
uint8_t ep_addr )
static

Submit the stashed IN/OUT transfer on a non-EP0 pipe.

Extracted from priv_transfer_request to keep the dispatcher under the NASA P10 Rule 4 60-line cap. For IN endpoints (bit-7 set in ep_addr) we push the buffer via ra8_usb_queue_in and record the actual length; for OUT endpoints we move PIPECTR.PID from NAK to BUF via ra8_usb_rearm_out_pipe so the controller ACKs the first host OUT token. HUM Ch 36.2.27 PIPECTR.PID.

Parameters
[in,out]trUSBX transfer request, already validated and stashed.
[in]pipePipe index the transfer is bound to (1..max_pipes-1).
[in]ep_addrEndpoint address byte (bit-7 = direction).
Returns
UX_SUCCESS on submission; UX_TRANSFER_ERROR if the bridge layer rejected the request.
Return values
UX_SUCCESSBuffer queued (IN) or PIPECTR.PID == BUF (OUT).
UX_TRANSFER_ERRORBridge layer rejected the submission; g_dcd.pipes[pipe].xfer has been cleared.
Precondition
g_dcd.pipes[pipe].xfer == tr (caller stashed the request).
pipe != 0 (EP0 handled separately).
Postcondition
On error, g_dcd.pipes[pipe].xfer is cleared.
On success, tr->ux_slave_transfer_request_actual_length is set for IN transfers; OUT pipes have PIPECTR.PID == BUF.
Note
Task-context only; not ISR-safe.
Since
0.1.0

Definition at line 409 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, g_orphan_len, g_orphan_pipe, internal_submit_consume_orphan(), internal_submit_in_pipe(), k_ra8_ok, k_ra8_usb_ep_addr_dir_in_bit, and ra8_usb_rearm_out_pipe().

Referenced by priv_transfer_request().

◆ internal_wait_completion()

unsigned int internal_wait_completion ( struct UX_SLAVE_TRANSFER_STRUCT * tr,
uint8_t pipe )
static

Block on the USBX transfer semaphore until the IRQ posts completion.

Mirrors the sync contract of ux_dcd_sim_slave_transfer_request: for non-EP0 transfers the DCD must wait on ux_slave_transfer_request_semaphore until the IRQ path sets completion_code and tx_semaphore_puts the semaphore. Returning UX_SUCCESS immediately would let class drivers observe actual_length=0 and treat it as a short-packet completion – loopback consumers then busy-spin on (n == 0).

Parameters
[in,out]trUSBX transfer request being awaited.
[in]pipePipe index used for g_diag accounting (2 == loopback).
Returns
The completion code posted by the IRQ, or UX_TRANSFER_ERROR on semaphore failure.
Return values
UX_SUCCESSIRQ posted a successful completion.
UX_TRANSFER_ERRORTimeout / abort / IRQ-side error.
Precondition
tr is the stashed transfer at g_dcd.pipes[pipe].xfer.
Caller is on the USBX task context (not in IRQ).
Postcondition
g_dcd.pipes[pipe].xfer is nullptr if the wait failed.
completion_code reflects the IRQ-posted result.
Note
Compiled out under UX_DEVICE_STANDALONE; not used in that build.
Since
0.1.0

Block on the USBX transfer semaphore until the IRQ posts completion.

See the canonical block immediately above this #ifndef for the full contract. Mirrored here so the doxy auditor can find a block immediately preceding the function definition (no preprocessor directive between the block and the signature).

Parameters
[in,out]trUSBX transfer request being awaited.
[in]pipePipe index used for g_diag accounting (2 == loopback).
Returns
The completion code posted by the IRQ, or UX_TRANSFER_ERROR on semaphore failure.
Return values
UX_SUCCESSIRQ posted a successful completion.
UX_TRANSFER_ERRORTimeout / abort / IRQ-side error.
Precondition
tr is the stashed transfer at g_dcd.pipes[pipe].xfer.
Caller is on the USBX task context (not in IRQ).
Postcondition
g_dcd.pipes[pipe].xfer is nullptr if the wait failed.
completion_code reflects the IRQ-posted result.
Note
Compiled out under UX_DEVICE_STANDALONE; not used in that build.
Since
0.1.0

Definition at line 181 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, g_diag, RA8_INTERNAL, TX_SUCCESS, and TX_WAIT_FOREVER.

Referenced by priv_transfer_request().

◆ priv_ep_to_pipe()

uint8_t priv_ep_to_pipe ( uint8_t ep_addr)

Map a USB EP number (1..9) into our PIPE table index.

Map a USB EP number (1..9) into the PIPE table index.

Parameters
[in]ep_addrEndpoint address (with dir bit in 0x80).
Returns
Pipe index 0..9, or k_ux_dcd_ra8_usb_max_pipes on overflow.

See implementation for details.

Return values
0Success or default value.
Precondition
Module has been initialized.
Caller has validated arguments.
Postcondition
Side effects bounded to documented state.
State reflects operation result.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 66 of file ux_dcd_ra8_usb_xfer.c.

References k_ra8_usb_ep_addr_num_mask, and k_ux_dcd_ra8_usb_max_pipes.

Referenced by internal_endpoint_create(), internal_endpoint_destroy(), internal_endpoint_stall(), and priv_transfer_request().

◆ priv_transfer_request()

unsigned int priv_transfer_request ( struct UX_SLAVE_TRANSFER_STRUCT * tr)

USBX UX_DCD_TRANSFER_REQUEST dispatcher entry point.

Validates the incoming transfer request, maps the endpoint address to a pipe index, and dispatches to either internal_ep0_transfer (EP0 control) or stashes the request and submits an IN/OUT data transfer via internal_submit_pipe through the ra8_usb_* register layer. Under non-standalone builds the request is then awaited via internal_wait_completion so USBX class drivers see synchronous completion with non-zero actual_length.

Parameters
[in,out]trUSBX transfer request to submit. ux_slave_transfer_request_endpoint must point at a configured endpoint; data pointer and requested length describe the buffer.
Returns
USBX result code.
Return values
UX_SUCCESSTransfer submitted (and, in non-standalone builds, completed by the IRQ path).
UX_TRANSFER_ERRORtr was null, endpoint was null, pipe was out of range, or the bridge layer rejected the submission.
Precondition
Bridge is past ux_dcd_ra8_usb_initialize.
Caller is the USBX device-stack dispatcher (task context).
Postcondition
For non-EP0 IN transfers, g_dcd.pipes[pipe] holds the active stash.
g_diag counters reflect the dispatch.
Note
Not ISR-safe; runs on the USBX device task context.
Since
0.1.0

Definition at line 465 of file ux_dcd_ra8_usb_xfer.c.

References g_dcd, g_diag, internal_ep0_transfer(), internal_submit_pipe(), internal_wait_completion(), k_ra8_usb_ep_addr_dir_in_bit, k_ux_dcd_ra8_usb_max_pipes, and priv_ep_to_pipe().

Referenced by _ux_dcd_ra8_usb_function().

Variable Documentation

◆ g_orphan_buf

uint8_t g_orphan_buf[k_ra8_usb_orphan_bytes]

One held OUT packet.

One held no-receiver bulk-OUT packet.

Definition at line 40 of file ux_dcd_ra8_usb_xfer.c.

Referenced by internal_irq_drain_orphan_out(), and internal_submit_consume_orphan().

◆ g_orphan_len

uint16_t g_orphan_len = 0U

Held byte count; 0 = empty.

Held orphan byte count; 0 = empty.

Definition at line 42 of file ux_dcd_ra8_usb_xfer.c.

Referenced by internal_endpoint_arm_out_pid(), internal_irq_drain_orphan_out(), internal_submit_consume_orphan(), and internal_submit_pipe().

◆ g_orphan_pipe

uint8_t g_orphan_pipe = 0U

Pipe the held packet is on.

Pipe the held orphan packet is on.

Definition at line 44 of file ux_dcd_ra8_usb_xfer.c.

Referenced by internal_irq_drain_orphan_out(), and internal_submit_pipe().