openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
aliro_ble_central.h
1// Device-side (User-Device) BLE transport interface: the central/client mirror of
2// aliro_ble.h. Where the reader advertises 0xFFF2, serves the GATT characteristics
3// and runs an L2CAP CoC server, the initiator scans, connects, reads the reader's
4// SPSM/versions, writes its selected version and opens a CoC client to that SPSM.
5//
6// The platform-free half (advert + READ-payload decoding, BleSK salt assembly)
7// lives in aliro_ble_central.c and is host-testable; the NimBLE backend for the
8// transport calls sits in ports/esp32, so a Zephyr bt_gap_*/bt_l2cap_* backend
9// can be written behind this same header.
10/*
11 * Copyright (c) 2026 asxeem
12 * SPDX-License-Identifier: ISC
13 *
14 * Provenance: clean-room. Every byte layout here is the inverse of our own
15 * reader's emitters in ports/esp32/components/aliro_ble/aliro_ble.c
16 * (build_aliro_svc_data, build_read_payload).
17 */
18#pragma once
19
20#include <stddef.h>
21#include <stdint.h>
22
23#include "aliro_advtag.h" /* ALIRO_ADVTAG_LEN */
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29/* Most protocol versions we will record from a peer's GATT READ. The reader caps
30 * its own advertised list at 8 (ALIRO_MAX_VERSIONS in aliro_ble.c). */
31#define ALIRO_BLE_CENTRAL_MAX_VERSIONS 8u
32
33/* Aliro service data is 26 B on air: the 2-byte 0xFFF2 UUID then 24 B payload. */
34#define ALIRO_BLE_CENTRAL_SVC_DATA_LEN 26u
35
36/* Reader identity recovered from one 0xFFF2 service-data advert. Field order is
37 * the inverse of build_aliro_svc_data (aliro_ble.c:532). */
38struct aliro_ble_central_adv {
39 uint8_t flags; /* bit7 = BLE+UWB supported, bits2:0 = advert version */
40 int8_t tx_power; /* dBm as advertised */
41 uint8_t group_id[8]; /* truncated reader group id = reader_id[0..7] */
42 uint8_t sub_id[2]; /* truncated reader group sub id = reader_id[16..17] */
43 uint32_t expiry; /* dynamic-tag expiry, 0xFFFFFFFF = reader has no clock */
44 uint8_t tag[ALIRO_ADVTAG_LEN];
45};
46
47/* What the peer publishes on the reader-SPSM characteristic
48 * (D3B5A130-9E23-4B3A-8BE4-6B1EE5F980A3), read before the CoC opens. */
49struct aliro_ble_central_peer {
50 uint16_t spsm; /* dynamic-range PSM; NOT well-known, must come from the READ */
51 uint16_t versions[ALIRO_BLE_CENTRAL_MAX_VERSIONS]; /* reader_supported_versions */
52 size_t versions_count;
53 uint8_t features;
54};
55
56/* Decode 0xFFF2 service data. svc_data must be the full 26 B including the
57 * leading little-endian UUID, which is checked. Returns 0, or -1 on a short
58 * buffer or a UUID that is not 0xFFF2. */
59int aliro_ble_central_parse_adv(const uint8_t *svc_data, size_t len,
60 struct aliro_ble_central_adv *out);
61
62/* True when this advert is from the reader we are provisioned against: the
63 * advertised group id/sub id are truncations of reader_id, so compare against
64 * reader_id[0..7] and reader_id[16..17] rather than the whole identifier. */
65int aliro_ble_central_adv_matches(const struct aliro_ble_central_adv *adv,
66 const uint8_t reader_id[32]);
67
68/* Decode the reader-SPSM READ payload, whose layout is
69 * [spsm_be16][versions_len = 2N][version_be16 x N][features_len = 1][features].
70 * Inverse of build_read_payload (aliro_ble.c:271). Returns 0, or -1 if the
71 * buffer is short, a length field disagrees with the payload, the version list
72 * is odd-length, or it holds more than ALIRO_BLE_CENTRAL_MAX_VERSIONS entries. */
73int aliro_ble_central_parse_read_payload(const uint8_t *payload, size_t len,
74 struct aliro_ble_central_peer *out);
75
76/* Build the BleSK HKDF salt: reader_supported_versions || selected_version, each
77 * big-endian (§11.8.1). The peer's list comes from the GATT READ above, so a
78 * multi-version reader is handled without hardcoding — this is the input the
79 * reader itself derives from k_proto_versions (aliro_reader.c:390). Needs
80 * 2 * (versions_count + 1) bytes of capacity. Returns 0 or -1. */
81int aliro_ble_central_blesk_salt(const struct aliro_ble_central_peer *peer, uint16_t selected,
82 uint8_t *out, size_t cap, size_t *out_len);
83
84/* ---- transport (backend-provided; NimBLE one lives in ports/esp32) ---- */
85
91 /* The CoC is open and the peer's GATT facts are known: the Aliro
92 * transaction can start. peer stays valid only for the call. */
93 void (*on_ready)(uint16_t conn_handle, const struct aliro_ble_central_peer *peer);
94 /* One inbound SDU from the reader (an AP response or a sealed ranging SDU). */
95 void (*on_data)(uint16_t conn_handle, const uint8_t *data, size_t len);
96 /* CoC or link dropped; any session state keyed on conn_handle is dead. */
97 void (*on_closed)(uint16_t conn_handle);
98};
99
105 /* The reader we are provisioned against; scanning matches its advert by
106 * the truncated group id/sub id (see aliro_ble_central_adv_matches). */
107 uint8_t reader_id[32];
108 uint16_t selected_version; /* written to the device-version characteristic */
110};
111
112/* Bring up the BLE host in the central role and start scanning for the
113 * configured reader. Drives connect -> GATT discovery -> READ/WRITE -> CoC and
114 * reports through cfg->cb. Returns 0 once the host is running, <0 on setup
115 * failure; discovery outcomes arrive via the callbacks. */
116int aliro_ble_central_start(const struct aliro_ble_central_config *cfg);
117
118/* Send one SDU to the reader over the open CoC. Returns 0 on success (including
119 * the queued-on-stall case), <0 if there is no channel or the send fails. */
120int aliro_ble_central_send(uint16_t conn_handle, const uint8_t *data, size_t len);
121
122#ifdef __cplusplus
123}
124#endif
Callbacks for BLE central transport: on_ready when CoC opens and peer facts are known,...
Definition aliro_ble_central.h:90
Configuration for BLE central transport: reader_id to match by truncated group/sub ID in adverts,...
Definition aliro_ble_central.h:104