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 "os/endian.h"
26 #include "services/gap/ble_svc_gap.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 ble_svc_gap_access(uint16_t conn_handle, uint16_t attr_handle,
45 struct ble_gatt_access_ctxt *ctxt, void *arg);
46
47 static const struct ble_gatt_svc_def ble_svc_gap_defs[] = {
48 {
49 /*** Service: GAP. */
50 .type = BLE_GATT_SVC_TYPE_PRIMARY,
51 .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_UUID16),
52 .characteristics = (struct ble_gatt_chr_def[])
53 { {
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 {
95 0, /* No more services. */
96 },
97 };
98
ble_svc_gap_device_name_read_access(struct ble_gatt_access_ctxt * ctxt)99 static int ble_svc_gap_device_name_read_access(struct ble_gatt_access_ctxt *ctxt)
100 {
101 int rc;
102 rc = os_mbuf_append(ctxt->om, ble_svc_gap_name, strlen(ble_svc_gap_name));
103 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
104 }
105
ble_svc_gap_device_name_write_access(struct ble_gatt_access_ctxt * ctxt)106 static int ble_svc_gap_device_name_write_access(struct ble_gatt_access_ctxt *ctxt)
107 {
108 #if MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM) < 0
109 assert(0);
110 return 0;
111 #else
112 uint16_t om_len;
113 int rc;
114 om_len = OS_MBUF_PKTLEN(ctxt->om);
115 if (om_len > BLE_SVC_GAP_NAME_MAX_LEN) {
116 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
117 }
118
119 rc = ble_hs_mbuf_to_flat(ctxt->om, ble_svc_gap_name, om_len, NULL);
120 if (rc != 0) {
121 return BLE_ATT_ERR_UNLIKELY;
122 }
123
124 ble_svc_gap_name[om_len] = '\0';
125
126 if (ble_svc_gap_chr_changed_cb_fn) {
127 ble_svc_gap_chr_changed_cb_fn(BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME);
128 }
129
130 return rc;
131 #endif
132 }
133
ble_svc_gap_appearance_read_access(struct ble_gatt_access_ctxt * ctxt)134 static int ble_svc_gap_appearance_read_access(struct ble_gatt_access_ctxt *ctxt)
135 {
136 uint16_t appearance = htole16(ble_svc_gap_appearance);
137 int rc;
138 rc = os_mbuf_append(ctxt->om, &appearance, sizeof(appearance));
139 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
140 }
141
ble_svc_gap_appearance_write_access(struct ble_gatt_access_ctxt * ctxt)142 static int ble_svc_gap_appearance_write_access(struct ble_gatt_access_ctxt *ctxt)
143 {
144 #if MYNEWT_VAL(BLE_SVC_GAP_APPEARANCE_WRITE_PERM) < 0
145 assert(0);
146 return 0;
147 #else
148 uint16_t om_len;
149 int rc;
150 om_len = OS_MBUF_PKTLEN(ctxt->om);
151 if (om_len != sizeof(ble_svc_gap_appearance)) {
152 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
153 }
154
155 rc = ble_hs_mbuf_to_flat(ctxt->om, &ble_svc_gap_appearance, om_len, NULL);
156 if (rc != 0) {
157 return BLE_ATT_ERR_UNLIKELY;
158 }
159
160 ble_svc_gap_appearance = le16toh(ble_svc_gap_appearance);
161
162 if (ble_svc_gap_chr_changed_cb_fn) {
163 ble_svc_gap_chr_changed_cb_fn(BLE_SVC_GAP_CHR_UUID16_APPEARANCE);
164 }
165
166 return rc;
167 #endif
168 }
169
ble_svc_gap_access(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)170 static int ble_svc_gap_access(uint16_t conn_handle, uint16_t attr_handle,
171 struct ble_gatt_access_ctxt *ctxt, void *arg)
172 {
173 uint16_t uuid16;
174 #if MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION) >= 0
175 uint8_t central_ar = MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION);
176 #endif
177 #if PPCP_ENABLED
178 uint16_t ppcp[4] = {
179 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL)),
180 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL)),
181 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_SLAVE_LATENCY)),
182 htole16(MYNEWT_VAL(BLE_SVC_GAP_PPCP_SUPERVISION_TMO))
183 };
184 #endif
185 int rc;
186 uuid16 = ble_uuid_u16(ctxt->chr->uuid);
187 assert(uuid16 != 0);
188
189 switch (uuid16) {
190 case BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME:
191 if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
192 rc = ble_svc_gap_device_name_read_access(ctxt);
193 } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
194 rc = ble_svc_gap_device_name_write_access(ctxt);
195 } else {
196 assert(0);
197 rc = BLE_ATT_ERR_UNLIKELY;
198 }
199
200 return rc;
201
202 case BLE_SVC_GAP_CHR_UUID16_APPEARANCE:
203 if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
204 rc = ble_svc_gap_appearance_read_access(ctxt);
205 } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
206 rc = ble_svc_gap_appearance_write_access(ctxt);
207 } else {
208 assert(0);
209 rc = BLE_ATT_ERR_UNLIKELY;
210 }
211
212 return rc;
213 #if PPCP_ENABLED
214
215 case BLE_SVC_GAP_CHR_UUID16_PERIPH_PREF_CONN_PARAMS:
216 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
217 rc = os_mbuf_append(ctxt->om, &ppcp, sizeof(ppcp));
218 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
219 #endif
220 #if MYNEWT_VAL(BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION) >= 0
221
222 case BLE_SVC_GAP_CHR_UUID16_CENTRAL_ADDRESS_RESOLUTION:
223 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
224 rc = os_mbuf_append(ctxt->om, ¢ral_ar, sizeof(central_ar));
225 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
226 #endif
227
228 default:
229 assert(0);
230 return BLE_ATT_ERR_UNLIKELY;
231 }
232 }
233
ble_svc_gap_device_name(void)234 const char *ble_svc_gap_device_name(void)
235 {
236 return ble_svc_gap_name;
237 }
238
ble_svc_gap_device_name_set(const char * name)239 int ble_svc_gap_device_name_set(const char *name)
240 {
241 int len;
242 len = strlen(name);
243 if (len > BLE_SVC_GAP_NAME_MAX_LEN) {
244 return BLE_HS_EINVAL;
245 }
246
247 memcpy(ble_svc_gap_name, name, len);
248 ble_svc_gap_name[len] = '\0';
249 return 0;
250 }
251
ble_svc_gap_device_appearance(void)252 uint16_t ble_svc_gap_device_appearance(void)
253 {
254 return ble_svc_gap_appearance;
255 }
256
ble_svc_gap_device_appearance_set(uint16_t appearance)257 int ble_svc_gap_device_appearance_set(uint16_t appearance)
258 {
259 ble_svc_gap_appearance = appearance;
260 return 0;
261 }
262
ble_svc_gap_set_chr_changed_cb(ble_svc_gap_chr_changed_fn * cb)263 void ble_svc_gap_set_chr_changed_cb(ble_svc_gap_chr_changed_fn *cb)
264 {
265 ble_svc_gap_chr_changed_cb_fn = cb;
266 }
267
ble_svc_gap_init(void)268 void ble_svc_gap_init(void)
269 {
270 int rc;
271 /* Ensure this function only gets called by sysinit. */
272 SYSINIT_ASSERT_ACTIVE();
273 rc = ble_gatts_count_cfg(ble_svc_gap_defs);
274 SYSINIT_PANIC_ASSERT(rc == 0);
275 rc = ble_gatts_add_svcs(ble_svc_gap_defs);
276 SYSINIT_PANIC_ASSERT(rc == 0);
277 }