ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_pmsc.c
Go to the documentation of this file.
1
43
44#include "ra8_usb_pmsc.h"
45
46#include <stdint.h>
47
48#include "ra8_attributes.h"
49#include "ra8_check.h"
50#include "ra8_err.h"
51#include "ra8_log.h"
52#include "ra8_usb.h"
54
55static const char* s_tag = "USBPMSC";
56
57/* =============================================================================
58 * Internal constants
59 * =============================================================================
60 */
61
78
92
101
109typedef enum : uint32_t {
113
118typedef enum : uint32_t {
121
133
134/* =============================================================================
135 * Internal state
136 * =============================================================================
137 */
138
140
141/* =============================================================================
142 * Internal helpers
143 * =============================================================================
144 */
145
168
186{
187 const uint16_t bulk_mp = internal_bulk_max_packet(g_usb_pmsc_state.speed);
188
189 /* Both calls receive the init-validated speed and compile-time pipe tuples
190 * that satisfy every `ra8_usb_configure_endpoint` argument guard. */
196 bulk_mp);
202 bulk_mp);
203}
204
219static void internal_pack_u32_le(uint32_t value, uint8_t* dst)
220{
221 dst[0] = (uint8_t)((value >> k_ra8_pmsc_shift_byte0) & k_ra8_pmsc_byte_mask);
222 dst[1] = (uint8_t)((value >> k_ra8_pmsc_shift_byte1) & k_ra8_pmsc_byte_mask);
223 dst[2] = (uint8_t)((value >> k_ra8_pmsc_shift_byte2) & k_ra8_pmsc_byte_mask);
224 dst[3] = (uint8_t)((value >> k_ra8_pmsc_shift_byte3) & k_ra8_pmsc_byte_mask);
225}
226
242static uint32_t internal_unpack_u32_le(const uint8_t* src)
243{
244 return ((uint32_t)src[0] << k_ra8_pmsc_shift_byte0) |
245 ((uint32_t)src[1] << k_ra8_pmsc_shift_byte1) |
246 ((uint32_t)src[2] << k_ra8_pmsc_shift_byte2) |
247 ((uint32_t)src[3] << k_ra8_pmsc_shift_byte3);
248}
249
250void priv_zero_bytes(uint8_t* dst, uint32_t len)
251{
252 for (uint32_t i = 0U; i < len; ++i) {
253 dst[i] = 0U;
254 }
255}
256
272static void internal_copy_bytes(uint8_t* dst, const uint8_t* src, uint32_t len)
273{
274 for (uint32_t i = 0U; i < len; ++i) {
275 dst[i] = src[i];
276 }
277}
278
279/* =============================================================================
280 * BOT state machine -- public entry points
281 * =============================================================================
282 */
283
285{
286 RA8_CHECK_NULL_PTR(cbw, s_tag, "feed_cbw: cbw");
287 if (!g_usb_pmsc_state.initialized) {
289 }
290 if (!g_usb_pmsc_state.storage_attached) {
292 }
293
294 /* USB MSC BBB rev 1.0 sec 6.2.1 "Valid CBW" -- signature must be
295 * 'USBC' little-endian. */
296 const uint32_t signature = internal_unpack_u32_le(&cbw[k_ra8_pmsc_cbw_off_signature]);
297 if (signature != k_ra8_pmsc_cbw_signature) {
298 /* Spec sec 6.6.1 "CBW Not Valid": stall both bulk pipes and
299 * await reset recovery. The starter surfaces this as a CSW
300 * with status `phase_error` so the caller's state machine can
301 * proceed deterministically. */
305 }
306
311 g_usb_pmsc_state.cbw_cdb_len =
315 (uint32_t)k_ra8_pmsc_cdb_max_len);
317 g_usb_pmsc_state.last_data_len = 0U;
318 return k_ra8_ok;
319}
320
343static ra8_err_t internal_dispatch_scsi(uint8_t* data_buf,
344 uint32_t data_buf_capacity,
345 uint32_t* data_len,
346 ra8_usb_pmsc_csw_status_t* csw_status)
347{
348 const uint8_t opcode = g_usb_pmsc_state.cbw_cdb[k_ra8_pmsc_cdb_off_opcode];
349 switch (opcode) {
351 /* Passing -- no data phase. */
352 return k_ra8_ok;
354 return priv_handle_inquiry(data_buf, data_buf_capacity, data_len);
356 return priv_handle_read_capacity(data_buf, data_buf_capacity, data_len);
358 return priv_handle_request_sense(data_buf, data_buf_capacity, data_len);
360 return priv_handle_mode_sense(data_buf, data_buf_capacity, data_len);
362 return priv_handle_read10(data_buf, data_buf_capacity, data_len);
364 return priv_handle_write10(data_buf, data_len);
365 default:
366 /* Unsupported opcode -> CSW status FAILED, no data. */
367 *csw_status = k_ra8_pmsc_csw_status_failed;
368 return k_ra8_ok;
369 }
370}
371
388static ra8_err_t internal_dispatch_preconditions(uint32_t data_buf_capacity)
389{
390 if (!g_usb_pmsc_state.initialized) {
392 }
393 if (!g_usb_pmsc_state.storage_attached) {
395 }
398 }
399 if (data_buf_capacity == 0U) {
401 }
402 return k_ra8_ok;
403}
404
418static void internal_dispatch_advance_state(uint32_t data_len)
419{
420 g_usb_pmsc_state.last_data_len = data_len;
421 if (data_len > 0U) {
422 g_usb_pmsc_state.bot_state =
424 } else {
426 }
427}
428
430 uint32_t data_buf_capacity,
431 uint32_t* data_len,
432 ra8_usb_pmsc_csw_status_t* csw_status)
433{
434 RA8_CHECK_NULL_PTR(data_buf, s_tag, "dispatch: data_buf");
435 RA8_CHECK_NULL_PTR(data_len, s_tag, "dispatch: data_len");
436 RA8_CHECK_NULL_PTR(csw_status, s_tag, "dispatch: csw_status");
437 const ra8_err_t pre_err = internal_dispatch_preconditions(data_buf_capacity);
438 if (pre_err != k_ra8_ok) {
439 return pre_err;
440 }
441
442 *data_len = 0U;
443 *csw_status = k_ra8_pmsc_csw_status_passed;
444 const ra8_err_t err = internal_dispatch_scsi(data_buf, data_buf_capacity, data_len, csw_status);
445 if (err != k_ra8_ok) {
446 *csw_status = k_ra8_pmsc_csw_status_failed;
447 *data_len = 0U;
448 }
450 return k_ra8_ok;
451}
452
454ra8_usb_pmsc_build_csw(ra8_usb_pmsc_csw_status_t csw_status, uint32_t residue, uint8_t* out_csw)
455{
456 RA8_CHECK_NULL_PTR(out_csw, s_tag, "build_csw: out_csw");
457 if (!g_usb_pmsc_state.initialized) {
459 }
460
461 priv_zero_bytes(out_csw, (uint32_t)k_ra8_pmsc_csw_len);
465 out_csw[k_ra8_pmsc_csw_off_status] = (uint8_t)csw_status;
467 return k_ra8_ok;
468}
469
471{
472 if (!g_usb_pmsc_state.initialized) {
474 }
475 if (!g_usb_pmsc_state.storage_attached) {
477 }
478
479 /* Each invocation advances by exactly one phase. The IDLE phase
480 * is observable through the public state; in production the
481 * caller pumps `step` from the bulk-OUT-completion ISR until the
482 * machine returns to IDLE waiting for the next CBW. The CBW_RX
483 * and CDB_DECODE phases are caller-driven: the application drains
484 * the bulk-OUT FIFO and invokes `ra8_usb_pmsc_feed_cbw` /
485 * `ra8_usb_pmsc_dispatch_command` directly, so `step` leaves
486 * those phases untouched. */
487 switch (g_usb_pmsc_state.bot_state) {
489 /* Awaiting CBW. */
491 break;
494 /* Caller drives the CBW capture / CDB dispatch directly. */
495 break;
498 /* Caller pushes / pulls the data phase; transition to CSW. */
500 break;
502 default:
503 /* CSW sent (or unknown state); rewind to IDLE. */
505 break;
506 }
507 return k_ra8_ok;
508}
509
510/* =============================================================================
511 * Storage backend
512 * =============================================================================
513 */
514
516{
517 if (!g_usb_pmsc_state.initialized) {
519 }
520 RA8_CHECK_NULL_PTR(storage, s_tag, "attach_storage: storage");
521 RA8_CHECK_NULL_PTR(storage->read_block, s_tag, "attach_storage: read_block");
522 RA8_CHECK_NULL_PTR(storage->write_block, s_tag, "attach_storage: write_block");
523 RA8_CHECK_NULL_PTR(storage->get_capacity, s_tag, "attach_storage: get_capacity");
524 RA8_CHECK_NULL_PTR(storage->get_inquiry, s_tag, "attach_storage: get_inquiry");
525
526 g_usb_pmsc_state.storage = *storage;
527 g_usb_pmsc_state.storage_attached = true;
529 ra8_log_info(s_tag, "storage attached");
530 return k_ra8_ok;
531}
532
533/* =============================================================================
534 * Lifecycle
535 * =============================================================================
536 */
537
539{
540 if ((speed != k_ra8_usb_speed_fs) && (speed != k_ra8_usb_speed_hs)) {
542 }
543 const ra8_err_t usb_err = ra8_usb_device_init(speed);
544 if (usb_err != k_ra8_ok) {
545 ra8_log_error_val(s_tag, "ra8_usb_device_init failed", (uint32_t)usb_err);
547 }
548
549 g_usb_pmsc_state.speed = speed;
551 g_usb_pmsc_state.storage_attached = false;
554 g_usb_pmsc_state.cbw_data_length = 0U;
555 g_usb_pmsc_state.cbw_dir_in = false;
556 g_usb_pmsc_state.cbw_lun = 0U;
557 g_usb_pmsc_state.cbw_cdb_len = 0U;
558 g_usb_pmsc_state.last_data_len = 0U;
560 g_usb_pmsc_state.initialized = true;
561
563
564 ra8_log_info_val(s_tag, "device-MSC ready", (uint32_t)speed);
565 return k_ra8_ok;
566}
567
569{
570 if (!g_usb_pmsc_state.initialized) {
572 }
573 /* Drop D+ pull-up so the host sees a clean detach. */
574 (void)ra8_usb_device_attach(g_usb_pmsc_state.speed, false);
576 g_usb_pmsc_state.initialized = false;
577 g_usb_pmsc_state.storage_attached = false;
579 return err;
580}
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_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
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
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
Native USB controller driver public API (device + host modes).
@ k_ra8_usb_ep_type_bulk
Bulk transfer.
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_device_deinit(ra8_usb_speed_t speed)
Tear down a USB controller and drop its MSTP reference.
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.
ra8_err_t ra8_usb_device_attach(ra8_usb_speed_t speed, bool attached)
Raise / drop the D+ pull-up to advertise the device to the host.
@ 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_usb_pmsc_csw_offset_t
Byte offsets inside the 13-byte CSW.
@ k_ra8_pmsc_csw_off_status
bCSWStatus.
@ k_ra8_pmsc_csw_off_signature
dCSWSignature [4].
@ k_ra8_pmsc_csw_off_tag
dCSWTag [4].
@ k_ra8_pmsc_csw_off_residue
dCSWDataResidue.
static void internal_configure_pipes(void)
Configure the two device-MSC bulk pipes against the local endpoints.
ra8_usb_pmsc_initial_tag_t
Initial value of the cached BOT dCBWTag.
@ k_ra8_pmsc_initial_tag
Reset value.
ra8_err_t ra8_usb_pmsc_close(void)
Tear down the device-MSC driver and release the controller.
static uint32_t internal_unpack_u32_le(const uint8_t *src)
Unpack a uint32 from 4 little-endian bytes.
static void internal_dispatch_advance_state(uint32_t data_len)
Advance BOT state after a successful CDB dispatch.
ra8_usb_pmsc_cbw_offset_t
Byte offsets inside the 31-byte CBW header.
@ k_ra8_pmsc_cbw_off_flags
bmCBWFlags.
@ k_ra8_pmsc_cbw_off_lun
bCBWLUN (low nib).
@ k_ra8_pmsc_cbw_off_cdb
CBWCB [16].
@ k_ra8_pmsc_cbw_off_tag
dCBWTag [4].
@ k_ra8_pmsc_cbw_off_signature
dCBWSignature [4].
@ k_ra8_pmsc_cbw_off_cdb_length
bCBWCBLength (low 5).
@ k_ra8_pmsc_cbw_off_data_length
dCBWDataTransferLen.
void priv_zero_bytes(uint8_t *dst, uint32_t len)
Zero len bytes at dst byte-by-byte.
ra8_err_t ra8_usb_pmsc_dispatch_command(uint8_t *data_buf, uint32_t data_buf_capacity, uint32_t *data_len, ra8_usb_pmsc_csw_status_t *csw_status)
Run the SCSI command currently parked in the BOT state.
ra8_usb_pmsc_state_data_t g_usb_pmsc_state
Singleton shadow state shared by both device-MSC TUs.
static void internal_pack_u32_le(uint32_t value, uint8_t *dst)
Pack a uint32 into 4 little-endian bytes.
static ra8_err_t internal_dispatch_preconditions(uint32_t data_buf_capacity)
Validate dispatch_command preconditions (driver state + bot phase + buffer capacity).
ra8_err_t ra8_usb_pmsc_init(ra8_usb_speed_t speed)
Bring up the device-MSC driver on a chosen USB controller.
ra8_err_t ra8_usb_pmsc_build_csw(ra8_usb_pmsc_csw_status_t csw_status, uint32_t residue, uint8_t *out_csw)
Build the 13-byte CSW for the in-flight command.
static void internal_copy_bytes(uint8_t *dst, const uint8_t *src, uint32_t len)
Copy len bytes from src to dst byte-by-byte.
static ra8_err_t internal_dispatch_scsi(uint8_t *data_buf, uint32_t data_buf_capacity, uint32_t *data_len, ra8_usb_pmsc_csw_status_t *csw_status)
Dispatch the cached CDB to the right SCSI handler.
ra8_usb_pmsc_signature_t
BOT wrapper signatures (USB MSC BBB rev 1.0 sec 5).
@ k_ra8_pmsc_csw_signature
'USBS' little-endian.
@ k_ra8_pmsc_cbw_signature
'USBC' little-endian.
ra8_err_t ra8_usb_pmsc_step(void)
Drive the BOT (Bulk-Only Transport) state machine forward by one step.
ra8_err_t ra8_usb_pmsc_feed_cbw(const uint8_t *cbw)
Inject a CBW directly into the BOT state machine.
ra8_usb_pmsc_cbw_flag_t
bmCBWFlags direction bit values.
@ k_ra8_pmsc_cbw_flag_data_in
Device -> host.
@ k_ra8_pmsc_cbw_flag_data_out
Host -> device.
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_pmsc_attach_storage(const ra8_usb_pmsc_storage_t *storage)
Bind a caller-owned storage backend to the device-MSC class.
ra8_usb_pmsc_lun_mask_t
Field-width masks for bCBWLUN + bCBWCBLength bytes.
@ k_ra8_pmsc_lun_field_mask
Low 4 bits of bCBWLUN.
@ k_ra8_pmsc_cdb_field_mask
Low 5 bits of bCBWCBLen.
Native USB device-side MSC (Mass Storage Class) class layer.
@ k_ra8_pmsc_pipe_bulk_out
PIPE4 -> local EP bulk OUT.
@ k_ra8_pmsc_pipe_bulk_in
PIPE3 -> local EP bulk IN.
@ k_ra8_pmsc_ep_bulk_in
Bulk-IN endpoint number.
@ k_ra8_pmsc_ep_bulk_out
Bulk-OUT endpoint number.
@ k_ra8_pmsc_bulk_max_packet_fs
Bulk size at full speed.
@ k_ra8_pmsc_bulk_max_packet_hs
Bulk size at high speed.
ra8_usb_pmsc_csw_status_t
CSW status field values the device returns after a CBW.
@ k_ra8_pmsc_csw_status_passed
Command succeeded.
@ k_ra8_pmsc_csw_status_failed
Command failed.
@ k_ra8_pmsc_scsi_write_10
WRITE(10).
@ k_ra8_pmsc_scsi_read_capacity_10
READ CAPACITY(10).
@ k_ra8_pmsc_scsi_read_10
READ(10).
@ k_ra8_pmsc_scsi_test_unit_ready
TEST UNIT READY.
@ k_ra8_pmsc_scsi_inquiry
INQUIRY.
@ k_ra8_pmsc_scsi_request_sense
REQUEST SENSE.
@ k_ra8_pmsc_scsi_mode_sense_6
MODE SENSE(6).
Cross-TU surface shared between the device-MSC BOT statemachine (ra8_usb_pmsc.c) and the SCSI command...
ra8_err_t priv_handle_write10(const uint8_t *data_buf, uint32_t *out_len)
Run a SCSI WRITE(10) – the data buffer holds the host-supplied payload.
@ k_ra8_pmsc_shift_byte3
RA8 pmsc shift byte3.
@ k_ra8_pmsc_shift_byte1
RA8 pmsc shift byte1.
@ k_ra8_pmsc_shift_byte0
RA8 pmsc shift byte0.
@ k_ra8_pmsc_shift_byte2
RA8 pmsc shift byte2.
ra8_err_t priv_handle_read10(uint8_t *data_buf, uint32_t capacity, uint32_t *out_len)
Run a SCSI READ(10).
ra8_err_t priv_handle_inquiry(uint8_t *data_buf, uint32_t capacity, uint32_t *out_len)
Build the 36-byte SCSI INQUIRY response.
@ k_ra8_pmsc_cdb_off_opcode
Operation Code (CDB byte 0).
@ k_ra8_pmsc_byte_mask
RA8 pmsc byte mask.
ra8_err_t priv_handle_mode_sense(uint8_t *data_buf, uint32_t capacity, uint32_t *out_len)
Build the minimal 4-byte MODE SENSE(6) header response.
ra8_err_t priv_handle_request_sense(uint8_t *data_buf, uint32_t capacity, uint32_t *out_len)
Build the 18-byte REQUEST SENSE response.
@ k_ra8_pmsc_cdb_max_len
CDB ceiling.
@ k_ra8_pmsc_csw_len
CSW length (BBB sec 5.2).
ra8_err_t priv_handle_read_capacity(uint8_t *data_buf, uint32_t capacity, uint32_t *out_len)
Build the 8-byte READ_CAPACITY(10) response.
@ k_ra8_pmsc_state_idle
Pre-CBW.
@ k_ra8_pmsc_state_data_tx
Push data on bulk-IN.
@ k_ra8_pmsc_state_data_rx
Pull data on bulk-OUT.
@ k_ra8_pmsc_state_cbw_rx
CBW arrived on bulk-OUT.
@ k_ra8_pmsc_state_cdb_decode
Dispatch to a SCSI handler.
@ k_ra8_pmsc_state_csw_tx
Push CSW on bulk-IN.
Singleton shadow state for the device-MSC driver.
Caller-owned storage backend bound to the device-MSC class.
ra8_usb_pmsc_get_inquiry_fn_t get_inquiry
INQUIRY hook.
ra8_usb_pmsc_read_block_fn_t read_block
Block read hook.
ra8_usb_pmsc_get_capacity_fn_t get_capacity
Capacity hook.
ra8_usb_pmsc_write_block_fn_t write_block
Block write hook.