• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &central_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 }