ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_hcdc.c
Go to the documentation of this file.
1
32
33#include "ra8_usb_hcdc.h"
34
35#include <stdint.h>
36
37#include "ra8_attributes.h"
38#include "ra8_check.h"
39#include "ra8_err.h"
40#include "ra8_log.h"
41#include "ra8_usb.h"
42
43static const char* s_tag = "USBHCDC";
44
45/* =============================================================================
46 * Internal constants
47 * =============================================================================
48 */
49
70
75typedef enum : uint8_t {
76 /* Chapter-9 standard requests (USB 2.0 spec section 9.4). */
84 /* CDC class-specific request envelope. */
86 /* Descriptor types in wValue's high byte. */
92
106
118
123typedef enum : uint32_t {
126
131typedef enum : uint32_t {
134
135/* =============================================================================
136 * Internal state
137 * =============================================================================
138 */
139
153
155
156/* =============================================================================
157 * Internal helpers
158 * =============================================================================
159 */
160
181
201{
202 const uint16_t bulk_mp = internal_bulk_max_packet(s_state.speed);
203
206 s_state.device.bulk_in_ep,
209 bulk_mp);
210 RA8_RETURN_ON_ERROR(err, s_tag, "hcdc: bulk-in cfg");
211
214 s_state.device.bulk_out_ep,
217 bulk_mp);
218 RA8_RETURN_ON_ERROR(err, s_tag, "hcdc: bulk-out cfg");
219
222 s_state.device.intr_in_ep,
226 return err;
227}
228
247static ra8_err_t internal_setup_get_descriptor(uint8_t desc_type, uint16_t length)
248{
249 const ra8_usb_setup_t setup = {
250 .bm_request_type = k_ra8_hcdc_bm_std_dev_in,
252 .w_value = (uint16_t)((uint16_t)desc_type << k_ra8_hcdc_shift_byte1),
253 .w_index = 0U,
254 .w_length = length,
255 };
256 return ra8_usb_host_setup_request(s_state.speed, &setup);
257}
258
275{
276 const ra8_usb_setup_t setup = {
277 .bm_request_type = k_ra8_hcdc_bm_std_dev_out,
278 .b_request = k_ra8_hcdc_breq_set_address,
279 .w_value = (uint16_t)address,
280 .w_index = 0U,
281 .w_length = 0U,
282 };
283 return ra8_usb_host_setup_request(s_state.speed, &setup);
284}
285
301static ra8_err_t internal_setup_set_config(uint8_t config_value)
302{
303 const ra8_usb_setup_t setup = {
304 .bm_request_type = k_ra8_hcdc_bm_std_dev_out,
305 .b_request = k_ra8_hcdc_breq_set_config,
306 .w_value = (uint16_t)config_value,
307 .w_index = 0U,
308 .w_length = 0U,
309 };
310 return ra8_usb_host_setup_request(s_state.speed, &setup);
311}
312
328{
329 const ra8_usb_setup_t setup = {
330 .bm_request_type = k_ra8_hcdc_bm_std_iface_out,
332 .w_value = 0U,
333 .w_index = 0U,
334 .w_length = 0U,
335 };
336 return ra8_usb_host_setup_request(s_state.speed, &setup);
337}
338
361{
362 s_state.device.device_address = k_ra8_hcdc_assigned_address;
363 s_state.device.bulk_in_ep = 1U;
364 s_state.device.bulk_out_ep = 2U;
365 s_state.device.intr_in_ep = 3U;
366 s_state.device.bulk_in_max_packet = internal_bulk_max_packet(s_state.speed);
367 s_state.device.bulk_out_max_packet = internal_bulk_max_packet(s_state.speed);
368 /* VID / PID stay at zero until the production GET_DEVICE_DESCRIPTOR
369 * data stage lands; both fields are informational. */
370}
371
387{
389 return ra8_usb_host_bus_reset(s_state.speed, true);
390}
391
407{
408 const ra8_err_t rel = ra8_usb_host_bus_reset(s_state.speed, false);
409 RA8_RETURN_ON_ERROR(rel, /* GCOVR_EXCL_BR_LINE -- stored speed valid; reset rejects only speed */
410 s_tag,
411 "hcdc: release bus reset");
414}
415
432{
434 RA8_RETURN_ON_ERROR(addr_err, /* GCOVR_EXCL_BR_LINE -- valid speed + static in-range address */
435 s_tag,
436 "hcdc: set USBADDR");
439}
440
460
480
500
521
537{
538 const ra8_err_t pipes_err = internal_configure_pipes();
539 RA8_RETURN_ON_ERROR(pipes_err, s_tag, "hcdc: configure pipes");
540 s_state.attached = true;
542 if (s_state.attach_cb != nullptr) {
543 s_state.attach_cb(s_state.attach_ctx, &s_state.device);
544 }
545 return k_ra8_ok;
546}
547
566{
567 switch (s_state.step) {
569 return internal_do_idle();
571 return internal_do_bus_reset();
579 return internal_do_set_config();
583 return internal_do_walk_desc();
584 default:
585 /* Already done; idempotent. */
586 return k_ra8_ok;
587 }
588}
589
590/* =============================================================================
591 * Lifecycle
592 * =============================================================================
593 */
594
596{
597 if ((speed != k_ra8_usb_speed_fs) && (speed != k_ra8_usb_speed_hs)) {
599 }
600 const ra8_err_t usb_err = ra8_usb_host_init(speed);
601 if (usb_err != k_ra8_ok) {
602 ra8_log_error_val(s_tag, "ra8_usb_host_init failed", (uint32_t)usb_err);
604 }
605
606 s_state.speed = speed;
608 s_state.attached = false;
609 s_state.attach_cb = nullptr;
610 s_state.attach_ctx = nullptr;
611 s_state.device = (ra8_usb_hcdc_device_t){};
612 s_state.initialized = true;
613
614 ra8_log_info_val(s_tag, "host-CDC ready", (uint32_t)speed);
615 return k_ra8_ok;
616}
617
619{
620 if (!s_state.initialized) {
622 }
623 /* Bus power down: drop UACT before tearing the controller. */
624 (void)ra8_usb_host_set_uact(s_state.speed, false);
625 const ra8_err_t err = ra8_usb_host_deinit(s_state.speed);
626 s_state.initialized = false;
627 s_state.attached = false;
628 s_state.attach_cb = nullptr;
629 s_state.attach_ctx = nullptr;
631 return err;
632}
633
634/* =============================================================================
635 * Attach callback
636 * =============================================================================
637 */
638
640{
641 if (!s_state.initialized) {
643 }
644 s_state.attach_cb = on_attach;
645 s_state.attach_ctx = ctx;
646 return k_ra8_ok;
647}
648
649/* =============================================================================
650 * Bulk byte pipe
651 * =============================================================================
652 */
653
654ra8_err_t ra8_usb_hcdc_send(const uint8_t* data, uint16_t len)
655{
656 if (!s_state.initialized) {
658 }
659 if (!s_state.attached) {
661 }
662 if ((data == nullptr) && (len != 0U)) {
664 }
665 return ra8_usb_queue_in(s_state.speed, k_ra8_hcdc_pipe_bulk_out, data, len);
666}
667
668ra8_err_t ra8_usb_hcdc_recv(uint8_t* out_buf, uint16_t max_len, uint16_t* got_len)
669{
670 RA8_CHECK_NULL_PTR(out_buf, s_tag, "hcdc_recv: out_buf");
671 RA8_CHECK_NULL_PTR(got_len, s_tag, "hcdc_recv: got_len");
672 if (!s_state.initialized) {
674 }
675 if (!s_state.attached) {
677 }
678 if (max_len == 0U) {
680 }
681 uint16_t inout_len = max_len;
682 const ra8_err_t err =
683 ra8_usb_queue_out(s_state.speed, k_ra8_hcdc_pipe_bulk_in, out_buf, &inout_len, true);
684 if (err == k_ra8_ok) {
685 *got_len = inout_len;
686 } else {
687 *got_len = 0U;
688 }
689 return err;
690}
691
692/* =============================================================================
693 * Class control transfer
694 * =============================================================================
695 */
696
699 ra8_usb_hcdc_stop_bits_t stop_bits)
700{
701 if (!s_state.initialized) {
703 }
704 if (!s_state.attached) {
706 }
707 if (baud < k_ra8_hcdc_baud_min) {
709 }
710 if (parity > k_ra8_hcdc_parity_space) {
712 }
713 if (stop_bits > k_ra8_hcdc_stop_2) {
715 }
716
717 /* The 7-byte payload for SET_LINE_CODING is staged in the DCP
718 * data stage; the SETUP packet itself only carries class /
719 * recipient / wLength = 7. The data stage (USBREQ / FIFO write)
720 * is wired in the production CTRT path; the starter issues the
721 * SETUP and lets the controller drive the data stage from the
722 * host-side CFIFO. The serialised line-coding word is held off
723 * the stack so its lifetime crosses the SETUP request. */
724 const ra8_usb_setup_t setup = {
725 .bm_request_type = k_ra8_hcdc_bm_class_iface_out,
727 .w_value = 0U,
728 .w_index = 0U,
729 .w_length = k_ra8_hcdc_line_coding_len,
730 };
731 /* Touch the parameters so the unit-test build does not warn about
732 * unused locals: the production data-stage path consumes them. */
733 (void)baud;
734 (void)parity;
735 (void)stop_bits;
736 return ra8_usb_host_setup_request(s_state.speed, &setup);
737}
738
739/* =============================================================================
740 * Test / introspection helpers
741 * =============================================================================
742 */
743
745{
746 if (!s_state.initialized) {
748 }
749 return internal_step_advance();
750}
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_hw_init_failed
Hardware peripheral failed to initialise.
Definition ra8_err.h:290
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
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.
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_error_val(tag, message, value)
RA8 log error val.
Definition ra8_log.h:337
static uint32_t s_state
Native USB controller driver public API (device + host modes).
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)
Drain an OUT transfer (host -> device) from pipe_num.
@ k_ra8_usb_ep_type_bulk
Bulk transfer.
@ k_ra8_usb_ep_type_intr
Interrupt transfer.
ra8_err_t ra8_usb_set_address(ra8_usb_speed_t speed, uint8_t address)
Program the device USB address into USBADDR.
ra8_err_t ra8_usb_configure_endpoint(ra8_usb_speed_t speed, uint8_t pipe_num, uint8_t ep_addr, ra8_usb_ep_dir_t dir, ra8_usb_ep_type_t type, uint16_t max_packet)
Configure a non-control PIPE for IN or OUT bulk / interrupt / iso transfers.
@ k_ra8_usb_ep_dir_in
Device -> host.
@ k_ra8_usb_ep_dir_out
Host -> device.
ra8_usb_speed_t
Selects which controller instance the call targets.
@ k_ra8_usb_speed_hs
High-Speed controller (USBHS @ 0x40351000).
@ k_ra8_usb_speed_fs
Full-Speed controller (USBFS @ 0x40250000).
ra8_err_t ra8_usb_queue_in(ra8_usb_speed_t speed, uint8_t pipe_num, const uint8_t *data, uint16_t len)
Queue an IN transfer (device -> host) on pipe_num.
ra8_err_t ra8_usb_hcdc_set_line_coding(uint32_t baud, ra8_usb_hcdc_parity_t parity, ra8_usb_hcdc_stop_bits_t stop_bits)
Issue SET_LINE_CODING to the attached CDC-ACM device.
static ra8_err_t internal_do_set_address(void)
Step handler – store assigned address + SETUP for GET_DEVICE_DESCRIPTOR.
static ra8_err_t internal_setup_set_config(uint8_t config_value)
Stage a SET_CONFIGURATION SETUP request.
static ra8_err_t internal_do_idle(void)
Step handler – bus-reset assert.
static ra8_err_t internal_step_advance(void)
Drive the enumeration step machine forward by one step.
static ra8_err_t internal_do_set_config(void)
Step handler – SETUP for SET_INTERFACE.
static ra8_err_t internal_setup_set_interface(void)
Stage a SET_INTERFACE (alt 0, iface 0) SETUP request.
static ra8_err_t internal_setup_get_descriptor(uint8_t desc_type, uint16_t length)
Stage a chapter-9 GET_DESCRIPTOR SETUP request.
ra8_usb_hcdc_byte_mask_t
Byte mask for little-endian baud serialisation.
@ k_ra8_hcdc_byte_mask
Single-byte extraction mask.
ra8_usb_hcdc_setup_field_t
Standard chapter-9 + CDC class request encodings.
@ k_ra8_hcdc_breq_set_config
SET_CONFIGURATION.
@ k_ra8_hcdc_bm_std_dev_in
Std | Device | In.
@ k_ra8_hcdc_bm_class_iface_out
Class | Interface | Out.
@ k_ra8_hcdc_breq_set_address
SET_ADDRESS.
@ k_ra8_hcdc_desc_endpoint
ENDPOINT descriptor.
@ k_ra8_hcdc_breq_get_descriptor
GET_DESCRIPTOR.
@ k_ra8_hcdc_breq_set_interface
SET_INTERFACE.
@ k_ra8_hcdc_desc_device
DEVICE descriptor.
@ k_ra8_hcdc_bm_std_dev_out
Std | Device | Out.
@ k_ra8_hcdc_bm_std_iface_out
Std | Interface | Out.
@ k_ra8_hcdc_desc_interface
INTERFACE descriptor.
@ k_ra8_hcdc_desc_configuration
CONFIGURATION descriptor.
static ra8_err_t internal_do_bus_reset(void)
Step handler – bus-reset release + SETUP for SET_ADDRESS.
ra8_err_t ra8_usb_hcdc_attach_callback(ra8_usb_hcdc_attach_fn_t on_attach, void *ctx)
Register (or detach) the attach callback.
static ra8_err_t internal_do_get_dev_desc(void)
Step handler – SETUP for GET_CONFIGURATION_DESCRIPTOR.
static void internal_walk_config_descriptor(void)
Populate s_state.device with stub descriptor data.
static ra8_err_t internal_do_set_interface(void)
Step handler – pure software descriptor walk.
ra8_err_t ra8_usb_hcdc_init(ra8_usb_speed_t speed)
Bring up the host-CDC driver on a chosen USB controller.
ra8_usb_hcdc_step_t
Enumeration step machine states.
@ k_ra8_hcdc_step_set_config
SET_CONFIGURATION (1).
@ k_ra8_hcdc_step_done
Attach callback fires.
@ k_ra8_hcdc_step_get_dev_desc
GET_DEVICE_DESCRIPTOR (18 B).
@ k_ra8_hcdc_step_walk_desc
Find CDC IFs; populate pipes.
@ k_ra8_hcdc_step_get_cfg_desc
GET_CONFIGURATION_DESCRIPTOR.
@ k_ra8_hcdc_step_set_interface
SET_INTERFACE (0).
@ k_ra8_hcdc_step_idle
Pre-attach.
@ k_ra8_hcdc_step_set_address
SET_ADDRESS to assigned 1.
@ k_ra8_hcdc_step_bus_reset
Drive USBRST then release.
ra8_usb_hcdc_baud_min_t
Minimum legal baud the host driver allows.
@ k_ra8_hcdc_baud_min
0 baud is rejected as bogus.
ra8_err_t ra8_usb_hcdc_step(void)
Drive the enumeration step machine forward by one step.
ra8_err_t ra8_usb_hcdc_send(const uint8_t *data, uint16_t len)
Send a chunk of bytes OUT to the attached CDC-ACM device.
ra8_usb_hcdc_byte_shift_t
Per-byte left-shift constants for little-endian baud serialisation.
@ k_ra8_hcdc_shift_byte3
RA8 hcdc shift byte3.
@ k_ra8_hcdc_shift_byte0
RA8 hcdc shift byte0.
@ k_ra8_hcdc_shift_byte2
RA8 hcdc shift byte2.
@ k_ra8_hcdc_shift_byte1
RA8 hcdc shift byte1.
static ra8_err_t internal_do_walk_desc(void)
Step handler – finalise pipes + fire attach callback.
ra8_err_t ra8_usb_hcdc_close(void)
Tear down the host-CDC driver and release the controller.
ra8_usb_hcdc_size_t
Standard descriptor sizes and request payload sizes.
@ k_ra8_hcdc_assigned_address
First assigned device addr.
@ k_ra8_hcdc_dev_desc_len
USB DEVICE descriptor.
@ k_ra8_hcdc_default_config
bConfigurationValue = 1.
@ k_ra8_hcdc_iface_desc_len
INTERFACE descriptor.
@ k_ra8_hcdc_line_coding_len
SET_LINE_CODING payload.
@ k_ra8_hcdc_ep_desc_len
ENDPOINT descriptor.
@ k_ra8_hcdc_cfg_desc_len
CONFIGURATION descriptor hdr.
static uint16_t internal_bulk_max_packet(ra8_usb_speed_t speed)
Pick the bulk-max-packet ceiling matching the negotiated speed.
ra8_err_t ra8_usb_hcdc_recv(uint8_t *out_buf, uint16_t max_len, uint16_t *got_len)
Drain a chunk of bytes IN from the attached CDC-ACM device.
static ra8_err_t internal_setup_set_address(uint8_t address)
Stage a SET_ADDRESS SETUP request.
static ra8_err_t internal_do_get_cfg_desc(void)
Step handler – SETUP for SET_CONFIGURATION.
static ra8_err_t internal_configure_pipes(void)
Configure the three host-CDC pipes against the attached device's endpoints.
Native USB host-side CDC ACM (Communications Device Class -Abstract Control Model) class layer.
@ k_ra8_hcdc_intr_max_packet
Notification pipe size.
@ k_ra8_hcdc_bulk_max_packet_hs
Bulk size at high speed.
@ k_ra8_hcdc_bulk_max_packet_fs
Bulk size at full speed.
ra8_usb_hcdc_parity_t
Parity selector used by ra8_usb_hcdc_set_line_coding.
@ k_ra8_hcdc_parity_space
Space parity.
void(* ra8_usb_hcdc_attach_fn_t)(void *ctx, const ra8_usb_hcdc_device_t *device)
Attach-callback signature.
@ k_ra8_hcdc_pipe_bulk_in
PIPE1 -> attached EP bulk IN.
@ k_ra8_hcdc_pipe_intr_in
PIPE6 -> attached EP intr IN.
@ k_ra8_hcdc_pipe_bulk_out
PIPE2 -> attached EP bulk OUT.
ra8_usb_hcdc_stop_bits_t
Stop-bit selector used by ra8_usb_hcdc_set_line_coding.
@ k_ra8_hcdc_stop_2
2 stop bits.
@ k_ra8_hcdc_req_set_line_coding
7-byte payload.
ra8_err_t ra8_usb_host_bus_reset(ra8_usb_speed_t speed, bool assert_reset)
Drive (or release) the bus reset line.
ra8_err_t ra8_usb_host_init(ra8_usb_speed_t speed)
Bring up a USB controller in HOST mode.
ra8_err_t ra8_usb_host_deinit(ra8_usb_speed_t speed)
Tear down a host-mode controller and drop its MSTP reference.
ra8_err_t ra8_usb_host_setup_request(ra8_usb_speed_t speed, const ra8_usb_setup_t *setup)
Issue a SETUP packet through the DCP (host -> peripheral).
ra8_err_t ra8_usb_host_set_uact(ra8_usb_speed_t speed, bool enable)
Enable / disable SOF (Start-of-Frame) generation on the bus.
Snapshot of the attached CDC-ACM device, passed to the attach callback.
Singleton shadow state for the host-CDC driver.
ra8_usb_hcdc_device_t device
Snapshot of attached device.
void * attach_ctx
Attach callback ctx.
bool initialized
True after ra8_usb_hcdc_init.
bool attached
True after enumeration done.
ra8_usb_hcdc_attach_fn_t attach_cb
Attach callback, or NULL.
ra8_usb_hcdc_step_t step
Current enumeration step.
ra8_usb_speed_t speed
Underlying controller.
Decoded 8-byte USB SETUP packet.