ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ble_gatt.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ble_gatt.cpp
6 * @brief Bluetooth ATT protocol codec + GATT characteristic bridge (see ble_gatt.h).
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_BLE_GATT
13
14#include <string.h>
15
16size_t att_read_req(uint16_t handle, uint8_t *out, size_t cap)
17{
18 if (!out || cap < 3)
19 {
20 return 0;
21 }
22 out[0] = AttOp::ATT_OP_READ_REQ;
23 out[1] = (uint8_t)handle;
24 out[2] = (uint8_t)(handle >> 8);
25 return 3;
26}
27
28size_t att_read_rsp(const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
29{
30 if (!out || (vlen && !val) || cap < 1 + vlen)
31 {
32 return 0;
33 }
34 out[0] = AttOp::ATT_OP_READ_RSP;
35 if (vlen)
36 {
37 memcpy(out + 1, val, vlen);
38 }
39 return 1 + vlen;
40}
41
42static size_t att_handle_value(uint8_t op, uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
43{
44 if (!out || (vlen && !val) || cap < 3 + vlen)
45 {
46 return 0;
47 }
48 out[0] = op;
49 out[1] = (uint8_t)handle;
50 out[2] = (uint8_t)(handle >> 8);
51 if (vlen)
52 {
53 memcpy(out + 3, val, vlen);
54 }
55 return 3 + vlen;
56}
57
58size_t att_write_req(uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
59{
60 return att_handle_value(AttOp::ATT_OP_WRITE_REQ, handle, val, vlen, out, cap);
61}
62
63size_t att_notify(uint16_t handle, const uint8_t *val, size_t vlen, uint8_t *out, size_t cap)
64{
65 return att_handle_value(AttOp::ATT_OP_HANDLE_VALUE_NTF, handle, val, vlen, out, cap);
66}
67
68size_t att_error_rsp(uint8_t req_op, uint16_t handle, uint8_t error, uint8_t *out, size_t cap)
69{
70 if (!out || cap < 5)
71 {
72 return 0;
73 }
74 out[0] = AttOp::ATT_OP_ERROR_RSP;
75 out[1] = req_op;
76 out[2] = (uint8_t)handle;
77 out[3] = (uint8_t)(handle >> 8);
78 out[4] = error;
79 return 5;
80}
81
82bool att_parse(const uint8_t *pdu, size_t len, AttPdu *out)
83{
84 if (!pdu || !out || len < 1)
85 {
86 return false;
87 }
88 out->opcode = pdu[0];
89 out->handle = 0;
90 out->req_op = 0;
91 out->error = 0;
92 out->value = nullptr;
93 out->value_len = 0;
94
95 switch (pdu[0])
96 {
97 case AttOp::ATT_OP_ERROR_RSP:
98 if (len < 5)
99 {
100 return false;
101 }
102 out->req_op = pdu[1];
103 out->handle = (uint16_t)(pdu[2] | (pdu[3] << 8));
104 out->error = pdu[4];
105 return true;
106 case AttOp::ATT_OP_READ_REQ:
107 if (len < 3)
108 {
109 return false;
110 }
111 out->handle = (uint16_t)(pdu[1] | (pdu[2] << 8));
112 return true;
113 case AttOp::ATT_OP_READ_RSP:
114 if (len > 1)
115 {
116 out->value = pdu + 1;
117 out->value_len = len - 1;
118 }
119 return true;
120 case AttOp::ATT_OP_WRITE_REQ:
121 case AttOp::ATT_OP_HANDLE_VALUE_NTF:
122 if (len < 3)
123 {
124 return false;
125 }
126 out->handle = (uint16_t)(pdu[1] | (pdu[2] << 8));
127 if (len > 3)
128 {
129 out->value = pdu + 3;
130 out->value_len = len - 3;
131 }
132 return true;
133 case AttOp::ATT_OP_WRITE_RSP:
134 return true;
135 default:
136 return true; // unknown opcode: still report it, no fixed fields
137 }
138}
139
140namespace
141{
142void put_hex16(pc_sb *b, uint16_t v)
143{
144 char t[7] = "0x0000";
145 static const char *H = "0123456789abcdef";
146 for (int i = 0; i < 4; i++)
147 {
148 t[2 + i] = H[(v >> ((3 - i) * 4)) & 0xF];
149 }
150 pc_sb_put(b, t);
151}
152} // namespace
153
154size_t pc_gatt_char_json(const GattChar *chars, size_t n, char *out, size_t cap)
155{
156 if (!out || cap == 0 || (n && !chars))
157 {
158 return 0;
159 }
160 pc_sb b = {out, cap, 0, true};
161 pc_sb_put(&b, "[");
162 for (size_t i = 0; i < n; i++)
163 {
164 if (i)
165 {
166 pc_sb_put(&b, ",");
167 }
168 pc_sb_put(&b, "{\"handle\":");
169 pc_sb_u32(&b, chars[i].handle);
170 pc_sb_put(&b, ",\"uuid\":\"");
171 put_hex16(&b, chars[i].uuid);
172 pc_sb_put(&b, "\",\"props\":");
173 pc_sb_u32(&b, chars[i].props);
174 pc_sb_put(&b, "}");
175 }
176 pc_sb_put(&b, "]");
177 if (!b.ok)
178 {
179 return 0;
180 }
181 out[b.len] = '\0';
182 return b.len;
183}
184
185#endif // PC_ENABLE_BLE_GATT
Bluetooth ATT protocol codec + GATT characteristic bridge (PC_ENABLE_BLE_GATT).
Bounded no-heap string builder that fails closed on overflow (one shared copy).
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Definition strbuf.h:303
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30
size_t len
Definition strbuf.h:33
bool ok
Definition strbuf.h:34