6#include "dawn/proto/nimble/prph_ots.hxx"
12#include "dawn/io/common.hxx"
13#include "host/ble_hs.h"
14#include "host/ble_hs_mbuf.h"
15#include "os/os_mbuf.h"
27static constexpr uint32_t OACP_FEATURE_BITS = (1u << 4) | (1u << 5) | (1u << 7) | (1u << 9);
33static constexpr uint8_t OACP_WRITE_MODE_TRUNCATE = 0x02;
38static constexpr uint32_t OLCP_FEATURE_BITS = (1u << 0);
42static inline void packU32LE(uint8_t *out, uint32_t v)
44 out[0] =
static_cast<uint8_t
>(v & 0xff);
45 out[1] =
static_cast<uint8_t
>((v >> 8) & 0xff);
46 out[2] =
static_cast<uint8_t
>((v >> 16) & 0xff);
47 out[3] =
static_cast<uint8_t
>((v >> 24) & 0xff);
50static inline uint32_t unpackU32LE(
const uint8_t *in)
52 return (
static_cast<uint32_t
>(in[0])) | (
static_cast<uint32_t
>(in[1]) << 8) |
53 (
static_cast<uint32_t
>(in[2]) << 16) | (
static_cast<uint32_t
>(in[3]) << 24);
56static inline void packU48LE(uint8_t *out, uint64_t v)
60 for (i = 0; i < 6; i++)
62 out[i] =
static_cast<uint8_t
>((v >> (8 * i)) & 0xff);
66static inline uint64_t unpackU48LE(
const uint8_t *in)
71 for (i = 0; i < 6; i++)
73 v |=
static_cast<uint64_t
>(in[i]) << (8 * i);
82static ble_uuid_any_t *makeUuid16(uint16_t value)
84 ble_uuid_any_t *u =
new (std::nothrow) ble_uuid_any_t{};
90 u->u.type = BLE_UUID_TYPE_16;
95static void freeUuid(
const ble_uuid_t *u)
99 delete reinterpret_cast<const ble_uuid_any_t *
>(u);
114 std::memset(&svc, 0,
sizeof(svc));
117CProtoNimblePrphOts::~CProtoNimblePrphOts()
122void CProtoNimblePrphOts::allocObject(
const SProtoNimblePrphIOBindOtsObjid &entry)
127 kind = cfgGetType(entry.cfg);
128 if (kind != PRPH_OTS_TYPE_FILE && kind != PRPH_OTS_TYPE_DESCRIPTOR && kind != PRPH_OTS_TYPE_CAPS)
130 DAWNERR(
"OTS: invalid object kind %u\n", kind);
137 case PRPH_OTS_TYPE_DESCRIPTOR:
140 case PRPH_OTS_TYPE_CAPS:
143 case PRPH_OTS_TYPE_FILE:
149 meta.access = cfgGetAccess(entry.cfg);
151 meta.iid = entry.objid.v;
153 vmeta.push_back(meta);
156 vio.push_back(entry.objid.v);
158 DAWNINFO(
"OTS: register object '%.*s' kind=%u access=%u id=0x%" PRIx32
"\n",
172 const SProtoNimblePrphIOBindOts *blob;
176 blob =
reinterpret_cast<const SProtoNimblePrphIOBindOts *
>(&item->
data[k]);
179 for (i = 0; i < count; i++)
181 allocObject(blob->obj[i]);
184 k += (
sizeof(SProtoNimblePrphIOBindOts) + count *
sizeof(SProtoNimblePrphIOBindOtsObjid)) / 4;
188int CProtoNimblePrphOts::setChrDef(
size_t idx,
190 ble_gatt_access_fn *cb_,
196 u = makeUuid16(uuid16);
202 chrs[idx].uuid = &u->u;
203 chrs[idx].access_cb = cb_;
204 chrs[idx].arg =
this;
205 chrs[idx].flags = flags;
206 chrs[idx].val_handle = valHandle;
214static constexpr size_t kNumChrs = 8;
216int CProtoNimblePrphOts::allocOTS()
221 constexpr uint16_t kRd = BLE_GATT_CHR_F_READ;
222 constexpr uint16_t kInd = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE;
227 ble_gatt_access_fn *
cb;
230 } table[kNumChrs] = {
241 ble_uuid_any_t *svcUuid;
243 chrs =
new (std::nothrow) ble_gatt_chr_def[kNumChrs + 1]();
246 DAWNERR(
"OTS: chr alloc failed\n");
250 for (
size_t i = 0; i < kNumChrs; i++)
252 if (setChrDef(i, table[i].uuid, table[i].
cb, table[i].flags, table[i].handle) < 0)
261 if (svcUuid ==
nullptr)
266 svc.type = BLE_GATT_SVC_TYPE_PRIMARY;
267 svc.uuid = &svcUuid->u;
268 svc.includes =
nullptr;
269 svc.characteristics = chrs;
276 if (xferBuf ==
nullptr || !xferBuf->isAllocated())
278 DAWNERR(
"OTS: xfer buffer alloc failed\n");
287void CProtoNimblePrphOts::deleteOTS()
291 for (
size_t i = 0; i < kNumChrs; i++)
293 freeUuid(chrs[i].uuid);
300 if (svc.uuid !=
nullptr)
306 if (xferBuf !=
nullptr)
313int CProtoNimblePrphOts::createOTS()
327 DAWNERR(
"OTS: IO 0x%" PRIx32
" not bound\n", objid);
333 DAWNERR(
"OTS: IO 0x%" PRIx32
" is not seekable\n", objid);
356 DAWNERR(
"OTS service registration failed\n");
389#ifndef CONFIG_DAWN_PROTO_NIMBLE_DUMMY
394 DAWNERR(
"OTS: ble_l2cap_create_server failed: %d\n", ret);
412bool CProtoNimblePrphOts::selected(uint16_t conn, SOtsConnState *&state, SOtsObjectMeta *&meta)
414 std::map<uint16_t, SOtsConnState>::iterator it;
416 it = vconn.find(conn);
417 if (it == vconn.end())
423 if (state->cursor < 0 ||
static_cast<size_t>(state->cursor) >= vmeta.size())
428 meta = &vmeta[state->cursor];
435static inline int gattAppend(
struct os_mbuf *om,
const void *buf,
size_t len)
437 return os_mbuf_append(om, buf, len) == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
445int CProtoNimblePrphOts::metaPrologue(uint16_t conn,
446 ble_gatt_access_ctxt *ctxt,
449 SOtsObjectMeta **meta,
454 if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR)
456 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
460 if (!(*self)->selected(conn, st, *meta))
462 return BLE_ATT_ERR_UNLIKELY;
465 if (cursorOut !=
nullptr)
467 *cursorOut = st->cursor;
473int CProtoNimblePrphOts::featureCb(uint16_t ,
475 struct ble_gatt_access_ctxt *ctxt,
480 if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR)
482 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
485 packU32LE(&buf[0], OACP_FEATURE_BITS);
486 packU32LE(&buf[4], OLCP_FEATURE_BITS);
488 return gattAppend(ctxt->om, buf,
sizeof(buf));
491int CProtoNimblePrphOts::nameCb(uint16_t conn,
493 struct ble_gatt_access_ctxt *ctxt,
498 SOtsObjectMeta *meta;
504 if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR)
506 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
510 if (!self->selected(conn, st, meta))
515 return gattAppend(ctxt->om, meta->name, strnlen(meta->name,
OBJ_NAME_MAX));
518int CProtoNimblePrphOts::typeCb(uint16_t conn,
520 struct ble_gatt_access_ctxt *ctxt,
524 SOtsObjectMeta *meta;
528 err = metaPrologue(conn, ctxt, arg, &self, &meta,
nullptr);
534 buf[0] =
static_cast<uint8_t
>(meta->typeUuid & 0xff);
535 buf[1] =
static_cast<uint8_t
>((meta->typeUuid >> 8) & 0xff);
536 return gattAppend(ctxt->om, buf,
sizeof(buf));
539int CProtoNimblePrphOts::sizeCb(uint16_t conn,
541 struct ble_gatt_access_ctxt *ctxt,
545 SOtsObjectMeta *meta;
551 err = metaPrologue(conn, ctxt, arg, &self, &meta,
nullptr);
558 cur = (io !=
nullptr) ?
static_cast<uint32_t
>(io->
getDataSize()) : 0;
566 packU32LE(&buf[0], cur);
567 packU32LE(&buf[4], cur);
568 return gattAppend(ctxt->om, buf,
sizeof(buf));
571int CProtoNimblePrphOts::idCb(uint16_t conn,
573 struct ble_gatt_access_ctxt *ctxt,
577 SOtsObjectMeta *meta;
582 err = metaPrologue(conn, ctxt, arg, &self, &meta, &cursor);
591 packU48LE(buf, 256ull +
static_cast<uint64_t
>(cursor));
592 return gattAppend(ctxt->om, buf,
sizeof(buf));
595int CProtoNimblePrphOts::propsCb(uint16_t conn,
597 struct ble_gatt_access_ctxt *ctxt,
601 SOtsObjectMeta *meta;
606 err = metaPrologue(conn, ctxt, arg, &self, &meta,
nullptr);
612 if (meta->access == PRPH_OTS_ACCESS_READ || meta->access == PRPH_OTS_ACCESS_RW)
614 props |= OBJ_PROP_READ;
617 if (meta->access == PRPH_OTS_ACCESS_WRITE || meta->access == PRPH_OTS_ACCESS_RW)
619 props |= OBJ_PROP_WRITE | OBJ_PROP_TRUNC;
622 packU32LE(buf, props);
623 return gattAppend(ctxt->om, buf,
sizeof(buf));
626void CProtoNimblePrphOts::sendCpResponse(uint16_t conn,
632#ifndef CONFIG_DAWN_PROTO_NIMBLE_DUMMY
633 uint8_t buf[3] = {respOp, reqOp, result};
637 om = ble_hs_mbuf_from_flat(buf,
sizeof(buf));
640 DAWNERR(
"OTS: response mbuf alloc failed\n");
644 rc = ble_gatts_indicate_custom(conn, handle, om);
647 DAWNERR(
"OTS: indicate failed: %d\n", rc);
658inline void CProtoNimblePrphOts::sendOacpResponse(uint16_t conn, uint8_t reqOp, uint8_t result)
660 sendCpResponse(conn, hOacp, OACP_OP_RESPONSE, reqOp, result);
663inline void CProtoNimblePrphOts::sendOlcpResponse(uint16_t conn, uint8_t reqOp, uint8_t result)
665 sendCpResponse(conn, hOlcp, OLCP_OP_RESPONSE, reqOp, result);
668int CProtoNimblePrphOts::oacpCb(uint16_t conn,
670 struct ble_gatt_access_ctxt *ctxt,
679 if (ctxt->op != BLE_GATT_ACCESS_OP_WRITE_CHR)
681 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
684 rc = ble_hs_mbuf_to_flat(ctxt->om, pkt,
sizeof(pkt), &copied);
685 if (rc != 0 || copied < 1)
687 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
695 self->vconn.try_emplace(conn);
706 self->sendOacpResponse(conn, op, OACP_RES_INVALID_PARAM);
710 off = unpackU32LE(&pkt[1]);
711 len = unpackU32LE(&pkt[5]);
712 self->handleOacpRead(conn, off, len);
724 self->sendOacpResponse(conn, op, OACP_RES_INVALID_PARAM);
728 off = unpackU32LE(&pkt[1]);
729 len = unpackU32LE(&pkt[5]);
731 self->handleOacpWrite(conn, off, len, mode);
736 self->handleOacpAbort(conn);
745int CProtoNimblePrphOts::olcpCb(uint16_t conn,
747 struct ble_gatt_access_ctxt *ctxt,
756 if (ctxt->op != BLE_GATT_ACCESS_OP_WRITE_CHR)
758 return BLE_ATT_ERR_REQ_NOT_SUPPORTED;
761 rc = ble_hs_mbuf_to_flat(ctxt->om, pkt,
sizeof(pkt), &copied);
762 if (rc != 0 || copied < 1)
764 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
770 self->vconn.try_emplace(conn);
775 return self->handleOlcpFirst(conn);
777 return self->handleOlcpLast(conn);
778 case OLCP_OP_PREVIOUS:
779 return self->handleOlcpPrev(conn);
781 return self->handleOlcpNext(conn);
788 self->sendOlcpResponse(conn, op, OLCP_RES_INVALID_PARAM);
792 oid = unpackU48LE(&pkt[1]);
793 return self->handleOlcpGoto(conn, oid);
797 self->sendOlcpResponse(conn, op, OLCP_RES_OPCODE_NS);
808uint8_t CProtoNimblePrphOts::oacpValidate(uint16_t conn,
813 SOtsObjectMeta *meta;
815 if (!selected(conn, st, meta))
817 return OACP_RES_INVALID_OBJ;
822 if (meta->access != PRPH_OTS_ACCESS_WRITE && meta->access != PRPH_OTS_ACCESS_RW)
824 return OACP_RES_PROC_NOT_PERM;
829 if (meta->access != PRPH_OTS_ACCESS_READ && meta->access != PRPH_OTS_ACCESS_RW)
831 return OACP_RES_PROC_NOT_PERM;
838 return OACP_RES_OPER_FAILED;
841 return OACP_RES_SUCCESS;
844int CProtoNimblePrphOts::handleOacpRead(uint16_t conn, uint32_t off, uint32_t len)
850 res = oacpValidate(conn,
false, st, io);
851 if (res == OACP_RES_SUCCESS && off + len > io->
getDataSize())
853 res = OACP_RES_INVALID_PARAM;
856 if (res != OACP_RES_SUCCESS)
858 sendOacpResponse(conn, OACP_OP_READ, res);
862 st->mode = MODE_READING;
866 sendOacpResponse(conn, OACP_OP_READ, OACP_RES_SUCCESS);
871int CProtoNimblePrphOts::handleOacpWrite(uint16_t conn, uint32_t off, uint32_t len, uint8_t mode)
883 if (mode != 0 && mode != OACP_WRITE_MODE_TRUNCATE)
885 sendOacpResponse(conn, OACP_OP_WRITE, OACP_RES_INVALID_PARAM);
889 res = oacpValidate(conn,
true, st, io);
890 if (res != OACP_RES_SUCCESS)
892 sendOacpResponse(conn, OACP_OP_WRITE, res);
896 st->mode = MODE_WRITING;
900 sendOacpResponse(conn, OACP_OP_WRITE, OACP_RES_SUCCESS);
904int CProtoNimblePrphOts::handleOacpAbort(uint16_t conn)
906 std::map<uint16_t, SOtsConnState>::iterator it;
908 it = vconn.find(conn);
909 if (it != vconn.end())
911 it->second.mode = MODE_IDLE;
912 it->second.remaining = 0;
915 sendOacpResponse(conn, OACP_OP_ABORT, OACP_RES_SUCCESS);
922int CProtoNimblePrphOts::olcpSeekAbs(uint16_t conn, uint8_t op,
size_t idx)
926 sendOlcpResponse(conn, op, OLCP_RES_NO_OBJECT);
930 vconn[conn].cursor =
static_cast<int16_t
>(idx);
931 sendOlcpResponse(conn, op, OLCP_RES_SUCCESS);
939int CProtoNimblePrphOts::olcpStep(uint16_t conn, uint8_t op,
int delta)
941 int16_t &c = vconn[conn].cursor;
942 int next =
static_cast<int>(c) + delta;
944 if (next < 0 ||
static_cast<size_t>(next) >= vmeta.size())
950 c =
static_cast<int16_t
>(next);
951 sendOlcpResponse(conn, op, OLCP_RES_SUCCESS);
955int CProtoNimblePrphOts::handleOlcpFirst(uint16_t conn)
957 return olcpSeekAbs(conn, OLCP_OP_FIRST, 0);
960int CProtoNimblePrphOts::handleOlcpLast(uint16_t conn)
962 return olcpSeekAbs(conn, OLCP_OP_LAST, vmeta.empty() ? 0 : vmeta.size() - 1);
965int CProtoNimblePrphOts::handleOlcpPrev(uint16_t conn)
967 return olcpStep(conn, OLCP_OP_PREVIOUS, -1);
970int CProtoNimblePrphOts::handleOlcpNext(uint16_t conn)
972 return olcpStep(conn, OLCP_OP_NEXT, +1);
975int CProtoNimblePrphOts::handleOlcpGoto(uint16_t conn, uint64_t obj_id)
979 sendOlcpResponse(conn, OLCP_OP_GOTO, OLCP_RES_OBJECT_ID_NF);
983 uint64_t idx = obj_id - 256;
984 if (idx >= vmeta.size())
986 sendOlcpResponse(conn, OLCP_OP_GOTO, OLCP_RES_OBJECT_ID_NF);
990 vconn[conn].cursor =
static_cast<int16_t
>(idx);
991 sendOlcpResponse(conn, OLCP_OP_GOTO, OLCP_RES_SUCCESS);
995int CProtoNimblePrphOts::pumpRead(uint16_t conn)
997#ifdef CONFIG_DAWN_PROTO_NIMBLE_DUMMY
1002 SOtsObjectMeta *meta;
1005 if (!selected(conn, st, meta))
1010 if (st->mode != MODE_READING || st->chan ==
nullptr)
1021 if (xferBuf ==
nullptr)
1023 DAWNERR(
"OTS: xferBuf not initialised\n");
1024 st->mode = MODE_IDLE;
1028 while (st->remaining > 0)
1030 struct os_mbuf *sdu;
1042 rc = io->
getData(*xferBuf, 1, st->offset);
1045 st->mode = MODE_IDLE;
1049 sdu = ble_hs_mbuf_from_flat(xferBuf->
getDataPtr(), chunk);
1055 rc = ble_l2cap_send(st->chan, sdu);
1056 if (rc == BLE_HS_ESTALLED)
1061 st->offset += chunk;
1062 st->remaining -= chunk;
1068 DAWNERR(
"OTS: l2cap_send failed: %d\n", rc);
1069 st->mode = MODE_IDLE;
1073 st->offset += chunk;
1074 st->remaining -= chunk;
1077 st->mode = MODE_IDLE;
1082#ifndef CONFIG_DAWN_PROTO_NIMBLE_DUMMY
1083void CProtoNimblePrphOts::onL2capRx(
struct ble_l2cap_event *event)
1085 std::map<uint16_t, SOtsConnState>::iterator it;
1086 struct os_mbuf *sdu;
1087 struct os_mbuf *next;
1088 SOtsObjectMeta *meta;
1096 conn =
event->receive.conn_handle;
1097 sdu =
event->receive.sdu_rx;
1098 pktLen = OS_MBUF_PKTLEN(sdu);
1099 it = vconn.find(conn);
1107 if (it == vconn.end() || it->second.mode != MODE_WRITING || pktLen == 0 ||
1108 pktLen >
L2CAP_MTU_OTS || xferBuf ==
nullptr || it->second.cursor < 0 ||
1109 static_cast<size_t>(it->second.cursor) >= vmeta.size())
1111 DAWNERR(
"OTS: unexpected L2CAP RX (conn=%u len=%u mode=%u)\n",
1114 (it != vconn.end()) ? it->second.mode : 0xff);
1117 else if (pktLen > it->second.remaining)
1121 DAWNERR(
"OTS: write overflow (conn=%u declared=%" PRIu32
" got=%u)\n",
1123 it->second.remaining,
1125 it->second.mode = MODE_IDLE;
1130 meta = &vmeta[it->second.cursor];
1134 DAWNERR(
"OTS: backing IO 0x%" PRIx32
" gone mid-write\n", meta->iid);
1135 it->second.mode = MODE_IDLE;
1141 ble_hs_mbuf_to_flat(sdu, xferBuf->
getDataPtr(), pktLen, &copied);
1147 xferBuf->N = copied;
1148 rc = io->
setData(*xferBuf, it->second.offset);
1151 DAWNERR(
"OTS: setData failed off=%" PRIu32
" rc=%d\n", it->second.offset, rc);
1152 it->second.mode = MODE_IDLE;
1157 it->second.offset += copied;
1158 it->second.remaining =
1159 (it->second.remaining > copied) ? it->second.remaining - copied : 0;
1160 if (it->second.remaining == 0)
1162 it->second.mode = MODE_IDLE;
1168 os_mbuf_free_chain(sdu);
1175 if (event->receive.chan !=
nullptr)
1177 ble_l2cap_disconnect(event->receive.chan);
1185 next = ble_hs_mbuf_att_pkt();
1186 if (next !=
nullptr)
1188 ble_l2cap_recv_ready(event->receive.chan, next);
1193int CProtoNimblePrphOts::l2capEventCb(
struct ble_l2cap_event *event,
void *arg)
1195#ifdef CONFIG_DAWN_PROTO_NIMBLE_DUMMY
1205 switch (event->type)
1207 case BLE_L2CAP_EVENT_COC_ACCEPT:
1219 struct os_mbuf *sdu;
1221 for (
auto &kv : self->vconn)
1223 if (kv.second.chan !=
nullptr)
1225 DAWNINFO(
"OTS: refusing second L2CAP CoC (busy)\n");
1226 return BLE_HS_EBUSY;
1230 sdu = ble_hs_mbuf_att_pkt();
1233 return BLE_HS_ENOMEM;
1236 ble_l2cap_recv_ready(event->accept.chan, sdu);
1240 case BLE_L2CAP_EVENT_COC_CONNECTED:
1242 conn =
event->connect.conn_handle;
1243 self->vconn.try_emplace(conn);
1244 self->vconn[conn].chan =
event->connect.chan;
1251 if (self->vconn[conn].mode == MODE_READING)
1253 self->pumpRead(conn);
1259 case BLE_L2CAP_EVENT_COC_DISCONNECTED:
1261 std::map<uint16_t, SOtsConnState>::iterator it;
1263 conn =
event->disconnect.conn_handle;
1264 it = self->vconn.find(conn);
1265 if (it != self->vconn.end())
1267 it->second.chan =
nullptr;
1268 it->second.mode = MODE_IDLE;
1273 case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
1274 self->pumpRead(event->tx_unstalled.conn_handle);
1277 case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
1278 self->onL2capRx(event);
Base class for all I/O objects.
int setData(IODataCmn &data, size_t offset=0)
Set data for I/O (public interface with stats tracking).
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
virtual size_t getDataSize() const =0
Get data size in bytes.
Object Transfer Service (OTS) for BLE Peripheral.
static uint16_t UUID16_OBJ_PROPS
Object Properties characteristic.
int start()
Start service.
@ OACP_RES_OPCODE_NS
Opcode Not Supported.
@ OLCP_RES_OOR
Out Of Bounds.
static uint16_t UUID16_OTS
OTS Service UUID (Bluetooth SIG).
static uint16_t OBJ_TYPE_DESCRIPTOR
Object Type for a Dawn descriptor blob (Dawn-private UUID16).
static uint8_t OBJ_NAME_MAX
OTS object name length (bytes, fixed).
static uint16_t L2CAP_PSM_OTS
L2CAP PSM for OTS bulk data (Bluetooth SIG-assigned).
static uint16_t UUID16_OBJ_NAME
Object Name characteristic.
static uint16_t UUID16_OBJ_SIZE
Object Size characteristic (current + allocated, 8 bytes).
int init()
Initialize service.
static uint16_t UUID16_OLCP
Object List Control Point characteristic.
static uint16_t L2CAP_MTU_OTS
L2CAP CoC MTU for OTS data channel.
static uint16_t OBJ_TYPE_UNSPECIFIED
Object Type for an "Unspecified" file (Bluetooth SIG).
static uint16_t UUID16_OBJ_ID
Object ID characteristic (48-bit).
static uint16_t UUID16_OACP
Object Action Control Point characteristic.
int deinit()
Deinitialize service.
static uint16_t UUID16_FEATURE
OTS Feature characteristic.
static uint16_t UUID16_OBJ_TYPE
Object Type characteristic.
static uint16_t OBJ_TYPE_CAPS
Object Type for a Dawn capabilities blob (Dawn-private UUID16).
Interface for BLE peripheral services with GATT characteristics.
virtual int startService(int id)=0
Start a specific service.
virtual CIOCommon * getObject(SObjectId::ObjectId id)=0
Get protocol object by ID.
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.
virtual int stopService(int id)=0
Stop a specific service.
Base interface for GATT services exposed by BLE peripheral.
std::vector< SObjectId::ObjectId > vio
Vector of I/O objects exposed by this service.
const SObjectCfg::SObjectCfgItem * desc
Configuration descriptor for this service.
IProtoNimblePrphCb * cb
Callback interface to peripheral.
Out-of-tree user-extension hooks for Dawn.
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_UINT8
Unsigned 8-bit integer (0 to 255).
uint32_t ObjectId
ObjectID type - single 32-bit value.
Heap-allocated dynamic I/O data buffer.
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
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.