CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
BleUartService.cpp
Go to the documentation of this file.
1
5
8#include "cdc_log.h"
9#include <cstring>
10
11static const char* TAG = "BLE_UART";
12
13namespace cdc::mod_ble_serial {
14
16static const uint8_t NUS_SVC_UUID[16] = {
17 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
18 0x93, 0xf3, 0xa3, 0xb5, 0x01, 0x00, 0x40, 0x6e
19};
20
21static const uint8_t NUS_RX_UUID[16] = {
22 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
23 0x93, 0xf3, 0xa3, 0xb5, 0x02, 0x00, 0x40, 0x6e
24};
25
26static const uint8_t NUS_TX_UUID[16] = {
27 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
28 0x93, 0xf3, 0xa3, 0xb5, 0x03, 0x00, 0x40, 0x6e
29};
30
35BleUartService& BleUartService::instance() {
36 static BleUartService* inst = new BleUartService();
37 return *inst;
38}
39
45 if (initialized_) {
46 return true;
47 }
48
50 if (!ble || !ble->isEnabled()) {
51 LOG_E(TAG, "BLE not enabled");
52 return false;
53 }
54
55 // Define GATT characteristics via API types
56 using namespace cdc::hal;
57
58 static GattCharacteristic chars[2];
59
60 // RX characteristic (phone writes to badge)
64 chars[0].valueHandle = nullptr;
65 chars[0].onWrite = [](uint16_t /*connHandle*/, uint16_t /*attrHandle*/,
66 const uint8_t* data, uint16_t len) -> int {
68 return 0;
69 };
70 chars[0].onRead = nullptr;
71
72 // TX characteristic (badge notifies phone)
75 chars[1].permissions = GattPerm::READ;
76 chars[1].valueHandle = &txCharHandle_;
77 chars[1].onWrite = nullptr;
78 chars[1].onRead = nullptr;
79
80 // Register GATT service
81 static GattServiceDef svcDef;
83 svcDef.characteristics = chars;
84 svcDef.numCharacteristics = 2;
85
86 if (!ble->registerGattService(svcDef)) {
87 LOG_E(TAG, "Failed to register NUS GATT service");
88 return false;
89 }
90
91 // Advertise NUS service UUID so scanners can find us
92 ble->addAdvertisingUuid(BleUuid::from128(NUS_SVC_UUID));
93
94 // Register connection callbacks
95 connToken_ = ble->addConnectionCallback([](uint16_t /*connHandle*/) {
97 });
98 disconnToken_ = ble->addDisconnectionCallback([](uint16_t /*connHandle*/, int /*reason*/) {
100 });
101
102 // Reset RX buffer
103 rxHead_.store(0, std::memory_order_relaxed);
104 rxTail_.store(0, std::memory_order_relaxed);
105
106 initialized_ = true;
107 LOG_I(TAG, "NUS service registered (txHandle=%d)", txCharHandle_);
108 return true;
109}
110
115 if (!initialized_) {
116 return;
117 }
118
120 if (ble) {
121 ble->removeAdvertisingUuid(cdc::hal::BleUuid::from128(NUS_SVC_UUID));
122 ble->removeConnectionCallback(connToken_);
123 ble->removeDisconnectionCallback(disconnToken_);
126 }
127
128 initialized_ = false;
129 LOG_I(TAG, "NUS service deinitialized");
130}
131
138size_t BleUartService::send(const uint8_t* data, size_t len) {
139 if (!initialized_ || !isConnected() || !data || len == 0) {
140 return 0;
141 }
142
143 // Recursion guard - prevent logging from causing infinite loop
144 if (txInProgress_) {
145 return 0;
146 }
147 txInProgress_ = true;
148
150 if (!ble) {
151 txInProgress_ = false;
152 return 0;
153 }
154
155 uint16_t connHandle = ble->getConnectionHandle();
156 uint16_t mtu = ble->getMtu();
157 // getMtu() already subtracts the 3-byte ATT header, but defend against
158 // implementations that return the raw ATT MTU instead.
159 if (mtu < 4) {
160 txInProgress_ = false;
161 return 0;
162 }
163
164 size_t sent = 0;
165 while (sent < len) {
166 size_t remaining = len - sent;
167 uint16_t chunkLen = static_cast<uint16_t>(
168 (remaining > mtu) ? mtu : remaining);
169
170 if (!ble->sendNotification(connHandle, txCharHandle_, data + sent, chunkLen)) {
171 break;
172 }
173 sent += chunkLen;
174 }
175
176 txInProgress_ = false;
177 return sent;
178}
179
185size_t BleUartService::send(const char* str) {
186 if (!str) return 0;
187 return send(reinterpret_cast<const uint8_t*>(str), strlen(str));
188}
189
195 return initialized_ && isConnected() && !txCongested_;
196}
197
203 if (!initialized_) return 0;
204
205 size_t head = rxHead_.load(std::memory_order_acquire);
206 size_t tail = rxTail_.load(std::memory_order_acquire);
207
208 if (head >= tail) {
209 return head - tail;
210 } else {
211 return RX_BUFFER_SIZE - tail + head;
212 }
213}
214
220 if (!initialized_ || available() == 0) {
221 return -1;
222 }
223
224 size_t tail = rxTail_.load(std::memory_order_relaxed);
225 uint8_t c = rxBuffer_[tail];
226 rxTail_.store((tail + 1) % RX_BUFFER_SIZE, std::memory_order_release);
227 return c;
228}
229
236size_t BleUartService::read(uint8_t* buf, size_t maxLen) {
237 if (!buf || maxLen == 0) return 0;
238
239 size_t count = 0;
240 while (count < maxLen && available() > 0) {
241 int c = getchar();
242 if (c < 0) break;
243 buf[count++] = static_cast<uint8_t>(c);
244 }
245 return count;
246}
247
254 return ble && ble->isConnected();
255}
256
262void BleUartService::onRxData(const uint8_t* data, size_t len) {
263 if (!data || len == 0) return;
264
265 size_t head = rxHead_.load(std::memory_order_relaxed);
266 for (size_t i = 0; i < len; i++) {
267 size_t nextHead = (head + 1) % RX_BUFFER_SIZE;
268 size_t tail = rxTail_.load(std::memory_order_acquire);
269 if (nextHead == tail) {
270 // Buffer full - drop oldest
271 rxTail_.store((tail + 1) % RX_BUFFER_SIZE, std::memory_order_release);
272 }
273 rxBuffer_[head] = data[i];
274 head = nextHead;
275 }
276 rxHead_.store(head, std::memory_order_release);
277}
278
284 if (connected) {
285 LOG_I(TAG, "Device connected");
286 if (onConnect_) onConnect_();
287 } else {
288 LOG_I(TAG, "Device disconnected");
289 rxHead_.store(0, std::memory_order_relaxed);
290 rxTail_.store(0, std::memory_order_relaxed);
291 if (onDisconnect_) onDisconnect_();
292 }
293}
294
295} // namespace cdc::mod_ble_serial
static const char * TAG
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
static constexpr ListenerToken INVALID_LISTENER
void onConnectionChange(bool connected)
Handles BLE connection state changes.
void deinit()
Deinitializes BLE UART service runtime state.
int getchar()
Reads one byte from RX ring buffer.
size_t available() const
Returns number of buffered RX bytes.
size_t read(uint8_t *buf, size_t maxLen)
Reads up to maxLen bytes from RX ring buffer.
bool isConnected() const
Returns whether BLE link is currently connected.
void onRxData(const uint8_t *data, size_t len)
Appends received BLE UART data into RX ring buffer.
bool txReady() const
Returns whether TX path is currently ready.
size_t send(const uint8_t *data, size_t len)
Sends binary payload to connected BLE peer via notifications.
bool init()
Initializes Nordic UART Service over BLE GATT.
static BleUartService & instance()
Returns singleton BLE UART service instance.
constexpr uint8_t WRITE
constexpr uint8_t READ
constexpr uint8_t NOTIFY
constexpr uint8_t WRITE_NO_RSP
constexpr uint8_t WRITE
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
static const uint8_t NUS_TX_UUID[16]
TX characteristic UUID (badge notifies phone).
static const uint8_t NUS_RX_UUID[16]
RX characteristic UUID (phone writes to badge).
static const uint8_t NUS_SVC_UUID[16]
NUS UUIDs in little-endian byte order for BleUuid::from128.
static BleUuid from128(const uint8_t v[16])
GattWriteCallback onWrite
GattCharacteristic * characteristics
static BleUuid from128(const uint8_t v[16])