CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
host_api_ble.cpp
Go to the documentation of this file.
1
17
20#include "esp_attr.h"
23
24#include "freertos/FreeRTOS.h"
25#include "freertos/semphr.h"
26
27#include <cstring>
28
35namespace pm = cdc::plugin_manager;
36
37extern "C" void* plg_get_active_plugin(void);
38
39namespace {
40
41constexpr uint16_t BLE_MAX_PAYLOAD = 244; // default-MTU payload budget
42constexpr uint32_t PLUGIN_SERVICE_HANDLE = 1; // only one plugin GATT service exists
43constexpr uint8_t MAX_PLUGIN_CHARS = cdc::hal::IBluetoothController::MAX_CHARS_PER_SERVICE;
44constexpr uint8_t WRITE_RING = 4;
45constexpr uint8_t NOTIFY_RING = 4;
46constexpr uint8_t MAX_DISC_CHARS = 8;
47
48// --- synchronisation between the NimBLE task and the plugin tick task ---
49SemaphoreHandle_t s_lock = nullptr;
50void lock_init() { if (!s_lock) s_lock = xSemaphoreCreateMutex(); }
51struct Guard {
52 bool held = false;
53 Guard() { if (s_lock) held = (xSemaphoreTake(s_lock, portMAX_DELAY) == pdTRUE); }
54 ~Guard() { if (held) xSemaphoreGive(s_lock); }
55};
56
57// --- peripheral (GATT server) state ---
58// NimBLE writes the assigned attribute handle here; it stays valid across a
59// deferred (BLE-disabled) registration that is only committed at enable().
60uint16_t s_value_handles[MAX_PLUGIN_CHARS];
61struct PeriphChar {
62 uint32_t write_action_id = 0;
63};
64struct {
65 void* plugin = nullptr;
66 bool active = false;
67 uint8_t uuid[16] = {};
68 uint8_t num_chars = 0;
69 PeriphChar chars[MAX_PLUGIN_CHARS];
70} s_periph;
71
72struct WriteEvt {
73 uint32_t char_handle;
74 uint16_t conn;
75 uint16_t len;
76 uint8_t data[BLE_MAX_PAYLOAD];
77};
78WriteEvt s_wring[WRITE_RING];
79volatile uint8_t s_wr_head = 0, s_wr_tail = 0; // head=produced, tail=consumed
80
81// Payload currently being delivered to a write action (tick-task only).
82uint32_t s_cur_write_char = 0;
83uint16_t s_cur_write_len = 0;
84uint8_t s_cur_write_buf[BLE_MAX_PAYLOAD];
85
86// --- central (GATT client) state ---
87struct NotifyEvt { uint16_t value_handle; uint16_t len; uint8_t data[BLE_MAX_PAYLOAD]; };
88struct {
89 void* plugin = nullptr;
90 bool listeners_registered = false;
91 // Connection handle of the plugin's own central op; shared GATT-client
92 // callbacks ignore events from any other connection (e.g. the system BLE
93 // name resolver) so they don't clobber this state.
94 uint16_t conn = 0xFFFF;
95 uint32_t discover_action_id = 0;
96 uint32_t read_action_id = 0;
97 uint32_t notify_action_id = 0;
98
99 // discovery result stash
100 ble_remote_char_t disc[MAX_DISC_CHARS];
101 uint8_t disc_count = 0;
102 bool fire_discovery = false;
103
104 // read result stash
105 uint8_t read_buf[BLE_MAX_PAYLOAD];
106 uint16_t read_len = 0;
107 bool fire_read = false;
108
109 // inbound notification ring
110 NotifyEvt nring[NOTIFY_RING];
111 uint8_t n_head = 0, n_tail = 0;
112} s_cen EXT_RAM_BSS_ATTR;
113
114// --- helpers ---
115
116IBluetoothController* ble() { return getBluetoothControllerInstance(); }
117
118bool ble_allowed() {
119 auto* p = static_cast<pm::Plugin*>(plg_get_active_plugin());
120 return p && p->manifest().capabilities.ble;
121}
122
123// Build a 16-byte little-endian UUID array from a stack-independent BleUuid.
124void uuid_to_bytes(const BleUuid& u, uint8_t out[16]) {
125 if (u.type == BleUuid::UUID_128) {
126 std::memcpy(out, u.u128, 16);
127 } else {
128 static const uint8_t base[16] = {
129 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
130 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
131 };
132 std::memcpy(out, base, 16);
133 out[12] = static_cast<uint8_t>(u.u16 & 0xFF);
134 out[13] = static_cast<uint8_t>(u.u16 >> 8);
135 }
136}
137
138// Reserved system service UUIDs (little-endian, BleUuid::from128 order).
139const uint8_t NUS_SVC[16] = { 0x9e,0xca,0xdc,0x24,0x0e,0xe5,0xa9,0xe0,
140 0x93,0xf3,0xa3,0xb5,0x01,0x00,0x40,0x6e };
141const uint8_t VCARD_SVC[16] = { 0x01,0x1A,0x8B,0x6A,0x9D,0x4C,0x6E,0x9A,
142 0x7A,0x4D,0x5D,0x8B,0x20,0x1F,0x2F,0x8E };
143const uint8_t GPG_SVC[16] = { 0x01,0x1A,0x8B,0x6A,0x9D,0x4C,0x6E,0x9A,
144 0x7A,0x4D,0x5D,0x8B,0x30,0x1F,0x2F,0x8E };
145
146// Block the Bluetooth SIG 16-bit range (so a plugin can never shadow HID,
147// Device Info, Battery, GAP, GATT, ...) and the system's 128-bit services.
148bool uuid_is_reserved(const uint8_t uuid[16]) {
149 static const uint8_t sig_base[12] = {
150 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00
151 };
152 if (std::memcmp(uuid, sig_base, 12) == 0 && uuid[14] == 0 && uuid[15] == 0) {
153 return true;
154 }
155 return std::memcmp(uuid, NUS_SVC, 16) == 0
156 || std::memcmp(uuid, VCARD_SVC, 16) == 0
157 || std::memcmp(uuid, GPG_SVC, 16) == 0;
158}
159
160// --- central callback registration (host-side, fires on NimBLE/EventBus task) ---
161void on_discovery(uint16_t connHandle, const IBluetoothController::DiscoveredService* svc, bool complete) {
162 Guard g;
163 if (connHandle != s_cen.conn) return; // not our central op
164 if (svc) {
165 uint8_t n = svc->numCharacteristics;
166 if (n > MAX_DISC_CHARS) n = MAX_DISC_CHARS;
167 for (uint8_t i = 0; i < n; i++) {
168 uuid_to_bytes(svc->characteristics[i].uuid, s_cen.disc[i].uuid);
169 s_cen.disc[i].value_handle = svc->characteristics[i].valueHandle;
170 s_cen.disc[i].properties = svc->characteristics[i].properties;
171 s_cen.disc[i].reserved = 0;
172 }
173 s_cen.disc_count = n;
174 }
175 if (complete) s_cen.fire_discovery = true;
176}
177
178void on_char_read(uint16_t connHandle, uint16_t, const uint8_t* data, uint16_t len) {
179 Guard g;
180 if (connHandle != s_cen.conn) return; // not our central op
181 if (len > BLE_MAX_PAYLOAD) len = BLE_MAX_PAYLOAD;
182 std::memcpy(s_cen.read_buf, data, len);
183 s_cen.read_len = len;
184 s_cen.fire_read = true;
185}
186
187void on_notification(uint16_t, uint16_t attr, const uint8_t* data, uint16_t len) {
188 Guard g;
189 uint8_t next = static_cast<uint8_t>((s_cen.n_head + 1) % NOTIFY_RING);
190 if (next == s_cen.n_tail) return; // ring full: drop
191 NotifyEvt& e = s_cen.nring[s_cen.n_head];
192 if (len > BLE_MAX_PAYLOAD) len = BLE_MAX_PAYLOAD;
193 e.value_handle = attr;
194 e.len = len;
195 std::memcpy(e.data, data, len);
196 s_cen.n_head = next;
197}
198
199void ensure_central_listeners() {
200 if (s_cen.listeners_registered) return;
201 auto* b = ble();
202 if (!b) return;
203 b->addServiceDiscoveryCallback(on_discovery);
204 b->addCharacteristicReadCallback(on_char_read);
205 b->addNotificationCallback(on_notification);
206 s_cen.listeners_registered = true;
207}
208
209} // namespace
210
211extern "C" {
212
213/* ---------------------------------------------------------------- state -- */
214
215bool host_ble_is_enabled(void) { auto* b = ble(); return b ? b->isEnabled() : false; }
216
217int host_ble_mac(uint8_t out[6]) {
218 if (!out) return HOST_ERR_INVALID_ARG;
219 auto* b = ble();
220 if (!b) return HOST_ERR_NOT_FOUND;
221 return b->getMacAddress(out) ? HOST_OK : HOST_ERR_GENERIC;
222}
223
224int host_ble_device_name(char* out, size_t out_size) {
225 if (!out || out_size == 0) return HOST_ERR_INVALID_ARG;
226 auto* b = ble();
227 if (!b) return HOST_ERR_NOT_FOUND;
228 const char* name = b->getDeviceName();
229 std::strncpy(out, name ? name : "", out_size - 1);
230 out[out_size - 1] = '\0';
231 return HOST_OK;
232}
233
234int8_t host_ble_rssi(void) { auto* b = ble(); return b ? b->getRssi() : 0; }
235
236/* ----------------------------------------------------- peripheral (GATT) -- */
237
238int host_ble_register_service(ble_service_def_t* def, ble_char_def_t* chars, uint32_t num_chars) {
239 if (!def || !chars || num_chars == 0) return HOST_ERR_INVALID_ARG;
240 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
241 auto* b = ble();
242 if (!b) return HOST_ERR_NOT_FOUND;
243 if (num_chars > MAX_PLUGIN_CHARS) return HOST_ERR_INVALID_ARG;
244 if (uuid_is_reserved(def->uuid)) return HOST_ERR_NO_CAPABILITY;
245
246 void* plugin = plg_get_active_plugin();
247 if (s_periph.active && s_periph.plugin != plugin) return HOST_ERR_BUSY;
248 if (s_periph.active) {
249 b->unregisterGattService(BleUuid::from128(s_periph.uuid));
250 s_periph = {};
251 }
252 lock_init();
253
254 GattCharacteristic gc[MAX_PLUGIN_CHARS] = {};
255 for (uint32_t i = 0; i < num_chars; i++) {
256 s_value_handles[i] = 0;
257 gc[i].uuid = BleUuid::from128(chars[i].uuid);
258 gc[i].properties = chars[i].properties;
259 gc[i].permissions = 0;
260 if (chars[i].properties & BLE_PROP_READ) gc[i].permissions |= cdc::hal::GattPerm::READ;
261 if (chars[i].properties & (BLE_PROP_WRITE | BLE_PROP_WRITE_NO_RSP))
263 gc[i].valueHandle = &s_value_handles[i];
264 const uint32_t char_handle = i + 1;
265 if (chars[i].properties & (BLE_PROP_WRITE | BLE_PROP_WRITE_NO_RSP)) {
266 gc[i].onWrite = [char_handle](uint16_t conn, uint16_t, const uint8_t* d, uint16_t l) -> int {
267 Guard g;
268 uint8_t next = static_cast<uint8_t>((s_wr_head + 1) % WRITE_RING);
269 if (next == s_wr_tail) return 0; // ring full: drop
270 WriteEvt& e = s_wring[s_wr_head];
271 if (l > BLE_MAX_PAYLOAD) l = BLE_MAX_PAYLOAD;
272 e.char_handle = char_handle;
273 e.conn = conn;
274 e.len = l;
275 std::memcpy(e.data, d, l);
276 s_wr_head = next;
277 return 0;
278 };
279 }
280 }
281
282 GattServiceDef svc{};
283 svc.uuid = BleUuid::from128(def->uuid);
284 svc.characteristics = gc;
285 svc.numCharacteristics = static_cast<uint8_t>(num_chars);
286
287 if (!b->registerGattService(svc, /*pluginReserved=*/true)) return HOST_ERR_BUSY;
288
289 s_periph.plugin = plugin;
290 s_periph.active = true;
291 std::memcpy(s_periph.uuid, def->uuid, 16);
292 s_periph.num_chars = static_cast<uint8_t>(num_chars);
293 for (uint32_t i = 0; i < num_chars; i++) {
294 s_periph.chars[i].write_action_id = chars[i].write_action_id;
295 chars[i].char_handle = i + 1;
296 }
297 def->service_handle = PLUGIN_SERVICE_HANDLE;
298 return HOST_OK;
299}
300
301int host_ble_unregister_service(uint32_t service_handle) {
302 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
303 if (!s_periph.active || s_periph.plugin != plg_get_active_plugin()) return HOST_ERR_NOT_FOUND;
304 if (service_handle != PLUGIN_SERVICE_HANDLE) return HOST_ERR_NOT_FOUND;
305 auto* b = ble();
306 if (b) b->unregisterGattService(BleUuid::from128(s_periph.uuid));
307 s_periph = {};
308 return HOST_OK;
309}
310
311static int periph_send(uint32_t char_handle, const uint8_t* data, size_t len, bool indicate) {
312 if (!data && len) return HOST_ERR_INVALID_ARG;
313 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
314 if (!s_periph.active || s_periph.plugin != plg_get_active_plugin()) return HOST_ERR_NOT_FOUND;
315 if (char_handle < 1 || char_handle > s_periph.num_chars) return HOST_ERR_INVALID_ARG;
316 auto* b = ble();
317 if (!b) return HOST_ERR_NOT_FOUND;
318 uint16_t vh = s_value_handles[char_handle - 1];
319 uint16_t conn = b->getConnectionHandle();
320 if (conn == 0xFFFF) return HOST_ERR_NOT_FOUND;
321 bool ok = indicate ? b->sendIndication(conn, vh, data, static_cast<uint16_t>(len))
322 : b->sendNotification(conn, vh, data, static_cast<uint16_t>(len));
323 return ok ? HOST_OK : HOST_ERR_GENERIC;
324}
325
326int host_ble_send_notification(uint32_t char_handle, const uint8_t* data, size_t len) {
327 return periph_send(char_handle, data, len, false);
328}
329
330int host_ble_send_indication(uint32_t char_handle, const uint8_t* data, size_t len) {
331 return periph_send(char_handle, data, len, true);
332}
333
334int host_ble_consume_write(uint32_t char_handle, uint8_t* buf, size_t buf_size) {
335 if (!buf || buf_size == 0) return HOST_ERR_INVALID_ARG;
336 if (char_handle != s_cur_write_char) return 0;
337 size_t n = s_cur_write_len;
338 if (n > buf_size) n = buf_size;
339 std::memcpy(buf, s_cur_write_buf, n);
340 return static_cast<int>(n);
341}
342
343/* ---------------------------------------------------------- central role -- */
344
345int host_ble_scan_start(uint32_t duration_ms) {
346 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
347 auto* b = ble();
348 if (!b) return HOST_ERR_NOT_FOUND;
349 return b->startScan(duration_ms ? duration_ms : 5000) ? HOST_OK : HOST_ERR_GENERIC;
350}
351
352bool host_ble_scan_done(void) { auto* b = ble(); return b ? b->isScanComplete() : true; }
353
354int host_ble_scan_results(ble_scan_result_t* out, size_t* count) {
355 if (!out || !count) return HOST_ERR_INVALID_ARG;
356 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
357 auto* b = ble();
358 if (!b) return HOST_ERR_NOT_FOUND;
359 uint8_t cap = static_cast<uint8_t>(*count > 32 ? 32 : *count);
360 BleScanResult tmp[32];
361 uint8_t n = b->getScanResults(tmp, cap);
362 for (uint8_t i = 0; i < n; i++) {
363 std::memcpy(out[i].addr, tmp[i].mac, 6);
364 out[i].addr_type = tmp[i].addrType;
365 out[i].rssi = tmp[i].rssi;
366 std::strncpy(out[i].name, tmp[i].name, sizeof(out[i].name) - 1);
367 out[i].name[sizeof(out[i].name) - 1] = '\0';
368 }
369 *count = n;
370 return HOST_OK;
371}
372
373int host_ble_connect(const uint8_t addr[6], uint8_t addr_type) {
374 if (!addr) return HOST_ERR_INVALID_ARG;
375 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
376 auto* b = ble();
377 if (!b) return HOST_ERR_NOT_FOUND;
378 s_cen.plugin = plg_get_active_plugin();
379 ensure_central_listeners();
380 return b->connect(addr, addr_type) ? HOST_OK : HOST_ERR_GENERIC;
381}
382
383uint32_t host_ble_conn_handle(void) {
384 auto* b = ble();
385 if (!b) return 0;
386 uint16_t h = b->getConnectionHandle();
387 return h == 0xFFFF ? 0u : static_cast<uint32_t>(h);
388}
389
390int host_ble_disconnect(uint32_t conn) {
391 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
392 auto* b = ble();
393 if (!b) return HOST_ERR_NOT_FOUND;
394 b->disconnectHandle(static_cast<uint16_t>(conn));
395 return HOST_OK;
396}
397
398int host_ble_discover(uint32_t conn, const uint8_t uuid[16], uint32_t action_id) {
399 if (!uuid) return HOST_ERR_INVALID_ARG;
400 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
401 auto* b = ble();
402 if (!b) return HOST_ERR_NOT_FOUND;
403 s_cen.plugin = plg_get_active_plugin();
404 s_cen.conn = static_cast<uint16_t>(conn);
405 s_cen.discover_action_id = action_id;
406 ensure_central_listeners();
407 return b->discoverServiceByUuid(static_cast<uint16_t>(conn), BleUuid::from128(uuid))
409}
410
412 if (!out || !count) return HOST_ERR_INVALID_ARG;
413 Guard g;
414 uint8_t n = s_cen.disc_count;
415 if (n > *count) n = static_cast<uint8_t>(*count);
416 std::memcpy(out, s_cen.disc, n * sizeof(ble_remote_char_t));
417 *count = n;
418 return HOST_OK;
419}
420
421int host_ble_read_char(uint32_t conn, uint16_t value_handle, uint32_t action_id) {
422 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
423 auto* b = ble();
424 if (!b) return HOST_ERR_NOT_FOUND;
425 s_cen.plugin = plg_get_active_plugin();
426 s_cen.conn = static_cast<uint16_t>(conn);
427 s_cen.read_action_id = action_id;
428 ensure_central_listeners();
429 return b->readCharacteristic(static_cast<uint16_t>(conn), value_handle)
431}
432
433int host_ble_consume_read(uint8_t* buf, size_t buf_size) {
434 if (!buf || buf_size == 0) return HOST_ERR_INVALID_ARG;
435 Guard g;
436 size_t n = s_cen.read_len;
437 if (n > buf_size) n = buf_size;
438 std::memcpy(buf, s_cen.read_buf, n);
439 s_cen.read_len = 0;
440 return static_cast<int>(n);
441}
442
443int host_ble_write_char(uint32_t conn, uint16_t value_handle,
444 const uint8_t* data, size_t len, uint8_t with_response) {
445 if (!data && len) return HOST_ERR_INVALID_ARG;
446 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
447 auto* b = ble();
448 if (!b) return HOST_ERR_NOT_FOUND;
449 return b->writeCharacteristic(static_cast<uint16_t>(conn), value_handle,
450 data, static_cast<uint16_t>(len), with_response != 0)
452}
453
454int host_ble_subscribe(uint32_t conn, uint16_t cccd_handle, uint32_t action_id) {
455 if (!ble_allowed()) return HOST_ERR_NO_CAPABILITY;
456 auto* b = ble();
457 if (!b) return HOST_ERR_NOT_FOUND;
458 s_cen.plugin = plg_get_active_plugin();
459 s_cen.notify_action_id = action_id;
460 ensure_central_listeners();
461 return b->enableNotifications(static_cast<uint16_t>(conn), cccd_handle)
463}
464
465int host_ble_consume_notification(uint16_t* value_handle_out, uint8_t* buf, size_t buf_size) {
466 if (!value_handle_out || !buf || buf_size == 0) return HOST_ERR_INVALID_ARG;
467 Guard g;
468 if (s_cen.n_tail == s_cen.n_head) return HOST_ERR_NOT_FOUND;
469 NotifyEvt& e = s_cen.nring[s_cen.n_tail];
470 *value_handle_out = e.value_handle;
471 size_t n = e.len;
472 if (n > buf_size) n = buf_size;
473 std::memcpy(buf, e.data, n);
474 s_cen.n_tail = static_cast<uint8_t>((s_cen.n_tail + 1) % NOTIFY_RING);
475 return static_cast<int>(n);
476}
477
478/* ------------------------------------------------ tick pump + unload hook -- */
479
480// Drains queued BLE events on the plugin tick task and fires plugin actions.
481void plg_ble_pump(void) {
482 // Peripheral writes.
483 for (;;) {
484 uint32_t ch = 0, aid = 0;
485 uint16_t conn = 0;
486 {
487 Guard g;
488 if (s_wr_tail == s_wr_head) break;
489 WriteEvt& e = s_wring[s_wr_tail];
490 ch = e.char_handle;
491 conn = e.conn;
492 s_cur_write_len = e.len;
493 std::memcpy(s_cur_write_buf, e.data, e.len);
494 s_wr_tail = static_cast<uint8_t>((s_wr_tail + 1) % WRITE_RING);
495 }
496 if (s_periph.active && s_periph.plugin && ch >= 1 && ch <= s_periph.num_chars) {
497 aid = s_periph.chars[ch - 1].write_action_id;
498 s_cur_write_char = ch;
499 if (aid) {
501 static_cast<pm::Plugin*>(s_periph.plugin), aid, ch, conn);
502 }
503 s_cur_write_char = 0;
504 s_cur_write_len = 0;
505 }
506 }
507
508 // Central completions.
509 if (!s_cen.plugin) return;
510 bool fd, fr, have_notif;
511 {
512 Guard g;
513 fd = s_cen.fire_discovery; s_cen.fire_discovery = false;
514 fr = s_cen.fire_read; s_cen.fire_read = false;
515 have_notif = (s_cen.n_tail != s_cen.n_head);
516 }
517 auto* pl = static_cast<pm::Plugin*>(s_cen.plugin);
518 auto& mgr = pm::PluginManager::instance();
519 if (fd && s_cen.discover_action_id) mgr.dispatchActionTo(pl, s_cen.discover_action_id, 0, 0);
520 if (fr && s_cen.read_action_id) mgr.dispatchActionTo(pl, s_cen.read_action_id, 0, 0);
521 if (have_notif && s_cen.notify_action_id) mgr.dispatchActionTo(pl, s_cen.notify_action_id, 0, 0);
522}
523
524// Drop any BLE resources owned by a plugin that is being unloaded.
525void plg_ble_on_unload(void* plugin) {
526 if (s_periph.active && s_periph.plugin == plugin) {
527 auto* b = ble();
528 if (b) b->unregisterGattService(BleUuid::from128(s_periph.uuid));
529 s_periph = {};
530 }
531 if (s_cen.plugin == plugin) {
532 s_cen.plugin = nullptr;
533 s_cen.discover_action_id = s_cen.read_action_id = s_cen.notify_action_id = 0;
534 }
535}
536
537} // extern "C"
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.
#define BLE_PROP_WRITE
Definition host_api.h:475
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
Definition host_api.h:474
int host_ble_disconnect(uint32_t conn)
Disconnect a connection.
int host_ble_device_name(char *out, size_t out_size)
Copy the local BLE device name into out.
#define BLE_PROP_READ
Definition host_api.h:473
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_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_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
Definition host_api.h:40
#define HOST_OK
Definition host_api.h:37
#define HOST_ERR_INVALID_ARG
Definition host_api.h:39
#define HOST_ERR_NOT_FOUND
Definition host_api.h:41
#define HOST_ERR_GENERIC
Definition host_api.h:38
#define HOST_ERR_BUSY
Definition host_api.h:44
void plg_ble_on_unload(void *plugin)
void * plg_get_active_plugin(void)
void plg_ble_pump(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)
constexpr uint8_t WRITE
constexpr uint8_t READ
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
static BleUuid from128(const uint8_t v[16])
DiscoveredCharacteristic characteristics[MAX_DISCOVERED_CHARS]
One characteristic of a plugin GATT service (peripheral role).
Definition host_api.h:480
uint32_t write_action_id
Definition host_api.h:484
uint32_t char_handle
Definition host_api.h:485
uint8_t properties
Definition host_api.h:482
One characteristic discovered on a connected peer (central role).
Definition host_api.h:505
One device from a central scan.
Definition host_api.h:497
uint8_t addr_type
Definition host_api.h:499
A plugin GATT service definition (peripheral role). Always primary.
Definition host_api.h:489
uint8_t uuid[16]
Definition host_api.h:490
uint32_t service_handle
Definition host_api.h:493
GattCharacteristic * characteristics