blecent.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  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. #include <assert.h>
  20. #include <string.h>
  21. #include <rtthread.h>
  22. /* BLE */
  23. #include "nimble/ble.h"
  24. #include "host/ble_hs.h"
  25. #include "host/util/util.h"
  26. /* Mandatory services. */
  27. #include "services/gap/ble_svc_gap.h"
  28. #include "services/gatt/ble_svc_gatt.h"
  29. /* Application-specified header. */
  30. #include "blecent.h"
  31. static int blecent_gap_event(struct ble_gap_event *event, void *arg);
  32. /**
  33. * Application callback. Called when the read of the ANS Supported New Alert
  34. * Category characteristic has completed.
  35. */
  36. static int
  37. blecent_on_read(uint16_t conn_handle,
  38. const struct ble_gatt_error *error,
  39. struct ble_gatt_attr *attr,
  40. void *arg)
  41. {
  42. MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
  43. conn_handle);
  44. if (error->status == 0) {
  45. MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
  46. print_mbuf(attr->om);
  47. }
  48. MODLOG_DFLT(INFO, "\n");
  49. return 0;
  50. }
  51. /**
  52. * Application callback. Called when the write to the ANS Alert Notification
  53. * Control Point characteristic has completed.
  54. */
  55. static int
  56. blecent_on_write(uint16_t conn_handle,
  57. const struct ble_gatt_error *error,
  58. struct ble_gatt_attr *attr,
  59. void *arg)
  60. {
  61. MODLOG_DFLT(INFO,
  62. "Write complete; status=%d conn_handle=%d attr_handle=%d\n",
  63. error->status, conn_handle, attr->handle);
  64. return 0;
  65. }
  66. /**
  67. * Application callback. Called when the attempt to subscribe to notifications
  68. * for the ANS Unread Alert Status characteristic has completed.
  69. */
  70. static int
  71. blecent_on_subscribe(uint16_t conn_handle,
  72. const struct ble_gatt_error *error,
  73. struct ble_gatt_attr *attr,
  74. void *arg)
  75. {
  76. MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d "
  77. "attr_handle=%d\n",
  78. error->status, conn_handle, attr->handle);
  79. return 0;
  80. }
  81. /**
  82. * Performs three concurrent GATT operations against the specified peer:
  83. * 1. Reads the ANS Supported New Alert Category characteristic.
  84. * 2. Writes the ANS Alert Notification Control Point characteristic.
  85. * 3. Subscribes to notifications for the ANS Unread Alert Status
  86. * characteristic.
  87. *
  88. * If the peer does not support a required service, characteristic, or
  89. * descriptor, then the peer lied when it claimed support for the alert
  90. * notification service! When this happens, or if a GATT procedure fails,
  91. * this function immediately terminates the connection.
  92. */
  93. static void
  94. blecent_read_write_subscribe(const struct peer *peer)
  95. {
  96. const struct peer_chr *chr;
  97. const struct peer_dsc *dsc;
  98. uint8_t value[2];
  99. int rc;
  100. /* Read the supported-new-alert-category characteristic. */
  101. chr = peer_chr_find_uuid(peer,
  102. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  103. BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID));
  104. if (chr == NULL) {
  105. MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New "
  106. "Alert Category characteristic\n");
  107. goto err;
  108. }
  109. rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
  110. blecent_on_read, NULL);
  111. if (rc != 0) {
  112. MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
  113. rc);
  114. goto err;
  115. }
  116. /* Write two bytes (99, 100) to the alert-notification-control-point
  117. * characteristic.
  118. */
  119. chr = peer_chr_find_uuid(peer,
  120. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  121. BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
  122. if (chr == NULL) {
  123. MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert "
  124. "Notification Control Point characteristic\n");
  125. goto err;
  126. }
  127. value[0] = 99;
  128. value[1] = 100;
  129. rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle,
  130. value, sizeof value, blecent_on_write, NULL);
  131. if (rc != 0) {
  132. MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
  133. rc);
  134. }
  135. /* Subscribe to notifications for the Unread Alert Status characteristic.
  136. * A central enables notifications by writing two bytes (1, 0) to the
  137. * characteristic's client-characteristic-configuration-descriptor (CCCD).
  138. */
  139. dsc = peer_dsc_find_uuid(peer,
  140. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  141. BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
  142. BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
  143. if (dsc == NULL) {
  144. MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert "
  145. "Status characteristic\n");
  146. goto err;
  147. }
  148. value[0] = 1;
  149. value[1] = 0;
  150. rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
  151. value, sizeof value, blecent_on_subscribe, NULL);
  152. if (rc != 0) {
  153. MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; "
  154. "rc=%d\n", rc);
  155. goto err;
  156. }
  157. return;
  158. err:
  159. /* Terminate the connection. */
  160. ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  161. }
  162. /**
  163. * Called when service discovery of the specified peer has completed.
  164. */
  165. static void
  166. blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
  167. {
  168. if (status != 0) {
  169. /* Service discovery failed. Terminate the connection. */
  170. MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d "
  171. "conn_handle=%d\n", status, peer->conn_handle);
  172. ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  173. return;
  174. }
  175. /* Service discovery has completed successfully. Now we have a complete
  176. * list of services, characteristics, and descriptors that the peer
  177. * supports.
  178. */
  179. MODLOG_DFLT(INFO, "Service discovery complete; status=%d "
  180. "conn_handle=%d\n", status, peer->conn_handle);
  181. /* Now perform three concurrent GATT procedures against the peer: read,
  182. * write, and subscribe to notifications.
  183. */
  184. blecent_read_write_subscribe(peer);
  185. }
  186. /**
  187. * Initiates the GAP general discovery procedure.
  188. */
  189. static void
  190. blecent_scan(void)
  191. {
  192. uint8_t own_addr_type;
  193. struct ble_gap_disc_params disc_params;
  194. int rc;
  195. /* Figure out address to use while advertising (no privacy for now) */
  196. rc = ble_hs_id_infer_auto(0, &own_addr_type);
  197. if (rc != 0) {
  198. MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
  199. return;
  200. }
  201. /* Tell the controller to filter duplicates; we don't want to process
  202. * repeated advertisements from the same device.
  203. */
  204. disc_params.filter_duplicates = 1;
  205. /**
  206. * Perform a passive scan. I.e., don't send follow-up scan requests to
  207. * each advertiser.
  208. */
  209. disc_params.passive = 1;
  210. /* Use defaults for the rest of the parameters. */
  211. disc_params.itvl = 0;
  212. disc_params.window = 0;
  213. disc_params.filter_policy = 0;
  214. disc_params.limited = 0;
  215. rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
  216. blecent_gap_event, NULL);
  217. if (rc != 0) {
  218. MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
  219. rc);
  220. }
  221. }
  222. /**
  223. * Indicates whether we should tre to connect to the sender of the specified
  224. * advertisement. The function returns a positive result if the device
  225. * advertises connectability and support for the Alert Notification service.
  226. */
  227. static int
  228. blecent_should_connect(const struct ble_gap_disc_desc *disc)
  229. {
  230. struct ble_hs_adv_fields fields;
  231. int rc;
  232. int i;
  233. /* The device has to be advertising connectability. */
  234. if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
  235. disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
  236. return 0;
  237. }
  238. rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
  239. if (rc != 0) {
  240. return rc;
  241. }
  242. /* The device has to advertise support for the Alert Notification
  243. * service (0x1811).
  244. */
  245. for (i = 0; i < fields.num_uuids16; i++) {
  246. if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) {
  247. return 1;
  248. }
  249. }
  250. return 0;
  251. }
  252. /**
  253. * Connects to the sender of the specified advertisement of it looks
  254. * interesting. A device is "interesting" if it advertises connectability and
  255. * support for the Alert Notification service.
  256. */
  257. static void
  258. blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc)
  259. {
  260. int rc;
  261. /* Don't do anything if we don't care about this advertiser. */
  262. if (!blecent_should_connect(disc)) {
  263. return;
  264. }
  265. /* Scanning must be stopped before a connection can be initiated. */
  266. rc = ble_gap_disc_cancel();
  267. if (rc != 0) {
  268. MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
  269. return;
  270. }
  271. /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
  272. * timeout.
  273. */
  274. rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &disc->addr, 30000, NULL,
  275. blecent_gap_event, NULL);
  276. if (rc != 0) {
  277. MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
  278. "addr=%s\n",
  279. disc->addr.type, addr_str(disc->addr.val));
  280. return;
  281. }
  282. }
  283. /**
  284. * The nimble host executes this callback when a GAP event occurs. The
  285. * application associates a GAP event callback with each connection that is
  286. * established. blecent uses the same callback for all connections.
  287. *
  288. * @param event The event being signalled.
  289. * @param arg Application-specified argument; unused by
  290. * blecent.
  291. *
  292. * @return 0 if the application successfully handled the
  293. * event; nonzero on failure. The semantics
  294. * of the return code is specific to the
  295. * particular GAP event being signalled.
  296. */
  297. static int
  298. blecent_gap_event(struct ble_gap_event *event, void *arg)
  299. {
  300. struct ble_gap_conn_desc desc;
  301. struct ble_hs_adv_fields fields;
  302. int rc;
  303. switch (event->type) {
  304. case BLE_GAP_EVENT_DISC:
  305. rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
  306. event->disc.length_data);
  307. if (rc != 0) {
  308. return 0;
  309. }
  310. /* An advertisment report was received during GAP discovery. */
  311. print_adv_fields(&fields);
  312. /* Try to connect to the advertiser if it looks interesting. */
  313. blecent_connect_if_interesting(&event->disc);
  314. return 0;
  315. case BLE_GAP_EVENT_CONNECT:
  316. /* A new connection was established or a connection attempt failed. */
  317. if (event->connect.status == 0) {
  318. /* Connection successfully established. */
  319. MODLOG_DFLT(INFO, "Connection established ");
  320. rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
  321. assert(rc == 0);
  322. print_conn_desc(&desc);
  323. MODLOG_DFLT(INFO, "\n");
  324. /* Remember peer. */
  325. rc = peer_add(event->connect.conn_handle);
  326. if (rc != 0) {
  327. MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
  328. return 0;
  329. }
  330. /* Perform service discovery. */
  331. rc = peer_disc_all(event->connect.conn_handle,
  332. blecent_on_disc_complete, NULL);
  333. if (rc != 0) {
  334. MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
  335. return 0;
  336. }
  337. } else {
  338. /* Connection attempt failed; resume scanning. */
  339. MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
  340. event->connect.status);
  341. blecent_scan();
  342. }
  343. return 0;
  344. case BLE_GAP_EVENT_DISCONNECT:
  345. /* Connection terminated. */
  346. MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
  347. print_conn_desc(&event->disconnect.conn);
  348. MODLOG_DFLT(INFO, "\n");
  349. /* Forget about peer. */
  350. peer_delete(event->disconnect.conn.conn_handle);
  351. /* Resume scanning. */
  352. blecent_scan();
  353. return 0;
  354. case BLE_GAP_EVENT_DISC_COMPLETE:
  355. MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
  356. event->disc_complete.reason);
  357. return 0;
  358. case BLE_GAP_EVENT_ENC_CHANGE:
  359. /* Encryption has been enabled or disabled for this connection. */
  360. MODLOG_DFLT(INFO, "encryption change event; status=%d ",
  361. event->enc_change.status);
  362. rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
  363. assert(rc == 0);
  364. print_conn_desc(&desc);
  365. return 0;
  366. case BLE_GAP_EVENT_NOTIFY_RX:
  367. /* Peer sent us a notification or indication. */
  368. MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
  369. "attr_len=%d\n",
  370. event->notify_rx.indication ?
  371. "indication" :
  372. "notification",
  373. event->notify_rx.conn_handle,
  374. event->notify_rx.attr_handle,
  375. OS_MBUF_PKTLEN(event->notify_rx.om));
  376. /* Attribute data is contained in event->notify_rx.attr_data. */
  377. return 0;
  378. case BLE_GAP_EVENT_MTU:
  379. MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
  380. event->mtu.conn_handle,
  381. event->mtu.channel_id,
  382. event->mtu.value);
  383. return 0;
  384. case BLE_GAP_EVENT_REPEAT_PAIRING:
  385. /* We already have a bond with the peer, but it is attempting to
  386. * establish a new secure link. This app sacrifices security for
  387. * convenience: just throw away the old bond and accept the new link.
  388. */
  389. /* Delete the old bond. */
  390. rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
  391. assert(rc == 0);
  392. ble_store_util_delete_peer(&desc.peer_id_addr);
  393. /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
  394. * continue with the pairing operation.
  395. */
  396. return BLE_GAP_REPEAT_PAIRING_RETRY;
  397. default:
  398. return 0;
  399. }
  400. }
  401. static void
  402. blecent_on_reset(int reason)
  403. {
  404. MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
  405. }
  406. static void
  407. blecent_on_sync(void)
  408. {
  409. int rc;
  410. /* Make sure we have proper identity address set (public preferred) */
  411. rc = ble_hs_util_ensure_addr(0);
  412. assert(rc == 0);
  413. /* Begin scanning for a peripheral to connect to. */
  414. blecent_scan();
  415. }
  416. extern int nimble_ble_enable(void);
  417. /**
  418. * main
  419. *
  420. * All application logic and NimBLE host work is performed in default task.
  421. *
  422. * @return int NOTE: this function should never return!
  423. */
  424. int blecent_entry(void)
  425. {
  426. int rc;
  427. /* Configure the host. */
  428. ble_hs_cfg.reset_cb = blecent_on_reset;
  429. ble_hs_cfg.sync_cb = blecent_on_sync;
  430. ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
  431. /* Initialize data structures to track connected peers. */
  432. rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
  433. assert(rc == 0);
  434. /* Set the default device name. */
  435. rc = ble_svc_gap_device_name_set("nimble-blecent");
  436. assert(rc == 0);
  437. /* startup bluetooth host stack*/
  438. ble_hs_thread_startup();
  439. return 0;
  440. }
  441. MSH_CMD_EXPORT_ALIAS(blecent_entry, blecent, "bluetooth centrol role sample");