ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_hmsc_enum.c
Go to the documentation of this file.
1
26
27#include <stdint.h>
28
29#include "ra8_attributes.h"
30#include "ra8_check.h"
31#include "ra8_err.h"
32#include "ra8_hal_internal.h"
33#include "ra8_time.h"
34#include "ra8_usb.h"
35#include "ra8_usb_hmsc.h"
37
38static const char* s_tag = "USBHMSC";
39
40/* =============================================================================
41 * Polled enumeration ladder (hardware-proven; replaces the CTRT machine)
42 * =============================================================================
43 */
44
65
90
110{
111 const ra8_usb_setup_t setup = {
112 .bm_request_type = k_ra8_hmsc_bm_std_dev_in,
114 .w_value = (uint16_t)((uint16_t)k_ra8_hmsc_desc_device << k_ra8_hmsc_byte_bits),
115 .w_index = 0U,
116 .w_length = k_ra8_hmsc_dev_desc_len,
117 };
118 uint16_t rx = 0U;
120 &setup,
121 desc,
122 (uint16_t)k_ra8_hmsc_dev_desc_len,
123 &rx);
124 RA8_RETURN_ON_ERROR(err, s_tag, "enum: dev desc");
125 if (rx != (uint16_t)k_ra8_hmsc_dev_desc_len) {
126 return k_ra8_err_hw_error;
127 }
128 return k_ra8_ok;
129}
130
154static ra8_err_t internal_enum_hunt(uint8_t* desc, uint8_t* out_addr)
155{
157 const uint32_t t0 = ra8_time_ms();
158 for (uint32_t spin = 0U; spin < (uint32_t)k_ra8_hmsc_attach_spin_limit; spin++) {
159 if (ra8_usb_host_line_state(g_usb_hmsc_state.speed) != 0U) {
160 break;
161 }
162 if ((ra8_time_ms() - t0) > (uint32_t)k_ra8_hmsc_attach_to_ms) {
163 break;
164 }
165 }
168 for (uint8_t attempt = 0U; attempt < (uint8_t)k_ra8_hmsc_enum_tries; attempt++) {
169 if (attempt >= (uint8_t)k_ra8_hmsc_no_reset_tries) {
170 (void)ra8_usb_host_bus_reset(g_usb_hmsc_state.speed, true);
172 (void)ra8_usb_host_bus_reset(g_usb_hmsc_state.speed, false);
173 }
174 (void)ra8_usb_host_set_uact(g_usb_hmsc_state.speed, true);
176 const uint8_t addr = (uint8_t)(attempt & (uint8_t)k_ra8_hmsc_addr_alt_mask);
177 (void)ra8_usb_host_set_target(g_usb_hmsc_state.speed, addr);
178 err = internal_enum_read_dev_desc(desc);
179 if (err == k_ra8_ok) {
180 *out_addr = addr;
181 return k_ra8_ok;
182 }
183 }
184 return err;
185}
186
206static ra8_err_t internal_enum_assign_addr(uint8_t* dev_addr)
207{
208 if (*dev_addr != 0U) {
209 return k_ra8_ok;
210 }
211 const ra8_usb_setup_t setup = {
212 .bm_request_type = k_ra8_hmsc_bm_std_dev_out,
213 .b_request = k_ra8_hmsc_breq_set_address,
215 .w_index = 0U,
216 .w_length = 0U,
217 };
218 ra8_err_t err = ra8_usb_host_control_xfer(g_usb_hmsc_state.speed, &setup, nullptr, 0U, nullptr);
219 RA8_RETURN_ON_ERROR(err, s_tag, "enum: set address");
222 RA8_RETURN_ON_ERROR(err, s_tag, "enum: set target");
223 *dev_addr = (uint8_t)k_ra8_hmsc_assigned_address;
224 return k_ra8_ok;
225}
226
242static void internal_enum_note_endpoint(const uint8_t* d)
243{
244 const uint8_t attr = (uint8_t)(d[k_ra8_hmsc_off_ep_attr] & (uint8_t)k_ra8_hmsc_ep_attr_mask);
245 if (attr != (uint8_t)k_ra8_hmsc_ep_attr_bulk) {
246 return;
247 }
248 const uint8_t ea = d[k_ra8_hmsc_off_ep_addr];
249 const uint16_t mps =
250 (uint16_t)((uint16_t)d[k_ra8_hmsc_off_ep_mps] |
251 (uint16_t)((uint16_t)d[k_ra8_hmsc_off_ep_mps + 1U] << k_ra8_hmsc_byte_bits));
252 if ((ea & (uint8_t)k_ra8_hmsc_ep_dir_in_bit) != 0U) {
253 if (g_usb_hmsc_state.device.bulk_in_ep == 0U) {
254 g_usb_hmsc_state.device.bulk_in_ep = (uint8_t)(ea & (uint8_t)k_ra8_hmsc_ep_num_mask);
255 g_usb_hmsc_state.device.bulk_in_max_packet = mps;
256 }
257 } else {
258 if (g_usb_hmsc_state.device.bulk_out_ep == 0U) {
259 g_usb_hmsc_state.device.bulk_out_ep = (uint8_t)(ea & (uint8_t)k_ra8_hmsc_ep_num_mask);
260 g_usb_hmsc_state.device.bulk_out_max_packet = mps;
261 }
262 }
263}
264
283static bool internal_enum_iface_is_msc(const uint8_t* d)
284{
286 return false;
287 }
289 return false;
290 }
292 return false;
293 }
294 return true;
295}
296
317static ra8_err_t internal_enum_walk_cfg(const uint8_t* cfg, uint16_t len)
318{
319 uint16_t off = 0U;
320 bool in_msc = false;
321 while (off < len) {
322 const uint8_t dlen = cfg[off + (uint16_t)k_ra8_hmsc_off_dlen];
323 if (dlen == 0U) {
324 break;
325 }
326 const uint8_t dtype = cfg[off + (uint16_t)k_ra8_hmsc_off_dtype];
327 if (dtype == (uint8_t)k_ra8_hmsc_desc_interface) {
328 in_msc = internal_enum_iface_is_msc(&cfg[off]);
329 if (in_msc) {
330 g_usb_hmsc_state.device.interface_number = cfg[off + (uint16_t)k_ra8_hmsc_off_iface_num];
331 }
332 }
333 if (dtype == (uint8_t)k_ra8_hmsc_desc_endpoint) {
334 if (in_msc) {
336 }
337 }
338 off = (uint16_t)(off + dlen);
339 }
340 if (g_usb_hmsc_state.device.bulk_in_ep == 0U) {
341 return k_ra8_err_hw_error;
342 }
343 if (g_usb_hmsc_state.device.bulk_out_ep == 0U) {
344 return k_ra8_err_hw_error;
345 }
346 return k_ra8_ok;
347}
348
367static ra8_err_t internal_enum_read_config(uint8_t* out_cfg_value)
368{
369 uint8_t cfg[k_ra8_hmsc_cfg_buf_len] = {};
370 uint16_t rx = 0U;
371 ra8_usb_setup_t setup = {
372 .bm_request_type = k_ra8_hmsc_bm_std_dev_in,
374 .w_value = (uint16_t)((uint16_t)k_ra8_hmsc_desc_configuration << k_ra8_hmsc_byte_bits),
375 .w_index = 0U,
376 .w_length = k_ra8_hmsc_cfg_desc_len,
377 };
379 &setup,
380 cfg,
381 (uint16_t)k_ra8_hmsc_cfg_desc_len,
382 &rx);
383 RA8_RETURN_ON_ERROR(err, s_tag, "enum: cfg header");
384 uint16_t total =
385 (uint16_t)((uint16_t)cfg[k_ra8_hmsc_off_cfg_total] |
386 (uint16_t)((uint16_t)cfg[k_ra8_hmsc_off_cfg_total + 1U] << k_ra8_hmsc_byte_bits));
387 if (total > (uint16_t)k_ra8_hmsc_cfg_buf_len) {
388 total = (uint16_t)k_ra8_hmsc_cfg_buf_len;
389 }
390 *out_cfg_value = cfg[k_ra8_hmsc_off_cfg_value];
391 setup.w_length = total;
392 err = ra8_usb_host_control_xfer(g_usb_hmsc_state.speed, &setup, cfg, total, &rx);
393 RA8_RETURN_ON_ERROR(err, s_tag, "enum: cfg full");
394 return internal_enum_walk_cfg(cfg, rx);
395}
396
417static ra8_err_t internal_enum_configure(uint8_t dev_addr, uint8_t cfg_value)
418{
419 const ra8_usb_setup_t set_cfg = {
420 .bm_request_type = k_ra8_hmsc_bm_std_dev_out,
421 .b_request = k_ra8_hmsc_breq_set_config,
422 .w_value = cfg_value,
423 .w_index = 0U,
424 .w_length = 0U,
425 };
426 ra8_err_t err = ra8_usb_host_control_xfer(g_usb_hmsc_state.speed, &set_cfg, nullptr, 0U, nullptr);
427 RA8_RETURN_ON_ERROR(err, s_tag, "enum: set config");
428 const ra8_usb_setup_t get_lun = {
429 .bm_request_type = k_ra8_hmsc_bm_class_iface_in,
430 .b_request = k_ra8_hmsc_req_get_max_lun,
431 .w_value = 0U,
432 .w_index = g_usb_hmsc_state.device.interface_number,
433 .w_length = k_ra8_hmsc_get_max_lun_len,
434 };
435 uint8_t lun = 0U;
436 uint16_t lun_rx = 0U;
438 &get_lun,
439 &lun,
441 &lun_rx);
442 g_usb_hmsc_state.device.max_lun = 0U;
443 if (lun_err == k_ra8_ok) {
444 if (lun_rx == (uint16_t)k_ra8_hmsc_get_max_lun_len) {
445 g_usb_hmsc_state.device.max_lun = lun;
446 }
447 }
450 dev_addr,
451 g_usb_hmsc_state.device.bulk_in_ep,
452 true,
453 g_usb_hmsc_state.device.bulk_in_max_packet);
454 RA8_RETURN_ON_ERROR(err, s_tag, "enum: pipe in");
457 dev_addr,
458 g_usb_hmsc_state.device.bulk_out_ep,
459 false,
460 g_usb_hmsc_state.device.bulk_out_max_packet);
461}
462
477static void internal_enum_fill_ids(const uint8_t* desc)
478{
479 g_usb_hmsc_state.device.vendor_id =
480 (uint16_t)((uint16_t)desc[k_ra8_hmsc_off_dev_vid] |
481 (uint16_t)((uint16_t)desc[k_ra8_hmsc_off_dev_vid + 1U] << k_ra8_hmsc_byte_bits));
482 g_usb_hmsc_state.device.product_id =
483 (uint16_t)((uint16_t)desc[k_ra8_hmsc_off_dev_pid] |
484 (uint16_t)((uint16_t)desc[k_ra8_hmsc_off_dev_pid + 1U] << k_ra8_hmsc_byte_bits));
485}
486
503static void internal_enum_publish(uint8_t dev_addr, ra8_usb_hmsc_device_t* out_device)
504{
505 g_usb_hmsc_state.device.device_address = dev_addr;
506 g_usb_hmsc_state.attached = true;
507 if (g_usb_hmsc_state.attach_cb != nullptr) {
508 g_usb_hmsc_state.attach_cb(g_usb_hmsc_state.attach_ctx, &g_usb_hmsc_state.device);
509 }
510 if (out_device != nullptr) {
511 *out_device = g_usb_hmsc_state.device;
512 }
513}
514
533static ra8_err_t internal_enum_ladder(uint8_t* out_addr)
534{
535 uint8_t desc[k_ra8_hmsc_dev_desc_len] = {};
536 ra8_err_t err = internal_enum_hunt(desc, out_addr);
537 RA8_RETURN_ON_ERROR(err, s_tag, "enumerate: hunt");
539
540 err = internal_enum_assign_addr(out_addr);
541 RA8_RETURN_ON_ERROR(err, s_tag, "enumerate: address");
542 uint8_t cfg_value = 0U;
543 err = internal_enum_read_config(&cfg_value);
544 RA8_RETURN_ON_ERROR(err, s_tag, "enumerate: config");
545 err = internal_enum_configure(*out_addr, cfg_value);
546 RA8_RETURN_ON_ERROR(err, s_tag, "enumerate: configure");
547 return k_ra8_ok;
548}
549
567{
568 if (!g_usb_hmsc_state.initialized) {
570 }
571 g_usb_hmsc_state.attached = false;
573
574 uint8_t dev_addr = 0U;
575 const ra8_err_t err = internal_enum_ladder(&dev_addr);
576 if (err != k_ra8_ok) {
577 return err;
578 }
579 internal_enum_publish(dev_addr, out_device);
580 return k_ra8_ok;
581}
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
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_hw_timeout
Hardware timed out waiting for a flag or handshake.
Definition ra8_err.h:304
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_hw_error
Generic hardware fault detected (error flag set, fault interrupt).
Definition ra8_err.h:310
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Cross-translation-unit private helpers shared inside ra8_hal.
ra8_usb_hmsc_state_t g_usb_hmsc_state
Singleton host-MSC shadow state, shared across the ra8_usb_hmsc split.
SysTick-based tick counter, delay and timestamp helpers.
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
uint32_t ra8_time_ms(void)
Get the current 1 kHz tick count.
Definition ra8_time.c:108
Native USB controller driver public API (device + host modes).
Native USB host-side MSC (Mass Storage Class) class layer.
@ k_ra8_hmsc_protocol_bbb
Bulk-Only Transport.
@ k_ra8_hmsc_class_msc
MSC interface class.
@ k_ra8_hmsc_subclass_scsi
Transparent SCSI subclass.
@ k_ra8_hmsc_pipe_bulk_in
PIPE3 -> attached EP bulk IN.
@ k_ra8_hmsc_pipe_bulk_out
PIPE4 -> attached EP bulk OUT.
@ k_ra8_hmsc_req_get_max_lun
1-byte response.
static ra8_err_t internal_enum_read_dev_desc(uint8_t *desc)
Read the 18-byte device descriptor over the polled control engine.
static ra8_err_t internal_enum_read_config(uint8_t *out_cfg_value)
Read + parse the configuration descriptor set.
ra8_usb_hmsc_enum_tune_t
Timing / retry tunables for the polled enumeration ladder.
@ k_ra8_hmsc_vbus_settle_ms
Supply settle before probing.
@ k_ra8_hmsc_addr_settle_ms
Post-SET_ADDRESS recovery.
@ k_ra8_hmsc_cfg_buf_len
Full-configuration read buffer.
@ k_ra8_hmsc_attach_spin_limit
P10 iteration bound on the attach wait: the loop is primarily ms-bounded via ra8_time_ms,...
@ k_ra8_hmsc_reset_hold_ms
USB bus-reset hold (>=10 ms).
@ k_ra8_hmsc_no_reset_tries
Attempts before using bus reset.
@ k_ra8_hmsc_recovery_ms
Post-reset recovery (TRSTRCY).
@ k_ra8_hmsc_enum_tries
(reset?
@ k_ra8_hmsc_attach_to_ms
Wait for the D+ pull-up.
@ k_ra8_hmsc_debounce_ms
Post-attach debounce (>=100 ms).
@ k_ra8_hmsc_addr_alt_mask
Alternate addr 0..3 per attempt.
static ra8_err_t internal_enum_walk_cfg(const uint8_t *cfg, uint16_t len)
Walk a configuration blob for the MSC interface + bulk endpoints.
ra8_usb_hmsc_walk_off_t
Descriptor-walk byte offsets and identity codes.
@ k_ra8_hmsc_byte_bits
Bit width of one byte.
@ k_ra8_hmsc_off_dlen
Any descriptor: bLength.
@ k_ra8_hmsc_off_dtype
Any descriptor: bDescriptorType.
@ k_ra8_hmsc_off_ep_attr
Endpoint: bmAttributes.
@ k_ra8_hmsc_off_iface_sub
Interface: bInterfaceSubClass.
@ k_ra8_hmsc_off_iface_proto
Interface: bInterfaceProtocol.
@ k_ra8_hmsc_off_ep_mps
Endpoint: wMaxPacketSize LSB.
@ k_ra8_hmsc_off_dev_vid
Device: idVendor LSB.
@ k_ra8_hmsc_off_cfg_value
Configuration: bConfigValue.
@ k_ra8_hmsc_ep_attr_bulk
bmAttributes: bulk transfer.
@ k_ra8_hmsc_off_iface_num
Interface: bInterfaceNumber.
@ k_ra8_hmsc_off_cfg_total
Configuration: wTotalLength LSB.
@ k_ra8_hmsc_ep_num_mask
bEndpointAddress number field.
@ k_ra8_hmsc_off_ep_addr
Endpoint: bEndpointAddress.
@ k_ra8_hmsc_off_dev_pid
Device: idProduct LSB.
@ k_ra8_hmsc_ep_dir_in_bit
bEndpointAddress direction bit.
@ k_ra8_hmsc_off_iface_class
Interface: bInterfaceClass.
@ k_ra8_hmsc_ep_attr_mask
bmAttributes transfer-type mask.
static void internal_enum_fill_ids(const uint8_t *desc)
Unpack VID/PID from a device descriptor into the snapshot.
static ra8_err_t internal_enum_assign_addr(uint8_t *dev_addr)
Move the device to address 1 when it answered at the default.
ra8_err_t ra8_usb_hmsc_enumerate(ra8_usb_hmsc_device_t *out_device)
Implementation of ra8_usb_hmsc_enumerate().
static bool internal_enum_iface_is_msc(const uint8_t *d)
Test whether an interface descriptor is MSC SCSI Bulk-Only.
static ra8_err_t internal_enum_hunt(uint8_t *desc, uint8_t *out_addr)
Wait for a device to attach, then hunt for its address.
static ra8_err_t internal_enum_ladder(uint8_t *out_addr)
Run the enumeration ladder: hunt, address, configure, pipes.
static ra8_err_t internal_enum_configure(uint8_t dev_addr, uint8_t cfg_value)
SET_CONFIGURATION, best-effort GET_MAX_LUN, and pipe setup.
static void internal_enum_publish(uint8_t dev_addr, ra8_usb_hmsc_device_t *out_device)
Publish a completed enumeration: snapshot, callback, out-copy.
static void internal_enum_note_endpoint(const uint8_t *d)
Record one bulk endpoint descriptor into the device snapshot.
Cross-TU surface shared between the host-MSC class layer(ra8_usb_hmsc.c) and the polled enumeration l...
@ k_ra8_hmsc_assigned_address
First assigned device addr.
@ k_ra8_hmsc_get_max_lun_len
Get-Max-LUN response len.
@ k_ra8_hmsc_dev_desc_len
USB DEVICE descriptor.
@ k_ra8_hmsc_cfg_desc_len
CONFIGURATION descriptor hdr.
@ k_ra8_hmsc_desc_interface
INTERFACE descriptor.
@ k_ra8_hmsc_bm_std_dev_out
Std | Device | Out.
@ k_ra8_hmsc_breq_get_descriptor
GET_DESCRIPTOR.
@ k_ra8_hmsc_breq_set_config
SET_CONFIGURATION.
@ k_ra8_hmsc_desc_endpoint
ENDPOINT descriptor.
@ k_ra8_hmsc_desc_configuration
CONFIGURATION descriptor.
@ k_ra8_hmsc_desc_device
DEVICE descriptor.
@ k_ra8_hmsc_bm_std_dev_in
Std | Device | In.
@ k_ra8_hmsc_bm_class_iface_in
Class | Interface | In.
@ k_ra8_hmsc_breq_set_address
SET_ADDRESS.
ra8_err_t ra8_usb_host_bus_reset(ra8_usb_speed_t speed, bool assert_reset)
Drive (or release) the bus reset line.
uint16_t ra8_usb_host_line_state(ra8_usb_speed_t speed)
Read the host port's D+/D- line state (SYSSTS0.LNST).
ra8_err_t ra8_usb_host_pipe_setup(ra8_usb_speed_t speed, uint8_t pipe_num, uint8_t dev_addr, uint8_t ep_num, bool device_to_host, uint16_t max_packet)
Configure a controller pipe against an attached device's bulk endpoint.
ra8_err_t ra8_usb_host_control_xfer(ra8_usb_speed_t speed, const ra8_usb_setup_t *setup, uint8_t *data, uint16_t data_len, uint16_t *out_received)
Run a complete host control transfer (SETUP, optional DATA, STATUS).
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.
ra8_err_t ra8_usb_host_set_target(ra8_usb_speed_t speed, uint8_t dev_addr)
Retarget the host's default control pipe at a device address.
Snapshot of the attached MSC device, passed to the attach callback.
Decoded 8-byte USB SETUP packet.
uint16_t w_length
wLength.