ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_composite.c
Go to the documentation of this file.
1
45
46#include "ra8_usb_composite.h"
47
48#include <stdint.h>
49
50#include "ra8_attributes.h"
51#include "ra8_check.h"
52#include "ra8_err.h"
53#include "ra8_log.h"
54#include "ra8_usb.h"
55
56static const char* s_tag = "USBCOMP";
57
58/* =============================================================================
59 * Internal constants
60 * =============================================================================
61 */
62
64typedef enum : uint16_t {
67
83
98
109
127
135
149
150/* =============================================================================
151 * Internal state
152 * =============================================================================
153 */
154
172
174
175/* =============================================================================
176 * Internal helpers
177 * =============================================================================
178 */
179
197{
198 for (uint8_t i = 0U; i < (uint8_t)k_ra8_usb_composite_max_ifs; ++i) {
199 s_state.if_owner_plus_one[i] = 0U;
200 }
201}
202
222{
223 if (cl == nullptr) {
224 return k_ra8_err_null_ptr;
225 }
226 if ((cl->init == nullptr) || (cl->handle_setup == nullptr) || (cl->close == nullptr)) {
227 return k_ra8_err_null_ptr;
228 }
229 if (cl->interface_number_count == 0U) {
231 }
232 const uint16_t end = (uint16_t)cl->interface_number_first + (uint16_t)cl->interface_number_count;
233 if (end > (uint16_t)k_ra8_usb_composite_max_ifs) {
235 }
236 return k_ra8_ok;
237}
238
259{
260 for (uint8_t i = 0U; i < cl->interface_number_count; ++i) {
261 const uint8_t idx = cl->interface_number_first + i;
262 if (s_state.if_owner_plus_one[idx] != 0U) {
263 return k_ra8_err_exists;
264 }
265 }
266 return k_ra8_ok;
267}
268
285static void internal_mark_ownership(const ra8_usb_composite_class_t* cl, uint8_t class_index)
286{
287 for (uint8_t i = 0U; i < cl->interface_number_count; ++i) {
288 const uint8_t idx = cl->interface_number_first + i;
289 s_state.if_owner_plus_one[idx] = (uint8_t)(class_index + 1U);
290 }
291}
292
311static ra8_err_t internal_lookup_class_for_if(uint8_t if_num, uint8_t* out_idx)
312{
313 if (if_num >= (uint8_t)k_ra8_usb_composite_max_ifs) {
314 return k_ra8_err_not_found;
315 }
316 const uint8_t slot = s_state.if_owner_plus_one[if_num];
317 if (slot == 0U) {
318 return k_ra8_err_not_found;
319 }
320 *out_idx = (uint8_t)(slot - 1U);
321 return k_ra8_ok;
322}
323
350{
351 /* USB 2.0 sec 9.4 "Standard Device Requests" -- the starter
352 * implements just the address-setter; the full GET_DESCRIPTOR
353 * delivery path is owned by the caller's chapter-9 stack. */
354 if (setup->b_request == (uint8_t)k_ra8_usb_composite_std_set_address) {
355 const uint16_t addr16 = setup->w_value;
356 if (addr16 > (uint16_t)k_ra8_usb_composite_max_address) {
358 }
359 return ra8_usb_set_address(s_state.speed, (uint8_t)addr16);
360 }
361 /* Other standard requests are accepted; descriptor staging /
362 * configuration tracking are caller-owned. */
363 return k_ra8_ok;
364}
365
388static ra8_err_t internal_route_class(const ra8_usb_setup_t* setup, uint8_t* out_idx)
389{
390 const uint8_t if_num = (uint8_t)(setup->w_index & k_usbc_byte_mask);
391 uint8_t idx = 0U;
392 const ra8_err_t err = internal_lookup_class_for_if(if_num, &idx);
393 if (err != k_ra8_ok) {
394 return err;
395 }
396 *out_idx = idx;
397 const ra8_usb_composite_class_t* const cl = &s_state.classes[idx];
398 return cl->handle_setup(cl->ctx, setup);
399}
400
417{
418 if (!s_state.initialized) {
420 }
421 return k_ra8_ok;
422}
423
424/* =============================================================================
425 * Lifecycle
426 * =============================================================================
427 */
428
430{
431 if ((speed != k_ra8_usb_speed_fs) && (speed != k_ra8_usb_speed_hs)) {
433 }
434 const ra8_err_t usb_err = ra8_usb_device_init(speed);
435 if (usb_err != k_ra8_ok) {
436 ra8_log_error_val(s_tag, "ra8_usb_device_init failed", (uint32_t)usb_err);
438 }
439
440 s_state.speed = speed;
442 s_state.class_count = 0U;
443 s_state.device_desc = nullptr;
444 s_state.config_desc = nullptr;
445 s_state.last_handler_class = (uint8_t)k_ra8_usb_composite_handler_self;
446 for (uint8_t i = 0U; i < (uint8_t)k_ra8_usb_composite_max_classes; ++i) {
447 s_state.classes[i] = (ra8_usb_composite_class_t){};
448 }
450 s_state.initialized = true;
451
452 ra8_log_info_val(s_tag, "composite ready", (uint32_t)speed);
453 return k_ra8_ok;
454}
455
457{
458 if (!s_state.initialized) {
460 }
461 /* Walk in reverse-registration order so a class registered late
462 * (e.g. an MSC layer that depends on CDC having brought up shared
463 * state) tears down first. USB 2.0 doesn't mandate this but it
464 * matches FSP's r_usb_pdriver shutdown ordering. */
465 for (uint8_t i = s_state.class_count; i > 0U; --i) {
466 const uint8_t idx = (uint8_t)(i - 1U);
467 const ra8_usb_composite_class_t* const cl = &s_state.classes[idx];
468 if (cl->close != nullptr) {
469 (void)cl->close(cl->ctx);
470 }
471 }
472
473 const ra8_err_t deinit_err = ra8_usb_device_deinit(s_state.speed);
474 s_state.initialized = false;
475 s_state.class_count = 0U;
476 s_state.device_desc = nullptr;
477 s_state.config_desc = nullptr;
479
480 if (deinit_err != k_ra8_ok) {
481 ra8_log_error_val(s_tag, "ra8_usb_device_deinit failed", (uint32_t)deinit_err);
482 return deinit_err;
483 }
484 return k_ra8_ok;
485}
486
487/* =============================================================================
488 * Class registration
489 * =============================================================================
490 */
491
493{
494 const ra8_err_t init_check = internal_require_init();
495 if (init_check != k_ra8_ok) {
496 return init_check;
497 }
498 const ra8_err_t val = internal_validate_class(class_layer);
499 if (val != k_ra8_ok) {
500 return val;
501 }
502 if (s_state.class_count >= (uint8_t)k_ra8_usb_composite_max_classes) {
503 return k_ra8_err_no_mem;
504 }
505 const ra8_err_t coll = internal_check_collision(class_layer);
506 if (coll != k_ra8_ok) {
507 return coll;
508 }
509
510 const uint8_t slot = s_state.class_count;
511 s_state.classes[slot] = *class_layer;
512 internal_mark_ownership(class_layer, slot);
513 s_state.class_count = (uint8_t)(slot + 1U);
514
515 /* Invoke the class's init callback last so a failure there leaves
516 * the registry in a clean state via close (caller can call
517 * `ra8_usb_composite_close` to tear everything down). */
518 const ra8_err_t init_err = class_layer->init(class_layer->ctx);
519 if (init_err != k_ra8_ok) {
520 ra8_log_error_val(s_tag, "class init failed", (uint32_t)init_err);
521 return init_err;
522 }
523 ra8_log_info_val(s_tag, "class registered", (uint32_t)slot);
524 return k_ra8_ok;
525}
526
527/* =============================================================================
528 * Descriptors
529 * =============================================================================
530 */
531
532ra8_err_t ra8_usb_composite_set_descriptors(const uint8_t* device_desc, const uint8_t* config_desc)
533{
534 const ra8_err_t init_check = internal_require_init();
535 if (init_check != k_ra8_ok) {
536 return init_check;
537 }
538 RA8_CHECK_NULL_PTR(device_desc, s_tag, "set_descriptors: device");
539 RA8_CHECK_NULL_PTR(config_desc, s_tag, "set_descriptors: config");
540
541 s_state.device_desc = device_desc;
542 s_state.config_desc = config_desc;
543 ra8_log_info(s_tag, "descriptors cached");
544 return k_ra8_ok;
545}
546
547/* =============================================================================
548 * Dispatch
549 * =============================================================================
550 */
551
553{
554 const ra8_err_t init_check = internal_require_init();
555 if (init_check != k_ra8_ok) {
556 return init_check;
557 }
558
559 /* The starter advances by one phase per call, looping back to IDLE.
560 * Production code drives the machine from EP0 / control completion
561 * ISRs; here we just keep it well-behaved against repeated calls. */
562 switch (s_state.state) {
565 break;
568 break;
571 break;
574 break;
576 default:
578 break;
579 }
580 return k_ra8_ok;
581}
582
583ra8_err_t ra8_usb_composite_dispatch_setup(const ra8_usb_setup_t* setup, uint8_t* out_handler_class)
584{
585 const ra8_err_t init_check = internal_require_init();
586 if (init_check != k_ra8_ok) {
587 return init_check;
588 }
589 RA8_CHECK_NULL_PTR(setup, s_tag, "dispatch_setup: setup");
590 RA8_CHECK_NULL_PTR(out_handler_class, s_tag, "dispatch_setup: out_handler");
591
592 /* USB 2.0 sec 9.3.1: bmRequestType bits [6:5] carry the request
593 * type. STANDARD goes through the composite layer; CLASS / VENDOR
594 * are routed to whichever class owns wIndex. */
595 const uint8_t type =
596 (uint8_t)(setup->bm_request_type & (uint8_t)k_ra8_usb_composite_req_type_mask);
597
598 if (type == (uint8_t)k_ra8_usb_composite_req_type_standard) {
599 s_state.last_handler_class = (uint8_t)k_ra8_usb_composite_handler_self;
600 *out_handler_class = (uint8_t)k_ra8_usb_composite_handler_self;
601 return internal_handle_standard(setup);
602 }
603
604 uint8_t class_idx = 0U;
605 const ra8_err_t route_err = internal_route_class(setup, &class_idx);
606 if (route_err == k_ra8_err_not_found) {
607 return k_ra8_err_not_found;
608 }
609 s_state.last_handler_class = class_idx;
610 *out_handler_class = class_idx;
611 return route_err;
612}
613
615{
616 const ra8_err_t init_check = internal_require_init();
617 if (init_check != k_ra8_ok) {
618 return init_check;
619 }
620 RA8_CHECK_NULL_PTR(out_count, s_tag, "get_class_count: out");
621 *out_count = s_state.class_count;
622 return k_ra8_ok;
623}
624
626{
627 const ra8_err_t init_check = internal_require_init();
628 if (init_check != k_ra8_ok) {
629 return init_check;
630 }
631 RA8_CHECK_NULL_PTR(out_desc, s_tag, "get_device_descriptor: out");
632 *out_desc = s_state.device_desc;
633 return k_ra8_ok;
634}
635
637{
638 const ra8_err_t init_check = internal_require_init();
639 if (init_check != k_ra8_ok) {
640 return init_check;
641 }
642 RA8_CHECK_NULL_PTR(out_desc, s_tag, "get_config_descriptor: out");
643 *out_desc = s_state.config_desc;
644 return k_ra8_ok;
645}
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_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_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ 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
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
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
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
static uint32_t s_state
Native USB controller driver public API (device + host modes).
static ra8_err_t internal_handle_standard(const ra8_usb_setup_t *setup)
Handle a STANDARD chapter-9 request internally.
ra8_err_t ra8_usb_composite_dispatch_setup(const ra8_usb_setup_t *setup, uint8_t *out_handler_class)
Inject a SETUP packet directly into the dispatch logic.
ra8_err_t ra8_usb_composite_get_class_count(uint8_t *out_count)
Read back the number of currently-registered class layers.
ra8_err_t ra8_usb_composite_init(ra8_usb_speed_t speed)
Bring up the composite-class driver on a chosen USB controller.
ra8_usb_composite_request_type_t
bmRequestType type-field values (USB 2.0 sec 9.3.1).
@ k_ra8_usb_composite_req_type_standard
STANDARD request.
@ k_ra8_usb_composite_req_type_mask
Mask for type field.
@ k_ra8_usb_composite_req_type_class
CLASS request.
@ k_ra8_usb_composite_req_type_vendor
VENDOR request.
ra8_err_t ra8_usb_composite_step(void)
Drive the composite dispatch state machine forward by one step.
ra8_usb_composite_std_request_t
Standard bRequest codes the composite layer answers (USB 2.0 sec 9.4 Table 9-4).
@ k_ra8_usb_composite_std_set_descriptor
SET_DESCRIPTOR.
@ k_ra8_usb_composite_std_get_configuration
GET_CONFIGURATION.
@ k_ra8_usb_composite_std_get_descriptor
GET_DESCRIPTOR.
@ k_ra8_usb_composite_std_set_configuration
SET_CONFIGURATION.
@ k_ra8_usb_composite_std_clear_feature
CLEAR_FEATURE.
@ k_ra8_usb_composite_std_set_address
SET_ADDRESS.
@ k_ra8_usb_composite_std_set_feature
SET_FEATURE.
@ k_ra8_usb_composite_std_get_status
GET_STATUS.
@ k_ra8_usb_composite_std_get_interface
GET_INTERFACE.
@ k_ra8_usb_composite_std_set_interface
SET_INTERFACE.
ra8_usb_composite_handler_sentinel_t
Marker the dispatch helper writes when the composite layer itself answered (no class fired).
@ k_ra8_usb_composite_handler_self
RA8 USB composite handler self.
usbc_mask_t
Low-byte mask for the setup-packet wIndex.
@ k_usbc_byte_mask
Usbc byte mask.
static void internal_clear_if_owners(void)
Reset every IF-ownership slot to "unowned".
static ra8_err_t internal_lookup_class_for_if(uint8_t if_num, uint8_t *out_idx)
Find the registered class that owns interface if_num.
ra8_err_t ra8_usb_composite_get_device_descriptor(const uint8_t **out_desc)
Read back the cached device descriptor pointer.
ra8_usb_composite_state_t
Composite dispatch state-machine phases.
@ k_ra8_usb_composite_state_std_dispatch
Standard request handler.
@ k_ra8_usb_composite_state_done
Reply staged.
@ k_ra8_usb_composite_state_idle
No SETUP in flight.
@ k_ra8_usb_composite_state_setup_rx
SETUP arrived.
@ k_ra8_usb_composite_state_class_dispatch
Class-routed dispatch.
ra8_usb_composite_address_limit_t
USB address ceiling (USB 2.0 sec 9.4.6).
@ k_ra8_usb_composite_max_address
7-bit USB address.
ra8_err_t ra8_usb_composite_set_descriptors(const uint8_t *device_desc, const uint8_t *config_desc)
Cache caller-owned device + configuration descriptor pointers.
ra8_err_t ra8_usb_composite_get_config_descriptor(const uint8_t **out_desc)
Read back the cached configuration descriptor pointer.
static ra8_err_t internal_require_init(void)
Pre-init guard helper used by every public entry point that is not init itself.
static ra8_err_t internal_route_class(const ra8_usb_setup_t *setup, uint8_t *out_idx)
Route a CLASS or VENDOR SETUP request to the registered class whose IF range covers setup->w_index.
static ra8_err_t internal_check_collision(const ra8_usb_composite_class_t *cl)
Detect IF-range overlap with already-registered classes.
static ra8_err_t internal_validate_class(const ra8_usb_composite_class_t *cl)
Validate caller-supplied class-layer record.
ra8_usb_composite_recipient_t
bmRequestType recipient-field values (USB 2.0 sec 9.3.1).
@ k_ra8_usb_composite_recipient_device
Device recipient.
@ k_ra8_usb_composite_recipient_mask
Mask for recipient.
@ k_ra8_usb_composite_recipient_endpoint
Endpoint recipient.
@ k_ra8_usb_composite_recipient_interface
Interface recipient.
ra8_err_t ra8_usb_composite_close(void)
Tear down the composite-class driver and release the controller.
ra8_err_t ra8_usb_composite_register_class(const ra8_usb_composite_class_t *class_layer)
Register a single class layer with the composite driver.
static void internal_mark_ownership(const ra8_usb_composite_class_t *cl, uint8_t class_index)
Mark each IF in a class's range as owned by class_index.
Native USB device-side composite-class layer.
@ k_ra8_usb_composite_max_classes
Registered class ceiling.
@ k_ra8_usb_composite_max_ifs
Highest IF number tracked.
ra8_err_t ra8_usb_device_init(ra8_usb_speed_t speed)
Bring up a USB controller in device mode.
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_device_deinit(ra8_usb_speed_t speed)
Tear down a USB controller and drop its MSTP reference.
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).
Caller-owned class-layer registration record.
ra8_usb_composite_setup_fn_t handle_setup
setup callback (non-NULL).
uint8_t interface_number_count
IF count (CDC=2, HID=1...).
ra8_usb_composite_init_fn_t init
init callback (non-NULL).
void * ctx
Opaque context.
uint8_t interface_number_first
First IF this class owns.
ra8_usb_composite_close_fn_t close
close callback (non-NULL).
Singleton shadow state for the composite-class driver.
ra8_usb_composite_class_t classes[k_ra8_usb_composite_max_classes]
Table.
const uint8_t * device_desc
Cached device descriptor.
ra8_usb_composite_state_t state
Dispatch state machine phase.
ra8_usb_speed_t speed
Underlying controller.
const uint8_t * config_desc
Cached config descriptor.
uint8_t class_count
Registered classes count.
uint8_t last_handler_class
Last dispatched class index.
uint8_t if_owner_plus_one[k_ra8_usb_composite_max_ifs]
IF -> class index + 1 (0 = unowned).
Decoded 8-byte USB SETUP packet.
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.