Dawn Framework 1.0
Universal data acquisition framework for embedded systems
prph.cxx
1// dawn/src/proto/nimble/prph.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/nimble/prph.hxx"
7
8#include <algorithm>
9#include <climits>
10#include <new>
11
12#include "dawn/proto/nimble/prph_aios.hxx"
13#include "dawn/proto/nimble/prph_bas.hxx"
14#include "dawn/proto/nimble/prph_custom.hxx"
15#include "dawn/proto/nimble/prph_dis.hxx"
16#include "dawn/proto/nimble/prph_ess.hxx"
17#include "dawn/proto/nimble/prph_imds.hxx"
18#include "dawn/proto/nimble/prph_ots.hxx"
19#include "host/ble_hs.h"
20#include "host/util/util.h"
21#include "nimble/nimble_npl.h"
22#include "nimble/nimble_port.h"
23#include "services/gap/ble_svc_gap.h"
24#include "services/gatt/ble_svc_gatt.h"
25
26using namespace dawn;
27
28#ifdef CONFIG_DAWN_PROTO_NIMBLE_TPS
29extern "C" void ble_svc_tps_init();
30#endif
31
32int CProtoNimblePrph::configureDesc(const CDescObject &desc)
33{
35 size_t offset = 0;
36
37 for (size_t i = 0; i < desc.getSize(); i++)
38 {
39 item = desc.objectCfgItemNext(offset);
40
42 {
43 DAWNERR("Unsupported Nimble config 0x%08" PRIx32 "\n", item->cfgid.v);
44 return -EINVAL;
45 }
46
47 switch (item->cfgid.s.id)
48 {
50 {
51 const char *name = reinterpret_cast<const char *>(item->data);
52
53 CProtoNimbleAdv::setGapName(name, item->cfgid.s.size * 4);
54 break;
55 }
56
57#ifdef CONFIG_DAWN_PROTO_NIMBLE_DIS
59 {
61 new CProtoNimblePrphDis(item, static_cast<IProtoNimblePrphCb *>(this));
62 vservices.push_back(tmp);
63 break;
64 }
65#endif
66
67#ifdef CONFIG_DAWN_PROTO_NIMBLE_BAS
69 {
71 new CProtoNimblePrphBas(item, static_cast<IProtoNimblePrphCb *>(this));
72 vservices.push_back(tmp);
73 break;
74 }
75#endif
76
77#ifdef CONFIG_DAWN_PROTO_NIMBLE_AIOS
79 {
81 new CProtoNimblePrphAios(item, static_cast<IProtoNimblePrphCb *>(this));
82 vservices.push_back(tmp);
83 break;
84 }
85#endif
86
87#ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS
89 {
91 new CProtoNimblePrphEss(item, static_cast<IProtoNimblePrphCb *>(this));
92 vservices.push_back(tmp);
93 break;
94 }
95#endif
96
97#ifdef CONFIG_DAWN_PROTO_NIMBLE_IMDS
99 {
101 new CProtoNimblePrphImds(item, static_cast<IProtoNimblePrphCb *>(this));
102 vservices.push_back(tmp);
103 break;
104 }
105#endif
106
107#ifdef CONFIG_DAWN_PROTO_NIMBLE_OTS
109 {
111 new CProtoNimblePrphOts(item, static_cast<IProtoNimblePrphCb *>(this));
112 vservices.push_back(tmp);
113 break;
114 }
115#endif
116
118 {
120 new CProtoNimblePrphCustom(item, static_cast<IProtoNimblePrphCb *>(this));
121 vservices.push_back(tmp);
122 break;
123 }
124
125 default:
126 {
127 DAWNERR("Unsupported Nimble config 0x%08" PRIx32 "\n", item->cfgid.v);
128 return -EINVAL;
129 }
130 }
131 }
132
133 return OK;
134}
135
136void CProtoNimblePrph::servicesDefault()
137{
138 // Service GAP - mandatory
139
140 ble_svc_gap_init();
141
142 // Service GATT - mandatory
143
144 ble_svc_gatt_init();
145
146#ifdef CONFIG_DAWN_PROTO_NIMBLE_TPS
147 // Tx Power Service
148
149 ble_svc_tps_init();
150#endif
151}
152
153int CProtoNimblePrph::servicesCount()
154{
155 // Count services to handle by us
156
157 size_t count = std::count_if(
158 vSvcDefs.begin(), vSvcDefs.end(), [](const ble_gatt_svc_def *svc) { return svc != nullptr; });
159 if (count > UCHAR_MAX - 1)
160 {
161 return -EOVERFLOW;
162 }
163
164 // One more service for the end of array
165
166 return static_cast<int>(count + 1);
167}
168
169int CProtoNimblePrph::servicesInit()
170{
171 int ret;
172
173 ret = ble_gatts_count_cfg(svcDefs);
174 if (ret != 0)
175 {
176 DAWNERR("ble_gatts_count_cfg failed: %d\n", ret);
177 return -EIO;
178 }
179
180 ret = ble_gatts_add_svcs(svcDefs);
181 if (ret != 0)
182 {
183 DAWNERR("ble_gatts_add_svcs failed: %d\n", ret);
184 return -EIO;
185 }
186
187 return OK;
188}
189
190int CProtoNimblePrph::servicesCreate()
191{
192 uint8_t i;
193 int ret;
194 int svcCount;
195
196 if (svcDefs != nullptr)
197 {
198 return OK;
199 }
200
201 // Get number of services that we have to manage
202
203 svcCount = servicesCount();
204 if (svcCount < 0)
205 {
206 DAWNERR("servicesCount failed: %d\n", svcCount);
207 return -EIO;
208 }
209
210 noAllocServices = static_cast<uint8_t>(svcCount);
211
212 // Allocate memory for services data
213
214 svcDefs = new (std::nothrow) ble_gatt_svc_def[noAllocServices]();
215 if (svcDefs == nullptr)
216 {
217 DAWNERR("Failed to allocate service definitions\n");
218 return -ENOMEM;
219 }
220
221 // Configure services
222
223 i = 0;
224 for (const struct ble_gatt_svc_def *svc : vSvcDefs)
225 {
226 if (svc != nullptr)
227 {
228 std::memcpy(&svcDefs[i], svc, sizeof(struct ble_gatt_svc_def));
229 i++;
230 }
231 }
232
233 // Initialize services
234
235 ret = servicesInit();
236 if (ret != OK)
237 {
238 DAWNERR("servicesInit failed: %d\n", ret);
239 delete[] svcDefs;
240 svcDefs = nullptr;
241 noAllocServices = 0;
242 return ret;
243 }
244
245 return OK;
246}
247
248void CProtoNimblePrph::bleSyncCb()
249{
250 ble_addr_t addr;
251 int ret;
252
253 // Generate new non-resolvable private address
254
255 ret = ble_hs_id_gen_rnd(1, &addr);
256 if (ret != 0)
257 {
258 DAWNERR("ble_hs_id_gen_rnd failed: %d\n", ret);
259 return;
260 }
261
262 // Set generated address
263
264 ret = ble_hs_id_set_rnd(addr.val);
265 if (ret != 0)
266 {
267 DAWNERR("ble_hs_id_set_rnd failed: %d\n", ret);
268 return;
269 }
270
271 ret = ble_hs_util_ensure_addr(0);
272 if (ret != 0)
273 {
274 DAWNERR("ble_hs_util_ensure_addr failed: %d\n", ret);
275 return;
276 }
277
278 ret = ble_hs_id_infer_auto(0, &CProtoNimbleAdv::ownAddrType);
279 if (ret != 0)
280 {
281 DAWNERR("ble_hs_id_infer_auto failed: %d\n", ret);
282 return;
283 }
284
285 CProtoNimbleAdv::startAdvertise();
286}
287
288CProtoNimblePrph::~CProtoNimblePrph()
289{
290 deinit();
291
292 // Delete all services
293
294 for (auto s : vservices)
295 {
296 delete s;
297 }
298
299 vservices.clear();
300 vSvcDefs.clear();
301 vstart.clear();
302}
303
305{
306 int ret;
307
308 // Configure object
309
310 ret = configureDesc(getDesc());
311 if (ret != OK)
312 {
313 DAWNERR("Nimble configure failed: %d\n", ret);
314 return ret;
315 }
316
317 // Initialize port
318
319 nimble_port_init();
320
321 // Initialize default services
322
323 servicesDefault();
324
325 // Init all services
326
327 for (auto s : vservices)
328 {
329 ret = s->init();
330 if (ret < 0)
331 {
332 DAWNERR("failed to init service %p\n", s);
333 return ret;
334 }
335 }
336
337 return OK;
338}
339
341{
342 // Deinit all services
343
344 for (auto s : vservices)
345 {
346 int ret = s->deinit();
347 if (ret < 0)
348 {
349 DAWNERR("failed to deinit service %p\n", s);
350 }
351 }
352
353 if (svcDefs != nullptr)
354 {
355 delete[] svcDefs;
356 svcDefs = nullptr;
357 noAllocServices = 0;
358 }
359
360 return OK;
361}
362
364{
365 // Start all services
366
367 for (auto s : vservices)
368 {
369 int ret = s->start();
370 if (ret < 0)
371 {
372 DAWNERR("failed to start service %p\n", s);
373 return ret;
374 }
375 }
376
377 return OK;
378}
379
381{
382 // Start all services
383
384 for (auto s : vservices)
385 {
386 int ret = s->stop();
387 if (ret < 0)
388 {
389 DAWNERR("failed to stop service %p\n", s);
390 }
391 }
392
393 return OK;
394}
395
397{
398 return hci.isRunning() || host.isRunning();
399}
400
401int CProtoNimblePrph::serviceRegister(struct ble_gatt_svc_def *svc)
402{
403 // Svc can be nullptr if its natively supported by NimBLE.
404 // For other services it must be not nullptr and filled.
405
406 if (vSvcDefs.size() >= static_cast<size_t>(INT_MAX))
407 {
408 return -EOVERFLOW;
409 }
410
411 // Allocate service
412
413 vSvcDefs.push_back(svc);
414 vstart.push_back(false);
415
416 // Return id for allocated service
417
418 return static_cast<int>(vSvcDefs.size() - 1);
419}
420
422{
423 int ret;
424
425 if (id < 0 || (size_t)id >= vstart.size())
426 {
427 return -EINVAL;
428 }
429
430 // Mark service as started
431
432 vstart.at(id) = true;
433
434 // Check if all are enabled
435
436 if (std::any_of(vstart.begin(), vstart.end(), [](bool en) { return !en; }))
437 {
438 return OK;
439 }
440
441 // If all services all started - start nimble
442
443 // Allocated objects are mapped so we can create services now
444
445 ret = servicesCreate();
446 if (ret != OK)
447 {
448 DAWNERR("servicesCreate failed: %d\n", ret);
449 return ret;
450 }
451
452 // Start threads
453
454 hci.start();
455
456 ble_hs_cfg.sync_cb = CProtoNimblePrph::bleSyncCb;
457 ble_svc_gap_device_name_set(CProtoNimbleAdv::gapName);
458
459 host.start();
460
461 return OK;
462};
463
465{
466 if (id < 0 || (size_t)id >= vstart.size())
467 {
468 return -EINVAL;
469 }
470
471 // Mark service as stopped
472
473 vstart[id] = false;
474
475 // Check if all are disabled
476
477 if (std::any_of(vstart.begin(), vstart.end(), [](bool en) { return en; }))
478 {
479 return OK;
480 }
481
482 // Stop nimble if all services are stopped
483
484 hci.stop();
485 host.stop();
486
487 return OK;
488};
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemNext(size_t &offset) const
Get config item at current offset and advance past it.
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
@ PROTO_CLASS_NIMBLE_PRPH
BLE Peripheral using Apache NimBLE stack.
Definition common.hxx:35
static char gapName[GAPNAME_MAX+1]
GAP device name visible during BLE discovery.
Definition adv.hxx:28
static uint8_t ownAddrType
BLE device address type (random/static/public).
Definition adv.hxx:27
Automation I/O Service (AIOS) for BLE Peripheral.
Definition prph_aios.hxx:27
Battery Service (BAS) for BLE Peripheral.
Definition prph_bas.hxx:25
Custom Service defined by user.
Device Information Service (DIS) for BLE Peripheral.
Definition prph_dis.hxx:22
Environmental Sensing Service (ESS) for BLE Peripheral.
Definition prph_ess.hxx:28
Industrial Measurement Device Service (IMDS) for BLE Peripheral.
Definition prph_imds.hxx:28
Object Transfer Service (OTS) for BLE Peripheral.
Definition prph_ots.hxx:42
int stopService(int id)
Stop a specific service.
Definition prph.cxx:464
int doStart()
Start implementation hook.
Definition prph.cxx:363
@ PROTO_NIMBLE_CFG_IOBIND_CUSTOM
Generic custom service binding.
Definition prph.hxx:39
@ PROTO_NIMBLE_CFG_IOBIND_OTS
Object Transfer Service binding.
Definition prph.hxx:38
@ PROTO_NIMBLE_CFG_IOBIND_ESS
Environmental Sensing Service binding.
Definition prph.hxx:36
@ PROTO_NIMBLE_CFG_IOBIND_AIOS
Automation I/O Service binding.
Definition prph.hxx:35
@ PROTO_NIMBLE_CFG_IOBIND_BAS
Battery Service binding.
Definition prph.hxx:34
@ PROTO_NIMBLE_CFG_IOBIND_DIS
Device Information Service binding.
Definition prph.hxx:33
@ PROTO_NIMBLE_CFG_GAPNAME
GAP device name configuration.
Definition prph.hxx:32
@ PROTO_NIMBLE_CFG_IOBIND_IMDS
Industrial Measurement Device Service binding.
Definition prph.hxx:37
int startService(int id)
Start a specific service.
Definition prph.cxx:421
int configure()
Configure object from descriptor data.
Definition prph.cxx:304
int serviceRegister(struct ble_gatt_svc_def *svc)
Register a GATT service with the peripheral.
Definition prph.cxx:401
int deinit()
De-initialize object.
Definition prph.cxx:340
bool hasThread() const
Check if a background thread is active.
Definition prph.cxx:396
int doStop()
Stop implementation hook.
Definition prph.cxx:380
Interface for BLE peripheral services with GATT characteristics.
Definition iprph.hxx:24
Base interface for GATT services exposed by BLE peripheral.
Definition iprph.hxx:467
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.