24#include "freertos/FreeRTOS.h"
25#include "freertos/semphr.h"
41constexpr uint16_t BLE_MAX_PAYLOAD = 244;
42constexpr uint32_t PLUGIN_SERVICE_HANDLE = 1;
44constexpr uint8_t WRITE_RING = 4;
45constexpr uint8_t NOTIFY_RING = 4;
46constexpr uint8_t MAX_DISC_CHARS = 8;
49SemaphoreHandle_t s_lock =
nullptr;
50void lock_init() {
if (!s_lock) s_lock = xSemaphoreCreateMutex(); }
53 Guard() {
if (s_lock) held = (xSemaphoreTake(s_lock, portMAX_DELAY) == pdTRUE); }
54 ~Guard() {
if (held) xSemaphoreGive(s_lock); }
60uint16_t s_value_handles[MAX_PLUGIN_CHARS];
62 uint32_t write_action_id = 0;
65 void* plugin =
nullptr;
67 uint8_t uuid[16] = {};
68 uint8_t num_chars = 0;
69 PeriphChar chars[MAX_PLUGIN_CHARS];
76 uint8_t data[BLE_MAX_PAYLOAD];
78WriteEvt s_wring[WRITE_RING];
79volatile uint8_t s_wr_head = 0, s_wr_tail = 0;
82uint32_t s_cur_write_char = 0;
83uint16_t s_cur_write_len = 0;
84uint8_t s_cur_write_buf[BLE_MAX_PAYLOAD];
87struct NotifyEvt { uint16_t value_handle; uint16_t len; uint8_t data[BLE_MAX_PAYLOAD]; };
88struct WcEvt { uint16_t attr; int16_t status; };
89constexpr uint8_t WC_RING = 4;
91 void* plugin =
nullptr;
92 bool listeners_registered =
false;
96 uint16_t conn = 0xFFFF;
97 uint32_t discover_action_id = 0;
98 uint32_t read_action_id = 0;
99 uint32_t notify_action_id = 0;
100 uint32_t write_action_id = 0;
103 WcEvt wcring[WC_RING];
104 uint8_t wc_head = 0, wc_tail = 0;
107 ble_remote_char_t disc[MAX_DISC_CHARS];
108 uint8_t disc_count = 0;
109 bool fire_discovery =
false;
112 uint8_t read_buf[BLE_MAX_PAYLOAD];
113 uint16_t read_len = 0;
114 bool fire_read =
false;
117 NotifyEvt nring[NOTIFY_RING];
118 uint8_t n_head = 0, n_tail = 0;
119} s_cen EXT_RAM_BSS_ATTR;
127 return p && p->manifest().capabilities.ble;
131void uuid_to_bytes(
const BleUuid& u, uint8_t out[16]) {
133 std::memcpy(out, u.
u128, 16);
135 static const uint8_t base[16] = {
136 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
137 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
139 std::memcpy(out, base, 16);
140 out[12] =
static_cast<uint8_t
>(u.
u16 & 0xFF);
141 out[13] =
static_cast<uint8_t
>(u.
u16 >> 8);
146const uint8_t NUS_SVC[16] = { 0x9e,0xca,0xdc,0x24,0x0e,0xe5,0xa9,0xe0,
147 0x93,0xf3,0xa3,0xb5,0x01,0x00,0x40,0x6e };
148const uint8_t VCARD_SVC[16] = { 0x01,0x1A,0x8B,0x6A,0x9D,0x4C,0x6E,0x9A,
149 0x7A,0x4D,0x5D,0x8B,0x20,0x1F,0x2F,0x8E };
150const uint8_t GPG_SVC[16] = { 0x01,0x1A,0x8B,0x6A,0x9D,0x4C,0x6E,0x9A,
151 0x7A,0x4D,0x5D,0x8B,0x30,0x1F,0x2F,0x8E };
155bool uuid_is_reserved(
const uint8_t uuid[16]) {
156 static const uint8_t sig_base[12] = {
157 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00
159 if (std::memcmp(uuid, sig_base, 12) == 0 && uuid[14] == 0 && uuid[15] == 0) {
162 return std::memcmp(uuid, NUS_SVC, 16) == 0
163 || std::memcmp(uuid, VCARD_SVC, 16) == 0
164 || std::memcmp(uuid, GPG_SVC, 16) == 0;
170 if (connHandle != s_cen.conn)
return;
173 if (n > MAX_DISC_CHARS) n = MAX_DISC_CHARS;
174 for (uint8_t i = 0; i < n; i++) {
178 s_cen.disc[i].reserved = 0;
180 s_cen.disc_count = n;
182 if (complete) s_cen.fire_discovery =
true;
185void on_char_read(uint16_t connHandle, uint16_t,
const uint8_t* data, uint16_t len) {
187 if (connHandle != s_cen.conn)
return;
188 if (len > BLE_MAX_PAYLOAD) len = BLE_MAX_PAYLOAD;
189 std::memcpy(s_cen.read_buf, data, len);
190 s_cen.read_len = len;
191 s_cen.fire_read =
true;
194void on_notification(uint16_t, uint16_t attr,
const uint8_t* data, uint16_t len) {
196 uint8_t next =
static_cast<uint8_t
>((s_cen.n_head + 1) % NOTIFY_RING);
197 if (next == s_cen.n_tail)
return;
198 NotifyEvt& e = s_cen.nring[s_cen.n_head];
199 if (len > BLE_MAX_PAYLOAD) len = BLE_MAX_PAYLOAD;
200 e.value_handle = attr;
202 std::memcpy(e.data, data, len);
206void on_write_complete(uint16_t connHandle, uint16_t attr,
int status) {
208 if (connHandle != s_cen.conn)
return;
209 uint8_t next =
static_cast<uint8_t
>((s_cen.wc_head + 1) % WC_RING);
210 if (next == s_cen.wc_tail) {
212 s_cen.wc_tail =
static_cast<uint8_t
>((s_cen.wc_tail + 1) % WC_RING);
214 WcEvt& e = s_cen.wcring[s_cen.wc_head];
216 e.status =
static_cast<int16_t
>(status);
217 s_cen.wc_head = next;
220void ensure_central_listeners() {
221 if (s_cen.listeners_registered)
return;
224 b->addServiceDiscoveryCallback(on_discovery);
225 b->addCharacteristicReadCallback(on_char_read);
226 b->addNotificationCallback(on_notification);
227 b->addWriteCompleteCallback(on_write_complete);
228 s_cen.listeners_registered =
true;
250 const char*
name = b->getDeviceName();
251 std::strncpy(out,
name ?
name :
"", out_size - 1);
252 out[out_size - 1] =
'\0';
269 if (s_periph.active && s_periph.plugin != plugin)
return HOST_ERR_BUSY;
270 if (s_periph.active) {
277 for (uint32_t i = 0; i < num_chars; i++) {
278 s_value_handles[i] = 0;
286 const uint32_t char_handle = i + 1;
288 gc[i].
onWrite = [char_handle](uint16_t conn, uint16_t,
const uint8_t* d, uint16_t l) ->
int {
290 uint8_t next =
static_cast<uint8_t
>((s_wr_head + 1) % WRITE_RING);
291 if (next == s_wr_tail)
return 0;
292 WriteEvt& e = s_wring[s_wr_head];
293 if (l > BLE_MAX_PAYLOAD) l = BLE_MAX_PAYLOAD;
294 e.char_handle = char_handle;
297 std::memcpy(e.data, d, l);
309 if (!b->registerGattService(svc,
true))
return HOST_ERR_BUSY;
311 s_periph.plugin = plugin;
312 s_periph.active =
true;
313 std::memcpy(s_periph.uuid, def->
uuid, 16);
314 s_periph.num_chars =
static_cast<uint8_t
>(num_chars);
315 for (uint32_t i = 0; i < num_chars; i++) {
333static int periph_send(uint32_t char_handle,
const uint8_t* data,
size_t len,
bool indicate) {
340 uint16_t vh = s_value_handles[char_handle - 1];
341 uint16_t conn = b->getConnectionHandle();
343 bool ok = indicate ? b->sendIndication(conn, vh, data,
static_cast<uint16_t
>(len))
344 : b->sendNotification(conn, vh, data,
static_cast<uint16_t
>(len));
358 if (char_handle != s_cur_write_char)
return 0;
359 size_t n = s_cur_write_len;
360 if (n > buf_size) n = buf_size;
361 std::memcpy(buf, s_cur_write_buf, n);
362 return static_cast<int>(n);
381 uint8_t cap =
static_cast<uint8_t
>(*count > 32 ? 32 : *count);
383 uint8_t n = b->getScanResults(tmp, cap);
384 for (uint8_t i = 0; i < n; i++) {
385 std::memcpy(out[i].addr, tmp[i].mac, 6);
388 std::strncpy(out[i].
name, tmp[i].
name,
sizeof(out[i].
name) - 1);
389 out[i].
name[
sizeof(out[i].
name) - 1] =
'\0';
401 ensure_central_listeners();
408 uint16_t h = b->getConnectionHandle();
409 return h == 0xFFFF ? 0u :
static_cast<uint32_t
>(h);
416 b->disconnectHandle(
static_cast<uint16_t
>(conn));
426 s_cen.conn =
static_cast<uint16_t
>(conn);
427 s_cen.discover_action_id = action_id;
428 ensure_central_listeners();
429 return b->discoverServiceByUuid(
static_cast<uint16_t
>(conn),
BleUuid::from128(uuid))
436 uint8_t n = s_cen.disc_count;
437 if (n > *count) n =
static_cast<uint8_t
>(*count);
448 s_cen.conn =
static_cast<uint16_t
>(conn);
449 s_cen.read_action_id = action_id;
450 ensure_central_listeners();
451 return b->readCharacteristic(
static_cast<uint16_t
>(conn), value_handle)
458 size_t n = s_cen.read_len;
459 if (n > buf_size) n = buf_size;
460 std::memcpy(buf, s_cen.read_buf, n);
462 return static_cast<int>(n);
466 const uint8_t* data,
size_t len, uint8_t with_response) {
471 return b->writeCharacteristic(
static_cast<uint16_t
>(conn), value_handle,
472 data,
static_cast<uint16_t
>(len), with_response != 0)
481 s_cen.notify_action_id = action_id;
482 ensure_central_listeners();
483 return b->enableNotifications(
static_cast<uint16_t
>(conn), cccd_handle)
492 s_cen.conn =
static_cast<uint16_t
>(conn);
493 s_cen.notify_action_id = action_id;
494 ensure_central_listeners();
495 return b->subscribeToCharacteristic(
static_cast<uint16_t
>(conn), value_handle)
501 if (!ble_allowed())
return 0;
503 return b ? b->getMtu() : 0;
509 s_cen.write_action_id = action_id;
510 if (action_id) ensure_central_listeners();
518 NotifyEvt& e = s_cen.nring[s_cen.n_tail];
519 *value_handle_out = e.value_handle;
521 if (n > buf_size) n = buf_size;
522 std::memcpy(buf, e.data, n);
523 s_cen.n_tail =
static_cast<uint8_t
>((s_cen.n_tail + 1) % NOTIFY_RING);
524 return static_cast<int>(n);
533 uint32_t ch = 0, aid = 0;
537 if (s_wr_tail == s_wr_head)
break;
538 WriteEvt& e = s_wring[s_wr_tail];
541 s_cur_write_len = e.len;
542 std::memcpy(s_cur_write_buf, e.data, e.len);
543 s_wr_tail =
static_cast<uint8_t
>((s_wr_tail + 1) % WRITE_RING);
545 if (s_periph.active && s_periph.plugin && ch >= 1 && ch <= s_periph.num_chars) {
546 aid = s_periph.chars[ch - 1].write_action_id;
547 s_cur_write_char = ch;
550 static_cast<pm::Plugin*
>(s_periph.plugin), aid, ch, conn);
552 s_cur_write_char = 0;
558 if (!s_cen.plugin)
return;
559 bool fd, fr, have_notif;
562 fd = s_cen.fire_discovery; s_cen.fire_discovery =
false;
563 fr = s_cen.fire_read; s_cen.fire_read =
false;
564 have_notif = (s_cen.n_tail != s_cen.n_head);
566 auto* pl =
static_cast<pm::Plugin*
>(s_cen.plugin);
568 if (fd && s_cen.discover_action_id) mgr.dispatchActionTo(pl, s_cen.discover_action_id, 0, 0);
569 if (fr && s_cen.read_action_id) mgr.dispatchActionTo(pl, s_cen.read_action_id, 0, 0);
570 if (have_notif && s_cen.notify_action_id) mgr.dispatchActionTo(pl, s_cen.notify_action_id, 0, 0);
573 while (s_cen.write_action_id) {
574 uint16_t attr; int16_t status;
577 if (s_cen.wc_tail == s_cen.wc_head)
break;
578 WcEvt& e = s_cen.wcring[s_cen.wc_tail];
581 s_cen.wc_tail =
static_cast<uint8_t
>((s_cen.wc_tail + 1) % WC_RING);
583 mgr.dispatchActionTo(pl, s_cen.write_action_id, attr,
584 static_cast<uint32_t
>(
static_cast<int32_t
>(status)));
590 if (s_periph.active && s_periph.plugin == plugin) {
595 if (s_cen.plugin == plugin) {
596 s_cen.plugin =
nullptr;
597 s_cen.discover_action_id = s_cen.read_action_id = s_cen.notify_action_id = 0;
598 s_cen.write_action_id = 0;
600 s_cen.wc_tail = s_cen.wc_head;
Discovers, loads, runs and unloads WASM plugins on the badge.
Owned WAMR module instance + per-plugin state.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
static constexpr uint8_t MAX_CHARS_PER_SERVICE
static PluginManager & instance() noexcept
void dispatchActionTo(Plugin *plugin, uint32_t action_id, uint32_t idx, uint32_t user_data)
int host_ble_consume_write(uint32_t char_handle, uint8_t *buf, size_t buf_size)
Pull the next queued inbound write for char_handle.
int host_ble_scan_start(uint32_t duration_ms)
Start a central scan for duration_ms milliseconds.
int host_ble_connect(const uint8_t addr[6], uint8_t addr_type)
Connect to a peer. Completion arrives as a BLE_CONNECTED event; read the resulting handle with host_b...
int host_ble_subscribe(uint32_t conn, uint16_t cccd_handle, uint32_t action_id)
Subscribe to notifications on a peer characteristic (by CCCD handle). Each notification fires action_...
int host_ble_send_notification(uint32_t char_handle, const uint8_t *data, size_t len)
Notify subscribers of a value on one of the plugin's characteristics.
int host_ble_discover(uint32_t conn, const uint8_t uuid[16], uint32_t action_id)
Discover the characteristics of one service on a connected peer. Completion fires action_id; read ent...
bool host_ble_scan_done(void)
True when the scan started by host_ble_scan_start() has finished.
int host_ble_read_char(uint32_t conn, uint16_t value_handle, uint32_t action_id)
Start reading a peer characteristic by value handle. Completion fires action_id; read the value with ...
#define BLE_PROP_WRITE_NO_RSP
int host_ble_disconnect(uint32_t conn)
Disconnect a connection.
uint16_t host_ble_get_mtu(uint32_t conn)
Usable ATT payload of the current connection (negotiated MTU - 3).
int host_ble_device_name(char *out, size_t out_size)
Copy the local BLE device name into out.
int8_t host_ble_rssi(void)
Signal strength of the active BLE link in dBm, or 0 when idle.
int host_ble_unregister_service(uint32_t service_handle)
Tear down the plugin's registered GATT service.
int host_ble_scan_results(ble_scan_result_t *out, size_t *count)
Read results from the last central scan.
int host_ble_on_write_complete(uint32_t action_id)
Register an action fired on every completed central write.
int host_ble_send_indication(uint32_t char_handle, const uint8_t *data, size_t len)
Indicate (acknowledged notify) a value on a plugin characteristic.
bool host_ble_is_enabled(void)
True when the BLE stack is initialised and advertising or connectable.
int host_ble_consume_notification(uint16_t *value_handle_out, uint8_t *buf, size_t buf_size)
Pull the next queued inbound notification.
int host_ble_subscribe_char(uint32_t conn, uint16_t value_handle, uint32_t action_id)
Subscribe to notifications on a peer characteristic by VALUE handle.
int host_ble_register_service(ble_service_def_t *def, ble_char_def_t *chars, uint32_t num_chars)
Register the plugin's GATT service and its characteristics.
int host_ble_write_char(uint32_t conn, uint16_t value_handle, const uint8_t *data, size_t len, uint8_t with_response)
Write a value to a peer characteristic by value handle.
int host_ble_consume_discovery(ble_remote_char_t *out, size_t *count)
Pull discovered characteristics after a discovery action fires.
int host_ble_mac(uint8_t out[6])
Read the local BLE MAC address.
int host_ble_consume_read(uint8_t *buf, size_t buf_size)
Pull the value delivered by the last read action.
uint32_t host_ble_conn_handle(void)
Current connection handle (central or peripheral), or 0 when idle.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NO_CAPABILITY
#define HOST_ERR_INVALID_ARG
#define HOST_ERR_NOT_FOUND
void plg_ble_on_unload(void *plugin)
void * plg_get_active_plugin(void)
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
static int periph_send(uint32_t char_handle, const uint8_t *data, size_t len, bool indicate)
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
static BleUuid from128(const uint8_t v[16])
DiscoveredCharacteristic characteristics[MAX_DISCOVERED_CHARS]
uint8_t numCharacteristics
One characteristic of a plugin GATT service (peripheral role).
One characteristic discovered on a connected peer (central role).
One device from a central scan.
A plugin GATT service definition (peripheral role). Always primary.
GattWriteCallback onWrite
uint8_t numCharacteristics
GattCharacteristic * characteristics