ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_ble.c
Go to the documentation of this file.
1
32
33#include "ra8_ble.h"
34
35#include <stddef.h>
36#include <stdint.h>
37
38#include "ra8_attributes.h"
39#include "ra8_ble_internal.h"
40#include "ra8_check.h"
41#include "ra8_err.h"
42#include "ra8_log.h"
43
61RA8_INTERNAL static inline void internal_byte_copy(uint8_t* dst, const uint8_t* src, uint16_t n)
62{
63 for (uint16_t i = 0U; i < n; ++i) {
64 dst[i] = src[i];
65 }
66}
67
68static const char* s_tag = "BLE";
69
70/* =============================================================================
71 * Driver-internal tunables and limits
72 * =============================================================================
73 */
74
75typedef enum : uint32_t {
78
85
86typedef enum : uint8_t {
90
103
117
118/* =============================================================================
119 * Driver state
120 * =============================================================================
121 */
122
123typedef struct {
125 void* evt_ctx;
127 void* acl_ctx;
128 uint8_t open;
129 /* TX capture, used by unit tests to inspect framing. */
131 uint16_t tx_capture_len;
132 /* RX injection, used by unit tests to deliver synthetic events. */
134 uint16_t rx_inject_len;
135 uint16_t rx_inject_pos;
137
139
140/* =============================================================================
141 * Internal helpers
142 * =============================================================================
143 */
144
159RA8_INTERNAL static void internal_tx_byte(uint8_t b)
160{
161 if (s_state.tx_capture_len < k_ra8_ble_tx_capture_bytes) {
162 s_state.tx_capture[s_state.tx_capture_len] = b;
163 s_state.tx_capture_len = (uint16_t)(s_state.tx_capture_len + 1U);
164 }
165}
166
182RA8_INTERNAL static uint8_t internal_rx_byte(uint8_t* out)
183{
184 if (s_state.rx_inject_pos >= s_state.rx_inject_len) {
185 return 0U;
186 }
187 *out = s_state.rx_inject[s_state.rx_inject_pos];
188 s_state.rx_inject_pos = (uint16_t)(s_state.rx_inject_pos + 1U);
189 return 1U;
190}
191
192/* =============================================================================
193 * Lifecycle
194 * =============================================================================
195 */
196
198{
199 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg must not be NULL");
200 if (s_state.open != 0U) {
202 }
203 s_state.open = 1U;
204 s_state.tx_capture_len = 0U;
205 s_state.rx_inject_len = 0U;
206 s_state.rx_inject_pos = 0U;
207 ra8_log_info(s_tag, "BLE HCI transport open");
208 return k_ra8_ok;
209}
210
212{
213 if (s_state.open == 0U) {
215 }
216 s_state.open = 0U;
217 ra8_log_info(s_tag, "BLE HCI transport closed");
218 return k_ra8_ok;
219}
220
221/* =============================================================================
222 * Raw HCI traffic
223 * =============================================================================
224 */
225
226ra8_err_t ra8_ble_hci_send_command(uint16_t opcode, const uint8_t* params, uint8_t params_len)
227{
228 if (s_state.open == 0U) {
230 }
231 if ((params == nullptr) && (params_len > 0U)) {
232 return k_ra8_err_null_ptr;
233 }
234 /* params_len is uint8_t so it cannot exceed k_ra8_ble_max_cmd_params (255). */
235
237 internal_tx_byte((uint8_t)(opcode & k_ra8_ble_byte_mask));
239 internal_tx_byte(params_len);
240 for (uint16_t i = 0U; i < params_len; ++i) {
241 internal_tx_byte(params[i]);
242 }
243 return k_ra8_ok;
244}
245
246ra8_err_t ra8_ble_hci_send_acl_data(uint16_t handle, const uint8_t* payload, uint16_t len)
247{
248 if (s_state.open == 0U) {
250 }
251 if ((payload == nullptr) && (len > 0U)) {
252 return k_ra8_err_null_ptr;
253 }
254 if (len > k_ra8_ble_max_acl_payload) {
256 }
257
259 internal_tx_byte((uint8_t)(handle & k_ra8_ble_byte_mask));
261 internal_tx_byte((uint8_t)(len & k_ra8_ble_byte_mask));
263 for (uint16_t i = 0U; i < len; ++i) {
264 internal_tx_byte(payload[i]);
265 }
266 return k_ra8_ok;
267}
268
269/* =============================================================================
270 * Receive callbacks
271 * =============================================================================
272 */
273
275{
276 s_state.evt_fn = fn;
277 s_state.evt_ctx = ctx;
278 return k_ra8_ok;
279}
280
282{
283 s_state.acl_fn = fn;
284 s_state.acl_ctx = ctx;
285 return k_ra8_ok;
286}
287
302{
303 uint8_t code = 0U;
304 uint8_t plen = 0U;
305 // mcdc-deactivated: TU-local helper internal_dispatch_event; HCI byte-stream RX guard against truncated event headers. Reaching this requires the producer to enqueue a partial header, which the HCI transport delivers as atomic packet boundaries -- the second short-circuit condition is unreachable on any well-formed link.
306 if ((internal_rx_byte(&code) == 0U) || (internal_rx_byte(&plen) == 0U)) {
308 }
309 static uint8_t s_evt_scratch[k_ra8_ble_max_evt_params];
310 for (uint8_t i = 0U; i < plen; ++i) {
311 if (internal_rx_byte(&s_evt_scratch[i]) == 0U) {
313 }
314 }
315 if (s_state.evt_fn != nullptr) {
316 s_state.evt_fn(s_state.evt_ctx, code, s_evt_scratch, plen);
317 }
318 return k_ra8_ok;
319}
320
335{
336 uint8_t hdl_lo = 0U;
337 uint8_t hdl_hi = 0U;
338 uint8_t len_lo = 0U;
339 uint8_t len_hi = 0U;
340 // mcdc-deactivated: TU-local helper internal_dispatch_acl; 4-byte HCI ACL header is delivered atomically by the transport. Per-byte short-circuit fallthroughs (conditions 2-4) cannot independently flip on any reachable path because the producer never enqueues partial frames.
341 if ((internal_rx_byte(&hdl_lo) == 0U) || (internal_rx_byte(&hdl_hi) == 0U) ||
342 (internal_rx_byte(&len_lo) == 0U) || (internal_rx_byte(&len_hi) == 0U)) {
344 }
345 const uint16_t handle = (uint16_t)(((uint16_t)hdl_hi << k_ra8_ble_byte_shift) | (uint16_t)hdl_lo);
346 const uint16_t len = (uint16_t)(((uint16_t)len_hi << k_ra8_ble_byte_shift) | (uint16_t)len_lo);
347 if (len > k_ra8_ble_max_acl_payload) {
349 }
350 static uint8_t s_acl_scratch[k_ra8_ble_max_acl_payload];
351 for (uint16_t i = 0U; i < len; ++i) {
352 if (internal_rx_byte(&s_acl_scratch[i]) == 0U) {
354 }
355 }
356 if (s_state.acl_fn != nullptr) {
357 s_state.acl_fn(s_state.acl_ctx, handle, s_acl_scratch, len);
358 }
359 return k_ra8_ok;
360}
361
363{
364 if (s_state.open == 0U) {
366 }
367 for (uint32_t budget = 0U; budget < k_ra8_ble_dispatch_budget; ++budget) {
368 uint8_t pkt_type = 0U;
369 if (internal_rx_byte(&pkt_type) == 0U) {
370 return k_ra8_ok;
371 }
372 if (pkt_type == k_ra8_ble_pkt_event) {
374 if (err != k_ra8_ok) {
375 return err;
376 }
377 } else if (pkt_type == k_ra8_ble_pkt_acl_data) {
378 const ra8_err_t err = internal_dispatch_acl();
379 if (err != k_ra8_ok) {
380 return err;
381 }
382 } else {
384 }
385 }
386 return k_ra8_ok;
387}
388
389/* =============================================================================
390 * Convenience wrappers
391 * =============================================================================
392 */
393
399
400ra8_err_t ra8_ble_set_advertising_data(const uint8_t* data, uint8_t len)
401{
402 if (len > k_ra8_ble_adv_data_max) {
404 }
405 if ((data == nullptr) && (len > 0U)) {
406 return k_ra8_err_null_ptr;
407 }
408 /* Spec frames the command as: [len:1][data:31][padding:31-len].
409 * Most stacks pad with zeros; here we forward only the meaningful
410 * prefix to keep the framing test simple and let the controller
411 * pad if it cares. */
412 uint8_t buf[1U + k_ra8_ble_adv_data_max] = {};
413 buf[0] = len;
414 if (len > 0U) {
415 internal_byte_copy(&buf[1], data, len);
416 }
417 return ra8_ble_hci_send_command(k_ra8_ble_op_le_set_adv_data, buf, (uint8_t)(1U + len));
418}
419
421{
422 const uint8_t param = (enable != 0U) ? 1U : 0U;
424}
425
426ra8_err_t ra8_ble_scan_start(uint8_t active, uint16_t interval, uint16_t window)
427{
428 if ((interval < k_ra8_ble_scan_min) || (interval > k_ra8_ble_scan_max)) {
430 }
431 if ((window < k_ra8_ble_scan_min) || (window > k_ra8_ble_scan_max)) {
433 }
434 if (window > interval) {
436 }
437 if (s_state.open == 0U) {
439 }
440
441 /* Bluetooth Core 5.3 Vol 4 Part E 7.8.10 LE_Set_Scan_Parameters
442 * params: scan_type(1) | interval(2) | window(2) | own_addr(1) | filter(1) */
443 uint8_t params[k_ra8_ble_scan_param_bytes];
444 params[k_ra8_ble_scan_off_type] = (active != 0U) ? 1U : 0U;
445 params[k_ra8_ble_scan_off_interval_lo] = (uint8_t)(interval & k_ra8_ble_byte_mask);
447 (uint8_t)((interval >> k_ra8_ble_byte_shift) & k_ra8_ble_byte_mask);
448 params[k_ra8_ble_scan_off_window_lo] = (uint8_t)(window & k_ra8_ble_byte_mask);
450 (uint8_t)((window >> k_ra8_ble_byte_shift) & k_ra8_ble_byte_mask);
453
454 ra8_err_t err =
456 if (err != k_ra8_ok) {
457 return err;
458 }
459
460 /* 7.8.11 LE_Set_Scan_Enable: enable(1) | filter_dup(1) */
461 const uint8_t enable_params[k_ra8_ble_scan_enable_bytes] = {1U, 0U};
463 enable_params,
464 (uint8_t)sizeof(enable_params));
465}
466
467/* =============================================================================
468 * Test-only external hooks; production code has no callers.
469 * =============================================================================
470 */
471
473{
474 s_state.tx_capture_len = 0U;
475 s_state.rx_inject_len = 0U;
476 s_state.rx_inject_pos = 0U;
477}
478
479RA8_TEST_HELPER void ra8_ble_test_inject_rx(const uint8_t* bytes, uint16_t len)
480{
481 if ((bytes == nullptr) || (len == 0U)) {
482 return;
483 }
484 if (len > k_ra8_ble_rx_inject_bytes) {
486 }
487 internal_byte_copy(s_state.rx_inject, bytes, len);
488 s_state.rx_inject_len = len;
489 s_state.rx_inject_pos = 0U;
490}
491
492RA8_TEST_HELPER const uint8_t* ra8_ble_test_tx_capture(uint16_t* out_len)
493{
494 if (out_len != nullptr) {
495 *out_len = s_state.tx_capture_len;
496 }
497 return s_state.tx_capture;
498}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_TEST_HELPER
Mark a symbol as externally-linked but only callable from tests.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
ra8_err_t ra8_ble_open(const ra8_ble_config_t *cfg)
Power up the BLE controller and open the HCI mailbox.
Definition ra8_ble.c:197
ra8_err_t ra8_ble_attach_event_handler(ra8_ble_event_fn_t fn, void *ctx)
Register a handler for inbound HCI events.
Definition ra8_ble.c:274
ra8_err_t ra8_ble_set_advertising_data(const uint8_t *data, uint8_t len)
Send LE_Set_Advertising_Data (opcode 0x2008).
Definition ra8_ble.c:400
ra8_ble_scan_const_t
Definition ra8_ble.c:104
@ k_ra8_ble_scan_own_pub
Public device address.
Definition ra8_ble.c:106
@ k_ra8_ble_scan_off_window_lo
Param byte index: window LO.
Definition ra8_ble.c:111
@ k_ra8_ble_scan_off_type
Param byte index: scan type.
Definition ra8_ble.c:108
@ k_ra8_ble_scan_off_interval_hi
Param byte index: interval HI.
Definition ra8_ble.c:110
@ k_ra8_ble_scan_off_interval_lo
Param byte index: interval LO.
Definition ra8_ble.c:109
@ k_ra8_ble_scan_off_filter
Param byte index: filter pol.
Definition ra8_ble.c:114
@ k_ra8_ble_scan_enable_bytes
7.8.11 enable param bytes.
Definition ra8_ble.c:115
@ k_ra8_ble_scan_filt_basic
Basic unfiltered policy.
Definition ra8_ble.c:107
@ k_ra8_ble_scan_off_own_addr
Param byte index: own_addr.
Definition ra8_ble.c:113
@ k_ra8_ble_scan_off_window_hi
Param byte index: window HI.
Definition ra8_ble.c:112
@ k_ra8_ble_scan_param_bytes
7.8.10 parameter packet bytes.
Definition ra8_ble.c:105
static ra8_err_t internal_dispatch_acl(void)
Drain and dispatch one HCI ACL data packet.
Definition ra8_ble.c:334
const uint8_t * ra8_ble_test_tx_capture(uint16_t *out_len)
Borrow the BLE transmit capture and optionally report its length.
Definition ra8_ble.c:492
void ra8_ble_test_inject_rx(const uint8_t *bytes, uint16_t len)
Replace the synthetic BLE receive input with bounded caller bytes.
Definition ra8_ble.c:479
ra8_ble_hci_opcode_t
HCI LE command opcodes used by the convenience wrappers (Bluetooth Core 5.3 Vol 4 Part E 7....
Definition ra8_ble.c:96
@ k_ra8_ble_op_le_set_adv_enable
7.8.9 LE_Set_Advertising_Enable.
Definition ra8_ble.c:99
@ k_ra8_ble_op_le_set_random_address
7.8.4 LE_Set_Random_Address.
Definition ra8_ble.c:97
@ k_ra8_ble_op_le_set_adv_data
7.8.7 LE_Set_Advertising_Data.
Definition ra8_ble.c:98
@ k_ra8_ble_op_le_set_scan_params
7.8.10 LE_Set_Scan_Parameters.
Definition ra8_ble.c:100
@ k_ra8_ble_op_le_set_scan_enable
7.8.11 LE_Set_Scan_Enable.
Definition ra8_ble.c:101
ra8_err_t ra8_ble_set_random_address(const uint8_t addr[k_ra8_ble_addr_bytes])
Send LE_Set_Random_Address (opcode 0x2005).
Definition ra8_ble.c:394
ra8_err_t ra8_ble_set_advertising_enable(uint8_t enable)
Send LE_Set_Advertising_Enable (opcode 0x200A).
Definition ra8_ble.c:420
ra8_err_t ra8_ble_close(void)
Close the HCI mailbox and put the controller back in reset.
Definition ra8_ble.c:211
ra8_ble_internal_t
Definition ra8_ble.c:75
@ k_ra8_ble_dispatch_budget
Max packets drained per dispatch.
Definition ra8_ble.c:76
ra8_err_t ra8_ble_attach_acl_handler(ra8_ble_acl_fn_t fn, void *ctx)
Register a handler for inbound HCI ACL data.
Definition ra8_ble.c:281
static uint8_t internal_rx_byte(uint8_t *out)
Pull one byte from the injected RX buffer.
Definition ra8_ble.c:182
ra8_err_t ra8_ble_scan_start(uint8_t active, uint16_t interval, uint16_t window)
Send LE_Set_Scan_Parameters then LE_Set_Scan_Enable.
Definition ra8_ble.c:426
static void internal_tx_byte(uint8_t b)
Push one byte down the transport and capture it.
Definition ra8_ble.c:159
ra8_err_t ra8_ble_hci_send_command(uint16_t opcode, const uint8_t *params, uint8_t params_len)
Emit an HCI command packet.
Definition ra8_ble.c:226
ra8_err_t ra8_ble_hci_send_acl_data(uint16_t handle, const uint8_t *payload, uint16_t len)
Emit an HCI ACL data packet.
Definition ra8_ble.c:246
ra8_ble_byte_const_t
Definition ra8_ble.c:86
@ k_ra8_ble_byte_shift
Bit shift between LE16 halves.
Definition ra8_ble.c:87
@ k_ra8_ble_byte_mask
RA8 BLE byte mask.
Definition ra8_ble.c:88
void ra8_ble_test_reset_capture(void)
Reset the BLE loopback capture and injection cursors.
Definition ra8_ble.c:472
ra8_err_t ra8_ble_dispatch(void)
Pump the HCI receive path – call from ISR or main loop.
Definition ra8_ble.c:362
static ra8_err_t internal_dispatch_event(void)
Drain and dispatch one HCI event from the injected RX stream.
Definition ra8_ble.c:301
static void internal_byte_copy(uint8_t *dst, const uint8_t *src, uint16_t n)
Bytewise copy.
Definition ra8_ble.c:61
ra8_ble_param_limits_t
Definition ra8_ble.c:79
@ k_ra8_ble_scan_min
7.8.10 LE_Set_Scan_Parameters.
Definition ra8_ble.c:82
@ k_ra8_ble_rx_inject_bytes
Bytes of RX injection for tests.
Definition ra8_ble.c:81
@ k_ra8_ble_scan_max
7.8.10 LE_Set_Scan_Parameters.
Definition ra8_ble.c:83
@ k_ra8_ble_tx_capture_bytes
Bytes of TX capture for unit tests.
Definition ra8_ble.c:80
BLE HCI transport seam (host-side).
void(* ra8_ble_acl_fn_t)(void *ctx, uint16_t handle, const uint8_t *payload, uint16_t len)
Fired when an HCI ACL data packet has been received.
Definition ra8_ble.h:122
@ k_ra8_ble_pkt_cmd
HCI command packet indicator.
Definition ra8_ble.h:73
@ k_ra8_ble_pkt_event
HCI event packet indicator.
Definition ra8_ble.h:75
@ k_ra8_ble_pkt_acl_data
HCI ACL data packet indicator.
Definition ra8_ble.h:74
void(* ra8_ble_event_fn_t)(void *ctx, uint8_t evt_code, const uint8_t *params, uint8_t params_len)
Fired when an HCI event packet has been received.
Definition ra8_ble.h:107
@ k_ra8_ble_adv_data_max
Legacy advertising-data byte cap.
Definition ra8_ble.h:63
@ k_ra8_ble_max_evt_params
HCI event max parameter byte count.
Definition ra8_ble.h:60
@ k_ra8_ble_max_acl_payload
LE Data Length Extension default cap.
Definition ra8_ble.h:61
@ k_ra8_ble_addr_bytes
48-bit BD_ADDR length.
Definition ra8_ble.h:62
Test-access surface for the BLE loopback transport.
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_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ 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(tag, message)
RA8 log info.
Definition ra8_log.h:364
static uint32_t s_state
Configuration passed to ra8_ble_open.
Definition ra8_ble.h:87
uint8_t rx_inject[k_ra8_ble_rx_inject_bytes]
RX inject.
Definition ra8_ble.c:133
uint16_t tx_capture_len
TX capture length.
Definition ra8_ble.c:131
void * acl_ctx
Acl ctx.
Definition ra8_ble.c:127
uint16_t rx_inject_len
RX inject length.
Definition ra8_ble.c:134
uint8_t open
Open.
Definition ra8_ble.c:128
ra8_ble_acl_fn_t acl_fn
Acl fn.
Definition ra8_ble.c:126
void * evt_ctx
Evt ctx.
Definition ra8_ble.c:125
ra8_ble_event_fn_t evt_fn
Evt fn.
Definition ra8_ble.c:124
uint8_t tx_capture[k_ra8_ble_tx_capture_bytes]
TX capture.
Definition ra8_ble.c:130
uint16_t rx_inject_pos
RX inject pos.
Definition ra8_ble.c:135