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

Native (bare-metal) USB printer + vendor composite device for EK-RA8D2. More...

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_err.h"
#include "ra8_isr.h"
#include "ra8_port_constants.h"
#include "ra8_port_utils.h"
#include "ra8_time.h"
#include "ra8_usb.h"
#include "ra8_usb_pprn.h"
#include "ra8_usb_pvnd.h"
#include "ra8_usb_regs.h"
#include "usb_printer_vendor_ch9.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  demo_cfg_t : uint32_t {
  k_demo_console_baud = 115200U ,
  k_demo_poll_budget = 200000U
}
 Compile-time settings for the polled device loop. More...
enum  demo_bulk_t : uint16_t { k_demo_bulk_cap = 64U }
 Bulk staging sizes for the printer / vendor data path. More...
enum  demo_intsts0_t : uint16_t {
  k_demo_int_brdy = (uint16_t)(1U << (uint16_t)k_ra8_int0_bit_brdy) ,
  k_demo_int_ctrt = (uint16_t)(1U << (uint16_t)k_ra8_int0_bit_ctrt) ,
  k_demo_int_dvst = (uint16_t)(1U << (uint16_t)k_ra8_int0_bit_dvst)
}
 The INTSTS0 event bits the polled loop reacts to (HUM Ch 36.2.9). More...

Functions

static void demo_puts (const char *s)
 Write a NUL-terminated string to the UART console.
static ra8_err_t demo_printer_setup (void *ctx, const ra8_usb_setup_t *setup)
 Printer class-setup handler: answer GET_PORT_STATUS / GET_DEVICE_ID.
static ra8_err_t demo_vendor_setup (void *ctx, const ra8_usb_setup_t *setup)
 Vendor class-setup handler: ACK the vendor control request.
static ra8_err_t demo_stage_descriptor (pv_action_t action, const ra8_usb_setup_t *setup)
 Stage a standard GET_DESCRIPTOR response on EP0.
static void demo_dispatch_setup (const ra8_usb_setup_t *setup)
 Dispatch one decoded SETUP packet from the DCP.
static void demo_handle_dvst (void)
 React to a device-state transition (INTSTS0.DVST).
static void demo_drain_print_job (void)
 Drain a bulk-OUT print job and echo it to the UART console.
static void demo_drain_vendor (void)
 Drain a bulk-OUT vendor packet and loop it back on the vendor IN pipe.
static void demo_poll_once (void)
 Run one poll-loop iteration: service EP0 + bulk events.
static void demo_panic_halt (void)
 Halt forever in WFI after a fatal bring-up error.
static ra8_err_t demo_pins_init (void)
 Route the four USB-FS pins to the USBFS controller.
static ra8_err_t demo_usb_bringup (void)
 Bring up both native class layers and advertise the device.
void main (void)
 Application entry: bring up the clocks + USB + poll chapter-9.

Variables

static const uint8_t s_device_id []
 IEEE 1284 device-ID payload returned by Printer GET_DEVICE_ID.
static volatile bool s_configured = false
 Latched true once the host drives the device to CONFIGURED.
volatile uint32_t g_usb_print_jobs = 0U
 HIL liveness counter – print-job packets drained to the UART.
volatile uint32_t g_usb_vendor_loops = 0U
 HIL liveness counter – vendor bulk packets looped back.
static const ra8_port_pin_t k_demo_pin_vbus = (ra8_port_pin_t)k_ra8_board_usbfs_pin_vbus
 USB_FS VBUS pin, packed ra8_port_pin_t.
static const ra8_port_pin_t k_demo_pin_vbusen = (ra8_port_pin_t)k_ra8_board_usbfs_pin_vbusen
 USB_FS VBUSEN pin, packed ra8_port_pin_t.
static const ra8_port_pin_t k_demo_pin_dp = (ra8_port_pin_t)k_ra8_board_usbfs_pin_dp
 USB_FS D+ pin, packed ra8_port_pin_t.
static const ra8_port_pin_t k_demo_pin_dm = (ra8_port_pin_t)k_ra8_board_usbfs_pin_dm
 USB_FS D- pin, packed ra8_port_pin_t.

Detailed Description

Native (bare-metal) USB printer + vendor composite device for EK-RA8D2.

Tag
[Ring 6 / APP] {World: NS}

Enumerates the EK-RA8D2 USB-FS port as a composite device that exposes two interfaces in a single configuration so one enumeration exercises both native class layers (issue #265):

  • IF0: USB Printer class 0x07 / subclass 0x01 / protocol 0x02 (bi-directional), driven by ra8_usb_pprn. Bulk OUT EP 0x01 carries the host's print job (echoed to the UART console); bulk IN EP 0x82 carries printer status. GET_PORT_STATUS / GET_DEVICE_ID / SOFT_RESET are answered from the class layer's port-status shadow.
  • IF1: Vendor specific class 0xFF, driven by ra8_usb_pvnd. Bulk OUT EP 0x02 -> bulk IN EP 0x81 form a loopback a host libusb / WinUSB tool can round-trip.

Unlike the ThreadX + USBX device examples, this app is fully bare-metal: the native ra8_usb driver has no chapter-9 responder, so main.c runs a small polled chapter-9 loop (see usb_printer_vendor_ch9.c for the pure SETUP router + descriptor tables) that answers the standard GET_DESCRIPTOR / SET_ADDRESS / SET_CONFIGURATION requests on EP0 and hands the printer / vendor SETUPs to the class layers.

Pinout (USB-FS, EK-RA8D2 board layer)

Net Pin Source
USB_FS_VBUS P4_07 k_ra8_board_usbfs_pin_vbus
USB_FS_VBUSEN P5_00 k_ra8_board_usbfs_pin_vbusen
USB_FS_DP P8_14 k_ra8_board_usbfs_pin_dp
USB_FS_DM P8_15 k_ra8_board_usbfs_pin_dm

Verification

  • ra8_emulator (EIL, headless): the emulated chapter-9 host walks the enumeration script against the polled responder; the run reaches device CONFIGURED. Gated by scripts/emu/smoke.sh usb_printer_vendor.
  • Hardware (TODO): the print-job -> UART echo needs a real host print subsystem (CUPS / usblp) driving the printer interface, and the vendor bulk loopback needs a host-side libusb script; both are marked TODO(host-side print job) / TODO(host-side vendor loopback) below because they cannot be driven without an attached PC.
Author
Brighton Sikarskie
Date
2026-07-16
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ demo_bulk_t

enum demo_bulk_t : uint16_t

Bulk staging sizes for the printer / vendor data path.

Enumerator
k_demo_bulk_cap 

Bulk-FS max packet; one drain per BRDY.

Definition at line 92 of file main.c.

◆ demo_cfg_t

enum demo_cfg_t : uint32_t

Compile-time settings for the polled device loop.

Enumerator
k_demo_console_baud 

J-Link OB VCOM console baud.

k_demo_poll_budget 

Bounded chapter-9 poll iterations.

Definition at line 83 of file main.c.

◆ demo_intsts0_t

enum demo_intsts0_t : uint16_t

The INTSTS0 event bits the polled loop reacts to (HUM Ch 36.2.9).

Enumerator
k_demo_int_brdy 

Buffer ready.

k_demo_int_ctrt 

Control stage.

k_demo_int_dvst 

Device state.

Definition at line 100 of file main.c.

Function Documentation

◆ demo_dispatch_setup()

void demo_dispatch_setup ( const ra8_usb_setup_t * setup)
static

Dispatch one decoded SETUP packet from the DCP.

Routes via pv_route_setup: descriptor actions stage bytes, SET_ADDRESS latches the address, SET_CONFIGURATION ACKs and the class actions hand off to ra8_usb_pprn_handle_setup / _pvnd_handle_setup. Unsupported requests STALL EP0.

Parameters
[in]setupDecoded SETUP packet (non-NULL).
Precondition
setup is non-NULL.
The USB device layers are initialized.
Postcondition
EP0 has been advanced (data staged, ACKed, or stalled).
Note
Not thread-safe; single poll-loop caller.
Since
0.1.0

Definition at line 308 of file main.c.

References demo_stage_descriptor(), k_pv_action_get_config_desc, k_pv_action_get_device_desc, k_pv_action_get_string_desc, k_pv_action_printer_class, k_pv_action_set_address, k_pv_action_set_configuration, k_pv_action_stall, k_pv_action_vendor_class, k_ra8_err_not_supported, k_ra8_ok, k_ra8_usb_speed_fs, pv_route_setup(), ra8_usb_control_response(), ra8_usb_pprn_handle_setup(), ra8_usb_pvnd_handle_setup(), ra8_usb_set_address(), and ra8_usb_setup_t::w_value.

Referenced by demo_poll_once().

◆ demo_drain_print_job()

void demo_drain_print_job ( void )
static

Drain a bulk-OUT print job and echo it to the UART console.

TODO(host-side print job): a physical host print subsystem (CUPS / usblp) must send the job; ra8_emulator reaches CONFIGURED but does not drive bulk traffic, so this path runs only on hardware.

Precondition
ra8_usb_pprn_init succeeded.
Postcondition
On data, the bytes were echoed and g_usb_print_jobs advanced.
Note
Not thread-safe.
Since
0.1.0

Definition at line 386 of file main.c.

References g_usb_print_jobs, k_demo_bulk_cap, k_ra8_ok, ra8_board_uart_console_write(), and ra8_usb_pprn_recv().

Referenced by demo_poll_once().

◆ demo_drain_vendor()

void demo_drain_vendor ( void )
static

Drain a bulk-OUT vendor packet and loop it back on the vendor IN pipe.

TODO(host-side vendor loopback): a host libusb / WinUSB tool must drive the bulk transfer; this path runs only on hardware.

Precondition
ra8_usb_pvnd_init succeeded.
Postcondition
On data, the packet was echoed back and g_usb_vendor_loops advanced.
Note
Not thread-safe.
Since
0.1.0

Definition at line 409 of file main.c.

References g_usb_vendor_loops, k_demo_bulk_cap, k_ra8_ok, ra8_usb_pvnd_recv(), and ra8_usb_pvnd_send().

Referenced by demo_poll_once().

◆ demo_handle_dvst()

void demo_handle_dvst ( void )
static

React to a device-state transition (INTSTS0.DVST).

On the host's bus reset (Default state) the DCP is re-armed; on CONFIGURED the banner is emitted once. USB 2.0 sec 9.1 "Device States".

Precondition
The USB device layers are initialized.
Postcondition
s_configured reflects the current state.
On Default the DCP was re-armed.
Note
Not thread-safe.
Since
0.1.0

Definition at line 356 of file main.c.

References demo_puts(), k_ra8_ok, k_ra8_usb_dev_state_configured, k_ra8_usb_dev_state_default, k_ra8_usb_dev_state_powered, k_ra8_usb_speed_fs, ra8_usb_device_busreset_rearm(), ra8_usb_get_device_state(), and s_configured.

Referenced by demo_poll_once().

◆ demo_panic_halt()

void demo_panic_halt ( void )
static

Halt forever in WFI after a fatal bring-up error.

Precondition
Reached only on an unrecoverable init failure.
Postcondition
CPU parked until reset.
Note
Not reachable post-boot.
Since
0.1.0

Definition at line 470 of file main.c.

References demo_puts().

◆ demo_pins_init()

ra8_err_t demo_pins_init ( void )
staticnodiscard

Route the four USB-FS pins to the USBFS controller.

Returns
ra8_err_t from the first failing route, or k_ra8_ok.
Return values
k_ra8_okAll four pins routed.
Precondition
IOPORT reachable; single-threaded init context.
Postcondition
On success the four USB-FS pins are in USB peripheral mode.
Note
VBUSEN is driven low (device mode); peripheral routing would force it high (host mode) and block enumeration.
Since
0.1.0

Definition at line 491 of file main.c.

References k_demo_pin_dm, k_demo_pin_dp, k_demo_pin_vbus, k_demo_pin_vbusen, k_ra8_level_low, k_ra8_ok, k_ra8_psel_usb_fs, ra8_gpio_output_init(), and ra8_pfs_route_peripheral().

Referenced by main().

◆ demo_poll_once()

void demo_poll_once ( void )
static

Run one poll-loop iteration: service EP0 + bulk events.

Precondition
The USB device layers are initialized and attached.
Postcondition
Any pending CTRT / DVST / BRDY event was serviced.
Note
Not thread-safe; single poll-loop caller.
Since
0.1.0

Definition at line 429 of file main.c.

References demo_dispatch_setup(), demo_drain_print_job(), demo_drain_vendor(), demo_handle_dvst(), k_demo_int_brdy, k_demo_int_ctrt, k_demo_int_dvst, k_ra8_ok, k_ra8_usb_speed_fs, ra8_usb_clear_status(), ra8_usb_control_response(), ra8_usb_get_status(), and ra8_usb_read_setup_if_valid().

Referenced by main().

◆ demo_printer_setup()

ra8_err_t demo_printer_setup ( void * ctx,
const ra8_usb_setup_t * setup )
static

Printer class-setup handler: answer GET_PORT_STATUS / GET_DEVICE_ID.

Invoked by ra8_usb_pprn_handle_setup after it validates the envelope. GET_PORT_STATUS stages the 1-byte port-status shadow; GET_DEVICE_ID stages the IEEE 1284 identity string; SOFT_RESET carries no data.

Parameters
[in]ctxUnused context.
[in]setupDecoded printer class SETUP (non-NULL, class-validated).
Returns
k_ra8_ok to ACK the status stage, else EP0 stalls.
Return values
k_ra8_okData staged (or none required).
Precondition
ra8_usb_pprn_init succeeded.
setup is non-NULL (class-layer guarantee).
Postcondition
For a control-IN request the DCP holds the response bytes.
Note
Runs in the poll loop's dispatch context.
Since
0.1.0

Definition at line 209 of file main.c.

References ra8_usb_setup_t::b_request, k_pv_pprn_get_device_id, k_pv_pprn_get_port_status, k_ra8_err_null_ptr, k_ra8_ok, k_ra8_usb_speed_fs, pv_clamp_len(), ra8_usb_dcp_in_data(), ra8_usb_pprn_get_port_status(), s_device_id, and ra8_usb_setup_t::w_length.

Referenced by demo_usb_bringup().

◆ demo_puts()

void demo_puts ( const char * s)
static

Write a NUL-terminated string to the UART console.

Parameters
[in]sString to emit (non-NULL, NUL-terminated).
Precondition
ra8_board_uart_console_init succeeded.
s is non-NULL.
Postcondition
The bytes have been queued to the console SCI.
Note
Not thread-safe; single-writer boot / poll context.
Since
0.1.0

Definition at line 177 of file main.c.

References ra8_board_uart_console_write(), and strlen().

Referenced by demo_handle_dvst(), demo_panic_halt(), and main().

◆ demo_stage_descriptor()

ra8_err_t demo_stage_descriptor ( pv_action_t action,
const ra8_usb_setup_t * setup )
static

Stage a standard GET_DESCRIPTOR response on EP0.

Parameters
[in]action::k_pv_action_get_* descriptor action.
[in]setupThe originating SETUP (for its wLength ceiling).
Returns
ra8_err_t from the DCP data stage.
Return values
k_ra8_okDescriptor staged (PID = BUF).
k_ra8_err_not_supportedAction names no descriptor.
Precondition
ra8_usb_pprn_init / _pvnd_init succeeded.
setup is non-NULL.
Postcondition
On success the DCP holds up to wLength descriptor bytes.
Note
Not thread-safe.
Since
0.1.0

Definition at line 280 of file main.c.

References k_ra8_err_not_supported, k_ra8_usb_speed_fs, pv_clamp_len(), pv_descriptor(), ra8_usb_dcp_in_data(), and ra8_usb_setup_t::w_length.

Referenced by demo_dispatch_setup().

◆ demo_usb_bringup()

ra8_err_t demo_usb_bringup ( void )
staticnodiscard

Bring up both native class layers and advertise the device.

Initialises ra8_usb_pprn (printer pipes PIPE3/PIPE4) then ra8_usb_pvnd (vendor pipes PIPE5/PIPE1), caches each layer's descriptor blob + the printer device-ID, installs the class-setup callbacks, and raises the D+ pull-up so the host begins enumeration. Both *_init calls run the shared ra8_usb_device_init; the second is idempotent for this polled device because the loop reads INTSTS0 directly and does not rely on the per-pipe BRDY enables the re-init clears.

Returns
ra8_err_t error code.
Return values
k_ra8_okBoth class layers up, pull-up raised.
Precondition
CGC + USB clock + pins already configured.
Postcondition
On success the device advertises on the bus.
Note
Not thread-safe.
Since
0.1.0

Definition at line 528 of file main.c.

References demo_printer_setup(), demo_vendor_setup(), k_pv_action_get_config_desc, k_ra8_err_not_supported, k_ra8_ok, k_ra8_usb_speed_fs, pv_descriptor(), ra8_usb_device_attach(), ra8_usb_pprn_attach_setup_handler(), ra8_usb_pprn_init(), ra8_usb_pprn_set_descriptors(), ra8_usb_pvnd_attach_setup_handler(), ra8_usb_pvnd_init(), ra8_usb_pvnd_set_descriptors(), and s_device_id.

Referenced by main().

◆ demo_vendor_setup()

ra8_err_t demo_vendor_setup ( void * ctx,
const ra8_usb_setup_t * setup )
static

Vendor class-setup handler: ACK the vendor control request.

The demo defines no vendor control protocol; every vendor-recipient request is ACKed so a host libusb tool can probe the interface. Real firmware would decode setup->b_request here.

Parameters
[in]ctxUnused context.
[in]setupDecoded vendor SETUP (non-NULL, envelope-validated).
Returns
k_ra8_ok to ACK.
Return values
k_ra8_okAlways.
Precondition
ra8_usb_pvnd_init succeeded.
setup is non-NULL (class-layer guarantee).
Postcondition
No device state mutated.
Note
Runs in the poll loop's dispatch context.
Since
0.1.0

Definition at line 250 of file main.c.

References k_ra8_err_null_ptr, and k_ra8_ok.

Referenced by demo_usb_bringup().

◆ main()

void main ( void )

Application entry: bring up the clocks + USB + poll chapter-9.

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler copied .data and zeroed .bss.
SystemInit configured VTOR / FPU.
Postcondition
On clean bring-up the CPU stays in the poll loop.
On any HAL init failure the function halts in WFI.
Note
Single entry point; not re-entrant.
Since
0.1.0

Definition at line 577 of file main.c.

References demo_panic_halt(), demo_pins_init(), demo_poll_once(), demo_puts(), demo_usb_bringup(), k_demo_console_baud, k_demo_poll_budget, k_ra8_clock_id_cpuclk0, k_ra8_ok, ra8_board_uart_console_init(), ra8_cgc_get_clock_hz(), ra8_cgc_init(), ra8_cgc_usbfs_clock_enable(), ra8_isr_globals_enable(), and ra8_time_init().

Variable Documentation

◆ g_usb_print_jobs

volatile uint32_t g_usb_print_jobs = 0U

HIL liveness counter – print-job packets drained to the UART.

Advances on every bulk-OUT packet the host sends to the printer interface. Read externally over SWD by the HIL mem-probe.

Note
Read externally only.
Since
0.1.0

Definition at line 138 of file main.c.

Referenced by demo_drain_print_job().

◆ g_usb_vendor_loops

volatile uint32_t g_usb_vendor_loops = 0U

HIL liveness counter – vendor bulk packets looped back.

Note
Read externally only.
Since
0.1.0

Definition at line 146 of file main.c.

Referenced by demo_drain_vendor().

◆ k_demo_pin_dm

USB_FS D- pin, packed ra8_port_pin_t.

Since
0.1.0

Definition at line 159 of file main.c.

Referenced by demo_pins_init().

◆ k_demo_pin_dp

USB_FS D+ pin, packed ra8_port_pin_t.

Since
0.1.0

Definition at line 157 of file main.c.

Referenced by demo_pins_init().

◆ k_demo_pin_vbus

const ra8_port_pin_t k_demo_pin_vbus = (ra8_port_pin_t)k_ra8_board_usbfs_pin_vbus
static

USB_FS VBUS pin, packed ra8_port_pin_t.

Since
0.1.0

Definition at line 153 of file main.c.

Referenced by demo_pins_init().

◆ k_demo_pin_vbusen

const ra8_port_pin_t k_demo_pin_vbusen = (ra8_port_pin_t)k_ra8_board_usbfs_pin_vbusen
static

USB_FS VBUSEN pin, packed ra8_port_pin_t.

Since
0.1.0

Definition at line 155 of file main.c.

Referenced by demo_pins_init().

◆ s_configured

volatile bool s_configured = false
static

Latched true once the host drives the device to CONFIGURED.

Note
Written only by the poll loop; read for the banner + counters.
Warning
Do not mutate outside demo_handle_dvst.
Since
0.1.0

Definition at line 128 of file main.c.

Referenced by board_usb_configured(), board_usb_init(), board_usb_report(), board_usb_state_string(), board_usb_tick(), demo_handle_dvst(), and priv_host_mark_configured().

◆ s_device_id

const uint8_t s_device_id[]
static
Initial value:
= {
0x00U, 0x31U, 'M', 'F', 'G', ':', 'R', 'e', 'n', 'e', 's', 'a', 's', ';', 'M', 'D', 'L', ':', 'E',
'K', '-', 'R', 'A', '8', 'D', '2', ';', 'C', 'M', 'D', ':', 'R', 'A', 'W', ';', 'C', 'L', 'S',
':', 'P', 'R', 'I', 'N', 'T', 'E', 'R', ';', 'D', 'E', 'S', 'C', ':', 'R', 'A', '8',
}

IEEE 1284 device-ID payload returned by Printer GET_DEVICE_ID.

Big-endian 2-byte length prefix then the ID string, per USB Printer Class 1.1 sec 4.2.1. Length prefix = 0x0031 = 49 bytes (the 2 length bytes plus the 47-char body).

Note
Read-only wire-format constant.
Since
0.1.0

Definition at line 115 of file main.c.

Referenced by demo_printer_setup(), and demo_usb_bringup().