Dawn Framework 1.0
Universal data acquisition framework for embedded systems
prph_ess.cxx
1// dawn/src/proto/nimble/prph_ess.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/nimble/prph_ess.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
20# if defined(CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_USER_DESCRIPTION) || \
21 defined(CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_VALID_RANGE) || \
22 defined(CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_MEASUREMENT) || \
23 defined(CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_CONFIGURATION) || \
24 defined(CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_TRIGGER_SETTING)
25# define DAWN_PROTO_NIMBLE_ESS_ANY_DESCRIPTOR 1
26# endif
27
28# ifdef DAWN_PROTO_NIMBLE_ESS_ANY_DESCRIPTOR
29static ble_uuid_any_t *essMakeUuid16(uint16_t value)
30{
31 ble_uuid_any_t *uuid = new (std::nothrow) ble_uuid_any_t{};
32 if (uuid == nullptr)
33 {
34 return nullptr;
35 }
36
37 uuid->u.type = BLE_UUID_TYPE_16;
38 uuid->u16.value = value;
39 return uuid;
40}
41
42static void essFreeUuid(const ble_uuid_t *uuid)
43{
44 if (uuid != nullptr)
45 {
46 delete reinterpret_cast<const ble_uuid_any_t *>(uuid);
47 }
48}
49# endif
50
51# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_VALID_RANGE
52static uint8_t essValueBytes(uint8_t type)
53{
54 switch (type)
55 {
59 return 2;
60
62 return 4;
63
65 return 1;
66
68 return 3;
69
70 default:
71 return 0;
72 }
73}
74# endif
75
76static int essReadFieldU32(CIOCommon *io, io_ddata_t *data, uint32_t *value)
77{
78 int ret;
79
80 if (io == nullptr)
81 {
82 *value = 0;
83 return OK;
84 }
85
86 ret = io->getData(*data, 1);
87 if (ret < 0)
88 {
89 return ret;
90 }
91
92 switch (io->getDtype())
93 {
95 *value = data->get<uint8_t>(0);
96 break;
97
99 *value = data->get<uint16_t>(0);
100 break;
101
103 *value = data->get<uint32_t>(0);
104 break;
105
106 default:
107 return -ENOTSUP;
108 }
109
110 return OK;
111}
112
113static int essReadFieldRaw(CIOCommon *io, io_ddata_t *data, uint8_t *value, uint8_t *len)
114{
115 int ret;
116
117 if (io == nullptr)
118 {
119 *len = 0;
120 return OK;
121 }
122
123 if (io->getDataSize() > CProtoNimblePrphEss::ESS_RAW_DESCRIPTOR_MAX)
124 {
125 return -EOVERFLOW;
126 }
127
128 ret = io->getData(*data, io->getDataDim());
129 if (ret < 0)
130 {
131 return ret;
132 }
133
134 *len = static_cast<uint8_t>(io->getDataSize());
135 std::memcpy(value, data->getDataPtr(), *len);
136 return OK;
137}
138#endif
139
140#ifdef CONFIG_DAWN_IO_NOTIFY
141int CProtoNimblePrphEss::notifierCb(void *priv, io_ddata_t *data)
142{
143 SPrphNotiferCb *ncb = (SPrphNotiferCb *)priv;
144
145 if (ncb == nullptr)
146 {
147 DAWNERR("NULL ncb pointer in notifier\n");
148 return -EINVAL;
149 }
150
151 CIOCommon *io = (CIOCommon *)ncb->io;
152
153 if (io == nullptr)
154 {
155 DAWNERR("NULL io pointer in notifier\n");
156 return -EINVAL;
157 }
158
159 // Copy data to local storage and check if value changed
160
161 std::memcpy(ncb->data->getDataPtr(), data->getDataPtr(), ncb->data->getDataSize());
162
163 // Notify nimble
164
165 ble_gatts_chr_updated(ncb->handle);
166
167 return OK;
168}
169#endif
170
171#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
172int CProtoNimblePrphEss::descriptorCb(uint16_t conn_handle,
173 uint16_t attr_handle,
174 struct ble_gatt_access_ctxt *ctxt,
175 void *arg)
176{
177 SEssDscCb *dcb = static_cast<SEssDscCb *>(arg);
178 int ret;
179
180 (void)conn_handle;
181 (void)attr_handle;
182
183 if (dcb == nullptr || ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC)
184 {
185 return BLE_ATT_ERR_UNLIKELY;
186 }
187
188 if (dcb->kind == ESS_DESC_MEASUREMENT)
189 {
190 uint32_t values[6];
191
192 for (size_t i = 0; i < 6; i++)
193 {
194 ret = essReadFieldU32(dcb->measurement[i].io, dcb->measurement[i].data, &values[i]);
195 if (ret < 0)
196 {
197 DAWNERR("ESS measurement descriptor field read failed %d\n", ret);
198 return BLE_ATT_ERR_UNLIKELY;
199 }
200 }
201
202 dcb->data[0] = static_cast<uint8_t>(values[0] & 0xff);
203 dcb->data[1] = static_cast<uint8_t>((values[0] >> 8) & 0xff);
204 dcb->data[2] = static_cast<uint8_t>(values[1] & 0xff);
205 dcb->data[3] = static_cast<uint8_t>(values[2] & 0xff);
206 dcb->data[4] = static_cast<uint8_t>((values[2] >> 8) & 0xff);
207 dcb->data[5] = static_cast<uint8_t>((values[2] >> 16) & 0xff);
208 dcb->data[6] = static_cast<uint8_t>(values[3] & 0xff);
209 dcb->data[7] = static_cast<uint8_t>((values[3] >> 8) & 0xff);
210 dcb->data[8] = static_cast<uint8_t>((values[3] >> 16) & 0xff);
211 dcb->data[9] = static_cast<uint8_t>(values[4] & 0xff);
212 dcb->data[10] = static_cast<uint8_t>(values[5] & 0xff);
213 }
214 else if (dcb->kind == ESS_DESC_CONFIGURATION || dcb->kind == ESS_DESC_TRIGGER_SETTING)
215 {
216 ret = essReadFieldRaw(dcb->field.io, dcb->field.data, dcb->data, &dcb->len);
217 if (ret < 0)
218 {
219 DAWNERR("ESS raw descriptor field read failed %d\n", ret);
220 return BLE_ATT_ERR_UNLIKELY;
221 }
222 }
223
224 ret = os_mbuf_append(ctxt->om, dcb->data, dcb->len);
225 if (ret < 0)
226 {
227 DAWNERR("ESS descriptor append failed %d\n", ret);
228 return BLE_ATT_ERR_UNLIKELY;
229 }
230
231 return 0;
232}
233#endif
234
235// NOTE: For digial IO - 1B data are supported For analog IO - 4B data are
236// supported
237template<typename T, size_t WriteBytes>
238int CProtoNimblePrphEss::callback(uint16_t conn_handle,
239 uint16_t attr_handle,
240 struct ble_gatt_access_ctxt *ctxt,
241 void *arg)
242{
243 SPrphNotiferCb *ncb = static_cast<SPrphNotiferCb *>(arg);
244 CIOCommon *io = static_cast<CIOCommon *>(ncb->io);
245 uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
246 int ret;
247
248 switch (ctxt->op)
249 {
250 case BLE_GATT_ACCESS_OP_READ_CHR:
251 {
252 uint32_t retval;
253
254#ifdef CONFIG_DAWN_IO_NOTIFY
255 if (!io->isNotify())
256#endif
257 {
258 // Get data
259
260 ret = io->getData(*ncb->data, 1);
261 if (ret < 0)
262 {
263 return BLE_ATT_ERR_UNLIKELY;
264 }
265 }
266
267 // Convert units if this is float type
268
269 if (io->getDtype() == SObjectId::DTYPE_FLOAT)
270 {
271 float tmp;
272
273 // Convert unit
274
275 tmp = ncb->data->get<float>(0) * IProtoNimblePrphCb::charScaleGet(uuid16);
276
277 // Convert data to int16_t
278
279 retval = (T)tmp;
280 }
281 else
282 {
283 DAWNERR("ESS data type not supported yet\n");
284 return BLE_ATT_ERR_UNLIKELY;
285 }
286
287 // Write data (WriteBytes may be smaller than sizeof(T) for
288 // packed types like uint24)
289
290 ret = os_mbuf_append(ctxt->om, &retval, WriteBytes);
291 if (ret < 0)
292 {
293 DAWNERR("os_mbuf_append failed %d\n", ret);
294 }
295
296 return 0;
297 }
298
299 default:
300 {
301 DAWNERR("ESS GATT operation not supported\n");
302 return BLE_ATT_ERR_UNLIKELY;
303 }
304 }
305}
306
307int CProtoNimblePrphEss::allocESS()
308{
309 // Configure service
310
311 svc.type = BLE_GATT_SVC_TYPE_PRIMARY;
312 svc.uuid = reinterpret_cast<const ble_uuid_t *>(IProtoNimblePrphCb::UUID_ESS);
313 svc.includes = nullptr;
314
315 // One more characteristic for end of array
316
317 noChar = cb->getObjectsLen() + 1;
318
319 // Allocate memory for characterisitcs
320
321 svc.characteristics = new (std::nothrow) ble_gatt_chr_def[noChar]();
322
323 if (svc.characteristics == nullptr)
324 {
325 DAWNERR("Failed to allocate characteristics\n");
326 return -ENOMEM;
327 }
328
329 return OK;
330}
331
332int CProtoNimblePrphEss::createESS()
333{
334 const uint32_t *char_uuid = nullptr;
335 int j = 0;
336 int ret;
337
338 if (svc.characteristics == nullptr)
339 {
340 DAWNERR("Failed to allocate characteristics\n");
341 return -ENOMEM;
342 }
343
344 // Configure service
345
346 svc.type = BLE_GATT_SVC_TYPE_PRIMARY;
347 svc.uuid = reinterpret_cast<const ble_uuid_t *>(IProtoNimblePrphCb::UUID_ESS);
348 svc.includes = nullptr;
349
350 for (auto const objid : vio)
351 {
352 CIOCommon *io = cb->getObject(objid);
353
354 // NOTE: NimBLE C API declares svc.characteristics as
355 // const ble_gatt_chr_def*, but Dawn allocates it dynamically
356 // and needs write access to populate fields at runtime.
357
358 struct ble_gatt_chr_def *chr = const_cast<struct ble_gatt_chr_def *>(&svc.characteristics[j]);
359
360 if (io == nullptr)
361 {
362 DAWNERR("ESS IO not found\n");
363 return -EIO;
364 }
365
366 // Seekable IOs not supported by ESS
367
368 if (io->isSeekable())
369 {
370 DAWNERR("seekable IO not supported by ESS"
371 " (objid=0x%08" PRIx32 ")\n",
372 io->getIdV());
373 return -ENOTSUP;
374 }
375
376 SPrphNotiferCb *ncb = nimbleGattNotifierCreate(io);
377 if (ncb == nullptr)
378 {
379 DAWNERR("ESS notifier allocation failed\n");
380 return -ENOMEM;
381 }
382
383 // Fill characteristic, access_cb is fill later
384
385 nimbleGattChrInit(*chr, ncb);
386 char_uuid = nullptr;
387
388 // Check characteristic properties
389
390#ifdef CONFIG_DAWN_IO_NOTIFY
391 ret = nimbleGattChrNotifySet(*chr, *io, *ncb, notifierCb, objid);
392 if (ret < 0)
393 {
394 DAWNERR("ESS notifier setup failed\n");
395 }
396#endif
397
398 nimbleGattChrAccessSet(*chr, *io);
399
400 // Get IO class
401
402 switch (ioTypeMap[objid])
403 {
405 {
406 chr->access_cb = CProtoNimblePrphEss::callback<int16_t>;
408 break;
409 }
410
412 {
413 chr->access_cb = CProtoNimblePrphEss::callback<int16_t>;
415 break;
416 }
417
419 {
420 chr->access_cb = CProtoNimblePrphEss::callback<uint32_t>;
422 break;
423 }
424
426 {
427 chr->access_cb = CProtoNimblePrphEss::callback<uint8_t>;
429 break;
430 }
431
433 {
434 // Non-standard UUID: 0x272A is not assigned by the BT SIG.
435
436 chr->access_cb = CProtoNimblePrphEss::callback<int16_t>;
438 break;
439 }
440
442 {
443 // Illuminance is a 24-bit unsigned (BT SIG 0x2AFB)
444
445 chr->access_cb = CProtoNimblePrphEss::callback<uint32_t, 3>;
447 break;
448 }
449
450 default:
451 {
452 DAWNERR("unknown ESS type %d\n", ioTypeMap[objid]);
453 nimbleGattChrNotifierFree(*chr);
454 return -EINVAL;
455 }
456 }
457
458 // Assignsome dummy value
459
460 if (char_uuid == nullptr)
461 {
462 DAWNERR("No UUID for ESS IO\n");
463 nimbleGattChrNotifierFree(*chr);
464 return -EINVAL;
465 }
466
467 ret = nimbleGattChrUuid128Set(*chr, char_uuid);
468 if (ret != OK)
469 {
470 DAWNERR("ESS UUID allocation failed\n");
471 nimbleGattChrNotifierFree(*chr);
472 return ret;
473 }
474
475 ret = configureDescriptors(chr, objid, ioTypeMap[objid]);
476 if (ret != OK)
477 {
478 nimbleGattChrUuidFree(*chr);
479 nimbleGattChrNotifierFree(*chr);
480 return ret;
481 }
482
483 // Next service
484
485 j++;
486 }
487
488 return OK;
489}
490
491void CProtoNimblePrphEss::deleteESS()
492{
493 // NOTE: svc.characteristics fields are const-qualified per NimBLE API.
494 // Dawn allocated this memory and is the rightful owner, so the cast is safe.
495 if (svc.characteristics)
496 {
497 for (size_t i = 0; i < noChar - 1; i++)
498 {
499 if (svc.characteristics[i].uuid)
500 {
501 nimbleGattChrUuidFree(const_cast<struct ble_gatt_chr_def &>(svc.characteristics[i]));
502 }
503
504#ifdef DAWN_PROTO_NIMBLE_ESS_ANY_DESCRIPTOR
505 if (svc.characteristics[i].descriptors)
506 {
507 struct ble_gatt_dsc_def *dsc =
508 const_cast<struct ble_gatt_dsc_def *>(svc.characteristics[i].descriptors);
509
510 for (size_t j = 0; dsc[j].uuid != nullptr; j++)
511 {
512 SEssDscCb *dcb = static_cast<SEssDscCb *>(dsc[j].arg);
513
514 essFreeUuid(dsc[j].uuid);
515 if (dcb != nullptr)
516 {
517 if (dcb->field.data != nullptr)
518 {
519 delete dcb->field.data;
520 }
521 for (size_t k = 0; k < 6; k++)
522 {
523 if (dcb->measurement[k].data != nullptr)
524 {
525 delete dcb->measurement[k].data;
526 }
527 }
528 delete dcb;
529 }
530 }
531
532 delete[] dsc;
533 }
534#endif
535
536 nimbleGattChrNotifierFree(const_cast<struct ble_gatt_chr_def &>(svc.characteristics[i]));
537 }
538
539 delete[] const_cast<ble_gatt_chr_def *>(svc.characteristics);
540
541 svc.characteristics = nullptr;
542 }
543 created = false;
544}
545
546int CProtoNimblePrphEss::configureDescriptors(struct ble_gatt_chr_def *chr,
548 uint8_t type)
549{
550#ifndef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
551 UNUSED(chr);
552 UNUSED(objid);
553 UNUSED(type);
554 return OK;
555#else
556 const auto metaIt = ioMetaMap.find(objid);
557 SEssMeta *meta;
558 struct ble_gatt_dsc_def *dsc;
559 size_t count = 0;
560 size_t idx = 0;
561 UNUSED(idx);
562
563 if (metaIt == ioMetaMap.end())
564 {
565 return OK;
566 }
567
568 meta = const_cast<SEssMeta *>(&metaIt->second);
569
570# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_USER_DESCRIPTION
571 if (meta->desc & ESS_DESC_USER_DESCRIPTION)
572 {
573 count++;
574 }
575# endif
576
577# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_VALID_RANGE
578 if (meta->desc & ESS_DESC_VALID_RANGE)
579 {
580 if (essValueBytes(type) == 0)
581 {
582 DAWNERR("ESS valid range unsupported for type %u\n", type);
583 return -EINVAL;
584 }
585 count++;
586 }
587# endif
588
589# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_MEASUREMENT
590 if (meta->desc & ESS_DESC_MEASUREMENT)
591 {
592 count++;
593 }
594# endif
595
596# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_CONFIGURATION
597 if (meta->desc & ESS_DESC_CONFIGURATION)
598 {
599 count++;
600 }
601# endif
602
603# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_TRIGGER_SETTING
604 if (meta->desc & ESS_DESC_TRIGGER_SETTING)
605 {
606 count++;
607 }
608# endif
609
610 if (count == 0)
611 {
612 return OK;
613 }
614
615 dsc = new (std::nothrow) ble_gatt_dsc_def[count + 1]();
616 if (dsc == nullptr)
617 {
618 return -ENOMEM;
619 }
620
621# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_USER_DESCRIPTION
622 if (meta->desc & ESS_DESC_USER_DESCRIPTION)
623 {
624 SEssDscCb *dcb = new (std::nothrow) SEssDscCb{};
625 ble_uuid_any_t *uuid = essMakeUuid16(UUID16_CHR_USER_DESCRIPTION);
626 if (dcb == nullptr || uuid == nullptr)
627 {
628 delete dcb;
629 essFreeUuid(uuid ? &uuid->u : nullptr);
630 delete[] dsc;
631 return -ENOMEM;
632 }
633
634 dcb->kind = ESS_DESC_USER_DESCRIPTION;
635 dcb->len = 0;
636 while (dcb->len < ESS_USER_DESCRIPTION_MAX && meta->userDescription[dcb->len] != '\0')
637 {
638 dcb->data[dcb->len] = static_cast<uint8_t>(meta->userDescription[dcb->len]);
639 dcb->len++;
640 }
641
642 dsc[idx].uuid = &uuid->u;
643 dsc[idx].att_flags = BLE_ATT_F_READ;
644 dsc[idx].access_cb = descriptorCb;
645 dsc[idx].arg = dcb;
646 idx++;
647 }
648# endif
649
650# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_VALID_RANGE
651 if (meta->desc & ESS_DESC_VALID_RANGE)
652 {
653 SEssDscCb *dcb = new (std::nothrow) SEssDscCb{};
654 ble_uuid_any_t *uuid = essMakeUuid16(UUID16_VALID_RANGE);
655 uint8_t width = essValueBytes(type);
656 if (dcb == nullptr || uuid == nullptr)
657 {
658 delete dcb;
659 essFreeUuid(uuid ? &uuid->u : nullptr);
660 delete[] dsc;
661 return -ENOMEM;
662 }
663
664 dcb->kind = ESS_DESC_VALID_RANGE;
665 dcb->len = width * 2;
666 for (uint8_t i = 0; i < width; i++)
667 {
668 dcb->data[i] = static_cast<uint8_t>((meta->validMin >> (8 * i)) & 0xff);
669 dcb->data[width + i] = static_cast<uint8_t>((meta->validMax >> (8 * i)) & 0xff);
670 }
671
672 dsc[idx].uuid = &uuid->u;
673 dsc[idx].att_flags = BLE_ATT_F_READ;
674 dsc[idx].access_cb = descriptorCb;
675 dsc[idx].arg = dcb;
676 idx++;
677 }
678# endif
679
680# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_CONFIGURATION
681 if (meta->desc & ESS_DESC_CONFIGURATION)
682 {
683 SEssDscCb *dcb = new (std::nothrow) SEssDscCb{};
684 ble_uuid_any_t *uuid = essMakeUuid16(UUID16_ES_CONFIGURATION);
685 if (dcb == nullptr || uuid == nullptr)
686 {
687 delete dcb;
688 essFreeUuid(uuid ? &uuid->u : nullptr);
689 delete[] dsc;
690 return -ENOMEM;
691 }
692
693 dcb->kind = ESS_DESC_CONFIGURATION;
694 dcb->field.io = cb->getObject(meta->configurationObjid);
695 if (dcb->field.io == nullptr)
696 {
697 delete dcb;
698 essFreeUuid(&uuid->u);
699 delete[] dsc;
700 return -EIO;
701 }
702
703 dcb->field.data = dcb->field.io->ddata_alloc(1);
704 if (dcb->field.data == nullptr)
705 {
706 delete dcb;
707 essFreeUuid(&uuid->u);
708 delete[] dsc;
709 return -ENOMEM;
710 }
711
712 dsc[idx].uuid = &uuid->u;
713 dsc[idx].att_flags = BLE_ATT_F_READ;
714 dsc[idx].access_cb = descriptorCb;
715 dsc[idx].arg = dcb;
716 idx++;
717 }
718# endif
719
720# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_MEASUREMENT
721 if (meta->desc & ESS_DESC_MEASUREMENT)
722 {
723 SEssDscCb *dcb = new (std::nothrow) SEssDscCb{};
724 ble_uuid_any_t *uuid = essMakeUuid16(UUID16_ES_MEASUREMENT);
725 SObjectId::ObjectId objids[6] = {
726 meta->measurementFlagsObjid,
727 meta->samplingFunctionObjid,
728 meta->measurementPeriodObjid,
729 meta->updateIntervalObjid,
730 meta->applicationObjid,
731 meta->uncertaintyObjid,
732 };
733 if (dcb == nullptr || uuid == nullptr)
734 {
735 delete dcb;
736 essFreeUuid(uuid ? &uuid->u : nullptr);
737 delete[] dsc;
738 return -ENOMEM;
739 }
740
741 dcb->kind = ESS_DESC_MEASUREMENT;
742 dcb->len = 11;
743 for (size_t i = 0; i < 6; i++)
744 {
745 if (objids[i] == 0)
746 {
747 continue;
748 }
749
750 dcb->measurement[i].io = cb->getObject(objids[i]);
751 if (dcb->measurement[i].io == nullptr)
752 {
753 for (size_t k = 0; k < 6; k++)
754 {
755 delete dcb->measurement[k].data;
756 }
757 delete dcb;
758 essFreeUuid(&uuid->u);
759 delete[] dsc;
760 return -EIO;
761 }
762
763 dcb->measurement[i].data = dcb->measurement[i].io->ddata_alloc(1);
764 if (dcb->measurement[i].data == nullptr)
765 {
766 for (size_t k = 0; k < 6; k++)
767 {
768 delete dcb->measurement[k].data;
769 }
770 delete dcb;
771 essFreeUuid(&uuid->u);
772 delete[] dsc;
773 return -ENOMEM;
774 }
775 }
776
777 dsc[idx].uuid = &uuid->u;
778 dsc[idx].att_flags = BLE_ATT_F_READ;
779 dsc[idx].access_cb = descriptorCb;
780 dsc[idx].arg = dcb;
781 idx++;
782 }
783# endif
784
785# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_TRIGGER_SETTING
786 if (meta->desc & ESS_DESC_TRIGGER_SETTING)
787 {
788 SEssDscCb *dcb = new (std::nothrow) SEssDscCb{};
789 ble_uuid_any_t *uuid = essMakeUuid16(UUID16_ES_TRIGGER_SETTING);
790 if (dcb == nullptr || uuid == nullptr)
791 {
792 delete dcb;
793 essFreeUuid(uuid ? &uuid->u : nullptr);
794 delete[] dsc;
795 return -ENOMEM;
796 }
797
798 dcb->kind = ESS_DESC_TRIGGER_SETTING;
799 dcb->field.io = cb->getObject(meta->triggerSettingObjid);
800 if (dcb->field.io == nullptr)
801 {
802 delete dcb;
803 essFreeUuid(&uuid->u);
804 delete[] dsc;
805 return -EIO;
806 }
807
808 dcb->field.data = dcb->field.io->ddata_alloc(1);
809 if (dcb->field.data == nullptr)
810 {
811 delete dcb;
812 essFreeUuid(&uuid->u);
813 delete[] dsc;
814 return -ENOMEM;
815 }
816
817 dsc[idx].uuid = &uuid->u;
818 dsc[idx].att_flags = BLE_ATT_F_READ;
819 dsc[idx].access_cb = descriptorCb;
820 dsc[idx].arg = dcb;
821 idx++;
822 }
823# endif
824
825 chr->descriptors = dsc;
826 return OK;
827#endif
828}
829
830void CProtoNimblePrphEss::allocObject(const SProtoNimblePrphIOBindEssObjid &obj,
831 const uint32_t *ext)
832{
833 uint8_t type = cfgIdIOBindEssCfgObjTypeGet(obj.cfg);
834#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
835 SEssMeta meta{};
836#endif
837
838 DAWNINFO("ESS allocate type=%d object 0x%" PRIx32 "\n", type, obj.objid.v);
839
840 // Store type for later
841
842 ioTypeMap.insert_or_assign(obj.objid.v, type);
843
844#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
845 for (size_t i = 0; i < obj.extCount; i++)
846 {
847 uint8_t kind = cfgIdIOBindEssExtKindGet(ext[0]);
848 uint8_t size = cfgIdIOBindEssExtSizeGet(ext[0]);
849 const uint32_t *data = &ext[1];
850 UNUSED(data);
851
852 switch (kind)
853 {
855 {
856 size_t bytes = static_cast<size_t>(size) * sizeof(uint32_t);
857 bytes = bytes < ESS_USER_DESCRIPTION_MAX ? bytes : ESS_USER_DESCRIPTION_MAX;
858
859# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_USER_DESCRIPTION
860 meta.desc |= ESS_DESC_USER_DESCRIPTION;
861 std::memcpy(meta.userDescription, data, bytes);
862# endif
863 break;
864 }
865
867 {
868# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_VALID_RANGE
869 if (size >= 2)
870 {
871 meta.desc |= ESS_DESC_VALID_RANGE;
872 meta.validMin = data[0];
873 meta.validMax = data[1];
874 }
875# endif
876 break;
877 }
878
880 {
881# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_MEASUREMENT
882 if (size >= 6)
883 {
884 meta.desc |= ESS_DESC_MEASUREMENT;
885 meta.measurementFlagsObjid = data[0];
886 meta.samplingFunctionObjid = data[1];
887 meta.measurementPeriodObjid = data[2];
888 meta.updateIntervalObjid = data[3];
889 meta.applicationObjid = data[4];
890 meta.uncertaintyObjid = data[5];
891 }
892# endif
893 break;
894 }
895
897 {
898# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_CONFIGURATION
899 if (size >= 1)
900 {
901 meta.desc |= ESS_DESC_CONFIGURATION;
902 meta.configurationObjid = data[0];
903 }
904# endif
905 break;
906 }
907
909 {
910# ifdef CONFIG_DAWN_PROTO_NIMBLE_ESS_DESC_TRIGGER_SETTING
911 if (size >= 1)
912 {
913 meta.desc |= ESS_DESC_TRIGGER_SETTING;
914 meta.triggerSettingObjid = data[0];
915 }
916# endif
917 break;
918 }
919
920 default:
921 {
922 DAWNERR("unknown ESS extension kind %u\n", kind);
923 break;
924 }
925 }
926
927 ext += 1 + size;
928 }
929
930 if (meta.desc != 0)
931 {
932 ioMetaMap.insert_or_assign(obj.objid.v, meta);
933 }
934#endif
935
936 // Allocate object in map
937
938 cb->regObject(obj.objid.v);
939#ifdef CONFIG_DAWN_PROTO_NIMBLE_EXTENDED_METADATA
940 if (meta.measurementFlagsObjid != 0)
941 {
942 cb->regObject(meta.measurementFlagsObjid);
943 }
944
945 if (meta.samplingFunctionObjid != 0)
946 {
947 cb->regObject(meta.samplingFunctionObjid);
948 }
949
950 if (meta.measurementPeriodObjid != 0)
951 {
952 cb->regObject(meta.measurementPeriodObjid);
953 }
954
955 if (meta.updateIntervalObjid != 0)
956 {
957 cb->regObject(meta.updateIntervalObjid);
958 }
959
960 if (meta.applicationObjid != 0)
961 {
962 cb->regObject(meta.applicationObjid);
963 }
964
965 if (meta.uncertaintyObjid != 0)
966 {
967 cb->regObject(meta.uncertaintyObjid);
968 }
969
970 if (meta.configurationObjid != 0)
971 {
972 cb->regObject(meta.configurationObjid);
973 }
974
975 if (meta.triggerSettingObjid != 0)
976 {
977 cb->regObject(meta.triggerSettingObjid);
978 }
979#endif
980
981 vio.push_back(obj.objid.v);
982}
983
984// Assumption: Configuration item passed by caller is a valid type supported by
985// this class.
986void CProtoNimblePrphEss::configureDesc(const SObjectCfg::SObjectCfgItem *item)
987{
988 // Iterrate over objectid list
989
990 for (size_t k = 0; k < item->cfgid.s.size;)
991 {
992 const SProtoNimblePrphIOBindEss *tmp =
993 reinterpret_cast<const SProtoNimblePrphIOBindEss *>(&item->data[k]);
994
995 uint8_t size = cfgIdIOBindEssCfg0SizeGet(tmp->cfg0);
996
997 size_t pos = k + sizeof(SProtoNimblePrphIOBindEss) / sizeof(uint32_t);
998
999 for (size_t i = 0; i < size; i++)
1000 {
1001 const SProtoNimblePrphIOBindEssObjid *obj =
1002 reinterpret_cast<const SProtoNimblePrphIOBindEssObjid *>(&item->data[pos]);
1003 const uint32_t *ext =
1004 &item->data[pos + sizeof(SProtoNimblePrphIOBindEssObjid) / sizeof(uint32_t)];
1005
1006 allocObject(*obj, ext);
1007
1008 pos += sizeof(SProtoNimblePrphIOBindEssObjid) / sizeof(uint32_t);
1009 for (size_t j = 0; j < obj->extCount; j++)
1010 {
1011 pos += 1 + cfgIdIOBindEssExtSizeGet(item->data[pos]);
1012 }
1013 }
1014
1015 k = pos;
1016 }
1017}
1018
1019CProtoNimblePrphEss::CProtoNimblePrphEss(const SObjectCfg::SObjectCfgItem *desc_,
1020 IProtoNimblePrphCb *cb_)
1021 : IProtoNimblePrphService(desc_, cb_)
1022 , created(false)
1023{
1024 // Service ID not assigned yet
1025
1026 id = -1;
1027 noChar = 0;
1028}
1029
1030CProtoNimblePrphEss::~CProtoNimblePrphEss()
1031{
1032 deinit();
1033}
1034
1036{
1037 int ret;
1038
1039 // Configure object
1040
1041 configureDesc(desc);
1042
1043 // Allocate service
1044
1045 ret = allocESS();
1046 if (ret != OK)
1047 {
1048 return ret;
1049 }
1050
1051 // Register servie in peripheral handler
1052
1053 id = cb->serviceRegister(&svc);
1054 if (id < 0)
1055 {
1056 DAWNERR("ESS service registration failed\n");
1057 return -EIO;
1058 }
1059
1060 return OK;
1061}
1062
1064{
1065 deleteESS();
1066 return OK;
1067}
1068
1070{
1071 int ret;
1072
1073 if (created)
1074 {
1075 return cb->startService(id);
1076 }
1077
1078 // Create service
1079
1080 ret = createESS();
1081 if (ret != OK)
1082 {
1083 deleteESS();
1084 allocESS();
1085 return ret;
1086 }
1087
1088 created = true;
1089
1090 // Signal start to peripheral handler
1091
1092 return cb->startService(id);
1093}
1094
1096{
1097 // Signal stop to peripheral handler
1098
1099 return cb->stopService(id);
1100}
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:353
virtual bool isNotify() const =0
Check if IO supports notifications.
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
Definition common.hxx:502
virtual size_t getDataDim() const =0
Get data vector dimension.
virtual size_t getDataSize() const =0
Get data size in bytes.
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.
int stop()
Stop service.
@ ESS_EXT_TRIGGER_SETTING
Environmental Sensing Trigger Setting descriptor.
Definition prph_ess.hxx:58
@ ESS_EXT_USER_DESCRIPTION
Characteristic User Description descriptor.
Definition prph_ess.hxx:54
@ ESS_EXT_CONFIGURATION
Environmental Sensing Configuration descriptor.
Definition prph_ess.hxx:57
@ ESS_EXT_MEASUREMENT
Environmental Sensing Measurement descriptor.
Definition prph_ess.hxx:56
@ ESS_EXT_VALID_RANGE
Valid Range descriptor.
Definition prph_ess.hxx:55
int start()
Start service.
int init()
Initialize service.
@ PRPH_ESS_TYPE_UVIDX
UV index sensor.
Definition prph_ess.hxx:66
@ PRPH_ESS_TYPE_TEMP
Temperature sensor.
Definition prph_ess.hxx:63
@ PRPH_ESS_TYPE_PRESS
Pressure (barometric) sensor.
Definition prph_ess.hxx:65
@ PRPH_ESS_TYPE_HUM
Humidity sensor.
Definition prph_ess.hxx:64
@ PRPH_ESS_TYPE_GAS
Gas resistance sensor (non-standard UUID).
Definition prph_ess.hxx:69
@ PRPH_ESS_TYPE_LIGHT
Illuminance / light sensor.
Definition prph_ess.hxx:70
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
static uint32_t UUID_ESS[4]
Environmental Sensing Service UUID (0x181A).
Definition iprph.hxx:78
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:383
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.
virtual void regObject(SObjectId::ObjectId id)=0
Register an I/O object for this service.
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:467
std::vector< SObjectId::ObjectId > vio
Vector of I/O objects exposed by this service.
Definition iprph.hxx:557
const SObjectCfg::SObjectCfgItem * desc
Configuration descriptor for this service.
Definition iprph.hxx:574
IProtoNimblePrphCb * cb
Callback interface to peripheral.
Definition iprph.hxx:566
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
@ DTYPE_UINT8
Unsigned 8-bit integer (0 to 255).
Definition objectid.hxx:80
@ DTYPE_UINT16
Unsigned 16-bit integer (0 to 65535).
Definition objectid.hxx:88
@ DTYPE_UINT32
Unsigned 32-bit integer (0 to 4294967295).
Definition objectid.hxx:96
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
T & get(size_t index, size_t batch=0)
Get data element by index and batch (type-safe).
Definition ddata.hxx:93
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.