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 #include <stdio.h>
23 #include <errno.h>
24
25 #include <rtthread.h>
26
27 /* BLE */
28 #include "nimble/ble.h"
29 #include "host/ble_hs.h"
30 #include "host/util/util.h"
31 #include "services/gap/ble_svc_gap.h"
32
33 /* Application-specified header. */
34 #include "bleprph.h"
35
36 static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
37
38 /**
39 * Logs information about a connection to the console.
40 */
41 static void
bleprph_print_conn_desc(struct ble_gap_conn_desc * desc)42 bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
43 {
44 MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
45 desc->conn_handle, desc->our_ota_addr.type);
46 print_addr(desc->our_ota_addr.val);
47 MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
48 desc->our_id_addr.type);
49 print_addr(desc->our_id_addr.val);
50 MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
51 desc->peer_ota_addr.type);
52 print_addr(desc->peer_ota_addr.val);
53 MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
54 desc->peer_id_addr.type);
55 print_addr(desc->peer_id_addr.val);
56 MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
57 "encrypted=%d authenticated=%d bonded=%d\n",
58 desc->conn_itvl, desc->conn_latency,
59 desc->supervision_timeout,
60 desc->sec_state.encrypted,
61 desc->sec_state.authenticated,
62 desc->sec_state.bonded);
63 }
64
65 /**
66 * Enables advertising with the following parameters:
67 * o General discoverable mode.
68 * o Undirected connectable mode.
69 */
70 static void
bleprph_advertise(void)71 bleprph_advertise(void)
72 {
73 uint8_t own_addr_type;
74 struct ble_gap_adv_params adv_params;
75 struct ble_hs_adv_fields fields;
76 const char *name;
77 int rc;
78
79 /* Figure out address to use while advertising (no privacy for now) */
80 rc = ble_hs_id_infer_auto(0, &own_addr_type);
81 if (rc != 0) {
82 MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
83 return;
84 }
85
86 /**
87 * Set the advertisement data included in our advertisements:
88 * o Flags (indicates advertisement type and other general info).
89 * o Advertising tx power.
90 * o Device name.
91 * o 16-bit service UUIDs (alert notifications).
92 */
93
94 memset(&fields, 0, sizeof fields);
95
96 /* Advertise two flags:
97 * o Discoverability in forthcoming advertisement (general)
98 * o BLE-only (BR/EDR unsupported).
99 */
100 fields.flags = BLE_HS_ADV_F_DISC_GEN |
101 BLE_HS_ADV_F_BREDR_UNSUP;
102
103 /* Indicate that the TX power level field should be included; have the
104 * stack fill this value automatically. This is done by assiging the
105 * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
106 */
107 fields.tx_pwr_lvl_is_present = 1;
108 fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
109
110 name = ble_svc_gap_device_name();
111 fields.name = (uint8_t *)name;
112 fields.name_len = strlen(name);
113 fields.name_is_complete = 1;
114
115 fields.uuids16 = (ble_uuid16_t[]){
116 BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
117 };
118 fields.num_uuids16 = 1;
119 fields.uuids16_is_complete = 1;
120
121 rc = ble_gap_adv_set_fields(&fields);
122 if (rc != 0) {
123 MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
124 return;
125 }
126
127 /* Begin advertising. */
128 memset(&adv_params, 0, sizeof adv_params);
129 adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
130 adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
131 rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
132 &adv_params, bleprph_gap_event, NULL);
133 if (rc != 0) {
134 MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
135 return;
136 }
137 }
138
139 /**
140 * The nimble host executes this callback when a GAP event occurs. The
141 * application associates a GAP event callback with each connection that forms.
142 * bleprph uses the same callback for all connections.
143 *
144 * @param event The type of event being signalled.
145 * @param ctxt Various information pertaining to the event.
146 * @param arg Application-specified argument; unuesd by
147 * bleprph.
148 *
149 * @return 0 if the application successfully handled the
150 * event; nonzero on failure. The semantics
151 * of the return code is specific to the
152 * particular GAP event being signalled.
153 */
154 static int
bleprph_gap_event(struct ble_gap_event * event,void * arg)155 bleprph_gap_event(struct ble_gap_event *event, void *arg)
156 {
157 struct ble_gap_conn_desc desc;
158 int rc;
159
160 switch (event->type) {
161 case BLE_GAP_EVENT_CONNECT:
162 /* A new connection was established or a connection attempt failed. */
163 MODLOG_DFLT(INFO, "connection %s; status=%d ",
164 event->connect.status == 0 ? "established" : "failed",
165 event->connect.status);
166 if (event->connect.status == 0) {
167 rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
168 assert(rc == 0);
169 bleprph_print_conn_desc(&desc);
170
171 #if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
172 phy_conn_changed(event->connect.conn_handle);
173 #endif
174 }
175 MODLOG_DFLT(INFO, "\n");
176
177 if (event->connect.status != 0) {
178 /* Connection failed; resume advertising. */
179 bleprph_advertise();
180 }
181 return 0;
182
183 case BLE_GAP_EVENT_DISCONNECT:
184 MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
185 bleprph_print_conn_desc(&event->disconnect.conn);
186 MODLOG_DFLT(INFO, "\n");
187
188 #if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
189 phy_conn_changed(CONN_HANDLE_INVALID);
190 #endif
191
192 /* Connection terminated; resume advertising. */
193 bleprph_advertise();
194 return 0;
195
196 case BLE_GAP_EVENT_CONN_UPDATE:
197 /* The central has updated the connection parameters. */
198 MODLOG_DFLT(INFO, "connection updated; status=%d ",
199 event->conn_update.status);
200 rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
201 assert(rc == 0);
202 bleprph_print_conn_desc(&desc);
203 MODLOG_DFLT(INFO, "\n");
204 return 0;
205
206 case BLE_GAP_EVENT_ADV_COMPLETE:
207 MODLOG_DFLT(INFO, "advertise complete; reason=%d",
208 event->adv_complete.reason);
209 bleprph_advertise();
210 return 0;
211
212 case BLE_GAP_EVENT_ENC_CHANGE:
213 /* Encryption has been enabled or disabled for this connection. */
214 MODLOG_DFLT(INFO, "encryption change event; status=%d ",
215 event->enc_change.status);
216 rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
217 assert(rc == 0);
218 bleprph_print_conn_desc(&desc);
219 MODLOG_DFLT(INFO, "\n");
220 return 0;
221
222 case BLE_GAP_EVENT_SUBSCRIBE:
223 MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
224 "reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
225 event->subscribe.conn_handle,
226 event->subscribe.attr_handle,
227 event->subscribe.reason,
228 event->subscribe.prev_notify,
229 event->subscribe.cur_notify,
230 event->subscribe.prev_indicate,
231 event->subscribe.cur_indicate);
232 return 0;
233
234 case BLE_GAP_EVENT_MTU:
235 MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
236 event->mtu.conn_handle,
237 event->mtu.channel_id,
238 event->mtu.value);
239 return 0;
240
241 case BLE_GAP_EVENT_REPEAT_PAIRING:
242 /* We already have a bond with the peer, but it is attempting to
243 * establish a new secure link. This app sacrifices security for
244 * convenience: just throw away the old bond and accept the new link.
245 */
246
247 /* Delete the old bond. */
248 rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
249 assert(rc == 0);
250 ble_store_util_delete_peer(&desc.peer_id_addr);
251
252 /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
253 * continue with the pairing operation.
254 */
255 return BLE_GAP_REPEAT_PAIRING_RETRY;
256
257 #if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
258 case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
259 /* XXX: assume symmetric phy for now */
260 phy_update(event->phy_updated.tx_phy);
261 return 0;
262 #endif
263 }
264
265 return 0;
266 }
267
268 static void
bleprph_on_reset(int reason)269 bleprph_on_reset(int reason)
270 {
271 MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
272 }
273
274 static void
bleprph_on_sync(void)275 bleprph_on_sync(void)
276 {
277 int rc;
278
279 /* Make sure we have proper identity address set (public preferred) */
280 rc = ble_hs_util_ensure_addr(0);
281 assert(rc == 0);
282
283 /* Begin advertising. */
284 bleprph_advertise();
285 }
286
287 /**
288 * main
289 *
290 * The main task for the project. This function initializes the packages,
291 * then starts serving events from default event queue.
292 *
293 * @return int NOTE: this function should never return!
294 */
bleprph_entry(void)295 int bleprph_entry(void)
296 {
297 int rc;
298
299 /* Initialize the NimBLE host configuration. */
300 ble_hs_cfg.reset_cb = bleprph_on_reset;
301 ble_hs_cfg.sync_cb = bleprph_on_sync;
302 ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
303 ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
304
305 rc = gatt_svr_init();
306 assert(rc == 0);
307
308 /* Set the default device name. */
309 rc = ble_svc_gap_device_name_set("nimble-bleprph");
310 assert(rc == 0);
311
312 #if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
313 phy_init();
314 #endif
315
316 /* startup bluetooth host stack*/
317 ble_hs_thread_startup();
318 }
319
320 MSH_CMD_EXPORT_ALIAS(bleprph_entry, bleprph, "bluetooth peripheral role sample");
321