1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21 #include <string.h>
22
23 #include "sysinit/sysinit.h"
24 #include "host/ble_hs.h"
25 #include "services/gap/ble_svc_gap.h"
26 #include "os/endian.h"
27
28 #define PPCP_ENABLED \
29 MYNEWT_VAL(BLE_ROLE_PERIPHERAL) && \
30 (MYNEWT_VAL(BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL) || \
31 MYNEWT_VAL(BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL) || \
32 MYNEWT_VAL(BLE_SVC_GAP_PPCP_SLAVE_LATENCY) || \
33 MYNEWT_VAL(BLE_SVC_GAP_PPCP_SUPERVISION_TMO))
34
35 #define BLE_SVC_GAP_NAME_MAX_LEN \
36 MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)
37
38 static ble_svc_gap_chr_changed_fn *ble_svc_gap_chr_changed_cb_fn;
39
40 static char ble_svc_gap_name[BLE_SVC_GAP_NAME_MAX_LEN + 1] =
41 MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME);
42 static uint16_t ble_svc_gap_appearance = MYNEWT_VAL(BLE_SVC_GAP_APPEARANCE);
43
44 static int
45 ble_svc_gap_access(uint16_t conn_handle, uint16_t attr_handle,
46 struct ble_gatt_access_ctxt *ctxt, void *arg);
47
48 static const struct ble_gatt_svc_def ble_svc_gap_defs[] = {
49 {
50 /*** Service: GAP. */
51 .type = BLE_GATT_SVC_TYPE_PRIMARY,
52 .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_UUID16),
53 .characteristics = (struct ble_gatt_chr_def[]) { {
54 /*** Characteristic: Device Name. */
55 .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME),
56 .access_cb = ble_svc_gap_access,
57 .flags = BLE_GATT_CHR_F_READ |
58 #if MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM) >= 0
59 BLE_GATT_CHR_F_WRITE |
60 MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM) |
61 #endif
62 0,
63 }, {
64 /*** Characteristic: Appearance. */
65 .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_CHR_UUID16_APPEARANCE),
66 .access_cb = ble_svc_gap_access,
67 .flags = BLE_GATT_CHR_F_READ |
68 #if MYNEWT_VAL(BLE_SVC_GAP_APPEARANCE_WRITE_PERM) >= 0
69 BLE_GATT_CHR_F_WRITE |
70 MYNEWT_VAL(BLE_SVC_GAP_APPEARANCE_WRITE_PERM) |
71 #endif
72 0,
73 }, {
74 #if PPCP_ENABLED
75 /*** Characteristic: Peripheral Preferred Connection Parameters. */
76 .uuid =
77 BLE_UUID16_DECLARE(BLE_SVC_GAP_CHR_UUID16_PERIPH_PREF_CONN_PARAMS),
78 .access_cb = ble_svc_gap_access,
79 .flags = BLE_GATT_CHR_F_READ,
80 }, {
81 #endif
82 #if MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION) >= 0
83 /*** Characteristic: Central Address Resolution. */
84 .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_CHR_UUID16_CENTRAL_ADDRESS_RESOLUTION),
85 .access_cb = ble_svc_gap_access,
86 .flags = BLE_GATT_CHR_F_READ,
87 }, {
88 #endif
89 0, /* No more characteristics in this service. */
90 } },
91 },
92
93 {
94 0, /* No more services. */
95 },
96 };
97
98 static int
ble_svc_gap_device_name_read_access(struct ble_gatt_access_ctxt * ctxt)99 ble_svc_gap_device_name_read_access(struct ble_gatt_access_ctxt *ctxt)
100 {
101 int rc;
102
103 rc = os_mbuf_append(ctxt->om, ble_svc_gap_name, strlen(ble_svc_gap_name));
104
105 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
106 }
107
108 static int
ble_svc_gap_device_name_write_access(struct ble_gatt_access_ctxt * ctxt)109 ble_svc_gap_device_name_write_access(struct ble_gatt_access_ctxt *ctxt)
110 {
111 #if MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM) < 0
112 assert(0);
113 return 0;
114 #else
115 uint16_t om_len;
116 int rc;
117
118 om_len = OS_MBUF_PKTLEN(ctxt->om);
119 if (om_len > BLE_SVC_GAP_NAME_MAX_LEN) {
120 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
121 }
122
123 rc = ble_hs_mbuf_to_flat(ctxt->om, ble_svc_gap_name, om_len, NULL);
124 if (rc != 0) {
125 return BLE_ATT_ERR_UNLIKELY;
126 }
127
128 ble_svc_gap_name[om_len] = '\0';
129
130 if (ble_svc_gap_chr_changed_cb_fn) {
131 ble_svc_gap_chr_changed_cb_fn(BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME);
132 }
133
134 return rc;
135 #endif
136 }
137
138 static int
ble_svc_gap_appearance_read_access(struct ble_gatt_access_ctxt * ctxt)139 ble_svc_gap_appearance_read_access(struct ble_gatt_access_ctxt *ctxt)
140 {
141 uint16_t appearance = htole16(ble_svc_gap_appearance);
142 int rc;
143
144 rc = os_mbuf_append(ctxt->om, &appearance, sizeof(appearance));
145
146 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
147 }
148
149 static int
ble_svc_gap_appearance_write_access(struct ble_gatt_access_ctxt * ctxt)150 ble_svc_gap_appearance_write_access(struct ble_gatt_access_ctxt *ctxt)
151 {
152 #if MYNEWT_VAL(BLE_SVC_GAP_APPEARANCE_WRITE_PERM) < 0
153 assert(0);
154 return 0;
155 #else
156 uint16_t om_len;
157 int rc;
158
159 om_len = OS_MBUF_PKTLEN(ctxt->om);
160 if (om_len != sizeof(ble_svc_gap_appearance)) {
161 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
162 }
163
164 rc = ble_hs_mbuf_to_flat(ctxt->om, ble_svc_gap_appearance, om_len, NULL);
165 if (rc != 0) {
166 return BLE_ATT_ERR_UNLIKELY;
167 }
168
169 ble_svc_gap_appearance = le16toh(ble_svc_gap_appearance);
170
171 if (ble_svc_gap_chr_changed_cb_fn) {
172 ble_svc_gap_chr_changed_cb_fn(BLE_SVC_GAP_CHR_UUID16_APPEARANCE);
173 }
174
175 return rc;
176 #endif
177 }
178
179 static int
ble_svc_gap_access(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)180 ble_svc_gap_access(uint16_t conn_handle, uint16_t attr_handle,
181 struct ble_gatt_access_ctxt *ctxt, void *arg)
182 {
183 uint16_t uuid16;
184 #if MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION) >= 0
185 uint8_t central_ar = MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION);
186 #endif
187 #if PPCP_ENABLED
188 uint16_t ppcp[4] = {
189 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL)),
190 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL)),
191 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_SLAVE_LATENCY)),
192 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_SUPERVISION_TMO))
193 };
194 #endif
195 int rc;
196
197 uuid16 = ble_uuid_u16(ctxt->chr->uuid);
198 assert(uuid16 != 0);
199
200 switch (uuid16) {
201 case BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME:
202 if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
203 rc = ble_svc_gap_device_name_read_access(ctxt);
204 } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
205 rc = ble_svc_gap_device_name_write_access(ctxt);
206 } else {
207 assert(0);
208 }
209 return rc;
210
211 case BLE_SVC_GAP_CHR_UUID16_APPEARANCE:
212 if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
213 rc = ble_svc_gap_appearance_read_access(ctxt);
214 } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
215 rc = ble_svc_gap_appearance_write_access(ctxt);
216 } else {
217 assert(0);
218 }
219 return rc;
220
221 #if PPCP_ENABLED
222 case BLE_SVC_GAP_CHR_UUID16_PERIPH_PREF_CONN_PARAMS:
223 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
224 rc = os_mbuf_append(ctxt->om, &ppcp, sizeof(ppcp));
225 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
226 #endif
227
228 #if MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION) >= 0
229 case BLE_SVC_GAP_CHR_UUID16_CENTRAL_ADDRESS_RESOLUTION:
230 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
231 rc = os_mbuf_append(ctxt->om, ¢ral_ar, sizeof(central_ar));
232 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
233 #endif
234
235 default:
236 assert(0);
237 return BLE_ATT_ERR_UNLIKELY;
238 }
239 }
240
241 const char *
ble_svc_gap_device_name(void)242 ble_svc_gap_device_name(void)
243 {
244 return ble_svc_gap_name;
245 }
246
247 int
ble_svc_gap_device_name_set(const char * name)248 ble_svc_gap_device_name_set(const char *name)
249 {
250 int len;
251
252 len = strlen(name);
253 if (len > BLE_SVC_GAP_NAME_MAX_LEN) {
254 return BLE_HS_EINVAL;
255 }
256
257 memcpy(ble_svc_gap_name, name, len);
258 ble_svc_gap_name[len] = '\0';
259
260 return 0;
261 }
262
263 uint16_t
ble_svc_gap_device_appearance(void)264 ble_svc_gap_device_appearance(void)
265 {
266 return ble_svc_gap_appearance;
267 }
268
269 int
ble_svc_gap_device_appearance_set(uint16_t appearance)270 ble_svc_gap_device_appearance_set(uint16_t appearance)
271 {
272 ble_svc_gap_appearance = appearance;
273
274 return 0;
275 }
276
277 void
ble_svc_gap_set_chr_changed_cb(ble_svc_gap_chr_changed_fn * cb)278 ble_svc_gap_set_chr_changed_cb(ble_svc_gap_chr_changed_fn *cb)
279 {
280 ble_svc_gap_chr_changed_cb_fn = cb;
281 }
282
283 void
ble_svc_gap_init(void)284 ble_svc_gap_init(void)
285 {
286 int rc;
287
288 /* Ensure this function only gets called by sysinit. */
289 SYSINIT_ASSERT_ACTIVE();
290
291 rc = ble_gatts_count_cfg(ble_svc_gap_defs);
292 SYSINIT_PANIC_ASSERT(rc == 0);
293
294 rc = ble_gatts_add_svcs(ble_svc_gap_defs);
295 SYSINIT_PANIC_ASSERT(rc == 0);
296 }
297