ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_phid.c
Go to the documentation of this file.
1
31
32#include "ra8_usb_phid.h"
33
34#include <stdint.h>
35
36#include "ra8_attributes.h"
37#include "ra8_check.h"
38#include "ra8_err.h"
39#include "ra8_log.h"
40#include "ra8_usb.h"
41
42static const char* s_tag = "USBPHID";
43
44/* =============================================================================
45 * Internal constants
46 * =============================================================================
47 */
48
65
74
79typedef enum : uint16_t {
82
83/* =============================================================================
84 * Internal state
85 * =============================================================================
86 */
87
113
115
116/* =============================================================================
117 * Internal helpers
118 * =============================================================================
119 */
120
143
158{
159 const uint16_t mp = internal_intr_max_packet(speed);
160
161 /* Both calls receive the init-validated speed and compile-time pipe tuples
162 * that satisfy every `ra8_usb_configure_endpoint` argument guard. */
163 (void)ra8_usb_configure_endpoint(speed,
168 mp);
169 (void)ra8_usb_configure_endpoint(speed,
174 mp);
175}
176
191{
192 s_state.speed = speed;
195 s_state.intr_max_packet = internal_intr_max_packet(speed);
196 s_state.report_desc = nullptr;
197 s_state.report_desc_len = 0U;
198 s_state.hid_desc = nullptr;
199 s_state.hid_desc_len = 0U;
202 s_state.setup_cb = nullptr;
203 s_state.setup_ctx = nullptr;
204}
205
221static bool internal_is_known_class_request(uint8_t b_request)
222{
223 return (b_request == k_ra8_phid_req_get_report) || (b_request == k_ra8_phid_req_set_report) ||
224 (b_request == k_ra8_phid_req_get_idle) || (b_request == k_ra8_phid_req_set_idle) ||
225 (b_request == k_ra8_phid_req_get_protocol) || (b_request == k_ra8_phid_req_set_protocol);
226}
227
244{
245 switch (setup->b_request) {
247 const uint8_t duration =
248 (uint8_t)((setup->w_value >> k_ra8_phid_shift_byte1) & k_ra8_phid_mask_byte);
249 s_state.idle_rate = duration;
250 break;
251 }
253 if (setup->w_value == (uint16_t)k_ra8_phid_proto_boot) {
255 } else if (setup->w_value == (uint16_t)k_ra8_phid_proto_report) {
257 } else {
258 /* Unknown selector -- leave shadow untouched. */
259 }
260 break;
261 }
262 default: {
263 /* GET_REPORT / GET_IDLE / GET_PROTOCOL / SET_REPORT have payloads
264 * the application owns; defer to the registered callback. */
265 break;
266 }
267 }
268}
269
270/* =============================================================================
271 * Lifecycle
272 * =============================================================================
273 */
274
276{
277 if ((speed != k_ra8_usb_speed_fs) && (speed != k_ra8_usb_speed_hs)) {
279 }
280 const ra8_err_t usb_err = ra8_usb_device_init(speed);
281 if (usb_err != k_ra8_ok) {
282 ra8_log_error_val(s_tag, "ra8_usb_device_init failed", (uint32_t)usb_err);
284 }
285
287
289 s_state.initialized = true;
290 ra8_log_info_val(s_tag, "device-HID ready", (uint32_t)speed);
291 return k_ra8_ok;
292}
293
295{
296 if (!s_state.initialized) {
298 }
299 (void)ra8_usb_device_attach(s_state.speed, false);
300 const ra8_err_t err = ra8_usb_device_deinit(s_state.speed);
301 s_state.initialized = false;
302 s_state.report_desc = nullptr;
303 s_state.hid_desc = nullptr;
304 s_state.setup_cb = nullptr;
305 s_state.setup_ctx = nullptr;
306 return err;
307}
308
309/* =============================================================================
310 * Descriptor handoff
311 * =============================================================================
312 */
313
314ra8_err_t ra8_usb_phid_set_descriptors(const uint8_t* report_desc,
315 uint16_t report_desc_len,
316 const uint8_t* hid_desc,
317 uint16_t hid_desc_len)
318{
319 if (!s_state.initialized) {
321 }
322 RA8_CHECK_NULL_PTR(report_desc, s_tag, "set_descriptors: report_desc");
323 RA8_CHECK_NULL_PTR(hid_desc, s_tag, "set_descriptors: hid_desc");
324 if ((report_desc_len == 0U) || (hid_desc_len == 0U)) {
326 }
327 s_state.report_desc = report_desc;
328 s_state.report_desc_len = report_desc_len;
329 s_state.hid_desc = hid_desc;
330 s_state.hid_desc_len = hid_desc_len;
331 return k_ra8_ok;
332}
333
334/* =============================================================================
335 * Input / output reports
336 * =============================================================================
337 */
338
339ra8_err_t ra8_usb_phid_send_report(uint8_t report_id, const uint8_t* payload, uint16_t len)
340{
341 if (!s_state.initialized) {
343 }
344 if ((payload == nullptr) && (len != 0U)) {
345 return k_ra8_err_null_ptr;
346 }
347 if ((report_id == 0U) && (len == 0U)) {
349 }
350 /* Per USB HID 1.11 sec 8 "Report Protocol", multi-report devices
351 * prepend the report ID. The combined frame must still fit the
352 * configured pipe max-packet. */
353 const uint16_t framed_len =
354 (report_id != 0U) ? (uint16_t)(len + (uint16_t)k_ra8_phid_report_id_prepend_len) : len;
355 if (framed_len > s_state.intr_max_packet) {
357 }
358
359 if (report_id != 0U) {
360 /* Prepend the report ID byte: queue it on its own first, then the
361 * payload. The controller's FIFO write coalesces both into a
362 * single interrupt-IN packet on the next IN token. */
363 const uint8_t rid_byte = report_id;
364 const ra8_err_t rid_err =
365 ra8_usb_queue_in(s_state.speed, k_ra8_phid_pipe_intr_in, &rid_byte, 1U);
366 RA8_RETURN_ON_ERROR(rid_err, s_tag, "send_report: rid byte");
367 }
368 return ra8_usb_queue_in(s_state.speed, k_ra8_phid_pipe_intr_in, payload, len);
369}
370
372ra8_usb_phid_recv_report(uint8_t report_id, uint8_t* buf, uint16_t max_len, uint16_t* got_len)
373{
374 RA8_CHECK_NULL_PTR(buf, s_tag, "recv_report: buf");
375 RA8_CHECK_NULL_PTR(got_len, s_tag, "recv_report: got_len");
376 if (!s_state.initialized) {
378 }
379 if (max_len == 0U) {
381 }
382 /* `report_id` is informational here -- the HID Report descriptor
383 * declares which IDs the device recognises; the host transmits the
384 * corresponding payload on PIPE7 directly. */
385 (void)report_id;
386
387 uint16_t inout_len = max_len;
388 const ra8_err_t err =
389 ra8_usb_queue_out(s_state.speed, k_ra8_phid_pipe_intr_out, buf, &inout_len, true);
390 if (err == k_ra8_ok) {
391 *got_len = inout_len;
392 } else {
393 *got_len = 0U;
394 }
395 return err;
396}
397
398/* =============================================================================
399 * Setup-handler attach
400 * =============================================================================
401 */
402
404{
405 if (!s_state.initialized) {
407 }
408 s_state.setup_cb = setup_fn;
409 s_state.setup_ctx = ctx;
410 return k_ra8_ok;
411}
412
413/* =============================================================================
414 * Class SETUP dispatch
415 * =============================================================================
416 */
417
419{
420 RA8_CHECK_NULL_PTR(setup, s_tag, "handle_setup: setup");
421 if (!s_state.initialized) {
423 }
427 }
430 }
432
433 /* Forward to the application handler if one is registered. The
434 * application decides whether to ACK or stall via its return value. */
435 if (s_state.setup_cb != nullptr) {
436 const ra8_err_t cb_err = s_state.setup_cb(s_state.setup_ctx, setup);
437 if (cb_err != k_ra8_ok) {
438 return ra8_usb_control_response(s_state.speed, false);
439 }
440 }
441 return ra8_usb_control_response(s_state.speed, true);
442}
443
444/* =============================================================================
445 * Introspection
446 * =============================================================================
447 */
448
449ra8_err_t ra8_usb_phid_get_idle(uint8_t* out_idle_rate)
450{
451 RA8_CHECK_NULL_PTR(out_idle_rate, s_tag, "get_idle: out_idle_rate");
452 if (!s_state.initialized) {
454 }
455 *out_idle_rate = s_state.idle_rate;
456 return k_ra8_ok;
457}
458
460{
461 RA8_CHECK_NULL_PTR(out_protocol, s_tag, "get_protocol: out_protocol");
462 if (!s_state.initialized) {
464 }
465 *out_protocol = s_state.protocol;
466 return k_ra8_ok;
467}
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_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ 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_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
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_intr
Interrupt 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_control_response(ra8_usb_speed_t speed, bool accept)
Issue a control-transfer status response on EP0.
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_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.
static ra8_err_t internal_configure_pipes(void)
Configure the host-Audio iso pipes against the attached device's endpoints.
ra8_err_t ra8_usb_phid_recv_report(uint8_t report_id, uint8_t *buf, uint16_t max_len, uint16_t *got_len)
Drain a HID output report from the interrupt-OUT endpoint.
ra8_usb_phid_byte_mask_t
Per-byte mask constants for wValue decoding.
@ k_ra8_phid_mask_byte
RA8 phid mask byte.
ra8_err_t ra8_usb_phid_get_idle(uint8_t *out_idle_rate)
Read the most-recently negotiated idle rate.
ra8_usb_phid_byte_shift_t
Per-byte shift constants for wValue decoding.
@ k_ra8_phid_shift_byte1
RA8 phid shift byte1.
@ k_ra8_phid_shift_byte0
RA8 phid shift byte0.
static uint16_t internal_intr_max_packet(ra8_usb_speed_t speed)
Pick the interrupt-max-packet ceiling matching the negotiated speed.
ra8_err_t ra8_usb_phid_get_protocol(ra8_usb_phid_protocol_select_t *out_protocol)
Read the most-recently negotiated protocol.
ra8_err_t ra8_usb_phid_init(ra8_usb_speed_t speed)
Bring up the device-HID function on a chosen USB controller.
ra8_err_t ra8_usb_phid_handle_setup(const ra8_usb_setup_t *setup)
Process a class-specific SETUP packet on EP0.
static void internal_reset_shadow(ra8_usb_speed_t speed)
Reset shadow state to spec defaults.
ra8_err_t ra8_usb_phid_attach_setup_handler(ra8_usb_phid_setup_fn_t setup_fn, void *ctx)
Register the application's HID class-setup handler.
ra8_usb_phid_setup_field_t
Constants used to decode HID class-specific SETUPs.
@ k_ra8_phid_bm_class_iface_in
Class | Iface | In.
@ k_ra8_phid_default_protocol
Spec default = report.
@ k_ra8_phid_report_id_prepend_len
Report ID byte.
@ k_ra8_phid_bm_class_iface_out
Class | Iface | Out.
@ k_ra8_phid_default_idle_rate
Spec default idle.
ra8_err_t ra8_usb_phid_set_descriptors(const uint8_t *report_desc, uint16_t report_desc_len, const uint8_t *hid_desc, uint16_t hid_desc_len)
Install the caller-supplied HID Report descriptor and HID class descriptor.
static bool internal_is_known_class_request(uint8_t b_request)
Recognise a HID class request code we shadow / forward.
static void internal_apply_class_setup(const ra8_usb_setup_t *setup)
Apply SET_IDLE / SET_PROTOCOL to the local shadow.
ra8_err_t ra8_usb_phid_close(void)
Tear down the device-HID function and release the controller.
ra8_err_t ra8_usb_phid_send_report(uint8_t report_id, const uint8_t *payload, uint16_t len)
Push a HID input report on the interrupt-IN endpoint.
Native USB device-side HID (Human Interface Device) class layer.
ra8_usb_phid_protocol_select_t
wValue payload for SET_PROTOCOL.
@ k_ra8_phid_proto_boot
Boot protocol.
@ k_ra8_phid_proto_report
Report protocol.
@ k_ra8_phid_intr_max_packet_default
Boot-protocol default.
@ k_ra8_phid_intr_max_packet_hs
HS ceiling.
@ k_ra8_phid_req_set_idle
SET_IDLE.
@ k_ra8_phid_req_get_report
GET_REPORT.
@ k_ra8_phid_req_get_idle
GET_IDLE.
@ k_ra8_phid_req_set_report
SET_REPORT.
@ k_ra8_phid_req_set_protocol
SET_PROTOCOL.
@ k_ra8_phid_req_get_protocol
GET_PROTOCOL.
@ k_ra8_phid_ep_intr_in_addr
EP1 IN address.
@ k_ra8_phid_ep_intr_out_addr
EP2 OUT address.
ra8_err_t(* ra8_usb_phid_setup_fn_t)(void *ctx, const ra8_usb_setup_t *setup)
Caller-supplied HID class-setup handler signature.
@ k_ra8_phid_pipe_intr_out
PIPE7 -> EP2 OUT (intr).
@ k_ra8_phid_pipe_intr_in
PIPE6 -> EP1 IN (intr).
Singleton shadow state for the device-HID function.
uint8_t intr_in_ep
IN endpoint number.
uint16_t intr_max_packet
Pipe max-packet size.
bool initialized
True after ra8_usb_phid_init.
uint8_t intr_out_ep
OUT endpoint number.
uint16_t report_desc_len
Report descriptor byte len.
ra8_usb_phid_setup_fn_t setup_cb
Application class handler.
const uint8_t * hid_desc
Cached HID class descriptor.
void * setup_ctx
Class handler context.
uint16_t hid_desc_len
HID class descriptor byte len.
const uint8_t * report_desc
Cached Report descriptor.
ra8_usb_speed_t speed
Underlying controller.
uint8_t idle_rate
4 ms ticks (0 = on-change).
ra8_usb_phid_protocol_select_t protocol
Boot vs report.
Decoded 8-byte USB SETUP packet.
uint8_t b_request
bRequest code.
uint8_t bm_request_type
Request direction / type / recipient.
uint16_t w_value
wValue.