ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_usb_vhost.c
Go to the documentation of this file.
1
14
15#include <stdio.h>
16#include <string.h>
17
18#include "board_usb_internal.h"
20#include "ra8_elc_regs.h"
21#include "ra8_usb_regs.h"
22
23/* =============================================================================
24 * Device-class awareness -- the host detects HID / MSC / CDC from the enumerated
25 * interface descriptor and surfaces the class-specific traffic after CONFIGURED,
26 * so usb_hid_device / usb_msc_device exercise their data path instead of being
27 * mislabelled "CDC-ACM active".
28 * =============================================================================
29 */
30
31uint8_t s_dev_class;
32uint32_t s_hid_reports;
33int32_t s_hid_cx;
34int32_t s_hid_cy;
36
39{
41 case k_usb_class_hid:
42 return "HID active";
43 case k_usb_class_msc:
44 return "MSC active";
45 case k_usb_class_cdc:
46 return "CDC-ACM active";
48 return "Printer active";
50 return "Vendor active";
52 default:
53 return "configured";
54 }
55}
56
69void priv_usb_detect_class(const uint8_t* d, uint16_t len)
70{
71 if (s_dev_class != (uint8_t)k_usb_class_unknown) {
72 return;
73 }
74 uint16_t i = 0U;
75 while (((uint32_t)i + 2U) <= (uint32_t)len) {
76 const uint8_t blen = d[i];
77 const uint8_t btype = d[i + 1U];
78 if (blen == 0U) {
79 break;
80 }
81 if ((btype == (uint8_t)k_usb_dt_interface) && (((uint32_t)i + 6U) <= (uint32_t)len)) {
82 const uint8_t icls = d[i + k_iface_class_off];
83 if (icls == (uint8_t)k_usb_iclass_hid) {
85 return;
86 }
87 if (icls == (uint8_t)k_usb_iclass_msc) {
89 return;
90 }
91 if ((icls == (uint8_t)k_usb_iclass_cdc_comm) || (icls == (uint8_t)k_usb_iclass_cdc_data)) {
93 return;
94 }
95 if (icls == (uint8_t)k_usb_iclass_printer) {
97 return;
98 }
99 if (icls == (uint8_t)k_usb_iclass_vendor) {
101 return;
102 }
103 }
104 i = (uint16_t)((uint32_t)i + (uint32_t)blen);
105 }
106}
107
123RA8_INTERNAL static void internal_usb_hid_decode_report(const uint8_t* d, uint16_t len)
124{
125 if (len < 3U) {
126 return;
127 }
128 s_hid_buttons = d[0];
129 s_hid_cx += (int32_t)(int8_t)d[1];
130 s_hid_cy += (int32_t)(int8_t)d[2];
132 if (s_trace) {
133 (void)priv_emu_io_errf(" [usb] HID mouse report: btn=0x%02X dx=%+d dy=%+d -> cursor (%d,%d)\n",
134 (unsigned)s_hid_buttons,
135 (int)(int8_t)d[1],
136 (int)(int8_t)d[2],
137 (int)s_hid_cx,
138 (int)s_hid_cy);
139 }
140}
141
142/* =============================================================================
143 * Virtual USB host -- chapter-9 enumeration script + step machine.
144 * =============================================================================
145 */
146
158 (uint16_t)(k_usb_dt_device << 8),
159 0U,
160 (uint16_t)k_usb_desc8_len,
161 "GET_DESCRIPTOR(device,8)"},
164 (uint16_t)(k_usb_dt_device << 8),
165 0U,
166 (uint16_t)k_usb_desc_dev_len,
167 "GET_DESCRIPTOR(device,full)"},
170 (uint16_t)k_usb_addr_assigned,
171 0U,
172 0U,
173 "SET_ADDRESS(7)"},
176 (uint16_t)(k_usb_dt_device << 8),
177 0U,
178 (uint16_t)k_usb_desc_dev_len,
179 "GET_DESCRIPTOR(device,full)"},
182 (uint16_t)(k_usb_dt_config << 8),
183 0U,
184 (uint16_t)k_usb_cfg_probe_len,
185 "GET_DESCRIPTOR(config,9)"},
188 (uint16_t)(k_usb_dt_config << 8),
189 0U,
190 (uint16_t)k_usb_cfg_full_cap,
191 "GET_DESCRIPTOR(config,full)"},
194 (uint16_t)(k_usb_dt_string << 8),
195 0U,
196 (uint16_t)k_usb_str_len,
197 "GET_DESCRIPTOR(string,0)"},
200 (uint16_t)k_usb_config_value,
201 0U,
202 0U,
203 "SET_CONFIGURATION(1)"},
206 0U,
207 0U,
209 "SET_LINE_CODING"},
212 0x0003U,
213 0U,
214 0U,
215 "SET_CONTROL_LINE_STATE"},
216};
217
220{
221 const uint16_t syscfg = s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_syscfg)];
222 return (syscfg & (uint16_t)(1U << k_ra8_syscfg_bit_dprpu)) != 0U;
223}
224
235{
237 (uint16_t)(s->bm_request_type | (uint16_t)((uint16_t)s->b_request << k_usb_byte_bits));
241}
242
255{
256 if ((s->bm_request_type & (uint8_t)k_usb_dir_device_to_host) != 0U) {
257 return (uint16_t)k_ra8_ctsq_rdds; /* control read data stage. */
258 }
259 if (s->w_length != 0U) {
260 return (uint16_t)k_ra8_ctsq_wrds; /* control write data stage. */
261 }
262 return (uint16_t)k_ra8_ctsq_wrnd; /* no-data control. */
263}
264
266void priv_host_deliver_setup(uc_engine* uc, const usb_setup_step_t* s)
267{
268 /* Clear any stale DCP state from the previous transfer. */
269 s_usb.dcp_in.len = 0U;
270 s_usb.dcp_in.valid = false;
271 s_usb.dcp_out.len = 0U;
272 s_usb.dcp_out.rd = 0U;
273 s_usb.dcp_out.ready = false;
274 /* For an OUT-data control transfer, stage placeholder bytes the device can
275 * drain through the DCP if its class handler reads them. Skipped under the
276 * external-host bridge: there the REAL host payload arrives via
277 * board_usb_bridge_dcp_out(), and a placeholder would let the device drain
278 * zeros before it lands. */
279 if (!s_external_host && ((s->bm_request_type & (uint8_t)k_usb_dir_device_to_host) == 0U) &&
280 (s->w_length != 0U)) {
281 const uint16_t n =
282 (s->w_length < (uint16_t)k_usb_dcp_mps) ? s->w_length : (uint16_t)k_usb_dcp_mps;
283 s_usb.dcp_out.len = n;
284 s_usb.dcp_out.rd = 0U;
285 s_usb.dcp_out.ready = true;
286 (void)memset(s_usb.dcp_out.data, 0, sizeof(s_usb.dcp_out.data));
287 }
289 s_usb.setup_valid = true;
292 char line[k_usb_log_width];
293 (void)snprintf(line, sizeof(line), "SETUP[%u] %s -> CTRT raised", (unsigned)s_host_step, s->name);
294 priv_usb_log_line(line);
296}
297
307{
308 priv_usb_log_count("control-IN: device returned", (unsigned)s_usb.dcp_in.len);
309 priv_usb_detect_class(s_usb.dcp_in.data, s_usb.dcp_in.len);
310 s_usb.dcp_in.len = 0U;
311 s_usb.dcp_in.valid = false;
312}
313
316{
317 const uint16_t dcpctr = s_usb.reg[internal_usb_word((uint64_t)k_ra8_usb_off_dcpctr)];
318 return (dcpctr & (uint16_t)k_ra8_pid_mask) == (uint16_t)k_ra8_pid_buf;
319}
320
323{
324 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_dcpctr);
325 const uint16_t ccpl = (uint16_t)(1U << k_ra8_dcpctr_bit_ccpl);
326 const bool seen = (s_usb.reg[w] & ccpl) != 0U;
327 if (seen) {
328 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] & (uint16_t)~ccpl); /* SIE clears CCPL. */
329 }
330 return seen;
331}
332
334void priv_host_apply_no_data(uc_engine* uc, const usb_setup_step_t* s)
335{
336 if (s->b_request == (uint8_t)k_usb_req_set_address) {
337 /* The SIE latches USBADDR and owns the IN-ZLP status stage itself; mirror
338 * that and advance the device state to Address (HUM Ch 36.3). */
340 (uint16_t)(s->w_value & (uint16_t)k_ra8_usbaddr_addr_mask);
341 s_usb.dvsq = (uint16_t)k_ra8_dvsq_address;
343 char line[k_usb_log_width];
344 (void)
345 snprintf(line, sizeof(line), "SET_ADDRESS: DVSQ -> Address (addr=%u)", (unsigned)s->w_value);
346 priv_usb_log_line(line);
348 }
349}
350
361RA8_INTERNAL static void internal_host_step_deliver(uc_engine* uc, const usb_setup_step_t* s)
362{
364 s_host_wait = 0U;
365 if ((s->bm_request_type & (uint8_t)k_usb_dir_device_to_host) != 0U) {
366 s_host_substate = (uint8_t)k_sub_wait_in; /* control read: wait for data. */
367 } else if (s->b_request == (uint8_t)k_usb_req_set_address) {
369 s_host_substate = (uint8_t)k_sub_next; /* SIE-handled; no device CCPL. */
370 } else {
371 s_host_substate = (uint8_t)k_sub_wait_ack; /* no/OUT-data: wait for CCPL. */
372 }
373}
374
384{
385 if (s_usb.dcp_in.valid && priv_host_dcp_pid_buf()) {
387 s_host_substate = (uint8_t)k_sub_status;
388 s_host_wait = 0U;
389 return;
390 }
391 s_host_wait++;
392 if (s_host_wait > (uint32_t)k_usb_step_timeout) {
393 priv_usb_log_count("STALLED waiting for control-IN data at step", (unsigned)s_host_step);
394 s_host_substate = (uint8_t)k_sub_status; /* push on; report the stall. */
395 s_host_wait = 0U;
396 }
397}
398
408RA8_INTERNAL static void internal_host_step_status(uc_engine* uc)
409{
410 s_usb.ctsq = (uint16_t)k_ra8_ctsq_rdss; /* read status stage. */
411 s_usb.setup_valid = false;
415 s_host_wait = 0U;
416}
417
427{
428 if (priv_host_take_ccpl()) {
429 s_host_substate = (uint8_t)k_sub_next;
430 s_host_wait = 0U;
431 return;
432 }
433 s_host_wait++;
434 if (s_host_wait > (uint32_t)k_usb_step_timeout) {
435 s_host_substate = (uint8_t)k_sub_next; /* device may auto-ack; move on. */
436 s_host_wait = 0U;
437 }
438}
439
451{
452 return (uint32_t)(sizeof(s_k_enum_script) / sizeof(s_k_enum_script[0]));
453}
454
456void priv_host_mark_configured(uc_engine* uc)
457{
458 s_usb.dvsq = (uint16_t)k_ra8_dvsq_configured;
461 if (!s_configured) {
462 priv_usb_log_line("SET_CONFIGURATION done: DVSQ -> Configured");
463 }
464}
465
476RA8_INTERNAL static void internal_host_step_next(uc_engine* uc, const usb_setup_step_t* s)
477{
478 if (s->b_request == (uint8_t)k_usb_req_set_config) {
480 }
481 s_host_step++;
483 s_host_wait = 0U;
486 s_host_wait = 0U;
487 }
488}
489
491void priv_host_run_setup_phase(uc_engine* uc)
492{
495 return;
496 }
499 case k_sub_deliver:
501 break;
502 case k_sub_wait_in:
504 break;
505 case k_sub_status:
507 break;
508 case k_sub_wait_ack:
510 break;
511 case k_sub_next:
512 default:
514 break;
515 }
516}
517
519void priv_host_run_idle_phase(uc_engine* uc)
520{
522 return;
523 }
524 priv_usb_log_line("device pull-up detected (SYSCFG.DPRPU) -> bus reset");
525 s_usb.dvsq = (uint16_t)k_ra8_dvsq_default;
528 s_host_phase = (uint8_t)k_phase_reset;
529 s_host_wait = 0U;
530}
531
533void priv_host_run_reset_phase(uc_engine* uc)
534{
535 /* Keep DVSQ at Default and re-raise DVST so the bridge's busreset_rearm runs
536 * (it re-defaults DCPCFG/DCPMAXP/PIPECTR/INTENB0 -- HUM Ch 36.3). */
537 s_usb.dvsq = (uint16_t)k_ra8_dvsq_default;
538 if (s_host_wait == 0U) {
541 }
542 s_host_wait++;
543 if (s_host_wait >= (uint32_t)k_usb_reset_settle) {
544 s_host_phase = (uint8_t)k_phase_setup;
545 s_host_step = 0U;
547 s_host_wait = 0U;
548 }
549}
550
561{
562 const uint32_t remaining = s_echo_out_len - s_echo_out_sent;
563 const uint32_t chunk = (remaining < (uint32_t)k_usb_pipe_mps) ? remaining : k_usb_pipe_mps;
565 (void)memcpy(b->data, &s_echo_out[s_echo_out_sent], chunk);
566 b->len = (uint16_t)chunk;
567 b->rd = 0U;
568 b->ready = true;
569 /* BRDYSTS bit for the OUT pipe: an OUT packet has landed (HUM Ch 36.2.12). */
570 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
571 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << k_usb_bulk_out_pipe));
573 s_echo_out_sent += chunk;
574 priv_usb_log_count("bulk OUT: delivered to data pipe", (unsigned)chunk);
576}
577
589void priv_host_echo_read_in(uc_engine* uc)
590{
592 if (b->valid && (b->len > 0U)) {
593 if (s_dev_class == (uint8_t)k_usb_class_hid) {
594 internal_usb_hid_decode_report(b->data, b->len); /* interrupt-IN mouse report. */
595 } else {
596 s_echo_in_got += b->len;
597 priv_usb_log_count("bulk IN: read echoed bytes from data pipe", (unsigned)b->len);
598 }
599 b->len = 0U;
600 b->valid = false;
601 /* Acknowledge the IN transfer (BEMP) so the device can queue the next. */
602 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_bempsts);
603 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << k_usb_bulk_in_pipe));
606 } else if (b->valid) {
607 b->valid = false; /* drained ZLP terminator. */
608 }
609}
610
611/* =============================================================================
612 * MSC Bulk-Only Transport host -- drives SCSI against the device's RAM disk so
613 * usb_msc_device actually serves storage (CBW -> data -> CSW per command).
614 * =============================================================================
615 */
616
618typedef enum : uint8_t {
624
631
633typedef enum : uint32_t {
640
641static uint8_t s_msc_phase;
642static uint8_t s_msc_cmd;
643static uint32_t s_msc_tag;
644static uint32_t s_msc_data_len;
645static uint32_t s_msc_data_got;
646static uint32_t s_msc_wait;
647uint32_t s_msc_blocks;
651
665RA8_INTERNAL static void
666internal_host_msc_build_cdb(uint8_t cmd, uint8_t* cdb, uint8_t* cdb_len, uint32_t* data_len)
667{
668 for (uint32_t i = 0U; i < 16U; i++) {
669 cdb[i] = 0U;
670 }
671 switch ((msc_script_cmd_t)cmd) {
672 case k_msc_cmd_inquiry: /* INQUIRY: standard data, 36-byte allocation. */
673 cdb[0] = (uint8_t)k_scsi_inquiry;
674 cdb[4] = (uint8_t)k_inquiry_len;
675 *cdb_len = 6U;
676 *data_len = (uint32_t)k_inquiry_len;
677 break;
678 case k_msc_cmd_read_capacity: /* READ CAPACITY (10): last LBA + block size. */
679 cdb[0] = (uint8_t)k_scsi_read_capacity;
680 *cdb_len = (uint8_t)k_cdb10_len;
681 *data_len = 8U;
682 break;
683 case k_msc_cmd_read10: /* READ (10): LBA 0, one block (512 bytes). */
684 default:
685 cdb[0] = (uint8_t)k_scsi_read10;
686 cdb[8] = 1U; /* transfer length = 1 block (big-endian low byte). */
687 *cdb_len = (uint8_t)k_cdb10_len;
688 *data_len = (uint32_t)k_sector_bytes;
689 break;
690 }
691}
692
702RA8_INTERNAL static void internal_host_msc_send_cbw(uc_engine* uc)
703{
704 uint8_t cdb[16];
705 uint8_t cdb_len = 0U;
706 uint32_t data_len = 0U;
707 internal_host_msc_build_cdb(s_msc_cmd, cdb, &cdb_len, &data_len);
708 s_msc_data_len = data_len;
709 s_msc_data_got = 0U;
710 s_msc_tag++;
711
713 uint8_t* d = b->data;
714 d[0] = (uint8_t)'U'; /* dCBWSignature 'USBC' (LE 0x43425355). */
715 d[1] = (uint8_t)'S';
716 d[2] = (uint8_t)'B';
717 d[3] = (uint8_t)'C';
718 /* dCBWTag / dCBWDataTransferLength are little-endian u32 (USB MSC BOT 5.1). */
719 d[k_cbw_tag_off + k_le_lane_b0] = (uint8_t)(s_msc_tag & (uint32_t)k_usb_byte_mask);
720 d[k_cbw_tag_off + k_le_lane_b1] = (uint8_t)((s_msc_tag >> 8) & (uint32_t)k_usb_byte_mask);
721 d[k_cbw_tag_off + k_le_lane_b2] = (uint8_t)((s_msc_tag >> 16) & (uint32_t)k_usb_byte_mask);
723 (uint8_t)((s_msc_tag >> (uint32_t)k_usb_shift24) & (uint32_t)k_usb_byte_mask);
724 d[k_cbw_dtl_off + k_le_lane_b0] = (uint8_t)(data_len & (uint32_t)k_usb_byte_mask);
725 d[k_cbw_dtl_off + k_le_lane_b1] = (uint8_t)((data_len >> 8) & (uint32_t)k_usb_byte_mask);
726 d[k_cbw_dtl_off + k_le_lane_b2] = (uint8_t)((data_len >> 16) & (uint32_t)k_usb_byte_mask);
728 (uint8_t)((data_len >> (uint32_t)k_usb_shift24) & (uint32_t)k_usb_byte_mask);
729 d[k_cbw_flags_off] = (uint8_t)k_msc_flag_in; /* all scripted commands read. */
730 d[k_cbw_lun_off] = 0U; /* LUN 0. */
731 d[k_cbw_cdblen_off] = cdb_len;
732 for (uint32_t i = 0U; i < 16U; i++) {
733 d[k_cbw_cdb_off + i] = cdb[i];
734 }
735 b->len = (uint16_t)k_msc_cbw_len;
736 b->rd = 0U;
737 b->ready = true;
738 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_brdysts);
739 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << k_usb_bulk_out_pipe));
742}
743
757RA8_INTERNAL static uint16_t internal_host_msc_take_in(uc_engine* uc, uint8_t* out, uint16_t cap)
758{
760 if (!b->valid) {
761 return 0U;
762 }
763 const uint16_t n = (b->len < cap) ? b->len : cap;
764 for (uint16_t i = 0U; i < n; i++) {
765 out[i] = b->data[i];
766 }
767 b->len = 0U;
768 b->valid = false;
769 const uint32_t w = internal_usb_word((uint64_t)k_ra8_usb_off_bempsts);
770 s_usb.reg[w] = (uint16_t)(s_usb.reg[w] | (uint16_t)(1U << k_usb_bulk_in_pipe));
773 return n;
774}
775
786RA8_INTERNAL static void internal_host_msc_parse_capacity(const uint8_t* d, uint16_t n)
787{
788 if (n < 8U) {
789 return;
790 }
791 /* Both parameter-block fields are big-endian u32 (SCSI READ CAPACITY(10)). */
792 const uint32_t last_lba =
793 ((uint32_t)d[k_cap10_last_lba_off + k_be_lane_b3] << (uint32_t)k_usb_shift24) |
794 ((uint32_t)d[k_cap10_last_lba_off + k_be_lane_b2] << 16) |
795 ((uint32_t)d[k_cap10_last_lba_off + k_be_lane_b1] << 8) |
796 (uint32_t)d[k_cap10_last_lba_off + k_be_lane_b0];
797 s_msc_block_len = ((uint32_t)d[k_cap10_blocklen_off + k_be_lane_b3] << (uint32_t)k_usb_shift24) |
798 ((uint32_t)d[k_cap10_blocklen_off + k_be_lane_b2] << 16) |
799 ((uint32_t)d[k_cap10_blocklen_off + k_be_lane_b1] << 8) |
800 (uint32_t)d[k_cap10_blocklen_off + k_be_lane_b0];
801 s_msc_blocks = last_lba + 1U;
802}
803
814{
815 if (s_msc_cmd >= (uint8_t)k_msc_cmd_count) {
816 s_msc_phase = (uint8_t)k_msc_done;
817 return;
818 }
820 s_msc_phase = (uint8_t)k_msc_data;
821 s_msc_wait = 0U;
822}
823
842RA8_INTERNAL static void internal_host_msc_record_data(const uint8_t* d, uint16_t n)
843{
844 switch ((msc_script_cmd_t)s_msc_cmd) {
847 break;
849 s_msc_inquiry_ok = true;
850 break;
851 case k_msc_cmd_read10:
852 s_msc_read_ok += n;
853 break;
854 default:
855 break;
856 }
857}
858
869{
870 uint8_t buf[k_usb_in_cap];
871 const uint16_t n = internal_host_msc_take_in(uc, buf, (uint16_t)sizeof(buf));
872 if (n == 0U) {
873 s_msc_wait++;
874 } else {
876 s_msc_data_got += n;
877 s_msc_wait = 0U;
878 }
880 s_msc_phase = (uint8_t)k_msc_csw;
881 s_msc_wait = 0U;
882 }
883}
884
895{
896 uint8_t buf[k_usb_in_cap];
897 const uint16_t n = internal_host_msc_take_in(uc, buf, (uint16_t)sizeof(buf));
898 if ((n >= (uint16_t)k_msc_csw_len) || (s_msc_wait > (uint32_t)k_usb_step_timeout)) {
899 s_msc_cmd++;
900 s_msc_phase = (uint8_t)k_msc_send;
901 s_msc_wait = 0U;
902 } else {
903 s_msc_wait++;
904 }
905}
906
916RA8_INTERNAL static void internal_host_msc_drive(uc_engine* uc)
917{
918 switch ((msc_phase_t)s_msc_phase) {
919 case k_msc_send:
921 break;
922 case k_msc_data:
924 break;
925 case k_msc_csw:
927 break;
928 case k_msc_done:
929 default:
930 break;
931 }
932}
933
936{
937 if (s_dev_class == (uint8_t)k_usb_class_msc) {
938 internal_host_msc_drive(uc); /* run the BOT/SCSI script against the RAM disk. */
939 return;
940 }
942 if (s_dev_class == (uint8_t)k_usb_class_hid) {
943 return; /* keep polling the HID interrupt-IN pipe; reports keep flowing. */
944 }
945 if (s_echo_out_len == 0U) {
946 s_host_phase = (uint8_t)k_phase_done;
947 return;
948 }
949 /* Pace one OUT packet, then let the device echo it back before the next. */
951 if (s_host_wait == 0U) {
953 }
954 s_host_wait++;
955 if (s_host_wait >= (uint32_t)k_usb_reset_settle) {
956 s_host_wait = 0U;
957 }
958 return;
959 }
960 /* All OUT bytes sent; drain the tail echo for a settle window, then finish. */
961 s_host_wait++;
962 if (s_host_wait >= (uint32_t)k_usb_post_cfg_idle) {
963 s_host_phase = (uint8_t)k_phase_done;
964 }
965}
Module-private state + interfaces shared by the board_usb TUs.
uint32_t s_msc_blocks
MSC capacity in blocks (vhost).
@ k_usb_dir_host_to_device
OUT: host -> device (control write).
@ k_usb_dir_device_to_host
IN: device -> host (control read).
void priv_usb_raise_irq(uc_engine *uc)
Pend the device's ICU interrupt via the registered raiser (dev).
@ k_le_lane_b2
Bits 23:16.
@ k_le_lane_b3
Most-significant byte (bits 31:24).
@ k_le_lane_b1
Bits 15:8.
@ k_le_lane_b0
Least-significant byte (bits 7:0).
usb_host_sub_t
Per-SETUP-step sub-states.
@ k_sub_status
Deliver the status stage + raise CTRT.
@ k_sub_wait_in
Wait for the device's IN data (control read).
@ k_sub_deliver
Latch the SETUP + raise CTRT.
@ k_sub_next
Advance to the next script step.
@ k_sub_wait_ack
Wait for the device's CCPL (transfer end).
bool s_external_host
Bridge host owns the bus (core).
Definition board_usb.c:41
@ k_phase_reset
Holding bus reset; device re-arms its DCP.
@ k_phase_done
Terminal idle.
@ k_phase_configured
Device configured; optional bulk echo.
@ k_phase_setup
Walking the enumeration script.
@ k_cdc_req_set_control_line_state
SET_CONTROL_LINE_STATE.
@ k_cdc_req_set_line_coding
SET_LINE_CODING (OUT data).
usb_state_t s_usb
Controller + FIFO state (core).
Definition board_usb.c:33
uint8_t s_host_substate
Per-step sub-state (core).
Definition board_usb.c:70
int32_t s_hid_cy
Accumulated HID mouse Y (vhost).
uint32_t s_echo_in_got
Echoed bytes read back (core).
Definition board_usb.c:79
@ k_usb_dt_interface
INTERFACE descriptor type.
@ k_usb_iclass_vendor
Vendor specific (issue #265).
@ k_usb_iclass_cdc_comm
Communications (CDC control).
@ k_usb_iclass_hid
Human Interface Device.
@ k_usb_iclass_cdc_data
CDC data.
@ k_usb_iclass_msc
Mass Storage.
@ k_usb_iclass_printer
Printer (issue #265).
uint8_t s_host_step
Enumeration-script index (core).
Definition board_usb.c:69
uint32_t s_host_wait
Sub-state wait ticks (core).
Definition board_usb.c:71
uint32_t s_msc_block_len
MSC block size (vhost).
uint8_t s_dev_class
Detected device class (vhost).
uint8_t s_echo_out[k_usb_echo_cap]
Host bulk-OUT payload (core).
Definition board_usb.c:76
uint8_t s_host_phase
Virtual-host phase (core).
Definition board_usb.c:68
@ k_usb_req_set_config
SET_CONFIGURATION.
@ k_usb_req_get_descriptor
GET_DESCRIPTOR.
@ k_usb_req_set_address
SET_ADDRESS.
void priv_usb_intsts0_set(uint8_t bit)
Set one INTSTS0 event bit in the register shadow (dev).
@ k_usb_in_cap
DCP/bulk IN staging cap (multi-MPS).
@ k_usb_dcp_mps
EP0 default control-pipe max packet.
@ k_usb_pipe_mps
Bulk-FS data pipe max packet.
@ k_usb_dt_string
STRING descriptor.
@ k_usb_dt_device
DEVICE descriptor.
@ k_usb_dt_config
CONFIGURATION descriptor.
@ k_be_lane_b3
Most-significant byte (bits 31:24).
@ k_be_lane_b1
Bits 15:8.
@ k_be_lane_b2
Bits 23:16.
@ k_be_lane_b0
Least-significant byte (bits 7:0).
uint32_t s_msc_read_ok
MSC read data bytes (vhost).
void priv_usb_log_line(const char *msg)
Append one already-formatted line to the enumeration-step log (core).
Definition board_usb.c:91
int32_t s_hid_cx
Accumulated HID mouse X (vhost).
usb_dev_class_t
USB device class the host detected from the interface descriptor.
@ k_usb_class_printer
Printer (7/1/x), issue #265.
@ k_usb_class_cdc
CDC-ACM (virtual serial).
@ k_usb_class_hid
HID (boot mouse / keyboard).
@ k_usb_class_msc
Mass storage (BOT/SCSI).
@ k_usb_class_unknown
Not yet detected.
@ k_usb_class_vendor
Vendor specific (0xFF).
@ k_cdc_line_coding_len
SET_LINE_CODING wLength.
@ k_cbw_cdblen_off
CBW bCBWCBLength offset.
@ k_usb_shift24
Byte-3 position in a 32-bit word.
@ k_sector_bytes
Logical block size.
@ k_usb_req_class_iface
bmRequestType: class, interface, host->device.
@ k_cbw_tag_off
CBW dCBWTag offset (LE u32).
@ k_scsi_inquiry
SCSI INQUIRY opcode.
@ k_iface_class_off
bInterfaceClass offset in the iface descriptor.
@ k_scsi_read_capacity
SCSI READ CAPACITY(10) opcode.
@ k_cbw_flags_off
CBW bmCBWFlags offset.
@ k_cbw_cdb_off
CBW CBWCB (command block) offset.
@ k_cbw_dtl_off
CBW dCBWDataTransferLength offset (LE u32).
@ k_inquiry_len
INQUIRY allocation length.
@ k_cdb10_len
10-byte CDB length.
@ k_cbw_lun_off
CBW bCBWLUN offset.
@ k_scsi_read10
SCSI READ(10) opcode.
bool s_msc_inquiry_ok
MSC INQUIRY completed (vhost).
@ k_cap10_last_lba_off
Returned logical block address (BE u32).
@ k_cap10_blocklen_off
Block length in bytes (BE u32).
@ k_usb_desc_dev_len
Full device-descriptor length.
@ k_usb_str_len
String-descriptor request length.
@ k_usb_step_timeout
Host per-step wait budget (ticks).
@ k_usb_reset_settle
Ticks held in bus reset before SETUP.
@ k_usb_cfg_full_cap
Config-descriptor full request cap.
@ k_usb_bulk_in_pipe
CDC bulk IN pipe (EP1 IN -> pipe 1).
@ k_usb_bulk_out_pipe
CDC bulk OUT pipe (EP2 OUT -> pipe 2).
@ k_usb_desc8_len
First GET_DESCRIPTOR(device) length.
@ k_usb_byte_mask
One-byte mask.
@ k_usb_log_width
Bytes per enumeration-step log line.
@ k_usb_post_cfg_idle
Settle ticks after CONFIGURED.
@ k_usb_cfg_probe_len
Config-descriptor header probe len.
@ k_usb_addr_assigned
Address the host assigns the device.
@ k_usb_config_value
bConfigurationValue the host sets.
@ k_usb_byte_bits
Bits per byte.
static uint32_t internal_usb_word(uint64_t off)
Word index into the 16-bit register shadow for a window offset.
void priv_usb_log_count(const char *label, unsigned n)
Log a labelled count once (label + n) into the step log (core).
Definition board_usb.c:109
uint32_t s_hid_reports
HID reports read (vhost).
uint32_t s_echo_out_len
Bytes queued by –usb-in (core).
Definition board_usb.c:77
bool priv_host_dcp_pid_buf(void)
True when the device armed its DCP to BUF (ready for OUT) (vhost).
uint8_t s_hid_buttons
Last HID button bitmap (vhost).
uint32_t s_echo_out_sent
Bytes delivered to device (core).
Definition board_usb.c:78
static void internal_host_msc_phase_csw(uc_engine *uc)
Phase k_msc_csw: take the CSW, then advance to the next command.
static uint32_t s_msc_tag
Running dCBWTag.
static void internal_host_msc_drive(uc_engine *uc)
Drive the MSC BOT state machine one tick while CONFIGURED.
static void internal_host_msc_phase_data(uc_engine *uc)
Phase k_msc_data: drain the data phase until it is done or stalls.
void priv_host_mark_configured(uc_engine *uc)
Mark the device CONFIGURED: advance DVSQ and raise DVST.
static void internal_host_step_next(uc_engine *uc, const usb_setup_step_t *s)
Sub-state k_sub_next: record any post-step effect, advance the step.
static void internal_host_msc_parse_capacity(const uint8_t *d, uint16_t n)
Parse a READ CAPACITY (10) response into block count + size.
static uint32_t s_msc_data_len
Expected data-phase length.
static void internal_host_step_wait_in(void)
Sub-state k_sub_wait_in: await the device's control-IN response.
void priv_host_run_configured_phase(uc_engine *uc)
Phase k_phase_configured: optionally drive the CDC bulk echo.
void priv_host_run_setup_phase(uc_engine *uc)
Run one micro-step of the active enumeration script entry.
bool priv_host_take_ccpl(void)
Observe (and clear) DCPCTR.CCPL: the device ended a control transfer.
void priv_host_apply_no_data(uc_engine *uc, const usb_setup_step_t *s)
Apply the SIE-owned side effects of a no-data control request.
static void internal_host_step_status(uc_engine *uc)
Sub-state k_sub_status: deliver the control-read status stage.
static void internal_host_msc_phase_send(uc_engine *uc)
Phase k_msc_send: push the next CBW, or finish the script.
static const usb_setup_step_t s_k_enum_script[]
The chapter-9 + CDC SETUP sequence the virtual host drives.
static uint16_t internal_host_setup_ctsq(const usb_setup_step_t *s)
CTSQ control-stage value the host advertises for a SETUP.
static void internal_host_msc_record_data(const uint8_t *d, uint16_t n)
Record one data-phase burst against the command that is in flight.
static void internal_host_msc_build_cdb(uint8_t cmd, uint8_t *cdb, uint8_t *cdb_len, uint32_t *data_len)
Build the SCSI CDB for the scripted command cmd.
static uint32_t s_msc_wait
Phase pacing.
bool priv_host_device_attached(void)
True when SYSCFG.DPRPU is set: the device has attached its pull-up.
msc_script_cmd_t
Index of each command in the scripted SCSI sequence.
@ k_msc_cmd_read10
READ (10): LBA 0, one block.
@ k_msc_cmd_inquiry
INQUIRY: standard data, 36-byte allocation.
@ k_msc_cmd_read_capacity
READ CAPACITY (10): last LBA + block size.
static uint32_t internal_host_script_len(void)
Number of steps in the enumeration script.
msc_const_t
BOT / SCSI sizing the host uses (USB Mass Storage BBB 1.0).
@ k_msc_cmd_count
Scripted commands (below).
@ k_msc_csw_len
Command Status Wrapper length.
@ k_msc_settle
Ticks to wait for a phase.
@ k_msc_flag_in
bmCBWFlags: device-to-host.
@ k_msc_cbw_len
Command Block Wrapper length.
const char * priv_usb_class_active_str(void)
Human-readable active-class suffix once the device is configured.
static void internal_host_step_deliver(uc_engine *uc, const usb_setup_step_t *s)
Sub-state k_sub_deliver: latch + raise CTRT, then pick the next sub.
void priv_host_echo_read_in(uc_engine *uc)
Drain bytes the device queued on the IN pipe; ack the IN transfer.
static void internal_host_msc_send_cbw(uc_engine *uc)
Push the current command's CBW onto the bulk-OUT pipe (raise BRDY).
static uint16_t internal_host_msc_take_in(uc_engine *uc, uint8_t *out, uint16_t cap)
Consume one IN buffer (data or CSW) and acknowledge it with BEMP.
void priv_host_run_idle_phase(uc_engine *uc)
Phase k_phase_idle: wait for the device pull-up, then bus-reset.
static uint8_t s_msc_cmd
Index into the SCSI command script.
static void internal_host_echo_send_out(uc_engine *uc)
Deliver one bulk-OUT packet to the CDC data OUT pipe and signal BRDY.
static void internal_usb_hid_decode_report(const uint8_t *d, uint16_t len)
Decode one HID boot-protocol mouse input report from the device.
static uint32_t s_msc_data_got
Data-phase bytes read so far.
static uint8_t s_msc_phase
msc_phase_t for the active command.
void priv_usb_detect_class(const uint8_t *d, uint16_t len)
Detect the device class from a returned descriptor, if it is config.
bool priv_host_dcp_pid_buf(void)
DCPCTR.PID == BUF: the device has armed an IN response.
static void internal_host_latch_setup(const usb_setup_step_t *s)
Latch a SETUP packet into USBREQ..USBLENG (host -> device).
msc_phase_t
MSC BOT per-command phase.
@ k_msc_csw
Read the 13-byte CSW.
@ k_msc_send
Push the next command's CBW.
@ k_msc_done
All scripted commands finished.
@ k_msc_data
Read the data-phase bytes.
void priv_host_run_reset_phase(uc_engine *uc)
Phase k_phase_reset: hold reset a few ticks so the DCP re-arms.
static void internal_host_drain_in(void)
Drain the device's queued control-IN data as the host's read.
static void internal_host_step_wait_ack(void)
Sub-state k_sub_wait_ack: await the device's CCPL (transfer end).
void priv_host_deliver_setup(uc_engine *uc, const usb_setup_step_t *s)
Deliver the current script step's SETUP and raise the CTRT IRQ.
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
static volatile bool s_configured
Latched true once the host drives the device to CONFIGURED.
Definition main.c:128
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Event Link Controller (ELC) register layout for the Renesas RA8D2.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_pid_buf
BUF response (transmit/receive).
@ k_ra8_pid_mask
PID[1:0] field mask.
@ k_ra8_syscfg_bit_dprpu
D+ pull-up enable (device).
@ k_ra8_usb_off_usbleng
USBLENG : USB request wLength.
@ k_ra8_usb_off_usbindx
USBINDX : USB request wIndex.
@ k_ra8_usb_off_usbaddr
USBADDR : USB address.
@ k_ra8_usb_off_usbval
USBVAL : USB request wValue.
@ k_ra8_usb_off_bempsts
BEMPSTS : BEMP Status.
@ k_ra8_usb_off_dcpctr
DCPCTR : DCP control.
@ k_ra8_usb_off_usbreq
USBREQ : USB request type.
@ k_ra8_usb_off_brdysts
BRDYSTS : BRDY Status.
@ k_ra8_usb_off_syscfg
SYSCFG : System config.
@ k_ra8_dcpctr_bit_ccpl
Control transfer end enable.
@ k_ra8_usbaddr_addr_mask
7-bit USB address [6:0].
@ k_ra8_ctsq_rdss
Control read status stage.
@ k_ra8_ctsq_rdds
Control read data stage.
@ k_ra8_ctsq_wrds
Control write data stage.
@ k_ra8_ctsq_wrnd
Control write nodata status.
@ k_ra8_int0_bit_bemp
Buffer-empty interrupt.
@ k_ra8_int0_bit_dvst
Device-state transition.
@ k_ra8_int0_bit_brdy
Buffer-ready interrupt.
@ k_ra8_int0_bit_ctrt
Control-transfer-stage transition.
@ k_ra8_dvsq_address
Address assigned.
@ k_ra8_dvsq_default
Default (post bus reset).
@ k_ra8_dvsq_configured
Configured.
One pipe's device-visible IN staging buffer (device -> host payload).
One pipe's host-visible OUT staging buffer (host -> device payload).
One SETUP step in the host enumeration script (USB 2.0 sec 9.4).
uint16_t w_length
wLength (host's expected data size).
uint16_t w_value
wValue.
uint16_t w_index
wIndex.
uint8_t b_request
bRequest.
uint8_t bm_request_type
Direction + type + recipient.
const char * name
Human label for the step log.
static volatile uint32_t s_trace[k_dcd_trace_entries]
JLink-readable ring of packed transfer/SETUP events.