Dawn Framework 1.0
Universal data acquisition framework for embedded systems
prph_imds.cxx
1// dawn/src/proto/nimble/prph_imds.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/nimble/prph_imds.hxx"
7
8#include <cstdlib>
9#include <new>
10
11#include "dawn/io/common.hxx"
12#include "dawn/io/sdata.hxx"
13#include "dawn/proto/nimble/gatt_runtime.hxx"
14#include "host/ble_hs_mbuf.h"
15#include "os/os_mbuf.h"
16
17using namespace dawn;
18
19#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
20static ble_uuid_any_t *imdsMakeUuid16(uint16_t value)
21{
22 ble_uuid_any_t *uuid = new (std::nothrow) ble_uuid_any_t{};
23 if (uuid == nullptr)
24 {
25 return nullptr;
26 }
27
28 uuid->u.type = BLE_UUID_TYPE_16;
29 uuid->u16.value = value;
30 return uuid;
31}
32
33static void imdsFreeUuid(const ble_uuid_t *uuid)
34{
35 if (uuid != nullptr)
36 {
37 delete reinterpret_cast<const ble_uuid_any_t *>(uuid);
38 }
39}
40#endif
41
42#ifdef CONFIG_DAWN_IO_NOTIFY
43int CProtoNimblePrphImds::notifierCb(void *priv, io_ddata_t *data)
44{
45 SPrphNotiferCb *ncb = static_cast<SPrphNotiferCb *>(priv);
46
47 if (ncb == nullptr)
48 {
49 DAWNERR("NULL ncb pointer in notifier\n");
50 return -EINVAL;
51 }
52
53 CIOCommon *io = reinterpret_cast<CIOCommon *>(ncb->io);
54
55 if (io == nullptr)
56 {
57 DAWNERR("NULL io pointer in notifier\n");
58 return -EINVAL;
59 }
60
61 // Copy data to local storage and check if value changed
62
63 std::memcpy(ncb->data->getDataPtr(), data->getDataPtr(), ncb->data->getDataSize());
64
65 // Notify nimble
66
67 ble_gatts_chr_updated(ncb->handle);
68
69 return OK;
70}
71#endif
72
73#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
74int CProtoNimblePrphImds::descriptorCb(uint16_t conn_handle,
75 uint16_t attr_handle,
76 struct ble_gatt_access_ctxt *ctxt,
77 void *arg)
78{
79 SImdsDscCb *dcb = static_cast<SImdsDscCb *>(arg);
80 int ret;
81
82 UNUSED(conn_handle);
83 UNUSED(attr_handle);
84
85 if (dcb == nullptr || ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC)
86 {
87 return BLE_ATT_ERR_UNLIKELY;
88 }
89
90 ret = os_mbuf_append(ctxt->om, dcb->data, dcb->len);
91 if (ret < 0)
92 {
93 DAWNERR("IMDS descriptor append failed %d\n", ret);
94 return BLE_ATT_ERR_UNLIKELY;
95 }
96
97 return 0;
98}
99#endif
100
101// NOTE: For digial IO - 1B data are supported For analog IO - 4B data are
102// supported
103template<typename T, size_t WriteBytes>
104int CProtoNimblePrphImds::callback(uint16_t conn_handle,
105 uint16_t attr_handle,
106 struct ble_gatt_access_ctxt *ctxt,
107 void *arg)
108{
109 SPrphNotiferCb *ncb = static_cast<SPrphNotiferCb *>(arg);
110 CIOCommon *io = reinterpret_cast<CIOCommon *>(ncb->io);
111 uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
112 int ret;
113
114 switch (ctxt->op)
115 {
116 case BLE_GATT_ACCESS_OP_READ_CHR:
117 {
118 uint32_t retval;
119
120#ifdef CONFIG_DAWN_IO_NOTIFY
121 if (!io->isNotify())
122#endif
123 {
124 // Get data
125
126 ret = io->getData(*ncb->data, 1);
127 if (ret < 0)
128 {
129 return BLE_ATT_ERR_UNLIKELY;
130 }
131 }
132
133 // Convert units if this is float type
134
135 if (io->getDtype() == SObjectId::DTYPE_FLOAT)
136 {
137 float tmp;
138
139 // Convert unit
140
141 tmp = ncb->data->get<float>(0) * IProtoNimblePrphCb::charScaleGet(uuid16);
142
143 // Convert data to int16_t
144
145 retval = (T)tmp;
146 }
147 else
148 {
149 DAWNERR("IMDS data type not supported yet\n");
150 return BLE_ATT_ERR_UNLIKELY;
151 }
152
153 // Write data
154
155 ret = os_mbuf_append(ctxt->om, &retval, WriteBytes);
156 if (ret < 0)
157 {
158 DAWNERR("os_mbuf_append failed %d\n", ret);
159 }
160
161 return 0;
162 }
163
164 default:
165 {
166 DAWNERR("IMDS GATT operation not supported\n");
167 return BLE_ATT_ERR_UNLIKELY;
168 }
169 }
170}
171
172int CProtoNimblePrphImds::allocIMDS()
173{
174 // Configure service
175
176 svc.type = BLE_GATT_SVC_TYPE_PRIMARY;
177 svc.uuid = reinterpret_cast<const ble_uuid_t *>(IProtoNimblePrphCb::UUID_IMDS);
178 svc.includes = nullptr;
179
180 // One more characteristic for end of array
181
182 noChar = cb->getObjectsLen() + 1;
183
184 // Allocate memory for characterisitcs
185
186 svc.characteristics = new (std::nothrow) ble_gatt_chr_def[noChar]();
187 if (svc.characteristics == nullptr)
188 {
189 DAWNERR("Failed to allocate characteristics\n");
190 return -ENOMEM;
191 }
192
193 return OK;
194}
195
196int CProtoNimblePrphImds::createIMDS()
197{
198 const uint32_t *char_uuid = nullptr;
199 int j = 0;
200 int ret;
201
202 if (svc.characteristics == nullptr)
203 {
204 DAWNERR("Failed to allocate characteristics\n");
205 return -ENOMEM;
206 }
207
208 // Configure service
209
210 svc.type = BLE_GATT_SVC_TYPE_PRIMARY;
211 svc.uuid = reinterpret_cast<const ble_uuid_t *>(IProtoNimblePrphCb::UUID_IMDS);
212 svc.includes = nullptr;
213
214 for (auto const objid : vio)
215 {
216 CIOCommon *io = cb->getObject(objid);
217
218 // NOTE: NimBLE C API declares svc.characteristics as
219 // const ble_gatt_chr_def*, but Dawn allocates it dynamically
220 // and needs write access to populate fields at runtime.
221
222 struct ble_gatt_chr_def *chr = const_cast<struct ble_gatt_chr_def *>(&svc.characteristics[j]);
223
224 if (io == nullptr)
225 {
226 DAWNERR("IMDS IO not found\n");
227 return -EIO;
228 }
229
230 // Seekable IOs not supported by IMDS
231
232 if (io->isSeekable())
233 {
234 DAWNERR("seekable IO not supported by IMDS"
235 " (objid=0x%08" PRIx32 ")\n",
236 io->getIdV());
237 return -ENOTSUP;
238 }
239
240 SPrphNotiferCb *ncb = nimbleGattNotifierCreate(io);
241 if (ncb == nullptr)
242 {
243 DAWNERR("IMDS notifier allocation failed\n");
244 return -ENOMEM;
245 }
246
247 // Fill characteristic, access_cb is fill later
248
249 nimbleGattChrInit(*chr, ncb);
250 char_uuid = nullptr;
251
252 // Check characteristic properties
253
254#ifdef CONFIG_DAWN_IO_NOTIFY
255 ret = nimbleGattChrNotifySet(*chr, *io, *ncb, notifierCb, objid);
256 if (ret < 0)
257 {
258 DAWNERR("IMDS notifier setup failed\n");
259 }
260#endif
261
262 nimbleGattChrAccessSet(*chr, *io);
263
264 // Get IO class
265
266 switch (ioTypeMap[objid])
267 {
269 {
270 chr->access_cb = CProtoNimblePrphImds::callback<int16_t>;
272 break;
273 }
274
276 {
277 chr->access_cb = CProtoNimblePrphImds::callback<int16_t>;
279 break;
280 }
281
283 {
284 chr->access_cb = CProtoNimblePrphImds::callback<uint32_t>;
286 break;
287 }
288
290 {
291 chr->access_cb = CProtoNimblePrphImds::callback<uint8_t>;
293 break;
294 }
295
297 {
298 // Non-standard UUID: 0x272A is not assigned by the BT SIG.
299
300 chr->access_cb = CProtoNimblePrphImds::callback<int16_t>;
302 break;
303 }
304
306 {
307 // Illuminance is a 24-bit unsigned (BT SIG 0x2AFB)
308
309 chr->access_cb = CProtoNimblePrphImds::callback<uint32_t, 3>;
311 break;
312 }
313
315 {
316 // CO2 Concentration is uint16 in ppm (BT SIG 0x2B8C)
317
318 chr->access_cb = CProtoNimblePrphImds::callback<uint16_t>;
320 break;
321 }
322
324 {
325 // VOC Concentration is uint16 in ppb (BT SIG 0x2BE7)
326
327 chr->access_cb = CProtoNimblePrphImds::callback<uint16_t>;
329 break;
330 }
331
332 default:
333 {
334 DAWNERR("unknown IMDS type %d\n", ioTypeMap[objid]);
335 nimbleGattChrNotifierFree(*chr);
336 return -EINVAL;
337 }
338 }
339
340 // Assignsome dummy value
341
342 if (char_uuid == nullptr)
343 {
344 DAWNERR("No UUID for IMDS IO\n");
345 nimbleGattChrNotifierFree(*chr);
346 return -EINVAL;
347 }
348
349 ret = nimbleGattChrUuid128Set(*chr, char_uuid);
350 if (ret != OK)
351 {
352 DAWNERR("IMDS UUID allocation failed\n");
353 nimbleGattChrNotifierFree(*chr);
354 return ret;
355 }
356
357 ret = configureDescriptors(chr, objid);
358 if (ret != OK)
359 {
360 nimbleGattChrUuidFree(*chr);
361 nimbleGattChrNotifierFree(*chr);
362 return ret;
363 }
364
365 // Next service
366
367 j++;
368 }
369
370 return OK;
371}
372
373void CProtoNimblePrphImds::deleteIMDS()
374{
375 // NOTE: svc.characteristics fields are const-qualified per NimBLE API.
376 // Dawn allocated this memory and is the rightful owner, so the cast is safe.
377
378 if (svc.characteristics)
379 {
380 for (size_t i = 0; i < noChar - 1; i++)
381 {
382 if (svc.characteristics[i].uuid)
383 {
384 nimbleGattChrUuidFree(const_cast<struct ble_gatt_chr_def &>(svc.characteristics[i]));
385 }
386
387#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
388 if (svc.characteristics[i].descriptors)
389 {
390 struct ble_gatt_dsc_def *dsc =
391 const_cast<struct ble_gatt_dsc_def *>(svc.characteristics[i].descriptors);
392
393 for (size_t j = 0; dsc[j].uuid != nullptr; j++)
394 {
395 SImdsDscCb *dcb = static_cast<SImdsDscCb *>(dsc[j].arg);
396
397 imdsFreeUuid(dsc[j].uuid);
398 delete dcb;
399 }
400
401 delete[] dsc;
402 }
403#endif
404
405 nimbleGattChrNotifierFree(const_cast<struct ble_gatt_chr_def &>(svc.characteristics[i]));
406 }
407
408 delete[] const_cast<ble_gatt_chr_def *>(svc.characteristics);
409
410 svc.characteristics = nullptr;
411 }
412 created = false;
413}
414
415int CProtoNimblePrphImds::configureDescriptors(struct ble_gatt_chr_def *chr,
417{
418#ifndef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
419 UNUSED(chr);
420 UNUSED(objid);
421 return OK;
422#else
423 const auto metaIt = ioMetaMap.find(objid);
424 SImdsMeta *meta;
425 struct ble_gatt_dsc_def *dsc;
426 size_t count = 0;
427 size_t idx = 0;
428
429 if (metaIt == ioMetaMap.end())
430 {
431 return OK;
432 }
433
434 meta = const_cast<SImdsMeta *>(&metaIt->second);
435
436# ifdef CONFIG_DAWN_PROTO_NIMBLE_IMDS_DESC_USER_DESCRIPTION
437 if (meta->desc & IMDS_DESC_USER_DESCRIPTION)
438 {
439 count++;
440 }
441# endif
442
443 if (count == 0)
444 {
445 return OK;
446 }
447
448 dsc = new (std::nothrow) ble_gatt_dsc_def[count + 1]();
449 if (dsc == nullptr)
450 {
451 return -ENOMEM;
452 }
453
454# ifdef CONFIG_DAWN_PROTO_NIMBLE_IMDS_DESC_USER_DESCRIPTION
455 if (meta->desc & IMDS_DESC_USER_DESCRIPTION)
456 {
457 SImdsDscCb *dcb = new (std::nothrow) SImdsDscCb{};
458 ble_uuid_any_t *uuid = imdsMakeUuid16(UUID16_CHR_USER_DESCRIPTION);
459 if (dcb == nullptr || uuid == nullptr)
460 {
461 delete dcb;
462 imdsFreeUuid(uuid ? &uuid->u : nullptr);
463 delete[] dsc;
464 return -ENOMEM;
465 }
466
467 dcb->kind = IMDS_DESC_USER_DESCRIPTION;
468 dcb->len = 0;
469 while (dcb->len < IMDS_USER_DESCRIPTION_MAX && meta->userDescription[dcb->len] != '\0')
470 {
471 dcb->data[dcb->len] = static_cast<uint8_t>(meta->userDescription[dcb->len]);
472 dcb->len++;
473 }
474
475 dsc[idx].uuid = &uuid->u;
476 dsc[idx].att_flags = BLE_ATT_F_READ;
477 dsc[idx].access_cb = descriptorCb;
478 dsc[idx].arg = dcb;
479 idx++;
480 }
481# endif
482
483 chr->descriptors = dsc;
484 return OK;
485#endif
486}
487
488void CProtoNimblePrphImds::allocObject(const SProtoNimblePrphIOBindImdsObjid &obj,
489 const uint32_t *ext)
490{
491 uint8_t type = cfgIdIOBindImdsCfgObjTypeGet(obj.cfg);
492#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
493 SImdsMeta meta{};
494#endif
495
496 DAWNINFO("IMDS allocate type=%d object 0x%" PRIx32 "\n", type, obj.objid.v);
497
498 // Store type for later
499
500 ioTypeMap.insert_or_assign(obj.objid.v, type);
501
502#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
503 for (size_t i = 0; i < obj.extCount; i++)
504 {
505 uint8_t kind = cfgIdIOBindImdsExtKindGet(ext[0]);
506 uint8_t size = cfgIdIOBindImdsExtSizeGet(ext[0]);
507 const uint32_t *data = &ext[1];
508
509 switch (kind)
510 {
512 {
513 size_t bytes = static_cast<size_t>(size) * sizeof(uint32_t);
514 bytes = bytes < IMDS_USER_DESCRIPTION_MAX ? bytes : IMDS_USER_DESCRIPTION_MAX;
515
516# ifdef CONFIG_DAWN_PROTO_NIMBLE_IMDS_DESC_USER_DESCRIPTION
517 meta.desc |= IMDS_DESC_USER_DESCRIPTION;
518 std::memcpy(meta.userDescription, data, bytes);
519# endif
520 break;
521 }
522
523 default:
524 {
525 DAWNERR("unknown IMDS extension kind %u\n", kind);
526 break;
527 }
528 }
529
530 ext += 1 + size;
531 }
532
533 if (meta.desc != 0)
534 {
535 ioMetaMap.insert_or_assign(obj.objid.v, meta);
536 }
537#endif
538
539 // Allocate object in map
540
541 cb->regObject(obj.objid.v);
542
543 vio.push_back(obj.objid.v);
544}
545
546// Assumption: Configuration item passed by caller is a valid type supported by
547// this class.
548void CProtoNimblePrphImds::configureDesc(const SObjectCfg::SObjectCfgItem *item)
549{
550 // Iterrate over objectid list
551
552 for (size_t k = 0; k < item->cfgid.s.size;)
553 {
554 const SProtoNimblePrphIOBindImds *tmp =
555 reinterpret_cast<const SProtoNimblePrphIOBindImds *>(&item->data[k]);
556
557 uint8_t size = cfgIdIOBindImdsCfg0SizeGet(tmp->cfg0);
558
559 size_t pos = k + sizeof(SProtoNimblePrphIOBindImds) / sizeof(uint32_t);
560
561 for (size_t i = 0; i < size; i++)
562 {
563 const SProtoNimblePrphIOBindImdsObjid *obj =
564 reinterpret_cast<const SProtoNimblePrphIOBindImdsObjid *>(&item->data[pos]);
565 const uint32_t *ext =
566 &item->data[pos + sizeof(SProtoNimblePrphIOBindImdsObjid) / sizeof(uint32_t)];
567
568 // Allocate object
569
570 allocObject(*obj, ext);
571
572 pos += sizeof(SProtoNimblePrphIOBindImdsObjid) / sizeof(uint32_t);
573 for (size_t j = 0; j < obj->extCount; j++)
574 {
575 pos += 1 + cfgIdIOBindImdsExtSizeGet(item->data[pos]);
576 }
577 }
578
579 k = pos;
580 }
581}
582
583CProtoNimblePrphImds::CProtoNimblePrphImds(const SObjectCfg::SObjectCfgItem *desc_,
585 : IProtoNimblePrphService(desc_, cb_)
586 , created(false)
587{
588 // Service ID not assigned yet
589
590 id = -1;
591 noChar = 0;
592}
593
594CProtoNimblePrphImds::~CProtoNimblePrphImds()
595{
596 deinit();
597}
598
600{
601 int ret;
602
603 // Configure object
604
605 configureDesc(desc);
606
607 // Allocate service
608
609 ret = allocIMDS();
610 if (ret != OK)
611 {
612 return ret;
613 }
614
615 // Register servie in peripheral handler
616
617 id = cb->serviceRegister(&svc);
618 if (id < 0)
619 {
620 DAWNERR("IMDS service registration failed\n");
621 return -EIO;
622 }
623
624 return OK;
625}
626
628{
629 deleteIMDS();
630 return OK;
631}
632
634{
635 int ret;
636
637 if (created)
638 {
639 return cb->startService(id);
640 }
641
642 // Create service
643
644 ret = createIMDS();
645 if (ret != OK)
646 {
647 deleteIMDS();
648 allocIMDS();
649 return ret;
650 }
651
652 created = true;
653
654 // Signal start to peripheral handler
655
656 return cb->startService(id);
657}
658
660{
661 // Signal stop to peripheral handler
662
663 return cb->stopService(id);
664}
Base class for all I/O objects.
Definition common.hxx:27
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
Definition common.hxx:375
virtual bool isNotify() const =0
Check if IO supports notifications.
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
Definition common.hxx:524
SObjectId::ObjectId getIdV() const
Get object identifier as raw 32-bit value.
Definition object.cxx:155
uint8_t getDtype() const
Get data type field.
Definition object.cxx:175
int deinit()
Deinitialize service.
@ IMDS_EXT_USER_DESCRIPTION
Characteristic User Description descriptor.
Definition prph_imds.hxx:45
int init()
Initialize service.
int stop()
Stop service.
int start()
Start service.
@ PRPH_IMDS_TYPE_LIGHT
Industrial illuminance / light sensor.
Definition prph_imds.hxx:56
@ PRPH_IMDS_TYPE_TVOC
Industrial VOC concentration sensor.
Definition prph_imds.hxx:58
@ PRPH_IMDS_TYPE_GAS
Industrial gas resistance sensor (non-standard UUID).
Definition prph_imds.hxx:54
@ PRPH_IMDS_TYPE_TEMP
Industrial temperature sensor.
Definition prph_imds.hxx:50
@ PRPH_IMDS_TYPE_UVIDX
Industrial UV index sensor.
Definition prph_imds.hxx:53
@ PRPH_IMDS_TYPE_HUM
Industrial humidity sensor.
Definition prph_imds.hxx:51
@ PRPH_IMDS_TYPE_CO2
Industrial CO2 concentration sensor.
Definition prph_imds.hxx:57
@ PRPH_IMDS_TYPE_PRESS
Industrial pressure sensor.
Definition prph_imds.hxx:52
Interface for BLE peripheral services with GATT characteristics.
Definition iprph.hxx:24
virtual int startService(int id)=0
Start a specific service.
static uint32_t UUID_UVIDX[4]
UV Index Characteristic UUID (0x2A76).
Definition iprph.hxx:197
virtual size_t getObjectsLen()=0
Get count of registered I/O objects.
static float charScaleGet(uint16_t u)
Get scaling factor for a characteristic UUID.
Definition iprph.hxx:411
static uint32_t UUID_CO2[4]
CO2 Concentration Characteristic UUID (0x2B8C).
Definition iprph.hxx:354
virtual CIOCommon * getObject(SObjectId::ObjectId id)=0
Get protocol object by ID.
static uint32_t UUID_PRESS[4]
Pressure Characteristic UUID (0x2A6D).
Definition iprph.hxx:186
static uint32_t UUID_TEMP[4]
Temperature Characteristic UUID (0x2A6E).
Definition iprph.hxx:149
static uint32_t UUID_ILLUMINANCE[4]
Illuminance Characteristic UUID (0x2AFB).
Definition iprph.hxx:328
static uint32_t UUID_RESISTANCE[4]
Electric Resistance Characteristic UUID (0x272A).
Definition iprph.hxx:342
virtual int serviceRegister(struct ble_gatt_svc_def *svc)=0
Register a GATT service with the peripheral.
static uint32_t UUID_TVOC[4]
VOC Concentration Characteristic UUID (0x2BE7).
Definition iprph.hxx:366
virtual void regObject(SObjectId::ObjectId id)=0
Register an I/O object for this service.
static uint32_t UUID_IMDS[4]
Industrial Measurement Device Service UUID (0x185A).
Definition iprph.hxx:89
static uint32_t UUID_HUM[4]
Humidity Characteristic UUID (0x2A6F).
Definition iprph.hxx:174
virtual int stopService(int id)=0
Stop a specific service.
Base interface for GATT services exposed by BLE peripheral.
Definition iprph.hxx:495
std::vector< SObjectId::ObjectId > vio
Vector of I/O objects exposed by this service.
Definition iprph.hxx:585
const SObjectCfg::SObjectCfgItem * desc
Configuration descriptor for this service.
Definition iprph.hxx:602
IProtoNimblePrphCb * cb
Callback interface to peripheral.
Definition iprph.hxx:594
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).
@ DTYPE_FLOAT
IEEE 754 single-precision floating point (32-bit).
Definition objectid.hxx:112
uint32_t ObjectId
ObjectID type - single 32-bit value.
Definition objectid.hxx:44
Heap-allocated dynamic I/O data buffer.
Definition ddata.hxx:21
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
Definition ddata.hxx:180
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.