From b5e57da5a0ca2c63328bf1235bcfb3fd9a6ac79c Mon Sep 17 00:00:00 2001 From: technyon Date: Tue, 22 Mar 2022 21:10:43 +0100 Subject: [PATCH] initial commit --- .gitignore | 4 + .gitmodules | 3 + CMakeLists.txt | 66 + LICENSE | 21 + Network.cpp | 95 + Network.h | 22 + Nuki.cpp | 35 + Nuki.h | 18 + lib/Crc16/Crc16.h | 243 + lib/Crc16/LICENSE | 20 + .../examples/Crc16_Example/Crc16_Example.ino | 85 + lib/Crc16/keywords.txt | 22 + lib/Crc16/readme.md | 19 + lib/ESP32_BLE_Arduino-1.0.1/README.md | 15 + .../examples/BLE_client/BLE_client.ino | 160 + .../examples/BLE_iBeacon/BLE_iBeacon.ino | 103 + .../examples/BLE_notify/BLE_notify.ino | 110 + .../examples/BLE_scan/BLE_scan.ino | 40 + .../examples/BLE_server/BLE_server.ino | 45 + .../BLE_server_multiconnect.ino | 111 + .../examples/BLE_uart/BLE_uart.ino | 125 + .../examples/BLE_write/BLE_write.ino | 65 + .../library.properties | 10 + lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.cpp | 62 + lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.h | 34 + lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.cpp | 74 + lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.h | 74 + .../src/BLEAddress.cpp | 95 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.h | 34 + .../src/BLEAdvertisedDevice.cpp | 529 ++ .../src/BLEAdvertisedDevice.h | 123 + .../src/BLEAdvertising.cpp | 505 ++ .../src/BLEAdvertising.h | 78 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.cpp | 89 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.h | 43 + .../src/BLECharacteristic.cpp | 760 ++ .../src/BLECharacteristic.h | 137 + .../src/BLECharacteristicMap.cpp | 133 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.cpp | 536 ++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.h | 103 + .../src/BLEDescriptor.cpp | 296 + .../src/BLEDescriptor.h | 77 + .../src/BLEDescriptorMap.cpp | 147 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.cpp | 646 ++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.h | 99 + .../src/BLEEddystoneTLM.cpp | 150 + .../src/BLEEddystoneTLM.h | 51 + .../src/BLEEddystoneURL.cpp | 148 + .../src/BLEEddystoneURL.h | 43 + .../src/BLEExceptions.cpp | 9 + .../src/BLEExceptions.h | 31 + .../src/BLEHIDDevice.cpp | 243 + .../src/BLEHIDDevice.h | 75 + .../src/BLERemoteCharacteristic.cpp | 588 ++ .../src/BLERemoteCharacteristic.h | 84 + .../src/BLERemoteDescriptor.cpp | 181 + .../src/BLERemoteDescriptor.h | 55 + .../src/BLERemoteService.cpp | 340 + .../src/BLERemoteService.h | 85 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.cpp | 331 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.h | 83 + .../src/BLESecurity.cpp | 104 + lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.h | 72 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.cpp | 424 ++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.h | 140 + .../src/BLEService.cpp | 418 ++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.h | 97 + .../src/BLEServiceMap.cpp | 134 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.cpp | 407 ++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.h | 39 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.cpp | 2033 ++++++ lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.h | 63 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.cpp | 139 + lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.h | 39 + lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.cpp | 274 + lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.h | 71 + .../src/GeneralUtils.cpp | 544 ++ .../src/GeneralUtils.h | 35 + .../src/HIDKeyboardTypes.h | 402 + lib/ESP32_BLE_Arduino-1.0.1/src/HIDTypes.h | 96 + lib/NimBLE-Arduino/CHANGELOG.md | 257 + lib/NimBLE-Arduino/LICENSE | 219 + lib/NimBLE-Arduino/README.md | 85 + .../docs/Command_line_config.md | 142 + .../docs/Improvements_and_updates.md | 148 + lib/NimBLE-Arduino/docs/Migration_guide.md | 399 + lib/NimBLE-Arduino/docs/New_user_guide.md | 339 + lib/NimBLE-Arduino/docs/Usage_tips.md | 41 + .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino | 153 + .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.md | 9 + .../BLE_EddystoneTLM_Beacon.ino | 113 + .../BLE_EddystoneTLM_Beacon.md | 14 + .../BLE_EddystoneURL_Beacon.ino | 185 + .../BLE_EddystoneURL_Beacon.md | 14 + .../examples/NimBLE_Client/NimBLE_Client.ino | 392 + .../NimBLE_Scan_Continuous.ino | 71 + .../NimBLE_Scan_whitelist.ino | 67 + .../NimBLE_Secure_Client.ino | 91 + .../NimBLE_Secure_Server.ino | 37 + .../examples/NimBLE_Server/NimBLE_Server.ino | 256 + .../NimBLE_Server_Whitelist.ino | 105 + .../NimBLE_Service_Data_Advertiser.ino | 33 + .../BLE_client/BLE_client.ino | 194 + .../BLE_iBeacon/BLE_iBeacon.ino | 118 + .../BLE_notify/BLE_notify.ino | 146 + .../BLE_scan/BLE_scan.ino | 49 + .../BLE_server/BLE_server.ino | 57 + .../BLE_server_multiconnect.ino | 150 + .../BLE_uart/BLE_uart.ino | 163 + .../BLE_write/BLE_write.ino | 75 + lib/NimBLE-Arduino/library.properties | 10 + lib/NimBLE-Arduino/src/HIDKeyboardTypes.h | 402 + lib/NimBLE-Arduino/src/HIDTypes.h | 91 + lib/NimBLE-Arduino/src/NimBLE2904.cpp | 86 + lib/NimBLE-Arduino/src/NimBLE2904.h | 83 + lib/NimBLE-Arduino/src/NimBLEAddress.cpp | 197 + lib/NimBLE-Arduino/src/NimBLEAddress.h | 62 + .../src/NimBLEAdvertisedDevice.cpp | 785 ++ .../src/NimBLEAdvertisedDevice.h | 177 + lib/NimBLE-Arduino/src/NimBLEAdvertising.cpp | 1029 +++ lib/NimBLE-Arduino/src/NimBLEAdvertising.h | 138 + lib/NimBLE-Arduino/src/NimBLEAttValue.h | 447 ++ lib/NimBLE-Arduino/src/NimBLEBeacon.cpp | 157 + lib/NimBLE-Arduino/src/NimBLEBeacon.h | 51 + .../src/NimBLECharacteristic.cpp | 659 ++ lib/NimBLE-Arduino/src/NimBLECharacteristic.h | 231 + lib/NimBLE-Arduino/src/NimBLEClient.cpp | 1247 ++++ lib/NimBLE-Arduino/src/NimBLEClient.h | 163 + lib/NimBLE-Arduino/src/NimBLEConnInfo.h | 55 + lib/NimBLE-Arduino/src/NimBLEDescriptor.cpp | 299 + lib/NimBLE-Arduino/src/NimBLEDescriptor.h | 119 + lib/NimBLE-Arduino/src/NimBLEDevice.cpp | 1159 +++ lib/NimBLE-Arduino/src/NimBLEDevice.h | 218 + lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp | 227 + lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.h | 61 + lib/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp | 204 + lib/NimBLE-Arduino/src/NimBLEEddystoneURL.h | 52 + lib/NimBLE-Arduino/src/NimBLEHIDDevice.cpp | 248 + lib/NimBLE-Arduino/src/NimBLEHIDDevice.h | 86 + lib/NimBLE-Arduino/src/NimBLELog.h | 80 + .../src/NimBLERemoteCharacteristic.cpp | 865 +++ .../src/NimBLERemoteCharacteristic.h | 190 + .../src/NimBLERemoteDescriptor.cpp | 365 + .../src/NimBLERemoteDescriptor.h | 108 + .../src/NimBLERemoteService.cpp | 411 ++ lib/NimBLE-Arduino/src/NimBLERemoteService.h | 85 + lib/NimBLE-Arduino/src/NimBLEScan.cpp | 541 ++ lib/NimBLE-Arduino/src/NimBLEScan.h | 103 + lib/NimBLE-Arduino/src/NimBLESecurity.cpp | 158 + lib/NimBLE-Arduino/src/NimBLESecurity.h | 131 + lib/NimBLE-Arduino/src/NimBLEServer.cpp | 867 +++ lib/NimBLE-Arduino/src/NimBLEServer.h | 172 + lib/NimBLE-Arduino/src/NimBLEService.cpp | 435 ++ lib/NimBLE-Arduino/src/NimBLEService.h | 87 + lib/NimBLE-Arduino/src/NimBLEUUID.cpp | 360 + lib/NimBLE-Arduino/src/NimBLEUUID.h | 64 + lib/NimBLE-Arduino/src/NimBLEUtils.cpp | 518 ++ lib/NimBLE-Arduino/src/NimBLEUtils.h | 51 + .../src/nimble/CODING_STANDARDS.md | 267 + lib/NimBLE-Arduino/src/nimble/LICENSE | 217 + lib/NimBLE-Arduino/src/nimble/NOTICE | 9 + lib/NimBLE-Arduino/src/nimble/README.md | 169 + .../src/nimble/RELEASE_NOTES.md | 33 + .../src/nimble/console/console.h | 16 + .../esp_port/esp-hci/include/esp_compiler.h | 59 + .../esp_port/esp-hci/include/esp_nimble_hci.h | 125 + .../esp_port/esp-hci/src/esp_nimble_hci.c | 583 ++ .../esp_port/port/include/esp_nimble_cfg.h | 1385 ++++ .../esp_port/port/include/esp_nimble_mem.h | 24 + .../nimble/esp_port/port/src/esp_nimble_mem.c | 44 + .../src/nimble/ext/tinycrypt/AUTHORS | 15 + .../src/nimble/ext/tinycrypt/LICENSE | 61 + .../src/nimble/ext/tinycrypt/README | 71 + .../src/nimble/ext/tinycrypt/VERSION | 1 + .../ext/tinycrypt/documentation/tinycrypt.rst | 352 + .../ext/tinycrypt/include/tinycrypt/aes.h | 130 + .../tinycrypt/include/tinycrypt/cbc_mode.h | 151 + .../tinycrypt/include/tinycrypt/ccm_mode.h | 211 + .../tinycrypt/include/tinycrypt/cmac_mode.h | 194 + .../tinycrypt/include/tinycrypt/constants.h | 61 + .../tinycrypt/include/tinycrypt/ctr_mode.h | 108 + .../tinycrypt/include/tinycrypt/ctr_prng.h | 166 + .../ext/tinycrypt/include/tinycrypt/ecc.h | 545 ++ .../ext/tinycrypt/include/tinycrypt/ecc_dh.h | 131 + .../ext/tinycrypt/include/tinycrypt/ecc_dsa.h | 139 + .../include/tinycrypt/ecc_platform_specific.h | 81 + .../ext/tinycrypt/include/tinycrypt/hmac.h | 139 + .../tinycrypt/include/tinycrypt/hmac_prng.h | 164 + .../ext/tinycrypt/include/tinycrypt/sha256.h | 129 + .../ext/tinycrypt/include/tinycrypt/utils.h | 95 + .../nimble/ext/tinycrypt/src/aes_decrypt.c | 164 + .../nimble/ext/tinycrypt/src/aes_encrypt.c | 191 + .../src/nimble/ext/tinycrypt/src/cbc_mode.c | 114 + .../src/nimble/ext/tinycrypt/src/ccm_mode.c | 266 + .../src/nimble/ext/tinycrypt/src/cmac_mode.c | 254 + .../src/nimble/ext/tinycrypt/src/ctr_mode.c | 85 + .../src/nimble/ext/tinycrypt/src/ctr_prng.c | 283 + .../src/nimble/ext/tinycrypt/src/ecc.c | 942 +++ .../src/nimble/ext/tinycrypt/src/ecc_dh.c | 200 + .../src/nimble/ext/tinycrypt/src/ecc_dsa.c | 295 + .../ext/tinycrypt/src/ecc_platform_specific.c | 105 + .../src/nimble/ext/tinycrypt/src/hmac.c | 148 + .../src/nimble/ext/tinycrypt/src/hmac_prng.c | 212 + .../src/nimble/ext/tinycrypt/src/sha256.c | 217 + .../src/nimble/ext/tinycrypt/src/utils.c | 74 + .../controller/include/controller/ble_hw.h | 116 + .../controller/include/controller/ble_ll.h | 584 ++ .../include/controller/ble_ll_adv.h | 209 + .../include/controller/ble_ll_conn.h | 425 ++ .../include/controller/ble_ll_ctrl.h | 313 + .../include/controller/ble_ll_hci.h | 75 + .../include/controller/ble_ll_resolv.h | 116 + .../include/controller/ble_ll_rfmgmt.h | 63 + .../include/controller/ble_ll_scan.h | 293 + .../include/controller/ble_ll_sched.h | 216 + .../include/controller/ble_ll_sync.h | 74 + .../include/controller/ble_ll_test.h | 35 + .../include/controller/ble_ll_trace.h | 96 + .../include/controller/ble_ll_utils.h | 29 + .../include/controller/ble_ll_whitelist.h | 52 + .../controller/include/controller/ble_phy.h | 242 + .../include/controller/ble_phy_trace.h | 96 + .../src/nimble/nimble/controller/src/ble_ll.c | 1723 +++++ .../nimble/nimble/controller/src/ble_ll_adv.c | 5143 +++++++++++++ .../nimble/controller/src/ble_ll_conn.c | 4279 +++++++++++ .../nimble/controller/src/ble_ll_conn_hci.c | 1898 +++++ .../nimble/controller/src/ble_ll_conn_priv.h | 226 + .../nimble/controller/src/ble_ll_ctrl.c | 2748 +++++++ .../nimble/nimble/controller/src/ble_ll_dtm.c | 728 ++ .../nimble/controller/src/ble_ll_dtm_priv.h | 40 + .../nimble/nimble/controller/src/ble_ll_hci.c | 1519 ++++ .../nimble/controller/src/ble_ll_hci_ev.c | 526 ++ .../nimble/controller/src/ble_ll_priv.h | 51 + .../nimble/controller/src/ble_ll_rand.c | 188 + .../nimble/controller/src/ble_ll_resolv.c | 755 ++ .../nimble/controller/src/ble_ll_rfmgmt.c | 349 + .../nimble/controller/src/ble_ll_scan.c | 3981 ++++++++++ .../nimble/controller/src/ble_ll_sched.c | 1838 +++++ .../nimble/controller/src/ble_ll_supp_cmd.c | 461 ++ .../nimble/controller/src/ble_ll_sync.c | 2248 ++++++ .../nimble/controller/src/ble_ll_trace.c | 56 + .../nimble/controller/src/ble_ll_utils.c | 303 + .../nimble/controller/src/ble_ll_whitelist.c | 297 + .../nimble/drivers/nrf51/include/ble/xcvr.h | 51 + .../nimble/nimble/drivers/nrf51/src/ble_hw.c | 491 ++ .../nimble/nimble/drivers/nrf51/src/ble_phy.c | 1527 ++++ .../nimble/drivers/nrf52/include/ble/xcvr.h | 55 + .../nimble/nimble/drivers/nrf52/src/ble_hw.c | 491 ++ .../nimble/nimble/drivers/nrf52/src/ble_phy.c | 2120 ++++++ .../nimble/drivers/nrf52/src/ble_phy_trace.c | 47 + .../nimble/nimble/host/include/host/ble_att.h | 194 + .../nimble/host/include/host/ble_eddystone.h | 117 + .../nimble/nimble/host/include/host/ble_gap.h | 2094 ++++++ .../nimble/host/include/host/ble_gatt.h | 902 +++ .../nimble/nimble/host/include/host/ble_hs.h | 392 + .../nimble/host/include/host/ble_hs_adv.h | 177 + .../nimble/host/include/host/ble_hs_hci.h | 98 + .../nimble/host/include/host/ble_hs_id.h | 132 + .../nimble/host/include/host/ble_hs_log.h | 52 + .../nimble/host/include/host/ble_hs_mbuf.h | 82 + .../nimble/host/include/host/ble_hs_pvcy.h | 73 + .../nimble/host/include/host/ble_hs_stop.h | 70 + .../nimble/host/include/host/ble_ibeacon.h | 34 + .../nimble/host/include/host/ble_l2cap.h | 266 + .../nimble/host/include/host/ble_monitor.h | 40 + .../nimble/nimble/host/include/host/ble_sm.h | 124 + .../nimble/host/include/host/ble_store.h | 304 + .../nimble/host/include/host/ble_uuid.h | 182 + .../nimble/host/mesh/include/mesh/access.h | 656 ++ .../nimble/host/mesh/include/mesh/cfg_cli.h | 234 + .../nimble/host/mesh/include/mesh/cfg_srv.h | 78 + .../nimble/host/mesh/include/mesh/glue.h | 513 ++ .../host/mesh/include/mesh/health_cli.h | 81 + .../host/mesh/include/mesh/health_srv.h | 100 + .../nimble/host/mesh/include/mesh/main.h | 441 ++ .../nimble/host/mesh/include/mesh/mesh.h | 26 + .../nimble/host/mesh/include/mesh/model_cli.h | 49 + .../nimble/host/mesh/include/mesh/model_srv.h | 67 + .../nimble/host/mesh/include/mesh/porting.h | 27 + .../nimble/host/mesh/include/mesh/proxy.h | 43 + .../nimble/host/mesh/include/mesh/slist.h | 468 ++ .../nimble/host/mesh/include/mesh/testing.h | 105 + .../src/nimble/nimble/host/mesh/src/access.c | 859 +++ .../src/nimble/nimble/host/mesh/src/access.h | 67 + .../src/nimble/nimble/host/mesh/src/adv.c | 450 ++ .../src/nimble/nimble/host/mesh/src/adv.h | 79 + .../src/nimble/nimble/host/mesh/src/atomic.h | 409 ++ .../src/nimble/nimble/host/mesh/src/beacon.c | 444 ++ .../src/nimble/nimble/host/mesh/src/beacon.h | 26 + .../src/nimble/nimble/host/mesh/src/cfg_cli.c | 1501 ++++ .../src/nimble/nimble/host/mesh/src/cfg_srv.c | 3622 +++++++++ .../src/nimble/nimble/host/mesh/src/crypto.c | 969 +++ .../src/nimble/nimble/host/mesh/src/crypto.h | 170 + .../nimble/nimble/host/mesh/src/foundation.h | 171 + .../src/nimble/nimble/host/mesh/src/friend.c | 1654 +++++ .../src/nimble/nimble/host/mesh/src/friend.h | 56 + .../src/nimble/nimble/host/mesh/src/glue.c | 895 +++ .../nimble/nimble/host/mesh/src/health_cli.c | 559 ++ .../nimble/nimble/host/mesh/src/health_srv.c | 456 ++ .../nimble/nimble/host/mesh/src/light_model.c | 59 + .../nimble/nimble/host/mesh/src/light_model.h | 19 + .../src/nimble/nimble/host/mesh/src/lpn.c | 1059 +++ .../src/nimble/nimble/host/mesh/src/lpn.h | 68 + .../src/nimble/nimble/host/mesh/src/mesh.c | 364 + .../nimble/nimble/host/mesh/src/mesh_priv.h | 39 + .../nimble/nimble/host/mesh/src/model_cli.c | 303 + .../nimble/nimble/host/mesh/src/model_srv.c | 269 + .../src/nimble/nimble/host/mesh/src/net.c | 1436 ++++ .../src/nimble/nimble/host/mesh/src/net.h | 412 ++ .../src/nimble/nimble/host/mesh/src/nodes.c | 164 + .../src/nimble/nimble/host/mesh/src/nodes.h | 10 + .../src/nimble/nimble/host/mesh/src/prov.c | 2011 +++++ .../src/nimble/nimble/host/mesh/src/prov.h | 37 + .../src/nimble/nimble/host/mesh/src/proxy.c | 1502 ++++ .../src/nimble/nimble/host/mesh/src/proxy.h | 45 + .../nimble/nimble/host/mesh/src/settings.c | 2086 ++++++ .../nimble/nimble/host/mesh/src/settings.h | 27 + .../src/nimble/nimble/host/mesh/src/shell.c | 2821 +++++++ .../src/nimble/nimble/host/mesh/src/shell.h | 6 + .../nimble/nimble/host/mesh/src/transport.c | 1671 +++++ .../nimble/nimble/host/mesh/src/transport.h | 105 + .../ans/include/services/ans/ble_svc_ans.h | 85 + .../host/services/ans/src/ble_svc_ans.c | 463 ++ .../bas/include/services/bas/ble_svc_bas.h | 31 + .../host/services/bas/src/ble_svc_bas.c | 127 + .../dis/include/services/dis/ble_svc_dis.h | 113 + .../host/services/dis/src/ble_svc_dis.c | 331 + .../gap/include/services/gap/ble_svc_gap.h | 54 + .../host/services/gap/src/ble_svc_gap.c | 298 + .../gatt/include/services/gatt/ble_svc_gatt.h | 40 + .../host/services/gatt/src/ble_svc_gatt.c | 108 + .../ias/include/services/ias/ble_svc_ias.h | 38 + .../host/services/ias/src/ble_svc_ias.c | 149 + .../ipss/include/services/ipss/ble_svc_ipss.h | 38 + .../host/services/ipss/src/ble_svc_ipss.c | 51 + .../lls/include/services/lls/ble_svc_lls.h | 51 + .../host/services/lls/src/ble_svc_lls.c | 192 + .../src/nimble/nimble/host/src/ble_att.c | 589 ++ .../src/nimble/nimble/host/src/ble_att_clt.c | 956 +++ .../src/nimble/nimble/host/src/ble_att_cmd.c | 636 ++ .../nimble/nimble/host/src/ble_att_cmd_priv.h | 449 ++ .../src/nimble/nimble/host/src/ble_att_priv.h | 301 + .../src/nimble/nimble/host/src/ble_att_svr.c | 2747 +++++++ .../nimble/nimble/host/src/ble_eddystone.c | 178 + .../src/nimble/nimble/host/src/ble_gap.c | 6455 +++++++++++++++++ .../src/nimble/nimble/host/src/ble_gap_priv.h | 154 + .../nimble/nimble/host/src/ble_gatt_priv.h | 199 + .../src/nimble/nimble/host/src/ble_gattc.c | 4806 ++++++++++++ .../src/nimble/nimble/host/src/ble_gatts.c | 2222 ++++++ .../nimble/nimble/host/src/ble_gatts_lcl.c | 211 + .../src/nimble/nimble/host/src/ble_hs.c | 843 +++ .../src/nimble/nimble/host/src/ble_hs_adv.c | 803 ++ .../nimble/nimble/host/src/ble_hs_adv_priv.h | 36 + .../nimble/nimble/host/src/ble_hs_atomic.c | 120 + .../nimble/host/src/ble_hs_atomic_priv.h | 41 + .../src/nimble/nimble/host/src/ble_hs_cfg.c | 33 + .../src/nimble/nimble/host/src/ble_hs_conn.c | 594 ++ .../nimble/nimble/host/src/ble_hs_conn_priv.h | 148 + .../src/nimble/nimble/host/src/ble_hs_flow.c | 283 + .../nimble/nimble/host/src/ble_hs_flow_priv.h | 36 + .../src/nimble/nimble/host/src/ble_hs_hci.c | 630 ++ .../nimble/nimble/host/src/ble_hs_hci_cmd.c | 152 + .../nimble/nimble/host/src/ble_hs_hci_evt.c | 949 +++ .../nimble/nimble/host/src/ble_hs_hci_priv.h | 124 + .../nimble/nimble/host/src/ble_hs_hci_util.c | 215 + .../src/nimble/nimble/host/src/ble_hs_id.c | 401 + .../nimble/nimble/host/src/ble_hs_id_priv.h | 45 + .../src/nimble/nimble/host/src/ble_hs_log.c | 47 + .../src/nimble/nimble/host/src/ble_hs_mbuf.c | 161 + .../nimble/nimble/host/src/ble_hs_mbuf_priv.h | 38 + .../src/nimble/nimble/host/src/ble_hs_misc.c | 149 + .../nimble/nimble/host/src/ble_hs_mqueue.c | 82 + .../nimble/host/src/ble_hs_periodic_sync.c | 154 + .../host/src/ble_hs_periodic_sync_priv.h | 55 + .../src/nimble/nimble/host/src/ble_hs_priv.h | 158 + .../src/nimble/nimble/host/src/ble_hs_pvcy.c | 334 + .../nimble/nimble/host/src/ble_hs_pvcy_priv.h | 46 + .../nimble/nimble/host/src/ble_hs_resolv.c | 795 ++ .../nimble/host/src/ble_hs_resolv_priv.h | 111 + .../nimble/nimble/host/src/ble_hs_shutdown.c | 69 + .../nimble/nimble/host/src/ble_hs_startup.c | 367 + .../nimble/host/src/ble_hs_startup_priv.h | 33 + .../src/nimble/nimble/host/src/ble_hs_stop.c | 289 + .../src/nimble/nimble/host/src/ble_ibeacon.c | 82 + .../src/nimble/nimble/host/src/ble_l2cap.c | 506 ++ .../nimble/nimble/host/src/ble_l2cap_coc.c | 614 ++ .../nimble/host/src/ble_l2cap_coc_priv.h | 106 + .../nimble/nimble/host/src/ble_l2cap_priv.h | 144 + .../nimble/nimble/host/src/ble_l2cap_sig.c | 1946 +++++ .../nimble/host/src/ble_l2cap_sig_cmd.c | 114 + .../nimble/host/src/ble_l2cap_sig_priv.h | 184 + .../src/nimble/nimble/host/src/ble_monitor.c | 473 ++ .../nimble/nimble/host/src/ble_monitor_priv.h | 100 + .../src/nimble/nimble/host/src/ble_sm.c | 2854 ++++++++ .../src/nimble/nimble/host/src/ble_sm_alg.c | 744 ++ .../src/nimble/nimble/host/src/ble_sm_cmd.c | 68 + .../src/nimble/nimble/host/src/ble_sm_lgcy.c | 254 + .../src/nimble/nimble/host/src/ble_sm_priv.h | 428 ++ .../src/nimble/nimble/host/src/ble_sm_sc.c | 916 +++ .../src/nimble/nimble/host/src/ble_store.c | 420 ++ .../nimble/nimble/host/src/ble_store_util.c | 276 + .../src/nimble/nimble/host/src/ble_uuid.c | 263 + .../nimble/nimble/host/src/ble_uuid_priv.h | 45 + .../include/store/config/ble_store_config.h | 39 + .../host/store/config/src/ble_store_config.c | 531 ++ .../store/config/src/ble_store_config_conf.c | 296 + .../store/config/src/ble_store_config_priv.h | 62 + .../host/store/config/src/ble_store_nvs.c | 653 ++ .../nimble/host/util/include/host/util/util.h | 46 + .../src/nimble/nimble/host/util/src/addr.c | 95 + .../src/nimble/nimble/include/nimble/ble.h | 304 + .../nimble/include/nimble/ble_hci_trans.h | 192 + .../nimble/nimble/include/nimble/hci_common.h | 1536 ++++ .../nimble/nimble/include/nimble/nimble_npl.h | 180 + .../nimble/nimble/include/nimble/nimble_opt.h | 34 + .../nimble/include/nimble/nimble_opt_auto.h | 117 + .../ram/include/transport/ram/ble_hci_ram.h | 35 + .../nimble/transport/ram/src/ble_hci_ram.c | 241 + .../porting/nimble/include/hal/hal_timer.h | 173 + .../nimble/porting/nimble/include/log/log.h | 34 + .../nimble/include/log_common/ignore.h | 64 + .../nimble/include/log_common/log_common.h | 42 + .../porting/nimble/include/logcfg/logcfg.h | 148 + .../nimble/porting/nimble/include/mem/mem.h | 68 + .../porting/nimble/include/modlog/modlog.h | 116 + .../nimble/include/nimble/nimble_port.h | 54 + .../nimble/porting/nimble/include/os/endian.h | 232 + .../src/nimble/porting/nimble/include/os/os.h | 63 + .../porting/nimble/include/os/os_cputime.h | 240 + .../porting/nimble/include/os/os_error.h | 43 + .../porting/nimble/include/os/os_mbuf.h | 646 ++ .../porting/nimble/include/os/os_mempool.h | 274 + .../porting/nimble/include/os/os_trace_api.h | 83 + .../nimble/porting/nimble/include/os/queue.h | 219 + .../nimble/porting/nimble/include/os/util.h | 38 + .../porting/nimble/include/stats/stats.h | 80 + .../porting/nimble/include/syscfg/syscfg.h | 1492 ++++ .../nimble/include/sysflash/sysflash.h | 24 + .../porting/nimble/include/sysinit/sysinit.h | 37 + .../src/nimble/porting/nimble/src/endian.c | 268 + .../src/nimble/porting/nimble/src/hal_timer.c | 829 +++ .../src/nimble/porting/nimble/src/mem.c | 322 + .../nimble/porting/nimble/src/nimble_port.c | 145 + .../nimble/porting/nimble/src/os_cputime.c | 126 + .../porting/nimble/src/os_cputime_pwr2.c | 109 + .../src/nimble/porting/nimble/src/os_mbuf.c | 1134 +++ .../nimble/porting/nimble/src/os_mempool.c | 355 + .../nimble/porting/nimble/src/os_msys_init.c | 133 + .../freertos/include/nimble/nimble_npl_os.h | 358 + .../include/nimble/nimble_port_freertos.h | 40 + .../freertos/include/nimble/npl_freertos.h | 88 + .../npl/freertos/src/nimble_port_freertos.c | 91 + .../npl/freertos/src/npl_os_freertos.c | 612 ++ lib/NimBLE-Arduino/src/nimconfig.h | 269 + lib/NimBLE-Arduino/src/nimconfig_rename.h | 61 + lib/WiFiManager/.travis.yml | 42 + lib/WiFiManager/LICENSE | 22 + lib/WiFiManager/README.md | 581 ++ lib/WiFiManager/WiFiManager.cpp | 3794 ++++++++++ lib/WiFiManager/WiFiManager.h | 737 ++ .../examples/Advanced/Advanced.ino | 141 + lib/WiFiManager/examples/Basic/Basic.ino | 41 + .../AutoConnectNonBlocking.ino | 27 + .../AutoConnectNonBlockingwParams.ino | 36 + .../onDemandNonBlocking.ino | 85 + .../AutoConnectWithFeedback.ino | 42 + .../AutoConnectWithReset.ino | 43 + .../AutoConnectWithStaticIP.ino | 71 + .../AutoConnectWithTimeout.ino | 38 + .../OnDemandConfigPortal.ino | 47 + .../OnDemandWebPortal/onDemandWebPortal.ino | 51 + .../AutoConnectWithFSParameters.ino | 169 + ...AutoConnectWithFSParametersAndCustomIP.ino | 194 + .../ParamsChildClass/ParamsChildClass.ino | 143 + .../OnDemandConfigPortal.ino | 365 + .../examples/Unique/cb/AnonymousCB.ino | 26 + .../extras/WiFiManager.template.html | 371 + lib/WiFiManager/extras/parse.js | 60 + .../extras/png_signal_strength_master.png | Bin 0 -> 59671 bytes lib/WiFiManager/extras/template.h | 12 + lib/WiFiManager/extras/test.html | 182 + lib/WiFiManager/keywords.txt | 39 + lib/WiFiManager/library.json | 29 + lib/WiFiManager/library.properties | 9 + lib/WiFiManager/strings_en.h | 488 ++ lib/WiFiManager/travis/common.sh | 60 + lib/nuki_ble | 1 + lib/pubsubclient/.travis.yml | 7 + lib/pubsubclient/CHANGES.txt | 85 + lib/pubsubclient/LICENSE.txt | 20 + lib/pubsubclient/README.md | 50 + .../examples/mqtt_auth/mqtt_auth.ino | 43 + .../examples/mqtt_basic/mqtt_basic.ino | 77 + .../examples/mqtt_esp8266/mqtt_esp8266.ino | 129 + .../mqtt_large_message/mqtt_large_message.ino | 179 + .../mqtt_publish_in_callback.ino | 60 + .../mqtt_reconnect_nonblocking.ino | 67 + .../examples/mqtt_stream/mqtt_stream.ino | 57 + lib/pubsubclient/keywords.txt | 36 + lib/pubsubclient/library.json | 18 + lib/pubsubclient/library.properties | 9 + lib/pubsubclient/src/PubSubClient.cpp | 769 ++ lib/pubsubclient/src/PubSubClient.h | 184 + lib/pubsubclient/tests/.gitignore | 4 + lib/pubsubclient/tests/Makefile | 25 + lib/pubsubclient/tests/README.md | 93 + lib/pubsubclient/tests/src/connect_spec.cpp | 336 + lib/pubsubclient/tests/src/keepalive_spec.cpp | 185 + lib/pubsubclient/tests/src/lib/Arduino.h | 26 + lib/pubsubclient/tests/src/lib/BDDTest.cpp | 50 + lib/pubsubclient/tests/src/lib/BDDTest.h | 23 + lib/pubsubclient/tests/src/lib/Buffer.cpp | 34 + lib/pubsubclient/tests/src/lib/Buffer.h | 23 + lib/pubsubclient/tests/src/lib/Client.h | 21 + lib/pubsubclient/tests/src/lib/IPAddress.cpp | 44 + lib/pubsubclient/tests/src/lib/IPAddress.h | 72 + lib/pubsubclient/tests/src/lib/Print.h | 28 + lib/pubsubclient/tests/src/lib/ShimClient.cpp | 153 + lib/pubsubclient/tests/src/lib/ShimClient.h | 51 + lib/pubsubclient/tests/src/lib/Stream.cpp | 39 + lib/pubsubclient/tests/src/lib/Stream.h | 22 + lib/pubsubclient/tests/src/lib/trace.h | 10 + lib/pubsubclient/tests/src/publish_spec.cpp | 191 + lib/pubsubclient/tests/src/receive_spec.cpp | 340 + lib/pubsubclient/tests/src/subscribe_spec.cpp | 178 + lib/pubsubclient/tests/testcases/__init__.py | 0 .../tests/testcases/mqtt_basic.py | 39 + .../testcases/mqtt_publish_in_callback.py | 59 + lib/pubsubclient/tests/testcases/settings.py | 2 + lib/pubsubclient/tests/testsuite.py | 181 + main.cpp | 36 + 531 files changed, 180736 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Network.cpp create mode 100644 Network.h create mode 100644 Nuki.cpp create mode 100644 Nuki.h create mode 100644 lib/Crc16/Crc16.h create mode 100644 lib/Crc16/LICENSE create mode 100644 lib/Crc16/examples/Crc16_Example/Crc16_Example.ino create mode 100644 lib/Crc16/keywords.txt create mode 100644 lib/Crc16/readme.md create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/README.md create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_client/BLE_client.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_iBeacon/BLE_iBeacon.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_notify/BLE_notify.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_scan/BLE_scan.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server/BLE_server.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_uart/BLE_uart.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_write/BLE_write.ino create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/library.properties create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristicMap.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptorMap.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEServiceMap.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.cpp create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/HIDKeyboardTypes.h create mode 100644 lib/ESP32_BLE_Arduino-1.0.1/src/HIDTypes.h create mode 100644 lib/NimBLE-Arduino/CHANGELOG.md create mode 100644 lib/NimBLE-Arduino/LICENSE create mode 100644 lib/NimBLE-Arduino/README.md create mode 100644 lib/NimBLE-Arduino/docs/Command_line_config.md create mode 100644 lib/NimBLE-Arduino/docs/Improvements_and_updates.md create mode 100644 lib/NimBLE-Arduino/docs/Migration_guide.md create mode 100644 lib/NimBLE-Arduino/docs/New_user_guide.md create mode 100644 lib/NimBLE-Arduino/docs/Usage_tips.md create mode 100644 lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino create mode 100644 lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md create mode 100644 lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino create mode 100644 lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md create mode 100644 lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino create mode 100644 lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Scan_Whitelist/NimBLE_Scan_whitelist.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Server_Whitelist/NimBLE_Server_Whitelist.ino create mode 100644 lib/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino create mode 100644 lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino create mode 100644 lib/NimBLE-Arduino/library.properties create mode 100644 lib/NimBLE-Arduino/src/HIDKeyboardTypes.h create mode 100644 lib/NimBLE-Arduino/src/HIDTypes.h create mode 100644 lib/NimBLE-Arduino/src/NimBLE2904.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLE2904.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEAddress.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEAddress.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEAdvertising.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEAdvertising.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEAttValue.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEBeacon.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEBeacon.h create mode 100644 lib/NimBLE-Arduino/src/NimBLECharacteristic.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLECharacteristic.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEClient.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEClient.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEConnInfo.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEDescriptor.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEDescriptor.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEDevice.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEDevice.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEEddystoneURL.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEHIDDevice.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEHIDDevice.h create mode 100644 lib/NimBLE-Arduino/src/NimBLELog.h create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.h create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteService.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLERemoteService.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEScan.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEScan.h create mode 100644 lib/NimBLE-Arduino/src/NimBLESecurity.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLESecurity.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEServer.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEServer.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEService.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEService.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEUUID.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEUUID.h create mode 100644 lib/NimBLE-Arduino/src/NimBLEUtils.cpp create mode 100644 lib/NimBLE-Arduino/src/NimBLEUtils.h create mode 100644 lib/NimBLE-Arduino/src/nimble/CODING_STANDARDS.md create mode 100644 lib/NimBLE-Arduino/src/nimble/LICENSE create mode 100644 lib/NimBLE-Arduino/src/nimble/NOTICE create mode 100644 lib/NimBLE-Arduino/src/nimble/README.md create mode 100644 lib/NimBLE-Arduino/src/nimble/RELEASE_NOTES.md create mode 100644 lib/NimBLE-Arduino/src/nimble/console/console.h create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_compiler.h create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_nimble_hci.h create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/src/esp_nimble_hci.c create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_cfg.h create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_mem.h create mode 100644 lib/NimBLE-Arduino/src/nimble/esp_port/port/src/esp_nimble_mem.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/AUTHORS create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/LICENSE create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/README create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/VERSION create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/documentation/tinycrypt.rst create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/aes.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cbc_mode.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/constants.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_mode.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_prng.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dsa.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_platform_specific.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac_prng.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/sha256.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/utils.h create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_decrypt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_encrypt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cbc_mode.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ccm_mode.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cmac_mode.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_mode.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_prng.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dh.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dsa.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_platform_specific.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac_prng.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/sha256.c create mode 100644 lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/utils.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_hw.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_adv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_conn.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_ctrl.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_hci.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_resolv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_scan.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sched.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sync.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_test.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_trace.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_utils.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_whitelist.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy_trace.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_adv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_hci.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_ctrl.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci_ev.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rand.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_resolv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rfmgmt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_scan.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sched.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_supp_cmd.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sync.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_trace.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_utils.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_whitelist.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/include/ble/xcvr.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_hw.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_phy.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/include/ble/xcvr.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_hw.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy_trace.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_att.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_eddystone.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gap.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gatt.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_adv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_hci.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_id.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_log.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_mbuf.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_pvcy.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_stop.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_ibeacon.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_l2cap.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_monitor.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_sm.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_store.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_uuid.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/access.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_cli.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_srv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/glue.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_cli.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_srv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/main.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/mesh.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_cli.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_srv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/porting.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/proxy.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/slist.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/testing.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/atomic.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_cli.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_srv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/foundation.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/glue.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_cli.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_srv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_cli.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_srv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/shell.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/shell.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/transport.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/transport.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ans/include/services/ans/ble_svc_ans.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ans/src/ble_svc_ans.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/bas/include/services/bas/ble_svc_bas.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/bas/src/ble_svc_bas.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/dis/include/services/dis/ble_svc_dis.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/dis/src/ble_svc_dis.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/gap/src/ble_svc_gap.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/gatt/src/ble_svc_gatt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ias/include/services/ias/ble_svc_ias.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ias/src/ble_svc_ias.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ipss/include/services/ipss/ble_svc_ipss.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/ipss/src/ble_svc_ipss.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/lls/include/services/lls/ble_svc_lls.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/services/lls/src/ble_svc_lls.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_clt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_cmd.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_cmd_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_att_svr.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_eddystone.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gap.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gap_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatt_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gattc.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatts.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_gatts_lcl.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_adv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_adv_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_atomic.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_atomic_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_cfg.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_conn.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_conn_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_flow.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_flow_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_hci.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_hci_cmd.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_hci_evt.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_hci_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_hci_util.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_id.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_id_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_log.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_mbuf.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_mbuf_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_misc.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_mqueue.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_periodic_sync.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_periodic_sync_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_pvcy.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_pvcy_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_resolv.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_resolv_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_shutdown.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_startup.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_startup_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_hs_stop.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_ibeacon.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_coc.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_coc_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_sig.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_sig_cmd.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_l2cap_sig_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_monitor.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_monitor_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm_alg.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm_cmd.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm_lgcy.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_sm_sc.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_store.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_store_util.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_uuid.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/src/ble_uuid_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/store/config/include/store/config/ble_store_config.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/store/config/src/ble_store_config.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/store/config/src/ble_store_config_conf.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/store/config/src/ble_store_config_priv.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/store/config/src/ble_store_nvs.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/util/include/host/util/util.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/host/util/src/addr.c create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/ble.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/ble_hci_trans.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/hci_common.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/nimble_npl.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/nimble_opt.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/include/nimble/nimble_opt_auto.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/transport/ram/include/transport/ram/ble_hci_ram.h create mode 100644 lib/NimBLE-Arduino/src/nimble/nimble/transport/ram/src/ble_hci_ram.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/hal/hal_timer.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/log/log.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/log_common/ignore.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/log_common/log_common.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/logcfg/logcfg.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/mem/mem.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/modlog/modlog.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/nimble/nimble_port.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/endian.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os_cputime.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os_error.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os_mbuf.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os_mempool.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/os_trace_api.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/queue.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/os/util.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/stats/stats.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/syscfg/syscfg.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/sysflash/sysflash.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/include/sysinit/sysinit.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/endian.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/hal_timer.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/mem.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/nimble_port.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/os_cputime.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/os_cputime_pwr2.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/os_mbuf.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/os_mempool.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/nimble/src/os_msys_init.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/npl/freertos/include/nimble/npl_freertos.h create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/npl/freertos/src/nimble_port_freertos.c create mode 100644 lib/NimBLE-Arduino/src/nimble/porting/npl/freertos/src/npl_os_freertos.c create mode 100644 lib/NimBLE-Arduino/src/nimconfig.h create mode 100644 lib/NimBLE-Arduino/src/nimconfig_rename.h create mode 100644 lib/WiFiManager/.travis.yml create mode 100644 lib/WiFiManager/LICENSE create mode 100644 lib/WiFiManager/README.md create mode 100644 lib/WiFiManager/WiFiManager.cpp create mode 100644 lib/WiFiManager/WiFiManager.h create mode 100644 lib/WiFiManager/examples/Advanced/Advanced.ino create mode 100644 lib/WiFiManager/examples/Basic/Basic.ino create mode 100644 lib/WiFiManager/examples/NonBlocking/AutoConnectNonBlocking/AutoConnectNonBlocking.ino create mode 100644 lib/WiFiManager/examples/NonBlocking/AutoConnectNonBlockingwParams/AutoConnectNonBlockingwParams.ino create mode 100644 lib/WiFiManager/examples/NonBlocking/OnDemandNonBlocking/onDemandNonBlocking.ino create mode 100644 lib/WiFiManager/examples/Old_examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino create mode 100644 lib/WiFiManager/examples/Old_examples/AutoConnectWithReset/AutoConnectWithReset.ino create mode 100644 lib/WiFiManager/examples/Old_examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino create mode 100644 lib/WiFiManager/examples/Old_examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino create mode 100644 lib/WiFiManager/examples/OnDemand/OnDemandConfigPortal/OnDemandConfigPortal.ino create mode 100644 lib/WiFiManager/examples/OnDemand/OnDemandWebPortal/onDemandWebPortal.ino create mode 100644 lib/WiFiManager/examples/Parameters/SPIFFS/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino create mode 100644 lib/WiFiManager/examples/Parameters/SPIFFS/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino create mode 100644 lib/WiFiManager/examples/ParamsChildClass/ParamsChildClass.ino create mode 100644 lib/WiFiManager/examples/Super/OnDemandConfigPortal/OnDemandConfigPortal.ino create mode 100644 lib/WiFiManager/examples/Unique/cb/AnonymousCB.ino create mode 100644 lib/WiFiManager/extras/WiFiManager.template.html create mode 100644 lib/WiFiManager/extras/parse.js create mode 100644 lib/WiFiManager/extras/png_signal_strength_master.png create mode 100644 lib/WiFiManager/extras/template.h create mode 100644 lib/WiFiManager/extras/test.html create mode 100644 lib/WiFiManager/keywords.txt create mode 100644 lib/WiFiManager/library.json create mode 100644 lib/WiFiManager/library.properties create mode 100644 lib/WiFiManager/strings_en.h create mode 100644 lib/WiFiManager/travis/common.sh create mode 160000 lib/nuki_ble create mode 100644 lib/pubsubclient/.travis.yml create mode 100755 lib/pubsubclient/CHANGES.txt create mode 100755 lib/pubsubclient/LICENSE.txt create mode 100644 lib/pubsubclient/README.md create mode 100755 lib/pubsubclient/examples/mqtt_auth/mqtt_auth.ino create mode 100755 lib/pubsubclient/examples/mqtt_basic/mqtt_basic.ino create mode 100644 lib/pubsubclient/examples/mqtt_esp8266/mqtt_esp8266.ino create mode 100644 lib/pubsubclient/examples/mqtt_large_message/mqtt_large_message.ino create mode 100644 lib/pubsubclient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino create mode 100644 lib/pubsubclient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino create mode 100644 lib/pubsubclient/examples/mqtt_stream/mqtt_stream.ino create mode 100755 lib/pubsubclient/keywords.txt create mode 100644 lib/pubsubclient/library.json create mode 100644 lib/pubsubclient/library.properties create mode 100755 lib/pubsubclient/src/PubSubClient.cpp create mode 100755 lib/pubsubclient/src/PubSubClient.h create mode 100644 lib/pubsubclient/tests/.gitignore create mode 100644 lib/pubsubclient/tests/Makefile create mode 100644 lib/pubsubclient/tests/README.md create mode 100644 lib/pubsubclient/tests/src/connect_spec.cpp create mode 100644 lib/pubsubclient/tests/src/keepalive_spec.cpp create mode 100644 lib/pubsubclient/tests/src/lib/Arduino.h create mode 100644 lib/pubsubclient/tests/src/lib/BDDTest.cpp create mode 100644 lib/pubsubclient/tests/src/lib/BDDTest.h create mode 100644 lib/pubsubclient/tests/src/lib/Buffer.cpp create mode 100644 lib/pubsubclient/tests/src/lib/Buffer.h create mode 100644 lib/pubsubclient/tests/src/lib/Client.h create mode 100644 lib/pubsubclient/tests/src/lib/IPAddress.cpp create mode 100644 lib/pubsubclient/tests/src/lib/IPAddress.h create mode 100644 lib/pubsubclient/tests/src/lib/Print.h create mode 100644 lib/pubsubclient/tests/src/lib/ShimClient.cpp create mode 100644 lib/pubsubclient/tests/src/lib/ShimClient.h create mode 100644 lib/pubsubclient/tests/src/lib/Stream.cpp create mode 100644 lib/pubsubclient/tests/src/lib/Stream.h create mode 100644 lib/pubsubclient/tests/src/lib/trace.h create mode 100644 lib/pubsubclient/tests/src/publish_spec.cpp create mode 100644 lib/pubsubclient/tests/src/receive_spec.cpp create mode 100644 lib/pubsubclient/tests/src/subscribe_spec.cpp create mode 100644 lib/pubsubclient/tests/testcases/__init__.py create mode 100644 lib/pubsubclient/tests/testcases/mqtt_basic.py create mode 100644 lib/pubsubclient/tests/testcases/mqtt_publish_in_callback.py create mode 100644 lib/pubsubclient/tests/testcases/settings.py create mode 100644 lib/pubsubclient/tests/testsuite.py create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2be016c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +build +cmake-build-debug +cmake-build-release diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..941da51 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/nuki_ble"] + path = lib/nuki_ble + url = git@github.com:technyon/nuki_ble.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..16f7d17 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.0.0) + +set(ARDUINO_BOARD "ESP32 Dev Module [esp32.esp32]") + +project(nuki_hub CXX) + +include_directories(${PROJECT_NAME} + PRIVATE + lib/Crc16 + lib/NimBLE-Arduino/src + lib/nuki_ble/src + lib/ESP32_BLE_Arduino-1.0.1/src + lib/WiFiManager + lib/pubsubclient/src/ +) + +file(GLOB SRCFILES +# "Lib/FreeRTOS/src/*.c" +# "Lib/FreeRTOS/src/*.cpp" + Network.cpp + Nuki.cpp + lib/ESP32_BLE_Arduino-1.0.1/src/*.cpp + lib/ESP32_BLE_Arduino-1.0.1/src/*.h + lib/WiFiManager/WiFiManager.cpp + lib/Crc16/Crc16.h + lib/nuki_ble/src/NukiBle.cpp + lib/nuki_ble/src/NukiConstants.h + lib/nuki_ble/src/NukiUtills.h + include_directories(Lib/PubSubClient) + lib/pubsubclient/src/PubSubClient.cpp +) + +file(GLOB_RECURSE SRCFILESREC + lib/NimBLE-Arduino/src/*.c + lib/NimBLE-Arduino/src/*.cpp + lib/NimBLE-Arduino/src/*.h +) + +add_executable(${PROJECT_NAME} + main.cpp + ${SRCFILES} + ${SRCFILESREC} +) + +target_link_esp32_sdk(${PROJECT_NAME}) + +# Arduino.h is included in hello_world.cpp, so link with Arduino core +target_link_arduino_libraries(${PROJECT_NAME} + PRIVATE + core + WiFi + Update + WebServer + DNSServer + Preferences +# esp32 +# Wire +# SPIFFS +# FS +) + +target_link_arduino_libraries(DNSServer PUBLIC WiFi) + +# This is needed for the generation of HEX binary and uploading it +target_enable_arduino_upload(${PROJECT_NAME}) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3b5d70c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Jan-Ole Schümann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Network.cpp b/Network.cpp new file mode 100644 index 0000000..24b3c8a --- /dev/null +++ b/Network.cpp @@ -0,0 +1,95 @@ +#include "Network.h" +#include "WiFi.h" +#include // https://github.com/tzapu/WiFiManager +#include "Arduino.h" + +Network::Network() +: _mqttClient(_wifiClient) +{ + +} + +void Network::initialize() +{ + WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP + // it is a good practice to make sure your code sets wifi mode how you want it. + + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager, Local intialization. Once its business is done, there is no need to keep it around + WiFiManager wm; + + // reset settings - wipe stored credentials for testing + // these are stored by the esp library + //wm.resetSettings(); + + // Automatically connect using saved credentials, + // if connection fails, it starts an access point with the specified name ( "AutoConnectAP"), + // if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect()) + // then goes into a blocking loop awaiting configuration and will return success result + + bool res; + // res = wm.autoConnect(); // auto generated AP name from chipid + // res = wm.autoConnect("AutoConnectAP"); // anonymous ap + res = wm.autoConnect("AutoConnectAP","password"); // password protected ap + + if(!res) { + Serial.println("Failed to connect"); + return; + // ESP.restart(); + } + else { + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + } + + _mqttClient.setServer("192.168.0.100", 1883); + _mqttClient.publish("nuki/test", "OK"); +} + + +void Network::reconnect() +{ + while (!_mqttClient.connected()) { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + if (_mqttClient.connect("arduinoClient")) { + Serial.println("connected"); + // Once connected, publish an announcement... + _mqttClient.publish("outTopic","hello world"); + // ... and resubscribe + _mqttClient.subscribe("inTopic"); + } else { + Serial.print("failed, rc="); + Serial.print(_mqttClient.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + + +void Network::update() +{ + if(!_mqttClient.connected()) + { + reconnect(); + } + + unsigned long ts = millis(); + if(_publishTs < ts) + { + _publishTs = ts + 1000; + + ++_count; + + char cstr[16]; + itoa(_count, cstr, 10); + + _mqttClient.publish("nuki/counter", cstr); + } + + _mqttClient.loop(); +} diff --git a/Network.h b/Network.h new file mode 100644 index 0000000..3ffdcd2 --- /dev/null +++ b/Network.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class Network +{ +public: + Network(); + + void initialize(); + void update(); + +private: + void reconnect(); + + PubSubClient _mqttClient; + WiFiClient _wifiClient; + + uint32_t _count = 0; + unsigned long _publishTs = 0; +}; diff --git a/Nuki.cpp b/Nuki.cpp new file mode 100644 index 0000000..9321aab --- /dev/null +++ b/Nuki.cpp @@ -0,0 +1,35 @@ +#include "Nuki.h" + +Nuki::Nuki(const std::string& name, uint32_t id) +: _nukiBle(name, id) +{ + +} + +void Nuki::initialize() +{ + _nukiBle.initialize(); +} + +void Nuki::update() +{ + if (!_paired) { + if (_nukiBle.pairNuki()) { + Serial.println("Nuki paired"); + _paired = true; + + _nukiBle.updateKeyTurnerState(); + // nukiBle.requestConfig(false); + // nukiBle.requestConfig(true); + // nukiBle.requestBatteryReport(); + _nukiBle.requestKeyPadCodes(0, 2); + // nukiBle.requestLogEntries(0, 10, 0, true); + + //execute action + // nukiBle.lockAction(LockAction::lock, 0, 0); + // addKeypadEntry(); + } + } + + _nukiBle.updateKeyTurnerState(); +} diff --git a/Nuki.h b/Nuki.h new file mode 100644 index 0000000..cc288cc --- /dev/null +++ b/Nuki.h @@ -0,0 +1,18 @@ +#pragma once + +#include "NukiBle.h" +#include "NukiConstants.h" + +class Nuki +{ +public: + Nuki(const std::string& name, uint32_t id); + + void initialize(); + void update(); + +private: + NukiBle _nukiBle; + bool _paired = false; + +}; diff --git a/lib/Crc16/Crc16.h b/lib/Crc16/Crc16.h new file mode 100644 index 0000000..fdcf2ff --- /dev/null +++ b/lib/Crc16/Crc16.h @@ -0,0 +1,243 @@ +//------------------------------------------------------------------------------------- +// CRC16 support class +// Based on various examples found on the web +// Copyright (C) 2014 Vincenzo Mennella (see license.txt) +// History +// 0.1.0 31/05/2014: First public code release +// 0.1.1 17/12/2014: Minor revision and commented code +// 0.1.2 06/06/2019: Fix reflect routine for 16 bit data +// Added ModBus and Mcrf4XX inline functions +// +// License +// "MIT Open Source Software License": +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in the +// Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//------------------------------------------------------------------------------------- +#ifndef CRC16_H +#define CRC16_H +#define LIBRARY_VERSION_CRC16_H "0.1.2" + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ARDUINO) + #include "WProgram.h" +#else + #include +#endif + +class Crc16 { + private: + //Crc parameters + uint16_t _msbMask; + uint16_t _mask; + uint16_t _xorIn; + uint16_t _xorOut; + uint16_t _polynomial; + uint8_t _reflectIn; + uint8_t _reflectOut; + //Crc value + uint16_t _crc; + uint8_t reflect(uint8_t data); + uint16_t reflect(uint16_t data); + + public: + inline Crc16() + { + //Default to XModem parameters + _reflectIn = false; + _reflectOut = false; + _polynomial = 0x1021; + _xorIn = 0x0000; + _xorOut = 0x0000; + _msbMask = 0x8000; + _mask = 0xFFFF; + _crc = _xorIn; + } + inline Crc16(uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask) + { + _reflectIn = reflectIn; + _reflectOut = reflectOut; + _polynomial = polynomial; + _xorIn = xorIn; + _xorOut = xorOut; + _msbMask = msbMask; + _mask = mask; + _crc = _xorIn; + } + inline void clearCrc(); + inline void updateCrc(uint8_t data); + inline uint16_t getCrc(); + inline unsigned int fastCrc(uint8_t data[], uint8_t start, uint16_t length, uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask); + inline unsigned int XModemCrc(uint8_t data[], uint8_t start, uint16_t length) + { + // XModem parameters: poly=0x1021 initialize=0x0000 refin=false refout=false xorout=0x0000 + return fastCrc(data, start, length, false, false, 0x1021, 0x0000, 0x0000, 0x8000, 0xffff); + } + inline unsigned int Mcrf4XX(uint8_t data[], uint8_t start, uint16_t length) + { + return fastCrc(data, start, length, true, true, 0x1021, 0xffff, 0x0000, 0x8000, 0xffff); + } + inline unsigned int Modbus(uint8_t data[], uint8_t start, uint16_t length) + { + return fastCrc(data, start, length, true, true, 0x8005, 0xffff, 0x0000, 0x8000, 0xffff); + } +}; + +//--------------------------------------------------- +// Initialize crc calculation +//--------------------------------------------------- +void Crc16::clearCrc() +{ + _crc = _xorIn; +} +//--------------------------------------------------- +// Update crc with new data +//--------------------------------------------------- +void Crc16::updateCrc(uint8_t data) +{ + if (_reflectIn != 0) + data = (uint8_t) reflect(data); + + int j = 0x80; + + while (j > 0) + { + uint16_t bit = (uint16_t)(_crc & _msbMask); + + _crc <<= 1; + + if ((data & j) != 0) + { + bit = (uint16_t)(bit ^ _msbMask); + } + + if (bit != 0) + { + _crc ^= _polynomial; + } + + j >>= 1; + } +} + +//--------------------------------------------------- +// Get final crc value +//--------------------------------------------------- +uint16_t Crc16::getCrc() +{ + if (_reflectOut != 0) + _crc = (unsigned int)((reflect(_crc) ^ _xorOut) & _mask); + + return _crc; +} + +//--------------------------------------------------- +// Calculate generic crc code on data array +// Examples of crc 16: +// Kermit: width=16 poly=0x1021 initialize=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 +// Modbus: width=16 poly=0x8005 initialize=0xffff refin=true refout=true xorout=0x0000 check=0x4b37 +// XModem: width=16 poly=0x1021 initialize=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3 +// CCITT-False: width=16 poly=0x1021 initialize=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 +//--------------------------------------------------- +unsigned int Crc16::fastCrc(uint8_t data[], uint8_t start, uint16_t length, uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask) +{ + uint16_t crc = xorIn; + + int j; + uint8_t c; + unsigned int bit; + + if (length == 0) return crc; + + for (int i = start; i < (start + length); i++) + { + c = data[i]; + + if (reflectIn != 0) + c = (uint8_t) reflect(c); + + j = 0x80; + + while (j > 0) + { + bit = (unsigned int)(crc & msbMask); + crc <<= 1; + + if ((c & j) != 0) + { + bit = (unsigned int)(bit ^ msbMask); + } + + if (bit != 0) + { + crc ^= polynomial; + } + + j >>= 1; + } + } + + if (reflectOut != 0) + crc = (unsigned int)((reflect((uint16_t) crc) ^ xorOut) & mask); + + return crc; +} + +//------------------------------------------------------- +// Reflects bit in a uint8_t +//------------------------------------------------------- +uint8_t Crc16::reflect(uint8_t data) +{ + const uint8_t bits = 8; + unsigned long reflection = 0x00000000; + // Reflect the data about the center bit. + for (uint8_t bit = 0; bit < bits; bit++) + { + // If the LSB bit is set, set the reflection of it. + if ((data & 0x01) != 0) + { + reflection |= (unsigned long)(1 << ((bits - 1) - bit)); + } + + data = (uint8_t)(data >> 1); + } + + return reflection; +} +//------------------------------------------------------- +// Reflects bit in a uint16_t +//------------------------------------------------------- +uint16_t Crc16::reflect(uint16_t data) +{ + const uint8_t bits = 16; + unsigned long reflection = 0x00000000; + // Reflect the data about the center bit. + for (uint8_t bit = 0; bit < bits; bit++) + { + // If the LSB bit is set, set the reflection of it. + if ((data & 0x01) != 0) + { + reflection |= (unsigned long)(1 << ((bits - 1) - bit)); + } + + data = (uint16_t)(data >> 1); + } + + return reflection; +} + +#endif diff --git a/lib/Crc16/LICENSE b/lib/Crc16/LICENSE new file mode 100644 index 0000000..20dc811 --- /dev/null +++ b/lib/Crc16/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Vincenzo Mennella + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/Crc16/examples/Crc16_Example/Crc16_Example.ino b/lib/Crc16/examples/Crc16_Example/Crc16_Example.ino new file mode 100644 index 0000000..121ac4e --- /dev/null +++ b/lib/Crc16/examples/Crc16_Example/Crc16_Example.ino @@ -0,0 +1,85 @@ +#include +//Crc 16 library (XModem) +Crc16 crc; + +void setup() +{ + Serial.begin(38400); + Serial.println("CRC-16 bit test program"); + Serial.println("======================="); + +} + +void loop() +{ + /* + Examples of crc-16 configurations + Kermit: width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 + Modbus: width=16 poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37 + XModem: width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3 + CCITT-False:width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 + see http://www.lammertbies.nl/comm/info/crc-calculation.html + */ + //calculate crc incrementally + byte data[] = "123456789"; + + Serial.println("Calculating crc incrementally"); + + crc.clearCrc(); + for(byte i=0;i<9;i++) + { + Serial.print("byte "); + Serial.print(i); + Serial.print(" = "); + Serial.println(data[i]); + crc.updateCrc(data[i]); + } + unsigned short value = crc.getCrc(); + Serial.print("crc = 0x"); + Serial.println(value, HEX); + + Serial.println("Calculating crc in a single call"); + + //XModem + value = crc.XModemCrc(data,0,9); + Serial.print("XModem crc = 0x"); + Serial.println(value, HEX); + //Reference xmodem + Serial.println("Reference XModem crc"); + value = calcrc((char*)data, 9); + Serial.print("crc = 0x"); + Serial.println(value, HEX); + + //Modbus + value = crc.Modbus(data,0,9); + Serial.print("Modbus crc = 0x"); + Serial.println(value, HEX); + + //Mcrf4XX + value = crc.Mcrf4XX(data,0,9); + Serial.print("Mcrf4XX crc = 0x"); + Serial.println(value, HEX); + + while(true); +} +//Check routine taken from +//http://web.mit.edu/6.115/www/miscfiles/amulet/amulet-help/xmodem.htm +int calcrc(char *ptr, int count) +{ + int crc; + char i; + crc = 0; + while (--count >= 0) + { + crc = crc ^ (int) *ptr++ << 8; + i = 8; + do + { + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } while(--i); + } + return (crc); +} \ No newline at end of file diff --git a/lib/Crc16/keywords.txt b/lib/Crc16/keywords.txt new file mode 100644 index 0000000..12f9577 --- /dev/null +++ b/lib/Crc16/keywords.txt @@ -0,0 +1,22 @@ +####################################### +# Syntax Coloring Map For CRC16 +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +Crc16 KEYWORD1 +####################################### +# Methods and Functions (KEYWORD2) +####################################### +clearCrc KEYWORD2 +updateCrc KEYWORD2 +getCrc KEYWORD2 +fastCrc KEYWORD2 +XModemCrc KEYWORD2 +Mcrf4XX KEYWORD2 +Modbus KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/Crc16/readme.md b/lib/Crc16/readme.md new file mode 100644 index 0000000..20c6190 --- /dev/null +++ b/lib/Crc16/readme.md @@ -0,0 +1,19 @@ +# Crc16 A simple crc-16 library for Arduino + +## Description +Use this library to implement crc checks on buffer arrays + +## Usage +There are two modes to calculate crc: incremental and single call: +* In first mode the crc is calculated adding data bytes one by one and then calculating final crc, this is useful +for reception routines that receives bytes asynchrously, +* The second mode is used to obtain crc from a buffer array. +Using one mode doesn't interfere with the other (So you can calculate tx crc while receiving data and updating rx crc) + +Is possible to configure crc with all crc-16bit standards (by default is defined XModem). + +See possible crc variants: +http://www.lammertbies.nl/comm/info/crc-calculation.html + + + diff --git a/lib/ESP32_BLE_Arduino-1.0.1/README.md b/lib/ESP32_BLE_Arduino-1.0.1/README.md new file mode 100644 index 0000000..e80fbe0 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/README.md @@ -0,0 +1,15 @@ +# ESP32 BLE for Arduino +The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino. + +The actual source of the project which is being maintained can be found here: + +https://github.com/nkolban/esp32-snippets + +Issues and questions should be raised here: + +https://github.com/nkolban/esp32-snippets/issues + + +Documentation for using the library can be found here: + +https://github.com/nkolban/esp32-snippets/tree/master/Documentation \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_client/BLE_client.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_client/BLE_client.ino new file mode 100644 index 0000000..4c58299 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_client/BLE_client.ino @@ -0,0 +1,160 @@ +/** + * A BLE client example that is rich in capabilities. + * There is a lot new capabilities implemented. + * author unknown + * updated by chegewara + */ + +#include "BLEDevice.h" +//#include "BLEScan.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); + +static boolean doConnect = false; +static boolean connected = false; +static boolean doScan = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; +static BLEAdvertisedDevice* myDevice; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("data: "); + Serial.println((char*)pData); +} + +class MyClientCallback : public BLEClientCallbacks { + void onConnect(BLEClient* pclient) { + } + + void onDisconnect(BLEClient* pclient) { + connected = false; + Serial.println("onDisconnect"); + } +}; + +bool connectToServer() { + Serial.print("Forming a connection to "); + Serial.println(myDevice->getAddress().toString().c_str()); + + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + pClient->setClientCallbacks(new MyClientCallback()); + + // Connect to the remove BLE Server. + pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + if(pRemoteCharacteristic->canRead()) { + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + } + + if(pRemoteCharacteristic->canNotify()) + pRemoteCharacteristic->registerForNotify(notifyCallback); + + connected = true; +} +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + + BLEDevice::getScan()->stop(); + myDevice = new BLEAdvertisedDevice(advertisedDevice); + doConnect = true; + doScan = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer()) { + Serial.println("We are now connected to the BLE Server."); + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + }else if(doScan){ + BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_iBeacon/BLE_iBeacon.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 0000000..e43174d --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,103 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by pcbreflux +*/ + + +/* + Create a BLE server that will send periodic iBeacon frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ +#include "sys/time.h" + +#include "BLEDevice.h" +#include "BLEUtils.h" +#include "BLEBeacon.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t temprature_sens_read(); +//uint8_t g_phyFuns; + +#ifdef __cplusplus +} +#endif + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +void setBeacon() { + + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); + oBeacon.setMinor(bootcount&0xFFFF); + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 + + std::string strServiceData = ""; + + strServiceData += (char)26; // Len + strServiceData += (char)0xFF; // Type + strServiceData += oBeacon.getData(); + oAdvertisementData.addData(strServiceData); + + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); + +} + +void setup() { + + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n",bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init(""); + + // Create the BLE Server + // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(100); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() { +} diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_notify/BLE_notify.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_notify/BLE_notify.ino new file mode 100644 index 0000000..42b9e72 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_notify/BLE_notify.ino @@ -0,0 +1,110 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ +#include +#include +#include +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + pCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_scan/BLE_scan.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_scan/BLE_scan.ino new file mode 100644 index 0000000..094f793 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_scan/BLE_scan.ino @@ -0,0 +1,40 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include +#include + +int scanTime = 5; //In seconds +BLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() { + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server/BLE_server.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server/BLE_server.ino new file mode 100644 index 0000000..3f9176a --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server/BLE_server.ino @@ -0,0 +1,45 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini + updates by chegewara +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("Long name works now"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino new file mode 100644 index 0000000..90704ef --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -0,0 +1,111 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ +#include +#include +#include +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + BLEDevice::startAdvertising(); + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + pCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_uart/BLE_uart.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_uart/BLE_uart.ino new file mode 100644 index 0000000..35b570b --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_uart/BLE_uart.ino @@ -0,0 +1,125 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" + Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + In this example rxValue is the data received (only accessible inside that function). + And txValue is the data to be sent, in this example just a byte incremented every second. +*/ +#include +#include +#include +#include + +BLEServer *pServer = NULL; +BLECharacteristic * pTxCharacteristic; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t txValue = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID +#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string rxValue = pCharacteristic->getValue(); + + if (rxValue.length() > 0) { + Serial.println("*********"); + Serial.print("Received Value: "); + for (int i = 0; i < rxValue.length(); i++) + Serial.print(rxValue[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("UART Service"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pTxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_TX, + BLECharacteristic::PROPERTY_NOTIFY + ); + + pTxCharacteristic->addDescriptor(new BLE2902()); + + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + BLECharacteristic::PROPERTY_WRITE + ); + + pRxCharacteristic->setCallbacks(new MyCallbacks()); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->start(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + + if (deviceConnected) { + pTxCharacteristic->setValue(&txValue, 1); + pTxCharacteristic->notify(); + txValue++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_write/BLE_write.ino b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_write/BLE_write.ino new file mode 100644 index 0000000..24a0cd2 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/examples/BLE_write/BLE_write.ino @@ -0,0 +1,65 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string value = pCharacteristic->getValue(); + + if (value.length() > 0) { + Serial.println("*********"); + Serial.print("New value: "); + for (int i = 0; i < value.length(); i++) + Serial.print(value[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + +void setup() { + Serial.begin(115200); + + Serial.println("1- Download and install an BLE scanner app in your phone"); + Serial.println("2- Scan for BLE devices in the app"); + Serial.println("3- Connect to MyESP32"); + Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); + Serial.println("5- See the magic =)"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setCallbacks(new MyCallbacks()); + + pCharacteristic->setValue("Hello World"); + pService->start(); + + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/library.properties b/lib/ESP32_BLE_Arduino-1.0.1/library.properties new file mode 100644 index 0000000..8c2a019 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/library.properties @@ -0,0 +1,10 @@ +name=ESP32 BLE Arduino +version=1.0.1 +author=Neil Kolban +maintainer=Dariusz Krempa +sentence=BLE functions for ESP32 +paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform. +category=Communication +url=https://github.com/nkolban/ESP32_BLE_Arduino +architectures=esp32 +includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.cpp new file mode 100644 index 0000000..23d9c77 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.cpp @@ -0,0 +1,62 @@ +/* + * BLE2902.cpp + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLE2902.h" + +BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) { + uint8_t data[2] = { 0, 0 }; + setValue(data, 2); +} // BLE2902 + + +/** + * @brief Get the notifications value. + * @return The notifications value. True if notifications are enabled and false if not. + */ +bool BLE2902::getNotifications() { + return (getValue()[0] & (1 << 0)) != 0; +} // getNotifications + + +/** + * @brief Get the indications value. + * @return The indications value. True if indications are enabled and false if not. + */ +bool BLE2902::getIndications() { + return (getValue()[0] & (1 << 1)) != 0; +} // getIndications + + +/** + * @brief Set the indications flag. + * @param [in] flag The indications flag. + */ +void BLE2902::setIndications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 1; + else pValue[0] &= ~(1 << 1); +} // setIndications + + +/** + * @brief Set the notifications flag. + * @param [in] flag The notifications flag. + */ +void BLE2902::setNotifications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 0; + else pValue[0] &= ~(1 << 0); +} // setNotifications + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.h new file mode 100644 index 0000000..397360a --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2902.h @@ -0,0 +1,34 @@ +/* + * BLE2902.h + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_ +#define COMPONENTS_CPP_UTILS_BLE2902_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEDescriptor.h" + +/** + * @brief Descriptor for Client Characteristic Configuration. + * + * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +class BLE2902: public BLEDescriptor { +public: + BLE2902(); + bool getNotifications(); + bool getIndications(); + void setNotifications(bool flag); + void setIndications(bool flag); + +}; // BLE2902 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.cpp new file mode 100644 index 0000000..02252a1 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.cpp @@ -0,0 +1,74 @@ +/* + * BLE2904.cpp + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLE2904.h" + + +BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) { + m_data.m_format = 0; + m_data.m_exponent = 0; + m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers + m_data.m_unit = 0; + m_data.m_description = 0; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // BLE2902 + + +/** + * @brief Set the description. + */ +void BLE2904::setDescription(uint16_t description) { + m_data.m_description = description; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} + + +/** + * @brief Set the exponent. + */ +void BLE2904::setExponent(int8_t exponent) { + m_data.m_exponent = exponent; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setExponent + + +/** + * @brief Set the format. + */ +void BLE2904::setFormat(uint8_t format) { + m_data.m_format = format; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setFormat + + +/** + * @brief Set the namespace. + */ +void BLE2904::setNamespace(uint8_t namespace_value) { + m_data.m_namespace = namespace_value; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setNamespace + + +/** + * @brief Set the units for this value. It should be one of the encoded values defined here: + * https://www.bluetooth.com/specifications/assigned-numbers/units + * @param [in] unit The type of units of this characteristic as defined by assigned numbers. + */ +void BLE2904::setUnit(uint16_t unit) { + m_data.m_unit = unit; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setUnit + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.h new file mode 100644 index 0000000..cb337e2 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLE2904.h @@ -0,0 +1,74 @@ +/* + * BLE2904.h + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_ +#define COMPONENTS_CPP_UTILS_BLE2904_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEDescriptor.h" + +struct BLE2904_Data { + uint8_t m_format; + int8_t m_exponent; + uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units + uint8_t m_namespace; + uint16_t m_description; + +} __attribute__((packed)); + +/** + * @brief Descriptor for Characteristic Presentation Format. + * + * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +class BLE2904: public BLEDescriptor { +public: + BLE2904(); + static const uint8_t FORMAT_BOOLEAN = 1; + static const uint8_t FORMAT_UINT2 = 2; + static const uint8_t FORMAT_UINT4 = 3; + static const uint8_t FORMAT_UINT8 = 4; + static const uint8_t FORMAT_UINT12 = 5; + static const uint8_t FORMAT_UINT16 = 6; + static const uint8_t FORMAT_UINT24 = 7; + static const uint8_t FORMAT_UINT32 = 8; + static const uint8_t FORMAT_UINT48 = 9; + static const uint8_t FORMAT_UINT64 = 10; + static const uint8_t FORMAT_UINT128 = 11; + static const uint8_t FORMAT_SINT8 = 12; + static const uint8_t FORMAT_SINT12 = 13; + static const uint8_t FORMAT_SINT16 = 14; + static const uint8_t FORMAT_SINT24 = 15; + static const uint8_t FORMAT_SINT32 = 16; + static const uint8_t FORMAT_SINT48 = 17; + static const uint8_t FORMAT_SINT64 = 18; + static const uint8_t FORMAT_SINT128 = 19; + static const uint8_t FORMAT_FLOAT32 = 20; + static const uint8_t FORMAT_FLOAT64 = 21; + static const uint8_t FORMAT_SFLOAT16 = 22; + static const uint8_t FORMAT_SFLOAT32 = 23; + static const uint8_t FORMAT_IEEE20601 = 24; + static const uint8_t FORMAT_UTF8 = 25; + static const uint8_t FORMAT_UTF16 = 26; + static const uint8_t FORMAT_OPAQUE = 27; + + void setDescription(uint16_t); + void setExponent(int8_t exponent); + void setFormat(uint8_t format); + void setNamespace(uint8_t namespace_value); + void setUnit(uint16_t unit); + +private: + BLE2904_Data m_data; +}; // BLE2904 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.cpp new file mode 100644 index 0000000..d688334 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.cpp @@ -0,0 +1,95 @@ +/* + * BLEAddress.cpp + * + * Created on: Jul 2, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEAddress.h" +#include +#include +#include +#include +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + + +/** + * @brief Create an address from the native ESP32 representation. + * @param [in] address The native representation. + */ +BLEAddress::BLEAddress(esp_bd_addr_t address) { + memcpy(m_address, address, ESP_BD_ADDR_LEN); +} // BLEAddress + + +/** + * @brief Create an address from a hex string + * + * A hex string is of the format: + * ``` + * 00:00:00:00:00:00 + * ``` + * which is 17 characters in length. + * + * @param [in] stringAddress The hex representation of the address. + */ +BLEAddress::BLEAddress(std::string stringAddress) { + if (stringAddress.length() != 17) return; + + int data[6]; + sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]); + m_address[0] = (uint8_t) data[0]; + m_address[1] = (uint8_t) data[1]; + m_address[2] = (uint8_t) data[2]; + m_address[3] = (uint8_t) data[3]; + m_address[4] = (uint8_t) data[4]; + m_address[5] = (uint8_t) data[5]; +} // BLEAddress + + +/** + * @brief Determine if this address equals another. + * @param [in] otherAddress The other address to compare against. + * @return True if the addresses are equal. + */ +bool BLEAddress::equals(BLEAddress otherAddress) { + return memcmp(otherAddress.getNative(), m_address, 6) == 0; +} // equals + + +/** + * @brief Return the native representation of the address. + * @return The native representation of the address. + */ +esp_bd_addr_t *BLEAddress::getNative() { + return &m_address; +} // getNative + + +/** + * @brief Convert a BLE address to a string. + * + * A string representation of an address is in the format: + * + * ``` + * xx:xx:xx:xx:xx:xx + * ``` + * + * @return The string representation of the address. + */ +std::string BLEAddress::toString() { + std::stringstream stream; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[0] << ':'; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[1] << ':'; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[2] << ':'; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[3] << ':'; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[4] << ':'; + stream << std::setfill('0') << std::setw(2) << std::hex << (int) ((uint8_t*) (m_address))[5]; + return stream.str(); +} // toString +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.h new file mode 100644 index 0000000..7eff4da --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAddress.h @@ -0,0 +1,34 @@ +/* + * BLEAddress.h + * + * Created on: Jul 2, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_ +#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include // ESP32 BLE +#include + + +/** + * @brief A %BLE device address. + * + * Every %BLE device has a unique address which can be used to identify it and form connections. + */ +class BLEAddress { +public: + BLEAddress(esp_bd_addr_t address); + BLEAddress(std::string stringAddress); + bool equals(BLEAddress otherAddress); + esp_bd_addr_t* getNative(); + std::string toString(); + +private: + esp_bd_addr_t m_address; +}; + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.cpp new file mode 100644 index 0000000..3f55e8c --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.cpp @@ -0,0 +1,529 @@ +/* + * BLEAdvertisedDevice.cpp + * + * During the scanning procedure, we will be finding advertised BLE devices. This class + * models a found device. + * + * + * See also: + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile + * + * Created on: Jul 3, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLEAdvertisedDevice.h" +#include "BLEUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG="BLEAdvertisedDevice"; +#endif + +BLEAdvertisedDevice::BLEAdvertisedDevice() { + m_adFlag = 0; + m_appearance = 0; + m_deviceType = 0; + m_manufacturerData = ""; + m_name = ""; + m_rssi = -9999; + m_serviceData = ""; + m_txPower = 0; + m_pScan = nullptr; + + m_haveAppearance = false; + m_haveManufacturerData = false; + m_haveName = false; + m_haveRSSI = false; + m_haveServiceData = false; + m_haveServiceUUID = false; + m_haveTXPower = false; + +} // BLEAdvertisedDevice + + +/** + * @brief Get the address. + * + * Every %BLE device exposes an address that is used to identify it and subsequently connect to it. + * Call this function to obtain the address of the advertised device. + * + * @return The address of the advertised device. + */ +BLEAddress BLEAdvertisedDevice::getAddress() { + return m_address; +} // getAddress + + +/** + * @brief Get the appearance. + * + * A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user + * typcially in the form of an icon. + * + * @return The appearance of the advertised device. + */ +uint16_t BLEAdvertisedDevice::getAppearance() { + return m_appearance; +} // getAppearance + + +/** + * @brief Get the manufacturer data. + * @return The manufacturer data of the advertised device. + */ +std::string BLEAdvertisedDevice::getManufacturerData() { + return m_manufacturerData; +} // getManufacturerData + + +/** + * @brief Get the name. + * @return The name of the advertised device. + */ +std::string BLEAdvertisedDevice::getName() { + return m_name; +} // getName + + +/** + * @brief Get the RSSI. + * @return The RSSI of the advertised device. + */ +int BLEAdvertisedDevice::getRSSI() { + return m_rssi; +} // getRSSI + + +/** + * @brief Get the scan object that created this advertisement. + * @return The scan object. + */ +BLEScan* BLEAdvertisedDevice::getScan() { + return m_pScan; +} // getScan + + +/** + * @brief Get the service data. + * @return The ServiceData of the advertised device. + */ +std::string BLEAdvertisedDevice::getServiceData() { + return m_serviceData; +} //getServiceData + + +/** + * @brief Get the service data UUID. + * @return The service data UUID. + */ +BLEUUID BLEAdvertisedDevice::getServiceDataUUID() { + return m_serviceDataUUID; +} // getServiceDataUUID + + +/** + * @brief Get the Service UUID. + * @return The Service UUID of the advertised device. + */ +BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful + return m_serviceUUIDs[0]; +} // getServiceUUID + +/** + * @brief Check advertised serviced for existence required UUID + * @return Return true if service is advertised + */ +bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){ + for (int i = 0; i < m_serviceUUIDs.size(); i++) { + if (m_serviceUUIDs[i].equals(uuid)) return true; + } + return false; +} + +/** + * @brief Get the TX Power. + * @return The TX Power of the advertised device. + */ +int8_t BLEAdvertisedDevice::getTXPower() { + return m_txPower; +} // getTXPower + + + +/** + * @brief Does this advertisement have an appearance value? + * @return True if there is an appearance value present. + */ +bool BLEAdvertisedDevice::haveAppearance() { + return m_haveAppearance; +} // haveAppearance + + +/** + * @brief Does this advertisement have manufacturer data? + * @return True if there is manufacturer data present. + */ +bool BLEAdvertisedDevice::haveManufacturerData() { + return m_haveManufacturerData; +} // haveManufacturerData + + +/** + * @brief Does this advertisement have a name value? + * @return True if there is a name value present. + */ +bool BLEAdvertisedDevice::haveName() { + return m_haveName; +} // haveName + + +/** + * @brief Does this advertisement have a signal strength value? + * @return True if there is a signal strength value present. + */ +bool BLEAdvertisedDevice::haveRSSI() { + return m_haveRSSI; +} // haveRSSI + + +/** + * @brief Does this advertisement have a service data value? + * @return True if there is a service data value present. + */ +bool BLEAdvertisedDevice::haveServiceData() { + return m_haveServiceData; +} // haveServiceData + + +/** + * @brief Does this advertisement have a service UUID value? + * @return True if there is a service UUID value present. + */ +bool BLEAdvertisedDevice::haveServiceUUID() { + return m_haveServiceUUID; +} // haveServiceUUID + + +/** + * @brief Does this advertisement have a transmission power value? + * @return True if there is a transmission power value present. + */ +bool BLEAdvertisedDevice::haveTXPower() { + return m_haveTXPower; +} // haveTXPower + + +/** + * @brief Parse the advertising pay load. + * + * The pay load is a buffer of bytes that is either 31 bytes long or terminated by + * a 0 length value. Each entry in the buffer has the format: + * [length][type][data...] + * + * The length does not include itself but does include everything after it until the next record. A record + * with a length value of 0 indicates a terminator. + * + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile + */ +void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) { + uint8_t length; + uint8_t ad_type; + uint8_t sizeConsumed = 0; + bool finished = false; + m_payload = payload; + m_payloadLength = total_len; + + while(!finished) { + length = *payload; // Retrieve the length of the record. + payload++; // Skip to type + sizeConsumed += 1 + length; // increase the size consumed. + + if (length != 0) { // A length of 0 indicates that we have reached the end. + ad_type = *payload; + payload++; + length--; + + char* pHex = BLEUtils::buildHexData(nullptr, payload, length); + ESP_LOGD(LOG_TAG, "Type: 0x%.2x (%s), length: %d, data: %s", + ad_type, BLEUtils::advTypeToString(ad_type), length, pHex); + free(pHex); + + switch(ad_type) { + case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09 + setName(std::string(reinterpret_cast(payload), length)); + break; + } // ESP_BLE_AD_TYPE_NAME_CMPL + + case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A + setTXPower(*payload); + break; + } // ESP_BLE_AD_TYPE_TX_PWR + + case ESP_BLE_AD_TYPE_APPEARANCE: { // Adv Data Type: 0x19 + setAppearance(*reinterpret_cast(payload)); + break; + } // ESP_BLE_AD_TYPE_APPEARANCE + + case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01 + setAdFlag(*payload); + break; + } // ESP_BLE_AD_TYPE_FLAG + + case ESP_BLE_AD_TYPE_16SRV_CMPL: + case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02 + for (int var = 0; var < length/2; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 2))); + } + break; + } // ESP_BLE_AD_TYPE_16SRV_PART + + case ESP_BLE_AD_TYPE_32SRV_CMPL: + case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04 + for (int var = 0; var < length/4; ++var) { + setServiceUUID(BLEUUID(*reinterpret_cast(payload + var * 4))); + } + break; + } // ESP_BLE_AD_TYPE_32SRV_PART + + case ESP_BLE_AD_TYPE_128SRV_CMPL: { // Adv Data Type: 0x07 + setServiceUUID(BLEUUID(payload, 16, false)); + break; + } // ESP_BLE_AD_TYPE_128SRV_CMPL + + case ESP_BLE_AD_TYPE_128SRV_PART: { // Adv Data Type: 0x06 + setServiceUUID(BLEUUID(payload, 16, false)); + break; + } // ESP_BLE_AD_TYPE_128SRV_PART + + // See CSS Part A 1.4 Manufacturer Specific Data + case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: { + setManufacturerData(std::string(reinterpret_cast(payload), length)); + break; + } // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE + + case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID + if (length < 2) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); + break; + } + uint16_t uuid = *(uint16_t*)payload; + setServiceDataUUID(BLEUUID(uuid)); + if (length > 2) { + setServiceData(std::string(reinterpret_cast(payload + 2), length - 2)); + } + break; + } //ESP_BLE_AD_TYPE_SERVICE_DATA + + case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID + if (length < 4) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); + break; + } + uint32_t uuid = *(uint32_t*) payload; + setServiceDataUUID(BLEUUID(uuid)); + if (length > 4) { + setServiceData(std::string(reinterpret_cast(payload + 4), length - 4)); + } + break; + } //ESP_BLE_AD_TYPE_32SERVICE_DATA + + case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID + if (length < 16) { + ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); + break; + } + + setServiceDataUUID(BLEUUID(payload, (size_t)16, false)); + if (length > 16) { + setServiceData(std::string(reinterpret_cast(payload + 16), length - 16)); + } + break; + } //ESP_BLE_AD_TYPE_32SERVICE_DATA + + default: { + ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type); + break; + } + } // switch + payload += length; + } // Length <> 0 + + + if (sizeConsumed >= total_len) + finished = true; + + } // !finished +} // parseAdvertisement + + +/** + * @brief Set the address of the advertised device. + * @param [in] address The address of the advertised device. + */ +void BLEAdvertisedDevice::setAddress(BLEAddress address) { + m_address = address; +} // setAddress + + +/** + * @brief Set the adFlag for this device. + * @param [in] The discovered adFlag. + */ +void BLEAdvertisedDevice::setAdFlag(uint8_t adFlag) { + m_adFlag = adFlag; +} // setAdFlag + + +/** + * @brief Set the appearance for this device. + * @param [in] The discovered appearance. + */ +void BLEAdvertisedDevice::setAppearance(uint16_t appearance) { + m_appearance = appearance; + m_haveAppearance = true; + ESP_LOGD(LOG_TAG, "- appearance: %d", m_appearance); +} // setAppearance + + +/** + * @brief Set the manufacturer data for this device. + * @param [in] The discovered manufacturer data. + */ +void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) { + m_manufacturerData = manufacturerData; + m_haveManufacturerData = true; + char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length()); + ESP_LOGD(LOG_TAG, "- manufacturer data: %s", pHex); + free(pHex); +} // setManufacturerData + + +/** + * @brief Set the name for this device. + * @param [in] name The discovered name. + */ +void BLEAdvertisedDevice::setName(std::string name) { + m_name = name; + m_haveName = true; + ESP_LOGD(LOG_TAG, "- setName(): name: %s", m_name.c_str()); +} // setName + + +/** + * @brief Set the RSSI for this device. + * @param [in] rssi The discovered RSSI. + */ +void BLEAdvertisedDevice::setRSSI(int rssi) { + m_rssi = rssi; + m_haveRSSI = true; + ESP_LOGD(LOG_TAG, "- setRSSI(): rssi: %d", m_rssi); +} // setRSSI + + +/** + * @brief Set the Scan that created this advertised device. + * @param pScan The Scan that created this advertised device. + */ +void BLEAdvertisedDevice::setScan(BLEScan* pScan) { + m_pScan = pScan; +} // setScan + + +/** + * @brief Set the Service UUID for this device. + * @param [in] serviceUUID The discovered serviceUUID + */ +void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { + return setServiceUUID(BLEUUID(serviceUUID)); +} // setServiceUUID + + +/** + * @brief Set the Service UUID for this device. + * @param [in] serviceUUID The discovered serviceUUID + */ +void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); + m_haveServiceUUID = true; + ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); +} // setServiceUUID + + +/** + * @brief Set the ServiceData value. + * @param [in] data ServiceData value. + */ +void BLEAdvertisedDevice::setServiceData(std::string serviceData) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceData = serviceData; // Save the service data that we received. +} //setServiceData + + +/** + * @brief Set the ServiceDataUUID value. + * @param [in] data ServiceDataUUID value. + */ +void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) { + m_haveServiceData = true; // Set the flag that indicates we have service data. + m_serviceDataUUID = uuid; +} // setServiceDataUUID + + +/** + * @brief Set the power level for this device. + * @param [in] txPower The discovered power level. + */ +void BLEAdvertisedDevice::setTXPower(int8_t txPower) { + m_txPower = txPower; + m_haveTXPower = true; + ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower); +} // setTXPower + + +/** + * @brief Create a string representation of this device. + * @return A string representation of this device. + */ +std::string BLEAdvertisedDevice::toString() { + std::stringstream ss; + ss << "Name: " << getName() << ", Address: " << getAddress().toString(); + if (haveAppearance()) { + ss << ", appearance: " << getAppearance(); + } + if (haveManufacturerData()) { + char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); + ss << ", manufacturer data: " << pHex; + free(pHex); + } + if (haveServiceUUID()) { + ss << ", serviceUUID: " << getServiceUUID().toString(); + } + if (haveTXPower()) { + ss << ", txPower: " << (int)getTXPower(); + } + return ss.str(); +} // toString + +uint8_t* BLEAdvertisedDevice::getPayload() { + return m_payload; +} + +esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() { + return m_addressType; +} + +void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) { + m_addressType = type; +} + +size_t BLEAdvertisedDevice::getPayloadLength() { + return m_payloadLength; +} + +#endif /* CONFIG_BT_ENABLED */ + diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.h new file mode 100644 index 0000000..aec8374 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertisedDevice.h @@ -0,0 +1,123 @@ +/* + * BLEAdvertisedDevice.h + * + * Created on: Jul 3, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ +#define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +#include + +#include "BLEAddress.h" +#include "BLEScan.h" +#include "BLEUUID.h" + + +class BLEScan; +/** + * @brief A representation of a %BLE advertised device found by a scan. + * + * When we perform a %BLE scan, the result will be a set of devices that are advertising. This + * class provides a model of a detected device. + */ +class BLEAdvertisedDevice { +public: + BLEAdvertisedDevice(); + + BLEAddress getAddress(); + uint16_t getAppearance(); + std::string getManufacturerData(); + std::string getName(); + int getRSSI(); + BLEScan* getScan(); + std::string getServiceData(); + BLEUUID getServiceDataUUID(); + BLEUUID getServiceUUID(); + int8_t getTXPower(); + uint8_t* getPayload(); + size_t getPayloadLength(); + esp_ble_addr_type_t getAddressType(); + void setAddressType(esp_ble_addr_type_t type); + + + bool isAdvertisingService(BLEUUID uuid); + bool haveAppearance(); + bool haveManufacturerData(); + bool haveName(); + bool haveRSSI(); + bool haveServiceData(); + bool haveServiceUUID(); + bool haveTXPower(); + + std::string toString(); + +private: + friend class BLEScan; + + void parseAdvertisement(uint8_t* payload, size_t total_len=62); + void setAddress(BLEAddress address); + void setAdFlag(uint8_t adFlag); + void setAdvertizementResult(uint8_t* payload); + void setAppearance(uint16_t appearance); + void setManufacturerData(std::string manufacturerData); + void setName(std::string name); + void setRSSI(int rssi); + void setScan(BLEScan* pScan); + void setServiceData(std::string data); + void setServiceDataUUID(BLEUUID uuid); + void setServiceUUID(const char* serviceUUID); + void setServiceUUID(BLEUUID serviceUUID); + void setTXPower(int8_t txPower); + + bool m_haveAppearance; + bool m_haveManufacturerData; + bool m_haveName; + bool m_haveRSSI; + bool m_haveServiceData; + bool m_haveServiceUUID; + bool m_haveTXPower; + + + BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); + uint8_t m_adFlag; + uint16_t m_appearance; + int m_deviceType; + std::string m_manufacturerData; + std::string m_name; + BLEScan* m_pScan; + int m_rssi; + std::vector m_serviceUUIDs; + int8_t m_txPower; + std::string m_serviceData; + BLEUUID m_serviceDataUUID; + uint8_t* m_payload; + size_t m_payloadLength = 0; + esp_ble_addr_type_t m_addressType; +}; + +/** + * @brief A callback handler for callbacks associated device scanning. + * + * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising + * has been found. This class can be sub-classed and registered such that when a scan is performed and + * a new advertised device has been found, we will be called back to be notified. + */ +class BLEAdvertisedDeviceCallbacks { +public: + virtual ~BLEAdvertisedDeviceCallbacks() {} + /** + * @brief Called when a new scan result is detected. + * + * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the + * device that was found. During any individual scan, a device will only be detected one time. + */ + virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0; +}; + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.cpp new file mode 100644 index 0000000..230d77c --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.cpp @@ -0,0 +1,505 @@ +/* + * BLEAdvertising.cpp + * + * This class encapsulates advertising a BLE Server. + * Created on: Jun 21, 2017 + * Author: kolban + * + * The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set + * of properties that are advertised and has built a data structure that can be populated by the programmer. + * This means that the programmer doesn't have to "mess with" the low level construction of a low level + * BLE advertising frame. Many of the fields are determined for us while others we can set before starting + * to advertise. + * + * Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters + * upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer + * set in the data will be advertised. + * + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "BLEAdvertising.h" +#include +#include "BLEUtils.h" +#include "GeneralUtils.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEAdvertising"; +#endif + + + +/** + * @brief Construct a default advertising object. + * + */ +BLEAdvertising::BLEAdvertising() { + m_advData.set_scan_rsp = false; + m_advData.include_name = true; + m_advData.include_txpower = true; + m_advData.min_interval = 0x20; + m_advData.max_interval = 0x40; + m_advData.appearance = 0x00; + m_advData.manufacturer_len = 0; + m_advData.p_manufacturer_data = nullptr; + m_advData.service_data_len = 0; + m_advData.p_service_data = nullptr; + m_advData.service_uuid_len = 0; + m_advData.p_service_uuid = nullptr; + m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT); + + m_advParams.adv_int_min = 0x20; + m_advParams.adv_int_max = 0x40; + m_advParams.adv_type = ADV_TYPE_IND; + m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + m_advParams.channel_map = ADV_CHNL_ALL; + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC; + + m_customAdvData = false; // No custom advertising data + m_customScanResponseData = false; // No custom scan response data +} // BLEAdvertising + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void BLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(BLEUUID(serviceUUID)); +} // addServiceUUID + + +/** + * @brief Set the device appearance in the advertising data. + * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. + * @param [in] appearance The appearance of the device in the advertising data. + * @return N/A. + */ +void BLEAdvertising::setAppearance(uint16_t appearance) { + m_advData.appearance = appearance; +} // setAppearance + +void BLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advParams.adv_int_min = mininterval; +} // setMinInterval + +void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advParams.adv_int_max = maxinterval; +} // setMaxInterval + +void BLEAdvertising::setMinPreferred(uint16_t mininterval) { + m_advData.min_interval = mininterval; +} // + +void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + m_advData.max_interval = maxinterval; +} // + +void BLEAdvertising::setScanResponse(bool set) { + m_scanResp = set; +} + +/** + * @brief Set the filtering for the scan filter. + * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. + * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. + */ +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); + if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (!scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } +} // setScanFilter + + +/** + * @brief Set the advertisement data that is to be published in a regular advertisement. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setAdvertisementData"); + esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. + ESP_LOGD(LOG_TAG, "<< setAdvertisementData"); +} // setAdvertisementData + + +/** + * @brief Set the advertisement data that is to be published in a scan response. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setScanResponseData"); + esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. + ESP_LOGD(LOG_TAG, "<< setScanResponseData"); +} // setScanResponseData + +/** + * @brief Start advertising. + * Start advertising. + * @return N/A. + */ +void BLEAdvertising::start() { + ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + + // We have a vector of service UUIDs that we wish to advertise. In order to use the + // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) + // representations. If we have 1 or more services to advertise then we allocate enough + // storage to host them and then copy them in one at a time into the contiguous storage. + int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { + m_advData.service_uuid_len = 16 * numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + uint8_t* p = m_advData.p_service_uuid; + for (int i = 0; i < numServices; i++) { + ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); + BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); + memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); + p += 16; + } + } else { + m_advData.service_uuid_len = 0; + ESP_LOGD(LOG_TAG, "- no services advertised"); + } + + esp_err_t errRc; + + if (!m_customAdvData) { + // Set the configuration for advertising. + m_advData.set_scan_rsp = false; + m_advData.include_name = !m_scanResp; + m_advData.include_txpower = !m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + if (!m_customScanResponseData && m_scanResp) { + m_advData.set_scan_rsp = true; + m_advData.include_name = m_scanResp; + m_advData.include_txpower = m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + // If we had services to advertise then we previously allocated some storage for them. + // Here we release that storage. + if (m_advData.service_uuid_len > 0) { + delete[] m_advData.p_service_uuid; + m_advData.p_service_uuid = nullptr; + } + + // Start advertising. + errRc = ::esp_ble_gap_start_advertising(&m_advParams); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + ESP_LOGD(LOG_TAG, "<< start"); +} // start + + +/** + * @brief Stop advertising. + * Stop advertising. + * @return N/A. + */ +void BLEAdvertising::stop() { + ESP_LOGD(LOG_TAG, ">> stop"); + esp_err_t errRc = ::esp_ble_gap_stop_advertising(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + ESP_LOGD(LOG_TAG, "<< stop"); +} // stop + +/** + * @brief Add data to the payload to be advertised. + * @param [in] data The data to be added to the payload. + */ +void BLEAdvertisementData::addData(std::string data) { + if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { + return; + } + m_payload.append(data); +} // addData + + +/** + * @brief Set the appearance. + * @param [in] appearance The appearance code value. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ +void BLEAdvertisementData::setAppearance(uint16_t appearance) { + char cdata[2]; + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); +} // setAppearance + + +/** + * @brief Set the complete services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); + break; + } + + default: + return; + } +} // setCompleteServices + + +/** + * @brief Set the advertisement flags. + * @param [in] The flags to be set in the advertisement. + * + * * ESP_BLE_ADV_FLAG_LIMIT_DISC + * * ESP_BLE_ADV_FLAG_GEN_DISC + * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT + * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT + * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT + * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC + */ +void BLEAdvertisementData::setFlags(uint8_t flag) { + char cdata[3]; + cdata[0] = 2; + cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 + cdata[2] = flag; + addData(std::string(cdata, 3)); +} // setFlag + + + +/** + * @brief Set manufacturer specific data. + * @param [in] data Manufacturer data. + */ +void BLEAdvertisementData::setManufacturerData(std::string data) { + ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); + char cdata[2]; + cdata[0] = data.length() + 1; + cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff + addData(std::string(cdata, 2) + data); + ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); +} // setManufacturerData + + +/** + * @brief Set the name. + * @param [in] The complete name of the device. + */ +void BLEAdvertisementData::setName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setName"); +} // setName + + +/** + * @brief Set the partial services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); + break; + } + + default: + return; + } +} // setPartialServices + + +/** + * @brief Set the service data (UUID + data) + * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. + * @param [in] data The data to be associated with the service data advert. + */ +void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x16] [UUID16] data + cdata[0] = data.length() + 3; + cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); + break; + } + + case 32: { + // [Len] [0x20] [UUID32] data + cdata[0] = data.length() + 5; + cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); + break; + } + + case 128: { + // [Len] [0x21] [UUID128] data + cdata[0] = data.length() + 17; + cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); + break; + } + + default: + return; + } +} // setServiceData + + +/** + * @brief Set the short name. + * @param [in] The short name of the device. + */ +void BLEAdvertisementData::setShortName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setShortName"); +} // setShortName + + +/** + * @brief Retrieve the payload that is to be advertised. + * @return The payload that is to be advertised. + */ +std::string BLEAdvertisementData::getPayload() { + return m_payload; +} // getPayload + +void BLEAdvertising::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event); + + switch(event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { + ESP_LOGI(LOG_TAG, "STOP advertising"); + start(); + break; + } + default: + break; + } +} + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.h new file mode 100644 index 0000000..3128b50 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEAdvertising.h @@ -0,0 +1,78 @@ +/* + * BLEAdvertising.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ +#define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLEUUID.h" +#include +#include "FreeRTOS.h" + +/** + * @brief Advertisement data set by the programmer to be published by the %BLE server. + */ +class BLEAdvertisementData { + // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will + // be exposed on demand/request or as time permits. + // +public: + void setAppearance(uint16_t appearance); + void setCompleteServices(BLEUUID uuid); + void setFlags(uint8_t); + void setManufacturerData(std::string data); + void setName(std::string name); + void setPartialServices(BLEUUID uuid); + void setServiceData(BLEUUID uuid, std::string data); + void setShortName(std::string name); + void addData(std::string data); // Add data to the payload. + std::string getPayload(); // Retrieve the current advert payload. + +private: + friend class BLEAdvertising; + std::string m_payload; // The payload of the advertisement. +}; // BLEAdvertisementData + + +/** + * @brief Perform and manage %BLE advertising. + * + * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. + */ +class BLEAdvertising { +public: + BLEAdvertising(); + void addServiceUUID(BLEUUID serviceUUID); + void addServiceUUID(const char* serviceUUID); + void start(); + void stop(); + void setAppearance(uint16_t appearance); + void setMaxInterval(uint16_t maxinterval); + void setMinInterval(uint16_t mininterval); + void setAdvertisementData(BLEAdvertisementData& advertisementData); + void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); + void setScanResponseData(BLEAdvertisementData& advertisementData); + void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM); + + void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); + void setScanResponse(bool); + +private: + esp_ble_adv_data_t m_advData; + esp_ble_adv_params_t m_advParams; + std::vector m_serviceUUIDs; + bool m_customAdvData = false; // Are we using custom advertising data? + bool m_customScanResponseData = false; // Are we using custom scan response data? + FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); + bool m_scanResp = true; + +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ \ No newline at end of file diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.cpp new file mode 100644 index 0000000..68f8d8e --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.cpp @@ -0,0 +1,89 @@ +/* + * BLEBeacon.cpp + * + * Created on: Jan 4, 2018 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLEBeacon.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEBeacon"; +#endif + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + + +BLEBeacon::BLEBeacon() { + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // BLEBeacon + +std::string BLEBeacon::getData() { + return std::string((char*) &m_beaconData, sizeof(m_beaconData)); +} // getData + +uint16_t BLEBeacon::getMajor() { + return m_beaconData.major; +} + +uint16_t BLEBeacon::getManufacturerId() { + return m_beaconData.manufacturerId; +} + +uint16_t BLEBeacon::getMinor() { + return m_beaconData.minor; +} + +BLEUUID BLEBeacon::getProximityUUID() { + return BLEUUID(m_beaconData.proximityUUID, 16, false); +} + +int8_t BLEBeacon::getSignalPower() { + return m_beaconData.signalPower; +} + +/** + * Set the raw data for the beacon record. + */ +void BLEBeacon::setData(std::string data) { + if (data.length() != sizeof(m_beaconData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); + return; + } + memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); +} // setData + +void BLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = ENDIAN_CHANGE_U16(major); +} // setMajor + +void BLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); +} // setManufacturerId + +void BLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); +} // setMinior + +void BLEBeacon::setProximityUUID(BLEUUID uuid) { + uuid = uuid.to128(); + memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16); +} // setProximityUUID + +void BLEBeacon::setSignalPower(int8_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.h new file mode 100644 index 0000000..277bd67 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEBeacon.h @@ -0,0 +1,43 @@ +/* + * BLEBeacon2.h + * + * Created on: Jan 4, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ +#define COMPONENTS_CPP_UTILS_BLEBEACON_H_ +#include "BLEUUID.h" +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class BLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + int8_t signalPower; + } __attribute__((packed)) m_beaconData; +public: + BLEBeacon(); + std::string getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + BLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(std::string data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(BLEUUID uuid); + void setSignalPower(int8_t signalPower); +}; // BLEBeacon + +#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.cpp new file mode 100644 index 0000000..e340287 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.cpp @@ -0,0 +1,760 @@ +/* + * BLECharacteristic.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include "sdkconfig.h" +#include +#include "BLECharacteristic.h" +#include "BLEService.h" +#include "BLEDevice.h" +#include "BLEUtils.h" +#include "BLE2902.h" +#include "GeneralUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLECharacteristic"; +#endif + +#define NULL_HANDLE (0xffff) + + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID (const char*) for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) { +} + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) { + m_bleUUID = uuid; + m_handle = NULL_HANDLE; + m_properties = (esp_gatt_char_prop_t)0; + m_pCallbacks = nullptr; + + setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); + setReadProperty((properties & PROPERTY_READ) != 0); + setWriteProperty((properties & PROPERTY_WRITE) != 0); + setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); + setIndicateProperty((properties & PROPERTY_INDICATE) != 0); + setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); +} // BLECharacteristic + +/** + * @brief Destructor. + */ +BLECharacteristic::~BLECharacteristic() { + //free(m_value.attr_value); // Release the storage for the value. +} // ~BLECharacteristic + + +/** + * @brief Associate a descriptor with this characteristic. + * @param [in] pDescriptor + * @return N/A. + */ +void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) { + ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); + m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); + ESP_LOGD(LOG_TAG, "<< addDescriptor()"); +} // addDescriptor + + +/** + * @brief Register a new characteristic with the ESP runtime. + * @param [in] pService The service with which to associate this characteristic. + */ +void BLECharacteristic::executeCreate(BLEService* pService) { + ESP_LOGD(LOG_TAG, ">> executeCreate()"); + + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "Characteristic already has a handle."); + return; + } + + m_pService = pService; // Save the service to which this characteristic belongs. + + ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", + getUUID().toString().c_str(), + m_pService->toString().c_str()); + + esp_attr_control_t control; + control.auto_rsp = ESP_GATT_RSP_BY_APP; + + m_semaphoreCreateEvt.take("executeCreate"); + esp_err_t errRc = ::esp_ble_gatts_add_char( + m_pService->getHandle(), + getUUID().getNative(), + static_cast(m_permissions), + getProperties(), + nullptr, + &control); // Whether to auto respond or not. + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreCreateEvt.wait("executeCreate"); + + BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + while (pDescriptor != nullptr) { + pDescriptor->executeCreate(this); + pDescriptor = m_descriptorMap.getNext(); + } // End while + + ESP_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ +BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { + return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); +} // getDescriptorByUUID + + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ +BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { + return m_descriptorMap.getByUUID(descriptorUUID); +} // getDescriptorByUUID + + +/** + * @brief Get the handle of the characteristic. + * @return The handle of the characteristic. + */ +uint16_t BLECharacteristic::getHandle() { + return m_handle; +} // getHandle + +void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) { + m_permissions = perm; +} + +esp_gatt_char_prop_t BLECharacteristic::getProperties() { + return m_properties; +} // getProperties + + +/** + * @brief Get the service associated with this characteristic. + */ +BLEService* BLECharacteristic::getService() { + return m_pService; +} // getService + + +/** + * @brief Get the UUID of the characteristic. + * @return The UUID of the characteristic. + */ +BLEUUID BLECharacteristic::getUUID() { + return m_bleUUID; +} // getUUID + + +/** + * @brief Retrieve the current value of the characteristic. + * @return A pointer to storage containing the current characteristic value. + */ +std::string BLECharacteristic::getValue() { + return m_value.getValue(); +} // getValue + +/** + * @brief Retrieve the current raw data of the characteristic. + * @return A pointer to storage containing the current characteristic data. + */ +uint8_t* BLECharacteristic::getData() { + return m_value.getData(); +} // getData + + +/** + * Handle a GATT server event. + */ +void BLECharacteristic::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + + switch(event) { + // Events handled: + // + // ESP_GATTS_ADD_CHAR_EVT + // ESP_GATTS_CONF_EVT + // ESP_GATTS_CONNECT_EVT + // ESP_GATTS_DISCONNECT_EVT + // ESP_GATTS_EXEC_WRITE_EVT + // ESP_GATTS_READ_EVT + // ESP_GATTS_WRITE_EVT + + // + // ESP_GATTS_EXEC_WRITE_EVT + // When we receive this event it is an indication that a previous write long needs to be committed. + // + // exec_write: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL + // + case ESP_GATTS_EXEC_WRITE_EVT: { + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + m_value.commit(); + if (m_pCallbacks != nullptr) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } else { + m_value.cancel(); + } +// ??? + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, + param->write.conn_id, + param->write.trans_id, ESP_GATT_OK, nullptr); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + break; + } // ESP_GATTS_EXEC_WRITE_EVT + + + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + case ESP_GATTS_ADD_CHAR_EVT: { + if (getHandle() == param->add_char.attr_handle) { + // we have created characteristic, now we can create descriptors + // BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + // while (pDescriptor != nullptr) { + // pDescriptor->executeCreate(this); + // pDescriptor = m_descriptorMap.getNext(); + // } // End while + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t *value + // + case ESP_GATTS_WRITE_EVT: { +// We check if this write request is for us by comparing the handles in the event. If it is for us +// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need +// to send a response. If we do, then we formulate a response and send it. + if (param->write.handle == m_handle) { + if (param->write.is_prep) { + m_value.addPart(param->write.value, param->write.len); + } else { + setValue(param->write.value, param->write.len); + } + + ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", + getHandle(), getUUID().toString().c_str()); + + char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); + ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData); + free(pHexData); + + if (param->write.need_rsp) { + esp_gatt_rsp_t rsp; + + rsp.attr_value.len = param->write.len; + rsp.attr_value.handle = m_handle; + rsp.attr_value.offset = param->write.offset; + rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + memcpy(rsp.attr_value.value, param->write.value, param->write.len); + + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, + param->write.conn_id, + param->write.trans_id, ESP_GATT_OK, &rsp); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // Response needed + + if (m_pCallbacks != nullptr && param->write.is_prep != true) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } // Match on handles. + break; + } // ESP_GATTS_WRITE_EVT + + + // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + if (param->read.handle == m_handle) { + + + +// Here's an interesting thing. The read request has the option of saying whether we need a response +// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like +// a very strange read. +// +// We have to handle the case where the data we wish to send back to the client is greater than the maximum +// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes. +// The apparent algorithm is as follows: +// +// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. +// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than +// 22 bytes, then we "just" send it and thats the end of the story. +// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. +// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. +// Because of follow on request processing, we need to maintain an offset of how much data we have already sent +// so that when a follow on request arrives, we know where to start in the data to send the next sequence. +// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response. +// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length. +// +// The following code has deliberately not been factored to make it fewer statements because this would cloud the +// the logic flow comprehension. +// + + // get mtu for peer device that we are sending read request to + uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1; + ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset); + if (param->read.need_rsp) { + ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); + esp_gatt_rsp_t rsp; + + if (param->read.is_long) { + std::string value = m_value.getValue(); + + if (value.length() - m_value.getReadOffset() < maxOffset) { + // This is the last in the chain + rsp.attr_value.len = value.length() - m_value.getReadOffset(); + rsp.attr_value.offset = m_value.getReadOffset(); + memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); + m_value.setReadOffset(0); + } else { + // There will be more to come. + rsp.attr_value.len = maxOffset; + rsp.attr_value.offset = m_value.getReadOffset(); + memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); + m_value.setReadOffset(rsp.attr_value.offset + maxOffset); + } + } else { // read.is_long == false + + std::string value = m_value.getValue(); + + if (value.length() + 1 > maxOffset) { + // Too big for a single shot entry. + m_value.setReadOffset(maxOffset); + rsp.attr_value.len = maxOffset; + rsp.attr_value.offset = 0; + memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); + } else { + // Will fit in a single packet with no callbacks required. + rsp.attr_value.len = value.length(); + rsp.attr_value.offset = 0; + memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); + } + + if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + m_pCallbacks->onRead(this); // Invoke the read callback. + } + } + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + + char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len); + ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset); + free(pHexData); + + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, param->read.conn_id, + param->read.trans_id, + ESP_GATT_OK, + &rsp); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // Response needed + } // Handle matches this characteristic. + break; + } // ESP_GATTS_READ_EVT + + + // ESP_GATTS_CONF_EVT + // + // conf: + // - esp_gatt_status_t status – The status code. + // - uint16_t conn_id – The connection used. + // + case ESP_GATTS_CONF_EVT: { + // ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); + if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet + m_semaphoreConfEvt.give(param->conf.status); + break; + } + + case ESP_GATTS_CONNECT_EVT: { + break; + } + + case ESP_GATTS_DISCONNECT_EVT: { + m_semaphoreConfEvt.give(); + break; + } + + default: { + break; + } // default + + } // switch event + + // Give each of the descriptors associated with this characteristic the opportunity to handle the + // event. + + m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); +} // handleGATTServerEvent + + +/** + * @brief Send an indication. + * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication + * will block waiting a positive confirmation from the client. + * @return N/A + */ +void BLECharacteristic::indicate() { + + ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); + notify(false); + ESP_LOGD(LOG_TAG, "<< indicate"); +} // indicate + + +/** + * @brief Send a notify. + * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification + * will not block; it is a fire and forget. + * @return N/A. + */ +void BLECharacteristic::notify(bool is_notification) { + ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); + + assert(getService() != nullptr); + assert(getService()->getServer() != nullptr); + + GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); + + if (getService()->getServer()->getConnectedCount() == 0) { + ESP_LOGD(LOG_TAG, "<< notify: No connected clients."); + return; + } + + // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled + // and, if not, prevent the notification. + + BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(is_notification) { + if (p2902 != nullptr && !p2902->getNotifications()) { + ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); + return; + } + } + else{ + if (p2902 != nullptr && !p2902->getIndications()) { + ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); + return; + } + } + for (auto &myPair : getService()->getServer()->getPeerDevices(false)) { + uint16_t _mtu = (myPair.second.mtu); + if (m_value.getValue().length() > _mtu - 3) { + ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); + } + + size_t length = m_value.getValue().length(); + if(!is_notification) + m_semaphoreConfEvt.take("indicate"); + esp_err_t errRc = ::esp_ble_gatts_send_indicate( + getService()->getServer()->getGattsIf(), + myPair.first, + getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreConfEvt.give(); + return; + } + if(!is_notification) + m_semaphoreConfEvt.wait("indicate"); + } + ESP_LOGD(LOG_TAG, "<< notify"); +} // Notify + + +/** + * @brief Set the permission to broadcast. + * A characteristics has properties associated with it which define what it is capable of doing. + * One of these is the broadcast flag. + * @param [in] value The flag value of the property. + * @return N/A + */ +void BLECharacteristic::setBroadcastProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST); + } +} // setBroadcastProperty + + +/** + * @brief Set the callback handlers for this characteristic. + * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. + */ +void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) { + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks); + m_pCallbacks = pCallbacks; + ESP_LOGD(LOG_TAG, "<< setCallbacks"); +} // setCallbacks + + +/** + * @brief Set the BLE handle associated with this characteristic. + * A user program will request that a characteristic be created against a service. When the characteristic has been + * registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the + * server/service but it is told to the service, not the characteristic associated with the service. This internally + * exposed function can be invoked by the service against this model of the characteristic to allow the characteristic + * to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events + * that will be propagated down to it which contain a handle value and now know that the event is destined for it. + * @param [in] handle The handle associated with this characteristic. + */ +void BLECharacteristic::setHandle(uint16_t handle) { + ESP_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str()); + m_handle = handle; + ESP_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Set the Indicate property value. + * @param [in] value Set to true if we are to allow indicate messages. + */ +void BLECharacteristic::setIndicateProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE); + } +} // setIndicateProperty + + +/** + * @brief Set the Notify property value. + * @param [in] value Set to true if we are to allow notification messages. + */ +void BLECharacteristic::setNotifyProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY); + } +} // setNotifyProperty + + +/** + * @brief Set the Read property value. + * @param [in] value Set to true if we are to allow reads. + */ +void BLECharacteristic::setReadProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ); + } +} // setReadProperty + + +/** + * @brief Set the value of the characteristic. + * @param [in] data The data to set for the characteristic. + * @param [in] length The length of the data in bytes. + */ +void BLECharacteristic::setValue(uint8_t* data, size_t length) { + char* pHex = BLEUtils::buildHexData(nullptr, data, length); + ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); + free(pHex); + if (length > ESP_GATT_MAX_ATTR_LEN) { + ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); + return; + } + m_value.setValue(data, length); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue + + +/** + * @brief Set the value of the characteristic from string data. + * We set the value of the characteristic from the bytes contained in the + * string. + * @param [in] Set the value of the characteristic. + * @return N/A. + */ +void BLECharacteristic::setValue(std::string value) { + setValue((uint8_t*)(value.data()), value.length()); +} // setValue + +void BLECharacteristic::setValue(uint16_t& data16) { + uint8_t temp[2]; + temp[0] = data16; + temp[1] = data16 >> 8; + setValue(temp, 2); +} // setValue + +void BLECharacteristic::setValue(uint32_t& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(int& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(float& data32) { + uint8_t temp[4]; + *((float*)temp) = data32; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(double& data64) { + uint8_t temp[8]; + *((double*)temp) = data64; + setValue(temp, 8); +} // setValue + + +/** + * @brief Set the Write No Response property value. + * @param [in] value Set to true if we are to allow writes with no response. + */ +void BLECharacteristic::setWriteNoResponseProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR); + } +} // setWriteNoResponseProperty + + +/** + * @brief Set the Write property value. + * @param [in] value Set to true if we are to allow writes. + */ +void BLECharacteristic::setWriteProperty(bool value) { + //ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value); + if (value) { + m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE); + } else { + m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE); + } +} // setWriteProperty + + +/** + * @brief Return a string representation of the characteristic. + * @return A string representation of the characteristic. + */ +std::string BLECharacteristic::toString() { + std::stringstream stringstream; + stringstream << std::hex << std::setfill('0'); + stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle; + stringstream << " " << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_READ) ? "Read " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) ? "Write " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) ? "WriteNoResponse " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) ? "Broadcast " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) ? "Notify " : "") << + ((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) ? "Indicate " : ""); + return stringstream.str(); +} // toString + + +BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} + + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) { + ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); + ESP_LOGD("BLECharacteristicCallbacks", "<< onRead"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) { + ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); + ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite"); +} // onWrite + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.h new file mode 100644 index 0000000..5eb1e8d --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristic.h @@ -0,0 +1,137 @@ +/* + * BLECharacteristic.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ +#define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEUUID.h" +#include +#include +#include "BLEDescriptor.h" +#include "BLEValue.h" +#include "FreeRTOS.h" + +class BLEService; +class BLEDescriptor; +class BLECharacteristicCallbacks; + +/** + * @brief A management structure for %BLE descriptors. + */ +class BLEDescriptorMap { +public: + void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); + void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); + void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); + BLEDescriptor* getByUUID(const char* uuid); + BLEDescriptor* getByUUID(BLEUUID uuid); + BLEDescriptor* getByHandle(uint16_t handle); + std::string toString(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + BLEDescriptor* getFirst(); + BLEDescriptor* getNext(); +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE Characteristic. + * + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and + * can be read and written to by a %BLE client. + */ +class BLECharacteristic { +public: + BLECharacteristic(const char* uuid, uint32_t properties = 0); + BLECharacteristic(BLEUUID uuid, uint32_t properties = 0); + virtual ~BLECharacteristic(); + + void addDescriptor(BLEDescriptor* pDescriptor); + BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); + BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); + BLEUUID getUUID(); + std::string getValue(); + uint8_t* getData(); + + void indicate(); + void notify(bool is_notification = true); + void setBroadcastProperty(bool value); + void setCallbacks(BLECharacteristicCallbacks* pCallbacks); + void setIndicateProperty(bool value); + void setNotifyProperty(bool value); + void setReadProperty(bool value); + void setValue(uint8_t* data, size_t size); + void setValue(std::string value); + void setValue(uint16_t& data16); + void setValue(uint32_t& data32); + void setValue(int& data32); + void setValue(float& data32); + void setValue(double& data64); + void setWriteProperty(bool value); + void setWriteNoResponseProperty(bool value); + std::string toString(); + uint16_t getHandle(); + void setAccessPermissions(esp_gatt_perm_t perm); + + static const uint32_t PROPERTY_READ = 1<<0; + static const uint32_t PROPERTY_WRITE = 1<<1; + static const uint32_t PROPERTY_NOTIFY = 1<<2; + static const uint32_t PROPERTY_BROADCAST = 1<<3; + static const uint32_t PROPERTY_INDICATE = 1<<4; + static const uint32_t PROPERTY_WRITE_NR = 1<<5; + +private: + + friend class BLEServer; + friend class BLEService; + friend class BLEDescriptor; + friend class BLECharacteristicMap; + + BLEUUID m_bleUUID; + BLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + esp_gatt_char_prop_t m_properties; + BLECharacteristicCallbacks* m_pCallbacks; + BLEService* m_pService; + BLEValue m_value; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + + void executeCreate(BLEService* pService); + esp_gatt_char_prop_t getProperties(); + BLEService* getService(); + void setHandle(uint16_t handle); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); +}; // BLECharacteristic + + +/** + * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. + * + * When a server application creates a %BLE characteristic, we may wish to be informed when there is either + * a read or write request to the characteristic's value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class BLECharacteristicCallbacks { +public: + virtual ~BLECharacteristicCallbacks(); + virtual void onRead(BLECharacteristic* pCharacteristic); + virtual void onWrite(BLECharacteristic* pCharacteristic); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristicMap.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristicMap.cpp new file mode 100644 index 0000000..d73aae9 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLECharacteristicMap.cpp @@ -0,0 +1,133 @@ +/* + * BLECharacteristicMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEService.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + + +/** + * @brief Return the characteristic by handle. + * @param [in] handle The handle to look up the characteristic. + * @return The characteristic. + */ +BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) { + return getByUUID(BLEUUID(uuid)); +} + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Get the first characteristic in the map. + * @return The first characteristic in the map. + */ +BLECharacteristic* BLECharacteristicMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + BLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next characteristic in the map. + * @return The next characteristic in the map. + */ +BLECharacteristic* BLECharacteristicMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + BLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + + +/** + * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto& myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent + + +/** + * @brief Set the characteristic by handle. + * @param [in] handle The handle of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) { + m_handleMap.insert(std::pair(handle, characteristic)); +} // setByHandle + + +/** + * @brief Set the characteristic by UUID. + * @param [in] uuid The uuid of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) { + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); +} // setByUUID + + +/** + * @brief Return a string representation of the characteristic map. + * @return A string representation of the characteristic map. + */ +std::string BLECharacteristicMap::toString() { + std::stringstream stringStream; + stringStream << std::hex << std::setfill('0'); + int count = 0; + for (auto &myPair: m_uuidMap) { + if (count > 0) { + stringStream << "\n"; + } + count++; + stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); + } + return stringStream.str(); +} // toString + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.cpp new file mode 100644 index 0000000..0e552ec --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.cpp @@ -0,0 +1,536 @@ +/* + * BLEDevice.cpp + * + * Created on: Mar 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include "BLEClient.h" +#include "BLEUtils.h" +#include "BLEService.h" +#include "GeneralUtils.h" +#include +#include +#include +#include "BLEDevice.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEClient"; +#endif + + +/* + * Design + * ------ + * When we perform a searchService() requests, we are asking the BLE server to return each of the services + * that it exposes. For each service, we received an ESP_GATTC_SEARCH_RES_EVT event which contains details + * of the exposed service including its UUID. + * + * The objects we will invent for a BLEClient will be as follows: + * * BLERemoteService - A model of a remote service. + * * BLERemoteCharacteristic - A model of a remote characteristic + * * BLERemoteDescriptor - A model of a remote descriptor. + * + * Since there is a hierarchical relationship here, we will have the idea that from a BLERemoteService will own + * zero or more remote characteristics and a BLERemoteCharacteristic will own zero or more remote BLEDescriptors. + * + * We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics + * and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors. + * + * + */ + +BLEClient::BLEClient() { + m_pClientCallbacks = nullptr; + m_conn_id = ESP_GATT_IF_NONE; + m_gattc_if = ESP_GATT_IF_NONE; + m_haveServices = false; + m_isConnected = false; // Initially, we are flagged as not connected. +} // BLEClient + + +/** + * @brief Destructor. + */ +BLEClient::~BLEClient() { + // We may have allocated service references associated with this client. Before we are finished + // with the client, we must release resources. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); +} // ~BLEClient + + +/** + * @brief Clear any existing services. + * + */ +void BLEClient::clearServices() { + ESP_LOGD(LOG_TAG, ">> clearServices"); + // Delete all the services. + for (auto &myPair : m_servicesMap) { + delete myPair.second; + } + m_servicesMap.clear(); + m_haveServices = false; + ESP_LOGD(LOG_TAG, "<< clearServices"); +} // clearServices + +/** + * Add overloaded function to ease connect to peer device with not public address + */ +bool BLEClient::connect(BLEAdvertisedDevice* device) { + BLEAddress address = device->getAddress(); + esp_ble_addr_type_t type = device->getAddressType(); + return connect(address, type); +} + +/** + * @brief Connect to the partner (BLE Server). + * @param [in] address The address of the partner. + * @return True on success. + */ +bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) { + ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + +// We need the connection handle that we get from registering the application. We register the app +// and then block on its completion. When the event has arrived, we will have the handle. + m_appId = BLEDevice::m_appId++; + BLEDevice::addPeerDevice(this, true, m_appId); + m_semaphoreRegEvt.take("connect"); + + // clearServices(); // we dont need to delete services since every client is unique? + esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + m_semaphoreRegEvt.wait("connect"); + + m_peerAddress = address; + + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + errRc = ::esp_ble_gattc_open( + m_gattc_if, + *getPeerAddress().getNative(), // address + type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature. + 1 // direct connection <-- maybe needs to be changed in case of direct indirect connection??? + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); + return rc == ESP_GATT_OK; +} // connect + + +/** + * @brief Disconnect from the peer. + * @return N/A. + */ +void BLEClient::disconnect() { + ESP_LOGD(LOG_TAG, ">> disconnect()"); + esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + ESP_LOGD(LOG_TAG, "<< disconnect()"); +} // disconnect + + +/** + * @brief Handle GATT Client events + */ +void BLEClient::gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + + // Execute handler code based on the type of event received. + switch(event) { + + case ESP_GATTC_SRVC_CHG_EVT: + ESP_LOGI(LOG_TAG, "SERVICE CHANGED"); + break; + + case ESP_GATTC_CLOSE_EVT: { + // esp_ble_gattc_app_unregister(m_appId); + // BLEDevice::removePeerDevice(m_gattc_if, true); + break; + } + + // + // ESP_GATTC_DISCONNECT_EVT + // + // disconnect: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + case ESP_GATTC_DISCONNECT_EVT: { + // If we receive a disconnect event, set the class flag that indicates that we are + // no longer connected. + m_isConnected = false; + if (m_pClientCallbacks != nullptr) { + m_pClientCallbacks->onDisconnect(this); + } + BLEDevice::removePeerDevice(m_appId, true); + esp_ble_gattc_app_unregister(m_gattc_if); + m_semaphoreRssiCmplEvt.give(); + m_semaphoreSearchCmplEvt.give(1); + break; + } // ESP_GATTC_DISCONNECT_EVT + + // + // ESP_GATTC_OPEN_EVT + // + // open: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // + case ESP_GATTC_OPEN_EVT: { + m_conn_id = evtParam->open.conn_id; + if (m_pClientCallbacks != nullptr) { + m_pClientCallbacks->onConnect(this); + } + if (evtParam->open.status == ESP_GATT_OK) { + m_isConnected = true; // Flag us as connected. + } + m_semaphoreOpenEvt.give(evtParam->open.status); + break; + } // ESP_GATTC_OPEN_EVT + + + // + // ESP_GATTC_REG_EVT + // + // reg: + // esp_gatt_status_t status + // uint16_t app_id + // + case ESP_GATTC_REG_EVT: { + m_gattc_if = gattc_if; + m_semaphoreRegEvt.give(); + break; + } // ESP_GATTC_REG_EVT + + case ESP_GATTC_CFG_MTU_EVT: + if(evtParam->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG,"Config mtu failed"); + } + m_mtu = evtParam->cfg_mtu.mtu; + break; + + case ESP_GATTC_CONNECT_EVT: { + BLEDevice::updatePeerDevice(this, true, m_gattc_if); + esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTC_CONNECT_EVT + + // + // ESP_GATTC_SEARCH_CMPL_EVT + // + // search_cmpl: + // - esp_gatt_status_t status + // - uint16_t conn_id + // + case ESP_GATTC_SEARCH_CMPL_EVT: { + esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam; + if (p_data->search_cmpl.status != ESP_GATT_OK){ + ESP_LOGE(LOG_TAG, "search service failed, error status = %x", p_data->search_cmpl.status); + break; + } +#ifndef ARDUINO_ARCH_ESP32 +// commented out just for now to keep backward compatibility + // if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) { + // ESP_LOGI(LOG_TAG, "Get service information from remote device"); + // } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) { + // ESP_LOGI(LOG_TAG, "Get service information from flash"); + // } else { + // ESP_LOGI(LOG_TAG, "unknown service source"); + // } +#endif + m_semaphoreSearchCmplEvt.give(0); + break; + } // ESP_GATTC_SEARCH_CMPL_EVT + + + // + // ESP_GATTC_SEARCH_RES_EVT + // + // search_res: + // - uint16_t conn_id + // - uint16_t start_handle + // - uint16_t end_handle + // - esp_gatt_id_t srvc_id + // + case ESP_GATTC_SEARCH_RES_EVT: { + BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id); + BLERemoteService* pRemoteService = new BLERemoteService( + evtParam->search_res.srvc_id, + this, + evtParam->search_res.start_handle, + evtParam->search_res.end_handle + ); + m_servicesMap.insert(std::pair(uuid.toString(), pRemoteService)); + m_servicesMapByInstID.insert(std::pair(pRemoteService, evtParam->search_res.srvc_id.inst_id)); + break; + } // ESP_GATTC_SEARCH_RES_EVT + + + default: { + break; + } + } // Switch + + // Pass the request on to all services. + for (auto &myPair : m_servicesMap) { + myPair.second->gattClientEventHandler(event, gattc_if, evtParam); + } + +} // gattClientEventHandler + + +uint16_t BLEClient::getConnId() { + return m_conn_id; +} // getConnId + + + +esp_gatt_if_t BLEClient::getGattcIf() { + return m_gattc_if; +} // getGattcIf + + +/** + * @brief Retrieve the address of the peer. + * + * Returns the Bluetooth device address of the %BLE peer to which this client is connected. + */ +BLEAddress BLEClient::getPeerAddress() { + return m_peerAddress; +} // getAddress + + +/** + * @brief Ask the BLE server for the RSSI value. + * @return The RSSI value. + */ +int BLEClient::getRssi() { + ESP_LOGD(LOG_TAG, ">> getRssi()"); + if (!isConnected()) { + ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected"); + return 0; + } + // We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive + // an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion. + // + m_semaphoreRssiCmplEvt.take("getRssi"); + esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative()); + if (rc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc)); + return 0; + } + int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi"); + ESP_LOGD(LOG_TAG, "<< getRssi(): %d", rssiValue); + return rssiValue; +} // getRssi + + +/** + * @brief Get the service BLE Remote Service instance corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A reference to the Service or nullptr if don't know about it. + */ +BLERemoteService* BLEClient::getService(const char* uuid) { + return getService(BLEUUID(uuid)); +} // getService + + +/** + * @brief Get the service object corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A reference to the Service or nullptr if don't know about it. + * @throws BLEUuidNotFound + */ +BLERemoteService* BLEClient::getService(BLEUUID uuid) { + ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); +// Design +// ------ +// We wish to retrieve the service given its UUID. It is possible that we have not yet asked the +// device what services it has in which case we have nothing to match against. If we have not +// asked the device about its services, then we do that now. Once we get the results we can then +// examine the services map to see if it has the service we are looking for. + if (!m_haveServices) { + getServices(); + } + std::string uuidStr = uuid.toString(); + for (auto &myPair : m_servicesMap) { + if (myPair.first == uuidStr) { + ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); + return myPair.second; + } + } // End of each of the services. + ESP_LOGD(LOG_TAG, "<< getService: not found"); + return nullptr; +} // getService + + +/** + * @brief Ask the remote %BLE server for its services. + * A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of + * services and wait until we have received them all. + * @return N/A + */ +std::map* BLEClient::getServices() { +/* + * Design + * ------ + * We invoke esp_ble_gattc_search_service. This will request a list of the service exposed by the + * peer BLE partner to be returned as events. Each event will be an an instance of ESP_GATTC_SEARCH_RES_EVT + * and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received. + */ + ESP_LOGD(LOG_TAG, ">> getServices"); +// TODO implement retrieving services from cache + clearServices(); // Clear any services that may exist. + + esp_err_t errRc = esp_ble_gattc_search_service( + getGattcIf(), + getConnId(), + NULL // Filter UUID + ); + + m_semaphoreSearchCmplEvt.take("getServices"); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return &m_servicesMap; + } + // If sucessfull, remember that we now have services. + m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0); + ESP_LOGD(LOG_TAG, "<< getServices"); + return &m_servicesMap; +} // getServices + + +/** + * @brief Get the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to read. + * @throws BLEUuidNotFound + */ +std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue(); + ESP_LOGD(LOG_TAG, "<read_rssi_cmpl.rssi); + break; + } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + + default: + break; + } +} // handleGAPEvent + + +/** + * @brief Are we connected to a partner? + * @return True if we are connected and false if we are not connected. + */ +bool BLEClient::isConnected() { + return m_isConnected; +} // isConnected + + + + +/** + * @brief Set the callbacks that will be invoked. + */ +void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) { + m_pClientCallbacks = pClientCallbacks; +} // setClientCallbacks + + +/** + * @brief Set the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to write. + * @throws BLEUuidNotFound + */ +void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue + +uint16_t BLEClient::getMTU() { + return m_mtu; +} + +/** + * @brief Return a string representation of this client. + * @return A string representation of this client. + */ +std::string BLEClient::toString() { + std::ostringstream ss; + ss << "peer address: " << m_peerAddress.toString(); + ss << "\nServices:\n"; + for (auto &myPair : m_servicesMap) { + ss << myPair.second->toString() << "\n"; + // myPair.second is the value + } + return ss.str(); +} // toString + + +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.h new file mode 100644 index 0000000..1b8144d --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEClient.h @@ -0,0 +1,103 @@ +/* + * BLEDevice.h + * + * Created on: Mar 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEDEVICE_H_ +#define MAIN_BLEDEVICE_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include +#include +#include +#include "BLEExceptions.h" +#include "BLERemoteService.h" +#include "BLEService.h" +#include "BLEAddress.h" +#include "BLEAdvertisedDevice.h" + +class BLERemoteService; +class BLEClientCallbacks; +class BLEAdvertisedDevice; + +/** + * @brief A model of a %BLE client. + */ +class BLEClient { +public: + BLEClient(); + ~BLEClient(); + + bool connect(BLEAdvertisedDevice* device); + bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server + void disconnect(); // Disconnect from the remote BLE Server + BLEAddress getPeerAddress(); // Get the address of the remote BLE Server + int getRssi(); // Get the RSSI of the remote BLE Server + std::map* getServices(); // Get a map of the services offered by the remote BLE Server + BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server. + BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server. + std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service. + + + void handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + + bool isConnected(); // Return true if we are connected. + + void setClientCallbacks(BLEClientCallbacks *pClientCallbacks); + void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service. + + std::string toString(); // Return a string representation of this client. + uint16_t getConnId(); + esp_gatt_if_t getGattcIf(); + uint16_t getMTU(); + +uint16_t m_appId; +private: + friend class BLEDevice; + friend class BLERemoteService; + friend class BLERemoteCharacteristic; + friend class BLERemoteDescriptor; + + void gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param); + + BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server. + uint16_t m_conn_id; +// int m_deviceType; + esp_gatt_if_t m_gattc_if; + bool m_haveServices = false; // Have we previously obtain the set of services from the remote server. + bool m_isConnected = false; // Are we currently connected. + + BLEClientCallbacks* m_pClientCallbacks; + FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt"); + FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); + FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt"); + FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt"); + std::map m_servicesMap; + std::map m_servicesMapByInstID; + void clearServices(); // Clear any existing services. + uint16_t m_mtu = 23; +}; // class BLEDevice + + +/** + * @brief Callbacks associated with a %BLE client. + */ +class BLEClientCallbacks { +public: + virtual ~BLEClientCallbacks() {}; + virtual void onConnect(BLEClient *pClient) = 0; + virtual void onDisconnect(BLEClient *pClient) = 0; +}; + +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_BLEDEVICE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.cpp new file mode 100644 index 0000000..ba5753d --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.cpp @@ -0,0 +1,296 @@ +/* + * BLEDescriptor.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include "sdkconfig.h" +#include +#include "BLEService.h" +#include "BLEDescriptor.h" +#include "GeneralUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEDescriptor"; +#endif + + + + +#define NULL_HANDLE (0xffff) + + +/** + * @brief BLEDescriptor constructor. + */ +BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) { +} + +/** + * @brief BLEDescriptor constructor. + */ +BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) { + m_bleUUID = uuid; + m_value.attr_len = 0; // Initial length is 0. + m_value.attr_max_len = max_len; // Maximum length of the data. + m_handle = NULL_HANDLE; // Handle is initially unknown. + m_pCharacteristic = nullptr; // No initial characteristic. + m_pCallback = nullptr; // No initial callback. + + m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. +} // BLEDescriptor + + +/** + * @brief BLEDescriptor destructor. + */ +BLEDescriptor::~BLEDescriptor() { + free(m_value.attr_value); // Release the storage we created in the constructor. +} // ~BLEDescriptor + + +/** + * @brief Execute the creation of the descriptor with the BLE runtime in ESP. + * @param [in] pCharacteristic The characteristic to which to register this descriptor. + */ +void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { + ESP_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str()); + + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "Descriptor already has a handle."); + return; + } + + m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. + + esp_attr_control_t control; + control.auto_rsp = ESP_GATT_AUTO_RSP; + m_semaphoreCreateEvt.take("executeCreate"); + esp_err_t errRc = ::esp_ble_gatts_add_char_descr( + pCharacteristic->getService()->getHandle(), + getUUID().getNative(), + (esp_gatt_perm_t)m_permissions, + &m_value, + &control); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreCreateEvt.wait("executeCreate"); + ESP_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Get the BLE handle for this descriptor. + * @return The handle for this descriptor. + */ +uint16_t BLEDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the length of the value of this descriptor. + * @return The length (in bytes) of the value of this descriptor. + */ +size_t BLEDescriptor::getLength() { + return m_value.attr_len; +} // getLength + + +/** + * @brief Get the UUID of the descriptor. + */ +BLEUUID BLEDescriptor::getUUID() { + return m_bleUUID; +} // getUUID + + + +/** + * @brief Get the value of this descriptor. + * @return A pointer to the value of this descriptor. + */ +uint8_t* BLEDescriptor::getValue() { + return m_value.attr_value; +} // getValue + + +/** + * @brief Handle GATT server events for the descripttor. + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +void BLEDescriptor::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + switch (event) { + // ESP_GATTS_ADD_CHAR_DESCR_EVT + // + // add_char_descr: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + case ESP_GATTS_ADD_CHAR_DESCR_EVT: { + if (m_pCharacteristic != nullptr && + m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) && + m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && + m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { + setHandle(param->add_char_descr.attr_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_ADD_CHAR_DESCR_EVT + + // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t *value + case ESP_GATTS_WRITE_EVT: { + if (param->write.handle == m_handle) { + setValue(param->write.value, param->write.len); // Set the value of the descriptor. + + if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. + m_pCallback->onWrite(this); // Invoke the onWrite callback handler. + } + } // End of ... this is our handle. + + break; + } // ESP_GATTS_WRITE_EVT + + // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it + + if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now. + m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler. + } + + } // End of this is our handle + break; + } // ESP_GATTS_READ_EVT + + default: + break; + } // switch event +} // handleGATTServerEvent + + +/** + * @brief Set the callback handlers for this descriptor. + * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. + */ +void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) { + ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t) pCallback); + m_pCallback = pCallback; + ESP_LOGD(LOG_TAG, "<< setCallbacks"); +} // setCallbacks + + +/** + * @brief Set the handle of this descriptor. + * Set the handle of this descriptor to be the supplied value. + * @param [in] handle The handle to be associated with this descriptor. + * @return N/A. + */ +void BLEDescriptor::setHandle(uint16_t handle) { + ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); + m_handle = handle; + ESP_LOGD(LOG_TAG, "<< setHandle()"); +} // setHandle + + +/** + * @brief Set the value of the descriptor. + * @param [in] data The data to set for the descriptor. + * @param [in] length The length of the data in bytes. + */ +void BLEDescriptor::setValue(uint8_t* data, size_t length) { + if (length > ESP_GATT_MAX_ATTR_LEN) { + ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); + return; + } + m_value.attr_len = length; + memcpy(m_value.attr_value, data, length); +} // setValue + + +/** + * @brief Set the value of the descriptor. + * @param [in] value The value of the descriptor in string form. + */ +void BLEDescriptor::setValue(std::string value) { + setValue((uint8_t*) value.data(), value.length()); +} // setValue + +void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) { + m_permissions = perm; +} + +/** + * @brief Return a string representation of the descriptor. + * @return A string representation of the descriptor. + */ +std::string BLEDescriptor::toString() { + std::stringstream stringstream; + stringstream << std::hex << std::setfill('0'); + stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle; + return stringstream.str(); +} // toString + + +BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) { + ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default"); + ESP_LOGD("BLEDescriptorCallbacks", "<< onRead"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) { + ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default"); + ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite"); +} // onWrite + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.h new file mode 100644 index 0000000..03cc579 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptor.h @@ -0,0 +1,77 @@ +/* + * BLEDescriptor.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ +#define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLEUUID.h" +#include "BLECharacteristic.h" +#include +#include "FreeRTOS.h" + +class BLEService; +class BLECharacteristic; +class BLEDescriptorCallbacks; + +/** + * @brief A model of a %BLE descriptor. + */ +class BLEDescriptor { +public: + BLEDescriptor(const char* uuid, uint16_t max_len = 100); + BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); + virtual ~BLEDescriptor(); + + uint16_t getHandle(); // Get the handle of the descriptor. + size_t getLength(); // Get the length of the value of the descriptor. + BLEUUID getUUID(); // Get the UUID of the descriptor. + uint8_t* getValue(); // Get a pointer to the value of the descriptor. + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + + void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor. + void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. + void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(std::string value); // Set the value of the descriptor as a data buffer. + + std::string toString(); // Convert the descriptor to a string representation. + +private: + friend class BLEDescriptorMap; + friend class BLECharacteristic; + BLEUUID m_bleUUID; + uint16_t m_handle; + BLEDescriptorCallbacks* m_pCallback; + BLECharacteristic* m_pCharacteristic; + esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + esp_attr_value_t m_value; + + void executeCreate(BLECharacteristic* pCharacteristic); + void setHandle(uint16_t handle); +}; // BLEDescriptor + + +/** + * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. + * + * When a server application creates a %BLE descriptor, we may wish to be informed when there is either + * a read or write request to the descriptors value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class BLEDescriptorCallbacks { +public: + virtual ~BLEDescriptorCallbacks(); + virtual void onRead(BLEDescriptor* pDescriptor); + virtual void onWrite(BLEDescriptor* pDescriptor); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptorMap.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptorMap.cpp new file mode 100644 index 0000000..6b84583 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDescriptorMap.cpp @@ -0,0 +1,147 @@ +/* + * BLEDescriptorMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLECharacteristic.h" +#include "BLEDescriptor.h" +#include // ESP32 BLE +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-log.h" +#endif + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) { + return getByUUID(BLEUUID(uuid)); +} + + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the descriptor by handle. + * @param [in] handle The handle to look up the descriptor. + * @return The descriptor. + */ +BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){ + m_uuidMap.insert(std::pair(pDescriptor, uuid)); +} // setByUUID + + + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) { + m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the descriptor by handle. + * @param [in] handle The handle of the descriptor. + * @param [in] descriptor The descriptor to cache. + * @return N/A. + */ +void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) { + m_handleMap.insert(std::pair(handle, pDescriptor)); +} // setByHandle + + +/** + * @brief Return a string representation of the descriptor map. + * @return A string representation of the descriptor map. + */ +std::string BLEDescriptorMap::toString() { + std::stringstream stringStream; + stringStream << std::hex << std::setfill('0'); + int count = 0; + for (auto &myPair : m_uuidMap) { + if (count > 0) { + stringStream << "\n"; + } + count++; + stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString(); + } + return stringStream.str(); +} // toString + + +/** + * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +void BLEDescriptorMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every descriptor we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent + + +/** + * @brief Get the first descriptor in the map. + * @return The first descriptor in the map. + */ +BLEDescriptor* BLEDescriptorMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next descriptor in the map. + * @return The next descriptor in the map. + */ +BLEDescriptor* BLEDescriptorMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.cpp new file mode 100644 index 0000000..2074298 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.cpp @@ -0,0 +1,646 @@ +/* + * BLE.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 ESP-IDF +#include // Part of C++ Standard library +#include // Part of C++ Standard library +#include // Part of C++ Standard library + +#include "BLEDevice.h" +#include "BLEClient.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#include "esp32-hal-bt.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEDevice"; +#endif + + +/** + * Singletons for the BLEDevice. + */ +BLEServer* BLEDevice::m_pServer = nullptr; +BLEScan* BLEDevice::m_pScan = nullptr; +BLEClient* BLEDevice::m_pClient = nullptr; +bool initialized = false; +esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; +BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; +uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful +BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; +uint16_t BLEDevice::m_appId = 0; +std::map BLEDevice::m_connectedClientsMap; +gap_event_handler BLEDevice::m_customGapHandler = nullptr; +gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; +gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; + +/** + * @brief Create a new instance of a client. + * @return A new instance of the client. + */ +/* STATIC */ BLEClient* BLEDevice::createClient() { + ESP_LOGD(LOG_TAG, ">> createClient"); +#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTC_ENABLE + m_pClient = new BLEClient(); + ESP_LOGD(LOG_TAG, "<< createClient"); + return m_pClient; +} // createClient + + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +/* STATIC */ BLEServer* BLEDevice::createServer() { + ESP_LOGD(LOG_TAG, ">> createServer"); +#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE + m_pServer = new BLEServer(); + m_pServer->createApp(m_appId++); + ESP_LOGD(LOG_TAG, "<< createServer"); + return m_pServer; +} // createServer + + +/** + * @brief Handle GATT server events. + * + * @param [in] event The event that has been newly received. + * @param [in] gatts_if The connection to the GATT interface. + * @param [in] param Parameters for the event. + */ +/* STATIC */ void BLEDevice::gattServerEventHandler( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param +) { + ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s", + gatts_if, + BLEUtils::gattServerEventTypeToString(event).c_str()); + + BLEUtils::dumpGattServerEvent(event, gatts_if, param); + + switch (event) { + case ESP_GATTS_CONNECT_EVT: { +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTS_CONNECT_EVT + + default: { + break; + } + } // switch + + + if (BLEDevice::m_pServer != nullptr) { + BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param); + } + + if(m_customGattsHandler != nullptr) { + m_customGattsHandler(event, gatts_if, param); + } + +} // gattServerEventHandler + + +/** + * @brief Handle GATT client events. + * + * Handler for the GATT client events. + * + * @param [in] event + * @param [in] gattc_if + * @param [in] param + */ +/* STATIC */ void BLEDevice::gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s", + gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str()); + BLEUtils::dumpGattClientEvent(event, gattc_if, param); + + switch(event) { + case ESP_GATTC_CONNECT_EVT: { +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityLevel){ + esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + } // ESP_GATTS_CONNECT_EVT + + default: + break; + } // switch + for(auto &myPair : BLEDevice::getPeerDevices(true)) { + conn_status_t conn_status = (conn_status_t)myPair.second; + if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){ + ((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param); + } + } + + if(m_customGattcHandler != nullptr) { + m_customGattcHandler(event, gattc_if, param); + } + + +} // gattClientEventHandler + + +/** + * @brief Handle GAP events. + */ +/* STATIC */ void BLEDevice::gapEventHandler( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t *param) { + + BLEUtils::dumpGapEvent(event, param); + + switch(event) { + + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + break; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: "); + // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda)); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * TODO should we add white/black list comparison? + */ + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should sent the security response with negative(false) accept value*/ + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks!=nullptr){ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); + } + else{ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + /* + * + */ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + //display the passkey number to the user to input it in the peer deivce within 30 seconds + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey); + if(BLEDevice::m_securityCallbacks!=nullptr){ + BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key type info share with peer device to the user. + ESP_LOGD(LOG_TAG, "ESP_GAP_BLE_KEY_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); +#endif // CONFIG_BLE_SMP_ENABLE + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT"); +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + if(BLEDevice::m_securityCallbacks != nullptr){ + BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); + } +#endif // CONFIG_BLE_SMP_ENABLE + break; + default: { + break; + } + } // switch + + if (BLEDevice::m_pClient != nullptr) { + BLEDevice::m_pClient->handleGAPEvent(event, param); + } + + if (BLEDevice::m_pScan != nullptr) { + BLEDevice::getScan()->handleGAPEvent(event, param); + } + + if(m_bleAdvertising != nullptr) { + BLEDevice::getAdvertising()->handleGAPEvent(event, param); + } + + if(m_customGapHandler != nullptr) { + BLEDevice::m_customGapHandler(event, param); + } + +} // gapEventHandler + + +/** + * @brief Get the BLE device address. + * @return The BLE device address. + */ +/* STATIC*/ BLEAddress BLEDevice::getAddress() { + const uint8_t* bdAddr = esp_bt_dev_get_address(); + esp_bd_addr_t addr; + memcpy(addr, bdAddr, sizeof(addr)); + return BLEAddress(addr); +} // getAddress + + +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. + */ +/* STATIC */ BLEScan* BLEDevice::getScan() { + //ESP_LOGD(LOG_TAG, ">> getScan"); + if (m_pScan == nullptr) { + m_pScan = new BLEScan(); + //ESP_LOGD(LOG_TAG, " - creating a new scan object"); + } + //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan); + return m_pScan; +} // getScan + + +/** + * @brief Get the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) { + ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient* pClient = createClient(); + pClient->connect(bdAddress); + std::string ret = pClient->getValue(serviceUUID, characteristicUUID); + pClient->disconnect(); + ESP_LOGD(LOG_TAG, "<< getValue"); + return ret; +} // getValue + + +/** + * @brief Initialize the %BLE environment. + * @param deviceName The device name of the device. + */ +/* STATIC */ void BLEDevice::init(std::string deviceName) { + if(!initialized){ + initialized = true; // Set the initialization flag to ensure we are only initialized once. + + esp_err_t errRc = ESP_OK; +#ifdef ARDUINO_ARCH_ESP32 + if (!btStart()) { + errRc = ESP_FAIL; + return; + } +#else + errRc = ::nvs_flash_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifndef CLASSIC_BT_ENABLED + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); +#endif + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + errRc = esp_bt_controller_init(&bt_cfg); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifndef CLASSIC_BT_ENABLED + errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#else + errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif +#endif + + esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); + if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) { + errRc = esp_bluedroid_init(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) { + errRc = esp_bluedroid_enable(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + +#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig + errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTC_ENABLE + +#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig + errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } +#endif // CONFIG_GATTS_ENABLE + + errRc = ::esp_ble_gap_set_device_name(deviceName.c_str()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; + +#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; + errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + }; +#endif // CONFIG_BLE_SMP_ENABLE + } + vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue. +} // initialize + + +/** + * @brief Set the transmission power. + * The power level can be one of: + * * ESP_PWR_LVL_N14 + * * ESP_PWR_LVL_N11 + * * ESP_PWR_LVL_N8 + * * ESP_PWR_LVL_N5 + * * ESP_PWR_LVL_N2 + * * ESP_PWR_LVL_P1 + * * ESP_PWR_LVL_P4 + * * ESP_PWR_LVL_P7 + * @param [in] powerLevel. + */ +/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) { + ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel); + esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + }; + ESP_LOGD(LOG_TAG, "<< setPower"); +} // setPower + + +/** + * @brief Set the value of a characteristic of a service on a remote device. + * @param [in] bdAddress + * @param [in] serviceUUID + * @param [in] characteristicUUID + */ +/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + BLEClient* pClient = createClient(); + pClient->connect(bdAddress); + pClient->setValue(serviceUUID, characteristicUUID, value); + pClient->disconnect(); +} // setValue + + +/** + * @brief Return a string representation of the nature of this device. + * @return A string representation of the nature of this device. + */ +/* STATIC */ std::string BLEDevice::toString() { + std::ostringstream oss; + oss << "BD Address: " << getAddress().toString(); + return oss.str(); +} // toString + + +/** + * @brief Add an entry to the BLE white list. + * @param [in] address The address to add to the white list. + */ +void BLEDevice::whiteListAdd(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListAdd"); +} // whiteListAdd + + +/** + * @brief Remove an entry from the BLE white list. + * @param [in] address The address to remove from the white list. + */ +void BLEDevice::whiteListRemove(BLEAddress address) { + ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str()); + esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + ESP_LOGD(LOG_TAG, "<< whiteListRemove"); +} // whiteListRemove + +/* + * @brief Set encryption level that will be negotiated with peer device durng connection + * @param [in] level Requested encryption level + */ +void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { + BLEDevice::m_securityLevel = level; +} + +/* + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback + */ +void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) { + BLEDevice::m_securityCallbacks = callbacks; +} + +/* + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer + * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517 + */ +esp_err_t BLEDevice::setMTU(uint16_t mtu) { + ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + esp_err_t err = esp_ble_gatt_set_local_mtu(mtu); + if (err == ESP_OK) { + m_localMTU = mtu; + } else { + ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu); + } + ESP_LOGD(LOG_TAG, "<< setLocalMTU"); + return err; +} + +/* + * @brief Get local MTU value set during mtu request or default value + */ +uint16_t BLEDevice::getMTU() { + return m_localMTU; +} + +bool BLEDevice::getInitialized() { + return initialized; +} + +BLEAdvertising* BLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) { + m_bleAdvertising = new BLEAdvertising(); + ESP_LOGI(LOG_TAG, "create advertising"); + } + ESP_LOGD(LOG_TAG, "get advertising"); + return m_bleAdvertising; +} + +void BLEDevice::startAdvertising() { + ESP_LOGD(LOG_TAG, ">> startAdvertising"); + getAdvertising()->start(); + ESP_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising + +/* multi connect support */ +/* requires a little more work */ +std::map BLEDevice::getPeerDevices(bool _client) { + return m_connectedClientsMap; +} + +BLEClient* BLEDevice::getClientByGattIf(uint16_t conn_id) { + return (BLEClient*)m_connectedClientsMap.find(conn_id)->second.peer_device; +} + +void BLEDevice::updatePeerDevice(void* peer, bool _client, uint16_t conn_id) { + ESP_LOGD(LOG_TAG, "update conn_id: %d, GATT role: %s", conn_id, _client? "client":"server"); + std::map::iterator it = m_connectedClientsMap.find(ESP_GATT_IF_NONE); + if (it != m_connectedClientsMap.end()) { + std::swap(m_connectedClientsMap[conn_id], it->second); + m_connectedClientsMap.erase(it); + }else{ + it = m_connectedClientsMap.find(conn_id); + if (it != m_connectedClientsMap.end()) { + conn_status_t _st = it->second; + _st.peer_device = peer; + std::swap(m_connectedClientsMap[conn_id], _st); + } + } +} + +void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + ESP_LOGI(LOG_TAG, "add conn_id: %d, GATT role: %s", conn_id, _client? "client":"server"); + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedClientsMap.insert(std::pair(conn_id, status)); +} + +void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) { + ESP_LOGI(LOG_TAG, "remove: %d, GATT role %s", conn_id, _client?"client":"server"); + if(m_connectedClientsMap.find(conn_id) != m_connectedClientsMap.end()) + m_connectedClientsMap.erase(conn_id); +} + +/* multi connect support */ + +/** + * @brief de-Initialize the %BLE environment. + * @param release_memory release the internal BT stack memory + */ +/* STATIC */ void BLEDevice::deinit(bool release_memory) { + if (!initialized) return; + + esp_bluedroid_disable(); + esp_bluedroid_deinit(); + esp_bt_controller_disable(); + esp_bt_controller_deinit(); +#ifndef ARDUINO_ARCH_ESP32 + if (release_memory) { + esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it) + } else { + initialized = false; + } +#endif +} + +void BLEDevice::setCustomGapHandler(gap_event_handler handler) { + m_customGapHandler = handler; +} + +void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) { + m_customGattcHandler = handler; +} + +void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { + m_customGattsHandler = handler; +} + +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.h new file mode 100644 index 0000000..e9cd40a --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEDevice.h @@ -0,0 +1,99 @@ +/* + * BLEDevice.h + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEDevice_H_ +#define MAIN_BLEDevice_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include // ESP32 BLE +#include // ESP32 BLE +#include // Part of C++ STL +#include +#include + +#include "BLEServer.h" +#include "BLEClient.h" +#include "BLEUtils.h" +#include "BLEScan.h" +#include "BLEAddress.h" + +/** + * @brief BLE functions. + */ +typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); +typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); +typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param); + +class BLEDevice { +public: + + static BLEClient* createClient(); // Create a new BLE client. + static BLEServer* createServer(); // Cretae a new BLE server. + static BLEAddress getAddress(); // Retrieve our own local BD address. + static BLEScan* getScan(); // Get the scan object + static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server. + static void init(std::string deviceName); // Initialize the local BLE environment. + static void setPower(esp_power_level_t powerLevel); // Set our power level. + static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server. + static std::string toString(); // Return a string representation of our device. + static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list. + static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list. + static void setEncryptionLevel(esp_ble_sec_act_t level); + static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks); + static esp_err_t setMTU(uint16_t mtu); + static uint16_t getMTU(); + static bool getInitialized(); // Returns the state of the device, is it initialized or not? + /* move advertising to BLEDevice for saving ram and flash in beacons */ + static BLEAdvertising* getAdvertising(); + static void startAdvertising(); + static uint16_t m_appId; + /* multi connect */ + static std::map getPeerDevices(bool client); + static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id); + static void removePeerDevice(uint16_t conn_id, bool client); + static BLEClient* getClientByGattIf(uint16_t conn_id); + static void setCustomGapHandler(gap_event_handler handler); + static void setCustomGattcHandler(gattc_event_handler handler); + static void setCustomGattsHandler(gatts_event_handler handler); + static void deinit(bool release_memory = false); + static uint16_t m_localMTU; + static esp_ble_sec_act_t m_securityLevel; + +private: + static BLEServer* m_pServer; + static BLEScan* m_pScan; + static BLEClient* m_pClient; + static BLESecurityCallbacks* m_securityCallbacks; + static BLEAdvertising* m_bleAdvertising; + static esp_gatt_if_t getGattcIF(); + static std::map m_connectedClientsMap; + + static void gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* param); + + static void gattServerEventHandler( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); + + static void gapEventHandler( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + +public: +/* custom gap and gatt handlers for flexibility */ + static gap_event_handler m_customGapHandler; + static gattc_event_handler m_customGattcHandler; + static gatts_event_handler m_customGattsHandler; + +}; // class BLE + +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_BLEDevice_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.cpp new file mode 100644 index 0000000..a92bcdb --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.cpp @@ -0,0 +1,150 @@ +/* + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include "BLEEddystoneTLM.h" + +static const char LOG_TAG[] = "BLEEddystoneTLM"; +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) + +BLEEddystoneTLM::BLEEddystoneTLM() { + beaconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t) ((float) 23.00); + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; +} // BLEEddystoneTLM + +std::string BLEEddystoneTLM::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +BLEUUID BLEEddystoneTLM::getUUID() { + return BLEUUID(beaconUUID); +} // getUUID + +uint8_t BLEEddystoneTLM::getVersion() { + return m_eddystoneData.version; +} // getVersion + +uint16_t BLEEddystoneTLM::getVolt() { + return m_eddystoneData.volt; +} // getVolt + +float BLEEddystoneTLM::getTemp() { + return (float)m_eddystoneData.temp; +} // getTemp + +uint32_t BLEEddystoneTLM::getCount() { + return m_eddystoneData.advCount; +} // getCount + +uint32_t BLEEddystoneTLM::getTime() { + return m_eddystoneData.tmil; +} // getTime + +std::string BLEEddystoneTLM::toString() { + std::stringstream ss; + std::string out = ""; + uint32_t rawsec; + ss << "Version "; + ss << std::dec << m_eddystoneData.version; + ss << "\n"; + + ss << "Battery Voltage "; + ss << std::dec << ENDIAN_CHANGE_U16(m_eddystoneData.volt); + ss << " mV\n"; + + ss << "Temperature "; + ss << (float) m_eddystoneData.temp; + ss << " °C\n"; + + ss << "Adv. Count "; + ss << std::dec << ENDIAN_CHANGE_U32(m_eddystoneData.advCount); + + ss << "\n"; + + ss << "Time "; + + rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + std::stringstream buffstream; + buffstream << "0000"; + buffstream << std::dec << rawsec / 864000; + std::string buff = buffstream.str(); + + ss << buff.substr(buff.length() - 4, buff.length()); + ss << "."; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec / 36000) % 24; + buff = buffstream.str(); + ss << buff.substr(buff.length()-2, buff.length()); + ss << ":"; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec / 600) % 60; + buff = buffstream.str(); + ss << buff.substr(buff.length() - 2, buff.length()); + ss << ":"; + + buffstream.str(""); + buffstream.clear(); + buffstream << "00"; + buffstream << std::dec << (rawsec / 10) % 60; + buff = buffstream.str(); + ss << buff.substr(buff.length() - 2, buff.length()); + ss << "\n"; + + return ss.str(); +} // toString + +/** + * Set the raw data for the beacon record. + */ +void BLEEddystoneTLM::setData(std::string data) { + if (data.length() != sizeof(m_eddystoneData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data.data(), data.length()); +} // setData + +void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->uuid.uuid16; +} // setUUID + +void BLEEddystoneTLM::setVersion(uint8_t version) { + m_eddystoneData.version = version; +} // setVersion + +void BLEEddystoneTLM::setVolt(uint16_t volt) { + m_eddystoneData.volt = volt; +} // setVolt + +void BLEEddystoneTLM::setTemp(float temp) { + m_eddystoneData.temp = (uint16_t)temp; +} // setTemp + +void BLEEddystoneTLM::setCount(uint32_t advCount) { + m_eddystoneData.advCount = advCount; +} // setCount + +void BLEEddystoneTLM::setTime(uint32_t tmil) { + m_eddystoneData.tmil = tmil; +} // setTime + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.h new file mode 100644 index 0000000..a93e224 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneTLM.h @@ -0,0 +1,51 @@ +/* + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _BLEEddystoneTLM_H_ +#define _BLEEddystoneTLM_H_ +#include "BLEUUID.h" + +#define EDDYSTONE_TLM_FRAME_TYPE 0x20 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class BLEEddystoneTLM { +public: + BLEEddystoneTLM(); + std::string getData(); + BLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + +private: + uint16_t beaconUUID; + struct { + uint8_t frameType; + uint8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed)) m_eddystoneData; + +}; // BLEEddystoneTLM + +#endif /* _BLEEddystoneTLM_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.cpp new file mode 100644 index 0000000..af3b674 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.cpp @@ -0,0 +1,148 @@ +/* + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEEddystoneURL.h" + +static const char LOG_TAG[] = "BLEEddystoneURL"; + +BLEEddystoneURL::BLEEddystoneURL() { + beaconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); +} // BLEEddystoneURL + +std::string BLEEddystoneURL::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +BLEUUID BLEEddystoneURL::getUUID() { + return BLEUUID(beaconUUID); +} // getUUID + +int8_t BLEEddystoneURL::getPower() { + return m_eddystoneData.advertisedTxPower; +} // getPower + +std::string BLEEddystoneURL::getURL() { + return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); +} // getURL + +std::string BLEEddystoneURL::getDecodedURL() { + std::string decodedURL = ""; + + switch (m_eddystoneData.url[0]) { + case 0x00: + decodedURL += "http://www."; + break; + case 0x01: + decodedURL += "https://www."; + break; + case 0x02: + decodedURL += "http://"; + break; + case 0x03: + decodedURL += "https://"; + break; + default: + decodedURL += m_eddystoneData.url[0]; + } + + for (int i = 1; i < lengthURL; i++) { + if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + default: + break; + } + } + } + return decodedURL; +} // getDecodedURL + + + +/** + * Set the raw data for the beacon record. + */ +void BLEEddystoneURL::setData(std::string data) { + if (data.length() > sizeof(m_eddystoneData)) { + ESP_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData)); + return; + } + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memcpy(&m_eddystoneData, data.data(), data.length()); + lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); +} // setData + +void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->uuid.uuid16; +} // setUUID + +void BLEEddystoneURL::setPower(int8_t advertisedTxPower) { + m_eddystoneData.advertisedTxPower = advertisedTxPower; +} // setPower + +void BLEEddystoneURL::setURL(std::string url) { + if (url.length() > sizeof(m_eddystoneData.url)) { + ESP_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url)); + return; + } + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + memcpy(m_eddystoneData.url, url.data(), url.length()); + lengthURL = url.length(); +} // setURL + + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.h new file mode 100644 index 0000000..0b538c0 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEEddystoneURL.h @@ -0,0 +1,43 @@ +/* + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _BLEEddystoneURL_H_ +#define _BLEEddystoneURL_H_ +#include "BLEUUID.h" + +#define EDDYSTONE_URL_FRAME_TYPE 0x10 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class BLEEddystoneURL { +public: + BLEEddystoneURL(); + std::string getData(); + BLEUUID getUUID(); + int8_t getPower(); + std::string getURL(); + std::string getDecodedURL(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(std::string url); + +private: + uint16_t beaconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed)) m_eddystoneData; + +}; // BLEEddystoneURL + +#endif /* _BLEEddystoneURL_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.cpp new file mode 100644 index 0000000..b6adfd8 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.cpp @@ -0,0 +1,9 @@ +/* + * BLExceptions.cpp + * + * Created on: Nov 27, 2017 + * Author: kolban + */ + +#include "BLEExceptions.h" + diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.h new file mode 100644 index 0000000..ea9db85 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEExceptions.h @@ -0,0 +1,31 @@ +/* + * BLExceptions.h + * + * Created on: Nov 27, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ +#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ +#include "sdkconfig.h" + +#if CONFIG_CXX_EXCEPTIONS != 1 +#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." +#endif + +#include + + +class BLEDisconnectedException : public std::exception { + const char* what() const throw () { + return "BLE Disconnected"; + } +}; + +class BLEUuidNotFoundException : public std::exception { + const char* what() const throw () { + return "No such UUID"; + } +}; + +#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.cpp new file mode 100644 index 0000000..69e18be --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.cpp @@ -0,0 +1,243 @@ +/* + * BLEHIDDevice.cpp + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLEHIDDevice.h" +#include "BLE2904.h" + + +BLEHIDDevice::BLEHIDDevice(BLEServer* server) { + /* + * Here we create mandatory services described in bluetooth specification + */ + m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a)); + m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40); + m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f)); + + /* + * Mandatory characteristic for device info service + */ + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ); + + /* + * Mandatory characteristics for HID service + */ + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ); + + /* + * Mandatory battery level characteristic with notification and presence descriptor + */ + BLE2904* batteryLevelDescriptor = new BLE2904(); + batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8); + batteryLevelDescriptor->setNamespace(1); + batteryLevelDescriptor->setUnit(0x27ad); + + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor); + m_batteryLevelCharacteristic->addDescriptor(new BLE2902()); + + /* + * This value is setup here because its default value in most usage cases, its very rare to use boot mode + * and we want to simplify library using as much as possible + */ + const uint8_t pMode[] = { 0x01 }; + protocolMode()->setValue((uint8_t*) pMode, 1); +} + +BLEHIDDevice::~BLEHIDDevice() { +} + +/* + * @brief + */ +void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { + m_reportMapCharacteristic->setValue(map, size); +} + +/* + * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service + */ +void BLEHIDDevice::startServices() { + m_deviceInfoService->start(); + m_hidService->start(); + m_batteryService->start(); +} + +/* + * @brief Create manufacturer characteristic (this characteristic is optional) + */ +BLECharacteristic* BLEHIDDevice::manufacturer() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ); + return m_manufacturerCharacteristic; +} + +/* + * @brief Set manufacturer name + * @param [in] name manufacturer name + */ +void BLEHIDDevice::manufacturer(std::string name) { + m_manufacturerCharacteristic->setValue(name); +} + +/* + * @brief + */ +void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { + uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; + m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); +} + +/* + * @brief + */ +void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { + uint8_t info[] = { 0x11, 0x1, country, flags }; + m_hidInfoCharacteristic->setValue(info, sizeof(info)); +} + +/* + * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic + * @return pointer to new input report characteristic + */ +BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) { + BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); + BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + BLE2902* p2902 = new BLE2902(); + inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x01 }; + inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + inputReportCharacteristic->addDescriptor(p2902); + inputReportCharacteristic->addDescriptor(inputReportDescriptor); + + return inputReportCharacteristic; +} + +/* + * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic + * @return Pointer to new output report characteristic + */ +BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) { + BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); + BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x02 }; + outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + outputReportCharacteristic->addDescriptor(outputReportDescriptor); + + return outputReportCharacteristic; +} + +/* + * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used + * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic + * @return Pointer to new feature report characteristic + */ +BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) { + BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); + BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908)); + + featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); + + uint8_t desc1_val[] = { reportID, 0x03 }; + featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); + featureReportCharacteristic->addDescriptor(featureReportDescriptor); + + return featureReportCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootInput() { + BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY); + bootInputCharacteristic->addDescriptor(new BLE2902()); + + return bootInputCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::bootOutput() { + return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR); +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::hidControl() { + return m_hidControlCharacteristic; +} + +/* + * @brief + */ +BLECharacteristic* BLEHIDDevice::protocolMode() { + return m_protocolModeCharacteristic; +} + +void BLEHIDDevice::setBatteryLevel(uint8_t level) { + m_batteryLevelCharacteristic->setValue(&level, 1); +} +/* + * @brief Returns battery level characteristic + * @ return battery level characteristic + *//* +BLECharacteristic* BLEHIDDevice::batteryLevel() { + return m_batteryLevelCharacteristic; +} + + + +BLECharacteristic* BLEHIDDevice::reportMap() { + return m_reportMapCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::pnp() { + return m_pnpCharacteristic; +} + + +BLECharacteristic* BLEHIDDevice::hidInfo() { + return m_hidInfoCharacteristic; +} +*/ +/* + * @brief + */ +BLEService* BLEHIDDevice::deviceInfo() { + return m_deviceInfoService; +} + +/* + * @brief + */ +BLEService* BLEHIDDevice::hidService() { + return m_hidService; +} + +/* + * @brief + */ +BLEService* BLEHIDDevice::batteryService() { + return m_batteryService; +} + +#endif // CONFIG_BT_ENABLED + diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.h new file mode 100644 index 0000000..33e6b46 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEHIDDevice.h @@ -0,0 +1,75 @@ +/* + * BLEHIDDevice.h + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ + +#ifndef _BLEHIDDEVICE_H_ +#define _BLEHIDDEVICE_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BLECharacteristic.h" +#include "BLEService.h" +#include "BLEDescriptor.h" +#include "BLE2902.h" +#include "HIDTypes.h" + +#define GENERIC_HID 0x03C0 +#define HID_KEYBOARD 0x03C1 +#define HID_MOUSE 0x03C2 +#define HID_JOYSTICK 0x03C3 +#define HID_GAMEPAD 0x03C4 +#define HID_TABLET 0x03C5 +#define HID_CARD_READER 0x03C6 +#define HID_DIGITAL_PEN 0x03C7 +#define HID_BARCODE 0x03C8 + +class BLEHIDDevice { +public: + BLEHIDDevice(BLEServer*); + virtual ~BLEHIDDevice(); + + void reportMap(uint8_t* map, uint16_t); + void startServices(); + + BLEService* deviceInfo(); + BLEService* hidService(); + BLEService* batteryService(); + + BLECharacteristic* manufacturer(); + void manufacturer(std::string name); + //BLECharacteristic* pnp(); + void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); + //BLECharacteristic* hidInfo(); + void hidInfo(uint8_t country, uint8_t flags); + //BLECharacteristic* batteryLevel(); + void setBatteryLevel(uint8_t level); + + + //BLECharacteristic* reportMap(); + BLECharacteristic* hidControl(); + BLECharacteristic* inputReport(uint8_t reportID); + BLECharacteristic* outputReport(uint8_t reportID); + BLECharacteristic* featureReport(uint8_t reportID); + BLECharacteristic* protocolMode(); + BLECharacteristic* bootInput(); + BLECharacteristic* bootOutput(); + +private: + BLEService* m_deviceInfoService; //0x180a + BLEService* m_hidService; //0x1812 + BLEService* m_batteryService = 0; //0x180f + + BLECharacteristic* m_manufacturerCharacteristic; //0x2a29 + BLECharacteristic* m_pnpCharacteristic; //0x2a50 + BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a + BLECharacteristic* m_reportMapCharacteristic; //0x2a4b + BLECharacteristic* m_hidControlCharacteristic; //0x2a4c + BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e + BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 +}; +#endif // CONFIG_BT_ENABLED +#endif /* _BLEHIDDEVICE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.cpp new file mode 100644 index 0000000..b6d36d8 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.cpp @@ -0,0 +1,588 @@ +/* + * BLERemoteCharacteristic.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#include "BLERemoteCharacteristic.h" + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include + +#include +#include "BLEExceptions.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#include "BLERemoteDescriptor.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class. +#endif + + + +/** + * @brief Constructor. + * @param [in] handle The BLE server side handle of this characteristic. + * @param [in] uuid The UUID of this characteristic. + * @param [in] charProp The properties of this characteristic. + * @param [in] pRemoteService A reference to the remote service to which this remote characteristic pertains. + */ +BLERemoteCharacteristic::BLERemoteCharacteristic( + uint16_t handle, + BLEUUID uuid, + esp_gatt_char_prop_t charProp, + BLERemoteService* pRemoteService) { + ESP_LOGD(LOG_TAG, ">> BLERemoteCharacteristic: handle: %d 0x%d, uuid: %s", handle, handle, uuid.toString().c_str()); + m_handle = handle; + m_uuid = uuid; + m_charProp = charProp; + m_pRemoteService = pRemoteService; + m_notifyCallback = nullptr; + + retrieveDescriptors(); // Get the descriptors for this characteristic + ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic"); +} // BLERemoteCharacteristic + + +/** + *@brief Destructor. + */ +BLERemoteCharacteristic::~BLERemoteCharacteristic() { + removeDescriptors(); // Release resources for any descriptor information we may have allocated. +} // ~BLERemoteCharacteristic + + +/** + * @brief Does the characteristic support broadcasting? + * @return True if the characteristic supports broadcasting. + */ +bool BLERemoteCharacteristic::canBroadcast() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0; +} // canBroadcast + + +/** + * @brief Does the characteristic support indications? + * @return True if the characteristic supports indications. + */ +bool BLERemoteCharacteristic::canIndicate() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0; +} // canIndicate + + +/** + * @brief Does the characteristic support notifications? + * @return True if the characteristic supports notifications. + */ +bool BLERemoteCharacteristic::canNotify() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0; +} // canNotify + + +/** + * @brief Does the characteristic support reading? + * @return True if the characteristic supports reading. + */ +bool BLERemoteCharacteristic::canRead() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0; +} // canRead + + +/** + * @brief Does the characteristic support writing? + * @return True if the characteristic supports writing. + */ +bool BLERemoteCharacteristic::canWrite() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0; +} // canWrite + + +/** + * @brief Does the characteristic support writing with no response? + * @return True if the characteristic supports writing with no response. + */ +bool BLERemoteCharacteristic::canWriteNoResponse() { + return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0; +} // canWriteNoResponse + + +/* +static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { + if (id1.id.inst_id != id2.id.inst_id) { + return false; + } + if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) { + return false; + } + return true; +} // compareSrvcId +*/ + +/* +static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) { + if (id1.inst_id != id2.inst_id) { + return false; + } + if (!BLEUUID(id1.uuid).equals(BLEUUID(id2.uuid))) { + return false; + } + return true; +} // compareCharId +*/ + + +/** + * @brief Handle GATT Client events. + * When an event arrives for a GATT client we give this characteristic the opportunity to + * take a look at it to see if there is interest in it. + * @param [in] event The type of event. + * @param [in] gattc_if The interface on which the event was received. + * @param [in] evtParam Payload data for the event. + * @returns N/A + */ +void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) { + switch(event) { + // ESP_GATTC_NOTIFY_EVT + // + // notify + // - uint16_t conn_id - The connection identifier of the server. + // - esp_bd_addr_t remote_bda - The device address of the BLE server. + // - uint16_t handle - The handle of the characteristic for which the event is being received. + // - uint16_t value_len - The length of the received data. + // - uint8_t* value - The received data. + // - bool is_notify - True if this is a notify, false if it is an indicate. + // + // We have received a notification event which means that the server wishes us to know about a notification + // piece of data. What we must now do is find the characteristic with the associated handle and then + // invoke its notification callback (if it has one). + case ESP_GATTC_NOTIFY_EVT: { + if (evtParam->notify.handle != getHandle()) break; + if (m_notifyCallback != nullptr) { + ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str()); + m_notifyCallback(this, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify); + } // End we have a callback function ... + break; + } // ESP_GATTC_NOTIFY_EVT + + // ESP_GATTC_READ_CHAR_EVT + // This event indicates that the server has responded to the read request. + // + // read: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + // - uint8_t* value + // - uint16_t value_len + case ESP_GATTC_READ_CHAR_EVT: { + // If this event is not for us, then nothing further to do. + if (evtParam->read.handle != getHandle()) break; + + // At this point, we have determined that the event is for us, so now we save the value + // and unlock the semaphore to ensure that the requestor of the data can continue. + if (evtParam->read.status == ESP_GATT_OK) { + m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len); + if(m_rawData != nullptr) free(m_rawData); + m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); + memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); + } else { + m_value = ""; + } + + m_semaphoreReadCharEvt.give(); + break; + } // ESP_GATTC_READ_CHAR_EVT + + // ESP_GATTC_REG_FOR_NOTIFY_EVT + // + // reg_for_notify: + // - esp_gatt_status_t status + // - uint16_t handle + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + // If the request is not for this BLERemoteCharacteristic then move on to the next. + if (evtParam->reg_for_notify.handle != getHandle()) break; + + // We have processed the notify registration and can unlock the semaphore. + m_semaphoreRegForNotifyEvt.give(); + break; + } // ESP_GATTC_REG_FOR_NOTIFY_EVT + + // ESP_GATTC_UNREG_FOR_NOTIFY_EVT + // + // unreg_for_notify: + // - esp_gatt_status_t status + // - uint16_t handle + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { + if (evtParam->unreg_for_notify.handle != getHandle()) break; + // We have processed the notify un-registration and can unlock the semaphore. + m_semaphoreRegForNotifyEvt.give(); + break; + } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT: + + // ESP_GATTC_WRITE_CHAR_EVT + // + // write: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + case ESP_GATTC_WRITE_CHAR_EVT: { + // Determine if this event is for us and, if not, pass onwards. + if (evtParam->write.handle != getHandle()) break; + + // There is nothing further we need to do here. This is merely an indication + // that the write has completed and we can unlock the caller. + m_semaphoreWriteCharEvt.give(); + break; + } // ESP_GATTC_WRITE_CHAR_EVT + + + default: + break; + } // End switch +}; // gattClientEventHandler + + +/** + * @brief Populate the descriptors (if any) for this characteristic. + */ +void BLERemoteCharacteristic::retrieveDescriptors() { + ESP_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); + + removeDescriptors(); // Remove any existing descriptors. + + // Loop over each of the descriptors within the service associated with this characteristic. + // For each descriptor we find, create a BLERemoteDescriptor instance. + uint16_t offset = 0; + esp_gattc_descr_elem_t result; + while(true) { + uint16_t count = 10; + esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr( + getRemoteService()->getClient()->getGattcIf(), + getRemoteService()->getClient()->getConnId(), + getHandle(), + &result, + &count, + offset + ); + + if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + break; + } + + if (status != ESP_GATT_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_descr: %s", BLEUtils::gattStatusToString(status).c_str()); + break; + } + + if (count == 0) break; + + ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str()); + + // We now have a new characteristic ... let us add that to our set of known characteristics + BLERemoteDescriptor* pNewRemoteDescriptor = new BLERemoteDescriptor( + result.handle, + BLEUUID(result.uuid), + this + ); + + m_descriptorMap.insert(std::pair(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor)); + + offset++; + } // while true + //m_haveCharacteristics = true; // Remember that we have received the characteristics. + ESP_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", offset); +} // getDescriptors + + +/** + * @brief Retrieve the map of descriptors keyed by UUID. + */ +std::map* BLERemoteCharacteristic::getDescriptors() { + return &m_descriptorMap; +} // getDescriptors + + +/** + * @brief Get the handle for this characteristic. + * @return The handle for this characteristic. + */ +uint16_t BLERemoteCharacteristic::getHandle() { + //ESP_LOGD(LOG_TAG, ">> getHandle: Characteristic: %s", getUUID().toString().c_str()); + //ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", m_handle, m_handle); + return m_handle; +} // getHandle + + +/** + * @brief Get the descriptor instance with the given UUID that belongs to this characteristic. + * @param [in] uuid The UUID of the descriptor to find. + * @return The Remote descriptor (if present) or null if not present. + */ +BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { + ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); + std::string v = uuid.toString(); + for (auto &myPair : m_descriptorMap) { + if (myPair.first == v) { + ESP_LOGD(LOG_TAG, "<< getDescriptor: found"); + return myPair.second; + } + } + ESP_LOGD(LOG_TAG, "<< getDescriptor: Not found"); + return nullptr; +} // getDescriptor + + +/** + * @brief Get the remote service associated with this characteristic. + * @return The remote service associated with this characteristic. + */ +BLERemoteService* BLERemoteCharacteristic::getRemoteService() { + return m_pRemoteService; +} // getRemoteService + + +/** + * @brief Get the UUID for this characteristic. + * @return The UUID for this characteristic. + */ +BLEUUID BLERemoteCharacteristic::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Read an unsigned 16 bit value + * @return The unsigned 16 bit value. + */ +uint16_t BLERemoteCharacteristic::readUInt16() { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*)(value.data()); + } + return 0; +} // readUInt16 + + +/** + * @brief Read an unsigned 32 bit value. + * @return the unsigned 32 bit value. + */ +uint32_t BLERemoteCharacteristic::readUInt32() { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*)(value.data()); + } + return 0; +} // readUInt32 + + +/** + * @brief Read a byte value + * @return The value as a byte + */ +uint8_t BLERemoteCharacteristic::readUInt8() { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t)value[0]; + } + return 0; +} // readUInt8 + + +/** + * @brief Read the value of the remote characteristic. + * @return The value of the remote characteristic. + */ +std::string BLERemoteCharacteristic::readValue() { + ESP_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle()); + + // Check to see that we are connected. + if (!getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + m_semaphoreReadCharEvt.take("readValue"); + + // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + // This is an asynchronous request which means that we must block waiting for the response + // to become available. + esp_err_t errRc = ::esp_ble_gattc_read_char( + m_pRemoteService->getClient()->getGattcIf(), + m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return ""; + } + + // Block waiting for the event that indicates that the read has completed. When it has, the std::string found + // in m_value will contain our data. + m_semaphoreReadCharEvt.wait("readValue"); + + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return m_value; +} // readValue + + +/** + * @brief Register for notifications. + * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are + * unregistering a notification. + * @return N/A. + */ +void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications) { + ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); + + m_notifyCallback = notifyCallback; // Save the notification callback. + + m_semaphoreRegForNotifyEvt.take("registerForNotify"); + + if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration. + esp_err_t errRc = ::esp_ble_gattc_register_for_notify( + m_pRemoteService->getClient()->getGattcIf(), + *m_pRemoteService->getClient()->getPeerAddress().getNative(), + getHandle() + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + + uint8_t val[] = {0x01, 0x00}; + if(!notifications) val[0] = 0x02; + BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902)); + desc->writeValue(val, 2); + } // End Register + else { // If we weren't passed a callback function, then this is an unregistration. + esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify( + m_pRemoteService->getClient()->getGattcIf(), + *m_pRemoteService->getClient()->getPeerAddress().getNative(), + getHandle() + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + + uint8_t val[] = {0x00, 0x00}; + BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902); + desc->writeValue(val, 2); + } // End Unregister + + m_semaphoreRegForNotifyEvt.wait("registerForNotify"); + + ESP_LOGD(LOG_TAG, "<< registerForNotify()"); +} // registerForNotify + + +/** + * @brief Delete the descriptors in the descriptor map. + * We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors + * object references. Since we allocated these in this class, we are also responsible for deleteing + * them. This method does just that. + * @return N/A. + */ +void BLERemoteCharacteristic::removeDescriptors() { + // Iterate through all the descriptors releasing their storage and erasing them from the map. + for (auto &myPair : m_descriptorMap) { + m_descriptorMap.erase(myPair.first); + delete myPair.second; + } + m_descriptorMap.clear(); // Technically not neeeded, but just to be sure. +} // removeCharacteristics + + +/** + * @brief Convert a BLERemoteCharacteristic to a string representation; + * @return a String representation. + */ +std::string BLERemoteCharacteristic::toString() { + std::ostringstream ss; + ss << "Characteristic: uuid: " << m_uuid.toString() << + ", handle: " << getHandle() << " 0x" << std::hex << getHandle() << + ", props: " << BLEUtils::characteristicPropertiesToString(m_charProp); + return ss.str(); +} // toString + + +/** + * @brief Write the new value for the characteristic. + * @param [in] newValue The new value to write. + * @param [in] response Do we expect a response? + * @return N/A. + */ +void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) { + writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic. + * + * This is a convenience function. Many BLE characteristics are a single byte of data. + * @param [in] newValue The new byte value to write. + * @param [in] response Whether we require a response from the write. + * @return N/A. + */ +void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) { + writeValue(&newValue, 1, response); +} // writeValue + + +/** + * @brief Write the new value for the characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + */ +void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) { + // writeValue(std::string((char*)data, length), response); + ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); + + // Check to see that we are connected. + if (!getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + m_semaphoreWriteCharEvt.take("writeValue"); + // Invoke the ESP-IDF API to perform the write. + esp_err_t errRc = ::esp_ble_gattc_write_char( + m_pRemoteService->getClient()->getGattcIf(), + m_pRemoteService->getClient()->getConnId(), + getHandle(), + length, + data, + response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE + ); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreWriteCharEvt.wait("writeValue"); + + ESP_LOGD(LOG_TAG, "<< writeValue"); +} // writeValue + +/** + * @brief Read raw data from remote characteristic as hex bytes + * @return return pointer data read + */ +uint8_t* BLERemoteCharacteristic::readRawData() { + return m_rawData; +} + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.h new file mode 100644 index 0000000..fbcafe8 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteCharacteristic.h @@ -0,0 +1,84 @@ +/* + * BLERemoteCharacteristic.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ +#define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include + +#include "BLERemoteService.h" +#include "BLERemoteDescriptor.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLERemoteService; +class BLERemoteDescriptor; +typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + +/** + * @brief A model of a remote %BLE characteristic. + */ +class BLERemoteCharacteristic { +public: + ~BLERemoteCharacteristic(); + + // Public member functions + bool canBroadcast(); + bool canIndicate(); + bool canNotify(); + bool canRead(); + bool canWrite(); + bool canWriteNoResponse(); + BLERemoteDescriptor* getDescriptor(BLEUUID uuid); + std::map* getDescriptors(); + uint16_t getHandle(); + BLEUUID getUUID(); + std::string readValue(); + uint8_t readUInt8(); + uint16_t readUInt16(); + uint32_t readUInt32(); + void registerForNotify(notify_callback _callback, bool notifications = true); + void writeValue(uint8_t* data, size_t length, bool response = false); + void writeValue(std::string newValue, bool response = false); + void writeValue(uint8_t newValue, bool response = false); + std::string toString(); + uint8_t* readRawData(); + +private: + BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService); + friend class BLEClient; + friend class BLERemoteService; + friend class BLERemoteDescriptor; + + // Private member functions + void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam); + + BLERemoteService* getRemoteService(); + void removeDescriptors(); + void retrieveDescriptors(); + + // Private properties + BLEUUID m_uuid; + esp_gatt_char_prop_t m_charProp; + uint16_t m_handle; + BLERemoteService* m_pRemoteService; + FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); + FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt"); + FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); + std::string m_value; + uint8_t *m_rawData; + notify_callback m_notifyCallback; + + // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. + std::map m_descriptorMap; +}; // BLERemoteCharacteristic +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.cpp new file mode 100644 index 0000000..96a8a57 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.cpp @@ -0,0 +1,181 @@ +/* + * BLERemoteDescriptor.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "BLERemoteDescriptor.h" +#include "GeneralUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteDescriptor"; +#endif + + + + +BLERemoteDescriptor::BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic) { + + m_handle = handle; + m_uuid = uuid; + m_pRemoteCharacteristic = pRemoteCharacteristic; +} + + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t BLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +BLEUUID BLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +std::string BLERemoteDescriptor::readValue() { + ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str()); + + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + m_semaphoreReadDescrEvt.take("readValue"); + + // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. + esp_err_t errRc = ::esp_ble_gattc_read_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server + getHandle(), // The handle of this characteristic + ESP_GATT_AUTH_REQ_NONE); // Security + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return ""; + } + + // Block waiting for the event that indicates that the read has completed. When it has, the std::string found + // in m_value will contain our data. + m_semaphoreReadDescrEvt.wait("readValue"); + + ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); + return m_value; +} // readValue + + +uint8_t BLERemoteDescriptor::readUInt8() { + std::string value = readValue(); + if (value.length() >= 1) { + return (uint8_t) value[0]; + } + return 0; +} // readUInt8 + + +uint16_t BLERemoteDescriptor::readUInt16() { + std::string value = readValue(); + if (value.length() >= 2) { + return *(uint16_t*) value.data(); + } + return 0; +} // readUInt16 + + +uint32_t BLERemoteDescriptor::readUInt32() { + std::string value = readValue(); + if (value.length() >= 4) { + return *(uint32_t*) value.data(); + } + return 0; +} // readUInt32 + + +/** + * @brief Return a string representation of this BLE Remote Descriptor. + * @retun A string representation of this BLE Remote Descriptor. + */ +std::string BLERemoteDescriptor::toString() { + std::stringstream ss; + ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString(); + return ss.str(); +} // toString + + +/** + * @brief Write data to the BLE Remote Descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) { + ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str()); + // Check to see that we are connected. + if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) { + ESP_LOGE(LOG_TAG, "Disconnected"); + throw BLEDisconnectedException(); + } + + esp_err_t errRc = ::esp_ble_gattc_write_char_descr( + m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(), + m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), + getHandle(), + length, // Data length + data, // Data + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc); + } + ESP_LOGD(LOG_TAG, "<< writeValue"); +} // writeValue + + +/** + * @brief Write data represented as a string to the BLE Remote Descriptor. + * @param [in] newValue The data to send to the remote descriptor. + * @param [in] response True if we expect a response. + */ +void BLERemoteDescriptor::writeValue(std::string newValue, bool response) { + writeValue((uint8_t*) newValue.data(), newValue.length(), response); +} // writeValue + + +/** + * @brief Write a byte value to the Descriptor. + * @param [in] The single byte to write. + * @param [in] True if we expect a response. + */ +void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) { + writeValue(&newValue, 1, response); +} // writeValue + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.h new file mode 100644 index 0000000..7bbc48f --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteDescriptor.h @@ -0,0 +1,55 @@ +/* + * BLERemoteDescriptor.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ +#define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +#include + +#include "BLERemoteCharacteristic.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLERemoteCharacteristic; +/** + * @brief A model of remote %BLE descriptor. + */ +class BLERemoteDescriptor { +public: + uint16_t getHandle(); + BLERemoteCharacteristic* getRemoteCharacteristic(); + BLEUUID getUUID(); + std::string readValue(void); + uint8_t readUInt8(void); + uint16_t readUInt16(void); + uint32_t readUInt32(void); + std::string toString(void); + void writeValue(uint8_t* data, size_t length, bool response = false); + void writeValue(std::string newValue, bool response = false); + void writeValue(uint8_t newValue, bool response = false); + + +private: + friend class BLERemoteCharacteristic; + BLERemoteDescriptor( + uint16_t handle, + BLEUUID uuid, + BLERemoteCharacteristic* pRemoteCharacteristic + ); + uint16_t m_handle; // Server handle of this descriptor. + BLEUUID m_uuid; // UUID of this descriptor. + std::string m_value; // Last received value of the descriptor. + BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated. + FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt"); + + +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.cpp new file mode 100644 index 0000000..c2b7d34 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.cpp @@ -0,0 +1,340 @@ +/* + * BLERemoteService.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include "BLERemoteService.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#include +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLERemoteService"; +#endif + + + +BLERemoteService::BLERemoteService( + esp_gatt_id_t srvcId, + BLEClient* pClient, + uint16_t startHandle, + uint16_t endHandle + ) { + + ESP_LOGD(LOG_TAG, ">> BLERemoteService()"); + m_srvcId = srvcId; + m_pClient = pClient; + m_uuid = BLEUUID(m_srvcId); + m_haveCharacteristics = false; + m_startHandle = startHandle; + m_endHandle = endHandle; + + ESP_LOGD(LOG_TAG, "<< BLERemoteService()"); +} + + +BLERemoteService::~BLERemoteService() { + removeCharacteristics(); +} + +/* +static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) { + if (id1.id.inst_id != id2.id.inst_id) { + return false; + } + if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) { + return false; + } + return true; +} // compareSrvcId +*/ + +/** + * @brief Handle GATT Client events + */ +void BLERemoteService::gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam) { + switch (event) { + // + // ESP_GATTC_GET_CHAR_EVT + // + // get_char: + // - esp_gatt_status_t status + // - uin1t6_t conn_id + // - esp_gatt_srvc_id_t srvc_id + // - esp_gatt_id_t char_id + // - esp_gatt_char_prop_t char_prop + // + /* + case ESP_GATTC_GET_CHAR_EVT: { + // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be + // the same. + if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) { + break; + } + + // If the status is NOT OK then we have a problem and continue. + if (evtParam->get_char.status != ESP_GATT_OK) { + m_semaphoreGetCharEvt.give(); + break; + } + + // This is an indication that we now have the characteristic details for a characteristic owned + // by this service so remember it. + m_characteristicMap.insert(std::pair( + BLEUUID(evtParam->get_char.char_id.uuid).toString(), + new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) )); + + + // Now that we have received a characteristic, lets ask for the next one. + esp_err_t errRc = ::esp_ble_gattc_get_characteristic( + m_pClient->getGattcIf(), + m_pClient->getConnId(), + &m_srvcId, + &evtParam->get_char.char_id); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + break; + } + + //m_semaphoreGetCharEvt.give(); + break; + } // ESP_GATTC_GET_CHAR_EVT +*/ + default: + break; + } // switch + + // Send the event to each of the characteristics owned by this service. + for (auto &myPair : m_characteristicMapByHandle) { + myPair.second->gattClientEventHandler(event, gattc_if, evtParam); + } +} // gattClientEventHandler + + +/** + * @brief Get the remote characteristic object for the characteristic UUID. + * @param [in] uuid Remote characteristic uuid. + * @return Reference to the remote characteristic object. + * @throws BLEUuidNotFoundException + */ +BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) { + return getCharacteristic(BLEUUID(uuid)); +} // getCharacteristic + +/** + * @brief Get the characteristic object for the UUID. + * @param [in] uuid Characteristic uuid. + * @return Reference to the characteristic object. + * @throws BLEUuidNotFoundException + */ +BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) { +// Design +// ------ +// We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the +// device what characteristics it has in which case we have nothing to match against. If we have not +// asked the device about its characteristics, then we do that now. Once we get the results we can then +// examine the characteristics map to see if it has the characteristic we are looking for. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + std::string v = uuid.toString(); + for (auto &myPair : m_characteristicMap) { + if (myPair.first == v) { + return myPair.second; + } + } + // throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another + return nullptr; +} // getCharacteristic + + +/** + * @brief Retrieve all the characteristics for this service. + * This function will not return until we have all the characteristics. + * @return N/A + */ +void BLERemoteService::retrieveCharacteristics() { + ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); + + removeCharacteristics(); // Forget any previous characteristics. + + uint16_t offset = 0; + esp_gattc_char_elem_t result; + while (true) { + uint16_t count = 10; // this value is used as in parameter that allows to search max 10 chars with the same uuid + esp_gatt_status_t status = ::esp_ble_gattc_get_all_char( + getClient()->getGattcIf(), + getClient()->getConnId(), + m_startHandle, + m_endHandle, + &result, + &count, + offset + ); + + if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries. + break; + } + + if (status != ESP_GATT_OK) { // If we got an error, end. + ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str()); + break; + } + + if (count == 0) { // If we failed to get any new records, end. + break; + } + + ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str()); + + // We now have a new characteristic ... let us add that to our set of known characteristics + BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic( + result.char_handle, + BLEUUID(result.uuid), + result.properties, + this + ); + + m_characteristicMap.insert(std::pair(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic)); + m_characteristicMapByHandle.insert(std::pair(result.char_handle, pNewRemoteCharacteristic)); + offset++; // Increment our count of number of descriptors found. + } // Loop forever (until we break inside the loop). + + m_haveCharacteristics = true; // Remember that we have received the characteristics. + ESP_LOGD(LOG_TAG, "<< getCharacteristics()"); +} // getCharacteristics + + +/** + * @brief Retrieve a map of all the characteristics of this service. + * @return A map of all the characteristics of this service. + */ +std::map* BLERemoteService::getCharacteristics() { + ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str()); + // If is possible that we have not read the characteristics associated with the service so do that + // now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking + // call and does not return until all the characteristics are available. + if (!m_haveCharacteristics) { + retrieveCharacteristics(); + } + ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str()); + return &m_characteristicMap; +} // getCharacteristics + +/** + * @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID + */ +void BLERemoteService::getCharacteristics(std::map* pCharacteristicMap) { +#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" + pCharacteristicMap = &m_characteristicMapByHandle; +} // Get the characteristics map. + +/** + * @brief Get the client associated with this service. + * @return A reference to the client associated with this service. + */ +BLEClient* BLERemoteService::getClient() { + return m_pClient; +} // getClient + + +uint16_t BLERemoteService::getEndHandle() { + return m_endHandle; +} // getEndHandle + + +esp_gatt_id_t* BLERemoteService::getSrvcId() { + return &m_srvcId; +} // getSrvcId + + +uint16_t BLERemoteService::getStartHandle() { + return m_startHandle; +} // getStartHandle + + +uint16_t BLERemoteService::getHandle() { + ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str()); + ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle()); + return getStartHandle(); +} // getHandle + + +BLEUUID BLERemoteService::getUUID() { + return m_uuid; +} + +/** + * @brief Read the value of a characteristic associated with this service. + */ +std::string BLERemoteService::getValue(BLEUUID characteristicUuid) { + ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); + std::string ret = getCharacteristic(characteristicUuid)->readValue(); + ESP_LOGD(LOG_TAG, "<< readValue"); + return ret; +} // readValue + + + +/** + * @brief Delete the characteristics in the characteristics map. + * We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic + * object references. Since we allocated these in this class, we are also responsible for deleteing + * them. This method does just that. + * @return N/A. + */ +void BLERemoteService::removeCharacteristics() { + for (auto &myPair : m_characteristicMap) { + delete myPair.second; + //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear + } + m_characteristicMap.clear(); // Clear the map + for (auto &myPair : m_characteristicMapByHandle) { + delete myPair.second; + } + m_characteristicMapByHandle.clear(); // Clear the map +} // removeCharacteristics + + +/** + * @brief Set the value of a characteristic. + * @param [in] characteristicUuid The characteristic to set. + * @param [in] value The value to set. + * @throws BLEUuidNotFound + */ +void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) { + ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); + getCharacteristic(characteristicUuid)->writeValue(value); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue + + +/** + * @brief Create a string representation of this remote service. + * @return A string representation of this remote service. + */ +std::string BLERemoteService::toString() { + std::ostringstream ss; + ss << "Service: uuid: " + m_uuid.toString(); + ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle << + ", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle; + for (auto &myPair : m_characteristicMap) { + ss << "\n" << myPair.second->toString(); + // myPair.second is the value + } + return ss.str(); +} // toString + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.h new file mode 100644 index 0000000..2ab8673 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLERemoteService.h @@ -0,0 +1,85 @@ +/* + * BLERemoteService.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ +#define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include "BLEClient.h" +#include "BLERemoteCharacteristic.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLEClient; +class BLERemoteCharacteristic; + + +/** + * @brief A model of a remote %BLE service. + */ +class BLERemoteService { +public: + virtual ~BLERemoteService(); + + // Public methods + BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference. + BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference. + BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference. + std::map* getCharacteristics(); + std::map* getCharacteristicsByHandle(); // Get the characteristics map. + void getCharacteristics(std::map* pCharacteristicMap); + + BLEClient* getClient(void); // Get a reference to the client associated with this service. + uint16_t getHandle(); // Get the handle of this service. + BLEUUID getUUID(void); // Get the UUID of this service. + std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic. + void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic. + std::string toString(void); + +private: + // Private constructor ... never meant to be created by a user application. + BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle); + + // Friends + friend class BLEClient; + friend class BLERemoteCharacteristic; + + // Private methods + void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server. + esp_gatt_id_t* getSrvcId(void); + uint16_t getStartHandle(); // Get the start handle for this service. + uint16_t getEndHandle(); // Get the end handle for this service. + + void gattClientEventHandler( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam); + + void removeCharacteristics(); + + // Properties + + // We maintain a map of characteristics owned by this service keyed by a string representation of the UUID. + std::map m_characteristicMap; + + // We maintain a map of characteristics owned by this service keyed by a handle. + std::map m_characteristicMapByHandle; + + bool m_haveCharacteristics; // Have we previously obtained the characteristics. + BLEClient* m_pClient; + FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt"); + esp_gatt_id_t m_srvcId; + BLEUUID m_uuid; // The UUID of this service. + uint16_t m_startHandle; // The starting handle of this service. + uint16_t m_endHandle; // The ending handle of this service. +}; // BLERemoteService + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.cpp new file mode 100644 index 0000000..d851a47 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.cpp @@ -0,0 +1,331 @@ +/* + * BLEScan.cpp + * + * Created on: Jul 1, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + + +#include + +#include + +#include "BLEAdvertisedDevice.h" +#include "BLEScan.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEScan"; +#endif + + + + +/** + * Constructor + */ +BLEScan::BLEScan() { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan. + m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; + m_pAdvertisedDeviceCallbacks = nullptr; + m_stopped = true; + m_wantDuplicates = false; + setInterval(100); + setWindow(100); +} // BLEScan + + +/** + * @brief Handle GAP events related to scans. + * @param [in] event The event type for this event. + * @param [in] param Parameter data for this event. + */ +void BLEScan::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + switch(event) { + + // --------------------------- + // scan_rst: + // esp_gap_search_evt_t search_evt + // esp_bd_addr_t bda + // esp_bt_dev_type_t dev_type + // esp_ble_addr_type_t ble_addr_type + // esp_ble_evt_type_t ble_evt_type + // int rssi + // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX] + // int flag + // int num_resps + // uint8_t adv_data_len + // uint8_t scan_rsp_len + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + + switch(param->scan_rst.search_evt) { + // + // ESP_GAP_SEARCH_INQ_CMPL_EVT + // + // Event that indicates that the duration allowed for the search has completed or that we have been + // asked to stop. + case ESP_GAP_SEARCH_INQ_CMPL_EVT: { + ESP_LOGW(LOG_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT"); + m_stopped = true; + m_semaphoreScanEnd.give(); + if (m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } + break; + } // ESP_GAP_SEARCH_INQ_CMPL_EVT + + // + // ESP_GAP_SEARCH_INQ_RES_EVT + // + // Result that has arrived back from a Scan inquiry. + case ESP_GAP_SEARCH_INQ_RES_EVT: { + if (m_stopped) { // If we are not scanning, nothing to do with the extra results. + break; + } + +// Examine our list of previously scanned addresses and, if we found this one already, +// ignore it. + BLEAddress advertisedAddress(param->scan_rst.bda); + bool found = false; + + if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) { + found = true; + } + + if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done. + ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str()); + vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here + break; + } + + // We now construct a model of the advertised device that we have just found for the first + // time. + // ESP_LOG_BUFFER_HEXDUMP(LOG_TAG, (uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG); + // ESP_LOGW(LOG_TAG, "bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type); + BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice(); + advertisedDevice->setAddress(advertisedAddress); + advertisedDevice->setRSSI(param->scan_rst.rssi); + advertisedDevice->setAdFlag(param->scan_rst.flag); + advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len); + advertisedDevice->setScan(this); + advertisedDevice->setAddressType(param->scan_rst.ble_addr_type); + + if (!found) { // If we have previously seen this device, don't record it again. + m_scanResults.m_vectorAdvertisedDevices.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); + } + + if (m_pAdvertisedDeviceCallbacks) { + m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); + } + if(found) + delete advertisedDevice; + + break; + } // ESP_GAP_SEARCH_INQ_RES_EVT + + default: { + break; + } + } // switch - search_evt + + + break; + } // ESP_GAP_BLE_SCAN_RESULT_EVT + + default: { + break; + } // default + } // End switch +} // gapEventHandler + + +/** + * @brief Should we perform an active or passive scan? + * The default is a passive scan. An active scan means that we will wish a scan response. + * @param [in] active If true, we perform an active scan otherwise a passive scan. + * @return N/A. + */ +void BLEScan::setActiveScan(bool active) { + if (active) { + m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE; + } else { + m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; + } +} // setActiveScan + + +/** + * @brief Set the call backs to be invoked. + * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + */ +void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) { + m_wantDuplicates = wantDuplicates; + m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; +} // setAdvertisedDeviceCallbacks + + +/** + * @brief Set the interval to scan. + * @param [in] The interval in msecs. + */ +void BLEScan::setInterval(uint16_t intervalMSecs) { + m_scan_params.scan_interval = intervalMSecs / 0.625; +} // setInterval + + +/** + * @brief Set the window to actively scan. + * @param [in] windowMSecs How long to actively scan. + */ +void BLEScan::setWindow(uint16_t windowMSecs) { + m_scan_params.scan_window = windowMSecs / 0.625; +} // setWindow + + +/** + * @brief Start scanning. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @param [in] are we continue scan (true) or we want to clear stored devices (false) + * @return True if scan started or false if there was an error. + */ +bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) { + ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + + m_semaphoreScanEnd.take(std::string("start")); + m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes. + + // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals + // then we should not clear map or we will connect the same device few times + if(!is_continue) { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); + } + + esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + errRc = ::esp_ble_gap_start_scanning(duration); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreScanEnd.give(); + return false; + } + + m_stopped = false; + + ESP_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @return The BLEScanResults. + */ +BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) { + if(start(duration, nullptr, is_continue)) { + m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. + } + return m_scanResults; +} // start + + +/** + * @brief Stop an in progress scan. + * @return N/A. + */ +void BLEScan::stop() { + ESP_LOGD(LOG_TAG, ">> stop()"); + + esp_err_t errRc = ::esp_ble_gap_stop_scanning(); + + m_stopped = true; + m_semaphoreScanEnd.give(); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // stop + +// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address +void BLEScan::erase(BLEAddress address) { + ESP_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); + BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second; + m_scanResults.m_vectorAdvertisedDevices.erase(address.toString()); + delete advertisedDevice; +} + + +/** + * @brief Dump the scan results to the log. + */ +void BLEScanResults::dump() { + ESP_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; isecond; + for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) { + dev = *it->second; + if (x==i) break; + x++; + } + return dev; +} + +BLEScanResults BLEScan::getResults() { + return m_scanResults; +} + +void BLEScan::clearResults() { + for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){ + delete _dev.second; + } + m_scanResults.m_vectorAdvertisedDevices.clear(); +} + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.h new file mode 100644 index 0000000..2f71a72 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEScan.h @@ -0,0 +1,83 @@ +/* + * BLEScan.h + * + * Created on: Jul 1, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ +#define COMPONENTS_CPP_UTILS_BLESCAN_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +// #include +#include +#include "BLEAdvertisedDevice.h" +#include "BLEClient.h" +#include "FreeRTOS.h" + +class BLEAdvertisedDevice; +class BLEAdvertisedDeviceCallbacks; +class BLEClient; +class BLEScan; + + +/** + * @brief The result of having performed a scan. + * When a scan completes, we have a set of found devices. Each device is described + * by a BLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ +class BLEScanResults { +public: + void dump(); + int getCount(); + BLEAdvertisedDevice getDevice(uint32_t i); + +private: + friend BLEScan; + std::map m_vectorAdvertisedDevices; +}; + +/** + * @brief Perform and manage %BLE scans. + * + * Scanning is associated with a %BLE client that is attempting to locate BLE servers. + */ +class BLEScan { +public: + void setActiveScan(bool active); + void setAdvertisedDeviceCallbacks( + BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates = false); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false); + BLEScanResults start(uint32_t duration, bool is_continue = false); + void stop(); + void erase(BLEAddress address); + BLEScanResults getResults(); + void clearResults(); + +private: + BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton. + friend class BLEDevice; + void handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload); + + + esp_ble_scan_params_t m_scan_params; + BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; + bool m_stopped = true; + FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + BLEScanResults m_scanResults; + bool m_wantDuplicates; + void (*m_scanCompleteCB)(BLEScanResults scanResults); +}; // BLEScan + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.cpp new file mode 100644 index 0000000..921f542 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.cpp @@ -0,0 +1,104 @@ +/* + * BLESecurity.cpp + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#include "BLESecurity.h" +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +BLESecurity::BLESecurity() { +} + +BLESecurity::~BLESecurity() { +} +/* + * @brief Set requested authentication mode + */ +void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { + m_authReq = auth_req; + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode +} + +/** + * @brief Set our device IO capability to let end user perform authorization + * either by displaying or entering generated 6-digits pin code + */ +void BLESecurity::setCapability(esp_ble_io_cap_t iocap) { + m_iocap = iocap; + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); +} // setCapability + + +/** + * @brief Init encryption key by server + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setInitEncryptionKey(uint8_t init_key) { + m_initKey = init_key; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); +} // setInitEncryptionKey + + +/** + * @brief Init encryption key by client + * @param key_size is value between 7 and 16 + */ +void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { + m_respKey = resp_key; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); +} // setRespEncryptionKey + + +/** + * + * + */ +void BLESecurity::setKeySize(uint8_t key_size) { + m_keySize = key_size; + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); +} //setKeySize + + +/** + * @brief Debug function to display what keys are exchanged by peers + */ +char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { + char* key_str = nullptr; + switch (key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*) "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*) "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*) "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*) "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*) "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*) "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = (char*) "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = (char*) "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = (char*) "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = (char*) "INVALID BLE KEY TYPE"; + break; + } + return key_str; +} // esp_key_type_to_str +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.h new file mode 100644 index 0000000..48d09d2 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLESecurity.h @@ -0,0 +1,72 @@ +/* + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#define COMPONENTS_CPP_UTILS_BLESECURITY_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +class BLESecurity { +public: + BLESecurity(); + virtual ~BLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + static char* esp_key_type_to_str(esp_ble_key_type_t key_type); + +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; + +}; // BLESecurity + + +/* + * @brief Callbacks to handle GAP events related to authorization + */ +class BLESecurityCallbacks { +public: + virtual ~BLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * return Return true if we accept this peer device request + */ + + virtual bool onSecurityRequest() = 0 ; + /** + * Provide us information when authentication process is completed + */ + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.cpp new file mode 100644 index 0000000..6a780aa --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.cpp @@ -0,0 +1,424 @@ +/* + * BLEServer.cpp + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "GeneralUtils.h" +#include "BLEDevice.h" +#include "BLEServer.h" +#include "BLEService.h" +#include "BLEUtils.h" +#include +#include +#include +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEServer"; +#endif + + + + +/** + * @brief Construct a %BLE Server + * + * This class is not designed to be individually instantiated. Instead one should create a server by asking + * the BLEDevice class. + */ +BLEServer::BLEServer() { + m_appId = ESP_GATT_IF_NONE; + m_gatts_if = ESP_GATT_IF_NONE; + m_connectedCount = 0; + m_connId = ESP_GATT_IF_NONE; + m_pServerCallbacks = nullptr; +} // BLEServer + + +void BLEServer::createApp(uint16_t appId) { + m_appId = appId; + registerApp(appId); +} // createApp + + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @return A reference to the new service object. + */ +BLEService* BLEServer::createService(const char* uuid) { + return createService(BLEUUID(uuid)); +} + + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @param [in] numHandles The maximum number of handles associated with this service. + * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. + * @return A reference to the new service object. + */ +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { + ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); + m_semaphoreCreateEvt.take("createService"); + + // Check that a service with the supplied UUID does not already exist. + if (m_serviceMap.getByUUID(uuid) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + uuid.toString().c_str()); + } + + BLEService* pService = new BLEService(uuid, numHandles); + pService->m_instId = inst_id; + m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. + pService->executeCreate(this); // Perform the API calls to actually create the service. + + m_semaphoreCreateEvt.wait("createService"); + + ESP_LOGD(LOG_TAG, "<< createService"); + return pService; +} // createService + + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(const char* uuid) { + return m_serviceMap.getByUUID(uuid); +} + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ +BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { + return m_serviceMap.getByUUID(uuid); +} + +/** + * @brief Retrieve the advertising object that can be used to advertise the existence of the server. + * + * @return An advertising object. + */ +BLEAdvertising* BLEServer::getAdvertising() { + return BLEDevice::getAdvertising(); +} + +uint16_t BLEServer::getConnId() { + return m_connId; +} + + +/** + * @brief Return the number of connected clients. + * @return The number of connected clients. + */ +uint32_t BLEServer::getConnectedCount() { + return m_connectedCount; +} // getConnectedCount + + +uint16_t BLEServer::getGattsIf() { + return m_gatts_if; +} + + +/** + * @brief Handle a GATT Server Event. + * + * @param [in] event + * @param [in] gatts_if + * @param [in] param + * + */ +void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", + BLEUtils::gattServerEventTypeToString(event).c_str()); + + switch(event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + // + case ESP_GATTS_ADD_CHAR_EVT: { + break; + } // ESP_GATTS_ADD_CHAR_EVT + + case ESP_GATTS_MTU_EVT: + updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); + break; + + // ESP_GATTS_CONNECT_EVT + // connect: + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // + case ESP_GATTS_CONNECT_EVT: { + m_connId = param->connect.conn_id; + addPeerDevice((void*)this, false, m_connId); + if (m_pServerCallbacks != nullptr) { + m_pServerCallbacks->onConnect(this); + m_pServerCallbacks->onConnect(this, param); + } + m_connectedCount++; // Increment the number of connected devices count. + break; + } // ESP_GATTS_CONNECT_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // + case ESP_GATTS_CREATE_EVT: { + BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid + m_serviceMap.setByHandle(param->create.service_handle, pService); + m_semaphoreCreateEvt.give(); + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DISCONNECT_EVT + // + // disconnect + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason + // + // If we receive a disconnect event then invoke the callback for disconnects (if one is present). + // we also want to start advertising again. + case ESP_GATTS_DISCONNECT_EVT: { + m_connectedCount--; // Decrement the number of connected devices count. + if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. + m_pServerCallbacks->onDisconnect(this); + } + startAdvertising(); //- do this with some delay from the loop() + removePeerDevice(param->disconnect.conn_id, false); + break; + } // ESP_GATTS_DISCONNECT_EVT + + + // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + break; + } // ESP_GATTS_READ_EVT + + + // ESP_GATTS_REG_EVT + // reg: + // - esp_gatt_status_t status + // - uint16_t app_id + // + case ESP_GATTS_REG_EVT: { + m_gatts_if = gatts_if; + m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. + break; + } // ESP_GATTS_REG_EVT + + + // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t* value + // + case ESP_GATTS_WRITE_EVT: { + break; + } + + case ESP_GATTS_OPEN_EVT: + m_semaphoreOpenEvt.give(param->open.status); + break; + + default: + break; + } + + // Invoke the handler for every Service we have. + m_serviceMap.handleGATTServerEvent(event, gatts_if, param); + + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); +} // handleGATTServerEvent + + +/** + * @brief Register the app. + * + * @return N/A + */ +void BLEServer::registerApp(uint16_t m_appId) { + ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); + m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. + ::esp_ble_gatts_app_register(m_appId); + m_semaphoreRegisterAppEvt.wait("registerApp"); + ESP_LOGD(LOG_TAG, "<< registerApp"); +} // registerApp + + +/** + * @brief Set the server callbacks. + * + * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client + * disconnecting. This function can be called to register a callback handler that will be invoked when these + * events are detected. + * + * @param [in] pCallbacks The callbacks to be invoked. + */ +void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) { + m_pServerCallbacks = pCallbacks; +} // setCallbacks + +/* + * Remove service + */ +void BLEServer::removeService(BLEService* service) { + service->stop(); + service->executeDelete(); + m_serviceMap.removeService(service); +} + +/** + * @brief Start advertising. + * + * Start the server advertising its existence. This is a convenience function and is equivalent to + * retrieving the advertising object and invoking start upon it. + */ +void BLEServer::startAdvertising() { + ESP_LOGD(LOG_TAG, ">> startAdvertising"); + BLEDevice::startAdvertising(); + ESP_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising + +/** + * Allow to connect GATT server to peer device + * Probably can be used in ANCS for iPhone + */ +bool BLEServer::connect(BLEAddress address) { + esp_bd_addr_t addr; + memcpy(&addr, address.getNative(), 6); + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + esp_err_t errRc = ::esp_ble_gatts_open( + getGattsIf(), + addr, // address + 1 // direct connection + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); + return rc == ESP_GATT_OK; +} // connect + + + +void BLEServerCallbacks::onConnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + +void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { + ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); + ESP_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + + +void BLEServerCallbacks::onDisconnect(BLEServer* pServer) { + ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); + ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); + ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()"); +} // onDisconnect + +/* multi connect support */ +/* TODO do some more tweaks */ +void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { + // set mtu in conn_status_t + const std::map::iterator it = m_connectedServersMap.find(conn_id); + if (it != m_connectedServersMap.end()) { + it->second.mtu = mtu; + std::swap(m_connectedServersMap[conn_id], it->second); + } +} + +std::map BLEServer::getPeerDevices(bool _client) { + return m_connectedServersMap; +} + + +uint16_t BLEServer::getPeerMTU(uint16_t conn_id) { + return m_connectedServersMap.find(conn_id)->second.mtu; +} + +void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedServersMap.insert(std::pair(conn_id, status)); +} + +void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { + m_connectedServersMap.erase(conn_id); +} +/* multi connect support */ + +/** + * Update connection parameters can be called only after connection has been established + */ +void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { + esp_ble_conn_update_params_t conn_params; + memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); + conn_params.latency = latency; + conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms + conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms + conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms + esp_ble_gap_update_conn_params(&conn_params); +} +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.h new file mode 100644 index 0000000..d39d8bf --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServer.h @@ -0,0 +1,140 @@ +/* + * BLEServer.h + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_ +#define COMPONENTS_CPP_UTILS_BLESERVER_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +#include +#include +// #include "BLEDevice.h" + +#include "BLEUUID.h" +#include "BLEAdvertising.h" +#include "BLECharacteristic.h" +#include "BLEService.h" +#include "BLESecurity.h" +#include "FreeRTOS.h" +#include "BLEAddress.h" + +class BLEServerCallbacks; +/* TODO possibly refactor this struct */ +typedef struct { + void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here + bool connected; // do we need it? + uint16_t mtu; // every peer device negotiate own mtu +} conn_status_t; + + +/** + * @brief A data structure that manages the %BLE servers owned by a BLE server. + */ +class BLEServiceMap { +public: + BLEService* getByHandle(uint16_t handle); + BLEService* getByUUID(const char* uuid); + BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void setByHandle(uint16_t handle, BLEService* service); + void setByUUID(const char* uuid, BLEService* service); + void setByUUID(BLEUUID uuid, BLEService* service); + std::string toString(); + BLEService* getFirst(); + BLEService* getNext(); + void removeService(BLEService *service); + int getRegisteredServiceCount(); + +private: + std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE server. + */ +class BLEServer { +public: + uint32_t getConnectedCount(); + BLEService* createService(const char* uuid); + BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); + BLEAdvertising* getAdvertising(); + void setCallbacks(BLEServerCallbacks* pCallbacks); + void startAdvertising(); + void removeService(BLEService* service); + BLEService* getServiceByUUID(const char* uuid); + BLEService* getServiceByUUID(BLEUUID uuid); + bool connect(BLEAddress address); + uint16_t m_appId; + void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + + /* multi connection support */ + std::map getPeerDevices(bool client); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); + BLEServer* getServerByConnId(uint16_t conn_id); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); + + +private: + BLEServer(); + friend class BLEService; + friend class BLECharacteristic; + friend class BLEDevice; + esp_ble_adv_data_t m_adv_data; + // BLEAdvertising m_bleAdvertising; + uint16_t m_connId; + uint32_t m_connectedCount; + uint16_t m_gatts_if; + std::map m_connectedServersMap; + + FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); + BLEServiceMap m_serviceMap; + BLEServerCallbacks* m_pServerCallbacks = nullptr; + + void createApp(uint16_t appId); + uint16_t getGattsIf(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + void registerApp(uint16_t); +}; // BLEServer + + +/** + * @brief Callbacks associated with the operation of a %BLE server. + */ +class BLEServerCallbacks { +public: + virtual ~BLEServerCallbacks() {}; + /** + * @brief Handle a new client connection. + * + * When a new client connects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the client connection. + */ + virtual void onConnect(BLEServer* pServer); + virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); + /** + * @brief Handle an existing client disconnection. + * + * When an existing client disconnects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. + */ + virtual void onDisconnect(BLEServer* pServer); +}; // BLEServerCallbacks + + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.cpp new file mode 100644 index 0000000..3034cf1 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.cpp @@ -0,0 +1,418 @@ +/* + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include + +#include +#include +#include + +#include "BLEServer.h" +#include "BLEService.h" +#include "BLEUtils.h" +#include "GeneralUtils.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEService"; // Tag for logging. +#endif + +#define NULL_HANDLE (0xffff) + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(const char* uuid, uint16_t numHandles) : BLEService(BLEUUID(uuid), numHandles) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +BLEService::BLEService(BLEUUID uuid, uint16_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; + //m_serializeMutex.setName("BLEService"); + m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; +} // BLEService + + +/** + * @brief Create the service. + * Create the service. + * @param [in] gatts_if The handle of the GATT server interface. + * @return N/A. + */ + +void BLEService::executeCreate(BLEServer* pServer) { + ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str()); + m_pServer = pServer; + m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + + esp_gatt_srvc_id_t srvc_id; + srvc_id.is_primary = true; + srvc_id.id.inst_id = m_instId; + srvc_id.id.uuid = *m_uuid.getNative(); + esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreCreateEvt.wait("executeCreate"); + ESP_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Delete the service. + * Delete the service. + * @return N/A. + */ + +void BLEService::executeDelete() { + ESP_LOGD(LOG_TAG, ">> executeDelete()"); + m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT + + esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle()); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreDeleteEvt.wait("executeDelete"); + ESP_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void BLEService::dump() { + ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", + m_uuid.toString().c_str(), + m_handle); + ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +BLEUUID BLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Start the service. + * Here we wish to start the service which means that we will respond to partner requests about it. + * Starting a service also means that we can create the corresponding characteristics. + * @return Start the service. + */ +void BLEService::start() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); + return; + } + + BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); + + while (pCharacteristic != nullptr) { + m_lastCreatedCharacteristic = pCharacteristic; + pCharacteristic->executeCreate(this); + + pCharacteristic = m_characteristicMap.getNext(); + } + // Start each of the characteristics ... these are found in the m_characteristicMap. + + m_semaphoreStartEvt.take("start"); + esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStartEvt.wait("start"); + + ESP_LOGD(LOG_TAG, "<< start()"); +} // start + + +/** + * @brief Stop the service. + */ +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start + + +/** + * @brief Set the handle associated with this service. + * @param [in] handle The handle associated with the service. + */ +void BLEService::setHandle(uint16_t handle) { + ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); + return; + } + m_handle = handle; + ESP_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t BLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Add a characteristic to the service. + * @param [in] pCharacteristic A pointer to the characteristic to be added. + */ +void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { + // We maintain a mapping of characteristics owned by this service. These are managed by the + // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic + // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). + + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + pCharacteristic->getUUID().toString().c_str(), + toString().c_str()); + + // Check that we don't add the same characteristic twice. + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + //return; + } + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); + + ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); +} // addCharacteristic + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(BLEUUID(uuid), properties); +} + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ +BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { + BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties); + addCharacteristic(pCharacteristic); + return pCharacteristic; +} // createCharacteristic + + +/** + * @brief Handle a GATTS server event. + */ +void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + switch (event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + + // If we have reached the correct service, then locate the characteristic and remember the handle + // for that characteristic. + case ESP_GATTS_ADD_CHAR_EVT: { + if (m_handle == param->add_char.service_handle) { + BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); + if (pCharacteristic == nullptr) { + ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", + BLEUUID(param->add_char.char_uuid).toString().c_str()); + dump(); + break; + } + pCharacteristic->setHandle(param->add_char.attr_handle); + m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); + break; + } // Reached the correct service. + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_START_EVT + // + // start: + // esp_gatt_status_t status + // uint16_t service_handle + case ESP_GATTS_START_EVT: { + if (param->start.service_handle == getHandle()) { + m_semaphoreStartEvt.give(); + } + break; + } // ESP_GATTS_START_EVT + + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } + break; + } // ESP_GATTS_STOP_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // * - esp_gatt_id id + // * - esp_bt_uuid uuid + // * - uint8_t inst_id + // * - bool is_primary + // + case ESP_GATTS_CREATE_EVT: { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) { + setHandle(param->create.service_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // + case ESP_GATTS_DELETE_EVT: { + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } + break; + } // ESP_GATTS_DELETE_EVT + + default: + break; + } // Switch + + // Invoke the GATTS handler in each of the associated characteristics. + m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); +} // handleGATTServerEvent + + +BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(BLEUUID(uuid)); +} + + +BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { + return m_characteristicMap.getByUUID(uuid); +} + + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string BLEService::toString() { + std::stringstream stringStream; + stringStream << "UUID: " << getUUID().toString() << + ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle(); + return stringStream.str(); +} // toString + + +/** + * @brief Get the last created characteristic. + * It is lamentable that this function has to exist. It returns the last created characteristic. + * We need this because the descriptor API is built around the notion that a new descriptor, when created, + * is associated with the last characteristics created and we need that information. + * @return The last created characteristic. + */ +BLECharacteristic* BLEService::getLastCreatedCharacteristic() { + return m_lastCreatedCharacteristic; +} // getLastCreatedCharacteristic + + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +BLEServer* BLEService::getServer() { + return m_pServer; +} // getServer + +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.h new file mode 100644 index 0000000..b42d57f --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEService.h @@ -0,0 +1,97 @@ +/* + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#define COMPONENTS_CPP_UTILS_BLESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include "BLECharacteristic.h" +#include "BLEServer.h" +#include "BLEUUID.h" +#include "FreeRTOS.h" + +class BLEServer; + +/** + * @brief A data mapping used to manage the set of %BLE characteristics known to the server. + */ +class BLECharacteristicMap { +public: + void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); + void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); + BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(BLEUUID uuid); + BLECharacteristic* getByHandle(uint16_t handle); + BLECharacteristic* getFirst(); + BLECharacteristic* getNext(); + std::string toString(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; + + +/** + * @brief The model of a %BLE service. + * + */ +class BLEService { +public: + void addCharacteristic(BLECharacteristic* pCharacteristic); + BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); + BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); + void dump(); + void executeCreate(BLEServer* pServer); + void executeDelete(); + BLECharacteristic* getCharacteristic(const char* uuid); + BLECharacteristic* getCharacteristic(BLEUUID uuid); + BLEUUID getUUID(); + BLEServer* getServer(); + void start(); + void stop(); + std::string toString(); + uint16_t getHandle(); + uint8_t m_instId = 0; + +private: + BLEService(const char* uuid, uint16_t numHandles); + BLEService(BLEUUID uuid, uint16_t numHandles); + friend class BLEServer; + friend class BLEServiceMap; + friend class BLEDescriptor; + friend class BLECharacteristic; + friend class BLEDevice; + + BLECharacteristicMap m_characteristicMap; + uint16_t m_handle; + BLECharacteristic* m_lastCreatedCharacteristic = nullptr; + BLEServer* m_pServer = nullptr; + BLEUUID m_uuid; + + FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); + FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); + FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); + + uint16_t m_numHandles; + + BLECharacteristic* getLastCreatedCharacteristic(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void setHandle(uint16_t handle); + //void setService(esp_gatt_srvc_id_t srvc_id); +}; // BLEService + + +#endif // CONFIG_BT_ENABLED +#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServiceMap.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServiceMap.cpp new file mode 100644 index 0000000..cf4f75f --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEServiceMap.cpp @@ -0,0 +1,134 @@ +/* + * BLEServiceMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include "BLEService.h" + + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(const char* uuid) { + return getByUUID(BLEUUID(uuid)); +} + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +BLEService* BLEServiceMap::getByUUID(BLEUUID uuid, uint8_t inst_id) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the service by handle. + * @param [in] handle The handle to look up the service. + * @return The service. + */ +BLEService* BLEServiceMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Set the service by UUID. + * @param [in] uuid The uuid of the service. + * @param [in] characteristic The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByUUID(BLEUUID uuid, BLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the service by handle. + * @param [in] handle The handle of the service. + * @param [in] service The service to cache. + * @return N/A. + */ +void BLEServiceMap::setByHandle(uint16_t handle, BLEService* service) { + m_handleMap.insert(std::pair(handle, service)); +} // setByHandle + + +/** + * @brief Return a string representation of the service map. + * @return A string representation of the service map. + */ +std::string BLEServiceMap::toString() { + std::stringstream stringStream; + stringStream << std::hex << std::setfill('0'); + for (auto &myPair: m_handleMap) { + stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n"; + } + return stringStream.str(); +} // toString + +void BLEServiceMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +BLEService* BLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +BLEService* BLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + BLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void BLEServiceMap::removeService(BLEService* service) { + m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +/** + * @brief Returns the amount of registered services + * @return amount of registered services + */ +int BLEServiceMap::getRegisteredServiceCount(){ + return m_handleMap.size(); +} + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.cpp new file mode 100644 index 0000000..4ddf8fc --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.cpp @@ -0,0 +1,407 @@ +/* + * BLEUUID.cpp + * + * Created on: Jun 21, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include +#include +#include +#include +#include +#include "BLEUUID.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEUUID"; +#endif + + +/** + * @brief Copy memory from source to target but in reverse order. + * + * When we move memory from one location it is normally: + * + * ``` + * [0][1][2]...[n] -> [0][1][2]...[n] + * ``` + * + * with this function, it is: + * + * ``` + * [0][1][2]...[n] -> [n][n-1][n-2]...[0] + * ``` + * + * @param [in] target The target of the copy + * @param [in] source The source of the copy + * @param [in] size The number of bytes to copy + */ +static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { + assert(size > 0); + target += (size - 1); // Point target to the last byte of the target data + while (size > 0) { + *target = *source; + target--; + source++; + size--; + } +} // memrcpy + + +/** + * @brief Create a UUID from a string. + * + * Create a UUID from a string. There will be two possible stories here. Either the string represents + * a binary data field or the string represents a hex encoding of a UUID. + * For the hex encoding, here is an example: + * + * ``` + * "beb5483e-36e1-4688-b7f5-ea07361b26a8" + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * 12345678-90ab-cdef-1234-567890abcdef + * ``` + * + * This has a length of 36 characters. We need to parse this into 16 bytes. + * + * @param [in] value The string to build a UUID from. + */ +BLEUUID::BLEUUID(std::string value) { + m_valueSet = true; + if (value.length() == 4) { + m_uuid.len = ESP_UUID_LEN_16; + m_uuid.uuid.uuid16 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid16 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(2-i)*4; + i+=2; + } + } + else if (value.length() == 8) { + m_uuid.len = ESP_UUID_LEN_32; + m_uuid.uuid.uuid32 = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid32 += (((MSB&0x0F) <<4) | (LSB & 0x0F))<<(6-i)*4; + i+=2; + } + } + else if (value.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be investigated (lack of time) + m_uuid.len = ESP_UUID_LEN_128; + memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16); + } + else if (value.length() == 36) { + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. + m_uuid.len = ESP_UUID_LEN_128; + int n = 0; + for(int i=0;i '9') MSB -= 7; + if(LSB > '9') LSB -= 7; + m_uuid.uuid.uuid128[15-n++] = ((MSB&0x0F) <<4) | (LSB & 0x0F); + i+=2; + } + } + else { + ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes"); + m_valueSet = false; + } +} //BLEUUID(std::string) + + +/** + * @brief Create a UUID from 16 bytes of memory. + * + * @param [in] pData The pointer to the start of the UUID. + * @param [in] size The size of the data. + * @param [in] msbFirst Is the MSB first in pData memory? + */ +BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) { + if (size != 16) { + ESP_LOGE(LOG_TAG, "ERROR: UUID length not 16 bytes"); + return; + } + m_uuid.len = ESP_UUID_LEN_128; + if (msbFirst) { + memrcpy(m_uuid.uuid.uuid128, pData, 16); + } else { + memcpy(m_uuid.uuid.uuid128, pData, 16); + } + m_valueSet = true; +} // BLEUUID + + +/** + * @brief Create a UUID from the 16bit value. + * + * @param [in] uuid The 16bit short form UUID. + */ +BLEUUID::BLEUUID(uint16_t uuid) { + m_uuid.len = ESP_UUID_LEN_16; + m_uuid.uuid.uuid16 = uuid; + m_valueSet = true; +} // BLEUUID + + +/** + * @brief Create a UUID from the 32bit value. + * + * @param [in] uuid The 32bit short form UUID. + */ +BLEUUID::BLEUUID(uint32_t uuid) { + m_uuid.len = ESP_UUID_LEN_32; + m_uuid.uuid.uuid32 = uuid; + m_valueSet = true; +} // BLEUUID + + +/** + * @brief Create a UUID from the native UUID. + * + * @param [in] uuid The native UUID. + */ +BLEUUID::BLEUUID(esp_bt_uuid_t uuid) { + m_uuid = uuid; + m_valueSet = true; +} // BLEUUID + + +/** + * @brief Create a UUID from the ESP32 esp_gat_id_t. + * + * @param [in] gattId The data to create the UUID from. + */ +BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) { +} // BLEUUID + + +BLEUUID::BLEUUID() { + m_valueSet = false; +} // BLEUUID + + +/** + * @brief Get the number of bits in this uuid. + * @return The number of bits in the UUID. One of 16, 32 or 128. + */ +uint8_t BLEUUID::bitSize() { + if (!m_valueSet) return 0; + switch (m_uuid.len) { + case ESP_UUID_LEN_16: + return 16; + case ESP_UUID_LEN_32: + return 32; + case ESP_UUID_LEN_128: + return 128; + default: + ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len); + return 0; + } // End of switch +} // bitSize + + +/** + * @brief Compare a UUID against this UUID. + * + * @param [in] uuid The UUID to compare against. + * @return True if the UUIDs are equal and false otherwise. + */ +bool BLEUUID::equals(BLEUUID uuid) { + //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str()); + if (!m_valueSet || !uuid.m_valueSet) return false; + + if (uuid.m_uuid.len != m_uuid.len) { + return uuid.toString() == toString(); + } + + if (uuid.m_uuid.len == ESP_UUID_LEN_16) { + return uuid.m_uuid.uuid.uuid16 == m_uuid.uuid.uuid16; + } + + if (uuid.m_uuid.len == ESP_UUID_LEN_32) { + return uuid.m_uuid.uuid.uuid32 == m_uuid.uuid.uuid32; + } + + return memcmp(uuid.m_uuid.uuid.uuid128, m_uuid.uuid.uuid128, 16) == 0; +} // equals + + +/** + * Create a BLEUUID from a string of the form: + * 0xNNNN + * 0xNNNNNNNN + * 0x + * NNNN + * NNNNNNNN + * + */ +BLEUUID BLEUUID::fromString(std::string _uuid) { + uint8_t start = 0; + if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. + start = 2; + } + uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use. + + if(len == 4) { + uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + } else if (len == 8) { + uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16); + return BLEUUID(x); + } else if (len == 36) { + return BLEUUID(_uuid); + } + return BLEUUID(); +} // fromString + + +/** + * @brief Get the native UUID value. + * + * @return The native UUID value or NULL if not set. + */ +esp_bt_uuid_t* BLEUUID::getNative() { + //ESP_LOGD(TAG, ">> getNative()") + if (m_valueSet == false) { + ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!"); + return nullptr; + } + //ESP_LOGD(TAG, "<< getNative()"); + return &m_uuid; +} // getNative + + +/** + * @brief Convert a UUID to its 128 bit representation. + * + * A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method + * will convert 16 or 32 bit representations to the full 128bit. + */ +BLEUUID BLEUUID::to128() { + //ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str()); + + // If we either don't have a value or are already a 128 bit UUID, nothing further to do. + if (!m_valueSet || m_uuid.len == ESP_UUID_LEN_128) { + return *this; + } + + // If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID. + if (m_uuid.len == ESP_UUID_LEN_16) { + uint16_t temp = m_uuid.uuid.uuid16; + m_uuid.uuid.uuid128[15] = 0; + m_uuid.uuid.uuid128[14] = 0; + m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff; + m_uuid.uuid.uuid128[12] = temp & 0xff; + + } + else if (m_uuid.len == ESP_UUID_LEN_32) { + uint32_t temp = m_uuid.uuid.uuid32; + m_uuid.uuid.uuid128[15] = (temp >> 24) & 0xff; + m_uuid.uuid.uuid128[14] = (temp >> 16) & 0xff; + m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff; + m_uuid.uuid.uuid128[12] = temp & 0xff; + } + + // Set the fixed parts of the UUID. + m_uuid.uuid.uuid128[11] = 0x00; + m_uuid.uuid.uuid128[10] = 0x00; + + m_uuid.uuid.uuid128[9] = 0x10; + m_uuid.uuid.uuid128[8] = 0x00; + + m_uuid.uuid.uuid128[7] = 0x80; + m_uuid.uuid.uuid128[6] = 0x00; + + m_uuid.uuid.uuid128[5] = 0x00; + m_uuid.uuid.uuid128[4] = 0x80; + m_uuid.uuid.uuid128[3] = 0x5f; + m_uuid.uuid.uuid128[2] = 0x9b; + m_uuid.uuid.uuid128[1] = 0x34; + m_uuid.uuid.uuid128[0] = 0xfb; + + m_uuid.len = ESP_UUID_LEN_128; + //ESP_LOGD(TAG, "<< toFull <- %s", toString().c_str()); + return *this; +} // to128 + + + + +/** + * @brief Get a string representation of the UUID. + * + * The format of a string is: + * 01234567 8901 2345 6789 012345678901 + * 0000180d-0000-1000-8000-00805f9b34fb + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * + * @return A string representation of the UUID. + */ +std::string BLEUUID::toString() { + if (!m_valueSet) return ""; // If we have no value, nothing to format. + + // If the UUIDs are 16 or 32 bit, pad correctly. + std::stringstream ss; + + if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly. + ss << "0000" << + std::hex << + std::setfill('0') << + std::setw(4) << + m_uuid.uuid.uuid16 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // Return the string + } // End 16bit UUID + + if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly. + ss << std::hex << + std::setfill('0') << + std::setw(8) << + m_uuid.uuid.uuid32 << + "-0000-1000-8000-00805f9b34fb"; + return ss.str(); // return the string + } // End 32bit UUID + + // The UUID is not 16bit or 32bit which means that it is 128bit. + // + // UUID string format: + // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP + ss << std::hex << std::setfill('0') << + std::setw(2) << (int) m_uuid.uuid.uuid128[15] << + std::setw(2) << (int) m_uuid.uuid.uuid128[14] << + std::setw(2) << (int) m_uuid.uuid.uuid128[13] << + std::setw(2) << (int) m_uuid.uuid.uuid128[12] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[11] << + std::setw(2) << (int) m_uuid.uuid.uuid128[10] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[9] << + std::setw(2) << (int) m_uuid.uuid.uuid128[8] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[7] << + std::setw(2) << (int) m_uuid.uuid.uuid128[6] << "-" << + std::setw(2) << (int) m_uuid.uuid.uuid128[5] << + std::setw(2) << (int) m_uuid.uuid.uuid128[4] << + std::setw(2) << (int) m_uuid.uuid.uuid128[3] << + std::setw(2) << (int) m_uuid.uuid.uuid128[2] << + std::setw(2) << (int) m_uuid.uuid.uuid128[1] << + std::setw(2) << (int) m_uuid.uuid.uuid128[0]; + return ss.str(); +} // toString + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.h new file mode 100644 index 0000000..700739b --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUUID.h @@ -0,0 +1,39 @@ +/* + * BLEUUID.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_ +#define COMPONENTS_CPP_UTILS_BLEUUID_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include + +/** + * @brief A model of a %BLE UUID. + */ +class BLEUUID { +public: + BLEUUID(std::string uuid); + BLEUUID(uint16_t uuid); + BLEUUID(uint32_t uuid); + BLEUUID(esp_bt_uuid_t uuid); + BLEUUID(uint8_t* pData, size_t size, bool msbFirst); + BLEUUID(esp_gatt_id_t gattId); + BLEUUID(); + uint8_t bitSize(); // Get the number of bits in this uuid. + bool equals(BLEUUID uuid); + esp_bt_uuid_t* getNative(); + BLEUUID to128(); + std::string toString(); + static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string + +private: + esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps. + bool m_valueSet = false; // Is there a value set for this instance. +}; // BLEUUID +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.cpp new file mode 100644 index 0000000..5cd55f9 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.cpp @@ -0,0 +1,2033 @@ +/* + * BLEUtils.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "BLEAddress.h" +#include "BLEClient.h" +#include "BLEUtils.h" +#include "BLEUUID.h" +#include "GeneralUtils.h" + +#include +#include +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 ESP-IDF +#include // Part of C++ STL +#include +#include + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "BLEUtils"; // Tag for logging. +#endif + + +/* +static std::map g_addressMap; +static std::map g_connIdMap; +*/ + +typedef struct { + uint32_t assignedNumber; + const char* name; +} member_t; + +static const member_t members_ids[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + {0xFE08, "Microsoft"}, + {0xFE09, "Pillsy, Inc."}, + {0xFE0A, "ruwido austria gmbh"}, + {0xFE0B, "ruwido austria gmbh"}, + {0xFE0C, "Procter & Gamble"}, + {0xFE0D, "Procter & Gamble"}, + {0xFE0E, "Setec Pty Ltd"}, + {0xFE0F, "Philips Lighting B.V."}, + {0xFE10, "Lapis Semiconductor Co., Ltd."}, + {0xFE11, "GMC-I Messtechnik GmbH"}, + {0xFE12, "M-Way Solutions GmbH"}, + {0xFE13, "Apple Inc."}, + {0xFE14, "Flextronics International USA Inc."}, + {0xFE15, "Amazon Fulfillment Services, Inc."}, + {0xFE16, "Footmarks, Inc."}, + {0xFE17, "Telit Wireless Solutions GmbH"}, + {0xFE18, "Runtime, Inc."}, + {0xFE19, "Google Inc."}, + {0xFE1A, "Tyto Life LLC"}, + {0xFE1B, "Tyto Life LLC"}, + {0xFE1C, "NetMedia, Inc."}, + {0xFE1D, "Illuminati Instrument Corporation"}, + {0xFE1E, "Smart Innovations Co., Ltd"}, + {0xFE1F, "Garmin International, Inc."}, + {0xFE20, "Emerson"}, + {0xFE21, "Bose Corporation"}, + {0xFE22, "Zoll Medical Corporation"}, + {0xFE23, "Zoll Medical Corporation"}, + {0xFE24, "August Home Inc"}, + {0xFE25, "Apple, Inc. "}, + {0xFE26, "Google Inc."}, + {0xFE27, "Google Inc."}, + {0xFE28, "Ayla Networks"}, + {0xFE29, "Gibson Innovations"}, + {0xFE2A, "DaisyWorks, Inc."}, + {0xFE2B, "ITT Industries"}, + {0xFE2C, "Google Inc."}, + {0xFE2D, "SMART INNOVATION Co.,Ltd"}, + {0xFE2E, "ERi,Inc."}, + {0xFE2F, "CRESCO Wireless, Inc"}, + {0xFE30, "Volkswagen AG"}, + {0xFE31, "Volkswagen AG"}, + {0xFE32, "Pro-Mark, Inc."}, + {0xFE33, "CHIPOLO d.o.o."}, + {0xFE34, "SmallLoop LLC"}, + {0xFE35, "HUAWEI Technologies Co., Ltd"}, + {0xFE36, "HUAWEI Technologies Co., Ltd"}, + {0xFE37, "Spaceek LTD"}, + {0xFE38, "Spaceek LTD"}, + {0xFE39, "TTS Tooltechnic Systems AG & Co. KG"}, + {0xFE3A, "TTS Tooltechnic Systems AG & Co. KG"}, + {0xFE3B, "Dolby Laboratories"}, + {0xFE3C, "Alibaba"}, + {0xFE3D, "BD Medical"}, + {0xFE3E, "BD Medical"}, + {0xFE3F, "Friday Labs Limited"}, + {0xFE40, "Inugo Systems Limited"}, + {0xFE41, "Inugo Systems Limited"}, + {0xFE42, "Nets A/S "}, + {0xFE43, "Andreas Stihl AG & Co. KG"}, + {0xFE44, "SK Telecom "}, + {0xFE45, "Snapchat Inc"}, + {0xFE46, "B&O Play A/S "}, + {0xFE47, "General Motors"}, + {0xFE48, "General Motors"}, + {0xFE49, "SenionLab AB"}, + {0xFE4A, "OMRON HEALTHCARE Co., Ltd."}, + {0xFE4B, "Philips Lighting B.V."}, + {0xFE4C, "Volkswagen AG"}, + {0xFE4D, "Casambi Technologies Oy"}, + {0xFE4E, "NTT docomo"}, + {0xFE4F, "Molekule, Inc."}, + {0xFE50, "Google Inc."}, + {0xFE51, "SRAM"}, + {0xFE52, "SetPoint Medical"}, + {0xFE53, "3M"}, + {0xFE54, "Motiv, Inc."}, + {0xFE55, "Google Inc."}, + {0xFE56, "Google Inc."}, + {0xFE57, "Dotted Labs"}, + {0xFE58, "Nordic Semiconductor ASA"}, + {0xFE59, "Nordic Semiconductor ASA"}, + {0xFE5A, "Chronologics Corporation"}, + {0xFE5B, "GT-tronics HK Ltd"}, + {0xFE5C, "million hunters GmbH"}, + {0xFE5D, "Grundfos A/S"}, + {0xFE5E, "Plastc Corporation"}, + {0xFE5F, "Eyefi, Inc."}, + {0xFE60, "Lierda Science & Technology Group Co., Ltd."}, + {0xFE61, "Logitech International SA"}, + {0xFE62, "Indagem Tech LLC"}, + {0xFE63, "Connected Yard, Inc."}, + {0xFE64, "Siemens AG"}, + {0xFE65, "CHIPOLO d.o.o."}, + {0xFE66, "Intel Corporation"}, + {0xFE67, "Lab Sensor Solutions"}, + {0xFE68, "Qualcomm Life Inc"}, + {0xFE69, "Qualcomm Life Inc"}, + {0xFE6A, "Kontakt Micro-Location Sp. z o.o."}, + {0xFE6B, "TASER International, Inc."}, + {0xFE6C, "TASER International, Inc."}, + {0xFE6D, "The University of Tokyo"}, + {0xFE6E, "The University of Tokyo"}, + {0xFE6F, "LINE Corporation"}, + {0xFE70, "Beijing Jingdong Century Trading Co., Ltd."}, + {0xFE71, "Plume Design Inc"}, + {0xFE72, "St. Jude Medical, Inc."}, + {0xFE73, "St. Jude Medical, Inc."}, + {0xFE74, "unwire"}, + {0xFE75, "TangoMe"}, + {0xFE76, "TangoMe"}, + {0xFE77, "Hewlett-Packard Company"}, + {0xFE78, "Hewlett-Packard Company"}, + {0xFE79, "Zebra Technologies"}, + {0xFE7A, "Bragi GmbH"}, + {0xFE7B, "Orion Labs, Inc."}, + {0xFE7C, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, + {0xFE7D, "Aterica Health Inc."}, + {0xFE7E, "Awear Solutions Ltd"}, + {0xFE7F, "Doppler Lab"}, + {0xFE80, "Doppler Lab"}, + {0xFE81, "Medtronic Inc."}, + {0xFE82, "Medtronic Inc."}, + {0xFE83, "Blue Bite"}, + {0xFE84, "RF Digital Corp"}, + {0xFE85, "RF Digital Corp"}, + {0xFE86, "HUAWEI Technologies Co., Ltd. ( )"}, + {0xFE87, "Qingdao Yeelink Information Technology Co., Ltd. ( )"}, + {0xFE88, "SALTO SYSTEMS S.L."}, + {0xFE89, "B&O Play A/S"}, + {0xFE8A, "Apple, Inc."}, + {0xFE8B, "Apple, Inc."}, + {0xFE8C, "TRON Forum"}, + {0xFE8D, "Interaxon Inc."}, + {0xFE8E, "ARM Ltd"}, + {0xFE8F, "CSR"}, + {0xFE90, "JUMA"}, + {0xFE91, "Shanghai Imilab Technology Co.,Ltd"}, + {0xFE92, "Jarden Safety & Security"}, + {0xFE93, "OttoQ Inc."}, + {0xFE94, "OttoQ Inc."}, + {0xFE95, "Xiaomi Inc."}, + {0xFE96, "Tesla Motor Inc."}, + {0xFE97, "Tesla Motor Inc."}, + {0xFE98, "Currant, Inc."}, + {0xFE99, "Currant, Inc."}, + {0xFE9A, "Estimote"}, + {0xFE9B, "Samsara Networks, Inc"}, + {0xFE9C, "GSI Laboratories, Inc."}, + {0xFE9D, "Mobiquity Networks Inc"}, + {0xFE9E, "Dialog Semiconductor B.V."}, + {0xFE9F, "Google Inc."}, + {0xFEA0, "Google Inc."}, + {0xFEA1, "Intrepid Control Systems, Inc."}, + {0xFEA2, "Intrepid Control Systems, Inc."}, + {0xFEA3, "ITT Industries"}, + {0xFEA4, "Paxton Access Ltd"}, + {0xFEA5, "GoPro, Inc."}, + {0xFEA6, "GoPro, Inc."}, + {0xFEA7, "UTC Fire and Security"}, + {0xFEA8, "Savant Systems LLC"}, + {0xFEA9, "Savant Systems LLC"}, + {0xFEAA, "Google Inc."}, + {0xFEAB, "Nokia Corporation"}, + {0xFEAC, "Nokia Corporation"}, + {0xFEAD, "Nokia Corporation"}, + {0xFEAE, "Nokia Corporation"}, + {0xFEAF, "Nest Labs Inc."}, + {0xFEB0, "Nest Labs Inc."}, + {0xFEB1, "Electronics Tomorrow Limited"}, + {0xFEB2, "Microsoft Corporation"}, + {0xFEB3, "Taobao"}, + {0xFEB4, "WiSilica Inc."}, + {0xFEB5, "WiSilica Inc."}, + {0xFEB6, "Vencer Co, Ltd"}, + {0xFEB7, "Facebook, Inc."}, + {0xFEB8, "Facebook, Inc."}, + {0xFEB9, "LG Electronics"}, + {0xFEBA, "Tencent Holdings Limited"}, + {0xFEBB, "adafruit industries"}, + {0xFEBC, "Dexcom, Inc. "}, + {0xFEBD, "Clover Network, Inc."}, + {0xFEBE, "Bose Corporation"}, + {0xFEBF, "Nod, Inc."}, + {0xFEC0, "KDDI Corporation"}, + {0xFEC1, "KDDI Corporation"}, + {0xFEC2, "Blue Spark Technologies, Inc."}, + {0xFEC3, "360fly, Inc."}, + {0xFEC4, "PLUS Location Systems"}, + {0xFEC5, "Realtek Semiconductor Corp."}, + {0xFEC6, "Kocomojo, LLC"}, + {0xFEC7, "Apple, Inc."}, + {0xFEC8, "Apple, Inc."}, + {0xFEC9, "Apple, Inc."}, + {0xFECA, "Apple, Inc."}, + {0xFECB, "Apple, Inc."}, + {0xFECC, "Apple, Inc."}, + {0xFECD, "Apple, Inc."}, + {0xFECE, "Apple, Inc."}, + {0xFECF, "Apple, Inc."}, + {0xFED0, "Apple, Inc."}, + {0xFED1, "Apple, Inc."}, + {0xFED2, "Apple, Inc."}, + {0xFED3, "Apple, Inc."}, + {0xFED4, "Apple, Inc."}, + {0xFED5, "Plantronics Inc."}, + {0xFED6, "Broadcom Corporation"}, + {0xFED7, "Broadcom Corporation"}, + {0xFED8, "Google Inc."}, + {0xFED9, "Pebble Technology Corporation"}, + {0xFEDA, "ISSC Technologies Corporation"}, + {0xFEDB, "Perka, Inc."}, + {0xFEDC, "Jawbone"}, + {0xFEDD, "Jawbone"}, + {0xFEDE, "Coin, Inc."}, + {0xFEDF, "Design SHIFT"}, + {0xFEE0, "Anhui Huami Information Technology Co."}, + {0xFEE1, "Anhui Huami Information Technology Co."}, + {0xFEE2, "Anki, Inc."}, + {0xFEE3, "Anki, Inc."}, + {0xFEE4, "Nordic Semiconductor ASA"}, + {0xFEE5, "Nordic Semiconductor ASA"}, + {0xFEE6, "Silvair, Inc."}, + {0xFEE7, "Tencent Holdings Limited"}, + {0xFEE8, "Quintic Corp."}, + {0xFEE9, "Quintic Corp."}, + {0xFEEA, "Swirl Networks, Inc."}, + {0xFEEB, "Swirl Networks, Inc."}, + {0xFEEC, "Tile, Inc."}, + {0xFEED, "Tile, Inc."}, + {0xFEEE, "Polar Electro Oy"}, + {0xFEEF, "Polar Electro Oy"}, + {0xFEF0, "Intel"}, + {0xFEF1, "CSR"}, + {0xFEF2, "CSR"}, + {0xFEF3, "Google Inc."}, + {0xFEF4, "Google Inc."}, + {0xFEF5, "Dialog Semiconductor GmbH"}, + {0xFEF6, "Wicentric, Inc."}, + {0xFEF7, "Aplix Corporation"}, + {0xFEF8, "Aplix Corporation"}, + {0xFEF9, "PayPal, Inc."}, + {0xFEFA, "PayPal, Inc."}, + {0xFEFB, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"}, + {0xFEFC, "Gimbal, Inc."}, + {0xFEFD, "Gimbal, Inc."}, + {0xFEFE, "GN ReSound A/S"}, + {0xFEFF, "GN Netcom"}, + {0xFFFF, "Reserved"}, /*for testing purposes only*/ +#endif + {0, "" } +}; + +typedef struct { + uint32_t assignedNumber; + const char* name; +} gattdescriptor_t; + +static const gattdescriptor_t g_descriptor_ids[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + {0x2905,"Characteristic Aggregate Format"}, + {0x2900,"Characteristic Extended Properties"}, + {0x2904,"Characteristic Presentation Format"}, + {0x2901,"Characteristic User Description"}, + {0x2902,"Client Characteristic Configuration"}, + {0x290B,"Environmental Sensing Configuration"}, + {0x290C,"Environmental Sensing Measurement"}, + {0x290D,"Environmental Sensing Trigger Setting"}, + {0x2907,"External Report Reference"}, + {0x2909,"Number of Digitals"}, + {0x2908,"Report Reference"}, + {0x2903,"Server Characteristic Configuration"}, + {0x290E,"Time Trigger Setting"}, + {0x2906,"Valid Range"}, + {0x290A,"Value Trigger Setting"}, +#endif + { 0, "" } +}; + +typedef struct { + uint32_t assignedNumber; + const char* name; +} characteristicMap_t; + +static const characteristicMap_t g_characteristicsMappings[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + {0x2A7E,"Aerobic Heart Rate Lower Limit"}, + {0x2A84,"Aerobic Heart Rate Upper Limit"}, + {0x2A7F,"Aerobic Threshold"}, + {0x2A80,"Age"}, + {0x2A5A,"Aggregate"}, + {0x2A43,"Alert Category ID"}, + {0x2A42,"Alert Category ID Bit Mask"}, + {0x2A06,"Alert Level"}, + {0x2A44,"Alert Notification Control Point"}, + {0x2A3F,"Alert Status"}, + {0x2AB3,"Altitude"}, + {0x2A81,"Anaerobic Heart Rate Lower Limit"}, + {0x2A82,"Anaerobic Heart Rate Upper Limit"}, + {0x2A83,"Anaerobic Threshold"}, + {0x2A58,"Analog"}, + {0x2A59,"Analog Output"}, + {0x2A73,"Apparent Wind Direction"}, + {0x2A72,"Apparent Wind Speed"}, + {0x2A01,"Appearance"}, + {0x2AA3,"Barometric Pressure Trend"}, + {0x2A19,"Battery Level"}, + {0x2A1B,"Battery Level State"}, + {0x2A1A,"Battery Power State"}, + {0x2A49,"Blood Pressure Feature"}, + {0x2A35,"Blood Pressure Measurement"}, + {0x2A9B,"Body Composition Feature"}, + {0x2A9C,"Body Composition Measurement"}, + {0x2A38,"Body Sensor Location"}, + {0x2AA4,"Bond Management Control Point"}, + {0x2AA5,"Bond Management Features"}, + {0x2A22,"Boot Keyboard Input Report"}, + {0x2A32,"Boot Keyboard Output Report"}, + {0x2A33,"Boot Mouse Input Report"}, + {0x2AA6,"Central Address Resolution"}, + {0x2AA8,"CGM Feature"}, + {0x2AA7,"CGM Measurement"}, + {0x2AAB,"CGM Session Run Time"}, + {0x2AAA,"CGM Session Start Time"}, + {0x2AAC,"CGM Specific Ops Control Point"}, + {0x2AA9,"CGM Status"}, + {0x2ACE,"Cross Trainer Data"}, + {0x2A5C,"CSC Feature"}, + {0x2A5B,"CSC Measurement"}, + {0x2A2B,"Current Time"}, + {0x2A66,"Cycling Power Control Point"}, + {0x2A66,"Cycling Power Control Point"}, + {0x2A65,"Cycling Power Feature"}, + {0x2A65,"Cycling Power Feature"}, + {0x2A63,"Cycling Power Measurement"}, + {0x2A64,"Cycling Power Vector"}, + {0x2A99,"Database Change Increment"}, + {0x2A85,"Date of Birth"}, + {0x2A86,"Date of Threshold Assessment"}, + {0x2A08,"Date Time"}, + {0x2A0A,"Day Date Time"}, + {0x2A09,"Day of Week"}, + {0x2A7D,"Descriptor Value Changed"}, + {0x2A00,"Device Name"}, + {0x2A7B,"Dew Point"}, + {0x2A56,"Digital"}, + {0x2A57,"Digital Output"}, + {0x2A0D,"DST Offset"}, + {0x2A6C,"Elevation"}, + {0x2A87,"Email Address"}, + {0x2A0B,"Exact Time 100"}, + {0x2A0C,"Exact Time 256"}, + {0x2A88,"Fat Burn Heart Rate Lower Limit"}, + {0x2A89,"Fat Burn Heart Rate Upper Limit"}, + {0x2A26,"Firmware Revision String"}, + {0x2A8A,"First Name"}, + {0x2AD9,"Fitness Machine Control Point"}, + {0x2ACC,"Fitness Machine Feature"}, + {0x2ADA,"Fitness Machine Status"}, + {0x2A8B,"Five Zone Heart Rate Limits"}, + {0x2AB2,"Floor Number"}, + {0x2A8C,"Gender"}, + {0x2A51,"Glucose Feature"}, + {0x2A18,"Glucose Measurement"}, + {0x2A34,"Glucose Measurement Context"}, + {0x2A74,"Gust Factor"}, + {0x2A27,"Hardware Revision String"}, + {0x2A39,"Heart Rate Control Point"}, + {0x2A8D,"Heart Rate Max"}, + {0x2A37,"Heart Rate Measurement"}, + {0x2A7A,"Heat Index"}, + {0x2A8E,"Height"}, + {0x2A4C,"HID Control Point"}, + {0x2A4A,"HID Information"}, + {0x2A8F,"Hip Circumference"}, + {0x2ABA,"HTTP Control Point"}, + {0x2AB9,"HTTP Entity Body"}, + {0x2AB7,"HTTP Headers"}, + {0x2AB8,"HTTP Status Code"}, + {0x2ABB,"HTTPS Security"}, + {0x2A6F,"Humidity"}, + {0x2A2A,"IEEE 11073-20601 Regulatory Certification Data List"}, + {0x2AD2,"Indoor Bike Data"}, + {0x2AAD,"Indoor Positioning Configuration"}, + {0x2A36,"Intermediate Cuff Pressure"}, + {0x2A1E,"Intermediate Temperature"}, + {0x2A77,"Irradiance"}, + {0x2AA2,"Language"}, + {0x2A90,"Last Name"}, + {0x2AAE,"Latitude"}, + {0x2A6B,"LN Control Point"}, + {0x2A6A,"LN Feature"}, + {0x2AB1,"Local East Coordinate"}, + {0x2AB0,"Local North Coordinate"}, + {0x2A0F,"Local Time Information"}, + {0x2A67,"Location and Speed Characteristic"}, + {0x2AB5,"Location Name"}, + {0x2AAF,"Longitude"}, + {0x2A2C,"Magnetic Declination"}, + {0x2AA0,"Magnetic Flux Density - 2D"}, + {0x2AA1,"Magnetic Flux Density - 3D"}, + {0x2A29,"Manufacturer Name String"}, + {0x2A91,"Maximum Recommended Heart Rate"}, + {0x2A21,"Measurement Interval"}, + {0x2A24,"Model Number String"}, + {0x2A68,"Navigation"}, + {0x2A3E,"Network Availability"}, + {0x2A46,"New Alert"}, + {0x2AC5,"Object Action Control Point"}, + {0x2AC8,"Object Changed"}, + {0x2AC1,"Object First-Created"}, + {0x2AC3,"Object ID"}, + {0x2AC2,"Object Last-Modified"}, + {0x2AC6,"Object List Control Point"}, + {0x2AC7,"Object List Filter"}, + {0x2ABE,"Object Name"}, + {0x2AC4,"Object Properties"}, + {0x2AC0,"Object Size"}, + {0x2ABF,"Object Type"}, + {0x2ABD,"OTS Feature"}, + {0x2A04,"Peripheral Preferred Connection Parameters"}, + {0x2A02,"Peripheral Privacy Flag"}, + {0x2A5F,"PLX Continuous Measurement Characteristic"}, + {0x2A60,"PLX Features"}, + {0x2A5E,"PLX Spot-Check Measurement"}, + {0x2A50,"PnP ID"}, + {0x2A75,"Pollen Concentration"}, + {0x2A2F,"Position 2D"}, + {0x2A30,"Position 3D"}, + {0x2A69,"Position Quality"}, + {0x2A6D,"Pressure"}, + {0x2A4E,"Protocol Mode"}, + {0x2A62,"Pulse Oximetry Control Point"}, + {0x2A60,"Pulse Oximetry Pulsatile Event Characteristic"}, + {0x2A78,"Rainfall"}, + {0x2A03,"Reconnection Address"}, + {0x2A52,"Record Access Control Point"}, + {0x2A14,"Reference Time Information"}, + {0x2A3A,"Removable"}, + {0x2A4D,"Report"}, + {0x2A4B,"Report Map"}, + {0x2AC9,"Resolvable Private Address Only"}, + {0x2A92,"Resting Heart Rate"}, + {0x2A40,"Ringer Control point"}, + {0x2A41,"Ringer Setting"}, + {0x2AD1,"Rower Data"}, + {0x2A54,"RSC Feature"}, + {0x2A53,"RSC Measurement"}, + {0x2A55,"SC Control Point"}, + {0x2A4F,"Scan Interval Window"}, + {0x2A31,"Scan Refresh"}, + {0x2A3C,"Scientific Temperature Celsius"}, + {0x2A10,"Secondary Time Zone"}, + {0x2A5D,"Sensor Location"}, + {0x2A25,"Serial Number String"}, + {0x2A05,"Service Changed"}, + {0x2A3B,"Service Required"}, + {0x2A28,"Software Revision String"}, + {0x2A93,"Sport Type for Aerobic and Anaerobic Thresholds"}, + {0x2AD0,"Stair Climber Data"}, + {0x2ACF,"Step Climber Data"}, + {0x2A3D,"String"}, + {0x2AD7,"Supported Heart Rate Range"}, + {0x2AD5,"Supported Inclination Range"}, + {0x2A47,"Supported New Alert Category"}, + {0x2AD8,"Supported Power Range"}, + {0x2AD6,"Supported Resistance Level Range"}, + {0x2AD4,"Supported Speed Range"}, + {0x2A48,"Supported Unread Alert Category"}, + {0x2A23,"System ID"}, + {0x2ABC,"TDS Control Point"}, + {0x2A6E,"Temperature"}, + {0x2A1F,"Temperature Celsius"}, + {0x2A20,"Temperature Fahrenheit"}, + {0x2A1C,"Temperature Measurement"}, + {0x2A1D,"Temperature Type"}, + {0x2A94,"Three Zone Heart Rate Limits"}, + {0x2A12,"Time Accuracy"}, + {0x2A15,"Time Broadcast"}, + {0x2A13,"Time Source"}, + {0x2A16,"Time Update Control Point"}, + {0x2A17,"Time Update State"}, + {0x2A11,"Time with DST"}, + {0x2A0E,"Time Zone"}, + {0x2AD3,"Training Status"}, + {0x2ACD,"Treadmill Data"}, + {0x2A71,"True Wind Direction"}, + {0x2A70,"True Wind Speed"}, + {0x2A95,"Two Zone Heart Rate Limit"}, + {0x2A07,"Tx Power Level"}, + {0x2AB4,"Uncertainty"}, + {0x2A45,"Unread Alert Status"}, + {0x2AB6,"URI"}, + {0x2A9F,"User Control Point"}, + {0x2A9A,"User Index"}, + {0x2A76,"UV Index"}, + {0x2A96,"VO2 Max"}, + {0x2A97,"Waist Circumference"}, + {0x2A98,"Weight"}, + {0x2A9D,"Weight Measurement"}, + {0x2A9E,"Weight Scale Feature"}, + {0x2A79,"Wind Chill"}, +#endif + {0, ""} +}; + +/** + * @brief Mapping from service ids to names + */ +typedef struct { + const char* name; + const char* type; + uint32_t assignedNumber; +} gattService_t; + + +/** + * Definition of the service ids to names that we know about. + */ +static const gattService_t g_gattServices[] = { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + {"Alert Notification Service", "org.bluetooth.service.alert_notification", 0x1811}, + {"Automation IO", "org.bluetooth.service.automation_io", 0x1815 }, + {"Battery Service","org.bluetooth.service.battery_service", 0x180F}, + {"Blood Pressure", "org.bluetooth.service.blood_pressure", 0x1810}, + {"Body Composition", "org.bluetooth.service.body_composition", 0x181B}, + {"Bond Management", "org.bluetooth.service.bond_management", 0x181E}, + {"Continuous Glucose Monitoring", "org.bluetooth.service.continuous_glucose_monitoring", 0x181F}, + {"Current Time Service", "org.bluetooth.service.current_time", 0x1805}, + {"Cycling Power", "org.bluetooth.service.cycling_power", 0x1818}, + {"Cycling Speed and Cadence", "org.bluetooth.service.cycling_speed_and_cadence", 0x1816}, + {"Device Information", "org.bluetooth.service.device_information", 0x180A}, + {"Environmental Sensing", "org.bluetooth.service.environmental_sensing", 0x181A}, + {"Generic Access", "org.bluetooth.service.generic_access", 0x1800}, + {"Generic Attribute", "org.bluetooth.service.generic_attribute", 0x1801}, + {"Glucose", "org.bluetooth.service.glucose", 0x1808}, + {"Health Thermometer", "org.bluetooth.service.health_thermometer", 0x1809}, + {"Heart Rate", "org.bluetooth.service.heart_rate", 0x180D}, + {"HTTP Proxy", "org.bluetooth.service.http_proxy", 0x1823}, + {"Human Interface Device", "org.bluetooth.service.human_interface_device", 0x1812}, + {"Immediate Alert", "org.bluetooth.service.immediate_alert", 0x1802}, + {"Indoor Positioning", "org.bluetooth.service.indoor_positioning", 0x1821}, + {"Internet Protocol Support", "org.bluetooth.service.internet_protocol_support", 0x1820}, + {"Link Loss", "org.bluetooth.service.link_loss", 0x1803}, + {"Location and Navigation", "org.bluetooth.service.location_and_navigation", 0x1819}, + {"Next DST Change Service", "org.bluetooth.service.next_dst_change", 0x1807}, + {"Object Transfer", "org.bluetooth.service.object_transfer", 0x1825}, + {"Phone Alert Status Service", "org.bluetooth.service.phone_alert_status", 0x180E}, + {"Pulse Oximeter", "org.bluetooth.service.pulse_oximeter", 0x1822}, + {"Reference Time Update Service", "org.bluetooth.service.reference_time_update", 0x1806}, + {"Running Speed and Cadence", "org.bluetooth.service.running_speed_and_cadence", 0x1814}, + {"Scan Parameters", "org.bluetooth.service.scan_parameters", 0x1813}, + {"Transport Discovery", "org.bluetooth.service.transport_discovery", 0x1824}, + {"Tx Power", "org.bluetooth.service.tx_power", 0x1804}, + {"User Data", "org.bluetooth.service.user_data", 0x181C}, + {"Weight Scale", "org.bluetooth.service.weight_scale", 0x181D}, +#endif + {"", "", 0 } +}; + + +/** + * @brief Convert characteristic properties into a string representation. + * @param [in] prop Characteristic properties. + * @return A string representation of characteristic properties. + */ +std::string BLEUtils::characteristicPropertiesToString(esp_gatt_char_prop_t prop) { + std::stringstream stream; + stream << + "broadcast: " << ((prop & ESP_GATT_CHAR_PROP_BIT_BROADCAST)?"1":"0") << + ", read: " << ((prop & ESP_GATT_CHAR_PROP_BIT_READ)?"1":"0") << + ", write_nr: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)?"1":"0") << + ", write: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE)?"1":"0") << + ", notify: " << ((prop & ESP_GATT_CHAR_PROP_BIT_NOTIFY)?"1":"0") << + ", indicate: " << ((prop & ESP_GATT_CHAR_PROP_BIT_INDICATE)?"1":"0") << + ", auth: " << ((prop & ESP_GATT_CHAR_PROP_BIT_AUTH)?"1":"0"); + return stream.str(); +} // characteristicPropertiesToString + +/** + * @brief Convert an esp_gatt_id_t to a string. + */ +static std::string gattIdToString(esp_gatt_id_t gattId) { + std::stringstream stream; + stream << "uuid: " << BLEUUID(gattId.uuid).toString() << ", inst_id: " << (int)gattId.inst_id; + //sprintf(buffer, "uuid: %s, inst_id: %d", uuidToString(gattId.uuid).c_str(), gattId.inst_id); + return stream.str(); +} // gattIdToString + + +/** + * @brief Convert an esp_ble_addr_type_t to a string representation. + */ +const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) { + switch (type) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case BLE_ADDR_TYPE_PUBLIC: + return "BLE_ADDR_TYPE_PUBLIC"; + case BLE_ADDR_TYPE_RANDOM: + return "BLE_ADDR_TYPE_RANDOM"; + case BLE_ADDR_TYPE_RPA_PUBLIC: + return "BLE_ADDR_TYPE_RPA_PUBLIC"; + case BLE_ADDR_TYPE_RPA_RANDOM: + return "BLE_ADDR_TYPE_RPA_RANDOM"; +#endif + default: + return " esp_ble_addr_type_t"; + } +} // addressTypeToString + + +/** + * @brief Convert the BLE Advertising Data flags to a string. + * @param adFlags The flags to convert + * @return std::string A string representation of the advertising flags. + */ +std::string BLEUtils::adFlagsToString(uint8_t adFlags) { + std::stringstream ss; + if (adFlags & (1 << 0)) { + ss << "[LE Limited Discoverable Mode] "; + } + if (adFlags & (1 << 1)) { + ss << "[LE General Discoverable Mode] "; + } + if (adFlags & (1 << 2)) { + ss << "[BR/EDR Not Supported] "; + } + if (adFlags & (1 << 3)) { + ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Controller)] "; + } + if (adFlags & (1 << 4)) { + ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Host)] "; + } + return ss.str(); +} // adFlagsToString + + +/** + * @brief Given an advertising type, return a string representation of the type. + * + * For details see ... + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile + * + * @return A string representation of the type. + */ +const char* BLEUtils::advTypeToString(uint8_t advType) { + switch (advType) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_BLE_AD_TYPE_FLAG: // 0x01 + return "ESP_BLE_AD_TYPE_FLAG"; + case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02 + return "ESP_BLE_AD_TYPE_16SRV_PART"; + case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03 + return "ESP_BLE_AD_TYPE_16SRV_CMPL"; + case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04 + return "ESP_BLE_AD_TYPE_32SRV_PART"; + case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05 + return "ESP_BLE_AD_TYPE_32SRV_CMPL"; + case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06 + return "ESP_BLE_AD_TYPE_128SRV_PART"; + case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07 + return "ESP_BLE_AD_TYPE_128SRV_CMPL"; + case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08 + return "ESP_BLE_AD_TYPE_NAME_SHORT"; + case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09 + return "ESP_BLE_AD_TYPE_NAME_CMPL"; + case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a + return "ESP_BLE_AD_TYPE_TX_PWR"; + case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b + return "ESP_BLE_AD_TYPE_DEV_CLASS"; + case ESP_BLE_AD_TYPE_SM_TK: // 0x10 + return "ESP_BLE_AD_TYPE_SM_TK"; + case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11 + return "ESP_BLE_AD_TYPE_SM_OOB_FLAG"; + case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12 + return "ESP_BLE_AD_TYPE_INT_RANGE"; + case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14 + return "ESP_BLE_AD_TYPE_SOL_SRV_UUID"; + case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15 + return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID"; + case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16 + return "ESP_BLE_AD_TYPE_SERVICE_DATA"; + case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17 + return "ESP_BLE_AD_TYPE_PUBLIC_TARGET"; + case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18 + return "ESP_BLE_AD_TYPE_RANDOM_TARGET"; + case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19 + return "ESP_BLE_AD_TYPE_APPEARANCE"; + case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a + return "ESP_BLE_AD_TYPE_ADV_INT"; + case ESP_BLE_AD_TYPE_32SOL_SRV_UUID: + return "ESP_BLE_AD_TYPE_32SOL_SRV_UUID"; + case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20 + return "ESP_BLE_AD_TYPE_32SERVICE_DATA"; + case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21 + return "ESP_BLE_AD_TYPE_128SERVICE_DATA"; + case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff + return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"; +#endif + default: + ESP_LOGV(LOG_TAG, " adv data type: 0x%x", advType); + return ""; + } // End switch +} // advTypeToString + + +esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) { + esp_gatt_id_t retGattId; + retGattId.uuid = uuid; + retGattId.inst_id = inst_id; + return retGattId; +} + +esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary) { + esp_gatt_srvc_id_t retSrvcId; + retSrvcId.id = gattId; + retSrvcId.is_primary = is_primary; + return retSrvcId; +} + +/** + * @brief Create a hex representation of data. + * + * @param [in] target Where to write the hex string. If this is null, we malloc storage. + * @param [in] source The start of the binary data. + * @param [in] length The length of the data to convert. + * @return A pointer to the formatted buffer. + */ +char* BLEUtils::buildHexData(uint8_t* target, uint8_t* source, uint8_t length) { + // Guard against too much data. + if (length > 100) length = 100; + + if (target == nullptr) { + target = (uint8_t*) malloc(length * 2 + 1); + if (target == nullptr) { + ESP_LOGE(LOG_TAG, "buildHexData: malloc failed"); + return nullptr; + } + } + char* startOfData = (char*) target; + + for (int i = 0; i < length; i++) { + sprintf((char*) target, "%.2x", (char) *source); + source++; + target += 2; + } + + // Handle the special case where there was no data. + if (length == 0) { + *startOfData = 0; + } + + return startOfData; +} // buildHexData + + +/** + * @brief Build a printable string of memory range. + * Create a string representation of a piece of memory. Only printable characters will be included + * while those that are not printable will be replaced with '.'. + * @param [in] source Start of memory. + * @param [in] length Length of memory. + * @return A string representation of a piece of memory. + */ +std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) { + std::ostringstream ss; + for (int i = 0; i < length; i++) { + char c = *source; + ss << (isprint(c) ? c : '.'); + source++; + } + return ss.str(); +} // buildPrintData + + +/** + * @brief Convert a close/disconnect reason to a string. + * @param [in] reason The close reason. + * @return A string representation of the reason. + */ +std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) { + switch (reason) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GATT_CONN_UNKNOWN: { + return "ESP_GATT_CONN_UNKNOWN"; + } + case ESP_GATT_CONN_L2C_FAILURE: { + return "ESP_GATT_CONN_L2C_FAILURE"; + } + case ESP_GATT_CONN_TIMEOUT: { + return "ESP_GATT_CONN_TIMEOUT"; + } + case ESP_GATT_CONN_TERMINATE_PEER_USER: { + return "ESP_GATT_CONN_TERMINATE_PEER_USER"; + } + case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: { + return "ESP_GATT_CONN_TERMINATE_LOCAL_HOST"; + } + case ESP_GATT_CONN_FAIL_ESTABLISH: { + return "ESP_GATT_CONN_FAIL_ESTABLISH"; + } + case ESP_GATT_CONN_LMP_TIMEOUT: { + return "ESP_GATT_CONN_LMP_TIMEOUT"; + } + case ESP_GATT_CONN_CONN_CANCEL: { + return "ESP_GATT_CONN_CONN_CANCEL"; + } + case ESP_GATT_CONN_NONE: { + return "ESP_GATT_CONN_NONE"; + } +#endif + default: { + return "Unknown"; + } + } +} // gattCloseReasonToString + + +std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType) { + switch (eventType) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GATTC_ACL_EVT: + return "ESP_GATTC_ACL_EVT"; + case ESP_GATTC_ADV_DATA_EVT: + return "ESP_GATTC_ADV_DATA_EVT"; + case ESP_GATTC_ADV_VSC_EVT: + return "ESP_GATTC_ADV_VSC_EVT"; + case ESP_GATTC_BTH_SCAN_CFG_EVT: + return "ESP_GATTC_BTH_SCAN_CFG_EVT"; + case ESP_GATTC_BTH_SCAN_DIS_EVT: + return "ESP_GATTC_BTH_SCAN_DIS_EVT"; + case ESP_GATTC_BTH_SCAN_ENB_EVT: + return "ESP_GATTC_BTH_SCAN_ENB_EVT"; + case ESP_GATTC_BTH_SCAN_PARAM_EVT: + return "ESP_GATTC_BTH_SCAN_PARAM_EVT"; + case ESP_GATTC_BTH_SCAN_RD_EVT: + return "ESP_GATTC_BTH_SCAN_RD_EVT"; + case ESP_GATTC_BTH_SCAN_THR_EVT: + return "ESP_GATTC_BTH_SCAN_THR_EVT"; + case ESP_GATTC_CANCEL_OPEN_EVT: + return "ESP_GATTC_CANCEL_OPEN_EVT"; + case ESP_GATTC_CFG_MTU_EVT: + return "ESP_GATTC_CFG_MTU_EVT"; + case ESP_GATTC_CLOSE_EVT: + return "ESP_GATTC_CLOSE_EVT"; + case ESP_GATTC_CONGEST_EVT: + return "ESP_GATTC_CONGEST_EVT"; + case ESP_GATTC_CONNECT_EVT: + return "ESP_GATTC_CONNECT_EVT"; + case ESP_GATTC_DISCONNECT_EVT: + return "ESP_GATTC_DISCONNECT_EVT"; + case ESP_GATTC_ENC_CMPL_CB_EVT: + return "ESP_GATTC_ENC_CMPL_CB_EVT"; + case ESP_GATTC_EXEC_EVT: + return "ESP_GATTC_EXEC_EVT"; + //case ESP_GATTC_GET_CHAR_EVT: +// return "ESP_GATTC_GET_CHAR_EVT"; + //case ESP_GATTC_GET_DESCR_EVT: +// return "ESP_GATTC_GET_DESCR_EVT"; + //case ESP_GATTC_GET_INCL_SRVC_EVT: +// return "ESP_GATTC_GET_INCL_SRVC_EVT"; + case ESP_GATTC_MULT_ADV_DATA_EVT: + return "ESP_GATTC_MULT_ADV_DATA_EVT"; + case ESP_GATTC_MULT_ADV_DIS_EVT: + return "ESP_GATTC_MULT_ADV_DIS_EVT"; + case ESP_GATTC_MULT_ADV_ENB_EVT: + return "ESP_GATTC_MULT_ADV_ENB_EVT"; + case ESP_GATTC_MULT_ADV_UPD_EVT: + return "ESP_GATTC_MULT_ADV_UPD_EVT"; + case ESP_GATTC_NOTIFY_EVT: + return "ESP_GATTC_NOTIFY_EVT"; + case ESP_GATTC_OPEN_EVT: + return "ESP_GATTC_OPEN_EVT"; + case ESP_GATTC_PREP_WRITE_EVT: + return "ESP_GATTC_PREP_WRITE_EVT"; + case ESP_GATTC_READ_CHAR_EVT: + return "ESP_GATTC_READ_CHAR_EVT"; + case ESP_GATTC_REG_EVT: + return "ESP_GATTC_REG_EVT"; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: + return "ESP_GATTC_REG_FOR_NOTIFY_EVT"; + case ESP_GATTC_SCAN_FLT_CFG_EVT: + return "ESP_GATTC_SCAN_FLT_CFG_EVT"; + case ESP_GATTC_SCAN_FLT_PARAM_EVT: + return "ESP_GATTC_SCAN_FLT_PARAM_EVT"; + case ESP_GATTC_SCAN_FLT_STATUS_EVT: + return "ESP_GATTC_SCAN_FLT_STATUS_EVT"; + case ESP_GATTC_SEARCH_CMPL_EVT: + return "ESP_GATTC_SEARCH_CMPL_EVT"; + case ESP_GATTC_SEARCH_RES_EVT: + return "ESP_GATTC_SEARCH_RES_EVT"; + case ESP_GATTC_SRVC_CHG_EVT: + return "ESP_GATTC_SRVC_CHG_EVT"; + case ESP_GATTC_READ_DESCR_EVT: + return "ESP_GATTC_READ_DESCR_EVT"; + case ESP_GATTC_UNREG_EVT: + return "ESP_GATTC_UNREG_EVT"; + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: + return "ESP_GATTC_UNREG_FOR_NOTIFY_EVT"; + case ESP_GATTC_WRITE_CHAR_EVT: + return "ESP_GATTC_WRITE_CHAR_EVT"; + case ESP_GATTC_WRITE_DESCR_EVT: + return "ESP_GATTC_WRITE_DESCR_EVT"; +#endif + default: + ESP_LOGV(LOG_TAG, "Unknown GATT Client event type: %d", eventType); + return "Unknown"; + } +} // gattClientEventTypeToString + + +/** + * @brief Return a string representation of a GATT server event code. + * @param [in] eventType A GATT server event code. + * @return A string representation of the GATT server event code. + */ +std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType) { + switch (eventType) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GATTS_REG_EVT: + return "ESP_GATTS_REG_EVT"; + case ESP_GATTS_READ_EVT: + return "ESP_GATTS_READ_EVT"; + case ESP_GATTS_WRITE_EVT: + return "ESP_GATTS_WRITE_EVT"; + case ESP_GATTS_EXEC_WRITE_EVT: + return "ESP_GATTS_EXEC_WRITE_EVT"; + case ESP_GATTS_MTU_EVT: + return "ESP_GATTS_MTU_EVT"; + case ESP_GATTS_CONF_EVT: + return "ESP_GATTS_CONF_EVT"; + case ESP_GATTS_UNREG_EVT: + return "ESP_GATTS_UNREG_EVT"; + case ESP_GATTS_CREATE_EVT: + return "ESP_GATTS_CREATE_EVT"; + case ESP_GATTS_ADD_INCL_SRVC_EVT: + return "ESP_GATTS_ADD_INCL_SRVC_EVT"; + case ESP_GATTS_ADD_CHAR_EVT: + return "ESP_GATTS_ADD_CHAR_EVT"; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + return "ESP_GATTS_ADD_CHAR_DESCR_EVT"; + case ESP_GATTS_DELETE_EVT: + return "ESP_GATTS_DELETE_EVT"; + case ESP_GATTS_START_EVT: + return "ESP_GATTS_START_EVT"; + case ESP_GATTS_STOP_EVT: + return "ESP_GATTS_STOP_EVT"; + case ESP_GATTS_CONNECT_EVT: + return "ESP_GATTS_CONNECT_EVT"; + case ESP_GATTS_DISCONNECT_EVT: + return "ESP_GATTS_DISCONNECT_EVT"; + case ESP_GATTS_OPEN_EVT: + return "ESP_GATTS_OPEN_EVT"; + case ESP_GATTS_CANCEL_OPEN_EVT: + return "ESP_GATTS_CANCEL_OPEN_EVT"; + case ESP_GATTS_CLOSE_EVT: + return "ESP_GATTS_CLOSE_EVT"; + case ESP_GATTS_LISTEN_EVT: + return "ESP_GATTS_LISTEN_EVT"; + case ESP_GATTS_CONGEST_EVT: + return "ESP_GATTS_CONGEST_EVT"; + case ESP_GATTS_RESPONSE_EVT: + return "ESP_GATTS_RESPONSE_EVT"; + case ESP_GATTS_CREAT_ATTR_TAB_EVT: + return "ESP_GATTS_CREAT_ATTR_TAB_EVT"; + case ESP_GATTS_SET_ATTR_VAL_EVT: + return "ESP_GATTS_SET_ATTR_VAL_EVT"; + case ESP_GATTS_SEND_SERVICE_CHANGE_EVT: + return "ESP_GATTS_SEND_SERVICE_CHANGE_EVT"; +#endif + default: + return "Unknown"; + } +} // gattServerEventTypeToString + + + +/** + * @brief Convert a BLE device type to a string. + * @param [in] type The device type. + */ +const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) { + switch (type) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_BT_DEVICE_TYPE_BREDR: + return "ESP_BT_DEVICE_TYPE_BREDR"; + case ESP_BT_DEVICE_TYPE_BLE: + return "ESP_BT_DEVICE_TYPE_BLE"; + case ESP_BT_DEVICE_TYPE_DUMO: + return "ESP_BT_DEVICE_TYPE_DUMO"; +#endif + default: + return "Unknown"; + } +} // devTypeToString + + +/** + * @brief Dump the GAP event to the log. + */ +void BLEUtils::dumpGapEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + ESP_LOGV(LOG_TAG, "Received a GAP event: %s", gapEventToString(event)); + switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT + // adv_data_cmpl + // - esp_bt_status_t + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status); + break; + } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT + + // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT + // + // adv_data_raw_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status); + break; + } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT + + // ESP_GAP_BLE_ADV_START_COMPLETE_EVT + // + // adv_start_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status); + break; + } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT + + // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT + // + // adv_stop_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status); + break; + } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT + + // ESP_GAP_BLE_AUTH_CMPL_EVT + // + // auth_cmpl + // - esp_bd_addr_t bd_addr + // - bool key_present + // - esp_link_key key + // - bool success + // - uint8_t fail_reason + // - esp_bd_addr_type_t addr_type + // - esp_bt_dev_type_t dev_type + case ESP_GAP_BLE_AUTH_CMPL_EVT: { + ESP_LOGV(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]", + BLEAddress(param->ble_security.auth_cmpl.bd_addr).toString().c_str(), + param->ble_security.auth_cmpl.key_present, + param->ble_security.auth_cmpl.key_type, + param->ble_security.auth_cmpl.success, + param->ble_security.auth_cmpl.fail_reason, + BLEUtils::devTypeToString(param->ble_security.auth_cmpl.dev_type) + ); + break; + } // ESP_GAP_BLE_AUTH_CMPL_EVT + + // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + // + // clear_bond_dev_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status); + break; + } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT + + // ESP_GAP_BLE_LOCAL_IR_EVT + case ESP_GAP_BLE_LOCAL_IR_EVT: { + break; + } // ESP_GAP_BLE_LOCAL_IR_EVT + + // ESP_GAP_BLE_LOCAL_ER_EVT + case ESP_GAP_BLE_LOCAL_ER_EVT: { + break; + } // ESP_GAP_BLE_LOCAL_ER_EVT + + // ESP_GAP_BLE_NC_REQ_EVT + case ESP_GAP_BLE_NC_REQ_EVT: { + ESP_LOGV(LOG_TAG, "[bd_addr: %s, passkey: %d]", + BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(), + param->ble_security.key_notif.passkey); + break; + } // ESP_GAP_BLE_NC_REQ_EVT + + // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + // + // read_rssi_cmpl + // - esp_bt_status_t status + // - int8_t rssi + // - esp_bd_addr_t remote_addr + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]", + param->read_rssi_cmpl.status, + param->read_rssi_cmpl.rssi, + BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str() + ); + break; + } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT + + // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT + // + // scan_param_cmpl. + // - esp_bt_status_t status + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT + + // ESP_GAP_BLE_SCAN_RESULT_EVT + // + // scan_rst: + // - search_evt + // - bda + // - dev_type + // - ble_addr_type + // - ble_evt_type + // - rssi + // - ble_adv + // - flag + // - num_resps + // - adv_data_len + // - scan_rsp_len + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + switch (param->scan_rst.search_evt) { + case ESP_GAP_SEARCH_INQ_RES_EVT: { + ESP_LOGV(LOG_TAG, "search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d (%s), num_resps: %d, adv_data_len: %d, scan_rsp_len: %d", + searchEventTypeToString(param->scan_rst.search_evt), + BLEAddress(param->scan_rst.bda).toString().c_str(), + devTypeToString(param->scan_rst.dev_type), + addressTypeToString(param->scan_rst.ble_addr_type), + eventTypeToString(param->scan_rst.ble_evt_type), + param->scan_rst.rssi, + param->scan_rst.flag, + adFlagsToString(param->scan_rst.flag).c_str(), + param->scan_rst.num_resps, + param->scan_rst.adv_data_len, + param->scan_rst.scan_rsp_len + ); + break; + } // ESP_GAP_SEARCH_INQ_RES_EVT + + default: { + ESP_LOGV(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt)); + break; + } + } + break; + } // ESP_GAP_BLE_SCAN_RESULT_EVT + + // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT + // + // scan_rsp_data_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT + + // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT + case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT + + // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + // + // scan_start_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + + // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + // + // scan_stop_cmpl + // - esp_bt_status_t status + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status); + break; + } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + + // ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT + // + // update_conn_params + // - esp_bt_status_t status + // - esp_bd_addr_t bda + // - uint16_t min_int + // - uint16_t max_int + // - uint16_t latency + // - uint16_t conn_int + // - uint16_t timeout + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: { + ESP_LOGV(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]", + param->update_conn_params.status, + BLEAddress(param->update_conn_params.bda).toString().c_str(), + param->update_conn_params.min_int, + param->update_conn_params.max_int, + param->update_conn_params.latency, + param->update_conn_params.conn_int, + param->update_conn_params.timeout + ); + break; + } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT + + // ESP_GAP_BLE_SEC_REQ_EVT + case ESP_GAP_BLE_SEC_REQ_EVT: { + ESP_LOGV(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str()); + break; + } // ESP_GAP_BLE_SEC_REQ_EVT +#endif + default: { + ESP_LOGV(LOG_TAG, "*** dumpGapEvent: Logger not coded ***"); + break; + } // default + } // switch +} // dumpGapEvent + + +/** + * @brief Decode and dump a GATT client event + * + * @param [in] event The type of event received. + * @param [in] evtParam The data associated with the event. + */ +void BLEUtils::dumpGattClientEvent( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam) { + + //esp_ble_gattc_cb_param_t* evtParam = (esp_ble_gattc_cb_param_t*) param; + ESP_LOGV(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str()); + switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + // ESP_GATTC_CLOSE_EVT + // + // close: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason + case ESP_GATTC_CLOSE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]", + BLEUtils::gattStatusToString(evtParam->close.status).c_str(), + BLEUtils::gattCloseReasonToString(evtParam->close.reason).c_str(), + evtParam->close.conn_id); + break; + } + + // ESP_GATTC_CONNECT_EVT + // + // connect: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + case ESP_GATTC_CONNECT_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + evtParam->connect.conn_id, + BLEAddress(evtParam->connect.remote_bda).toString().c_str() + ); + break; + } + + // ESP_GATTC_DISCONNECT_EVT + // + // disconnect: + // - esp_gatt_conn_reason_t reason + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + case ESP_GATTC_DISCONNECT_EVT: { + ESP_LOGV(LOG_TAG, "[reason: %s, conn_id: %d, remote_bda: %s]", + BLEUtils::gattCloseReasonToString(evtParam->disconnect.reason).c_str(), + evtParam->disconnect.conn_id, + BLEAddress(evtParam->disconnect.remote_bda).toString().c_str() + ); + break; + } // ESP_GATTC_DISCONNECT_EVT + + // ESP_GATTC_GET_CHAR_EVT + // + // get_char: + // - esp_gatt_status_t status + // - uin1t6_t conn_id + // - esp_gatt_srvc_id_t srvc_id + // - esp_gatt_id_t char_id + // - esp_gatt_char_prop_t char_prop + /* + case ESP_GATTC_GET_CHAR_EVT: { + + // If the status of the event shows that we have a value other than ESP_GATT_OK then the + // characteristic fields are not set to a usable value .. so don't try and log them. + if (evtParam->get_char.status == ESP_GATT_OK) { + std::string description = "Unknown"; + if (evtParam->get_char.char_id.uuid.len == ESP_UUID_LEN_16) { + description = BLEUtils::gattCharacteristicUUIDToString(evtParam->get_char.char_id.uuid.uuid.uuid16); + } + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]", + BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), + evtParam->get_char.conn_id, + BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str(), + gattIdToString(evtParam->get_char.char_id).c_str(), + description.c_str(), + BLEUtils::characteristicPropertiesToString(evtParam->get_char.char_prop).c_str() + ); + } else { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s]", + BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(), + evtParam->get_char.conn_id, + BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str() + ); + } + break; + } // ESP_GATTC_GET_CHAR_EVT + */ + + // ESP_GATTC_NOTIFY_EVT + // + // notify + // uint16_t conn_id + // esp_bd_addr_t remote_bda + // handle handle + // uint16_t value_len + // uint8_t* value + // bool is_notify + // + case ESP_GATTC_NOTIFY_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]", + evtParam->notify.conn_id, + BLEAddress(evtParam->notify.remote_bda).toString().c_str(), + evtParam->notify.handle, + evtParam->notify.handle, + evtParam->notify.value_len, + evtParam->notify.is_notify + ); + break; + } + + // ESP_GATTC_OPEN_EVT + // + // open: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - uint16_t mtu + // + case ESP_GATTC_OPEN_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]", + BLEUtils::gattStatusToString(evtParam->open.status).c_str(), + evtParam->open.conn_id, + BLEAddress(evtParam->open.remote_bda).toString().c_str(), + evtParam->open.mtu); + break; + } // ESP_GATTC_OPEN_EVT + + // ESP_GATTC_READ_CHAR_EVT + // + // Callback to indicate that requested data that we wanted to read is now available. + // + // read: + // esp_gatt_status_t status + // uint16_t conn_id + // uint16_t handle + // uint8_t* value + // uint16_t value_type + // uint16_t value_len + case ESP_GATTC_READ_CHAR_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]", + BLEUtils::gattStatusToString(evtParam->read.status).c_str(), + evtParam->read.conn_id, + evtParam->read.handle, + evtParam->read.handle, + evtParam->read.value_len + ); + if (evtParam->read.status == ESP_GATT_OK) { + GeneralUtils::hexDump(evtParam->read.value, evtParam->read.value_len); + /* + char* pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len); + ESP_LOGV(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str()); + free(pHexData); + */ + } + break; + } // ESP_GATTC_READ_CHAR_EVT + + // ESP_GATTC_REG_EVT + // + // reg: + // - esp_gatt_status_t status + // - uint16_t app_id + case ESP_GATTC_REG_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, app_id: 0x%x]", + BLEUtils::gattStatusToString(evtParam->reg.status).c_str(), + evtParam->reg.app_id); + break; + } // ESP_GATTC_REG_EVT + + // ESP_GATTC_REG_FOR_NOTIFY_EVT + // + // reg_for_notify: + // - esp_gatt_status_t status + // - uint16_t handle + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, handle: %d 0x%.2x]", + BLEUtils::gattStatusToString(evtParam->reg_for_notify.status).c_str(), + evtParam->reg_for_notify.handle, + evtParam->reg_for_notify.handle + ); + break; + } // ESP_GATTC_REG_FOR_NOTIFY_EVT + + // ESP_GATTC_SEARCH_CMPL_EVT + // + // search_cmpl: + // - esp_gatt_status_t status + // - uint16_t conn_id + case ESP_GATTC_SEARCH_CMPL_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d]", + BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(), + evtParam->search_cmpl.conn_id); + break; + } // ESP_GATTC_SEARCH_CMPL_EVT + + // ESP_GATTC_SEARCH_RES_EVT + // + // search_res: + // - uint16_t conn_id + // - uint16_t start_handle + // - uint16_t end_handle + // - esp_gatt_id_t srvc_id + case ESP_GATTC_SEARCH_RES_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s", + evtParam->search_res.conn_id, + evtParam->search_res.start_handle, + evtParam->search_res.start_handle, + evtParam->search_res.end_handle, + evtParam->search_res.end_handle, + gattIdToString(evtParam->search_res.srvc_id).c_str()); + break; + } // ESP_GATTC_SEARCH_RES_EVT + + // ESP_GATTC_WRITE_CHAR_EVT + // + // write: + // - esp_gatt_status_t status + // - uint16_t conn_id + // - uint16_t handle + // - uint16_t offset + case ESP_GATTC_WRITE_CHAR_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]", + BLEUtils::gattStatusToString(evtParam->write.status).c_str(), + evtParam->write.conn_id, + evtParam->write.handle, + evtParam->write.handle, + evtParam->write.offset + ); + break; + } // ESP_GATTC_WRITE_CHAR_EVT +#endif + default: + break; + } +} // dumpGattClientEvent + + +/** + * @brief Dump the details of a GATT server event. + * A GATT Server event is a callback received from the BLE subsystem when we are acting as a BLE + * server. The callback indicates the type of event in the `event` field. The `evtParam` is a + * union of structures where we can use the `event` to indicate which of the structures has been + * populated and hence is valid. + * + * @param [in] event The event type that was posted. + * @param [in] evtParam A union of structures only one of which is populated. + */ +void BLEUtils::dumpGattServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* evtParam) { + ESP_LOGV(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + switch (event) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + + case ESP_GATTS_ADD_CHAR_DESCR_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + gattStatusToString(evtParam->add_char_descr.status).c_str(), + evtParam->add_char_descr.attr_handle, + evtParam->add_char_descr.attr_handle, + evtParam->add_char_descr.service_handle, + evtParam->add_char_descr.service_handle, + BLEUUID(evtParam->add_char_descr.descr_uuid).toString().c_str()); + break; + } // ESP_GATTS_ADD_CHAR_DESCR_EVT + + case ESP_GATTS_ADD_CHAR_EVT: { + if (evtParam->add_char.status == ESP_GATT_OK) { + ESP_LOGV(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + gattStatusToString(evtParam->add_char.status).c_str(), + evtParam->add_char.attr_handle, + evtParam->add_char.attr_handle, + evtParam->add_char.service_handle, + evtParam->add_char.service_handle, + BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]", + gattStatusToString(evtParam->add_char.status).c_str(), + evtParam->add_char.attr_handle, + evtParam->add_char.attr_handle, + evtParam->add_char.service_handle, + evtParam->add_char.service_handle, + BLEUUID(evtParam->add_char.char_uuid).toString().c_str()); + } + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_CONF_EVT + // + // conf: + // - esp_gatt_status_t status – The status code. + // - uint16_t conn_id – The connection used. + case ESP_GATTS_CONF_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, conn_id: 0x%.2x]", + gattStatusToString(evtParam->conf.status).c_str(), + evtParam->conf.conn_id); + break; + } // ESP_GATTS_CONF_EVT + + + case ESP_GATTS_CONGEST_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, congested: %d]", + evtParam->congest.conn_id, + evtParam->congest.congested); + break; + } // ESP_GATTS_CONGEST_EVT + + case ESP_GATTS_CONNECT_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + evtParam->connect.conn_id, + BLEAddress(evtParam->connect.remote_bda).toString().c_str()); + break; + } // ESP_GATTS_CONNECT_EVT + + case ESP_GATTS_CREATE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]", + gattStatusToString(evtParam->create.status).c_str(), + evtParam->create.service_handle, + evtParam->create.service_handle, + gattServiceIdToString(evtParam->create.service_id).c_str()); + break; + } // ESP_GATTS_CREATE_EVT + + case ESP_GATTS_DISCONNECT_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, remote_bda: %s]", + evtParam->connect.conn_id, + BLEAddress(evtParam->connect.remote_bda).toString().c_str()); + break; + } // ESP_GATTS_DISCONNECT_EVT + + + // ESP_GATTS_EXEC_WRITE_EVT + // exec_write: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint8_t exec_write_flag + case ESP_GATTS_EXEC_WRITE_EVT: { + char* pWriteFlagText; + switch (evtParam->exec_write.exec_write_flag) { + case ESP_GATT_PREP_WRITE_EXEC: { + pWriteFlagText = (char*) "WRITE"; + break; + } + + case ESP_GATT_PREP_WRITE_CANCEL: { + pWriteFlagText = (char*) "CANCEL"; + break; + } + + default: + pWriteFlagText = (char*) ""; + break; + } + + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]", + evtParam->exec_write.conn_id, + evtParam->exec_write.trans_id, + BLEAddress(evtParam->exec_write.bda).toString().c_str(), + evtParam->exec_write.exec_write_flag, + pWriteFlagText); + break; + } // ESP_GATTS_DISCONNECT_EVT + + + case ESP_GATTS_MTU_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, mtu: %d]", + evtParam->mtu.conn_id, + evtParam->mtu.mtu); + break; + } // ESP_GATTS_MTU_EVT + + case ESP_GATTS_READ_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]", + evtParam->read.conn_id, + evtParam->read.trans_id, + BLEAddress(evtParam->read.bda).toString().c_str(), + evtParam->read.handle, + evtParam->read.is_long, + evtParam->read.need_rsp); + break; + } // ESP_GATTS_READ_EVT + + case ESP_GATTS_RESPONSE_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, handle: 0x%.2x]", + gattStatusToString(evtParam->rsp.status).c_str(), + evtParam->rsp.handle); + break; + } // ESP_GATTS_RESPONSE_EVT + + case ESP_GATTS_REG_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, app_id: %d]", + gattStatusToString(evtParam->reg.status).c_str(), + evtParam->reg.app_id); + break; + } // ESP_GATTS_REG_EVT + + + // ESP_GATTS_START_EVT + // + // start: + // - esp_gatt_status_t status + // - uint16_t service_handle + case ESP_GATTS_START_EVT: { + ESP_LOGV(LOG_TAG, "[status: %s, service_handle: 0x%.2x]", + gattStatusToString(evtParam->start.status).c_str(), + evtParam->start.service_handle); + break; + } // ESP_GATTS_START_EVT + + + // ESP_GATTS_WRITE_EVT + // + // write: + // - uint16_t conn_id – The connection id. + // - uint16_t trans_id – The transfer id. + // - esp_bd_addr_t bda – The address of the partner. + // - uint16_t handle – The attribute handle. + // - uint16_t offset – The offset of the currently received within the whole value. + // - bool need_rsp – Do we need a response? + // - bool is_prep – Is this a write prepare? If set, then this is to be considered part of the received value and not the whole value. A subsequent ESP_GATTS_EXEC_WRITE will mark the total. + // - uint16_t len – The length of the incoming value part. + // - uint8_t* value – The data for this value part. + case ESP_GATTS_WRITE_EVT: { + ESP_LOGV(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]", + evtParam->write.conn_id, + evtParam->write.trans_id, + BLEAddress(evtParam->write.bda).toString().c_str(), + evtParam->write.handle, + evtParam->write.offset, + evtParam->write.need_rsp, + evtParam->write.is_prep, + evtParam->write.len); + char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len); + ESP_LOGV(LOG_TAG, "[Data: %s]", pHex); + free(pHex); + break; + } // ESP_GATTS_WRITE_EVT +#endif + default: + ESP_LOGV(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***"); + break; + } +} // dumpGattServerEvent + + +/** + * @brief Convert a BLE event type to a string. + * @param [in] eventType The event type. + * @return The event type as a string. + */ +const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) { + switch (eventType) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_BLE_EVT_CONN_ADV: + return "ESP_BLE_EVT_CONN_ADV"; + case ESP_BLE_EVT_CONN_DIR_ADV: + return "ESP_BLE_EVT_CONN_DIR_ADV"; + case ESP_BLE_EVT_DISC_ADV: + return "ESP_BLE_EVT_DISC_ADV"; + case ESP_BLE_EVT_NON_CONN_ADV: + return "ESP_BLE_EVT_NON_CONN_ADV"; + case ESP_BLE_EVT_SCAN_RSP: + return "ESP_BLE_EVT_SCAN_RSP"; +#endif + default: + ESP_LOGV(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType); + return "*** Unknown ***"; + } +} // eventTypeToString + + + +/** + * @brief Convert a BT GAP event type to a string representation. + * @param [in] eventType The type of event. + * @return A string representation of the event type. + */ +const char* BLEUtils::gapEventToString(uint32_t eventType) { + switch (eventType) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + return "ESP_GAP_BLE_ADV_START_COMPLETE_EVT"; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /* !< When stop adv complete, the event comes */ + return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT"; + case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */ + return "ESP_GAP_BLE_AUTH_CMPL_EVT"; + case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT"; + case ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT"; + case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */ + return "ESP_GAP_BLE_KEY_EVT"; + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + return "ESP_GAP_BLE_LOCAL_IR_EVT"; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + return "ESP_GAP_BLE_LOCAL_ER_EVT"; + case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */ + return "ESP_GAP_BLE_NC_REQ_EVT"; + case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ + return "ESP_GAP_BLE_OOB_REQ_EVT"; + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */ + return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT"; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + return "ESP_GAP_BLE_PASSKEY_REQ_EVT"; + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT"; + case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: + return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_RESULT_EVT: + return "ESP_GAP_BLE_SCAN_RESULT_EVT"; + case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT"; + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + return "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT"; + case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */ + return "ESP_GAP_BLE_SEC_REQ_EVT"; + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT"; + case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: + return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT"; + case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: + return "ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT"; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT"; +#endif + default: + ESP_LOGV(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + return "Unknown event type"; + } +} // gapEventToString + + +std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) { + const characteristicMap_t* p = g_characteristicsMappings; + while (strlen(p->name) > 0) { + if (p->assignedNumber == characteristicUUID) { + return std::string(p->name); + } + p++; + } + return "Unknown"; +} // gattCharacteristicUUIDToString + + +/** + * @brief Given the UUID for a BLE defined descriptor, return its string representation. + * @param [in] descriptorUUID UUID of the descriptor to be returned as a string. + * @return The string representation of a descriptor UUID. + */ +std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) { + gattdescriptor_t* p = (gattdescriptor_t*) g_descriptor_ids; + while (strlen(p->name) > 0) { + if (p->assignedNumber == descriptorUUID) { + return std::string(p->name); + } + p++; + } + return ""; +} // gattDescriptorUUIDToString + + +/** + * @brief Return a string representation of an esp_gattc_service_elem_t. + * @return A string representation of an esp_gattc_service_elem_t. + */ +std::string BLEUtils::gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement) { + std::stringstream ss; + + ss << "[uuid: " << BLEUUID(pGATTCServiceElement->uuid).toString() << + ", start_handle: " << pGATTCServiceElement->start_handle << + " 0x" << std::hex << pGATTCServiceElement->start_handle << + ", end_handle: " << std::dec << pGATTCServiceElement->end_handle << + " 0x" << std::hex << pGATTCServiceElement->end_handle << "]"; + return ss.str(); +} // gattcServiceElementToString + + +/** + * @brief Convert an esp_gatt_srvc_id_t to a string. + */ +std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) { + return gattIdToString(srvcId.id); +} // gattServiceIdToString + + +std::string BLEUtils::gattServiceToString(uint32_t serviceId) { + gattService_t* p = (gattService_t*) g_gattServices; + while (strlen(p->name) > 0) { + if (p->assignedNumber == serviceId) { + return std::string(p->name); + } + p++; + } + return "Unknown"; +} // gattServiceToString + + +/** + * @brief Convert a GATT status to a string. + * + * @param [in] status The status to convert. + * @return A string representation of the status. + */ +std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) { + switch (status) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GATT_OK: + return "ESP_GATT_OK"; + case ESP_GATT_INVALID_HANDLE: + return "ESP_GATT_INVALID_HANDLE"; + case ESP_GATT_READ_NOT_PERMIT: + return "ESP_GATT_READ_NOT_PERMIT"; + case ESP_GATT_WRITE_NOT_PERMIT: + return "ESP_GATT_WRITE_NOT_PERMIT"; + case ESP_GATT_INVALID_PDU: + return "ESP_GATT_INVALID_PDU"; + case ESP_GATT_INSUF_AUTHENTICATION: + return "ESP_GATT_INSUF_AUTHENTICATION"; + case ESP_GATT_REQ_NOT_SUPPORTED: + return "ESP_GATT_REQ_NOT_SUPPORTED"; + case ESP_GATT_INVALID_OFFSET: + return "ESP_GATT_INVALID_OFFSET"; + case ESP_GATT_INSUF_AUTHORIZATION: + return "ESP_GATT_INSUF_AUTHORIZATION"; + case ESP_GATT_PREPARE_Q_FULL: + return "ESP_GATT_PREPARE_Q_FULL"; + case ESP_GATT_NOT_FOUND: + return "ESP_GATT_NOT_FOUND"; + case ESP_GATT_NOT_LONG: + return "ESP_GATT_NOT_LONG"; + case ESP_GATT_INSUF_KEY_SIZE: + return "ESP_GATT_INSUF_KEY_SIZE"; + case ESP_GATT_INVALID_ATTR_LEN: + return "ESP_GATT_INVALID_ATTR_LEN"; + case ESP_GATT_ERR_UNLIKELY: + return "ESP_GATT_ERR_UNLIKELY"; + case ESP_GATT_INSUF_ENCRYPTION: + return "ESP_GATT_INSUF_ENCRYPTION"; + case ESP_GATT_UNSUPPORT_GRP_TYPE: + return "ESP_GATT_UNSUPPORT_GRP_TYPE"; + case ESP_GATT_INSUF_RESOURCE: + return "ESP_GATT_INSUF_RESOURCE"; + case ESP_GATT_NO_RESOURCES: + return "ESP_GATT_NO_RESOURCES"; + case ESP_GATT_INTERNAL_ERROR: + return "ESP_GATT_INTERNAL_ERROR"; + case ESP_GATT_WRONG_STATE: + return "ESP_GATT_WRONG_STATE"; + case ESP_GATT_DB_FULL: + return "ESP_GATT_DB_FULL"; + case ESP_GATT_BUSY: + return "ESP_GATT_BUSY"; + case ESP_GATT_ERROR: + return "ESP_GATT_ERROR"; + case ESP_GATT_CMD_STARTED: + return "ESP_GATT_CMD_STARTED"; + case ESP_GATT_ILLEGAL_PARAMETER: + return "ESP_GATT_ILLEGAL_PARAMETER"; + case ESP_GATT_PENDING: + return "ESP_GATT_PENDING"; + case ESP_GATT_AUTH_FAIL: + return "ESP_GATT_AUTH_FAIL"; + case ESP_GATT_MORE: + return "ESP_GATT_MORE"; + case ESP_GATT_INVALID_CFG: + return "ESP_GATT_INVALID_CFG"; + case ESP_GATT_SERVICE_STARTED: + return "ESP_GATT_SERVICE_STARTED"; + case ESP_GATT_ENCRYPED_NO_MITM: + return "ESP_GATT_ENCRYPED_NO_MITM"; + case ESP_GATT_NOT_ENCRYPTED: + return "ESP_GATT_NOT_ENCRYPTED"; + case ESP_GATT_CONGESTED: + return "ESP_GATT_CONGESTED"; + case ESP_GATT_DUP_REG: + return "ESP_GATT_DUP_REG"; + case ESP_GATT_ALREADY_OPEN: + return "ESP_GATT_ALREADY_OPEN"; + case ESP_GATT_CANCEL: + return "ESP_GATT_CANCEL"; + case ESP_GATT_STACK_RSP: + return "ESP_GATT_STACK_RSP"; + case ESP_GATT_APP_RSP: + return "ESP_GATT_APP_RSP"; + case ESP_GATT_UNKNOWN_ERROR: + return "ESP_GATT_UNKNOWN_ERROR"; + case ESP_GATT_CCC_CFG_ERR: + return "ESP_GATT_CCC_CFG_ERR"; + case ESP_GATT_PRC_IN_PROGRESS: + return "ESP_GATT_PRC_IN_PROGRESS"; + case ESP_GATT_OUT_OF_RANGE: + return "ESP_GATT_OUT_OF_RANGE"; +#endif + default: + return "Unknown"; + } +} // gattStatusToString + + + +std::string BLEUtils::getMember(uint32_t memberId) { + member_t* p = (member_t*) members_ids; + + while (strlen(p->name) > 0) { + if (p->assignedNumber == memberId) { + return std::string(p->name); + } + p++; + } + return "Unknown"; +} + +/** + * @brief convert a GAP search event to a string. + * @param [in] searchEvt + * @return The search event type as a string. + */ +const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) { + switch (searchEvt) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_GAP_SEARCH_INQ_RES_EVT: + return "ESP_GAP_SEARCH_INQ_RES_EVT"; + case ESP_GAP_SEARCH_INQ_CMPL_EVT: + return "ESP_GAP_SEARCH_INQ_CMPL_EVT"; + case ESP_GAP_SEARCH_DISC_RES_EVT: + return "ESP_GAP_SEARCH_DISC_RES_EVT"; + case ESP_GAP_SEARCH_DISC_BLE_RES_EVT: + return "ESP_GAP_SEARCH_DISC_BLE_RES_EVT"; + case ESP_GAP_SEARCH_DISC_CMPL_EVT: + return "ESP_GAP_SEARCH_DISC_CMPL_EVT"; + case ESP_GAP_SEARCH_DI_DISC_CMPL_EVT: + return "ESP_GAP_SEARCH_DI_DISC_CMPL_EVT"; + case ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT: + return "ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT"; +#endif + default: + ESP_LOGV(LOG_TAG, "Unknown event type: 0x%x", searchEvt); + return "Unknown event type"; + } +} // searchEventTypeToString + +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.h new file mode 100644 index 0000000..7981691 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEUtils.h @@ -0,0 +1,63 @@ +/* + * BLEUtils.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_ +#define COMPONENTS_CPP_UTILS_BLEUTILS_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include // ESP32 BLE +#include // ESP32 BLE +#include // ESP32 BLE +#include +#include "BLEClient.h" + +/** + * @brief A set of general %BLE utilities. + */ +class BLEUtils { +public: + static const char* addressTypeToString(esp_ble_addr_type_t type); + static std::string adFlagsToString(uint8_t adFlags); + static const char* advTypeToString(uint8_t advType); + static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length); + static std::string buildPrintData(uint8_t* source, size_t length); + static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop); + static const char* devTypeToString(esp_bt_dev_type_t type); + static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id = 0); + static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary = true); + static void dumpGapEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param); + static void dumpGattClientEvent( + esp_gattc_cb_event_t event, + esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t* evtParam); + static void dumpGattServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* evtParam); + static const char* eventTypeToString(esp_ble_evt_type_t eventType); + static BLEClient* findByAddress(BLEAddress address); + static BLEClient* findByConnId(uint16_t conn_id); + static const char* gapEventToString(uint32_t eventType); + static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID); + static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType); + static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason); + static std::string gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement); + static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID); + static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType); + static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId); + static std::string gattServiceToString(uint32_t serviceId); + static std::string gattStatusToString(esp_gatt_status_t status); + static std::string getMember(uint32_t memberId); + static void registerByAddress(BLEAddress address, BLEClient* pDevice); + static void registerByConnId(uint16_t conn_id, BLEClient* pDevice); + static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt); +}; + +#endif // CONFIG_BT_ENABLED +#endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.cpp new file mode 100644 index 0000000..ec1e61f --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.cpp @@ -0,0 +1,139 @@ +/* + * BLEValue.cpp + * + * Created on: Jul 17, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "BLEValue.h" + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG="BLEValue"; +#endif + + + +BLEValue::BLEValue() { + m_accumulation = ""; + m_value = ""; + m_readOffset = 0; +} // BLEValue + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] part A message part being added. + */ +void BLEValue::addPart(std::string part) { + ESP_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); + m_accumulation += part; +} // addPart + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] pData A message part being added. + * @param [in] length The number of bytes being added. + */ +void BLEValue::addPart(uint8_t* pData, size_t length) { + ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length); + m_accumulation += std::string((char*) pData, length); +} // addPart + + +/** + * @brief Cancel the current accumulation. + */ +void BLEValue::cancel() { + ESP_LOGD(LOG_TAG, ">> cancel"); + m_accumulation = ""; + m_readOffset = 0; +} // cancel + + +/** + * @brief Commit the current accumulation. + * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces + * of the overall message. After the last part has been received, we may perform a commit which means that + * we now have the complete message and commit the change as a unit. + */ +void BLEValue::commit() { + ESP_LOGD(LOG_TAG, ">> commit"); + // If there is nothing to commit, do nothing. + if (m_accumulation.length() == 0) return; + setValue(m_accumulation); + m_accumulation = ""; + m_readOffset = 0; +} // commit + + +/** + * @brief Get a pointer to the data. + * @return A pointer to the data. + */ +uint8_t* BLEValue::getData() { + return (uint8_t*) m_value.data(); +} + + +/** + * @brief Get the length of the data in bytes. + * @return The length of the data in bytes. + */ +size_t BLEValue::getLength() { + return m_value.length(); +} // getLength + + +/** + * @brief Get the read offset. + * @return The read offset into the read. + */ +uint16_t BLEValue::getReadOffset() { + return m_readOffset; +} // getReadOffset + + +/** + * @brief Get the current value. + */ +std::string BLEValue::getValue() { + return m_value; +} // getValue + + +/** + * @brief Set the read offset + * @param [in] readOffset The offset into the read. + */ +void BLEValue::setReadOffset(uint16_t readOffset) { + m_readOffset = readOffset; +} // setReadOffset + + +/** + * @brief Set the current value. + */ +void BLEValue::setValue(std::string value) { + m_value = value; +} // setValue + + +/** + * @brief Set the current value. + * @param [in] pData The data for the current value. + * @param [in] The length of the new current value. + */ +void BLEValue::setValue(uint8_t* pData, size_t length) { + m_value = std::string((char*) pData, length); +} // setValue + + +#endif // CONFIG_BT_ENABLED diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.h b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.h new file mode 100644 index 0000000..5df904c --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/BLEValue.h @@ -0,0 +1,39 @@ +/* + * BLEValue.h + * + * Created on: Jul 17, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_ +#define COMPONENTS_CPP_UTILS_BLEVALUE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +/** + * @brief The model of a %BLE value. + */ +class BLEValue { +public: + BLEValue(); + void addPart(std::string part); + void addPart(uint8_t* pData, size_t length); + void cancel(); + void commit(); + uint8_t* getData(); + size_t getLength(); + uint16_t getReadOffset(); + std::string getValue(); + void setReadOffset(uint16_t readOffset); + void setValue(std::string value); + void setValue(uint8_t* pData, size_t length); + +private: + std::string m_accumulation; + uint16_t m_readOffset; + std::string m_value; + +}; +#endif // CONFIG_BT_ENABLED +#endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.cpp new file mode 100644 index 0000000..1f12c88 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.cpp @@ -0,0 +1,274 @@ +/* + * FreeRTOS.cpp + * + * Created on: Feb 24, 2017 + * Author: kolban + */ +#include // Include the base FreeRTOS definitions +#include // Include the task definitions +#include // Include the semaphore definitions +#include +#include +#include +#include "FreeRTOS.h" +#include "sdkconfig.h" +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "FreeRTOS"; +#endif + + +/** + * Sleep for the specified number of milliseconds. + * @param[in] ms The period in milliseconds for which to sleep. + */ +void FreeRTOS::sleep(uint32_t ms) { + ::vTaskDelay(ms / portTICK_PERIOD_MS); +} // sleep + + +/** + * Start a new task. + * @param[in] task The function pointer to the function to be run in the task. + * @param[in] taskName A string identifier for the task. + * @param[in] param An optional parameter to be passed to the started task. + * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task. + */ +void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) { + ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL); +} // startTask + + +/** + * Delete the task. + * @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted. + */ +void FreeRTOS::deleteTask(TaskHandle_t pTask) { + ::vTaskDelete(pTask); +} // deleteTask + + +/** + * Get the time in milliseconds since the %FreeRTOS scheduler started. + * @return The time in milliseconds since the %FreeRTOS scheduler started. + */ +uint32_t FreeRTOS::getTimeSinceStart() { + return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS); +} // getTimeSinceStart + + +/** + * @brief Wait for a semaphore to be released by trying to take it and + * then releasing it again. + * @param [in] owner A debug tag. + * @return The value associated with the semaphore. + */ +uint32_t FreeRTOS::Semaphore::wait(std::string owner) { + ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + xSemaphoreTake(m_semaphore, portMAX_DELAY); + } + + m_owner = owner; + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + + ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); + m_owner = std::string(""); + return m_value; +} // wait + + +FreeRTOS::Semaphore::Semaphore(std::string name) { + m_usePthreads = false; // Are we using pThreads or FreeRTOS? + if (m_usePthreads) { + pthread_mutex_init(&m_pthread_mutex, nullptr); + } else { + m_semaphore = xSemaphoreCreateMutex(); + } + + m_name = name; + m_owner = std::string(""); + m_value = 0; +} + + +FreeRTOS::Semaphore::~Semaphore() { + if (m_usePthreads) { + pthread_mutex_destroy(&m_pthread_mutex); + } else { + vSemaphoreDelete(m_semaphore); + } +} + + +/** + * @brief Give a semaphore. + * The Semaphore is given. + */ +void FreeRTOS::Semaphore::give() { + ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str()); + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } +// #ifdef ARDUINO_ARCH_ESP32 +// FreeRTOS::sleep(10); +// #endif + + m_owner = std::string(""); +} // Semaphore::give + + +/** + * @brief Give a semaphore. + * The Semaphore is given with an associated value. + * @param [in] value The value to associate with the semaphore. + */ +void FreeRTOS::Semaphore::give(uint32_t value) { + m_value = value; + give(); +} // give + + +/** + * @brief Give a semaphore from an ISR. + */ +void FreeRTOS::Semaphore::giveFromISR() { + BaseType_t higherPriorityTaskWoken; + if (m_usePthreads) { + assert(false); + } else { + xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken); + } +} // giveFromISR + + +/** + * @brief Take a semaphore. + * Take a semaphore and wait indefinitely. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool FreeRTOS::Semaphore::take(std::string owner) { + ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE; + } + m_owner = owner; + if (rc) { + ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + +/** + * @brief Take a semaphore. + * Take a semaphore but return if we haven't obtained it in the given period of milliseconds. + * @param [in] timeoutMs Timeout in milliseconds. + * @param [in] owner The new owner (for debugging) + * @return True if we took the semaphore. + */ +bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { + ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + bool rc = false; + if (m_usePthreads) { + assert(false); // We apparently don't have a timed wait for pthreads. + } else { + rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE; + } + m_owner = owner; + if (rc) { + ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + } else { + ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + } + return rc; +} // Semaphore::take + + + +/** + * @brief Create a string representation of the semaphore. + * @return A string representation of the semaphore. + */ +std::string FreeRTOS::Semaphore::toString() { + std::stringstream stringStream; + stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner; + return stringStream.str(); +} // toString + + +/** + * @brief Set the name of the semaphore. + * @param [in] name The name of the semaphore. + */ +void FreeRTOS::Semaphore::setName(std::string name) { + m_name = name; +} // setName + + +/** + * @brief Create a ring buffer. + * @param [in] length The amount of storage to allocate for the ring buffer. + * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF. + */ +Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) { + m_handle = ::xRingbufferCreate(length, type); +} // Ringbuffer + + +Ringbuffer::~Ringbuffer() { + ::vRingbufferDelete(m_handle); +} // ~Ringbuffer + + +/** + * @brief Receive data from the buffer. + * @param [out] size On return, the size of data returned. + * @param [in] wait How long to wait. + * @return A pointer to the storage retrieved. + */ +void* Ringbuffer::receive(size_t* size, TickType_t wait) { + return ::xRingbufferReceive(m_handle, size, wait); +} // receive + + +/** + * @brief Return an item. + * @param [in] item The item to be returned/released. + */ +void Ringbuffer::returnItem(void* item) { + ::vRingbufferReturnItem(m_handle, item); +} // returnItem + + +/** + * @brief Send data to the buffer. + * @param [in] data The data to place into the buffer. + * @param [in] length The length of data to place into the buffer. + * @param [in] wait How long to wait before giving up. The default is to wait indefinitely. + * @return + */ +bool Ringbuffer::send(void* data, size_t length, TickType_t wait) { + return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE; +} // send + + diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.h b/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.h new file mode 100644 index 0000000..b861c87 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/FreeRTOS.h @@ -0,0 +1,71 @@ +/* + * FreeRTOS.h + * + * Created on: Feb 24, 2017 + * Author: kolban + */ + +#ifndef MAIN_FREERTOS_H_ +#define MAIN_FREERTOS_H_ +#include +#include +#include + +#include // Include the base FreeRTOS definitions. +#include // Include the task definitions. +#include // Include the semaphore definitions. +#include // Include the ringbuffer definitions. + + +/** + * @brief Interface to %FreeRTOS functions. + */ +class FreeRTOS { +public: + static void sleep(uint32_t ms); + static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048); + static void deleteTask(TaskHandle_t pTask = nullptr); + + static uint32_t getTimeSinceStart(); + + class Semaphore { + public: + Semaphore(std::string owner = ""); + ~Semaphore(); + void give(); + void give(uint32_t value); + void giveFromISR(); + void setName(std::string name); + bool take(std::string owner = ""); + bool take(uint32_t timeoutMs, std::string owner = ""); + std::string toString(); + uint32_t wait(std::string owner = ""); + + private: + SemaphoreHandle_t m_semaphore; + pthread_mutex_t m_pthread_mutex; + std::string m_name; + std::string m_owner; + uint32_t m_value; + bool m_usePthreads; + + }; +}; + + +/** + * @brief Ringbuffer. + */ +class Ringbuffer { +public: + Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT); + ~Ringbuffer(); + + void* receive(size_t* size, TickType_t wait = portMAX_DELAY); + void returnItem(void* item); + bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY); +private: + RingbufHandle_t m_handle; +}; + +#endif /* MAIN_FREERTOS_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.cpp b/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.cpp new file mode 100644 index 0000000..019c81b --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.cpp @@ -0,0 +1,544 @@ +/* + * GeneralUtils.cpp + * + * Created on: May 20, 2017 + * Author: kolban + */ + +#include "GeneralUtils.h" +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include +#include +#include +#include +#include + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#define LOG_TAG "" +#else +#include "esp_log.h" +static const char* LOG_TAG = "GeneralUtils"; +#endif + + +static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static int base64EncodedLength(size_t length) { + return (length + 2 - ((length + 2) % 3)) / 3 * 4; +} // base64EncodedLength + + +static int base64EncodedLength(const std::string& in) { + return base64EncodedLength(in.length()); +} // base64EncodedLength + + +static void a3_to_a4(unsigned char* a4, unsigned char* a3) { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = (a3[2] & 0x3f); +} // a3_to_a4 + + +static void a4_to_a3(unsigned char* a3, unsigned char* a4) { + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; +} // a4_to_a3 + + +/** + * @brief Encode a string into base 64. + * @param [in] in + * @param [out] out + */ +bool GeneralUtils::base64Encode(const std::string& in, std::string* out) { + int i = 0, j = 0; + size_t enc_len = 0; + unsigned char a3[3]; + unsigned char a4[4]; + + out->resize(base64EncodedLength(in)); + + int input_len = in.size(); + std::string::const_iterator input = in.begin(); + + while (input_len--) { + a3[i++] = *(input++); + if (i == 3) { + a3_to_a4(a4, a3); + + for (i = 0; i < 4; i++) { + (*out)[enc_len++] = kBase64Alphabet[a4[i]]; + } + + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) { + a3[j] = '\0'; + } + + a3_to_a4(a4, a3); + + for (j = 0; j < i + 1; j++) { + (*out)[enc_len++] = kBase64Alphabet[a4[j]]; + } + + while ((i++ < 3)) { + (*out)[enc_len++] = '='; + } + } + + return (enc_len == out->size()); +} // base64Encode + + +/** + * @brief Dump general info to the log. + * Data includes: + * * Amount of free RAM + */ +void GeneralUtils::dumpInfo() { + size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + ESP_LOGV(LOG_TAG, "--- dumpInfo ---"); + ESP_LOGV(LOG_TAG, "Free heap: %d", freeHeap); + ESP_LOGV(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision); + ESP_LOGV(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version()); + ESP_LOGV(LOG_TAG, "---"); +} // dumpInfo + + +/** + * @brief Does the string end with a specific character? + * @param [in] str The string to examine. + * @param [in] c The character to look form. + * @return True if the string ends with the given character. + */ +bool GeneralUtils::endsWith(std::string str, char c) { + if (str.empty()) { + return false; + } + if (str.at(str.length() - 1) == c) { + return true; + } + return false; +} // endsWidth + + +static int DecodedLength(const std::string& in) { + int numEq = 0; + int n = (int) in.size(); + + for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { + ++numEq; + } + return ((6 * n) / 8) - numEq; +} // DecodedLength + + +static unsigned char b64_lookup(unsigned char c) { + if(c >='A' && c <='Z') return c - 'A'; + if(c >='a' && c <='z') return c - 71; + if(c >='0' && c <='9') return c + 4; + if(c == '+') return 62; + if(c == '/') return 63; + return 255; +}; // b64_lookup + + +/** + * @brief Decode a chunk of data that is base64 encoded. + * @param [in] in The string to be decoded. + * @param [out] out The resulting data. + */ +bool GeneralUtils::base64Decode(const std::string& in, std::string* out) { + int i = 0, j = 0; + size_t dec_len = 0; + unsigned char a3[3]; + unsigned char a4[4]; + + int input_len = in.size(); + std::string::const_iterator input = in.begin(); + + out->resize(DecodedLength(in)); + + while (input_len--) { + if (*input == '=') { + break; + } + + a4[i++] = *(input++); + if (i == 4) { + for (i = 0; i <4; i++) { + a4[i] = b64_lookup(a4[i]); + } + + a4_to_a3(a3,a4); + + for (i = 0; i < 3; i++) { + (*out)[dec_len++] = a3[i]; + } + + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) { + a4[j] = '\0'; + } + + for (j = 0; j < 4; j++) { + a4[j] = b64_lookup(a4[j]); + } + + a4_to_a3(a3,a4); + + for (j = 0; j < i - 1; j++) { + (*out)[dec_len++] = a3[j]; + } + } + + return (dec_len == out->size()); + } // base64Decode + +/* +void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { + uint32_t index=0; + std::stringstream ascii; + std::stringstream hex; + char asciiBuf[80]; + char hexBuf[80]; + hex.str(""); + ascii.str(""); + while(index < length) { + hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' '; + if (std::isprint(pData[index])) { + ascii << pData[index]; + } else { + ascii << '.'; + } + index++; + if (index % 16 == 0) { + strcpy(hexBuf, hex.str().c_str()); + strcpy(asciiBuf, ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); + hex.str(""); + ascii.str(""); + } + } + if (index %16 != 0) { + while(index % 16 != 0) { + hex << " "; + index++; + } + strcpy(hexBuf, hex.str().c_str()); + strcpy(asciiBuf, ascii.str().c_str()); + ESP_LOGV(tag, "%s %s", hexBuf, asciiBuf); + //ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + } + FreeRTOS::sleep(1000); +} +*/ + +/* +void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) { + uint32_t index=0; + static std::stringstream ascii; + static std::stringstream hex; + hex.str(""); + ascii.str(""); + while(index < length) { + hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' '; + if (std::isprint(pData[index])) { + ascii << pData[index]; + } else { + ascii << '.'; + } + index++; + if (index % 16 == 0) { + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + hex.str(""); + ascii.str(""); + } + } + if (index %16 != 0) { + while(index % 16 != 0) { + hex << " "; + index++; + } + ESP_LOGV(tag, "%s %s", hex.str().c_str(), ascii.str().c_str()); + } + FreeRTOS::sleep(1000); +} +*/ + + +/** + * @brief Dump a representation of binary data to the console. + * + * @param [in] pData Pointer to the start of data to be logged. + * @param [in] length Length of the data (in bytes) to be logged. + * @return N/A. + */ +void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { + char ascii[80]; + char hex[80]; + char tempBuf[80]; + uint32_t lineNumber = 0; + + ESP_LOGV(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + ESP_LOGV(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); + strcpy(ascii, ""); + strcpy(hex, ""); + uint32_t index = 0; + while (index < length) { + sprintf(tempBuf, "%.2x ", pData[index]); + strcat(hex, tempBuf); + if (isprint(pData[index])) { + sprintf(tempBuf, "%c", pData[index]); + } else { + sprintf(tempBuf, "."); + } + strcat(ascii, tempBuf); + index++; + if (index % 16 == 0) { + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); + strcpy(ascii, ""); + strcpy(hex, ""); + lineNumber++; + } + } + if (index %16 != 0) { + while (index % 16 != 0) { + strcat(hex, " "); + index++; + } + ESP_LOGV(LOG_TAG, "%.4x %s %s", lineNumber * 16, hex, ascii); + } +} // hexDump + + +/** + * @brief Convert an IP address to string. + * @param ip The 4 byte IP address. + * @return A string representation of the IP address. + */ +std::string GeneralUtils::ipToString(uint8_t *ip) { + std::stringstream s; + s << (int) ip[0] << '.' << (int) ip[1] << '.' << (int) ip[2] << '.' << (int) ip[3]; + return s.str(); +} // ipToString + + +/** + * @brief Split a string into parts based on a delimiter. + * @param [in] source The source string to split. + * @param [in] delimiter The delimiter characters. + * @return A vector of strings that are the split of the input. + */ +std::vector GeneralUtils::split(std::string source, char delimiter) { + // See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g + std::vector strings; + std::istringstream iss(source); + std::string s; + while (std::getline(iss, s, delimiter)) { + strings.push_back(trim(s)); + } + return strings; +} // split + + +/** + * @brief Convert an ESP error code to a string. + * @param [in] errCode The errCode to be converted. + * @return A string representation of the error code. + */ +const char* GeneralUtils::errorToString(esp_err_t errCode) { + switch (errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case ESP_OK: + return "ESP_OK"; + case ESP_FAIL: + return "ESP_FAIL"; + case ESP_ERR_NO_MEM: + return "ESP_ERR_NO_MEM"; + case ESP_ERR_INVALID_ARG: + return "ESP_ERR_INVALID_ARG"; + case ESP_ERR_INVALID_SIZE: + return "ESP_ERR_INVALID_SIZE"; + case ESP_ERR_INVALID_STATE: + return "ESP_ERR_INVALID_STATE"; + case ESP_ERR_NOT_FOUND: + return "ESP_ERR_NOT_FOUND"; + case ESP_ERR_NOT_SUPPORTED: + return "ESP_ERR_NOT_SUPPORTED"; + case ESP_ERR_TIMEOUT: + return "ESP_ERR_TIMEOUT"; + case ESP_ERR_NVS_NOT_INITIALIZED: + return "ESP_ERR_NVS_NOT_INITIALIZED"; + case ESP_ERR_NVS_NOT_FOUND: + return "ESP_ERR_NVS_NOT_FOUND"; + case ESP_ERR_NVS_TYPE_MISMATCH: + return "ESP_ERR_NVS_TYPE_MISMATCH"; + case ESP_ERR_NVS_READ_ONLY: + return "ESP_ERR_NVS_READ_ONLY"; + case ESP_ERR_NVS_NOT_ENOUGH_SPACE: + return "ESP_ERR_NVS_NOT_ENOUGH_SPACE"; + case ESP_ERR_NVS_INVALID_NAME: + return "ESP_ERR_NVS_INVALID_NAME"; + case ESP_ERR_NVS_INVALID_HANDLE: + return "ESP_ERR_NVS_INVALID_HANDLE"; + case ESP_ERR_NVS_REMOVE_FAILED: + return "ESP_ERR_NVS_REMOVE_FAILED"; + case ESP_ERR_NVS_KEY_TOO_LONG: + return "ESP_ERR_NVS_KEY_TOO_LONG"; + case ESP_ERR_NVS_PAGE_FULL: + return "ESP_ERR_NVS_PAGE_FULL"; + case ESP_ERR_NVS_INVALID_STATE: + return "ESP_ERR_NVS_INVALID_STATE"; + case ESP_ERR_NVS_INVALID_LENGTH: + return "ESP_ERR_NVS_INVALID_LENGTH"; + case ESP_ERR_WIFI_NOT_INIT: + return "ESP_ERR_WIFI_NOT_INIT"; + //case ESP_ERR_WIFI_NOT_START: + // return "ESP_ERR_WIFI_NOT_START"; + case ESP_ERR_WIFI_IF: + return "ESP_ERR_WIFI_IF"; + case ESP_ERR_WIFI_MODE: + return "ESP_ERR_WIFI_MODE"; + case ESP_ERR_WIFI_STATE: + return "ESP_ERR_WIFI_STATE"; + case ESP_ERR_WIFI_CONN: + return "ESP_ERR_WIFI_CONN"; + case ESP_ERR_WIFI_NVS: + return "ESP_ERR_WIFI_NVS"; + case ESP_ERR_WIFI_MAC: + return "ESP_ERR_WIFI_MAC"; + case ESP_ERR_WIFI_SSID: + return "ESP_ERR_WIFI_SSID"; + case ESP_ERR_WIFI_PASSWORD: + return "ESP_ERR_WIFI_PASSWORD"; + case ESP_ERR_WIFI_TIMEOUT: + return "ESP_ERR_WIFI_TIMEOUT"; + case ESP_ERR_WIFI_WAKE_FAIL: + return "ESP_ERR_WIFI_WAKE_FAIL"; +#endif + default: + return "Unknown ESP_ERR error"; + } +} // errorToString + +/** + * @brief Convert a wifi_err_reason_t code to a string. + * @param [in] errCode The errCode to be converted. + * @return A string representation of the error code. + * + * @note: wifi_err_reason_t values as of April 2018 are: (1-24, 200-204) and are defined in ~/esp-idf/components/esp32/include/esp_wifi_types.h. + */ +const char* GeneralUtils::wifiErrorToString(uint8_t errCode) { + if (errCode == ESP_OK) return "ESP_OK (received SYSTEM_EVENT_STA_GOT_IP event)"; + if (errCode == UINT8_MAX) return "Not Connected (default value)"; + + switch ((wifi_err_reason_t) errCode) { +#if CONFIG_LOG_DEFAULT_LEVEL > 4 + case WIFI_REASON_UNSPECIFIED: + return "WIFI_REASON_UNSPECIFIED"; + case WIFI_REASON_AUTH_EXPIRE: + return "WIFI_REASON_AUTH_EXPIRE"; + case WIFI_REASON_AUTH_LEAVE: + return "WIFI_REASON_AUTH_LEAVE"; + case WIFI_REASON_ASSOC_EXPIRE: + return "WIFI_REASON_ASSOC_EXPIRE"; + case WIFI_REASON_ASSOC_TOOMANY: + return "WIFI_REASON_ASSOC_TOOMANY"; + case WIFI_REASON_NOT_AUTHED: + return "WIFI_REASON_NOT_AUTHED"; + case WIFI_REASON_NOT_ASSOCED: + return "WIFI_REASON_NOT_ASSOCED"; + case WIFI_REASON_ASSOC_LEAVE: + return "WIFI_REASON_ASSOC_LEAVE"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "WIFI_REASON_ASSOC_NOT_AUTHED"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "WIFI_REASON_DISASSOC_PWRCAP_BAD"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "WIFI_REASON_DISASSOC_SUPCHAN_BAD"; + case WIFI_REASON_IE_INVALID: + return "WIFI_REASON_IE_INVALID"; + case WIFI_REASON_MIC_FAILURE: + return "WIFI_REASON_MIC_FAILURE"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "WIFI_REASON_IE_IN_4WAY_DIFFERS"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "WIFI_REASON_GROUP_CIPHER_INVALID"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "WIFI_REASON_PAIRWISE_CIPHER_INVALID"; + case WIFI_REASON_AKMP_INVALID: + return "WIFI_REASON_AKMP_INVALID"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "WIFI_REASON_UNSUPP_RSN_IE_VERSION"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "WIFI_REASON_INVALID_RSN_IE_CAP"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "WIFI_REASON_802_1X_AUTH_FAILED"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "WIFI_REASON_CIPHER_SUITE_REJECTED"; + case WIFI_REASON_BEACON_TIMEOUT: + return "WIFI_REASON_BEACON_TIMEOUT"; + case WIFI_REASON_NO_AP_FOUND: + return "WIFI_REASON_NO_AP_FOUND"; + case WIFI_REASON_AUTH_FAIL: + return "WIFI_REASON_AUTH_FAIL"; + case WIFI_REASON_ASSOC_FAIL: + return "WIFI_REASON_ASSOC_FAIL"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "WIFI_REASON_HANDSHAKE_TIMEOUT"; +#endif + default: + return "Unknown ESP_ERR error"; + } +} // wifiErrorToString + + +/** + * @brief Convert a string to lower case. + * @param [in] value The string to convert to lower case. + * @return A lower case representation of the string. + */ +std::string GeneralUtils::toLower(std::string& value) { + // Question: Could this be improved with a signature of: + // std::string& GeneralUtils::toLower(std::string& value) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + return value; +} // toLower + + +/** + * @brief Remove white space from a string. + */ +std::string GeneralUtils::trim(const std::string& str) { + size_t first = str.find_first_not_of(' '); + if (std::string::npos == first) return str; + size_t last = str.find_last_not_of(' '); + return str.substr(first, (last - first + 1)); +} // trim diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.h b/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.h new file mode 100644 index 0000000..8eecbd4 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/GeneralUtils.h @@ -0,0 +1,35 @@ +/* + * GeneralUtils.h + * + * Created on: May 20, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_GENERALUTILS_H_ +#define COMPONENTS_CPP_UTILS_GENERALUTILS_H_ +#include +#include +#include +#include +#include + +/** + * @brief General utilities. + */ +class GeneralUtils { +public: + static bool base64Decode(const std::string& in, std::string* out); + static bool base64Encode(const std::string& in, std::string* out); + static void dumpInfo(); + static bool endsWith(std::string str, char c); + static const char* errorToString(esp_err_t errCode); + static const char* wifiErrorToString(uint8_t value); + static void hexDump(const uint8_t* pData, uint32_t length); + static std::string ipToString(uint8_t* ip); + static std::vector split(std::string source, char delimiter); + static std::string toLower(std::string& value); + static std::string trim(const std::string& str); + +}; + +#endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */ diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/HIDKeyboardTypes.h b/lib/ESP32_BLE_Arduino-1.0.1/src/HIDKeyboardTypes.h new file mode 100644 index 0000000..4e221d5 --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/lib/ESP32_BLE_Arduino-1.0.1/src/HIDTypes.h b/lib/ESP32_BLE_Arduino-1.0.1/src/HIDTypes.h new file mode 100644 index 0000000..64850ef --- /dev/null +++ b/lib/ESP32_BLE_Arduino-1.0.1/src/HIDTypes.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#ifdef ARDUINO_ARCH_ESP32 +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#else +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#endif +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/lib/NimBLE-Arduino/CHANGELOG.md b/lib/NimBLE-Arduino/CHANGELOG.md new file mode 100644 index 0000000..08d2741 --- /dev/null +++ b/lib/NimBLE-Arduino/CHANGELOG.md @@ -0,0 +1,257 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [1.3.6] - 2022-01-18 + +### Changed +- When retrieving attributes from a server fails with a 128bit UUID containing the ble base UUID another attempt will be made with the 16bit version of the UUID. + +### Fixed +- Memory leak when services are changed on server devices. +- Rare crashing that occurs when BLE commands are sent from ISR context using IPC. +- Crashing caused by uninitialized disconnect timer in client. +- Potential crash due to unintialized advertising callback pointer. + +## [1.3.5] - 2022-01-14 + +### Added +- CONFIG_NIMBLE_CPP_DEBUG_LEVEL macro in nimconfig.h to allow setting the log level separately from the Arduino core log level. + +### Fixed +- Memory leak when initializing/deinitializing the BLE stack caused by new FreeRTOS timers be created on each initialization. + +## [1.3.4] - 2022-01-09 + +### Fixed +- Workaround for latest Arduino-esp32 core that causes tasks not to block when required, which caused functions to return prematurely resulting in exceptions/crashing. +- The wrong length value was being used to set the values read from peer attributes. This has been corrected to use the proper value size. + +## [1.3.3] - 2021-11-24 + +### Fixed +- Workaround added for FreeRTOS bug that affected timers, causing scan and advertising timer expirations to not correctly trigger callbacks. + +## [1.3.2] - 2021-11-20 + +### Fixed +- Added missing macros for scan filter. + +### Added +- `NimBLEClient::getLastError` : Gets the error code of the last function call that produces a return code from the stack. + +## [1.3.1] - 2021-08-04 + +### Fixed +- Corrected a compiler/linker error when an application or a library uses bluetooth classic due to the redefinition of `btInUse`. + +## [1.3.0] - 2021-08-02 + +### Added +- `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characterisic. Takes effect after all connections are closed and sends a service changed indication. +- `NimBLEService::removeCharacteristic`: Dynamically remove a characteristic from a service. Takes effect after all connections are closed and sends a service changed indication +- `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client. +- ESP32C3 support + +- Whitelist API: + - `NimBLEDevice::whiteListAdd`: Add a device to the whitelist. + - `NimBLEDevice::whiteListRemove`: Remove a device from the whitelist. + - `NimBLEDevice::onWhiteList`: Check if the device is on the whitelist. + - `NimBLEDevice::getWhiteListCount`: Gets the size of the whitelist + - `NimBLEDevice::getWhiteListAddress`: Get the address of a device on the whitelist by index value. + +- Bond management API: + - `NimBLEDevice::getNumBonds`: Gets the number of bonds stored. + - `NimBLEDevice::isBonded`: Checks if the device is bonded. + - `NimBLEDevice::deleteAllBonds`: Deletes all bonds. + - `NimBLEDevice::getBondedAddress`: Gets the address of a bonded device by the index value. + +- `NimBLECharacteristic::getCallbacks` to retrieve the current callback handler. +- Connection Information class: `NimBLEConnInfo`. +- `NimBLEScan::clearDuplicateCache`: This can be used to reset the cache of advertised devices so they will be immediately discovered again. + +### Changed +- FreeRTOS files have been removed as they are not used by the library. +- Services, characteristics and descriptors can now be created statically and added after. +- Excess logging and some asserts removed. +- Use ESP_LOGx macros to enable using local log level filtering. + +### Fixed +- `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector. +- Corrected bonding failure when reinitializing the BLE stack. +- Writing to a characterisic with a std::string value now correctly writes values with null characters. +- Retrieving remote descriptors now uses the characterisic end handle correctly. +- Missing data in long writes to remote descriptors. +- Hanging on task notification when sending an indication from the characteristic callback. +- BLE controller memory could be released when using Arduino as a component. +- Complile errors with NimBLE release 1.3.0. + +## [1.2.0] - 2021-02-08 + +### Added +- `NimBLECharacteristic::getDescriptorByHandle`: Return the BLE Descriptor for the given handle. + +- `NimBLEDescriptor::getStringValue`: Get the value of this descriptor as a string. + +- `NimBLEServer::getServiceByHandle`: Get a service by its handle. + +- `NimBLEService::getCharacteristicByHandle`: Get a pointer to the characteristic object with the specified handle. + +- `NimBLEService::getCharacteristics`: Get the vector containing pointers to each characteristic associated with this service. +Overloads to get a vector containing pointers to all the characteristics in a service with the UUID. (supports multiple same UUID's in a service) + - `NimBLEService::getCharacteristics(const char *uuid)` + - `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)` + +- `NimBLEAdvertisementData` New methods: + - `NimBLEAdvertisementData::addTxPower`: Adds transmission power to the advertisement. + - `NimBLEAdvertisementData::setPreferredParams`: Adds connection parameters to the advertisement. + - `NimBLEAdvertisementData::setURI`: Adds URI data to the advertisement. + +- `NimBLEAdvertising` New methods: + - `NimBLEAdvertising::setName`: Set the name advertised. + - `NimBLEAdvertising::setManufacturerData`: Adds manufacturer data to the advertisement. + - `NimBLEAdvertising::setURI`: Adds URI data to the advertisement. + - `NimBLEAdvertising::setServiceData`: Adds service data to the advertisement. + - `NimBLEAdvertising::addTxPower`: Adds transmission power to the advertisement. + - `NimBLEAdvertising::reset`: Stops the current advertising and resets the advertising data to the default values. + +- `NimBLEDevice::setScanFilterMode`: Set the controller duplicate filter mode for filtering scanned devices. + +- `NimBLEDevice::setScanDuplicateCacheSize`: Sets the number of advertisements filtered before the cache is reset. + +- `NimBLEScan::setMaxResults`: This allows for setting a maximum number of advertised devices stored in the results vector. + +- `NimBLEAdvertisedDevice` New data retrieval methods added: + - `haveAdvInterval/getAdvInterval`: checks if the interval is advertised / gets the advertisement interval value. + + - `haveConnParams/getMinInterval/getMaxInterval`: checks if the parameters are advertised / get min value / get max value. + + - `haveURI/getURI`: checks if a URI is advertised / gets the URI data. + + - `haveTargetAddress/getTargetAddressCount/getTargetAddress(index)`: checks if a target address is present / gets a count of the addresses targeted / gets the address of the target at index. + +### Changed +- `nimconfig.h` (Arduino) is now easier to use. + +- `NimBLEServer::getServiceByUUID` Now takes an extra parameter of instanceID to support multiple services with the same UUID. + +- `NimBLEService::getCharacteristic` Now takes an extra parameter of instanceID to support multiple characteristics with the same UUID. + +- `NimBLEAdvertising` Transmission power is no longer advertised by default and can be added to the advertisement by calling `NimBLEAdvertising::addTxPower` + +- `NimBLEAdvertising` Custom scan response data can now be used without custom advertisment. + +- `NimBLEScan` Now uses the controller duplicate filter. + +- `NimBLEAdvertisedDevice` Has been refactored to store the complete advertisement payload and no longer parses the data from each advertisement. +Instead the data will be parsed on-demand when the user application asks for specific data. + +### Fixed +- `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices. + + +## [1.1.0] - 2021-01-20 + +### Added +- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa + +- New examples for securing and authenticating client/server connections, by mblasee. + +- `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added. + +- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio. + +- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false). + +- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find +the NimBLERemoteCharacteristic object. + +- `NimBLEHIDDevice` class added by wakwak-koba. + +- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application +to obtain information about the disconnected client. + +- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings. + +### Changed +- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure. + +- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging. + +- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite. + +- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback +regardless of the existance of the CCCD and return true unless the descriptor write operation failed. + +- Advertising tx power level is now sent in the advertisement packet instead of scan response. + +- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used) +this allows the starting of a new scan from the callback function. + +### Fixed +- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock. +A time limit has been added to timeout appropriately. + +- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end +handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible. + +- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being +deleted. A flag has been added to prevent this. + +- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did +not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding. + +- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected +and would be unable to reconnect. A timer has been added to reset the host/controller if it expires. + +- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed. + +- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device +advertised them as 16/32bit but resolved them to 128bits. Both are now checked. + +- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3. + +- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions. + +- Advertisement type now correctly set when using non-connectable (advertiser only) mode. + +- Advertising payload length correction, now accounts for appearance. + +- (Arduino) Ensure controller mode is set to BLE Only. + + +## [1.0.2] - 2020-09-13 + +### Changed + +- `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a +callback that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). + +- (Arduino) Maximum BLE connections can now be altered by only changing the value of `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in `nimconfig.h`. +Any changes to the controller max connection settings in `sdkconfig.h` will now have no effect when using this library. + +- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from +a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions. + + +## [1.0.1] - 2020-09-02 + +### Added + +- Empty `NimBLEAddress` constructor: `NimBLEAddress()` produces an address of 00:00:00:00:00:00 type 0. +- Documentation of the difference of NimBLEAddress::getNative vs the original bluedroid library. + +### Changed + +- notify_callback typedef is now defined as std::function to enable the use of std::bind to call a class member function. + +### Fixed + +- Fix advertising start delay when first called. + + +## [1.0.0] - 2020-08-22 + +First stable release. + +All the original library functionality is complete and many extras added with full documentation. diff --git a/lib/NimBLE-Arduino/LICENSE b/lib/NimBLE-Arduino/LICENSE new file mode 100644 index 0000000..4abe696 --- /dev/null +++ b/lib/NimBLE-Arduino/LICENSE @@ -0,0 +1,219 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {2020} {Ryan Powell} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE + +This product partly derives from esp32-snippets; Copyright 2017 Neil Kolban. \ No newline at end of file diff --git a/lib/NimBLE-Arduino/README.md b/lib/NimBLE-Arduino/README.md new file mode 100644 index 0000000..fd480fd --- /dev/null +++ b/lib/NimBLE-Arduino/README.md @@ -0,0 +1,85 @@ +[Latest release ![Release Version](https://img.shields.io/github/release/h2zero/NimBLE-Arduino.svg?style=plastic) +![Release Date](https://img.shields.io/github/release-date/h2zero/NimBLE-Arduino.svg?style=plastic)](https://github.com/h2zero/NimBLE-Arduino/releases/latest/) + +Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +
+ +# NimBLE-Arduino +A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32. + +**Note for IDF users: This repo will not compile correctly in ESP-IDF. An ESP-IDF component version of this library can be [found here.](https://github.com/h2zero/esp-nimble-cpp)** + +This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared +with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original +library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained +to provide improved capabilites and stability over the original. +
+ +## Resource use improvement + +### (Original) BLE_client example comparison (Debug): +#### Arduino BLE Library +Sketch uses **1216377** bytes (58%) of program storage space. +Memory after connection: Free Heap: **171548** + +#### NimBLE-Arduino library +Sketch uses **617256** bytes (29%) of program storage space. +Memory after connection: Free Heap: **270336** +*** +### (Original) BLE_notify example comparison (Debug): +#### Arduino BLE Library +Sketch uses **1208409** bytes (57%) of program storage space. +Memory after connection: Free Heap: **173300** + +#### NimBLE-Arduino library +Sketch uses **603432** bytes (28%) of program storage space. +Memory after connection: Free Heap: **269792** + +**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!** +
+ +# Installation +**Arduino Library manager:** Go to `sketch` -> `Include Library` -> `Manage Libraries` and search for NimBLE and install. + +**Alternatively:** Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. + +`#include "NimBLEDevice.h"` at the beginning of your sketch. + +Tested and working with esp32-arduino in Arduino IDE and platform IO. +
+ +# Using +This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes. + +If you have not used the original Bluedroid library please refer to the [New user guide](docs/New_user_guide.md). + +If you are familiar with the original library, see: [The migration guide](docs/Migration_guide.md) for details about breaking changes and migration. + +Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for information about non-breaking changes. + +[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/) + +For added performance and optimizations see [Usage tips](docs/Usage_tips.md). + +Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library. + +More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client. + +Beacon examples provided by @beegee-tokyo are in examples/ BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon. + +Change the settings in the `src/nimconfig.h` file to customize NimBLE to your project, +such as increasing max connections, default is 3, absolute maximum connections is 9. +
+ +# Development Status +This Library is tracking the esp-nimble repo, nimble-1.3.0-idf master branch, currently [@5bb7b40.](https://github.com/espressif/esp-nimble) + +Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@639e7ad.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble) +
+ +# Acknowledgments +* [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from. +* [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples. +* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code. +
+ diff --git a/lib/NimBLE-Arduino/docs/Command_line_config.md b/lib/NimBLE-Arduino/docs/Command_line_config.md new file mode 100644 index 0000000..3fe4acb --- /dev/null +++ b/lib/NimBLE-Arduino/docs/Command_line_config.md @@ -0,0 +1,142 @@ +# Arduino command line and platformio config options + +`CONFIG_BT_NIMBLE_MAX_CONNECTIONS` + +Sets the number of simultaneous connections (esp controller max is 9) +- Default value is 3 +
+ +`CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED` + +Enable/disable storing the timestamp when an attribute value is updated +This allows for checking the last update time using getTimeStamp() or getValue(time_t*) +If disabled, the timestamp returned from these functions will be 0. +Disabling timestamps will reduce the memory used for each value. +1 = Enabled, 0 = Disabled; Default = Disabled +
+ +`CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH` + +Set the default allocation size (bytes) for each attribute. +If not specified when the constructor is called. This is also the size used when a remote +characteristic or descriptor is constructed before a value is read/notifed. +Increasing this will reduce reallocations but increase memory footprint. +Default value is 20. Range: 1 : 512 (BLE_ATT_ATTR_MAX_LEN) +
+ +`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU` + +Sets the default MTU size. +- Default value is 255 +
+ +`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME` + +Set the default device name +- Default value is "nimble" +
+ +`CONFIG_BT_NIMBLE_DEBUG` + +If defined, enables debug log messages from the NimBLE host +- Uses approx. 32kB of flash memory. +
+ +`CONFIG_NIMBLE_CPP_LOG_LEVEL` + +Define to set the debug log message level from the NimBLE CPP Wrapper. +If not defined it will use the same value as the Arduino core debug level. +Values: 0 = NONE, 1 = ERROR, 2 = WARNING, 3 = INFO, 4+ = DEBUG +
+ +`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT` + +If defined, NimBLE host return codes will be printed as text in debug log messages. +- Uses approx. 7kB of flash memory. +
+ +`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT` + +If defined, GAP event codes will be printed as text in debug log messages. +- Uses approx. 1kB of flash memory. +
+ +`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT` + +If defined, advertisment types will be printed as text while scanning in debug log messages. +- Uses approx. 250 bytes of flash memory. +
+ +`CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE` + +Set the default appearance. +- Default value is 0x00 +
+ +`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED` + +If defined, NimBLE Client functions will not be included. +- Reduces flash size by approx. 7kB. +
+ +`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED` + +If defined, NimBLE Scan functions will not be included. +- Reduces flash size by approx. 26kB. +
+ +`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED` + +If defined NimBLE Server functions will not be included. +- Reduces flash size by approx. 16kB. +
+ +`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED` + +If defined, NimBLE Advertising functions will not be included. +- Reduces flash size by approx. 5kB. +
+ +`CONFIG_BT_NIMBLE_MAX_BONDS` + +Sets the number of devices allowed to store/bond with +- Default value is 3 +
+ +`CONFIG_BT_NIMBLE_MAX_CCCDS` + +Sets the maximum number of CCCD subscriptions to store +- Default value is 8 +
+ +`CONFIG_BT_NIMBLE_RPA_TIMEOUT` + +Sets the random address refresh time in seconds. +- Default value is 900 +
+ +`CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT` + +Set the number of msys blocks For prepare write & prepare responses. This may need to be increased if +you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail. +- Default value is 12 +
+ +`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL` + +Sets the NimBLE stack to use external PSRAM will be loaded +- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1 +
+ +`CONFIG_BT_NIMBLE_PINNED_TO_CORE` + +Sets the core the NimBLE host stack will run on +- Options: 0 or 1 +
+ +`CONFIG_BT_NIMBLE_TASK_STACK_SIZE` + +Set the task stack size for the NimBLE core. +- Default is 4096 +
+ diff --git a/lib/NimBLE-Arduino/docs/Improvements_and_updates.md b/lib/NimBLE-Arduino/docs/Improvements_and_updates.md new file mode 100644 index 0000000..c3c1126 --- /dev/null +++ b/lib/NimBLE-Arduino/docs/Improvements_and_updates.md @@ -0,0 +1,148 @@ +# Improvements and updates + +Many improvements have been made to this library vs the original, this is a brief overview of the most significant changes. +Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) for futher information on class specifics. + +* [Server](#server) +* [Advertising](#advertising) +* [Client](#client) +* [General](#general) +
+ + +# Server + +`NimBLECharacteristic::setValue(const T &s)` +`NimBLEDescriptor::setValue(const T &s)` + +Now use a template to accomodate standard and custom types/values. + +**Example** +``` +struct my_struct{ + uint8_t one; + uint16_t two; + uint32_t four; + uint64_t eight; + float flt; +}myStruct; + + myStruct.one = 1; + myStruct.two = 2; + myStruct.four = 4; + myStruct.eight = 8; + myStruct.flt = 1234.56; + + pCharacteristic->setValue(myStruct); + ``` +This will send the struct to the recieving client when read or a notification sent. + +`NimBLECharacteristic::getValue` now takes an optional timestamp parameter which will update it's value with +the time the last value was recieved. In addition an overloaded template has been added to retrieve the value +as a type specified by the user. + +**Example** +``` + time_t timestamp; + myStruct = pCharacteristic->getValue(×tamp); // timestamp optional +``` +
+ +**Advertising will automatically start when a client disconnects.** + +A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled. +
+ +`NimBLEServer::removeService` takes an additional parameter `bool deleteSvc` that if true will delete the service +and all characteristics / descriptors belonging to it and invalidating any pointers to them. + +If false the service is only removed from visibility by clients. The pointers to the service and +it's characteristics / descriptors will remain valid and the service can be re-added in the future +using `NimBLEServer::addService`. +
+ + +# Advertising +`NimBLEAdvertising::start` + +Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback +that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). + +This provides an opportunity to update the advertisment data if desired. + +Also now returns a bool value to indicate if advertising successfully started or not. +
+ + +# Client + +`NimBLERemoteCharacteristic::readValue(time_t\*, bool)` +`NimBLERemoteDescriptor::readValue(bool)` + +Have been added as templates to allow reading the values as any specified type. + +**Example** +``` +struct my_struct{ + uint8_t one; + uint16_t two; + uint32_t four; + uint64_t eight; + float flt; +}myStruct; + + time_t timestamp; + myStruct = pRemoteCharacteristic->readValue(×tamp); // timestamp optional +``` +
+ +`NimBLERemoteCharacteristic::registerForNotify` +Has been **deprecated** as now the internally stored characteristic value is updated when notification/indication is recieved. + +`NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unsubscribe` have been implemented to replace it. +A callback is no longer requred to get the most recent value unless timing is important. Instead, the application can call `NimBLERemoteCharacteristic::getValue` to +get the last updated value any time. +
+ +The `notifiy_callback` function is now defined as a `std::function` to take advantage of using `std::bind` to specifiy a class member function for the callback. + +Example: +``` +using namespace std::placeholders; +notify_callback callback = std::bind(&::, this, _1, _2, _3, _4); +->subscribe(true, callback); +``` + +`NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with +the time the last value was recieved. + +> NimBLEClient::getService +> NimBLERemoteService::getCharacteristic +> NimBLERemoteCharacteristic::getDescriptor + +These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only) +the specified attribute from the peripheral. + +These changes allow more control for the user to manage the resources used for the attributes. +
+ +`NimBLEClient::connect()` can now be called without an address or advertised device parameter. This will connect to the +device with the address previously set when last connected or set with `NimBLEDevice::setPeerAddress()`. + + +# General +To reduce resource use all instances of `std::map` have been replaced with `std::vector`. + +Use of `FreeRTOS::Semaphore` has been removed as it was consuming too much ram, the related files have been left in place to accomodate application use. + +Operators `==`, `!=` and `std::string` have been added to `NimBLEAddress` and `NimBLEUUID` for easier comparison and logging. + +New constructor for `NimBLEUUID(uint32_t, uint16_t, uint16_t, uint64_t)` added to lower memory use vs string construction. See: [#21](https://github.com/h2zero/NimBLE-Arduino/pull/21). + +Security/pairing operations are now handled in the respective `NimBLEClientCallbacks` and `NimBLEServerCallbacks` classes, `NimBLESecurity`(deprecated) remains for backward compatibility. + +Configuration options have been added to add or remove debugging information, when disabled (default) significatly reduces binary size. +In ESP-IDF the options are in menuconfig: `Main menu -> ESP-NimBLE-cpp configuration`. +For Arduino the options must be commented / uncommented in nimconfig.h. +
+ diff --git a/lib/NimBLE-Arduino/docs/Migration_guide.md b/lib/NimBLE-Arduino/docs/Migration_guide.md new file mode 100644 index 0000000..62406c6 --- /dev/null +++ b/lib/NimBLE-Arduino/docs/Migration_guide.md @@ -0,0 +1,399 @@ +# Migrating from Bluedroid to NimBLE + +This guide describes the required changes to existing projects migrating from the original bluedroid API to NimBLE. + +**The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications. + +For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) and [Improvements and updates](Improvements_and_updates.md) + +* [General Changes](#general-information) +* [Server](#server-api) + * [Services](#services) + * [characteristics](#characteristics) + * [descriptors](#descriptors) + * [Security](#server-security) +* [Advertising](#advertising-api) +* [Client](#client-api) + * [Remote Services](#remote-services) + * [Remote characteristics](#remote-characteristics) + * [Security](#client-security) +* [General Security](#security-api) +* [Configuration](#arduino-configuration) +
+ + +## General Information + +### Header Files +All classes are accessible by including `NimBLEDevice.h` in your application, no further headers need to be included. + +(Mainly for Arduino) You may choose to include `NimBLELog.h` in your appplication if you want to use the `NIMBLE_LOGx` macros for debugging. +These macros are used the same way as the `ESP_LOGx` macros. +
+ +### Class Names +Class names remain the same as the original with the addition of a "Nim" prefix. +For example `BLEDevice` is now `NimBLEDevice` and `BLEServer` is now `NimBLEServer` etc. + +For convienience definitions have been added to allow applications to use either name for all classes +this means **no class names need to be changed in existing code** and makes migrating easier. +
+ +### BLE Addresses +`BLEAddress` (`NimBLEAddress`) When constructing an address the constructor now takes an *(optional)* `uint8_t type` paramameter +to specify the address type. Default is (0) Public static address. + +For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address object with an address type of: 1 (Random). + +As this paramameter is optional no changes to existing code are needed, it is mentioned here for information. +
+`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. +In this library the address bytes are stored in reverse order from the original library. This is due to the way +the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are +not affected as the endian change is made within them. +
+ + +## Server API +Creating a `BLEServer` instance is the same as original, no changes required. +For example `BLEDevice::createServer()` will work just as it did before. + +`BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations. +**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable. +
+ + +### Services +Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required. +For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before. + + +### Characteristics +`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed. + +When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`. + +#### Originally +> BLECharacteristic::PROPERTY_READ | +> BLECharacteristic::PROPERTY_WRITE + +#### Is Now +> NIMBLE_PROPERTY::READ | +> NIMBLE_PROPERTY::WRITE +
+ +#### The full list of properties +> NIMBLE_PROPERTY::READ +> NIMBLE_PROPERTY::READ_ENC +> NIMBLE_PROPERTY::READ_AUTHEN +> NIMBLE_PROPERTY::READ_AUTHOR +> NIMBLE_PROPERTY::WRITE +> NIMBLE_PROPERTY::WRITE_NR +> NIMBLE_PROPERTY::WRITE_ENC +> NIMBLE_PROPERTY::WRITE_AUTHEN +> NIMBLE_PROPERTY::WRITE_AUTHOR +> NIMBLE_PROPERTY::BROADCAST +> NIMBLE_PROPERTY::NOTIFY +> NIMBLE_PROPERTY::INDICATE +
+ +**Example:** +``` +BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + +``` +Needs to be changed to: +``` +BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); +``` +
+ +`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` +which is called when a client subscribes to notifications/indications. + +**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable. +
+ +> BLECharacteristic::getData + +**Has been removed from the API.** +Originally this returned a `uint8_t*` to the internal data, which is volatile. +To prevent possibly throwing exceptions this has been removed and `NimBLECharacteristic::getValue` should be used +to get a copy of the data first which can then safely be accessed via pointer. + +**Example:** +``` +std::string value = pCharacteristic->getValue(); +uint8_t *pData = (uint8_t*)value.data(); +``` +Alternatively use the `getValue` template: +``` +my_struct_t myStruct = pChr->getValue(); +``` +
+ + +### Descriptors +The previous method `BLECharacteristic::addDescriptor()` has been removed. + +Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method. + +BLE2902 or NimBLE2902 class has been removed. +NimBLE automatically creates the 0x2902 descriptor if a characteristic has a notification or indication property assigned to it. + +It was no longer useful to have a class for the 0x2902 descriptor as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added +to handle callback functionality and the client subscription status is handled internally. + +**Note:** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error, +allowing the creation of it would cause a fault in the NimBLE stack. + +All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below). +Which are defined as: +``` +NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); + +NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); +``` +##### Example +``` +pDescriptor = pCharacteristic->createDescriptor("ABCD", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::WRITE_ENC, + 25); +``` +Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes. +
+ +For the 0x2904, there is a special class that is created when you call `createDescriptor("2904"). + +The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to +`NimBLE2904` to access the specific class methods. + +##### Example +``` +p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904"); +``` +
+ + +### Server Security +Security is set on the characteristic or descriptor properties by applying one of the following: +> NIMBLE_PROPERTY::READ_ENC +> NIMBLE_PROPERTY::READ_AUTHEN +> NIMBLE_PROPERTY::READ_AUTHOR +> NIMBLE_PROPERTY::WRITE_ENC +> NIMBLE_PROPERTY::WRITE_AUTHEN +> NIMBLE_PROPERTY::WRITE_AUTHOR + +When a peer wants to read or write a characteristic or descriptor with any of these properties applied +it will trigger the pairing process. By default the "just-works" pairing will be performed automatically. +This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details. +
+ + +## Advertising API +Advertising works the same as the original API except: +> BLEAdvertising::setMinPreferred +> BLEAdvertising::setMaxPreferred + +These methods were found to not provide useful functionality and consumed valuable advertising space (6 bytes of 31) if used unknowingly. +If you wish to advertise these parameters you can still do so manually via `BLEAdvertisementData::addData` (`NimBLEAdvertisementData::addData`). +
+ +Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or +`NimBLEAdvertising::setAppearance` or similar methods. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead. + +~~Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect. +When using custom scan response data you must also use custom advertisement data.~~ +No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data. +
+ +> BLEAdvertising::start (NimBLEAdvertising::start) + +Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback +that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). + +This provides an opportunity to update the advertisment data if desired. +
+ + +## Client API + +Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`). + +Multiple client instances can be created, up to the maximum number of connections set in the config file (default: 3). +To delete a client instance you must use `NimBLEDevice::deleteClient`. + +`BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered. +Defined as: +> NimBLEClient::connect(bool deleteServices = true); +> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true); +> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true); + +The type parameter has been removed and a new bool parameter has been added to indicate if the client should +delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true. +If set to false the client will use the attribute database it retrieved from the peripheral when previously connected. +This allows for faster connections and power saving if the devices dropped connection and are reconnecting. +
+ +> `BLEClient::getServices` (`NimBLEClient::getServices`) + +This method now takes an optional (bool) parameter to indicate if the services should be retrieved from the server (true) or +the currently known database returned (false : default). +Also now returns a pointer to `std::vector` instead of `std::map`. +
+ +**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data +the user may not be interested in. + +**Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes +to replace the the removed automatic functionality. +
+ + +### Remote Services +`BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of: + +> BLERemoteService::getCharacteristicsByHandle + +This method has been removed. +
+ +> `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`) + +This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or +the currently known database returned (false : default). +Also now returns a pointer to `std::vector` instead of `std::map`. +
+ + +### Remote Characteristics +`BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) There have been a few changes to the methods in this class: + +> `BLERemoteCharacteristic::writeValue` (`NimBLERemoteCharacteristic::writeValue`) +> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`) + +Now return true or false to indicate success or failure so you can choose to disconnect or try again. +
+ +> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`) + +Is now **deprecated**. +> `NimBLERemoteCharacteristic::subscribe` +> `NimBLERemoteCharacteristic::unsubscribe` + +Are the new methods added to replace it. +
+ +> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`) +> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`) +> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`) +> `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`) + +Are **deprecated** a template: NimBLERemoteCharacteristic::readValue(time_t\*, bool) has been added to replace them. +
+ +> `BLERemoteCharacteristic::readRawData` + +**Has been removed from the API** +Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data. +The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`. +To obatain a copy of the data, then cast the returned std::string to the type required such as: +``` +std::string value = pChr->readValue(); +uint8_t *data = (uint8_t*)value.data(); +``` +Alternatively use the `readValue` template: +``` +my_struct_t myStruct = pChr->readValue(); +``` +
+ +> `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`) + +This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or +the currently known database returned (false : default). +Also now returns a pointer to `std::vector` instead of `std::map`. +
+ + +### Client Security +The client will automatically initiate security when the peripheral responds that it's required. +The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below. +
+ + +## Security API +Security operations have been moved to `BLEDevice` (`NimBLEDevice`). + +Also security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes. +However backward compatibility with the original `BLESecurity` (`NimBLESecurity`) class is retained to minimize application code changes. + +The callback methods are: + +> `bool onConfirmPIN(uint32_t pin)` + +Receives the pin when using numeric comparison authentication, `return true;` to accept. +
+ +> `uint32_t onPassKeyRequest()` + +For server callback; return the passkey expected from the client. +For client callback; return the passkey to send to the server. +
+ +> `void onAuthenticationComplete(ble_gap_conn_desc\* desc)` + +Authentication complete, success or failed information is in `desc`. +
+ +Security settings and IO capabilities are now set by the following methods of NimBLEDevice. +> `NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc)` +> `NimBLEDevice::setSecurityAuth(uint8_t auth_req)` + +Sets the authorization mode for this device. +
+ +> `NimBLEDevice::setSecurityIOCap(uint8_t iocap)` + +Sets the Input/Output capabilities of this device. +
+ +> `NimBLEDevice::setSecurityInitKey(uint8_t init_key)` + +If we are the initiator of the security procedure this sets the keys we will distribute. +
+ +> `NimBLEDevice::setSecurityRespKey(uint8_t resp_key)` + +Sets the keys we are willing to accept from the peer during pairing. +
+ + +## Arduino Configuration + +Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration +options that are normally set in menuconfig available in the *src/nimconfig.h* file. + +This allows Arduino users to fully customize the build, such as increasing max connections +or loading the BLE stack into external PSRAM. + +For details on the options, they are fully commented in *nimconfig.h* +
diff --git a/lib/NimBLE-Arduino/docs/New_user_guide.md b/lib/NimBLE-Arduino/docs/New_user_guide.md new file mode 100644 index 0000000..8eb0262 --- /dev/null +++ b/lib/NimBLE-Arduino/docs/New_user_guide.md @@ -0,0 +1,339 @@ +# New User Guide + +**Note:** If you are migrating an existing project from the original Bluedroid library please see the [Migration Guide.](Migration_guide.md) + +If you are a new user this will guide you through a simple server and client application. + +* [Creating a Server](#creating-a-server) +* [Creating a Client](#creating-a-client) +
+ +## Include Files +At the top of your application file add `#include NimBLEDevice.h`, this is the only header required and provides access to all classes. +
+ +## Using the Library +In order to perform any BLE tasks you must first initialize the library, this prepares the NimBLE stack to be ready for commands. + +To do this you must call `NimBLEDevice::initialize("your device name here")`, the parameter passed is a character string containing the name you want to advertise. +If you're not creating a server or do not want to advertise a name, simply pass an empty string for the parameter. + +This can be called any time you wish to use BLE functions and does not need to be called from app_main(IDF) or setup(Arduino) but usually is. +
+ + +## Creating a Server +BLE servers perform 2 tasks, they advertise their existance for clients to find them and they provide services which contain information for the connecting client. + +After initializing the NimBLE stack we create a server by calling `NimBLEDevice::createServer()`, this will create a server instance and return a pointer to it. + +Once we have created the server we need to tell it the services it hosts. +To do this we call `NimBLEServer::createService(const char* uuid)`. Which returns a pointer to an instance of `NimBLEService`. +The `uuid` parameter is a hexadecimal string with the uuid we want to give the service, it can be 16, 32, or 128 bits. + +For this example we will keep it simple and use a 16 bit value: ABCD. +
+ +**Example code:** +``` +#include "NimBLEDevice.h" + +// void setup() in Arduino +void app_main(void) +{ + NimBLEDevice::initialize("NimBLE"); + + NimBLEServer *pServer = NimBLEDevice::createServer(); + NimBLEService *pService = pServer->createService("ABCD"); +} +``` + +Now we have NimBLE initialized, a server created and a service assigned to it. +We can't do much with this yet so now we should add a characteristic to the service to provide some data. + +Next we call `NimBLEService::createCharacteristic` which returns a pointer to an instance of `NimBLECharacteristic`, and takes two parameters: +A `uuid` to specify the UUID of the characteristic and a bitmask of the properties we want applied to it. + +Just as with the service UUID we will use a simple 16 bit value: 1234. +The properties bitmask is a little more involved. It is a combination of NIMBLE_PROPERTY:: values. + +Here is the list of options: +> NIMBLE_PROPERTY\::READ +> NIMBLE_PROPERTY\::READ_ENC +> NIMBLE_PROPERTY\::READ_AUTHEN +> NIMBLE_PROPERTY\::READ_AUTHOR +> NIMBLE_PROPERTY\::WRITE +> NIMBLE_PROPERTY\::WRITE_NR +> NIMBLE_PROPERTY\::WRITE_ENC +> NIMBLE_PROPERTY\::WRITE_AUTHEN +> NIMBLE_PROPERTY\::WRITE_AUTHOR +> NIMBLE_PROPERTY\::BROADCAST +> NIMBLE_PROPERTY\::NOTIFY +> NIMBLE_PROPERTY\::INDICATE + +For this example we won't need to specify these as the default value is `NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE` +which will allow reading and writing values to the characteristic without encryption or security. +The function call will simply be `pService->createCharacteristic("1234");` +
+ +**Our example code now is:** +``` +#include "NimBLEDevice.h" + +// void setup() in Arduino +void app_main(void) +{ + NimBLEDevice::initialize("NimBLE"); + + NimBLEServer *pServer = NimBLEDevice::createServer(); + NimBLEService *pService = pServer->createService("ABCD"); + NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234"); +} +``` + +All that's left to do now is start the sevice, give the characteristic a value and start advertising for clients. + +Fist we start the service by calling `NimBLEService::start()`. + +Next we need to call `NimBLECharacteristic::setValue` to set the characteristic value that the client will read. +There are many different types you can send as parameters for the value but for this example we will use a simple string. +`pCharacteristic->setValue("Hello BLE");` + +Next we need to advertise for connections. +To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng. + +**The code for this will be:** +``` +NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance +pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service +pAdvertising->start(); // start advertising +``` +That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections. + +**The full example code:** +``` +#include "NimBLEDevice.h" + +// void setup() in Arduino +void app_main(void) +{ + NimBLEDevice::initialize("NimBLE"); + + NimBLEServer *pServer = NimBLEDevice::createServer(); + NimBLEService *pService = pServer->createService("ABCD"); + NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234"); + + pService->start(); + pCharacteristic->setValue("Hello BLE"); + + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->addServiceUUID("ABCD"); + pAdvertising->start(); +} +``` + +Now if you scan with your phone using nRFConnect or any other BLE app you should see a device named "NimBLE" with a service of "ABCD". + +For more advanced features and options please see the server examples in the examples folder. +
+ + +## Creating a Client + +BLE clients perform 2 tasks, they scan for advertising servers and form connections to them to read and write to their characteristics/descriptors. + +After initializing the NimBLE stack we create a scan instance by calling `NimBLEDevice::getScan()`, this will create a `NimBLEScan` instance and return a pointer to it. + +Once we have created the scan we can start looking for advertising servers. + +To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of seconds to scan for, +passing 0 will scan forever. + +In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available). +This call returns an instance of `NimBLEScanResults` when the scan completes which can be parsed for advertisers we are interested in. + +**Example Code:** +``` +#include "NimBLEDevice.h" + +// void setup() in Arduino +void app_main(void) +{ + NimBLEDevice::initialize(""); + + NimBLEScan *pScan = NimBLEDevice::getScan(); + NimBLEScanResults results = pScan->start(10); +} +``` +
+ +Now that we have scanned we need to check the results for any advertisers we are interested in connecting to. + +To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`. +Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from. + +We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`. +This takes an instance of `NimBLEUUID` as a parameter so we will need to create one. + +**The code for this looks like:** +``` +NimBLEUUID serviceUuid("ABCD"); + +for(int i = 0; i < results.getCount(); i++) { + NimBLEAdvertisedDevice device = results.getDevice(i); + + if (device.isAdvertisingService(serviceUuid)) { + // create a client and connect + } +} +``` +
+ +Now that we can scan and parse advertisers we need to be able to create a `NimBLEClient` instance and use it to connect. + +To do this we call `NimBLEDevice::createClient` which creates the `NimBLEClient` instance and returns a pointer to it. + +After this we call `NimBLEClient::connect` to connect to the advertiser. +This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if successful. + +**Lets do that now:** +``` +NimBLEUUID serviceUuid("ABCD"); + +for(int i = 0; i < results.getCount(); i++) { + NimBLEAdvertisedDevice device = results.getDevice(i); + + if (device.isAdvertisingService(serviceUuid)) { + NimBLEClient *pClient = NimBLEDevice::createClient(); + + if(pClient->connect(&device)) { + //success + } else { + // failed to connect + } + } +} +``` +As shown, the call to `NimBLEClient::connect` should have it's eturn value tested to make sure it succeeded before proceeding to get data. +
+ +Next we need to access the servers data by asking it for the service and the characteristic we are interested in, then read the characteristic value. + +To do this we call `NimBLEClient::getService`, which takes as a parameter the UUID of the service and returns +a pointer an instance to `NimBLERemoteService` or `nullptr` if the service was not found. + +Next we will call `NimBLERemoteService::getCharateristic` which takes as a parameter the UUID of the service and returns +a pointer to an instance of `NimBLERemoteCharacteristic` or `nullptr` if not found. + +Finally we will read the characteristic value with `NimBLERemoteCharacteristic::readValue()`. + +**Here is what that looks like:** +``` +NimBLEUUID serviceUuid("ABCD"); + +for(int i = 0; i < results.getCount(); i++) { + NimBLEAdvertisedDevice device = results.getDevice(i); + + if (device.isAdvertisingService(serviceUuid)) { + NimBLEClient *pClient = NimBLEDevice::createClient(); + + if (pClient->connect(&device)) { + NimBLERemoteService *pService = pClient->getService(serviceUuid); + + if (pService != nullptr) { + NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234"); + + if (pCharacteristic != nullptr) { + std::string value = pCharacteristic->readValue(); + // print or do whatever you need with the value + } + } + } else { + // failed to connect + } + } +} +``` +
+ +The last thing we should do is clean up once we are done with the connection. +Because multiple clients are supported and can be created we should delete them when finished with them to conserve resources. +This is done by calling `NimBLEDevice::deleteClient`. + +**Lets add that now:** +``` +NimBLEUUID serviceUuid("ABCD"); + +for(int i = 0; i < results.getCount(); i++) { + NimBLEAdvertisedDevice device = results.getDevice(i); + + if (device.isAdvertisingService(serviceUuid)) { + NimBLEClient *pClient = NimBLEDevice::createClient(); + + if (pClient->connect(&device)) { + NimBLERemoteService *pService = pClient->getService(serviceUuid); + + if (pService != nullptr) { + NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234"); + + if (pCharacteristic != nullptr) { + std::string value = pCharacteristic->readValue(); + // print or do whatever you need with the value + } + } + } else { + // failed to connect + } + + NimBLEDevice::deleteClient(pClient); + } +} +``` +Note that there is no need to disconnect as that will be done when deleting the client instance. +
+ +**Here is the full example code:** +``` +#include "NimBLEDevice.h" + +// void setup() in Arduino +void app_main(void) +{ + NimBLEDevice::initialize(""); + + NimBLEScan *pScan = NimBLEDevice::getScan(); + NimBLEScanResults results = pScan->start(10); + + NimBLEUUID serviceUuid("ABCD"); + + for(int i = 0; i < results.getCount(); i++) { + NimBLEAdvertisedDevice device = results.getDevice(i); + + if (device.isAdvertisingService(serviceUuid)) { + NimBLEClient *pClient = NimBLEDevice::createClient(); + + if (pClient->connect(&device)) { + NimBLERemoteService *pService = pClient->getService(serviceUuid); + + if (pService != nullptr) { + NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234"); + + if (pCharacteristic != nullptr) { + std::string value = pCharacteristic->readValue(); + // print or do whatever you need with the value + } + } + } else { + // failed to connect + } + + NimBLEDevice::deleteClient(pClient); + } + } +} +``` +
+ +For more advanced features and options please see the client examples in the examples folder. +
+ diff --git a/lib/NimBLE-Arduino/docs/Usage_tips.md b/lib/NimBLE-Arduino/docs/Usage_tips.md new file mode 100644 index 0000000..b8edde2 --- /dev/null +++ b/lib/NimBLE-Arduino/docs/Usage_tips.md @@ -0,0 +1,41 @@ +# Usage Tips + +## Put BLE functions in a task running on the NimBLE stack core + +When commands are sent to the stack from a differnt core they can experience delays in execution. +This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead. +Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core. +
+ +## Do not delete client instances unless necessary or unused + +When a client instance has been created and has connected to a peer device and it has retrieved service/characteristic information it will store that data for the life of the client instance. +If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again. +This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance. + +Client instances in this library use approximately 20% of the original bluedroid library, deleteing them will provide much less gain than it did before. + +It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes. +
+ +## Only retrieve the services and characteriscs needed + +As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known. +Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application. +This reduces energy consumed, heap allocated, connection time and improves overall efficiency. +
+ +## Check return values + +Many user issues can be avoided by checking if a function returned successfully, by either testing for true/false such as when calling `NimBLEClient::connect`, +or nullptr such as when calling `NimBLEClient::getService`. The latter being a must, as calling a method on a nullptr will surely result in a crash. +Most of the functions in this library return something that should be checked before proceeding. +
+ +## There will be bugs - please report them + +No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable. +Best efforts will be made to correct any errors ASAP. + +Bug reports can be made at https://github.com/h2zero/NimBLE-Arduino/issues or https://github.com/h2zero/esp-nimble-cpp/issues. +Questions and suggestions will be happily accepted there as well. diff --git a/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino new file mode 100644 index 0000000..4161aab --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -0,0 +1,153 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** + #include + #include + #include + #include + #include "BLEEddystoneURL.h" + #include "BLEEddystoneTLM.h" + #include "BLEBeacon.h" +***********************/ + +#include + +#include +#include +#include "NimBLEEddystoneURL.h" +#include "NimBLEEddystoneTLM.h" +#include "NimBLEBeacon.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) + +int scanTime = 5; //In seconds +BLEScan *pBLEScan; + +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks +{ + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + if (advertisedDevice->haveName()) + { + Serial.print("Device name: "); + Serial.println(advertisedDevice->getName().c_str()); + Serial.println(""); + } + + if (advertisedDevice->haveServiceUUID()) + { + BLEUUID devUUID = advertisedDevice->getServiceUUID(); + Serial.print("Found ServiceUUID: "); + Serial.println(devUUID.toString().c_str()); + Serial.println(""); + } + else + { + if (advertisedDevice->haveManufacturerData() == true) + { + std::string strManufacturerData = advertisedDevice->getManufacturerData(); + + uint8_t cManufacturerData[100]; + strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); + + if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) + { + Serial.println("Found an iBeacon!"); + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(strManufacturerData); + Serial.printf("iBeacon Frame\n"); + Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()); + } + else + { + Serial.println("Found another manufacturers beacon!"); + Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); + for (int i = 0; i < strManufacturerData.length(); i++) + { + Serial.printf("[%X]", cManufacturerData[i]); + } + Serial.printf("\n"); + } + } + return; + } + + BLEUUID eddyUUID = (uint16_t)0xfeaa; + + if (advertisedDevice->getServiceUUID().equals(eddyUUID)) + { + std::string serviceData = advertisedDevice->getServiceData(eddyUUID); + if (serviceData[0] == 0x10) + { + Serial.println("Found an EddystoneURL beacon!"); + BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); + + foundEddyURL.setData(serviceData); + std::string bareURL = foundEddyURL.getURL(); + if (bareURL[0] == 0x00) + { + Serial.println("DATA-->"); + for (int idx = 0; idx < serviceData.length(); idx++) + { + Serial.printf("0x%08X ", serviceData[idx]); + } + Serial.println("\nInvalid Data"); + return; + } + + Serial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str()); + Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str()); + Serial.printf("TX power %d\n", foundEddyURL.getPower()); + Serial.println("\n"); + } + else if (serviceData[0] == 0x20) + { + Serial.println("Found an EddystoneTLM beacon!"); + BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); + foundEddyURL.setData(serviceData); + + Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); + Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); + int temp = (int)serviceData[5] + (int)(serviceData[4] << 8); + float calcTemp = temp / 256.0f; + Serial.printf("Reported temperature from data: %.2fC\n", calcTemp); + Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); + Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime()); + Serial.println("\n"); + Serial.print(foundEddyURL.toString().c_str()); + Serial.println("\n"); + } + } + } +}; + +void setup() +{ + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() +{ + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} diff --git a/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md b/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md new file mode 100644 index 0000000..558c3e7 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md @@ -0,0 +1,9 @@ +## BLE Beacon Scanner + +Initiates a BLE device scan. +Checks if the discovered devices are +- an iBeacon +- an Eddystone TLM beacon +- an Eddystone URL beacon + +and sends the decoded beacon information over Serial log \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino new file mode 100644 index 0000000..32e0b1e --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino @@ -0,0 +1,113 @@ +/* + EddystoneTLM beacon for NimBLE by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino + EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "NimBLEAdvertising.h" +#include "NimBLEEddystoneURL.h" + +#include "sys/time.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +BLEAdvertising *pAdvertising; +struct timeval nowTimeStruct; + +time_t lastTenth; + +// Check +// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm +// for the temperature value. It is a 8.8 fixed-point notation +void setBeacon() +{ + char beacon_data[25]; + uint16_t beconUUID = 0xFEAA; + uint16_t volt = random(2800, 3700); // 3300mV = 3.3V + float tempFloat = random(2000, 3100) / 100.0f; + Serial.printf("Random temperature is %.2fC\n", tempFloat); + int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00); + Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF)); + + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oScanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + oScanResponseData.setCompleteServices(BLEUUID(beconUUID)); + + beacon_data[0] = 0x20; // Eddystone Frame Type (Unencrypted Eddystone-TLM) + beacon_data[1] = 0x00; // TLM version + beacon_data[2] = (volt >> 8); // Battery voltage, 1 mV/bit i.e. 0xCE4 = 3300mV = 3.3V + beacon_data[3] = (volt & 0xFF); // + beacon_data[4] = (temp >> 8); // Beacon temperature + beacon_data[5] = (temp & 0xFF); // + beacon_data[6] = ((bootcount & 0xFF000000) >> 24); // Advertising PDU count + beacon_data[7] = ((bootcount & 0xFF0000) >> 16); // + beacon_data[8] = ((bootcount & 0xFF00) >> 8); // + beacon_data[9] = (bootcount & 0xFF); // + beacon_data[10] = ((lastTenth & 0xFF000000) >> 24); // Time since power-on or reboot as 0.1 second resolution counter + beacon_data[11] = ((lastTenth & 0xFF0000) >> 16); // + beacon_data[12] = ((lastTenth & 0xFF00) >> 8); // + beacon_data[13] = (lastTenth & 0xFF); // + + oScanResponseData.setServiceData(BLEUUID(beconUUID), std::string(beacon_data, 14)); + oAdvertisementData.setName("TLMBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&nowTimeStruct, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last); + + last = nowTimeStruct.tv_sec; + lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter + + // Create the BLE Device + BLEDevice::init("TLMBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started for 10s ..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep for 10s\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md b/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md new file mode 100644 index 0000000..2e34029 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone TLM beacon +EddystoneTLM beacon by BeeGee based on +[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino) + +[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md) + + Create a BLE server that will send periodic Eddystone TLM frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino b/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino new file mode 100644 index 0000000..07879b2 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino @@ -0,0 +1,185 @@ +/* + EddystoneURL beacon for NimBLE by BeeGee + EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md + +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "NimBLEEddystoneURL.h" + +#include "sys/time.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +BLEAdvertising *pAdvertising; +struct timeval now; + +static const char *eddystone_url_prefix_subs[] = { + "http://www.", + "https://www.", + "http://", + "https://", + "urn:uuid:", + NULL +}; + +static const char *eddystone_url_suffix_subs[] = { + ".com/", + ".org/", + ".edu/", + ".net/", + ".info/", + ".biz/", + ".gov/", + ".com", + ".org", + ".edu", + ".net", + ".info", + ".biz", + ".gov", + NULL +}; + +static int string_begin_with(const char *str, const char *prefix) +{ + int prefix_len = strlen(prefix); + if (strncmp(prefix, str, prefix_len) == 0) + { + return prefix_len; + } + return 0; +} + +void setBeacon() +{ + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + const char url[] = "https://d.giesecke.tk"; + + int scheme_len, ext_len = 1, i, idx, url_idx; + char *ret_data; + int url_len = strlen(url); + + ret_data = (char *)calloc(1, url_len + 13); + + ret_data[0] = 2; // Len + ret_data[1] = 0x01; // Type Flags + ret_data[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + ret_data[3] = 3; // Len + ret_data[4] = 0x03; // Type 16-Bit UUID + ret_data[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[6] = 0xFE; // Eddystone UUID 1 MSB + ret_data[7] = 19; // Length of Beacon Data + ret_data[8] = 0x16; // Type Service Data + ret_data[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[10] = 0xFE; // Eddystone UUID 1 MSB + ret_data[11] = 0x10; // Eddystone Frame Type + ret_data[12] = 0xF4; // Beacons TX power at 0m + + i = 0, idx = 13, url_idx = 0; + + //replace prefix + scheme_len = 0; + while (eddystone_url_prefix_subs[i] != NULL) + { + if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0) + { + ret_data[idx] = i; + idx++; + url_idx += scheme_len; + break; + } + i++; + } + while (url_idx < url_len) + { + i = 0; + ret_data[idx] = url[url_idx]; + ext_len = 1; + while (eddystone_url_suffix_subs[i] != NULL) + { + if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0) + { + ret_data[idx] = i; + break; + } + else + { + ext_len = 1; //inc 1 + } + i++; + } + url_idx += ext_len; + idx++; + } + ret_data[7] = idx - 8; + + Serial.printf("struct size %d url size %d reported len %d\n", + url_len + 13, + url_len, ret_data[7]); + + Serial.printf("URL in data %s\n", &ret_data[13]); + + std::string eddyStoneData(ret_data); + + oAdvertisementData.addData(eddyStoneData); + oScanResponseData.setName("MeBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init("MeBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md b/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md new file mode 100644 index 0000000..2baf1cc --- /dev/null +++ b/lib/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone URL beacon +EddystoneURL beacon by BeeGee based on +[pcbreflux ESP32 Eddystone URL deepsleep](https://github.com/pcbreflux/espressif/tree/master/esp32/arduino/sketchbook/ESP32_Eddystone_URL_deepsleep) + +[EddystoneURL frame specification](https://github.com/google/eddystone/blob/master/eddystone-url/README.md) + + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino new file mode 100644 index 0000000..a3899cd --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino @@ -0,0 +1,392 @@ + +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE client library. + * + * Created: on March 24 2020 + * Author: H2zero + * +*/ + +#include + +void scanEndedCB(NimBLEScanResults results); + +static NimBLEAdvertisedDevice* advDevice; + +static bool doConnect = false; +static uint32_t scanTime = 0; /** 0 = scan forever */ + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class ClientCallbacks : public NimBLEClientCallbacks { + void onConnect(NimBLEClient* pClient) { + Serial.println("Connected"); + /** After connection we should change the parameters if we don't need fast response times. + * These settings are 150ms interval, 0 latency, 450ms timout. + * Timeout should be a multiple of the interval, minimum is 100ms. + * I find a multiple of 3-5 * the interval works best for quick response/reconnect. + * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout + */ + pClient->updateConnParams(120,120,0,60); + }; + + void onDisconnect(NimBLEClient* pClient) { + Serial.print(pClient->getPeerAddress().toString().c_str()); + Serial.println(" Disconnected - Starting scan"); + NimBLEDevice::getScan()->start(scanTime, scanEndedCB); + }; + + /** Called when the peripheral requests a change to the connection parameters. + * Return true to accept and apply them or false to reject and keep + * the currently used parameters. Default will return true. + */ + bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { + if(params->itvl_min < 24) { /** 1.25ms units */ + return false; + } else if(params->itvl_max > 40) { /** 1.25ms units */ + return false; + } else if(params->latency > 2) { /** Number of intervals allowed to skip */ + return false; + } else if(params->supervision_timeout > 100) { /** 10ms units */ + return false; + } + + return true; + }; + + /********************* Security handled here ********************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client Passkey Request"); + /** return the passkey to send to the server */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: "); + Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + /** Pairing process complete, we can check the results in ble_gap_conn_desc */ + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + if(!desc->sec_state.encrypted) { + Serial.println("Encrypt connection failed - disconnecting"); + /** Find the client with the connection handle provided in desc */ + NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); + return; + } + }; +}; + + +/** Define a class to handle the callbacks when advertisments are received */ +class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { + + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + Serial.print("Advertised Device found: "); + Serial.println(advertisedDevice->toString().c_str()); + if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) + { + Serial.println("Found Our Service"); + /** stop scan before connecting */ + NimBLEDevice::getScan()->stop(); + /** Save the device reference in a global for the client to use*/ + advDevice = advertisedDevice; + /** Ready to connect now */ + doConnect = true; + } + }; +}; + + +/** Notification / Indication receiving handler callback */ +void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ + std::string str = (isNotify == true) ? "Notification" : "Indication"; + str += " from "; + /** NimBLEAddress and NimBLEUUID have std::string operators */ + str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress()); + str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID()); + str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID()); + str += ", Value = " + std::string((char*)pData, length); + Serial.println(str.c_str()); +} + +/** Callback to process the results of the last scan or restart it */ +void scanEndedCB(NimBLEScanResults results){ + Serial.println("Scan Ended"); +} + + +/** Create a single global instance of the callback class to be used by all clients */ +static ClientCallbacks clientCB; + + +/** Handles the provisioning of clients and connects / interfaces with the server */ +bool connectToServer() { + NimBLEClient* pClient = nullptr; + + /** Check if we have a client we should reuse first **/ + if(NimBLEDevice::getClientListSize()) { + /** Special case when we already know this device, we send false as the + * second argument in connect() to prevent refreshing the service database. + * This saves considerable time and power. + */ + pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress()); + if(pClient){ + if(!pClient->connect(advDevice, false)) { + Serial.println("Reconnect failed"); + return false; + } + Serial.println("Reconnected client"); + } + /** We don't already have a client that knows this device, + * we will check for a client that is disconnected that we can use. + */ + else { + pClient = NimBLEDevice::getDisconnectedClient(); + } + } + + /** No client to reuse? Create a new one. */ + if(!pClient) { + if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { + Serial.println("Max clients reached - no more connections available"); + return false; + } + + pClient = NimBLEDevice::createClient(); + + Serial.println("New client created"); + + pClient->setClientCallbacks(&clientCB, false); + /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout. + * These settings are safe for 3 clients to connect reliably, can go faster if you have less + * connections. Timeout should be a multiple of the interval, minimum is 100ms. + * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout + */ + pClient->setConnectionParams(12,12,0,51); + /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ + pClient->setConnectTimeout(5); + + + if (!pClient->connect(advDevice)) { + /** Created a client but failed to connect, don't need to keep it as it has no data */ + NimBLEDevice::deleteClient(pClient); + Serial.println("Failed to connect, deleted client"); + return false; + } + } + + if(!pClient->isConnected()) { + if (!pClient->connect(advDevice)) { + Serial.println("Failed to connect"); + return false; + } + } + + Serial.print("Connected to: "); + Serial.println(pClient->getPeerAddress().toString().c_str()); + Serial.print("RSSI: "); + Serial.println(pClient->getRssi()); + + /** Now we can read/write/subscribe the charateristics of the services we are interested in */ + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + NimBLERemoteDescriptor* pDsc = nullptr; + + pSvc = pClient->getService("DEAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("BEEF"); + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). + * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. + * Unsubscribe parameter defaults are: response=false. + */ + if(pChr->canNotify()) { + //if(!pChr->registerForNotify(notifyCB)) { + if(!pChr->subscribe(true, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as first argument to subscribe to indications instead of notifications */ + //if(!pChr->registerForNotify(notifyCB, false)) { + if(!pChr->subscribe(false, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + } else { + Serial.println("DEAD service not found."); + } + + pSvc = pClient->getService("BAAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("F00D"); + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc) { /** make sure it's not null */ + Serial.print("Descriptor: "); + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("No tip!")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). + * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. + * Unsubscribe parameter defaults are: response=false. + */ + if(pChr->canNotify()) { + //if(!pChr->registerForNotify(notifyCB)) { + if(!pChr->subscribe(true, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as first argument to subscribe to indications instead of notifications */ + //if(!pChr->registerForNotify(notifyCB, false)) { + if(!pChr->subscribe(false, notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + } else { + Serial.println("BAAD service not found."); + } + + Serial.println("Done with this device!"); + return true; +} + +void setup (){ + Serial.begin(115200); + Serial.println("Starting NimBLE Client"); + /** Initialize NimBLE, no device name spcified as we are not advertising */ + NimBLEDevice::init(""); + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + /** Optional: set the transmit power, default is 3db */ + NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */ + + /** Optional: set any devices you don't want to get advertisments from */ + // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff")); + + /** create new scan */ + NimBLEScan* pScan = NimBLEDevice::getScan(); + + /** create a callback that gets called when advertisers are found */ + pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); + + /** Set scan interval (how often) and window (how long) in milliseconds */ + pScan->setInterval(45); + pScan->setWindow(15); + + /** Active scan will gather scan response data from advertisers + * but will use more energy from both devices + */ + pScan->setActiveScan(true); + /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever + * Optional callback for when scanning stops. + */ + pScan->start(scanTime, scanEndedCB); +} + + +void loop (){ + /** Loop here until we find a device we want to connect to */ + while(!doConnect){ + delay(1); + } + + doConnect = false; + + /** Found a device we want to connect to, do it now */ + if(connectToServer()) { + Serial.println("Success! we should now be getting notifications, scanning for more!"); + } else { + Serial.println("Failed to connect, starting scan"); + } + + NimBLEDevice::getScan()->start(scanTime,scanEndedCB); +} diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino b/lib/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino new file mode 100644 index 0000000..b8d24d2 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino @@ -0,0 +1,71 @@ +/** Example of continuous scanning for BLE advertisements. + * This example will scan forever while consuming as few resources as possible + * and report all advertisments on the serial monitor. + * + * Created: on January 31 2021 + * Author: H2zero + * + */ + +#include "NimBLEDevice.h" + +NimBLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + +/** *Optional* Sets the filtering mode used by the scanner in the BLE controller. + * + * Can be one of: + * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default) + * Filter by device address only, advertisements from the same address will be reported only once. + * + * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1) + * Filter by data only, advertisements with the same data will only be reported once, + * even from different addresses. + * + * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2) + * Filter by address and data, advertisements from the same address will be reported only once, + * except if the data in the advertisement has changed, then it will be reported again. + * + * Can only be used BEFORE calling NimBLEDevice::init. +*/ + NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE); + +/** *Optional* Sets the scan filter cache size in the BLE controller. + * When the number of duplicate advertisements seen by the controller + * reaches this value it will clear the cache and start reporting previously + * seen devices. The larger this number, the longer time between repeated + * device reports. Range 10 - 1000. (default 20) + * + * Can only be used BEFORE calling NimBLEDevice::init. + */ + NimBLEDevice::setScanDuplicateCacheSize(200); + + NimBLEDevice::init(""); + + pBLEScan = NimBLEDevice::getScan(); //create new scan + // Set the callback for when devices are discovered, no duplicates. + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false); + pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser. + pBLEScan->setInterval(97); // How often the scan occurs / switches channels; in milliseconds, + pBLEScan->setWindow(37); // How long to scan during the interval; in milliseconds. + pBLEScan->setMaxResults(0); // do not store the scan results, use callback only. +} + +void loop() { + // If an error occurs that stops the scan, it will be restarted here. + if(pBLEScan->isScanning() == false) { + // Start scan with: duration = 0 seconds(forever), no scan end callback, not a continuation of a previous scan. + pBLEScan->start(0, nullptr, false); + } + + delay(2000); +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Scan_Whitelist/NimBLE_Scan_whitelist.ino b/lib/NimBLE-Arduino/examples/NimBLE_Scan_Whitelist/NimBLE_Scan_whitelist.ino new file mode 100644 index 0000000..cddbcc9 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Scan_Whitelist/NimBLE_Scan_whitelist.ino @@ -0,0 +1,67 @@ +/* + * NimBLE_Scan_Whitelist demo + * + * Created May 16, 2021 + * Author: h2zero + */ + +#include + +int scanTime = 5; //In seconds +NimBLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice* advertisedDevice) { + Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); + /* + * Here we add the device scanned to the whitelist based on service data but any + * advertised data can be used for your preffered data. + */ + if (advertisedDevice->haveServiceData()) { + /* If this is a device with data we want to capture, add it to the whitelist */ + if (advertisedDevice->getServiceData(NimBLEUUID("AABB")) != "") { + Serial.printf("Adding %s to whitelist\n", std::string(advertisedDevice->getAddress()).c_str()); + NimBLEDevice::whiteListAdd(advertisedDevice->getAddress()); + } + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + + NimBLEDevice::init(""); + pBLEScan = NimBLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->setInterval(100); + pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL); + pBLEScan->setWindow(99); +} + +void loop() { + NimBLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + + Serial.println("Whitelist contains:"); + for (auto i=0; i 0) { + if (foundDevices.getCount() == 0) { + pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL); + } else { + pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL); + } + } + pBLEScan->clearResults(); + delay(2000); +} diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino b/lib/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino new file mode 100644 index 0000000..3a35ebe --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino @@ -0,0 +1,91 @@ +/** NimBLE_Secure_Client Demo: + * + * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client. + * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective. + * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash. + * + * Created: on Jan 08 2021 + * Author: mblasee + */ + +#include + +class ClientCallbacks : public NimBLEClientCallbacks +{ + uint32_t onPassKeyRequest() + { + Serial.println("Client Passkey Request"); + /** return the passkey to send to the server */ + /** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */ + return 123456; + }; +}; +static ClientCallbacks clientCB; + +void setup() +{ + Serial.begin(115200); + Serial.println("Starting NimBLE Client"); + + NimBLEDevice::init(""); + NimBLEDevice::setPower(ESP_PWR_LVL_P9); + NimBLEDevice::setSecurityAuth(true, true, true); + NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); + NimBLEScan *pScan = NimBLEDevice::getScan(); + NimBLEScanResults results = pScan->start(5); + + NimBLEUUID serviceUuid("ABCD"); + + for (int i = 0; i < results.getCount(); i++) + { + NimBLEAdvertisedDevice device = results.getDevice(i); + Serial.println(device.getName().c_str()); + + if (device.isAdvertisingService(serviceUuid)) + { + NimBLEClient *pClient = NimBLEDevice::createClient(); + pClient->setClientCallbacks(&clientCB, false); + + if (pClient->connect(&device)) + { + pClient->secureConnection(); + NimBLERemoteService *pService = pClient->getService(serviceUuid); + if (pService != nullptr) + { + NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234"); + + if (pNonSecureCharacteristic != nullptr) + { + // Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys. + std::string value = pNonSecureCharacteristic->readValue(); + // print or do whatever you need with the value + Serial.println(value.c_str()); + } + + NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235"); + + if (pSecureCharacteristic != nullptr) + { + // Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should + // get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261" + // This means you are trying to do something without the proper permissions. + std::string value = pSecureCharacteristic->readValue(); + // print or do whatever you need with the value + Serial.println(value.c_str()); + } + } + } + else + { + // failed to connect + Serial.println("failed to connect"); + } + + NimBLEDevice::deleteClient(pClient); + } + } +} + +void loop() +{ +} diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino b/lib/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino new file mode 100644 index 0000000..4de731e --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino @@ -0,0 +1,37 @@ +/** NimBLE_Secure_Server Demo: + * + * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client. + * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective. + * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash. + * + * Created: on Jan 08 2021 + * Author: mblasee + */ + +#include + +void setup() { + Serial.begin(115200); + Serial.println("Starting NimBLE Server"); + NimBLEDevice::init("NimBLE"); + NimBLEDevice::setPower(ESP_PWR_LVL_P9); + + NimBLEDevice::setSecurityAuth(true, true, true); + NimBLEDevice::setSecurityPasskey(123456); + NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); + NimBLEServer *pServer = NimBLEDevice::createServer(); + NimBLEService *pService = pServer->createService("ABCD"); + NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ ); + NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN); + + pService->start(); + pNonSecureCharacteristic->setValue("Hello Non Secure BLE"); + pSecureCharacteristic->setValue("Hello Secure BLE"); + + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->addServiceUUID("ABCD"); + pAdvertising->start(); +} + +void loop() { +} diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino b/lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino new file mode 100644 index 0000000..7b8c429 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino @@ -0,0 +1,256 @@ + +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE server library. + * + * Created: on March 22 2020 + * Author: H2zero + * +*/ + +#include + +static NimBLEServer* pServer; + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class ServerCallbacks: public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer) { + Serial.println("Client connected"); + Serial.println("Multi-connect support: start advertising"); + NimBLEDevice::startAdvertising(); + }; + /** Alternative onConnect() method to extract details of the connection. + * See: src/ble_gap.h for the details of the ble_gap_conn_desc struct. + */ + void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + Serial.print("Client address: "); + Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); + /** We can use the connection handle here to ask for different connection parameters. + * Args: connection handle, min connection interval, max connection interval + * latency, supervision timeout. + * Units; Min/Max Intervals: 1.25 millisecond increments. + * Latency: number of intervals allowed to skip. + * Timeout: 10 millisecond increments, try for 5x interval time for best results. + */ + pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 60); + }; + void onDisconnect(NimBLEServer* pServer) { + Serial.println("Client disconnected - start advertising"); + NimBLEDevice::startAdvertising(); + }; + void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) { + Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle); + }; + +/********************* Security handled here ********************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server Passkey Request"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!desc->sec_state.encrypted) { + NimBLEDevice::getServer()->disconnect(desc->conn_handle); + Serial.println("Encrypt connection failed - disconnecting client"); + return; + } + Serial.println("Starting BLE work!"); + }; +}; + +/** Handler class for characteristic actions */ +class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { + void onRead(NimBLECharacteristic* pCharacteristic){ + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onRead(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + + void onWrite(NimBLECharacteristic* pCharacteristic) { + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onWrite(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + /** Called before notification or indication is sent, + * the value can be changed here before sending if desired. + */ + void onNotify(NimBLECharacteristic* pCharacteristic) { + Serial.println("Sending notification to clients"); + }; + + + /** The status returned in status is defined in NimBLECharacteristic.h. + * The value returned in code is the NimBLE host return code. + */ + void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) { + String str = ("Notification/Indication status code: "); + str += status; + str += ", return code: "; + str += code; + str += ", "; + str += NimBLEUtils::returnCodeToString(code); + Serial.println(str); + }; + + void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue) { + String str = "Client ID: "; + str += desc->conn_handle; + str += " Address: "; + str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str(); + if(subValue == 0) { + str += " Unsubscribed to "; + }else if(subValue == 1) { + str += " Subscribed to notfications for "; + } else if(subValue == 2) { + str += " Subscribed to indications for "; + } else if(subValue == 3) { + str += " Subscribed to notifications and indications for "; + } + str += std::string(pCharacteristic->getUUID()).c_str(); + + Serial.println(str); + }; +}; + +/** Handler class for descriptor actions */ +class DescriptorCallbacks : public NimBLEDescriptorCallbacks { + void onWrite(NimBLEDescriptor* pDescriptor) { + std::string dscVal = pDescriptor->getValue(); + Serial.print("Descriptor witten value:"); + Serial.println(dscVal.c_str()); + }; + + void onRead(NimBLEDescriptor* pDescriptor) { + Serial.print(pDescriptor->getUUID().toString().c_str()); + Serial.println(" Descriptor read"); + }; +}; + + +/** Define callback instances globally to use for multiple Charateristics \ Descriptors */ +static DescriptorCallbacks dscCallbacks; +static CharacteristicCallbacks chrCallbacks; + + +void setup() { + Serial.begin(115200); + Serial.println("Starting NimBLE Server"); + + /** sets device name */ + NimBLEDevice::init("NimBLE-Arduino"); + + /** Optional: set the transmit power, default is 3db */ + NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */ + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( + "BEEF", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + /** Require a secure connection for read and write access */ + NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted + NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted + ); + + pBeefCharacteristic->setValue("Burger"); + pBeefCharacteristic->setCallbacks(&chrCallbacks); + + /** 2904 descriptors are a special case, when createDescriptor is called with + * 0x2904 a NimBLE2904 class is created with the correct properties and sizes. + * However we must cast the returned reference to the correct type as the method + * only returns a pointer to the base NimBLEDescriptor class. + */ + NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); + pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); + pBeef2904->setCallbacks(&dscCallbacks); + + + NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( + "F00D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY + ); + + pFoodCharacteristic->setValue("Fries"); + pFoodCharacteristic->setCallbacks(&chrCallbacks); + + /** Note a 0x2902 descriptor MUST NOT be created as NimBLE will create one automatically + * if notification or indication properties are assigned to a characteristic. + */ + + /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */ + NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( + "C01D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE| + NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted + 20 + ); + pC01Ddsc->setValue("Send it back!"); + pC01Ddsc->setCallbacks(&dscCallbacks); + + /** Start the services when finished creating all Characteristics and Descriptors */ + pDeadService->start(); + pBaadService->start(); + + NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + /** Add the services to the advertisment data **/ + pAdvertising->addServiceUUID(pDeadService->getUUID()); + pAdvertising->addServiceUUID(pBaadService->getUUID()); + /** If your device is battery powered you may consider setting scan response + * to false as it will extend battery life at the expense of less data sent. + */ + pAdvertising->setScanResponse(true); + pAdvertising->start(); + + Serial.println("Advertising Started"); +} + + +void loop() { + /** Do your thing here, this just spams notifications to all connected clients */ + if(pServer->getConnectedCount()) { + NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); + if(pSvc) { + NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); + if(pChr) { + pChr->notify(true); + } + } + } + + delay(2000); +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Server_Whitelist/NimBLE_Server_Whitelist.ino b/lib/NimBLE-Arduino/examples/NimBLE_Server_Whitelist/NimBLE_Server_Whitelist.ino new file mode 100644 index 0000000..4c7d335 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Server_Whitelist/NimBLE_Server_Whitelist.ino @@ -0,0 +1,105 @@ +/* + * NimBLE_Server_Whitelist demo + * + * Created May 17, 2021 + * Author: h2zero + */ + +#include + +NimBLECharacteristic* pCharacteristic = nullptr; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + // Peer disconnected, add them to the whitelist + // This allows us to use the whitelist to filter connection attempts + // which will minimize reconnection time. + NimBLEDevice::whiteListAdd(NimBLEAddress(desc->peer_ota_addr)); + deviceConnected = false; + } +}; + +void onAdvComplete(NimBLEAdvertising *pAdvertising) { + Serial.println("Advertising stopped"); + if (deviceConnected) { + return; + } + // If advertising timed out without connection start advertising without whitelist filter + pAdvertising->setScanFilter(false,false); + pAdvertising->start(); +} + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + NimBLEDevice::init("ESP32"); + + // Create the BLE Server + NimBLEServer* pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + pServer->advertiseOnDisconnect(false); + + // Create the BLE Service + NimBLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY ); + + // Start the service + pService->start(); + + // Start advertising + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->start(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + if (NimBLEDevice::getWhiteListCount() > 0) { + // Allow anyone to scan but only whitelisted can connect. + pAdvertising->setScanFilter(false,true); + } + // advertise with whitelist for 30 seconds + pAdvertising->start(30, onAdvComplete); + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } + + delay(2000); +} diff --git a/lib/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino b/lib/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino new file mode 100644 index 0000000..dd588fd --- /dev/null +++ b/lib/NimBLE-Arduino/examples/NimBLE_Service_Data_Advertiser/NimBLE_Service_Data_Advertiser.ino @@ -0,0 +1,33 @@ +/** NimBLE_Service_Data_Advertiser Demo: + * + * Simple demo of advertising service data that changes every 5 seconds + * + * Created: on February 7 2021 + * Author: H2zero + * +*/ + +#include + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" + +static NimBLEUUID dataUuid(SERVICE_UUID); +static NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); +static uint32_t count = 0; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + NimBLEDevice::init("svc data"); +} + +void loop() { + pAdvertising->stop(); + pAdvertising->setServiceData(dataUuid, std::string((char*)&count, sizeof(count))); + pAdvertising->start(); + + Serial.printf("Advertising count = %d\n", count); + count++; + delay(5000); +} diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino new file mode 100644 index 0000000..0d4ab94 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino @@ -0,0 +1,194 @@ +/** + * A BLE client example that is rich in capabilities. + * There is a lot new capabilities implemented. + * author unknown + * updated by chegewara + * updated for NimBLE by H2zero + */ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include "BLEDevice.h" +***********************/ +#include "NimBLEDevice.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); + +static boolean doConnect = false; +static boolean connected = false; +static boolean doScan = false; +static BLERemoteCharacteristic* pRemoteCharacteristic; +static BLEAdvertisedDevice* myDevice; + +static void notifyCallback( + BLERemoteCharacteristic* pBLERemoteCharacteristic, + uint8_t* pData, + size_t length, + bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("data: "); + Serial.println((char*)pData); +} + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyClientCallback : public BLEClientCallbacks { + void onConnect(BLEClient* pclient) { + } + + void onDisconnect(BLEClient* pclient) { + connected = false; + Serial.println("onDisconnect"); + } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client PassKeyRequest"); + return 123456; + } + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ +}; + +bool connectToServer() { + Serial.print("Forming a connection to "); + Serial.println(myDevice->getAddress().toString().c_str()); + + BLEClient* pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + pClient->setClientCallbacks(new MyClientCallback()); + + // Connect to the remove BLE Server. + pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) + Serial.println(" - Connected to server"); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService* pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our service"); + + + // Obtain a reference to the characteristic in the service of the remote BLE server. + pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find our characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our characteristic"); + + // Read the value of the characteristic. + if(pRemoteCharacteristic->canRead()) { + std::string value = pRemoteCharacteristic->readValue(); + Serial.print("The characteristic value was: "); + Serial.println(value.c_str()); + } + + if(pRemoteCharacteristic->canNotify()) + pRemoteCharacteristic->registerForNotify(notifyCallback); + + connected = true; + return true; +} + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + +/*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice* advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice->toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. +/******************************************************************************** + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { +********************************************************************************/ + if (advertisedDevice->haveServiceUUID() && advertisedDevice->isAdvertisingService(serviceUUID)) { + + BLEDevice::getScan()->stop(); +/******************************************************************* + myDevice = new BLEAdvertisedDevice(advertisedDevice); +*******************************************************************/ + myDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */ + doConnect = true; + doScan = true; + + } // Found our server + } // onResult +}; // MyAdvertisedDeviceCallbacks + + +void setup() { + Serial.begin(115200); + Serial.println("Starting Arduino BLE Client application..."); + BLEDevice::init(""); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + BLEScan* pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); +} // End of setup. + + +// This is the Arduino main loop function. +void loop() { + + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. Once we are + // connected we set the connected flag to be true. + if (doConnect == true) { + if (connectToServer()) { + Serial.println("We are now connected to the BLE Server."); + } else { + Serial.println("We have failed to connect to the server; there is nothin more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, update the characteristic each time we are reached + // with the current time since boot. + if (connected) { + String newValue = "Time since boot: " + String(millis()/1000); + Serial.println("Setting new characteristic value to \"" + newValue + "\""); + + // Set the characteristic's value to be the array of bytes that is actually a string. + /*** Note: write / read value now returns true if successful, false otherwise - try again or disconnect ***/ + pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + }else if(doScan){ + BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino + } + + delay(1000); // Delay a second between loops. +} // End of loop diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 0000000..86b97de --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,118 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by pcbreflux +*/ + + +/* + Create a BLE server that will send periodic iBeacon frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ + + +/** NimBLE differences highlighted in comment blocks **/ + + +#include "sys/time.h" +/*******original******** +#include "BLEDevice.h" +#include "BLEUtils.h" +#include "BLEBeacon.h" +***********************/ +#include "NimBLEDevice.h" +#include "NimBLEBeacon.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t temprature_sens_read(); +//uint8_t g_phyFuns; + +#ifdef __cplusplus +} +#endif + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +void setBeacon() { + + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); + oBeacon.setMinor(bootcount&0xFFFF); + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 + + std::string strServiceData = ""; + + strServiceData += (char)26; // Len + strServiceData += (char)0xFF; // Type + strServiceData += oBeacon.getData(); + oAdvertisementData.addData(strServiceData); + + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); + /** pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND); + * Advertising mode. Can be one of following constants: + * - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2). + * - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3). + * - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4). + */ + pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON); + +} + +void setup() { + + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n",bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init(""); + + // Create the BLE Server + // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(100); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() { +} diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino new file mode 100644 index 0000000..cb04888 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino @@ -0,0 +1,146 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + /*************************************************** + NOTE: DO NOT create a 2902 descriptor. + it will be created automatically if notifications + or indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ****************************************************/ + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + /** Note, this could be left out as that is the default value */ + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino new file mode 100644 index 0000000..86cdaf4 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino @@ -0,0 +1,49 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ + +#include + +int scanTime = 5; //In seconds +BLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice* advertisedDevice) { + /** Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); **/ + Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() { + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino new file mode 100644 index 0000000..faa4d88 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino @@ -0,0 +1,57 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini + updates by chegewara +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ + +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("Long name works now"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /***** Enum Type NIMBLE_PROPERTY now ***** + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMaxPreferred(0x12); + + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino new file mode 100644 index 0000000..9ae3859 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -0,0 +1,150 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + BLEDevice::startAdvertising(); + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + /*************************************************** + NOTE: DO NOT create a 2902 descriptor + it will be created automatically if notifications + or indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ****************************************************/ + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + /** Note, this could be left out as that is the default value */ + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino new file mode 100644 index 0000000..0a31ef2 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino @@ -0,0 +1,163 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" + Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + In this example rxValue is the data received (only accessible inside that function). + And txValue is the data to be sent, in this example just a byte incremented every second. +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ +#include + +BLEServer *pServer = NULL; +BLECharacteristic * pTxCharacteristic; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t txValue = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID +#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ +}; + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string rxValue = pCharacteristic->getValue(); + + if (rxValue.length() > 0) { + Serial.println("*********"); + Serial.print("Received Value: "); + for (int i = 0; i < rxValue.length(); i++) + Serial.print(rxValue[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("UART Service"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pTxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_TX, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_NOTIFY + ); + **********************************************/ + NIMBLE_PROPERTY::NOTIFY + ); + + /*************************************************** + NOTE: DO NOT create a 2902 descriptor + it will be created automatically if notifications + or indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); + ****************************************************/ + + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + /******* Enum Type NIMBLE_PROPERTY now ******* + BLECharacteristic::PROPERTY_WRITE + ); + *********************************************/ + NIMBLE_PROPERTY::WRITE + ); + + pRxCharacteristic->setCallbacks(new MyCallbacks()); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->start(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + + if (deviceConnected) { + pTxCharacteristic->setValue(&txValue, 1); + pTxCharacteristic->notify(); + txValue++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino new file mode 100644 index 0000000..b1eb0f8 --- /dev/null +++ b/lib/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino @@ -0,0 +1,75 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string value = pCharacteristic->getValue(); + + if (value.length() > 0) { + Serial.println("*********"); + Serial.print("New value: "); + for (int i = 0; i < value.length(); i++) + Serial.print(value[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + +void setup() { + Serial.begin(115200); + + Serial.println("1- Download and install an BLE scanner app in your phone"); + Serial.println("2- Scan for BLE devices in the app"); + Serial.println("3- Connect to MyESP32"); + Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); + Serial.println("5- See the magic =)"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + /***** Enum Type NIMBLE_PROPERTY now ***** + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); + + pCharacteristic->setCallbacks(new MyCallbacks()); + + pCharacteristic->setValue("Hello World"); + pService->start(); + + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/lib/NimBLE-Arduino/library.properties b/lib/NimBLE-Arduino/library.properties new file mode 100644 index 0000000..38f044c --- /dev/null +++ b/lib/NimBLE-Arduino/library.properties @@ -0,0 +1,10 @@ +name=NimBLE-Arduino +version=1.3.6 +author=h2zero +maintainer=h2zero +sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE. +paragraph=This is a more updated and lower resource alternative to the original bluedroid BLE library for esp32. Uses 50% less flash space and approximately 100KB less ram with the same functionality. Nearly 100% compatible with existing application code, migration guide included. +url=https://github.com/h2zero/NimBLE-Arduino +category=Communication +architectures=esp32 +includes=NimBLEDevice.h \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/HIDKeyboardTypes.h b/lib/NimBLE-Arduino/src/HIDKeyboardTypes.h new file mode 100644 index 0000000..531437e --- /dev/null +++ b/lib/NimBLE-Arduino/src/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/HIDTypes.h b/lib/NimBLE-Arduino/src/HIDTypes.h new file mode 100644 index 0000000..8ee31da --- /dev/null +++ b/lib/NimBLE-Arduino/src/HIDTypes.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLE2904.cpp b/lib/NimBLE-Arduino/src/NimBLE2904.cpp new file mode 100644 index 0000000..282eff5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLE2904.cpp @@ -0,0 +1,86 @@ +/* + * NimBLE2904.cpp + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.cpp + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLE2904.h" + + +NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic) +: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), + BLE_GATT_CHR_F_READ, + sizeof(BLE2904_Data), + pCharacterisitic) +{ + m_data.m_format = 0; + m_data.m_exponent = 0; + m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers + m_data.m_unit = 0; + m_data.m_description = 0; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // BLE2904 + + +/** + * @brief Set the description. + */ +void NimBLE2904::setDescription(uint16_t description) { + m_data.m_description = description; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} + + +/** + * @brief Set the exponent. + */ +void NimBLE2904::setExponent(int8_t exponent) { + m_data.m_exponent = exponent; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setExponent + + +/** + * @brief Set the format. + */ +void NimBLE2904::setFormat(uint8_t format) { + m_data.m_format = format; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setFormat + + +/** + * @brief Set the namespace. + */ +void NimBLE2904::setNamespace(uint8_t namespace_value) { + m_data.m_namespace = namespace_value; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setNamespace + + +/** + * @brief Set the units for this value. It should be one of the encoded values defined here: + * https://www.bluetooth.com/specifications/assigned-numbers/units + * @param [in] unit The type of units of this characteristic as defined by assigned numbers. + */ +void NimBLE2904::setUnit(uint16_t unit) { + m_data.m_unit = unit; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setUnit + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLE2904.h b/lib/NimBLE-Arduino/src/NimBLE2904.h new file mode 100644 index 0000000..29dde51 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLE2904.h @@ -0,0 +1,83 @@ +/* + * NimBLE2904.h + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.h + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE2904_H_ +#define MAIN_NIMBLE2904_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEDescriptor.h" + +struct BLE2904_Data { + uint8_t m_format; + int8_t m_exponent; + uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units + uint8_t m_namespace; + uint16_t m_description; + +} __attribute__((packed)); + + +/** + * @brief Descriptor for Characteristic Presentation Format. + * + * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +class NimBLE2904: public NimBLEDescriptor { +public: + NimBLE2904(NimBLECharacteristic* pCharacterisitic = nullptr); + static const uint8_t FORMAT_BOOLEAN = 1; + static const uint8_t FORMAT_UINT2 = 2; + static const uint8_t FORMAT_UINT4 = 3; + static const uint8_t FORMAT_UINT8 = 4; + static const uint8_t FORMAT_UINT12 = 5; + static const uint8_t FORMAT_UINT16 = 6; + static const uint8_t FORMAT_UINT24 = 7; + static const uint8_t FORMAT_UINT32 = 8; + static const uint8_t FORMAT_UINT48 = 9; + static const uint8_t FORMAT_UINT64 = 10; + static const uint8_t FORMAT_UINT128 = 11; + static const uint8_t FORMAT_SINT8 = 12; + static const uint8_t FORMAT_SINT12 = 13; + static const uint8_t FORMAT_SINT16 = 14; + static const uint8_t FORMAT_SINT24 = 15; + static const uint8_t FORMAT_SINT32 = 16; + static const uint8_t FORMAT_SINT48 = 17; + static const uint8_t FORMAT_SINT64 = 18; + static const uint8_t FORMAT_SINT128 = 19; + static const uint8_t FORMAT_FLOAT32 = 20; + static const uint8_t FORMAT_FLOAT64 = 21; + static const uint8_t FORMAT_SFLOAT16 = 22; + static const uint8_t FORMAT_SFLOAT32 = 23; + static const uint8_t FORMAT_IEEE20601 = 24; + static const uint8_t FORMAT_UTF8 = 25; + static const uint8_t FORMAT_UTF16 = 26; + static const uint8_t FORMAT_OPAQUE = 27; + + void setDescription(uint16_t); + void setExponent(int8_t exponent); + void setFormat(uint8_t format); + void setNamespace(uint8_t namespace_value); + void setUnit(uint16_t unit); + +private: + friend class NimBLECharacteristic; + BLE2904_Data m_data; +}; // BLE2904 + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif /* MAIN_NIMBLE2904_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEAddress.cpp b/lib/NimBLE-Arduino/src/NimBLEAddress.cpp new file mode 100644 index 0000000..b8df5ac --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAddress.cpp @@ -0,0 +1,197 @@ +/* + * NimBLEAddress.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAddress.cpp + * + * Created on: Jul 2, 2017 + * Author: kolban + */ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include + +#include "NimBLEAddress.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAddress"; + +/************************************************* + * NOTE: NimBLE address bytes are in INVERSE ORDER! + * We will accomodate that fact in these methods. +*************************************************/ + +/** + * @brief Create an address from the native NimBLE representation. + * @param [in] address The native NimBLE address. + */ +NimBLEAddress::NimBLEAddress(ble_addr_t address) { + memcpy(m_address, address.val, 6); + m_addrType = address.type; +} // NimBLEAddress + + +/** + * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0. + */ +NimBLEAddress::NimBLEAddress() { + NimBLEAddress(""); +} // NimBLEAddress + + +/** + * @brief Create an address from a hex string + * + * A hex string is of the format: + * ``` + * 00:00:00:00:00:00 + * ``` + * which is 17 characters in length. + * + * @param [in] stringAddress The hex string representation of the address. + * @param [in] type The type of the address. + */ +NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) { + m_addrType = type; + + if (stringAddress.length() == 0) { + memset(m_address, 0, 6); + return; + } + + if (stringAddress.length() == 6) { + std::reverse_copy(stringAddress.data(), stringAddress.data() + 6, m_address); + return; + } + + if (stringAddress.length() != 17) { + memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address + NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str()); + return; + } + + int data[6]; + if(sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]) != 6) { + memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address + NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str()); + } + for(size_t index = 0; index < sizeof m_address; index++) { + m_address[index] = data[index]; + } +} // NimBLEAddress + + +/** + * @brief Constructor for compatibility with bluedroid esp library using native ESP representation. + * @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address. + * @param [in] type The type of the address. + */ +NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) { + std::reverse_copy(address, address + sizeof m_address, m_address); + m_addrType = type; +} // NimBLEAddress + + +/** + * @brief Constructor for address using a hex value.\n + * Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16" + * @param [in] address uint64_t containing the address. + * @param [in] type The type of the address. + */ +NimBLEAddress::NimBLEAddress(const uint64_t &address, uint8_t type) { + memcpy(m_address, &address, sizeof m_address); + m_addrType = type; +} // NimBLEAddress + + +/** + * @brief Determine if this address equals another. + * @param [in] otherAddress The other address to compare against. + * @return True if the addresses are equal. + */ +bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const { + return *this == otherAddress; +} // equals + + +/** + * @brief Get the native representation of the address. + * @return a pointer to the uint8_t[6] array of the address. + */ +const uint8_t *NimBLEAddress::getNative() const { + return m_address; +} // getNative + + +/** + * @brief Get the address type. + * @return The address type. + */ +uint8_t NimBLEAddress::getType() const { + return m_addrType; +} // getType + + +/** + * @brief Convert a BLE address to a string. + * + * A string representation of an address is in the format: + * + * ``` + * xx:xx:xx:xx:xx:xx + * ``` + * + * @return The string representation of the address. + * @deprecated Use std::string() operator instead. + */ +std::string NimBLEAddress::toString() const { + return std::string(*this); +} // toString + + +/** + * @brief Convienience operator to check if this address is equal to another. + */ +bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const { + return memcmp(rhs.m_address, m_address, sizeof m_address) == 0; +} // operator == + + +/** + * @brief Convienience operator to check if this address is not equal to another. + */ +bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const { + return !this->operator==(rhs); +} // operator != + + +/** + * @brief Convienience operator to convert this address to string representation. + * @details This allows passing NimBLEAddress to functions + * that accept std::string and/or or it's methods as a parameter. + */ +NimBLEAddress::operator std::string() const { + char buffer[18]; + snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", + m_address[5], m_address[4], m_address[3], + m_address[2], m_address[1], m_address[0]); + return std::string(buffer); +} // operator std::string + + +/** + * @brief Convienience operator to convert the native address representation to uint_64. + */ +NimBLEAddress::operator uint64_t() const { + uint64_t address = 0; + memcpy(&address, m_address, sizeof m_address); + return address; +} // operator uint64_t + +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLEAddress.h b/lib/NimBLE-Arduino/src/NimBLEAddress.h new file mode 100644 index 0000000..a6e10a0 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAddress.h @@ -0,0 +1,62 @@ +/* + * NimBLEAddress.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAddress.h + * + * Created on: Jul 2, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEADDRESS_H_ +#define COMPONENTS_NIMBLEADDRESS_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "nimble/ble.h" +#else +#include "nimble/nimble/include/nimble/ble.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include +#include + +/** + * @brief A %BLE device address. + * + * Every %BLE device has a unique address which can be used to identify it and form connections. + */ +class NimBLEAddress { +public: + NimBLEAddress(); + NimBLEAddress(ble_addr_t address); + NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC); + NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); + NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC); + bool equals(const NimBLEAddress &otherAddress) const; + const uint8_t* getNative() const; + std::string toString() const; + uint8_t getType() const; + + bool operator ==(const NimBLEAddress & rhs) const; + bool operator !=(const NimBLEAddress & rhs) const; + operator std::string() const; + operator uint64_t() const; + +private: + uint8_t m_address[6]; + uint8_t m_addrType; +}; + +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEADDRESS_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp b/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp new file mode 100644 index 0000000..01dd75d --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp @@ -0,0 +1,785 @@ +/* + * NimBLEAdvertisedDevice.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertisedDevice.cpp + * + * Created on: Jul 3, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + +#include "NimBLEDevice.h" +#include "NimBLEAdvertisedDevice.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAdvertisedDevice"; + + +/** + * @brief Constructor + */ +NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() : + m_payload(62,0) +{ + m_advType = 0; + m_rssi = -9999; + m_callbackSent = false; + m_timestamp = 0; + m_advLength = 0; +} // NimBLEAdvertisedDevice + + +/** + * @brief Get the address of the advertising device. + * @return The address of the advertised device. + */ +NimBLEAddress NimBLEAdvertisedDevice::getAddress() { + return m_address; +} // getAddress + + +/** + * @brief Get the advertisement type. + * @return The advertising type the device is reporting: + * * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising + * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle + * * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response + * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable + * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle + */ +uint8_t NimBLEAdvertisedDevice::getAdvType() { + return m_advType; +} // getAdvType + + +/** + * @brief Get the appearance. + * + * A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user + * typcially in the form of an icon. + * + * @return The appearance of the advertised device. + */ +uint16_t NimBLEAdvertisedDevice::getAppearance() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) { + return *field->value | *(field->value + 1) << 8; + } + } + + return 0; +} // getAppearance + + +/** + * @brief Get the advertisement interval. + * @return The advertisement interval in 0.625ms units. + */ +uint16_t NimBLEAdvertisedDevice::getAdvInterval() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) { + return *field->value | *(field->value + 1) << 8; + } + } + + return 0; +} // getAdvInterval + + +/** + * @brief Get the preferred min connection interval. + * @return The preferred min connection interval in 1.25ms units. + */ +uint16_t NimBLEAdvertisedDevice::getMinInterval() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { + return *field->value | *(field->value + 1) << 8; + } + } + + return 0; +} // getMinInterval + + +/** + * @brief Get the preferred max connection interval. + * @return The preferred max connection interval in 1.25ms units. + */ +uint16_t NimBLEAdvertisedDevice::getMaxInterval() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) { + return *(field->value + 2) | *(field->value + 3) << 8; + } + } + + return 0; +} // getMaxInterval + + +/** + * @brief Get the manufacturer data. + * @return The manufacturer data of the advertised device. + */ +std::string NimBLEAdvertisedDevice::getManufacturerData() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length > 1) { + return std::string((char*)field->value, field->length - 1); + } + } + + return ""; +} // getManufacturerData + + +/** + * @brief Get the URI from the advertisement. + * @return The URI data. + */ +std::string NimBLEAdvertisedDevice::getURI() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length > 1) { + return std::string((char*)field->value, field->length - 1); + } + } + + return ""; +} // getURI + + +/** + * @brief Get the advertised name. + * @return The name of the advertised device. + */ +std::string NimBLEAdvertisedDevice::getName() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 || + findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0) + { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length > 1) { + return std::string((char*)field->value, field->length - 1); + } + } + + return ""; +} // getName + + +/** + * @brief Get the RSSI. + * @return The RSSI of the advertised device. + */ +int NimBLEAdvertisedDevice::getRSSI() { + return m_rssi; +} // getRSSI + + +/** + * @brief Get the scan object that created this advertised device. + * @return The scan object. + */ +NimBLEScan* NimBLEAdvertisedDevice::getScan() { + return NimBLEDevice::getScan(); +} // getScan + + +/** + * @brief Get the number of target addresses. + * @return The number of addresses. + */ +size_t NimBLEAdvertisedDevice::getTargetAddressCount() { + uint8_t count = 0; + + count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR); + count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR); + + return count; +} + + +/** + * @brief Get the target address at the index. + * @param [in] index The index of the target address. + * @return The target address. + */ +NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) { + ble_hs_adv_field *field = nullptr; + uint8_t count = 0; + uint8_t data_loc = 0xFF; + + index++; + count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc); + + if (count < index) { + index -= count; + count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc); + } + + if(count > 0 && data_loc != 0xFF) { + field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { + index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + if(field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) { + return NimBLEAddress(field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + } + } + + return NimBLEAddress(""); +} + + +/** + * @brief Get the service data. + * @param [in] index The index of the service data requested. + * @return The advertised service data or empty string if no data. + */ +std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) { + ble_hs_adv_field *field = nullptr; + uint8_t bytes; + uint8_t data_loc = findServiceData(index, &bytes); + + if(data_loc != 0xFF) { + field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length > bytes) { + return std::string((char*)(field->value + bytes), field->length - bytes - 1); + } + } + + return ""; +} //getServiceData + + +/** + * @brief Get the service data. + * @param [in] uuid The uuid of the service data requested. + * @return The advertised service data or empty string if no data. + */ +std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) { + ble_hs_adv_field *field = nullptr; + uint8_t bytes; + uint8_t index = 0; + uint8_t data_loc = findServiceData(index, &bytes); + uint8_t uuidBytes = uuid.bitSize() / 8; + uint8_t plSize = m_payload.size() - 2; + + while(data_loc < plSize) { + field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(bytes == uuidBytes && NimBLEUUID(field->value, bytes, false) == uuid) { + return std::string((char*)(field->value + bytes), field->length - bytes - 1); + } + + index++; + data_loc = findServiceData(index, &bytes); + } + + NIMBLE_LOGI(LOG_TAG, "No service data found"); + return ""; +} //getServiceData + + +/** + * @brief Get the UUID of the serice data at the index. + * @param [in] index The index of the service data UUID requested. + * @return The advertised service data UUID or an empty UUID if not found. + */ +NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) { + ble_hs_adv_field *field = nullptr; + uint8_t bytes; + uint8_t data_loc = findServiceData(index, &bytes); + + if(data_loc != 0xFF) { + field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length >= bytes) { + return NimBLEUUID(field->value, bytes, false); + } + } + + return NimBLEUUID(""); +} // getServiceDataUUID + + +/** + * @brief Find the service data at the index. + * @param [in] index The index of the service data to find. + * @param [in] bytes A pointer to storage for the number of the bytes in the UUID. + * @return The index in the vector where the data is located, 0xFF if not found. + */ +uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) { + uint8_t data_loc = 0; + uint8_t found = 0; + + *bytes = 0; + index++; + found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc); + if(found == index) { + *bytes = 2; + return data_loc; + } + + index -= found; + found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc); + if(found == index) { + *bytes = 4; + return data_loc; + } + + index -= found; + found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc); + if(found == index) { + *bytes = 16; + return data_loc; + } + + return 0xFF; +} + + +/** + * @brief Get the count of advertised service data UUIDS + * @return The number of service data UUIDS in the vector. + */ +size_t NimBLEAdvertisedDevice::getServiceDataCount() { + uint8_t count = 0; + + count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16); + count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32); + count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128); + + return count; +} // getServiceDataCount + + +/** + * @brief Get the Service UUID. + * @param [in] index The index of the service UUID requested. + * @return The Service UUID of the advertised service, or an empty UUID if not found. + */ +NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) { + uint8_t count = 0; + uint8_t data_loc = 0; + uint8_t uuidBytes = 0; + uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; + ble_hs_adv_field *field = nullptr; + + index++; + + do { + count = findAdvField(type, index, &data_loc); + if(count >= index) { + if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) { + uuidBytes = 2; + } else if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) { + uuidBytes = 4; + } else { + uuidBytes = 16; + } + break; + + } else { + type++; + index -= count; + } + + } while(type <= BLE_HS_ADV_TYPE_COMP_UUIDS128); + + if(uuidBytes > 0) { + field = (ble_hs_adv_field *)&m_payload[data_loc]; + // In the case of more than one field of service uuid's we need to adjust + // the index to account for the uuids of the previous fields. + if(field->length < index * uuidBytes) { + index -= count - field->length / uuidBytes; + } + + if(field->length > uuidBytes * index) { + return NimBLEUUID(field->value + uuidBytes * (index - 1), uuidBytes, false); + } + } + + return NimBLEUUID(""); +} // getServiceUUID + + +/** + * @brief Get the number of services advertised + * @return The count of services in the advertising packet. + */ +size_t NimBLEAdvertisedDevice::getServiceUUIDCount() { + uint8_t count = 0; + + count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16); + count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32); + count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128); + count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128); + + return count; +} // getServiceUUIDCount + + +/** + * @brief Check advertised services for existance of the required UUID + * @param [in] uuid The service uuid to look for in the advertisement. + * @return Return true if service is advertised + */ +bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) { + size_t count = getServiceUUIDCount(); + for(size_t i = 0; i < count; i++) { + if(uuid == getServiceUUID(i)) { + return true; + } + } + + return false; +} // isAdvertisingService + + +/** + * @brief Get the TX Power. + * @return The TX Power of the advertised device. + */ +int8_t NimBLEAdvertisedDevice::getTXPower() { + uint8_t data_loc = 0; + + if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) { + ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc]; + if(field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) { + return *(int8_t*)field->value; + } + } + + return -99; +} // getTXPower + + +/** + * @brief Does this advertisement have preferred connection parameters? + * @return True if connection parameters are present. + */ +bool NimBLEAdvertisedDevice::haveConnParams() { + return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0; +} // haveConnParams + + +/** + * @brief Does this advertisement have have the advertising interval? + * @return True if the advertisement interval is present. + */ +bool NimBLEAdvertisedDevice::haveAdvInterval() { + return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0; +} // haveAdvInterval + + +/** + * @brief Does this advertisement have an appearance value? + * @return True if there is an appearance value present. + */ +bool NimBLEAdvertisedDevice::haveAppearance() { + return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0; +} // haveAppearance + + +/** + * @brief Does this advertisement have manufacturer data? + * @return True if there is manufacturer data present. + */ +bool NimBLEAdvertisedDevice::haveManufacturerData() { + return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0; +} // haveManufacturerData + + +/** + * @brief Does this advertisement have a URI? + * @return True if there is a URI present. + */ +bool NimBLEAdvertisedDevice::haveURI() { + return findAdvField(BLE_HS_ADV_TYPE_URI) > 0; +} // haveURI + + +/** + * @brief Does the advertisement contain a target address? + * @return True if an address is present. + */ +bool NimBLEAdvertisedDevice::haveTargetAddress() { + return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 || + findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0; +} + + +/** + * @brief Does this advertisement have a name value? + * @return True if there is a name value present. + */ +bool NimBLEAdvertisedDevice::haveName() { + return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 || + findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0; +} // haveName + + +/** + * @brief Does this advertisement have a signal strength value? + * @return True if there is a signal strength value present. + */ +bool NimBLEAdvertisedDevice::haveRSSI() { + return m_rssi != -9999; +} // haveRSSI + + +/** + * @brief Does this advertisement have a service data value? + * @return True if there is a service data value present. + */ +bool NimBLEAdvertisedDevice::haveServiceData() { + return getServiceDataCount() > 0; +} // haveServiceData + + +/** + * @brief Does this advertisement have a service UUID value? + * @return True if there is a service UUID value present. + */ +bool NimBLEAdvertisedDevice::haveServiceUUID() { + return getServiceUUIDCount() > 0; +} // haveServiceUUID + + +/** + * @brief Does this advertisement have a transmission power value? + * @return True if there is a transmission power value present. + */ +bool NimBLEAdvertisedDevice::haveTXPower() { + return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0; +} // haveTXPower + + +uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) { + ble_hs_adv_field *field = nullptr; + uint8_t data = 0; + uint8_t length = m_payload.size(); + uint8_t count = 0; + + if(length < 2) { + return count; + } + + while (length > 1) { + field = (ble_hs_adv_field*)&m_payload[data]; + + if (field->length >= length) { + return count; + } + + if (field->type == type) { + switch(type) { + case BLE_HS_ADV_TYPE_INCOMP_UUIDS16: + case BLE_HS_ADV_TYPE_COMP_UUIDS16: + count += field->length / 2; + break; + + case BLE_HS_ADV_TYPE_INCOMP_UUIDS32: + case BLE_HS_ADV_TYPE_COMP_UUIDS32: + count += field->length / 4; + break; + + case BLE_HS_ADV_TYPE_INCOMP_UUIDS128: + case BLE_HS_ADV_TYPE_COMP_UUIDS128: + count += field->length / 16; + break; + + case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR: + case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR: + count += field->length / 6; + break; + + default: + count++; + break; + } + + if(data_loc != nullptr) { + if(index == 0 || count >= index) { + break; + } + } + } + + length -= 1 + field->length; + data += 1 + field->length; + } + + if(data_loc != nullptr && field != nullptr) { + *data_loc = data; + } + + return count; +} + + +/** + * @brief Set the address of the advertised device. + * @param [in] address The address of the advertised device. + */ +void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) { + m_address = address; +} // setAddress + + +/** + * @brief Set the adFlag for this device. + * @param [in] advType The advertisement flag data from the advertisement. + */ +void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) { + m_advType = advType; +} // setAdvType + + +/** + * @brief Set the RSSI for this device. + * @param [in] rssi The RSSI of the discovered device. + */ +void NimBLEAdvertisedDevice::setRSSI(int rssi) { + m_rssi = rssi; +} // setRSSI + + +/** + * @brief Create a string representation of this device. + * @return A string representation of this device. + */ +std::string NimBLEAdvertisedDevice::toString() { + std::string res = "Name: " + getName() + ", Address: " + getAddress().toString(); + + if (haveAppearance()) { + char val[6]; + snprintf(val, sizeof(val), "%d", getAppearance()); + res += ", appearance: "; + res += val; + } + + if (haveManufacturerData()) { + char *pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); + res += ", manufacturer data: "; + res += pHex; + free(pHex); + } + + if (haveServiceUUID()) { + res += ", serviceUUID: " + getServiceUUID().toString(); + } + + if (haveTXPower()) { + char val[5]; + snprintf(val, sizeof(val), "%d", getTXPower()); + res += ", txPower: "; + res += val; + } + + if(haveServiceData()) { + size_t count = getServiceDataCount(); + res += "\nService Data:"; + for(size_t i = 0; i < count; i++) { + res += "\nUUID: " + std::string(getServiceDataUUID(i)); + res += ", Data: " + getServiceData(i); + } + } + + return res; + +} // toString + + +/** + * @brief Get the payload advertised by the device. + * @return The advertisement payload. + */ +uint8_t* NimBLEAdvertisedDevice::getPayload() { + return &m_payload[0]; +} // getPayload + + +/** + * @brief Stores the payload of the advertised device in a vector. + * @param [in] payload The advertisement payload. + * @param [in] length The length of the payload in bytes. + * @param [in] append Indicates if the the data should be appended (scan response). + */ +void NimBLEAdvertisedDevice::setPayload(const uint8_t *payload, uint8_t length, bool append) { + if(!append) { + m_advLength = length; + m_payload.assign(payload, payload + length); + } else { + m_payload.insert(m_payload.end(), payload, payload + length); + } +} + + +/** + * @brief Get the length of the advertisement data in the payload. + * @return The number of bytes in the payload that is from the advertisment. + */ +uint8_t NimBLEAdvertisedDevice::getAdvLength() { + return m_advLength; +} + + +/** + * @brief Get the advertised device address type. + * @return The device address type: + * * BLE_ADDR_PUBLIC (0x00) + * * BLE_ADDR_RANDOM (0x01) + * * BLE_ADDR_PUBLIC_ID (0x02) + * * BLE_ADDR_RANDOM_ID (0x03) + */ +uint8_t NimBLEAdvertisedDevice::getAddressType() { + return m_address.getType(); +} // getAddressType + + +/** + * @brief Get the timeStamp of when the device last advertised. + * @return The timeStamp of when the device was last seen. + */ +time_t NimBLEAdvertisedDevice::getTimestamp() { + return m_timestamp; +} // getTimestamp + + +/** + * @brief Get the length of the payload advertised by the device. + * @return The size of the payload in bytes. + */ +size_t NimBLEAdvertisedDevice::getPayloadLength() { + return m_payload.size(); +} // getPayloadLength + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ + diff --git a/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h b/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h new file mode 100644 index 0000000..39410e6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h @@ -0,0 +1,177 @@ +/* + * NimBLEAdvertisedDevice.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertisedDevice.h + * + * Created on: Jul 3, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ +#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + +#include "NimBLEAddress.h" +#include "NimBLEScan.h" +#include "NimBLEUUID.h" + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_hs_adv.h" +#else +#include "nimble/nimble/host/include/host/ble_hs_adv.h" +#endif + +#include +#include +#include + + +class NimBLEScan; +/** + * @brief A representation of a %BLE advertised device found by a scan. + * + * When we perform a %BLE scan, the result will be a set of devices that are advertising. This + * class provides a model of a detected device. + */ +class NimBLEAdvertisedDevice { +public: + NimBLEAdvertisedDevice(); + + NimBLEAddress getAddress(); + uint8_t getAdvType(); + uint16_t getAppearance(); + uint16_t getAdvInterval(); + uint16_t getMinInterval(); + uint16_t getMaxInterval(); + std::string getManufacturerData(); + std::string getURI(); + + /** + * @brief A template to convert the service data to . + * @tparam T The type to convert the data to. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: getManufacturerData(skipSizeCheck); + */ + template + T getManufacturerData(bool skipSizeCheck = false) { + std::string data = getManufacturerData(); + if(!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char *pData = data.data(); + return *((T *)pData); + } + + std::string getName(); + int getRSSI(); + NimBLEScan* getScan(); + size_t getServiceDataCount(); + std::string getServiceData(uint8_t index = 0); + std::string getServiceData(const NimBLEUUID &uuid); + + /** + * @brief A template to convert the service data to . + * @tparam T The type to convert the data to. + * @param [in] index The vector index of the service data requested. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: getServiceData(skipSizeCheck); + */ + template + T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) { + std::string data = getServiceData(index); + if(!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char *pData = data.data(); + return *((T *)pData); + } + + /** + * @brief A template to convert the service data to . + * @tparam T The type to convert the data to. + * @param [in] uuid The uuid of the service data requested. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: getServiceData(skipSizeCheck); + */ + template + T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) { + std::string data = getServiceData(uuid); + if(!skipSizeCheck && data.size() < sizeof(T)) return T(); + const char *pData = data.data(); + return *((T *)pData); + } + + NimBLEUUID getServiceDataUUID(uint8_t index = 0); + NimBLEUUID getServiceUUID(uint8_t index = 0); + size_t getServiceUUIDCount(); + NimBLEAddress getTargetAddress(uint8_t index = 0); + size_t getTargetAddressCount(); + int8_t getTXPower(); + uint8_t* getPayload(); + uint8_t getAdvLength(); + size_t getPayloadLength(); + uint8_t getAddressType(); + time_t getTimestamp(); + bool isAdvertisingService(const NimBLEUUID &uuid); + bool haveAppearance(); + bool haveManufacturerData(); + bool haveName(); + bool haveRSSI(); + bool haveServiceData(); + bool haveServiceUUID(); + bool haveTXPower(); + bool haveConnParams(); + bool haveAdvInterval(); + bool haveTargetAddress(); + bool haveURI(); + std::string toString(); + +private: + friend class NimBLEScan; + + void setAddress(NimBLEAddress address); + void setAdvType(uint8_t advType); + void setPayload(const uint8_t *payload, uint8_t length, bool append); + void setRSSI(int rssi); + uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr); + uint8_t findServiceData(uint8_t index, uint8_t* bytes); + + NimBLEAddress m_address = NimBLEAddress(""); + uint8_t m_advType; + int m_rssi; + time_t m_timestamp; + bool m_callbackSent; + uint8_t m_advLength; + + std::vector m_payload; +}; + +/** + * @brief A callback handler for callbacks associated device scanning. + * + * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising + * has been found. This class can be sub-classed and registered such that when a scan is performed and + * a new advertised device has been found, we will be called back to be notified. + */ +class NimBLEAdvertisedDeviceCallbacks { +public: + virtual ~NimBLEAdvertisedDeviceCallbacks() {} + /** + * @brief Called when a new scan result is detected. + * + * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the + * device that was found. During any individual scan, a device will only be detected one time. + */ + virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ +#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/lib/NimBLE-Arduino/src/NimBLEAdvertising.cpp new file mode 100644 index 0000000..a804130 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAdvertising.cpp @@ -0,0 +1,1029 @@ +/* + * NimBLEAdvertising.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.cpp + * + * This class encapsulates advertising a BLE Server. + * Created on: Jun 21, 2017 + * Author: kolban + * + */ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "services/gap/ble_svc_gap.h" +#else +#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +#endif +#include "NimBLEAdvertising.h" +#include "NimBLEDevice.h" +#include "NimBLEServer.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEAdvertising"; + + +/** + * @brief Construct a default advertising object. + */ +NimBLEAdvertising::NimBLEAdvertising() { + reset(); +} // NimBLEAdvertising + + +/** + * @brief Stops the current advertising and resets the advertising data to the default values. + */ +void NimBLEAdvertising::reset() { + if(NimBLEDevice::getInitialized() && isAdvertising()) { + stop(); + } + memset(&m_advData, 0, sizeof m_advData); + memset(&m_scanData, 0, sizeof m_scanData); + memset(&m_advParams, 0, sizeof m_advParams); + memset(&m_slaveItvl, 0, sizeof m_slaveItvl); + const char *name = ble_svc_gap_device_name(); + + m_advData.name = (uint8_t *)name; + m_advData.name_len = strlen(name); + m_advData.name_is_complete = 1; + m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); + +#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; +#else + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; +#endif + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; + m_customAdvData = false; + m_customScanResponseData = false; + m_scanResp = true; + m_advDataSet = false; + // Set this to non-zero to prevent auto start if host reset before started by app. + m_duration = BLE_HS_FOREVER; + m_advCompCB = nullptr; +} // reset + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); + m_advDataSet = false; +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(NimBLEUUID(serviceUUID)); + m_advDataSet = false; +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { + for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) { + if((*it) == serviceUUID) { + m_serviceUUIDs.erase(it); + break; + } + } + m_advDataSet = false; +} // addServiceUUID + + +/** + * @brief Set the device appearance in the advertising data. + * The codes for distinct appearances can be found here:\n + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. + * @param [in] appearance The appearance of the device in the advertising data. + */ +void NimBLEAdvertising::setAppearance(uint16_t appearance) { + m_advData.appearance = appearance; + m_advData.appearance_is_present = 1; + m_advDataSet = false; +} // setAppearance + + +/** + * @brief Add the transmission power level to the advertisement packet. + */ +void NimBLEAdvertising::addTxPower() { + m_advData.tx_pwr_lvl_is_present = 1; + m_advDataSet = false; +} // addTxPower + + +/** + * @brief Set the advertised name of the device. + * @param [in] name The name to advertise. + */ +void NimBLEAdvertising::setName(const std::string &name) { + m_name.assign(name.begin(), name.end()); + m_advData.name = &m_name[0]; + m_advData.name_len = m_name.size(); + m_advDataSet = false; +} // setName + + +/** + * @brief Set the advertised manufacturer data. + * @param [in] data The data to advertise. + */ +void NimBLEAdvertising::setManufacturerData(const std::string &data) { + m_mfgData.assign(data.begin(), data.end()); + m_advData.mfg_data = &m_mfgData[0]; + m_advData.mfg_data_len = m_mfgData.size(); + m_advDataSet = false; +} // setManufacturerData + + +/** + * @brief Set the advertised URI. + * @param [in] uri The URI to advertise. + */ +void NimBLEAdvertising::setURI(const std::string &uri) { + m_uri.assign(uri.begin(), uri.end()); + m_advData.uri = &m_uri[0]; + m_advData.uri_len = m_uri.size(); + m_advDataSet = false; +} // setURI + + +/** + * @brief Set the service data advertised for the UUID. + * @param [in] uuid The UUID the service data belongs to. + * @param [in] data The data to advertise. + * @note If data length is 0 the service data will not be advertised. + */ +void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) { + switch (uuid.bitSize()) { + case 16: { + m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2); + m_svcData16.insert(m_svcData16.end(), data.begin(), data.end()); + m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0]; + m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0; + break; + } + + case 32: { + m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4); + m_svcData32.insert(m_svcData32.end(), data.begin(), data.end()); + m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0]; + m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0; + break; + } + + case 128: { + m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16); + m_svcData128.insert(m_svcData128.end(), data.begin(), data.end()); + m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0]; + m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0; + break; + } + + default: + return; + } + + m_advDataSet = false; +} // setServiceData + + +/** + * @brief Set the type of advertisment to use. + * @param [in] adv_type: + * * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising + * * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising + * * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising + */ +void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){ + m_advParams.conn_mode = adv_type; +} // setAdvertisementType + + +/** + * @brief Set the minimum advertising interval. + * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default. + */ +void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advParams.itvl_min = mininterval; +} // setMinInterval + + +/** + * @brief Set the maximum advertising interval. + * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default. + */ +void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advParams.itvl_max = maxinterval; +} // setMaxInterval + + +/** + * @brief Set the advertised min connection interval preferred by this device. + * @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80. + * @details Values not within the range will cancel advertising of this data.\n + * Consumes 6 bytes of advertising space (combined with max interval). + */ +void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { + // invalid paramters, set the slave interval to null + if(mininterval < 0x0006 || mininterval > 0x0C80) { + m_advData.slave_itvl_range = nullptr; + return; + } + + if(m_advData.slave_itvl_range == nullptr) { + m_advData.slave_itvl_range = m_slaveItvl; + } + + m_slaveItvl[0] = mininterval; + m_slaveItvl[1] = mininterval >> 8; + + uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2); + + // If mininterval is higher than the maxinterval make them the same + if(mininterval > maxinterval) { + m_slaveItvl[2] = m_slaveItvl[0]; + m_slaveItvl[3] = m_slaveItvl[1]; + } + + m_advDataSet = false; +} // setMinPreferred + + +/** + * @brief Set the advertised max connection interval preferred by this device. + * @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80. + * @details Values not within the range will cancel advertising of this data.\n + * Consumes 6 bytes of advertising space (combined with min interval). + */ +void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + // invalid paramters, set the slave interval to null + if(maxinterval < 0x0006 || maxinterval > 0x0C80) { + m_advData.slave_itvl_range = nullptr; + return; + } + if(m_advData.slave_itvl_range == nullptr) { + m_advData.slave_itvl_range = m_slaveItvl; + } + m_slaveItvl[2] = maxinterval; + m_slaveItvl[3] = maxinterval >> 8; + + uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range); + + // If mininterval is higher than the maxinterval make them the same + if(mininterval > maxinterval) { + m_slaveItvl[0] = m_slaveItvl[2]; + m_slaveItvl[1] = m_slaveItvl[3]; + } + + m_advDataSet = false; +} // setMaxPreferred + + +/** + * @brief Set if scan response is available. + * @param [in] set true = scan response available. + */ +void NimBLEAdvertising::setScanResponse(bool set) { + m_scanResp = set; + m_advDataSet = false; +} // setScanResponse + + +/** + * @brief Set the filtering for the scan filter. + * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. + * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. + */ +void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", + scanRequestWhitelistOnly, connectWhitelistOnly); + if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_SCAN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (!scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_CONN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.filter_policy = BLE_HCI_ADV_FILT_BOTH; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } +} // setScanFilter + + +/** + * @brief Set the advertisement data that is to be published in a regular advertisement. + * @param [in] advertisementData The data to be advertised. + * @details The use of this function will replace any data set with addServiceUUID\n + * or setAppearance. If you wish for these to be advertised you must include them\n + * in the advertisementData parameter sent. + */ + +void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setAdvertisementData"); + int rc = ble_gap_adv_set_data( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", + rc, NimBLEUtils::returnCodeToString(rc)); + } + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. + NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData"); +} // setAdvertisementData + + +/** + * @brief Set the advertisement data that is to be published in a scan response. + * @param [in] advertisementData The data to be advertised. + * @details Calling this without also using setAdvertisementData will have no effect.\n + * When using custom scan response data you must also use custom advertisement data. + */ +void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData"); + int rc = ble_gap_adv_rsp_set_data( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", + rc, NimBLEUtils::returnCodeToString(rc)); + } + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. + NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData"); +} // setScanResponseData + + +/** + * @brief Start advertising. + * @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever. + * @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends. + * @return True if advertising started successfully. + */ +bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) { + NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", + m_customAdvData, m_customScanResponseData); + + // If Host is not synced we cannot start advertising. + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + return false; + } + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + NimBLEServer* pServer = NimBLEDevice::getServer(); + if(pServer != nullptr) { + if(!pServer->m_gattsStarted){ + pServer->start(); + } else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising"); + return false; + } + } +#endif + + // If already advertising just return + if(ble_gap_adv_active()) { + NIMBLE_LOGW(LOG_TAG, "Advertising already active"); + return false; + } + + // Save the duration incase of host reset so we can restart with the same params + m_duration = duration; + + if(duration == 0){ + duration = BLE_HS_FOREVER; + } + else{ + duration = duration*1000; // convert duration to milliseconds + } + + m_advCompCB = advCompleteCB; + + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); + if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) { + if(!m_scanResp) { + m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON; + m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP; + } + } + + int rc = 0; + + if (!m_customAdvData && !m_advDataSet) { + //start with 3 bytes for the flags data + uint8_t payloadLen = (2 + 1); + if(m_advData.mfg_data_len > 0) + payloadLen += (2 + m_advData.mfg_data_len); + + if(m_advData.svc_data_uuid16_len > 0) + payloadLen += (2 + m_advData.svc_data_uuid16_len); + + if(m_advData.svc_data_uuid32_len > 0) + payloadLen += (2 + m_advData.svc_data_uuid32_len); + + if(m_advData.svc_data_uuid128_len > 0) + payloadLen += (2 + m_advData.svc_data_uuid128_len); + + if(m_advData.uri_len > 0) + payloadLen += (2 + m_advData.uri_len); + + if(m_advData.appearance_is_present) + payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN); + + if(m_advData.tx_pwr_lvl_is_present) + payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN); + + if(m_advData.slave_itvl_range != nullptr) + payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + + for(auto &it : m_serviceUUIDs) { + if(it.getNative()->u.type == BLE_UUID_TYPE_16) { + int add = (m_advData.num_uuids16 > 0) ? 2 : 4; + if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + m_advData.uuids16_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16, + (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) + { + NIMBLE_LOGC(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], + &it.getNative()->u16, sizeof(ble_uuid16_t)); + m_advData.uuids16_is_complete = 1; + m_advData.num_uuids16++; + } + if(it.getNative()->u.type == BLE_UUID_TYPE_32) { + int add = (m_advData.num_uuids32 > 0) ? 4 : 6; + if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + m_advData.uuids32_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32, + (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) + { + NIMBLE_LOGC(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], + &it.getNative()->u32, sizeof(ble_uuid32_t)); + m_advData.uuids32_is_complete = 1; + m_advData.num_uuids32++; + } + if(it.getNative()->u.type == BLE_UUID_TYPE_128){ + int add = (m_advData.num_uuids128 > 0) ? 16 : 18; + if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + m_advData.uuids128_is_complete = 0; + continue; + } + payloadLen += add; + + if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128, + (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) + { + NIMBLE_LOGC(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], + &it.getNative()->u128, sizeof(ble_uuid128_t)); + m_advData.uuids128_is_complete = 1; + m_advData.num_uuids128++; + } + } + + // check if there is room for the name, if not put it in scan data + if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) { + if(m_scanResp && !m_customScanResponseData){ + m_scanData.name = m_advData.name; + m_scanData.name_len = m_advData.name_len; + if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) { + m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2; + m_scanData.name_is_complete = 0; + } else { + m_scanData.name_is_complete = 1; + } + m_advData.name = nullptr; + m_advData.name_len = 0; + m_advData.name_is_complete = 0; + } else { + if(m_advData.tx_pwr_lvl_is_present) { + m_advData.tx_pwr_lvl_is_present = 0; + payloadLen -= (2 + 1); + } + // if not using scan response just cut the name down + // leaving 2 bytes for the data specifier. + if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) { + m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2); + m_advData.name_is_complete = 0; + } + } + } + + if(m_scanResp && !m_customScanResponseData) { + rc = ble_gap_adv_rsp_set_fields(&m_scanData); + switch(rc) { + case 0: + break; + + case BLE_HS_EBUSY: + NIMBLE_LOGE(LOG_TAG, "Already advertising"); + break; + + case BLE_HS_EMSGSIZE: + NIMBLE_LOGE(LOG_TAG, "Scan data too long"); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + } + + if(rc == 0) { + rc = ble_gap_adv_set_fields(&m_advData); + switch(rc) { + case 0: + break; + + case BLE_HS_EBUSY: + NIMBLE_LOGE(LOG_TAG, "Already advertising"); + break; + + case BLE_HS_EMSGSIZE: + NIMBLE_LOGE(LOG_TAG, "Advertisement data too long"); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + } + + if(m_advData.num_uuids128 > 0) { + free((void*)m_advData.uuids128); + m_advData.uuids128 = nullptr; + m_advData.num_uuids128 = 0; + } + + if(m_advData.num_uuids32 > 0) { + free((void*)m_advData.uuids32); + m_advData.uuids32 = nullptr; + m_advData.num_uuids32 = 0; + } + + if(m_advData.num_uuids16 > 0) { + free((void*)m_advData.uuids16); + m_advData.uuids16 = nullptr; + m_advData.num_uuids16 = 0; + } + + if(rc !=0) { + return false; + } + + m_advDataSet = true; + } + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration, + &m_advParams, + (pServer != nullptr) ? NimBLEServer::handleGapEvent : + NimBLEAdvertising::handleGapEvent, + (pServer != nullptr) ? (void*)pServer : (void*)this); +#else + rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration, + &m_advParams, NimBLEAdvertising::handleGapEvent, this); +#endif + switch(rc) { + case 0: + break; + + case BLE_HS_EINVAL: + NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long"); + break; + + case BLE_HS_EPREEMPTED: + NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy"); + break; + + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset"); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + + NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); + return (rc == 0); +} // start + + +/** + * @brief Stop advertising. + */ +void NimBLEAdvertising::stop() { + NIMBLE_LOGD(LOG_TAG, ">> stop"); + + int rc = ble_gap_adv_stop(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", + rc, NimBLEUtils::returnCodeToString(rc)); + return; + } + + NIMBLE_LOGD(LOG_TAG, "<< stop"); +} // stop + + +/** + * @brief Handles the callback when advertising stops. + */ +void NimBLEAdvertising::advCompleteCB() { + if(m_advCompCB != nullptr) { + m_advCompCB(this); + } +} // advCompleteCB + + +/** + * @brief Check if currently advertising. + * @return true if advertising is active. + */ +bool NimBLEAdvertising::isAdvertising() { + return ble_gap_adv_active(); +} // isAdvertising + + +/* + * Host reset seems to clear advertising data, + * we need clear the flag so it reloads it. + */ +void NimBLEAdvertising::onHostSync() { + NIMBLE_LOGD(LOG_TAG, "Host re-synced"); + + m_advDataSet = false; + // If we were advertising forever, restart it now + if(m_duration == 0) { + start(m_duration, m_advCompCB); + } else { + // Otherwise we should tell the app that advertising stopped. + advCompleteCB(); + } +} // onHostSync + + +/** + * @brief Handler for gap events when not using peripheral role. + * @param [in] event the event data. + * @param [in] arg pointer to the advertising instance. + */ +/*STATIC*/ +int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { + NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg; + + if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) { + switch(event->adv_complete.reason) { + // Don't call the callback if host reset, we want to + // preserve the active flag until re-sync to restart advertising. + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason); + NimBLEDevice::onReset(event->adv_complete.reason); + return 0; + default: + break; + } + pAdv->advCompleteCB(); + } + return 0; +} + + +/** + * @brief Add data to the payload to be advertised. + * @param [in] data The data to be added to the payload. + */ +void NimBLEAdvertisementData::addData(const std::string &data) { + if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { + NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded"); + return; + } + m_payload.append(data); +} // addData + + +/** + * @brief Add data to the payload to be advertised. + * @param [in] data The data to be added to the payload. + * @param [in] length The size of data to be added to the payload. + */ +void NimBLEAdvertisementData::addData(char * data, size_t length) { + addData(std::string(data, length)); +} // addData + + +/** + * @brief Set the appearance. + * @param [in] appearance The appearance code value. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ +void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { + char cdata[2]; + cdata[0] = 3; + cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); +} // setAppearance + + +/** + * @brief Set the advertisement flags. + * @param [in] flag The flags to be set in the advertisement. + * * BLE_HS_ADV_F_DISC_LTD + * * BLE_HS_ADV_F_DISC_GEN + * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE + */ +void NimBLEAdvertisementData::setFlags(uint8_t flag) { + char cdata[3]; + cdata[0] = 2; + cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 + cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; + addData(std::string(cdata, 3)); +} // setFlag + + +/** + * @brief Set manufacturer specific data. + * @param [in] data The manufacturer data to advertise. + */ +void NimBLEAdvertisementData::setManufacturerData(const std::string &data) { + char cdata[2]; + cdata[0] = data.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff + addData(std::string(cdata, 2) + data); +} // setManufacturerData + + +/** + * @brief Set the URI to advertise. + * @param [in] uri The uri to advertise. + */ +void NimBLEAdvertisementData::setURI(const std::string &uri) { + char cdata[2]; + cdata[0] = uri.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_URI; + addData(std::string(cdata, 2) + uri); +} // setURI + + +/** + * @brief Set the complete name of this device. + * @param [in] name The name to advertise. + */ +void NimBLEAdvertisementData::setName(const std::string &name) { + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 + addData(std::string(cdata, 2) + name); +} // setName + + +/** + * @brief Set a single service to advertise as a complete list of services. + * @param [in] uuid The service to advertise. + */ +void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) { + setServices(true, uuid.bitSize(), {uuid}); +} // setCompleteServices + + +/** + * @brief Set the complete list of 16 bit services to advertise. + * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + */ +void NimBLEAdvertisementData::setCompleteServices16(const std::vector& v_uuid) { + setServices(true, 16, v_uuid); +} // setCompleteServices16 + + +/** + * @brief Set the complete list of 32 bit services to advertise. + * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + */ +void NimBLEAdvertisementData::setCompleteServices32(const std::vector& v_uuid) { + setServices(true, 32, v_uuid); +} // setCompleteServices32 + + +/** + * @brief Set a single service to advertise as a partial list of services. + * @param [in] uuid The service to advertise. + */ +void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) { + setServices(false, uuid.bitSize(), {uuid}); +} // setPartialServices + + +/** + * @brief Set the partial list of services to advertise. + * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + */ +void NimBLEAdvertisementData::setPartialServices16(const std::vector& v_uuid) { + setServices(false, 16, v_uuid); +} // setPartialServices16 + + +/** + * @brief Set the partial list of services to advertise. + * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + */ +void NimBLEAdvertisementData::setPartialServices32(const std::vector& v_uuid) { + setServices(false, 32, v_uuid); +} // setPartialServices32 + + +/** + * @brief Utility function to create the list of service UUID's from a vector. + * @param [in] complete If true the vector is the complete set of services. + * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128). + * @param [in] v_uuid The vector of service UUID's to advertise. + */ +void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size, + const std::vector &v_uuid) +{ + char cdata[2]; + cdata[0] = (size / 8) * v_uuid.size() + 1; + switch(size) { + case 16: + cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16; + break; + case 32: + cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32; + break; + case 128: + cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128; + break; + default: + return; + } + + std::string uuids; + + for(auto &it : v_uuid){ + if(it.bitSize() != size) { + NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); + return; + } else { + switch(size) { + case 16: + uuids += std::string((char*)&it.getNative()->u16.value, 2); + break; + case 32: + uuids += std::string((char*)&it.getNative()->u32.value, 4); + break; + case 128: + uuids += std::string((char*)&it.getNative()->u128.value, 16); + break; + default: + return; + } + } + } + + addData(std::string(cdata, 2) + uuids); +} // setServices + + +/** + * @brief Set the service data (UUID + data) + * @param [in] uuid The UUID to set with the service data. + * @param [in] data The data to be associated with the service data advertised. + */ +void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x16] [UUID16] data + cdata[0] = data.length() + 3; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data); + break; + } + + case 32: { + // [Len] [0x20] [UUID32] data + cdata[0] = data.length() + 5; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data); + break; + } + + case 128: { + // [Len] [0x21] [UUID128] data + cdata[0] = data.length() + 17; + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); + break; + } + + default: + return; + } +} // setServiceData + + +/** + * @brief Set the short name. + * @param [in] name The short name of the device. + */ +void NimBLEAdvertisementData::setShortName(const std::string &name) { + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 + addData(std::string(cdata, 2) + name); +} // setShortName + + +/** + * @brief Adds Tx power level to the advertisement data. + */ +void NimBLEAdvertisementData::addTxPower() { + char cdata[3]; + cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1; + cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL; + cdata[2] = NimBLEDevice::getPower(); + addData(cdata, 3); +} // addTxPower + + +/** + * @brief Set the preferred connection interval parameters. + * @param [in] min The minimum interval desired. + * @param [in] max The maximum interval desired. + */ +void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) { + char cdata[6]; + cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1; + cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE; + cdata[2] = min; + cdata[3] = min >> 8; + cdata[4] = max; + cdata[5] = max >> 8; + addData(cdata, 6); +} // setPreferredParams + + +/** + * @brief Retrieve the payload that is to be advertised. + * @return The payload that is to be advertised. + */ +std::string NimBLEAdvertisementData::getPayload() { + return m_payload; +} // getPayload + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */ diff --git a/lib/NimBLE-Arduino/src/NimBLEAdvertising.h b/lib/NimBLE-Arduino/src/NimBLEAdvertising.h new file mode 100644 index 0000000..63a21d8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAdvertising.h @@ -0,0 +1,138 @@ +/* + * NimBLEAdvertising.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEADVERTISING_H_ +#define MAIN_BLEADVERTISING_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_gap.h" +#else +#include "nimble/nimble/host/include/host/ble_gap.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include "NimBLEUUID.h" + +#include + +/* COMPATIBILITY - DO NOT USE */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) + /* ************************* */ + + +/** + * @brief Advertisement data set by the programmer to be published by the %BLE server. + */ +class NimBLEAdvertisementData { + // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will + // be exposed on demand/request or as time permits. + // +public: + void setAppearance(uint16_t appearance); + void setCompleteServices(const NimBLEUUID &uuid); + void setCompleteServices16(const std::vector &v_uuid); + void setCompleteServices32(const std::vector &v_uuid); + void setFlags(uint8_t); + void setManufacturerData(const std::string &data); + void setURI(const std::string &uri); + void setName(const std::string &name); + void setPartialServices(const NimBLEUUID &uuid); + void setPartialServices16(const std::vector &v_uuid); + void setPartialServices32(const std::vector &v_uuid); + void setServiceData(const NimBLEUUID &uuid, const std::string &data); + void setShortName(const std::string &name); + void addData(const std::string &data); // Add data to the payload. + void addData(char * data, size_t length); + void addTxPower(); + void setPreferredParams(uint16_t min, uint16_t max); + std::string getPayload(); // Retrieve the current advert payload. + +private: + friend class NimBLEAdvertising; + void setServices(const bool complete, const uint8_t size, + const std::vector &v_uuid); + std::string m_payload; // The payload of the advertisement. +}; // NimBLEAdvertisementData + + +/** + * @brief Perform and manage %BLE advertising. + * + * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. + */ +class NimBLEAdvertising { +public: + NimBLEAdvertising(); + void addServiceUUID(const NimBLEUUID &serviceUUID); + void addServiceUUID(const char* serviceUUID); + void removeServiceUUID(const NimBLEUUID &serviceUUID); + bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr); + void stop(); + void setAppearance(uint16_t appearance); + void setName(const std::string &name); + void setManufacturerData(const std::string &data); + void setURI(const std::string &uri); + void setServiceData(const NimBLEUUID &uuid, const std::string &data); + void setAdvertisementType(uint8_t adv_type); + void setMaxInterval(uint16_t maxinterval); + void setMinInterval(uint16_t mininterval); + void setAdvertisementData(NimBLEAdvertisementData& advertisementData); + void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); + void setScanResponseData(NimBLEAdvertisementData& advertisementData); + void setScanResponse(bool); + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); + void addTxPower(); + void reset(); + void advCompleteCB(); + bool isAdvertising(); + +private: + friend class NimBLEDevice; + + void onHostSync(); + static int handleGapEvent(struct ble_gap_event *event, void *arg); + + ble_hs_adv_fields m_advData; + ble_hs_adv_fields m_scanData; + ble_gap_adv_params m_advParams; + std::vector m_serviceUUIDs; + bool m_customAdvData; + bool m_customScanResponseData; + bool m_scanResp; + bool m_advDataSet; + void (*m_advCompCB)(NimBLEAdvertising *pAdv); + uint8_t m_slaveItvl[4]; + uint32_t m_duration; + std::vector m_svcData16; + std::vector m_svcData32; + std::vector m_svcData128; + std::vector m_name; + std::vector m_mfgData; + std::vector m_uri; +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */ +#endif /* MAIN_BLEADVERTISING_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEAttValue.h b/lib/NimBLE-Arduino/src/NimBLEAttValue.h new file mode 100644 index 0000000..11cd3f8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEAttValue.h @@ -0,0 +1,447 @@ +/* + * NimBLEAttValue.h + * + * Created: on March 18, 2021 + * Author H2zero + * + */ + +#ifndef MAIN_NIMBLEATTVALUE_H_ +#define MAIN_NIMBLEATTVALUE_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE +#include +#endif + +#include "NimBLELog.h" + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include +#include + +#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED +# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0 +#endif + +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED +# include +#endif + +#if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH) +# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20 +#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN +# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN) +#elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1 +# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512 +#endif + + +/* Used to determine if the type passed to a template has a c_str() and length() method. */ +template +struct Has_c_str_len : std::false_type {}; + +template +struct Has_c_str_len().c_str())), + decltype(void(std::declval().length()))> : std::true_type {}; + + +/** + * @brief A specialized container class to hold BLE attribute values. + * @details This class is designed to be more memory efficient than using\n + * standard container types for value storage, while being convertable to\n + * many different container classes. + */ +class NimBLEAttValue +{ + uint8_t* m_attr_value = nullptr; + uint16_t m_attr_max_len = 0; + uint16_t m_attr_len = 0; + uint16_t m_capacity = 0; +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + time_t m_timestamp = 0; +#endif + void deepCopy(const NimBLEAttValue & source); + +public: + /** + * @brief Default constructor. + * @param[in] init_len The initial size in bytes. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + + /** + * @brief Construct with an initial value from a buffer. + * @param value A pointer to the initial value to set. + * @param[in] len The size in bytes of the value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(const uint8_t *value, uint16_t len, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + + /** + * @brief Construct with an initializer list. + * @param list An initializer list containing the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(std::initializer_list list, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + :NimBLEAttValue(list.begin(), (uint16_t)list.size(), max_len){} + + /** + * @brief Construct with an initial value from a const char string. + * @param value A pointer to the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + :NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){} + + /** + * @brief Construct with an initial value from a std::string. + * @param str A std::string containing to the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + :NimBLEAttValue((uint8_t*)str.data(), (uint16_t)str.length(), max_len){} + + /** + * @brief Construct with an initial value from a std::vector. + * @param vec A std::vector containing to the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(const std::vector vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + :NimBLEAttValue(&vec[0], (uint16_t)vec.size(), max_len){} + +#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE + /** + * @brief Construct with an initial value from an Arduino String. + * @param str An Arduino String containing to the initial value to set. + * @param[in] max_len The max size in bytes that the value can be. + */ + NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN) + :NimBLEAttValue((uint8_t*)str.c_str(), str.length(), max_len){} +#endif + + /** @brief Copy constructor */ + NimBLEAttValue(const NimBLEAttValue & source) { deepCopy(source); } + + /** @brief Move constructor */ + NimBLEAttValue(NimBLEAttValue && source) { *this = std::move(source); } + + /** @brief Destructor */ + ~NimBLEAttValue(); + + /** @brief Returns the max size in bytes */ + uint16_t max_size() const { return m_attr_max_len; } + + /** @brief Returns the currently allocated capacity in bytes */ + uint16_t capacity() const { return m_capacity; } + + /** @brief Returns the current length of the value in bytes */ + uint16_t length() const { return m_attr_len; } + + /** @brief Returns the current size of the value in bytes */ + uint16_t size() const { return m_attr_len; } + + /** @brief Returns a pointer to the internal buffer of the value */ + const uint8_t* data() const { return m_attr_value; } + + /** @brief Returns a pointer to the internal buffer of the value as a const char* */ + const char* c_str() const { return (const char*)m_attr_value; } + + /** @brief Iterator begin */ + const uint8_t* begin() const { return m_attr_value; } + + /** @brief Iterator end */ + const uint8_t* end() const { return m_attr_value + m_attr_len; } + +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + /** @brief Returns a timestamp of when the value was last updated */ + time_t getTimeStamp() const { return m_timestamp; } + + /** @brief Set the timestamp to the current time */ + void setTimeStamp() { m_timestamp = time(nullptr); } + + /** + * @brief Set the timestamp to the specified time + * @param[in] t The timestamp value to set + */ + void setTimeStamp(time_t t) { m_timestamp = t; } +#else + time_t getTimeStamp() const { return 0; } + void setTimeStamp() { } + void setTimeStamp(time_t t) { } +#endif + + /** + * @brief Set the value from a buffer + * @param[in] value A ponter to a buffer containing the value. + * @param[in] len The length of the value in bytes. + * @returns True if successful. + */ + bool setValue(const uint8_t *value, uint16_t len); + + /** + * @brief Set value to the value of const char*. + * @param [in] s A ponter to a const char value to set. + */ + bool setValue(const char* s) { + return setValue((uint8_t*)s, (uint16_t)strlen(s)); } + + /** + * @brief Get a pointer to the value buffer with timestamp. + * @param[in] timestamp A ponter to a time_t variable to store the timestamp. + * @returns A pointer to the internal value buffer. + */ + const uint8_t* getValue(time_t *timestamp); + + /** + * @brief Append data to the value. + * @param[in] value A ponter to a data buffer with the value to append. + * @param[in] len The length of the value to append in bytes. + * @returns A reference to the appended NimBLEAttValue. + */ + NimBLEAttValue& append(const uint8_t *value, uint16_t len); + + + /*********************** Template Functions ************************/ + + /** + * @brief Template to set value to the value of val. + * @param [in] s The value to set. + * @details Only used for types without a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value, bool>::type +#endif + setValue(const T &s) { + return setValue((uint8_t*)&s, sizeof(T)); + } + + /** + * @brief Template to set value to the value of val. + * @param [in] s The value to set. + * @details Only used if the has a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value, bool>::type +#endif + setValue(const T & s) { + return setValue((uint8_t*)s.c_str(), (uint16_t)s.length()); + } + + /** + * @brief Template to return the value as a . + * @tparam T The type to convert the data to. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than\n + * sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is\n + * less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + */ + template + T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { + if(!skipSizeCheck && size() < sizeof(T)) { + return T(); + } + return *((T *)getValue(timestamp)); + } + + + /*********************** Operators ************************/ + + /** @brief Subscript operator */ + uint8_t operator [](int pos) const { + assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; } + + /** @brief Operator; Get the value as a std::vector. */ + operator std::vector() const { + return std::vector(m_attr_value, m_attr_value + m_attr_len); } + + /** @brief Operator; Get the value as a std::string. */ + operator std::string() const { + return std::string((char*)m_attr_value, m_attr_len); } + + /** @brief Operator; Get the value as a const uint8_t*. */ + operator const uint8_t*() const { return m_attr_value; } + + /** @brief Operator; Append another NimBLEAttValue. */ + NimBLEAttValue& operator +=(const NimBLEAttValue & source) { + return append(source.data(), source.size()); } + + /** @brief Operator; Set the value from a std::string source. */ + NimBLEAttValue& operator =(const std::string & source) { + setValue((uint8_t*)source.data(), (uint16_t)source.size()); return *this; } + + /** @brief Move assignment operator */ + NimBLEAttValue& operator =(NimBLEAttValue && source); + + /** @brief Copy assignment operator */ + NimBLEAttValue& operator =(const NimBLEAttValue & source); + + /** @brief Equality operator */ + bool operator ==(const NimBLEAttValue & source) { + return (m_attr_len == source.size()) ? + memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false; } + + /** @brief Inequality operator */ + bool operator !=(const NimBLEAttValue & source){ return !(*this == source); } + +#ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE + /** @brief Operator; Get the value as an Arduino String value. */ + operator String() const { return String((char*)m_attr_value); } +#endif + +}; + + +inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) { + m_attr_value = (uint8_t*)calloc(init_len + 1, 1); + assert(m_attr_value && "No Mem"); + m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len); + m_attr_len = 0; + m_capacity = init_len; + setTimeStamp(0); +} + +inline NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len) +: NimBLEAttValue(len, max_len) { + memcpy(m_attr_value, value, len); + m_attr_value[len] = '\0'; + m_attr_len = len; +} + +inline NimBLEAttValue::~NimBLEAttValue() { + if(m_attr_value != nullptr) { + free(m_attr_value); + } +} + +inline NimBLEAttValue& NimBLEAttValue::operator =(NimBLEAttValue && source) { + if (this != &source){ + free(m_attr_value); + + m_attr_value = source.m_attr_value; + m_attr_max_len = source.m_attr_max_len; + m_attr_len = source.m_attr_len; + m_capacity = source.m_capacity; + setTimeStamp(source.getTimeStamp()); + source.m_attr_value = nullptr; + } + return *this; +} + +inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source) { + if (this != &source) { + deepCopy(source); + } + return *this; +} + +inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) { + uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1); + assert(res && "deepCopy: realloc failed"); + + ble_npl_hw_enter_critical(); + m_attr_value = res; + m_attr_max_len = source.m_attr_max_len; + m_attr_len = source.m_attr_len; + m_capacity = source.m_capacity; + setTimeStamp(source.getTimeStamp()); + memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1); + ble_npl_hw_exit_critical(0); +} + +inline const uint8_t* NimBLEAttValue::getValue(time_t *timestamp) { + if(timestamp != nullptr) { +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + *timestamp = m_timestamp; +#else + *timestamp = 0; +#endif + } + return m_attr_value; +} + +inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) { + if (len > m_attr_max_len) { + NIMBLE_LOGE("NimBLEAttValue", "value exceeds max, len=%u, max=%u", + len, m_attr_max_len); + return false; + } + + uint8_t *res = m_attr_value; + if (len > m_capacity) { + res = (uint8_t*)realloc(m_attr_value, (len + 1)); + m_capacity = len; + } + assert(res && "setValue: realloc failed"); + +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + time_t t = time(nullptr); +#else + time_t t = 0; +#endif + + ble_npl_hw_enter_critical(); + m_attr_value = res; + memcpy(m_attr_value, value, len); + m_attr_value[len] = '\0'; + m_attr_len = len; + setTimeStamp(t); + ble_npl_hw_exit_critical(0); + return true; +} + +inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len) { + if (len < 1) { + return *this; + } + + if ((m_attr_len + len) > m_attr_max_len) { + NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u", + len, m_attr_max_len); + return *this; + } + + uint8_t* res = m_attr_value; + uint16_t new_len = m_attr_len + len; + if (new_len > m_capacity) { + res = (uint8_t*)realloc(m_attr_value, (new_len + 1)); + m_capacity = new_len; + } + assert(res && "append: realloc failed"); + +#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED + time_t t = time(nullptr); +#else + time_t t = 0; +#endif + + ble_npl_hw_enter_critical(); + m_attr_value = res; + memcpy(m_attr_value + m_attr_len, value, len); + m_attr_len = new_len; + m_attr_value[m_attr_len] = '\0'; + setTimeStamp(t); + ble_npl_hw_exit_critical(0); + + return *this; +} + +#endif /*(CONFIG_BT_ENABLED) */ +#endif /* MAIN_NIMBLEATTVALUE_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEBeacon.cpp b/lib/NimBLE-Arduino/src/NimBLEBeacon.cpp new file mode 100644 index 0000000..996893a --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEBeacon.cpp @@ -0,0 +1,157 @@ +/* + * NimBLEBeacon2.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon.cpp + * + * Created on: Jan 4, 2018 + * Author: kolban + */ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include +#include "NimBLEBeacon.h" +#include "NimBLELog.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + +static const char* LOG_TAG = "NimBLEBeacon"; + + +/** + * @brief Construct a default beacon object. + */ +NimBLEBeacon::NimBLEBeacon() { + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // NimBLEBeacon + + +/** + * @brief Retrieve the data that is being advertised. + * @return The advertised data. + */ +std::string NimBLEBeacon::getData() { + return std::string((char*) &m_beaconData, sizeof(m_beaconData)); +} // getData + + +/** + * @brief Get the major value being advertised. + * @return The major value advertised. + */ +uint16_t NimBLEBeacon::getMajor() { + return m_beaconData.major; +} + + +/** + * @brief Get the manufacturer ID being advertised. + * @return The manufacturer ID value advertised. + */ +uint16_t NimBLEBeacon::getManufacturerId() { + return m_beaconData.manufacturerId; +} + + +/** + * @brief Get the minor value being advertised. + * @return minor value advertised. + */ +uint16_t NimBLEBeacon::getMinor() { + return m_beaconData.minor; +} + + +/** + * @brief Get the proximity UUID being advertised. + * @return The UUID advertised. + */ +NimBLEUUID NimBLEBeacon::getProximityUUID() { + return NimBLEUUID(m_beaconData.proximityUUID, 16, true); +} + + +/** + * @brief Get the signal power being advertised. + * @return signal power level advertised. + */ +int8_t NimBLEBeacon::getSignalPower() { + return m_beaconData.signalPower; +} + + +/** + * @brief Set the raw data for the beacon record. + * @param [in] data The raw beacon data. + */ +void NimBLEBeacon::setData(const std::string &data) { + if (data.length() != sizeof(m_beaconData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_beaconData)); + return; + } + memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); +} // setData + + +/** + * @brief Set the major value. + * @param [in] major The major value. + */ +void NimBLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = ENDIAN_CHANGE_U16(major); +} // setMajor + + +/** + * @brief Set the manufacturer ID. + * @param [in] manufacturerId The manufacturer ID value. + */ +void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); +} // setManufacturerId + + +/** + * @brief Set the minor value. + * @param [in] minor The minor value. + */ +void NimBLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); +} // setMinior + + +/** + * @brief Set the proximity UUID. + * @param [in] uuid The proximity UUID. + */ +void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) { + NimBLEUUID temp_uuid = uuid; + temp_uuid.to128(); + std::reverse_copy(temp_uuid.getNative()->u128.value, + temp_uuid.getNative()->u128.value + 16, + m_beaconData.proximityUUID); +} // setProximityUUID + + +/** + * @brief Set the signal power. + * @param [in] signalPower The signal power value. + */ +void NimBLEBeacon::setSignalPower(int8_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLEBeacon.h b/lib/NimBLE-Arduino/src/NimBLEBeacon.h new file mode 100644 index 0000000..82ee61c --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEBeacon.h @@ -0,0 +1,51 @@ +/* + * NimBLEBeacon2.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon2.h + * + * Created on: Jan 4, 2018 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEBEACON_H_ +#define MAIN_NIMBLEBEACON_H_ + +#include "NimBLEUUID.h" +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class NimBLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + int8_t signalPower; + } __attribute__((packed)) m_beaconData; +public: + NimBLEBeacon(); + std::string getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + NimBLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(const std::string &data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(const NimBLEUUID &uuid); + void setSignalPower(int8_t signalPower); +}; // NimBLEBeacon + +#endif /* MAIN_NIMBLEBEACON_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/lib/NimBLE-Arduino/src/NimBLECharacteristic.cpp new file mode 100644 index 0000000..3b95030 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLECharacteristic.cpp @@ -0,0 +1,659 @@ +/* + * NimBLECharacteristic.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * BLECharacteristic.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLECharacteristic.h" +#include "NimBLE2904.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#define NULL_HANDLE (0xffff) +#define NIMBLE_SUB_NOTIFY 0x0001 +#define NIMBLE_SUB_INDICATE 0x0002 + +static NimBLECharacteristicCallbacks defaultCallback; +static const char* LOG_TAG = "NimBLECharacteristic"; + + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID (const char*) for the characteristic. + * @param [in] properties - Properties for the characteristic. + * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] pService - pointer to the service instance this characteristic belongs to. + */ +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, + uint16_t max_len, NimBLEService* pService) +: NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) { +} + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID for the characteristic. + * @param [in] properties - Properties for the characteristic. + * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] pService - pointer to the service instance this characteristic belongs to. + */ +NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, + uint16_t max_len, NimBLEService* pService) +: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_properties = properties; + m_pCallbacks = &defaultCallback; + m_pService = pService; + m_removed = 0; +} // NimBLECharacteristic + +/** + * @brief Destructor. + */ +NimBLECharacteristic::~NimBLECharacteristic() { + for(auto &it : m_dscVec) { + delete it; + } +} // ~NimBLECharacteristic + + +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @param [in] max_len - The max length in bytes of the descriptor value. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) { + return createDescriptor(NimBLEUUID(uuid), properties, max_len); +} + + +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @param [in] max_len - The max length in bytes of the descriptor value. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) { + NimBLEDescriptor* pDescriptor = nullptr; + if(uuid == NimBLEUUID(uint16_t(0x2902))) { + assert(0 && "0x2902 descriptors cannot be manually created"); + } else if (uuid == NimBLEUUID(uint16_t(0x2904))) { + pDescriptor = new NimBLE2904(this); + } else { + pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); + } + + addDescriptor(pDescriptor); + return pDescriptor; +} // createDescriptor + + +/** + * @brief Add a descriptor to the characteristic. + * @param [in] pDescriptor A pointer to the descriptor to add. + */ +void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) { + bool foundRemoved = false; + + if(pDescriptor->m_removed > 0) { + for(auto& it : m_dscVec) { + if(it == pDescriptor) { + foundRemoved = true; + pDescriptor->m_removed = 0; + } + } + } + + if(!foundRemoved) { + m_dscVec.push_back(pDescriptor); + } + + pDescriptor->setCharacteristic(this); + NimBLEDevice::getServer()->serviceChanged(); +} + + +/** + * @brief Remove a descriptor from the characterisitc. + * @param[in] pDescriptor A pointer to the descriptor instance to remove from the characterisitc. + * @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources. + */ +void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc) { + // Check if the descriptor was already removed and if so, check if this + // is being called to delete the object and do so if requested. + // Otherwise, ignore the call and return. + if(pDescriptor->m_removed > 0) { + if(deleteDsc) { + for(auto it = m_dscVec.begin(); it != m_dscVec.end(); ++it) { + if ((*it) == pDescriptor) { + delete *it; + m_dscVec.erase(it); + break; + } + } + } + + return; + } + + pDescriptor->m_removed = deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + NimBLEDevice::getServer()->serviceChanged(); +} // removeDescriptor + + +/** + * @brief Return the BLE Descriptor for the given UUID. + * @param [in] uuid The UUID of the descriptor. + * @return A pointer to the descriptor object or nullptr if not found. + */ +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) { + return getDescriptorByUUID(NimBLEUUID(uuid)); +} // getDescriptorByUUID + + +/** + * @brief Return the BLE Descriptor for the given UUID. + * @param [in] uuid The UUID of the descriptor. + * @return A pointer to the descriptor object or nullptr if not found. + */ +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) { + for (auto &it : m_dscVec) { + if (it->getUUID() == uuid) { + return it; + } + } + return nullptr; +} // getDescriptorByUUID + +/** + * @brief Return the BLE Descriptor for the given handle. + * @param [in] handle The handle of the descriptor. + * @return A pointer to the descriptor object or nullptr if not found. + */ +NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) { + for (auto &it : m_dscVec) { + if (it->getHandle() == handle) { + return it; + } + } + return nullptr; +} + + +/** + * @brief Get the handle of the characteristic. + * @return The handle of the characteristic. + */ +uint16_t NimBLECharacteristic::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the properties of the characteristic. + * @return The properties of the characteristic. + */ +uint16_t NimBLECharacteristic::getProperties() { + return m_properties; +} // getProperties + + +/** + * @brief Get the service associated with this characteristic. + */ +NimBLEService* NimBLECharacteristic::getService() { + return m_pService; +} // getService + + +void NimBLECharacteristic::setService(NimBLEService *pService) { + m_pService = pService; +} + + +/** + * @brief Get the UUID of the characteristic. + * @return The UUID of the characteristic. + */ +NimBLEUUID NimBLECharacteristic::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Retrieve the current value of the characteristic. + * @return The NimBLEAttValue containing the current characteristic value. + */ +NimBLEAttValue NimBLECharacteristic::getValue(time_t *timestamp) { + if(timestamp != nullptr) { + m_value.getValue(timestamp); + } + + return m_value; +} // getValue + + +/** + * @brief Retrieve the the current data length of the characteristic. + * @return The length of the current characteristic data. + */ +size_t NimBLECharacteristic::getDataLength() { + return m_value.size(); +} + + +/** + * @brief STATIC callback to handle events from the NimBLE stack. + */ +int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rc; + struct ble_gap_conn_desc desc; + NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg; + + NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); + + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: { + // If the packet header is only 8 bytes this is a follow up of a long read + // so we don't want to call the onRead() callback again. + if(ctxt->om->om_pkthdr_len > 8) { + rc = ble_gap_conn_find(conn_handle, &desc); + assert(rc == 0); + pCharacteristic->m_pCallbacks->onRead(pCharacteristic); + pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc); + } + + ble_npl_hw_enter_critical(); + rc = os_mbuf_append(ctxt->om, pCharacteristic->m_value.data(), pCharacteristic->m_value.size()); + ble_npl_hw_exit_critical(0); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_CHR: { + uint16_t att_max_len = pCharacteristic->m_value.max_size(); + + if (ctxt->om->om_len > att_max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + uint8_t buf[att_max_len]; + size_t len = ctxt->om->om_len; + memcpy(buf, ctxt->om->om_data,len); + + os_mbuf *next; + next = SLIST_NEXT(ctxt->om, om_next); + while(next != NULL){ + if((len + next->om_len) > att_max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + memcpy(&buf[len], next->om_data, next->om_len); + len += next->om_len; + next = SLIST_NEXT(next, om_next); + } + rc = ble_gap_conn_find(conn_handle, &desc); + assert(rc == 0); + pCharacteristic->setValue(buf, len); + pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); + pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc); + return 0; + } + default: + break; + } + } + + return BLE_ATT_ERR_UNLIKELY; +} + + +/** + * @brief Get the number of clients subscribed to the characteristic. + * @returns Number of clients subscribed to notifications / indications. + */ +size_t NimBLECharacteristic::getSubscribedCount() { + return m_subscribedVec.size(); +} + + +/** + * @brief Set the subscribe status for this characteristic.\n + * This will maintain a vector of subscribed clients and their indicate/notify status. + */ +void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { + ble_gap_conn_desc desc; + if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) { + return; + } + + uint16_t subVal = 0; + if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) { + subVal |= NIMBLE_SUB_NOTIFY; + } + if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) { + subVal |= NIMBLE_SUB_INDICATE; + } + + NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", + event->subscribe.conn_handle, subVal); + + if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) { + NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle); + } + + + auto it = m_subscribedVec.begin(); + for(;it != m_subscribedVec.end(); ++it) { + if((*it).first == event->subscribe.conn_handle) { + break; + } + } + + if(subVal > 0) { + if(it == m_subscribedVec.end()) { + m_subscribedVec.push_back({event->subscribe.conn_handle, subVal}); + } else { + (*it).second = subVal; + } + } else if(it != m_subscribedVec.end()) { + m_subscribedVec.erase(it); + } + + m_pCallbacks->onSubscribe(this, &desc, subVal); +} + + +/** + * @brief Send an indication. + */ +void NimBLECharacteristic::indicate() { + notify(false); +} // indicate + + +/** + * @brief Send an indication. + * @param[in] value A pointer to the data to send. + * @param[in] length The length of the data to send. + */ +void NimBLECharacteristic::indicate(const uint8_t* value, size_t length) { + notify(value, length, false); +} // indicate + + +/** + * @brief Send an indication. + * @param[in] value A std::vector containing the value to send as the notification value. + */ +void NimBLECharacteristic::indicate(const std::vector& value) { + notify(value.data(), value.size(), false); +} // indicate + + +/** + * @brief Send a notification or indication. + * @param[in] is_notification if true sends a notification, false sends an indication. + */ +void NimBLECharacteristic::notify(bool is_notification) { + notify(m_value.data(), m_value.length(), is_notification); +} // notify + + +/** + * @brief Send a notification or indication. + * @param[in] value A std::vector containing the value to send as the notification value. + * @param[in] is_notification if true sends a notification, false sends an indication. + */ +void NimBLECharacteristic::notify(const std::vector& value, bool is_notification) { + notify(value.data(), value.size(), is_notification); +} // notify + + +/** + * @brief Send a notification or indication. + * @param[in] value A pointer to the data to send. + * @param[in] length The length of the data to send. + * @param[in] is_notification if true sends a notification, false sends an indication. + */ +void NimBLECharacteristic::notify(const uint8_t* value, size_t length, bool is_notification) { + NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", length); + + if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) && + !(m_properties & NIMBLE_PROPERTY::INDICATE)) + { + NIMBLE_LOGE(LOG_TAG, + "<< notify-Error; Notify/indicate not enabled for characterisitc: %s", + std::string(getUUID()).c_str()); + } + + if (m_subscribedVec.size() == 0) { + NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed."); + return; + } + + m_pCallbacks->onNotify(this); + + bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || + (m_properties & BLE_GATT_CHR_F_READ_AUTHOR) || + (m_properties & BLE_GATT_CHR_F_READ_ENC); + int rc = 0; + + for (auto &it : m_subscribedVec) { + uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first) - 3; + + // check if connected and subscribed + if(_mtu == 0 || it.second == 0) { + continue; + } + + // check if security requirements are satisfied + if(reqSec) { + struct ble_gap_conn_desc desc; + rc = ble_gap_conn_find(it.first, &desc); + if(rc != 0 || !desc.sec_state.encrypted) { + continue; + } + } + + if (length > _mtu) { + NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu); + } + + if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) { + NIMBLE_LOGW(LOG_TAG, + "Sending notification to client subscribed to indications, sending indication instead"); + is_notification = false; + } + + if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) { + NIMBLE_LOGW(LOG_TAG, + "Sending indication to client subscribed to notification, sending notification instead"); + is_notification = true; + } + + // don't create the m_buf until we are sure to send the data or else + // we could be allocating a buffer that doesn't get released. + // We also must create it in each loop iteration because it is consumed with each host call. + os_mbuf *om = ble_hs_mbuf_from_flat(value, length); + + if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) { + if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) { + NIMBLE_LOGE(LOG_TAG, "prior Indication in progress"); + os_mbuf_free_chain(om); + return; + } + + rc = ble_gattc_indicate_custom(it.first, m_handle, om); + if(rc != 0){ + NimBLEDevice::getServer()->clearIndicateWait(it.first); + } + } else { + ble_gattc_notify_custom(it.first, m_handle, om); + } + } + + NIMBLE_LOGD(LOG_TAG, "<< notify"); +} // Notify + + +/** + * @brief Set the callback handlers for this characteristic. + * @param [in] pCallbacks An instance of a NimBLECharacteristicCallbacks class\n + * used to define any callbacks for the characteristic. + */ +void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallback; + } +} // setCallbacks + +/** + * @brief Get the callback handlers for this characteristic. + */ +NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() { + return m_pCallbacks; +} //getCallbacks + + +/** + * @brief Set the value of the characteristic from a data buffer . + * @param [in] data The data buffer to set for the characteristic. + * @param [in] length The number of bytes in the data buffer. + */ +void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) { +#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 + char* pHex = NimBLEUtils::buildHexData(nullptr, data, length); + NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", + length, pHex, getUUID().toString().c_str()); + free(pHex); +#endif + + m_value.setValue(data, length); + NIMBLE_LOGD(LOG_TAG, "<< setValue"); +} // setValue + + +/** + * @brief Set the value of the characteristic from a `std::vector`.\n + * @param [in] vec The std::vector reference to set the characteristic value from. + */ +void NimBLECharacteristic::setValue(const std::vector& vec) { + return setValue((uint8_t*)&vec[0], vec.size()); +}// setValue + + +/** + * @brief Return a string representation of the characteristic. + * @return A string representation of the characteristic. + */ +std::string NimBLECharacteristic::toString() { + std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x"; + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + res += hex; + res += " "; + if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse "; + if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast "; + if (m_properties & BLE_GATT_CHR_PROP_NOTIFY) res += "Notify "; + if (m_properties & BLE_GATT_CHR_PROP_INDICATE) res += "Indicate "; + return res; +} // toString + + +NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {} + + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); +} // onRead + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] desc The connection description struct that is associated with the peer that performed the read. + */ +void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); +} // onRead + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); +} // onWrite + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] desc The connection description struct that is associated with the peer that performed the write. + */ +void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); +} // onWrite + +/** + * @brief Callback function to support a Notify request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default"); +} // onNotify + + +/** + * @brief Callback function to support a Notify/Indicate Status report. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] s Status of the notification/indication. + * @param [in] code Additional return code from the NimBLE stack. + */ +void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default"); +} // onStatus + + +/** + * @brief Callback function called when a client changes subscription status. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] desc The connection description struct that is associated with the client. + * @param [in] subValue The subscription status: + * * 0 = Un-Subscribed + * * 1 = Notifications + * * 2 = Indications + * * 3 = Notifications and Indications + */ +void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic, + ble_gap_conn_desc* desc, + uint16_t subValue) +{ + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default"); +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLECharacteristic.h b/lib/NimBLE-Arduino/src/NimBLECharacteristic.h new file mode 100644 index 0000000..0f84e2d --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLECharacteristic.h @@ -0,0 +1,231 @@ +/* + * NimBLECharacteristic.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * BLECharacteristic.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLECHARACTERISTIC_H_ +#define MAIN_NIMBLECHARACTERISTIC_H_ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_hs.h" +#else +#include "nimble/nimble/host/include/host/ble_hs.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +typedef enum { + READ = BLE_GATT_CHR_F_READ, + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE = BLE_GATT_CHR_F_WRITE, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, + WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, + WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, + WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, + BROADCAST = BLE_GATT_CHR_F_BROADCAST, + NOTIFY = BLE_GATT_CHR_F_NOTIFY, + INDICATE = BLE_GATT_CHR_F_INDICATE +} NIMBLE_PROPERTY; + +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "NimBLEAttValue.h" + +#include +#include + +class NimBLEService; +class NimBLEDescriptor; +class NimBLECharacteristicCallbacks; + + +/** + * @brief The model of a %BLE Characteristic. + * + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and + * can be read and written to by a %BLE client. + */ +class NimBLECharacteristic { +public: + NimBLECharacteristic(const char* uuid, + uint16_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, + NimBLEService* pService = nullptr); + NimBLECharacteristic(const NimBLEUUID &uuid, + uint16_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, + NimBLEService* pService = nullptr); + + ~NimBLECharacteristic(); + + uint16_t getHandle(); + NimBLEUUID getUUID(); + std::string toString(); + void indicate(); + void indicate(const uint8_t* value, size_t length); + void indicate(const std::vector& value); + void notify(bool is_notification = true); + void notify(const uint8_t* value, size_t length, bool is_notification = true); + void notify(const std::vector& value, bool is_notification = true); + size_t getSubscribedCount(); + void addDescriptor(NimBLEDescriptor *pDescriptor); + NimBLEDescriptor* getDescriptorByUUID(const char* uuid); + NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); + NimBLEDescriptor* getDescriptorByHandle(uint16_t handle); + void removeDescriptor(NimBLEDescriptor *pDescriptor, bool deleteDsc = false); + NimBLEService* getService(); + uint16_t getProperties(); + NimBLEAttValue getValue(time_t *timestamp = nullptr); + size_t getDataLength(); + void setValue(const uint8_t* data, size_t size); + void setValue(const std::vector& vec); + void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); + NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);; + NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + + NimBLECharacteristicCallbacks* getCallbacks(); + + + /*********************** Template Functions ************************/ + + /** + * @brief Template to set the characteristic value to val. + * @param [in] s The value to set. + */ + template + void setValue(const T &s) { m_value.setValue(s); } + + /** + * @brief Template to convert the characteristic data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + */ + template + T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { + return m_value.getValue(timestamp, skipSizeCheck); + } + + /** + * @brief Template to send a notification from a class type that has a c_str() and length() method. + * @tparam T The a reference to a class containing the data to send. + * @param[in] value The value to set. + * @param[in] is_notification if true sends a notification, false sends an indication. + * @details Only used if the has a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + void +#else + typename std::enable_if::value, void>::type +#endif + notify(const T& value, bool is_notification = true) { + notify((uint8_t*)value.c_str(), value.length(), is_notification); + } + + /** + * @brief Template to send an indication from a class type that has a c_str() and length() method. + * @tparam T The a reference to a class containing the data to send. + * @param[in] value The value to set. + * @details Only used if the has a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + void +#else + typename std::enable_if::value, void>::type +#endif + indicate(const T& value) { + indicate((uint8_t*)value.c_str(), value.length()); + } + +private: + + friend class NimBLEServer; + friend class NimBLEService; + + void setService(NimBLEService *pService); + void setSubscribe(struct ble_gap_event *event); + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + + NimBLEUUID m_uuid; + uint16_t m_handle; + uint16_t m_properties; + NimBLECharacteristicCallbacks* m_pCallbacks; + NimBLEService* m_pService; + NimBLEAttValue m_value; + std::vector m_dscVec; + uint8_t m_removed; + + std::vector> m_subscribedVec; +}; // NimBLECharacteristic + + +/** + * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. + * + * When a server application creates a %BLE characteristic, we may wish to be informed when there is either + * a read or write request to the characteristic's value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLECharacteristicCallbacks { +public: + +/** + * @brief An enum to provide the callback the status of the + * notification/indication, implemented for backward compatibility. + * @deprecated To be removed in the future as the NimBLE stack return code is also provided. + */ + typedef enum { + SUCCESS_INDICATE, + SUCCESS_NOTIFY, + ERROR_INDICATE_DISABLED, + ERROR_NOTIFY_DISABLED, + ERROR_GATT, + ERROR_NO_CLIENT, + ERROR_INDICATE_TIMEOUT, + ERROR_INDICATE_FAILURE + }Status; + + virtual ~NimBLECharacteristicCallbacks(); + virtual void onRead(NimBLECharacteristic* pCharacteristic); + virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc); + virtual void onWrite(NimBLECharacteristic* pCharacteristic); + virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc); + virtual void onNotify(NimBLECharacteristic* pCharacteristic); + virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code); + virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue); +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ diff --git a/lib/NimBLE-Arduino/src/NimBLEClient.cpp b/lib/NimBLE-Arduino/src/NimBLEClient.cpp new file mode 100644 index 0000000..d822732 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEClient.cpp @@ -0,0 +1,1247 @@ +/* + * NimBLEClient.cpp + * + * Created: on Jan 26 2020 + * Author H2zero + * + * Originally: + * BLEClient.cpp + * + * Created on: Mar 22, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLEClient.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#include +#include +#include + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "nimble/nimble_port.h" +#else +#include "nimble/porting/nimble/include/nimble/nimble_port.h" +#endif + +static const char* LOG_TAG = "NimBLEClient"; +static NimBLEClientCallbacks defaultCallbacks; + +/* + * Design + * ------ + * When we perform a getService() request, we are asking the BLE server to return each of the services + * that it exposes. For each service, we receive a callback which contains details + * of the exposed service including its UUID. + * + * The objects we will invent for a NimBLEClient will be as follows: + * * NimBLERemoteService - A model of a remote service. + * * NimBLERemoteCharacteristic - A model of a remote characteristic + * * NimBLERemoteDescriptor - A model of a remote descriptor. + * + * Since there is a hierarchical relationship here, we will have the idea that from a NimBLERemoteService will own + * zero or more remote characteristics and a NimBLERemoteCharacteristic will own zero or more remote NimBLEDescriptors. + * + * We will assume that a NimBLERemoteService contains a vector of owned characteristics + * and that a NimBLECharacteristic contains a vector of owned descriptors. + * + * + */ + + +/** + * @brief Constructor, private - only callable by NimBLEDevice::createClient + * to ensure proper handling of the list of client objects. + */ +NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) { + m_pClientCallbacks = &defaultCallbacks; + m_conn_id = BLE_HS_CONN_HANDLE_NONE; + m_connectTimeout = 30000; + m_deleteCallbacks = false; + m_pTaskData = nullptr; + m_connEstablished = false; + m_lastErr = 0; + + m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) + m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) + m_pConnParams.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN; // min_int = 0x10*1.25ms = 20ms + m_pConnParams.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX; // max_int = 0x20*1.25ms = 40ms + m_pConnParams.latency = BLE_GAP_INITIAL_CONN_LATENCY; // number of packets allowed to skip (extends max interval) + m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms + m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units + m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units + + memset(&m_dcTimer, 0, sizeof(m_dcTimer)); + ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(), + NimBLEClient::dcTimerCb, this); +} // NimBLEClient + + +/** + * @brief Destructor, private - only callable by NimBLEDevice::deleteClient + * to ensure proper disconnect and removal from device list. + */ +NimBLEClient::~NimBLEClient() { + // We may have allocated service references associated with this client. + // Before we are finished with the client, we must release resources. + deleteServices(); + + if(m_deleteCallbacks && m_pClientCallbacks != &defaultCallbacks) { + delete m_pClientCallbacks; + } + + ble_npl_callout_deinit(&m_dcTimer); + +} // ~NimBLEClient + + +/** + * @brief If we have asked to disconnect and the event does not + * occur within the supervision timeout + added delay, this will + * be called to reset the host in the case of a stalled controller. + */ +void NimBLEClient::dcTimerCb(ble_npl_event *event) { + /* NimBLEClient *pClient = (NimBLEClient*)event->arg; + NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host", + std::string(pClient->getPeerAddress()).c_str()); + */ + ble_hs_sched_reset(BLE_HS_ECONTROLLER); +} + + +/** + * @brief Delete all service objects created by this client and clear the vector. + */ +void NimBLEClient::deleteServices() { + NIMBLE_LOGD(LOG_TAG, ">> deleteServices"); + // Delete all the services. + for(auto &it: m_servicesVector) { + delete it; + } + m_servicesVector.clear(); + + NIMBLE_LOGD(LOG_TAG, "<< deleteServices"); +} // deleteServices + + +/** + * @brief Delete service by UUID + * @param [in] uuid The UUID of the service to be deleted from the local database. + * @return Number of services left. + */ +size_t NimBLEClient::deleteService(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> deleteService"); + // Delete the requested service. + for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) { + if((*it)->getUUID() == uuid) { + delete *it; + m_servicesVector.erase(it); + break; + } + } + + NIMBLE_LOGD(LOG_TAG, "<< deleteService"); + + return m_servicesVector.size(); +} // deleteServices + + +/** + * @brief Connect to the BLE Server. + * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n + * have created and clears the vectors after successful connection. + * @return True on success. + */ +bool NimBLEClient::connect(bool deleteAttibutes) { + return connect(m_peerAddress, deleteAttibutes); +} + +/** + * @brief Connect to an advertising device. + * @param [in] device The device to connect to. + * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n + * have created and clears the vectors after successful connection. + * @return True on success. + */ +bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes) { + NimBLEAddress address(device->getAddress()); + return connect(address, deleteAttibutes); +} + + +/** + * @brief Connect to the BLE Server. + * @param [in] address The address of the server. + * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n + * have created and clears the vectors after successful connection. + * @return True on success. + */ +bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) { + NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); + return false; + } + + if(isConnected() || m_connEstablished || m_pTaskData != nullptr) { + NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d", + std::string(m_peerAddress).c_str(), getConnId()); + return false; + } + + ble_addr_t peerAddr_t; + memcpy(&peerAddr_t.val, address.getNative(),6); + peerAddr_t.type = address.getType(); + if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) { + NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists", + address.toString().c_str()); + return false; + } + + if(address == NimBLEAddress("")) { + NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)"); + return false; + } else { + m_peerAddress = address; + } + + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + m_pTaskData = &taskData; + int rc = 0; + + /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for + * timeout (default value of m_connectTimeout). + * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. + */ + do { + rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t, + m_connectTimeout, &m_pConnParams, + NimBLEClient::handleGapEvent, this); + switch (rc) { + case 0: + break; + + case BLE_HS_EBUSY: + // Scan was still running, stop it and try again + if (!NimBLEDevice::getScan()->stop()) { + rc = BLE_HS_EUNKNOWN; + } + break; + + case BLE_HS_EDONE: + // A connection to this device already exists, do not connect twice. + NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s", + std::string(m_peerAddress).c_str()); + break; + + case BLE_HS_EALREADY: + // Already attemting to connect to this device, cancel the previous + // attempt and report failure here so we don't get 2 connections. + NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling", + std::string(m_peerAddress).c_str()); + ble_gap_conn_cancel(); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s", + std::string(m_peerAddress).c_str(), + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + + } while (rc == BLE_HS_EBUSY); + + m_lastErr = rc; + + if(rc != 0) { + m_pTaskData = nullptr; + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + // Wait for the connect timeout time +1 second for the connection to complete + if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) { + m_pTaskData = nullptr; + // If a connection was made but no response from MTU exchange; disconnect + if(isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response"); + disconnect(); + } else { + // workaround; if the controller doesn't cancel the connection + // at the timeout, cancel it here. + NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling"); + ble_gap_conn_cancel(); + } + + return false; + + } else if(taskData.rc != 0){ + m_lastErr = taskData.rc; + NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", + taskData.rc, + NimBLEUtils::returnCodeToString(taskData.rc)); + // If the failure was not a result of a disconnection + // make sure we disconnect now to avoid dangling connections + if(isConnected()) { + disconnect(); + } + return false; + } else { + NIMBLE_LOGI(LOG_TAG, "Connection established"); + } + + if(deleteAttibutes) { + deleteServices(); + } + + m_connEstablished = true; + m_pClientCallbacks->onConnect(this); + + NIMBLE_LOGD(LOG_TAG, "<< connect()"); + // Check if still connected before returning + return isConnected(); +} // connect + + +/** + * @brief Initiate a secure connection (pair/bond) with the server.\n + * Called automatically when a characteristic or descriptor requires encryption or authentication to access it. + * @return True on success. + */ +bool NimBLEClient::secureConnection() { + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + int retryCount = 1; + + do { + m_pTaskData = &taskData; + + int rc = NimBLEDevice::startSecurity(m_conn_id); + if(rc != 0){ + m_lastErr = rc; + m_pTaskData = nullptr; + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--); + + if(taskData.rc != 0){ + m_lastErr = taskData.rc; + return false; + } + + return true; +} // secureConnection + + +/** + * @brief Disconnect from the peer. + * @return Error code from NimBLE stack, 0 = success. + */ +int NimBLEClient::disconnect(uint8_t reason) { + NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); + int rc = 0; + if(isConnected()) { + // If the timer was already started, ignore this call. + if(ble_npl_callout_is_active(&m_dcTimer)) { + NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started"); + return BLE_HS_EALREADY; + } + + ble_gap_conn_desc desc; + if(ble_gap_conn_find(m_conn_id, &desc) != 0){ + NIMBLE_LOGI(LOG_TAG, "Connection ID not found"); + return BLE_HS_EALREADY; + } + + // We use a timer to detect a controller error in the event that it does + // not inform the stack when disconnection is complete. + // This is a common error in certain esp-idf versions. + // The disconnect timeout time is the supervison timeout time + 1 second. + // In the case that the event happenss shortly after the supervision timeout + // we don't want to prematurely reset the host. + ble_npl_time_t ticks; + ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks); + ble_npl_callout_reset(&m_dcTimer, ticks); + + rc = ble_gap_terminate(m_conn_id, reason); + if (rc != 0) { + if(rc != BLE_HS_EALREADY) { + ble_npl_callout_stop(&m_dcTimer); + } + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", + rc, NimBLEUtils::returnCodeToString(rc)); + } + } else { + NIMBLE_LOGD(LOG_TAG, "Not connected to any peers"); + } + + NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); + m_lastErr = rc; + return rc; +} // disconnect + + +/** + * @brief Set the connection paramaters to use when connecting to a server. + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. + * @param [in] scanInterval The scan interval to use when attempting to connect in 0.625ms units. + * @param [in] scanWindow The scan window to use when attempting to connect in 0.625ms units. + */ +void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t scanInterval, uint16_t scanWindow)/*, + uint16_t minConnTime, uint16_t maxConnTime)*/ +{ + + m_pConnParams.scan_itvl = scanInterval; + m_pConnParams.scan_window = scanWindow; + m_pConnParams.itvl_min = minInterval; + m_pConnParams.itvl_max = maxInterval; + m_pConnParams.latency = latency; + m_pConnParams.supervision_timeout = timeout; + + // These are not used by NimBLE at this time - Must leave at defaults + //m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units + //m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = NimBLEUtils::checkConnParams(&m_pConnParams); + assert(rc == 0 && "Invalid Connection parameters"); +} // setConnectionParams + + +/** + * @brief Update the connection parameters: + * * Can only be used after a connection has been established. + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. + */ +void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout) +{ + ble_gap_upd_params params; + + params.latency = latency; + params.itvl_max = maxInterval; + params.itvl_min = minInterval; + params.supervision_timeout = timeout; + // These are not used by NimBLE at this time - Must leave at defaults + params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; + params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; + + int rc = ble_gap_update_params(m_conn_id, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + } +} // updateConnParams + + +/** + * @brief Request an update of the data packet length. + * * Can only be used after a connection has been established. + * @details Sends a data length update request to the server the client is connected to. + * The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes. + * The server needs to support the Bluetooth 4.2 specifications, to be capable of DLE. + * @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB). + */ +void NimBLEClient::setDataLen(uint16_t tx_octets) { +#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ + (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 + return; +#else + uint16_t tx_time = (tx_octets + 14) * 8; + + int rc = ble_gap_set_data_len(m_conn_id, tx_octets, tx_time); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } +#endif +} // setDataLen + + +/** + * @brief Get detailed information about the current peer connection. + */ +NimBLEConnInfo NimBLEClient::getConnInfo() { + NimBLEConnInfo connInfo; + if (!isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Not connected"); + } else { + int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Connection info not found"); + } + } + + return connInfo; +} // getConnInfo + + +/** + * @brief Set the timeout to wait for connection attempt to complete. + * @param [in] time The number of seconds before timeout. + */ +void NimBLEClient::setConnectTimeout(uint8_t time) { + m_connectTimeout = (uint32_t)(time * 1000); +} // setConnectTimeout + + +/** + * @brief Get the connection id for this client. + * @return The connection id. + */ +uint16_t NimBLEClient::getConnId() { + return m_conn_id; +} // getConnId + + +/** + * @brief Retrieve the address of the peer. + */ +NimBLEAddress NimBLEClient::getPeerAddress() { + return m_peerAddress; +} // getPeerAddress + + +/** + * @brief Set the peer address. + * @param [in] address The address of the peer that this client is + * connected or should connect to. + */ +void NimBLEClient::setPeerAddress(const NimBLEAddress &address) { + if(isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Cannot set peer address while connected"); + return; + } + + m_peerAddress = address; + NIMBLE_LOGD(LOG_TAG, "Peer address set: %s", std::string(m_peerAddress).c_str()); +} // setPeerAddress + + +/** + * @brief Ask the BLE server for the RSSI value. + * @return The RSSI value. + */ +int NimBLEClient::getRssi() { + NIMBLE_LOGD(LOG_TAG, ">> getRssi()"); + if (!isConnected()) { + NIMBLE_LOGE(LOG_TAG, "<< getRssi(): Not connected"); + return 0; + } + + int8_t rssiValue = 0; + int rc = ble_gap_conn_rssi(m_conn_id, &rssiValue); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + return 0; + } + + return rssiValue; +} // getRssi + + +/** + * @brief Get iterator to the beginning of the vector of remote service pointers. + * @return An iterator to the beginning of the vector of remote service pointers. + */ +std::vector::iterator NimBLEClient::begin() { + return m_servicesVector.begin(); +} + + +/** + * @brief Get iterator to the end of the vector of remote service pointers. + * @return An iterator to the end of the vector of remote service pointers. + */ +std::vector::iterator NimBLEClient::end() { + return m_servicesVector.end(); +} + + +/** + * @brief Get the service BLE Remote Service instance corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A pointer to the service or nullptr if not found. + */ +NimBLERemoteService* NimBLEClient::getService(const char* uuid) { + return getService(NimBLEUUID(uuid)); +} // getService + + +/** + * @brief Get the service object corresponding to the uuid. + * @param [in] uuid The UUID of the service being sought. + * @return A pointer to the service or nullptr if not found. + */ +NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str()); + + for(auto &it: m_servicesVector) { + if(it->getUUID() == uuid) { + NIMBLE_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str()); + return it; + } + } + + size_t prev_size = m_servicesVector.size(); + if(retrieveServices(&uuid)) { + if(m_servicesVector.size() > prev_size) { + return m_servicesVector.back(); + } + + // If the request was successful but 16/32 bit uuid not found + // try again with the 128 bit uuid. + if(uuid.bitSize() == BLE_UUID_TYPE_16 || + uuid.bitSize() == BLE_UUID_TYPE_32) + { + NimBLEUUID uuid128(uuid); + uuid128.to128(); + if(retrieveServices(&uuid128)) { + if(m_servicesVector.size() > prev_size) { + return m_servicesVector.back(); + } + } + } else { + // If the request was successful but the 128 bit uuid not found + // try again with the 16 bit uuid. + NimBLEUUID uuid16(uuid); + uuid16.to16(); + // if the uuid was 128 bit but not of the BLE base type this check will fail + if (uuid16.bitSize() == BLE_UUID_TYPE_16) { + if(retrieveServices(&uuid16)) { + if(m_servicesVector.size() > prev_size) { + return m_servicesVector.back(); + } + } + } + } + } + + NIMBLE_LOGD(LOG_TAG, "<< getService: not found"); + return nullptr; +} // getService + + +/** + * @brief Get a pointer to the vector of found services. + * @param [in] refresh If true the current services vector will be cleared and\n + * all services will be retrieved from the peripheral.\n + * If false the vector will be returned with the currently stored services. + * @return A pointer to the vector of available services. + */ +std::vector* NimBLEClient::getServices(bool refresh) { + if(refresh) { + deleteServices(); + + if (!retrieveServices()) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services"); + } + else{ + NIMBLE_LOGI(LOG_TAG, "Found %d services", m_servicesVector.size()); + } + } + return &m_servicesVector; +} // getServices + + +/** + * @brief Retrieves the full database of attributes that the peripheral has available. + */ +void NimBLEClient::discoverAttributes() { + for(auto svc: *getServices(true)) { + for(auto chr: *svc->getCharacteristics(true)) { + chr->getDescriptors(true); + } + } +} // discoverAttributes + + +/** + * @brief Ask the remote %BLE server for its services.\n + * Here we ask the server for its set of services and wait until we have received them all. + * @return true on success otherwise false if an error occurred + */ +bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) { +/** + * Design + * ------ + * We invoke ble_gattc_disc_all_svcs. This will request a list of the services exposed by the + * peer BLE partner to be returned in the callback function provided. + */ + + NIMBLE_LOGD(LOG_TAG, ">> retrieveServices"); + + if(!isConnected()){ + NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); + return false; + } + + int rc = 0; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + if(uuid_filter == nullptr) { + rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData); + } else { + rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u, + NimBLEClient::serviceDiscoveredCB, &taskData); + } + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_lastErr = rc; + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + + // wait until we have all the services + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + m_lastErr = taskData.rc; + + if(taskData.rc == 0){ + NIMBLE_LOGD(LOG_TAG, "<< retrieveServices"); + return true; + } + else { + NIMBLE_LOGE(LOG_TAG, "Could not retrieve services"); + return false; + } +} // getServices + + +/** + * @brief STATIC Callback for the service discovery API function.\n + * When a service is found or there is none left or there was an error + * the API will call this and report findings. + */ +int NimBLEClient::serviceDiscoveredCB( + uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d", + error->status, (error->status == 0) ? service->start_handle : -1); + + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLEClient *client = (NimBLEClient*)pTaskData->pATT; + + // Make sure the service discovery is for this device + if(client->getConnId() != conn_handle){ + return 0; + } + + if(error->status == 0) { + // Found a service - add it to the vector + NimBLERemoteService* pRemoteService = new NimBLERemoteService(client, service); + client->m_servicesVector.push_back(pRemoteService); + return 0; + } + + if(error->status == BLE_HS_EDONE) { + pTaskData->rc = 0; + } else { + NIMBLE_LOGE(LOG_TAG, "serviceDiscoveredCB() rc=%d %s", + error->status, + NimBLEUtils::returnCodeToString(error->status)); + pTaskData->rc = error->status; + } + + xTaskNotifyGive(pTaskData->task); + + NIMBLE_LOGD(LOG_TAG,"<< Service Discovered"); + return error->status; +} + + +/** + * @brief Get the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to read. + * @returns characteristic value or an empty string if not found + */ +NimBLEAttValue NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID) { + NIMBLE_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", + serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + + NimBLEAttValue ret; + NimBLERemoteService* pService = getService(serviceUUID); + + if(pService != nullptr) { + NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); + if(pChar != nullptr) { + ret = pChar->readValue(); + } + } + + NIMBLE_LOGD(LOG_TAG, "<< getValue"); + return ret; +} // getValue + + +/** + * @brief Set the value of a specific characteristic associated with a specific service. + * @param [in] serviceUUID The service that owns the characteristic. + * @param [in] characteristicUUID The characteristic whose value we wish to write. + * @param [in] value The value to write to the characteristic. + * @param [in] response If true, uses write with response operation. + * @returns true if successful otherwise false + */ +bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, + const NimBLEAttValue &value, bool response) +{ + NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", + serviceUUID.toString().c_str(), characteristicUUID.toString().c_str()); + + bool ret = false; + NimBLERemoteService* pService = getService(serviceUUID); + + if(pService != nullptr) { + NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID); + if(pChar != nullptr) { + ret = pChar->writeValue(value, response); + } + } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); + return ret; +} // setValue + + +/** + * @brief Get the remote characteristic with the specified handle. + * @param [in] handle The handle of the desired characteristic. + * @returns The matching remote characteristic, nullptr otherwise. + */ +NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle) +{ + NimBLERemoteService *pService = nullptr; + for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) { + if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) { + pService = *it; + break; + } + } + + if (pService != nullptr) { + for (auto it = pService->begin(); it != pService->end(); ++it) { + if ((*it)->getHandle() == handle) { + return *it; + } + } + } + + return nullptr; +} + +/** + * @brief Get the current mtu of this connection. + * @returns The MTU value. + */ +uint16_t NimBLEClient::getMTU() { + return ble_att_mtu(m_conn_id); +} // getMTU + + +/** + * @brief Handle a received GAP event. + * @param [in] event The event structure sent by the NimBLE stack. + * @param [in] arg A pointer to the client instance that registered for this callback. + */ + /*STATIC*/ +int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { + NimBLEClient* client = (NimBLEClient*)arg; + int rc; + + NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); + + switch(event->type) { + + case BLE_GAP_EVENT_DISCONNECT: { + rc = event->disconnect.reason; + // If Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. + switch(rc) { + case BLE_HS_ECONTROLLER: + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_ENOTSYNCED: + case BLE_HS_EOS: + NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc); + NimBLEDevice::onReset(rc); + break; + default: + // Check that the event is for this client. + if(client->m_conn_id != event->disconnect.conn.conn_handle) { + return 0; + } + break; + } + + // Stop the disconnect timer since we are now disconnected. + ble_npl_callout_stop(&client->m_dcTimer); + + // Remove the device from ignore list so we will scan it again + NimBLEDevice::removeIgnored(client->m_peerAddress); + + // No longer connected, clear the connection ID. + client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + + // If we received a connected event but did not get established (no PDU) + // then a disconnect event will be sent but we should not send it to the + // app for processing. Instead we will ensure the task is released + // and report the error. + if(!client->m_connEstablished) + break; + + NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + + client->m_connEstablished = false; + client->m_pClientCallbacks->onDisconnect(client); + break; + } // BLE_GAP_EVENT_DISCONNECT + + case BLE_GAP_EVENT_CONNECT: { + // If we aren't waiting for this connection response + // we should drop the connection immediately. + if(client->isConnected() || client->m_pTaskData == nullptr) { + ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + rc = event->connect.status; + if (rc == 0) { + NIMBLE_LOGI(LOG_TAG, "Connected event"); + + client->m_conn_id = event->connect.conn_handle; + + rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s", + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + + // In the case of a multiconnecting device we ignore this device when + // scanning since we are already connected to it + NimBLEDevice::addIgnored(client->m_peerAddress); + } else { + client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + break; + } + + return 0; + } // BLE_GAP_EVENT_CONNECT + + case BLE_GAP_EVENT_NOTIFY_RX: { + if(client->m_conn_id != event->notify_rx.conn_handle) + return 0; + + // If a notification comes before this flag is set we might + // access a vector while it is being cleared in connect() + if(!client->m_connEstablished) { + return 0; + } + + NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d", + event->notify_rx.attr_handle); + + for(auto &it: client->m_servicesVector) { + // Dont waste cycles searching services without this handle in its range + if(it->getEndHandle() < event->notify_rx.attr_handle) { + continue; + } + + auto cVector = &it->m_characteristicVector; + NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d", + it->getUUID().toString().c_str(), + event->notify_rx.attr_handle); + + auto characteristic = cVector->cbegin(); + for(; characteristic != cVector->cend(); ++characteristic) { + if((*characteristic)->m_handle == event->notify_rx.attr_handle) + break; + } + + if(characteristic != cVector->cend()) { + NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", + (*characteristic)->toString().c_str()); + + uint32_t data_len = OS_MBUF_PKTLEN(event->notify_rx.om); + (*characteristic)->m_value.setValue(event->notify_rx.om->om_data, data_len); + + if ((*characteristic)->m_notifyCallback != nullptr) { + NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", + (*characteristic)->toString().c_str()); + (*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data, + data_len, !event->notify_rx.indication); + } + break; + } + } + + return 0; + } // BLE_GAP_EVENT_NOTIFY_RX + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { + if(client->m_conn_id != event->conn_update_req.conn_handle){ + return 0; + } + NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); + NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout); + + rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client, + event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS; + + + if(!rc && event->type == BLE_GAP_EVENT_CONN_UPDATE_REQ ) { + event->conn_update_req.self_params->itvl_min = client->m_pConnParams.itvl_min; + event->conn_update_req.self_params->itvl_max = client->m_pConnParams.itvl_max; + event->conn_update_req.self_params->latency = client->m_pConnParams.latency; + event->conn_update_req.self_params->supervision_timeout = client->m_pConnParams.supervision_timeout; + } + + NIMBLE_LOGD(LOG_TAG, "%s peer params", (rc == 0) ? "Accepted" : "Rejected"); + return rc; + } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ + + case BLE_GAP_EVENT_CONN_UPDATE: { + if(client->m_conn_id != event->conn_update.conn_handle){ + return 0; + } + if(event->conn_update.status == 0) { + NIMBLE_LOGI(LOG_TAG, "Connection parameters updated."); + } else { + NIMBLE_LOGE(LOG_TAG, "Update connection parameters failed."); + } + return 0; + } // BLE_GAP_EVENT_CONN_UPDATE + + case BLE_GAP_EVENT_ENC_CHANGE: { + if(client->m_conn_id != event->enc_change.conn_handle){ + return 0; + } + + if(event->enc_change.status == 0 || + event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) + { + struct ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + + if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) { + // Key is missing, try deleting. + ble_store_util_delete_peer(&desc.peer_id_addr); + } else if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + } else { + client->m_pClientCallbacks->onAuthenticationComplete(&desc); + } + } + + rc = event->enc_change.status; + break; + } //BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_MTU: { + if(client->m_conn_id != event->mtu.conn_handle){ + return 0; + } + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.value); + rc = 0; + break; + } // BLE_GAP_EVENT_MTU + + case BLE_GAP_EVENT_PASSKEY_ACTION: { + struct ble_sm_io pkey = {0,0}; + + if(client->m_conn_id != event->passkey.conn_handle) + return 0; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); + pkey.action = event->passkey.params.action; + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); + //////////////////////////////////////////////////// + } else { + pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + //TODO: Handle out of band pairing + } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + static uint8_t tem_oob[16] = {0}; + pkey.action = event->passkey.params.action; + for (int i = 0; i < 16; i++) { + pkey.oob[i] = tem_oob[i]; + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + //////// + } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { + NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); + pkey.action = event->passkey.params.action; + + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); + ///////////////////////////////////////////// + } else { + pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { + NIMBLE_LOGD(LOG_TAG, "No passkey action required"); + } + + return 0; + } // BLE_GAP_EVENT_PASSKEY_ACTION + + default: { + return 0; + } + } // Switch + + if(client->m_pTaskData != nullptr) { + client->m_pTaskData->rc = rc; + if(client->m_pTaskData->task) { + xTaskNotifyGive(client->m_pTaskData->task); + } + client->m_pTaskData = nullptr; + } + + return 0; +} // handleGapEvent + + +/** + * @brief Are we connected to a server? + * @return True if we are connected and false if we are not connected. + */ +bool NimBLEClient::isConnected() { + return m_conn_id != BLE_HS_CONN_HANDLE_NONE; +} // isConnected + + +/** + * @brief Set the callbacks that will be invoked when events are received. + * @param [in] pClientCallbacks A pointer to a class to receive the event callbacks. + * @param [in] deleteCallbacks If true this will delete the callback class sent when the client is destructed. + */ +void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { + if (pClientCallbacks != nullptr){ + m_pClientCallbacks = pClientCallbacks; + } else { + m_pClientCallbacks = &defaultCallbacks; + } + m_deleteCallbacks = deleteCallbacks; +} // setClientCallbacks + + +/** + * @brief Return a string representation of this client. + * @return A string representation of this client. + */ +std::string NimBLEClient::toString() { + std::string res = "peer address: " + m_peerAddress.toString(); + res += "\nServices:\n"; + + for(auto &it: m_servicesVector) { + res += it->toString() + "\n"; + } + + return res; +} // toString + + +/** + * @brief Get the last error code reported by the NimBLE host + * @return int, the NimBLE error code. + */ +int NimBLEClient::getLastError() { + return m_lastErr; +} // getLastError + + +void NimBLEClientCallbacks::onConnect(NimBLEClient* pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); +} + +void NimBLEClientCallbacks::onDisconnect(NimBLEClient* pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onDisconnect: default"); +} + +bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnParamsUpdateRequest: default"); + return true; +} + +uint32_t NimBLEClientCallbacks::onPassKeyRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456"); + return 123456; +} +/* +void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyNotify: default: %d", pass_key); +} + +bool NimBLEClientCallbacks::onSecurityRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true"); + return true; +}*/ +void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); +} +bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true"); + return true; +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLEClient.h b/lib/NimBLE-Arduino/src/NimBLEClient.h new file mode 100644 index 0000000..7c93c30 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEClient.h @@ -0,0 +1,163 @@ +/* + * NimBLEClient.h + * + * Created: on Jan 26 2020 + * Author H2zero + * + * Originally: + * BLEClient.h + * + * Created on: Mar 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLECLIENT_H_ +#define MAIN_NIMBLECLIENT_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLEAddress.h" +#include "NimBLEUUID.h" +#include "NimBLEUtils.h" +#include "NimBLEConnInfo.h" +#include "NimBLEAttValue.h" +#include "NimBLEAdvertisedDevice.h" +#include "NimBLERemoteService.h" + +#include +#include + +class NimBLERemoteService; +class NimBLERemoteCharacteristic; +class NimBLEClientCallbacks; +class NimBLEAdvertisedDevice; + +/** + * @brief A model of a %BLE client. + */ +class NimBLEClient { +public: + bool connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes = true); + bool connect(const NimBLEAddress &address, bool deleteAttibutes = true); + bool connect(bool deleteAttibutes = true); + int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); + NimBLEAddress getPeerAddress(); + void setPeerAddress(const NimBLEAddress &address); + int getRssi(); + std::vector* getServices(bool refresh = false); + std::vector::iterator begin(); + std::vector::iterator end(); + NimBLERemoteService* getService(const char* uuid); + NimBLERemoteService* getService(const NimBLEUUID &uuid); + void deleteServices(); + size_t deleteService(const NimBLEUUID &uuid); + NimBLEAttValue getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); + bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, + const NimBLEAttValue &value, bool response = false); + NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle); + bool isConnected(); + void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks, + bool deleteCallbacks = true); + std::string toString(); + uint16_t getConnId(); + uint16_t getMTU(); + bool secureConnection(); + void setConnectTimeout(uint8_t timeout); + void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t scanInterval=16, uint16_t scanWindow=16); + void updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout); + void setDataLen(uint16_t tx_octets); + void discoverAttributes(); + NimBLEConnInfo getConnInfo(); + int getLastError(); + +private: + NimBLEClient(const NimBLEAddress &peerAddress); + ~NimBLEClient(); + + friend class NimBLEDevice; + friend class NimBLERemoteService; + + static int handleGapEvent(struct ble_gap_event *event, void *arg); + static int serviceDiscoveredCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, + void *arg); + static void dcTimerCb(ble_npl_event *event); + bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr); + + NimBLEAddress m_peerAddress; + int m_lastErr; + uint16_t m_conn_id; + bool m_connEstablished; + bool m_deleteCallbacks; + int32_t m_connectTimeout; + NimBLEClientCallbacks* m_pClientCallbacks; + ble_task_data_t* m_pTaskData; + ble_npl_callout m_dcTimer; + + std::vector m_servicesVector; + +private: + friend class NimBLEClientCallbacks; + ble_gap_conn_params m_pConnParams; + +}; // class NimBLEClient + + +/** + * @brief Callbacks associated with a %BLE client. + */ +class NimBLEClientCallbacks { +public: + virtual ~NimBLEClientCallbacks() {}; + + /** + * @brief Called after client connects. + * @param [in] pClient A pointer to the calling client object. + */ + virtual void onConnect(NimBLEClient* pClient); + + /** + * @brief Called when disconnected from the server. + * @param [in] pClient A pointer to the calling client object. + */ + virtual void onDisconnect(NimBLEClient* pClient); + + /** + * @brief Called when server requests to update the connection parameters. + * @param [in] pClient A pointer to the calling client object. + * @param [in] params A pointer to the struct containing the connection parameters requested. + * @return True to accept the parmeters. + */ + virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params); + + /** + * @brief Called when server requests a passkey for pairing. + * @return The passkey to be sent to the server. + */ + virtual uint32_t onPassKeyRequest(); + + /*virtual void onPassKeyNotify(uint32_t pass_key); + virtual bool onSecurityRequest();*/ + + /** + * @brief Called when the pairing procedure is complete. + * @param [in] desc A pointer to the struct containing the connection information.\n + * This can be used to check the status of the connection encryption/pairing. + */ + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc); + + /** + * @brief Called when using numeric comparision for pairing. + * @param [in] pin The pin to compare with the server. + * @return True to accept the pin. + */ + virtual bool onConfirmPIN(uint32_t pin); +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif /* MAIN_NIMBLECLIENT_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEConnInfo.h b/lib/NimBLE-Arduino/src/NimBLEConnInfo.h new file mode 100644 index 0000000..e357d8c --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEConnInfo.h @@ -0,0 +1,55 @@ +#ifndef NIMBLECONNINFO_H_ +#define NIMBLECONNINFO_H_ + +#include "NimBLEAddress.h" + +/** + * @brief Connection information. + */ +class NimBLEConnInfo { +friend class NimBLEServer; +friend class NimBLEClient; + ble_gap_conn_desc m_desc; + NimBLEConnInfo() { m_desc = {}; } + NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; } +public: + /** @brief Gets the over-the-air address of the connected peer */ + NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); } + + /** @brief Gets the ID address of the connected peer */ + NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); } + + /** @brief Gets the connection handle of the connected peer */ + uint16_t getConnHandle() { return m_desc.conn_handle; } + + /** @brief Gets the connection interval for this connection (in 1.25ms units) */ + uint16_t getConnInterval() { return m_desc.conn_itvl; } + + /** @brief Gets the supervision timeout for this connection (in 10ms units) */ + uint16_t getConnTimeout() { return m_desc.supervision_timeout; } + + /** @brief Gets the allowable latency for this connection (unit = number of intervals) */ + uint16_t getConnLatency() { return m_desc.conn_latency; } + + /** @brief Gets the maximum transmission unit size for this connection (in bytes) */ + uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); } + + /** @brief Check if we are in the master role in this connection */ + bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); } + + /** @brief Check if we are in the slave role in this connection */ + bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); } + + /** @brief Check if we are connected to a bonded peer */ + bool isBonded() { return (m_desc.sec_state.bonded == 1); } + + /** @brief Check if the connection in encrypted */ + bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); } + + /** @brief Check if the the connection has been authenticated */ + bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); } + + /** @brief Gets the key size used to encrypt the connection */ + uint8_t getSecKeySize() { return m_desc.sec_state.key_size; } +}; +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLEDescriptor.cpp b/lib/NimBLE-Arduino/src/NimBLEDescriptor.cpp new file mode 100644 index 0000000..3429d13 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEDescriptor.cpp @@ -0,0 +1,299 @@ +/* + * NimBLEDescriptor.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "NimBLELog.h" + +#include + +#define NULL_HANDLE (0xffff) + +static const char* LOG_TAG = "NimBLEDescriptor"; +static NimBLEDescriptorCallbacks defaultCallbacks; + + +/** + * @brief Construct a descriptor + * @param [in] uuid - UUID (const char*) for the descriptor. + * @param [in] properties - Properties for the descriptor. + * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to. + */ +NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) { +} + + +/** + * @brief Construct a descriptor + * @param [in] uuid - UUID (const char*) for the descriptor. + * @param [in] properties - Properties for the descriptor. + * @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to. + */ +NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +: m_value(std::min(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH , (int)max_len), max_len) { + m_uuid = uuid; + m_handle = NULL_HANDLE; // Handle is initially unknown. + m_pCharacteristic = pCharacteristic; + m_pCallbacks = &defaultCallbacks; // No initial callback. + m_properties = 0; + m_removed = 0; + + if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t + m_properties |= BLE_ATT_F_READ; + } + if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) { + m_properties |= BLE_ATT_F_WRITE; + } + if (properties & BLE_GATT_CHR_F_READ_ENC) { + m_properties |= BLE_ATT_F_READ_ENC; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHEN) { + m_properties |= BLE_ATT_F_READ_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHOR) { + m_properties |= BLE_ATT_F_READ_AUTHOR; + } + if (properties & BLE_GATT_CHR_F_WRITE_ENC) { + m_properties |= BLE_ATT_F_WRITE_ENC; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) { + m_properties |= BLE_ATT_F_WRITE_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) { + m_properties |= BLE_ATT_F_WRITE_AUTHOR; + } + +} // NimBLEDescriptor + + +/** + * @brief NimBLEDescriptor destructor. + */ +NimBLEDescriptor::~NimBLEDescriptor() { +} // ~NimBLEDescriptor + +/** + * @brief Get the BLE handle for this descriptor. + * @return The handle for this descriptor. + */ +uint16_t NimBLEDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the length of the value of this descriptor. + * @return The length (in bytes) of the value of this descriptor. + */ +size_t NimBLEDescriptor::getLength() { + return m_value.size(); +} // getLength + + +/** + * @brief Get the UUID of the descriptor. + */ +NimBLEUUID NimBLEDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Get the value of this descriptor. + * @return The NimBLEAttValue of this descriptor. + */ +NimBLEAttValue NimBLEDescriptor::getValue(time_t *timestamp) { + if (timestamp != nullptr) { + m_value.getValue(timestamp); + } + + return m_value; +} // getValue + + +/** + * @brief Get the value of this descriptor as a string. + * @return A std::string instance containing a copy of the descriptor's value. + */ +std::string NimBLEDescriptor::getStringValue() { + return std::string(m_value); +} + + +/** + * @brief Get the characteristic this descriptor belongs to. + * @return A pointer to the characteristic this descriptor belongs to. + */ +NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() { + return m_pCharacteristic; +} // getCharacteristic + + +int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) { + (void)conn_handle; + (void)attr_handle; + + const ble_uuid_t *uuid; + int rc; + NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; + + NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write"); + + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_DSC: { + // If the packet header is only 8 bytes this is a follow up of a long read + // so we don't want to call the onRead() callback again. + if(ctxt->om->om_pkthdr_len > 8) { + pDescriptor->m_pCallbacks->onRead(pDescriptor); + } + + ble_npl_hw_enter_critical(); + rc = os_mbuf_append(ctxt->om, pDescriptor->m_value.data(), pDescriptor->m_value.size()); + ble_npl_hw_exit_critical(0); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_DSC: { + uint16_t att_max_len = pDescriptor->m_value.max_size(); + + if (ctxt->om->om_len > att_max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + uint8_t buf[att_max_len]; + size_t len = ctxt->om->om_len; + memcpy(buf, ctxt->om->om_data,len); + os_mbuf *next; + next = SLIST_NEXT(ctxt->om, om_next); + while(next != NULL){ + if((len + next->om_len) > att_max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + memcpy(&buf[len], next->om_data, next->om_len); + len += next->om_len; + next = SLIST_NEXT(next, om_next); + } + + pDescriptor->setValue(buf, len); + pDescriptor->m_pCallbacks->onWrite(pDescriptor); + return 0; + } + default: + break; + } + } + + return BLE_ATT_ERR_UNLIKELY; +} + +/** + * @brief Set the callback handlers for this descriptor. + * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. + */ +void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallbacks; + } +} // setCallbacks + + +/** + * @brief Set the handle of this descriptor. + * Set the handle of this descriptor to be the supplied value. + * @param [in] handle The handle to be associated with this descriptor. + * @return N/A. + */ +void NimBLEDescriptor::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle()"); +} // setHandle + + +/** + * @brief Set the value of the descriptor. + * @param [in] data The data to set for the descriptor. + * @param [in] length The length of the data in bytes. + */ +void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) { + m_value.setValue(data, length); +} // setValue + + +/** + * @brief Set the value of the descriptor from a `std::vector`.\n + * @param [in] vec The std::vector reference to set the descriptor value from. + */ +void NimBLEDescriptor::setValue(const std::vector& vec) { + return setValue((uint8_t*)&vec[0], vec.size()); +} // setValue + + +/** + * @brief Set the characteristic this descriptor belongs to. + * @param [in] pChar A pointer to the characteristic this descriptior belongs to. + */ +void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) { + m_pCharacteristic = pChar; +} // setCharacteristic + + +/** + * @brief Return a string representation of the descriptor. + * @return A string representation of the descriptor. + */ +std::string NimBLEDescriptor::toString() { + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex; + return res; +} // toString + + +NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) { + (void)pDescriptor; + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) { + (void)pDescriptor; + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); +} // onWrite + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLEDescriptor.h b/lib/NimBLE-Arduino/src/NimBLEDescriptor.h new file mode 100644 index 0000000..4ee9a62 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEDescriptor.h @@ -0,0 +1,119 @@ +/* + * NimBLEDescriptor.h + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEDESCRIPTOR_H_ +#define MAIN_NIMBLEDESCRIPTOR_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLECharacteristic.h" +#include "NimBLEUUID.h" +#include "NimBLEAttValue.h" + +#include + +class NimBLEService; +class NimBLECharacteristic; +class NimBLEDescriptorCallbacks; + + +/** + * @brief A model of a %BLE descriptor. + */ +class NimBLEDescriptor { +public: + NimBLEDescriptor(const char* uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic = nullptr); + + NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic = nullptr); + + ~NimBLEDescriptor(); + + uint16_t getHandle(); + NimBLEUUID getUUID(); + std::string toString(); + void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); + NimBLECharacteristic* getCharacteristic(); + + size_t getLength(); + NimBLEAttValue getValue(time_t *timestamp = nullptr); + std::string getStringValue(); + + void setValue(const uint8_t* data, size_t size); + void setValue(const std::vector& vec); + + /*********************** Template Functions ************************/ + + /** + * @brief Template to set the characteristic value to val. + * @param [in] s The value to set. + */ + template + void setValue(const T &s) { m_value.setValue(s); } + + /** + * @brief Template to convert the descriptor data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp (Optional) A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + */ + template + T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { + return m_value.getValue(timestamp, skipSizeCheck); + } + +private: + friend class NimBLECharacteristic; + friend class NimBLEService; + friend class NimBLE2904; + + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + void setHandle(uint16_t handle); + void setCharacteristic(NimBLECharacteristic* pChar); + + NimBLEUUID m_uuid; + uint16_t m_handle; + NimBLEDescriptorCallbacks* m_pCallbacks; + NimBLECharacteristic* m_pCharacteristic; + uint8_t m_properties; + NimBLEAttValue m_value; + uint8_t m_removed; +}; // NimBLEDescriptor + + +/** + * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. + * + * When a server application creates a %BLE descriptor, we may wish to be informed when there is either + * a read or write request to the descriptors value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLEDescriptorCallbacks { +public: + virtual ~NimBLEDescriptorCallbacks(); + virtual void onRead(NimBLEDescriptor* pDescriptor); + virtual void onWrite(NimBLEDescriptor* pDescriptor); +}; + +#include "NimBLE2904.h" + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEDevice.cpp b/lib/NimBLE-Arduino/src/NimBLEDevice.cpp new file mode 100644 index 0000000..48a0828 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEDevice.cpp @@ -0,0 +1,1159 @@ +/* + * NimBLEDevice.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEDevice.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" + +#ifdef ESP_PLATFORM +# include "esp_err.h" +# include "esp_bt.h" +# include "nvs_flash.h" +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "esp_nimble_hci.h" +# include "nimble/nimble_port.h" +# include "nimble/nimble_port_freertos.h" +# include "host/ble_hs.h" +# include "host/ble_hs_pvcy.h" +# include "host/util/util.h" +# include "services/gap/ble_svc_gap.h" +# include "services/gatt/ble_svc_gatt.h" +# else +# include "nimble/esp_port/esp-hci/include/esp_nimble_hci.h" +# endif +#else +# include "nimble/nimble/controller/include/controller/ble_phy.h" +#endif + +#ifndef CONFIG_NIMBLE_CPP_IDF +# include "nimble/porting/nimble/include/nimble/nimble_port.h" +# include "nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h" +# include "nimble/nimble/host/include/host/ble_hs.h" +# include "nimble/nimble/host/include/host/ble_hs_pvcy.h" +# include "nimble/nimble/host/util/include/host/util/util.h" +# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" +#endif + +#if defined(ESP_PLATFORM) && defined(CONFIG_ENABLE_ARDUINO_DEPENDS) +# include "esp32-hal-bt.h" +#endif + +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEDevice"; + +/** + * Singletons for the NimBLEDevice. + */ +static bool initialized = false; +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) +NimBLEScan* NimBLEDevice::m_pScan = nullptr; +#endif +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +NimBLEServer* NimBLEDevice::m_pServer = nullptr; +#endif +uint32_t NimBLEDevice::m_passkey = 123456; +bool NimBLEDevice::m_synced = false; +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; +#endif + +gap_event_handler NimBLEDevice::m_customGapHandler = nullptr; +ble_gap_event_listener NimBLEDevice::m_listener; +#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) +std::list NimBLEDevice::m_cList; +#endif +std::list NimBLEDevice::m_ignoreList; +std::vector NimBLEDevice::m_whiteList; +NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; +uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC; +#ifdef ESP_PLATFORM +uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE; +uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE; +#endif + +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +/* STATIC */ NimBLEServer* NimBLEDevice::createServer() { + if(NimBLEDevice::m_pServer == nullptr) { + NimBLEDevice::m_pServer = new NimBLEServer(); + ble_gatts_reset(); + ble_svc_gap_init(); + ble_svc_gatt_init(); + } + + return m_pServer; +} // createServer + + +/** + * @brief Get the instance of the server. + * @return A pointer to the server instance. + */ +/* STATIC */ NimBLEServer* NimBLEDevice::getServer() { + return m_pServer; +} // getServer +#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +/** + * @brief Get the instance of the advertising object. + * @return A pointer to the advertising object. + */ +NimBLEAdvertising* NimBLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) { + m_bleAdvertising = new NimBLEAdvertising(); + } + return m_bleAdvertising; +} + + +/** + * @brief Convenience function to begin advertising. + */ +void NimBLEDevice::startAdvertising() { + getAdvertising()->start(); +} // startAdvertising + + +/** + * @brief Convenience function to stop advertising. + */ +void NimBLEDevice::stopAdvertising() { + getAdvertising()->stop(); +} // stopAdvertising +#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + + +/** + * @brief Retrieve the Scan object that we use for scanning. + * @return The scanning object reference. This is a singleton object. The caller should not + * try and release/delete it. + */ +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) +/* STATIC */ +NimBLEScan* NimBLEDevice::getScan() { + if (m_pScan == nullptr) { + m_pScan = new NimBLEScan(); + } + return m_pScan; +} // getScan +#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + + +/** + * @brief Creates a new client object and maintains a list of all client objects + * each client can connect to 1 peripheral device. + * @param [in] peerAddress An optional peer address that is copied to the new client + * object, allows for calling NimBLEClient::connect(bool) without a device or address parameter. + * @return A reference to the new client object. + */ +#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +/* STATIC */ +NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) { + if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d", + m_cList.size(), NIMBLE_MAX_CONNECTIONS); + } + + NimBLEClient* pClient = new NimBLEClient(peerAddress); + m_cList.push_back(pClient); + + return pClient; +} // createClient + + +/** + * @brief Delete the client object and remove it from the list.\n + * Checks if it is connected or trying to connect and disconnects/stops it first. + * @param [in] pClient A pointer to the client object. + */ +/* STATIC */ +bool NimBLEDevice::deleteClient(NimBLEClient* pClient) { + if(pClient == nullptr) { + return false; + } + + // Set the connection established flag to false to stop notifications + // from accessing the attribute vectors while they are being deleted. + pClient->m_connEstablished = false; + int rc =0; + + if(pClient->isConnected()) { + rc = pClient->disconnect(); + if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) { + return false; + } + + while(pClient->isConnected()) { + taskYIELD(); + } + // Since we set the flag to false the app will not get a callback + // in the disconnect event so we call it here for good measure. + pClient->m_pClientCallbacks->onDisconnect(pClient); + + } else if(pClient->m_pTaskData != nullptr) { + rc = ble_gap_conn_cancel(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + return false; + } + while(pClient->m_pTaskData != nullptr) { + taskYIELD(); + } + } + + m_cList.remove(pClient); + delete pClient; + + return true; +} // deleteClient + + +/** + * @brief Get the list of created client objects. + * @return A pointer to the list of clients. + */ +/* STATIC */ +std::list* NimBLEDevice::getClientList() { + return &m_cList; +} // getClientList + + +/** + * @brief Get the number of created client objects. + * @return Number of client objects created. + */ +/* STATIC */ +size_t NimBLEDevice::getClientListSize() { + return m_cList.size(); +} // getClientList + + +/** + * @brief Get a reference to a client by connection ID. + * @param [in] conn_id The client connection ID to search for. + * @return A pointer to the client object with the spcified connection ID. + */ +/* STATIC */ +NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if((*it)->getConnId() == conn_id) { + return (*it); + } + } + assert(0); + return nullptr; +} // getClientByID + + +/** + * @brief Get a reference to a client by peer address. + * @param [in] peer_addr The address of the peer to search for. + * @return A pointer to the client object with the peer address. + */ +/* STATIC */ +NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if((*it)->getPeerAddress().equals(peer_addr)) { + return (*it); + } + } + return nullptr; +} // getClientPeerAddress + + +/** + * @brief Finds the first disconnected client in the list. + * @return A pointer to the first client object that is not connected to a peer. + */ +/* STATIC */ +NimBLEClient* NimBLEDevice::getDisconnectedClient() { + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + if(!(*it)->isConnected()) { + return (*it); + } + } + return nullptr; +} // getDisconnectedClient + +#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#ifdef ESP_PLATFORM +/** + * @brief Set the transmission power. + * @param [in] powerLevel The power level to set, can be one of: + * * ESP_PWR_LVL_N12 = 0, Corresponding to -12dbm + * * ESP_PWR_LVL_N9 = 1, Corresponding to -9dbm + * * ESP_PWR_LVL_N6 = 2, Corresponding to -6dbm + * * ESP_PWR_LVL_N3 = 3, Corresponding to -3dbm + * * ESP_PWR_LVL_N0 = 4, Corresponding to 0dbm + * * ESP_PWR_LVL_P3 = 5, Corresponding to +3dbm + * * ESP_PWR_LVL_P6 = 6, Corresponding to +6dbm + * * ESP_PWR_LVL_P9 = 7, Corresponding to +9dbm + * @param [in] powerType The BLE function to set the power level for, can be one of: + * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0 + * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1 + * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2 + * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3 + * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4 + * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5 + * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6 + * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7 + * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8 + * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising + * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan + * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value + */ +/* STATIC */ +void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { + NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType); + + esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel); + if (errRc != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc); + } + + NIMBLE_LOGD(LOG_TAG, "<< setPower"); +} // setPower + + +/** + * @brief Get the transmission power. + * @param [in] powerType The power level to set, can be one of: + * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0 + * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1 + * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2 + * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3 + * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4 + * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5 + * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6 + * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7 + * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8 + * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising + * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan + * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value + * @return the power level currently used by the type specified. + */ +/* STATIC */ +int NimBLEDevice::getPower(esp_ble_power_type_t powerType) { + switch(esp_ble_tx_power_get(powerType)) { + case ESP_PWR_LVL_N12: + return -12; + case ESP_PWR_LVL_N9: + return -9; + case ESP_PWR_LVL_N6: + return -6; + case ESP_PWR_LVL_N3: + return -6; + case ESP_PWR_LVL_N0: + return 0; + case ESP_PWR_LVL_P3: + return 3; + case ESP_PWR_LVL_P6: + return 6; + case ESP_PWR_LVL_P9: + return 9; + default: + return BLE_HS_ADV_TX_PWR_LVL_AUTO; + } +} // getPower + +#else + +void NimBLEDevice::setPower(int dbm) { + ble_phy_txpwr_set(dbm); +} + + +int NimBLEDevice::getPower() { + return ble_phy_txpwr_get(); +} +#endif + +/** + * @brief Get our device address. + * @return A NimBLEAddress object of our public address if we have one, + * if not then our current random address. + */ +/* STATIC*/ +NimBLEAddress NimBLEDevice::getAddress() { + ble_addr_t addr = {BLE_ADDR_PUBLIC, 0}; + + if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) { + NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random"); + addr.type = BLE_ADDR_RANDOM; + ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL); + } + + return NimBLEAddress(addr); +} // getAddress + + +/** + * @brief Return a string representation of the address of this device. + * @return A string representation of this device address. + */ +/* STATIC */ +std::string NimBLEDevice::toString() { + return getAddress().toString(); +} // toString + + +/** + * @brief Setup local mtu that will be used to negotiate mtu during request from client peer. + * @param [in] mtu Value to set local mtu: + * * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527. + */ +/* STATIC */ +int NimBLEDevice::setMTU(uint16_t mtu) { + NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu); + + int rc = ble_att_set_preferred_mtu(mtu); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Could not set local mtu value to: %d", mtu); + } + + NIMBLE_LOGD(LOG_TAG, "<< setLocalMTU"); + return rc; +} // setMTU + + +/** + * @brief Get local MTU value set. + * @return The current preferred MTU setting. + */ +/* STATIC */ +uint16_t NimBLEDevice::getMTU() { + return ble_att_preferred_mtu(); +} + + +#ifdef ESP_PLATFORM +/** + * @brief Set the duplicate filter cache size for filtering scanned devices. + * @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n + * Range is 10-1000, a larger value will reduce how often the same devices are reported. + * @details Must only be called before calling NimBLEDevice::init. + */ +/*STATIC*/ +void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) { + if(initialized) { + NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache size while initialized"); + return; + } else if(cacheSize > 1000 || cacheSize <10) { + NIMBLE_LOGE(LOG_TAG, "Invalid scan cache size; min=10 max=1000"); + return; + } + + m_scanDuplicateSize = cacheSize; +} + + +/** + * @brief Set the duplicate filter mode for filtering scanned devices. + * @param [in] mode One of three possible options: + * * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)\n + Filter by device address only, advertisements from the same address will be reported only once. + * * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)\n + Filter by data only, advertisements with the same data will only be reported once,\n + even from different addresses. + * * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)\n + Filter by address and data, advertisements from the same address will be reported only once,\n + except if the data in the advertisement has changed, then it will be reported again. + * @details Must only be called before calling NimBLEDevice::initialize. + */ +/*STATIC*/ +void NimBLEDevice::setScanFilterMode(uint8_t mode) { + if(initialized) { + NIMBLE_LOGE(LOG_TAG, "Cannot change scan duplicate type while initialized"); + return; + } else if(mode > 2) { + NIMBLE_LOGE(LOG_TAG, "Invalid scan duplicate type"); + return; + } + + m_scanFilterMode = mode; +} +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +/** + * @brief Gets the number of bonded peers stored + */ +/*STATIC*/ +int NimBLEDevice::getNumBonds() { + ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; + int num_peers, rc; + + rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); + if (rc !=0) { + return 0; + } + + return num_peers; +} + + +/** + * @brief Deletes all bonding information. + */ +/*STATIC*/ +void NimBLEDevice::deleteAllBonds() { + ble_store_clear(); +} + + +/** + * @brief Deletes a peer bond. + * @param [in] address The address of the peer with which to delete bond info. + * @returns true on success. + */ +/*STATIC*/ +bool NimBLEDevice::deleteBond(const NimBLEAddress &address) { + ble_addr_t delAddr; + memcpy(&delAddr.val, address.getNative(),6); + delAddr.type = address.getType(); + + int rc = ble_gap_unpair(&delAddr); + if (rc != 0) { + return false; + } + + return true; +} + + +/** + * @brief Checks if a peer device is bonded. + * @param [in] address The address to check for bonding. + * @returns true if bonded. + */ +/*STATIC*/ +bool NimBLEDevice::isBonded(const NimBLEAddress &address) { + ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; + int num_peers, rc; + + rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); + if (rc != 0) { + return false; + } + + for (int i = 0; i < num_peers; i++) { + NimBLEAddress storedAddr(peer_id_addrs[i]); + if(storedAddr == address) { + return true; + } + } + + return false; +} + + +/** + * @brief Get the address of a bonded peer device by index. + * @param [in] index The index to retrieve the peer address of. + * @returns NimBLEAddress of the found bonded peer or nullptr if not found. + */ +/*STATIC*/ +NimBLEAddress NimBLEDevice::getBondedAddress(int index) { + ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; + int num_peers, rc; + + rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS)); + if (rc != 0) { + return nullptr; + } + + if (index > num_peers || index < 0) { + return nullptr; + } + + return NimBLEAddress(peer_id_addrs[index]); +} +#endif + +/** + * @brief Checks if a peer device is whitelisted. + * @param [in] address The address to check for in the whitelist. + * @returns true if the address is in the whitelist. + */ +/*STATIC*/ +bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) { + for (auto &it : m_whiteList) { + if (it == address) { + return true; + } + } + + return false; +} + + +/** + * @brief Add a peer address to the whitelist. + * @param [in] address The address to add to the whitelist. + * @returns true if successful. + */ +/*STATIC*/ +bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) { + if (NimBLEDevice::onWhiteList(address)) { + return true; + } + + m_whiteList.push_back(address); + std::vector wlVec; + wlVec.reserve(m_whiteList.size()); + + for (auto &it : m_whiteList) { + ble_addr_t wlAddr; + memcpy(&wlAddr.val, it.getNative(), 6); + wlAddr.type = it.getType(); + wlVec.push_back(wlAddr); + } + + int rc = ble_gap_wl_set(&wlVec[0], wlVec.size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc); + return false; + } + + return true; +} + + +/** + * @brief Remove a peer address from the whitelist. + * @param [in] address The address to remove from the whitelist. + * @returns true if successful. + */ +/*STATIC*/ +bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) { + if (!NimBLEDevice::onWhiteList(address)) { + return true; + } + + std::vector wlVec; + wlVec.reserve(m_whiteList.size()); + + for (auto &it : m_whiteList) { + if (it != address) { + ble_addr_t wlAddr; + memcpy(&wlAddr.val, it.getNative(), 6); + wlAddr.type = it.getType(); + wlVec.push_back(wlAddr); + } + } + + int rc = ble_gap_wl_set(&wlVec[0], wlVec.size()); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc); + return false; + } + + // Don't remove from the list unless NimBLE returned success + for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) { + if ((*it) == address) { + m_whiteList.erase(it); + break; + } + } + + return true; +} + + +/** + * @brief Gets the count of addresses in the whitelist. + * @returns The number of addresses in the whitelist. + */ +/*STATIC*/ +size_t NimBLEDevice::getWhiteListCount() { + return m_whiteList.size(); +} + + +/** + * @brief Gets the address at the vector index. + * @param [in] index The vector index to retrieve the address from. + * @returns the NimBLEAddress at the whitelist index or nullptr if not found. + */ +/*STATIC*/ +NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) { + if (index > m_whiteList.size()) { + NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index); + return nullptr; + } + return m_whiteList[index]; +} + + +/** + * @brief Host reset, we pass the message so we don't make calls until resynced. + * @param [in] reason The reason code for the reset. + */ +/* STATIC */ +void NimBLEDevice::onReset(int reason) +{ + if(!m_synced) { + return; + } + + m_synced = false; + + NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason, + NimBLEUtils::returnCodeToString(reason)); + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + if(initialized) { + if(m_pScan != nullptr) { + m_pScan->onHostReset(); + } + } +#endif +} // onReset + + +/** + * @brief Host resynced with controller, all clear to make calls to the stack. + */ +/* STATIC */ +void NimBLEDevice::onSync(void) +{ + NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); + // This check is needed due to potentially being called multiple times in succession + // If this happens, the call to scan start may get stuck or cause an advertising fault. + if(m_synced) { + return; + } + + /* Make sure we have proper identity address set (public preferred) */ + int rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + +#ifndef ESP_PLATFORM + rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d", rc); + return; + } +#endif + + // Yield for houskeeping before returning to operations. + // Occasionally triggers exception without. + taskYIELD(); + + m_synced = true; + + if(initialized) { +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + if(m_pScan != nullptr) { + m_pScan->onHostSync(); + } +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + if(m_bleAdvertising != nullptr) { + m_bleAdvertising->onHostSync(); + } +#endif + } +} // onSync + + +/** + * @brief The main host task. + */ +/* STATIC */ +void NimBLEDevice::host_task(void *param) +{ + NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started"); + + /* This function will return only when nimble_port_stop() is executed */ + nimble_port_run(); + + nimble_port_freertos_deinit(); +} // host_task + + +/** + * @brief Initialize the %BLE environment. + * @param [in] deviceName The device name of the device. + */ +/* STATIC */ +void NimBLEDevice::init(const std::string &deviceName) { + if(!initialized){ + int rc=0; +#ifdef ESP_PLATFORM + esp_err_t errRc = ESP_OK; + +#ifdef CONFIG_ENABLE_ARDUINO_DEPENDS + // make sure the linker includes esp32-hal-bt.c so ardruino initialize doesn't release BLE memory. + btStarted(); +#endif + + errRc = nvs_flash_init(); + + if (errRc == ESP_ERR_NVS_NO_FREE_PAGES || errRc == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + errRc = nvs_flash_init(); + } + + ESP_ERROR_CHECK(errRc); + + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); +#if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; +#else + bt_cfg.mode = ESP_BT_MODE_BLE; + bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS; +#endif + bt_cfg.normal_adv_size = m_scanDuplicateSize; + bt_cfg.scan_duplicate_type = m_scanFilterMode; + + ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); + ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); + ESP_ERROR_CHECK(esp_nimble_hci_init()); +#endif + nimble_port_init(); + + // Setup callbacks for host events + ble_hs_cfg.reset_cb = NimBLEDevice::onReset; + ble_hs_cfg.sync_cb = NimBLEDevice::onSync; + + // Set initial security capabilities + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + ble_hs_cfg.sm_our_key_dist = 1; + ble_hs_cfg.sm_their_key_dist = 3; + + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/ + + // Set the device name. + rc = ble_svc_gap_device_name_set(deviceName.c_str()); + assert(rc == 0); + + ble_store_config_init(); + + nimble_port_freertos_init(NimBLEDevice::host_task); + } + + // Wait for host and controller to sync before returning and accepting new tasks + while(!m_synced){ + taskYIELD(); + } + + initialized = true; // Set the initialization flag to ensure we are only initialized once. +} // initialize + + +/** + * @brief Shutdown the NimBLE stack/controller. + * @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing. + * @note If clearAll is true when called, any references to the created objects become invalid. + */ +/* STATIC */ +void NimBLEDevice::deinit(bool clearAll) { + int ret = nimble_port_stop(); + if (ret == 0) { + nimble_port_deinit(); +#ifdef ESP_PLATFORM + ret = esp_nimble_hci_and_controller_deinit(); + if (ret != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); + } +#endif + initialized = false; + m_synced = false; + + if(clearAll) { +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + if(NimBLEDevice::m_pServer != nullptr) { + delete NimBLEDevice::m_pServer; + NimBLEDevice::m_pServer = nullptr; + } +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + if(NimBLEDevice::m_bleAdvertising != nullptr) { + delete NimBLEDevice::m_bleAdvertising; + NimBLEDevice::m_bleAdvertising = nullptr; + } +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + if(NimBLEDevice::m_pScan != nullptr) { + delete NimBLEDevice::m_pScan; + NimBLEDevice::m_pScan= nullptr; + } +#endif + +#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) + for(auto &it : m_cList) { + deleteClient(it); + m_cList.clear(); + } +#endif + + m_ignoreList.clear(); + + if(m_securityCallbacks != nullptr) { + delete m_securityCallbacks; + } + } + } +} // deinit + + +/** + * @brief Check if the initialization is complete. + * @return true if initialized. + */ +/*STATIC*/ +bool NimBLEDevice::getInitialized() { + return initialized; +} // getInitialized + + +/** + * @brief Set the authorization mode for this device. + * @param bonding If true we allow bonding, false no bonding will be performed. + * @param mitm If true we are capable of man in the middle protection, false if not. + * @param sc If true we will perform secure connection pairing, false we will use legacy pairing. + */ +/*STATIC*/ +void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) { + NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc); + ble_hs_cfg.sm_bonding = bonding; + ble_hs_cfg.sm_mitm = mitm; + ble_hs_cfg.sm_sc = sc; +} // setSecurityAuth + + +/** + * @brief Set the authorization mode for this device. + * @param auth_req A bitmap indicating what modes are supported.\n + * The available bits are defined as: + * * 0x01 BLE_SM_PAIR_AUTHREQ_BOND + * * 0x04 BLE_SM_PAIR_AUTHREQ_MITM + * * 0x08 BLE_SM_PAIR_AUTHREQ_SC + * * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. + */ +/*STATIC*/ +void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { + NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); +} // setSecurityAuth + + +/** + * @brief Set the Input/Output capabilities of this device. + * @param iocap One of the following values: + * * 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability + * * 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability + * * 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability + * * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability + * * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability + */ +/*STATIC*/ +void NimBLEDevice::setSecurityIOCap(uint8_t iocap) { + ble_hs_cfg.sm_io_cap = iocap; +} // setSecurityIOCap + + +/** + * @brief If we are the initiator of the security procedure this sets the keys we will distribute. + * @param init_key A bitmap indicating which keys to distribute during pairing.\n + * The available bits are defined as: + * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key. + * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK). + * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +/*STATIC*/ +void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { + ble_hs_cfg.sm_our_key_dist = init_key; +} // setsSecurityInitKey + + +/** + * @brief Set the keys we are willing to accept during pairing. + * @param resp_key A bitmap indicating which keys to accept during pairing. + * The available bits are defined as: + * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key. + * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK). + * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN + * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK + */ +/*STATIC*/ +void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) { + ble_hs_cfg.sm_their_key_dist = resp_key; +} // setsSecurityRespKey + + +/** + * @brief Set the passkey the server will ask for when pairing. + * @param [in] pin The passkey to use. + */ +/*STATIC*/ +void NimBLEDevice::setSecurityPasskey(uint32_t pin) { + m_passkey = pin; +} // setSecurityPasskey + + +/** + * @brief Get the current passkey used for pairing. + * @return The current passkey. + */ +/*STATIC*/ +uint32_t NimBLEDevice::getSecurityPasskey() { + return m_passkey; +} // getSecurityPasskey + + +/** + * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events + * @param [in] callbacks Pointer to NimBLESecurityCallbacks class + * @deprecated For backward compatibility, New code should use client/server callback methods. + */ +/*STATIC*/ +void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) { + NimBLEDevice::m_securityCallbacks = callbacks; +} // setSecurityCallbacks + + +#ifdef ESP_PLATFORM +/** + * @brief Set the own address type. + * @param [in] own_addr_type Own Bluetooth Device address type.\n + * The available bits are defined as: + * * 0x00: BLE_OWN_ADDR_PUBLIC + * * 0x01: BLE_OWN_ADDR_RANDOM + * * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address. + */ +/*STATIC*/ +void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) { + m_own_addr_type = own_addr_type; + switch (own_addr_type) { +#ifdef CONFIG_IDF_TARGET_ESP32 + case BLE_OWN_ADDR_PUBLIC: + ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY); + break; +#endif + case BLE_OWN_ADDR_RANDOM: + setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID); +#ifdef CONFIG_IDF_TARGET_ESP32 + ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA); +#endif + break; + case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: + case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT: + setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID); +#ifdef CONFIG_IDF_TARGET_ESP32 + ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA); +#endif + break; + } +} // setOwnAddrType +#endif + +/** + * @brief Start the connection securing and authorization for this connection. + * @param conn_id The connection id of the peer device. + * @returns NimBLE stack return code, 0 = success. + */ +/* STATIC */ +int NimBLEDevice::startSecurity(uint16_t conn_id) { + int rc = ble_gap_security_initiate(conn_id); + if(rc != 0){ + NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + return rc; +} // startSecurity + + +/** + * @brief Check if the device address is on our ignore list. + * @param [in] address The address to look for. + * @return True if ignoring. + */ +/*STATIC*/ +bool NimBLEDevice::isIgnored(const NimBLEAddress &address) { + for(auto &it : m_ignoreList) { + if(it.equals(address)){ + return true; + } + } + + return false; +} + + +/** + * @brief Add a device to the ignore list. + * @param [in] address The address of the device we want to ignore. + */ +/*STATIC*/ +void NimBLEDevice::addIgnored(const NimBLEAddress &address) { + m_ignoreList.push_back(address); +} + + +/** + * @brief Remove a device from the ignore list. + * @param [in] address The address of the device we want to remove from the list. + */ +/*STATIC*/ +void NimBLEDevice::removeIgnored(const NimBLEAddress &address) { + for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) { + if((*it).equals(address)){ + m_ignoreList.erase(it); + return; + } + } +} + + +/** + * @brief Set a custom callback for gap events. + * @param [in] handler The function to call when gap events occur. + */ +/*STATIC*/ +void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) { + m_customGapHandler = handler; + int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL); + if(rc == BLE_HS_EALREADY){ + NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events."); + } + else{ + assert(rc == 0); + } +} // setCustomGapHandler + +#endif // CONFIG_BT_ENABLED diff --git a/lib/NimBLE-Arduino/src/NimBLEDevice.h b/lib/NimBLE-Arduino/src/NimBLEDevice.h new file mode 100644 index 0000000..94ad294 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEDevice.h @@ -0,0 +1,218 @@ +/* + * NimBLEDevice.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEDevice.h + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEDEVICE_H_ +#define MAIN_NIMBLEDEVICE_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) +#include "NimBLEScan.h" +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) +#include "NimBLEAdvertising.h" +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) +#include "NimBLEClient.h" +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +#include "NimBLEServer.h" +#endif + +#include "NimBLEUtils.h" +#include "NimBLESecurity.h" +#include "NimBLEAddress.h" + +#ifdef ESP_PLATFORM +# include "esp_bt.h" +#endif + +#include +#include +#include + +#define BLEDevice NimBLEDevice +#define BLEClient NimBLEClient +#define BLERemoteService NimBLERemoteService +#define BLERemoteCharacteristic NimBLERemoteCharacteristic +#define BLERemoteDescriptor NimBLERemoteDescriptor +#define BLEAdvertisedDevice NimBLEAdvertisedDevice +#define BLEScan NimBLEScan +#define BLEUUID NimBLEUUID +#define BLESecurity NimBLESecurity +#define BLESecurityCallbacks NimBLESecurityCallbacks +#define BLEAddress NimBLEAddress +#define BLEUtils NimBLEUtils +#define BLEClientCallbacks NimBLEClientCallbacks +#define BLEAdvertisedDeviceCallbacks NimBLEAdvertisedDeviceCallbacks +#define BLEScanResults NimBLEScanResults +#define BLEServer NimBLEServer +#define BLEService NimBLEService +#define BLECharacteristic NimBLECharacteristic +#define BLEAdvertising NimBLEAdvertising +#define BLEServerCallbacks NimBLEServerCallbacks +#define BLECharacteristicCallbacks NimBLECharacteristicCallbacks +#define BLEAdvertisementData NimBLEAdvertisementData +#define BLEDescriptor NimBLEDescriptor +#define BLE2902 NimBLE2902 +#define BLE2904 NimBLE2904 +#define BLEDescriptorCallbacks NimBLEDescriptorCallbacks +#define BLEBeacon NimBLEBeacon +#define BLEEddystoneTLM NimBLEEddystoneTLM +#define BLEEddystoneURL NimBLEEddystoneURL + +#ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#else +#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS +#endif + +typedef int (*gap_event_handler)(ble_gap_event *event, void *arg); + +extern "C" void ble_store_config_init(void); + +/** + * @brief A model of a %BLE Device from which all the BLE roles are created. + */ +class NimBLEDevice { +public: + static void init(const std::string &deviceName); + static void deinit(bool clearAll = false); + static bool getInitialized(); + static NimBLEAddress getAddress(); + static std::string toString(); + static bool whiteListAdd(const NimBLEAddress & address); + static bool whiteListRemove(const NimBLEAddress & address); + static bool onWhiteList(const NimBLEAddress & address); + static size_t getWhiteListCount(); + static NimBLEAddress getWhiteListAddress(size_t index); + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + static NimBLEScan* getScan(); +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + static NimBLEServer* createServer(); + static NimBLEServer* getServer(); +#endif + +#ifdef ESP_PLATFORM + static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); + static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); + static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false); + static void setScanDuplicateCacheSize(uint16_t cacheSize); + static void setScanFilterMode(uint8_t type); +#else + static void setPower(int dbm); + static int getPower(); +#endif + + static void setCustomGapHandler(gap_event_handler handler); + static void setSecurityAuth(bool bonding, bool mitm, bool sc); + static void setSecurityAuth(uint8_t auth_req); + static void setSecurityIOCap(uint8_t iocap); + static void setSecurityInitKey(uint8_t init_key); + static void setSecurityRespKey(uint8_t init_key); + static void setSecurityPasskey(uint32_t pin); + static uint32_t getSecurityPasskey(); + static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks); + static int startSecurity(uint16_t conn_id); + static int setMTU(uint16_t mtu); + static uint16_t getMTU(); + static bool isIgnored(const NimBLEAddress &address); + static void addIgnored(const NimBLEAddress &address); + static void removeIgnored(const NimBLEAddress &address); + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + static NimBLEAdvertising* getAdvertising(); + static void startAdvertising(); + static void stopAdvertising(); +#endif + +#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) + static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress("")); + static bool deleteClient(NimBLEClient* pClient); + static NimBLEClient* getClientByID(uint16_t conn_id); + static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr); + static NimBLEClient* getDisconnectedClient(); + static size_t getClientListSize(); + static std::list* getClientList(); +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + static bool deleteBond(const NimBLEAddress &address); + static int getNumBonds(); + static bool isBonded(const NimBLEAddress &address); + static void deleteAllBonds(); + static NimBLEAddress getBondedAddress(int index); +#endif + +private: +#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) + friend class NimBLEClient; +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + friend class NimBLEScan; +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + friend class NimBLEServer; + friend class NimBLECharacteristic; +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + friend class NimBLEAdvertising; +#endif + + static void onReset(int reason); + static void onSync(void); + static void host_task(void *param); + static bool m_synced; + +#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + static NimBLEScan* m_pScan; +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + static NimBLEServer* m_pServer; +#endif + +#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + static NimBLEAdvertising* m_bleAdvertising; +#endif + +#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) + static std::list m_cList; +#endif + static std::list m_ignoreList; + static NimBLESecurityCallbacks* m_securityCallbacks; + static uint32_t m_passkey; + static ble_gap_event_listener m_listener; + static gap_event_handler m_customGapHandler; + static uint8_t m_own_addr_type; +#ifdef ESP_PLATFORM + static uint16_t m_scanDuplicateSize; + static uint8_t m_scanFilterMode; +#endif + static std::vector m_whiteList; +}; + + +#endif // CONFIG_BT_ENABLED +#endif // MAIN_NIMBLEDEVICE_H_ diff --git a/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp b/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp new file mode 100644 index 0000000..255131c --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp @@ -0,0 +1,227 @@ +/* + * NimBLEEddystoneTLM.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneTLM.h" +#include "NimBLELog.h" + +#include +#include + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) + +static const char LOG_TAG[] = "NimBLEEddystoneTLM"; + +/** + * @brief Construct a default EddystoneTLM beacon object. + */ +NimBLEEddystoneTLM::NimBLEEddystoneTLM() { + beaconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t) ((float) 23.00 * 256); // 8.8 fixed format + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; +} // NimBLEEddystoneTLM + + +/** + * @brief Retrieve the data that is being advertised. + * @return The advertised data. + */ +std::string NimBLEEddystoneTLM::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + + +/** + * @brief Get the UUID being advertised. + * @return The UUID advertised. + */ +NimBLEUUID NimBLEEddystoneTLM::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + + +/** + * @brief Get the version being advertised. + * @return The version number. + */ +uint8_t NimBLEEddystoneTLM::getVersion() { + return m_eddystoneData.version; +} // getVersion + + +/** + * @brief Get the battery voltage. + * @return The battery voltage. + */ +uint16_t NimBLEEddystoneTLM::getVolt() { + return ENDIAN_CHANGE_U16(m_eddystoneData.volt); +} // getVolt + + +/** + * @brief Get the temperature being advertised. + * @return The temperature value. + */ +float NimBLEEddystoneTLM::getTemp() { + return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; +} // getTemp + +/** + * @brief Get the count of advertisments sent. + * @return The number of advertisments. + */ +uint32_t NimBLEEddystoneTLM::getCount() { + return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); +} // getCount + + +/** + * @brief Get the advertisment time. + * @return The advertisment time. + */ +uint32_t NimBLEEddystoneTLM::getTime() { + return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10; +} // getTime + + +/** + * @brief Get a string representation of the beacon. + * @return The string representation. + */ +std::string NimBLEEddystoneTLM::toString() { + std::string out = ""; + uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + char val[12]; + + out += "Version "; // + std::string(m_eddystoneData.version); + snprintf(val, sizeof(val), "%d", m_eddystoneData.version); + out += val; + out += "\n"; + out += "Battery Voltage "; // + ENDIAN_CHANGE_U16(m_eddystoneData.volt); + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt)); + out += val; + out += " mV\n"; + + out += "Temperature "; + snprintf(val, sizeof(val), "%.2f", ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f); + out += val; + out += " C\n"; + + out += "Adv. Count "; + snprintf(val, sizeof(val), "%" PRIu32, ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); + out += val; + out += "\n"; + + out += "Time in seconds "; + snprintf(val, sizeof(val), "%" PRIu32, rawsec/10); + out += val; + out += "\n"; + + out += "Time "; + + snprintf(val, sizeof(val), "%04" PRIu32, rawsec / 864000); + out += val; + out += "."; + + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 36000) % 24); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 600) % 60); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 10) % 60); + out += val; + out += "\n"; + + return out; +} // toString + + +/** + * @brief Set the raw data for the beacon advertisment. + * @param [in] data The raw data to advertise. + */ +void NimBLEEddystoneTLM::setData(const std::string &data) { + if (data.length() != sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data.data(), data.length()); +} // setData + + +/** + * @brief Set the UUID to advertise. + * @param [in] l_uuid The UUID. + */ +void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + + +/** + * @brief Set the version to advertise. + * @param [in] version The version number. + */ +void NimBLEEddystoneTLM::setVersion(uint8_t version) { + m_eddystoneData.version = version; +} // setVersion + + +/** + * @brief Set the battery voltage to advertise. + * @param [in] volt The voltage in millivolts. + */ +void NimBLEEddystoneTLM::setVolt(uint16_t volt) { + m_eddystoneData.volt = volt; +} // setVolt + + +/** + * @brief Set the temperature to advertise. + * @param [in] temp The temperature value. + */ +void NimBLEEddystoneTLM::setTemp(float temp) { + m_eddystoneData.temp = (uint16_t)temp; +} // setTemp + + +/** + * @brief Set the advertisment count. + * @param [in] advCount The advertisment number. + */ +void NimBLEEddystoneTLM::setCount(uint32_t advCount) { + m_eddystoneData.advCount = advCount; +} // setCount + + +/** + * @brief Set the advertisment time. + * @param [in] tmil The advertisment time in milliseconds. + */ +void NimBLEEddystoneTLM::setTime(uint32_t tmil) { + m_eddystoneData.tmil = tmil; +} // setTime + +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.h b/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.h new file mode 100644 index 0000000..265c81b --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEEddystoneTLM.h @@ -0,0 +1,61 @@ +/* + * NimBLEEddystoneTLM.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NimBLEEddystoneTLM_H_ +#define _NimBLEEddystoneTLM_H_ + +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_TLM_FRAME_TYPE 0x20 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneTLM { +public: + NimBLEEddystoneTLM(); + std::string getData(); + NimBLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(const std::string &data); + void setUUID(const NimBLEUUID &l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + +private: + uint16_t beaconUUID; + struct { + uint8_t frameType; + uint8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed)) m_eddystoneData; + +}; // NimBLEEddystoneTLM + +#endif /* _NimBLEEddystoneTLM_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp b/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp new file mode 100644 index 0000000..424df95 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp @@ -0,0 +1,204 @@ +/* + * NimBLEEddystoneURL.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneURL.h" +#include "NimBLELog.h" + +#include + +static const char LOG_TAG[] = "NimBLEEddystoneURL"; + + +/** + * @brief Construct a default EddystoneURL beacon object. + */ +NimBLEEddystoneURL::NimBLEEddystoneURL() { + beaconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); +} // BLEEddystoneURL + + +/** + * @brief Retrieve the data that is being advertised. + * @return The advertised data. + */ +std::string NimBLEEddystoneURL::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + + +/** + * @brief Get the UUID being advertised. + * @return The UUID advertised. + */ +NimBLEUUID NimBLEEddystoneURL::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + + +/** + * @brief Get the transmit power being advertised. + * @return The transmit power. + */ +int8_t NimBLEEddystoneURL::getPower() { + return m_eddystoneData.advertisedTxPower; +} // getPower + + +/** + * @brief Get the raw URL being advertised. + * @return The raw URL. + */ +std::string NimBLEEddystoneURL::getURL() { + return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); +} // getURL + + +/** + * @brief Get the full URL being advertised. + * @return The full URL. + */ +std::string NimBLEEddystoneURL::getDecodedURL() { + std::string decodedURL = ""; + + switch (m_eddystoneData.url[0]) { + case 0x00: + decodedURL += "http://www."; + break; + case 0x01: + decodedURL += "https://www."; + break; + case 0x02: + decodedURL += "http://"; + break; + case 0x03: + decodedURL += "https://"; + break; + default: + decodedURL += m_eddystoneData.url[0]; + } + + for (int i = 1; i < lengthURL; i++) { + if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + default: + break; + } + } + } + return decodedURL; +} // getDecodedURL + + + +/** + * @brief Set the raw data for the beacon advertisment. + * @param [in] data The raw data to advertise. + */ +void NimBLEEddystoneURL::setData(const std::string &data) { + if (data.length() > sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", + data.length(), sizeof(m_eddystoneData)); + return; + } + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memcpy(&m_eddystoneData, data.data(), data.length()); + lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); +} // setData + + +/** + * @brief Set the UUID to advertise. + * @param [in] l_uuid The UUID. + */ +void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + + +/** + * @brief Set the transmit power to advertise. + * @param [in] advertisedTxPower The transmit power level. + */ +void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) { + m_eddystoneData.advertisedTxPower = advertisedTxPower; +} // setPower + + +/** + * @brief Set the URL to advertise. + * @param [in] url The URL. + */ +void NimBLEEddystoneURL::setURL(const std::string &url) { + if (url.length() > sizeof(m_eddystoneData.url)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", + url.length(), sizeof(m_eddystoneData.url)); + return; + } + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + memcpy(m_eddystoneData.url, url.data(), url.length()); + lengthURL = url.length(); +} // setURL + + +#endif diff --git a/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.h b/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.h new file mode 100644 index 0000000..9c5f37f --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEEddystoneURL.h @@ -0,0 +1,52 @@ +/* + * NimBLEEddystoneURL.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NIMBLEEddystoneURL_H_ +#define _NIMBLEEddystoneURL_H_ +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_URL_FRAME_TYPE 0x10 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneURL { +public: + NimBLEEddystoneURL(); + std::string getData(); + NimBLEUUID getUUID(); + int8_t getPower(); + std::string getURL(); + std::string getDecodedURL(); + void setData(const std::string &data); + void setUUID(const NimBLEUUID &l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(const std::string &url); + +private: + uint16_t beaconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed)) m_eddystoneData; + +}; // NIMBLEEddystoneURL + +#endif /* _NIMBLEEddystoneURL_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEHIDDevice.cpp b/lib/NimBLE-Arduino/src/NimBLEHIDDevice.cpp new file mode 100644 index 0000000..78c8fea --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEHIDDevice.cpp @@ -0,0 +1,248 @@ +/* + * NimBLEHIDDevice.cpp + * + * Created: on Oct 06 2020 + * Author wakwak-koba + * + * Originally: + * + * BLEHIDDevice.cpp + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEHIDDevice.h" +#include "NimBLE2904.h" + +/** + * @brief Construct a default NimBLEHIDDevice object. + * @param [in] server A pointer to the server instance this HID Device will use. + */ +NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) { + /* + * Here we create mandatory services described in bluetooth specification + */ + m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a)); + m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812)); + m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f)); + + /* + * Mandatory characteristic for device info service + */ + m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ); + + /* + * Mandatory characteristics for HID service + */ + m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ); + m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ); + m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR); + m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ); + + /* + * Mandatory battery level characteristic with notification and presence descriptor + */ + m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); + NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904); + batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); + batteryLevelDescriptor->setNamespace(1); + batteryLevelDescriptor->setUnit(0x27ad); + + /* + * This value is setup here because its default value in most usage cases, its very rare to use boot mode + * and we want to simplify library using as much as possible + */ + const uint8_t pMode[] = { 0x01 }; + protocolMode()->setValue((uint8_t*) pMode, 1); +} + +NimBLEHIDDevice::~NimBLEHIDDevice() { +} + +/** + * @brief Set the report map data formatting information. + * @param [in] map A pointer to an array with the values to set. + * @param [in] size The number of values in the array. + */ +void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) { + m_reportMapCharacteristic->setValue(map, size); +} + +/** + * @brief Start the HID device services.\n + * This function called when all the services have been created. + */ +void NimBLEHIDDevice::startServices() { + m_deviceInfoService->start(); + m_hidService->start(); + m_batteryService->start(); +} + +/** + * @brief Create a manufacturer characteristic (this characteristic is optional). + */ +NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { + m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ); + return m_manufacturerCharacteristic; +} + +/** + * @brief Set manufacturer name + * @param [in] name The manufacturer name of this HID device. + */ +void NimBLEHIDDevice::manufacturer(std::string name) { + m_manufacturerCharacteristic->setValue(name); +} + +/** + * @brief Sets the Plug n Play characterisc value. + * @param [in] sig The vendor ID source number. + * @param [in] vid The vendor ID number. + * @param [in] pid The product ID number. + * @param [in] version The produce version number. + */ +void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { + uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; + m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); +} + +/** + * @brief Sets the HID Information characteristic value. + * @param [in] country The country code for the device. + * @param [in] flags The HID Class Specification release number to use. + */ +void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) { + uint8_t info[] = { 0x11, 0x1, country, flags }; + m_hidInfoCharacteristic->setValue(info, sizeof(info)); +} + +/** + * @brief Create input report characteristic + * @param [in] reportID input report ID, the same as in report map for input object related to the characteristic + * @return pointer to new input report characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { + NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC); + NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC); + + uint8_t desc1_val[] = { reportID, 0x01 }; + inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + + return inputReportCharacteristic; +} + +/** + * @brief Create output report characteristic + * @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic + * @return Pointer to new output report characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { + NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + + uint8_t desc1_val[] = { reportID, 0x02 }; + outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); + + return outputReportCharacteristic; +} + +/** + * @brief Create feature report characteristic. + * @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic + * @return Pointer to new feature report characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { + NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC); + + uint8_t desc1_val[] = { reportID, 0x03 }; + featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); + + return featureReportCharacteristic; +} + +/** + * @brief Creates a keyboard boot input report characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::bootInput() { + return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY); +} + +/** + * @brief Create a keyboard boot output report characteristic + */ +NimBLECharacteristic* NimBLEHIDDevice::bootOutput() { + return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR); +} + +/** + * @brief Returns a pointer to the HID control point characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::hidControl() { + return m_hidControlCharacteristic; +} + +/** + * @brief Returns a pointer to the protocol mode characteristic. + */ +NimBLECharacteristic* NimBLEHIDDevice::protocolMode() { + return m_protocolModeCharacteristic; +} + +/** + * @brief Set the battery level characteristic value. + * @param [in] level The battery level value. + */ +void NimBLEHIDDevice::setBatteryLevel(uint8_t level) { + m_batteryLevelCharacteristic->setValue(&level, 1); +} +/* + * @brief Returns battery level characteristic + * @ return battery level characteristic + *//* +BLECharacteristic* BLEHIDDevice::batteryLevel() { + return m_batteryLevelCharacteristic; +} + + + +BLECharacteristic* BLEHIDDevice::reportMap() { + return m_reportMapCharacteristic; +} + +BLECharacteristic* BLEHIDDevice::pnp() { + return m_pnpCharacteristic; +} + + +BLECharacteristic* BLEHIDDevice::hidInfo() { + return m_hidInfoCharacteristic; +} +*/ + +/** + * @brief Returns a pointer to the device information service. + */ +NimBLEService* NimBLEHIDDevice::deviceInfo() { + return m_deviceInfoService; +} + +/** + * @brief Returns a pointer to the HID service. + */ +NimBLEService* NimBLEHIDDevice::hidService() { + return m_hidService; +} + +/** + * @brief @brief Returns a pointer to the battery service. + */ +NimBLEService* NimBLEHIDDevice::batteryService() { + return m_batteryService; +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLEHIDDevice.h b/lib/NimBLE-Arduino/src/NimBLEHIDDevice.h new file mode 100644 index 0000000..ef2ed73 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEHIDDevice.h @@ -0,0 +1,86 @@ +/* + * NimBLEHIDDevice.h + * + * Created: on Oct 06 2020 + * Author wakwak-koba + * + * Originally: + * + * BLEHIDDevice.h + * + * Created on: Jan 03, 2018 + * Author: chegewara + */ + +#ifndef _BLEHIDDEVICE_H_ +#define _BLEHIDDEVICE_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) + +#include "NimBLECharacteristic.h" +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "HIDTypes.h" + +#define GENERIC_HID 0x03C0 +#define HID_KEYBOARD 0x03C1 +#define HID_MOUSE 0x03C2 +#define HID_JOYSTICK 0x03C3 +#define HID_GAMEPAD 0x03C4 +#define HID_TABLET 0x03C5 +#define HID_CARD_READER 0x03C6 +#define HID_DIGITAL_PEN 0x03C7 +#define HID_BARCODE 0x03C8 + + +/** + * @brief A model of a %BLE Human Interface Device. + */ +class NimBLEHIDDevice { +public: + NimBLEHIDDevice(NimBLEServer*); + virtual ~NimBLEHIDDevice(); + + void reportMap(uint8_t* map, uint16_t); + void startServices(); + + NimBLEService* deviceInfo(); + NimBLEService* hidService(); + NimBLEService* batteryService(); + + NimBLECharacteristic* manufacturer(); + void manufacturer(std::string name); + //NimBLECharacteristic* pnp(); + void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); + //NimBLECharacteristic* hidInfo(); + void hidInfo(uint8_t country, uint8_t flags); + //NimBLECharacteristic* batteryLevel(); + void setBatteryLevel(uint8_t level); + + + //NimBLECharacteristic* reportMap(); + NimBLECharacteristic* hidControl(); + NimBLECharacteristic* inputReport(uint8_t reportID); + NimBLECharacteristic* outputReport(uint8_t reportID); + NimBLECharacteristic* featureReport(uint8_t reportID); + NimBLECharacteristic* protocolMode(); + NimBLECharacteristic* bootInput(); + NimBLECharacteristic* bootOutput(); + +private: + NimBLEService* m_deviceInfoService; //0x180a + NimBLEService* m_hidService; //0x1812 + NimBLEService* m_batteryService = 0; //0x180f + + NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29 + NimBLECharacteristic* m_pnpCharacteristic; //0x2a50 + NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a + NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b + NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c + NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e + NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */ +#endif /* _BLEHIDDEVICE_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLELog.h b/lib/NimBLE-Arduino/src/NimBLELog.h new file mode 100644 index 0000000..dda9073 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLELog.h @@ -0,0 +1,80 @@ +/* + * NimBLELog.h + * + * Created: on Feb 24 2020 + * Author H2zero + * + */ +#ifndef MAIN_NIMBLELOG_H_ +#define MAIN_NIMBLELOG_H_ + +#include "nimconfig.h" + +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf +# include "esp_log.h" +# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL +# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 +# endif + +# define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) do { \ + if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) \ + ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \ + } while(0) + +# define NIMBLE_LOGD(tag, format, ...) \ + NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__) + +# define NIMBLE_LOGI(tag, format, ...) \ + NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__) + +# define NIMBLE_LOGW(tag, format, ...) \ + NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__) + +# define NIMBLE_LOGE(tag, format, ...) \ + NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) + +# define NIMBLE_LOGC(tag, format, ...) \ + NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__) + +#else // using Arduino +# include "nimble/porting/nimble/include/syscfg/syscfg.h" +# include "nimble/console/console.h" +# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL +# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL) +# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL +# else +# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0 +# endif +# endif + +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 +# define NIMBLE_LOGD( tag, format, ... ) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__) +# else +# define NIMBLE_LOGD( tag, format, ... ) (void)tag +# endif + +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3 +# define NIMBLE_LOGI( tag, format, ... ) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__) +# else +# define NIMBLE_LOGI( tag, format, ... ) (void)tag +# endif + +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2 +# define NIMBLE_LOGW( tag, format, ... ) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__) +# else +# define NIMBLE_LOGW( tag, format, ... ) (void)tag +# endif + +# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1 +# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__) +# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__) +# else +# define NIMBLE_LOGE( tag, format, ... ) (void)tag +# define NIMBLE_LOGC( tag, format, ... ) (void)tag +# endif + +#endif /* CONFIG_NIMBLE_CPP_IDF */ +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLELOG_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp new file mode 100644 index 0000000..6cca615 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp @@ -0,0 +1,865 @@ +/* + * NimBLERemoteCharacteristic.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteCharacteristic.cpp + * + * Created on: Mar 16, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLERemoteCharacteristic.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLERemoteCharacteristic"; + +/** + * @brief Constructor. + * @param [in] reference to the service this characteristic belongs to. + * @param [in] ble_gatt_chr struct defined as: + * struct ble_gatt_chr { + * uint16_t def_handle; + * uint16_t val_handle; + * uint8_t properties; + * ble_uuid_any_t uuid; + * }; + */ + NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService, + const struct ble_gatt_chr *chr) +{ + NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()"); + switch (chr->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(chr->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(chr->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128)); + break; + default: + break; + } + + m_handle = chr->val_handle; + m_defHandle = chr->def_handle; + m_endHandle = 0; + m_charProp = chr->properties; + m_pRemoteService = pRemoteService; + m_notifyCallback = nullptr; + + NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str()); + } // NimBLERemoteCharacteristic + + +/** + *@brief Destructor. + */ +NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { + deleteDescriptors(); +} // ~NimBLERemoteCharacteristic + +/* +#define BLE_GATT_CHR_PROP_BROADCAST 0x01 +#define BLE_GATT_CHR_PROP_READ 0x02 +#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04 +#define BLE_GATT_CHR_PROP_WRITE 0x08 +#define BLE_GATT_CHR_PROP_NOTIFY 0x10 +#define BLE_GATT_CHR_PROP_INDICATE 0x20 +#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40 +#define BLE_GATT_CHR_PROP_EXTENDED 0x80 +*/ + +/** + * @brief Does the characteristic support broadcasting? + * @return True if the characteristic supports broadcasting. + */ +bool NimBLERemoteCharacteristic::canBroadcast() { + return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0; +} // canBroadcast + + +/** + * @brief Does the characteristic support indications? + * @return True if the characteristic supports indications. + */ +bool NimBLERemoteCharacteristic::canIndicate() { + return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0; +} // canIndicate + + +/** + * @brief Does the characteristic support notifications? + * @return True if the characteristic supports notifications. + */ +bool NimBLERemoteCharacteristic::canNotify() { + return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0; +} // canNotify + + +/** + * @brief Does the characteristic support reading? + * @return True if the characteristic supports reading. + */ +bool NimBLERemoteCharacteristic::canRead() { + return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0; +} // canRead + + +/** + * @brief Does the characteristic support writing? + * @return True if the characteristic supports writing. + */ +bool NimBLERemoteCharacteristic::canWrite() { + return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0; +} // canWrite + + +/** + * @brief Does the characteristic support writing with no response? + * @return True if the characteristic supports writing with no response. + */ +bool NimBLERemoteCharacteristic::canWriteNoResponse() { + return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0; +} // canWriteNoResponse + + +/** + * @brief Callback used by the API when a descriptor is discovered or search complete. + */ +int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) +{ + int rc = error->status; + NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d", + rc, (rc == 0) ? dsc->handle : -1); + + desc_filter_t *filter = (desc_filter_t*)arg; + const NimBLEUUID *uuid_filter = filter->uuid; + ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data; + NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; + + if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + switch (rc) { + case 0: { + if (uuid_filter != nullptr) { + if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) { + return 0; + } else { + rc = BLE_HS_EDONE; + } + } + + NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc); + characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor); + break; + } + default: + break; + } + + /* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process. + * Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE. + * If we get any other error code tell the application to abort by returning non-zero in the rc. + */ + if (rc == BLE_HS_EDONE) { + pTaskData->rc = 0; + xTaskNotifyGive(pTaskData->task); + } else if(rc != 0) { + // Error; abort discovery. + pTaskData->rc = rc; + xTaskNotifyGive(pTaskData->task); + } + + NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc); + return rc; +} + + +/** + * @brief callback from NimBLE when the next characteristic of the service is discovered. + */ +int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + int rc = error->status; + NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d", + rc, (rc == 0) ? chr->val_handle : -1); + + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT; + + if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) { + return 0; + } + + if (rc == 0) { + pChar->m_endHandle = chr->def_handle - 1; + rc = BLE_HS_EDONE; + } else if (rc == BLE_HS_EDONE) { + pChar->m_endHandle = pChar->getRemoteService()->getEndHandle(); + } else { + pTaskData->rc = rc; + } + + xTaskNotifyGive(pTaskData->task); + return rc; +} + + +/** + * @brief Populate the descriptors (if any) for this characteristic. + * @param [in] the end handle of the characteristic, or the service, whichever comes first. + */ +bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) { + NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); + + // If this is the last handle then there are no descriptors + if (m_handle == getRemoteService()->getEndHandle()) { + return true; + } + + int rc = 0; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + // If we don't know the end handle of this characteristic retrieve the next one in the service + // The end handle is the next characteristic definition handle -1. + if (m_endHandle == 0) { + rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(), + m_handle, + getRemoteService()->getEndHandle(), + NimBLERemoteCharacteristic::nextCharCB, + &taskData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc); + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (taskData.rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc); + return false; + } + } + + if (m_handle == m_endHandle) { + return true; + } + + desc_filter_t filter = {uuid_filter, &taskData}; + + rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), + m_handle, + m_endHandle, + NimBLERemoteCharacteristic::descriptorDiscCB, + &filter); + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (taskData.rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d", + m_handle, m_endHandle, taskData.rc); + } + + NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size()); + return (taskData.rc == 0); +} // retrieveDescriptors + + +/** + * @brief Get the descriptor instance with the given UUID that belongs to this characteristic. + * @param [in] uuid The UUID of the descriptor to find. + * @return The Remote descriptor (if present) or null if not present. + */ +NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); + + for(auto &it: m_descriptorVector) { + if(it->getUUID() == uuid) { + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str()); + return it; + } + } + + size_t prev_size = m_descriptorVector.size(); + if(retrieveDescriptors(&uuid)) { + if(m_descriptorVector.size() > prev_size) { + return m_descriptorVector.back(); + } + + // If the request was successful but 16/32 bit uuid not found + // try again with the 128 bit uuid. + if(uuid.bitSize() == BLE_UUID_TYPE_16 || + uuid.bitSize() == BLE_UUID_TYPE_32) + { + NimBLEUUID uuid128(uuid); + uuid128.to128(); + if(retrieveDescriptors(&uuid128)) { + if(m_descriptorVector.size() > prev_size) { + return m_descriptorVector.back(); + } + } + } else { + // If the request was successful but the 128 bit uuid not found + // try again with the 16 bit uuid. + NimBLEUUID uuid16(uuid); + uuid16.to16(); + // if the uuid was 128 bit but not of the BLE base type this check will fail + if (uuid16.bitSize() == BLE_UUID_TYPE_16) { + if(retrieveDescriptors(&uuid16)) { + if(m_descriptorVector.size() > prev_size) { + return m_descriptorVector.back(); + } + } + } + } + } + + NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found"); + return nullptr; +} // getDescriptor + + +/** + * @brief Get a pointer to the vector of found descriptors. + * @param [in] refresh If true the current descriptor vector will be cleared and\n + * all descriptors for this characteristic retrieved from the peripheral.\n + * If false the vector will be returned with the currently stored descriptors + * of this characteristic. + * @return A pointer to the vector of descriptors for this characteristic. + */ +std::vector* NimBLERemoteCharacteristic::getDescriptors(bool refresh) { + if(refresh) { + deleteDescriptors(); + + if (!retrieveDescriptors()) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors"); + } + else{ + NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size()); + } + } + return &m_descriptorVector; +} // getDescriptors + + +/** + * @brief Get iterator to the beginning of the vector of remote descriptor pointers. + * @return An iterator to the beginning of the vector of remote descriptor pointers. + */ +std::vector::iterator NimBLERemoteCharacteristic::begin() { + return m_descriptorVector.begin(); +} + + +/** + * @brief Get iterator to the end of the vector of remote descriptor pointers. + * @return An iterator to the end of the vector of remote descriptor pointers. + */ +std::vector::iterator NimBLERemoteCharacteristic::end() { + return m_descriptorVector.end(); +} + + +/** + * @brief Get the handle for this characteristic. + * @return The handle for this characteristic. + */ +uint16_t NimBLERemoteCharacteristic::getHandle() { + return m_handle; +} // getHandle + +/** + * @brief Get the handle for this characteristics definition. + * @return The handle for this characteristic definition. + */ +uint16_t NimBLERemoteCharacteristic::getDefHandle() { + return m_defHandle; +} // getDefHandle + + +/** + * @brief Get the remote service associated with this characteristic. + * @return The remote service associated with this characteristic. + */ +NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() { + return m_pRemoteService; +} // getRemoteService + + +/** + * @brief Get the UUID for this characteristic. + * @return The UUID for this characteristic. + */ +NimBLEUUID NimBLERemoteCharacteristic::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Get the value of the remote characteristic. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @return The value of the remote characteristic. + */ +NimBLEAttValue NimBLERemoteCharacteristic::getValue(time_t *timestamp) { + if(timestamp != nullptr) { + *timestamp = m_value.getTimeStamp(); + } + + return m_value; +} + + +/** + * @brief Read an unsigned 16 bit value + * @return The unsigned 16 bit value. + * @deprecated Use readValue(). + */ +uint16_t NimBLERemoteCharacteristic::readUInt16() { + return readValue(); +} // readUInt16 + + +/** + * @brief Read an unsigned 32 bit value. + * @return the unsigned 32 bit value. + * @deprecated Use readValue(). + */ +uint32_t NimBLERemoteCharacteristic::readUInt32() { + return readValue(); +} // readUInt32 + + +/** + * @brief Read a byte value + * @return The value as a byte + * @deprecated Use readValue(). + */ +uint8_t NimBLERemoteCharacteristic::readUInt8() { + return readValue(); +} // readUInt8 + + +/** + * @brief Read a float value. + * @return the float value. + */ +float NimBLERemoteCharacteristic::readFloat() { + return readValue(); +} // readFloat + + +/** + * @brief Read the value of the remote characteristic. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @return The value of the remote characteristic. + */ +NimBLEAttValue NimBLERemoteCharacteristic::readValue(time_t *timestamp) { + NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", + getUUID().toString().c_str(), getHandle(), getHandle()); + + NimBLEClient* pClient = getRemoteService()->getClient(); + NimBLEAttValue value; + + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return value; + } + + int rc = 0; + int retryCount = 1; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, &value}; + + do { + rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, + NimBLERemoteCharacteristic::onReadCB, + &taskData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + return value; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + rc = taskData.rc; + + switch(rc){ + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + // Characteristic is not long-readable, return with what we have. + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGI(LOG_TAG, "Attribute not long"); + rc = 0; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc); + return value; + } + } while(rc != 0 && retryCount--); + + value.setTimeStamp(); + m_value = value; + if(timestamp != nullptr) { + *timestamp = value.getTimeStamp(); + } + + NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc); + return value; +} // readValue + + +/** + * @brief Callback for characteristic read operation. + * @return success == 0 or error code. + */ +int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; + uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId(); + + if(conn_id != conn_handle) { + return 0; + } + + NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); + + NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf; + int rc = error->status; + + if(rc == 0) { + if(attr) { + uint16_t data_len = OS_MBUF_PKTLEN(attr->om); + if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { + rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } else { + NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len); + valBuf->append(attr->om->om_data, data_len); + return 0; + } + } + } + + pTaskData->rc = rc; + xTaskNotifyGive(pTaskData->task); + + return rc; +} + + +/** + * @brief Subscribe or unsubscribe for notifications or indications. + * @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications. + * @param [in] notifyCallback A callback to be invoked for a notification. + * @param [in] response If write response required set this to true. + * If NULL is provided then no callback is performed. + * @return false if writing to the descriptor failed. + */ +bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) { + NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val); + + m_notifyCallback = notifyCallback; + + NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902)); + if(desc == nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found"); + return true; + } + + NIMBLE_LOGD(LOG_TAG, "<< setNotify()"); + + return desc->writeValue((uint8_t *)&val, 2, response); +} // setNotify + + +/** + * @brief Subscribe for notifications or indications. + * @param [in] notifications If true, subscribe for notifications, false subscribe for indications. + * @param [in] notifyCallback A callback to be invoked for a notification. + * @param [in] response If true, require a write response from the descriptor write operation. + * If NULL is provided then no callback is performed. + * @return false if writing to the descriptor failed. + */ +bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) { + if(notifications) { + return setNotify(0x01, notifyCallback, response); + } else { + return setNotify(0x02, notifyCallback, response); + } +} // subscribe + + +/** + * @brief Unsubscribe for notifications or indications. + * @param [in] response bool if true, require a write response from the descriptor write operation. + * @return false if writing to the descriptor failed. + */ +bool NimBLERemoteCharacteristic::unsubscribe(bool response) { + return setNotify(0x00, nullptr, response); +} // unsubscribe + + + /** + * @brief backward-compatibility method for subscribe/unsubscribe notifications/indications + * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we + * will unregister for notifications. + * @param [in] notifications If true, register for notifications, false register for indications. + * @param [in] response If true, require a write response from the descriptor write operation. + * @return true if successful. + * @deprecated Use subscribe() / unsubscribe() instead. + */ +bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) { + bool success; + if(notifyCallback != nullptr) { + success = subscribe(notifications, notifyCallback, response); + } else { + success = unsubscribe(response); + } + return success; +} // registerForNotify + + +/** + * @brief Delete the descriptors in the descriptor vector. + * @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors + * object references. Since we allocated these in this class, we are also responsible for deleting + * them. This method does just that. + */ +void NimBLERemoteCharacteristic::deleteDescriptors() { + NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors"); + + for(auto &it: m_descriptorVector) { + delete it; + } + m_descriptorVector.clear(); + NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors"); +} // deleteDescriptors + + +/** + * @brief Delete descriptor by UUID + * @param [in] uuid The UUID of the descriptor to be deleted. + * @return Number of descriptors left in the vector. + */ +size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor"); + + for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) { + if((*it)->getUUID() == uuid) { + delete *it; + m_descriptorVector.erase(it); + break; + } + } + + NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor"); + + return m_descriptorVector.size(); +} // deleteDescriptor + + +/** + * @brief Convert a NimBLERemoteCharacteristic to a string representation; + * @return a String representation. + */ +std::string NimBLERemoteCharacteristic::toString() { + std::string res = "Characteristic: uuid: " + m_uuid.toString(); + char val[6]; + res += ", handle: "; + snprintf(val, sizeof(val), "%d", getHandle()); + res += val; + res += " 0x"; + snprintf(val, sizeof(val), "%04x", getHandle()); + res += val; + res += ", props: "; + res += " 0x"; + snprintf(val, sizeof(val), "%02x", m_charProp); + res += val; + + for(auto &it: m_descriptorVector) { + res += "\n" + it->toString(); + } + + return res; +} // toString + + +/** + * @brief Write a new value to the remote characteristic from a std::vector. + * @param [in] vec A std::vector value to write to the remote characteristic. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteCharacteristic::writeValue(const std::vector& vec, bool response) { + return writeValue((uint8_t*)&vec[0], vec.size(), response); +} // writeValue + + +/** + * @brief Write a new value to the remote characteristic from a const char*. + * @param [in] char_s A character string to write to the remote characteristic. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteCharacteristic::writeValue(const char* char_s, bool response) { + return writeValue((uint8_t*)char_s, strlen(char_s), response); +} // writeValue + + +/** + * @brief Write a new value to the remote characteristic from a data buffer. + * @param [in] data A pointer to a data buffer. + * @param [in] length The length of the data in the data buffer. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) { + + NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); + + NimBLEClient* pClient = getRemoteService()->getClient(); + + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return false; + } + + int rc = 0; + int retryCount = 1; + uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3; + + // Check if the data length is longer than we can write in one connection event. + // If so we must do a long write which requires a response. + if(length <= mtu && !response) { + rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); + return (rc==0); + } + + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + do { + if(length > mtu) { + NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); + os_mbuf *om = ble_hs_mbuf_from_flat(data, length); + rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, + NimBLERemoteCharacteristic::onWriteCB, + &taskData); + } else { + rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, + data, length, + NimBLERemoteCharacteristic::onWriteCB, + &taskData); + } + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + rc = taskData.rc; + + switch(rc){ + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu); + retryCount++; + length = mtu; + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc); + return false; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc); + return (rc == 0); +} // writeValue + + +/** + * @brief Callback for characteristic write operation. + * @return success == 0 or error code. + */ +int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; + + if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); + + pTaskData->rc = error->status; + xTaskNotifyGive(pTaskData->task); + + return 0; +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h b/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h new file mode 100644 index 0000000..353d832 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h @@ -0,0 +1,190 @@ +/* + * NimBLERemoteCharacteristic.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteCharacteristic.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ +#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLERemoteService.h" +#include "NimBLERemoteDescriptor.h" + +#include +#include +#include "NimBLELog.h" + +class NimBLERemoteService; +class NimBLERemoteDescriptor; + + +typedef std::function notify_callback; + +typedef struct { + const NimBLEUUID *uuid; + void *task_data; +} desc_filter_t; + + +/** + * @brief A model of a remote %BLE characteristic. + */ +class NimBLERemoteCharacteristic { +public: + ~NimBLERemoteCharacteristic(); + + // Public member functions + bool canBroadcast(); + bool canIndicate(); + bool canNotify(); + bool canRead(); + bool canWrite(); + bool canWriteNoResponse(); + std::vector::iterator begin(); + std::vector::iterator end(); + NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid); + std::vector* getDescriptors(bool refresh = false); + void deleteDescriptors(); + size_t deleteDescriptor(const NimBLEUUID &uuid); + uint16_t getHandle(); + uint16_t getDefHandle(); + NimBLEUUID getUUID(); + NimBLEAttValue readValue(time_t *timestamp = nullptr); + std::string toString(); + NimBLERemoteService* getRemoteService(); + + uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue()"))); + uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()"))); + uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()"))); + float readFloat() __attribute__ ((deprecated("Use template readValue()"))); + NimBLEAttValue getValue(time_t *timestamp = nullptr); + + bool subscribe(bool notifications = true, + notify_callback notifyCallback = nullptr, + bool response = false); + bool unsubscribe(bool response = false); + bool registerForNotify(notify_callback notifyCallback, + bool notifications = true, + bool response = true) + __attribute__ ((deprecated("Use subscribe()/unsubscribe()"))); + bool writeValue(const uint8_t* data, + size_t length, + bool response = false); + bool writeValue(const std::vector& v, bool response = false); + bool writeValue(const char* s, bool response = false); + + + /*********************** Template Functions ************************/ + + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @details Only used for non-arrays and types without a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value && !Has_c_str_len::value, bool>::type +#endif + writeValue(const T& s, bool response = false) { + return writeValue((uint8_t*)&s, sizeof(T), response); + } + + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @details Only used if the has a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value, bool>::type +#endif + writeValue(const T& s, bool response = false) { + return writeValue((uint8_t*)s.c_str(), s.length(), response); + } + + /** + * @brief Template to convert the remote characteristic data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: getValue(×tamp, skipSizeCheck); + */ + template + T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { + if(!skipSizeCheck && m_value.size() < sizeof(T)) return T(); + return *((T *)m_value.getValue(timestamp)); + } + + /** + * @brief Template to convert the remote characteristic data to . + * @tparam T The type to convert the data to. + * @param [in] timestamp A pointer to a time_t struct to store the time the value was read. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: readValue(×tamp, skipSizeCheck); + */ + template + T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { + NimBLEAttValue value = readValue(); + if(!skipSizeCheck && value.size() < sizeof(T)) return T(); + return *((T *)value.getValue(timestamp)); + } + +private: + + NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr); + + friend class NimBLEClient; + friend class NimBLERemoteService; + friend class NimBLERemoteDescriptor; + + // Private member functions + bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true); + bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr); + static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg); + static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg); + static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg); + static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg); + + // Private properties + NimBLEUUID m_uuid; + uint8_t m_charProp; + uint16_t m_handle; + uint16_t m_defHandle; + uint16_t m_endHandle; + NimBLERemoteService* m_pRemoteService; + NimBLEAttValue m_value; + notify_callback m_notifyCallback; + + // We maintain a vector of descriptors owned by this characteristic. + std::vector m_descriptorVector; +}; // NimBLERemoteCharacteristic + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp new file mode 100644 index 0000000..cae9103 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp @@ -0,0 +1,365 @@ +/* + * NimBLERemoteDescriptor.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteDescriptor.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLERemoteDescriptor.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLERemoteDescriptor"; + +/** + * @brief Remote descriptor constructor. + * @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to. + * @param [in] dsc A pointer to the struct that contains the descriptor information. + */ +NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, + const struct ble_gatt_dsc *dsc) +{ + NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()"); + switch (dsc->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(dsc->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(dsc->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128)); + break; + default: + break; + } + + m_handle = dsc->handle; + m_pRemoteCharacteristic = pRemoteCharacteristic; + + NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str()); +} + + +/** + * @brief Retrieve the handle associated with this remote descriptor. + * @return The handle associated with this remote descriptor. + */ +uint16_t NimBLERemoteDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the characteristic that owns this descriptor. + * @return The characteristic that owns this descriptor. + */ +NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() { + return m_pRemoteCharacteristic; +} // getRemoteCharacteristic + + +/** + * @brief Retrieve the UUID associated this remote descriptor. + * @return The UUID associated this remote descriptor. + */ +NimBLEUUID NimBLERemoteDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Read a byte value + * @return The value as a byte + * @deprecated Use readValue(). + */ +uint8_t NimBLERemoteDescriptor::readUInt8() { + return readValue(); +} // readUInt8 + + +/** + * @brief Read an unsigned 16 bit value + * @return The unsigned 16 bit value. + * @deprecated Use readValue(). + */ +uint16_t NimBLERemoteDescriptor::readUInt16() { + return readValue(); +} // readUInt16 + + +/** + * @brief Read an unsigned 32 bit value. + * @return the unsigned 32 bit value. + * @deprecated Use readValue(). + */ +uint32_t NimBLERemoteDescriptor::readUInt32() { + return readValue(); +} // readUInt32 + + +/** + * @brief Read the value of the remote descriptor. + * @return The value of the remote descriptor. + */ +NimBLEAttValue NimBLERemoteDescriptor::readValue() { + NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str()); + + NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); + NimBLEAttValue value; + + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return value; + } + + int rc = 0; + int retryCount = 1; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, &value}; + + do { + rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, + NimBLERemoteDescriptor::onReadCB, + &taskData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + return value; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + rc = taskData.rc; + + switch(rc){ + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + // Descriptor is not long-readable, return with what we have. + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGI(LOG_TAG, "Attribute not long"); + rc = 0; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return value; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %u rc=%d", value.length(), rc); + return value; +} // readValue + + +/** + * @brief Callback for Descriptor read operation. + * @return success == 0 or error code. + */ +int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + (void)attr; + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT; + uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId(); + + if(conn_id != conn_handle){ + return 0; + } + + NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); + + NimBLEAttValue *valBuf = (NimBLEAttValue*)pTaskData->buf; + int rc = error->status; + + if(rc == 0) { + if(attr) { + uint16_t data_len = OS_MBUF_PKTLEN(attr->om); + if((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { + rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } else { + NIMBLE_LOGD(LOG_TAG, "Got %u bytes", data_len); + valBuf->append(attr->om->om_data, data_len); + return 0; + } + } + } + + pTaskData->rc = rc; + xTaskNotifyGive(pTaskData->task); + + return rc; +} + + +/** + * @brief Return a string representation of this Remote Descriptor. + * @return A string representation of this Remote Descriptor. + */ +std::string NimBLERemoteDescriptor::toString() { + std::string res = "Descriptor: uuid: " + getUUID().toString(); + char val[6]; + res += ", handle: "; + snprintf(val, sizeof(val), "%d", getHandle()); + res += val; + + return res; +} // toString + + +/** + * @brief Callback for descriptor write operation. + * @return success == 0 or error code. + */ +int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT; + + if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){ + return 0; + } + + NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); + + pTaskData->rc = error->status; + xTaskNotifyGive(pTaskData->task); + + return 0; +} + + +/** + * @brief Write a new value to a remote descriptor from a std::vector. + * @param [in] vec A std::vector value to write to the remote descriptor. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteDescriptor::writeValue(const std::vector& vec, bool response) { + return writeValue((uint8_t*)&vec[0], vec.size(), response); +} // writeValue + + +/** + * @brief Write a new value to the remote descriptor from a const char*. + * @param [in] char_s A character string to write to the remote descriptor. + * @param [in] response Whether we require a response from the write. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteDescriptor::writeValue(const char* char_s, bool response) { + return writeValue((uint8_t*)char_s, strlen(char_s), response); +} // writeValue + + +/** + * @brief Write a new value to a remote descriptor. + * @param [in] data The data to send to the remote descriptor. + * @param [in] length The length of the data to send. + * @param [in] response True if we expect a write response. + * @return false if not connected or otherwise cannot perform write. + */ +bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) { + + NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str()); + + NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); + + // Check to see that we are connected. + if (!pClient->isConnected()) { + NIMBLE_LOGE(LOG_TAG, "Disconnected"); + return false; + } + + int rc = 0; + int retryCount = 1; + uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3; + + // Check if the data length is longer than we can write in 1 connection event. + // If so we must do a long write which requires a response. + if(length <= mtu && !response) { + rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); + return (rc == 0); + } + + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + do { + if(length > mtu) { + NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); + os_mbuf *om = ble_hs_mbuf_from_flat(data, length); + rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, + NimBLERemoteDescriptor::onWriteCB, + &taskData); + } else { + rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, + data, length, + NimBLERemoteDescriptor::onWriteCB, + &taskData); + } + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc); + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + rc = taskData.rc; + + switch(rc) { + case 0: + case BLE_HS_EDONE: + rc = 0; + break; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG): + NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu); + retryCount++; + length = mtu; + break; + + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + if (retryCount && pClient->secureConnection()) + break; + /* Else falls through. */ + default: + return false; + } + } while(rc != 0 && retryCount--); + + NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc); + return (rc == 0); +} // writeValue + + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.h b/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.h new file mode 100644 index 0000000..28863df --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteDescriptor.h @@ -0,0 +1,108 @@ +/* + * NimBLERemoteDescriptor.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteDescriptor.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ +#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLERemoteCharacteristic.h" + +class NimBLERemoteCharacteristic; +/** + * @brief A model of remote %BLE descriptor. + */ +class NimBLERemoteDescriptor { +public: + uint16_t getHandle(); + NimBLERemoteCharacteristic* getRemoteCharacteristic(); + NimBLEUUID getUUID(); + NimBLEAttValue readValue(); + + uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue()"))); + uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()"))); + uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()"))); + std::string toString(void); + bool writeValue(const uint8_t* data, size_t length, bool response = false); + bool writeValue(const std::vector& v, bool response = false); + bool writeValue(const char* s, bool response = false); + + + /*********************** Template Functions ************************/ + + /** + * @brief Template to set the remote descriptor value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @details Only used for non-arrays and types without a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value && !Has_c_str_len::value, bool>::type +#endif + writeValue(const T& s, bool response = false) { + return writeValue((uint8_t*)&s, sizeof(T), response); + } + + /** + * @brief Template to set the remote descriptor value to val. + * @param [in] s The value to write. + * @param [in] response True == request write response. + * @details Only used if the has a `c_str()` method. + */ + template +#ifdef _DOXYGEN_ + bool +#else + typename std::enable_if::value, bool>::type +#endif + writeValue(const T& s, bool response = false) { + return writeValue((uint8_t*)s.c_str(), s.length(), response); + } + + /** + * @brief Template to convert the remote descriptor data to . + * @tparam T The type to convert the data to. + * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof(). + * @return The data converted to or NULL if skipSizeCheck is false and the data is + * less than sizeof(). + * @details Use: readValue(skipSizeCheck); + */ + template + T readValue(bool skipSizeCheck = false) { + NimBLEAttValue value = readValue(); + if(!skipSizeCheck && value.size() < sizeof(T)) return T(); + return *((T *)value.data()); + } + +private: + friend class NimBLERemoteCharacteristic; + + NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic, + const struct ble_gatt_dsc *dsc); + static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg); + static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg); + + uint16_t m_handle; + NimBLEUUID m_uuid; + NimBLERemoteCharacteristic* m_pRemoteCharacteristic; +}; + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteService.cpp b/lib/NimBLE-Arduino/src/NimBLERemoteService.cpp new file mode 100644 index 0000000..cd3d528 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteService.cpp @@ -0,0 +1,411 @@ +/* + * NimBLERemoteService.cpp + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteService.cpp + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLERemoteService.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLERemoteService"; + +/** + * @brief Remote Service constructor. + * @param [in] pClient A pointer to the client this belongs to. + * @param [in] service A pointer to the structure with the service information. + */ +NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) { + + NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()"); + m_pClient = pClient; + switch (service->uuid.u.type) { + case BLE_UUID_TYPE_16: + m_uuid = NimBLEUUID(service->uuid.u16.value); + break; + case BLE_UUID_TYPE_32: + m_uuid = NimBLEUUID(service->uuid.u32.value); + break; + case BLE_UUID_TYPE_128: + m_uuid = NimBLEUUID(const_cast(&service->uuid.u128)); + break; + default: + break; + } + m_startHandle = service->start_handle; + m_endHandle = service->end_handle; + NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str()); +} + + +/** + * @brief When deleting the service make sure we delete all characteristics and descriptors. + */ +NimBLERemoteService::~NimBLERemoteService() { + deleteCharacteristics(); +} + + +/** + * @brief Get iterator to the beginning of the vector of remote characteristic pointers. + * @return An iterator to the beginning of the vector of remote characteristic pointers. + */ +std::vector::iterator NimBLERemoteService::begin() { + return m_characteristicVector.begin(); +} + + +/** + * @brief Get iterator to the end of the vector of remote characteristic pointers. + * @return An iterator to the end of the vector of remote characteristic pointers. + */ +std::vector::iterator NimBLERemoteService::end() { + return m_characteristicVector.end(); +} + + +/** + * @brief Get the remote characteristic object for the characteristic UUID. + * @param [in] uuid Remote characteristic uuid. + * @return A pointer to the remote characteristic object. + */ +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) { + return getCharacteristic(NimBLEUUID(uuid)); +} // getCharacteristic + + +/** + * @brief Get the characteristic object for the UUID. + * @param [in] uuid Characteristic uuid. + * @return A pointer to the characteristic object, or nullptr if not found. + */ +NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str()); + + for(auto &it: m_characteristicVector) { + if(it->getUUID() == uuid) { + NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str()); + return it; + } + } + + size_t prev_size = m_characteristicVector.size(); + if(retrieveCharacteristics(&uuid)) { + if(m_characteristicVector.size() > prev_size) { + return m_characteristicVector.back(); + } + + // If the request was successful but 16/32 bit uuid not found + // try again with the 128 bit uuid. + if(uuid.bitSize() == BLE_UUID_TYPE_16 || + uuid.bitSize() == BLE_UUID_TYPE_32) + { + NimBLEUUID uuid128(uuid); + uuid128.to128(); + if (retrieveCharacteristics(&uuid128)) { + if(m_characteristicVector.size() > prev_size) { + return m_characteristicVector.back(); + } + } + } else { + // If the request was successful but the 128 bit uuid not found + // try again with the 16 bit uuid. + NimBLEUUID uuid16(uuid); + uuid16.to16(); + // if the uuid was 128 bit but not of the BLE base type this check will fail + if (uuid16.bitSize() == BLE_UUID_TYPE_16) { + if(retrieveCharacteristics(&uuid16)) { + if(m_characteristicVector.size() > prev_size) { + return m_characteristicVector.back(); + } + } + } + } + } + + NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found"); + return nullptr; +} // getCharacteristic + + +/** + * @brief Get a pointer to the vector of found characteristics. + * @param [in] refresh If true the current characteristics vector will cleared and + * all characteristics for this service retrieved from the peripheral. + * If false the vector will be returned with the currently stored characteristics of this service. + * @return A pointer to the vector of descriptors for this characteristic. + */ +std::vector* NimBLERemoteService::getCharacteristics(bool refresh) { + if(refresh) { + deleteCharacteristics(); + + if (!retrieveCharacteristics()) { + NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics"); + } + else{ + NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size()); + } + } + return &m_characteristicVector; +} // getCharacteristics + + +/** + * @brief Callback for Characterisic discovery. + * @return success == 0 or error code. + */ +int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", + error->status, (error->status == 0) ? chr->val_handle : -1); + + ble_task_data_t *pTaskData = (ble_task_data_t*)arg; + NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT; + + // Make sure the discovery is for this device + if(service->getClient()->getConnId() != conn_handle){ + return 0; + } + + if(error->status == 0) { + // Found a service - add it to the vector + NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr); + service->m_characteristicVector.push_back(pRemoteCharacteristic); + return 0; + } + + if(error->status == BLE_HS_EDONE) { + pTaskData->rc = 0; + } else { + NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", + error->status, + NimBLEUtils::returnCodeToString(error->status)); + pTaskData->rc = error->status; + } + + xTaskNotifyGive(pTaskData->task); + + NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered"); + return error->status; +} + + +/** + * @brief Retrieve all the characteristics for this service. + * This function will not return until we have all the characteristics. + * @return True if successful. + */ +bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) { + NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); + + int rc = 0; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {this, cur_task, 0, nullptr}; + + if(uuid_filter == nullptr) { + rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), + m_startHandle, + m_endHandle, + NimBLERemoteService::characteristicDiscCB, + &taskData); + } else { + rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(), + m_startHandle, + m_endHandle, + &uuid_filter->getNative()->u, + NimBLERemoteService::characteristicDiscCB, + &taskData); + } + + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if(taskData.rc == 0){ + if (uuid_filter == nullptr) { + if (m_characteristicVector.size() > 1) { + for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) { + auto nx = std::next(it, 1); + if (nx == m_characteristicVector.end()) { + break; + } + (*it)->m_endHandle = (*nx)->m_defHandle - 1; + } + } + + m_characteristicVector.back()->m_endHandle = getEndHandle(); + } + + NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); + return true; + } + + NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics"); + return false; + +} // retrieveCharacteristics + + +/** + * @brief Get the client associated with this service. + * @return A reference to the client associated with this service. + */ +NimBLEClient* NimBLERemoteService::getClient() { + return m_pClient; +} // getClient + + +/** + * @brief Get the service end handle. + */ +uint16_t NimBLERemoteService::getEndHandle() { + return m_endHandle; +} // getEndHandle + + +/** + * @brief Get the service start handle. + */ +uint16_t NimBLERemoteService::getStartHandle() { + return m_startHandle; +} // getStartHandle + + +/** + * @brief Get the service UUID. + */ +NimBLEUUID NimBLERemoteService::getUUID() { + return m_uuid; +} + + +/** + * @brief Read the value of a characteristic associated with this service. + * @param [in] characteristicUuid The characteristic to read. + * @returns a string containing the value or an empty string if not found or error. + */ +std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) { + NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); + + std::string ret = ""; + NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); + + if(pChar != nullptr) { + ret = pChar->readValue(); + } + + NIMBLE_LOGD(LOG_TAG, "<< readValue"); + return ret; +} // readValue + + +/** + * @brief Set the value of a characteristic. + * @param [in] characteristicUuid The characteristic to set. + * @param [in] value The value to set. + * @returns true on success, false if not found or error + */ +bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) { + NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); + + bool ret = false; + NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid); + + if(pChar != nullptr) { + ret = pChar->writeValue(value); + } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); + return ret; +} // setValue + + +/** + * @brief Delete the characteristics in the characteristics vector. + * @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic + * object references. Since we allocated these in this class, we are also responsible for deleting + * them. This method does just that. + */ +void NimBLERemoteService::deleteCharacteristics() { + NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics"); + for(auto &it: m_characteristicVector) { + delete it; + } + m_characteristicVector.clear(); + NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics"); +} // deleteCharacteristics + + +/** + * @brief Delete characteristic by UUID + * @param [in] uuid The UUID of the characteristic to be removed from the local database. + * @return Number of characteristics left. + */ +size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic"); + + for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) { + if((*it)->getUUID() == uuid) { + delete *it; + m_characteristicVector.erase(it); + break; + } + } + + NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic"); + + return m_characteristicVector.size(); +} // deleteCharacteristic + + +/** + * @brief Create a string representation of this remote service. + * @return A string representation of this remote service. + */ +std::string NimBLERemoteService::toString() { + std::string res = "Service: uuid: " + m_uuid.toString(); + char val[6]; + res += ", start_handle: "; + snprintf(val, sizeof(val), "%d", m_startHandle); + res += val; + snprintf(val, sizeof(val), "%04x", m_startHandle); + res += " 0x"; + res += val; + res += ", end_handle: "; + snprintf(val, sizeof(val), "%d", m_endHandle); + res += val; + snprintf(val, sizeof(val), "%04x", m_endHandle); + res += " 0x"; + res += val; + + for (auto &it: m_characteristicVector) { + res += "\n" + it->toString(); + } + + return res; +} // toString + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLERemoteService.h b/lib/NimBLE-Arduino/src/NimBLERemoteService.h new file mode 100644 index 0000000..0443cfd --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLERemoteService.h @@ -0,0 +1,85 @@ +/* + * NimBLERemoteService.h + * + * Created: on Jan 27 2020 + * Author H2zero + * + * Originally: + * + * BLERemoteService.h + * + * Created on: Jul 8, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_ +#define COMPONENTS_NIMBLEREMOTESERVICE_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) + +#include "NimBLEClient.h" +#include "NimBLEUUID.h" +#include "NimBLERemoteCharacteristic.h" + +#include + +class NimBLEClient; +class NimBLERemoteCharacteristic; + + +/** + * @brief A model of a remote %BLE service. + */ +class NimBLERemoteService { +public: + virtual ~NimBLERemoteService(); + + // Public methods + std::vector::iterator begin(); + std::vector::iterator end(); + NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); + NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); + void deleteCharacteristics(); + size_t deleteCharacteristic(const NimBLEUUID &uuid); + NimBLEClient* getClient(void); + //uint16_t getHandle(); + NimBLEUUID getUUID(void); + std::string getValue(const NimBLEUUID &characteristicUuid); + bool setValue(const NimBLEUUID &characteristicUuid, + const std::string &value); + std::string toString(void); + std::vector* getCharacteristics(bool refresh = false); + +private: + // Private constructor ... never meant to be created by a user application. + NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service); + + // Friends + friend class NimBLEClient; + friend class NimBLERemoteCharacteristic; + + // Private methods + bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr); + static int characteristicDiscCB(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, + void *arg); + + uint16_t getStartHandle(); + uint16_t getEndHandle(); + void releaseSemaphores(); + + // Properties + + // We maintain a vector of characteristics owned by this service. + std::vector m_characteristicVector; + + NimBLEClient* m_pClient; + NimBLEUUID m_uuid; + uint16_t m_startHandle; + uint16_t m_endHandle; +}; // NimBLERemoteService + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */ +#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEScan.cpp b/lib/NimBLE-Arduino/src/NimBLEScan.cpp new file mode 100644 index 0000000..57a5df3 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEScan.cpp @@ -0,0 +1,541 @@ +/* + * NimBLEScan.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEScan.cpp + * + * Created on: Jul 1, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + +#include "NimBLEScan.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#include +#include + +static const char* LOG_TAG = "NimBLEScan"; + + +/** + * @brief Scan constuctor. + */ +NimBLEScan::NimBLEScan() { + m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; + m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data). + m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec) + m_scan_params.window = 0; // The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec) + m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode. + m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device. + m_pAdvertisedDeviceCallbacks = nullptr; + m_ignoreResults = false; + m_pTaskData = nullptr; + m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset + m_maxResults = 0xFF; +} + + +/** + * @brief Scan destructor, release any allocated resources. + */ +NimBLEScan::~NimBLEScan() { + clearResults(); +} + +/** + * @brief Handle GAP events related to scans. + * @param [in] event The event type for this event. + * @param [in] param Parameter data for this event. + */ +/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) { + + NimBLEScan* pScan = (NimBLEScan*)arg; + + switch(event->type) { + + case BLE_GAP_EVENT_DISC: { + if(pScan->m_ignoreResults) { + NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results"); + return 0; + } + + NimBLEAddress advertisedAddress(event->disc.addr); + + // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected + if(NimBLEDevice::isIgnored(advertisedAddress)) { + NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); + return 0; + } + + NimBLEAdvertisedDevice* advertisedDevice = nullptr; + + // If we've seen this device before get a pointer to it from the vector + for(auto &it: pScan->m_scanResults.m_advertisedDevicesVector) { + if(it->getAddress() == advertisedAddress) { + advertisedDevice = it; + break; + } + } + + // If we haven't seen this device before; create a new instance and insert it in the vector. + // Otherwise just update the relevant parameters of the already known device. + if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){ + // Check if we have reach the scan results limit, ignore this one if so. + // We still need to store each device when maxResults is 0 to be able to append the scan results + if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF && + (pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults)) + { + return 0; + } + advertisedDevice = new NimBLEAdvertisedDevice(); + advertisedDevice->setAddress(advertisedAddress); + advertisedDevice->setAdvType(event->disc.event_type); + pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); + NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str()); + } else if(advertisedDevice != nullptr) { + NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str()); + } else { + // Scan response from unknown device + return 0; + } + + advertisedDevice->m_timestamp = time(nullptr); + advertisedDevice->setRSSI(event->disc.rssi); + advertisedDevice->setPayload(event->disc.data, event->disc.length_data, + event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP); + + if (pScan->m_pAdvertisedDeviceCallbacks) { + // If not active scanning or scan response is not available + // report the result to the callback now. + if(pScan->m_scan_params.passive || + (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND && + advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) + { + advertisedDevice->m_callbackSent = true; + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + + // Otherwise, wait for the scan response so we can report the complete data. + } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + advertisedDevice->m_callbackSent = true; + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + } + // If not storing results and we have invoked the callback, delete the device. + if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) { + pScan->erase(advertisedAddress); + } + } + + return 0; + } + case BLE_GAP_EVENT_DISC_COMPLETE: { + NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", + event->disc_complete.reason); + + // If a device advertised with scan reponse available and it was not received + // the callback would not have been invoked, so do it here. + if(pScan->m_pAdvertisedDeviceCallbacks) { + for(auto &it : pScan->m_scanResults.m_advertisedDevicesVector) { + if(!it->m_callbackSent) { + pScan->m_pAdvertisedDeviceCallbacks->onResult(it); + } + } + } + + if(pScan->m_maxResults == 0) { + pScan->clearResults(); + } + + if (pScan->m_scanCompleteCB != nullptr) { + pScan->m_scanCompleteCB(pScan->m_scanResults); + } + + if(pScan->m_pTaskData != nullptr) { + pScan->m_pTaskData->rc = event->disc_complete.reason; + xTaskNotifyGive(pScan->m_pTaskData->task); + } + + return 0; + } + + default: + return 0; + } +} // gapEventHandler + + +/** + * @brief Should we perform an active or passive scan? + * The default is a passive scan. An active scan means that we will request a scan response. + * @param [in] active If true, we perform an active scan otherwise a passive scan. + */ +void NimBLEScan::setActiveScan(bool active) { + m_scan_params.passive = !active; +} // setActiveScan + + +/** + * @brief Set whether or not the BLE controller should only report results + * from devices it has not already seen. + * @param [in] enabled If true, scanned devices will only be reported once. + * @details The controller has a limited buffer and will start reporting + * dupicate devices once the limit is reached. + */ +void NimBLEScan::setDuplicateFilter(bool enabled) { + m_scan_params.filter_duplicates = enabled; +} // setDuplicateFilter + + +/** + * @brief Set whether or not the BLE controller only report scan results + * from devices advertising in limited discovery mode, i.e. directed advertising. + * @param [in] enabled If true, only limited discovery devices will be in scan results. + */ +void NimBLEScan::setLimitedOnly(bool enabled) { + m_scan_params.limited = enabled; +} // setLimited + + +/** + * @brief Sets the scan filter policy. + * @param [in] filter Can be one of: + * * BLE_HCI_SCAN_FILT_NO_WL (0) + * Scanner processes all advertising packets (white list not used) except\n + * directed, connectable advertising packets not sent to the scanner. + * * BLE_HCI_SCAN_FILT_USE_WL (1) + * Scanner processes advertisements from white list only. A connectable,\n + * directed advertisment is ignored unless it contains scanners address. + * * BLE_HCI_SCAN_FILT_NO_WL_INITA (2) + * Scanner process all advertising packets (white list not used). A\n + * connectable, directed advertisement shall not be ignored if the InitA + * is a resolvable private address. + * * BLE_HCI_SCAN_FILT_USE_WL_INITA (3) + * Scanner process advertisements from white list only. A connectable,\n + * directed advertisement shall not be ignored if the InitA is a + * resolvable private address. + */ +void NimBLEScan::setFilterPolicy(uint8_t filter) { + m_scan_params.filter_policy = filter; +} // setFilterPolicy + + +/** + * @brief Sets the max number of results to store. + * @param [in] maxResults The number of results to limit storage to\n + * 0 == none (callbacks only) 0xFF == unlimited, any other value is the limit. + */ +void NimBLEScan::setMaxResults(uint8_t maxResults) { + m_maxResults = maxResults; +} + + +/** + * @brief Set the call backs to be invoked. + * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. + * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. + */ +void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, + bool wantDuplicates) { + setDuplicateFilter(!wantDuplicates); + m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; +} // setAdvertisedDeviceCallbacks + + +/** + * @brief Set the interval to scan. + * @param [in] intervalMSecs The scan interval (how often) in milliseconds. + */ +void NimBLEScan::setInterval(uint16_t intervalMSecs) { + m_scan_params.itvl = intervalMSecs / 0.625; +} // setInterval + + +/** + * @brief Set the window to actively scan. + * @param [in] windowMSecs How long to actively scan. + */ +void NimBLEScan::setWindow(uint16_t windowMSecs) { + m_scan_params.window = windowMSecs / 0.625; +} // setWindow + + +/** + * @brief Get the status of the scanner. + * @return true if scanning or scan starting. + */ +bool NimBLEScan::isScanning() { + return ble_gap_disc_active(); +} + + +/** + * @brief Start scanning. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] scanCompleteCB A function to be called when scanning has completed. + * @param [in] is_continue Set to true to save previous scan results, false to clear them. + * @return True if scan started or false if there was an error. + */ +bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { + NIMBLE_LOGD(LOG_TAG, ">> start: duration=%" PRIu32, duration); + + // Save the callback to be invoked when the scan completes. + m_scanCompleteCB = scanCompleteCB; + // Save the duration in the case that the host is reset so we can reuse it. + m_duration = duration; + + // If 0 duration specified then we assume a continuous scan is desired. + if(duration == 0){ + duration = BLE_HS_FOREVER; + } + else{ + // convert duration to milliseconds + duration = duration * 1000; + } + + // Set the flag to ignore the results while we are deleting the vector + if(!is_continue) { + m_ignoreResults = true; + } + + int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params, + NimBLEScan::handleGapEvent, this); + + switch(rc) { + case 0: + if(!is_continue) { + clearResults(); + } + break; + + case BLE_HS_EALREADY: + // Clear the cache if already scanning in case an advertiser was missed. + clearDuplicateCache(); + break; + + case BLE_HS_EBUSY: + NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress."); + break; + + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset"); + break; + + default: + NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); + break; + } + + m_ignoreResults = false; + NIMBLE_LOGD(LOG_TAG, "<< start()"); + + if(rc != 0 && rc != BLE_HS_EALREADY) { + return false; + } + return true; +} // start + + +/** + * @brief Start scanning and block until scanning has been completed. + * @param [in] duration The duration in seconds for which to scan. + * @param [in] is_continue Set to true to save previous scan results, false to clear them. + * @return The NimBLEScanResults. + */ +NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) { + if(duration == 0) { + NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); + } + + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + ble_task_data_t taskData = {nullptr, cur_task, 0, nullptr}; + m_pTaskData = &taskData; + + if(start(duration, nullptr, is_continue)) { +#ifdef ulTaskNotifyValueClear + // Clear the task notification value to ensure we block + ulTaskNotifyValueClear(cur_task, ULONG_MAX); +#endif + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } + + m_pTaskData = nullptr; + return m_scanResults; +} // start + + +/** + * @brief Stop an in progress scan. + * @return True if successful. + */ +bool NimBLEScan::stop() { + NIMBLE_LOGD(LOG_TAG, ">> stop()"); + + int rc = ble_gap_disc_cancel(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d", rc); + return false; + } + + if(m_maxResults == 0) { + clearResults(); + } + + if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) { + m_scanCompleteCB(m_scanResults); + } + + if(m_pTaskData != nullptr) { + xTaskNotifyGive(m_pTaskData->task); + } + + NIMBLE_LOGD(LOG_TAG, "<< stop()"); + return true; +} // stop + + +/** + * @brief Clears the duplicate scan filter cache. + */ +void NimBLEScan::clearDuplicateCache() { +#ifdef CONFIG_IDF_TARGET_ESP32 // Not available for ESP32C3 + esp_ble_scan_dupilcate_list_flush(); +#endif +} + + +/** + * @brief Delete peer device from the scan results vector. + * @param [in] address The address of the device to delete from the results. + * @details After disconnecting, it may be required in the case we were connected to a device without a public address. + */ +void NimBLEScan::erase(const NimBLEAddress &address) { + NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str()); + + for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) { + if((*it)->getAddress() == address) { + delete *it; + m_scanResults.m_advertisedDevicesVector.erase(it); + break; + } + } +} + + +/** + * @brief Called when host reset, we set a flag to stop scanning until synced. + */ +void NimBLEScan::onHostReset() { + m_ignoreResults = true; +} + + +/** + * @brief If the host reset and re-synced this is called. + * If the application was scanning indefinitely with a callback, restart it. + */ +void NimBLEScan::onHostSync() { + m_ignoreResults = false; + + if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) { + start(m_duration, m_scanCompleteCB); + } +} + +/** + * @brief Get the results of the scan. + * @return NimBLEScanResults object. + */ +NimBLEScanResults NimBLEScan::getResults() { + return m_scanResults; +} + + +/** + * @brief Clear the results of the scan. + */ +void NimBLEScan::clearResults() { + for(auto &it: m_scanResults.m_advertisedDevicesVector) { + delete it; + } + m_scanResults.m_advertisedDevicesVector.clear(); + clearDuplicateCache(); +} + + +/** + * @brief Dump the scan results to the log. + */ +void NimBLEScanResults::dump() { + NIMBLE_LOGD(LOG_TAG, ">> Dump scan results:"); + for (int i=0; i::iterator NimBLEScanResults::begin() { + return m_advertisedDevicesVector.begin(); +} + + +/** + * @brief Get iterator to the end of the vector of advertised device pointers. + * @return An iterator to the end of the vector of advertised device pointers. + */ +std::vector::iterator NimBLEScanResults::end() { + return m_advertisedDevicesVector.end(); +} + + +/** + * @brief Get a pointer to the specified device at the given address. + * If the address is not found a nullptr is returned. + * @param [in] address The address of the device. + * @return A pointer to the device at the specified address. + */ +NimBLEAdvertisedDevice *NimBLEScanResults::getDevice(const NimBLEAddress &address) { + for(size_t index = 0; index < m_advertisedDevicesVector.size(); index++) { + if(m_advertisedDevicesVector[index]->getAddress() == address) { + return m_advertisedDevicesVector[index]; + } + } + + return nullptr; +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */ diff --git a/lib/NimBLE-Arduino/src/NimBLEScan.h b/lib/NimBLE-Arduino/src/NimBLEScan.h new file mode 100644 index 0000000..76a1142 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEScan.h @@ -0,0 +1,103 @@ +/* + * NimBLEScan.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEScan.h + * + * Created on: Jul 1, 2017 + * Author: kolban + */ +#ifndef COMPONENTS_NIMBLE_SCAN_H_ +#define COMPONENTS_NIMBLE_SCAN_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) + +#include "NimBLEAdvertisedDevice.h" +#include "NimBLEUtils.h" + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_gap.h" +#else +#include "nimble/nimble/host/include/host/ble_gap.h" +#endif + +#include + +class NimBLEDevice; +class NimBLEScan; +class NimBLEAdvertisedDevice; +class NimBLEAdvertisedDeviceCallbacks; +class NimBLEAddress; + +/** + * @brief A class that contains and operates on the results of a BLE scan. + * @details When a scan completes, we have a set of found devices. Each device is described + * by a NimBLEAdvertisedDevice object. The number of items in the set is given by + * getCount(). We can retrieve a device by calling getDevice() passing in the + * index (starting at 0) of the desired device. + */ +class NimBLEScanResults { +public: + void dump(); + int getCount(); + NimBLEAdvertisedDevice getDevice(uint32_t i); + std::vector::iterator begin(); + std::vector::iterator end(); + NimBLEAdvertisedDevice *getDevice(const NimBLEAddress &address); + +private: + friend NimBLEScan; + std::vector m_advertisedDevicesVector; +}; + +/** + * @brief Perform and manage %BLE scans. + * + * Scanning is associated with a %BLE client that is attempting to locate BLE servers. + */ +class NimBLEScan { +public: + bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false); + NimBLEScanResults start(uint32_t duration, bool is_continue = false); + bool isScanning(); + void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates = false); + void setActiveScan(bool active); + void setInterval(uint16_t intervalMSecs); + void setWindow(uint16_t windowMSecs); + void setDuplicateFilter(bool enabled); + void setLimitedOnly(bool enabled); + void setFilterPolicy(uint8_t filter); + void clearDuplicateCache(); + bool stop(); + void clearResults(); + NimBLEScanResults getResults(); + void setMaxResults(uint8_t maxResults); + void erase(const NimBLEAddress &address); + + +private: + friend class NimBLEDevice; + + NimBLEScan(); + ~NimBLEScan(); + static int handleGapEvent(ble_gap_event* event, void* arg); + void onHostReset(); + void onHostSync(); + + NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr; + void (*m_scanCompleteCB)(NimBLEScanResults scanResults); + ble_gap_disc_params m_scan_params; + bool m_ignoreResults; + NimBLEScanResults m_scanResults; + uint32_t m_duration; + ble_task_data_t *m_pTaskData; + uint8_t m_maxResults; +}; + +#endif /* CONFIG_BT_ENABLED CONFIG_BT_NIMBLE_ROLE_OBSERVER */ +#endif /* COMPONENTS_NIMBLE_SCAN_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLESecurity.cpp b/lib/NimBLE-Arduino/src/NimBLESecurity.cpp new file mode 100644 index 0000000..df6d192 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLESecurity.cpp @@ -0,0 +1,158 @@ +/* + * NimBLESecurity.cpp + * + * Created: on Feb 22 2020 + * Author H2zero + * + * Originally: + * + * BLESecurity.cpp + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLESecurity.h" +#include "NimBLEDevice.h" + +NimBLESecurity::NimBLESecurity() { +} + +NimBLESecurity::~NimBLESecurity() { +} + + +/** + * @brief Set requested authentication mode + * @param [in] auth_req A bitmask containing one or more of: + * * ESP_LE_AUTH_NO_BOND 0x00 + * * ESP_LE_AUTH_BOND 0x01 + * * ESP_LE_AUTH_REQ_MITM (1 << 2) + * * ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM) + * * ESP_LE_AUTH_REQ_SC_ONLY (1 << 3) + * * ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY) + * * ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY) + * * ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND) + */ +void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { + NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0, + (auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0); +} + + +/** + * @brief Set our device IO capability to let end user perform authorization + * either by displaying or entering generated 6-digit pin code or use \"just works\". + * @param [in] iocap The IO capabilites our device has.\n + * Can be set to one of: + * * ESP_IO_CAP_OUT 0 + * * ESP_IO_CAP_IO 1 + * * ESP_IO_CAP_IN 2 + * * ESP_IO_CAP_NONE 3 + * * ESP_IO_CAP_KBDISP 4 + */ +void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) { + NimBLEDevice::setSecurityIOCap(iocap); +} // setCapability + + +/** + * @brief Sets the keys we will distibute during encryption. + * @param [in] init_key A bitmask of the keys we will distibute.\n + * Can be one or more of: + * * ESP_BLE_ENC_KEY_MASK (1 << 0) + * * ESP_BLE_ID_KEY_MASK (1 << 1) + * * ESP_BLE_CSR_KEY_MASK (1 << 2) + * * ESP_BLE_LINK_KEY_MASK (1 << 3) + */ +void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) { + NimBLEDevice::setSecurityInitKey(init_key); +} // setInitEncryptionKey + + +/** + * @brief Sets the keys we will accept during encryption. + * @param [in] resp_key A bitmask of the keys we will accept.\n + * Can be one or more of: + * * ESP_BLE_ENC_KEY_MASK (1 << 0) + * * ESP_BLE_ID_KEY_MASK (1 << 1) + * * ESP_BLE_CSR_KEY_MASK (1 << 2) + * * ESP_BLE_LINK_KEY_MASK (1 << 3) + */ +void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) { + NimBLEDevice::setSecurityRespKey(resp_key); +} // setRespEncryptionKey + + +/** + *@todo Requires implementation + */ +void NimBLESecurity::setKeySize(uint8_t key_size) { + + //m_keySize = key_size; + //esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); +} //setKeySize + + +/** + * @brief Sets a static PIN used to authenticate/encrypt the connection. + * @param [in] pin The 6 digit pin code to accept. + */ +void NimBLESecurity::setStaticPIN(uint32_t pin){ + //uint32_t passkey = pin; + //esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + NimBLEDevice::setSecurityPasskey(pin); + setCapability(ESP_IO_CAP_OUT); + setKeySize(); + setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); + setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +} + + +/** + * @brief Debug function to display what keys are exchanged by peers + */ + /* +char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { + char* key_str = nullptr; + switch (key_type) { + case ESP_LE_KEY_NONE: + key_str = (char*) "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = (char*) "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = (char*) "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = (char*) "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = (char*) "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = (char*) "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = (char*) "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = (char*) "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = (char*) "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = (char*) "INVALID BLE KEY TYPE"; + break; + } + return key_str; + +} // esp_key_type_to_str +*/ +#endif // CONFIG_BT_ENABLED diff --git a/lib/NimBLE-Arduino/src/NimBLESecurity.h b/lib/NimBLE-Arduino/src/NimBLESecurity.h new file mode 100644 index 0000000..0baf8b1 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLESecurity.h @@ -0,0 +1,131 @@ +/* + * NimBLESecurity.h + * + * Created: on Feb 22 2020 + * Author H2zero + * + * Originally: + * + * BLESecurity.h + * + * Created on: Dec 17, 2017 + * Author: chegewara + */ + +#ifndef COMPONENTS_NIMBLESECURITY_H_ +#define COMPONENTS_NIMBLESECURITY_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_gap.h" +#else +#include "nimble/nimble/host/include/host/ble_gap.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +#define ESP_LE_AUTH_NO_BOND 0x00 /*!< 0*/ /* relate to BTM_LE_AUTH_NO_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_BOND 0x01 /*!< 1 << 0 */ /* relate to BTM_LE_AUTH_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_MITM (1 << 2) /*!< 1 << 2 */ /* relate to BTM_LE_AUTH_REQ_MITM in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)/*!< 0101*/ +#define ESP_LE_AUTH_REQ_SC_ONLY (1 << 3) /*!< 1 << 3 */ /* relate to BTM_LE_AUTH_REQ_SC_ONLY in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1001 */ /* relate to BTM_LE_AUTH_REQ_SC_BOND in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY) /*!< 1100 */ /* relate to BTM_LE_AUTH_REQ_SC_MITM in stack/btm_api.h */ +#define ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND) /*!< 1101 */ /* relate to BTM_LE_AUTH_REQ_SC_MITM_BOND in stack/btm_api.h */ + +#define ESP_IO_CAP_OUT 0 /*!< DisplayOnly */ /* relate to BTM_IO_CAP_OUT in stack/btm_api.h */ +#define ESP_IO_CAP_IO 1 /*!< DisplayYesNo */ /* relate to BTM_IO_CAP_IO in stack/btm_api.h */ +#define ESP_IO_CAP_IN 2 /*!< KeyboardOnly */ /* relate to BTM_IO_CAP_IN in stack/btm_api.h */ +#define ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */ /* relate to BTM_IO_CAP_NONE in stack/btm_api.h */ +#define ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */ /* relate to BTM_IO_CAP_KBDISP in stack/btm_api.h */ + +/// Used to exchange the encryption key in the initialize key & response key +#define ESP_BLE_ENC_KEY_MASK (1 << 0) /* relate to BTM_BLE_ENC_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the IRK key in the initialize key & response key +#define ESP_BLE_ID_KEY_MASK (1 << 1) /* relate to BTM_BLE_ID_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the CSRK key in the initialize key & response key +#define ESP_BLE_CSR_KEY_MASK (1 << 2) /* relate to BTM_BLE_CSR_KEY_MASK in stack/btm_api.h */ +/// Used to exchange the link key(this key just used in the BLE & BR/EDR coexist mode) in the initialize key & response key +#define ESP_BLE_LINK_KEY_MASK (1 << 3) /* relate to BTM_BLE_LINK_KEY_MASK in stack/btm_api.h */ + +typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit pattern */ +typedef uint8_t esp_ble_io_cap_t; /*!< combination of the io capability */ + + +/** + * @brief A class to handle BLE security operations. + * Deprecated - provided for backward compatibility only. + * @deprecated Use the security methods provided in NimBLEDevice instead. + */ +class NimBLESecurity { +public: + NimBLESecurity(); + virtual ~NimBLESecurity(); + void setAuthenticationMode(esp_ble_auth_req_t auth_req); + void setCapability(esp_ble_io_cap_t iocap); + void setInitEncryptionKey(uint8_t init_key); + void setRespEncryptionKey(uint8_t resp_key); + void setKeySize(uint8_t key_size = 16); + void setStaticPIN(uint32_t pin); + //static char* esp_key_type_to_str(esp_ble_key_type_t key_type); +/* +private: + esp_ble_auth_req_t m_authReq; + esp_ble_io_cap_t m_iocap; + uint8_t m_initKey; + uint8_t m_respKey; + uint8_t m_keySize; +*/ +}; // BLESecurity + + +/** + * @brief Callbacks to handle GAP events related to authorization. + * Deprecated - provided for backward compatibility only. + * @deprecated Use the callbacks provided in NimBLEClientCallbacks and NimBLEServerCallbacks instead. + */ +class NimBLESecurityCallbacks { +public: + virtual ~NimBLESecurityCallbacks() {}; + + /** + * @brief Its request from peer device to input authentication pin code displayed on peer device. + * It requires that our device is capable to input 6-digits code by end user + * @return Return 6-digits integer value from input device + */ + virtual uint32_t onPassKeyRequest() = 0; + + /** + * @brief Provide us 6-digits code to perform authentication. + * It requires that our device is capable to display this code to end user + * @param [in] pass_key The PIN provided by the peer. + */ + virtual void onPassKeyNotify(uint32_t pass_key) = 0; + + /** + * @brief Here we can make decision if we want to let negotiate authorization with peer device or not + * @return Return true if we accept this peer device request + */ + virtual bool onSecurityRequest() = 0 ; + /** + * @brief Provides us information when authentication process is completed + */ + virtual void onAuthenticationComplete(ble_gap_conn_desc*) = 0; + + /** + * @brief Called when using numeric comparison for authentication. + * @param [in] pin The PIN to compare. + * @return True to accept and pair. + */ + virtual bool onConfirmPIN(uint32_t pin) = 0; +}; // BLESecurityCallbacks + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_NIMBLESECURITY_H_ diff --git a/lib/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/NimBLE-Arduino/src/NimBLEServer.cpp new file mode 100644 index 0000000..03ee785 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEServer.cpp @@ -0,0 +1,867 @@ +/* + * NimBLEServer.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.cpp + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEServer.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" +#else +#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" +#endif + +static const char* LOG_TAG = "NimBLEServer"; +static NimBLEServerCallbacks defaultCallbacks; + + +/** + * @brief Construct a %BLE Server + * + * This class is not designed to be individually instantiated. Instead one should create a server by asking + * the NimBLEDevice class. + */ +NimBLEServer::NimBLEServer() { + memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait)); +// m_svcChgChrHdl = 0xffff; // Future Use + m_pServerCallbacks = &defaultCallbacks; + m_gattsStarted = false; + m_advertiseOnDisconnect = true; + m_svcChanged = false; + m_deleteCallbacks = true; +} // NimBLEServer + + +/** + * @brief Destructor: frees all resources / attributes created. + */ +NimBLEServer::~NimBLEServer() { + for(auto &it : m_svcVec) { + delete it; + } + + if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) { + delete m_pServerCallbacks; + } +} + + +/** + * @brief Create a %BLE Service. + * @param [in] uuid The UUID of the new service. + * @return A reference to the new service object. + */ +NimBLEService* NimBLEServer::createService(const char* uuid) { + return createService(NimBLEUUID(uuid)); +} // createService + + +/** + * @brief Create a %BLE Service. + * @param [in] uuid The UUID of the new service. + * @return A reference to the new service object. + */ +NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid) { + NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); + + // Check that a service with the supplied UUID does not already exist. + if(getServiceByUUID(uuid) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", + std::string(uuid).c_str()); + } + + NimBLEService* pService = new NimBLEService(uuid); + m_svcVec.push_back(pService); + serviceChanged(); + + NIMBLE_LOGD(LOG_TAG, "<< createService"); + return pService; +} // createService + + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the service. + * @param instanceId The index of the service to return (used when multiple services have the same UUID). + * @return A pointer to the service object or nullptr if not found. + */ +NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) { + return getServiceByUUID(NimBLEUUID(uuid), instanceId); +} // getServiceByUUID + + +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the service. + * @param instanceId The index of the service to return (used when multiple services have the same UUID). + * @return A pointer to the service object or nullptr if not found. + */ +NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) { + uint16_t position = 0; + for (auto &it : m_svcVec) { + if (it->getUUID() == uuid) { + if (position == instanceId){ + return it; + } + position++; + } + } + return nullptr; +} // getServiceByUUID + +/** + * @brief Get a %BLE Service by its handle + * @param handle The handle of the service. + * @return A pointer to the service object or nullptr if not found. + */ +NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) { + for (auto &it : m_svcVec) { + if (it->getHandle() == handle) { + return it; + } + } + return nullptr; +} + +/** + * @brief Retrieve the advertising object that can be used to advertise the existence of the server. + * + * @return An advertising object. + */ +NimBLEAdvertising* NimBLEServer::getAdvertising() { + return NimBLEDevice::getAdvertising(); +} // getAdvertising + + +/** + * @brief Sends a service changed notification and resets the GATT server. + */ +void NimBLEServer::serviceChanged() { + if(m_gattsStarted) { + m_svcChanged = true; + ble_svc_gatt_changed(0x0001, 0xffff); + resetGATT(); + } +} + + +/** + * @brief Start the GATT server. Required to be called after setup of all + * services and characteristics / descriptors for the NimBLE host to register them. + */ +void NimBLEServer::start() { + if(m_gattsStarted) { + NIMBLE_LOGW(LOG_TAG, "Gatt server already started"); + return; + } + + int rc = ble_gatts_start(); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); + abort(); + } + +#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4 + ble_gatts_show_local(); +#endif +/*** Future use *** + * TODO: implement service changed handling + + ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; + ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; + + rc = ble_gatts_find_chr(&svc.u, &chr.u, NULL, &m_svcChgChrHdl); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); +*/ + // Get the assigned service handles and build a vector of characteristics + // with Notify / Indicate capabilities for event handling + for(auto &svc : m_svcVec) { + if(svc->m_removed == 0) { + rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle); + if(rc != 0) { + abort(); + } + } + + for(auto &chr : svc->m_chrVec) { + // if Notify / Indicate is enabled but we didn't create the descriptor + // we do it now. + if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || + (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { + m_notifyChrVec.push_back(chr); + } + } + } + + m_gattsStarted = true; +} // start + + +/** + * @brief Disconnect the specified client with optional reason. + * @param [in] connId Connection Id of the client to disconnect. + * @param [in] reason code for disconnecting. + * @return NimBLE host return code. + */ +int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { + NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); + + int rc = ble_gap_terminate(connId, reason); + if(rc != 0){ + NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, + NimBLEUtils::returnCodeToString(rc)); + } + + NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); + return rc; +} // disconnect + + +/** + * @brief Set the server to automatically start advertising when a client disconnects. + * @param [in] aod true == advertise, false == don't advertise. + */ +void NimBLEServer::advertiseOnDisconnect(bool aod) { + m_advertiseOnDisconnect = aod; +} // advertiseOnDisconnect + + +/** + * @brief Return the number of connected clients. + * @return The number of connected clients. + */ +size_t NimBLEServer::getConnectedCount() { + return m_connectedPeersVec.size(); +} // getConnectedCount + + +/** + * @brief Get the vector of the connected client ID's. + */ +std::vector NimBLEServer::getPeerDevices() { + return m_connectedPeersVec; +} // getPeerDevices + + +/** + * @brief Get the connection information of a connected peer by vector index. + * @param [in] index The vector index of the peer. + */ +NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) { + if (index >= m_connectedPeersVec.size()) { + NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index); + return NimBLEConnInfo(); + } + + return getPeerIDInfo(m_connectedPeersVec[index]); +} // getPeerInfo + + +/** + * @brief Get the connection information of a connected peer by address. + * @param [in] address The address of the peer. + */ +NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) { + ble_addr_t peerAddr; + memcpy(&peerAddr.val, address.getNative(),6); + peerAddr.type = address.getType(); + + NimBLEConnInfo peerInfo; + int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Peer info not found"); + } + + return peerInfo; +} // getPeerInfo + + +/** + * @brief Get the connection information of a connected peer by connection ID. + * @param [in] id The connection id of the peer. + */ +NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) { + NimBLEConnInfo peerInfo; + + int rc = ble_gap_conn_find(id, &peerInfo.m_desc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Peer info not found"); + } + + return peerInfo; +} // getPeerIDInfo + + +/** + * @brief Handle a GATT Server Event. + * + * @param [in] event + * @param [in] gatts_if + * @param [in] param + * + */ +/*STATIC*/ +int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { + NimBLEServer* server = (NimBLEServer*)arg; + NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", + NimBLEUtils::gapEventToString(event->type)); + int rc = 0; + struct ble_gap_conn_desc desc; + + switch(event->type) { + + case BLE_GAP_EVENT_CONNECT: { + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + NIMBLE_LOGE(LOG_TAG, "Connection failed"); + NimBLEDevice::startAdvertising(); + } + else { + server->m_connectedPeersVec.push_back(event->connect.conn_handle); + + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + if (rc != 0) { + return 0; + } + + server->m_pServerCallbacks->onConnect(server); + server->m_pServerCallbacks->onConnect(server, &desc); + } + + return 0; + } // BLE_GAP_EVENT_CONNECT + + + case BLE_GAP_EVENT_DISCONNECT: { + // If Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. + switch(event->disconnect.reason) { + case BLE_HS_ETIMEOUT_HCI: + case BLE_HS_EOS: + case BLE_HS_ECONTROLLER: + case BLE_HS_ENOTSYNCED: + NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NimBLEDevice::onReset(event->disconnect.reason); + break; + default: + break; + } + + server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(), + server->m_connectedPeersVec.end(), + event->disconnect.conn.conn_handle), + server->m_connectedPeersVec.end()); + + if(server->m_svcChanged) { + server->resetGATT(); + } + + server->m_pServerCallbacks->onDisconnect(server); + server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn); + + if(server->m_advertiseOnDisconnect) { + server->startAdvertising(); + } + return 0; + } // BLE_GAP_EVENT_DISCONNECT + + case BLE_GAP_EVENT_SUBSCRIBE: { + NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s", + event->subscribe.attr_handle, + (event->subscribe.cur_notify ? "true":"false")); + + for(auto &it : server->m_notifyChrVec) { + if(it->getHandle() == event->subscribe.attr_handle) { + if((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || + (it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) || + (it->getProperties() & BLE_GATT_CHR_F_READ_ENC)) + { + rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc); + if (rc != 0) { + break; + } + + if(!desc.sec_state.encrypted) { + NimBLEDevice::startSecurity(event->subscribe.conn_handle); + } + } + + it->setSubscribe(event); + break; + } + } + + return 0; + } // BLE_GAP_EVENT_SUBSCRIBE + + case BLE_GAP_EVENT_MTU: { + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.value); + rc = ble_gap_conn_find(event->mtu.conn_handle, &desc); + if (rc != 0) { + return 0; + } + + server->m_pServerCallbacks->onMTUChange(event->mtu.value, &desc); + return 0; + } // BLE_GAP_EVENT_MTU + + case BLE_GAP_EVENT_NOTIFY_TX: { + NimBLECharacteristic *pChar = nullptr; + + for(auto &it : server->m_notifyChrVec) { + if(it->getHandle() == event->notify_tx.attr_handle) { + pChar = it; + } + } + + if(pChar == nullptr) { + return 0; + } + + NimBLECharacteristicCallbacks::Status statusRC; + + if(event->notify_tx.indication) { + if(event->notify_tx.status != 0) { + if(event->notify_tx.status == BLE_HS_EDONE) { + statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE; + } else if(rc == BLE_HS_ETIMEOUT) { + statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT; + } else { + statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE; + } + } else { + return 0; + } + + server->clearIndicateWait(event->notify_tx.conn_handle); + } else { + if(event->notify_tx.status == 0) { + statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY; + } else { + statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT; + } + } + + pChar->m_pCallbacks->onStatus(pChar, statusRC, event->notify_tx.status); + + return 0; + } // BLE_GAP_EVENT_NOTIFY_TX + + case BLE_GAP_EVENT_ADV_COMPLETE: { + NIMBLE_LOGD(LOG_TAG, "Advertising Complete"); + NimBLEDevice::getAdvertising()->advCompleteCB(); + return 0; + } + + case BLE_GAP_EVENT_CONN_UPDATE: { + NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); + return 0; + } // BLE_GAP_EVENT_CONN_UPDATE + + case BLE_GAP_EVENT_REPEAT_PAIRING: { + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + if (rc != 0){ + return BLE_GAP_REPEAT_PAIRING_IGNORE; + } + + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + } // BLE_GAP_EVENT_REPEAT_PAIRING + + case BLE_GAP_EVENT_ENC_CHANGE: { + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if(rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + ///////////////////////////////////////////// + } else { + server->m_pServerCallbacks->onAuthenticationComplete(&desc); + } + + return 0; + } // BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_PASSKEY_ACTION: { + struct ble_sm_io pkey = {0,0}; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + // backward compatibility + pkey.passkey = NimBLEDevice::getSecurityPasskey(); // This is the passkey to be entered on peer + // if the (static)passkey is the default, check the callback for custom value + // both values default to the same. + if(pkey.passkey == 123456) { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp); + pkey.action = event->passkey.params.action; + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); + ///////////////////////////////////////////// + } else { + pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); + + //TODO: Handle out of band pairing + } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + static uint8_t tem_oob[16] = {0}; + pkey.action = event->passkey.params.action; + for (int i = 0; i < 16; i++) { + pkey.oob[i] = tem_oob[i]; + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); + ////////////////////////////////// + } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { + NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); + pkey.action = event->passkey.params.action; + + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); + ///////////////////////////////////////////// + } else { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + } + + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); + + } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { + NIMBLE_LOGD(LOG_TAG, "No passkey action required"); + } + + NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + return 0; + } // BLE_GAP_EVENT_PASSKEY_ACTION + + default: + break; + } + + NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + return 0; +} // handleGapEvent + + +/** + * @brief Set the server callbacks. + * + * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client + * disconnecting. This function can be called to register a callback handler that will be invoked when these + * events are detected. + * + * @param [in] pCallbacks The callbacks to be invoked. + * @param [in] deleteCallbacks if true callback class will be deleted when server is destructed. + */ +void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks) { + if (pCallbacks != nullptr){ + m_pServerCallbacks = pCallbacks; + m_deleteCallbacks = deleteCallbacks; + } else { + m_pServerCallbacks = &defaultCallbacks; + } +} // setCallbacks + + +/** + * @brief Remove a service from the server. + * + * @details Immediately removes access to the service by clients, sends a service changed indication, + * and removes the service (if applicable) from the advertisments. + * The service is not deleted unless the deleteSvc parameter is true, otherwise the service remains + * available and can be re-added in the future. If desired a removed but not deleted service can + * be deleted later by calling this method with deleteSvc set to true. + * + * @note The service will not be removed from the database until all open connections are closed + * as it requires resetting the GATT server. In the interim the service will have it's visibility disabled. + * + * @note Advertising will need to be restarted by the user after calling this as we must stop + * advertising in order to remove the service. + * + * @param [in] service The service object to remove. + * @param [in] deleteSvc true if the service should be deleted. + */ +void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) { + // Check if the service was already removed and if so check if this + // is being called to delete the object and do so if requested. + // Otherwise, ignore the call and return. + if(service->m_removed > 0) { + if(deleteSvc) { + for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) { + if ((*it) == service) { + delete *it; + m_svcVec.erase(it); + break; + } + } + } + + return; + } + + int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0); + if(rc !=0) { + return; + } + + service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + serviceChanged(); + NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID()); +} + + +/** + * @brief Adds a service which was either already created but removed from availability,\n + * or created and later added to services list. + * @param [in] service The service object to add. + * @note If it is desired to advertise the service it must be added by + * calling NimBLEAdvertising::addServiceUUID. + */ +void NimBLEServer::addService(NimBLEService* service) { + // Check that a service with the supplied UUID does not already exist. + if(getServiceByUUID(service->getUUID()) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s", + std::string(service->getUUID()).c_str()); + } + + // If adding a service that was not removed add it and return. + // Else reset GATT and send service changed notification. + if(service->m_removed == 0) { + m_svcVec.push_back(service); + return; + } + + service->m_removed = 0; + serviceChanged(); +} + + +/** + * @brief Resets the GATT server, used when services are added/removed after initialization. + */ +void NimBLEServer::resetGATT() { + if(getConnectedCount() > 0) { + return; + } + + NimBLEDevice::stopAdvertising(); + ble_gatts_reset(); + ble_svc_gap_init(); + ble_svc_gatt_init(); + + for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) { + if ((*it)->m_removed > 0) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + delete *it; + it = m_svcVec.erase(it); + } else { + ++it; + } + continue; + } + + (*it)->start(); + ++it; + } + + m_svcChanged = false; + m_gattsStarted = false; +} + + +/** + * @brief Start advertising. + * + * Start the server advertising its existence. This is a convenience function and is equivalent to + * retrieving the advertising object and invoking start upon it. + */ +void NimBLEServer::startAdvertising() { + NimBLEDevice::startAdvertising(); +} // startAdvertising + + +/** + * @brief Stop advertising. + */ +void NimBLEServer::stopAdvertising() { + NimBLEDevice::stopAdvertising(); +} // stopAdvertising + + +/** + * @brief Get the MTU of the client. + * @returns The client MTU or 0 if not found/connected. + */ +uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { + return ble_att_mtu(conn_id); +} //getPeerMTU + + +/** + * @brief Request an Update the connection parameters: + * * Can only be used after a connection has been established. + * @param [in] conn_handle The connection handle of the peer to send the request to. + * @param [in] minInterval The minimum connection interval in 1.25ms units. + * @param [in] maxInterval The maximum connection interval in 1.25ms units. + * @param [in] latency The number of packets allowed to skip (extends max interval). + * @param [in] timeout The timeout time in 10ms units before disconnecting. + */ +void NimBLEServer::updateConnParams(uint16_t conn_handle, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout) +{ + ble_gap_upd_params params; + + params.latency = latency; + params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms + params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms + params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms + params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units + params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units + + int rc = ble_gap_update_params(conn_handle, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } +} // updateConnParams + + +/** + * @brief Request an update of the data packet length. + * * Can only be used after a connection has been established. + * @details Sends a data length update request to the peer. + * The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes. + * The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE. + * @param [in] conn_handle The connection handle of the peer to send the request to. + * @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB). + */ +void NimBLEServer::setDataLen(uint16_t conn_handle, uint16_t tx_octets) { +#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \ + (ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432 + return; +#else + uint16_t tx_time = (tx_octets + 14) * 8; + + int rc = ble_gap_set_data_len(conn_handle, tx_octets, tx_time); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } +#endif +} // setDataLen + + +bool NimBLEServer::setIndicateWait(uint16_t conn_handle) { + for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { + if(m_indWait[i] == conn_handle) { + return false; + } + } + + return true; +} + + +void NimBLEServer::clearIndicateWait(uint16_t conn_handle) { + for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { + if(m_indWait[i] == conn_handle) { + m_indWait[i] = BLE_HS_CONN_HANDLE_NONE; + return; + } + } +} + + +/** Default callback handlers */ + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +} // onConnect + + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +} // onConnect + + +void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); +} // onDisconnect + +void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); +} // onDisconnect + +void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default"); +} // onMTUChange + +uint32_t NimBLEServerCallbacks::onPassKeyRequest(){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456"); + return 123456; +} +/* +void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key); +} + +bool NimBLEServerCallbacks::onSecurityRequest(){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true"); + return true; +} +*/ +void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default"); +} +bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){ + NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true"); + return true; +} + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLEServer.h b/lib/NimBLE-Arduino/src/NimBLEServer.h new file mode 100644 index 0000000..605445a --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEServer.h @@ -0,0 +1,172 @@ +/* + * NimBLEServer.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.h + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVER_H_ +#define MAIN_NIMBLESERVER_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#define NIMBLE_ATT_REMOVE_HIDE 1 +#define NIMBLE_ATT_REMOVE_DELETE 2 + +#define onMtuChanged onMTUChange + +#include "NimBLEUtils.h" +#include "NimBLEAddress.h" +#include "NimBLEAdvertising.h" +#include "NimBLEService.h" +#include "NimBLESecurity.h" +#include "NimBLEConnInfo.h" + + +class NimBLEService; +class NimBLECharacteristic; +class NimBLEServerCallbacks; + + +/** + * @brief The model of a %BLE server. + */ +class NimBLEServer { +public: + size_t getConnectedCount(); + NimBLEService* createService(const char* uuid); + NimBLEService* createService(const NimBLEUUID &uuid); + void removeService(NimBLEService* service, bool deleteSvc = false); + void addService(NimBLEService* service); + NimBLEAdvertising* getAdvertising(); + void setCallbacks(NimBLEServerCallbacks* pCallbacks, + bool deleteCallbacks = true); + void startAdvertising(); + void stopAdvertising(); + void start(); + NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0); + NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0); + NimBLEService* getServiceByHandle(uint16_t handle); + int disconnect(uint16_t connID, + uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); + void updateConnParams(uint16_t conn_handle, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout); + void setDataLen(uint16_t conn_handle, uint16_t tx_octets); + uint16_t getPeerMTU(uint16_t conn_id); + std::vector getPeerDevices(); + NimBLEConnInfo getPeerInfo(size_t index); + NimBLEConnInfo getPeerInfo(const NimBLEAddress& address); + NimBLEConnInfo getPeerIDInfo(uint16_t id); + void advertiseOnDisconnect(bool); + +private: + NimBLEServer(); + ~NimBLEServer(); + friend class NimBLECharacteristic; + friend class NimBLEService; + friend class NimBLEDevice; + friend class NimBLEAdvertising; + + bool m_gattsStarted; + bool m_advertiseOnDisconnect; + bool m_svcChanged; + NimBLEServerCallbacks* m_pServerCallbacks; + bool m_deleteCallbacks; + uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS]; + std::vector m_connectedPeersVec; + +// uint16_t m_svcChgChrHdl; // Future use + + std::vector m_svcVec; + std::vector m_notifyChrVec; + + static int handleGapEvent(struct ble_gap_event *event, void *arg); + void serviceChanged(); + void resetGATT(); + bool setIndicateWait(uint16_t conn_handle); + void clearIndicateWait(uint16_t conn_handle); +}; // NimBLEServer + + +/** + * @brief Callbacks associated with the operation of a %BLE server. + */ +class NimBLEServerCallbacks { +public: + virtual ~NimBLEServerCallbacks() {}; + + /** + * @brief Handle a client connection. + * This is called when a client connects. + * @param [in] pServer A pointer to the %BLE server that received the client connection. + */ + virtual void onConnect(NimBLEServer* pServer); + + /** + * @brief Handle a client connection. + * This is called when a client connects. + * @param [in] pServer A pointer to the %BLE server that received the client connection. + * @param [in] desc A pointer to the connection description structure containig information + * about the connection parameters. + */ + virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc); + + /** + * @brief Handle a client disconnection. + * This is called when a client disconnects. + * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. + */ + virtual void onDisconnect(NimBLEServer* pServer); + + /** + * @brief Handle a client disconnection. + * This is called when a client discconnects. + * @param [in] pServer A pointer to the %BLE server that received the client disconnection. + * @param [in] desc A pointer to the connection description structure containig information + * about the connection. + */ + virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc); + + /** + * @brief Called when the connection MTU changes. + * @param [in] MTU The new MTU value. + * @param [in] desc A pointer to the connection description structure containig information + * about the connection. + */ + virtual void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc); + + /** + * @brief Called when a client requests a passkey for pairing. + * @return The passkey to be sent to the client. + */ + virtual uint32_t onPassKeyRequest(); + + //virtual void onPassKeyNotify(uint32_t pass_key); + //virtual bool onSecurityRequest(); + + /** + * @brief Called when the pairing procedure is complete. + * @param [in] desc A pointer to the struct containing the connection information.\n + * This can be used to check the status of the connection encryption/pairing. + */ + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc); + + /** + * @brief Called when using numeric comparision for pairing. + * @param [in] pin The pin to compare with the client. + * @return True to accept the pin. + */ + virtual bool onConfirmPIN(uint32_t pin); +}; // NimBLEServerCallbacks + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif /* MAIN_NIMBLESERVER_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEService.cpp b/lib/NimBLE-Arduino/src/NimBLEService.cpp new file mode 100644 index 0000000..b124fcb --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEService.cpp @@ -0,0 +1,435 @@ +/* + * NimBLEService.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEDevice.h" +#include "NimBLEService.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEService"; // Tag for logging. + +#define NULL_HANDLE (0xffff) + + +/** + * @brief Construct an instance of the NimBLEService + * @param [in] uuid The UUID of the service. + */ +NimBLEService::NimBLEService(const char* uuid) +: NimBLEService(NimBLEUUID(uuid)) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + */ +NimBLEService::NimBLEService(const NimBLEUUID &uuid) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pSvcDef = nullptr; + m_removed = 0; + +} // NimBLEService + + +NimBLEService::~NimBLEService() { + if(m_pSvcDef != nullptr) { + if(m_pSvcDef->characteristics != nullptr) { + for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) { + if(m_pSvcDef->characteristics[i].descriptors) { + delete(m_pSvcDef->characteristics[i].descriptors); + } + } + delete(m_pSvcDef->characteristics); + } + + delete(m_pSvcDef); + } + + for(auto &it : m_chrVec) { + delete it; + } +} + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void NimBLEService::dump() { + NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x", + m_uuid.toString().c_str(), + m_handle); + + std::string res; + int count = 0; + char hex[5]; + for (auto &it: m_chrVec) { + if (count > 0) {res += "\n";} + snprintf(hex, sizeof(hex), "%04x", it->getHandle()); + count++; + res += "handle: 0x"; + res += hex; + res += ", uuid: " + std::string(it->getUUID()); + } + NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +NimBLEUUID NimBLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Builds the database of characteristics/descriptors for the service + * and registers it with the NimBLE stack. + * @return bool success/failure . + */ +bool NimBLEService::start() { + NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str()); + + // Rebuild the service definition if the server attributes have changed. + if(getServer()->m_svcChanged && m_pSvcDef != nullptr) { + if(m_pSvcDef[0].characteristics) { + if(m_pSvcDef[0].characteristics[0].descriptors) { + delete(m_pSvcDef[0].characteristics[0].descriptors); + } + delete(m_pSvcDef[0].characteristics); + } + delete(m_pSvcDef); + m_pSvcDef = nullptr; + } + + if(m_pSvcDef == nullptr) { + // Nimble requires an array of services to be sent to the api + // Since we are adding 1 at a time we create an array of 2 and set the type + // of the second service to 0 to indicate the end of the array. + ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; + ble_gatt_chr_def* pChr_a = nullptr; + ble_gatt_dsc_def* pDsc_a = nullptr; + + svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; + svc[0].uuid = &m_uuid.getNative()->u; + svc[0].includes = NULL; + + int removedCount = 0; + for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ) { + if ((*it)->m_removed > 0) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + delete *it; + it = m_chrVec.erase(it); + } else { + ++removedCount; + ++it; + } + continue; + } + + ++it; + } + + size_t numChrs = m_chrVec.size() - removedCount; + NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); + + if(!numChrs){ + svc[0].characteristics = NULL; + }else{ + // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end + // of the characteristics for the service. We create 1 extra and set it to null + // for this purpose. + pChr_a = new ble_gatt_chr_def[numChrs + 1]; + uint8_t i = 0; + for(auto chr_it = m_chrVec.begin(); chr_it != m_chrVec.end(); ++chr_it) { + if((*chr_it)->m_removed > 0) { + continue; + } + + removedCount = 0; + for(auto it = (*chr_it)->m_dscVec.begin(); it != (*chr_it)->m_dscVec.end(); ) { + if ((*it)->m_removed > 0) { + if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) { + delete *it; + it = (*chr_it)->m_dscVec.erase(it); + } else { + ++removedCount; + ++it; + } + continue; + } + + ++it; + } + + size_t numDscs = (*chr_it)->m_dscVec.size() - removedCount; + + if(!numDscs){ + pChr_a[i].descriptors = NULL; + } else { + // Must have last descriptor uuid = 0 so we have to create 1 extra + pDsc_a = new ble_gatt_dsc_def[numDscs+1]; + uint8_t d = 0; + for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) { + if((*dsc_it)->m_removed > 0) { + continue; + } + pDsc_a[d].uuid = &(*dsc_it)->m_uuid.getNative()->u; + pDsc_a[d].att_flags = (*dsc_it)->m_properties; + pDsc_a[d].min_key_size = 0; + pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; + pDsc_a[d].arg = (*dsc_it); + ++d; + } + + pDsc_a[numDscs].uuid = NULL; + pChr_a[i].descriptors = pDsc_a; + } + + pChr_a[i].uuid = &(*chr_it)->m_uuid.getNative()->u; + pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; + pChr_a[i].arg = (*chr_it); + pChr_a[i].flags = (*chr_it)->m_properties; + pChr_a[i].min_key_size = 0; + pChr_a[i].val_handle = &(*chr_it)->m_handle; + ++i; + } + + pChr_a[numChrs].uuid = NULL; + svc[0].characteristics = pChr_a; + } + + // end of services must indicate to api with type = 0 + svc[1].type = 0; + m_pSvcDef = svc; + } + + int rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + } + + rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; + + } + + NIMBLE_LOGD(LOG_TAG, "<< start()"); + return true; +} // start + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t NimBLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. + * @return The new BLE characteristic. + */ +NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties, uint16_t max_len) { + return createCharacteristic(NimBLEUUID(uuid), properties, max_len); +} + + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. + * @return The new BLE characteristic. + */ +NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) { + NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, max_len, this); + + if (getCharacteristic(uuid) != nullptr) { + NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s", + std::string(uuid).c_str()); + } + + addCharacteristic(pCharacteristic); + return pCharacteristic; +} // createCharacteristic + + +/** + * @brief Add a characteristic to the service. + * @param[in] pCharacteristic A pointer to the characteristic instance to add to the service. + */ +void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) { + bool foundRemoved = false; + + if(pCharacteristic->m_removed > 0) { + for(auto& it : m_chrVec) { + if(it == pCharacteristic) { + foundRemoved = true; + pCharacteristic->m_removed = 0; + } + } + } + + if(!foundRemoved) { + m_chrVec.push_back(pCharacteristic); + } + + pCharacteristic->setService(this); + getServer()->serviceChanged(); +} // addCharacteristic + + +/** + * @brief Remove a characteristic from the service. + * @param[in] pCharacteristic A pointer to the characteristic instance to remove from the service. + * @param[in] deleteChr If true it will delete the characteristic instance and free it's resources. + */ +void NimBLEService::removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr) { + // Check if the characteristic was already removed and if so, check if this + // is being called to delete the object and do so if requested. + // Otherwise, ignore the call and return. + if(pCharacteristic->m_removed > 0) { + if(deleteChr) { + for(auto it = m_chrVec.begin(); it != m_chrVec.end(); ++it) { + if ((*it) == pCharacteristic) { + m_chrVec.erase(it); + delete *it; + break; + } + } + } + + return; + } + + pCharacteristic->m_removed = deleteChr ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE; + getServer()->serviceChanged(); +} // removeCharacteristic + + +/** + * @brief Get a pointer to the characteristic object with the specified UUID. + * @param [in] uuid The UUID of the characteristic. + * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID). + * @return A pointer to the characteristic object or nullptr if not found. + */ +NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) { + return getCharacteristic(NimBLEUUID(uuid), instanceId); +} + +/** + * @brief Get a pointer to the characteristic object with the specified UUID. + * @param [in] uuid The UUID of the characteristic. + * @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID). + * @return A pointer to the characteristic object or nullptr if not found. + */ +NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) { + uint16_t position = 0; + for (auto &it : m_chrVec) { + if (it->getUUID() == uuid) { + if (position == instanceId) { + return it; + } + position++; + } + } + return nullptr; +} + +/** + * @brief Get a pointer to the characteristic object with the specified handle. + * @param handle The handle of the characteristic. + * @return A pointer to the characteristic object or nullptr if not found. + */ +NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) { + for (auto &it : m_chrVec) { + if (it->getHandle() == handle) { + return it; + } + } + return nullptr; +} + +/** + * @return A vector containing pointers to each characteristic associated with this service. + */ +std::vector NimBLEService::getCharacteristics() { + return m_chrVec; +} + +/** + * @return A vector containing pointers to each characteristic with the provided UUID associated with this service. + */ +std::vector NimBLEService::getCharacteristics(const char *uuid) { + return getCharacteristics(NimBLEUUID(uuid)); +} + +/** + * @return A vector containing pointers to each characteristic with the provided UUID associated with this service. + */ +std::vector NimBLEService::getCharacteristics(const NimBLEUUID &uuid) { + std::vector result; + for (auto &it : m_chrVec) { + if (it->getUUID() == uuid) { + result.push_back(it); + } + } + return result; +} + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string NimBLEService::toString() { + std::string res = "UUID: " + getUUID().toString(); + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", getHandle()); + res += ", handle: 0x"; + res += hex; + return res; +} // toString + + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +NimBLEServer* NimBLEService::getServer() { + return NimBLEDevice::getServer(); +}// getServer + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ diff --git a/lib/NimBLE-Arduino/src/NimBLEService.h b/lib/NimBLE-Arduino/src/NimBLEService.h new file mode 100644 index 0000000..21ec1af --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEService.h @@ -0,0 +1,87 @@ +/* + * NimBLEService.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVICE_H_ +#define MAIN_NIMBLESERVICE_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + +#include "NimBLEServer.h" +#include "NimBLECharacteristic.h" +#include "NimBLEUUID.h" + + +class NimBLEServer; +class NimBLECharacteristic; + + +/** + * @brief The model of a %BLE service. + * + */ +class NimBLEService { +public: + + NimBLEService(const char* uuid); + NimBLEService(const NimBLEUUID &uuid); + ~NimBLEService(); + + NimBLEServer* getServer(); + + NimBLEUUID getUUID(); + uint16_t getHandle(); + std::string toString(); + void dump(); + + bool start(); + + NimBLECharacteristic* createCharacteristic(const char* uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + + NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid, + uint32_t properties = + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + + void addCharacteristic(NimBLECharacteristic* pCharacteristic); + void removeCharacteristic(NimBLECharacteristic* pCharacteristic, bool deleteChr = false); + NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0); + NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0); + NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle); + + std::vector getCharacteristics(); + std::vector getCharacteristics(const char* uuid); + std::vector getCharacteristics(const NimBLEUUID &uuid); + + +private: + + friend class NimBLEServer; + friend class NimBLEDevice; + + uint16_t m_handle; + NimBLEUUID m_uuid; + ble_gatt_svc_def* m_pSvcDef; + uint8_t m_removed; + std::vector m_chrVec; + +}; // NimBLEService + +#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */ +#endif /* MAIN_NIMBLESERVICE_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEUUID.cpp b/lib/NimBLE-Arduino/src/NimBLEUUID.cpp new file mode 100644 index 0000000..255f771 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEUUID.cpp @@ -0,0 +1,360 @@ +/* + * NimBLEUUID.cpp + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEUUID.cpp + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEUtils.h" +#include "NimBLEUUID.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEUUID"; + + +/** + * @brief Create a UUID from a string. + * + * Create a UUID from a string. There will be two possible stories here. Either the string represents + * a binary data field or the string represents a hex encoding of a UUID. + * For the hex encoding, here is an example: + * + * ``` + * "beb5483e-36e1-4688-b7f5-ea07361b26a8" + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * 12345678-90ab-cdef-1234-567890abcdef + * ``` + * + * This has a length of 36 characters. We need to parse this into 16 bytes. + * + * @param [in] value The string to build a UUID from. + */ + NimBLEUUID::NimBLEUUID(const std::string &value) { + m_valueSet = true; + if (value.length() == 4) { + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = strtoul(value.c_str(), NULL, 16); + } + else if (value.length() == 8) { + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = strtoul(value.c_str(), NULL, 16); + } + else if (value.length() == 16) { + *this = NimBLEUUID((uint8_t*)value.data(), 16, true); + } + else if (value.length() == 36) { + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. + char * position = const_cast(value.c_str()); + uint32_t first = strtoul(position, &position, 16); + uint16_t second = strtoul(position + 1, &position, 16); + uint16_t third = strtoul(position + 1, &position, 16); + uint16_t fourth = strtoul(position + 1, &position, 16); + uint64_t fifth = strtoull(position + 1, NULL, 16); + *this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth); + } + else { + m_valueSet = false; + } +} // NimBLEUUID(std::string) + + +/** + * @brief Create a UUID from 2, 4, 16 bytes of memory. + * @param [in] pData The pointer to the start of the UUID. + * @param [in] size The size of the data. + * @param [in] msbFirst Is the MSB first in pData memory? + */ +NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) { + uint8_t *uuidValue = nullptr; + + switch(size) { + case 2: + uuidValue = (uint8_t*)&m_uuid.u16.value; + m_uuid.u.type = BLE_UUID_TYPE_16; + break; + case 4: + uuidValue = (uint8_t*)&m_uuid.u32.value; + m_uuid.u.type = BLE_UUID_TYPE_32; + break; + case 16: + uuidValue = m_uuid.u128.value; + m_uuid.u.type = BLE_UUID_TYPE_128; + break; + default: + m_valueSet = false; + NIMBLE_LOGE(LOG_TAG, "Invalid UUID size"); + return; + } + if (msbFirst) { + std::reverse_copy(pData, pData + size, uuidValue); + } else { + memcpy(uuidValue, pData, size); + } + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the 16bit value. + * @param [in] uuid The 16bit short form UUID. + */ +NimBLEUUID::NimBLEUUID(uint16_t uuid) { + m_uuid.u.type = BLE_UUID_TYPE_16; + m_uuid.u16.value = uuid; + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the 32bit value. + * @param [in] uuid The 32bit short form UUID. + */ +NimBLEUUID::NimBLEUUID(uint32_t uuid) { + m_uuid.u.type = BLE_UUID_TYPE_32; + m_uuid.u32.value = uuid; + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the native UUID. + * @param [in] uuid The native UUID. + */ +NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) { + m_uuid.u.type = BLE_UUID_TYPE_128; + memcpy(m_uuid.u128.value, uuid->value, 16); + m_valueSet = true; +} // NimBLEUUID + + +/** + * @brief Create a UUID from the 128bit value using hex parts instead of string, + * instead of NimBLEUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"), it becomes + * NimBLEUUID(0xebe0ccb0, 0x7a0a, 0x4b0c, 0x8a1a6ff2997da3a6) + * + * @param [in] first The first 32bit of the UUID. + * @param [in] second The next 16bit of the UUID. + * @param [in] third The next 16bit of the UUID. + * @param [in] fourth The last 64bit of the UUID, combining the last 2 parts of the string equivalent + */ +NimBLEUUID::NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth) { + m_uuid.u.type = BLE_UUID_TYPE_128; + memcpy(m_uuid.u128.value + 12, &first, 4); + memcpy(m_uuid.u128.value + 10, &second, 2); + memcpy(m_uuid.u128.value + 8, &third, 2); + memcpy(m_uuid.u128.value, &fourth, 8); + m_valueSet = true; +} + + +/** + * @brief Creates an empty UUID. + */ +NimBLEUUID::NimBLEUUID() { + m_valueSet = false; +} // NimBLEUUID + + +/** + * @brief Get the number of bits in this uuid. + * @return The number of bits in the UUID. One of 16, 32 or 128. + */ +uint8_t NimBLEUUID::bitSize() const { + if (!m_valueSet) return 0; + return m_uuid.u.type; +} // bitSize + + +/** + * @brief Compare a UUID against this UUID. + * + * @param [in] uuid The UUID to compare against. + * @return True if the UUIDs are equal and false otherwise. + */ +bool NimBLEUUID::equals(const NimBLEUUID &uuid) const { + return *this == uuid; +} + + +/** + * Create a NimBLEUUID from a string of the form: + * 0xNNNN + * 0xNNNNNNNN + * 0x + * NNNN + * NNNNNNNN + * + * + * @param [in] uuid The string to create the UUID from. + */ +NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) { + uint8_t start = 0; + if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. + start = 2; + } + uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use. + + if(len == 4) { + uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16); + return NimBLEUUID(x); + } else if (len == 8) { + uint32_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16); + return NimBLEUUID(x); + } else if (len == 36) { + return NimBLEUUID(uuid); + } + return NimBLEUUID(); +} // fromString + + +/** + * @brief Get the native UUID value. + * @return The native UUID value or nullptr if not set. + */ +const ble_uuid_any_t* NimBLEUUID::getNative() const { + if (m_valueSet == false) { + NIMBLE_LOGD(LOG_TAG,"<< Return of un-initialized UUID!"); + return nullptr; + } + return &m_uuid; +} // getNative + + +/** + * @brief Convert a UUID to its 128 bit representation. + * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. + * This method will convert 16 or 32bit representations to the full 128bit. + * @return The NimBLEUUID converted to 128bit. + */ +const NimBLEUUID &NimBLEUUID::to128() { + // If we either don't have a value or are already a 128 bit UUID, nothing further to do. + if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_128) { + return *this; + } + + // If we are 16 bit or 32 bit, then set the other bytes of the UUID. + if (m_uuid.u.type == BLE_UUID_TYPE_16) { + *this = NimBLEUUID(m_uuid.u16.value, 0x0000, 0x1000, 0x800000805f9b34fb); + } + else if (m_uuid.u.type == BLE_UUID_TYPE_32) { + *this = NimBLEUUID(m_uuid.u32.value, 0x0000, 0x1000, 0x800000805f9b34fb); + } + + return *this; +} // to128 + + +/** + * @brief Convert 128 bit UUID to its 16 bit representation. + * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. + * This method will convert a 128bit uuid to 16bit if it contains the ble base uuid. + * @return The NimBLEUUID converted to 16bit if successful, otherwise the original uuid. + */ +const NimBLEUUID& NimBLEUUID::to16() { + if (!m_valueSet || m_uuid.u.type == BLE_UUID_TYPE_16) { + return *this; + } + + if (m_uuid.u.type == BLE_UUID_TYPE_128) { + uint8_t base128[] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x10, 0x00, 0x00}; + if (memcmp(m_uuid.u128.value, base128, sizeof(base128)) == 0 ) { + *this = NimBLEUUID(*(uint16_t*)(m_uuid.u128.value + 12)); + } + } + + return *this; +} + + +/** + * @brief Get a string representation of the UUID. + * @details + * The format of a string is: + * 01234567 8901 2345 6789 012345678901 + * 0000180d-0000-1000-8000-00805f9b34fb + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * + * @return A string representation of the UUID. + * @deprecated Use std::string() operator instead. + */ +std::string NimBLEUUID::toString() const { + return std::string(*this); +} // toString + + +/** + * @brief Convienience operator to check if this UUID is equal to another. + */ +bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const { + if(m_valueSet && rhs.m_valueSet) { + if(m_uuid.u.type != rhs.m_uuid.u.type) { + uint8_t uuidBase[16] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + if(m_uuid.u.type == BLE_UUID_TYPE_128){ + if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){ + memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2); + } else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){ + memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4); + } + return memcmp(m_uuid.u128.value,uuidBase,16) == 0; + + } else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) { + if(m_uuid.u.type == BLE_UUID_TYPE_16){ + memcpy(uuidBase+12, &m_uuid.u16.value, 2); + } else if (m_uuid.u.type == BLE_UUID_TYPE_32){ + memcpy(uuidBase+12, &m_uuid.u32.value, 4); + } + return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0; + + } else { + return false; + } + } + + return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0; + } + + return m_valueSet == rhs.m_valueSet; +} + + +/** + * @brief Convienience operator to check if this UUID is not equal to another. + */ +bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const { + return !this->operator==(rhs); +} + + +/** + * @brief Convienience operator to convert this UUID to string representation. + * @details This allows passing NimBLEUUID to functions + * that accept std::string and/or or it's methods as a parameter. + */ +NimBLEUUID::operator std::string() const { + if (!m_valueSet) return std::string(); // If we have no value, nothing to format. + + char buf[BLE_UUID_STR_LEN]; + + return ble_uuid_to_str(&m_uuid.u, buf); +} + + +#endif /* CONFIG_BT_ENABLED */ diff --git a/lib/NimBLE-Arduino/src/NimBLEUUID.h b/lib/NimBLE-Arduino/src/NimBLEUUID.h new file mode 100644 index 0000000..2c24971 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEUUID.h @@ -0,0 +1,64 @@ +/* + * NimBLEUUID.h + * + * Created: on Jan 24 2020 + * Author H2zero + * + * Originally: + * + * BLEUUID.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef COMPONENTS_NIMBLEUUID_H_ +#define COMPONENTS_NIMBLEUUID_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_uuid.h" +#else +#include "nimble/nimble/host/include/host/ble_uuid.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +/** + * @brief A model of a %BLE UUID. + */ +class NimBLEUUID { +public: + NimBLEUUID(const std::string &uuid); + NimBLEUUID(uint16_t uuid); + NimBLEUUID(uint32_t uuid); + NimBLEUUID(const ble_uuid128_t* uuid); + NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst); + NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth); + NimBLEUUID(); + + uint8_t bitSize() const; + bool equals(const NimBLEUUID &uuid) const; + const ble_uuid_any_t* getNative() const; + const NimBLEUUID & to128(); + const NimBLEUUID& to16(); + std::string toString() const; + static NimBLEUUID fromString(const std::string &uuid); + + bool operator ==(const NimBLEUUID & rhs) const; + bool operator !=(const NimBLEUUID & rhs) const; + operator std::string() const; + +private: + ble_uuid_any_t m_uuid; + bool m_valueSet = false; +}; // NimBLEUUID +#endif /* CONFIG_BT_ENABLED */ +#endif /* COMPONENTS_NIMBLEUUID_H_ */ diff --git a/lib/NimBLE-Arduino/src/NimBLEUtils.cpp b/lib/NimBLE-Arduino/src/NimBLEUtils.cpp new file mode 100644 index 0000000..c6bd823 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEUtils.cpp @@ -0,0 +1,518 @@ +/* + * NimBLEUtils.cpp + * + * Created: on Jan 25 2020 + * Author H2zero + * + */ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEUtils"; + + +/** + * @brief A function for checking validity of connection parameters. + * @param [in] params A pointer to the structure containing the parameters to check. + * @return valid == 0 or error code. + */ +int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) { + /* Check connection interval min */ + if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + /* Check connection interval max */ + if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_max > BLE_HCI_CONN_ITVL_MAX) || + (params->itvl_max < params->itvl_min)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection latency */ + if (params->latency > BLE_HCI_CONN_LATENCY_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check supervision timeout */ + if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection event length */ + if (params->min_ce_len > params->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return 0; +} + + +/** + * @brief Converts a return code from the NimBLE stack to a text string. + * @param [in] rc The return code to convert. + * @return A string representation of the return code. + */ +const char* NimBLEUtils::returnCodeToString(int rc) { +#if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) + switch(rc) { + case 0: + return "SUCCESS"; + case BLE_HS_EAGAIN: + return "Temporary failure; try again."; + case BLE_HS_EALREADY: + return "Operation already in progress or completed."; + case BLE_HS_EINVAL: + return "One or more arguments are invalid."; + case BLE_HS_EMSGSIZE: + return "The provided buffer is too small."; + case BLE_HS_ENOENT: + return "No entry matching the specified criteria."; + case BLE_HS_ENOMEM: + return "Operation failed due to resource exhaustion."; + case BLE_HS_ENOTCONN: + return "No open connection with the specified handle."; + case BLE_HS_ENOTSUP: + return "Operation disabled at compile time."; + case BLE_HS_EAPP: + return "Application callback behaved unexpectedly."; + case BLE_HS_EBADDATA: + return "Command from peer is invalid."; + case BLE_HS_EOS: + return "Mynewt OS error."; + case BLE_HS_ECONTROLLER: + return "Event from controller is invalid."; + case BLE_HS_ETIMEOUT: + return "Operation timed out."; + case BLE_HS_EDONE: + return "Operation completed successfully."; + case BLE_HS_EBUSY: + return "Operation cannot be performed until procedure completes."; + case BLE_HS_EREJECT: + return "Peer rejected a connection parameter update request."; + case BLE_HS_EUNKNOWN: + return "Unexpected failure; catch all."; + case BLE_HS_EROLE: + return "Operation requires different role (e.g., central vs. peripheral)."; + case BLE_HS_ETIMEOUT_HCI: + return "HCI request timed out; controller unresponsive."; + case BLE_HS_ENOMEM_EVT: + return "Controller failed to send event due to memory exhaustion (combined host-controller only)."; + case BLE_HS_ENOADDR: + return "Operation requires an identity address but none configured."; + case BLE_HS_ENOTSYNCED: + return "Attempt to use the host before it is synced with controller."; + case BLE_HS_EAUTHEN: + return "Insufficient authentication."; + case BLE_HS_EAUTHOR: + return "Insufficient authorization."; + case BLE_HS_EENCRYPT: + return "Insufficient encryption level."; + case BLE_HS_EENCRYPT_KEY_SZ: + return "Insufficient key size."; + case BLE_HS_ESTORE_CAP: + return "Storage at capacity."; + case BLE_HS_ESTORE_FAIL: + return "Storage IO error."; + case (0x0100+BLE_ATT_ERR_INVALID_HANDLE ): + return "The attribute handle given was not valid on this server."; + case (0x0100+BLE_ATT_ERR_READ_NOT_PERMITTED ): + return "The attribute cannot be read."; + case (0x0100+BLE_ATT_ERR_WRITE_NOT_PERMITTED ): + return "The attribute cannot be written."; + case (0x0100+BLE_ATT_ERR_INVALID_PDU ): + return "The attribute PDU was invalid."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHEN ): + return "The attribute requires authentication before it can be read or written."; + case (0x0100+BLE_ATT_ERR_REQ_NOT_SUPPORTED ): + return "Attribute server does not support the request received from the client."; + case (0x0100+BLE_ATT_ERR_INVALID_OFFSET ): + return "Offset specified was past the end of the attribute."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHOR ): + return "The attribute requires authorization before it can be read or written."; + case (0x0100+BLE_ATT_ERR_PREPARE_QUEUE_FULL ): + return "Too many prepare writes have been queued."; + case (0x0100+BLE_ATT_ERR_ATTR_NOT_FOUND ): + return "No attribute found within the given attribute handle range."; + case (0x0100+BLE_ATT_ERR_ATTR_NOT_LONG ): + return "The attribute cannot be read or written using the Read Blob Request."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_KEY_SZ ): + return "The Encryption Key Size used for encrypting this link is insufficient."; + case (0x0100+BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ): + return "The attribute value length is invalid for the operation."; + case (0x0100+BLE_ATT_ERR_UNLIKELY ): + return "The attribute request has encountered an error that was unlikely, could not be completed as requested."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_ENC ): + return "The attribute requires encryption before it can be read or written."; + case (0x0100+BLE_ATT_ERR_UNSUPPORTED_GROUP ): + return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."; + case (0x0100+BLE_ATT_ERR_INSUFFICIENT_RES ): + return "Insufficient Resources to complete the request."; + case (0x0200+BLE_ERR_UNKNOWN_HCI_CMD ): + return "Unknown HCI Command"; + case (0x0200+BLE_ERR_UNK_CONN_ID ): + return "Unknown Connection Identifier"; + case (0x0200+BLE_ERR_HW_FAIL ): + return "Hardware Failure"; + case (0x0200+BLE_ERR_PAGE_TMO ): + return "Page Timeout"; + case (0x0200+BLE_ERR_AUTH_FAIL ): + return "Authentication Failure"; + case (0x0200+BLE_ERR_PINKEY_MISSING ): + return "PIN or Key Missing"; + case (0x0200+BLE_ERR_MEM_CAPACITY ): + return "Memory Capacity Exceeded"; + case (0x0200+BLE_ERR_CONN_SPVN_TMO ): + return "Connection Timeout"; + case (0x0200+BLE_ERR_CONN_LIMIT ): + return "Connection Limit Exceeded"; + case (0x0200+BLE_ERR_SYNCH_CONN_LIMIT ): + return "Synchronous Connection Limit To A Device Exceeded"; + case (0x0200+BLE_ERR_ACL_CONN_EXISTS ): + return "ACL Connection Already Exists"; + case (0x0200+BLE_ERR_CMD_DISALLOWED ): + return "Command Disallowed"; + case (0x0200+BLE_ERR_CONN_REJ_RESOURCES ): + return "Connection Rejected due to Limited Resources"; + case (0x0200+BLE_ERR_CONN_REJ_SECURITY ): + return "Connection Rejected Due To Security Reasons"; + case (0x0200+BLE_ERR_CONN_REJ_BD_ADDR ): + return "Connection Rejected due to Unacceptable BD_ADDR"; + case (0x0200+BLE_ERR_CONN_ACCEPT_TMO ): + return "Connection Accept Timeout Exceeded"; + case (0x0200+BLE_ERR_UNSUPPORTED ): + return "Unsupported Feature or Parameter Value"; + case (0x0200+BLE_ERR_INV_HCI_CMD_PARMS ): + return "Invalid HCI Command Parameters"; + case (0x0200+BLE_ERR_REM_USER_CONN_TERM ): + return "Remote User Terminated Connection"; + case (0x0200+BLE_ERR_RD_CONN_TERM_RESRCS ): + return "Remote Device Terminated Connection due to Low Resources"; + case (0x0200+BLE_ERR_RD_CONN_TERM_PWROFF ): + return "Remote Device Terminated Connection due to Power Off"; + case (0x0200+BLE_ERR_CONN_TERM_LOCAL ): + return "Connection Terminated By Local Host"; + case (0x0200+BLE_ERR_REPEATED_ATTEMPTS ): + return "Repeated Attempts"; + case (0x0200+BLE_ERR_NO_PAIRING ): + return "Pairing Not Allowed"; + case (0x0200+BLE_ERR_UNK_LMP ): + return "Unknown LMP PDU"; + case (0x0200+BLE_ERR_UNSUPP_REM_FEATURE ): + return "Unsupported Remote Feature / Unsupported LMP Feature"; + case (0x0200+BLE_ERR_SCO_OFFSET ): + return "SCO Offset Rejected"; + case (0x0200+BLE_ERR_SCO_ITVL ): + return "SCO Interval Rejected"; + case (0x0200+BLE_ERR_SCO_AIR_MODE ): + return "SCO Air Mode Rejected"; + case (0x0200+BLE_ERR_INV_LMP_LL_PARM ): + return "Invalid LMP Parameters / Invalid LL Parameters"; + case (0x0200+BLE_ERR_UNSPECIFIED ): + return "Unspecified Error"; + case (0x0200+BLE_ERR_UNSUPP_LMP_LL_PARM ): + return "Unsupported LMP Parameter Value / Unsupported LL Parameter Value"; + case (0x0200+BLE_ERR_NO_ROLE_CHANGE ): + return "Role Change Not Allowed"; + case (0x0200+BLE_ERR_LMP_LL_RSP_TMO ): + return "LMP Response Timeout / LL Response Timeout"; + case (0x0200+BLE_ERR_LMP_COLLISION ): + return "LMP Error Transaction Collision"; + case (0x0200+BLE_ERR_LMP_PDU ): + return "LMP PDU Not Allowed"; + case (0x0200+BLE_ERR_ENCRYPTION_MODE ): + return "Encryption Mode Not Acceptable"; + case (0x0200+BLE_ERR_LINK_KEY_CHANGE ): + return "Link Key cannot be Changed"; + case (0x0200+BLE_ERR_UNSUPP_QOS ): + return "Requested QoS Not Supported"; + case (0x0200+BLE_ERR_INSTANT_PASSED ): + return "Instant Passed"; + case (0x0200+BLE_ERR_UNIT_KEY_PAIRING ): + return "Pairing With Unit Key Not Supported"; + case (0x0200+BLE_ERR_DIFF_TRANS_COLL ): + return "Different Transaction Collision"; + case (0x0200+BLE_ERR_QOS_PARM ): + return "QoS Unacceptable Parameter"; + case (0x0200+BLE_ERR_QOS_REJECTED ): + return "QoS Rejected"; + case (0x0200+BLE_ERR_CHAN_CLASS ): + return "Channel Classification Not Supported"; + case (0x0200+BLE_ERR_INSUFFICIENT_SEC ): + return "Insufficient Security"; + case (0x0200+BLE_ERR_PARM_OUT_OF_RANGE ): + return "Parameter Out Of Mandatory Range"; + case (0x0200+BLE_ERR_PENDING_ROLE_SW ): + return "Role Switch Pending"; + case (0x0200+BLE_ERR_RESERVED_SLOT ): + return "Reserved Slot Violation"; + case (0x0200+BLE_ERR_ROLE_SW_FAIL ): + return "Role Switch Failed"; + case (0x0200+BLE_ERR_INQ_RSP_TOO_BIG ): + return "Extended Inquiry Response Too Large"; + case (0x0200+BLE_ERR_SEC_SIMPLE_PAIR ): + return "Secure Simple Pairing Not Supported By Host"; + case (0x0200+BLE_ERR_HOST_BUSY_PAIR ): + return "Host Busy - Pairing"; + case (0x0200+BLE_ERR_CONN_REJ_CHANNEL ): + return "Connection Rejected, No Suitable Channel Found"; + case (0x0200+BLE_ERR_CTLR_BUSY ): + return "Controller Busy"; + case (0x0200+BLE_ERR_CONN_PARMS ): + return "Unacceptable Connection Parameters"; + case (0x0200+BLE_ERR_DIR_ADV_TMO ): + return "Directed Advertising Timeout"; + case (0x0200+BLE_ERR_CONN_TERM_MIC ): + return "Connection Terminated due to MIC Failure"; + case (0x0200+BLE_ERR_CONN_ESTABLISHMENT ): + return "Connection Failed to be Established"; + case (0x0200+BLE_ERR_MAC_CONN_FAIL ): + return "MAC Connection Failed"; + case (0x0200+BLE_ERR_COARSE_CLK_ADJ ): + return "Coarse Clock Adjustment Rejected"; + case (0x0300+BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD ): + return "Invalid or unsupported incoming L2CAP sig command."; + case (0x0300+BLE_L2CAP_SIG_ERR_MTU_EXCEEDED ): + return "Incoming packet too large."; + case (0x0300+BLE_L2CAP_SIG_ERR_INVALID_CID ): + return "No channel with specified ID."; + case (0x0400+BLE_SM_ERR_PASSKEY ): + return "The user input of passkey failed, for example, the user cancelled the operation."; + case (0x0400+BLE_SM_ERR_OOB ): + return "The OOB data is not available."; + case (0x0400+BLE_SM_ERR_AUTHREQ ): + return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; + case (0x0400+BLE_SM_ERR_CONFIRM_MISMATCH ): + return "The confirm value does not match the calculated compare value."; + case (0x0400+BLE_SM_ERR_PAIR_NOT_SUPP ): + return "Pairing is not supported by the device."; + case (0x0400+BLE_SM_ERR_ENC_KEY_SZ ): + return "The resultant encryption key size is insufficient for the security requirements of this device."; + case (0x0400+BLE_SM_ERR_CMD_NOT_SUPP ): + return "The SMP command received is not supported on this device."; + case (0x0400+BLE_SM_ERR_UNSPECIFIED ): + return "Pairing failed due to an unspecified reason."; + case (0x0400+BLE_SM_ERR_REPEATED ): + return "Pairing or authentication procedure disallowed, too little time has elapsed since last pairing request or security request."; + case (0x0400+BLE_SM_ERR_INVAL ): + return "Command length is invalid or that a parameter is outside of the specified range."; + case (0x0400+BLE_SM_ERR_DHKEY ): + return "DHKey Check value received doesn't match the one calculated by the local device."; + case (0x0400+BLE_SM_ERR_NUMCMP ): + return "Confirm values in the numeric comparison protocol do not match."; + case (0x0400+BLE_SM_ERR_ALREADY ): + return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; + case (0x0400+BLE_SM_ERR_CROSS_TRANS ): + return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; + case (0x0500+BLE_SM_ERR_PASSKEY ): + return "The user input of passkey failed or the user cancelled the operation."; + case (0x0500+BLE_SM_ERR_OOB ): + return "The OOB data is not available."; + case (0x0500+BLE_SM_ERR_AUTHREQ ): + return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; + case (0x0500+BLE_SM_ERR_CONFIRM_MISMATCH ): + return "The confirm value does not match the calculated compare value."; + case (0x0500+BLE_SM_ERR_PAIR_NOT_SUPP ): + return "Pairing is not supported by the device."; + case (0x0500+BLE_SM_ERR_ENC_KEY_SZ ): + return "The resultant encryption key size is insufficient for the security requirements of this device."; + case (0x0500+BLE_SM_ERR_CMD_NOT_SUPP ): + return "The SMP command received is not supported on this device."; + case (0x0500+BLE_SM_ERR_UNSPECIFIED ): + return "Pairing failed due to an unspecified reason."; + case (0x0500+BLE_SM_ERR_REPEATED ): + return "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request."; + case (0x0500+BLE_SM_ERR_INVAL ): + return "Command length is invalid or a parameter is outside of the specified range."; + case (0x0500+BLE_SM_ERR_DHKEY ): + return "Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device."; + case (0x0500+BLE_SM_ERR_NUMCMP ): + return "Confirm values in the numeric comparison protocol do not match."; + case (0x0500+BLE_SM_ERR_ALREADY ): + return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; + case (0x0500+BLE_SM_ERR_CROSS_TRANS ): + return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; + default: + return "Unknown"; + } +#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) + (void)rc; + return ""; +#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) +} + + +/** + * @brief Convert the advertising type flag to a string. + * @param advType The type to convert. + * @return A string representation of the advertising flags. + */ +const char* NimBLEUtils::advTypeToString(uint8_t advType) { +#if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT) + switch(advType) { + case BLE_HCI_ADV_TYPE_ADV_IND : //0 + return "Undirected - Connectable / Scannable"; + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: //1 + return "Directed High Duty - Connectable"; + case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: //2 + return "Non-Connectable - Scan Response Available"; + case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: //3 + return "Non-Connectable - No Scan Response"; + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: //4 + return "Directed Low Duty - Connectable"; + default: + return "Unknown flag"; + } +#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT) + (void)advType; + return ""; +#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT) +} // adFlagsToString + + +/** + * @brief Create a hex representation of data. + * + * @param [in] target Where to write the hex string. If this is null, we malloc storage. + * @param [in] source The start of the binary data. + * @param [in] length The length of the data to convert. + * @return A pointer to the formatted buffer. + */ +char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t length) { + // Guard against too much data. + if (length > 100) length = 100; + + if (target == nullptr) { + target = (uint8_t*) malloc(length * 2 + 1); + if (target == nullptr) { + NIMBLE_LOGE(LOG_TAG, "buildHexData: malloc failed"); + return nullptr; + } + } + char* startOfData = (char*) target; + + for (int i = 0; i < length; i++) { + sprintf((char*) target, "%.2x", (char) *source); + source++; + target += 2; + } + + // Handle the special case where there was no data. + if (length == 0) { + *startOfData = 0; + } + + return startOfData; +} // buildHexData + + +/** + * @brief Utility function to log the gap event info. + * @param [in] event A pointer to the gap event structure. + * @param [in] arg Unused. + */ +void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){ + (void)arg; +#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) + NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type)); +#else + (void)event; +#endif +} + + +/** + * @brief Convert a GAP event type to a string representation. + * @param [in] eventType The type of event. + * @return A string representation of the event type. + */ +const char* NimBLEUtils::gapEventToString(uint8_t eventType) { +#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) + switch (eventType) { + case BLE_GAP_EVENT_CONNECT : //0 + return "BLE_GAP_EVENT_CONNECT "; + + case BLE_GAP_EVENT_DISCONNECT: //1 + return "BLE_GAP_EVENT_DISCONNECT"; + + case BLE_GAP_EVENT_CONN_UPDATE: //3 + return "BLE_GAP_EVENT_CONN_UPDATE"; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: //4 + return "BLE_GAP_EVENT_CONN_UPDATE_REQ"; + + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: //5 + return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ"; + + case BLE_GAP_EVENT_TERM_FAILURE: //6 + return "BLE_GAP_EVENT_TERM_FAILURE"; + + case BLE_GAP_EVENT_DISC: //7 + return "BLE_GAP_EVENT_DISC"; + + case BLE_GAP_EVENT_DISC_COMPLETE: //8 + return "BLE_GAP_EVENT_DISC_COMPLETE"; + + case BLE_GAP_EVENT_ADV_COMPLETE: //9 + return "BLE_GAP_EVENT_ADV_COMPLETE"; + + case BLE_GAP_EVENT_ENC_CHANGE: //10 + return "BLE_GAP_EVENT_ENC_CHANGE"; + + case BLE_GAP_EVENT_PASSKEY_ACTION : //11 + return "BLE_GAP_EVENT_PASSKEY_ACTION"; + + case BLE_GAP_EVENT_NOTIFY_RX: //12 + return "BLE_GAP_EVENT_NOTIFY_RX"; + + case BLE_GAP_EVENT_NOTIFY_TX : //13 + return "BLE_GAP_EVENT_NOTIFY_TX"; + + case BLE_GAP_EVENT_SUBSCRIBE : //14 + return "BLE_GAP_EVENT_SUBSCRIBE"; + + case BLE_GAP_EVENT_MTU: //15 + return "BLE_GAP_EVENT_MTU"; + + case BLE_GAP_EVENT_IDENTITY_RESOLVED: //16 + return "BLE_GAP_EVENT_IDENTITY_RESOLVED"; + + case BLE_GAP_EVENT_REPEAT_PAIRING: //17 + return "BLE_GAP_EVENT_REPEAT_PAIRING"; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: //18 + return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE"; + + case BLE_GAP_EVENT_EXT_DISC: //19 + return "BLE_GAP_EVENT_EXT_DISC"; +#ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these + case BLE_GAP_EVENT_PERIODIC_SYNC: //20 + return "BLE_GAP_EVENT_PERIODIC_SYNC"; + + case BLE_GAP_EVENT_PERIODIC_REPORT: //21 + return "BLE_GAP_EVENT_PERIODIC_REPORT"; + + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: //22 + return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST"; + + case BLE_GAP_EVENT_SCAN_REQ_RCVD: //23 + return "BLE_GAP_EVENT_SCAN_REQ_RCVD"; +#endif + default: + NIMBLE_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); + return "Unknown event type"; + } +#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) + (void)eventType; + return ""; +#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) +} // gapEventToString + +#endif //CONFIG_BT_ENABLED diff --git a/lib/NimBLE-Arduino/src/NimBLEUtils.h b/lib/NimBLE-Arduino/src/NimBLEUtils.h new file mode 100644 index 0000000..006d935 --- /dev/null +++ b/lib/NimBLE-Arduino/src/NimBLEUtils.h @@ -0,0 +1,51 @@ +/* + * NimBLEUtils.h + * + * Created: on Jan 25 2020 + * Author H2zero + * + */ + +#ifndef COMPONENTS_NIMBLEUTILS_H_ +#define COMPONENTS_NIMBLEUTILS_H_ + +#include "nimconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#if defined(CONFIG_NIMBLE_CPP_IDF) +#include "host/ble_gap.h" +#else +#include "nimble/nimble/host/include/host/ble_gap.h" +#endif + +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + +#include + +typedef struct { + void *pATT; + TaskHandle_t task; + int rc; + void *buf; +} ble_task_data_t; + + +/** + * @brief A BLE Utility class with methods for debugging and general purpose use. + */ +class NimBLEUtils { +public: + static void dumpGapEvent(ble_gap_event *event, void *arg); + static const char* gapEventToString(uint8_t eventType); + static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length); + static const char* advTypeToString(uint8_t advType); + static const char* returnCodeToString(int rc); + static int checkConnParams(ble_gap_conn_params* params); +}; + + +#endif // CONFIG_BT_ENABLED +#endif // COMPONENTS_NIMBLEUTILS_H_ diff --git a/lib/NimBLE-Arduino/src/nimble/CODING_STANDARDS.md b/lib/NimBLE-Arduino/src/nimble/CODING_STANDARDS.md new file mode 100644 index 0000000..d14b9fd --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/CODING_STANDARDS.md @@ -0,0 +1,267 @@ +# Coding Style for Apache NimBLE + +Apache NimBLE project is part of Apache Mynewt projct and follows its coding +style. + +# Coding Style for Apache Mynewt Core + +This document is meant to define the coding style for Apache Mynewt, and +all subprojects of Apache Mynewt. This covers C and Assembly coding +conventions, *only*. Other languages (such as Go), have their own +coding conventions. + +## Headers + +* All files that are newly written, should have the Apache License clause +at the top of them. + +* For files that are copied from another source, but contain an Apache +compatible license, the original license header shall be maintained. + +* For more information on applying the Apache license, the definitive +source is here: http://www.apache.org/dev/apply-license.html + +* The Apache License clause for the top of files is as follows: + +```no-highlight +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +``` + +## Whitespace and Braces + +* Code must be indented to 4 spaces, tabs should not be used. + +* Do not add whitespace at the end of a line. + +* Put space after keywords (for, if, return, switch, while). + +* for, else, if, while statements must have braces around their +code blocks, i.e., do: + +``` + if (x) { + assert(0); + } else { + assert(0); + } +``` + +Not: + +``` + if (x) + assert(0); + else + assert(0); +``` + +* Braces for statements must be on the same line as the statement. Good: + +``` + for (i = 0; i < 10; i++) { + if (i == 5) { + break; + } else { + continue; + } + } +``` + +Not: + +``` + for (i = 0; i < 10; i++) + { <-- brace must be on same line as for + if (i == 5) { + break; + } <-- no new line between else + else { + continue; + } + } +``` + +* After a function declaration, the braces should be on a newline, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +not: + +``` + static void * + function(int var1, int var2) { +``` + +## Line Length and Wrap + +* Line length should never exceed 79 columns. + +* When you have to wrap a long statement, put the operator at the end of the + line. i.e.: + +``` + if (x && + y == 10 && + b) +``` + +Not: + +``` + if (x + && y == 10 + && b) +``` + +## Comments + +* No C++ style comments allowed. + +* When using a single line comment, put it above the line of code that you +intend to comment, i.e., do: + +``` + /* check variable */ + if (a) { +``` + +Not: + +``` + if (a) { /* check variable */ +``` + + +* All public APIs should be commented with Doxygen style comments describing +purpose, parameters and return values. Private APIs need not be documented. + + +## Header files + +* Header files must contain the following structure: + * Apache License (see above) + * ```#ifdef``` aliasing, to prevent multiple includes + * ```#include``` directives for other required header files + * ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs + * Contents of the header file + +* ```#ifdef``` aliasing, shall be in the following format, where +the package name is "os" and the file name is "callout.h": + +```no-highlight +#ifndef _OS_CALLOUT_H +#define _OS_CALLOUT_H +``` + +* ```#include``` directives must happen prior to the cplusplus +wrapper. + +* The cplusplus wrapper must have the following format, and precedes +any contents of the header file: + +```no-highlight +#ifdef __cplusplus +#extern "C" { +##endif +``` + +## Naming + +* Names of functions, structures and variables must be in all lowercase. + +* Names should be as short as possible, but no shorter. + +* Globally visible names must be prefixed with the name of the module, +followed by the '_' character, i.e.: + +``` + os_callout_init(&c) +``` + +Not: + +``` + callout_init(c) +``` + +## Functions + +* No spaces after function names when calling a function, i.e, do: + +``` + rc = function(a) +``` + +Not: + +``` + rc = function (a) +``` + + +* Arguments to function calls should have spaces between the comma, i.e. do: + +``` + rc = function(a, b) +``` + +Not: + +``` + rc = function(a,b) +``` + +* The function type must be on a line by itself preceding the function, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +Not: + +``` + static void *function(int var1, int var2) + { +``` + +* In general, for functions that return values that denote success or error, 0 +shall be success, and non-zero shall be the failure code. + +## Variables and Macros + +* Do not use typedefs for structures. This makes it impossible for +applications to use pointers to those structures opaquely. + +* typedef may be used for non-structure types, where it is beneficial to +hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate +typedefs by applying the ```_t``` marker to them. + +* Place all function-local variable definitions at the top of the function body, before any statements. + +## Compiler Directives + +* Code must compile cleanly with -Wall enabled. + diff --git a/lib/NimBLE-Arduino/src/nimble/LICENSE b/lib/NimBLE-Arduino/src/nimble/LICENSE new file mode 100644 index 0000000..08b9b21 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/LICENSE @@ -0,0 +1,217 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE diff --git a/lib/NimBLE-Arduino/src/nimble/NOTICE b/lib/NimBLE-Arduino/src/nimble/NOTICE new file mode 100644 index 0000000..02fe592 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/NOTICE @@ -0,0 +1,9 @@ +Apache Mynewt NimBLE +Copyright 2015-2020 The Apache Software Foundation +Modifications Copyright 2017-2020 Espressif Systems (Shanghai) CO., LTD. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. diff --git a/lib/NimBLE-Arduino/src/nimble/README.md b/lib/NimBLE-Arduino/src/nimble/README.md new file mode 100644 index 0000000..37103be --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/README.md @@ -0,0 +1,169 @@ + + +Apache Mynewt + +## Overview + +Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) +that completely replaces the proprietary SoftDevice on Nordic chipsets. It is +part of [Apache Mynewt project](https://github.com/apache/mynewt-core). + +Features highlight: + - Support for 251 byte packet size + - Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central + - Support for up to 32 simultaneous connections. + - Legacy and SC (secure connections) SMP support (pairing and bonding). + - Advertising Extensions. + - Coded (aka Long Range) and 2M PHYs. + - Bluetooth Mesh. + +## Supported hardware + +Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board +and architecture [supported](https://github.com/apache/mynewt-core#overview) +by Apache Mynewt OS. + + +## Browsing + +If you are browsing around the source tree, and want to see some of the +major functional chunks, here are a few pointers: + +- nimble/controller: Contains code for controller including Link Layer and HCI implementation +([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller)) + +- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52) +([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers)) + +- nimble/host: Contains code for host subsystem. This includes protocols like +L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP), +Generic Attribute Profile (GATT) and Security Manager (SM). +([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host)) + +- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem. +([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh)) + +- nimble/transport: Contains code for supported transport protocols between host +and controller. This includes UART, emSPI and RAM (used in combined build when +host and controller run on same CPU) +([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport)) + +- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported +operating systems +([porting](https://github.com/apache/mynewt-nimble/tree/master/porting)) + +- ext: Contains external libraries used by NimBLE. Those are used if not +provided by OS +([ext](https://github.com/apache/mynewt-nimble/tree/master/ext)) + +- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os)) + +## Sample Applications + +There are also some sample applications that show how to Apache Mynewt NimBLE +stack. These sample applications are located in the `apps/` directory of +Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples: + +* [blecent](https://github.com/apache/mynewt-nimble/tree/master/apps/blecent): +A basic central device with no user interface. This application scans for +a peripheral that supports the alert notification service (ANS). Upon +discovering such a peripheral, blecent connects and performs a characteristic +read, characteristic write, and notification subscription. +* [blehci](https://github.com/apache/mynewt-nimble/tree/master/apps/blehci): +Implements a BLE controller-only application. A separate host-only +implementation, such as Linux's BlueZ, can interface with this application via +HCI over UART. +* [bleprph](https://github.com/apache/mynewt-nimble/tree/master/apps/bleprph): An + implementation of a minimal BLE peripheral. +* [btshell](https://github.com/apache/mynewt-nimble/tree/master/apps/btshell): A + shell-like application allowing to configure and use most of NimBLE + functionality from command line. +* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart): +Implements a simple BLE peripheral that supports the Nordic +UART / Serial Port Emulation service +(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html). + +# Getting Help + +If you are having trouble using or contributing to Apache Mynewt NimBLE, or just +want to talk to a human about what you're working on, you can contact us via the +[developers mailing list](mailto:dev@mynewt.apache.org). + +Although not a formal channel, you can also find a number of core developers +on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://mynewt.slack.com/join/shared_invite/enQtNjA1MTg0NzgyNzg3LTcyMmZiOGQzOGMxM2U4ODFmMTIwNjNmYTE5Y2UwYjQwZWIxNTE0MTUzY2JmMTEzOWFjYWZkNGM0YmM4MzAxNWQ) + +Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers) +for some help troubleshooting first. + +# Contributing + +Anybody who works with Apache Mynewt can be a contributing member of the +community that develops and deploys it. The process of releasing an operating +system for microcontrollers is never done: and we welcome your contributions +to that effort. + +More information can be found at the Community section of the Apache Mynewt +website, located [here](https://mynewt.apache.org/community). + +## Pull Requests + +Apache Mynewt welcomes pull request via Github. Discussions are done on Github, +but depending on the topic, can also be relayed to the official Apache Mynewt +developer mailing list dev@mynewt.apache.org. + +If you are suggesting a new feature, please email the developer list directly, +with a description of the feature you are planning to work on. + +## Filing Bugs + +Bugs can be filed on the +[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Bug". + +Where possible, please include a self-contained reproduction case! + +## Feature Requests + +Feature requests should also be filed on the +[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Feature" or "Enhancement" depending on the scope. + +## Writing Tests + +We love getting newt tests! Apache Mynewt is a huge undertaking, and improving +code coverage is a win for every Apache Mynewt user. + + + +# License + +The code in this repository is all under either the Apache 2 license, or a +license compatible with the Apache 2 license. See the LICENSE file for more +information. diff --git a/lib/NimBLE-Arduino/src/nimble/RELEASE_NOTES.md b/lib/NimBLE-Arduino/src/nimble/RELEASE_NOTES.md new file mode 100644 index 0000000..3bdd31a --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/RELEASE_NOTES.md @@ -0,0 +1,33 @@ +# RELEASE NOTES + +18 March 2020 - Apache NimBLE v1.3.0 + +For full release notes, please visit the +[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes). + +Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) that completely +replaces the proprietary SoftDevice on Nordic chipsets. + +New features in this version of NimBLE include: + +* Support for Bluetooth Core Specification 5.1 +* New blestress test application +* Dialog DA1469x CMAC driver +* Support for LE Secure Connections out-of-band (OOB) association model +* Support for automated generation of syscfg for ports +* Qualification related bugfixes +* BLE Mesh improvements - fixes and resync with latest Zephyr code +* RIOT OS port fixes and improvements +* btshell sample application improvements +* improvements for bttester application +* Controller duplicates filtering improvements +* Multi PHY support improvements +* Memory and CPU usage optimizations +* Use of packed structs for HCI (code size reduction) +* Linux sample improvements +* PTS test instructions updates +* Clock managements improvements in controller + +If working on next-generation RTOS and Bluetooth protocol stack +sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt +Developer's list, dev@mynewt.apache.org. diff --git a/lib/NimBLE-Arduino/src/nimble/console/console.h b/lib/NimBLE-Arduino/src/nimble/console/console.h new file mode 100644 index 0000000..b1052e6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/console/console.h @@ -0,0 +1,16 @@ +#ifndef __CONSOLE_H__ +#define __CONSOLE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define console_printf printf + +#ifdef __cplusplus +} +#endif + +#endif /* __CONSOLE_H__ */ \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_compiler.h b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_compiler.h new file mode 100644 index 0000000..917c660 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_compiler.h @@ -0,0 +1,59 @@ +// Copyright 2016-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_COMPILER_H +#define __ESP_COMPILER_H + +/* + * The likely and unlikely macro pairs: + * These macros are useful to place when application + * knows the majority ocurrence of a decision paths, + * placing one of these macros can hint the compiler + * to reorder instructions producing more optimized + * code. + */ +#if (CONFIG_COMPILER_OPTIMIZATION_PERF) +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +/* + * Utility macros used for designated initializers, which work differently + * in C99 and C++ standards mainly for aggregate types. + * The member separator, comma, is already part of the macro, please omit the trailing comma. + * Usage example: + * struct config_t { char* pchr; char arr[SIZE]; } config = { + * ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(pchr) + * ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(arr, "Value") + * }; + */ +#ifdef __cplusplus +#define ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(member, value) { .member = value }, +#define ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(member) .member = { }, +#else +#define ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(member, value) .member = value, +#define ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(member) +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_nimble_hci.h b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_nimble_hci.h new file mode 100644 index 0000000..aa05a20 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/include/esp_nimble_hci.h @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef ESP_PLATFORM +#ifndef __ESP_NIMBLE_HCI_H__ +#define __ESP_NIMBLE_HCI_H__ + +#include "nimble/nimble/include/nimble/ble_hci_trans.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HCI_UART_H4_NONE 0x00 +#define BLE_HCI_UART_H4_CMD 0x01 +#define BLE_HCI_UART_H4_ACL 0x02 +#define BLE_HCI_UART_H4_SCO 0x03 +#define BLE_HCI_UART_H4_EVT 0x04 + +/** + * @brief Initialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * This function initializes the transport buffers to be exchanged + * between NimBLE host and ESP controller. It also registers required + * host callbacks with the controller. + * + * @return + * - ESP_OK if the initialization is successful + * - Appropriate error code from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_init(void); + +/** + * @brief Initialize ESP Bluetooth controller(link layer) and VHCI transport + * layer between NimBLE Host and ESP Bluetooth controller + * + * This function initializes ESP controller in BLE only mode and the + * transport buffers to be exchanged between NimBLE host and ESP controller. + * It also registers required host callbacks with the controller. + * + * Below is the sequence of APIs to be called to initialize/enable NimBLE host and ESP controller: + * + * @code{c} + * void ble_host_task(void *param) + * { + * nimble_port_run(); //This function will return only when nimble_port_stop() is executed. + * nimble_port_freertos_deinit(); + * } + * + * int ret = esp_nimble_hci_and_controller_init(); + * if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_hci_and_controller_init() failed with error: %d", ret); + * return; + * } + * + * nimble_port_init(); + * + * //Initialize the NimBLE Host configuration + * + * nimble_port_freertos_init(ble_host_task); + * @endcode + * + * nimble_port_freertos_init() is an optional call that creates a new task in which the NimBLE + * host will run. The task function should have a call to nimble_port_run(). If a separate task + * is not required, calling nimble_port_run() will run the NimBLE host in the current task. + * + * @return + * - ESP_OK if the initialization is successful + * - Appropriate error code from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_and_controller_init(void); + +/** + * @brief Deinitialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller + * + * @note This function should be called after the NimBLE host is deinitialized. + * + * @return + * - ESP_OK if the deinitialization is successful + * - Appropriate error codes from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_deinit(void); + +/** + * @brief Deinitialize VHCI transport layer between NimBLE Host and + * ESP Bluetooth controller and disable and deinitialize the controller + * + * @note This function should not be executed in the context of Bluetooth host task. + * + * @note This function should be called after the NimBLE host is deinitialized. + * + * Below is the sequence of APIs to be called to disable/deinit NimBLE host and ESP controller: + * + * @code{c} + * int ret = nimble_port_stop(); + * if (ret == 0) { + * nimble_port_deinit(); + * + * ret = esp_nimble_hci_and_controller_deinit(); + * if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret); + * } + * } + * @endcode + * + * If nimble_port_freertos_init() is used during initialization, then + * nimble_port_freertos_deinit() should be called in the host task after nimble_port_run(). + * + * @return + * - ESP_OK if the deinitialization is successful + * - Appropriate error codes from esp_err_t in case of an error + */ +esp_err_t esp_nimble_hci_and_controller_deinit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_NIMBLE_HCI_H__ */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/src/esp_nimble_hci.c b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/src/esp_nimble_hci.c new file mode 100644 index 0000000..f4857ee --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/esp-hci/src/esp_nimble_hci.c @@ -0,0 +1,583 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef ESP_PLATFORM + +#include +#include "nimble/porting/nimble/include/sysinit/sysinit.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/host/include/host/ble_hs.h" +#include "nimble/porting/nimble/include/nimble/nimble_port.h" +#include "nimble/porting/npl/freertos/include/nimble/nimble_port_freertos.h" +#include "../include/esp_nimble_hci.h" +#include "../../port/include/esp_nimble_mem.h" +#include +#include +#include "../include/esp_compiler.h" +/* IPC is used to improve performance when calls come from a processor not running the NimBLE stack */ +/* but does not exist for solo */ +#ifndef CONFIG_FREERTOS_UNICORE + #include "esp_ipc.h" +#endif + +#define NIMBLE_VHCI_TIMEOUT_MS 2000 +#define BLE_HCI_EVENT_HDR_LEN (2) +#define BLE_HCI_CMD_HDR_LEN (3) + +static ble_hci_trans_rx_cmd_fn *ble_hci_rx_cmd_hs_cb; +static void *ble_hci_rx_cmd_hs_arg; + +static ble_hci_trans_rx_acl_fn *ble_hci_rx_acl_hs_cb; +static void *ble_hci_rx_acl_hs_arg; + +static struct os_mbuf_pool ble_hci_acl_mbuf_pool; +static struct os_mempool_ext ble_hci_acl_pool; +/* + * The MBUF payload size must accommodate the HCI data header size plus the + * maximum ACL data packet length. The ACL block size is the size of the + * mbufs we will allocate. + */ +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) + +static os_membuf_t *ble_hci_acl_buf; + +static struct os_mempool ble_hci_cmd_pool; +static os_membuf_t *ble_hci_cmd_buf; + +static struct os_mempool ble_hci_evt_hi_pool; +static os_membuf_t *ble_hci_evt_hi_buf; + +static struct os_mempool ble_hci_evt_lo_pool; +static os_membuf_t *ble_hci_evt_lo_buf; + +static SemaphoreHandle_t vhci_send_sem; +const static char *TAG = "NimBLE"; + +int os_msys_buf_alloc(void); +void os_msys_buf_free(void); + +void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb, + void *cmd_arg, + ble_hci_trans_rx_acl_fn *acl_cb, + void *acl_arg) +{ + ble_hci_rx_cmd_hs_cb = cmd_cb; + ble_hci_rx_cmd_hs_arg = cmd_arg; + ble_hci_rx_acl_hs_cb = acl_cb; + ble_hci_rx_acl_hs_arg = acl_arg; +} + +/* Added; Called from the core NimBLE is running on, not used for unicore */ +#ifndef CONFIG_FREERTOS_UNICORE +void ble_hci_trans_hs_cmd_tx_on_core(void *arg) +{ + // Ugly but necessary as the arduino core does not provide enough IPC stack for variables. + esp_vhci_host_send_packet((uint8_t*)arg, *((uint8_t*)arg + 3) + 1 + BLE_HCI_CMD_HDR_LEN); +} +#endif + +/* Modified to use ipc calls in arduino to correct performance issues */ +int ble_hci_trans_hs_cmd_tx(uint8_t *cmd) +{ + uint16_t len; + uint8_t rc = 0; + + assert(cmd != NULL); + *cmd = BLE_HCI_UART_H4_CMD; + len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1; + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { +/* esp_ipc_call_blocking does not exist for solo */ +#ifndef CONFIG_FREERTOS_UNICORE + if (xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE && !xPortInIsrContext()) { + esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE, + ble_hci_trans_hs_cmd_tx_on_core, cmd); + } else { + esp_vhci_host_send_packet(cmd, len); + } +#else /* Unicore */ + esp_vhci_host_send_packet(cmd, len); +#endif + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + ble_hci_trans_buf_free(cmd); + return rc; +} + +int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev) +{ + int rc = ESP_FAIL; + + if (ble_hci_rx_cmd_hs_cb) { + rc = ble_hci_rx_cmd_hs_cb(hci_ev, ble_hci_rx_cmd_hs_arg); + } + return rc; +} + +/* Added; Called from the core NimBLE is running on, not used for unicore */ +#ifndef CONFIG_FREERTOS_UNICORE +void ble_hci_trans_hs_acl_tx_on_core(void *arg) +{ + // Ugly but necessary as the arduino core does not provide enough IPC stack for variables. + esp_vhci_host_send_packet((uint8_t*)arg + 2, *(uint16_t*)arg); +} +#endif + +/* Modified to use ipc calls in arduino to correct performance issues */ +int ble_hci_trans_hs_acl_tx(struct os_mbuf *om) +{ + uint16_t len = 0; + uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 3], rc = 0; + bool tx_using_nimble_core = 0; + /* If this packet is zero length, just free it */ + if (OS_MBUF_PKTLEN(om) == 0) { + os_mbuf_free_chain(om); + return 0; + } + + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "Controller not ready to receive packets"); + } + + len = 1 + OS_MBUF_PKTLEN(om); +/* Don't check core ID if unicore */ +#ifndef CONFIG_FREERTOS_UNICORE + tx_using_nimble_core = xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE; + if (tx_using_nimble_core && !xPortInIsrContext()) { + data[0] = len; + data[1] = (len >> 8); + data[2] = BLE_HCI_UART_H4_ACL; + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[3]); + } else { + data[0] = BLE_HCI_UART_H4_ACL; + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]); + } +#else /* Unicore */ + data[0] = BLE_HCI_UART_H4_ACL; + os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]); +#endif + + if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) { +/* esp_ipc_call_blocking does not exist for solo */ +#ifndef CONFIG_FREERTOS_UNICORE + if (tx_using_nimble_core && !xPortInIsrContext()) { + esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE, + ble_hci_trans_hs_acl_tx_on_core, data); + } else { + esp_vhci_host_send_packet(data, len); + } +#else /* Unicore */ + esp_vhci_host_send_packet(data, len); +#endif + } else { + rc = BLE_HS_ETIMEOUT_HCI; + } + + os_mbuf_free_chain(om); + + return rc; +} + +int ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + int rc = ESP_FAIL; + + if (ble_hci_rx_acl_hs_cb) { + rc = ble_hci_rx_acl_hs_cb(om, ble_hci_rx_acl_hs_arg); + } + return rc; +} + +uint8_t *ble_hci_trans_buf_alloc(int type) +{ + uint8_t *buf; + + switch (type) { + case BLE_HCI_TRANS_BUF_CMD: + buf = os_memblock_get(&ble_hci_cmd_pool); + break; + + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_evt_hi_pool); + if (buf == NULL) { + /* If no high-priority event buffers remain, try to grab a + * low-priority one. + */ + buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_evt_lo_pool); + break; + + default: + assert(0); + buf = NULL; + } + + return buf; +} + +void ble_hci_trans_buf_free(uint8_t *buf) +{ + int rc; + /* XXX: this may look a bit odd, but the controller uses the command + * buffer to send back the command complete/status as an immediate + * response to the command. This was done to insure that the controller + * could always send back one of these events when a command was received. + * Thus, we check to see which pool the buffer came from so we can free + * it to the appropriate pool + */ + if (os_memblock_from(&ble_hci_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Unsupported; the RAM transport does not have a dedicated ACL data packet + * pool. + */ +int ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg) +{ + ble_hci_acl_pool.mpe_put_cb = cb; + ble_hci_acl_pool.mpe_put_arg = arg; + return 0; +} + +int ble_hci_trans_reset(void) +{ + /* No work to do. All allocated buffers are owned by the host or + * controller, and they will get freed by their owners. + */ + return 0; +} + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf *ble_hci_trans_acl_buf_alloc(void) +{ + struct os_mbuf *m; + uint8_t usrhdr_len; + +#if MYNEWT_VAL(BLE_DEVICE) + usrhdr_len = sizeof(struct ble_mbuf_hdr); +#elif MYNEWT_VAL(BLE_HS_FLOW_CTRL) + usrhdr_len = BLE_MBUF_HS_HDR_LEN; +#else + usrhdr_len = 0; +#endif + + m = os_mbuf_get_pkthdr(&ble_hci_acl_mbuf_pool, usrhdr_len); + return m; +} + +static void ble_hci_rx_acl(uint8_t *data, uint16_t len) +{ + struct os_mbuf *m; + int rc; + int sr; + if (len < BLE_HCI_DATA_HDR_SZ || len > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) { + return; + } + + m = ble_hci_trans_acl_buf_alloc(); + + if (!m) { + ESP_LOGE(TAG, "%s failed to allocate ACL buffers; increase ACL_BUF_COUNT", __func__); + return; + } + if ((rc = os_mbuf_append(m, data, len)) != 0) { + ESP_LOGE(TAG, "%s failed to os_mbuf_append; rc = %d", __func__, rc); + os_mbuf_free_chain(m); + return; + } + OS_ENTER_CRITICAL(sr); + if (ble_hci_rx_acl_hs_cb) { + ble_hci_rx_acl_hs_cb(m, NULL); + } + OS_EXIT_CRITICAL(sr); +} + +static void ble_hci_transport_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_ext_init(&ble_hci_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_acl_buf, + "ble_hci_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_acl_mbuf_pool, + &ble_hci_acl_pool.mpe_mp, + ACL_BLOCK_SIZE, + MYNEWT_VAL(BLE_ACL_BUF_COUNT)); + SYSINIT_PANIC_ASSERT(rc == 0); + + /* + * Create memory pool of HCI command buffers. NOTE: we currently dont + * allow this to be configured. The controller will only allow one + * outstanding command. We decided to keep this a pool in case we allow + * allow the controller to handle more than one outstanding command. + */ + rc = os_mempool_init(&ble_hci_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + ble_hci_cmd_buf, + "ble_hci_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_evt_hi_buf, + "ble_hci_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_evt_lo_buf, + "ble_hci_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); +} + +/* + * @brief: BT controller callback function, used to notify the upper layer that + * controller is ready to receive command + */ +static void controller_rcv_pkt_ready(void) +{ + if (vhci_send_sem) { + xSemaphoreGive(vhci_send_sem); + } +} + +/* + * @brief: BT controller callback function, to transfer data packet to the host + */ +static int host_rcv_pkt(uint8_t *data, uint16_t len) +{ + + if (data[0] == BLE_HCI_UART_H4_EVT) { + uint8_t *evbuf; + int totlen; + int rc; + + totlen = BLE_HCI_EVENT_HDR_LEN + data[2]; + assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN); + + if (totlen > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) { + ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).", + totlen, MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)); + ble_hs_sched_reset(BLE_HS_ECONTROLLER); + return 0; + } + + if (data[1] == BLE_HCI_EVCODE_HW_ERROR) { + assert(0); + } + + /* Allocate LE Advertising Report Event from lo pool only */ + if ((data[1] == BLE_HCI_EVCODE_LE_META) && (data[3] == BLE_HCI_LE_SUBEV_ADV_RPT)) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + /* Skip advertising report if we're out of memory */ + if (!evbuf) { + return 0; + } + } else { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + assert(evbuf != NULL); + } + + memcpy(evbuf, &data[1], totlen); + + rc = ble_hci_trans_ll_evt_tx(evbuf); + assert(rc == 0); + } else if (data[0] == BLE_HCI_UART_H4_ACL) { + ble_hci_rx_acl(data + 1, len - 1); + } + return 0; +} + +static const esp_vhci_host_callback_t vhci_host_cb = { + .notify_host_send_available = controller_rcv_pkt_ready, + .notify_host_recv = host_rcv_pkt, +}; + +static void ble_buf_free(void) +{ + os_msys_buf_free(); + + nimble_platform_mem_free(ble_hci_evt_hi_buf); + ble_hci_evt_hi_buf = NULL; + nimble_platform_mem_free(ble_hci_evt_lo_buf); + ble_hci_evt_lo_buf = NULL; + nimble_platform_mem_free(ble_hci_cmd_buf); + ble_hci_cmd_buf = NULL; + nimble_platform_mem_free(ble_hci_acl_buf); + ble_hci_acl_buf = NULL; +} + +static esp_err_t ble_buf_alloc(void) +{ + if (os_msys_buf_alloc()) { + return ESP_ERR_NO_MEM; + } + + ble_hci_evt_hi_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)))); + + ble_hci_evt_lo_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)))); + + ble_hci_cmd_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ))); + + ble_hci_acl_buf = (os_membuf_t *) nimble_platform_mem_calloc(1, + (sizeof(os_membuf_t) * OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE))); + + if (!ble_hci_evt_hi_buf || !ble_hci_evt_lo_buf || !ble_hci_cmd_buf || !ble_hci_acl_buf) { + ble_buf_free(); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +esp_err_t esp_nimble_hci_init(void) +{ + esp_err_t ret; + + ret = ble_buf_alloc(); + if (ret != ESP_OK) { + goto err; + } + if ((ret = esp_vhci_host_register_callback(&vhci_host_cb)) != ESP_OK) { + goto err; + } + + ble_hci_transport_init(); + + vhci_send_sem = xSemaphoreCreateBinary(); + if (vhci_send_sem == NULL) { + ret = ESP_ERR_NO_MEM; + goto err; + } + + xSemaphoreGive(vhci_send_sem); + + return ret; +err: + ble_buf_free(); + return ret; + +} + +esp_err_t esp_nimble_hci_and_controller_init(void) +{ + esp_err_t ret; + + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + return ret; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) { + return ret; + } + return esp_nimble_hci_init(); +} + +static esp_err_t ble_hci_transport_deinit(void) +{ + int ret = 0; + + ret += os_mempool_clear(&ble_hci_evt_lo_pool); + + ret += os_mempool_clear(&ble_hci_evt_hi_pool); + + ret += os_mempool_clear(&ble_hci_cmd_pool); + + ret += os_mempool_ext_clear(&ble_hci_acl_pool); + + if (ret) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} + +esp_err_t esp_nimble_hci_deinit(void) +{ + if (vhci_send_sem) { + /* Dummy take & give semaphore before deleting */ + xSemaphoreTake(vhci_send_sem, portMAX_DELAY); + xSemaphoreGive(vhci_send_sem); + vSemaphoreDelete(vhci_send_sem); + vhci_send_sem = NULL; + } + esp_err_t ret = ble_hci_transport_deinit(); + if (ret != ESP_OK) { + return ret; + } + + ble_buf_free(); + + return ESP_OK; +} + +esp_err_t esp_nimble_hci_and_controller_deinit(void) +{ + int ret; + ret = esp_nimble_hci_deinit(); + if (ret != ESP_OK) { + return ret; + } + + ret = esp_bt_controller_disable(); + if (ret != ESP_OK) { + return ret; + } + + ret = esp_bt_controller_deinit(); + if (ret != ESP_OK) { + return ret; + } + + return ESP_OK; +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_cfg.h b/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_cfg.h new file mode 100644 index 0000000..46fdcc5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_cfg.h @@ -0,0 +1,1385 @@ + +#ifndef __ESP_NIMBLE_CFG__ +#define __ESP_NIMBLE_CFG__ +#include "nimconfig.h" + +/** + * This macro exists to ensure code includes this header when needed. If code + * checks the existence of a setting directly via ifdef without including this + * header, the setting macro will silently evaluate to 0. In contrast, an + * attempt to use these macros without including this header will result in a + * compiler error. + */ +#define MYNEWT_VAL(x) MYNEWT_VAL_ ## x + +/*** kernel/os */ +#ifndef MYNEWT_VAL_MSYS_1_BLOCK_COUNT +#ifdef CONFIG_BT_NIMBLE_MESH +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT + 8) +#else +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT +#endif +#endif + +#ifndef MYNEWT_VAL_MSYS_1_BLOCK_SIZE +#define MYNEWT_VAL_MSYS_1_BLOCK_SIZE (292) +#endif + +#ifndef MYNEWT_VAL_MSYS_1_SANITY_MIN_COUNT +#define MYNEWT_VAL_MSYS_1_SANITY_MIN_COUNT (0) +#endif + +#ifndef MYNEWT_VAL_MSYS_2_BLOCK_COUNT +#define MYNEWT_VAL_MSYS_2_BLOCK_COUNT (0) +#endif + +#ifndef MYNEWT_VAL_MSYS_2_BLOCK_SIZE +#define MYNEWT_VAL_MSYS_2_BLOCK_SIZE (0) +#endif + +#ifndef MYNEWT_VAL_OS_CPUTIME_FREQ +#define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000) +#endif + +#ifndef MYNEWT_VAL_OS_CPUTIME_TIMER_NUM +#define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0) +#endif + +/*** nimble */ +#ifndef MYNEWT_VAL_BLE_EXT_ADV +#define MYNEWT_VAL_BLE_EXT_ADV (CONFIG_BT_NIMBLE_EXT_ADV) +#endif + +#ifndef MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE +#ifdef CONFIG_BT_NIMBLE_EXT_ADV +#define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (CONFIG_BT_NIMBLE_MAX_EXT_ADV_DATA_LEN) +#else +#define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MAX_CONNECTIONS +#define MYNEWT_VAL_BLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS +#endif + +#ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES +#ifdef CONFIG_BT_NIMBLE_EXT_ADV +#define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES) +#else +#define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SYNCS +#ifdef CONFIG_BT_NIMBLE_ENABLE_PERIODIC_ADV +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SYNCS (CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS) +#else +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SYNCS (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_BROADCASTER +#ifdef CONFIG_BT_NIMBLE_ROLE_BROADCASTER +#define MYNEWT_VAL_BLE_ROLE_BROADCASTER (1) +#else +#define MYNEWT_VAL_BLE_ROLE_BROADCASTER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_CENTRAL +#ifdef CONFIG_BT_NIMBLE_ROLE_CENTRAL +#define MYNEWT_VAL_BLE_ROLE_CENTRAL (1) +#else +#define MYNEWT_VAL_BLE_ROLE_CENTRAL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_OBSERVER +#ifdef CONFIG_BT_NIMBLE_ROLE_OBSERVER +#define MYNEWT_VAL_BLE_ROLE_OBSERVER (1) +#else +#define MYNEWT_VAL_BLE_ROLE_OBSERVER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_ROLE_PERIPHERAL +#ifdef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL +#define MYNEWT_VAL_BLE_ROLE_PERIPHERAL (1) +#else +#define MYNEWT_VAL_BLE_ROLE_PERIPHERAL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_WHITELIST +#define MYNEWT_VAL_BLE_WHITELIST (1) +#endif + +/*** @apache-mynewt-nimble/nimble/controller */ +#ifndef MYNEWT_VAL_BLE_DEVICE +#define MYNEWT_VAL_BLE_DEVICE (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_HW_WHITELIST_ENABLE +#define MYNEWT_VAL_BLE_HW_WHITELIST_ENABLE (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_ADD_STRICT_SCHED_PERIODS +#define MYNEWT_VAL_BLE_LL_ADD_STRICT_SCHED_PERIODS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_EXT_SCAN_FILT +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_EXT_SCAN_FILT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CSA2 +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CSA2 (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_PING +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_PING (MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_EXT_ADV +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_EXT_ADV (MYNEWT_VAL_BLE_EXT_ADV) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_LL_PRIVACY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG +#define MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG (1) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_MAX_TX_BYTES +#define MYNEWT_VAL_BLE_LL_CONN_INIT_MAX_TX_BYTES (27) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_MIN_WIN_OFFSET +#define MYNEWT_VAL_BLE_LL_CONN_INIT_MIN_WIN_OFFSET (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_CONN_INIT_SLOTS +#define MYNEWT_VAL_BLE_LL_CONN_INIT_SLOTS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_DIRECT_TEST_MODE +#define MYNEWT_VAL_BLE_LL_DIRECT_TEST_MODE (0) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/controller (defined by @apache-mynewt-nimble/nimble/controller) */ +#ifndef MYNEWT_VAL_BLE_LL_EXT_ADV_AUX_PTR_CNT +#define MYNEWT_VAL_BLE_LL_EXT_ADV_AUX_PTR_CNT (5) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MASTER_SCA +#define MYNEWT_VAL_BLE_LL_MASTER_SCA (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE +#define MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE (251) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_MFRG_ID +#define MYNEWT_VAL_BLE_LL_MFRG_ID (0xFFFF) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_NUM_SCAN_DUP_ADVS +#define MYNEWT_VAL_BLE_LL_NUM_SCAN_DUP_ADVS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_NUM_SCAN_RSP_ADVS +#define MYNEWT_VAL_BLE_LL_NUM_SCAN_RSP_ADVS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_OUR_SCA +#define MYNEWT_VAL_BLE_LL_OUR_SCA (60) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_PRIO +#define MYNEWT_VAL_BLE_LL_PRIO (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE +#define MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_RNG_BUFSIZE +#define MYNEWT_VAL_BLE_LL_RNG_BUFSIZE (32) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_STRICT_CONN_SCHEDULING +#define MYNEWT_VAL_BLE_LL_STRICT_CONN_SCHEDULING (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SUPP_MAX_RX_BYTES +#define MYNEWT_VAL_BLE_LL_SUPP_MAX_RX_BYTES (MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SUPP_MAX_TX_BYTES +#define MYNEWT_VAL_BLE_LL_SUPP_MAX_TX_BYTES (MYNEWT_VAL_BLE_LL_MAX_PKT_SIZE) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_SYSVIEW +#define MYNEWT_VAL_BLE_LL_SYSVIEW (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_TX_PWR_DBM +#define MYNEWT_VAL_BLE_LL_TX_PWR_DBM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_USECS_PER_PERIOD +#define MYNEWT_VAL_BLE_LL_USECS_PER_PERIOD (3250) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_VND_EVENT_ON_ASSERT +#define MYNEWT_VAL_BLE_LL_VND_EVENT_ON_ASSERT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_LL_WHITELIST_SIZE +#define MYNEWT_VAL_BLE_LL_WHITELIST_SIZE (8) +#endif + +#ifndef MYNEWT_VAL_BLE_LP_CLOCK +#define MYNEWT_VAL_BLE_LP_CLOCK (1) +#endif + +#ifndef MYNEWT_VAL_BLE_NUM_COMP_PKT_RATE +#define MYNEWT_VAL_BLE_NUM_COMP_PKT_RATE ((2 * OS_TICKS_PER_SEC)) +#endif + +#ifndef MYNEWT_VAL_BLE_PUBLIC_DEV_ADDR +#define MYNEWT_VAL_BLE_PUBLIC_DEV_ADDR ((uint8_t[6]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +#endif + +#ifndef MYNEWT_VAL_BLE_XTAL_SETTLE_TIME +#define MYNEWT_VAL_BLE_XTAL_SETTLE_TIME (0) +#endif + +/*** @apache-mynewt-nimble/nimble/host */ +#ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU +#define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU +#endif + +#ifndef MYNEWT_VAL_BLE_HS_LOG_LVL +#define MYNEWT_VAL_BLE_HS_LOG_LVL CONFIG_BT_NIMBLE_LOG_LEVEL +#endif + +#ifndef MYNEWT_VAL_BLE_PERIODIC_ADV +#ifdef CONFIG_BT_NIMBLE_EXT_ADV +#define MYNEWT_VAL_BLE_PERIODIC_ADV (CONFIG_BT_NIMBLE_ENABLE_PERIODIC_ADV) +#else +#define MYNEWT_VAL_BLE_PERIODIC_ADV (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_PERIODIC_ADV_SYNC_TRANSFER +#define MYNEWT_VAL_BLE_PERIODIC_ADV_SYNC_TRANSFER (0) +#endif + +#ifndef MYNEWT_VAL_BLE_VERSION +#define MYNEWT_VAL_BLE_VERSION (50) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO +#define MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_FIND_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_FIND_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_INDICATE +#define MYNEWT_VAL_BLE_ATT_SVR_INDICATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_MAX_PREP_ENTRIES +#define MYNEWT_VAL_BLE_ATT_SVR_MAX_PREP_ENTRIES (64) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_NOTIFY +#define MYNEWT_VAL_BLE_ATT_SVR_NOTIFY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE_TMO +#define MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE_TMO (30000) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ +#define MYNEWT_VAL_BLE_ATT_SVR_READ (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_BLOB +#define MYNEWT_VAL_BLE_ATT_SVR_READ_BLOB (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_GROUP_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_READ_GROUP_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_MULT +#define MYNEWT_VAL_BLE_ATT_SVR_READ_MULT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE +#define MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_SIGNED_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_SIGNED_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_WRITE +#define MYNEWT_VAL_BLE_ATT_SVR_WRITE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP +#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE +#define MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_CHRS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_CHRS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_DSCS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_DSCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_ALL_SVCS +#define MYNEWT_VAL_BLE_GATT_DISC_ALL_SVCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_CHR_UUID +#define MYNEWT_VAL_BLE_GATT_DISC_CHR_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_DISC_SVC_UUID +#define MYNEWT_VAL_BLE_GATT_DISC_SVC_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_FIND_INC_SVCS +#define MYNEWT_VAL_BLE_GATT_FIND_INC_SVCS (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_INDICATE +#define MYNEWT_VAL_BLE_GATT_INDICATE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_MAX_PROCS +#define MYNEWT_VAL_BLE_GATT_MAX_PROCS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_NOTIFY +#define MYNEWT_VAL_BLE_GATT_NOTIFY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ +#define MYNEWT_VAL_BLE_GATT_READ (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_LONG +#define MYNEWT_VAL_BLE_GATT_READ_LONG (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_MAX_ATTRS +#define MYNEWT_VAL_BLE_GATT_READ_MAX_ATTRS (8) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_MULT +#define MYNEWT_VAL_BLE_GATT_READ_MULT (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_READ_UUID +#define MYNEWT_VAL_BLE_GATT_READ_UUID (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_RESUME_RATE +#define MYNEWT_VAL_BLE_GATT_RESUME_RATE (1000) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_SIGNED_WRITE +#define MYNEWT_VAL_BLE_GATT_SIGNED_WRITE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE +#define MYNEWT_VAL_BLE_GATT_WRITE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_LONG +#define MYNEWT_VAL_BLE_GATT_WRITE_LONG (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_MAX_ATTRS +#define MYNEWT_VAL_BLE_GATT_WRITE_MAX_ATTRS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP +#define MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE +#define MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE (MYNEWT_VAL_BLE_ROLE_CENTRAL) +#endif + +#ifndef MYNEWT_VAL_BLE_HOST +#define MYNEWT_VAL_BLE_HOST (1) +#endif + +#ifndef MYNEWT_VAL_ESP_BLE_MESH +#ifdef CONFIG_BLE_MESH_HCI_5_0 +#define MYNEWT_VAL_ESP_BLE_MESH (1) +#else +#define MYNEWT_VAL_ESP_BLE_MESH (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_DEBUG +#ifdef CONFIG_BT_NIMBLE_DEBUG +#define MYNEWT_VAL_BLE_HS_DEBUG (1) +#else +#define MYNEWT_VAL_BLE_HS_DEBUG (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS +#ifdef CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS +#define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (1) +#else +#define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_AUTO_START +#define MYNEWT_VAL_BLE_HS_AUTO_START (1) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL +#ifdef CONFIG_BT_NIMBLE_HS_FLOW_CTRL +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL (1) +#else +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH +#endif + +#ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT +#endif + +#ifndef MYNEWT_VAL_BLE_HS_PHONY_HCI_ACKS +#define MYNEWT_VAL_BLE_HS_PHONY_HCI_ACKS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_REQUIRE_OS +#define MYNEWT_VAL_BLE_HS_REQUIRE_OS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_STOP_ON_SHUTDOWN +#define MYNEWT_VAL_BLE_HS_STOP_ON_SHUTDOWN (1) +#endif + +#ifndef MYNEWT_VAL_BLE_HS_STOP_ON_SHUTDOWN_TIMEOUT +#define MYNEWT_VAL_BLE_HS_STOP_ON_SHUTDOWN_TIMEOUT CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS +#endif + +#ifndef MYNEWT_VAL_BLE_HS_SYSINIT_STAGE +#define MYNEWT_VAL_BLE_HS_SYSINIT_STAGE (200) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM +#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS +#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE - 8) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS +#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_MAX_CHANS +#define MYNEWT_VAL_BLE_L2CAP_MAX_CHANS (3*MYNEWT_VAL_BLE_MAX_CONNECTIONS) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_RX_FRAG_TIMEOUT +#define MYNEWT_VAL_BLE_L2CAP_RX_FRAG_TIMEOUT (30000) +#endif + +#ifndef MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS +#define MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH +#ifdef CONFIG_BT_NIMBLE_MESH +#define MYNEWT_VAL_BLE_MESH (1) +#else +#define MYNEWT_VAL_BLE_MESH (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_CONSOLE_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_CONSOLE_BUFFER_SIZE (128) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT +#define MYNEWT_VAL_BLE_MONITOR_RTT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFERED +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFERED (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_NAME +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_NAME ("monitor") +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_RTT_BUFFER_SIZE (256) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART +#define MYNEWT_VAL_BLE_MONITOR_UART (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_BAUDRATE +#define MYNEWT_VAL_BLE_MONITOR_UART_BAUDRATE (1000000) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_BUFFER_SIZE +#define MYNEWT_VAL_BLE_MONITOR_UART_BUFFER_SIZE (64) +#endif + +#ifndef MYNEWT_VAL_BLE_MONITOR_UART_DEV +#define MYNEWT_VAL_BLE_MONITOR_UART_DEV ("uart0") +#endif + +#if CONFIG_IDF_TARGET_ESP32 +#define MYNEWT_VAL_BLE_HOST_BASED_PRIVACY (1) +#else +#ifndef MYNEWT_VAL_BLE_HOST_BASED_PRIVACY +#define MYNEWT_VAL_BLE_HOST_BASED_PRIVACY (CONFIG_BT_NIMBLE_HOST_BASED_PRIVACY) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_RPA_TIMEOUT +#define MYNEWT_VAL_BLE_RPA_TIMEOUT (CONFIG_BT_NIMBLE_RPA_TIMEOUT) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_BONDING +#define MYNEWT_VAL_BLE_SM_BONDING (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_IO_CAP +#define MYNEWT_VAL_BLE_SM_IO_CAP (BLE_HS_IO_NO_INPUT_OUTPUT) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_KEYPRESS +#define MYNEWT_VAL_BLE_SM_KEYPRESS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_LEGACY +#ifdef CONFIG_BT_NIMBLE_SM_LEGACY +#define MYNEWT_VAL_BLE_SM_LEGACY (1) +#else +#define MYNEWT_VAL_BLE_SM_LEGACY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_MAX_PROCS +#define MYNEWT_VAL_BLE_SM_MAX_PROCS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_MITM +#define MYNEWT_VAL_BLE_SM_MITM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_OOB_DATA_FLAG +#define MYNEWT_VAL_BLE_SM_OOB_DATA_FLAG (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_OUR_KEY_DIST +#define MYNEWT_VAL_BLE_SM_OUR_KEY_DIST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SM_SC +#ifdef CONFIG_BT_NIMBLE_SM_SC +#define MYNEWT_VAL_BLE_SM_SC (1) +#else +#define MYNEWT_VAL_BLE_SM_SC (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST +#define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_CRYPTO_STACK_MBEDTLS +#define MYNEWT_VAL_BLE_CRYPTO_STACK_MBEDTLS (CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS) +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_MAX_BONDS +#define MYNEWT_VAL_BLE_STORE_MAX_BONDS CONFIG_BT_NIMBLE_MAX_BONDS +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_MAX_CCCDS +#define MYNEWT_VAL_BLE_STORE_MAX_CCCDS CONFIG_BT_NIMBLE_MAX_CCCDS +#endif + +/*** @apache-mynewt-nimble/nimble/host/mesh */ +#ifndef MYNEWT_VAL_BLE_MESH_ACCESS_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_ACCESS_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_ACCESS_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_ACCESS_LOG_MOD (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT +#define MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT (20) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_ADV_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_ADV_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_ADV_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_ADV_LOG_MOD (11) +#endif + +#ifndef MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST +#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST +#define MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST (1) +#else +#define MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST (0) +#endif +#endif + +/*** nimble/host/services/ans */ +#ifndef MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT (0) +#endif + + +#ifndef MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT (0) +#endif + +/*** nimble/host/services/bas */ +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM (0) +#endif +#ifndef MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO +#define MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO (9) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT (4) +#endif + +/*** @apache-mynewt-nimble/nimble/host/mesh */ +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT +#define MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT (20) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_CFG_CLI +#define MYNEWT_VAL_BLE_MESH_CFG_CLI (0) +#endif +#ifndef MYNEWT_VAL_BLE_MESH_CRPL +#define MYNEWT_VAL_BLE_MESH_CRPL (10) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG +#define MYNEWT_VAL_BLE_MESH_DEBUG (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS +#define MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ADV +#define MYNEWT_VAL_BLE_MESH_DEBUG_ADV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG +#define MYNEWT_VAL_BLE_MESH_DEBUG (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS +#define MYNEWT_VAL_BLE_MESH_DEBUG_ACCESS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_ADV +#define MYNEWT_VAL_BLE_MESH_DEBUG_ADV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_BEACON +#define MYNEWT_VAL_BLE_MESH_DEBUG_BEACON (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_CRYPTO +#define MYNEWT_VAL_BLE_MESH_DEBUG_CRYPTO (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_FRIEND +#define MYNEWT_VAL_BLE_MESH_DEBUG_FRIEND (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_LOW_POWER +#define MYNEWT_VAL_BLE_MESH_DEBUG_LOW_POWER (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_MODEL +#define MYNEWT_VAL_BLE_MESH_DEBUG_MODEL (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_NET +#define MYNEWT_VAL_BLE_MESH_DEBUG_NET (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_PROV +#define MYNEWT_VAL_BLE_MESH_DEBUG_PROV (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_PROXY +#define MYNEWT_VAL_BLE_MESH_DEBUG_PROXY (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_SETTINGS +#define MYNEWT_VAL_BLE_MESH_DEBUG_SETTINGS (1) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_TRANS +#define MYNEWT_VAL_BLE_MESH_DEBUG_TRANS (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEVICE_NAME +#define MYNEWT_VAL_BLE_MESH_DEVICE_NAME CONFIG_BT_NIMBLE_MESH_DEVICE_NAME +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_DEV_UUID +#define MYNEWT_VAL_BLE_MESH_DEV_UUID (((uint8_t[16]){0x11, 0x22, 0})) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND +#ifdef CONFIG_BT_NIMBLE_MESH_FRIEND +#define MYNEWT_VAL_BLE_MESH_FRIEND (1) +#else +#define MYNEWT_VAL_BLE_MESH_FRIEND (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_FRIEND_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_FRIEND_LOG_MOD (14) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_LPN_COUNT +#define MYNEWT_VAL_BLE_MESH_FRIEND_LPN_COUNT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_QUEUE_SIZE +#define MYNEWT_VAL_BLE_MESH_FRIEND_QUEUE_SIZE (16) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_RECV_WIN +#define MYNEWT_VAL_BLE_MESH_FRIEND_RECV_WIN (255) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_SEG_RX +#define MYNEWT_VAL_BLE_MESH_FRIEND_SEG_RX (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_SUB_LIST_SIZE +#define MYNEWT_VAL_BLE_MESH_FRIEND_SUB_LIST_SIZE (3) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_GATT_PROXY +#ifdef CONFIG_BT_NIMBLE_MESH_GATT_PROXY +#define MYNEWT_VAL_BLE_MESH_GATT_PROXY (1) +#else +#define MYNEWT_VAL_BLE_MESH_GATT_PROXY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_HEALTH_CLI +#define MYNEWT_VAL_BLE_MESH_HEALTH_CLI (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_IVU_DIVIDER +#define MYNEWT_VAL_BLE_MESH_IVU_DIVIDER (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST +#define MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LABEL_COUNT +#define MYNEWT_VAL_BLE_MESH_LABEL_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_LOG_MOD (9) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOW_POWER +#ifdef CONFIG_BT_NIMBLE_MESH_LOW_POWER +#define MYNEWT_VAL_BLE_MESH_LOW_POWER (1) +#else +#define MYNEWT_VAL_BLE_MESH_LOW_POWER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOW_POWER_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_LOW_POWER_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LOW_POWER_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_LOW_POWER_LOG_MOD (15) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_AUTO +#define MYNEWT_VAL_BLE_MESH_LPN_AUTO (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_AUTO_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_AUTO_TIMEOUT (15) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_ESTABLISHMENT +#define MYNEWT_VAL_BLE_MESH_LPN_ESTABLISHMENT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_GROUPS +#define MYNEWT_VAL_BLE_MESH_LPN_GROUPS (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT (MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_MIN_QUEUE_SIZE +#define MYNEWT_VAL_BLE_MESH_LPN_MIN_QUEUE_SIZE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT (300) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RECV_DELAY +#define MYNEWT_VAL_BLE_MESH_LPN_RECV_DELAY (100) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RECV_WIN_FACTOR +#define MYNEWT_VAL_BLE_MESH_LPN_RECV_WIN_FACTOR (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RETRY_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_LPN_RETRY_TIMEOUT (8) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_RSSI_FACTOR +#define MYNEWT_VAL_BLE_MESH_LPN_RSSI_FACTOR (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_LPN_SCAN_LATENCY +#define MYNEWT_VAL_BLE_MESH_LPN_SCAN_LATENCY (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_EXTENSIONS +#define MYNEWT_VAL_BLE_MESH_MODEL_EXTENSIONS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_GROUP_COUNT +#define MYNEWT_VAL_BLE_MESH_MODEL_GROUP_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_MODEL_KEY_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_MODEL_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MODEL_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_MODEL_LOG_MOD (16) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE +#define MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE (10) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_NET_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_NET_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_NET_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_NET_LOG_MOD (17) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_NODE_COUNT +#define MYNEWT_VAL_BLE_MESH_NODE_COUNT CONFIG_BT_NIMBLE_MESH_NODE_COUNT +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT (60) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_INPUT_ACTIONS +#define MYNEWT_VAL_BLE_MESH_OOB_INPUT_ACTIONS (((BT_MESH_NO_INPUT))) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_INPUT_SIZE +#define MYNEWT_VAL_BLE_MESH_OOB_INPUT_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_ACTIONS +#define MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_ACTIONS (((BT_MESH_DISPLAY_NUMBER))) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_SIZE +#define MYNEWT_VAL_BLE_MESH_OOB_OUTPUT_SIZE (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PB_ADV +#ifdef CONFIG_BT_NIMBLE_MESH_PB_ADV +#define MYNEWT_VAL_BLE_MESH_PB_ADV (1) +#else +#define MYNEWT_VAL_BLE_MESH_PB_ADV (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PB_GATT +#ifdef CONFIG_BT_NIMBLE_MESH_PB_GATT +#define MYNEWT_VAL_BLE_MESH_PB_GATT (1) +#else +#define MYNEWT_VAL_BLE_MESH_PB_GATT (0) +#endif +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_PROV +#ifdef CONFIG_BT_NIMBLE_MESH_PROV +#define MYNEWT_VAL_BLE_MESH_PROV (1) +#else +#define MYNEWT_VAL_BLE_MESH_PROV (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROVISIONER +#ifdef CONFIG_BT_NIMBLE_MESH_PROVISIONER +#define MYNEWT_VAL_BLE_MESH_PROVISIONER (1) +#else +#define MYNEWT_VAL_BLE_MESH_PROVISIONER (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROV_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_PROV_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROV_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_PROV_LOG_MOD (18) +#endif + +/* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_PROXY +#ifdef CONFIG_BT_NIMBLE_MESH_PROXY +#define MYNEWT_VAL_BLE_MESH_PROXY (1) +#else +#define MYNEWT_VAL_BLE_MESH_PROXY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE +#define MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROXY_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_PROXY_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_PROXY_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_PROXY_LOG_MOD (19) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RELAY +#ifdef CONFIG_BT_NIMBLE_MESH_RELAY +#define MYNEWT_VAL_BLE_MESH_RELAY (1) +#else +#define MYNEWT_VAL_BLE_MESH_RELAY (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT (5) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RX_SDU_MAX +#define MYNEWT_VAL_BLE_MESH_RX_SDU_MAX (72) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT +#define MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SEG_RETRANSMIT_ATTEMPTS +#define MYNEWT_VAL_BLE_MESH_SEG_RETRANSMIT_ATTEMPTS (4) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SEQ_STORE_RATE +#define MYNEWT_VAL_BLE_MESH_SEQ_STORE_RATE (128) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_SETTINGS +#define MYNEWT_VAL_BLE_MESH_SETTINGS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SETTINGS_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_SETTINGS_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SETTINGS_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_SETTINGS_LOG_MOD (20) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SHELL +#define MYNEWT_VAL_BLE_MESH_SHELL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SHELL_MODELS +#define MYNEWT_VAL_BLE_MESH_SHELL_MODELS (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_STORE_TIMEOUT +#define MYNEWT_VAL_BLE_MESH_STORE_TIMEOUT (2) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SUBNET_COUNT +#define MYNEWT_VAL_BLE_MESH_SUBNET_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SYSINIT_STAGE +#define MYNEWT_VAL_BLE_MESH_SYSINIT_STAGE (500) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_SYSINIT_STAGE_SHELL +#define MYNEWT_VAL_BLE_MESH_SYSINIT_STAGE_SHELL (1000) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TESTING +#define MYNEWT_VAL_BLE_MESH_TESTING (0) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TRANS_LOG_LVL +#define MYNEWT_VAL_BLE_MESH_TRANS_LOG_LVL (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TRANS_LOG_MOD +#define MYNEWT_VAL_BLE_MESH_TRANS_LOG_MOD (21) +#endif + +/* Overridden by apps/blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */ +#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MAX +#define MYNEWT_VAL_BLE_MESH_TX_SEG_MAX (6) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT +#define MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT (4) +#endif + +/*** @apache-mynewt-nimble/nimble/host/services/ans */ +#ifndef MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_NEW_ALERT_CAT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_ANS_SYSINIT_STAGE +#define MYNEWT_VAL_BLE_SVC_ANS_SYSINIT_STAGE (303) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT +#define MYNEWT_VAL_BLE_SVC_ANS_UNR_ALERT_CAT (0) +#endif + +/*** @apache-mynewt-nimble/nimble/host/services/bas */ +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM +#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_BAS_SYSINIT_STAGE +#define MYNEWT_VAL_BLE_SVC_BAS_SYSINIT_STAGE (303) +#endif + +/*** @apache-mynewt-nimble/nimble/host/services/dis */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_DEFAULT_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_DEFAULT_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_MODEL_NUMBER_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_MODEL_NUMBER_DEFAULT ("NimBLE") +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_MODEL_NUMBER_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_MODEL_NUMBER_READ_PERM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSINIT_STAGE +#define MYNEWT_VAL_BLE_SVC_DIS_SYSINIT_STAGE (303) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1) +#endif + +/*** @apache-mynewt-nimble/nimble/host/services/gap */ +#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE +#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM +#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION +#define MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM +#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM (-1) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_SLAVE_LATENCY +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_SLAVE_LATENCY (0) +#endif + +#ifndef MYNEWT_VAL_BLE_SVC_GAP_PPCP_SUPERVISION_TMO +#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_SUPERVISION_TMO (0) +#endif + +/*** nimble/transport */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI (0) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport) */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_NIMBLE_BUILTIN +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_NIMBLE_BUILTIN (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_RAM +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_RAM (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_SOCKET +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_SOCKET (0) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport) */ +#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_UART +#define MYNEWT_VAL_BLE_HCI_TRANSPORT_UART (1) +#endif + +/*** nimble/transport/uart */ +#ifndef MYNEWT_VAL_BLE_ACL_BUF_COUNT +#define MYNEWT_VAL_BLE_ACL_BUF_COUNT CONFIG_BT_NIMBLE_ACL_BUF_COUNT +#endif + +#ifndef MYNEWT_VAL_BLE_ACL_BUF_SIZE +#define MYNEWT_VAL_BLE_ACL_BUF_SIZE CONFIG_BT_NIMBLE_ACL_BUF_SIZE +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_ACL_OUT_COUNT +#define MYNEWT_VAL_BLE_HCI_ACL_OUT_COUNT (12) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_BUF_SIZE +#define MYNEWT_VAL_BLE_HCI_EVT_BUF_SIZE CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_HI_BUF_COUNT +#define MYNEWT_VAL_BLE_HCI_EVT_HI_BUF_COUNT CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_EVT_LO_BUF_COUNT +#define MYNEWT_VAL_BLE_HCI_EVT_LO_BUF_COUNT CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport/uart) */ +#ifndef MYNEWT_VAL_BLE_HCI_UART_BAUD +#define MYNEWT_VAL_BLE_HCI_UART_BAUD (115200) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_DATA_BITS +#define MYNEWT_VAL_BLE_HCI_UART_DATA_BITS (8) +#endif + +/* Overridden by targets/porting-nimble (defined by nimble/transport/uart) */ +#ifndef MYNEWT_VAL_BLE_HCI_UART_FLOW_CTRL +#define MYNEWT_VAL_BLE_HCI_UART_FLOW_CTRL (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_PARITY +#define MYNEWT_VAL_BLE_HCI_UART_PARITY (HAL_UART_PARITY_NONE) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_PORT +#define MYNEWT_VAL_BLE_HCI_UART_PORT (0) +#endif + +#ifndef MYNEWT_VAL_BLE_HCI_UART_STOP_BITS +#define MYNEWT_VAL_BLE_HCI_UART_STOP_BITS (1) +#endif + +#ifndef MYNEWT_VAL_NEWT_FEATURE_LOGCFG +#define MYNEWT_VAL_NEWT_FEATURE_LOGCFG (1) +#endif + +#ifndef MYNEWT_VAL_BLE_USE_ESP_TIMER +#ifdef CONFIG_BT_NIMBLE_USE_ESP_TIMER +#define MYNEWT_VAL_BLE_USE_ESP_TIMER (1) +#else +#define MYNEWT_VAL_BLE_USE_ESP_TIMER (0) +#endif +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_mem.h b/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_mem.h new file mode 100644 index 0000000..9a89c12 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/port/include/esp_nimble_mem.h @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_NIMBLE_MEM_H__ +#define __ESP_NIMBLE_MEM_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *nimble_platform_mem_malloc(size_t size); +void *nimble_platform_mem_calloc(size_t n, size_t size); +void nimble_platform_mem_free(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_NIMBLE_MEM_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/esp_port/port/src/esp_nimble_mem.c b/lib/NimBLE-Arduino/src/nimble/esp_port/port/src/esp_nimble_mem.c new file mode 100644 index 0000000..7e1899d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/esp_port/port/src/esp_nimble_mem.c @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef ESP_PLATFORM + +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "nimconfig.h" +#include "../include/esp_nimble_mem.h" + +IRAM_ATTR void *nimble_platform_mem_malloc(size_t size) +{ +#ifdef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL + return heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); +#elif CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT); +#elif CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_IRAM_8BIT + return heap_caps_malloc_prefer(size, 2, MALLOC_CAP_INTERNAL|MALLOC_CAP_IRAM_8BIT, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); +#else + return malloc(size); +#endif +} + +IRAM_ATTR void *nimble_platform_mem_calloc(size_t n, size_t size) +{ +#ifdef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL + return heap_caps_calloc(n, size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); +#elif CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL + return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT); +#elif CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_IRAM_8BIT + return heap_caps_calloc_prefer(n, size, 2, MALLOC_CAP_INTERNAL|MALLOC_CAP_IRAM_8BIT, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); +#else + return calloc(n, size); +#endif +} + +IRAM_ATTR void nimble_platform_mem_free(void *ptr) +{ + heap_caps_free(ptr); +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/AUTHORS b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/AUTHORS new file mode 100644 index 0000000..0a8e9f8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/AUTHORS @@ -0,0 +1,15 @@ +Architect: +Rafael Misoczki + +Open Source Maintainer: +Constanza Heath +Rafael Misoczki + +Contributors: +Constanza Heath +Rafael Misoczki +Flavio Santes +Jarkko Sakkinen +Chris Morrison +Marti Bolivar +Colin Ian King diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/LICENSE b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/LICENSE new file mode 100644 index 0000000..2e1db51 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/LICENSE @@ -0,0 +1,61 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +https://github.com/kmackay/micro-ecc + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/README b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/README new file mode 100644 index 0000000..fb52c19 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/README @@ -0,0 +1,71 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ + +Overview: + +The TinyCrypt Library provides an implementation for constrained devices of a +minimal set of standard cryptography primitives. + +Please, ***SEE THE DOCUMENTATION*** folder for more information on the supported +cryptographic primitives and the limitations of TinyCrypt library. For usage, +security and technicalities, please see the corresponding header file of each +cryptographic primitive. + +================================================================================ + +Organization: + +/lib: C source code of the cryptographic primitives. +/lib/include/tinycrypt: C header files of the cryptographic primitives. +/tests: Test vectors of the cryptographic primitives. +/doc: Documentation of TinyCrypt. + +================================================================================ + +Building: + +1) In Makefile.conf set: + - CFLAGS for compiler flags. + - CC for compiler. + - ENABLE_TESTS for enabling (true) or disabling (false) tests compilation. +2) In lib/Makefile select the primitives required by your project. +3) In tests/Makefile select the corresponding tests of the selected primitives. +4) make +5) run tests in tests/ + +================================================================================ + diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/VERSION b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/VERSION new file mode 100644 index 0000000..a45be46 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/VERSION @@ -0,0 +1 @@ +0.2.8 diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/documentation/tinycrypt.rst b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/documentation/tinycrypt.rst new file mode 100644 index 0000000..356c099 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/documentation/tinycrypt.rst @@ -0,0 +1,352 @@ + +TinyCrypt Cryptographic Library +############################### +Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + +Overview +******** +The TinyCrypt Library provides an implementation for targeting constrained devices +with a minimal set of standard cryptography primitives, as listed below. To better +serve applications targeting constrained devices, TinyCrypt implementations differ +from the standard specifications (see the Important Remarks section for some +important differences). Certain cryptographic primitives depend on other +primitives, as mentioned in the list below. + +Aside from the Important Remarks section below, valuable information on the usage, +security and technicalities of each cryptographic primitive are found in the +corresponding header file. + +* SHA-256: + + * Type of primitive: Hash function. + * Standard Specification: NIST FIPS PUB 180-4. + * Requires: -- + +* HMAC-SHA256: + + * Type of primitive: Message authentication code. + * Standard Specification: RFC 2104. + * Requires: SHA-256 + +* HMAC-PRNG: + + * Type of primitive: Pseudo-random number generator (256-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: SHA-256 and HMAC-SHA256. + +* AES-128: + + * Type of primitive: Block cipher. + * Standard Specification: NIST FIPS PUB 197. + * Requires: -- + +* AES-CBC mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CTR mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CMAC mode: + + * Type of primitive: Message authentication code. + * Standard Specification: NIST SP 800-38B. + * Requires: AES-128. + +* AES-CCM mode: + + * Type of primitive: Authenticated encryption. + * Standard Specification: NIST SP 800-38C. + * Requires: AES-128. + +* CTR-PRNG: + + * Type of primitive: Pseudo-random number generator (128-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: AES-128. + +* ECC-DH: + + * Type of primitive: Key exchange based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +* ECC-DSA: + + * Type of primitive: Digital signature based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +Design Goals +************ + +* Minimize the code size of each cryptographic primitive. This means minimize + the size of a platform-independent implementation, as presented in TinyCrypt. + Note that various applications may require further features, optimizations with + respect to other metrics and countermeasures for particular threats. These + peculiarities would increase the code size and thus are not considered here. + +* Minimize the dependencies among the cryptographic primitives. This means + that it is unnecessary to build and allocate object code for more primitives + than the ones strictly required by the intended application. In other words, + one can select and compile only the primitives required by the application. + + +Important Remarks +***************** + +The cryptographic implementations in TinyCrypt library have some limitations. +Some of these limitations are inherent to the cryptographic primitives +themselves, while others are specific to TinyCrypt. These limitations were accepted +in order to meet its design goals (in special, minimal code size) and to better +serve applications targeting constrained devices in general. Some of these +limitations are discussed in-depth below. + +General Remarks +*************** + +* TinyCrypt does **not** intend to be fully side-channel resistant. Due to the + variety of side-channel attacks, many of them only relevant to certain + platforms. In this sense, instead of penalizing all library users with + side-channel countermeasures such as increasing the overall code size, + TinyCrypt only implements certain generic timing-attack countermeasures. + +Specific Remarks +**************** + +* SHA-256: + + * The number of bits_hashed in the state is not checked for overflow. Note + however that this will only be a problem if you intend to hash more than + 2^64 bits, which is an extremely large window. + +* HMAC: + + * The HMAC verification process is assumed to be performed by the application. + This compares the computed tag with some given tag. + Note that conventional memory-comparison methods (such as memcmp function) + might be vulnerable to timing attacks; thus be sure to use a constant-time + memory comparison function (such as compare_constant_time + function provided in lib/utils.c). + + * The tc_hmac_final function, responsible for computing the message tag, + cleans the state context before exiting. Thus, applications do not need to + clean the TCHmacState_t ctx after calling tc_hmac_final. This should not + be changed in future versions of the library as there are applications + currently relying on this good-practice/feature of TinyCrypt. + +* HMAC-PRNG: + + * Before using HMAC-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + + * NIST SP 800-90A requires three items as seed material in the initialization + step: entropy seed, personalization and a nonce (which is not implemented). + TinyCrypt requires the personalization byte array and automatically creates + the entropy seed using a mandatory call to the re-seed function. + +* AES-128: + + * The current implementation does not support other key-lengths (such as 256 + bits). Note that if you need AES-256, it doesn't sound as though your + application is running in a constrained environment. AES-256 requires keys + twice the size as for AES-128, and the key schedule is 40% larger. + +* CTR mode: + + * The AES-CTR mode limits the size of a data message they encrypt to 2^32 + blocks. If you need to encrypt larger data sets, your application would + need to replace the key after 2^32 block encryptions. + +* CTR-PRNG: + + * Before using CTR-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + +* CBC mode: + + * TinyCrypt CBC decryption assumes that the iv and the ciphertext are + contiguous (as produced by TinyCrypt CBC encryption). This allows for a + very efficient decryption algorithm that would not otherwise be possible. + +* CMAC mode: + + * AES128-CMAC mode of operation offers 64 bits of security against collision + attacks. Note however that an external attacker cannot generate the tags + him/herself without knowing the MAC key. In this sense, to attack the + collision property of AES128-CMAC, an external attacker would need the + cooperation of the legal user to produce an exponentially high number of + tags (e.g. 2^64) to finally be able to look for collisions and benefit + from them. As an extra precaution, the current implementation allows to at + most 2^48 calls to tc_cmac_update function before re-calling tc_cmac_setup + (allowing a new key to be set), as suggested in Appendix B of SP 800-38B. + +* CCM mode: + + * There are a few tradeoffs for the selection of the parameters of CCM mode. + In special, there is a tradeoff between the maximum number of invocations + of CCM under a given key and the maximum payload length for those + invocations. Both things are related to the parameter 'q' of CCM mode. The + maximum number of invocations of CCM under a given key is determined by + the nonce size, which is: 15-q bytes. The maximum payload length for those + invocations is defined as 2^(8q) bytes. + + To achieve minimal code size, TinyCrypt CCM implementation fixes q = 2, + which is a quite reasonable choice for constrained applications. The + implications of this choice are: + + The nonce size is: 13 bytes. + + The maximum payload length is: 2^16 bytes = 65 KB. + + The mac size parameter is an important parameter to estimate the security + against collision attacks (that aim at finding different messages that + produce the same authentication tag). TinyCrypt CCM implementation + accepts any even integer between 4 and 16, as suggested in SP 800-38C. + + * TinyCrypt CCM implementation accepts associated data of any length between + 0 and (2^16 - 2^8) = 65280 bytes. + + * TinyCrypt CCM implementation accepts: + + * Both non-empty payload and associated data (it encrypts and + authenticates the payload and only authenticates the associated data); + + * Non-empty payload and empty associated data (it encrypts and + authenticates the payload); + + * Non-empty associated data and empty payload (it degenerates to an + authentication-only mode on the associated data). + + * RFC-3610, which also specifies CCM, presents a few relevant security + suggestions, such as: it is recommended for most applications to use a + mac size greater than 8. Besides, it is emphasized that the usage of the + same nonce for two different messages which are encrypted with the same + key obviously destroys the security properties of CCM mode. + +* ECC-DH and ECC-DSA: + + * TinyCrypt ECC implementation is based on micro-ecc (see + https://github.com/kmackay/micro-ecc). In the original micro-ecc + documentation, there is an important remark about the way integers are + represented: + + "Integer representation: To reduce code size, all large integers are + represented using little-endian words - so the least significant word is + first. You can use the 'ecc_bytes2native()' and 'ecc_native2bytes()' + functions to convert between the native integer representation and the + standardized octet representation." + + Note that the assumed bit layout is: {31, 30, ..., 0}, {63, 62, ..., 32}, + {95, 94, ..., 64}, {127, 126, ..., 96} for a very-long-integer (vli) + consisting of 4 unsigned integers (as an example). + + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + before calling uECC_make_key() or uECC_sign(). + +Examples of Applications +************************ +It is possible to do useful cryptography with only the given small set of +primitives. With this list of primitives it becomes feasible to support a range +of cryptography usages: + + * Measurement of code, data structures, and other digital artifacts (SHA256); + + * Generate commitments (SHA256); + + * Construct keys (HMAC-SHA256); + + * Extract entropy from strings containing some randomness (HMAC-SHA256); + + * Construct random mappings (HMAC-SHA256); + + * Construct nonces and challenges (HMAC-PRNG, CTR-PRNG); + + * Authenticate using a shared secret (HMAC-SHA256); + + * Create an authenticated, replay-protected session (HMAC-SHA256 + HMAC-PRNG); + + * Authenticated encryption (AES-128 + AES-CCM); + + * Key-exchange (EC-DH); + + * Digital signature (EC-DSA); + +Test Vectors +************ + +The library provides a test program for each cryptographic primitive (see 'test' +folder). Besides illustrating how to use the primitives, these tests evaluate +the correctness of the implementations by checking the results against +well-known publicly validated test vectors. + +For the case of the HMAC-PRNG, due to the necessity of performing an extensive +battery test to produce meaningful conclusions, we suggest the user to evaluate +the unpredictability of the implementation by using the NIST Statistical Test +Suite (see References). + +For the case of the EC-DH and EC-DSA implementations, most of the test vectors +were obtained from the site of the NIST Cryptographic Algorithm Validation +Program (CAVP), see References. + +References +********** + +* `NIST FIPS PUB 180-4 (SHA-256)`_ + +.. _NIST FIPS PUB 180-4 (SHA-256): + http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +* `NIST FIPS PUB 197 (AES-128)`_ + +.. _NIST FIPS PUB 197 (AES-128): + http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +* `NIST SP800-90A (HMAC-PRNG)`_ + +.. _NIST SP800-90A (HMAC-PRNG): + http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + +* `NIST SP 800-38A (AES-CBC and AES-CTR)`_ + +.. _NIST SP 800-38A (AES-CBC and AES-CTR): + http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + +* `NIST SP 800-38B (AES-CMAC)`_ + +.. _NIST SP 800-38B (AES-CMAC): + http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + +* `NIST SP 800-38C (AES-CCM)`_ + +.. _NIST SP 800-38C (AES-CCM): + http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + +* `NIST Statistical Test Suite (useful for testing HMAC-PRNG)`_ + +.. _NIST Statistical Test Suite (useful for testing HMAC-PRNG): + http://csrc.nist.gov/groups/ST/toolkit/rng/documentation_software.html + +* `NIST Cryptographic Algorithm Validation Program (CAVP) site`_ + +.. _NIST Cryptographic Algorithm Validation Program (CAVP) site: + http://csrc.nist.gov/groups/STM/cavp/ + +* `RFC 2104 (HMAC-SHA256)`_ + +.. _RFC 2104 (HMAC-SHA256): + https://www.ietf.org/rfc/rfc2104.txt + +* `RFC 6090 (ECC-DH and ECC-DSA)`_ + +.. _RFC 6090 (ECC-DH and ECC-DSA): + https://www.ietf.org/rfc/rfc6090.txt diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/aes.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/aes.h new file mode 100644 index 0000000..b612213 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/aes.h @@ -0,0 +1,130 @@ +/* aes.h - TinyCrypt interface to an AES-128 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to an AES-128 implementation. + * + * Overview: AES-128 is a NIST approved block cipher specified in + * FIPS 197. Block ciphers are deterministic algorithms that + * perform a transformation specified by a symmetric key in fixed- + * length data sets, also called blocks. + * + * Security: AES-128 provides approximately 128 bits of security. + * + * Usage: 1) call tc_aes128_set_encrypt/decrypt_key to set the key. + * + * 2) call tc_aes_encrypt/decrypt to process the data. + */ + +#ifndef __TC_AES_H__ +#define __TC_AES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Nb (4) /* number of columns (32-bit words) comprising the state */ +#define Nk (4) /* number of 32-bit words comprising the key */ +#define Nr (10) /* number of rounds */ +#define TC_AES_BLOCK_SIZE (Nb*Nk) +#define TC_AES_KEY_SIZE (Nb*Nk) + +typedef struct tc_aes_key_sched_struct { + unsigned int words[Nb*(Nr+1)]; +} *TCAesKeySched_t; + +/** + * @brief Set AES-128 encryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This implementation skips the additional steps required for keys + * larger than 128 bits, and must not be used for AES-192 or + * AES-256 key schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Encrypts contents of in buffer into out buffer under key; + * schedule s + * @note Assumes s was initialized by aes_set_encrypt_key; + * out and in point to 16 byte buffers + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out == NULL or in == NULL or s == NULL + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +/** + * @brief Set the AES-128 decryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This is the implementation of the straightforward inverse cipher + * using the cipher documented in FIPS-197 figure 12, not the + * equivalent inverse cipher presented in Figure 15 + * @warning This routine skips the additional steps required for keys larger + * than 128, and must not be used for AES-192 or AES-256 key + * schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Decrypts in buffer into out buffer under key schedule s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out is NULL or in is NULL or s is NULL + * @note Assumes s was initialized by aes_set_encrypt_key + * out and in point to 16 byte buffers + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_AES_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cbc_mode.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cbc_mode.h new file mode 100644 index 0000000..a53318e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cbc_mode.h @@ -0,0 +1,151 @@ +/* cbc_mode.h - TinyCrypt interface to a CBC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CBC mode implementation. + * + * Overview: CBC (for "cipher block chaining") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any block + * cipher to provide confidentiality of strings whose lengths are + * multiples of the block_size of the underlying block cipher. + * TinyCrypt hard codes AES as the block cipher. + * + * Security: CBC mode provides data confidentiality given that the maximum + * number q of blocks encrypted under a single key satisfies + * q < 2^63, which is not a practical constraint (it is considered a + * good practice to replace the encryption when q == 2^56). CBC mode + * provides NO data integrity. + * + * CBC mode assumes that the IV value input into the + * tc_cbc_mode_encrypt is randomly generated. The TinyCrypt library + * provides HMAC-PRNG module, which generates suitable IVs. Other + * methods for generating IVs are acceptable, provided that the + * values of the IVs generated appear random to any adversary, + * including someone with complete knowledge of the system design. + * + * The randomness property on which CBC mode's security depends is + * the unpredictability of the IV. Since it is unpredictable, this + * means in practice that CBC mode requires that the IV is stored + * somehow with the ciphertext in order to recover the plaintext. + * + * TinyCrypt CBC encryption prepends the IV to the ciphertext, + * because this affords a more efficient (few buffers) decryption. + * Hence tc_cbc_mode_encrypt assumes the ciphertext buffer is always + * 16 bytes larger than the plaintext buffer. + * + * Requires: AES-128 + * + * Usage: 1) call tc_cbc_mode_encrypt to encrypt data. + * + * 2) call tc_cbc_mode_decrypt to decrypt data. + * + */ + +#ifndef __TC_CBC_MODE_H__ +#define __TC_CBC_MODE_H__ + +#include "aes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CBC encryption procedure + * CBC encrypts inlen bytes of the in buffer into the out buffer + * using the encryption key schedule provided, prepends iv to out + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes: - sched has been configured by aes_set_encrypt_key + * - iv contains a 16 byte random string + * - out buffer is large enough to hold the ciphertext + iv + * - out buffer is a contiguous buffer + * - in holds the plaintext and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive the ciphertext + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- plaintext to encrypt + * @param inlen IN -- length of plaintext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this encrypt + */ +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +/** + * @brief CBC decryption procedure + * CBC decrypts inlen bytes of the in buffer into the out buffer + * using the provided encryption key schedule + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes:- in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption + * algorithm that would not otherwise be possible + * - sched was configured by aes_set_decrypt_key + * - out buffer is large enough to hold the decrypted plaintext + * and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive decrypted data + * @param outlen IN -- length of plaintext buffer in bytes + * @param in IN -- ciphertext to decrypt, including IV + * @param inlen IN -- length of ciphertext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this decrypt + * + */ +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CBC_MODE_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h new file mode 100644 index 0000000..c22ac08 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h @@ -0,0 +1,211 @@ +/* ccm_mode.h - TinyCrypt interface to a CCM mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CCM mode implementation. + * + * Overview: CCM (for "Counter with CBC-MAC") mode is a NIST approved mode of + * operation defined in SP 800-38C. + * + * TinyCrypt CCM implementation accepts: + * + * 1) Both non-empty payload and associated data (it encrypts and + * authenticates the payload and also authenticates the associated + * data); + * 2) Non-empty payload and empty associated data (it encrypts and + * authenticates the payload); + * 3) Non-empty associated data and empty payload (it degenerates to + * an authentication mode on the associated data). + * + * TinyCrypt CCM implementation accepts associated data of any length + * between 0 and (2^16 - 2^8) bytes. + * + * Security: The mac length parameter is an important parameter to estimate the + * security against collision attacks (that aim at finding different + * messages that produce the same authentication tag). TinyCrypt CCM + * implementation accepts any even integer between 4 and 16, as + * suggested in SP 800-38C. + * + * RFC-3610, which also specifies CCM, presents a few relevant + * security suggestions, such as: it is recommended for most + * applications to use a mac length greater than 8. Besides, the + * usage of the same nonce for two different messages which are + * encrypted with the same key destroys the security of CCM mode. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ccm_config to configure. + * + * 2) call tc_ccm_mode_encrypt to encrypt data and generate tag. + * + * 3) call tc_ccm_mode_decrypt to decrypt data and verify tag. + */ + +#ifndef __TC_CCM_MODE_H__ +#define __TC_CCM_MODE_H__ + +#include "aes.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* max additional authenticated size in bytes: 2^16 - 2^8 = 65280 */ +#define TC_CCM_AAD_MAX_BYTES 0xff00 + +/* max message size in bytes: 2^(8L) = 2^16 = 65536 */ +#define TC_CCM_PAYLOAD_MAX_BYTES 0x10000 + +/* struct tc_ccm_mode_struct represents the state of a CCM computation */ +typedef struct tc_ccm_mode_struct { + TCAesKeySched_t sched; /* AES key schedule */ + uint8_t *nonce; /* nonce required by CCM */ + unsigned int mlen; /* mac length in bytes (parameter t in SP-800 38C) */ +} *TCCcmMode_t; + +/** + * @brief CCM configuration procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * c == NULL or + * sched == NULL or + * nonce == NULL or + * mlen != {4, 6, 8, 10, 12, 16} + * @param c -- CCM state + * @param sched IN -- AES key schedule + * @param nonce IN - nonce + * @param nlen -- nonce length in bytes + * @param mlen -- mac length in bytes (parameter t in SP-800 38C) + */ +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen); + +/** + * @brief CCM tag generation and encryption procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen + maclength) + * + * @param out OUT -- encrypted data + * @param olen IN -- output length in bytes + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen + c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c); + +/** + * @brief CCM decryption and tag verification procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen - c->mlen) + * + * @param out OUT -- decrypted data + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen - c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, unsigned int plen, + TCCcmMode_t c); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CCM_MODE_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h new file mode 100644 index 0000000..3270970 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h @@ -0,0 +1,194 @@ +/* cmac_mode.h -- interface to a CMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CMAC implementation. + * + * Overview: CMAC is defined NIST in SP 800-38B, and is the standard algorithm + * for computing a MAC using a block cipher. It can compute the MAC + * for a byte string of any length. It is distinguished from CBC-MAC + * in the processing of the final message block; CMAC uses a + * different technique to compute the final message block is full + * size or only partial, while CBC-MAC uses the same technique for + * both. This difference permits CMAC to be applied to variable + * length messages, while all messages authenticated by CBC-MAC must + * be the same length. + * + * Security: AES128-CMAC mode of operation offers 64 bits of security against + * collision attacks. Note however that an external attacker cannot + * generate the tags him/herself without knowing the MAC key. In this + * sense, to attack the collision property of AES128-CMAC, an + * external attacker would need the cooperation of the legal user to + * produce an exponentially high number of tags (e.g. 2^64) to + * finally be able to look for collisions and benefit from them. As + * an extra precaution, the current implementation allows to at most + * 2^48 calls to the tc_cmac_update function before re-calling + * tc_cmac_setup (allowing a new key to be set), as suggested in + * Appendix B of SP 800-38B. + * + * Requires: AES-128 + * + * Usage: This implementation provides a "scatter-gather" interface, so that + * the CMAC value can be computed incrementally over a message + * scattered in different segments throughout memory. Experience shows + * this style of interface tends to minimize the burden of programming + * correctly. Like all symmetric key operations, it is session + * oriented. + * + * To begin a CMAC session, use tc_cmac_setup to initialize a struct + * tc_cmac_struct with encryption key and buffer. Our implementation + * always assume that the AES key to be the same size as the block + * cipher block size. Once setup, this data structure can be used for + * many CMAC computations. + * + * Once the state has been setup with a key, computing the CMAC of + * some data requires three steps: + * + * (1) first use tc_cmac_init to initialize a new CMAC computation. + * (2) next mix all of the data into the CMAC computation state using + * tc_cmac_update. If all of the data resides in a single data + * segment then only one tc_cmac_update call is needed; if data + * is scattered throughout memory in n data segments, then n calls + * will be needed. CMAC IS ORDER SENSITIVE, to be able to detect + * attacks that swap bytes, so the order in which data is mixed + * into the state is critical! + * (3) Once all of the data for a message has been mixed, use + * tc_cmac_final to compute the CMAC tag value. + * + * Steps (1)-(3) can be repeated as many times as you want to CMAC + * multiple messages. A practical limit is 2^48 1K messages before you + * have to change the key. + * + * Once you are done computing CMAC with a key, it is a good idea to + * destroy the state so an attacker cannot recover the key; use + * tc_cmac_erase to accomplish this. + */ + +#ifndef __TC_CMAC_MODE_H__ +#define __TC_CMAC_MODE_H__ + +#include "aes.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* padding for last message block */ +#define TC_CMAC_PADDING 0x80 + +/* struct tc_cmac_struct represents the state of a CMAC computation */ +typedef struct tc_cmac_struct { +/* initialization vector */ + uint8_t iv[TC_AES_BLOCK_SIZE]; +/* used if message length is a multiple of block_size bytes */ + uint8_t K1[TC_AES_BLOCK_SIZE]; +/* used if message length isn't a multiple block_size bytes */ + uint8_t K2[TC_AES_BLOCK_SIZE]; +/* where to put bytes that didn't fill a block */ + uint8_t leftover[TC_AES_BLOCK_SIZE]; +/* identifies the encryption key */ + unsigned int keyid; +/* next available leftover location */ + unsigned int leftover_offset; +/* AES key schedule */ + TCAesKeySched_t sched; +/* calls to tc_cmac_update left before re-key */ + uint64_t countdown; +} *TCCmacState_t; + +/** + * @brief Configures the CMAC state to use the given AES key + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * key == NULL + * + * @param s IN/OUT -- the state to set up + * @param key IN -- the key to use + * @param sched IN -- AES key schedule + */ +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, + TCAesKeySched_t sched); + +/** + * @brief Erases the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to erase + */ +int tc_cmac_erase(TCCmacState_t s); + +/** + * @brief Initializes a new CMAC computation + * @return returns TC_CRYPTO_SUCCESS (1) after having initialized the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to initialize + */ +int tc_cmac_init(TCCmacState_t s); + +/** + * @brief Incrementally computes CMAC over the next data segment + * @return returns TC_CRYPTO_SUCCESS (1) after successfully updating the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * if data == NULL when dlen > 0 + * + * @param s IN/OUT -- the CMAC state + * @param data IN -- the next data segment to MAC + * @param dlen IN -- the length of data in bytes + */ +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t dlen); + +/** + * @brief Generates the tag from the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after successfully generating the tag + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * s == NULL + * + * @param tag OUT -- the CMAC tag + * @param s IN -- CMAC state + */ +int tc_cmac_final(uint8_t *tag, TCCmacState_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CMAC_MODE_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/constants.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/constants.h new file mode 100644 index 0000000..965490e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/constants.h @@ -0,0 +1,61 @@ +/* constants.h - TinyCrypt interface to constants */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to constants. + * + */ + +#ifndef __TC_CONSTANTS_H__ +#define __TC_CONSTANTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define TC_CRYPTO_SUCCESS 1 +#define TC_CRYPTO_FAIL 0 + +#define TC_ZERO_BYTE 0x00 + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CONSTANTS_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_mode.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_mode.h new file mode 100644 index 0000000..e2da5b4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_mode.h @@ -0,0 +1,108 @@ +/* ctr_mode.h - TinyCrypt interface to CTR mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to CTR mode. + * + * Overview: CTR (pronounced "counter") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any + * block cipher to provide confidentiality of strings of any + * length. TinyCrypt hard codes AES128 as the block cipher. + * + * Security: CTR mode achieves confidentiality only if the counter value is + * never reused with a same encryption key. If the counter is + * repeated, than an adversary might be able to defeat the scheme. + * + * A usual method to ensure different counter values refers to + * initialize the counter in a given value (0, for example) and + * increases it every time a new block is enciphered. This naturally + * leaves to a limitation on the number q of blocks that can be + * enciphered using a same key: q < 2^(counter size). + * + * TinyCrypt uses a counter of 32 bits. This means that after 2^32 + * block encryptions, the counter will be reused (thus losing CBC + * security). 2^32 block encryptions should be enough for most of + * applications targeting constrained devices. Applications intended + * to encrypt a larger number of blocks must replace the key after + * 2^32 block encryptions. + * + * CTR mode provides NO data integrity. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ctr_mode to process the data to encrypt/decrypt. + * + */ + +#ifndef __TC_CTR_MODE_H__ +#define __TC_CTR_MODE_H__ + +#include "aes.h" +#include "constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CTR mode encryption/decryption procedure. + * CTR mode encrypts (or decrypts) inlen bytes from in buffer into out buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * inlen != outlen + * @note Assumes:- The current value in ctr has NOT been used with sched + * - out points to inlen bytes + * - in points to inlen bytes + * - ctr is an integer counter in littleEndian format + * - sched was initialized by aes_set_encrypt_key + * @param out OUT -- produced ciphertext (plaintext) + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- data to encrypt (or decrypt) + * @param inlen IN -- length of input data in bytes + * @param ctr IN/OUT -- the current counter value + * @param sched IN -- an initialized AES key schedule + */ +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_MODE_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_prng.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_prng.h new file mode 100644 index 0000000..bff7f97 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ctr_prng.h @@ -0,0 +1,166 @@ +/* ctr_prng.h - TinyCrypt interface to a CTR-PRNG implementation */ + +/* + * Copyright (c) 2016, Chris Morrison + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CTR-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the CTR-PRNG one + * which is based on AES. TinyCrypt implements CTR-PRNG with + * AES-128. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (AES-128 + * in this instance). + * + * Requires: - AES-128 + * + * Usage: 1) call tc_ctr_prng_init to seed the prng context + * + * 2) call tc_ctr_prng_reseed to mix in additional entropy into + * the prng context + * + * 3) call tc_ctr_prng_generate to output the pseudo-random data + * + * 4) call tc_ctr_prng_uninstantiate to zero out the prng context + */ + +#ifndef __TC_CTR_PRNG_H__ +#define __TC_CTR_PRNG_H__ + +#include "aes.h" + +#define TC_CTR_PRNG_RESEED_REQ -1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* updated each time another BLOCKLEN_BYTES bytes are produced */ + uint8_t V[TC_AES_BLOCK_SIZE]; + + /* updated whenever the PRNG is reseeded */ + struct tc_aes_key_sched_struct key; + + /* number of requests since initialization/reseeding */ + uint64_t reseedCount; +} TCCtrPrng_t; + + +/** + * @brief CTR-PRNG initialization procedure + * Initializes prng context with entropy and personalization string (if any) + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropyLen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note Only the first (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes of + * both the entropy and personalization inputs are used - + * supplying additional bytes has no effect. + * @param ctx IN/OUT -- the PRNG context to initialize + * @param entropy IN -- entropy used to seed the PRNG + * @param entropyLen IN -- entropy length in bytes + * @param personalization IN -- personalization string used to seed the PRNG + * (may be null) + * @param plen IN -- personalization length in bytes + * + */ +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen); + +/** + * @brief CTR-PRNG reseed procedure + * Mixes entropy and additional_input into the prng context + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropylen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note It is better to reseed an existing prng context rather than + * re-initialise, so that any existing entropy in the context is + * presereved. This offers some protection against undetected failures + * of the entropy source. + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG state + * @param entropy IN -- entropy to mix into the prng + * @param entropylen IN -- length of entropy in bytes + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + */ +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen); + +/** + * @brief CTR-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CTR_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * out == NULL, + * outlen >= 2^16 + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG context + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + */ +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen); + +/** + * @brief CTR-PRNG uninstantiate procedure + * Zeroes the internal state of the supplied prng context + * @return none + * @param ctx IN/OUT -- the PRNG context + */ +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_PRNG_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc.h new file mode 100644 index 0000000..8abc949 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc.h @@ -0,0 +1,545 @@ +/* ecc.h - TinyCrypt interface to common ECC functions */ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to common ECC functions. + * + * Overview: This software is an implementation of common functions + * necessary to elliptic curve cryptography. This implementation uses + * curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + */ + +#ifndef __TC_UECC_H__ +#define __TC_UECC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Word size (4 bytes considering 32-bits architectures) */ +#define uECC_WORD_SIZE 4 + +/* setting max number of calls to prng: */ +#ifndef uECC_RNG_MAX_TRIES +#define uECC_RNG_MAX_TRIES 64 +#endif + +/* defining data types to store word and bit counts: */ +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +/* defining data type for comparison result: */ +typedef int8_t cmpresult_t; +/* defining data type to store ECC coordinate/point in 32bits words: */ +typedef unsigned int uECC_word_t; +/* defining data type to store an ECC coordinate/point in 64bits words: */ +typedef uint64_t uECC_dword_t; + +/* defining masks useful for ecc computations: */ +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +/* Number of words of 32 bits to represent an element of the the curve p-256: */ +#define NUM_ECC_WORDS 8 +/* Number of bytes to represent an element of the the curve p-256: */ +#define NUM_ECC_BYTES (uECC_WORD_SIZE*NUM_ECC_WORDS) + +/* structure that represents an elliptic curve (e.g. p256):*/ +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[NUM_ECC_WORDS]; + uECC_word_t n[NUM_ECC_WORDS]; + uECC_word_t G[NUM_ECC_WORDS * 2]; + uECC_word_t b[NUM_ECC_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * Z1, + uECC_Curve curve); + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +}; + +/* + * @brief computes doubling of point ion jacobian coordinates, in place. + * @param X1 IN/OUT -- x coordinate + * @param Y1 IN/OUT -- y coordinate + * @param Z1 IN/OUT -- z coordinate + * @param curve IN -- elliptic curve + */ +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve); + +/* + * @brief Computes x^3 + ax + b. result must not overlap x. + * @param result OUT -- x^3 + ax + b + * @param x IN -- value of x + * @param curve IN -- elliptic curve + */ +void x_side_default(uECC_word_t *result, const uECC_word_t *x, + uECC_Curve curve); + +/* + * @brief Computes result = product % curve_p + * from http://www.nsa.gov/ia/_files/nist-routines.pdf + * @param result OUT -- product % curve_p + * @param product IN -- value to be reduced mod curve_p + */ +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int *product); + +/* Bytes to words ordering: */ +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a +#define BITS_TO_WORDS(num_bits) \ + ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) +#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) + +/* definition of curve NIST p-256: */ +static const struct uECC_Curve_t curve_secp256r1 = { + NUM_ECC_WORDS, + NUM_ECC_BYTES, + 256, /* num_n_bits */ { + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) + }, { + BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), + BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) + }, { + BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), + BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), + BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), + BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), + + BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), + BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), + BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), + BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) + }, { + BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), + BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), + BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), + BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) + }, + &double_jacobian_default, + &x_side_default, + &vli_mmod_fast_secp256r1 +}; + +uECC_Curve uECC_secp256r1(void); + +/* + * @brief Generates a random integer in the range 0 < random < top. + * Both random and top have num_words words. + * @param random OUT -- random integer in the range 0 < random < top + * @param top IN -- upper limit + * @param num_words IN -- number of words + * @return a random integer in the range 0 < random < top + */ +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words); + + +/* uECC_RNG_Function type + * The RNG function should fill 'size' random bytes into 'dest'. It should + * return 1 if 'dest' was filled with random data, or 0 if the random data could + * not be generated. The filled-in values should be either truly random, or from + * a cryptographically-secure PRNG. + * + * A correctly functioning RNG function must be set (using uECC_set_rng()) + * before calling uECC_make_key() or uECC_sign(). + * + * Setting a correctly functioning RNG function improves the resistance to + * side-channel attacks for uECC_shared_secret(). + * + * A correct RNG function is set by default. If you are building on another + * POSIX-compliant system that supports /dev/random or /dev/urandom, you can + * define uECC_POSIX to use the predefined RNG. + */ +typedef int(*uECC_RNG_Function)(uint8_t *dest, unsigned int size); + +/* + * @brief Set the function that will be used to generate random bytes. The RNG + * function should return 1 if the random data was generated, or 0 if the random + * data could not be generated. + * + * @note On platforms where there is no predefined RNG function, this must be + * called before uECC_make_key() or uECC_sign() are used. + * + * @param rng_function IN -- function that will be used to generate random bytes + */ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* + * @brief provides current uECC_RNG_Function. + * @return Returns the function that will be used to generate random bytes. + */ +uECC_RNG_Function uECC_get_rng(void); + +/* + * @brief computes the size of a private key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return size of a private key for the curve in bytes. + */ +int uECC_curve_private_key_size(uECC_Curve curve); + +/* + * @brief computes the size of a public key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return the size of a public key for the curve in bytes. + */ +int uECC_curve_public_key_size(uECC_Curve curve); + +/* + * @brief Compute the corresponding public key for a private key. + * @param private_key IN -- The private key to compute the public key for + * @param public_key OUT -- Will be filled in with the corresponding public key + * @param curve + * @return Returns 1 if key was computed successfully, 0 if an error occurred. + */ +int uECC_compute_public_key(const uint8_t *private_key, + uint8_t *public_key, uECC_Curve curve); + +/* + * @brief Compute public-key. + * @return corresponding public-key. + * @param result OUT -- public-key + * @param private_key IN -- private-key + * @param curve IN -- elliptic curve + */ +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, uECC_Curve curve); + +/* + * @brief Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. + * @return Regularized k + * @param k IN -- private-key + * @param k0 IN/OUT -- regularized k + * @param k1 IN/OUT -- regularized k + * @param curve IN -- elliptic curve + */ +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve); + +/* + * @brief Point multiplication algorithm using Montgomery's ladder with co-Z + * coordinates. See http://eprint.iacr.org/2011/338.pdf. + * @note Result may overlap point. + * @param result OUT -- returns scalar*point + * @param point IN -- elliptic curve point + * @param scalar IN -- scalar + * @param initial_Z IN -- initial value for z + * @param num_bits IN -- number of bits in scalar + * @param curve IN -- elliptic curve + */ +void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point, + const uECC_word_t * scalar, const uECC_word_t * initial_Z, + bitcount_t num_bits, uECC_Curve curve); + +/* + * @brief Constant-time comparison to zero - secure way to compare long integers + * @param vli IN -- very long integer + * @param num_words IN -- number of words in the vli + * @return 1 if vli == 0, 0 otherwise. + */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief Check if 'point' is the point at infinity + * @param point IN -- elliptic curve point + * @param curve IN -- elliptic curve + * @return if 'point' is the point at infinity, 0 otherwise. + */ +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief computes the sign of left - right, in constant time. + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief computes sign of left - right, not in constant time. + * @note should not be used if inputs are part of a secret + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes result = (left - right) % mod. + * @note Assumes that (left < mod) and (right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left - right) % mod + * @param left IN -- leftright term in modular subtraction + * @param right IN -- right term in modular subtraction + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Computes P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) or + * P => P', Q => P + Q + * @note assumes Input P = (x1, y1, Z), Q = (x2, y2, Z) + * @param X1 IN -- x coordinate of P + * @param Y1 IN -- y coordinate of P + * @param X2 IN -- x coordinate of Q + * @param Y2 IN -- y coordinate of Q + * @param curve IN -- elliptic curve + */ +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * X2, + uECC_word_t * Y2, uECC_Curve curve); + +/* + * @brief Computes (x1 * z^2, y1 * z^3) + * @param X1 IN -- previous x1 coordinate + * @param Y1 IN -- previous y1 coordinate + * @param Z IN -- z value + * @param curve IN -- elliptic curve + */ +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve); + +/* + * @brief Check if bit is set. + * @return Returns nonzero if bit 'bit' of vli is set. + * @warning It is assumed that the value provided in 'bit' is within the + * boundaries of the word-array 'vli'. + * @note The bit ordering layout assumed for vli is: {31, 30, ..., 0}, + * {63, 62, ..., 32}, {95, 94, ..., 64}, {127, 126,..., 96} for a vli consisting + * of 4 uECC_word_t elements. + */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* + * @brief Computes result = product % mod, where product is 2N words long. + * @param result OUT -- product % mod + * @param mod IN -- module + * @param num_words IN -- number of words + * @warning Currently only designed to work for curve_p or curve_n. + */ +void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product, + const uECC_word_t *mod, wordcount_t num_words); + +/* + * @brief Computes modular product (using curve->mmod_fast) + * @param result OUT -- (left * right) mod % curve_p + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param curve IN -- elliptic curve + */ +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve); + +/* + * @brief Computes result = left - right. + * @note Can modify in place. + * @param result OUT -- left - right + * @param left IN -- left term in subtraction + * @param right IN -- right term in subtraction + * @param num_words IN -- number of words + * @return borrow + */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words); + +/* + * @brief Constant-time comparison function(secure way to compare long ints) + * @param left IN -- left term in comparison + * @param right IN -- right term in comparison + * @param num_words IN -- number of words + * @return Returns 0 if left == right, 1 otherwise. + */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes (left * right) % mod + * @param result OUT -- (left * right) % mod + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Computes (1 / input) % mod + * @note All VLIs are the same size. + * @note See "Euclid's GCD to Montgomery Multiplication to the Great Divide" + * @param result OUT -- (1 / input) % mod + * @param input IN -- value to be modular inverted + * @param mod IN -- mod + * @param num_words -- number of words + */ +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words); + +/* + * @brief Sets dest = src. + * @param dest OUT -- destination buffer + * @param src IN -- origin buffer + * @param num_words IN -- number of words + */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words); + +/* + * @brief Computes (left + right) % mod. + * @note Assumes that (left < mod) and right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left + right) % mod. + * @param left IN -- left term in addition + * @param right IN -- right term in addition + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Counts the number of bits required to represent vli. + * @param vli IN -- very long integer + * @param max_words IN -- number of words + * @return number of bits in given vli + */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words); + +/* + * @brief Erases (set to 0) vli + * @param vli IN -- very long integer + * @param num_words IN -- number of words + */ +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief check if it is a valid point in the curve + * @param point IN -- point to be checked + * @param curve IN -- elliptic curve + * @return 0 if point is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + */ +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief Check if a public key is valid. + * @param public_key IN -- The public key to be checked. + * @return returns 0 if the public key is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + * @exception returns -4 if public key is the group generator. + * + * @note Note that you are not required to check for a valid public key before + * using any other uECC functions. However, you may wish to avoid spending CPU + * time computing a shared secret or verifying a signature using an invalid + * public key. + */ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + + /* + * @brief Converts an integer in uECC native format to big-endian bytes. + * @param bytes OUT -- bytes representation + * @param num_bytes IN -- number of bytes + * @param native IN -- uECC native representation + */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native); + +/* + * @brief Converts big-endian bytes to an integer in uECC native format. + * @param native OUT -- uECC native representation + * @param bytes IN -- bytes representation + * @param num_bytes IN -- number of bytes + */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UECC_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h new file mode 100644 index 0000000..930e916 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h @@ -0,0 +1,131 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DH implementation */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to EC-DH implementation. + * + * Overview: This software is an implementation of EC-DH. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + */ + +#ifndef __TC_ECC_DH_H__ +#define __TC_ECC_DH_H__ + +#include "ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a public/private key pair. + * @return returns TC_CRYPTO_SUCCESS (1) if the key pair was generated successfully + * returns TC_CRYPTO_FAIL (0) if error while generating key pair + * + * @param p_public_key OUT -- Will be filled in with the public key. Must be at + * least 2 * the curve size (in bytes) long. For curve secp256r1, p_public_key + * must be 64 bytes long. + * @param p_private_key OUT -- Will be filled in with the private key. Must be as + * long as the curve order (for secp256r1, p_private_key must be 32 bytes long). + * + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_make_key(). + */ +int uECC_make_key(uint8_t *p_public_key, uint8_t *p_private_key, uECC_Curve curve); + +#ifdef ENABLE_TESTS + +/** + * @brief Create a public/private key pair given a specific d. + * + * @note THIS FUNCTION SHOULD BE CALLED ONLY FOR TEST PURPOSES. Refer to + * uECC_make_key() function for real applications. + */ +int uECC_make_key_with_d(uint8_t *p_public_key, uint8_t *p_private_key, + unsigned int *d, uECC_Curve curve); +#endif + +/** + * @brief Compute a shared secret given your secret key and someone else's + * public key. + * @return returns TC_CRYPTO_SUCCESS (1) if the shared secret was computed successfully + * returns TC_CRYPTO_FAIL (0) otherwise + * + * @param p_secret OUT -- Will be filled in with the shared secret value. Must be + * the same size as the curve size (for curve secp256r1, secret must be 32 bytes + * long. + * @param p_public_key IN -- The public key of the remote party. + * @param p_private_key IN -- Your private key. + * + * @warning It is recommended to use the output of uECC_shared_secret() as the + * input of a recommended Key Derivation Function (see NIST SP 800-108) in + * order to produce a cryptographically secure symmetric key. + */ +int uECC_shared_secret(const uint8_t *p_public_key, const uint8_t *p_private_key, + uint8_t *p_secret, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DH_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dsa.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dsa.h new file mode 100644 index 0000000..8cb421b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_dsa.h @@ -0,0 +1,139 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DSA implementation */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to EC-DSA implementation. + * + * Overview: This software is an implementation of EC-DSA. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + * Usage: - To sign: Compute a hash of the data you wish to sign (SHA-2 is + * recommended) and pass it in to ecdsa_sign function along with your + * private key and a random number. You must use a new non-predictable + * random number to generate each new signature. + * - To verify a signature: Compute the hash of the signed data using + * the same hash as the signer and pass it to this function along with + * the signer's public key and the signature values (r and s). + */ + +#ifndef __TC_ECC_DSA_H__ +#define __TC_ECC_DSA_H__ + +#include "ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate an ECDSA signature for a given hash value. + * @return returns TC_CRYPTO_SUCCESS (1) if the signature generated successfully + * returns TC_CRYPTO_FAIL (0) if an error occurred. + * + * @param p_private_key IN -- Your private key. + * @param p_message_hash IN -- The hash of the message to sign. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature OUT -- Will be filled in with the signature value. Must be + * at least 2 * curve size long (for secp256r1, signature must be 64 bytes long). + * + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_sign(). + * @note Usage: Compute a hash of the data you wish to sign (SHA-2 is + * recommended) and pass it in to this function along with your private key. + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + */ +int uECC_sign(const uint8_t *p_private_key, const uint8_t *p_message_hash, + unsigned p_hash_size, uint8_t *p_signature, uECC_Curve curve); + +#ifdef ENABLE_TESTS +/* + * THIS FUNCTION SHOULD BE CALLED FOR TEST PURPOSES ONLY. + * Refer to uECC_sign() function for real applications. + */ +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned int hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve); +#endif + +/** + * @brief Verify an ECDSA signature. + * @return returns TC_SUCCESS (1) if the signature is valid + * returns TC_FAIL (0) if the signature is invalid. + * + * @param p_public_key IN -- The signer's public key. + * @param p_message_hash IN -- The hash of the signed data. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature IN -- The signature values. + * + * @note Usage: Compute the hash of the signed data using the same hash as the + * signer and pass it to this function along with the signer's public key and + * the signature values (hash_size and signature). + */ +int uECC_verify(const uint8_t *p_public_key, const uint8_t *p_message_hash, + unsigned int p_hash_size, const uint8_t *p_signature, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DSA_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_platform_specific.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_platform_specific.h new file mode 100644 index 0000000..a55adf4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/ecc_platform_specific.h @@ -0,0 +1,81 @@ +/* uECC_platform_specific.h - Interface to platform specific functions*/ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * uECC_platform_specific.h -- Interface to platform specific functions + */ + +#ifndef __UECC_PLATFORM_SPECIFIC_H_ +#define __UECC_PLATFORM_SPECIFIC_H_ + +/* + * The RNG function should fill 'size' random bytes into 'dest'. It should + * return 1 if 'dest' was filled with random data, or 0 if the random data could + * not be generated. The filled-in values should be either truly random, or from + * a cryptographically-secure PRNG. + * + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + * before calling uECC_make_key() or uECC_sign(). + * + * Setting a cryptographically-secure PRNG function improves the resistance to + * side-channel attacks for uECC_shared_secret(). + * + * A correct PRNG function is set by default (default_RNG_defined = 1) and works + * for some platforms, such as Unix and Linux. For other platforms, you may need + * to provide another PRNG function. +*/ +#define default_RNG_defined 0 + +int default_CSPRNG(uint8_t *dest, unsigned int size); + +#endif /* __UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac.h new file mode 100644 index 0000000..c3b3f02 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac.h @@ -0,0 +1,139 @@ +/* hmac.h - TinyCrypt interface to an HMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to an HMAC implementation. + * + * Overview: HMAC is a message authentication code based on hash functions. + * TinyCrypt hard codes SHA-256 as the hash function. A message + * authentication code based on hash functions is also called a + * keyed cryptographic hash function since it performs a + * transformation specified by a key in an arbitrary length data + * set into a fixed length data set (also called tag). + * + * Security: The security of the HMAC depends on the length of the key and + * on the security of the hash function. Note that HMAC primitives + * are much less affected by collision attacks than their + * corresponding hash functions. + * + * Requires: SHA-256 + * + * Usage: 1) call tc_hmac_set_key to set the HMAC key. + * + * 2) call tc_hmac_init to initialize a struct hash_state before + * processing the data. + * + * 3) call tc_hmac_update to process the next input segment; + * tc_hmac_update can be called as many times as needed to process + * all of the segments of the input; the order is important. + * + * 4) call tc_hmac_final to out put the tag. + */ + +#ifndef __TC_HMAC_H__ +#define __TC_HMAC_H__ + +#include "sha256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tc_hmac_state_struct { + /* the internal state required by h */ + struct tc_sha256_state_struct hash_state; + /* HMAC key schedule */ + uint8_t key[2*TC_SHA256_BLOCK_SIZE]; +}; +typedef struct tc_hmac_state_struct *TCHmacState_t; + +/** + * @brief HMAC set key procedure + * Configures ctx to use key + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if + * ctx == NULL or + * key == NULL or + * key_size == 0 + * @param ctx IN/OUT -- the struct tc_hmac_state_struct to initial + * @param key IN -- the HMAC key to configure + * @param key_size IN -- the HMAC key size + */ +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size); + +/** + * @brief HMAC initialize procedure + * Initializes ctx to begin the next HMAC operation + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @param ctx IN/OUT -- struct tc_hmac_state_struct buffer to initialize + */ +int tc_hmac_init(TCHmacState_t ctx); + +/** + * @brief HMAC update procedure + * Mixes data_length bytes addressed by data into state + * @return returns TC_CRYPTO_SUCCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @note Assumes state has been initialized by tc_hmac_init + * @param ctx IN/OUT -- state of HMAC computation so far + * @param data IN -- data to incorporate into state + * @param data_length IN -- size of data in bytes + */ +int tc_hmac_update(TCHmacState_t ctx, const void *data, + unsigned int data_length); + +/** + * @brief HMAC final procedure + * Writes the HMAC tag into the tag buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * ctx == NULL or + * key == NULL or + * taglen != TC_SHA256_DIGEST_SIZE + * @note ctx is erased before exiting. This should never be changed/removed. + * @note Assumes the tag bufer is at least sizeof(hmac_tag_size(state)) bytes + * state has been initialized by tc_hmac_init + * @param tag IN/OUT -- buffer to receive computed HMAC tag + * @param taglen IN -- size of tag in bytes + * @param ctx IN/OUT -- the HMAC state for computing tag + */ +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx); + +#ifdef __cplusplus +} +#endif + +#endif /*__TC_HMAC_H__*/ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac_prng.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac_prng.h new file mode 100644 index 0000000..7d63277 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/hmac_prng.h @@ -0,0 +1,164 @@ +/* hmac_prng.h - TinyCrypt interface to an HMAC-PRNG implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to an HMAC-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the HMAC-PRNG one + * which is based on HMAC. TinyCrypt implements HMAC-PRNG with + * certain modifications from the NIST SP 800-90A spec. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (HMAC and + * SHA256, for TinyCrypt). + * + * The NIST SP 800-90A standard tolerates a null personalization, + * while TinyCrypt requires a non-null personalization. This is + * because a personalization string (the host name concatenated + * with a time stamp, for example) is easily computed and might be + * the last line of defense against failure of the entropy source. + * + * Requires: - SHA-256 + * - HMAC + * + * Usage: 1) call tc_hmac_prng_init to set the HMAC key and process the + * personalization data. + * + * 2) call tc_hmac_prng_reseed to process the seed and additional + * input. + * + * 3) call tc_hmac_prng_generate to out put the pseudo-random data. + */ + +#ifndef __TC_HMAC_PRNG_H__ +#define __TC_HMAC_PRNG_H__ + +#include "sha256.h" +#include "hmac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_HMAC_PRNG_RESEED_REQ -1 + +struct tc_hmac_prng_struct { + /* the HMAC instance for this PRNG */ + struct tc_hmac_state_struct h; + /* the PRNG key */ + uint8_t key[TC_SHA256_DIGEST_SIZE]; + /* PRNG state */ + uint8_t v[TC_SHA256_DIGEST_SIZE]; + /* calls to tc_hmac_prng_generate left before re-seed */ + unsigned int countdown; +}; + +typedef struct tc_hmac_prng_struct *TCHmacPrng_t; + +/** + * @brief HMAC-PRNG initialization procedure + * Initializes prng with personalization, disables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * personalization == NULL, + * plen > MAX_PLEN + * @note Assumes: - personalization != NULL. + * The personalization is a platform unique string (e.g., the host + * name) and is the last line of defense against failure of the + * entropy source + * @warning NIST SP 800-90A specifies 3 items as seed material during + * initialization: entropy seed, personalization, and an optional + * nonce. TinyCrypts requires instead a non-null personalization + * (which is easily computed) and indirectly requires an entropy + * seed (since the reseed function is mandatorily called after + * initialize) + * @param prng IN/OUT -- the PRNG state to initialize + * @param personalization IN -- personalization string + * @param plen IN -- personalization length in bytes + */ +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen); + +/** + * @brief HMAC-PRNG reseed procedure + * Mixes seed into prng, enables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * seed == NULL, + * seedlen < MIN_SLEN, + * seendlen > MAX_SLEN, + * additional_input != (const uint8_t *) 0 && additionallen == 0, + * additional_input != (const uint8_t *) 0 && additionallen > MAX_ALEN + * @note Assumes:- tc_hmac_prng_init has been called for prng + * - seed has sufficient entropy. + * + * @param prng IN/OUT -- the PRNG state + * @param seed IN -- entropy to mix into the prng + * @param seedlen IN -- length of seed in bytes + * @param additional_input IN -- additional input to the prng + * @param additionallen IN -- additional input length in bytes + */ +int tc_hmac_prng_reseed(TCHmacPrng_t prng, const uint8_t *seed, + unsigned int seedlen, const uint8_t *additional_input, + unsigned int additionallen); + +/** + * @brief HMAC-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_HMAC_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL, + * prng == NULL, + * outlen == 0, + * outlen >= MAX_OUT + * @note Assumes tc_hmac_prng_init has been called for prng + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + * @param prng IN/OUT -- the PRNG state + */ +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_HMAC_PRNG_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/sha256.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/sha256.h new file mode 100644 index 0000000..af5e8ba --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/sha256.h @@ -0,0 +1,129 @@ +/* sha256.h - TinyCrypt interface to a SHA-256 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a SHA-256 implementation. + * + * Overview: SHA-256 is a NIST approved cryptographic hashing algorithm + * specified in FIPS 180. A hash algorithm maps data of arbitrary + * size to data of fixed length. + * + * Security: SHA-256 provides 128 bits of security against collision attacks + * and 256 bits of security against pre-image attacks. SHA-256 does + * NOT behave like a random oracle, but it can be used as one if + * the string being hashed is prefix-free encoded before hashing. + * + * Usage: 1) call tc_sha256_init to initialize a struct + * tc_sha256_state_struct before hashing a new string. + * + * 2) call tc_sha256_update to hash the next string segment; + * tc_sha256_update can be called as many times as needed to hash + * all of the segments of a string; the order is important. + * + * 3) call tc_sha256_final to out put the digest from a hashing + * operation. + */ + +#ifndef __TC_SHA256_H__ +#define __TC_SHA256_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_SHA256_BLOCK_SIZE (64) +#define TC_SHA256_DIGEST_SIZE (32) +#define TC_SHA256_STATE_BLOCKS (TC_SHA256_DIGEST_SIZE/4) + +struct tc_sha256_state_struct { + unsigned int iv[TC_SHA256_STATE_BLOCKS]; + uint64_t bits_hashed; + uint8_t leftover[TC_SHA256_BLOCK_SIZE]; + size_t leftover_offset; +}; + +typedef struct tc_sha256_state_struct *TCSha256State_t; + +/** + * @brief SHA256 initialization procedure + * Initializes s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if s == NULL + * @param s Sha256 state struct + */ +int tc_sha256_init(TCSha256State_t s); + +/** + * @brief SHA256 update procedure + * Hashes data_length bytes addressed by data into state s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * data == NULL + * @note Assumes s has been initialized by tc_sha256_init + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param s Sha256 state struct + * @param data message to hash + * @param datalen length of message to hash + */ +int tc_sha256_update (TCSha256State_t s, const uint8_t *data, size_t datalen); + +/** + * @brief SHA256 final procedure + * Inserts the completed hash computation into digest + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * digest == NULL + * @note Assumes: s has been initialized by tc_sha256_init + * digest points to at least TC_SHA256_DIGEST_SIZE bytes + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param digest unsigned eight bit integer + * @param Sha256 state struct + */ +int tc_sha256_final(uint8_t *digest, TCSha256State_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_SHA256_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/utils.h b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/utils.h new file mode 100644 index 0000000..bab5c32 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/include/tinycrypt/utils.h @@ -0,0 +1,95 @@ +/* utils.h - TinyCrypt interface to platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to platform-dependent run-time operations. + * + */ + +#ifndef __TC_UTILS_H__ +#define __TC_UTILS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Copy the the buffer 'from' to the buffer 'to'. + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * from_len > to_len. + * + * @param to OUT -- destination buffer + * @param to_len IN -- length of destination buffer + * @param from IN -- origin buffer + * @param from_len IN -- length of origin buffer + */ +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len); + +/** + * @brief Set the value 'val' into the buffer 'to', 'len' times. + * + * @param to OUT -- destination buffer + * @param val IN -- value to be set in 'to' + * @param len IN -- number of times the value will be copied + */ +void _set(void *to, uint8_t val, unsigned int len); + +/* + * @brief AES specific doubling function, which utilizes + * the finite field used by AES. + * @return Returns a^2 + * + * @param a IN/OUT -- value to be doubled + */ +uint8_t _double_byte(uint8_t a); + +/* + * @brief Constant-time algorithm to compare if two sequences of bytes are equal + * @return Returns 0 if equal, and non-zero otherwise + * + * @param a IN -- sequence of bytes a + * @param b IN -- sequence of bytes b + * @param size IN -- size of sequences a and b + */ +int _compare(const uint8_t *a, const uint8_t *b, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UTILS_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_decrypt.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_decrypt.c new file mode 100644 index 0000000..0bab6e2 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_decrypt.c @@ -0,0 +1,164 @@ +/* aes_decrypt.c - TinyCrypt implementation of AES decryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/aes.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +static const uint8_t inv_sbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, + 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, + 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, + 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, + 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, + 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, + 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0c, 0x7d +}; + +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + return tc_aes128_set_encrypt_key(s, k); +} + +#define mult8(a)(_double_byte(_double_byte(_double_byte(a)))) +#define mult9(a)(mult8(a)^(a)) +#define multb(a)(mult8(a)^_double_byte(a)^(a)) +#define multd(a)(mult8(a)^_double_byte(_double_byte(a))^(a)) +#define multe(a)(mult8(a)^_double_byte(_double_byte(a))^_double_byte(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = multe(in[0]) ^ multb(in[1]) ^ multd(in[2]) ^ mult9(in[3]); + out[1] = mult9(in[0]) ^ multe(in[1]) ^ multb(in[2]) ^ multd(in[3]); + out[2] = multd(in[0]) ^ mult9(in[1]) ^ multe(in[2]) ^ multb(in[3]); + out[3] = multb(in[0]) ^ multd(in[1]) ^ mult9(in[2]) ^ multe(in[3]); +} + +static inline void inv_mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2*Nb], s+(2*Nb)); + mult_row_column(&t[3*Nb], s+(3*Nb)); + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void inv_sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb*Nk); ++i) { + s[i] = inv_sbox[s[i]]; + } +} + +/* + * This inv_shift_rows also implements the matrix flip required for + * inv_mix_columns, but performs it here to reduce the number of memory + * operations. + */ +static inline void inv_shift_rows(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + t[0] = s[0]; t[1] = s[13]; t[2] = s[10]; t[3] = s[7]; + t[4] = s[4]; t[5] = s[1]; t[6] = s[14]; t[7] = s[11]; + t[8] = s[8]; t[9] = s[5]; t[10] = s[2]; t[11] = s[15]; + t[12] = s[12]; t[13] = s[9]; t[14] = s[6]; t[15] = s[3]; + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + + add_round_key(state, s->words + Nb*Nr); + + for (i = Nr - 1; i > 0; --i) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words + Nb*i); + inv_mix_columns(state); + } + + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /*zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_encrypt.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_encrypt.c new file mode 100644 index 0000000..bdc434b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/aes_encrypt.c @@ -0,0 +1,191 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/aes.h" +#include "../include/tinycrypt/utils.h" +#include "../include/tinycrypt/constants.h" + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24)|((a) << 8)); +} + +#define subbyte(a, o)(sbox[((a) >> (o))&0xff] << (o)) +#define subword(a)(subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb*i]<<24) | (k[Nb*i+1]<<16) | + (k[Nb*i+2]<<8) | (k[Nb*i+3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i-1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i/Nk]; + } + s->words[i] = s->words[i-Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2 * Nb], s + (2 * Nb)); + mult_row_column(&t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + t[0] = s[0]; t[1] = s[5]; t[2] = s[10]; t[3] = s[15]; + t[4] = s[4]; t[5] = s[9]; t[6] = s[14]; t[7] = s[3]; + t[8] = s[8]; t[9] = s[13]; t[10] = s[2]; t[11] = s[7]; + t[12] = s[12]; t[13] = s[1]; t[14] = s[6]; t[15] = s[11]; + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + add_round_key(state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, s->words + Nb*(i+1)); + } + + sub_bytes(state); + shift_rows(state); + add_round_key(state, s->words + Nb*(i+1)); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /* zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cbc_mode.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cbc_mode.c new file mode 100644 index 0000000..b743878 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cbc_mode.c @@ -0,0 +1,114 @@ +/* cbc_mode.c - TinyCrypt implementation of CBC mode encryption & decryption */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/cbc_mode.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + unsigned int n, m; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen + TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* copy iv to the buffer */ + (void)_copy(buffer, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + /* copy iv to the output buffer */ + (void)_copy(out, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + + for (n = m = 0; n < inlen; ++n) { + buffer[m++] ^= *in++; + if (m == TC_AES_BLOCK_SIZE) { + (void)tc_aes_encrypt(buffer, buffer, sched); + (void)_copy(out, TC_AES_BLOCK_SIZE, + buffer, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + m = 0; + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + const uint8_t *p; + unsigned int n, m; + + /* sanity check the inputs */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen - TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* + * Note that in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption algorithm + * that would not otherwise be possible. + */ + p = iv; + for (n = m = 0; n < inlen; ++n) { + if ((n % TC_AES_BLOCK_SIZE) == 0) { + (void)tc_aes_decrypt(buffer, in, sched); + in += TC_AES_BLOCK_SIZE; + m = 0; + } + *out++ = buffer[m++] ^ *p++; + } + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ccm_mode.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ccm_mode.c new file mode 100644 index 0000000..5985312 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ccm_mode.c @@ -0,0 +1,266 @@ +/* ccm_mode.c - TinyCrypt implementation of CCM mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/ccm_mode.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +#include + +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen) +{ + + /* input sanity check: */ + if (c == (TCCcmMode_t) 0 || + sched == (TCAesKeySched_t) 0 || + nonce == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (nlen != 13) { + return TC_CRYPTO_FAIL; /* The allowed nonce size is: 13. See documentation.*/ + } else if ((mlen < 4) || (mlen > 16) || (mlen & 1)) { + return TC_CRYPTO_FAIL; /* The allowed mac sizes are: 4, 6, 8, 10, 12, 14, 16.*/ + } + + c->mlen = mlen; + c->sched = sched; + c->nonce = nonce; + + return TC_CRYPTO_SUCCESS; +} + +/** + * Variation of CBC-MAC mode used in CCM. + */ +static void ccm_cbc_mac(uint8_t *T, const uint8_t *data, unsigned int dlen, + unsigned int flag, TCAesKeySched_t sched) +{ + + unsigned int i; + + if (flag > 0) { + T[0] ^= (uint8_t)(dlen >> 8); + T[1] ^= (uint8_t)(dlen); + dlen += 2; i = 2; + } else { + i = 0; + } + + while (i < dlen) { + T[i++ % (Nb * Nk)] ^= *data++; + if (((i % (Nb * Nk)) == 0) || dlen == i) { + (void) tc_aes_encrypt(T, T, sched); + } + } +} + +/** + * Variation of CTR mode used in CCM. + * The CTR mode used by CCM is slightly different than the conventional CTR + * mode (the counter is increased before encryption, instead of after + * encryption). Besides, it is assumed that the counter is stored in the last + * 2 bytes of the nonce. + */ +static int ccm_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + uint16_t block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the counter to the nonce */ + (void) _copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 2 bytes of the nonce to be incremented */ + block_num = (uint16_t) ((nonce[14] << 8)|(nonce[15])); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + block_num++; + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + if (!tc_aes_encrypt(buffer, nonce, sched)) { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i % (TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < (plen + c->mlen))) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* GENERATING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0) | (((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i <= 13; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)(plen >> 8); + b[15] = (uint8_t)(plen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(tag, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(tag, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(tag, payload, plen, 0, c->sched); + } + + /* ENCRYPTION: */ + + /* formatting the sequence b for encryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + b[14] = b[15] = TC_ZERO_BYTE; + + /* encrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen, payload, plen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter for ctr_mode (0):*/ + + /* encrypting b and adding the tag to the output: */ + (void) tc_aes_encrypt(b, b, c->sched); + out += plen; + for (i = 0; i < c->mlen; ++i) { + *out++ = tag[i] ^ b[i]; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < plen - c->mlen)) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* DECRYPTION: */ + + /* formatting the sequence b for decryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = b[15] = TC_ZERO_BYTE; /* initial counter value is 0 */ + + /* decrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen - c->mlen, payload, plen - c->mlen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter value (0) */ + + /* encrypting b and restoring the tag from input: */ + (void) tc_aes_encrypt(b, b, c->sched); + for (i = 0; i < c->mlen; ++i) { + tag[i] = *(payload + plen - c->mlen + i) ^ b[i]; + } + + /* VERIFYING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0)|(((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)((plen - c->mlen) >> 8); + b[15] = (uint8_t)(plen - c->mlen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(b, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(b, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(b, out, plen - c->mlen, 0, c->sched); + } + + /* comparing the received tag and the computed one: */ + if (_compare(b, tag, c->mlen) == 0) { + return TC_CRYPTO_SUCCESS; + } else { + /* erase the decrypted buffer in case of mac validation failure: */ + _set(out, 0, plen - c->mlen); + return TC_CRYPTO_FAIL; + } +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cmac_mode.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cmac_mode.c new file mode 100644 index 0000000..dff28cf --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/cmac_mode.c @@ -0,0 +1,254 @@ +/* cmac_mode.c - TinyCrypt CMAC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/aes.h" +#include "../include/tinycrypt/cmac_mode.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +/* max number of calls until change the key (2^48).*/ +const static uint64_t MAX_CALLS = ((uint64_t)1 << 48); + +/* + * gf_wrap -- In our implementation, GF(2^128) is represented as a 16 byte + * array with byte 0 the most significant and byte 15 the least significant. + * High bit carry reduction is based on the primitive polynomial + * + * X^128 + X^7 + X^2 + X + 1, + * + * which leads to the reduction formula X^128 = X^7 + X^2 + X + 1. Indeed, + * since 0 = (X^128 + X^7 + X^2 + 1) mod (X^128 + X^7 + X^2 + X + 1) and since + * addition of polynomials with coefficients in Z/Z(2) is just XOR, we can + * add X^128 to both sides to get + * + * X^128 = (X^7 + X^2 + X + 1) mod (X^128 + X^7 + X^2 + X + 1) + * + * and the coefficients of the polynomial on the right hand side form the + * string 1000 0111 = 0x87, which is the value of gf_wrap. + * + * This gets used in the following way. Doubling in GF(2^128) is just a left + * shift by 1 bit, except when the most significant bit is 1. In the latter + * case, the relation X^128 = X^7 + X^2 + X + 1 says that the high order bit + * that overflows beyond 128 bits can be replaced by addition of + * X^7 + X^2 + X + 1 <--> 0x87 to the low order 128 bits. Since addition + * in GF(2^128) is represented by XOR, we therefore only have to XOR 0x87 + * into the low order byte after a left shift when the starting high order + * bit is 1. + */ +const unsigned char gf_wrap = 0x87; + +/* + * assumes: out != NULL and points to a GF(2^n) value to receive the + * doubled value; + * in != NULL and points to a 16 byte GF(2^n) value + * to double; + * the in and out buffers do not overlap. + * effects: doubles the GF(2^n) value pointed to by "in" and places + * the result in the GF(2^n) value pointed to by "out." + */ +void gf_double(uint8_t *out, uint8_t *in) +{ + + /* start with low order byte */ + uint8_t *x = in + (TC_AES_BLOCK_SIZE - 1); + + /* if msb == 1, we need to add the gf_wrap value, otherwise add 0 */ + uint8_t carry = (in[0] >> 7) ? gf_wrap : 0; + + out += (TC_AES_BLOCK_SIZE - 1); + for (;;) { + *out-- = (*x << 1) ^ carry; + if (x == in) { + break; + } + carry = *x-- >> 7; + } +} + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched) +{ + + /* input sanity check: */ + if (s == (TCCmacState_t) 0 || + key == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + /* put s into a known state */ + _set(s, 0, sizeof(*s)); + s->sched = sched; + + /* configure the encryption key used by the underlying block cipher */ + tc_aes128_set_encrypt_key(s->sched, key); + + /* compute s->K1 and s->K2 from s->iv using s->keyid */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + tc_aes_encrypt(s->iv, s->iv, s->sched); + gf_double (s->K1, s->iv); + gf_double (s->K2, s->K1); + + /* reset s->iv to 0 in case someone wants to compute now */ + tc_cmac_init(s); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_erase(TCCmacState_t s) +{ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_init(TCCmacState_t s) +{ + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* CMAC starts with an all zero initialization vector */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + + /* and the leftover buffer is empty */ + _set(s->leftover, 0, TC_AES_BLOCK_SIZE); + s->leftover_offset = 0; + + /* Set countdown to max number of calls allowed before re-keying: */ + s->countdown = MAX_CALLS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length) +{ + unsigned int i; + + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + if (data_length == 0) { + return TC_CRYPTO_SUCCESS; + } + if (data == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->countdown == 0) { + return TC_CRYPTO_FAIL; + } + + s->countdown--; + + if (s->leftover_offset > 0) { + /* last data added to s didn't end on a TC_AES_BLOCK_SIZE byte boundary */ + size_t remaining_space = TC_AES_BLOCK_SIZE - s->leftover_offset; + + if (data_length < remaining_space) { + /* still not enough data to encrypt this time either */ + _copy(&s->leftover[s->leftover_offset], data_length, data, data_length); + s->leftover_offset += data_length; + return TC_CRYPTO_SUCCESS; + } + /* leftover block is now full; encrypt it first */ + _copy(&s->leftover[s->leftover_offset], + remaining_space, + data, + remaining_space); + data_length -= remaining_space; + data += remaining_space; + s->leftover_offset = 0; + + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + } + + /* CBC encrypt each (except the last) of the data blocks */ + while (data_length > TC_AES_BLOCK_SIZE) { + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= data[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + data += TC_AES_BLOCK_SIZE; + data_length -= TC_AES_BLOCK_SIZE; + } + + if (data_length > 0) { + /* save leftover data for next time */ + _copy(s->leftover, data_length, data, data_length); + s->leftover_offset = data_length; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s) +{ + uint8_t *k; + unsigned int i; + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->leftover_offset == TC_AES_BLOCK_SIZE) { + /* the last message block is a full-sized block */ + k = (uint8_t *) s->K1; + } else { + /* the final message block is not a full-sized block */ + size_t remaining = TC_AES_BLOCK_SIZE - s->leftover_offset; + + _set(&s->leftover[s->leftover_offset], 0, remaining); + s->leftover[s->leftover_offset] = TC_CMAC_PADDING; + k = (uint8_t *) s->K2; + } + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i] ^ k[i]; + } + + tc_aes_encrypt(tag, s->iv, s->sched); + + /* erasing state: */ + tc_cmac_erase(s); + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_mode.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_mode.c new file mode 100644 index 0000000..6653afc --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_mode.c @@ -0,0 +1,85 @@ +/* ctr_mode.c - TinyCrypt CTR mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/ctr_mode.h" +#include "../include/tinycrypt/utils.h" + +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + unsigned int block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the ctr to the nonce */ + (void)_copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 4 bytes of the nonce to be incremented */ + block_num = (nonce[12] << 24) | (nonce[13] << 16) | + (nonce[14] << 8) | (nonce[15]); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + /* encrypt data using the current nonce */ + if (tc_aes_encrypt(buffer, nonce, sched)) { + block_num++; + nonce[12] = (uint8_t)(block_num >> 24); + nonce[13] = (uint8_t)(block_num >> 16); + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + } else { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i%(TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[12] = nonce[12]; ctr[13] = nonce[13]; + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_prng.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_prng.c new file mode 100644 index 0000000..f908932 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ctr_prng.c @@ -0,0 +1,283 @@ +/* ctr_prng.c - TinyCrypt implementation of CTR-PRNG */ + +/* + * Copyright (c) 2016, Chris Morrison + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/ctr_prng.h" +#include "../include/tinycrypt/utils.h" +#include "../include/tinycrypt/constants.h" +#include + +/* + * This PRNG is based on the CTR_DRBG described in Recommendation for Random + * Number Generation Using Deterministic Random Bit Generators, + * NIST SP 800-90A Rev. 1. + * + * Annotations to particular steps (e.g. 10.2.1.2 Step 1) refer to the steps + * described in that document. + * + */ + +/** + * @brief Array incrementer + * Treats the supplied array as one contiguous number (MSB in arr[0]), and + * increments it by one + * @return none + * @param arr IN/OUT -- array to be incremented + * @param len IN -- size of arr in bytes + */ +static void arrInc(uint8_t arr[], unsigned int len) +{ + unsigned int i; + if (0 != arr) { + for (i = len; i > 0U; i--) { + if (++arr[i-1] != 0U) { + break; + } + } + } +} + +/** + * @brief CTR PRNG update + * Updates the internal state of supplied the CTR PRNG context + * increments it by one + * @return none + * @note Assumes: providedData is (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes long + * @param ctx IN/OUT -- CTR PRNG state + * @param providedData IN -- data used when updating the internal state + */ +static void tc_ctr_prng_update(TCCtrPrng_t * const ctx, uint8_t const * const providedData) +{ + if (0 != ctx) { + /* 10.2.1.2 step 1 */ + uint8_t temp[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + unsigned int len = 0U; + + /* 10.2.1.2 step 2 */ + while (len < sizeof temp) { + unsigned int blocklen = sizeof(temp) - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.2 step 2.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.2 step 2.2 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.2 step 2.3/step 3 */ + memcpy(&(temp[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.2 step 4 */ + if (0 != providedData) { + unsigned int i; + for (i = 0U; i < sizeof temp; i++) { + temp[i] ^= providedData[i]; + } + } + + /* 10.2.1.2 step 5 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, temp); + + /* 10.2.1.2 step 6 */ + memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE); + } +} + +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen) +{ + int result = TC_CRYPTO_FAIL; + unsigned int i; + uint8_t personalization_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + uint8_t zeroArr[TC_AES_BLOCK_SIZE] = {0U}; + + if (0 != personalization) { + /* 10.2.1.3.1 step 1 */ + unsigned int len = pLen; + if (len > sizeof personalization_buf) { + len = sizeof personalization_buf; + } + + /* 10.2.1.3.1 step 2 */ + memcpy(personalization_buf, personalization, len); + } + + if ((0 != ctx) && (0 != entropy) && (entropyLen >= sizeof seed_material)) { + /* 10.2.1.3.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= personalization_buf[i]; + } + + /* 10.2.1.3.1 step 4 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, zeroArr); + + /* 10.2.1.3.1 step 5 */ + memset(ctx->V, 0x00, sizeof ctx->V); + + /* 10.2.1.3.1 step 6 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.3.1 step 7 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen) +{ + unsigned int i; + int result = TC_CRYPTO_FAIL; + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + + if (0 != additional_input) { + /* 10.2.1.4.1 step 1 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + + /* 10.2.1.4.1 step 2 */ + memcpy(additional_input_buf, additional_input, len); + } + + unsigned int seedlen = (unsigned int)TC_AES_KEY_SIZE + (unsigned int)TC_AES_BLOCK_SIZE; + if ((0 != ctx) && (entropyLen >= seedlen)) { + /* 10.2.1.4.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= additional_input_buf[i]; + } + + /* 10.2.1.4.1 step 4 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.4.1 step 5 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen) +{ + /* 2^48 - see section 10.2.1 */ + static const uint64_t MAX_REQS_BEFORE_RESEED = 0x1000000000000ULL; + + /* 2^19 bits - see section 10.2.1 */ + static const unsigned int MAX_BYTES_PER_REQ = 65536U; + + unsigned int result = TC_CRYPTO_FAIL; + + if ((0 != ctx) && (0 != out) && (outlen < MAX_BYTES_PER_REQ)) { + /* 10.2.1.5.1 step 1 */ + if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) { + result = TC_CTR_PRNG_RESEED_REQ; + } else { + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + if (0 != additional_input) { + /* 10.2.1.5.1 step 2 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + memcpy(additional_input_buf, additional_input, len); + tc_ctr_prng_update(ctx, additional_input_buf); + } + + /* 10.2.1.5.1 step 3 - implicit */ + + /* 10.2.1.5.1 step 4 */ + unsigned int len = 0U; + while (len < outlen) { + unsigned int blocklen = outlen - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.5.1 step 4.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.5.1 step 4.2 */ + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.5.1 step 4.3/step 5 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + memcpy(&(out[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.5.1 step 6 */ + tc_ctr_prng_update(ctx, additional_input_buf); + + /* 10.2.1.5.1 step 7 */ + ctx->reseedCount++; + + /* 10.2.1.5.1 step 8 */ + result = TC_CRYPTO_SUCCESS; + } + } + + return result; +} + +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx) +{ + if (0 != ctx) { + memset(ctx->key.words, 0x00, sizeof ctx->key.words); + memset(ctx->V, 0x00, sizeof ctx->V); + ctx->reseedCount = 0U; + } +} + + + + diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc.c new file mode 100644 index 0000000..e7fce27 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc.c @@ -0,0 +1,942 @@ +/* ecc.c - TinyCrypt implementation of common ECC functions */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/ecc.h" +#include "../include/tinycrypt/ecc_platform_specific.h" +#include + +/* IMPORTANT: Make sure a cryptographically-secure PRNG is set and the platform + * has access to enough entropy in order to feed the PRNG regularly. */ +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) +{ + g_rng_function = rng_function; +} + +uECC_RNG_Function uECC_get_rng(void) +{ + return g_rng_function; +} + +int uECC_curve_private_key_size(uECC_Curve curve) +{ + return BITS_TO_BYTES(curve->num_n_bits); +} + +int uECC_curve_public_key_size(uECC_Curve curve) +{ + return 2 * curve->num_bytes; +} + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) +{ + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} + +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) +{ + return (vli[bit >> uECC_WORD_BITS_SHIFT] & + ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + wordcount_t i; + /* Search from the end until we find a non-zero digit. We do it in reverse + * because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} + +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + + uECC_word_t diff = 0; + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return !(diff == 0); +} + +uECC_word_t cond_set(uECC_word_t p_true, uECC_word_t p_false, unsigned int cond) +{ + return (p_true*(cond)) | (p_false*(!cond)); +} + +/* Computes result = left - right, returning borrow, in constant time. + * Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + uECC_word_t val = (diff > left[i]); + borrow = cond_set(val, borrow, (diff != left[i])); + + result[i] = diff; + } + return borrow; +} + +/* Computes result = left + right, returning carry, in constant time. + * Can modify in place. */ +static uECC_word_t uECC_vli_add(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + uECC_word_t val = (sum < left[i]); + carry = cond_set(val, carry, (sum != left[i])); + result[i] = sum; + } + return carry; +} + +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +static void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} + +static void muladd(uECC_word_t a, uECC_word_t b, uECC_word_t *r0, + uECC_word_t *r1, uECC_word_t *r2) +{ + + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; + +} + +/* Computes result = left * right. Result must be 2 * num_words long. */ +static void uECC_vli_mult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + for (k = num_words; k < num_words * 2 - 1; ++k) { + + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} + +void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get + * remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + * we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t mod_multiple[2 * NUM_ECC_WORDS]; + uECC_word_t tmp[2 * NUM_ECC_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - + uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + /* Swap the index if there was no borrow */ + index = !(index ^ borrow); + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << + (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + + curve->mmod_fast(result, product); +} + +static void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) +{ + uECC_vli_modMult_fast(result, left, left, curve); +} + + +#define EVEN(vli) (!(vli[0] & 1)) + +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) +{ + + uECC_word_t carry = 0; + + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t a[NUM_ECC_WORDS], b[NUM_ECC_WORDS]; + uECC_word_t u[NUM_ECC_WORDS], v[NUM_ECC_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve) +{ + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[NUM_ECC_WORDS]; + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + + /* t1 = 3/2*(x1^2 - z1^4) = B */ + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + /* t4 = B * (A - x3) - y1^4 = y3: */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +void x_side_default(uECC_word_t *result, + const uECC_word_t *x, + uECC_Curve curve) +{ + uECC_word_t _3[NUM_ECC_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + /* r = x^3 - 3x + b: */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); +} + +uECC_Curve uECC_secp256r1(void) +{ + return &curve_secp256r1; +} + +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int*product) +{ + unsigned int tmp[NUM_ECC_WORDS]; + int carry; + + /* t */ + uECC_vli_set(result, product, NUM_ECC_WORDS); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + while (carry < 0); + } else { + while (carry || + uECC_vli_cmp_unsafe(curve_secp256r1.p, result, NUM_ECC_WORDS) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + } +} + +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve) +{ + return uECC_vli_isZero(point, curve->num_words * 2); +} + +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve) +{ + uECC_word_t t1[NUM_ECC_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) +{ + uECC_word_t z[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + or P => P - Q, Q => P + Q + */ +static void XYcZ_addC(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + uECC_word_t t6[NUM_ECC_WORDS]; + uECC_word_t t7[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + /* t4 = (y2 - y1)*(B - x3) - E = y3: */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + /* t2 = (y2+y1)*(x3' - B) - E = y3': */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); + + uECC_vli_set(X1, t7, num_words); +} + +void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, uECC_Curve curve) +{ + /* R0 and R1 */ + uECC_word_t Rx[2][NUM_ECC_WORDS]; + uECC_word_t Ry[2][NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0))*/ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + /* Xb * yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve) +{ + + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + bitcount_t num_n_bits = curve->num_n_bits; + + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + + uECC_vli_add(k1, k0, curve->n, num_n_words); + + return carry; +} + +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) +{ + + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. */ + carry = regularize_k(private_key, tmp1, tmp2, curve); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native) +{ + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes) +{ + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words) +{ + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= + mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) +{ + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return -1; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return -2; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + if (uECC_vli_equal(tmp1, tmp2, num_words) != 0) + return -3; + + return 0; +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, + public_key + curve->num_bytes, + curve->num_bytes); + + if (uECC_vli_cmp_unsafe(_public, curve->G, NUM_ECC_WORDS * 2) == 0) { + return -4; + } + + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, + uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative( + _private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(_public, _private, curve)) { + return 0; + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + + curve->num_bytes, curve->num_bytes, _public + curve->num_words); + return 1; +} + + + diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dh.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dh.c new file mode 100644 index 0000000..5cd6a2e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dh.c @@ -0,0 +1,200 @@ +/* ec_dh.c - TinyCrypt implementation of EC-DH */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/ecc.h" +#include "../include/tinycrypt/ecc_dh.h" +#include + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key, + unsigned int *d, uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + /* This function is designed for test purposes-only (such as validating NIST + * test vectors) as it uses a provided value for d instead of generating + * it uniformly at random. */ + memcpy (_private, d, NUM_ECC_BYTES); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer used to store secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + return 1; + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve) +{ + + uECC_word_t _random[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _private uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2 * NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + /* computing modular reduction of _random (see FIPS 186.4 B.4.1): */ + uECC_vli_mmod(_private, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer that stored secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key, + uint8_t *secret, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {_private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + int r; + + /* Converting buffers to correct bit order: */ + uECC_vli_bytesToNative(_private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(_public, + public_key, + num_bytes); + uECC_vli_bytesToNative(_public + num_words, + public_key + num_bytes, + num_bytes); + + /* Regularize the bitcount for the private key so that attackers cannot use a + * side channel attack to learn the number of leading zeros. */ + carry = regularize_k(_private, _private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to + * improve protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + r = 0; + goto clear_and_out; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, + curve); + + uECC_vli_nativeToBytes(secret, num_bytes, _public); + r = !EccPoint_isZero(_public, curve); + +clear_and_out: + /* erasing temporary buffer used to store secret: */ + memset(p2, 0, sizeof(p2)); + __asm__ __volatile__("" :: "g"(p2) : "memory"); + memset(tmp, 0, sizeof(tmp)); + __asm__ __volatile__("" :: "g"(tmp) : "memory"); + memset(_private, 0, sizeof(_private)); + __asm__ __volatile__("" :: "g"(_private) : "memory"); + + return r; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dsa.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dsa.c new file mode 100644 index 0000000..31046c8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_dsa.c @@ -0,0 +1,295 @@ +/* ec_dsa.c - TinyCrypt implementation of EC-DSA */ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/ecc.h" +#include "../include/tinycrypt/ecc_dsa.h" + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +static void bits2int(uECC_word_t *native, const uint8_t *bits, + unsigned bits_size, uECC_Curve curve) +{ + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + + uECC_vli_clear(native, num_n_words); + uECC_vli_bytesToNative(native, bits, bits_size); + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t s[NUM_ECC_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t p[NUM_ECC_WORDS * 2]; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || + uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } + else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + + /* tmp = d: */ + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); + + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } + + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + return 1; +} + +int uECC_sign(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uint8_t *signature, uECC_Curve curve) +{ + uECC_word_t _random[2*NUM_ECC_WORDS]; + uECC_word_t k[NUM_ECC_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _random uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2*NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + // computing k as modular reduction of _random (see FIPS 186.4 B.5.1): + uECC_vli_mmod(k, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, + curve)) { + return 1; + } + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) +{ + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, const uint8_t *message_hash, + unsigned hash_size, const uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t u1[NUM_ECC_WORDS], u2[NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + uECC_word_t sum[NUM_ECC_WORDS * 2]; + uECC_word_t rx[NUM_ECC_WORDS]; + uECC_word_t ry[NUM_ECC_WORDS]; + uECC_word_t tx[NUM_ECC_WORDS]; + uECC_word_t ty[NUM_ECC_WORDS]; + uECC_word_t tz[NUM_ECC_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t r[NUM_ECC_WORDS], s[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, + curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, _public, num_words); + uECC_vli_set(sum + num_words, _public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + XYcZ_add(tx, ty, sum, sum + num_words, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = _public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words) == 0); +} + diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_platform_specific.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_platform_specific.c new file mode 100644 index 0000000..1867988 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/ecc_platform_specific.c @@ -0,0 +1,105 @@ +/* uECC_platform_specific.c - Implementation of platform specific functions*/ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * uECC_platform_specific.c -- Implementation of platform specific functions + */ + + +#if defined(unix) || defined(__linux__) || defined(__unix__) || \ + defined(__unix) | (defined(__APPLE__) && defined(__MACH__)) || \ + defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#include + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int default_CSPRNG(uint8_t *dest, unsigned int size) { + + /* input sanity check: */ + if (dest == (uint8_t *) 0 || (size <= 0)) + return 0; + + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = (size_t) size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { // read failed + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} + +#endif /* platform */ + diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac.c new file mode 100644 index 0000000..c86fd35 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac.c @@ -0,0 +1,148 @@ +/* hmac.c - TinyCrypt implementation of the HMAC algorithm */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/hmac.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +static void rekey(uint8_t *key, const uint8_t *new_key, unsigned int key_size) +{ + const uint8_t inner_pad = (uint8_t) 0x36; + const uint8_t outer_pad = (uint8_t) 0x5c; + unsigned int i; + + for (i = 0; i < key_size; ++i) { + key[i] = inner_pad ^ new_key[i]; + key[i + TC_SHA256_BLOCK_SIZE] = outer_pad ^ new_key[i]; + } + for (; i < TC_SHA256_BLOCK_SIZE; ++i) { + key[i] = inner_pad; key[i + TC_SHA256_BLOCK_SIZE] = outer_pad; + } +} + +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0 || + key == (const uint8_t *) 0 || + key_size == 0) { + return TC_CRYPTO_FAIL; + } + + const uint8_t dummy_key[key_size]; + struct tc_hmac_state_struct dummy_state; + + if (key_size <= TC_SHA256_BLOCK_SIZE) { + /* + * The next three lines consist of dummy calls just to avoid + * certain timing attacks. Without these dummy calls, + * adversaries would be able to learn whether the key_size is + * greater than TC_SHA256_BLOCK_SIZE by measuring the time + * consumed in this process. + */ + (void)tc_sha256_init(&dummy_state.hash_state); + (void)tc_sha256_update(&dummy_state.hash_state, + dummy_key, + key_size); + (void)tc_sha256_final(&dummy_state.key[TC_SHA256_DIGEST_SIZE], + &dummy_state.hash_state); + + /* Actual code for when key_size <= TC_SHA256_BLOCK_SIZE: */ + rekey(ctx->key, key, key_size); + } else { + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, key, key_size); + (void)tc_sha256_final(&ctx->key[TC_SHA256_DIGEST_SIZE], + &ctx->hash_state); + rekey(ctx->key, + &ctx->key[TC_SHA256_DIGEST_SIZE], + TC_SHA256_DIGEST_SIZE); + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_init(TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_init(&ctx->hash_state); + (void) tc_sha256_update(&ctx->hash_state, ctx->key, TC_SHA256_BLOCK_SIZE); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_update(TCHmacState_t ctx, + const void *data, + unsigned int data_length) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)tc_sha256_update(&ctx->hash_state, data, data_length); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + taglen != TC_SHA256_DIGEST_SIZE || + ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_final(tag, &ctx->hash_state); + + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, + &ctx->key[TC_SHA256_BLOCK_SIZE], + TC_SHA256_BLOCK_SIZE); + (void)tc_sha256_update(&ctx->hash_state, tag, TC_SHA256_DIGEST_SIZE); + (void)tc_sha256_final(tag, &ctx->hash_state); + + /* destroy the current state */ + _set(ctx, 0, sizeof(*ctx)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac_prng.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac_prng.c new file mode 100644 index 0000000..a41ea43 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/hmac_prng.c @@ -0,0 +1,212 @@ +/* hmac_prng.c - TinyCrypt implementation of HMAC-PRNG */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/hmac_prng.h" +#include "../include/tinycrypt/hmac.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +/* + * min bytes in the seed string. + * MIN_SLEN*8 must be at least the expected security level. + */ +static const unsigned int MIN_SLEN = 32; + +/* + * max bytes in the seed string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_SLEN = UINT32_MAX; + +/* + * max bytes in the personalization string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_PLEN = UINT32_MAX; + +/* + * max bytes in the additional_info string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_ALEN = UINT32_MAX; + +/* + * max number of generates between re-seeds; + * TinyCrypt accepts up to (2^32 - 1) which is the maximal value of + * a 32-bit unsigned int variable, while SP800-90A specifies a maximum of 2^48. + */ +static const unsigned int MAX_GENS = UINT32_MAX; + +/* + * maximum bytes per generate call; + * SP800-90A specifies a maximum up to 2^19. + */ +static const unsigned int MAX_OUT = (1 << 19); + +/* + * Assumes: prng != NULL, e != NULL, len >= 0. + */ +static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len) +{ + const uint8_t separator0 = 0x00; + const uint8_t separator1 = 0x01; + + /* use current state, e and separator 0 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator0, sizeof(separator0)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + /* use current state, e and separator 1 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); +} + +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + personalization == (uint8_t *) 0 || + plen > MAX_PLEN) { + return TC_CRYPTO_FAIL; + } + + /* put the generator into a known state: */ + _set(prng->key, 0x00, sizeof(prng->key)); + _set(prng->v, 0x01, sizeof(prng->v)); + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + /* update assumes SOME key has been configured into HMAC */ + + update(prng, personalization, plen); + + /* force a reseed before allowing tc_hmac_prng_generate to succeed: */ + prng->countdown = 0; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_reseed(TCHmacPrng_t prng, + const uint8_t *seed, + unsigned int seedlen, + const uint8_t *additional_input, + unsigned int additionallen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + seed == (const uint8_t *) 0 || + seedlen < MIN_SLEN || + seedlen > MAX_SLEN) { + return TC_CRYPTO_FAIL; + } + + if (additional_input != (const uint8_t *) 0) { + /* + * Abort if additional_input is provided but has inappropriate + * length + */ + if (additionallen == 0 || + additionallen > MAX_ALEN) { + return TC_CRYPTO_FAIL; + } else { + /* call update for the seed and additional_input */ + update(prng, seed, seedlen); + update(prng, additional_input, additionallen); + } + } else { + /* call update only for the seed */ + update(prng, seed, seedlen); + } + + /* ... and enable hmac_prng_generate */ + prng->countdown = MAX_GENS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng) +{ + unsigned int bufferlen; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + prng == (TCHmacPrng_t) 0 || + outlen == 0 || + outlen > MAX_OUT) { + return TC_CRYPTO_FAIL; + } else if (prng->countdown == 0) { + return TC_HMAC_PRNG_RESEED_REQ; + } + + prng->countdown--; + + while (outlen != 0) { + /* operate HMAC in OFB mode to create "random" outputs */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + bufferlen = (TC_SHA256_DIGEST_SIZE > outlen) ? + outlen : TC_SHA256_DIGEST_SIZE; + (void)_copy(out, bufferlen, prng->v, bufferlen); + + out += bufferlen; + outlen = (outlen > TC_SHA256_DIGEST_SIZE) ? + (outlen - TC_SHA256_DIGEST_SIZE) : 0; + } + + /* block future PRNG compromises from revealing past state */ + update(prng, prng->v, TC_SHA256_DIGEST_SIZE); + + return TC_CRYPTO_SUCCESS; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/sha256.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/sha256.c new file mode 100644 index 0000000..b7e0bba --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/sha256.c @@ -0,0 +1,217 @@ +/* sha256.c - TinyCrypt SHA-256 crypto hash algorithm implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/sha256.h" +#include "../include/tinycrypt/constants.h" +#include "../include/tinycrypt/utils.h" + +static void compress(unsigned int *iv, const uint8_t *data); + +int tc_sha256_init(TCSha256State_t s) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* + * Setting the initial state values. + * These values correspond to the first 32 bits of the fractional parts + * of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17 + * and 19. + */ + _set((uint8_t *) s, 0x00, sizeof(*s)); + s->iv[0] = 0x6a09e667; + s->iv[1] = 0xbb67ae85; + s->iv[2] = 0x3c6ef372; + s->iv[3] = 0xa54ff53a; + s->iv[4] = 0x510e527f; + s->iv[5] = 0x9b05688c; + s->iv[6] = 0x1f83d9ab; + s->iv[7] = 0x5be0cd19; + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_update(TCSha256State_t s, const uint8_t *data, size_t datalen) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0 || + data == (void *) 0) { + return TC_CRYPTO_FAIL; + } else if (datalen == 0) { + return TC_CRYPTO_SUCCESS; + } + + while (datalen-- > 0) { + s->leftover[s->leftover_offset++] = *(data++); + if (s->leftover_offset >= TC_SHA256_BLOCK_SIZE) { + compress(s->iv, s->leftover); + s->leftover_offset = 0; + s->bits_hashed += (TC_SHA256_BLOCK_SIZE << 3); + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_final(uint8_t *digest, TCSha256State_t s) +{ + unsigned int i; + + /* input sanity check: */ + if (digest == (uint8_t *) 0 || + s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + s->bits_hashed += (s->leftover_offset << 3); + + s->leftover[s->leftover_offset++] = 0x80; /* always room for one byte */ + if (s->leftover_offset > (sizeof(s->leftover) - 8)) { + /* there is not room for all the padding in this block */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - s->leftover_offset); + compress(s->iv, s->leftover); + s->leftover_offset = 0; + } + + /* add the padding and the length in big-Endian format */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - 8 - s->leftover_offset); + s->leftover[sizeof(s->leftover) - 1] = (uint8_t)(s->bits_hashed); + s->leftover[sizeof(s->leftover) - 2] = (uint8_t)(s->bits_hashed >> 8); + s->leftover[sizeof(s->leftover) - 3] = (uint8_t)(s->bits_hashed >> 16); + s->leftover[sizeof(s->leftover) - 4] = (uint8_t)(s->bits_hashed >> 24); + s->leftover[sizeof(s->leftover) - 5] = (uint8_t)(s->bits_hashed >> 32); + s->leftover[sizeof(s->leftover) - 6] = (uint8_t)(s->bits_hashed >> 40); + s->leftover[sizeof(s->leftover) - 7] = (uint8_t)(s->bits_hashed >> 48); + s->leftover[sizeof(s->leftover) - 8] = (uint8_t)(s->bits_hashed >> 56); + + /* hash the padding and length */ + compress(s->iv, s->leftover); + + /* copy the iv out to digest */ + for (i = 0; i < TC_SHA256_STATE_BLOCKS; ++i) { + unsigned int t = *((unsigned int *) &s->iv[i]); + *digest++ = (uint8_t)(t >> 24); + *digest++ = (uint8_t)(t >> 16); + *digest++ = (uint8_t)(t >> 8); + *digest++ = (uint8_t)(t); + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +/* + * Initializing SHA-256 Hash constant words K. + * These values correspond to the first 32 bits of the fractional parts of the + * cube roots of the first 64 primes between 2 and 311. + */ +static const unsigned int k256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static inline unsigned int ROTR(unsigned int a, unsigned int n) +{ + return (((a) >> n) | ((a) << (32 - n))); +} + +#define Sigma0(a)(ROTR((a), 2) ^ ROTR((a), 13) ^ ROTR((a), 22)) +#define Sigma1(a)(ROTR((a), 6) ^ ROTR((a), 11) ^ ROTR((a), 25)) +#define sigma0(a)(ROTR((a), 7) ^ ROTR((a), 18) ^ ((a) >> 3)) +#define sigma1(a)(ROTR((a), 17) ^ ROTR((a), 19) ^ ((a) >> 10)) + +#define Ch(a, b, c)(((a) & (b)) ^ ((~(a)) & (c))) +#define Maj(a, b, c)(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +static inline unsigned int BigEndian(const uint8_t **c) +{ + unsigned int n = 0; + + n = (((unsigned int)(*((*c)++))) << 24); + n |= ((unsigned int)(*((*c)++)) << 16); + n |= ((unsigned int)(*((*c)++)) << 8); + n |= ((unsigned int)(*((*c)++))); + return n; +} + +static void compress(unsigned int *iv, const uint8_t *data) +{ + unsigned int a, b, c, d, e, f, g, h; + unsigned int s0, s1; + unsigned int t1, t2; + unsigned int work_space[16]; + unsigned int n; + unsigned int i; + + a = iv[0]; b = iv[1]; c = iv[2]; d = iv[3]; + e = iv[4]; f = iv[5]; g = iv[6]; h = iv[7]; + + for (i = 0; i < 16; ++i) { + n = BigEndian(&data); + t1 = work_space[i] = n; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + for ( ; i < 64; ++i) { + s0 = work_space[(i+1)&0x0f]; + s0 = sigma0(s0); + s1 = work_space[(i+14)&0x0f]; + s1 = sigma1(s1); + + t1 = work_space[i&0xf] += s0 + s1 + work_space[(i+9)&0xf]; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + iv[0] += a; iv[1] += b; iv[2] += c; iv[3] += d; + iv[4] += e; iv[5] += f; iv[6] += g; iv[7] += h; +} diff --git a/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/utils.c b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/utils.c new file mode 100644 index 0000000..792a781 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/ext/tinycrypt/src/utils.c @@ -0,0 +1,74 @@ +/* utils.c - TinyCrypt platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../include/tinycrypt/utils.h" +#include "../include/tinycrypt/constants.h" + +#include + +#define MASK_TWENTY_SEVEN 0x1b + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_hw.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_hw.h new file mode 100644 index 0000000..dfac69b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_hw.h @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HW_ +#define H_BLE_HW_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#if defined(ARCH_sim) +#define BLE_USES_HW_WHITELIST (0) +#else +#define BLE_USES_HW_WHITELIST MYNEWT_VAL(BLE_HW_WHITELIST_ENABLE) +#endif + +/* Returns the number of hw whitelist elements */ +uint8_t ble_hw_whitelist_size(void); + +/* Clear the whitelist */ +void ble_hw_whitelist_clear(void); + +/* Remove a device from the hw whitelist */ +void ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type); + +/* Add a device to the hw whitelist */ +int ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type); + +/* Enable hw whitelisting */ +void ble_hw_whitelist_enable(void); + +/* Enable hw whitelisting */ +void ble_hw_whitelist_disable(void); + +/* Boolean function returning true if address matches a whitelist entry */ +int ble_hw_whitelist_match(void); + +/* Encrypt data */ +struct ble_encryption_block; +int ble_hw_encrypt_block(struct ble_encryption_block *ecb); + +/* Random number generation */ +typedef void (*ble_rng_isr_cb_t)(uint8_t rnum); +int ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias); + +/** + * Start the random number generator + * + * @return int + */ +int ble_hw_rng_start(void); + +/** + * Stop the random generator + * + * @return int + */ +int ble_hw_rng_stop(void); + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t ble_hw_rng_read(void); + +/* Clear the resolving list*/ +void ble_hw_resolv_list_clear(void); + +/* Add a device to the hw resolving list */ +int ble_hw_resolv_list_add(uint8_t *irk); + +/* Remove a device from the hw resolving list */ +void ble_hw_resolv_list_rmv(int index); + +/* Returns the size of the whitelist in HW */ +uint8_t ble_hw_resolv_list_size(void); + +/* Enable the resolving list */ +void ble_hw_resolv_list_enable(void); + +/* Disables resolving list devices */ +void ble_hw_resolv_list_disable(void); + +/* Returns index of resolved address; -1 if not resolved */ +int ble_hw_resolv_list_match(void); + +/* Returns public device address or -1 if not present */ +int ble_hw_get_public_addr(ble_addr_t *addr); + +/* Returns random static address or -1 if not present */ +int ble_hw_get_static_addr(ble_addr_t *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_HW_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll.h new file mode 100644 index 0000000..77b85e1 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll.h @@ -0,0 +1,584 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_ +#define H_BLE_LL_ + +#include "nimble/porting/nimble/include/stats/stats.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" +#include "ble_phy.h" + +#ifdef MYNEWT +#include "./ble_ll_ctrl.h" +#include "hal/hal_system.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 +#error 32.768kHz clock required +#endif + +#if defined(MYNEWT) && MYNEWT_VAL(BLE_LL_VND_EVENT_ON_ASSERT) +#ifdef NDEBUG +#define BLE_LL_ASSERT(cond) (void(0)) +#else +#define BLE_LL_ASSERT(cond) \ + if (!(cond)) { \ + if (hal_debugger_connected()) { \ + assert(0);\ + } else {\ + ble_ll_hci_ev_send_vendor_err(__FILE__, __LINE__); \ + while(1) {}\ + }\ + } +#endif +#else +#define BLE_LL_ASSERT(cond) assert(cond) +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#define BLE_LL_BT5_PHY_SUPPORTED (1) +#else +#define BLE_LL_BT5_PHY_SUPPORTED (0) +#endif + +/* Controller revision. */ +#define BLE_LL_SUB_VERS_NR (0x0000) + +/* Timing jitter as per spec is +/16 usecs */ +#define BLE_LL_JITTER_USECS (16) + +/* Packet queue header definition */ +STAILQ_HEAD(ble_ll_pkt_q, os_mbuf_pkthdr); + +/* + * Global Link Layer data object. There is only one Link Layer data object + * per controller although there may be many instances of the link layer state + * machine running. + */ +struct ble_ll_obj +{ + /* Supported features */ + uint64_t ll_supp_features; + + /* Current Link Layer state */ + uint8_t ll_state; + + /* Number of ACL data packets supported */ + uint8_t ll_num_acl_pkts; + + /* ACL data packet size */ + uint16_t ll_acl_pkt_size; + + /* Preferred PHY's */ + uint8_t ll_pref_tx_phys; + uint8_t ll_pref_rx_phys; + + /* Task event queue */ + struct ble_npl_eventq ll_evq; + + /* Wait for response timer */ + struct hal_timer ll_wfr_timer; + + /* Packet receive queue (and event). Holds received packets from PHY */ + struct ble_npl_event ll_rx_pkt_ev; + struct ble_ll_pkt_q ll_rx_pkt_q; + + /* Packet transmit queue */ + struct ble_npl_event ll_tx_pkt_ev; + struct ble_ll_pkt_q ll_tx_pkt_q; + + /* Data buffer overflow event */ + struct ble_npl_event ll_dbuf_overflow_ev; + + /* Number of completed packets event */ + struct ble_npl_event ll_comp_pkt_ev; + + /* HW error callout */ + struct ble_npl_callout ll_hw_err_timer; +}; +extern struct ble_ll_obj g_ble_ll_data; + +/* Link layer statistics */ +STATS_SECT_START(ble_ll_stats) + STATS_SECT_ENTRY(hci_cmds) + STATS_SECT_ENTRY(hci_cmd_errs) + STATS_SECT_ENTRY(hci_events_sent) + STATS_SECT_ENTRY(bad_ll_state) + STATS_SECT_ENTRY(bad_acl_hdr) + STATS_SECT_ENTRY(no_bufs) + STATS_SECT_ENTRY(rx_adv_pdu_crc_ok) + STATS_SECT_ENTRY(rx_adv_pdu_crc_err) + STATS_SECT_ENTRY(rx_adv_bytes_crc_ok) + STATS_SECT_ENTRY(rx_adv_bytes_crc_err) + STATS_SECT_ENTRY(rx_data_pdu_crc_ok) + STATS_SECT_ENTRY(rx_data_pdu_crc_err) + STATS_SECT_ENTRY(rx_data_bytes_crc_ok) + STATS_SECT_ENTRY(rx_data_bytes_crc_err) + STATS_SECT_ENTRY(rx_adv_malformed_pkts) + STATS_SECT_ENTRY(rx_adv_ind) + STATS_SECT_ENTRY(rx_adv_direct_ind) + STATS_SECT_ENTRY(rx_adv_nonconn_ind) + STATS_SECT_ENTRY(rx_adv_ext_ind) + STATS_SECT_ENTRY(rx_scan_reqs) + STATS_SECT_ENTRY(rx_scan_rsps) + STATS_SECT_ENTRY(rx_connect_reqs) + STATS_SECT_ENTRY(rx_scan_ind) + STATS_SECT_ENTRY(rx_aux_connect_rsp) + STATS_SECT_ENTRY(adv_txg) + STATS_SECT_ENTRY(adv_late_starts) + STATS_SECT_ENTRY(adv_resched_pdu_fail) + STATS_SECT_ENTRY(adv_drop_event) + STATS_SECT_ENTRY(sched_state_conn_errs) + STATS_SECT_ENTRY(sched_state_adv_errs) + STATS_SECT_ENTRY(scan_starts) + STATS_SECT_ENTRY(scan_stops) + STATS_SECT_ENTRY(scan_req_txf) + STATS_SECT_ENTRY(scan_req_txg) + STATS_SECT_ENTRY(scan_rsp_txg) + STATS_SECT_ENTRY(aux_missed_adv) + STATS_SECT_ENTRY(aux_scheduled) + STATS_SECT_ENTRY(aux_received) + STATS_SECT_ENTRY(aux_fired_for_read) + STATS_SECT_ENTRY(aux_allocated) + STATS_SECT_ENTRY(aux_freed) + STATS_SECT_ENTRY(aux_sched_cb) + STATS_SECT_ENTRY(aux_conn_req_tx) + STATS_SECT_ENTRY(aux_conn_rsp_tx) + STATS_SECT_ENTRY(aux_conn_rsp_err) + STATS_SECT_ENTRY(aux_scan_req_tx) + STATS_SECT_ENTRY(aux_scan_rsp_err) + STATS_SECT_ENTRY(aux_chain_cnt) + STATS_SECT_ENTRY(aux_chain_err) + STATS_SECT_ENTRY(aux_scan_drop) + STATS_SECT_ENTRY(adv_evt_dropped) + STATS_SECT_ENTRY(scan_timer_stopped) + STATS_SECT_ENTRY(scan_timer_restarted) + STATS_SECT_ENTRY(periodic_adv_drop_event) + STATS_SECT_ENTRY(periodic_chain_drop_event) + STATS_SECT_ENTRY(sync_event_failed) + STATS_SECT_ENTRY(sync_received) + STATS_SECT_ENTRY(sync_chain_failed) + STATS_SECT_ENTRY(sync_missed_err) + STATS_SECT_ENTRY(sync_crc_err) + STATS_SECT_ENTRY(sync_rx_buf_err) + STATS_SECT_ENTRY(sync_scheduled) + STATS_SECT_ENTRY(sched_state_sync_errs) + STATS_SECT_ENTRY(sched_invalid_pdu) +STATS_SECT_END +extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats; + +/* States */ +#define BLE_LL_STATE_STANDBY (0) +#define BLE_LL_STATE_ADV (1) +#define BLE_LL_STATE_SCANNING (2) +#define BLE_LL_STATE_INITIATING (3) +#define BLE_LL_STATE_CONNECTION (4) +#define BLE_LL_STATE_DTM (5) +#define BLE_LL_STATE_SYNC (6) + +/* LL Features */ +#define BLE_LL_FEAT_LE_ENCRYPTION (0x0000000001) +#define BLE_LL_FEAT_CONN_PARM_REQ (0x0000000002) +#define BLE_LL_FEAT_EXTENDED_REJ (0x0000000004) +#define BLE_LL_FEAT_SLAVE_INIT (0x0000000008) +#define BLE_LL_FEAT_LE_PING (0x0000000010) +#define BLE_LL_FEAT_DATA_LEN_EXT (0x0000000020) +#define BLE_LL_FEAT_LL_PRIVACY (0x0000000040) +#define BLE_LL_FEAT_EXT_SCAN_FILT (0x0000000080) +#define BLE_LL_FEAT_LE_2M_PHY (0x0000000100) +#define BLE_LL_FEAT_STABLE_MOD_ID_TX (0x0000000200) +#define BLE_LL_FEAT_STABLE_MOD_ID_RX (0x0000000400) +#define BLE_LL_FEAT_LE_CODED_PHY (0x0000000800) +#define BLE_LL_FEAT_EXT_ADV (0x0000001000) +#define BLE_LL_FEAT_PERIODIC_ADV (0x0000002000) +#define BLE_LL_FEAT_CSA2 (0x0000004000) +#define BLE_LL_FEAT_LE_POWER_CLASS_1 (0x0000008000) +#define BLE_LL_FEAT_MIN_USED_CHAN (0x0000010000) +#define BLE_LL_FEAT_CTE_REQ (0x0000020000) +#define BLE_LL_FEAT_CTE_RSP (0x0000040000) +#define BLE_LL_FEAT_CTE_TX (0x0000080000) +#define BLE_LL_FEAT_CTE_RX (0x0000100000) +#define BLE_LL_FEAT_CTE_AOD (0x0000200000) +#define BLE_LL_FEAT_CTE_AOA (0x0000400000) +#define BLE_LL_FEAT_CTE_RECV (0x0000800000) +#define BLE_LL_FEAT_SYNC_TRANS_SEND (0x0001000000) +#define BLE_LL_FEAT_SYNC_TRANS_RECV (0x0002000000) +#define BLE_LL_FEAT_SCA_UPDATE (0x0004000000) +#define BLE_LL_FEAT_REM_PKEY (0x0008000000) +#define BLE_LL_FEAT_CIS_MASTER (0x0010000000) +#define BLE_LL_FEAT_CIS_SLAVE (0x0020000000) +#define BLE_LL_FEAT_ISO_BROADCASTER (0x0040000000) +#define BLE_LL_FEAT_SYNC_RECV (0x0080000000) +#define BLE_LL_FEAT_ISO_HOST_SUPPORT (0x0100000000) +#define BLE_LL_FEAT_POWER_CTRL_REQ (0x0200000000) +#define BLE_LL_FEAT_POWER_CHANGE_IND (0x0400000000) +#define BLE_LL_FEAT_PATH_LOSS_MON (0x0800000000) + +/* This is initial mask, so if feature exchange will not happen, + * but host will want to use this procedure, we will try. If not + * succeed, feature bit will be cleared. + * Look at LL Features above to find out what is allowed + */ +#define BLE_LL_CONN_INITIAL_FEATURES (0x00000022) +#define BLE_LL_CONN_CLEAR_FEATURE(connsm, feature) (connsm->conn_features &= ~(feature)) + +/* All the features which can be controlled by the Host */ +#define BLE_LL_HOST_CONTROLLED_FEATURES (BLE_LL_FEAT_ISO_HOST_SUPPORT) + +/* LL timing */ +#define BLE_LL_IFS (150) /* usecs */ +#define BLE_LL_MAFS (300) /* usecs */ + +/* + * BLE LL device address. Note that element 0 of the array is the LSB and + * is sent over the air first. Byte 5 is the MSB and is the last one sent over + * the air. + */ +#define BLE_DEV_ADDR_LEN (6) /* bytes */ + +struct ble_dev_addr +{ + uint8_t u8[BLE_DEV_ADDR_LEN]; +}; + +#define BLE_IS_DEV_ADDR_STATIC(addr) ((addr->u8[5] & 0xc0) == 0xc0) +#define BLE_IS_DEV_ADDR_RESOLVABLE(addr) ((addr->u8[5] & 0xc0) == 0x40) +#define BLE_IS_DEV_ADDR_UNRESOLVABLE(addr) ((addr->u8[5] & 0xc0) == 0x00) + +/* + * LL packet format + * + * -> Preamble (1/2 bytes) + * -> Access Address (4 bytes) + * -> PDU (2 to 257 octets) + * -> CRC (3 bytes) + */ +#define BLE_LL_PREAMBLE_LEN (1) +#define BLE_LL_ACC_ADDR_LEN (4) +#define BLE_LL_CRC_LEN (3) +#define BLE_LL_PDU_HDR_LEN (2) +#define BLE_LL_MAX_PAYLOAD_LEN (255) +#define BLE_LL_MIN_PDU_LEN (BLE_LL_PDU_HDR_LEN) +#define BLE_LL_MAX_PDU_LEN ((BLE_LL_PDU_HDR_LEN) + (BLE_LL_MAX_PAYLOAD_LEN)) +#define BLE_LL_CRCINIT_ADV (0x555555) + +/* Access address for advertising channels */ +#define BLE_ACCESS_ADDR_ADV (0x8E89BED6) + +/* + * Advertising PDU format: + * -> 2 byte header + * -> LSB contains pdu type, txadd and rxadd bits. + * -> MSB contains length (6 bits). Length is length of payload. Does + * not include the header length itself. + * -> Payload (max 37 bytes) + */ +#define BLE_ADV_PDU_HDR_TYPE_MASK (0x0F) +#define BLE_ADV_PDU_HDR_CHSEL_MASK (0x20) +#define BLE_ADV_PDU_HDR_TXADD_MASK (0x40) +#define BLE_ADV_PDU_HDR_RXADD_MASK (0x80) + +/* Advertising channel PDU types */ +#define BLE_ADV_PDU_TYPE_ADV_IND (0) +#define BLE_ADV_PDU_TYPE_ADV_DIRECT_IND (1) +#define BLE_ADV_PDU_TYPE_ADV_NONCONN_IND (2) +#define BLE_ADV_PDU_TYPE_SCAN_REQ (3) +#define BLE_ADV_PDU_TYPE_SCAN_RSP (4) +#define BLE_ADV_PDU_TYPE_CONNECT_IND (5) +#define BLE_ADV_PDU_TYPE_ADV_SCAN_IND (6) +#define BLE_ADV_PDU_TYPE_ADV_EXT_IND (7) +#define BLE_ADV_PDU_TYPE_AUX_ADV_IND BLE_ADV_PDU_TYPE_ADV_EXT_IND +#define BLE_ADV_PDU_TYPE_AUX_SCAN_RSP BLE_ADV_PDU_TYPE_ADV_EXT_IND +#define BLE_ADV_PDU_TYPE_AUX_SYNC_IND BLE_ADV_PDU_TYPE_ADV_EXT_IND +#define BLE_ADV_PDU_TYPE_AUX_CHAIN_IND BLE_ADV_PDU_TYPE_ADV_EXT_IND +#define BLE_ADV_PDU_TYPE_AUX_CONNECT_REQ BLE_ADV_PDU_TYPE_CONNECT_IND +#define BLE_ADV_PDU_TYPE_AUX_SCAN_REQ BLE_ADV_PDU_TYPE_SCAN_REQ +#define BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP (8) + +/* Extended Header Length (6b) + AdvMode (2b) */ +#define BLE_LL_EXT_ADV_HDR_LEN (1) + +#define BLE_LL_EXT_ADV_ADVA_BIT (0) +#define BLE_LL_EXT_ADV_TARGETA_BIT (1) +#define BLE_LL_EXT_ADV_CTE_INFO_BIT (2) +#define BLE_LL_EXT_ADV_DATA_INFO_BIT (3) +#define BLE_LL_EXT_ADV_AUX_PTR_BIT (4) +#define BLE_LL_EXT_ADV_SYNC_INFO_BIT (5) +#define BLE_LL_EXT_ADV_TX_POWER_BIT (6) + +#define BLE_LL_EXT_ADV_FLAGS_SIZE (1) +#define BLE_LL_EXT_ADV_ADVA_SIZE (6) +#define BLE_LL_EXT_ADV_TARGETA_SIZE (6) +#define BLE_LL_EXT_ADV_DATA_INFO_SIZE (2) +#define BLE_LL_EXT_ADV_AUX_PTR_SIZE (3) +#define BLE_LL_EXT_ADV_SYNC_INFO_SIZE (18) +#define BLE_LL_EXT_ADV_TX_POWER_SIZE (1) + +#define BLE_LL_EXT_ADV_MODE_NON_CONN (0x00) +#define BLE_LL_EXT_ADV_MODE_CONN (0x01) +#define BLE_LL_EXT_ADV_MODE_SCAN (0x02) + +/* If Channel Selection Algorithm #2 is supported */ +#define BLE_ADV_PDU_HDR_CHSEL (0x20) + +/* + * TxAdd and RxAdd bit definitions. A 0 is a public address; a 1 is a + * random address. + */ +#define BLE_ADV_PDU_HDR_TXADD_RAND (0x40) +#define BLE_ADV_PDU_HDR_RXADD_RAND (0x80) + +/* + * Data Channel format + * + * -> Header (2 bytes) + * -> LSB contains llid, nesn, sn and md + * -> MSB contains length (8 bits) + * -> Payload (0 to 251) + * -> MIC (0 or 4 bytes) + */ +#define BLE_LL_DATA_HDR_LLID_MASK (0x03) +#define BLE_LL_DATA_HDR_NESN_MASK (0x04) +#define BLE_LL_DATA_HDR_SN_MASK (0x08) +#define BLE_LL_DATA_HDR_MD_MASK (0x10) +#define BLE_LL_DATA_HDR_RSRVD_MASK (0xE0) +#define BLE_LL_DATA_PDU_MAX_PYLD (251) +#define BLE_LL_DATA_MIC_LEN (4) + +/* LLID definitions */ +#define BLE_LL_LLID_RSRVD (0) +#define BLE_LL_LLID_DATA_FRAG (1) +#define BLE_LL_LLID_DATA_START (2) +#define BLE_LL_LLID_CTRL (3) + +/* + * CONNECT_REQ + * -> InitA (6 bytes) + * -> AdvA (6 bytes) + * -> LLData (22 bytes) + * -> Access address (4 bytes) + * -> CRC initialize (3 bytes) + * -> WinSize (1 byte) + * -> WinOffset (2 bytes) + * -> Interval (2 bytes) + * -> Latency (2 bytes) + * -> Timeout (2 bytes) + * -> Channel Map (5 bytes) + * -> Hop Increment (5 bits) + * -> SCA (3 bits) + * + * InitA is the initiators public (TxAdd=0) or random (TxAdd=1) address. + * AdvaA is the advertisers public (RxAdd=0) or random (RxAdd=1) address. + * LLData contains connection request data. + * aa: Link Layer's access address + * crc_init: The CRC initialization value used for CRC calculation. + * winsize: The transmit window size = winsize * 1.25 msecs + * winoffset: The transmit window offset = winoffset * 1.25 msecs + * interval: The connection interval = interval * 1.25 msecs. + * latency: connection slave latency = latency + * timeout: Connection supervision timeout = timeout * 10 msecs. + * chanmap: contains channel mapping indicating used and unused data + * channels. Only bits that are 1 are usable. LSB is channel 0. + * hop_inc: Hop increment used for frequency hopping. Random value in + * range of 5 to 16. + */ +#define BLE_CONNECT_REQ_LEN (34) +#define BLE_CONNECT_REQ_PDU_LEN (BLE_CONNECT_REQ_LEN + BLE_LL_PDU_HDR_LEN) + +#define BLE_SCAN_REQ_LEN (12) +#define BLE_SCAN_RSP_MAX_LEN (37) +#define BLE_SCAN_RSP_MAX_EXT_LEN (251) + +#define BLE_LL_ADDR_SUBTYPE_IDENTITY (0) +#define BLE_LL_ADDR_SUBTYPE_RPA (1) +#define BLE_LL_ADDR_SUBTYPE_NRPA (2) + +/*--- External API ---*/ +/* Initialize the Link Layer */ +void ble_ll_init(void); + +/* Reset the Link Layer */ +int ble_ll_reset(void); + +int ble_ll_is_valid_public_addr(const uint8_t *addr); + +/* 'Boolean' function returning true if address is a valid random address */ +int ble_ll_is_valid_random_addr(const uint8_t *addr); + +/* + * Check if given own_addr_type is valid for current controller configuration + * given the random address provided (when applicable) + */ +int ble_ll_is_valid_own_addr_type(uint8_t own_addr_type, + const uint8_t *random_addr); + +/* Calculate the amount of time in microseconds a PDU with payload length of + * 'payload_len' will take to transmit on a PHY 'phy_mode'. */ +uint32_t ble_ll_pdu_tx_time_get(uint16_t payload_len, int phy_mode); + +/* Calculate maximum octets of PDU payload which can be transmitted during + * 'usecs' on a PHY 'phy_mode'. */ +uint16_t ble_ll_pdu_max_tx_octets_get(uint32_t usecs, int phy_mode); + +/* Is this address a resolvable private address? */ +int ble_ll_is_rpa(const uint8_t *addr, uint8_t addr_type); + +int ble_ll_addr_subtype(const uint8_t *addr, uint8_t addr_type); + +/* Is this address an identity address? */ +int ble_ll_addr_is_id(uint8_t *addr, uint8_t addr_type); + +/* Is 'addr' our device address? 'addr_type' is public (0) or random (!=0) */ +int ble_ll_is_our_devaddr(uint8_t *addr, int addr_type); + +/* Get identity address 'addr_type' is public (0) or random (!=0) */ +uint8_t *ble_ll_get_our_devaddr(uint8_t addr_type); + +/** + * Called to put a packet on the Link Layer transmit packet queue. + * + * @param txpdu Pointer to transmit packet + */ +void ble_ll_acl_data_in(struct os_mbuf *txpkt); + +/** + * Allocates mbuf for received PDU + * + * This allocated mbuf (may be chained if necessary) that has capacity large + * enough to store received PDU of given length. It does not set mbufs length + * as this has to be done by PHY when copying data. + * + * @param len Length of PDU, including PDU header and excluding MIC (if encrypted) + * + * @return mbuf large enough to store received PDU on success + * NULL on failure (oom) + */ +struct os_mbuf *ble_ll_rxpdu_alloc(uint16_t len); + +/* Tell the Link Layer there has been a data buffer overflow */ +void ble_ll_data_buffer_overflow(void); + +/* Tell the link layer there has been a hardware error */ +void ble_ll_hw_error(void); + +/*--- PHY interfaces ---*/ +struct ble_mbuf_hdr; + +/* Called by the PHY when a packet has started */ +int ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *hdr); + +/* Called by the PHY when a packet reception ends */ +int ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); + +/* Helper callback to tx mbuf using ble_phy_tx() */ +uint8_t ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte); +uint8_t ble_ll_tx_flat_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte); + +/*--- Controller API ---*/ +void ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr); + +/* Set the link layer state */ +void ble_ll_state_set(uint8_t ll_state); + +/* Get the link layer state */ +uint8_t ble_ll_state_get(void); + +/* Send an event to LL task */ +void ble_ll_event_send(struct ble_npl_event *ev); + +/* Hand received pdu's to LL task */ +void ble_ll_rx_pdu_in(struct os_mbuf *rxpdu); + +/* + * Set public address + * + * This can be used to set controller public address from vendor specific storage, + * usually should be done in hal_bsp_init(). + * Shall be *only* called before LL is initialized, i.e. before sysinit stage. + */ +int ble_ll_set_public_addr(const uint8_t *addr); + +/* Set random address */ +int ble_ll_set_random_addr(const uint8_t *cmdbuf, uint8_t len, bool hci_adv_ext); + +/* Wait for response timer expiration callback */ +void ble_ll_wfr_timer_exp(void *arg); + +/* Read set of features supported by the Link Layer */ +uint64_t ble_ll_read_supp_features(void); + +/* Set host supported features */ +int ble_ll_set_host_feat(const uint8_t *cmdbuf, uint8_t len); + +/* Read set of states supported by the Link Layer */ +uint64_t ble_ll_read_supp_states(void); + +/* Check if octets and time are valid. Returns 0 if not valid */ +int ble_ll_chk_txrx_octets(uint16_t octets); +int ble_ll_chk_txrx_time(uint16_t time); + +/* Random numbers */ +int ble_ll_rand_init(void); +void ble_ll_rand_sample(uint8_t rnum); +int ble_ll_rand_data_get(uint8_t *buf, uint8_t len); +void ble_ll_rand_prand_get(uint8_t *prand); +int ble_ll_rand_start(void); + +static inline int +ble_ll_get_addr_type(uint8_t txrxflag) +{ + if (txrxflag) { + return BLE_HCI_ADV_OWN_ADDR_RANDOM; + } + return BLE_HCI_ADV_OWN_ADDR_PUBLIC; +} + +/* Convert usecs to ticks and round up to nearest tick */ +static inline uint32_t +ble_ll_usecs_to_ticks_round_up(uint32_t usecs) +{ + return os_cputime_usecs_to_ticks(usecs + 30); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* LTK 0x4C68384139F574D836BCF34E9DFB01BF */ +extern const uint8_t g_bletest_LTK[]; +extern uint16_t g_bletest_EDIV; +extern uint64_t g_bletest_RAND; +extern uint64_t g_bletest_SKDm; +extern uint64_t g_bletest_SKDs; +extern uint32_t g_bletest_IVm; +extern uint32_t g_bletest_IVs; +#endif + +#if MYNEWT_VAL(BLE_LL_DTM) +void ble_ll_dtm_init(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_LL_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_adv.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_adv.h new file mode 100644 index 0000000..eee07cb --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_adv.h @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_ADV_ +#define H_BLE_LL_ADV_ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ADV event timing + * T_advEvent = advInterval + advDelay + * + * advInterval: increments of 625 usecs + * advDelay: RAND[0, 10] msecs + * + */ +#define BLE_LL_ADV_ITVL (625) /* usecs */ +#define BLE_LL_ADV_ITVL_MIN (32) /* units */ +#define BLE_LL_ADV_ITVL_MAX (16384) /* units */ +#define BLE_LL_ADV_ITVL_MS_MIN (20) /* msecs */ +#define BLE_LL_ADV_ITVL_MS_MAX (10240) /* msecs */ +#define BLE_LL_ADV_ITVL_SCAN_MIN (160) /* units */ +#define BLE_LL_ADV_ITVL_SCAN_MS_MIN (100) /* msecs */ +#define BLE_LL_ADV_ITVL_NONCONN_MS_MIN (100) /* msecs */ +#define BLE_LL_ADV_DELAY_MS_MIN (0) /* msecs */ +#define BLE_LL_ADV_DELAY_MS_MAX (10) /* msecs */ +#define BLE_LL_ADV_PDU_ITVL_LD_MS_MAX (10) /* msecs */ +#define BLE_LL_ADV_PDU_ITVL_HD_MS_MAX (3750) /* usecs */ +#define BLE_LL_ADV_STATE_HD_MAX (1280) /* msecs */ +#define BLE_LL_ADV_PERIODIC_ITVL (1250) /* usecs */ + +/* Maximum advertisement data length */ +#define BLE_ADV_LEGACY_DATA_MAX_LEN (31) +#define BLE_ADV_LEGACY_MAX_PKT_LEN (37) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_ADV_DATA_MAX_LEN MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) +#else +#define BLE_ADV_DATA_MAX_LEN BLE_ADV_LEGACY_DATA_MAX_LEN +#endif + +/* + * ADV_IND + * -> AdvA (6 bytes) + * -> AdvData (0 - 31 bytes) + * + * The advertising address (AdvA) is a public address (TxAdd=0) or random + * address (TxAdd = 1) + */ +#define BLE_ADV_IND_MIN_LEN (6) +#define BLE_ADV_IND_MAX_LEN (37) + +/* + * ADV_DIRECT_IND + * -> AdvA (6 bytes) + * -> InitA (6 bytes) + * + * AdvA is the advertisers public address (TxAdd=0) or random address + * (TxAdd = 1). + * + * InitA is the initiators public or random address. This is the address + * to which this packet is addressed. + * + */ +#define BLE_ADV_DIRECT_IND_LEN (12) + +/* + * ADV_NONCONN_IND + * -> AdvA (6 bytes) + * -> AdvData (0 - 31 bytes) + * + * The advertising address (AdvA) is a public address (TxAdd=0) or random + * address (TxAdd = 1) + * + */ +#define BLE_ADV_NONCONN_IND_MIN_LEN (6) +#define BLE_ADV_NONCONN_IND_MAX_LEN (37) + +/* + * ADV_SCAN_IND + * -> AdvA (6 bytes) + * -> AdvData (0 - 31 bytes) + * + * The advertising address (AdvA) is a public address (TxAdd=0) or random + * address (TxAdd = 1) + * + */ +#define BLE_ADV_SCAN_IND_MIN_LEN (6) +#define BLE_ADV_SCAN_IND_MAX_LEN (37) + +/*---- HCI ----*/ +struct ble_ll_adv_sm; +struct ble_ll_conn_sm; + +/* Start an advertiser */ +int ble_ll_adv_start_req(uint8_t adv_chanmask, uint8_t adv_type, + uint8_t *init_addr, uint16_t adv_itvl, void *handle); + +/* Start or stop advertising */ +int ble_ll_hci_adv_set_enable(const uint8_t *cmdbuf, uint8_t len); + +/* Set legacy advertising data */ +int ble_ll_hci_set_adv_data(const uint8_t *cmdbuf, uint8_t len); + +/* Set scan response data */ +int ble_ll_hci_set_scan_rsp_data(const uint8_t *cmd, uint8_t cmd_len); + +/* Set advertising parameters */ +int ble_ll_adv_set_adv_params(const uint8_t *cmdbuf, uint8_t len); + +/* Read advertising channel power */ +int ble_ll_adv_read_txpwr(uint8_t *rspbuf, uint8_t *rsplen); + +/*---- API used by BLE LL ----*/ +/* Send the connection complete event */ +void ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *rxhdr); + +/* Returns local resolvable private address */ +uint8_t *ble_ll_adv_get_local_rpa(struct ble_ll_adv_sm *advsm); + +/* Returns peer resolvable private address */ +uint8_t *ble_ll_adv_get_peer_rpa(struct ble_ll_adv_sm *advsm); + +/* Called to initialize advertising functionality. */ +void ble_ll_adv_init(void); + +/* Called when LL wait for response timer expires in advertising state */ +void ble_ll_adv_wfr_timer_exp(void); + +/* Called to reset the advertiser. */ +void ble_ll_adv_reset(void); + +/* Called on rx pdu start when in advertising state */ +int ble_ll_adv_rx_isr_start(uint8_t pdu_type); + +/* Called on rx pdu end when in advertising state */ +int ble_ll_adv_rx_isr_end(uint8_t pdu_type, struct os_mbuf *rxpdu, int crcok); + +/* Processes received packets at the link layer task */ +void ble_ll_adv_rx_pkt_in(uint8_t ptype, uint8_t *rxbuf, + struct ble_mbuf_hdr *hdr); + +/* Boolean function denoting whether or not the whitelist can be changed */ +int ble_ll_adv_can_chg_whitelist(void); + +/* + * Called when an advertising event has been removed from the scheduler + * without being run. + */ +void ble_ll_adv_event_rmvd_from_sched(struct ble_ll_adv_sm *advsm); + +/* + * Called when a periodic event has been removed from the scheduler + * without being run. + */ +void ble_ll_adv_periodic_rmvd_from_sched(struct ble_ll_adv_sm *advsm); + +/* Called to halt currently running advertising event */ +void ble_ll_adv_halt(void); + +/* Called to determine if advertising is enabled */ +uint8_t ble_ll_adv_enabled(void); + +int ble_ll_adv_hci_set_random_addr(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_adv_set_random_addr(const uint8_t *addr, uint8_t instance); +int ble_ll_adv_remove(const uint8_t *addr, uint8_t len); +int ble_ll_adv_clear_all(void); +int ble_ll_adv_ext_set_param(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_adv_ext_set_adv_data(const uint8_t *cmdbuf, uint8_t cmdlen); +int ble_ll_adv_ext_set_scan_rsp(const uint8_t *cmdbuf, uint8_t cmdlen); +int ble_ll_adv_ext_set_enable(const uint8_t *cmdbuf, uint8_t len); + +int ble_ll_adv_periodic_set_param(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_adv_periodic_set_data(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_adv_periodic_enable(const uint8_t *cmdbuf, uint8_t len); + +int ble_ll_adv_periodic_set_info_transfer(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); + +/* Called to notify adv code about RPA rotation */ +void ble_ll_adv_rpa_timeout(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_ADV_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_conn.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_conn.h new file mode 100644 index 0000000..178d671 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_conn.h @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_CONN_ +#define H_BLE_LL_CONN_ + +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" +#include "ble_ll_sched.h" +#include "ble_ll_ctrl.h" +#include "ble_phy.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Roles */ +#define BLE_LL_CONN_ROLE_NONE (0) +#define BLE_LL_CONN_ROLE_MASTER (1) +#define BLE_LL_CONN_ROLE_SLAVE (2) + +/* Connection states */ +#define BLE_LL_CONN_STATE_IDLE (0) +#define BLE_LL_CONN_STATE_CREATED (1) +#define BLE_LL_CONN_STATE_ESTABLISHED (2) + +/* Channel map size */ +#define BLE_LL_CONN_CHMAP_LEN (5) + +/* Definitions for source clock accuracy */ +#define BLE_MASTER_SCA_251_500_PPM (0) +#define BLE_MASTER_SCA_151_250_PPM (1) +#define BLE_MASTER_SCA_101_150_PPM (2) +#define BLE_MASTER_SCA_76_100_PPM (3) +#define BLE_MASTER_SCA_51_75_PPM (4) +#define BLE_MASTER_SCA_31_50_PPM (5) +#define BLE_MASTER_SCA_21_30_PPM (6) +#define BLE_MASTER_SCA_0_20_PPM (7) + +/* Definition for RSSI when the RSSI is unknown */ +#define BLE_LL_CONN_UNKNOWN_RSSI (127) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* + * Encryption states for a connection + * + * NOTE: the states are ordered so that we can check to see if the state + * is greater than ENCRYPTED. If so, it means that the start or pause + * encryption procedure is running and we should not send data pdu's. + */ +enum conn_enc_state { + CONN_ENC_S_UNENCRYPTED = 1, + CONN_ENC_S_ENCRYPTED, + CONN_ENC_S_ENC_RSP_WAIT, + CONN_ENC_S_PAUSE_ENC_RSP_WAIT, + CONN_ENC_S_PAUSED, + CONN_ENC_S_START_ENC_REQ_WAIT, + CONN_ENC_S_START_ENC_RSP_WAIT, + CONN_ENC_S_LTK_REQ_WAIT, + CONN_ENC_S_LTK_NEG_REPLY +}; + +/* + * Note that the LTK is the key, the SDK is the plain text, and the + * session key is the cipher text portion of the encryption block. + * + * NOTE: we have intentionally violated the specification by making the + * transmit and receive packet counters 32-bits as opposed to 39 (as per the + * specification). We do this to save code space, ram and calculation time. The + * only drawback is that any encrypted connection that sends more than 2^32 + * packets will suffer a MIC failure and thus be disconnected. + */ +struct ble_ll_conn_enc_data +{ + uint8_t enc_state; + uint8_t tx_encrypted; + uint16_t enc_div; + uint32_t tx_pkt_cntr; + uint32_t rx_pkt_cntr; + uint64_t host_rand_num; + uint8_t iv[8]; + struct ble_encryption_block enc_block; +}; +#endif + +/* Connection state machine flags. */ +union ble_ll_conn_sm_flags { + struct { + uint32_t pkt_rxd:1; + uint32_t terminate_ind_txd:1; + uint32_t terminate_ind_rxd:1; + uint32_t terminate_ind_rxd_acked:1; + uint32_t allow_slave_latency:1; + uint32_t slave_set_last_anchor:1; + uint32_t awaiting_host_reply:1; + uint32_t terminate_started:1; + uint32_t conn_update_sched:1; + uint32_t host_expects_upd_event:1; + uint32_t version_ind_sent:1; + uint32_t rxd_version_ind:1; + uint32_t chanmap_update_scheduled:1; + uint32_t conn_empty_pdu_txd:1; + uint32_t last_txd_md:1; + uint32_t conn_req_txd:1; + uint32_t send_ltk_req:1; + uint32_t encrypted:1; + uint32_t encrypt_chg_sent:1; + uint32_t le_ping_supp:1; + uint32_t csa2_supp:1; + uint32_t host_phy_update: 1; + uint32_t phy_update_sched: 1; + uint32_t ctrlr_phy_update: 1; + uint32_t phy_update_event: 1; + uint32_t peer_phy_update: 1; /* XXX:combine with ctrlr udpate bit? */ + uint32_t aux_conn_req: 1; + uint32_t rxd_features:1; + uint32_t pending_hci_rd_features:1; + uint32_t pending_initiate_dle:1; + } cfbit; + uint32_t conn_flags; +} __attribute__((packed)); + +/** + * Structure used for PHY data inside a connection. + * + * NOTE: the new phy's are the phys we will change to when a phy update + * procedure is ongoing and the event counter hits the instant. + * + * tx_phy_mode: chip specific phy mode for tx + * rx_phy_mode: chip specific phy mode for rx + * cur_tx_phy: value denoting current tx_phy (not a bitmask!) + * cur_rx_phy: value denoting current rx phy (not a bitmask!) + * new_tx_phy: value denoting new tx_phy (not a bitmask!) + * new_rx_phy: value denoting new rx phy (not a bitmask!) + * req_pref_tx_phy: tx phy sent in a phy request (may be different than host) + * req_pref_rx_phy: rx phy sent in a phy request (may be different than host) + * host_pref_tx_phys: bitmask of preferred transmit PHYs sent by host + * host_pref_rx_phys: bitmask of preferred receive PHYs sent by host + * phy_options: preferred phy options for coded phy + */ +struct ble_ll_conn_phy_data +{ + uint32_t tx_phy_mode: 2; + uint32_t rx_phy_mode: 2; + uint32_t cur_tx_phy: 2; + uint32_t cur_rx_phy: 2; + uint32_t new_tx_phy: 2; + uint32_t new_rx_phy: 2; + uint32_t host_pref_tx_phys_mask: 3; + uint32_t host_pref_rx_phys_mask: 3; + uint32_t req_pref_tx_phys_mask: 3; + uint32_t req_pref_rx_phys_mask: 3; + uint32_t phy_options: 2; +} __attribute__((packed)); + +#define CONN_CUR_TX_PHY_MASK(csm) (1 << ((csm)->phy_data.cur_tx_phy - 1)) +#define CONN_CUR_RX_PHY_MASK(csm) (1 << ((csm)->phy_data.cur_rx_phy - 1)) + +struct hci_conn_update +{ + uint16_t handle; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +struct hci_ext_conn_params +{ + uint16_t scan_itvl; + uint16_t scan_window; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +struct hci_ext_create_conn +{ + uint8_t filter_policy; + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint8_t init_phy_mask; + struct hci_ext_conn_params params[3]; +}; + +/* Connection state machine */ +struct ble_ll_conn_sm +{ + /* Connection state machine flags */ + union ble_ll_conn_sm_flags csmflags; + + /* Current connection handle, state and role */ + uint16_t conn_handle; + uint8_t conn_state; + uint8_t conn_role; /* Can possibly be 1 bit */ + + /* RSSI */ + int8_t conn_rssi; + + /* For privacy */ + int8_t rpa_index; + + /* Connection data length management */ + uint8_t max_tx_octets; + uint8_t max_rx_octets; + uint8_t rem_max_tx_octets; + uint8_t rem_max_rx_octets; + uint8_t eff_max_tx_octets; + uint8_t eff_max_rx_octets; + uint16_t max_tx_time; + uint16_t max_rx_time; + uint16_t rem_max_tx_time; + uint16_t rem_max_rx_time; + uint16_t eff_max_tx_time; + uint16_t eff_max_rx_time; + uint8_t max_tx_octets_phy_mode[BLE_PHY_NUM_MODE]; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + uint16_t host_req_max_tx_time; +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + struct ble_ll_conn_phy_data phy_data; + uint16_t phy_instant; + uint8_t phy_tx_transition; +#endif + + /* Used to calculate data channel index for connection */ + uint8_t chanmap[BLE_LL_CONN_CHMAP_LEN]; + uint8_t req_chanmap[BLE_LL_CONN_CHMAP_LEN]; + uint16_t chanmap_instant; + uint16_t channel_id; /* TODO could be union with hop and last chan used */ + uint8_t hop_inc; + uint8_t data_chan_index; + uint8_t last_unmapped_chan; + uint8_t num_used_chans; + +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + uint8_t period_occ_mask; /* mask: period 0 = 0x01, period 3 = 0x08 */ +#endif + + /* Ack/Flow Control */ + uint8_t tx_seqnum; /* note: can be 1 bit */ + uint8_t next_exp_seqnum; /* note: can be 1 bit */ + uint8_t cons_rxd_bad_crc; /* note: can be 1 bit */ + uint8_t last_rxd_sn; /* note: cant be 1 bit given current code */ + uint8_t last_rxd_hdr_byte; /* note: possibly can make 1 bit since we + only use the MD bit now */ + + /* connection event mgmt */ + uint8_t reject_reason; + uint8_t host_reply_opcode; + uint8_t master_sca; + uint8_t tx_win_size; + uint8_t cur_ctrl_proc; + uint8_t disconnect_reason; + uint8_t rxd_disconnect_reason; + uint8_t vers_nr; + uint8_t conn_features; + uint8_t remote_features[7]; + uint16_t pending_ctrl_procs; + uint16_t event_cntr; + uint16_t completed_pkts; + uint16_t comp_id; + uint16_t sub_vers_nr; + uint16_t auth_pyld_tmo; /* could be ifdef'd. 10 msec units */ + + uint32_t access_addr; + uint32_t crcinit; /* only low 24 bits used */ + /* XXX: do we need ce_end_time? Cant this be sched end time? */ + uint32_t ce_end_time; /* cputime at which connection event should end */ + uint32_t terminate_timeout; + uint32_t last_scheduled; + + /* Connection timing */ + uint16_t conn_itvl; + uint16_t slave_latency; + uint16_t supervision_tmo; + uint16_t min_ce_len; + uint16_t max_ce_len; + uint16_t tx_win_off; + uint32_t anchor_point; + uint8_t anchor_point_usecs; /* XXX: can this be uint8_t ?*/ + uint8_t conn_itvl_usecs; + uint32_t conn_itvl_ticks; + uint32_t last_anchor_point; /* Slave only */ + uint32_t slave_cur_tx_win_usecs; + uint32_t slave_cur_window_widening; + uint32_t last_rxd_pdu_cputime; /* Used exclusively for supervision timer */ + + /* + * Used to mark that identity address was used as InitA + */ + uint8_t inita_identity_used; + + /* address information */ + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + + /* + * XXX: TODO. Could save memory. Have single event at LL and put these + * on a singly linked list. Only would need list pointer here. + */ + /* Connection end event */ + struct ble_npl_event conn_ev_end; + + /* Packet transmit queue */ + struct os_mbuf *cur_tx_pdu; + STAILQ_HEAD(conn_txq_head, os_mbuf_pkthdr) conn_txq; + + /* List entry for active/free connection pools */ + union { + SLIST_ENTRY(ble_ll_conn_sm) act_sle; + STAILQ_ENTRY(ble_ll_conn_sm) free_stqe; + }; + + /* LL control procedure response timer */ + struct ble_npl_callout ctrl_proc_rsp_timer; + + /* For scheduling connections */ + struct ble_ll_sched_item conn_sch; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + struct ble_npl_callout auth_pyld_timer; +#endif + + /* + * XXX: a note on all these structures for control procedures. First off, + * all of these need to be ifdef'd to save memory. Another thing to + * consider is this: since most control procedures can only run when no + * others are running, can I use just one structure (a union)? Should I + * allocate these from a pool? Not sure what to do. For now, I just use + * a large chunk of memory per connection. + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + struct ble_ll_conn_enc_data enc_data; +#endif + /* + * For connection update procedure. XXX: can make this a pointer and + * malloc it if we want to save space. + */ + struct hci_conn_update conn_param_req; + + /* For connection update procedure */ + struct ble_ll_conn_upd_req conn_update_req; + + /* XXX: for now, just store them all */ + struct ble_ll_conn_params conn_cp; + + struct ble_ll_scan_sm *scansm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct hci_ext_create_conn initial_params; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + uint8_t sync_transfer_mode; + uint16_t sync_transfer_skip; + uint32_t sync_transfer_sync_timeout; +#endif +}; + +/* Flags */ +#define CONN_F_UPDATE_SCHED(csm) ((csm)->csmflags.cfbit.conn_update_sched) +#define CONN_F_EMPTY_PDU_TXD(csm) ((csm)->csmflags.cfbit.conn_empty_pdu_txd) +#define CONN_F_LAST_TXD_MD(csm) ((csm)->csmflags.cfbit.last_txd_md) +#define CONN_F_CONN_REQ_TXD(csm) ((csm)->csmflags.cfbit.conn_req_txd) +#define CONN_F_ENCRYPTED(csm) ((csm)->csmflags.cfbit.encrypted) +#define CONN_F_ENC_CHANGE_SENT(csm) ((csm)->csmflags.cfbit.encrypt_chg_sent) +#define CONN_F_LE_PING_SUPP(csm) ((csm)->csmflags.cfbit.le_ping_supp) +#define CONN_F_TERMINATE_STARTED(csm) ((csm)->csmflags.cfbit.terminate_started) +#define CONN_F_CSA2_SUPP(csm) ((csm)->csmflags.cfbit.csa2_supp) +#define CONN_F_HOST_PHY_UPDATE(csm) ((csm)->csmflags.cfbit.host_phy_update) +#define CONN_F_PHY_UPDATE_SCHED(csm) ((csm)->csmflags.cfbit.phy_update_sched) +#define CONN_F_CTRLR_PHY_UPDATE(csm) ((csm)->csmflags.cfbit.ctrlr_phy_update) +#define CONN_F_PHY_UPDATE_EVENT(csm) ((csm)->csmflags.cfbit.phy_update_event) +#define CONN_F_PEER_PHY_UPDATE(csm) ((csm)->csmflags.cfbit.peer_phy_update) +#define CONN_F_AUX_CONN_REQ(csm) ((csm)->csmflags.cfbit.aux_conn_req) + +/* Role */ +#define CONN_IS_MASTER(csm) (csm->conn_role == BLE_LL_CONN_ROLE_MASTER) +#define CONN_IS_SLAVE(csm) (csm->conn_role == BLE_LL_CONN_ROLE_SLAVE) + +/* + * Given a handle, returns an active connection state machine (or NULL if the + * handle does not exist + * + */ +struct ble_ll_conn_sm *ble_ll_conn_find_active_conn(uint16_t handle); + +/* required for unit testing */ +uint8_t ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn, uint16_t latency); + +/* used to get anchor point for connection event specified */ +void ble_ll_conn_get_anchor(struct ble_ll_conn_sm *connsm, uint16_t conn_event, + uint32_t *anchor, uint8_t *anchor_usecs); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_CONN_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_ctrl.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_ctrl.h new file mode 100644 index 0000000..b0da1e7 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_ctrl.h @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_CTRL_ +#define H_BLE_LL_CTRL_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * LL control procedures. This "enumeration" is not in the specification; + * It is used to determine which LL control procedure is currently running + * in a connection and which ones may be pending. + */ +#define BLE_LL_CTRL_PROC_CONN_UPDATE (0) +#define BLE_LL_CTRL_PROC_CHAN_MAP_UPD (1) +#define BLE_LL_CTRL_PROC_ENCRYPT (2) +#define BLE_LL_CTRL_PROC_FEATURE_XCHG (3) +#define BLE_LL_CTRL_PROC_VERSION_XCHG (4) +#define BLE_LL_CTRL_PROC_TERMINATE (5) +#define BLE_LL_CTRL_PROC_CONN_PARAM_REQ (6) +#define BLE_LL_CTRL_PROC_LE_PING (7) +#define BLE_LL_CTRL_PROC_DATA_LEN_UPD (8) +#define BLE_LL_CTRL_PROC_PHY_UPDATE (9) +#define BLE_LL_CTRL_PROC_NUM (10) +#define BLE_LL_CTRL_PROC_IDLE (255) + +/* Checks if a particular control procedure is running */ +#define IS_PENDING_CTRL_PROC(sm, proc) (sm->pending_ctrl_procs & (1 << proc)) +#define CLR_PENDING_CTRL_PROC(sm, proc) (sm->pending_ctrl_procs &= ~(1 << proc)) + +/* LL control procedure timeout */ +#define BLE_LL_CTRL_PROC_TIMEOUT_MS (40000) /* ms */ + +/* + * LL CTRL PDU format + * -> Opcode (1 byte) + * -> Data (0 - 26 bytes) + */ +#define BLE_LL_CTRL_CONN_UPDATE_IND (0) +#define BLE_LL_CTRL_CHANNEL_MAP_REQ (1) +#define BLE_LL_CTRL_TERMINATE_IND (2) +#define BLE_LL_CTRL_ENC_REQ (3) +#define BLE_LL_CTRL_ENC_RSP (4) +#define BLE_LL_CTRL_START_ENC_REQ (5) +#define BLE_LL_CTRL_START_ENC_RSP (6) +#define BLE_LL_CTRL_UNKNOWN_RSP (7) +#define BLE_LL_CTRL_FEATURE_REQ (8) +#define BLE_LL_CTRL_FEATURE_RSP (9) +#define BLE_LL_CTRL_PAUSE_ENC_REQ (10) +#define BLE_LL_CTRL_PAUSE_ENC_RSP (11) +#define BLE_LL_CTRL_VERSION_IND (12) +#define BLE_LL_CTRL_REJECT_IND (13) +#define BLE_LL_CTRL_SLAVE_FEATURE_REQ (14) +#define BLE_LL_CTRL_CONN_PARM_REQ (15) +#define BLE_LL_CTRL_CONN_PARM_RSP (16) +#define BLE_LL_CTRL_REJECT_IND_EXT (17) +#define BLE_LL_CTRL_PING_REQ (18) +#define BLE_LL_CTRL_PING_RSP (19) +#define BLE_LL_CTRL_LENGTH_REQ (20) +#define BLE_LL_CTRL_LENGTH_RSP (21) +#define BLE_LL_CTRL_PHY_REQ (22) +#define BLE_LL_CTRL_PHY_RSP (23) +#define BLE_LL_CTRL_PHY_UPDATE_IND (24) +#define BLE_LL_CTRL_MIN_USED_CHAN_IND (25) +#define BLE_LL_CTRL_CTE_REQ (26) +#define BLE_LL_CTRL_CTE_RSP (27) +#define BLE_LL_CTRL_PERIODIC_SYNC_IND (28) +#define BLE_LL_CTRL_CLOCK_ACCURACY_REQ (29) +#define BLE_LL_CTRL_CLOCK_ACCURACY_RSP (30) + +/* Maximum opcode value */ +#define BLE_LL_CTRL_OPCODES (BLE_LL_CTRL_CLOCK_ACCURACY_RSP + 1) + +extern const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES]; + +/* Maximum LL control PDU size */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +#define BLE_LL_CTRL_MAX_PDU_LEN (35) +#else +#define BLE_LL_CTRL_MAX_PDU_LEN (27) +#endif + +/* LL control connection update request */ +struct ble_ll_conn_upd_req +{ + uint8_t winsize; + uint16_t winoffset; + uint16_t interval; + uint16_t latency; + uint16_t timeout; + uint16_t instant; +}; +#define BLE_LL_CTRL_CONN_UPD_REQ_LEN (11) + +/* LL control channel map request */ +struct ble_ll_chan_map_req +{ + uint8_t chmap[5]; + uint16_t instant; +}; +#define BLE_LL_CTRL_CHAN_MAP_LEN (7) + +/* + * LL control terminate ind + * -> error code (1 byte) + */ +#define BLE_LL_CTRL_TERMINATE_IND_LEN (1) + +/* LL control enc req */ +struct ble_ll_enc_req +{ + uint8_t rand[8]; + uint16_t ediv; + uint8_t skdm[8]; + uint32_t ivm; +}; + +#define BLE_LL_CTRL_ENC_REQ_LEN (22) + +/* LL control enc rsp */ +struct ble_ll_enc_rsp +{ + uint8_t skds[8]; + uint32_t ivs; +}; + +#define BLE_LL_CTRL_ENC_RSP_LEN (12) + +/* LL control start/pause enc request and response */ +#define BLE_LL_CTRL_START_ENC_REQ_LEN (0) +#define BLE_LL_CTRL_START_ENC_RSP_LEN (0) +#define BLE_LL_CTRL_PAUSE_ENC_REQ_LEN (0) +#define BLE_LL_CTRL_PAUSE_ENC_RSP_LEN (0) + +/* + * LL control unknown response + * -> 1 byte which contains the unknown or un-supported opcode. + */ +#define BLE_LL_CTRL_UNK_RSP_LEN (1) + +/* + * LL control feature req and LL control feature rsp + * -> 8 bytes of data containing features supported by device. + */ +#define BLE_LL_CTRL_FEATURE_LEN (8) + +/* + * LL control version ind + * -> version (1 byte): + * Contains the version number of the bluetooth controller specification. + * -> comp_id (2 bytes) + * Contains the company identifier of the manufacturer of the controller. + * -> sub_ver_num: Contains a unique value for implementation or revision of + * the bluetooth controller. + */ +struct ble_ll_version_ind +{ + uint8_t ble_ctrlr_ver; + uint16_t company_id; + uint16_t sub_ver_num; +}; + +#define BLE_LL_CTRL_VERSION_IND_LEN (5) + +/* + * LL control reject ind + * -> error code (1 byte): contains reason why request was rejected. + */ +#define BLE_LL_CTRL_REJ_IND_LEN (1) + +/* + * LL control slave feature req + * -> 8 bytes of data containing features supported by device. + */ +#define BLE_LL_CTRL_SLAVE_FEATURE_REQ_LEN (8) + +/* LL control connection param req and connection param rsp */ +struct ble_ll_conn_params +{ + uint16_t interval_min; + uint16_t interval_max; + uint16_t latency; + uint16_t timeout; + uint8_t pref_periodicity; + uint16_t ref_conn_event_cnt; + uint16_t offset0; + uint16_t offset1; + uint16_t offset2; + uint16_t offset3; + uint16_t offset4; + uint16_t offset5; +}; + +#define BLE_LL_CTRL_CONN_PARAMS_LEN (23) + +/* LL control reject ind ext */ +struct ble_ll_reject_ind_ext +{ + uint8_t reject_opcode; + uint8_t err_code; +}; + +#define BLE_LL_CTRL_REJECT_IND_EXT_LEN (2) + +/* LL control ping req and ping rsp (contain no data) */ +#define BLE_LL_CTRL_PING_LEN (0) + +/* + * LL control length req and length rsp + * -> max_rx_bytes (2 bytes): defines connMaxRxOctets. Range 27 to 251 + * -> max_rx_time (2 bytes): defines connMaxRxTime. Range 328 to 2120 usecs. + * -> max_tx_bytes (2 bytes): defines connMaxTxOctets. Range 27 to 251 + * -> max_tx_time (2 bytes): defines connMaxTxTime. Range 328 to 2120 usecs. + */ +struct ble_ll_len_req +{ + uint16_t max_rx_bytes; + uint16_t max_rx_time; + uint16_t max_tx_bytes; + uint16_t max_tx_time; +}; + +#define BLE_LL_CTRL_LENGTH_REQ_LEN (8) + +/* PHY request/response */ +#define BLE_LL_CTRL_PHY_REQ_LEN (2) +#define BLE_LL_CTRL_PHY_RSP_LEN (2) +#define BLE_LL_CTRL_PHY_UPD_IND_LEN (4) + +/* Min used channels */ +#define BLE_LL_CTRL_MIN_USED_CHAN_LEN (2) + +/* CTE REQ */ +#define BLE_LL_CTRL_CTE_REQ_LEN (1) + +/* CTE RSP (contains no data) */ +#define BLE_LL_CTRL_CTE_RSP_LEN (0) + +/* Periodic Sync Transfer IND */ +#define BLE_LL_CTRL_PERIODIC_SYNC_IND_LEN (34) + +/* Clock accuracy request/response */ +#define BLE_LL_CTRL_CLOCK_ACCURACY_REQ_LEN (1) +#define BLE_LL_CTRL_CLOCK_ACCURACY_RSP_LEN (1) + +/* API */ +struct ble_ll_conn_sm; +void ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc); +void ble_ll_ctrl_proc_stop(struct ble_ll_conn_sm *connsm, int ctrl_proc); +int ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om); +void ble_ll_ctrl_chk_proc_start(struct ble_ll_conn_sm *connsm); +void ble_ll_ctrl_terminate_start(struct ble_ll_conn_sm *connsm); +int ble_ll_ctrl_is_terminate_ind(uint8_t hdr, uint8_t opcode); +uint8_t ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, + uint8_t *rsp, + struct ble_ll_conn_params *req); +int ble_ll_ctrl_reject_ind_send(struct ble_ll_conn_sm *connsm, + uint8_t rej_opcode, uint8_t err); +int ble_ll_ctrl_start_enc_send(struct ble_ll_conn_sm *connsm); +int ble_ll_ctrl_enc_allowed_pdu_rx(struct os_mbuf *rxpdu); +int ble_ll_ctrl_enc_allowed_pdu_tx(struct os_mbuf_pkthdr *pkthdr); +int ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm); +int ble_ll_ctrl_is_start_enc_rsp(struct os_mbuf *txpdu); + +void ble_ll_hci_ev_datalen_chg(struct ble_ll_conn_sm *connsm); +void ble_ll_hci_ev_rem_conn_parm_req(struct ble_ll_conn_sm *connsm, + struct ble_ll_conn_params *cp); +void ble_ll_hci_ev_conn_update(struct ble_ll_conn_sm *connsm, uint8_t status); +void ble_ll_hci_ev_rd_rem_used_feat(struct ble_ll_conn_sm *connsm, + uint8_t status); +void ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, uint8_t status); +void ble_ll_hci_ev_encrypt_chg(struct ble_ll_conn_sm *connsm, uint8_t status); +int ble_ll_hci_ev_ltk_req(struct ble_ll_conn_sm *connsm); +int ble_ll_hci_ev_hw_err(uint8_t hw_err); +void ble_ll_hci_ev_databuf_overflow(void); +void ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm); +void ble_ll_hci_ev_send_scan_req_recv(uint8_t adv_handle, const uint8_t *peer, + uint8_t peer_addr_type); +void ble_ll_hci_ev_send_scan_timeout(void); +void ble_ll_hci_ev_send_adv_set_terminated(uint8_t status, uint8_t adv_handle, + uint16_t conn_handle, uint8_t events); +int ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status); +void ble_ll_calc_session_key(struct ble_ll_conn_sm *connsm); +void ble_ll_ctrl_phy_update_proc_complete(struct ble_ll_conn_sm *connsm); +void ble_ll_ctrl_initiate_dle(struct ble_ll_conn_sm *connsm); +void ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line); + +uint8_t ble_ll_ctrl_phy_tx_transition_get(uint8_t phy_mask); +uint8_t ble_ll_ctrl_phy_from_phy_mask(uint8_t phy_mask); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_CTRL_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_hci.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_hci.h new file mode 100644 index 0000000..5806f67 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_hci.h @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_HCI_ +#define H_BLE_LL_HCI_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nimble/nimble/include/nimble/hci_common.h" + +/* For supported commands */ +#define BLE_LL_SUPP_CMD_LEN (42) +extern const uint8_t g_ble_ll_supp_cmds[BLE_LL_SUPP_CMD_LEN]; + +/* The largest event the controller will send. */ +#define BLE_LL_MAX_EVT_LEN MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE) + +/* + * This determines the number of outstanding commands allowed from the + * host to the controller. NOTE: you cannot change this without modifying + * other portions of the code as we currently use a global os event for + * the command; you would need to allocate a pool of these. + */ +#define BLE_LL_CFG_NUM_HCI_CMD_PKTS (1) + +typedef void (*ble_ll_hci_post_cmd_complete_cb)(void); + +/* Initialize LL HCI */ +void ble_ll_hci_init(void); + +/* Used to determine if the LE event is enabled/disabled */ +bool ble_ll_hci_is_le_event_enabled(unsigned int subev); + +/* Used to determine if event is enabled/disabled */ +bool ble_ll_hci_is_event_enabled(unsigned int evcode); + +/* Send event from controller to host */ +int ble_ll_hci_event_send(struct ble_hci_ev *hci_ev); + +/* Sends a command complete with a no-op opcode to host */ +void ble_ll_hci_send_noop(void); + +/* Checks the preferref phy masks from set default phy and set phy commands */ +int ble_ll_hci_chk_phy_masks(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys, + uint8_t *txphy, uint8_t *rxphy); + +/* Returns true if Extended Advertising HCI commands are in use */ +bool ble_ll_hci_adv_mode_ext(void); + +/* Get TX power compensation rounded to integer dB */ +int8_t ble_ll_get_tx_pwr_compensation(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_HCI_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_resolv.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_resolv.h new file mode 100644 index 0000000..228e0a3 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_resolv.h @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_RESOLV_ +#define H_BLE_LL_RESOLV_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * An entry in the resolving list. + * The identity address is stored in little endian format. + * The local rpa is stored in little endian format. + * The IRKs are stored in big endian format. + * + * Note: + * rl_local_irk and rl_peer_irk need to be word aligned + */ +struct ble_ll_resolv_entry +{ + uint8_t rl_addr_type; + uint8_t rl_priv_mode; + uint8_t rl_has_local; + uint8_t rl_has_peer; + uint8_t rl_local_irk[16]; + uint8_t rl_peer_irk[16]; + uint8_t rl_identity_addr[BLE_DEV_ADDR_LEN]; + uint8_t rl_local_rpa[BLE_DEV_ADDR_LEN]; + uint8_t rl_peer_rpa[BLE_DEV_ADDR_LEN]; +}; + +extern struct ble_ll_resolv_entry g_ble_ll_resolv_list[]; + +/* Clear the resolving list */ +int ble_ll_resolv_list_clr(void); + +/* Read the size of the resolving list */ +int ble_ll_resolv_list_read_size(uint8_t *rspbuf, uint8_t *rsplen); + +/* Add a device to the resolving list */ +int ble_ll_resolv_list_add(const uint8_t *cmdbuf, uint8_t len); + +/* Remove a device from the resolving list */ +int ble_ll_resolv_list_rmv(const uint8_t *cmdbuf, uint8_t len); + +/* Address resolution enable command */ +int ble_ll_resolv_enable_cmd(const uint8_t *cmdbuf, uint8_t len); + +int ble_ll_resolv_peer_addr_rd(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_resolv_local_addr_rd(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); + +/* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */ +struct ble_ll_resolv_entry * +ble_ll_resolv_list_find(const uint8_t *addr, uint8_t addr_type); + +/* Returns true if address resolution is enabled */ +uint8_t ble_ll_resolv_enabled(void); + +/* Reset private address resolution */ +void ble_ll_resolv_list_reset(void); + +/* Generate local or peer RPA. It is up to caller to make sure required IRK + * is present on RL + */ +void ble_ll_resolv_get_priv_addr(struct ble_ll_resolv_entry *rl, int local, + uint8_t *addr); + +void ble_ll_resolv_set_peer_rpa(int index, uint8_t *rpa); +void ble_ll_resolv_set_local_rpa(int index, uint8_t *rpa); + +/* Generate a resolvable private address. */ +int ble_ll_resolv_gen_rpa(uint8_t *addr, uint8_t addr_type, uint8_t *rpa, + int local); + +/* Set the resolvable private address timeout */ +int ble_ll_resolv_set_rpa_tmo(const uint8_t *cmdbuf, uint8_t len); + +/* Set the privacy mode */ +int ble_ll_resolve_set_priv_mode(const uint8_t *cmdbuf, uint8_t len); + +/* Get the RPA timeout, in seconds */ +uint32_t ble_ll_resolv_get_rpa_tmo(void); + +/* Resolve a resolvable private address */ +int ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk); + +/* Try to resolve peer RPA and return index on RL if matched */ +int ble_ll_resolv_peer_rpa_any(const uint8_t *rpa); + +/* Initialize resolv*/ +void ble_ll_resolv_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h new file mode 100644 index 0000000..37b81a8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_RFMGMT_ +#define H_BLE_LL_RFMGMT_ + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_ll_rfmgmt_init(void); + +#if MYNEWT_VAL(BLE_LL_RFMGMT_ENABLE_TIME) > 0 + +void ble_ll_rfmgmt_reset(void); + +/* Notify rfmgmt that scan window has changed (only called from ble_ll_scan) */ +void ble_ll_rfmgmt_scan_changed(bool enabled, uint32_t next_window); + +/* Notify rfmgmt that 1st scheduled item has changed (only called from ble_ll_sched) */ +void ble_ll_rfmgmt_sched_changed(struct ble_ll_sched_item *first); + +/* Notify rfmgmt that RF is no longer needed by current event */ +void ble_ll_rfmgmt_release(void); + +/* Enables RF immediately and returns tick at which RF will be fully enabled */ +uint32_t ble_ll_rfmgmt_enable_now(void); + +/* Returns true only if RF is currently fully enabled (i.e. not off or enabling) */ +bool ble_ll_rfmgmt_is_enabled(void); + +#else + +static inline void ble_ll_rfmgmt_reset(void) { } +static inline void ble_ll_rfmgmt_scan_changed(bool e, uint32_t n) { } +static inline void ble_ll_rfmgmt_sched_changed(struct ble_ll_sched_item *f) { } +static inline void ble_ll_rfmgmt_release(void) { } +static inline uint32_t ble_ll_rfmgmt_enable_now(void) { return 0; } +static inline bool ble_ll_rfmgmt_is_enabled(void) { return true; } + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_RFMGMT_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_scan.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_scan.h new file mode 100644 index 0000000..ca50824 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_scan.h @@ -0,0 +1,293 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_SCAN_ +#define H_BLE_LL_SCAN_ + +#include "ble_ll_sched.h" +#include "nimble/porting/nimble/include/hal/hal_timer.h" +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SCAN_REQ + * -> ScanA (6 bytes) + * -> AdvA (6 bytes) + * + * ScanA is the scanners public (TxAdd=0) or random (TxAdd = 1) address + * AdvaA is the advertisers public (RxAdd=0) or random (RxAdd=1) address. + * + * Sent by the LL in the Scanning state; received by the LL in the advertising + * state. The advertising address is the intended recipient of this frame. + */ +#define BLE_SCAN_REQ_LEN (12) + +/* + * SCAN_RSP + * -> AdvA (6 bytes) + * -> ScanRspData (0 - 31 bytes) + * + * AdvaA is the advertisers public (TxAdd=0) or random (TxAdd=1) address. + * ScanRspData may contain any data from the advertisers host. + * + * Sent by the LL in the advertising state; received by the LL in the + * scanning state. + */ +#define BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN (31) +#define BLE_SCAN_LEGACY_MAX_PKT_LEN (37) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_SCAN_RSP_DATA_MAX_LEN MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) + +/* For Bluetooth 5.0 we need state machine for two PHYs*/ +#define BLE_LL_SCAN_PHY_NUMBER (2) +#else +#define BLE_LL_SCAN_PHY_NUMBER (1) +#define BLE_SCAN_RSP_DATA_MAX_LEN BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN +#endif + +#define PHY_UNCODED (0) +#define PHY_CODED (1) + +#define BLE_LL_EXT_ADV_MODE_NON_CONN (0x00) +#define BLE_LL_EXT_ADV_MODE_CONN (0x01) +#define BLE_LL_EXT_ADV_MODE_SCAN (0x02) + +/* All values are stored as ticks */ +struct ble_ll_scan_timing { + uint32_t interval; + uint32_t window; + uint32_t start_time; +}; + +struct ble_ll_scan_params +{ + uint8_t phy; + uint8_t own_addr_type; + uint8_t scan_filt_policy; + uint8_t configured; + uint8_t scan_type; + uint8_t scan_chan; + struct ble_ll_scan_timing timing; +}; + +#define BLE_LL_AUX_HAS_ADVA 0x01 +#define BLE_LL_AUX_HAS_TARGETA 0x02 +#define BLE_LL_AUX_HAS_ADI 0x04 +#define BLE_LL_AUX_IS_MATCHED 0x08 +#define BLE_LL_AUX_IS_TARGETA_RESOLVED 0x10 + +#define BLE_LL_AUX_FLAG_HCI_SENT_ANY 0x02 +#define BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED 0x04 +#define BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED 0x08 +#define BLE_LL_AUX_FLAG_SCAN_COMPLETE 0x10 +#define BLE_LL_AUX_FLAG_SCAN_ERROR 0x20 +#define BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED 0x40 +#define BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED 0x80 + +struct ble_ll_aux_data { + uint8_t flags; + + /* + * Since aux_data can be accessed from ISR and LL, we have separate copies + * of flags to make sure that ISR does not modify flags while LL uses them. + * ISR updates 'flags_isr' and LL adds these to 'flags_ll' which it then + * uses for further processing allowing to update 'flags_isr' if another + * scan for given 'aux_data' is scheduled. Note that flags must not be unset + * while aux_data is valid. + */ + uint8_t flags_isr; + uint8_t flags_ll; + + uint8_t ref_cnt; + uint8_t chan; + uint8_t aux_phy; + uint8_t aux_primary_phy; + uint8_t mode; + uint8_t scanning; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + int8_t rpa_index; +#endif + uint16_t adi; + uint32_t offset; + uint8_t offset_units; + uint8_t adva[6]; + uint8_t adva_type; + uint8_t targeta[6]; + uint8_t targeta_type; + uint16_t evt_type; + struct ble_ll_sched_item sch; + struct ble_hci_ev *evt; + struct ble_npl_event ev; +}; + +struct ble_ll_scan_pdu_data { + uint8_t hdr_byte; + /* ScanA for SCAN_REQ and InitA for CONNECT_IND */ + union { + uint8_t scana[BLE_DEV_ADDR_LEN]; + uint8_t inita[BLE_DEV_ADDR_LEN]; + }; + uint8_t adva[BLE_DEV_ADDR_LEN]; +}; + +struct ble_ll_scan_sm +{ + uint8_t scan_enabled; + uint8_t own_addr_type; + uint8_t scan_filt_dups; + uint8_t scan_rsp_pending; + uint8_t scan_rsp_cons_fails; + uint8_t scan_rsp_cons_ok; + uint8_t scan_peer_rpa[BLE_DEV_ADDR_LEN]; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_npl_time_t scan_nrpa_timer; + uint8_t scan_nrpa[BLE_DEV_ADDR_LEN]; +#endif + struct ble_ll_scan_pdu_data pdu_data; + + /* XXX: Shall we count backoff per phy? */ + uint16_t upper_limit; + uint16_t backoff_count; + uint32_t scan_win_start_time; + struct ble_npl_event scan_sched_ev; + struct hal_timer scan_timer; + struct ble_npl_event scan_interrupted_ev; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct hal_timer duration_timer; + struct hal_timer period_timer; + uint32_t duration_ticks; + uint32_t period_ticks; + uint8_t ext_scanning; +#endif + + uint8_t restart_timer_needed; + struct ble_ll_aux_data *cur_aux_data; + + struct ble_ll_scan_params *scanp; + struct ble_ll_scan_params *scanp_next; + struct ble_ll_scan_params scanp_phys[BLE_LL_SCAN_PHY_NUMBER]; +}; + +/* Scan types */ +#define BLE_SCAN_TYPE_PASSIVE (BLE_HCI_SCAN_TYPE_PASSIVE) +#define BLE_SCAN_TYPE_ACTIVE (BLE_HCI_SCAN_TYPE_ACTIVE) +#define BLE_SCAN_TYPE_INITIATE (2) + +/*---- HCI ----*/ +/* Set scanning parameters */ +int ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len); + +/* Turn scanning on/off */ +int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_hci_ext_scan_set_enable(const uint8_t *cmdbuf, uint8_t len); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +int ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len); +#endif + +/*--- Controller Internal API ---*/ +/* Initialize the scanner */ +void ble_ll_scan_init(void); + +/* Reset the scanner */ +void ble_ll_scan_reset(void); + +/* Called when Link Layer starts to receive a PDU and is in scanning state */ +int ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags); + +/* Called when Link Layer has finished receiving a PDU while scanning */ +int ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok); + +/* Process a scan response PDU */ +void ble_ll_scan_rx_pkt_in(uint8_t pdu_type, struct os_mbuf *om, + struct ble_mbuf_hdr *hdr); + +/* Boolean function denoting whether or not the whitelist can be changed */ +int ble_ll_scan_can_chg_whitelist(void); + +/* Boolean function returning true if scanning enabled */ +int ble_ll_scan_enabled(void); + +/* Boolean function returns true if whitelist is enabled for scanning */ +int ble_ll_scan_whitelist_enabled(void); + +/* Initialize the scanner when we start initiating */ +struct hci_create_conn; +int ble_ll_scan_initiator_start(struct hci_create_conn *hcc, + struct ble_ll_scan_sm **sm); + +/* Returns storage for PDU data (for SCAN_REQ or CONNECT_IND) */ +struct ble_ll_scan_pdu_data *ble_ll_scan_get_pdu_data(void); + +/* Called to set the resolvable private address of the last connected peer */ +void ble_ll_scan_set_peer_rpa(uint8_t *rpa); + +/* Returns peer RPA of last connection made */ +uint8_t *ble_ll_scan_get_peer_rpa(void); + +/* Returns the local RPA used by the scanner/initiator */ +uint8_t *ble_ll_scan_get_local_rpa(void); + +/* Stop the scanning state machine */ +void ble_ll_scan_sm_stop(int chk_disable); + +/* Resume scanning */ +void ble_ll_scan_chk_resume(void); + +/* Called when wait for response timer expires in scanning mode */ +void ble_ll_scan_wfr_timer_exp(void); + +/* Called when scan could be interrupted */ +void ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm); + +int ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *ble_hdr, + uint8_t **addr, uint8_t *addr_type, + uint8_t **inita, uint8_t *init_addr_type, + int *ext_mode); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +int ble_ll_scan_update_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf, + bool *adva_present); + +/* Initialize the extended scanner when we start initiating */ +struct hci_ext_create_conn; +int ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc, + struct ble_ll_scan_sm **sm); + +/* Called to parse extended advertising*/ +struct ble_ll_aux_data *ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_scan); +void ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_scan); +void ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data); +#endif + +/* Called to halt currently running scan */ +void ble_ll_scan_halt(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_SCAN_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sched.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sched.h new file mode 100644 index 0000000..a614cf0 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sched.h @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_SCHED_ +#define H_BLE_LL_SCHED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Time per BLE scheduler slot */ +#define BLE_LL_SCHED_USECS_PER_SLOT (1250) +#define BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT (41) /* 1 tick = 30.517 usecs */ + +/* + * Worst case time needed for scheduled advertising item. This is the longest + * possible time to receive a scan request and send a scan response (with the + * appropriate IFS time between them). This number is calculated using the + * following formula: IFS + SCAN_REQ + IFS + SCAN_RSP = 150 + 176 + 150 + 376. + * Note: worst case time to tx adv, rx scan req and send scan rsp is 1228 usecs. + * This assumes maximum sized advertising PDU and scan response PDU. + * + * For connectable advertising events no scan request is allowed. In this case + * we just need to receive a connect request PDU: IFS + CONNECT_REQ = 150 + 352. + * Note: worst-case is 376 + 150 + 352 = 878 usecs + * + * NOTE: The advertising PDU transmit time is NOT included here since we know + * how long that will take (worst-case is 376 usecs). + */ +#define BLE_LL_SCHED_ADV_MAX_USECS (852) +#define BLE_LL_SCHED_DIRECT_ADV_MAX_USECS (502) +#define BLE_LL_SCHED_MAX_ADV_PDU_USECS (376) + +/* + * This is the offset from the start of the scheduled item until the actual + * tx/rx should occur, in ticks. + */ +extern uint8_t g_ble_ll_sched_offset_ticks; + +/* + * This is the number of slots needed to transmit and receive a maximum + * size PDU, including an IFS time before each. The actual time is + * 2120 usecs for tx/rx and 150 for IFS = 4540 usecs. + */ +#define BLE_LL_SCHED_MAX_TXRX_SLOT (4 * BLE_LL_SCHED_USECS_PER_SLOT) + +/* BLE scheduler errors */ +#define BLE_LL_SCHED_ERR_OVERLAP (1) + +/* Types of scheduler events */ +#define BLE_LL_SCHED_TYPE_ADV (1) +#define BLE_LL_SCHED_TYPE_SCAN (2) +#define BLE_LL_SCHED_TYPE_CONN (3) +#define BLE_LL_SCHED_TYPE_AUX_SCAN (4) +#define BLE_LL_SCHED_TYPE_DTM (5) +#define BLE_LL_SCHED_TYPE_PERIODIC (6) +#define BLE_LL_SCHED_TYPE_SYNC (7) + +/* Return values for schedule callback. */ +#define BLE_LL_SCHED_STATE_RUNNING (0) +#define BLE_LL_SCHED_STATE_DONE (1) + +/* Callback function */ +struct ble_ll_sched_item; +typedef int (*sched_cb_func)(struct ble_ll_sched_item *sch); +typedef void (*sched_remove_cb_func)(struct ble_ll_sched_item *sch); +/* + * Strict connection scheduling (for the master) is different than how + * connections are normally scheduled. With strict connection scheduling we + * introduce the concept of a "period". A period is a collection of slots. Each + * slot is 1.25 msecs in length. The number of slots in a period is determined + * by the syscfg value BLE_LL_CONN_INIT_SLOTS. A collection of periods is called + * an epoch. The length of an epoch is determined by the number of connections + * (BLE_MAX_CONNECTIONS plus BLE_LL_ADD_STRICT_SCHED_PERIODS). Connections + * will be scheduled at period boundaries. Any scanning/initiating/advertising + * will be done in unused periods, if possible. + */ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) +#define BLE_LL_SCHED_PERIODS (MYNEWT_VAL(BLE_MAX_CONNECTIONS) + \ + MYNEWT_VAL(BLE_LL_ADD_STRICT_SCHED_PERIODS)) + +struct ble_ll_sched_obj +{ + uint8_t sch_num_occ_periods; + uint32_t sch_occ_period_mask; + uint32_t sch_ticks_per_period; + uint32_t sch_ticks_per_epoch; + uint32_t sch_epoch_start; +}; + +extern struct ble_ll_sched_obj g_ble_ll_sched_data; + +/* + * XXX: TODO: + * -> How do we know epoch start is up to date? Not wrapped? + * -> for now, only do this with no more than 32 connections. + * -> Do not let initiating occur if no empty sched slots + */ +#endif + +/* + * Schedule item + * sched_type: This is the type of the schedule item. + * enqueued: Flag denoting if item is on the scheduler list. 0: no, 1:yes + * remainder: # of usecs from offset till tx/rx should occur + * txrx_offset: Number of ticks from start time until tx/rx should occur. + * + */ +struct ble_ll_sched_item +{ + uint8_t sched_type; + uint8_t enqueued; + uint8_t remainder; + uint32_t start_time; + uint32_t end_time; + void *cb_arg; + sched_cb_func sched_cb; + TAILQ_ENTRY(ble_ll_sched_item) link; +}; + +/* Initialize the scheduler */ +int ble_ll_sched_init(void); + +/* Remove item(s) from schedule */ +int ble_ll_sched_rmv_elem(struct ble_ll_sched_item *sch); + +void ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb); + +/* Schedule a new master connection */ +struct ble_ll_conn_sm; +int ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len); + +/* Schedule a new slave connection */ +int ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm); + +struct ble_ll_adv_sm; +typedef void ble_ll_sched_adv_new_cb(struct ble_ll_adv_sm *advsm, + uint32_t sch_start, void *arg); + +/* Schedule a new advertising event */ +int ble_ll_sched_adv_new(struct ble_ll_sched_item *sch, + ble_ll_sched_adv_new_cb cb, void *arg); + +/* Schedule periodic advertising event */ +int ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start, + bool after_overlap); + +int ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch, + uint32_t anchor_point, + uint8_t anchor_point_usecs, + uint32_t window_widening, int8_t phy_mode); +int ble_ll_sched_sync(struct ble_ll_sched_item *sch, + uint32_t beg_cputime, uint32_t rem_usecs, uint32_t offset, + int8_t phy_mode); + +/* Reschedule an advertising event */ +int ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start, + uint32_t max_delay_ticks); + +/* Reschedule and advertising pdu */ +int ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch); + +/* Reschedule a connection that had previously been scheduled or that is over */ +int ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm * connsm); + +/** + * Called to determine when the next scheduled event will occur. + * + * If there are not scheduled events this function returns 0; otherwise it + * returns 1 and *next_event_time is set to the start time of the next event. + * + * @param next_event_time cputime at which next scheduled event will occur + * + * @return int 0: No events are scheduled 1: there is an upcoming event + */ +int ble_ll_sched_next_time(uint32_t *next_event_time); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +struct ble_ll_scan_sm; +struct ble_ll_aux_data; +int ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr, + struct ble_ll_scan_sm *scansm, + struct ble_ll_aux_data *aux_scan); + +int ble_ll_sched_scan_req_over_aux_ptr(uint32_t chan, uint8_t phy_mode); +#endif + +/* Stop the scheduler */ +void ble_ll_sched_stop(void); + +#if MYNEWT_VAL(BLE_LL_DTM) +int ble_ll_sched_dtm(struct ble_ll_sched_item *sch); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_LL_SCHED_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sync.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sync.h new file mode 100644 index 0000000..96c52a9 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_sync.h @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_SYNC_ +#define H_BLE_LL_SYNC_ + +#include + +#include "nimble/nimble/include/nimble/ble.h" +#include "ble_ll_hci.h" +#include "ble_ll_conn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_ll_sync_sm; + +int ble_ll_sync_create(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_sync_cancel(ble_ll_hci_post_cmd_complete_cb *post_cmd_cb); +int ble_ll_sync_terminate(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_sync_list_add(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_sync_list_remove(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_sync_list_clear(void); +int ble_ll_sync_list_size(uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_sync_receive_enable(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_sync_transfer(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); + +void ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm, + const uint8_t *sync_ind, bool reports_disabled, + uint16_t max_skip, uint32_t sync_timeout); +void ble_ll_sync_transfer_disconnected(struct ble_ll_conn_sm *connsm); + +void ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, + int rpa_index, uint8_t sid, + struct ble_mbuf_hdr *rxhdr, + const uint8_t *syncinfo); + +int ble_ll_sync_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr); +int ble_ll_sync_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); +void ble_ll_sync_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr); +void ble_ll_sync_wfr_timer_exp(void); +void ble_ll_sync_halt(void); +void ble_ll_sync_rmvd_from_sched(struct ble_ll_sync_sm *sm); + +uint32_t ble_ll_sync_get_event_end_time(void); + +bool ble_ll_sync_enabled(void); + +void ble_ll_sync_reset(void); +void ble_ll_sync_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_SYNC_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_test.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_test.h new file mode 100644 index 0000000..32984c6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_test.h @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_LL_TEST_ +#define H_LL_TEST_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_ll_csa2_test_all(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_trace.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_trace.h new file mode 100644 index 0000000..f0af4de --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_trace.h @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_TRACE_ +#define H_BLE_LL_TRACE_ + +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_LL_TRACE_ID_SCHED 0 +#define BLE_LL_TRACE_ID_RX_START 1 +#define BLE_LL_TRACE_ID_RX_END 2 +#define BLE_LL_TRACE_ID_WFR_EXP 3 +#define BLE_LL_TRACE_ID_CTRL_RX 4 +#define BLE_LL_TRACE_ID_CONN_EV_START 5 +#define BLE_LL_TRACE_ID_CONN_EV_END 6 +#define BLE_LL_TRACE_ID_CONN_END 7 +#define BLE_LL_TRACE_ID_CONN_TX 8 +#define BLE_LL_TRACE_ID_CONN_RX 9 +#define BLE_LL_TRACE_ID_ADV_TXDONE 10 +#define BLE_LL_TRACE_ID_ADV_HALT 11 +#define BLE_LL_TRACE_ID_AUX_REF 12 +#define BLE_LL_TRACE_ID_AUX_UNREF 13 + +#if MYNEWT_VAL(BLE_LL_SYSVIEW) + +extern uint32_t ble_ll_trace_off; + +void ble_ll_trace_init(void); + +static inline void +ble_ll_trace_u32(unsigned id, uint32_t p1) +{ + os_trace_api_u32(ble_ll_trace_off + id, p1); +} + +static inline void +ble_ll_trace_u32x2(unsigned id, uint32_t p1, uint32_t p2) +{ + os_trace_api_u32x2(ble_ll_trace_off + id, p1, p2); +} + +static inline void +ble_ll_trace_u32x3(unsigned id, uint32_t p1, uint32_t p2, uint32_t p3) +{ + os_trace_api_u32x3(ble_ll_trace_off + id, p1, p2, p3); +} + +#else + +static inline void +ble_ll_trace_init(void) +{ +} + +static inline void +ble_ll_trace_u32(unsigned id, uint32_t p1) +{ +} + +static inline void +ble_ll_trace_u32x2(unsigned id, uint32_t p1, uint32_t p2) +{ +} + +static inline void +ble_ll_trace_u32x3(unsigned id, uint32_t p1, uint32_t p2, uint32_t p3) +{ +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_TRACE_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_utils.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_utils.h new file mode 100644 index 0000000..2483090 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_utils.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +uint32_t ble_ll_utils_calc_access_addr(void); +uint8_t ble_ll_utils_remapped_channel(uint8_t remap_index, const uint8_t *chanmap); +uint8_t ble_ll_utils_calc_dci_csa2(uint16_t event_cntr, uint16_t channel_id, + uint8_t num_used_chans, const uint8_t *chanmap); +uint8_t ble_ll_utils_calc_num_used_chans(const uint8_t *chanmap); +uint32_t ble_ll_utils_calc_window_widening(uint32_t anchor_point, + uint32_t last_anchor_point, + uint8_t master_sca); diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_whitelist.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_whitelist.h new file mode 100644 index 0000000..2d3b6c5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_ll_whitelist.h @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_WHITELIST_ +#define H_BLE_LL_WHITELIST_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Clear the whitelist */ +int ble_ll_whitelist_clear(void); + +/* Read the size of the whitelist */ +int ble_ll_whitelist_read_size(uint8_t *rspbuf, uint8_t *rsplen); + +/* Add a device to the whitelist */ +int ble_ll_whitelist_add(const uint8_t *cmdbuf, uint8_t len); + +/* Remove a device fromthe whitelist */ +int ble_ll_whitelist_rmv(const uint8_t *cmdbuf, uint8_t len); + +/* Enable whitelisting */ +void ble_ll_whitelist_enable(void); + +/* Disable whitelisting */ +void ble_ll_whitelist_disable(void); + +/* Boolean function returning true if address matches a whitelist entry */ +int ble_ll_whitelist_match(uint8_t *addr, uint8_t addr_type, int is_ident); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_WHITELIST_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy.h new file mode 100644 index 0000000..8a6fd17 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy.h @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_PHY_ +#define H_BLE_PHY_ + +#include "nimble/nimble/include/nimble/hci_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +struct os_mbuf; + +/* Channel/Frequency defintions */ +#define BLE_PHY_NUM_CHANS (40) +#define BLE_PHY_NUM_DATA_CHANS (37) +#define BLE_PHY_CHAN0_FREQ_MHZ (2402) +#define BLE_PHY_DATA_CHAN0_FREQ_MHZ (2404) +#define BLE_PHY_CHAN_SPACING_MHZ (2) +#define BLE_PHY_NUM_ADV_CHANS (3) +#define BLE_PHY_ADV_CHAN_START (37) + +/* Power */ +#define BLE_PHY_MAX_PWR_DBM (10) + +/* Deviation */ +#define BLE_PHY_DEV_KHZ (185) +#define BLE_PHY_BINARY_ZERO (-BLE_PHY_DEV) +#define BLE_PHY_BINARY_ONE (BLE_PHY_DEV) + +/* Max. clock drift */ +#define BLE_PHY_MAX_DRIFT_PPM (50) + +/* Data rate */ +#define BLE_PHY_BIT_RATE_BPS (1000000) + +/* Macros */ +#define BLE_IS_ADV_CHAN(chan) (chan >= BLE_PHY_ADV_CHAN_START) +#define BLE_IS_DATA_CHAN(chan) (chan < BLE_PHY_ADV_CHAN_START) + +/* PHY states */ +#define BLE_PHY_STATE_IDLE (0) +#define BLE_PHY_STATE_RX (1) +#define BLE_PHY_STATE_TX (2) + +/* BLE PHY transitions */ +#define BLE_PHY_TRANSITION_NONE (0) +#define BLE_PHY_TRANSITION_RX_TX (1) +#define BLE_PHY_TRANSITION_TX_RX (2) + +/* PHY error codes */ +#define BLE_PHY_ERR_RADIO_STATE (1) +#define BLE_PHY_ERR_INIT (2) +#define BLE_PHY_ERR_INV_PARAM (3) +#define BLE_PHY_ERR_NO_BUFS (4) +#define BLE_PHY_ERR_TX_LATE (5) +#define BLE_PHY_ERR_RX_LATE (6) + +/* Maximun PDU length. Includes LL header of 2 bytes and 255 bytes payload. */ +#define BLE_PHY_MAX_PDU_LEN (257) + +/* Wait for response timer */ +typedef void (*ble_phy_tx_end_func)(void *arg); + +/* Initialize the PHY */ +int ble_phy_init(void); + +/* Reset the PHY */ +int ble_phy_reset(void); + +/* Set the PHY channel */ +int ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit); + +/* Set transmit start time */ +int ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs); + +/* Set receive start time */ +int ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs); + +/* Set the transmit end callback and argument */ +void ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg); + +typedef uint8_t (*ble_phy_tx_pducb_t)(uint8_t *dptr, void *pducb_arg, + uint8_t *hdr_byte); + +/* Place the PHY into transmit mode */ +int ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans); + +/* Place the PHY into receive mode */ +int ble_phy_rx(void); + +/* Copies the received PHY buffer into the allocated pdu */ +void ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu); + +/* Set the transmit power */ +int ble_phy_txpwr_set(int dbm); + +/* Get highest allowed power from range */ +int ble_phy_txpower_round(int dbm); + +/* Get the transmit power */ +int ble_phy_txpwr_get(void); + +/* Set RX path power compensation value rounded to integer dB */ +void ble_phy_set_rx_pwr_compensation(int8_t compensation); + +/* Disable the PHY */ +void ble_phy_disable(void); + +#define BLE_PHY_WFR_ENABLE_RX (0) +#define BLE_PHY_WFR_ENABLE_TXRX (1) + +void ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs); + +/* Starts rf clock */ +void ble_phy_rfclk_enable(void); + +/* Stops rf clock */ +void ble_phy_rfclk_disable(void); + +/* + * Used to restart reception on same channel after wfr timer expiration or + * frame received. + */ +void ble_phy_restart_rx(void); + +/* Gets the current state of the PHY */ +int ble_phy_state_get(void); + +/* Gets current state of transceiver */ +uint8_t ble_phy_xcvr_state_get(void); + +/* Returns 'true' if a reception has started */ +int ble_phy_rx_started(void); + +/* + * Returns the maximum supported tx/rx PDU payload size, in bytes, for data + * channel PDUs (this does not apply to advertising channel PDUs). Note + * that the data channel PDU is composed of a 2-byte header, the payload, and + * an optional MIC. The maximum payload is 251 bytes. + */ +uint8_t ble_phy_max_data_pdu_pyld(void); + +/* Gets the current access address */ +uint32_t ble_phy_access_addr_get(void); + +/* Enable encryption */ +void ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master); + +/* Disable encryption */ +void ble_phy_encrypt_disable(void); + +/* Set the packet counters and dir used by LE encyption */ +void ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir); + +/* Enable phy resolving list */ +void ble_phy_resolv_list_enable(void); + +/* Disable phy resolving list */ +void ble_phy_resolv_list_disable(void); + +/* + * PHY mode values for 1M, 2M and Coded S=8 are the same as corresponding values + * of PHY. This makes conversion between 'phy' and 'phy_mode' easier and it also + * means that default coding for Coded will be S=8, unless explicitly translated + * to S=2. + */ +#define BLE_PHY_MODE_CODED_500KBPS (0) +#define BLE_PHY_MODE_1M (1) +#define BLE_PHY_MODE_2M (2) +#define BLE_PHY_MODE_CODED_125KBPS (3) + +/* The number of different modes */ +#define BLE_PHY_NUM_MODE (4) + +/* PHY numbers (compatible with HCI) */ +#define BLE_PHY_1M (BLE_HCI_LE_PHY_1M) +#define BLE_PHY_2M (BLE_HCI_LE_PHY_2M) +#define BLE_PHY_CODED (BLE_HCI_LE_PHY_CODED) + +/* PHY bitmasks (compatible with HCI) */ +#define BLE_PHY_MASK_1M (BLE_HCI_LE_PHY_1M_PREF_MASK) +#define BLE_PHY_MASK_2M (BLE_HCI_LE_PHY_2M_PREF_MASK) +#define BLE_PHY_MASK_CODED (BLE_HCI_LE_PHY_CODED_PREF_MASK) + +#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)) +uint32_t ble_phy_mode_pdu_start_off(int phy); +void ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode); +#else +#define ble_phy_mode_pdu_start_off(phy) (40) + +#endif + +int ble_phy_get_cur_phy(void); +static inline int ble_ll_phy_to_phy_mode(int phy, int phy_options) +{ + int phy_mode; + + /* + * 'phy' value can be used as 'phy_mode' value unless S=2 coding is explicitly + * required. By default we'll use S=2 for Coded. + */ + phy_mode = phy; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (phy == BLE_PHY_CODED && phy_options == BLE_HCI_LE_PHY_CODED_S2_PREF) { + phy_mode = BLE_PHY_MODE_CODED_500KBPS; + } +#endif + + return phy_mode; +} + +#if MYNEWT_VAL(BLE_LL_DTM) +void ble_phy_enable_dtm(void); +void ble_phy_disable_dtm(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_PHY_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy_trace.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy_trace.h new file mode 100644 index 0000000..c0dca7a --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/include/controller/ble_phy_trace.h @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_PHY_TRACE_ +#define H_BLE_PHY_TRACE_ + +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_PHY_TRACE_ID_START_TX 0 +#define BLE_PHY_TRACE_ID_START_RX 1 +#define BLE_PHY_TRACE_ID_DISABLE 2 + +#if MYNEWT_VAL(BLE_PHY_SYSVIEW) + +extern uint32_t ble_phy_trace_off; + +void ble_phy_trace_init(void); + +static inline void +ble_phy_trace_void(unsigned id) +{ + os_trace_api_void(ble_phy_trace_off + id); +} + +static inline void +ble_phy_trace_u32(unsigned id, uint32_t p1) +{ + os_trace_api_u32(ble_phy_trace_off + id, p1); +} + +static inline void +ble_phy_trace_u32x2(unsigned id, uint32_t p1, uint32_t p2) +{ + os_trace_api_u32x2(ble_phy_trace_off + id, p1, p2); +} + +static inline void +ble_phy_trace_u32x3(unsigned id, uint32_t p1, uint32_t p2, uint32_t p3) +{ + os_trace_api_u32x3(ble_phy_trace_off + id, p1, p2, p3); +} + +#else + +static inline void +ble_phy_trace_init(void) +{ +} + +static inline void +ble_phy_trace_void(unsigned id) +{ +} + +static inline void +ble_phy_trace_u32(unsigned id, uint32_t p1) +{ +} + +static inline void +ble_phy_trace_u32x2(unsigned id, uint32_t p1, uint32_t p2) +{ +} + +static inline void +ble_phy_trace_u32x3(unsigned id, uint32_t p1, uint32_t p2, uint32_t p3) +{ +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_PHY_TRACE_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll.c new file mode 100644 index 0000000..cc3d96d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll.c @@ -0,0 +1,1723 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/sysinit/sysinit.h" +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" +#include "nimble/porting/nimble/include/stats/stats.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_phy_trace.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_ll_sync.h" +#include "ble_ll_conn_priv.h" + +#if MYNEWT_VAL(BLE_LL_DTM) +#include "ble_ll_dtm_priv.h" +#endif + +/* XXX: + * + * 1) use the sanity task! + * 2) Need to figure out what to do with packets that we hand up that did + * not pass the filter policy for the given state. Currently I count all + * packets I think. Need to figure out what to do with this. + * 3) For the features defined, we need to conditionally compile code. + * 4) Should look into always disabled the wfr interrupt if we receive the + * start of a frame. Need to look at the various states to see if this is the + * right thing to do. + */ + +/* Supported states */ +#define BLE_LL_S_NCA (0x00000000001) +#define BLE_LL_S_SA (0x00000000002) +#define BLE_LL_S_CA (0x00000000004) +#define BLE_LL_S_HDCA (0x00000000008) +#define BLE_LL_S_PS (0x00000000010) +#define BLE_LL_S_AS (0x00000000020) +#define BLE_LL_S_INIT (0x00000000040) +#define BLE_LL_S_SLAVE (0x00000000080) +#define BLE_LL_S_NCA_PS (0x00000000100) +#define BLE_LL_S_SA_PS (0x00000000200) +#define BLE_LL_S_CA_PS (0x00000000400) +#define BLE_LL_S_HDCA_PS (0x00000000800) +#define BLE_LL_S_NCA_AS (0x00000001000) +#define BLE_LL_S_SA_AS (0x00000002000) +#define BLE_LL_S_CA_AS (0x00000004000) +#define BLE_LL_S_HDCA_AS (0x00000008000) +#define BLE_LL_S_NCA_INIT (0x00000010000) +#define BLE_LL_S_SA_INIT (0x00000020000) +#define BLE_LL_S_NCA_MASTER (0x00000040000) +#define BLE_LL_S_SA_MASTER (0x00000080000) +#define BLE_LL_S_NCA_SLAVE (0x00000100000) +#define BLE_LL_S_SA_SLAVE (0x00000200000) +#define BLE_LL_S_PS_INIT (0x00000400000) +#define BLE_LL_S_AS_INIT (0x00000800000) +#define BLE_LL_S_PS_MASTER (0x00001000000) +#define BLE_LL_S_AS_MASTER (0x00002000000) +#define BLE_LL_S_PS_SLAVE (0x00004000000) +#define BLE_LL_S_AS_SLAVE (0x00008000000) +#define BLE_LL_S_INIT_MASTER (0x00010000000) +#define BLE_LL_S_LDCA (0x00020000000) +#define BLE_LL_S_LDCA_PS (0x00040000000) +#define BLE_LL_S_LDCA_AS (0x00080000000) +#define BLE_LL_S_CA_INIT (0x00100000000) +#define BLE_LL_S_HDCA_INIT (0x00200000000) +#define BLE_LL_S_LDCA_INIT (0x00400000000) +#define BLE_LL_S_CA_MASTER (0x00800000000) +#define BLE_LL_S_HDCA_MASTER (0x01000000000) +#define BLE_LL_S_LDCA_MASTER (0x02000000000) +#define BLE_LL_S_CA_SLAVE (0x04000000000) +#define BLE_LL_S_HDCA_SLAVE (0x08000000000) +#define BLE_LL_S_LDCA_SLAVE (0x10000000000) +#define BLE_LL_S_INIT_SLAVE (0x20000000000) + +#define BLE_LL_SUPPORTED_STATES \ +( \ + BLE_LL_S_NCA | \ + BLE_LL_S_SA | \ + BLE_LL_S_CA | \ + BLE_LL_S_HDCA | \ + BLE_LL_S_PS | \ + BLE_LL_S_AS | \ + BLE_LL_S_INIT | \ + BLE_LL_S_SLAVE | \ + BLE_LL_S_NCA_PS | \ + BLE_LL_S_SA_PS | \ + BLE_LL_S_CA_PS | \ + BLE_LL_S_HDCA_PS | \ + BLE_LL_S_NCA_AS | \ + BLE_LL_S_SA_AS | \ + BLE_LL_S_CA_AS | \ + BLE_LL_S_HDCA_AS | \ + BLE_LL_S_NCA_INIT | \ + BLE_LL_S_SA_INIT | \ + BLE_LL_S_NCA_MASTER | \ + BLE_LL_S_SA_MASTER | \ + BLE_LL_S_NCA_SLAVE | \ + BLE_LL_S_SA_SLAVE | \ + BLE_LL_S_PS_INIT | \ + BLE_LL_S_AS_INIT | \ + BLE_LL_S_PS_MASTER | \ + BLE_LL_S_AS_MASTER | \ + BLE_LL_S_PS_SLAVE | \ + BLE_LL_S_AS_SLAVE | \ + BLE_LL_S_INIT_MASTER | \ + BLE_LL_S_LDCA | \ + BLE_LL_S_LDCA_PS | \ + BLE_LL_S_LDCA_AS | \ + BLE_LL_S_CA_INIT | \ + BLE_LL_S_HDCA_INIT | \ + BLE_LL_S_LDCA_INIT | \ + BLE_LL_S_CA_MASTER | \ + BLE_LL_S_HDCA_MASTER | \ + BLE_LL_S_LDCA_MASTER | \ + BLE_LL_S_CA_SLAVE | \ + BLE_LL_S_HDCA_SLAVE | \ + BLE_LL_S_LDCA_SLAVE | \ + BLE_LL_S_INIT_SLAVE) + +/* The global BLE LL data object */ +struct ble_ll_obj g_ble_ll_data; + +/* Global link layer statistics */ +STATS_SECT_DECL(ble_ll_stats) ble_ll_stats; +STATS_NAME_START(ble_ll_stats) + STATS_NAME(ble_ll_stats, hci_cmds) + STATS_NAME(ble_ll_stats, hci_cmd_errs) + STATS_NAME(ble_ll_stats, hci_events_sent) + STATS_NAME(ble_ll_stats, bad_ll_state) + STATS_NAME(ble_ll_stats, bad_acl_hdr) + STATS_NAME(ble_ll_stats, no_bufs) + STATS_NAME(ble_ll_stats, rx_adv_pdu_crc_ok) + STATS_NAME(ble_ll_stats, rx_adv_pdu_crc_err) + STATS_NAME(ble_ll_stats, rx_adv_bytes_crc_ok) + STATS_NAME(ble_ll_stats, rx_adv_bytes_crc_err) + STATS_NAME(ble_ll_stats, rx_data_pdu_crc_ok) + STATS_NAME(ble_ll_stats, rx_data_pdu_crc_err) + STATS_NAME(ble_ll_stats, rx_data_bytes_crc_ok) + STATS_NAME(ble_ll_stats, rx_data_bytes_crc_err) + STATS_NAME(ble_ll_stats, rx_adv_malformed_pkts) + STATS_NAME(ble_ll_stats, rx_adv_ind) + STATS_NAME(ble_ll_stats, rx_adv_direct_ind) + STATS_NAME(ble_ll_stats, rx_adv_nonconn_ind) + STATS_NAME(ble_ll_stats, rx_adv_ext_ind) + STATS_NAME(ble_ll_stats, rx_scan_reqs) + STATS_NAME(ble_ll_stats, rx_scan_rsps) + STATS_NAME(ble_ll_stats, rx_connect_reqs) + STATS_NAME(ble_ll_stats, rx_scan_ind) + STATS_NAME(ble_ll_stats, rx_aux_connect_rsp) + STATS_NAME(ble_ll_stats, adv_txg) + STATS_NAME(ble_ll_stats, adv_late_starts) + STATS_NAME(ble_ll_stats, adv_resched_pdu_fail) + STATS_NAME(ble_ll_stats, adv_drop_event) + STATS_NAME(ble_ll_stats, sched_state_conn_errs) + STATS_NAME(ble_ll_stats, sched_state_adv_errs) + STATS_NAME(ble_ll_stats, scan_starts) + STATS_NAME(ble_ll_stats, scan_stops) + STATS_NAME(ble_ll_stats, scan_req_txf) + STATS_NAME(ble_ll_stats, scan_req_txg) + STATS_NAME(ble_ll_stats, scan_rsp_txg) + STATS_NAME(ble_ll_stats, aux_missed_adv) + STATS_NAME(ble_ll_stats, aux_scheduled) + STATS_NAME(ble_ll_stats, aux_received) + STATS_NAME(ble_ll_stats, aux_fired_for_read) + STATS_NAME(ble_ll_stats, aux_allocated) + STATS_NAME(ble_ll_stats, aux_freed) + STATS_NAME(ble_ll_stats, aux_sched_cb) + STATS_NAME(ble_ll_stats, aux_conn_req_tx) + STATS_NAME(ble_ll_stats, aux_conn_rsp_tx) + STATS_NAME(ble_ll_stats, aux_conn_rsp_err) + STATS_NAME(ble_ll_stats, aux_scan_req_tx) + STATS_NAME(ble_ll_stats, aux_scan_rsp_err) + STATS_NAME(ble_ll_stats, aux_chain_cnt) + STATS_NAME(ble_ll_stats, aux_chain_err) + STATS_NAME(ble_ll_stats, aux_scan_drop) + STATS_NAME(ble_ll_stats, adv_evt_dropped) + STATS_NAME(ble_ll_stats, scan_timer_stopped) + STATS_NAME(ble_ll_stats, scan_timer_restarted) + STATS_NAME(ble_ll_stats, periodic_adv_drop_event) + STATS_NAME(ble_ll_stats, periodic_chain_drop_event) + STATS_NAME(ble_ll_stats, sync_event_failed) + STATS_NAME(ble_ll_stats, sync_received) + STATS_NAME(ble_ll_stats, sync_chain_failed) + STATS_NAME(ble_ll_stats, sync_missed_err) + STATS_NAME(ble_ll_stats, sync_crc_err) + STATS_NAME(ble_ll_stats, sync_rx_buf_err) + STATS_NAME(ble_ll_stats, sync_scheduled) + STATS_NAME(ble_ll_stats, sched_state_sync_errs) + STATS_NAME(ble_ll_stats, sched_invalid_pdu) +STATS_NAME_END(ble_ll_stats) + +static void ble_ll_event_rx_pkt(struct ble_npl_event *ev); +static void ble_ll_event_tx_pkt(struct ble_npl_event *ev); +static void ble_ll_event_dbuf_overflow(struct ble_npl_event *ev); + +#if MYNEWT + +/* The BLE LL task data structure */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_LL_STACK_SIZE (120) +#else +#define BLE_LL_STACK_SIZE (90) +#endif + +struct os_task g_ble_ll_task; + +OS_TASK_STACK_DEFINE(g_ble_ll_stack, BLE_LL_STACK_SIZE); + +#endif /* MYNEWT */ + +/** Our global device address (public) */ +uint8_t g_dev_addr[BLE_DEV_ADDR_LEN]; + +/** Our random address */ +uint8_t g_random_addr[BLE_DEV_ADDR_LEN]; + +/** Our supported features which can be controller by the host */ +uint64_t g_ble_ll_supported_host_features = 0; + +static const uint16_t g_ble_ll_pdu_header_tx_time[BLE_PHY_NUM_MODE] = +{ + [BLE_PHY_MODE_1M] = + (BLE_LL_PREAMBLE_LEN + BLE_LL_ACC_ADDR_LEN + BLE_LL_CRC_LEN + + BLE_LL_PDU_HDR_LEN) << 3, + [BLE_PHY_MODE_2M] = + (BLE_LL_PREAMBLE_LEN * 2 + BLE_LL_ACC_ADDR_LEN + BLE_LL_CRC_LEN + + BLE_LL_PDU_HDR_LEN) << 2, + /* For Coded PHY we have exact TX times provided by specification: + * - Preamble, Access Address, CI, TERM1 (always coded as S=8) + * - PDU, CRC, TERM2 (coded as S=2 or S=8) + * (Vol 6, Part B, 2.2). + */ + [BLE_PHY_MODE_CODED_125KBPS] = + (80 + 256 + 16 + 24 + 8 * (BLE_LL_PDU_HDR_LEN * 8 + 24 + 3)), + [BLE_PHY_MODE_CODED_500KBPS] = + (80 + 256 + 16 + 24 + 2 * (BLE_LL_PDU_HDR_LEN * 8 + 24 + 3)), +}; + +/** + * Counts the number of advertising PDU's received, by type. For advertising + * PDU's that contain a destination address, we still count these packets even + * if they are not for us. + * + * @param pdu_type + */ +static void +ble_ll_count_rx_adv_pdus(uint8_t pdu_type) +{ + /* Count received packet types */ + switch (pdu_type) { + case BLE_ADV_PDU_TYPE_ADV_EXT_IND: + STATS_INC(ble_ll_stats, rx_adv_ext_ind); + break; + case BLE_ADV_PDU_TYPE_ADV_IND: + STATS_INC(ble_ll_stats, rx_adv_ind); + break; + case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: + STATS_INC(ble_ll_stats, rx_adv_direct_ind); + break; + case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND: + STATS_INC(ble_ll_stats, rx_adv_nonconn_ind); + break; + case BLE_ADV_PDU_TYPE_SCAN_REQ: + STATS_INC(ble_ll_stats, rx_scan_reqs); + break; + case BLE_ADV_PDU_TYPE_SCAN_RSP: + STATS_INC(ble_ll_stats, rx_scan_rsps); + break; + case BLE_ADV_PDU_TYPE_CONNECT_IND: + STATS_INC(ble_ll_stats, rx_connect_reqs); + break; + case BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP: + STATS_INC(ble_ll_stats, rx_aux_connect_rsp); + break; + case BLE_ADV_PDU_TYPE_ADV_SCAN_IND: + STATS_INC(ble_ll_stats, rx_scan_ind); + break; + default: + break; + } +} + +struct os_mbuf * +ble_ll_rxpdu_alloc(uint16_t len) +{ + struct os_mbuf *om_ret; + struct os_mbuf *om_next; + struct os_mbuf *om; + struct os_mbuf_pkthdr *pkthdr; + uint16_t databuf_len; + int rem_len; + + /* + * Make sure that data in mbuf are word-aligned with and without packet + * header. This is essential for proper and quick copying of received PDUs + * into mbufs. + */ + _Static_assert((offsetof(struct os_mbuf, om_data) & 3) == 0, + "Unaligned om_data"); + _Static_assert(((offsetof(struct os_mbuf, om_data) + + sizeof(struct os_mbuf_pkthdr) + + sizeof(struct ble_mbuf_hdr)) & 3) == 0, + "Unaligned data trailing packet header"); + + om_ret = os_msys_get_pkthdr(len, sizeof(struct ble_mbuf_hdr)); + if (!om_ret) { + goto rxpdu_alloc_fail; + } + + /* Set complete PDU length in packet header */ + pkthdr = OS_MBUF_PKTHDR(om_ret); + pkthdr->omp_len = len; + + rem_len = len; + + /* + * Calculate length of data in memory block. We assume length is rounded + * down to word size so PHY can do word-size aligned data copy to mbufs + * (except for last one) and leave remainder unused. + * + * Note that there likely won't be any remainder here since all pools have + * block size aligned to word size anyway. + */ + databuf_len = om_ret->om_omp->omp_databuf_len & ~3; + + /* + * First mbuf can store less data due to packet header. Also we reserve one + * word for leading space to prepend header when necessary (like for data + * PDU before handing over to HCI) + */ + om_ret->om_data += 4; + rem_len -= databuf_len - om_ret->om_pkthdr_len - 4; + + /* Allocate and chain mbufs until there's enough space to store complete PDU */ + om = om_ret; + while (rem_len > 0) { + om_next = os_msys_get(rem_len, 0); + if (!om_next) { + os_mbuf_free_chain(om_ret); + goto rxpdu_alloc_fail; + } + + SLIST_NEXT(om, om_next) = om_next; + om = om_next; + + rem_len -= databuf_len; + } + + return om_ret; + +rxpdu_alloc_fail: + STATS_INC(ble_ll_stats, no_bufs); + return NULL; +} + +int +ble_ll_chk_txrx_octets(uint16_t octets) +{ + int rc; + + if ((octets < BLE_LL_CONN_SUPP_BYTES_MIN) || + (octets > BLE_LL_CONN_SUPP_BYTES_MAX)) { + rc = 0; + } else { + rc = 1; + } + + return rc; +} + +int +ble_ll_chk_txrx_time(uint16_t time) +{ + int rc; + + if ((time < BLE_LL_CONN_SUPP_TIME_MIN) || + (time > BLE_LL_CONN_SUPP_TIME_MAX)) { + rc = 0; + } else { + rc = 1; + } + + return rc; +} + +/** + * Checks to see if the address is a resolvable private address. + * + * NOTE: the addr_type parameter will be 0 if the address is public; + * any other value is random (all non-zero values). + * + * @param addr + * @param addr_type Public (zero) or Random (non-zero) address + * + * @return int + */ +int +ble_ll_is_rpa(const uint8_t *addr, uint8_t addr_type) +{ + int rc; + + if (addr_type && ((addr[5] & 0xc0) == 0x40)) { + rc = 1; + } else { + rc = 0; + } + return rc; +} + +int +ble_ll_addr_is_id(uint8_t *addr, uint8_t addr_type) +{ + return !addr_type || ((addr[5] & 0xc0) == 0xc0); +} + +int +ble_ll_addr_subtype(const uint8_t *addr, uint8_t addr_type) +{ + if (!addr_type) { + return BLE_LL_ADDR_SUBTYPE_IDENTITY; + } + + switch (addr[5] >> 6) { + case 0: + return BLE_LL_ADDR_SUBTYPE_NRPA; /* NRPA */ + case 1: + return BLE_LL_ADDR_SUBTYPE_RPA; /* RPA */ + default: + return BLE_LL_ADDR_SUBTYPE_IDENTITY; /* static random */ + } +} + +int +ble_ll_is_valid_public_addr(const uint8_t *addr) +{ + int i; + + for (i = 0; i < BLE_DEV_ADDR_LEN; ++i) { + if (addr[i]) { + return 1; + } + } + + return 0; +} + +/* Checks to see that the device is a valid random address */ +int +ble_ll_is_valid_random_addr(const uint8_t *addr) +{ + int i; + int rc; + uint16_t sum; + uint8_t addr_type; + + /* Make sure all bits are neither one nor zero */ + sum = 0; + for (i = 0; i < (BLE_DEV_ADDR_LEN -1); ++i) { + sum += addr[i]; + } + sum += addr[5] & 0x3f; + + if ((sum == 0) || (sum == ((5*255) + 0x3f))) { + return 0; + } + + /* Get the upper two bits of the address */ + rc = 1; + addr_type = addr[5] & 0xc0; + if (addr_type == 0xc0) { + /* Static random address. No other checks needed */ + } else if (addr_type == 0x40) { + /* Resolvable */ + sum = addr[3] + addr[4] + (addr[5] & 0x3f); + if ((sum == 0) || (sum == (255 + 255 + 0x3f))) { + rc = 0; + } + } else if (addr_type == 0) { + /* non-resolvable. Cant be equal to public */ + if (!memcmp(g_dev_addr, addr, BLE_DEV_ADDR_LEN)) { + rc = 0; + } + } else { + /* Invalid upper two bits */ + rc = 0; + } + + return rc; +} +int +ble_ll_is_valid_own_addr_type(uint8_t own_addr_type, const uint8_t *random_addr) +{ + int rc; + + switch (own_addr_type) { + case BLE_HCI_ADV_OWN_ADDR_PUBLIC: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + case BLE_HCI_ADV_OWN_ADDR_PRIV_PUB: +#endif + rc = ble_ll_is_valid_public_addr(g_dev_addr); + break; + case BLE_HCI_ADV_OWN_ADDR_RANDOM: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + case BLE_HCI_ADV_OWN_ADDR_PRIV_RAND: +#endif + rc = ble_ll_is_valid_random_addr(random_addr); + break; + default: + rc = 0; + break; + } + + return rc; +} + +int +ble_ll_set_public_addr(const uint8_t *addr) +{ + memcpy(g_dev_addr, addr, BLE_DEV_ADDR_LEN); + + return BLE_ERR_SUCCESS; +} + +/** + * Called from the HCI command parser when the set random address command + * is received. + * + * Context: Link Layer task (HCI command parser) + * + * @param addr Pointer to address + * + * @return int 0: success + */ +int +ble_ll_set_random_addr(const uint8_t *cmdbuf, uint8_t len, bool hci_adv_ext) +{ + const struct ble_hci_le_set_rand_addr_cp *cmd = (const void *) cmdbuf; + + if (len < sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If the Host issues this command when scanning or legacy advertising is + * enabled, the Controller shall return the error code Command Disallowed. + * + * Test specification extends this also to initiating. + */ + + if (g_ble_ll_conn_create_sm || ble_ll_scan_enabled() || + (!hci_adv_ext && ble_ll_adv_enabled())) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (!ble_ll_is_valid_random_addr(cmd->addr)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + memcpy(g_random_addr, cmd->addr, BLE_DEV_ADDR_LEN); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* For instance 0 we need same address if legacy advertising might be + * used. If extended advertising is in use than this command doesn't + * affect instance 0. + */ + if (!hci_adv_ext) + ble_ll_adv_set_random_addr(cmd->addr, 0); +#endif + + return BLE_ERR_SUCCESS; +} + +/** + * Checks to see if an address is our device address (either public or + * random) + * + * @param addr + * @param addr_type + * + * @return int 0: not our device address. 1: is our device address + */ +int +ble_ll_is_our_devaddr(uint8_t *addr, int addr_type) +{ + int rc; + uint8_t *our_addr; + + if (addr_type) { + our_addr = g_random_addr; + } else { + our_addr = g_dev_addr; + } + + rc = 0; + if (!memcmp(our_addr, addr, BLE_DEV_ADDR_LEN)) { + rc = 1; + } + + return rc; +} + +/** + * Get identity address + * + * @param addr_type Random (1). Public(0) + * + * @return pointer to identity address of given type. + */ +uint8_t* +ble_ll_get_our_devaddr(uint8_t addr_type) +{ + if (addr_type) { + return g_random_addr; + } + + return g_dev_addr; +} + +/** + * Wait for response timeout function + * + * Context: interrupt (ble scheduler) + * + * @param arg + */ +void +ble_ll_wfr_timer_exp(void *arg) +{ + int rx_start; + uint8_t lls; + + rx_start = ble_phy_rx_started(); + lls = g_ble_ll_data.ll_state; + + ble_ll_trace_u32x3(BLE_LL_TRACE_ID_WFR_EXP, lls, ble_phy_xcvr_state_get(), + (uint32_t)rx_start); + + /* If we have started a reception, there is nothing to do here */ + if (!rx_start) { + switch (lls) { + case BLE_LL_STATE_ADV: + ble_ll_adv_wfr_timer_exp(); + break; + case BLE_LL_STATE_CONNECTION: + ble_ll_conn_wfr_timer_exp(); + break; + case BLE_LL_STATE_SCANNING: + ble_ll_scan_wfr_timer_exp(); + break; + case BLE_LL_STATE_INITIATING: + ble_ll_conn_init_wfr_timer_exp(); + break; +#if MYNEWT_VAL(BLE_LL_DTM) + case BLE_LL_STATE_DTM: + ble_ll_dtm_wfr_timer_exp(); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_LL_STATE_SYNC: + ble_ll_sync_wfr_timer_exp(); + break; +#endif + default: + break; + } + } +} + +/** + * ll tx pkt in proc + * + * Process ACL data packet input from host + * + * Context: Link layer task + * + */ +static void +ble_ll_tx_pkt_in(void) +{ + uint16_t handle; + uint16_t length; + uint16_t pb; + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + + /* Drain all packets off the queue */ + while (STAILQ_FIRST(&g_ble_ll_data.ll_tx_pkt_q)) { + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(&g_ble_ll_data.ll_tx_pkt_q); + om = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf)); + + /* Remove from queue */ + STAILQ_REMOVE_HEAD(&g_ble_ll_data.ll_tx_pkt_q, omp_next); + + /* Strip HCI ACL header to get handle and length */ + handle = get_le16(om->om_data); + length = get_le16(om->om_data + 2); + os_mbuf_adj(om, sizeof(struct hci_data_hdr)); + + /* Do some basic error checking */ + pb = handle & 0x3000; + if ((pkthdr->omp_len != length) || (pb > 0x1000) || (length == 0)) { + /* This is a bad ACL packet. Count a stat and free it */ + STATS_INC(ble_ll_stats, bad_acl_hdr); + os_mbuf_free_chain(om); + continue; + } + + /* Hand to connection state machine */ + ble_ll_conn_tx_pkt_in(om, handle, length); + } +} + +/** + * Count Link Layer statistics for received PDUs + * + * Context: Link layer task + * + * @param hdr + * @param len + */ +static void +ble_ll_count_rx_stats(struct ble_mbuf_hdr *hdr, uint16_t len, uint8_t pdu_type) +{ + uint8_t crcok; + bool connection_data; + + crcok = BLE_MBUF_HDR_CRC_OK(hdr); + connection_data = (BLE_MBUF_HDR_RX_STATE(hdr) == BLE_LL_STATE_CONNECTION); + +#if MYNEWT_VAL(BLE_LL_DTM) + /* Reuse connection stats for DTM */ + if (!connection_data) { + connection_data = (BLE_MBUF_HDR_RX_STATE(hdr) == BLE_LL_STATE_DTM); + } +#endif + + if (crcok) { + if (connection_data) { + STATS_INC(ble_ll_stats, rx_data_pdu_crc_ok); + STATS_INCN(ble_ll_stats, rx_data_bytes_crc_ok, len); + } else { + STATS_INC(ble_ll_stats, rx_adv_pdu_crc_ok); + STATS_INCN(ble_ll_stats, rx_adv_bytes_crc_ok, len); + ble_ll_count_rx_adv_pdus(pdu_type); + } + } else { + if (connection_data) { + STATS_INC(ble_ll_stats, rx_data_pdu_crc_err); + STATS_INCN(ble_ll_stats, rx_data_bytes_crc_err, len); + } else { + STATS_INC(ble_ll_stats, rx_adv_pdu_crc_err); + STATS_INCN(ble_ll_stats, rx_adv_bytes_crc_err, len); + } + } +} + +/** + * ll rx pkt in + * + * Process received packet from PHY. + * + * Context: Link layer task + * + */ +static void +ble_ll_rx_pkt_in(void) +{ + os_sr_t sr; + uint8_t pdu_type; + uint8_t *rxbuf; + struct os_mbuf_pkthdr *pkthdr; + struct ble_mbuf_hdr *ble_hdr; + struct os_mbuf *m; + + /* Drain all packets off the queue */ + while (STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q)) { + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q); + m = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf)); + + /* Remove from queue */ + OS_ENTER_CRITICAL(sr); + STAILQ_REMOVE_HEAD(&g_ble_ll_data.ll_rx_pkt_q, omp_next); + OS_EXIT_CRITICAL(sr); + + /* Note: pdu type wont get used unless this is an advertising pdu */ + ble_hdr = BLE_MBUF_HDR_PTR(m); + rxbuf = m->om_data; + pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; + ble_ll_count_rx_stats(ble_hdr, pkthdr->omp_len, pdu_type); + + /* Process the data or advertising pdu */ + /* Process the PDU */ + switch (BLE_MBUF_HDR_RX_STATE(ble_hdr)) { + case BLE_LL_STATE_CONNECTION: + ble_ll_conn_rx_data_pdu(m, ble_hdr); + /* m is going to be free by function above */ + m = NULL; + break; + case BLE_LL_STATE_ADV: + ble_ll_adv_rx_pkt_in(pdu_type, rxbuf, ble_hdr); + break; + case BLE_LL_STATE_SCANNING: + ble_ll_scan_rx_pkt_in(pdu_type, m, ble_hdr); + break; + case BLE_LL_STATE_INITIATING: + ble_ll_init_rx_pkt_in(pdu_type, rxbuf, ble_hdr); + break; +#if MYNEWT_VAL(BLE_LL_DTM) + case BLE_LL_STATE_DTM: + ble_ll_dtm_rx_pkt_in(m, ble_hdr); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_LL_STATE_SYNC: + ble_ll_sync_rx_pkt_in(m, ble_hdr); + break; +#endif + default: + /* Any other state should never occur */ + STATS_INC(ble_ll_stats, bad_ll_state); + break; + } + if (m) { + /* Free the packet buffer */ + os_mbuf_free_chain(m); + } + } +} + +/** + * Called to put a packet on the Link Layer receive packet queue. + * + * @param rxpdu Pointer to received PDU + */ +void +ble_ll_rx_pdu_in(struct os_mbuf *rxpdu) +{ + struct os_mbuf_pkthdr *pkthdr; + + pkthdr = OS_MBUF_PKTHDR(rxpdu); + STAILQ_INSERT_TAIL(&g_ble_ll_data.ll_rx_pkt_q, pkthdr, omp_next); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_rx_pkt_ev); +} + +/** + * Called to put a packet on the Link Layer transmit packet queue. + * + * @param txpdu Pointer to transmit packet + */ +void +ble_ll_acl_data_in(struct os_mbuf *txpkt) +{ + os_sr_t sr; + struct os_mbuf_pkthdr *pkthdr; + + pkthdr = OS_MBUF_PKTHDR(txpkt); + OS_ENTER_CRITICAL(sr); + STAILQ_INSERT_TAIL(&g_ble_ll_data.ll_tx_pkt_q, pkthdr, omp_next); + OS_EXIT_CRITICAL(sr); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_tx_pkt_ev); +} + +/** + * Called to post event to Link Layer when a data buffer overflow has + * occurred. + * + * Context: Interrupt + * + */ +void +ble_ll_data_buffer_overflow(void) +{ + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_dbuf_overflow_ev); +} + +/** + * Called when a HW error occurs. + * + * Context: Interrupt + */ +void +ble_ll_hw_error(void) +{ + ble_npl_callout_reset(&g_ble_ll_data.ll_hw_err_timer, 0); +} + +/** + * Called when the HW error timer expires. + * + * @param arg + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static void +ble_ll_hw_err_timer_cb(struct ble_npl_event *ev) +{ + if (ble_ll_hci_ev_hw_err(BLE_HW_ERR_HCI_SYNC_LOSS)) { + /* + * Restart callout if failed to allocate event. Try to allocate an + * event every 50 milliseconds (or each OS tick if a tick is longer + * than 100 msecs). + */ + ble_npl_callout_reset(&g_ble_ll_data.ll_hw_err_timer, + ble_npl_time_ms_to_ticks32(50)); + } +} +#pragma GCC diagnostic pop + +/** + * Called upon start of received PDU + * + * Context: Interrupt + * + * @param rxpdu + * chan + * + * @return int + * < 0: A frame we dont want to receive. + * = 0: Continue to receive frame. Dont go from rx to tx + * > 0: Continue to receive frame and go from rx to tx when done + */ +int +ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *rxhdr) +{ + int rc; + uint8_t pdu_type; + + /* Advertising channel PDU */ + pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; + + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_RX_START, g_ble_ll_data.ll_state, + pdu_type); + + switch (g_ble_ll_data.ll_state) { + case BLE_LL_STATE_CONNECTION: + rc = ble_ll_conn_rx_isr_start(rxhdr, ble_phy_access_addr_get()); + break; + case BLE_LL_STATE_ADV: + rc = ble_ll_adv_rx_isr_start(pdu_type); + break; + case BLE_LL_STATE_INITIATING: + rc = ble_ll_init_rx_isr_start(pdu_type, rxhdr); + break; + case BLE_LL_STATE_SCANNING: + rc = ble_ll_scan_rx_isr_start(pdu_type, &rxhdr->rxinfo.flags); + break; +#if MYNEWT_VAL(BLE_LL_DTM) + case BLE_LL_STATE_DTM: + rc = ble_ll_dtm_rx_isr_start(rxhdr, ble_phy_access_addr_get()); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_LL_STATE_SYNC: + rc = ble_ll_sync_rx_isr_start(pdu_type, rxhdr); + break; +#endif + default: + /* Should not be in this state! */ + rc = -1; + STATS_INC(ble_ll_stats, bad_ll_state); + break; + } + + return rc; +} + +/** + * Called by the PHY when a receive packet has ended. + * + * NOTE: Called from interrupt context! + * + * @param rxbuf Pointer to received PDU data + * rxhdr Pointer to BLE header of received mbuf + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + int rc; + int badpkt; + uint8_t pdu_type; + uint8_t len; + uint8_t crcok; + struct os_mbuf *rxpdu; + + /* Get CRC status from BLE header */ + crcok = BLE_MBUF_HDR_CRC_OK(rxhdr); + + /* Get advertising PDU type and length */ + pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; + len = rxbuf[1]; + + ble_ll_trace_u32x3(BLE_LL_TRACE_ID_RX_END, pdu_type, len, + rxhdr->rxinfo.flags); + +#if MYNEWT_VAL(BLE_LL_DTM) + if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_DTM) { + rc = ble_ll_dtm_rx_isr_end(rxbuf, rxhdr); + return rc; + } +#endif + + if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_CONNECTION) { + rc = ble_ll_conn_rx_isr_end(rxbuf, rxhdr); + return rc; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_SYNC) { + rc = ble_ll_sync_rx_isr_end(rxbuf, rxhdr); + return rc; + } +#endif + + /* If the CRC checks, make sure lengths check! */ + badpkt = 0; + if (crcok) { + switch (pdu_type) { + case BLE_ADV_PDU_TYPE_SCAN_REQ: + case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: + if (len != BLE_SCAN_REQ_LEN) { + badpkt = 1; + } + break; + case BLE_ADV_PDU_TYPE_SCAN_RSP: + case BLE_ADV_PDU_TYPE_ADV_IND: + case BLE_ADV_PDU_TYPE_ADV_SCAN_IND: + case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND: + if ((len < BLE_DEV_ADDR_LEN) || (len > BLE_ADV_SCAN_IND_MAX_LEN)) { + badpkt = 1; + } + break; + case BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP: + break; + case BLE_ADV_PDU_TYPE_ADV_EXT_IND: + break; + case BLE_ADV_PDU_TYPE_CONNECT_IND: + if (len != BLE_CONNECT_REQ_LEN) { + badpkt = 1; + } + break; + default: + badpkt = 1; + break; + } + + /* If this is a malformed packet, just kill it here */ + if (badpkt) { + STATS_INC(ble_ll_stats, rx_adv_malformed_pkts); + } + } + + /* Hand packet to the appropriate state machine (if crc ok) */ + rxpdu = NULL; + switch (BLE_MBUF_HDR_RX_STATE(rxhdr)) { + case BLE_LL_STATE_ADV: + if (!badpkt) { + rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN); + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + } + } + rc = ble_ll_adv_rx_isr_end(pdu_type, rxpdu, crcok); + break; + case BLE_LL_STATE_SCANNING: + if (!badpkt) { + rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN); + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + } + } + rc = ble_ll_scan_rx_isr_end(rxpdu, crcok); + break; + case BLE_LL_STATE_INITIATING: + rc = ble_ll_init_rx_isr_end(rxbuf, crcok, rxhdr); + break; + default: + rc = -1; + STATS_INC(ble_ll_stats, bad_ll_state); + break; + } + + /* Hand packet up to higher layer (regardless of CRC failure) */ + if (rxpdu) { + ble_ll_rx_pdu_in(rxpdu); + } + + return rc; +} + +uint8_t +ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct os_mbuf *txpdu; + struct ble_mbuf_hdr *ble_hdr; + + txpdu = pducb_arg; + BLE_LL_ASSERT(txpdu); + ble_hdr = BLE_MBUF_HDR_PTR(txpdu); + + os_mbuf_copydata(txpdu, ble_hdr->txinfo.offset, ble_hdr->txinfo.pyld_len, + dptr); + + *hdr_byte = ble_hdr->txinfo.hdr_byte; + + return ble_hdr->txinfo.pyld_len; +} + +uint8_t +ble_ll_tx_flat_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct os_mbuf *txpdu; + struct ble_mbuf_hdr *ble_hdr; + + txpdu = pducb_arg; + BLE_LL_ASSERT(txpdu); + ble_hdr = BLE_MBUF_HDR_PTR(txpdu); + + memcpy(dptr, txpdu->om_data, ble_hdr->txinfo.pyld_len); + + *hdr_byte = ble_hdr->txinfo.hdr_byte; + + return ble_hdr->txinfo.pyld_len; +} + +static void +ble_ll_event_rx_pkt(struct ble_npl_event *ev) +{ + ble_ll_rx_pkt_in(); +} + +static void +ble_ll_event_tx_pkt(struct ble_npl_event *ev) +{ + ble_ll_tx_pkt_in(); +} + +static void +ble_ll_event_dbuf_overflow(struct ble_npl_event *ev) +{ + ble_ll_hci_ev_databuf_overflow(); +} + +static void +ble_ll_event_comp_pkts(struct ble_npl_event *ev) +{ + ble_ll_conn_num_comp_pkts_event_send(NULL); +} + +/** + * Link Layer task. + * + * This is the task that runs the Link Layer. + * + * @param arg + */ +void +ble_ll_task(void *arg) +{ + struct ble_npl_event *ev; + + /* Init ble phy */ + ble_phy_init(); + + /* Set output power to 1mW (0 dBm) */ + ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM)); + + /* Register callback for transport */ + ble_hci_trans_cfg_ll(ble_ll_hci_cmd_rx, NULL, ble_ll_hci_acl_rx, NULL); + + /* Tell the host that we are ready to receive packets */ + ble_ll_hci_send_noop(); + + ble_ll_rand_start(); + + while (1) { + ev = ble_npl_eventq_get(&g_ble_ll_data.ll_evq, BLE_NPL_TIME_FOREVER); + assert(ev); + ble_npl_event_run(ev); + } +} + +/** + * ble ll state set + * + * Called to set the current link layer state. + * + * Context: Interrupt and Link Layer task + * + * @param ll_state + */ +void +ble_ll_state_set(uint8_t ll_state) +{ + g_ble_ll_data.ll_state = ll_state; +} + +/** + * ble ll state get + * + * Called to get the current link layer state. + * + * Context: Link Layer task (can be called from interrupt context though). + * + * @return ll_state + */ +uint8_t +ble_ll_state_get(void) +{ + return g_ble_ll_data.ll_state; +} + +/** + * ble ll event send + * + * Send an event to the Link Layer task + * + * @param ev Event to add to the Link Layer event queue. + */ +void +ble_ll_event_send(struct ble_npl_event *ev) +{ + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, ev); +} + +/** + * Returns the features supported by the link layer + * + * @return uint8_t bitmask of supported features. + */ +uint64_t +ble_ll_read_supp_states(void) +{ + return BLE_LL_SUPPORTED_STATES; +} + +/** + * Returns the features supported by the link layer + * + * @return uint64_t bitmask of supported features. + */ +uint64_t +ble_ll_read_supp_features(void) +{ + return g_ble_ll_data.ll_supp_features; +} + +/** + * Sets the features controlled by the host. + * + * @return HCI command status + */ +int +ble_ll_set_host_feat(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_host_feat_cp *cmd = (const void *) cmdbuf; + uint64_t mask; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!SLIST_EMPTY(&g_ble_ll_conn_active_list)) { + return BLE_ERR_CMD_DISALLOWED; + } + + if ((cmd->bit_num > 0x3F) || (cmd->val > 1)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + mask = (uint64_t)1 << (cmd->bit_num); + if (!(mask & BLE_LL_HOST_CONTROLLED_FEATURES)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!(mask & g_ble_ll_supported_host_features)) { + return BLE_ERR_UNSUPPORTED; + } + + if (cmd->val == 0) { + g_ble_ll_data.ll_supp_features &= ~(mask); + } else { + g_ble_ll_data.ll_supp_features |= mask; + } + + return BLE_ERR_SUCCESS; +} +/** + * Flush a link layer packet queue. + * + * @param pktq + */ +static void +ble_ll_flush_pkt_queue(struct ble_ll_pkt_q *pktq) +{ + struct os_mbuf_pkthdr *pkthdr; + struct os_mbuf *om; + + /* FLush all packets from Link layer queues */ + while (STAILQ_FIRST(pktq)) { + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(pktq); + om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + + /* Remove from queue and free the mbuf */ + STAILQ_REMOVE_HEAD(pktq, omp_next); + os_mbuf_free_chain(om); + } +} + +/** + * Called to initialize a mbuf used by the controller + * + * NOTE: this is only used when the mbuf is created by the controller; + * it should not be used for data packets (ACL data packets) that come from + * the host. This routine assumes that the entire pdu length can fit in + * one mbuf contiguously. + * + * @param m + * @param pdulen + * @param hdr + */ +void +ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr) +{ + struct ble_mbuf_hdr *ble_hdr; + + /* Set mbuf length and packet length */ + m->om_len = pdulen; + OS_MBUF_PKTHDR(m)->omp_len = pdulen; + + /* Set BLE transmit header */ + ble_hdr = BLE_MBUF_HDR_PTR(m); + ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.offset = 0; + ble_hdr->txinfo.pyld_len = pdulen; + ble_hdr->txinfo.hdr_byte = hdr; +} + +/** + * Called to reset the controller. This performs a "software reset" of the link + * layer; it does not perform a HW reset of the controller nor does it reset + * the HCI interface. + * + * Context: Link Layer task (HCI command) + * + * @return int The ble error code to place in the command complete event that + * is returned when this command is issued. + */ +int +ble_ll_reset(void) +{ + int rc; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + ble_phy_disable(); + ble_ll_sched_stop(); + ble_ll_scan_reset(); + ble_ll_rfmgmt_reset(); + OS_EXIT_CRITICAL(sr); + + /* Stop any advertising */ + ble_ll_adv_reset(); + +#if MYNEWT_VAL(BLE_LL_DTM) + ble_ll_dtm_reset(); +#endif + + /* Stop sync */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + ble_ll_sync_reset(); +#endif + + /* FLush all packets from Link layer queues */ + ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_tx_pkt_q); + ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_rx_pkt_q); + + /* Reset LL stats */ + STATS_RESET(ble_ll_stats); + + /* Reset any preferred PHYs */ + g_ble_ll_data.ll_pref_tx_phys = 0; + g_ble_ll_data.ll_pref_rx_phys = 0; + + /* Reset connection module */ + ble_ll_conn_module_reset(); + + /* All this does is re-initialize the event masks so call the hci initialize */ + ble_ll_hci_init(); + + /* Reset scheduler */ + ble_ll_sched_init(); + + /* Set state to standby */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + + /* Reset our random address */ + memset(g_random_addr, 0, BLE_DEV_ADDR_LEN); + + /* Clear the whitelist */ + ble_ll_whitelist_clear(); + + /* Reset resolving list */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_ll_resolv_list_reset(); +#endif + + /* Re-initialize the PHY */ + rc = ble_phy_init(); + + return rc; +} + +static void +ble_ll_seed_prng(void) +{ + uint32_t seed; + int i; + + /* Seed random number generator with least significant bytes of device + * address. + */ + seed = 0; + for (i = 0; i < 4; ++i) { + seed |= g_dev_addr[i]; + seed <<= 8; + } + srand(seed); +} + +uint32_t +ble_ll_pdu_tx_time_get(uint16_t payload_len, int phy_mode) +{ + uint32_t usecs; + +#if (BLE_LL_BT5_PHY_SUPPORTED) + if (phy_mode == BLE_PHY_MODE_1M) { + /* 8 usecs per byte */ + usecs = payload_len << 3; + } else if (phy_mode == BLE_PHY_MODE_2M) { + /* 4 usecs per byte */ + usecs = payload_len << 2; + } else if (phy_mode == BLE_PHY_MODE_CODED_125KBPS) { + /* S=8 => 8 * 8 = 64 usecs per byte */ + usecs = payload_len << 6; + } else if (phy_mode == BLE_PHY_MODE_CODED_500KBPS) { + /* S=2 => 2 * 8 = 16 usecs per byte */ + usecs = payload_len << 4; + } else { + BLE_LL_ASSERT(0); + } + + usecs += g_ble_ll_pdu_header_tx_time[phy_mode]; +#else + usecs = (((payload_len) + BLE_LL_PDU_HDR_LEN + BLE_LL_ACC_ADDR_LEN + + BLE_LL_PREAMBLE_LEN + BLE_LL_CRC_LEN) << 3); +#endif + + return usecs; +} + +uint16_t +ble_ll_pdu_max_tx_octets_get(uint32_t usecs, int phy_mode) +{ + uint32_t header_tx_time; + uint16_t octets = 0; + + BLE_LL_ASSERT(phy_mode < BLE_PHY_NUM_MODE); + + header_tx_time = g_ble_ll_pdu_header_tx_time[phy_mode]; + + /* + * Current conn max tx time can be too short to even send a packet header + * and this can happen if we changed connection form uncoded to coded phy. + * However, the lower bound for conn max tx time (all of them) depends on + * current phy (uncoded/coded) but it always allows to send at least 27 + * bytes of payload thus we alwyas return at least 27 from here. + * + * Reference: + * Core v5.0, Vol 6, Part B, section 4.5.10 + * see connEffectiveMaxTxTime and connEffectiveMaxRxTime definitions + */ + + if (usecs < header_tx_time) { + return 27; + } + + usecs -= header_tx_time; + + if (phy_mode == BLE_PHY_MODE_1M) { + /* 8 usecs per byte */ + octets = usecs >> 3; + } else if (phy_mode == BLE_PHY_MODE_2M) { + /* 4 usecs per byte */ + octets = usecs >> 2; + } else if (phy_mode == BLE_PHY_MODE_CODED_125KBPS) { + /* S=8 => 8 * 8 = 64 usecs per byte */ + octets = usecs >> 6; + } else if (phy_mode == BLE_PHY_MODE_CODED_500KBPS) { + /* S=2 => 2 * 8 = 16 usecs per byte */ + octets = usecs >> 4; + } else { + BLE_LL_ASSERT(0); + } + + /* see comment at the beginning */ + return max(27, octets); +} + +static inline bool +ble_ll_is_addr_empty(const uint8_t *addr) +{ + return memcmp(addr, BLE_ADDR_ANY, BLE_DEV_ADDR_LEN) == 0; +} + +/** + * Initialize the Link Layer. Should be called only once + * + * @return int + */ +void +ble_ll_init(void) +{ + int rc; + uint64_t features; + ble_addr_t addr; + struct ble_ll_obj *lldata; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + ble_ll_trace_init(); + ble_phy_trace_init(); + + /* Set public device address if not already set */ + if (ble_ll_is_addr_empty(g_dev_addr)) { + /* Use sycfg address if configured, otherwise try to read from HW */ + if (!ble_ll_is_addr_empty(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR))) { + memcpy(g_dev_addr, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), BLE_DEV_ADDR_LEN); + } else { + rc = ble_hw_get_public_addr(&addr); + if (!rc) { + memcpy(g_dev_addr, &addr.val[0], BLE_DEV_ADDR_LEN); + } + } + } + + ble_ll_rfmgmt_init(); + + /* Get pointer to global data object */ + lldata = &g_ble_ll_data; + + /* Set acl pkt size and number */ + lldata->ll_num_acl_pkts = MYNEWT_VAL(BLE_ACL_BUF_COUNT); + lldata->ll_acl_pkt_size = MYNEWT_VAL(BLE_ACL_BUF_SIZE); + + /* Initialize eventq */ + ble_npl_eventq_init(&lldata->ll_evq); + + /* Initialize the transmit (from host) and receive (from phy) queues */ + STAILQ_INIT(&lldata->ll_tx_pkt_q); + STAILQ_INIT(&lldata->ll_rx_pkt_q); + + /* Initialize transmit (from host) and receive packet (from phy) event */ + ble_npl_event_init(&lldata->ll_rx_pkt_ev, ble_ll_event_rx_pkt, NULL); + ble_npl_event_init(&lldata->ll_tx_pkt_ev, ble_ll_event_tx_pkt, NULL); + + /* Initialize data buffer overflow event and completed packets */ + ble_npl_event_init(&lldata->ll_dbuf_overflow_ev, ble_ll_event_dbuf_overflow, NULL); + ble_npl_event_init(&lldata->ll_comp_pkt_ev, ble_ll_event_comp_pkts, NULL); + + /* Initialize the HW error timer */ + /* NOT USED WITH RAM HCI + * Commented out to prevent creating an unnecessary timer + ble_npl_callout_init(&g_ble_ll_data.ll_hw_err_timer, + &g_ble_ll_data.ll_evq, + ble_ll_hw_err_timer_cb, + NULL); + */ + + /* Initialize LL HCI */ + ble_ll_hci_init(); + + /* Init the scheduler */ + ble_ll_sched_init(); + + /* Initialize advertiser */ + ble_ll_adv_init(); + + /* Initialize a scanner */ + ble_ll_scan_init(); + + /* Initialize the connection module */ + ble_ll_conn_module_init(); + + /* Set the supported features. NOTE: we always support extended reject. */ + features = BLE_LL_FEAT_EXTENDED_REJ; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) + features |= BLE_LL_FEAT_DATA_LEN_EXT; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CONN_PARAM_REQ) + features |= BLE_LL_FEAT_CONN_PARM_REQ; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG) + features |= BLE_LL_FEAT_SLAVE_INIT; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + features |= BLE_LL_FEAT_LE_ENCRYPTION; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + features |= (BLE_LL_FEAT_LL_PRIVACY | BLE_LL_FEAT_EXT_SCAN_FILT); + ble_ll_resolv_init(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + features |= BLE_LL_FEAT_LE_PING; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + features |= BLE_LL_FEAT_EXT_ADV; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + /* CSA2 */ + features |= BLE_LL_FEAT_CSA2; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + features |= BLE_LL_FEAT_LE_2M_PHY; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + features |= BLE_LL_FEAT_LE_CODED_PHY; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + features |= BLE_LL_FEAT_PERIODIC_ADV; + ble_ll_sync_init(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + features |= BLE_LL_FEAT_SYNC_TRANS_RECV; + features |= BLE_LL_FEAT_SYNC_TRANS_SEND; +#endif + + /* Initialize random number generation */ + ble_ll_rand_init(); + + /* XXX: This really doesn't belong here, as the address probably has not + * been set yet. + */ + ble_ll_seed_prng(); + + lldata->ll_supp_features = features; + + rc = stats_init_and_reg(STATS_HDR(ble_ll_stats), + STATS_SIZE_INIT_PARMS(ble_ll_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_ll_stats), + "ble_ll"); + SYSINIT_PANIC_ASSERT(rc == 0); + +#if MYNEWT_VAL(BLE_LL_DTM) + ble_ll_dtm_init(); +#endif + +#if MYNEWT + /* Initialize the LL task */ + os_task_init(&g_ble_ll_task, "ble_ll", ble_ll_task, NULL, + MYNEWT_VAL(BLE_LL_PRIO), OS_WAIT_FOREVER, g_ble_ll_stack, + BLE_LL_STACK_SIZE); +#else + +/* + * For non-Mynewt OS it is required that OS creates task for LL and run LL + * routine which is wrapped by nimble_port_ll_task_func(). + */ + +#endif +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_adv.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_adv.c new file mode 100644 index 0000000..83fd250 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_adv.c @@ -0,0 +1,5143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) +#include "nimble/nimble/drivers/nrf51/include/ble/xcvr.h" +#elif defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) +#include "nimble/nimble/drivers/nrf52/include/ble/xcvr.h" +#endif + +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_ll_utils.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "ble_ll_conn_priv.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) \ + (sizeof(array) / sizeof((array)[0])) +#endif + +/* XXX: TODO + * 1) Need to look at advertising and scan request PDUs. Do I allocate these + * once? Do I use a different pool for smaller ones? Do I statically declare + * them? + * 3) How do features get supported? What happens if device does not support + * advertising? (for example) + * 4) How to determine the advertising interval we will actually use. As of + * now, we set it to max. + */ + +/* Scheduling data for secondary channel */ +struct ble_ll_adv_aux { + struct ble_ll_sched_item sch; + uint32_t start_time; + uint16_t aux_data_offset; + uint8_t chan; + uint8_t ext_hdr; + uint8_t aux_data_len; + uint8_t payload_len; +}; + +/* Scheduling data for sync PDUs */ +struct ble_ll_adv_sync { + struct ble_ll_sched_item sch; + uint32_t start_time; + uint16_t sync_data_offset; + uint8_t chan; + uint8_t ext_hdr; + uint8_t sync_data_len; + uint8_t payload_len; +}; + +/* + * Advertising state machine + * + * The advertising state machine data structure. + * + * adv_pdu_len + * The length of the advertising PDU that will be sent. This does not + * include the preamble, access address and CRC. + * + * initiator_addr: + * This is the address that we send in directed advertisements (the + * INITA field). If we are using Privacy this is a RPA that we need to + * generate. We reserve space in the advsm to save time when creating + * the ADV_DIRECT_IND. If own address type is not 2 or 3, this is simply + * the peer address from the set advertising parameters. + */ +struct ble_ll_adv_sm +{ + uint8_t adv_enabled; + uint8_t adv_instance; + uint8_t adv_chanmask; + uint8_t adv_filter_policy; + uint8_t own_addr_type; + uint8_t peer_addr_type; + uint8_t adv_chan; + uint8_t adv_pdu_len; + int8_t adv_rpa_index; + int8_t adv_txpwr; + uint16_t flags; + uint16_t props; + uint16_t adv_itvl_min; + uint16_t adv_itvl_max; + uint32_t adv_itvl_usecs; + uint32_t adv_event_start_time; + uint32_t adv_pdu_start_time; + uint32_t adv_end_time; + uint32_t adv_rpa_timer; + uint8_t adva[BLE_DEV_ADDR_LEN]; + uint8_t adv_rpa[BLE_DEV_ADDR_LEN]; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint8_t initiator_addr[BLE_DEV_ADDR_LEN]; + struct os_mbuf *adv_data; + struct os_mbuf *new_adv_data; + struct os_mbuf *scan_rsp_data; + struct os_mbuf *new_scan_rsp_data; + uint8_t *conn_comp_ev; + struct ble_npl_event adv_txdone_ev; + struct ble_ll_sched_item adv_sch; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + uint16_t channel_id; + uint16_t event_cntr; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + uint8_t aux_active : 1; + uint8_t aux_index : 1; + uint8_t aux_first_pdu : 1; + uint8_t aux_not_scanned : 1; + struct ble_mbuf_hdr *rx_ble_hdr; + struct os_mbuf **aux_data; + struct ble_ll_adv_aux aux[2]; + struct ble_npl_event adv_sec_txdone_ev; + uint16_t duration; + uint16_t adi; + uint8_t adv_random_addr[BLE_DEV_ADDR_LEN]; + uint8_t events_max; + uint8_t events; + uint8_t pri_phy; + uint8_t sec_phy; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + struct os_mbuf *periodic_adv_data; + struct os_mbuf *periodic_new_data; + uint32_t periodic_crcinit; /* only 3 bytes are used */ + uint32_t periodic_access_addr; + uint16_t periodic_adv_itvl_min; + uint16_t periodic_adv_itvl_max; + uint16_t periodic_adv_props; + uint16_t periodic_channel_id; + uint16_t periodic_event_cntr; + uint16_t periodic_chain_event_cntr; + uint8_t periodic_adv_enabled : 1; + uint8_t periodic_adv_active : 1; + uint8_t periodic_sync_active : 1; + uint8_t periodic_sync_index : 1; + uint8_t periodic_num_used_chans; + uint8_t periodic_chanmap[BLE_LL_CONN_CHMAP_LEN]; + uint32_t periodic_adv_itvl_ticks; + uint8_t periodic_adv_itvl_rem_usec; + uint8_t periodic_adv_event_start_time_remainder; + uint32_t periodic_adv_event_start_time; + struct ble_ll_adv_sync periodic_sync[2]; + struct ble_npl_event adv_periodic_txdone_ev; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + uint16_t periodic_event_cntr_last_sent; +#endif +#endif +#endif +}; + +#define BLE_LL_ADV_SM_FLAG_TX_ADD 0x0001 +#define BLE_LL_ADV_SM_FLAG_RX_ADD 0x0002 +#define BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF 0x0004 +#define BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD 0x0008 +#define BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK 0x0030 /* use helpers! */ +#define BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE 0x0040 +#define BLE_LL_ADV_SM_FLAG_CONFIGURED 0x0080 +#define BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO 0x0100 +#define BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA 0x0200 +#define BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA 0x0400 +#define BLE_LL_ADV_SM_FLAG_PERIODIC_CONFIGURED 0x0800 +#define BLE_LL_ADV_SM_FLAG_PERIODIC_DATA_INCOMPLETE 0x1000 +#define BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING 0x2000 +#define BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA 0x4000 + +#define ADV_DATA_LEN(_advsm) \ + ((_advsm->adv_data) ? OS_MBUF_PKTLEN(advsm->adv_data) : 0) +#define SCAN_RSP_DATA_LEN(_advsm) \ + ((_advsm->scan_rsp_data) ? OS_MBUF_PKTLEN(advsm->scan_rsp_data) : 0) +#define AUX_DATA_LEN(_advsm) \ + (*(_advsm->aux_data) ? OS_MBUF_PKTLEN(*advsm->aux_data) : 0) + +#define AUX_CURRENT(_advsm) (&(_advsm->aux[_advsm->aux_index])) +#define AUX_NEXT(_advsm) (&(_advsm->aux[_advsm->aux_index ^ 1])) + +#define SYNC_CURRENT(_advsm) (&(_advsm->periodic_sync[_advsm->periodic_sync_index])) +#define SYNC_NEXT(_advsm) (&(_advsm->periodic_sync[_advsm->periodic_sync_index ^ 1])) +#define SYNC_DATA_LEN(_advsm) \ + (_advsm->periodic_adv_data ? OS_MBUF_PKTLEN(advsm->periodic_adv_data) : 0) + +/* The advertising state machine global object */ +struct ble_ll_adv_sm g_ble_ll_adv_sm[BLE_ADV_INSTANCES]; +struct ble_ll_adv_sm *g_ble_ll_cur_adv_sm; + +static struct ble_ll_adv_sm * +ble_ll_adv_sm_find_configured(uint8_t instance) +{ + struct ble_ll_adv_sm *advsm; + int i; + + /* in legacy mode we only allow instance 0 */ + if (!ble_ll_hci_adv_mode_ext()) { + BLE_LL_ASSERT(instance == 0); + return &g_ble_ll_adv_sm[0]; + } + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_adv_sm); i++) { + advsm = &g_ble_ll_adv_sm[i]; + + if ((advsm->flags & BLE_LL_ADV_SM_FLAG_CONFIGURED) && + (advsm->adv_instance == instance)) { + return advsm; + } + } + + return NULL; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +ble_ll_adv_active_chanset_is_pri(struct ble_ll_adv_sm *advsm) +{ + return (advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0x10; +} + +static int +ble_ll_adv_active_chanset_is_sec(struct ble_ll_adv_sm *advsm) +{ + return (advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0x20; +} +#endif + +static void +ble_ll_adv_active_chanset_clear(struct ble_ll_adv_sm *advsm) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK; + OS_EXIT_CRITICAL(sr); +} + +static void +ble_ll_adv_active_chanset_set_pri(struct ble_ll_adv_sm *advsm) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + assert((advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0); + advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK; + advsm->flags |= 0x10; + OS_EXIT_CRITICAL(sr); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_adv_active_chanset_set_sec(struct ble_ll_adv_sm *advsm) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + assert((advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0); + advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK; + advsm->flags |= 0x20; + OS_EXIT_CRITICAL(sr); +} +#endif + +static void +ble_ll_adv_flags_set(struct ble_ll_adv_sm *advsm, uint16_t flags) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + advsm->flags |= flags; + OS_EXIT_CRITICAL(sr); +} + +static void +ble_ll_adv_flags_clear(struct ble_ll_adv_sm *advsm, uint16_t flags) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + advsm->flags &= ~flags; + OS_EXIT_CRITICAL(sr); +} + +static void ble_ll_adv_make_done(struct ble_ll_adv_sm *advsm, struct ble_mbuf_hdr *hdr); +static void ble_ll_adv_sm_init(struct ble_ll_adv_sm *advsm); +static void ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +static void +ble_ll_adv_rpa_update(struct ble_ll_adv_sm *advsm) +{ + if (ble_ll_resolv_gen_rpa(advsm->peer_addr, advsm->peer_addr_type, + advsm->adva, 1)) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_TX_ADD); + } else { + if (advsm->own_addr_type & 1) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_TX_ADD); + } else { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_TX_ADD); + } + } + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + if (ble_ll_resolv_gen_rpa(advsm->peer_addr, advsm->peer_addr_type, + advsm->initiator_addr, 0)) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_RX_ADD); + } else { + if (advsm->peer_addr_type & 1) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_RX_ADD); + } else { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_RX_ADD); + } + } + } +} + +/** + * Called to change advertisers ADVA and INITA (for directed advertisements) + * as an advertiser needs to adhere to the resolvable private address generation + * timer. + * + * NOTE: the resolvable private address code uses its own timer to regenerate + * local resolvable private addresses. The advertising code uses its own + * timer to reset the INITA (for directed advertisements). This code also sets + * the appropriate txadd and rxadd bits that will go into the advertisement. + * + * Another thing to note: it is possible that an IRK is all zeroes in the + * resolving list. That is why we need to check if the generated address is + * in fact a RPA as a resolving list entry with all zeroes will use the + * identity address (which may be a private address or public). + * + * @param advsm + */ +void +ble_ll_adv_chk_rpa_timeout(struct ble_ll_adv_sm *advsm) +{ + if (advsm->own_addr_type < BLE_HCI_ADV_OWN_ADDR_PRIV_PUB) { + return; + } + + if (advsm->flags & BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO) { + ble_ll_adv_rpa_update(advsm); + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO); + } +} + +void +ble_ll_adv_rpa_timeout(void) +{ + struct ble_ll_adv_sm *advsm; + int i; + + for (i = 0; i < BLE_ADV_INSTANCES; i++) { + advsm = &g_ble_ll_adv_sm[i]; + + if (advsm->adv_enabled && + advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + /* Mark RPA as timed out so we get a new RPA */ + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO); + } + } +} +#endif + +/** + * Calculate the first channel that we should advertise upon when we start + * an advertising event. + * + * @param advsm + * + * @return uint8_t The number of the first channel usable for advertising. + */ +static uint8_t +ble_ll_adv_first_chan(struct ble_ll_adv_sm *advsm) +{ + uint8_t adv_chan; + + /* Set first advertising channel */ + if (advsm->adv_chanmask & 0x01) { + adv_chan = BLE_PHY_ADV_CHAN_START; + } else if (advsm->adv_chanmask & 0x02) { + adv_chan = BLE_PHY_ADV_CHAN_START + 1; + } else { + adv_chan = BLE_PHY_ADV_CHAN_START + 2; + } + + return adv_chan; +} + +/** + * Calculate the final channel that we should advertise upon when we start + * an advertising event. + * + * @param advsm + * + * @return uint8_t The number of the final channel usable for advertising. + */ +static uint8_t +ble_ll_adv_final_chan(struct ble_ll_adv_sm *advsm) +{ + uint8_t adv_chan; + + if (advsm->adv_chanmask & 0x04) { + adv_chan = BLE_PHY_ADV_CHAN_START + 2; + } else if (advsm->adv_chanmask & 0x02) { + adv_chan = BLE_PHY_ADV_CHAN_START + 1; + } else { + adv_chan = BLE_PHY_ADV_CHAN_START; + } + + return adv_chan; +} + +/** + * Create the advertising legacy PDU + * + * @param advsm Pointer to advertisement state machine + */ +static uint8_t +ble_ll_adv_legacy_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + uint8_t adv_data_len; + uint8_t pdulen; + uint8_t pdu_type; + + advsm = pducb_arg; + + /* assume this is not a direct ind */ + adv_data_len = ADV_DATA_LEN(advsm); + pdulen = BLE_DEV_ADDR_LEN + adv_data_len; + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + pdu_type = BLE_ADV_PDU_TYPE_ADV_DIRECT_IND; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + pdu_type |= BLE_ADV_PDU_HDR_CHSEL; +#endif + + if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND; + } + + adv_data_len = 0; + pdulen = BLE_ADV_DIRECT_IND_LEN; + } else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + pdu_type = BLE_ADV_PDU_TYPE_ADV_IND; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + pdu_type |= BLE_ADV_PDU_HDR_CHSEL; +#endif + } else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + pdu_type = BLE_ADV_PDU_TYPE_ADV_SCAN_IND; + } else { + pdu_type = BLE_ADV_PDU_TYPE_ADV_NONCONN_IND; + } + + /* An invalid advertising data length indicates a memory overwrite */ + assert(adv_data_len <= BLE_ADV_LEGACY_DATA_MAX_LEN); + + /* Set the PDU length in the state machine (includes header) */ + advsm->adv_pdu_len = pdulen + BLE_LL_PDU_HDR_LEN; + + /* Set TxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + + *hdr_byte = pdu_type; + + /* Construct advertisement */ + memcpy(dptr, advsm->adva, BLE_DEV_ADDR_LEN); + dptr += BLE_DEV_ADDR_LEN; + + /* For ADV_DIRECT_IND add inita */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + memcpy(dptr, advsm->initiator_addr, BLE_DEV_ADDR_LEN); + } + + /* Copy in advertising data, if any */ + if (adv_data_len != 0) { + os_mbuf_copydata(advsm->adv_data, 0, adv_data_len, dptr); + } + + return pdulen; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_adv_put_aux_ptr(uint8_t chan, uint8_t phy, uint32_t offset, + uint8_t *dptr) +{ + dptr[0] = chan; + + if (offset > 245700) { + dptr[0] |= 0x80; + offset = offset / 300; + } else { + offset = offset / 30; + } + + if (offset > 0x1fff) { + offset = 0; + } + + /* offset is 13bits and PHY 3 bits */ + dptr[1] = (offset & 0x000000ff); + dptr[2] = ((offset >> 8) & 0x0000001f) | (phy - 1) << 5; +} + +/** + * Create the advertising PDU + */ +static uint8_t +ble_ll_adv_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + uint8_t pdu_type; + uint8_t adv_mode; + uint8_t ext_hdr_len; + uint8_t ext_hdr_flags; + uint32_t offset; + + advsm = pducb_arg; + + assert(ble_ll_adv_active_chanset_is_pri(advsm)); + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return ble_ll_adv_legacy_pdu_make(dptr, advsm, hdr_byte); + } + + /* only ADV_EXT_IND goes on primary advertising channels */ + pdu_type = BLE_ADV_PDU_TYPE_ADV_EXT_IND; + + /* Set TxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + + *hdr_byte = pdu_type; + + adv_mode = 0; + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + adv_mode |= BLE_LL_EXT_ADV_MODE_CONN; + } + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + adv_mode |= BLE_LL_EXT_ADV_MODE_SCAN; + } + + ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE + BLE_LL_EXT_ADV_DATA_INFO_SIZE + + BLE_LL_EXT_ADV_AUX_PTR_SIZE; + ext_hdr_flags = (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT) | + (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT); + + /* ext hdr len and adv mode */ + dptr[0] = ext_hdr_len | (adv_mode << 6); + dptr += 1; + + /* ext hdr flags */ + dptr[0] = ext_hdr_flags; + dptr += 1; + + /* ADI */ + dptr[0] = advsm->adi & 0x00ff; + dptr[1] = advsm->adi >> 8; + dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + + /* AuxPtr */ + if (AUX_CURRENT(advsm)->sch.enqueued) { + offset = os_cputime_ticks_to_usecs(AUX_CURRENT(advsm)->start_time - advsm->adv_pdu_start_time); + } else { + offset = 0; + } + /* Always use channel from 1st AUX */ + ble_ll_adv_put_aux_ptr(AUX_CURRENT(advsm)->chan, advsm->sec_phy, + offset, dptr); + + return BLE_LL_EXT_ADV_HDR_LEN + ext_hdr_len; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +static void +ble_ll_adv_put_syncinfo(struct ble_ll_adv_sm *advsm, + struct ble_ll_conn_sm *connsm, uint8_t *conn_event_cnt, + uint8_t *dptr) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + uint8_t anchor_usecs; + uint16_t conn_cnt; +#endif + unsigned int event_cnt_off = 0; + uint32_t offset = 0; + uint32_t anchor; + uint8_t units; + + if (connsm) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + anchor = connsm->anchor_point; + anchor_usecs = connsm->anchor_point_usecs; + conn_cnt = connsm->event_cntr; + + /* get anchor for conn event that is before periodic_adv_event_start_time */ + while (CPUTIME_GT(anchor, advsm->periodic_adv_event_start_time)) { + ble_ll_conn_get_anchor(connsm, --conn_cnt, &anchor, &anchor_usecs); + } + + offset = os_cputime_ticks_to_usecs(advsm->periodic_adv_event_start_time - anchor); + offset -= anchor_usecs; + offset += advsm->periodic_adv_event_start_time_remainder; + + /* connEventCount */ + put_le16(conn_event_cnt, conn_cnt); +#endif + } else { + anchor = advsm->periodic_adv_event_start_time; + + /* Get periodic event that is past AUX start time (so that we always + * provide valid offset if it is not too far in future). This can + * happen if advertising event is interleaved with periodic advertising + * event (when chaining). + */ + while (CPUTIME_GT(AUX_CURRENT(advsm)->start_time, anchor)) { + anchor += advsm->periodic_adv_itvl_ticks; + event_cnt_off++; + } + + offset = os_cputime_ticks_to_usecs(anchor - AUX_CURRENT(advsm)->start_time); + offset += advsm->periodic_adv_event_start_time_remainder; + offset += advsm->periodic_adv_itvl_rem_usec; + } + + /* Sync Packet Offset (13 bits), Offset Units (1 bit), Offset Adjust (1 bit), + * RFU (1 bit) + */ + if (offset > 245700) { + units = 0x20; + offset = offset / 300; + + if (offset >= 0x2000) { + if (connsm) { + offset -= 0x2000; + units |= 0x40; + } else { + /* not able to represent time in offset */ + offset = 0; + units = 0x00; + event_cnt_off = 0; + } + } + + } else { + units = 0x00; + offset = offset / 30; + } + + dptr[0] = (offset & 0x000000ff); + dptr[1] = ((offset >> 8) & 0x0000001f) | units; + + /* Interval (2 bytes) */ + put_le16(&dptr[2], advsm->periodic_adv_itvl_max); + + /* Channels Mask (37 bits) */ + dptr[4] = advsm->periodic_chanmap[0]; + dptr[5] = advsm->periodic_chanmap[1]; + dptr[6] = advsm->periodic_chanmap[2]; + dptr[7] = advsm->periodic_chanmap[3]; + dptr[8] = advsm->periodic_chanmap[4] & 0x1f; + + /* SCA (3 bits) */ + dptr[8] |= MYNEWT_VAL(BLE_LL_MASTER_SCA) << 5; + + /* AA (4 bytes) */ + put_le32(&dptr[9], advsm->periodic_access_addr); + + /* CRCInit (3 bytes) */ + dptr[13] = (uint8_t)advsm->periodic_crcinit; + dptr[14] = (uint8_t)(advsm->periodic_crcinit >> 8); + dptr[15] = (uint8_t)(advsm->periodic_crcinit >> 16); + + /* Event Counter (2 bytes) */ + put_le16(&dptr[16], advsm->periodic_event_cntr + event_cnt_off); +} +#endif + +/** + * Create the AUX PDU + */ +static uint8_t +ble_ll_adv_aux_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + struct ble_ll_adv_aux *aux; + uint8_t adv_mode; + uint8_t pdu_type; + uint8_t ext_hdr_len; + uint32_t offset; + + advsm = pducb_arg; + aux = AUX_CURRENT(advsm); + + assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)); + assert(ble_ll_adv_active_chanset_is_sec(advsm)); + + /* It's the same for AUX_ADV_IND and AUX_CHAIN_IND */ + pdu_type = BLE_ADV_PDU_TYPE_AUX_ADV_IND; + + /* We do not create scannable PDUs here - this is handled separately */ + adv_mode = 0; + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + adv_mode |= BLE_LL_EXT_ADV_MODE_CONN; + } + + ext_hdr_len = aux->payload_len - BLE_LL_EXT_ADV_HDR_LEN - aux->aux_data_len; + dptr[0] = (adv_mode << 6) | ext_hdr_len; + dptr += 1; + + /* only put flags if needed */ + if (aux->ext_hdr) { + dptr[0] = aux->ext_hdr; + dptr += 1; + } + + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + + /* Set TxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + + memcpy(dptr, advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE); + dptr += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + memcpy(dptr, advsm->initiator_addr, BLE_LL_EXT_ADV_TARGETA_SIZE); + dptr += BLE_LL_EXT_ADV_TARGETA_SIZE; + + /* Set RxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND; + } + } + + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { + dptr[0] = advsm->adi & 0x00ff; + dptr[1] = advsm->adi >> 8; + dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } + + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + if (!AUX_NEXT(advsm)->sch.enqueued) { + /* + * Trim data here in case we do not have next aux scheduled. This + * can happen if next aux was outside advertising set period and + * was removed from scheduler. + */ + offset = 0; + } else if (advsm->rx_ble_hdr) { + offset = os_cputime_ticks_to_usecs(AUX_NEXT(advsm)->start_time - advsm->rx_ble_hdr->beg_cputime); + offset -= (advsm->rx_ble_hdr->rem_usecs + ble_ll_pdu_tx_time_get(12, advsm->sec_phy) + BLE_LL_IFS); + } else { + offset = os_cputime_ticks_to_usecs(AUX_NEXT(advsm)->start_time - aux->start_time); + } + + ble_ll_adv_put_aux_ptr(AUX_NEXT(advsm)->chan, advsm->sec_phy, + offset, dptr); + + dptr += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { + ble_ll_adv_put_syncinfo(advsm, NULL, NULL, dptr); + dptr += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; + } +#endif + + if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { + dptr[0] = advsm->adv_txpwr + ble_ll_get_tx_pwr_compensation(); + dptr += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + if (aux->aux_data_len) { + os_mbuf_copydata(*advsm->aux_data, aux->aux_data_offset, + aux->aux_data_len, dptr); + } + + *hdr_byte = pdu_type; + + return aux->payload_len; +} + +static uint8_t +ble_ll_adv_aux_scannable_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + uint8_t pdu_type; + uint8_t *ext_hdr_len; + uint8_t *ext_hdr; + uint8_t pdulen; + + advsm = pducb_arg; + + assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)); + assert(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE); + assert(advsm->aux_first_pdu); + assert(ble_ll_adv_active_chanset_is_sec(advsm)); + + pdu_type = BLE_ADV_PDU_TYPE_AUX_ADV_IND; + + ext_hdr_len = &dptr[0]; + ext_hdr = &dptr[1]; + dptr += 2; + + /* Flags always */ + *ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE; + *ext_hdr = 0; + + /* AdvA when non anonymous */ + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { + /* Set TxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + + *ext_hdr_len += BLE_LL_EXT_ADV_ADVA_SIZE; + *ext_hdr |= (1 << BLE_LL_EXT_ADV_ADVA_BIT); + memcpy(dptr, advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE); + dptr += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + /* TargetA only for directed */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + *ext_hdr_len += BLE_LL_EXT_ADV_TARGETA_SIZE; + *ext_hdr |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT); + memcpy(dptr, advsm->initiator_addr, BLE_LL_EXT_ADV_TARGETA_SIZE); + dptr += BLE_LL_EXT_ADV_TARGETA_SIZE; + + /* Set RxAdd to random if needed. */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) { + pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND; + } + } + + /* ADI always */ + *ext_hdr_len += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + *ext_hdr |= (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT); + dptr[0] = advsm->adi & 0x00ff; + dptr[1] = advsm->adi >> 8; + dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + + /* TxPower if configured */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR) { + *ext_hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + *ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT); + dptr[0] = advsm->adv_txpwr + ble_ll_get_tx_pwr_compensation(); + dptr += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + pdulen = BLE_LL_EXT_ADV_HDR_LEN + *ext_hdr_len; + + *hdr_byte = pdu_type; + *ext_hdr_len |= (BLE_LL_EXT_ADV_MODE_SCAN << 6); + + return pdulen; +} +#endif + +static uint8_t +ble_ll_adv_scan_rsp_legacy_pdu_make(uint8_t *dptr, void *pducb_arg, + uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + uint8_t scan_rsp_len; + uint8_t pdulen; + uint8_t hdr; + + advsm = pducb_arg; + + /* Make sure that the length is valid */ + scan_rsp_len = SCAN_RSP_DATA_LEN(advsm); + assert(scan_rsp_len <= BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN); + + /* Set BLE transmit header */ + pdulen = BLE_DEV_ADDR_LEN + scan_rsp_len; + hdr = BLE_ADV_PDU_TYPE_SCAN_RSP; + if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + hdr |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + + *hdr_byte = hdr; + + /* + * The adva in this packet will be the same one that was being advertised + * and is based on the peer identity address in the set advertising + * parameters. If a different peer sends us a scan request (for some reason) + * we will reply with an adva that was not generated based on the local irk + * of the peer sending the scan request. + */ + + /* Construct scan response */ + memcpy(dptr, advsm->adva, BLE_DEV_ADDR_LEN); + if (scan_rsp_len != 0) { + os_mbuf_copydata(advsm->scan_rsp_data, 0, scan_rsp_len, + dptr + BLE_DEV_ADDR_LEN); + } + + return pdulen; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/** + * Create a scan response PDU + * + * @param advsm + */ +static uint8_t +ble_ll_adv_scan_rsp_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + + advsm = pducb_arg; + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return ble_ll_adv_scan_rsp_legacy_pdu_make(dptr, pducb_arg, hdr_byte); + } + + return ble_ll_adv_aux_pdu_make(dptr, pducb_arg, hdr_byte); +} + +struct aux_conn_rsp_data { + struct ble_ll_adv_sm *advsm; + uint8_t *peer; + uint8_t rxadd; +}; + +/** + * Create a AUX connect response PDU + * + * @param advsm + */ +static uint8_t +ble_ll_adv_aux_conn_rsp_pdu_make(uint8_t *dptr, void *pducb_arg, + uint8_t *hdr_byte) +{ + struct aux_conn_rsp_data *rsp_data; + uint8_t pdulen; + uint8_t ext_hdr_len; + uint8_t ext_hdr_flags; + uint8_t hdr; + + rsp_data = pducb_arg; + + /* flags,AdvA and TargetA */ + ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE + BLE_LL_EXT_ADV_ADVA_SIZE + + BLE_LL_EXT_ADV_TARGETA_SIZE; + ext_hdr_flags = (1 << BLE_LL_EXT_ADV_ADVA_BIT); + ext_hdr_flags |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT); + + pdulen = BLE_LL_EXT_ADV_HDR_LEN + ext_hdr_len; + + /* Set BLE transmit header */ + hdr = BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP; + if (rsp_data->rxadd) { + hdr |= BLE_ADV_PDU_HDR_RXADD_MASK; + } + if (rsp_data->advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) { + hdr |= BLE_ADV_PDU_HDR_TXADD_MASK; + } + + *hdr_byte = hdr; + + /* ext hdr len and adv mode (00b) */ + dptr[0] = ext_hdr_len; + dptr += 1; + + /* ext hdr flags */ + dptr[0] = ext_hdr_flags; + dptr += 1; + + memcpy(dptr, rsp_data->advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE); + dptr += BLE_LL_EXT_ADV_ADVA_SIZE; + + memcpy(dptr, rsp_data->peer, BLE_LL_EXT_ADV_TARGETA_SIZE); + dptr += BLE_LL_EXT_ADV_ADVA_SIZE; + + return pdulen; +} +#endif + +/** + * Called to indicate the advertising event is over. + * + * Context: Interrupt + * + * @param advsm + * + */ +static void +ble_ll_adv_tx_done(void *arg) +{ + struct ble_ll_adv_sm *advsm; + + /* reset power to max after advertising */ + ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM)); + + advsm = (struct ble_ll_adv_sm *)arg; + + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_ADV_TXDONE, advsm->adv_instance, + advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (ble_ll_adv_active_chanset_is_pri(advsm)) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); + } else if (ble_ll_adv_active_chanset_is_sec(advsm)) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev); + } else { + assert(0); + } +#else + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); +#endif + + ble_ll_state_set(BLE_LL_STATE_STANDBY); + + ble_ll_adv_active_chanset_clear(advsm); + + /* We no longer have a current state machine */ + g_ble_ll_cur_adv_sm = NULL; +} + +/* + * Called when an advertising event has been removed from the scheduler + * without being run. + */ +void +ble_ll_adv_event_rmvd_from_sched(struct ble_ll_adv_sm *advsm) +{ + /* + * Need to set advertising channel to final chan so new event gets + * scheduled. + */ + advsm->adv_chan = ble_ll_adv_final_chan(advsm); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +/* + * Called when a periodic event has been removed from the scheduler + * without being run. + */ +void +ble_ll_adv_periodic_rmvd_from_sched(struct ble_ll_adv_sm *advsm) +{ + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_periodic_txdone_ev); +} +#endif + +/** + * This is the scheduler callback (called from interrupt context) which + * transmits an advertisement. + * + * Context: Interrupt (scheduler) + * + * @param sch + * + * @return int + */ +static int +ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) +{ + int rc; + uint8_t end_trans; + uint32_t txstart; + struct ble_ll_adv_sm *advsm; + + /* Get the state machine for the event */ + advsm = (struct ble_ll_adv_sm *)sch->cb_arg; + + /* Set the current advertiser */ + g_ble_ll_cur_adv_sm = advsm; + + ble_ll_adv_active_chanset_set_pri(advsm); + + if ((advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA) || + (advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA)) { + goto adv_tx_done; + } + + /* Set the power */ + ble_phy_txpwr_set(advsm->adv_txpwr); + + /* Set channel */ + rc = ble_phy_setchan(advsm->adv_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); + assert(rc == 0); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* Set phy mode */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M); + } else { + ble_phy_mode_set(advsm->pri_phy, advsm->pri_phy); + } +#else + ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M); +#endif +#endif + + /* Set transmit start time. */ + txstart = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(txstart, sch->remainder); + if (rc) { + STATS_INC(ble_ll_stats, adv_late_starts); + goto adv_tx_done; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + advsm->adv_rpa_index = -1; + if (ble_ll_resolv_enabled()) { + ble_phy_resolv_list_enable(); + } else { + ble_phy_resolv_list_disable(); + } +#endif + + /* We switch to RX after connectable or scannable legacy packets. */ + if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && + ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) || + (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE))) { + end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_set_txend_cb(NULL, NULL); + } else { + end_trans = BLE_PHY_TRANSITION_NONE; + ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); + } + + /* Transmit advertisement */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + rc = ble_phy_tx(ble_ll_adv_pdu_make, advsm, end_trans); +#else + rc = ble_phy_tx(ble_ll_adv_legacy_pdu_make, advsm, end_trans); +#endif + if (rc) { + goto adv_tx_done; + } + + /* Enable/disable whitelisting based on filter policy */ + if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) { + ble_ll_whitelist_enable(); + } else { + ble_ll_whitelist_disable(); + } + + /* Set link layer state to advertising */ + ble_ll_state_set(BLE_LL_STATE_ADV); + + /* Count # of adv. sent */ + STATS_INC(ble_ll_stats, adv_txg); + + return BLE_LL_SCHED_STATE_RUNNING; + +adv_tx_done: + ble_ll_adv_tx_done(advsm); + return BLE_LL_SCHED_STATE_DONE; +} + +static void +ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm) +{ + uint32_t max_usecs; + struct ble_ll_sched_item *sch; + + sch = &advsm->adv_sch; + sch->cb_arg = advsm; + sch->sched_cb = ble_ll_adv_tx_start_cb; + sch->sched_type = BLE_LL_SCHED_TYPE_ADV; + + /* Set end time to maximum time this schedule item may take */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + max_usecs = ble_ll_pdu_tx_time_get(advsm->adv_pdu_len, BLE_PHY_MODE_1M); + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + max_usecs += BLE_LL_SCHED_DIRECT_ADV_MAX_USECS; + } else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + max_usecs += BLE_LL_SCHED_ADV_MAX_USECS; + } + } else { + /* + * In ADV_EXT_IND we always set only ADI and AUX so the payload length + * is always 7 bytes. + */ + max_usecs = ble_ll_pdu_tx_time_get(7, advsm->pri_phy); + } +#else + max_usecs = ble_ll_pdu_tx_time_get(advsm->adv_pdu_len, BLE_PHY_MODE_1M); + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + max_usecs += BLE_LL_SCHED_DIRECT_ADV_MAX_USECS; + } else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + max_usecs += BLE_LL_SCHED_ADV_MAX_USECS; + } +#endif + + sch->start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks; + sch->remainder = 0; + sch->end_time = advsm->adv_pdu_start_time + + ble_ll_usecs_to_ticks_round_up(max_usecs); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +ble_ll_adv_secondary_tx_start_cb(struct ble_ll_sched_item *sch) +{ + int rc; + uint8_t end_trans; + uint32_t txstart; + struct ble_ll_adv_sm *advsm; + ble_phy_tx_pducb_t pducb; + struct ble_ll_adv_aux *aux; + + /* Get the state machine for the event */ + advsm = (struct ble_ll_adv_sm *)sch->cb_arg; + + /* Set the current advertiser */ + g_ble_ll_cur_adv_sm = advsm; + + ble_ll_adv_active_chanset_set_sec(advsm); + + /* Set the power */ + ble_phy_txpwr_set(advsm->adv_txpwr); + + /* Set channel */ + aux = AUX_CURRENT(advsm); + rc = ble_phy_setchan(aux->chan, BLE_ACCESS_ADDR_ADV, + BLE_LL_CRCINIT_ADV); + assert(rc == 0); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* Set phy mode */ + ble_phy_mode_set(advsm->sec_phy, advsm->sec_phy); +#endif + + /* Set transmit start time. */ + txstart = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(txstart, sch->remainder); + if (rc) { + STATS_INC(ble_ll_stats, adv_late_starts); + goto adv_tx_done; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + advsm->adv_rpa_index = -1; + if (ble_ll_resolv_enabled()) { + ble_phy_resolv_list_enable(); + } else { + ble_phy_resolv_list_disable(); + } +#endif + + /* Set phy mode based on type of advertisement */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_set_txend_cb(NULL, NULL); + pducb = ble_ll_adv_aux_pdu_make; + } else if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) && + advsm->aux_first_pdu) { + end_trans = BLE_PHY_TRANSITION_TX_RX; + ble_phy_set_txend_cb(NULL, NULL); + pducb = ble_ll_adv_aux_scannable_pdu_make; + } else { + end_trans = BLE_PHY_TRANSITION_NONE; + ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); + pducb = ble_ll_adv_aux_pdu_make; + } + + /* Transmit advertisement */ + rc = ble_phy_tx(pducb, advsm, end_trans); + if (rc) { + goto adv_tx_done; + } + + /* Enable/disable whitelisting based on filter policy */ + if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) { + ble_ll_whitelist_enable(); + } else { + ble_ll_whitelist_disable(); + } + + /* Set link layer state to advertising */ + ble_ll_state_set(BLE_LL_STATE_ADV); + + /* Count # of adv. sent */ + STATS_INC(ble_ll_stats, adv_txg); + + return BLE_LL_SCHED_STATE_RUNNING; + +adv_tx_done: + ble_ll_adv_tx_done(advsm); + return BLE_LL_SCHED_STATE_DONE; +} + +static uint8_t +ble_ll_adv_aux_scannable_pdu_payload_len(struct ble_ll_adv_sm *advsm) +{ + uint8_t len; + + /* Flags, ADI always */ + len = BLE_LL_EXT_ADV_HDR_LEN + BLE_LL_EXT_ADV_FLAGS_SIZE + + BLE_LL_EXT_ADV_DATA_INFO_SIZE; + + /* AdvA if not anonymous */ + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { + len += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + /* TargetA only for directed */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + len += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + /* TxPower if configured */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR) { + len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + return len; +} + +static void +ble_ll_adv_aux_calculate(struct ble_ll_adv_sm *advsm, + struct ble_ll_adv_aux *aux, uint16_t aux_data_offset) +{ + uint16_t rem_aux_data_len; + uint8_t hdr_len; + bool chainable; + + assert(!aux->sch.enqueued); + assert((AUX_DATA_LEN(advsm) > aux_data_offset) || + (AUX_DATA_LEN(advsm) == 0 && aux_data_offset == 0)); + + aux->aux_data_offset = aux_data_offset; + aux->aux_data_len = 0; + aux->payload_len = 0; + aux->ext_hdr = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + aux->chan = ble_ll_utils_calc_dci_csa2(advsm->event_cntr++, + advsm->channel_id, + g_ble_ll_conn_params.num_used_chans, + g_ble_ll_conn_params.master_chan_map); +#else + aux->chan = ble_ll_utils_remapped_channel(rand() % BLE_PHY_NUM_DATA_CHANS, + g_ble_ll_conn_params.master_chan_map); +#endif + + rem_aux_data_len = AUX_DATA_LEN(advsm) - aux_data_offset; + chainable = !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE); + + hdr_len = BLE_LL_EXT_ADV_HDR_LEN; + + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) { + /* ADI */ + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT); + hdr_len += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } + + /* AdvA for 1st PDU in chain (i.e. AUX_ADV_IND or AUX_SCAN_RSP) */ + if (aux_data_offset == 0 && + !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_ADVA_BIT); + hdr_len += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + /* Note: this function does not calculate AUX_ADV_IND when advertising is + * scannable. Instead it is calculated in ble_ll_adv_aux_schedule_first(). + * + * However this function calculates length of AUX_SCAN_RSP and according + * to BT 5.0 Vol 6 Part B, 2.3.2.3, TargetA shall not be include there. + * + * This is why TargetA is added to all directed advertising here unless it + * is scannable one. + * + * Note. TargetA shall not be also in AUX_CHAIN_IND + */ + if (aux_data_offset == 0 && + (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) && + !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) { + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT); + hdr_len += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + /* TxPower if configured. + * Note: TxPower should not be be present in AUX_CHAIN_IND + */ + if (aux_data_offset == 0 && + (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR)) { + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT); + hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + /* SyncInfo for 1st PDU in chain (i.e. AUX_ADV_IND only) if periodic + * advertising is enabled + */ + if (aux_data_offset == 0 && advsm->periodic_adv_active) { + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT); + hdr_len += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; + } +#endif + + /* if we have any fields in ext header we need to add flags, note that Aux + * PTR is handled later and it will account for flags if needed + */ + if (aux->ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + /* AdvData always */ + aux->aux_data_len = min(BLE_LL_MAX_PAYLOAD_LEN - hdr_len, rem_aux_data_len); + + /* AuxPtr if there are more AdvData remaining that we can fit here */ + if (chainable && (rem_aux_data_len > aux->aux_data_len)) { + /* adjust for flags that needs to be added if AuxPtr is only field + * in Extended Header + */ + if (!aux->ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + aux->aux_data_len -= BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT); + hdr_len += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + aux->aux_data_len -= BLE_LL_EXT_ADV_AUX_PTR_SIZE; + + /* PDU payload should be full if chained */ + assert(hdr_len + aux->aux_data_len == BLE_LL_MAX_PAYLOAD_LEN); + } + + aux->payload_len = hdr_len + aux->aux_data_len; +} + +static void +ble_ll_adv_aux_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start, + void *arg) +{ + struct ble_ll_adv_aux *aux = arg; + + aux->start_time = sch_start + g_ble_ll_sched_offset_ticks; +} + +static void +ble_ll_adv_aux_schedule_next(struct ble_ll_adv_sm *advsm) +{ + struct ble_ll_adv_aux *aux; + struct ble_ll_adv_aux *aux_next; + struct ble_ll_sched_item *sch; + uint16_t rem_aux_data_len; + uint16_t next_aux_data_offset; + uint32_t max_usecs; + + assert(advsm->aux_active); + + aux = AUX_CURRENT(advsm); + aux_next = AUX_NEXT(advsm); + + assert(!aux_next->sch.enqueued); + + /* + * Do not schedule next aux if current aux is no longer scheduled since we + * do not have reference time for scheduling. + */ + if (!aux->sch.enqueued) { + return; + } + + /* + * Do not schedule next aux if current aux does not have AuxPtr in extended + * header as this means we do not need subsequent ADV_CHAIN_IND to be sent. + */ + if (!(aux->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT))) { + return; + } + + next_aux_data_offset = aux->aux_data_offset + aux->aux_data_len; + + assert(AUX_DATA_LEN(advsm) >= next_aux_data_offset); + + rem_aux_data_len = AUX_DATA_LEN(advsm) - next_aux_data_offset; + assert(rem_aux_data_len > 0); + + ble_ll_adv_aux_calculate(advsm, aux_next, next_aux_data_offset); + max_usecs = ble_ll_pdu_tx_time_get(aux_next->payload_len, advsm->sec_phy); + + aux_next->start_time = aux->sch.end_time + + ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS + MYNEWT_VAL(BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY)); + + sch = &aux_next->sch; + sch->start_time = aux_next->start_time - g_ble_ll_sched_offset_ticks; + sch->remainder = 0; + sch->end_time = aux_next->start_time + + ble_ll_usecs_to_ticks_round_up(max_usecs); + ble_ll_sched_adv_new(&aux_next->sch, ble_ll_adv_aux_scheduled, aux_next); + + /* + * In case duration is set for advertising set we need to check if newly + * scheduled aux will fit inside duration. If not, remove it from scheduler + * so advertising will stop after current aux. + */ + if (advsm->duration && (aux_next->sch.end_time > advsm->adv_end_time)) { + ble_ll_sched_rmv_elem(&aux_next->sch); + } +} + +static void +ble_ll_adv_aux_schedule_first(struct ble_ll_adv_sm *advsm) +{ + struct ble_ll_adv_aux *aux; + struct ble_ll_sched_item *sch; + uint32_t max_usecs; + + assert(!advsm->aux_active); + assert(!advsm->aux[0].sch.enqueued); + assert(!advsm->aux[1].sch.enqueued); + + advsm->aux_active = 1; + advsm->aux_index = 0; + advsm->aux_first_pdu = 1; + advsm->aux_not_scanned = 0; + + aux = AUX_CURRENT(advsm); + ble_ll_adv_aux_calculate(advsm, aux, 0); + + /* Set end time to maximum time this schedule item may take */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + max_usecs = ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy) + + BLE_LL_IFS + + /* AUX_CONN_REQ */ + ble_ll_pdu_tx_time_get(34 + 14, advsm->sec_phy) + + BLE_LL_IFS + + /* AUX_CONN_RSP */ + ble_ll_pdu_tx_time_get(14, advsm->sec_phy); + } else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + /* For scannable advertising we need to calculate how much time we + * need for AUX_ADV_IND along with AUX_SCAN_REQ, AUX_SCAN_RSP and + * IFS in between. + * + * Note: + * 1. aux->payload_len, which calculated by above ble_ll_adv_aux_calulcate(), + * contains AUX_SCAN_RSP length. + * 2. length of AUX_ADV_IND is calculated by special function: + * ble_ll_adv_aux_scannable_pdu_payload_len() + */ + max_usecs = ble_ll_pdu_tx_time_get(ble_ll_adv_aux_scannable_pdu_payload_len(advsm), + advsm->sec_phy) + + BLE_LL_IFS + + /* AUX_SCAN_REQ */ + ble_ll_pdu_tx_time_get(12, advsm->sec_phy) + + BLE_LL_IFS + + /* AUX_SCAN_RSP */ + ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy); + } else { + max_usecs = ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy); + } + + sch = &aux->sch; + sch->start_time = aux->start_time - g_ble_ll_sched_offset_ticks; + sch->remainder = 0; + sch->end_time = aux->start_time + ble_ll_usecs_to_ticks_round_up(max_usecs); + ble_ll_sched_adv_new(sch, ble_ll_adv_aux_scheduled, aux); +} + +static void +ble_ll_adv_aux_set_start_time(struct ble_ll_adv_sm *advsm) +{ + static const uint8_t bits[8] = {0, 1, 1, 2, 1, 2, 2, 3}; + struct ble_ll_sched_item *sched = &advsm->adv_sch; + uint32_t adv_pdu_dur; + uint32_t adv_event_dur; + uint8_t chans; + + assert(!advsm->aux_active); + assert(!advsm->aux[0].sch.enqueued); + assert(!advsm->aux[1].sch.enqueued); + + assert(advsm->adv_chanmask > 0 && + advsm->adv_chanmask <= BLE_HCI_ADV_CHANMASK_DEF); + + chans = bits[advsm->adv_chanmask]; + + /* + * We want to schedule auxiliary packet as soon as possible after the end + * of advertising event, but no sooner than T_MAFS. The interval between + * advertising packets is 250 usecs (8.19 ticks) on LE Coded and a bit less + * on 1M, but it can vary a bit due to scheduling which we can't really + * control. Since we round ticks up for both interval and T_MAFS, we still + * have some margin here. The worst thing that can happen is that we skip + * last advertising packet which is not a bit problem so leave it as-is, no + * need to make code more complicated. + */ + + /* + * XXX: this could be improved if phy has TX-TX transition with controlled + * or predefined interval, but since it makes advertising code even + * more complicated let's skip it for now... + */ + + adv_pdu_dur = (int32_t)(sched->end_time - sched->start_time) - + g_ble_ll_sched_offset_ticks; + + /* 9 is 8.19 ticks rounded up - see comment above */ + adv_event_dur = (adv_pdu_dur * chans) + (9 * (chans - 1)); + + advsm->aux[0].start_time = advsm->adv_event_start_time + adv_event_dur + + ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS + MYNEWT_VAL(BLE_LL_SCHED_AUX_MAFS_DELAY)); +} + +static void +ble_ll_adv_aux_schedule(struct ble_ll_adv_sm *advsm) +{ + /* + * For secondary channel we always start by scheduling two consecutive + * auxiliary packets at once. Then, after sending one packet we try to + * schedule another one as long as there are some data left to send. This + * is to make sure we can always calculate AuxPtr to subsequent packet + * without need to scheduled it in an interrupt. + */ + + ble_ll_adv_aux_set_start_time(advsm); + ble_ll_adv_aux_schedule_first(advsm); + ble_ll_adv_aux_schedule_next(advsm); + + /* + * In case duration is set for advertising set we need to check if at least + * 1st aux will fit inside duration. If not, stop advertising now so we do + * not start extended advertising event which we cannot finish in time. + */ + if (advsm->duration && + (AUX_CURRENT(advsm)->sch.end_time > advsm->adv_end_time)) { + ble_ll_adv_sm_stop_timeout(advsm); + } +} +#endif + +/** + * Called when advertising need to be halted. This normally should not be called + * and is only called when a scheduled item executes but advertising is still + * running. + * + * Context: Interrupt + */ +void +ble_ll_adv_halt(void) +{ + struct ble_ll_adv_sm *advsm; + + if (g_ble_ll_cur_adv_sm != NULL) { + advsm = g_ble_ll_cur_adv_sm; + + ble_ll_trace_u32(BLE_LL_TRACE_ID_ADV_HALT, advsm->adv_instance); + + ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM)); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING) { + ble_ll_adv_flags_clear(advsm, + BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, + &advsm->adv_periodic_txdone_ev); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_cur_adv_sm = NULL; + return; + } +#endif + + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev); + } +#endif + + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_adv_active_chanset_clear(g_ble_ll_cur_adv_sm); + g_ble_ll_cur_adv_sm = NULL; + } else { + ble_ll_trace_u32(BLE_LL_TRACE_ID_ADV_HALT, UINT32_MAX); + } +} + +/** + * Called by the HCI command parser when a set advertising parameters command + * has been received. + * + * Context: Link Layer task (HCI command parser) + * + * @param cmd + * + * @return int + */ +int +ble_ll_adv_set_adv_params(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_adv_params_cp *cmd = (const void *) cmdbuf; + struct ble_ll_adv_sm *advsm; + uint8_t adv_filter_policy; + uint16_t adv_itvl_min; + uint16_t adv_itvl_max; + uint16_t props; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + advsm = &g_ble_ll_adv_sm[0]; + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Make sure intervals are OK (along with advertising type */ + adv_itvl_min = le16toh(cmd->min_interval); + adv_itvl_max = le16toh(cmd->max_interval); + + /* + * Get the filter policy now since we will ignore it if we are doing + * directed advertising + */ + adv_filter_policy = cmd->filter_policy; + + switch (cmd->type) { + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: + adv_filter_policy = BLE_HCI_ADV_FILT_NONE; + memcpy(advsm->peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + + /* Ignore min/max interval */ + adv_itvl_min = 0; + adv_itvl_max = 0; + + props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR ; + break; + case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: + adv_filter_policy = BLE_HCI_ADV_FILT_NONE; + memcpy(advsm->peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + + props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR ; + break; + case BLE_HCI_ADV_TYPE_ADV_IND: + props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND; + break; + case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: + props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN; + break; + case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: + props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN; + break; + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Make sure intervals values are valid + * (HD directed advertising ignores those parameters) + */ + if (!(props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED)) { + if ((adv_itvl_min > adv_itvl_max) || + (adv_itvl_min < BLE_HCI_ADV_ITVL_MIN) || + (adv_itvl_min > BLE_HCI_ADV_ITVL_MAX) || + (adv_itvl_max < BLE_HCI_ADV_ITVL_MIN) || + (adv_itvl_max > BLE_HCI_ADV_ITVL_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + if ((cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) || + (cmd->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + advsm->adv_txpwr = MYNEWT_VAL(BLE_LL_TX_PWR_DBM); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + /* Copy peer address */ + memcpy(advsm->peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + } +#else + /* If we dont support privacy some address types wont work */ + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + return BLE_ERR_UNSUPPORTED; + } +#endif + + /* There are only three adv channels, so check for any outside the range */ + if (((cmd->chan_map & 0xF8) != 0) || (cmd->chan_map == 0)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check for valid filter policy */ + if (adv_filter_policy > BLE_HCI_ADV_FILT_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Fill out rest of advertising state machine */ + advsm->own_addr_type = cmd->own_addr_type; + advsm->peer_addr_type = cmd->peer_addr_type; + advsm->adv_filter_policy = adv_filter_policy; + advsm->adv_chanmask = cmd->chan_map; + advsm->adv_itvl_min = adv_itvl_min; + advsm->adv_itvl_max = adv_itvl_max; + advsm->props = props; + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_adv_update_did(struct ble_ll_adv_sm *advsm) +{ + uint16_t old_adi = advsm->adi; + + /* + * The Advertising DID for a given advertising set shall be initialized + * with a randomly chosen value. Whenever the Host provides new advertising + * data or scan response data for a given advertising set (whether it is the + * same as the previous data or not), the Advertising DID shall be updated. + * The new value shall be a randomly chosen value that is not the same as + * the previously used value. + */ + do { + advsm->adi = (advsm->adi & 0xf000) | (rand() & 0x0fff); + } while (old_adi == advsm->adi); +} +#endif + +static void +ble_ll_adv_update_adv_scan_rsp_data(struct ble_ll_adv_sm *advsm) +{ + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA) && + !(advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA)) { + return; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->aux_active) { + return; + } +#endif + + if (advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA) { + if (advsm->new_adv_data) { + os_mbuf_free_chain(advsm->adv_data); + advsm->adv_data = advsm->new_adv_data; + advsm->new_adv_data = NULL; + } + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA); + } else if (advsm->flags & BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA) { + os_mbuf_free_chain(advsm->scan_rsp_data); + advsm->scan_rsp_data = advsm->new_scan_rsp_data; + advsm->new_scan_rsp_data = NULL; + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA); + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* DID shall be updated when host provides new advertising data */ + ble_ll_adv_update_did(advsm); +#endif +} + +/** + * Stop advertising state machine + * + * Context: Link Layer task. + * + * @param advsm + */ +static void +ble_ll_adv_sm_stop(struct ble_ll_adv_sm *advsm) +{ + os_sr_t sr; + + if (advsm->adv_enabled) { + ble_ll_rfmgmt_release(); + + /* Remove any scheduled advertising items */ + ble_ll_sched_rmv_elem(&advsm->adv_sch); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + advsm->aux_active = 0; + ble_ll_sched_rmv_elem(&advsm->aux[0].sch); + ble_ll_sched_rmv_elem(&advsm->aux[1].sch); +#endif + + /* Set to standby if we are no longer advertising */ + OS_ENTER_CRITICAL(sr); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if ((g_ble_ll_cur_adv_sm == advsm) && + !(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING)) { + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_cur_adv_sm = NULL; + ble_ll_scan_chk_resume(); + } +#else + if (ble_ll_state_get() == BLE_LL_STATE_ADV) { + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_cur_adv_sm = NULL; + ble_ll_scan_chk_resume(); + } +#endif + OS_EXIT_CRITICAL(sr); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev); +#endif + + /* If there is an event buf we need to free it */ + if (advsm->conn_comp_ev) { + ble_hci_trans_buf_free(advsm->conn_comp_ev); + advsm->conn_comp_ev = NULL; + } + + ble_ll_adv_active_chanset_clear(advsm); + + /* Disable advertising */ + advsm->adv_enabled = 0; + + /* Check if there is outstanding update */ + ble_ll_adv_update_adv_scan_rsp_data(advsm); + } +} + +static void +ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (ble_ll_hci_adv_mode_ext()) { + ble_ll_hci_ev_send_adv_set_terminated(BLE_ERR_DIR_ADV_TMO, + advsm->adv_instance, 0, + advsm->events); + } +#endif + + /* + * For high duty directed advertising we need to send connection + * complete event with proper status + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO, + advsm->conn_comp_ev, advsm); + advsm->conn_comp_ev = NULL; + } + + /* Disable advertising */ + ble_ll_adv_sm_stop(advsm); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_adv_sm_stop_limit_reached(struct ble_ll_adv_sm *advsm) +{ + ble_ll_hci_ev_send_adv_set_terminated(BLE_ERR_LIMIT_REACHED, + advsm->adv_instance, 0, + advsm->events); + + /* + * For high duty directed advertising we need to send connection + * complete event with proper status + * + * Spec is a bit unambiguous here since it doesn't define what code should + * be used if HD directed advertising was terminated before timeout due to + * events count limit. For now just use same code as with duration timeout. + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO, + advsm->conn_comp_ev, advsm); + advsm->conn_comp_ev = NULL; + } + + /* Disable advertising */ + ble_ll_adv_sm_stop(advsm); +} +#endif + +static void +ble_ll_adv_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start, void *arg) +{ + /* The event start time is when we start transmission of the adv PDU */ + advsm->adv_event_start_time = sch_start + g_ble_ll_sched_offset_ticks; + advsm->adv_pdu_start_time = advsm->adv_event_start_time; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* this is validated for HD adv so no need to do additional checks here + * duration is in 10ms units + */ + if (advsm->duration) { + advsm->adv_end_time = advsm->adv_event_start_time + + os_cputime_usecs_to_ticks(advsm->duration * 10000); + } +#else + /* Set the time at which we must end directed, high-duty cycle advertising. + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + advsm->adv_end_time = advsm->adv_event_start_time + + os_cputime_usecs_to_ticks(BLE_LL_ADV_STATE_HD_MAX * 1000); + } +#endif +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +static uint8_t +ble_ll_adv_sync_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_adv_sm *advsm; + struct ble_ll_adv_sync *sync; + uint8_t adv_mode; + uint8_t pdu_type; + uint8_t ext_hdr_len; + uint32_t offset; + + advsm = pducb_arg; + sync = SYNC_CURRENT(advsm); + + assert(!ble_ll_adv_active_chanset_is_sec(advsm)); + assert(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + + /* It's the same for AUX_SYNC_IND and AUX_CHAIN_IND */ + pdu_type = BLE_ADV_PDU_TYPE_AUX_SYNC_IND; + + /* non-connectable and non-scannable */ + adv_mode = 0; + + ext_hdr_len = sync->payload_len - BLE_LL_EXT_ADV_HDR_LEN - sync->sync_data_len; + dptr[0] = (adv_mode << 6) | ext_hdr_len; + dptr += 1; + + /* only put flags if needed */ + if (sync->ext_hdr) { + dptr[0] = sync->ext_hdr; + dptr += 1; + } + + if (sync->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + if (!SYNC_NEXT(advsm)->sch.enqueued) { + /* + * Trim data here in case we do not have next sync scheduled. This + * can happen if next sync was outside advertising set period and + * was removed from scheduler. + */ + offset = 0; + } else { + offset = os_cputime_ticks_to_usecs(SYNC_NEXT(advsm)->start_time - sync->start_time); + } + + ble_ll_adv_put_aux_ptr(SYNC_NEXT(advsm)->chan, advsm->sec_phy, + offset, dptr); + + dptr += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + } + + if (sync->ext_hdr & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { + dptr[0] = advsm->adv_txpwr + ble_ll_get_tx_pwr_compensation(); + dptr += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + if (sync->sync_data_len) { + os_mbuf_copydata(advsm->periodic_adv_data, sync->sync_data_offset, + sync->sync_data_len, dptr); + } + + *hdr_byte = pdu_type; + + return sync->payload_len; +} + + +static void +ble_ll_adv_sync_tx_done(struct ble_ll_adv_sm *advsm) +{ + /* reset power to max after advertising */ + ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM)); + + /* for sync we trace a no pri nor sec set */ + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_ADV_TXDONE, advsm->adv_instance, 0); + + assert(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + assert(!ble_ll_adv_active_chanset_is_sec(advsm)); + + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_periodic_txdone_ev); + + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + + /* We no longer have a current state machine */ + g_ble_ll_cur_adv_sm = NULL; +} + +/** + * Called to indicate the advertising sync event is over. + * + * Context: Interrupt + * + * @param advsm + * + */ +static void +ble_ll_adv_sync_tx_end(void *arg) +{ + struct ble_ll_adv_sm *advsm = arg; + + ble_ll_adv_sync_tx_done(advsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + /* store last sent periodic counter */ + advsm->periodic_event_cntr_last_sent = advsm->periodic_event_cntr; +#endif +} + +static int +ble_ll_adv_sync_tx_start_cb(struct ble_ll_sched_item *sch) +{ + int rc; + uint32_t txstart; + struct ble_ll_adv_sm *advsm; + struct ble_ll_adv_sync *sync; + + /* Get the state machine for the event */ + advsm = (struct ble_ll_adv_sm *)sch->cb_arg; + + /* Set the current advertiser */ + g_ble_ll_cur_adv_sm = advsm; + + ble_ll_adv_active_chanset_clear(advsm); + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + + /* Set the power */ + ble_phy_txpwr_set(advsm->adv_txpwr); + + /* Set channel */ + sync = SYNC_CURRENT(advsm); + rc = ble_phy_setchan(sync->chan, advsm->periodic_access_addr, + advsm->periodic_crcinit); + + assert(rc == 0); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* Set phy mode */ + ble_phy_mode_set(advsm->sec_phy, advsm->sec_phy); +#endif + + /* Set transmit start time. */ + txstart = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(txstart, sch->remainder); + if (rc) { + STATS_INC(ble_ll_stats, adv_late_starts); + goto adv_tx_done; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_phy_resolv_list_disable(); +#endif + + /* Transmit advertisement */ + ble_phy_set_txend_cb(ble_ll_adv_sync_tx_end, advsm); + rc = ble_phy_tx(ble_ll_adv_sync_pdu_make, advsm, BLE_PHY_TRANSITION_NONE); + if (rc) { + goto adv_tx_done; + } + + /* disable whitelisting, we are always non-connectable non-scannable */ + ble_ll_whitelist_disable(); + + /* Set link layer state to advertising */ + ble_ll_state_set(BLE_LL_STATE_ADV); + + /* Count # of adv. sent */ + STATS_INC(ble_ll_stats, adv_txg); + + return BLE_LL_SCHED_STATE_RUNNING; + +adv_tx_done: + ble_ll_adv_sync_tx_done(advsm); + return BLE_LL_SCHED_STATE_DONE; +} + +static void +ble_ll_adv_sync_calculate(struct ble_ll_adv_sm *advsm, + struct ble_ll_adv_sync *sync, + uint16_t sync_data_offset, + uint8_t chan) +{ + uint16_t rem_sync_data_len; + uint8_t hdr_len; + + assert(!sync->sch.enqueued); + assert((SYNC_DATA_LEN(advsm) > sync_data_offset) || + (SYNC_DATA_LEN(advsm) == 0 && sync_data_offset == 0)); + + sync->sync_data_offset = sync_data_offset; + sync->sync_data_len = 0; + sync->payload_len = 0; + sync->ext_hdr = 0; + sync->chan = chan; + + rem_sync_data_len = SYNC_DATA_LEN(advsm) - sync_data_offset; + + hdr_len = BLE_LL_EXT_ADV_HDR_LEN; + + /* TxPower if configured + * Note: TxPower shall not be present in chain PDU for SYNC + */ + if (sync_data_offset == 0 && + (advsm->periodic_adv_props & BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR)) { + sync->ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT); + hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + /* if we have any fields in ext header we need to add flags, note that Aux + * PTR is handled later and it will account for flags if needed + * + * This could be handled inside TxPower but lets keep code consistent with + * how Aux calculate works and this also make it easier to add more fields + * into flags if needed in future + */ + if (sync->ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + /* AdvData always */ + sync->sync_data_len = min(BLE_LL_MAX_PAYLOAD_LEN - hdr_len, rem_sync_data_len); + + /* AuxPtr if there are more AdvData remaining that we can fit here */ + if ((rem_sync_data_len > sync->sync_data_len)) { + /* adjust for flags that needs to be added if AuxPtr is only field + * in Extended Header + */ + if (!sync->ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + sync->sync_data_len -= BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + sync->ext_hdr |= (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT); + hdr_len += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + sync->sync_data_len -= BLE_LL_EXT_ADV_AUX_PTR_SIZE; + + /* PDU payload should be full if chained */ + assert(hdr_len + sync->sync_data_len == BLE_LL_MAX_PAYLOAD_LEN); + } + + sync->payload_len = hdr_len + sync->sync_data_len; +} + +static void +ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm, + bool first_pdu) +{ + struct ble_ll_adv_sync *sync; + struct ble_ll_sched_item *sch; + uint32_t sch_start; + uint32_t max_usecs; + uint8_t chan; + int rc; + + assert(!advsm->periodic_sync_active); + assert(!advsm->periodic_sync[0].sch.enqueued); + assert(!advsm->periodic_sync[1].sch.enqueued); + + advsm->periodic_sync_active = 1; + advsm->periodic_sync_index = 0; + + sync = SYNC_CURRENT(advsm); + + /* For first SYNC packet in chain we use separate CSA#2 state to maintain + * freq hopping as advertised in SyncInfo + * + * Preincrement event counter as we later send this in PDU so make sure + * same values are used + */ + chan = ble_ll_utils_calc_dci_csa2(++advsm->periodic_event_cntr, + advsm->periodic_channel_id, + advsm->periodic_num_used_chans, + advsm->periodic_chanmap); + + ble_ll_adv_sync_calculate(advsm, sync, 0, chan); + + /* sync is always non-connectable and non-scannable*/ + max_usecs = ble_ll_pdu_tx_time_get(sync->payload_len, advsm->sec_phy); + + sch = &sync->sch; + + advsm->periodic_adv_event_start_time_remainder += advsm->periodic_adv_itvl_rem_usec; + if (advsm->periodic_adv_event_start_time_remainder >= 31) { + advsm->periodic_adv_event_start_time++; + advsm->periodic_adv_event_start_time_remainder -= 31; + } + + sch->start_time = advsm->periodic_adv_event_start_time; + sch->remainder = advsm->periodic_adv_event_start_time_remainder; + sch->end_time = sch->start_time + ble_ll_usecs_to_ticks_round_up(max_usecs); + sch->start_time -= g_ble_ll_sched_offset_ticks; + + rc = ble_ll_sched_periodic_adv(sch, &sch_start, first_pdu); + if (rc) { + STATS_INC(ble_ll_stats, periodic_adv_drop_event); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, + &advsm->adv_periodic_txdone_ev); + return; + } + + sync->start_time = sch_start + g_ble_ll_sched_offset_ticks; + + assert(first_pdu || + (sync->start_time == advsm->periodic_adv_event_start_time)); + + /* The event start time is when we start transmission of the SYNC PDU */ + advsm->periodic_adv_event_start_time = sync->start_time; +} + +static void +ble_ll_adv_sync_next_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start, + void *arg) +{ + struct ble_ll_adv_sync *sync = arg; + + sync->start_time = sch_start + g_ble_ll_sched_offset_ticks; +} + +static void +ble_ll_adv_periodic_schedule_next(struct ble_ll_adv_sm *advsm) +{ + struct ble_ll_adv_sync *sync; + struct ble_ll_adv_sync *sync_next; + struct ble_ll_sched_item *sch; + uint16_t rem_sync_data_len; + uint16_t next_sync_data_offset; + uint32_t max_usecs; + uint8_t chan; + + assert(advsm->periodic_sync_active); + + sync = SYNC_CURRENT(advsm); + sync_next = SYNC_NEXT(advsm); + + assert(!sync_next->sch.enqueued); + + /* + * Do not schedule next sync if current sync is no longer scheduled since we + * do not have reference time for scheduling. + */ + if (!sync->sch.enqueued) { + return; + } + + /* + * Do not schedule next sync if current sync does not have AuxPtr in extended + * header as this means we do not need subsequent ADV_CHAIN_IND to be sent. + */ + if (!(sync->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT))) { + return; + } + + next_sync_data_offset = sync->sync_data_offset + sync->sync_data_len; + + assert(SYNC_DATA_LEN(advsm) >= next_sync_data_offset); + + rem_sync_data_len = SYNC_DATA_LEN(advsm) - next_sync_data_offset; + assert(rem_sync_data_len > 0); + + /* we use separate counter for chaining */ + chan = ble_ll_utils_calc_dci_csa2(advsm->periodic_chain_event_cntr++, + advsm->periodic_channel_id, + advsm->periodic_num_used_chans, + advsm->periodic_chanmap); + + ble_ll_adv_sync_calculate(advsm, sync_next, next_sync_data_offset, chan); + max_usecs = ble_ll_pdu_tx_time_get(sync_next->payload_len, advsm->sec_phy); + + sync_next->start_time = sync->sch.end_time + + ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS + MYNEWT_VAL(BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY)); + + sch = &sync_next->sch; + sch->start_time = sync_next->start_time - g_ble_ll_sched_offset_ticks; + + /* adjust for previous packets remainder */ + sch->remainder = sync->sch.remainder; + sch->end_time = sync_next->start_time + + ble_ll_usecs_to_ticks_round_up(max_usecs); + + /* here we can use ble_ll_sched_adv_new as we don't care about timing */ + ble_ll_sched_adv_new(&sync_next->sch, ble_ll_adv_sync_next_scheduled, + sync_next); + + /* if we are pass advertising interval, drop chain */ + if (sch->end_time > advsm->periodic_adv_event_start_time + + advsm->periodic_adv_itvl_ticks) { + STATS_INC(ble_ll_stats, periodic_chain_drop_event); + ble_ll_sched_rmv_elem(&sync->sch); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, + &advsm->adv_periodic_txdone_ev); + } +} + +static void +ble_ll_adv_sync_schedule(struct ble_ll_adv_sm *advsm, bool first_pdu) +{ + /* + * For secondary channel we always start by scheduling two consecutive + * auxiliary packets at once. Then, after sending one packet we try to + * schedule another one as long as there are some data left to send. This + * is to make sure we can always calculate AuxPtr to subsequent packet + * without need to scheduled it in an interrupt. + */ + + ble_ll_adv_periodic_schedule_first(advsm, first_pdu); + ble_ll_adv_periodic_schedule_next(advsm); +} + +static void +ble_ll_adv_reschedule_periodic_event(struct ble_ll_adv_sm *advsm) +{ + advsm->periodic_adv_event_start_time += advsm->periodic_adv_itvl_ticks; + ble_ll_adv_sync_schedule(advsm, false); +} + +static void +ble_ll_adv_update_periodic_data(struct ble_ll_adv_sm *advsm) +{ + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA)) { + return; + } + + if (advsm->periodic_sync_active) { + return; + } + + if (advsm->periodic_new_data) { + os_mbuf_free_chain(advsm->periodic_adv_data); + advsm->periodic_adv_data = advsm->periodic_new_data; + advsm->periodic_new_data = NULL; + } + + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA); +} + +/** + * Called when periodic packet is txd on secondary channel + * + * Context: Link Layer task. + * + * @param ev + */ +static void +ble_ll_adv_periodic_done(struct ble_ll_adv_sm *advsm) +{ + struct ble_ll_adv_sync *sync; + struct ble_ll_adv_sync *sync_next; + + assert(advsm->periodic_adv_enabled); + assert(advsm->periodic_adv_active); + assert(advsm->periodic_sync_active); + + ble_ll_rfmgmt_release(); + + sync = SYNC_CURRENT(advsm); + sync_next = SYNC_NEXT(advsm); + + /* Remove anything else scheduled for periodic */ + ble_ll_sched_rmv_elem(&sync->sch); + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_periodic_txdone_ev); + + /* If we have next SYNC scheduled, try to schedule another one */ + if (sync_next->sch.enqueued) { + advsm->periodic_sync_index ^= 1; + ble_ll_adv_periodic_schedule_next(advsm); + return; + } + + /* Check if we need to resume scanning */ + ble_ll_scan_chk_resume(); + + advsm->periodic_sync_active = 0; + ble_ll_adv_update_periodic_data(advsm); + ble_ll_adv_reschedule_periodic_event(advsm); +} + +static void +ble_ll_adv_periodic_event_done(struct ble_npl_event *ev) +{ + ble_ll_adv_periodic_done(ble_npl_event_get_arg(ev)); +} + +static void +ble_ll_adv_sm_start_periodic(struct ble_ll_adv_sm *advsm) +{ + uint32_t usecs; + uint32_t ticks; + + /* + * The Advertising DID is not required to change when a SyncInfo field is + * added to or removed from an advertising set. However, if it does not + * change, then scanners may fail to synchronize to periodic advertising + * because entries in the Advertising DID cache (see Section 4.3.3) mean + * they ignore the advertisements containing the SyncInfo field. Therefore, + * advertisers should update the Advertising DID when a periodic advertising + * train is enabled. + */ + ble_ll_adv_update_did(advsm); + + advsm->periodic_adv_active = 1; + + /* keep channel map since we cannot change it later on */ + memcpy(advsm->periodic_chanmap, g_ble_ll_conn_params.master_chan_map, + BLE_LL_CONN_CHMAP_LEN); + advsm->periodic_num_used_chans = g_ble_ll_conn_params.num_used_chans; + advsm->periodic_event_cntr = 0; + /* for chaining we start with random counter as we share access addr */ + advsm->periodic_chain_event_cntr = rand(); + advsm->periodic_access_addr = ble_ll_utils_calc_access_addr(); + advsm->periodic_channel_id = ((advsm->periodic_access_addr & 0xffff0000) >> 16) ^ + (advsm->periodic_access_addr & 0x0000ffff); + advsm->periodic_crcinit = rand() & 0xffffff; + + usecs = (uint32_t)advsm->periodic_adv_itvl_max * BLE_LL_ADV_PERIODIC_ITVL; + ticks = os_cputime_usecs_to_ticks(usecs); + + advsm->periodic_adv_itvl_rem_usec = (usecs - os_cputime_ticks_to_usecs(ticks)); + if (advsm->periodic_adv_itvl_rem_usec == 31) { + advsm->periodic_adv_itvl_rem_usec = 0; + ticks++; + } + advsm->periodic_adv_itvl_ticks = ticks; + + /* There is no point in starting periodic advertising until next advertising + * event since SyncInfo is needed for synchronization + */ + advsm->periodic_adv_event_start_time_remainder = 0; + advsm->periodic_adv_event_start_time = advsm->adv_pdu_start_time + + os_cputime_usecs_to_ticks(advsm->adv_itvl_usecs + 5000); + + ble_ll_adv_sync_schedule(advsm, true); +} + +static void +ble_ll_adv_sm_stop_periodic(struct ble_ll_adv_sm *advsm) +{ + os_sr_t sr; + + ble_ll_rfmgmt_release(); + + if (!advsm->periodic_adv_active) { + return; + } + + /* + * The Advertising DID is not required to change when a SyncInfo field is + * added to or removed from an advertising set. However, if it does not + * change, then scanners may unnecessary try to synchronize to instance that + * no longer has periodic advertising enabled because entries in the + * Advertising DID cache (see Section 4.3.3) mean they ignore the + * advertisements no longer containing the SyncInfo field. Therefore, + * advertisers should update the Advertising DID when a periodic advertising + * train is disabled. + */ + ble_ll_adv_update_did(advsm); + + /* Remove any scheduled advertising items */ + advsm->periodic_adv_active = 0; + advsm->periodic_sync_active = 0; + ble_ll_sched_rmv_elem(&advsm->periodic_sync[0].sch); + ble_ll_sched_rmv_elem(&advsm->periodic_sync[1].sch); + + /* Set to standby if we are no longer advertising */ + OS_ENTER_CRITICAL(sr); + if ((g_ble_ll_cur_adv_sm == advsm) && + (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING)) { + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_cur_adv_sm = NULL; + ble_ll_scan_chk_resume(); + } + OS_EXIT_CRITICAL(sr); + + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, + &advsm->adv_periodic_txdone_ev); + + ble_ll_adv_update_periodic_data(advsm); +} +#endif + +/** + * Start the advertising state machine. This is called when the host sends + * the "enable advertising" command and is not called again while in the + * advertising state. + * + * Context: Link-layer task. + * + * @param advsm Pointer to advertising state machine + * + * @return int + */ +static int +ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm) +{ + uint8_t adv_chan; + uint8_t *addr; + uint8_t *evbuf; + uint32_t start_delay_us; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + uint32_t access_addr; +#endif + const uint8_t *random_addr; + uint32_t earliest_start_time; + int32_t delta; + + /* only clear flags that are not set from HCI */ + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_TX_ADD | + BLE_LL_ADV_SM_FLAG_RX_ADD | + BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + random_addr = advsm->adv_random_addr; +#else + random_addr = g_random_addr; +#endif + + if (!ble_ll_is_valid_own_addr_type(advsm->own_addr_type, random_addr)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* + * Get an event with which to send the connection complete event if + * this is connectable + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + /* We expect this to be NULL but if not we wont allocate one... */ + if (advsm->conn_comp_ev == NULL) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!evbuf) { + return BLE_ERR_MEM_CAPACITY; + } + advsm->conn_comp_ev = evbuf; + } + } + + /* Set advertising address */ + if ((advsm->own_addr_type & 1) == 0) { + addr = g_dev_addr; + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + addr = advsm->adv_random_addr; +#else + addr = g_random_addr; +#endif + advsm->flags |= BLE_LL_ADV_SM_FLAG_TX_ADD; + } + memcpy(advsm->adva, addr, BLE_DEV_ADDR_LEN); + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + memcpy(advsm->initiator_addr, advsm->peer_addr, BLE_DEV_ADDR_LEN); + if (advsm->peer_addr_type & 1) { + advsm->flags |= BLE_LL_ADV_SM_FLAG_RX_ADD; + } + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* This will generate an RPA for both initiator addr and adva */ + if (advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + ble_ll_adv_rpa_update(advsm); + } +#endif + + /* Set flag telling us that advertising is enabled */ + advsm->adv_enabled = 1; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + advsm->event_cntr = 0; + access_addr = ble_ll_utils_calc_access_addr(); + advsm->channel_id = ((access_addr & 0xffff0000) >> 16) ^ + (access_addr & 0x0000ffff); +#endif + + /* Determine the advertising interval we will use */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + /* Set it to max. allowed for high duty cycle advertising */ + advsm->adv_itvl_usecs = BLE_LL_ADV_PDU_ITVL_HD_MS_MAX; + } else { + advsm->adv_itvl_usecs = (uint32_t)advsm->adv_itvl_max; + advsm->adv_itvl_usecs *= BLE_LL_ADV_ITVL; + } + + /* Set first advertising channel */ + adv_chan = ble_ll_adv_first_chan(advsm); + advsm->adv_chan = adv_chan; + + /* + * Scheduling 1st PDU is a bit tricky. + * Earliest possible start time is after RF is enabled so just force RF to + * start here to see when if will be fully enabled - it will be too early, + * but this is the only reliable way to have it enabled on time. + * Next we calculate expected start time (randomize it a bit) and this is + * used to setup start time for scheduler item. + * Then we check if start time for scheduler item (which includes scheduler + * overhead) is no earlier than calculated earliest possible start time and + * adjust scheduler item if necessary. + */ + earliest_start_time = ble_ll_rfmgmt_enable_now(); + + start_delay_us = rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); + advsm->adv_pdu_start_time = os_cputime_get32() + + os_cputime_usecs_to_ticks(start_delay_us); + + ble_ll_adv_set_sched(advsm); + + delta = (int32_t)(advsm->adv_sch.start_time - earliest_start_time); + if (delta < 0) { + advsm->adv_sch.start_time -= delta; + advsm->adv_sch.end_time -= delta; + } + + /* This does actual scheduling */ + ble_ll_sched_adv_new(&advsm->adv_sch, ble_ll_adv_scheduled, NULL); + + /* we start periodic before AE since we need PDU start time in SyncInfo */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (advsm->periodic_adv_enabled && !advsm->periodic_adv_active) { + ble_ll_adv_sm_start_periodic(advsm); + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { + ble_ll_adv_aux_schedule(advsm); + } +#endif + + return BLE_ERR_SUCCESS; +} + +/** + * Called when the LE HCI command read advertising channel tx power command + * has been received. Returns the current advertising transmit power. + * + * Context: Link Layer task (HCI command parser) + * + * @return int + */ +int +ble_ll_adv_read_txpwr(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_adv_chan_txpwr_rp *rsp = (void *) rspbuf; + + rsp->power_level = MYNEWT_VAL(BLE_LL_TX_PWR_DBM); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Turn advertising on/off. + * + * Context: Link Layer task + * + * @param cmd + * + * @return int + */ +static int +ble_ll_adv_set_enable(uint8_t instance, uint8_t enable, int duration, + uint8_t events) +{ + int rc; + struct ble_ll_adv_sm *advsm; + + advsm = ble_ll_adv_sm_find_configured(instance); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + rc = BLE_ERR_SUCCESS; + if (enable == 1) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->flags & BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (ble_ll_hci_adv_mode_ext() && + (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) && + !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && + SCAN_RSP_DATA_LEN(advsm) == 0) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* handle specifics of HD dir adv enabled in legacy way */ + if (duration < 0) { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + duration = BLE_LL_ADV_STATE_HD_MAX / 10; + } else { + duration = 0; + } + } + advsm->duration = duration; + advsm->events_max = events; + advsm->events = 0; +#endif + + /* If already enabled, do nothing */ + if (!advsm->adv_enabled) { + /* Start the advertising state machine */ + rc = ble_ll_adv_sm_start(advsm); + } + } else if (enable == 0) { + ble_ll_adv_sm_stop(advsm); + } else { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + } + + return rc; +} + +int +ble_ll_hci_adv_set_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_adv_enable_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_adv_set_enable(0, cmd->enable, -1, 0); +} + +static void +ble_ll_adv_update_data_mbuf(struct os_mbuf **omp, bool new_data, uint16_t maxlen, + const void *data, uint16_t datalen) +{ + struct os_mbuf *om; + int ret; + + om = *omp; + + if (new_data) { + if (om) { + os_mbuf_free_chain(om); + } + + om = os_msys_get_pkthdr(datalen, 0); + if (!om) { + goto done; + } + } + + assert(om); + + if (OS_MBUF_PKTLEN(om) + datalen > maxlen) { + os_mbuf_free_chain(om); + om = NULL; + goto done; + } + + ret = os_mbuf_append(om, data, datalen); + if (ret) { + os_mbuf_free_chain(om); + om = NULL; + } + +done: + *omp = om; +} + +/** + * Set the scan response data that the controller will send. + * + * @param cmd + * @param len + * + * @return int + */ +static int +ble_ll_adv_set_scan_rsp_data(const uint8_t *data, uint8_t datalen, + uint8_t instance, uint8_t operation) +{ + struct ble_ll_adv_sm *advsm; + bool new_data; + + advsm = ble_ll_adv_sm_find_configured(instance); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + /* check if type of advertising support scan rsp */ + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) { + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + switch (operation) { + case BLE_HCI_LE_SET_DATA_OPER_COMPLETE: + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + if (datalen > BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_HCI_LE_SET_DATA_OPER_LAST: + /* TODO mark scan rsp as complete? */ + /* fall through */ + case BLE_HCI_LE_SET_DATA_OPER_INT: + if (!advsm->scan_rsp_data) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (!datalen) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + break; + case BLE_HCI_LE_SET_DATA_OPER_FIRST: + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (!datalen) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + break; +#endif + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + new_data = (operation == BLE_HCI_LE_SET_DATA_OPER_COMPLETE) || + (operation == BLE_HCI_LE_SET_DATA_OPER_FIRST); + + if (advsm->adv_enabled) { + if (advsm->new_scan_rsp_data) { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA); + os_mbuf_free_chain(advsm->new_scan_rsp_data); + advsm->new_scan_rsp_data = NULL; + } + + ble_ll_adv_update_data_mbuf(&advsm->new_scan_rsp_data, new_data, + BLE_ADV_DATA_MAX_LEN, data, datalen); + if (!advsm->new_scan_rsp_data) { + return BLE_ERR_MEM_CAPACITY; + } + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_NEW_SCAN_RSP_DATA); + } else { + ble_ll_adv_update_data_mbuf(&advsm->scan_rsp_data, new_data, + BLE_SCAN_RSP_DATA_MAX_LEN, data, datalen); + if (!advsm->scan_rsp_data) { + return BLE_ERR_MEM_CAPACITY; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* DID shall be updated when host provides new scan response data */ + ble_ll_adv_update_did(advsm); +#endif + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_hci_set_scan_rsp_data(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_scan_rsp_data_cp *cmd = (const void *) cmdbuf; + + if ((len != sizeof(*cmd)) || (cmd->scan_rsp_len > sizeof(cmd->scan_rsp))) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_adv_set_scan_rsp_data(cmd->scan_rsp, cmd->scan_rsp_len, 0, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE); +} +/** + * Called by the LL HCI command parser when a set advertising + * data command has been sent from the host to the controller. + * + * @param cmd Pointer to command data + * @param len Length of command data + * + * @return int 0: success; BLE_ERR_INV_HCI_CMD_PARMS otherwise. + */ +static int +ble_ll_adv_set_adv_data(const uint8_t *data, uint8_t datalen, uint8_t instance, + uint8_t operation) +{ + struct ble_ll_adv_sm *advsm; + bool new_data; + + advsm = ble_ll_adv_sm_find_configured(instance); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + /* check if type of advertising support adv data */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + if (ble_ll_hci_adv_mode_ext()) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + } else { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + switch (operation) { + case BLE_HCI_LE_SET_DATA_OPER_COMPLETE: + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + if (datalen > BLE_ADV_LEGACY_DATA_MAX_LEN) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE); + + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_HCI_LE_SET_DATA_OPER_UNCHANGED: + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!advsm->adv_enabled || !ADV_DATA_LEN(advsm) || datalen) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* update DID only */ + ble_ll_adv_update_did(advsm); + return BLE_ERR_SUCCESS; + case BLE_HCI_LE_SET_DATA_OPER_LAST: + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE); + /* fall through */ + case BLE_HCI_LE_SET_DATA_OPER_INT: + if (!advsm->adv_data) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!datalen) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + break; + case BLE_HCI_LE_SET_DATA_OPER_FIRST: + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (!datalen) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE); + break; +#endif + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + new_data = (operation == BLE_HCI_LE_SET_DATA_OPER_COMPLETE) || + (operation == BLE_HCI_LE_SET_DATA_OPER_FIRST); + + if (advsm->adv_enabled) { + if (advsm->new_adv_data) { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA); + os_mbuf_free_chain(advsm->new_adv_data); + advsm->new_adv_data = NULL; + } + + ble_ll_adv_update_data_mbuf(&advsm->new_adv_data, new_data, + BLE_ADV_DATA_MAX_LEN, data, datalen); + if (!advsm->new_adv_data) { + return BLE_ERR_MEM_CAPACITY; + } + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_NEW_ADV_DATA); + } else { + ble_ll_adv_update_data_mbuf(&advsm->adv_data, new_data, + BLE_ADV_DATA_MAX_LEN, data, datalen); + if (!advsm->adv_data) { + return BLE_ERR_MEM_CAPACITY; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* DID shall be updated when host provides new advertising data */ + ble_ll_adv_update_did(advsm); +#endif + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_hci_set_adv_data(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_adv_data_cp *cmd = (const void *) cmdbuf; + + if ((len != sizeof(*cmd)) || (cmd->adv_data_len > sizeof(cmd->adv_data))) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_adv_set_adv_data(cmd->adv_data, cmd->adv_data_len, 0, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static bool +pri_phy_valid(uint8_t phy) +{ + switch (phy) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_HCI_LE_PHY_CODED: +#endif + case BLE_HCI_LE_PHY_1M: + return true; + default: + return false; + } +} + +static bool +sec_phy_valid(uint8_t phy) +{ + switch (phy) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_HCI_LE_PHY_CODED: +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_HCI_LE_PHY_2M: +#endif + case BLE_HCI_LE_PHY_1M: + return true; + default: + return false; + } +} + +static struct ble_ll_adv_sm * +ble_ll_adv_sm_get(uint8_t instance) +{ + struct ble_ll_adv_sm *advsm; + int i; + + advsm = ble_ll_adv_sm_find_configured(instance); + if (advsm) { + return advsm; + } + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_adv_sm); i++) { + advsm = &g_ble_ll_adv_sm[i]; + + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_CONFIGURED)) { + ble_ll_adv_sm_init(advsm); + + /* configured flag is set by caller on success config */ + advsm->adv_instance = instance; + return advsm; + } + } + + return NULL; +} + +int +ble_ll_adv_ext_set_param(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_set_ext_adv_params_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_set_ext_adv_params_rp *rsp = (void *) rspbuf; + struct ble_ll_adv_sm *advsm; + uint32_t adv_itvl_min; + uint32_t adv_itvl_max; + uint16_t props; + int rc; + + if (len != sizeof(*cmd )) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + advsm = ble_ll_adv_sm_get(cmd->adv_handle); + if (!advsm) { + rc = BLE_ERR_MEM_CAPACITY; + goto done; + } + + if (advsm->adv_enabled) { + rc = BLE_ERR_CMD_DISALLOWED; + goto done; + } + + props = le16toh(cmd->props); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + /* If the Host issues this command when periodic advertising is enabled for + * the specified advertising set and connectable, scannable, legacy, or + * anonymous advertising is specified, the Controller shall return the + * error code Invalid HCI Command Parameters (0x12). + */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_CONFIGURED) { + if (advsm->periodic_adv_enabled) { + if (props & (BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY | + BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + } + } +#endif + + adv_itvl_min = cmd->pri_itvl_min[2] << 16 | cmd->pri_itvl_min[1] << 8 | + cmd->pri_itvl_min[0]; + adv_itvl_max = cmd->pri_itvl_max[2] << 16 | cmd->pri_itvl_max[1] << 8 | + cmd->pri_itvl_max[0]; + + if (props & ~BLE_HCI_LE_SET_EXT_ADV_PROP_MASK) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + if (ADV_DATA_LEN(advsm) > BLE_ADV_LEGACY_DATA_MAX_LEN || + SCAN_RSP_DATA_LEN(advsm) > BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* if legacy bit is set possible values are limited */ + switch (props) { + case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND: + case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR: + case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR: + case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN: + case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN: + break; + default: + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + } else { + /* HD directed advertising allowed only on legacy PDUs */ + if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* if ext advertising PDUs are used then it shall not be both + * connectable and scanable + */ + if ((props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) && + (props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + } + + /* High Duty Directed advertising is special */ + if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + if (ADV_DATA_LEN(advsm) || SCAN_RSP_DATA_LEN(advsm)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* Ignore min/max interval */ + adv_itvl_min = 0; + adv_itvl_max = 0; + } else { + /* validate intervals for non HD-directed advertising */ + if ((adv_itvl_min > adv_itvl_max) || + (adv_itvl_min < BLE_HCI_ADV_ITVL_MIN) || + (adv_itvl_max < BLE_HCI_ADV_ITVL_MIN)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* TODO for now limit those to values from legacy advertising + * + * If the primary advertising interval range is outside the advertising + * interval range supported by the Controller, then the Controller shall + * return the error code Unsupported Feature or Parameter Value (0x11). + */ + if ((adv_itvl_min > BLE_HCI_ADV_ITVL_MAX) || + (adv_itvl_max > BLE_HCI_ADV_ITVL_MAX)) { + rc = BLE_ERR_UNSUPPORTED; + goto done; + } + } + + /* There are only three adv channels, so check for any outside the range */ + if (((cmd->pri_chan_map & 0xF8) != 0) || (cmd->pri_chan_map == 0)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* If we dont support privacy some address types wont work */ + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + rc = BLE_ERR_UNSUPPORTED; + goto done; + } +#endif + + /* peer address type is only valid for directed */ + if ((props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) && + (cmd->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* Check filter policy (valid only for undirected) */ + if (!(props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) && + cmd->filter_policy > BLE_HCI_ADV_FILT_MAX) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (!pri_phy_valid(cmd->pri_phy)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* check secondary phy only if not using legacy PDUs */ + if (!(props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && + !sec_phy_valid(cmd->sec_phy)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (cmd->sid > 0x0f) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (cmd->scan_req_notif > 0x01) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + rc = BLE_ERR_SUCCESS; + + if (cmd->tx_power == 127) { + /* no preference */ + advsm->adv_txpwr = MYNEWT_VAL(BLE_LL_TX_PWR_DBM); + } else { + advsm->adv_txpwr = ble_phy_txpower_round(cmd->tx_power); + } + + /* we can always store as those are validated and used only when needed */ + advsm->peer_addr_type = cmd->peer_addr_type; + memcpy(advsm->peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + advsm->own_addr_type = cmd->own_addr_type; + advsm->adv_filter_policy = cmd->filter_policy; + advsm->adv_chanmask = cmd->pri_chan_map; + advsm->adv_itvl_min = adv_itvl_min; + advsm->adv_itvl_max = adv_itvl_max; + advsm->pri_phy = cmd->pri_phy; + advsm->sec_phy = cmd->sec_phy; + /* Update SID only */ + advsm->adi = (advsm->adi & 0x0fff) | ((cmd->sid << 12)); + + advsm->props = props; + + /* Set proper mbuf chain for aux data */ + if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + advsm->aux_data = NULL; + } else if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + advsm->aux_data = &advsm->scan_rsp_data; + } else { + advsm->aux_data = &advsm->adv_data; + } + + if (cmd->scan_req_notif) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF); + } else { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF); + } + + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_CONFIGURED); + +done: + /* Update TX power */ + rsp->tx_power = rc ? 0 : advsm->adv_txpwr; + + *rsplen = sizeof(*rsp); + return rc; +} + +int +ble_ll_adv_ext_set_adv_data(const uint8_t *cmdbuf, uint8_t cmdlen) +{ + const struct ble_hci_le_set_ext_adv_data_cp *cmd = (const void *) cmdbuf; + + if (cmdlen < sizeof(*cmd )) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->adv_data_len > BLE_HCI_MAX_EXT_ADV_DATA_LEN || + cmd->adv_data_len > cmdlen - sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* TODO fragment preference ignored for now */ + + return ble_ll_adv_set_adv_data(cmd->adv_data, cmd->adv_data_len, + cmd->adv_handle, cmd->operation); +} + +int +ble_ll_adv_ext_set_scan_rsp(const uint8_t *cmdbuf, uint8_t cmdlen) +{ + const struct ble_hci_le_set_ext_scan_rsp_data_cp *cmd = (const void *) cmdbuf; + + if (cmdlen < sizeof(*cmd )) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->scan_rsp_len > BLE_HCI_MAX_EXT_ADV_DATA_LEN || + cmd->scan_rsp_len > cmdlen - sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* TODO fragment preference ignored for now */ + + return ble_ll_adv_set_scan_rsp_data(cmd->scan_rsp, cmd->scan_rsp_len, + cmd->adv_handle, cmd->operation); +} + +/** + * HCI LE extended advertising enable command + * + * @param cmd Pointer to command data + * @param len Command data length + * + * @return int BLE error code + */ +int +ble_ll_adv_ext_set_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_ext_adv_enable_cp *cmd = (const void *) cmdbuf; + struct ble_ll_adv_sm *advsm; + int i, j, rc; + + if (len < sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* check if length is correct */ + if (len != 2 + (cmd->num_sets * sizeof(cmd->sets[0]))) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->num_sets > BLE_ADV_INSTANCES) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->num_sets == 0) { + if (cmd->enable) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* disable all instances */ + for (i = 0; i < BLE_ADV_INSTANCES; i++) { + ble_ll_adv_set_enable(i, 0, 0, 0); + } + + return BLE_ERR_SUCCESS; + } + + /* validate instances */ + for (i = 0; i < cmd->num_sets; i++) { + /* validate duplicated sets */ + for (j = i + 1; j < cmd->num_sets; j++) { + if (cmd->sets[i].adv_handle == cmd->sets[j].adv_handle) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + + advsm = ble_ll_adv_sm_find_configured(cmd->sets[i].adv_handle); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + if (cmd->enable) { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + if (cmd->sets[i].duration == 0 || + le16toh(cmd->sets[i].duration) > 128) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } + } + } + + for (i = 0; i < cmd->num_sets; i++) { + rc = ble_ll_adv_set_enable(cmd->sets[i].adv_handle, cmd->enable, + le16toh(cmd->sets[i].duration), + cmd->sets[i].max_events); + if (rc) { + return rc; + } + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_adv_set_random_addr(const uint8_t *addr, uint8_t instance) +{ + struct ble_ll_adv_sm *advsm; + + advsm = ble_ll_adv_sm_find_configured(instance); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + /* + * Reject if connectable advertising is on + * Core Spec Vol. 2 Part E 7.8.52 + */ + if (advsm->adv_enabled && + (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE)) { + return BLE_ERR_CMD_DISALLOWED; + } + + memcpy(advsm->adv_random_addr, addr, BLE_DEV_ADDR_LEN); + return BLE_ERR_SUCCESS; +} + +int +ble_ll_adv_hci_set_random_addr(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_adv_set_rnd_addr_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_adv_set_random_addr(cmd->addr, cmd->adv_handle); +} + +/** + * HCI LE extended advertising remove command + * + * @return int BLE error code + */ +int +ble_ll_adv_remove(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_remove_adv_set_cp *cmd = (const void *) cmdbuf; + struct ble_ll_adv_sm *advsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + advsm = ble_ll_adv_sm_find_configured(cmd->adv_handle); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + if (advsm->adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (advsm->periodic_adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (advsm->periodic_adv_data) { + os_mbuf_free_chain(advsm->periodic_adv_data); + } +#endif + + if (advsm->adv_data) { + os_mbuf_free_chain(advsm->adv_data); + } + if (advsm->scan_rsp_data) { + os_mbuf_free_chain(advsm->scan_rsp_data); + } + + ble_ll_adv_sm_init(advsm); + + return BLE_ERR_SUCCESS; +} + +/** + * HCI LE extended advertising clear command + * + * @return int BLE error code + */ +int +ble_ll_adv_clear_all(void) +{ + int i; + + for (i = 0; i < BLE_ADV_INSTANCES; i++) { + if (g_ble_ll_adv_sm[i].adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (g_ble_ll_adv_sm[i].periodic_adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } +#endif + } + + ble_ll_adv_reset(); + + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +static uint16_t +ble_ll_adv_sync_get_pdu_len(uint16_t data_len, uint16_t *data_offset, + uint16_t props) +{ + uint16_t rem_data_len = data_len - *data_offset; + uint8_t hdr_len = BLE_LL_EXT_ADV_HDR_LEN; + uint8_t ext_hdr = 0; + + /* TxPower if configured + * Note: TxPower shall not be present in chain PDU for SYNC + */ + if (*data_offset == 0 && + (props & BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR)) { + ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT); + hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + /* if we have any fields in ext header we need to add flags, note that Aux + * PTR is handled later and it will account for flags if needed + * + * This could be handled inside TxPower but lets keep code consistent with + * how Aux calculate works and this also make it easier to add more fields + * into flags if needed in future + */ + if (ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + /* AdvData always */ + data_len = min(BLE_LL_MAX_PAYLOAD_LEN - hdr_len, rem_data_len); + + /* AuxPtr if there are more AdvData remaining that we can fit here */ + if (rem_data_len > data_len) { + /* adjust for flags that needs to be added if AuxPtr is only field + * in Extended Header + */ + if (!ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + data_len -= BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + hdr_len += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + data_len -= BLE_LL_EXT_ADV_AUX_PTR_SIZE; + + /* PDU payload should be full if chained */ + BLE_LL_ASSERT(hdr_len + data_len == BLE_LL_MAX_PAYLOAD_LEN); + } + + *data_offset += data_len; + + return hdr_len + data_len; +} + +static bool +ble_ll_adv_periodic_check_data_itvl(uint16_t payload_len, uint16_t props, + uint16_t itvl, uint8_t phy) +{ + uint32_t max_usecs = 0; + uint32_t itvl_usecs; + uint16_t offset = 0; + uint16_t pdu_len; + + while (offset < payload_len) { + pdu_len = ble_ll_adv_sync_get_pdu_len(payload_len, &offset, props); + + max_usecs += ble_ll_pdu_tx_time_get(pdu_len, phy); + max_usecs += ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS + + MYNEWT_VAL(BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY)); + } + + itvl_usecs = (uint32_t)itvl * BLE_LL_ADV_PERIODIC_ITVL; + + return max_usecs < itvl_usecs; +} + +int +ble_ll_adv_periodic_set_param(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_periodic_adv_params_cp *cmd = (const void *) cmdbuf; + struct ble_ll_adv_sm *advsm; + uint16_t adv_itvl_min; + uint16_t adv_itvl_max; + uint16_t props; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + adv_itvl_min = le16toh(cmd->min_itvl); + adv_itvl_max = le16toh(cmd->max_itvl); + props = le16toh(cmd->props); + + advsm = ble_ll_adv_sm_find_configured(cmd->adv_handle); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + /* If the advertising set identified by the Advertising_Handle specified + * scannable, connectable, legacy, or anonymous advertising, the Controller + * shall return the error code Invalid HCI Command Parameters (0x12). + */ + if (advsm->props & (BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV | + BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If the Host issues this command when periodic advertising is enabled for + * the specified advertising set, the Controller shall return the error code + * Command Disallowed (0x0C). + */ + if (advsm->periodic_adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* validate intervals */ + if ((adv_itvl_min < 0x0006) || (adv_itvl_max < 0x006) || + (adv_itvl_min > adv_itvl_max)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* validate properties */ + if (props & ~BLE_HCI_LE_SET_PERIODIC_ADV_PROP_MASK) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If the advertising set already contains periodic advertising data and the + * length of the data is greater than the maximum that the Controller can + * transmit within a periodic advertising interval of + * Periodic_Advertising_Interval_Max, the Controller shall return the error + * code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(SYNC_DATA_LEN(advsm), props, + adv_itvl_max, advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + + advsm->periodic_adv_itvl_min = adv_itvl_min; + advsm->periodic_adv_itvl_max = adv_itvl_max; + advsm->periodic_adv_props = props; + + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_CONFIGURED); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_adv_periodic_set_data(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_periodic_adv_data_cp *cmd = (const void *) cmdbuf; + struct ble_ll_adv_sm *advsm; + uint16_t payload_total_len; + bool new_data = false; + + if (len < sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->adv_data_len > BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN || + cmd->adv_data_len != len - sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + advsm = ble_ll_adv_sm_find_configured(cmd->adv_handle); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_CONFIGURED)) { + return BLE_ERR_CMD_DISALLOWED; + } + + switch (cmd->operation) { + case BLE_HCI_LE_SET_DATA_OPER_LAST: + case BLE_HCI_LE_SET_DATA_OPER_INT: + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_DATA_INCOMPLETE)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!advsm->periodic_adv_data || !cmd->adv_data_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (advsm->periodic_adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + break; + case BLE_HCI_LE_SET_DATA_OPER_FIRST: + if (advsm->periodic_adv_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (!cmd->adv_data_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + new_data = true; + break; + case BLE_HCI_LE_SET_DATA_OPER_COMPLETE: + new_data = true; + break; + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + payload_total_len = cmd->adv_data_len; + if (!new_data) { + payload_total_len += SYNC_DATA_LEN(advsm); + } + + /* If the combined length of the data is greater than the maximum that the + * Controller can transmit within the current periodic advertising interval + * (if periodic advertising is currently enabled) or the + * Periodic_Advertising_Interval_Max for the advertising set (if currently + * disabled), all the data shall be discarded and the Controller shall + * return the error code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(payload_total_len, + advsm->periodic_adv_props, + advsm->periodic_adv_itvl_max, + advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + + if (advsm->periodic_adv_active) { + ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA); + + ble_ll_adv_update_data_mbuf(&advsm->periodic_new_data, true, + BLE_ADV_DATA_MAX_LEN, + cmd->adv_data, cmd->adv_data_len); + if (!advsm->periodic_new_data) { + return BLE_ERR_MEM_CAPACITY; + } + + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA); + } else { + ble_ll_adv_update_data_mbuf(&advsm->periodic_adv_data, new_data, + BLE_ADV_DATA_MAX_LEN, cmd->adv_data, + cmd->adv_data_len); + if (!advsm->periodic_adv_data) { + return BLE_ERR_MEM_CAPACITY; + } + } + + /* set/clear incomplete data flag only on success */ + switch (cmd->operation) { + case BLE_HCI_LE_SET_DATA_OPER_LAST: + case BLE_HCI_LE_SET_DATA_OPER_COMPLETE: + ble_ll_adv_flags_clear(advsm, + BLE_LL_ADV_SM_FLAG_PERIODIC_DATA_INCOMPLETE); + break; + case BLE_HCI_LE_SET_DATA_OPER_INT: + case BLE_HCI_LE_SET_DATA_OPER_FIRST: + default: + ble_ll_adv_flags_set(advsm, + BLE_LL_ADV_SM_FLAG_PERIODIC_DATA_INCOMPLETE); + break; + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_adv_periodic_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_periodic_adv_enable_cp *cmd = (const void *)cmdbuf; + struct ble_ll_adv_sm *advsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + advsm = ble_ll_adv_sm_find_configured(cmd->adv_handle); + if (!advsm) { + return BLE_ERR_UNK_ADV_INDENT; + } + + if (cmd->enable) { + if (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_DATA_INCOMPLETE) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* If Enable is set to 0x01 and the length of the periodic advertising + * data is greater than the maximum that the Controller can transmit + * within the chosen periodicadvertising interval, the Controller shall + * return the error code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(SYNC_DATA_LEN(advsm), + advsm->periodic_adv_props, + advsm->periodic_adv_itvl_max, + advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + + /* If the advertising set is not currently enabled (see the + * LE_Set_Extended_Advertising_Enable command), the periodic advertising + * is not started until the advertising set is enabled. + */ + if (advsm->adv_enabled && !advsm->periodic_adv_active) { + /* Start the periodic advertising state machine */ + ble_ll_adv_sm_start_periodic(advsm); + } + } else { + /* Stop the periodic advertising state machine */ + ble_ll_adv_sm_stop_periodic(advsm); + } + + advsm->periodic_adv_enabled = cmd->enable; + + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +static int +ble_ll_adv_periodic_send_sync_ind(struct ble_ll_adv_sm *advsm, + struct ble_ll_conn_sm *connsm, + uint16_t service_data) +{ + struct os_mbuf *om; + uint8_t *sync_ind; + + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, + sizeof(struct ble_mbuf_hdr)); + if (!om) { + return BLE_ERR_MEM_CAPACITY; + } + + om->om_data[0] = BLE_LL_CTRL_PERIODIC_SYNC_IND; + + sync_ind = om->om_data + 1; + + /* ID (service_data), already in LE order */ + memcpy(sync_ind, &service_data, sizeof(service_data)); + + /* fill in syncinfo */ + ble_ll_adv_put_syncinfo(advsm, connsm, sync_ind + 20, sync_ind + 2); + + /* lastPaEventCounter */ + put_le16(sync_ind + 22, advsm->periodic_event_cntr_last_sent); + + /* SID, AType, SCA */ + sync_ind[24] = (advsm->adi >> 12); + sync_ind[24] |= !!(advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) << 4 ; + sync_ind[24] |= MYNEWT_VAL(BLE_LL_MASTER_SCA) << 5; + + /* PHY */ + sync_ind[25] = (0x01 << (advsm->sec_phy - 1)); + + /* AdvA */ + memcpy(sync_ind + 26, advsm->adva, BLE_DEV_ADDR_LEN); + + /* syncConnEventCount */ + put_le16(sync_ind + 32, connsm->event_cntr); + + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, + BLE_LL_CTRL_PERIODIC_SYNC_IND_LEN + 1); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_adv_periodic_set_info_transfer(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_periodic_adv_set_info_transfer_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_periodic_adv_set_info_transfer_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + struct ble_ll_adv_sm *advsm; + uint16_t handle; + int rc; + + if (len != sizeof(*cmd)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + advsm = ble_ll_adv_sm_find_configured(cmd->adv_handle); + if (!advsm) { + rc = BLE_ERR_UNK_ADV_INDENT; + goto done; + } + + if (!advsm->periodic_adv_active) { + rc = BLE_ERR_CMD_DISALLOWED; + goto done; + } + + handle = le16toh(cmd->conn_handle); + if (handle > 0xeff) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto done; + } + + /* TODO should not need to shift + * byte 3 (0 byte is conn_feature) , bit 1 + * + * Allow initiate LL procedure only if remote supports it. + */ + if (!(connsm->remote_features[2] & (BLE_LL_FEAT_SYNC_TRANS_RECV >> (8 * 3)))) { + rc = BLE_ERR_UNSUPP_REM_FEATURE; + goto done; + } + + rc = ble_ll_adv_periodic_send_sync_ind(advsm, connsm, cmd->service_data); + done: + rsp->conn_handle = cmd->conn_handle; + *rsplen = sizeof(*rsp); + return rc; +} +#endif +#endif +#endif + +/** + * Says whether the specified address is already connected or not. + * @param [in] addr The peer address. + * @param [in] addr_type Public address (0) or random address (1). + * @return Return 1 if already connected, 0 otherwise. + */ +static int +ble_ll_adv_already_connected(const uint8_t* addr, uint8_t addr_type) +{ + struct ble_ll_conn_sm *connsm; + + /* extracted from ble_ll_conn_slave_start function */ + SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) { + if (!memcmp(&connsm->peer_addr, addr, BLE_DEV_ADDR_LEN)) { + if (addr_type == BLE_ADDR_RANDOM) { + if (connsm->peer_addr_type & 1) { + return 1; + } + } else { + if ((connsm->peer_addr_type & 1) == 0) { + return 1; + } + } + } + } + + return 0; +} + +/** + * Called when the LL receives a scan request or connection request + * + * Context: Called from interrupt context. + * + * @param rxbuf + * + * @return -1: request not for us or is a connect request. + * 0: request (scan) is for us and we successfully went from rx to tx. + * > 0: PHY error attempting to go from rx to tx. + */ +static int +ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) +{ + int rc; + int resolved; + uint8_t chk_wl; + uint8_t txadd; + uint8_t peer_addr_type; + uint8_t *rxbuf; + uint8_t *adva; + uint8_t *peer; + struct ble_mbuf_hdr *ble_hdr; + struct ble_ll_adv_sm *advsm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct aux_conn_rsp_data rsp_data; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + struct ble_ll_resolv_entry *rl; +#endif + + /* See if adva in the request (scan or connect) matches what we sent */ + advsm = g_ble_ll_cur_adv_sm; + rxbuf = rxpdu->om_data; + adva = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; + if (memcmp(advsm->adva, adva, BLE_DEV_ADDR_LEN)) { + return -1; + } + + /* Set device match bit if we are whitelisting */ + if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { + chk_wl = advsm->adv_filter_policy & 1; + } else { + chk_wl = advsm->adv_filter_policy & 2; + } + + /* Get the peer address type */ + if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) { + txadd = BLE_ADDR_RANDOM; + } else { + txadd = BLE_ADDR_PUBLIC; + } + + ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); + peer = rxbuf + BLE_LL_PDU_HDR_LEN; + peer_addr_type = txadd; + resolved = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + rl = NULL; + if (ble_ll_resolv_enabled()) { + if (ble_ll_is_rpa(peer, txadd)) { + advsm->adv_rpa_index = ble_hw_resolv_list_match(); + if (advsm->adv_rpa_index >= 0) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED; + rl = &g_ble_ll_resolv_list[advsm->adv_rpa_index]; + if (chk_wl) { + peer = rl->rl_identity_addr; + peer_addr_type = rl->rl_addr_type; + resolved = 1; + } + } else { + if (chk_wl) { + return -1; + } + } + } else { + /* Verify privacy mode */ + rl = ble_ll_resolv_list_find(peer, peer_addr_type); + if (rl && (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && + rl->rl_has_peer) { + return -1; + } + } + } +#endif + + /* Set device match bit if we are whitelisting */ + if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) { + return -1; + } + + /* + * We set the device match bit to tell the upper layer that we will + * accept the request + */ + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH; + + /* Setup to transmit the scan response if appropriate */ + rc = -1; + + if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { + /* PHY used for scan requests shall be the same as the PHY used for the + * PDU that they reply to so no need to change PHY mode. + */ + ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->flags & BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF) { + ble_ll_hci_ev_send_scan_req_recv(advsm->adv_instance, peer, + peer_addr_type); + } + + /* + * We need to store current rxed packet header temporarily so AuxPtr + * can be calculated (if necessary) relative to AUX_SCAN_RSP instead of + * AUX_ADV_IND. + */ + + advsm->rx_ble_hdr = ble_hdr; + rc = ble_phy_tx(ble_ll_adv_scan_rsp_pdu_make, advsm, + BLE_PHY_TRANSITION_NONE); + advsm->rx_ble_hdr = NULL; +#else + rc = ble_phy_tx(ble_ll_adv_scan_rsp_legacy_pdu_make, advsm, + BLE_PHY_TRANSITION_NONE); +#endif + + if (!rc) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD; + STATS_INC(ble_ll_stats, scan_rsp_txg); + } + } else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_CONNECT_REQ) { + /* See if the device is already connected */ + if (ble_ll_adv_already_connected(peer, peer_addr_type)) { + return -1; + } + + /* + * Only accept connect requests from the desired address if we + * are doing directed advertising + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + if (memcmp(advsm->initiator_addr, peer, BLE_DEV_ADDR_LEN)) { + return -1; + } + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + return -1; + } + + /* use remote address used over the air */ + rsp_data.advsm = advsm; + rsp_data.peer = rxbuf + BLE_LL_PDU_HDR_LEN; + rsp_data.rxadd = rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK; + + ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); + rc = ble_phy_tx(ble_ll_adv_aux_conn_rsp_pdu_make, &rsp_data, + BLE_PHY_TRANSITION_NONE); + if (!rc) { + ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD); + STATS_INC(ble_ll_stats, aux_conn_rsp_tx); + } +#endif + } + + return rc; +} + +/** + * Called when a connect request has been received. + * + * Context: Link Layer + * + * @param rxbuf + * @param flags + * + * @return 0: no connection started. 1: connection started + */ +static int +ble_ll_adv_conn_req_rxd(uint8_t *rxbuf, struct ble_mbuf_hdr *hdr, + struct ble_ll_adv_sm *advsm) +{ + int valid; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + uint8_t resolved; +#endif + uint8_t addr_type; + uint8_t *inita; + uint8_t *ident_addr; + + /* Don't create connection if AUX_CONNECT_RSP was not send */ + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD)) { + return 0; + } + } + + /* Check filter policy. */ + valid = 0; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + resolved = BLE_MBUF_HDR_RESOLVED(hdr); +#endif + inita = rxbuf + BLE_LL_PDU_HDR_LEN; + if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_DEVMATCH) { + + valid = 1; + if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) { + addr_type = BLE_ADDR_RANDOM; + } else { + addr_type = BLE_ADDR_PUBLIC; + } + + /* + * Only accept connect requests from the desired address if we + * are doing directed advertising + */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) { + ident_addr = inita; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (resolved) { + ident_addr = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr; + addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type; + } +#endif + if ((addr_type != advsm->peer_addr_type) || + memcmp(advsm->peer_addr, ident_addr, BLE_DEV_ADDR_LEN)) { + valid = 0; + } + } + } + + if (valid) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (resolved) { + /* Retain the resolvable private address that we received. */ + memcpy(advsm->adv_rpa, inita, BLE_DEV_ADDR_LEN); + + /* Update resolving list with current peer RPA */ + ble_ll_resolv_set_peer_rpa(advsm->adv_rpa_index, inita); + + /* + * Overwrite received inita with identity address since that + * is used from now on. + */ + memcpy(inita, + g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr, + BLE_DEV_ADDR_LEN); + + /* Peer address type is an identity address */ + addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type; + addr_type += 2; + } +#endif + + /* Try to start slave connection. If successful, stop advertising */ + valid = ble_ll_conn_slave_start(rxbuf, addr_type, hdr, + !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)); + if (valid) { + /* stop advertising only if not transmitting connection response */ + if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD)) { + ble_ll_adv_sm_stop(advsm); + } + } + } + + return valid; +} + +/** + * Called on phy rx pdu end when in advertising state. + * + * There are only two pdu types we care about in this state: scan requests + * and connection requests. When we receive a scan request we must determine if + * we need to send a scan response and that needs to be acted on within T_IFS. + * + * When we receive a connection request, we need to determine if we will allow + * this device to start a connection with us. However, no immediate response is + * sent so we handle this at the link layer task. + * + * Context: Interrupt + * + * @param pdu_type Type of pdu received. + * @param rxpdu Pointer to received PDU + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Do not disable the PHY + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_adv_rx_isr_end(uint8_t pdu_type, struct os_mbuf *rxpdu, int crcok) +{ + int rc; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_mbuf_hdr *rxhdr; +#endif + + rc = -1; + if (rxpdu == NULL) { + ble_ll_adv_tx_done(g_ble_ll_cur_adv_sm); + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + rxhdr = BLE_MBUF_HDR_PTR(rxpdu); + rxhdr->rxinfo.user_data = g_ble_ll_cur_adv_sm; + if (ble_ll_adv_active_chanset_is_sec(g_ble_ll_cur_adv_sm)) { + rxhdr->rxinfo.flags |= BLE_MBUF_HDR_F_EXT_ADV_SEC; + } else { + assert(ble_ll_adv_active_chanset_is_pri(g_ble_ll_cur_adv_sm)); + } +#endif + if (crcok) { + if ((pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) || + (pdu_type == BLE_ADV_PDU_TYPE_CONNECT_IND)) { + /* Process request */ + rc = ble_ll_adv_rx_req(pdu_type, rxpdu); + } + } + + if (rc) { + /* We no longer have a current state machine */ + g_ble_ll_cur_adv_sm = NULL; + } + } + + if (rc) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + + return rc; +} + +/** + * Process a received packet at the link layer task when in the advertising + * state + * + * Context: Link Layer + * + * + * @param ptype + * @param rxbuf + * @param hdr + * + * @return int + */ +void +ble_ll_adv_rx_pkt_in(uint8_t ptype, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr) +{ + int adv_event_over; + struct ble_ll_adv_sm *advsm; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + advsm = (struct ble_ll_adv_sm *)hdr->rxinfo.user_data; +#else + advsm = &g_ble_ll_adv_sm[0]; +#endif + + /* + * It is possible that advertising was stopped and a packet plcaed on the + * LL receive packet queue. In this case, just ignore the received packet + * as the advertising state machine is no longer "valid" + */ + if (!advsm->adv_enabled) { + return; + } + + /* + * If we have received a scan request and we are transmitting a response + * or we have received a valid connect request, dont "end" the advertising + * event. In the case of a connect request we will stop advertising. In + * the case of the scan response transmission we will get a transmit + * end callback. + */ + adv_event_over = 1; + if (BLE_MBUF_HDR_CRC_OK(hdr)) { + if (ptype == BLE_ADV_PDU_TYPE_CONNECT_IND) { + if (ble_ll_adv_conn_req_rxd(rxbuf, hdr, advsm)) { + adv_event_over = 0; + } + } else { + if ((ptype == BLE_ADV_PDU_TYPE_SCAN_REQ) && + (hdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_TXD)) { + adv_event_over = 0; + } + } + } + + if (adv_event_over) { + ble_ll_adv_make_done(advsm, hdr); + } +} + +/** + * Called when a receive PDU has started and we are advertising. + * + * Context: interrupt + * + * @param pdu_type + * @param rxpdu + * + * @return int + * < 0: A frame we dont want to receive. + * = 0: Continue to receive frame. Dont go from rx to tx + * > 0: Continue to receive frame and go from rx to tx when done + */ +int +ble_ll_adv_rx_isr_start(uint8_t pdu_type) +{ + int rc; + struct ble_ll_adv_sm *advsm; + + /* Assume we will abort the frame */ + rc = -1; + + /* If we get a scan request we must tell the phy to go from rx to tx */ + advsm = g_ble_ll_cur_adv_sm; + if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { + /* Only accept scan requests if we are indirect adv or scan adv */ + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) { + rc = 1; + } + } else { + /* Only accept connect requests if connectable advertising event */ + if (pdu_type == BLE_ADV_PDU_TYPE_CONNECT_IND) { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) { + /* Need transition to TX if extended adv */ + rc = !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY); + } + } + } + + /* + * If we abort the frame, we need to post the LL task to check if the + * advertising event is over. + */ + if (rc < 0) { + ble_ll_adv_tx_done(advsm); + } + + return rc; +} + +static void +ble_ll_adv_drop_event(struct ble_ll_adv_sm *advsm) +{ + STATS_INC(ble_ll_stats, adv_drop_event); + + ble_ll_sched_rmv_elem(&advsm->adv_sch); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_ll_sched_rmv_elem(&advsm->aux[0].sch); + ble_ll_sched_rmv_elem(&advsm->aux[1].sch); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev); + advsm->aux_active = 0; +#endif + + advsm->adv_chan = ble_ll_adv_final_chan(advsm); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); +} + +static void +ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm) +{ + int rc; + uint32_t start_time; + uint32_t max_delay_ticks; + + assert(advsm->adv_enabled); + + if (!advsm->adv_sch.enqueued) { + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) { + max_delay_ticks = 0; + } else { + max_delay_ticks = + os_cputime_usecs_to_ticks(BLE_LL_ADV_DELAY_MS_MAX * 1000); + } + + rc = ble_ll_sched_adv_reschedule(&advsm->adv_sch, &start_time, + max_delay_ticks); + if (rc) { + ble_ll_adv_drop_event(advsm); + return; + } + + start_time += g_ble_ll_sched_offset_ticks; + advsm->adv_event_start_time = start_time; + advsm->adv_pdu_start_time = start_time; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && + !advsm->aux_active) { + ble_ll_adv_aux_schedule(advsm); + } +#endif +} + +/** + * Called when an advertising event is over. + * + * Context: Link Layer task. + * + * @param arg Pointer to advertising state machine. + */ +static void +ble_ll_adv_done(struct ble_ll_adv_sm *advsm) + +{ + int rc; + int resched_pdu; + uint8_t mask; + uint8_t final_adv_chan; + int32_t delta_t; + uint32_t itvl; + uint32_t tick_itvl; + uint32_t start_time; + + assert(advsm->adv_enabled); + + ble_ll_rfmgmt_release(); + + ble_ll_adv_update_adv_scan_rsp_data(advsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) { + /* stop advertising this was due to transmitting connection response */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD) { + ble_ll_adv_sm_stop(advsm); + return; + } + } +#endif + + /* Remove the element from the schedule if it is still there. */ + ble_ll_sched_rmv_elem(&advsm->adv_sch); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); + + /* + * Check if we have ended our advertising event. If our last advertising + * packet was sent on the last channel, it means we are done with this + * event. + */ + final_adv_chan = ble_ll_adv_final_chan(advsm); + + if (advsm->adv_chan == final_adv_chan) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->events_max) { + advsm->events++; + } +#endif + + ble_ll_scan_chk_resume(); + + /* This event is over. Set adv channel to first one */ + advsm->adv_chan = ble_ll_adv_first_chan(advsm); + + /* + * Calculate start time of next advertising event. NOTE: we do not + * add the random advDelay as the scheduling code will do that. + */ + itvl = advsm->adv_itvl_usecs; + tick_itvl = os_cputime_usecs_to_ticks(itvl); + advsm->adv_event_start_time += tick_itvl; + advsm->adv_pdu_start_time = advsm->adv_event_start_time; + + /* + * The scheduled time better be in the future! If it is not, we will + * just keep advancing until we the time is in the future + */ + start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks; + + delta_t = (int32_t)(start_time - os_cputime_get32()); + if (delta_t < 0) { + /* + * NOTE: we just the same interval that we calculated earlier. + * No real need to keep recalculating a new interval. + */ + while (delta_t < 0) { + advsm->adv_event_start_time += tick_itvl; + advsm->adv_pdu_start_time = advsm->adv_event_start_time; + delta_t += (int32_t)tick_itvl; + } + } + resched_pdu = 0; + } else { + /* + * Move to next advertising channel. If not in the mask, just + * increment by 1. We can do this because we already checked if we + * just transmitted on the last advertising channel + */ + ++advsm->adv_chan; + mask = 1 << (advsm->adv_chan - BLE_PHY_ADV_CHAN_START); + if ((mask & advsm->adv_chanmask) == 0) { + ++advsm->adv_chan; + } + + /* + * We will transmit right away. Set next pdu start time to now + * plus a xcvr start delay just so we dont count late adv starts + */ + advsm->adv_pdu_start_time = os_cputime_get32() + + g_ble_ll_sched_offset_ticks; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* If we're past aux (unlikely, but can happen), just drop an event */ + if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) && + advsm->aux_active && + advsm->adv_pdu_start_time > AUX_CURRENT(advsm)->start_time) { + ble_ll_adv_drop_event(advsm); + return; + } +#endif + + resched_pdu = 1; + } + + /* check if advertising timed out */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->duration && + advsm->adv_pdu_start_time >= advsm->adv_end_time) { + /* Legacy PDUs need to be stop here. + * For ext adv it will be stopped when AUX is done (unless it was + * dropped so check if AUX is active here as well). + */ + if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) || + !advsm->aux_active) { + ble_ll_adv_sm_stop_timeout(advsm); + } + + return; + } +#else + if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) && + (advsm->adv_pdu_start_time >= advsm->adv_end_time)) { + ble_ll_adv_sm_stop_timeout(advsm); + return; + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (advsm->events_max && (advsm->events >= advsm->events_max)) { + /* Legacy PDUs need to be stop here. + * For ext adv it will be stopped when AUX is done (unless it was + * dropped so check if AUX is active here as well). + */ + if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) || + !advsm->aux_active) { + ble_ll_adv_sm_stop_limit_reached(advsm); + } + + return; + } +#endif + + /* We need to regenerate our RPA's if we have passed timeout */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_ll_adv_chk_rpa_timeout(advsm); +#endif + + /* Schedule advertising transmit */ + ble_ll_adv_set_sched(advsm); + + if (!resched_pdu) { + ble_ll_adv_reschedule_event(advsm); + return; + } + + /* + * In the unlikely event we can't reschedule this, just post a done event + * and we will reschedule the next advertising PDU. + */ + rc = ble_ll_sched_adv_resched_pdu(&advsm->adv_sch); + if (rc) { + STATS_INC(ble_ll_stats, adv_resched_pdu_fail); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); + } +} + +static void +ble_ll_adv_event_done(struct ble_npl_event *ev) +{ + ble_ll_adv_done(ble_npl_event_get_arg(ev)); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/** + * Called when auxiliary packet is txd on secondary channel + * + * Context: Link Layer task. + * + * @param ev + */ +static void +ble_ll_adv_sec_done(struct ble_ll_adv_sm *advsm) +{ + struct ble_ll_adv_aux *aux; + struct ble_ll_adv_aux *aux_next; + + assert(advsm->adv_enabled); + assert(advsm->aux_active); + + aux = AUX_CURRENT(advsm); + aux_next = AUX_NEXT(advsm); + + /* We don't need RF anymore */ + ble_ll_rfmgmt_release(); + + if (advsm->aux_not_scanned) { + ble_ll_sched_rmv_elem(&aux_next->sch); + } + + /* Remove anything else scheduled for secondary channel */ + ble_ll_sched_rmv_elem(&aux->sch); + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev); + + /* Stop advertising due to transmitting connection response */ + if (advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD) { + ble_ll_adv_sm_stop(advsm); + return; + } + + /* If we have next AUX scheduled, try to schedule another one */ + if (aux_next->sch.enqueued) { + advsm->aux_index ^= 1; + advsm->aux_first_pdu = 0; + ble_ll_adv_aux_schedule_next(advsm); + return; + } + + ble_ll_scan_chk_resume(); + + /* Check if advertising timed out */ + if (advsm->duration && (advsm->adv_pdu_start_time >= advsm->adv_end_time)) { + ble_ll_adv_sm_stop_timeout(advsm); + return; + } + + if (advsm->events_max && (advsm->events >= advsm->events_max)) { + ble_ll_adv_sm_stop_limit_reached(advsm); + return; + } + + advsm->aux_active = 0; + ble_ll_adv_update_adv_scan_rsp_data(advsm); + ble_ll_adv_reschedule_event(advsm); +} + +static void +ble_ll_adv_sec_event_done(struct ble_npl_event *ev) +{ + ble_ll_adv_sec_done(ble_npl_event_get_arg(ev)); +} +#endif + +static void +ble_ll_adv_make_done(struct ble_ll_adv_sm *advsm, struct ble_mbuf_hdr *hdr) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (BLE_MBUF_HDR_EXT_ADV_SEC(hdr)) { + assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)); + assert(ble_ll_adv_active_chanset_is_sec(advsm)); + ble_ll_adv_active_chanset_clear(advsm); + ble_ll_adv_sec_done(advsm); + } else { + assert(ble_ll_adv_active_chanset_is_pri(advsm)); + ble_ll_adv_active_chanset_clear(advsm); + ble_ll_adv_done(advsm); + } +#else + ble_ll_adv_active_chanset_clear(advsm); + ble_ll_adv_done(advsm); +#endif +} + +/** + * Checks if the controller can change the whitelist. If advertising is enabled + * and is using the whitelist the controller is not allowed to change the + * whitelist. + * + * @return int 0: not allowed to change whitelist; 1: change allowed. + */ +int +ble_ll_adv_can_chg_whitelist(void) +{ + struct ble_ll_adv_sm *advsm; + int rc; + int i; + + rc = 1; + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + advsm = &g_ble_ll_adv_sm[i]; + if (advsm->adv_enabled && + (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE)) { + rc = 0; + break; + } + } + + return rc; +} + +/** + * Sends the connection complete event when advertising a connection starts. + * + * @return uint8_t* Pointer to event buffer + */ +void +ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *rxhdr) +{ + uint8_t *evbuf; + struct ble_ll_adv_sm *advsm; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + advsm = (struct ble_ll_adv_sm *)rxhdr->rxinfo.user_data; +#else + advsm = &g_ble_ll_adv_sm[0]; +#endif + + evbuf = advsm->conn_comp_ev; + assert(evbuf != NULL); + advsm->conn_comp_ev = NULL; + + ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, evbuf, advsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + ble_ll_hci_ev_le_csa(connsm); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (ble_ll_hci_adv_mode_ext()) { + ble_ll_hci_ev_send_adv_set_terminated(0, advsm->adv_instance, + connsm->conn_handle, advsm->events); + } +#endif +} + +/** + * Returns the local resolvable private address currently being using by + * the advertiser + * + * @return uint8_t* + */ +uint8_t * +ble_ll_adv_get_local_rpa(struct ble_ll_adv_sm *advsm) +{ + uint8_t *rpa = NULL; + + if (advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + if ((advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) && + ble_ll_is_rpa(advsm->adva, 1)) { + rpa = advsm->adva; + } + } + + return rpa; +} + +/** + * Returns the peer resolvable private address of last device connecting to us + * + * @return uint8_t* + */ +uint8_t * +ble_ll_adv_get_peer_rpa(struct ble_ll_adv_sm *advsm) +{ + /* XXX: should this go into IRK list or connection? */ + return advsm->adv_rpa; +} + +/** + * Called when the LL wait for response timer expires while in the advertising + * state. Disables the phy and + * + */ +void +ble_ll_adv_wfr_timer_exp(void) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + g_ble_ll_cur_adv_sm->aux_not_scanned = 1; +#endif + + ble_phy_disable(); + ble_ll_adv_tx_done(g_ble_ll_cur_adv_sm); +} + +/** + * Reset the advertising state machine. + * + * Context: Link Layer task + * + */ +void +ble_ll_adv_reset(void) +{ + int i; + struct ble_ll_adv_sm *advsm; + + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + advsm = &g_ble_ll_adv_sm[i]; + + /* Stop advertising state machine */ + ble_ll_adv_sm_stop(advsm); + + /* clear any data present */ + os_mbuf_free_chain(advsm->adv_data); + os_mbuf_free_chain(advsm->scan_rsp_data); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + /* Stop periodic advertising state machine */ + ble_ll_adv_sm_stop_periodic(advsm); + + /* clear any periodic data present */ + os_mbuf_free_chain(advsm->periodic_adv_data); +#endif + + /* re-initialize the advertiser state machine */ + ble_ll_adv_sm_init(advsm); + } +} + +/* Called to determine if advertising is enabled. + */ +uint8_t +ble_ll_adv_enabled(void) +{ + int i; + + for (i = 0; i < BLE_ADV_INSTANCES; i++) { + if (g_ble_ll_adv_sm[i].adv_enabled) { + return 1; + } + } + + return 0; +} + +static void +ble_ll_adv_sm_init(struct ble_ll_adv_sm *advsm) +{ + memset(advsm, 0, sizeof(struct ble_ll_adv_sm)); + + advsm->adv_itvl_min = BLE_HCI_ADV_ITVL_DEF; + advsm->adv_itvl_max = BLE_HCI_ADV_ITVL_DEF; + advsm->adv_chanmask = BLE_HCI_ADV_CHANMASK_DEF; + + /* Initialize advertising tx done event */ + ble_npl_event_init(&advsm->adv_txdone_ev, ble_ll_adv_event_done, advsm); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_npl_event_init(&advsm->adv_sec_txdone_ev, ble_ll_adv_sec_event_done, advsm); +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + ble_npl_event_init(&advsm->adv_periodic_txdone_ev, + ble_ll_adv_periodic_event_done, advsm); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* Initialize aux schedulers */ + advsm->aux_active = 0; + advsm->aux[0].sch.cb_arg = advsm; + advsm->aux[0].sch.sched_cb = ble_ll_adv_secondary_tx_start_cb; + advsm->aux[0].sch.sched_type = BLE_LL_SCHED_TYPE_ADV; + advsm->aux[1].sch.cb_arg = advsm; + advsm->aux[1].sch.sched_cb = ble_ll_adv_secondary_tx_start_cb; + advsm->aux[1].sch.sched_type = BLE_LL_SCHED_TYPE_ADV; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + /* Initialize sync schedulers */ + advsm->periodic_sync_active = 0; + advsm->periodic_sync[0].sch.cb_arg = advsm; + advsm->periodic_sync[0].sch.sched_cb = ble_ll_adv_sync_tx_start_cb; + advsm->periodic_sync[0].sch.sched_type = BLE_LL_SCHED_TYPE_PERIODIC; + advsm->periodic_sync[1].sch.cb_arg = advsm; + advsm->periodic_sync[1].sch.sched_cb = ble_ll_adv_sync_tx_start_cb; + advsm->periodic_sync[1].sch.sched_type = BLE_LL_SCHED_TYPE_PERIODIC; +#endif +#endif + + /* Configure instances to be legacy on start */ + advsm->props |= BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE; + advsm->props |= BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY; +} + +/** + * Initialize the advertising functionality of a BLE device. This should + * be called once on initialization + */ +void +ble_ll_adv_init(void) +{ + int i; + + /* Set default advertising parameters */ + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + ble_ll_adv_sm_init(&g_ble_ll_adv_sm[i]); + } +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn.c new file mode 100644 index 0000000..898563b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn.c @@ -0,0 +1,4279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) +#include "nimble/nimble/drivers/nrf51/include/ble/xcvr.h" +#elif defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) +#include "nimble/nimble/drivers/nrf52/include/ble/xcvr.h" +#endif + +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_ctrl.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll_utils.h" +#include "ble_ll_conn_priv.h" + +#if (BLETEST_THROUGHPUT_TEST == 1) +extern void bletest_completed_pkt(uint16_t handle); +#endif + +/* XXX TODO + * 1) I think if we are initiating and we already have a connection with + * a device that we will still try and connect to it. Fix this. + * -> This is true. There are a couple things to do + * i) When a connection create is issued, if we already are connected + * deny it. BLE ERROR = 0x0B (ACL connection exists). + * ii) If we receive an advertisement while initiating and want to send + * a connect request to the device, make sure we dont have it. + * iii) I think I need to do something like this: I am initiating and + * advertising. Suppose the device I want to connect to sends me a connect + * request because I am advertising? What happens to connection? Deal + * with this! + * + * 2) Make sure we check incoming data packets for size and all that. You + * know, supported octets and all that. For both rx and tx. + * + * 3) Make sure we are setting the schedule end time properly for both slave + * and master. We should just set this to the end of the connection event. + * We might want to guarantee a IFS time as well since the next event needs + * to be scheduled prior to the start of the event to account for the time it + * takes to get a frame ready (which is pretty much the IFS time). + * + * 4) looks like the current code will allow the 1st packet in a + * connection to extend past the end of the allocated connection end + * time. That is not good. Need to deal with that. Need to extend connection + * end time. + * + * 6) Use error code 0x3E correctly! Connection failed to establish. If you + * read the LE connection complete event, it says that if the connection + * fails to be established that the connection complete event gets sent to + * the host that issued the create connection. Need to resolve this. + * + * 7) How does peer address get set if we are using whitelist? Look at filter + * policy and make sure you are doing this correctly. + * + * 8) Right now I use a fixed definition for required slots. CHange this. + * + * 10) See what connection state machine elements are purely master and + * purely slave. We can make a union of them. + * + * 11) Not sure I am dealing with the connection terminate timeout perfectly. + * I may extend a connection event too long although if it is always in terms + * of connection events I am probably fine. Checking at end that the next + * connection event will occur past terminate timeould would be fine. + * + * 12) When a slave receives a data packet in a connection it has to send a + * response. Well, it should. If this packet will overrun the next scheduled + * event, what should we do? Transmit anyway? Not transmit? For now, we just + * transmit. + * + * 32kHz crystal + * 1) When scheduling, I need to make sure I have time between + * this one and the next. Should I deal with this in the sched. Or + * is this basically accounted for given a slot? I really just need to + * make sure everything is over N ticks before the next sched start! + * Just add to end time? + * + * 2) I think one way to handle the problem of losing up to a microsecond + * every time we call ble_ll_conn_next_event in a loop is to do everything by + * keeping track of last anchor point. Would need last anchor usecs too. I guess + * we could also keep last anchor usecs as a uint32 or something and when we + * do the next event keep track of the residual using a different ticks to + * usecs calculation. Not sure. + */ + +/* + * XXX: How should we deal with a late connection event? We need to determine + * what we want to do under the following cases: + * 1) The current connection event has not ended but a schedule item starts + */ + +/* This is a dummy structure we use for the empty PDU */ +struct ble_ll_empty_pdu +{ + struct os_mbuf om; + struct os_mbuf_pkthdr pkt_hdr; + struct ble_mbuf_hdr ble_hdr; +}; + +/* We cannot have more than 254 connections given our current implementation */ +#if (MYNEWT_VAL(BLE_MAX_CONNECTIONS) >= 255) + #error "Maximum # of connections is 254" +#endif + +/* Global connection complete event. Used when initiating */ +uint8_t *g_ble_ll_conn_comp_ev; + +/* Global LL connection parameters */ +struct ble_ll_conn_global_params g_ble_ll_conn_params; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +/* Global default sync transfer params */ +struct ble_ll_conn_sync_transfer_params g_ble_ll_conn_sync_transfer_params; +#endif + +/* Pointer to connection state machine we are trying to create */ +struct ble_ll_conn_sm *g_ble_ll_conn_create_sm; + +/* Pointer to current connection */ +struct ble_ll_conn_sm *g_ble_ll_conn_cur_sm; + +/* Connection state machine array */ +struct ble_ll_conn_sm g_ble_ll_conn_sm[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; + +/* List of active connections */ +struct ble_ll_conn_active_list g_ble_ll_conn_active_list; + +/* List of free connections */ +struct ble_ll_conn_free_list g_ble_ll_conn_free_list; + +STATS_SECT_START(ble_ll_conn_stats) + STATS_SECT_ENTRY(cant_set_sched) + STATS_SECT_ENTRY(conn_ev_late) + STATS_SECT_ENTRY(wfr_expirations) + STATS_SECT_ENTRY(handle_not_found) + STATS_SECT_ENTRY(no_conn_sm) + STATS_SECT_ENTRY(no_free_conn_sm) + STATS_SECT_ENTRY(rx_data_pdu_no_conn) + STATS_SECT_ENTRY(rx_data_pdu_bad_aa) + STATS_SECT_ENTRY(slave_rxd_bad_conn_req_params) + STATS_SECT_ENTRY(slave_ce_failures) + STATS_SECT_ENTRY(data_pdu_rx_dup) + STATS_SECT_ENTRY(data_pdu_txg) + STATS_SECT_ENTRY(data_pdu_txf) + STATS_SECT_ENTRY(conn_req_txd) + STATS_SECT_ENTRY(l2cap_enqueued) + STATS_SECT_ENTRY(rx_ctrl_pdus) + STATS_SECT_ENTRY(rx_l2cap_pdus) + STATS_SECT_ENTRY(rx_l2cap_bytes) + STATS_SECT_ENTRY(rx_malformed_ctrl_pdus) + STATS_SECT_ENTRY(rx_bad_llid) + STATS_SECT_ENTRY(tx_ctrl_pdus) + STATS_SECT_ENTRY(tx_ctrl_bytes) + STATS_SECT_ENTRY(tx_l2cap_pdus) + STATS_SECT_ENTRY(tx_l2cap_bytes) + STATS_SECT_ENTRY(tx_empty_pdus) + STATS_SECT_ENTRY(mic_failures) + STATS_SECT_ENTRY(sched_start_in_idle) + STATS_SECT_ENTRY(sched_end_in_idle) + STATS_SECT_ENTRY(conn_event_while_tmo) +STATS_SECT_END +STATS_SECT_DECL(ble_ll_conn_stats) ble_ll_conn_stats; + +STATS_NAME_START(ble_ll_conn_stats) + STATS_NAME(ble_ll_conn_stats, cant_set_sched) + STATS_NAME(ble_ll_conn_stats, conn_ev_late) + STATS_NAME(ble_ll_conn_stats, wfr_expirations) + STATS_NAME(ble_ll_conn_stats, handle_not_found) + STATS_NAME(ble_ll_conn_stats, no_conn_sm) + STATS_NAME(ble_ll_conn_stats, no_free_conn_sm) + STATS_NAME(ble_ll_conn_stats, rx_data_pdu_no_conn) + STATS_NAME(ble_ll_conn_stats, rx_data_pdu_bad_aa) + STATS_NAME(ble_ll_conn_stats, slave_rxd_bad_conn_req_params) + STATS_NAME(ble_ll_conn_stats, slave_ce_failures) + STATS_NAME(ble_ll_conn_stats, data_pdu_rx_dup) + STATS_NAME(ble_ll_conn_stats, data_pdu_txg) + STATS_NAME(ble_ll_conn_stats, data_pdu_txf) + STATS_NAME(ble_ll_conn_stats, conn_req_txd) + STATS_NAME(ble_ll_conn_stats, l2cap_enqueued) + STATS_NAME(ble_ll_conn_stats, rx_ctrl_pdus) + STATS_NAME(ble_ll_conn_stats, rx_l2cap_pdus) + STATS_NAME(ble_ll_conn_stats, rx_l2cap_bytes) + STATS_NAME(ble_ll_conn_stats, rx_malformed_ctrl_pdus) + STATS_NAME(ble_ll_conn_stats, rx_bad_llid) + STATS_NAME(ble_ll_conn_stats, tx_ctrl_pdus) + STATS_NAME(ble_ll_conn_stats, tx_ctrl_bytes) + STATS_NAME(ble_ll_conn_stats, tx_l2cap_pdus) + STATS_NAME(ble_ll_conn_stats, tx_l2cap_bytes) + STATS_NAME(ble_ll_conn_stats, tx_empty_pdus) + STATS_NAME(ble_ll_conn_stats, mic_failures) + STATS_NAME(ble_ll_conn_stats, sched_start_in_idle) + STATS_NAME(ble_ll_conn_stats, sched_end_in_idle) + STATS_NAME(ble_ll_conn_stats, conn_event_while_tmo) +STATS_NAME_END(ble_ll_conn_stats) + +static void ble_ll_conn_event_end(struct ble_npl_event *ev); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/** + * Checks to see if we should start a PHY update procedure + * + * If current phy is not one of the preferred we need to start control + * procedure. + * + * XXX: we could also decide to change the PHY if RSSI is really good + * and we are currently at 1Mbps or lower data rate and we could use + * a higher data rate. + * + * @param connsm + * @return 0: success; -1: no phy update procedure started + */ +int +ble_ll_conn_chk_phy_upd_start(struct ble_ll_conn_sm *csm) +{ + int rc; + + /* If no host preferences or */ + if (((csm->phy_data.host_pref_tx_phys_mask == 0) && + (csm->phy_data.host_pref_rx_phys_mask == 0)) || + ((csm->phy_data.host_pref_tx_phys_mask & CONN_CUR_TX_PHY_MASK(csm)) && + (csm->phy_data.host_pref_rx_phys_mask & CONN_CUR_RX_PHY_MASK(csm)))) { + rc = -1; + } else { + csm->phy_data.req_pref_tx_phys_mask = csm->phy_data.host_pref_tx_phys_mask; + csm->phy_data.req_pref_rx_phys_mask = csm->phy_data.host_pref_rx_phys_mask; + ble_ll_ctrl_proc_start(csm, BLE_LL_CTRL_PROC_PHY_UPDATE); + rc = 0; + } + + return rc; +} +#endif + +static void +ble_ll_conn_calc_itvl_ticks(struct ble_ll_conn_sm *connsm) +{ + uint32_t ticks; + uint32_t usecs; + + /* + * Precalculate the number of ticks and remaining microseconds for + * the connection interval + */ + usecs = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; + ticks = os_cputime_usecs_to_ticks(usecs); + connsm->conn_itvl_usecs = (uint8_t)(usecs - + os_cputime_ticks_to_usecs(ticks)); + if (connsm->conn_itvl_usecs == 31) { + connsm->conn_itvl_usecs = 0; + ++ticks; + } + connsm->conn_itvl_ticks = ticks; +} + +/** + * Get the event buffer allocated to send the connection complete event + * when we are initiating. + * + * @return uint8_t* + */ +static uint8_t * +ble_ll_init_get_conn_comp_ev(void) +{ + uint8_t *evbuf; + + evbuf = g_ble_ll_conn_comp_ev; + BLE_LL_ASSERT(evbuf != NULL); + g_ble_ll_conn_comp_ev = NULL; + + return evbuf; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/** + * Called to determine if the received PDU is an empty PDU or not. + */ +static int +ble_ll_conn_is_empty_pdu(uint8_t *rxbuf) +{ + int rc; + uint8_t llid; + + llid = rxbuf[0] & BLE_LL_DATA_HDR_LLID_MASK; + if ((llid == BLE_LL_LLID_DATA_FRAG) && (rxbuf[1] == 0)) { + rc = 1; + } else { + rc = 0; + } + return rc; +} +#endif + +/** + * Called to return the currently running connection state machine end time. + * Always called when interrupts are disabled. + * + * @return int 0: s1 is not least recently used. 1: s1 is least recently used + */ +int +ble_ll_conn_is_lru(struct ble_ll_conn_sm *s1, struct ble_ll_conn_sm *s2) +{ + int rc; + + /* Set time that we last serviced the schedule */ + if ((int32_t)(s1->last_scheduled - s2->last_scheduled) < 0) { + rc = 1; + } else { + rc = 0; + } + + return rc; +} + +/** + * Called to return the currently running connection state machine end time. + * Always called when interrupts are disabled. + * + * @return uint32_t + */ +uint32_t +ble_ll_conn_get_ce_end_time(void) +{ + uint32_t ce_end_time; + + if (g_ble_ll_conn_cur_sm) { + ce_end_time = g_ble_ll_conn_cur_sm->ce_end_time; + } else { + ce_end_time = os_cputime_get32(); + } + return ce_end_time; +} + +/** + * Called when connection state machine needs to halt. This function will: + * -> Disable the PHY, which will prevent any transmit/receive interrupts. + * -> Disable the wait for response timer, if running. + * -> Remove the connection state machine from the scheduler. + * -> Sets the Link Layer state to standby. + * -> Sets the current state machine to NULL. + * + * NOTE: the ordering of these function calls is important! We have to stop + * the PHY and remove the schedule item before we can set the state to + * standby and set the current state machine pointer to NULL. + */ +static void +ble_ll_conn_halt(void) +{ + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_conn_cur_sm = NULL; +} + +/** + * Called when the current connection state machine is no longer being used. + */ +static void +ble_ll_conn_current_sm_over(struct ble_ll_conn_sm *connsm) +{ + + ble_ll_conn_halt(); + + /* + * NOTE: the connection state machine may be NULL if we are calling + * this when we are ending the connection. In that case, there is no + * need to post to the LL the connection event end event + */ + if (connsm) { + ble_ll_event_send(&connsm->conn_ev_end); + } +} + +/** + * Given a handle, find an active connection matching the handle + * + * @param handle + * + * @return struct ble_ll_conn_sm* + */ +struct ble_ll_conn_sm * +ble_ll_conn_find_active_conn(uint16_t handle) +{ + struct ble_ll_conn_sm *connsm; + + connsm = NULL; + if ((handle != 0) && (handle <= MYNEWT_VAL(BLE_MAX_CONNECTIONS))) { + connsm = &g_ble_ll_conn_sm[handle - 1]; + if (connsm->conn_state == BLE_LL_CONN_STATE_IDLE) { + connsm = NULL; + } + } + return connsm; +} + +/** + * Get a connection state machine. + */ +struct ble_ll_conn_sm * +ble_ll_conn_sm_get(void) +{ + struct ble_ll_conn_sm *connsm; + + connsm = STAILQ_FIRST(&g_ble_ll_conn_free_list); + if (connsm) { + STAILQ_REMOVE_HEAD(&g_ble_ll_conn_free_list, free_stqe); + } else { + STATS_INC(ble_ll_conn_stats, no_free_conn_sm); + } + + return connsm; +} + +static uint8_t +ble_ll_conn_calc_dci_csa1(struct ble_ll_conn_sm *conn) +{ + uint8_t curchan; + uint8_t remap_index; + uint8_t bitpos; + + /* Get next unmapped channel */ + curchan = conn->last_unmapped_chan + conn->hop_inc; + if (curchan > BLE_PHY_NUM_DATA_CHANS) { + curchan -= BLE_PHY_NUM_DATA_CHANS; + } + + /* Save unmapped channel */ + conn->last_unmapped_chan = curchan; + + /* Is this a valid channel? */ + bitpos = 1 << (curchan & 0x07); + if (conn->chanmap[curchan >> 3] & bitpos) { + return curchan; + } + + /* Calculate remap index */ + remap_index = curchan % conn->num_used_chans; + + return ble_ll_utils_remapped_channel(remap_index, conn->chanmap); +} + +/** + * Determine data channel index to be used for the upcoming/current + * connection event + * + * @param conn + * @param latency Used only for CSA #1 + * + * @return uint8_t + */ +uint8_t +ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn, uint16_t latency) +{ + uint8_t index; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + if (CONN_F_CSA2_SUPP(conn)) { + return ble_ll_utils_calc_dci_csa2(conn->event_cntr, conn->channel_id, + conn->num_used_chans, conn->chanmap); + } +#endif + + index = conn->data_chan_index; + + while (latency > 0) { + index = ble_ll_conn_calc_dci_csa1(conn); + latency--; + } + + return index; +} + +/** + * Called when we are in the connection state and the wait for response timer + * fires off. + * + * Context: Interrupt + */ +void +ble_ll_conn_wfr_timer_exp(void) +{ + struct ble_ll_conn_sm *connsm; + + connsm = g_ble_ll_conn_cur_sm; + ble_ll_conn_current_sm_over(connsm); + STATS_INC(ble_ll_conn_stats, wfr_expirations); +} + +void +ble_ll_conn_reset_pending_aux_conn_rsp(void) +{ +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + return; +#endif + struct ble_ll_conn_sm *connsm; + + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { + return; + } + + if (CONN_F_AUX_CONN_REQ(connsm)) { + STATS_INC(ble_ll_stats, aux_conn_rsp_err); + CONN_F_CONN_REQ_TXD(connsm) = 0; + CONN_F_AUX_CONN_REQ(connsm) = 0; + ble_ll_sched_rmv_elem(&connsm->conn_sch); + return; + } + + return; +} + +bool +ble_ll_conn_init_pending_aux_conn_rsp(void) +{ +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + return false; +#endif + struct ble_ll_conn_sm *connsm; + + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { + return false; + } + + return CONN_F_AUX_CONN_REQ(connsm); +} + +void +ble_ll_conn_init_wfr_timer_exp(void) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_conn_sm *connsm; + + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { + return; + } + + ble_ll_conn_reset_pending_aux_conn_rsp(); + connsm->inita_identity_used = 0; + + ble_ll_scan_interrupted(connsm->scansm); + +#endif +} +/** + * Callback for slave when it transmits a data pdu and the connection event + * ends after the transmission. + * + * Context: Interrupt + * + * @param sch + * + */ +static void +ble_ll_conn_wait_txend(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + ble_ll_conn_current_sm_over(connsm); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +static void +ble_ll_conn_start_rx_encrypt(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + CONN_F_ENCRYPTED(connsm) = 1; + ble_phy_encrypt_enable(connsm->enc_data.rx_pkt_cntr, + connsm->enc_data.iv, + connsm->enc_data.enc_block.cipher_text, + !CONN_IS_MASTER(connsm)); +} + +static void +ble_ll_conn_start_rx_unencrypt(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + CONN_F_ENCRYPTED(connsm) = 0; + ble_phy_encrypt_disable(); +} + +static void +ble_ll_conn_txend_encrypt(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + CONN_F_ENCRYPTED(connsm) = 1; + ble_ll_conn_current_sm_over(connsm); +} + +static void +ble_ll_conn_rxend_unencrypt(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + CONN_F_ENCRYPTED(connsm) = 0; + ble_ll_conn_current_sm_over(connsm); +} + +static void +ble_ll_conn_continue_rx_encrypt(void *arg) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)arg; + ble_phy_encrypt_set_pkt_cntr(connsm->enc_data.rx_pkt_cntr, + !CONN_IS_MASTER(connsm)); +} +#endif + +/** + * Returns the cputime of the next scheduled item on the scheduler list or + * when the current connection will start its next interval (whichever is + * earlier). This API is called when determining at what time we should end + * the current connection event. The current connection event must end before + * the next scheduled item. However, the current connection itself is not + * in the scheduler list! Thus, we need to calculate the time at which the + * next connection will start (the schedule start time; not the anchor point) + * and not overrun it. + * + * Context: Interrupt + * + * @param connsm + * + * @return uint32_t + */ +static uint32_t +ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm) +{ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + uint32_t ce_end; + ce_end = connsm->ce_end_time; +#else + uint32_t ce_end; + uint32_t next_sched_time; + + /* Calculate time at which next connection event will start */ + /* NOTE: We dont care if this time is tick short. */ + ce_end = connsm->anchor_point + connsm->conn_itvl_ticks - + g_ble_ll_sched_offset_ticks; + if ((connsm->anchor_point_usecs + connsm->conn_itvl_usecs) >= 31) { + ++ce_end; + } + + if (ble_ll_sched_next_time(&next_sched_time)) { + if (CPUTIME_LT(next_sched_time, ce_end)) { + ce_end = next_sched_time; + } + } +#endif + + return ce_end; +} + +/** + * Called to check if certain connection state machine flags have been + * set. + * + * @param connsm + */ +static void +ble_ll_conn_chk_csm_flags(struct ble_ll_conn_sm *connsm) +{ + uint8_t update_status; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->csmflags.cfbit.send_ltk_req) { + /* + * Send Long term key request event to host. If masked, we need to + * send a REJECT_IND. + */ + if (ble_ll_hci_ev_ltk_req(connsm)) { + ble_ll_ctrl_reject_ind_send(connsm, BLE_LL_CTRL_ENC_REQ, + BLE_ERR_PINKEY_MISSING); + } + connsm->csmflags.cfbit.send_ltk_req = 0; + } +#endif + + /* + * There are two cases where this flag gets set: + * 1) A connection update procedure was started and the event counter + * has passed the instant. + * 2) We successfully sent the reject reason. + */ + if (connsm->csmflags.cfbit.host_expects_upd_event) { + update_status = BLE_ERR_SUCCESS; + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE)) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE); + } else { + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + update_status = connsm->reject_reason; + } + } + ble_ll_hci_ev_conn_update(connsm, update_status); + connsm->csmflags.cfbit.host_expects_upd_event = 0; + } + + /* Check if we need to send PHY update complete event */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + if (CONN_F_PHY_UPDATE_EVENT(connsm)) { + if (!ble_ll_hci_ev_phy_update(connsm, BLE_ERR_SUCCESS)) { + /* Sent event. Clear flag */ + CONN_F_PHY_UPDATE_EVENT(connsm) = 0; + } + } +#endif +} + +/** + * Called when we want to send a data channel pdu inside a connection event. + * + * Context: interrupt + * + * @param connsm + * + * @return int 0: success; otherwise failure to transmit + */ +static uint16_t +ble_ll_conn_adjust_pyld_len(struct ble_ll_conn_sm *connsm, uint16_t pyld_len) +{ + uint16_t phy_max_tx_octets; + uint16_t ret; + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + uint8_t phy_mode; + + if (connsm->phy_tx_transition) { + phy_mode = ble_ll_phy_to_phy_mode(connsm->phy_tx_transition, + connsm->phy_data.phy_options); + } else { + phy_mode = connsm->phy_data.tx_phy_mode; + } + + phy_max_tx_octets = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time, + phy_mode); + +#else + phy_max_tx_octets = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time, + BLE_PHY_MODE_1M); +#endif + + ret = pyld_len; + + if (ret > connsm->eff_max_tx_octets) { + ret = connsm->eff_max_tx_octets; + } + + if (ret > phy_max_tx_octets) { + ret = phy_max_tx_octets; + } + + return ret; +} + +static int +ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm) +{ + int rc; + uint8_t md; + uint8_t hdr_byte; + uint8_t end_transition; + uint8_t cur_txlen; + uint8_t next_txlen; + uint8_t cur_offset; + uint16_t pktlen; + uint32_t next_event_time; + uint32_t ticks; + struct os_mbuf *m; + struct ble_mbuf_hdr *ble_hdr; + struct os_mbuf_pkthdr *pkthdr = NULL; + struct os_mbuf_pkthdr *nextpkthdr; + struct ble_ll_empty_pdu empty_pdu; + ble_phy_tx_end_func txend_func; + int tx_phy_mode; + uint8_t llid; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + int is_ctrl; + uint8_t opcode; +#endif + + /* For compiler warnings... */ + ble_hdr = NULL; + m = NULL; + md = 0; + hdr_byte = BLE_LL_LLID_DATA_FRAG; + + if (connsm->csmflags.cfbit.terminate_ind_rxd) { + /* We just received terminate indication. + * Just send empty packet as an ACK + */ + CONN_F_EMPTY_PDU_TXD(connsm) = 1; + goto conn_tx_pdu; + } + + /* + * We need to check if we are retrying a pdu or if there is a pdu on + * the transmit queue. + */ + pkthdr = STAILQ_FIRST(&connsm->conn_txq); + if (!connsm->cur_tx_pdu && !CONN_F_EMPTY_PDU_TXD(connsm) && !pkthdr) { + CONN_F_EMPTY_PDU_TXD(connsm) = 1; + goto conn_tx_pdu; + } + + /* + * If we dont have a pdu we have previously transmitted, take it off + * the connection transmit queue + */ + cur_offset = 0; + if (!connsm->cur_tx_pdu && !CONN_F_EMPTY_PDU_TXD(connsm)) { + /* Convert packet header to mbuf */ + m = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + nextpkthdr = STAILQ_NEXT(pkthdr, omp_next); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* + * If we are encrypting, we are only allowed to send certain + * kinds of LL control PDU's. If none is enqueued, send empty pdu! + */ + if (connsm->enc_data.enc_state > CONN_ENC_S_ENCRYPTED) { + if (!ble_ll_ctrl_enc_allowed_pdu_tx(pkthdr)) { + CONN_F_EMPTY_PDU_TXD(connsm) = 1; + goto conn_tx_pdu; + } + + /* + * We will allow a next packet if it itself is allowed or we are + * a slave and we are sending the START_ENC_RSP. The master has + * to wait to receive the START_ENC_RSP from the slave before + * packets can be let go. + */ + if (nextpkthdr && !ble_ll_ctrl_enc_allowed_pdu_tx(nextpkthdr) + && ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) || + !ble_ll_ctrl_is_start_enc_rsp(m))) { + nextpkthdr = NULL; + } + } +#endif + /* Take packet off queue*/ + STAILQ_REMOVE_HEAD(&connsm->conn_txq, omp_next); + ble_hdr = BLE_MBUF_HDR_PTR(m); + + /* + * We dequeued new packet for transmission. + * If this is a data PDU we need to calculate payload length we can send + * over current PHY. Effectively, this determines fragmentation of packet + * into PDUs. + * If this is a control PDU we send complete PDU as only data PDU can be + * fragmented. We assume that checks (i.e. if remote supports such PDU) + * were already performed before putting packet on queue. + */ + llid = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + pktlen = pkthdr->omp_len; + if (llid == BLE_LL_LLID_CTRL) { + cur_txlen = pktlen; + } else { + cur_txlen = ble_ll_conn_adjust_pyld_len(connsm, pktlen); + } + ble_hdr->txinfo.pyld_len = cur_txlen; + + /* NOTE: header was set when first enqueued */ + hdr_byte = ble_hdr->txinfo.hdr_byte; + connsm->cur_tx_pdu = m; + } else { + nextpkthdr = pkthdr; + if (connsm->cur_tx_pdu) { + m = connsm->cur_tx_pdu; + ble_hdr = BLE_MBUF_HDR_PTR(m); + pktlen = OS_MBUF_PKTLEN(m); + cur_txlen = ble_hdr->txinfo.pyld_len; + cur_offset = ble_hdr->txinfo.offset; + if (cur_offset == 0) { + hdr_byte = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->enc_data.enc_state > CONN_ENC_S_ENCRYPTED) { + /* We will allow a next packet if it itself is allowed */ + pkthdr = OS_MBUF_PKTHDR(connsm->cur_tx_pdu); + if (nextpkthdr && !ble_ll_ctrl_enc_allowed_pdu_tx(nextpkthdr) + && ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) || + !ble_ll_ctrl_is_start_enc_rsp(connsm->cur_tx_pdu))) { + nextpkthdr = NULL; + } + } +#endif + } else { + /* Empty PDU here. NOTE: header byte gets set later */ + pktlen = 0; + cur_txlen = 0; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->enc_data.enc_state > CONN_ENC_S_ENCRYPTED) { + /* We will allow a next packet if it itself is allowed */ + if (nextpkthdr && !ble_ll_ctrl_enc_allowed_pdu_tx(nextpkthdr)) { + nextpkthdr = NULL; + } + } +#endif + } + } + + /* + * Set the more data data flag if we have more data to send and we + * have not been asked to terminate + */ + if (nextpkthdr || ((cur_offset + cur_txlen) < pktlen)) { + /* Get next event time */ + next_event_time = ble_ll_conn_get_next_sched_time(connsm); + + /* XXX: TODO: need to check this with phy update procedure. There are + limitations if we have started update */ + + /* + * Dont bother to set the MD bit if we cannot do the following: + * -> wait IFS, send the current frame. + * -> wait IFS, receive a maximum size frame. + * -> wait IFS, send the next frame. + * -> wait IFS, receive a maximum size frame. + * + * For slave: + * -> wait IFS, send current frame. + * -> wait IFS, receive maximum size frame. + * -> wait IFS, send next frame. + */ + if ((cur_offset + cur_txlen) < pktlen) { + next_txlen = pktlen - (cur_offset + cur_txlen); + } else { + if (nextpkthdr->omp_len > connsm->eff_max_tx_octets) { + next_txlen = connsm->eff_max_tx_octets; + } else { + next_txlen = nextpkthdr->omp_len; + } + } + + /* + * XXX: this calculation is based on using the current time + * and assuming the transmission will occur an IFS time from + * now. This is not the most accurate especially if we have + * received a frame and we are replying to it. + */ +#if BLE_LL_BT5_PHY_SUPPORTED + tx_phy_mode = connsm->phy_data.tx_phy_mode; +#else + tx_phy_mode = BLE_PHY_MODE_1M; +#endif + + ticks = (BLE_LL_IFS * 3) + connsm->eff_max_rx_time + + ble_ll_pdu_tx_time_get(next_txlen, tx_phy_mode) + + ble_ll_pdu_tx_time_get(cur_txlen, tx_phy_mode); + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + ticks += (BLE_LL_IFS + connsm->eff_max_rx_time); + } + + ticks = os_cputime_usecs_to_ticks(ticks); + if ((int32_t)((os_cputime_get32() + ticks) - next_event_time) < 0) { + md = 1; + } + } + + /* If we send an empty PDU we need to initialize the header */ +conn_tx_pdu: + if (CONN_F_EMPTY_PDU_TXD(connsm)) { + /* + * This looks strange, but we dont use the data pointer in the mbuf + * when we have an empty pdu. + */ + m = (struct os_mbuf *)&empty_pdu; + m->om_data = (uint8_t *)&empty_pdu; + m->om_data += BLE_MBUF_MEMBLOCK_OVERHEAD; + ble_hdr = &empty_pdu.ble_hdr; + ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.offset = 0; + ble_hdr->txinfo.pyld_len = 0; + } + + /* Set tx seqnum */ + if (connsm->tx_seqnum) { + hdr_byte |= BLE_LL_DATA_HDR_SN_MASK; + } + + /* If we have more data, set the bit */ + if (md) { + hdr_byte |= BLE_LL_DATA_HDR_MD_MASK; + } + + /* Set NESN (next expected sequence number) bit */ + if (connsm->next_exp_seqnum) { + hdr_byte |= BLE_LL_DATA_HDR_NESN_MASK; + } + + /* Set the header byte in the outgoing frame */ + ble_hdr->txinfo.hdr_byte = hdr_byte; + + /* + * If we are a slave, check to see if this transmission will end the + * connection event. We will end the connection event if we have + * received a valid frame with the more data bit set to 0 and we dont + * have more data. + * + * XXX: for a slave, we dont check to see if we can: + * -> wait IFS, rx frame from master (either big or small). + * -> wait IFS, send empty pdu or next pdu. + * + * We could do this. Now, we just keep going and hope that we dont + * overrun next scheduled item. + */ + if ((connsm->csmflags.cfbit.terminate_ind_rxd) || + ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && (md == 0) && + (connsm->cons_rxd_bad_crc == 0) && + ((connsm->last_rxd_hdr_byte & BLE_LL_DATA_HDR_MD_MASK) == 0) && + !ble_ll_ctrl_is_terminate_ind(hdr_byte, m->om_data[0]))) { + /* We will end the connection event */ + end_transition = BLE_PHY_TRANSITION_NONE; + txend_func = ble_ll_conn_wait_txend; + } else { + /* Wait for a response here */ + end_transition = BLE_PHY_TRANSITION_TX_RX; + txend_func = NULL; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + llid = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + if (llid == BLE_LL_LLID_CTRL) { + is_ctrl = 1; + opcode = m->om_data[0]; + } else { + is_ctrl = 0; + opcode = 0; + } + + if (is_ctrl && (opcode == BLE_LL_CTRL_START_ENC_RSP)) { + /* + * Both master and slave send the START_ENC_RSP encrypted and receive + * encrypted + */ + CONN_F_ENCRYPTED(connsm) = 1; + connsm->enc_data.tx_encrypted = 1; + ble_phy_encrypt_enable(connsm->enc_data.tx_pkt_cntr, + connsm->enc_data.iv, + connsm->enc_data.enc_block.cipher_text, + CONN_IS_MASTER(connsm)); + } else if (is_ctrl && (opcode == BLE_LL_CTRL_START_ENC_REQ)) { + /* + * Only the slave sends this and it gets sent unencrypted but + * we receive encrypted + */ + CONN_F_ENCRYPTED(connsm) = 0; + connsm->enc_data.enc_state = CONN_ENC_S_START_ENC_RSP_WAIT; + connsm->enc_data.tx_encrypted = 0; + ble_phy_encrypt_disable(); + if (txend_func == NULL) { + txend_func = ble_ll_conn_start_rx_encrypt; + } else { + txend_func = ble_ll_conn_txend_encrypt; + } + } else if (is_ctrl && (opcode == BLE_LL_CTRL_PAUSE_ENC_RSP)) { + /* + * The slave sends the PAUSE_ENC_RSP encrypted. The master sends + * it unencrypted (note that link was already set unencrypted). + */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + CONN_F_ENCRYPTED(connsm) = 1; + connsm->enc_data.tx_encrypted = 1; + ble_phy_encrypt_enable(connsm->enc_data.tx_pkt_cntr, + connsm->enc_data.iv, + connsm->enc_data.enc_block.cipher_text, + CONN_IS_MASTER(connsm)); + if (txend_func == NULL) { + txend_func = ble_ll_conn_start_rx_unencrypt; + } else { + txend_func = ble_ll_conn_rxend_unencrypt; + } + } else { + CONN_F_ENCRYPTED(connsm) = 0; + connsm->enc_data.enc_state = CONN_ENC_S_PAUSED; + connsm->enc_data.tx_encrypted = 0; + ble_phy_encrypt_disable(); + } + } else { + /* If encrypted set packet counter */ + if (CONN_F_ENCRYPTED(connsm)) { + connsm->enc_data.tx_encrypted = 1; + ble_phy_encrypt_set_pkt_cntr(connsm->enc_data.tx_pkt_cntr, + CONN_IS_MASTER(connsm)); + if (txend_func == NULL) { + txend_func = ble_ll_conn_continue_rx_encrypt; + } + } + } +#endif + + /* Set transmit end callback */ + ble_phy_set_txend_cb(txend_func, connsm); + rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, m, end_transition); + if (!rc) { + /* Log transmit on connection state */ + cur_txlen = ble_hdr->txinfo.pyld_len; + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_CONN_TX, cur_txlen, + ble_hdr->txinfo.offset); + + /* Set last transmitted MD bit */ + CONN_F_LAST_TXD_MD(connsm) = md; + + /* Increment packets transmitted */ + if (CONN_F_EMPTY_PDU_TXD(connsm)) { + if (connsm->csmflags.cfbit.terminate_ind_rxd) { + connsm->csmflags.cfbit.terminate_ind_rxd_acked = 1; + } + STATS_INC(ble_ll_conn_stats, tx_empty_pdus); + } else if ((hdr_byte & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) { + STATS_INC(ble_ll_conn_stats, tx_ctrl_pdus); + STATS_INCN(ble_ll_conn_stats, tx_ctrl_bytes, cur_txlen); + } else { + STATS_INC(ble_ll_conn_stats, tx_l2cap_pdus); + STATS_INCN(ble_ll_conn_stats, tx_l2cap_bytes, cur_txlen); + } + } + return rc; +} + +/** + * Schedule callback for start of connection event. + * + * Context: Interrupt + * + * @param sch + * + * @return int 0: scheduled item is still running. 1: schedule item is done. + */ +static int +ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) +{ + int rc; + uint32_t usecs; + uint32_t start; + struct ble_ll_conn_sm *connsm; + + /* XXX: note that we can extend end time here if we want. Look at this */ + + /* Set current connection state machine */ + connsm = (struct ble_ll_conn_sm *)sch->cb_arg; + g_ble_ll_conn_cur_sm = connsm; + BLE_LL_ASSERT(connsm); + if (connsm->conn_state == BLE_LL_CONN_STATE_IDLE) { + /* That should not happen. If it does it means connection + * is already closed + */ + STATS_INC(ble_ll_conn_stats, sched_start_in_idle); + BLE_LL_ASSERT(0); + ble_ll_conn_current_sm_over(connsm); + return BLE_LL_SCHED_STATE_DONE; + } + + /* Log connection event start */ + ble_ll_trace_u32(BLE_LL_TRACE_ID_CONN_EV_START, connsm->conn_handle); + + /* Disable whitelisting as connections do not use it */ + ble_ll_whitelist_disable(); + + /* Set LL state */ + ble_ll_state_set(BLE_LL_STATE_CONNECTION); + + /* Set channel */ + ble_phy_setchan(connsm->data_chan_index, connsm->access_addr, + connsm->crcinit); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_phy_resolv_list_disable(); +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_set(connsm->phy_data.tx_phy_mode, connsm->phy_data.rx_phy_mode); +#endif + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + /* Set start time of transmission */ + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(start, sch->remainder); + if (!rc) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (CONN_F_ENCRYPTED(connsm)) { + ble_phy_encrypt_enable(connsm->enc_data.tx_pkt_cntr, + connsm->enc_data.iv, + connsm->enc_data.enc_block.cipher_text, + 1); + } else { + ble_phy_encrypt_disable(); + } +#endif + rc = ble_ll_conn_tx_pdu(connsm); + if (!rc) { + rc = BLE_LL_SCHED_STATE_RUNNING; + } else { + /* Inform LL task of connection event end */ + rc = BLE_LL_SCHED_STATE_DONE; + } + } else { + STATS_INC(ble_ll_conn_stats, conn_ev_late); + rc = BLE_LL_SCHED_STATE_DONE; + } + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (CONN_F_ENCRYPTED(connsm)) { + ble_phy_encrypt_enable(connsm->enc_data.rx_pkt_cntr, + connsm->enc_data.iv, + connsm->enc_data.enc_block.cipher_text, + 1); + } else { + ble_phy_encrypt_disable(); + } +#endif + + /* XXX: what is this really for the slave? */ + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_rx_set_start_time(start, sch->remainder); + if (rc) { + /* End the connection event as we have no more buffers */ + STATS_INC(ble_ll_conn_stats, slave_ce_failures); + rc = BLE_LL_SCHED_STATE_DONE; + } else { + /* + * Set flag that tells slave to set last anchor point if a packet + * has been received. + */ + connsm->csmflags.cfbit.slave_set_last_anchor = 1; + + /* + * Set the wait for response time. The anchor point is when we + * expect the master to start transmitting. Worst-case, we expect + * to hear a reply within the anchor point plus: + * -> current tx window size + * -> current window widening amount (includes +/- 16 usec jitter) + * -> Amount of time it takes to detect packet start. + * -> Some extra time (16 usec) to insure timing is OK + */ + + /* + * For the 32 kHz crystal, the amount of usecs we have to wait + * is not from the anchor point; we have to account for the time + * from when the receiver is enabled until the anchor point. The + * time we start before the anchor point is this: + * -> current window widening. + * -> up to one 32 kHz tick since we discard remainder. + * -> Up to one tick since the usecs to ticks calc can be off + * by up to one tick. + * NOTES: + * 1) the 61 we add is for the two ticks mentioned above. + * 2) The address rx time and jitter is accounted for in the + * phy function + */ + usecs = connsm->slave_cur_tx_win_usecs + 61 + + (2 * connsm->slave_cur_window_widening); + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, usecs); + /* Set next wakeup time to connection event end time */ + rc = BLE_LL_SCHED_STATE_RUNNING; + } + } + + if (rc == BLE_LL_SCHED_STATE_DONE) { + ble_ll_event_send(&connsm->conn_ev_end); + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_conn_cur_sm = NULL; + } + + /* Set time that we last serviced the schedule */ + connsm->last_scheduled = os_cputime_get32(); + return rc; +} + +/** + * Called to determine if the device is allowed to send the next pdu in the + * connection event. This will always return 'true' if we are a slave. If we + * are a master, we must be able to send the next fragment and get a minimum + * sized response from the slave. + * + * Context: Interrupt context (rx end isr). + * + * @param connsm + * @param begtime Time at which IFS before pdu transmission starts + * + * @return int 0: not allowed to send 1: allowed to send + */ +static int +ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime, + uint32_t add_usecs) +{ + int rc; + uint8_t rem_bytes; + uint32_t ticks; + uint32_t usecs; + uint32_t next_sched_time; + struct os_mbuf *txpdu; + struct os_mbuf_pkthdr *pkthdr; + struct ble_mbuf_hdr *txhdr; + uint32_t allowed_usecs; + int tx_phy_mode; + +#if BLE_LL_BT5_PHY_SUPPORTED + tx_phy_mode = connsm->phy_data.tx_phy_mode; +#else + tx_phy_mode = BLE_PHY_MODE_1M; +#endif + + rc = 1; + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + /* Get next scheduled item time */ + next_sched_time = ble_ll_conn_get_next_sched_time(connsm); + + txpdu = connsm->cur_tx_pdu; + if (!txpdu) { + pkthdr = STAILQ_FIRST(&connsm->conn_txq); + if (pkthdr) { + txpdu = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + } + } else { + pkthdr = OS_MBUF_PKTHDR(txpdu); + } + + /* XXX: TODO: need to check this with phy update procedure. There are + limitations if we have started update */ + if (txpdu) { + txhdr = BLE_MBUF_HDR_PTR(txpdu); + rem_bytes = pkthdr->omp_len - txhdr->txinfo.offset; + if (rem_bytes > connsm->eff_max_tx_octets) { + rem_bytes = connsm->eff_max_tx_octets; + } + usecs = ble_ll_pdu_tx_time_get(rem_bytes, tx_phy_mode); + } else { + /* We will send empty pdu (just a LL header) */ + usecs = ble_ll_pdu_tx_time_get(0, tx_phy_mode); + } + usecs += (BLE_LL_IFS * 2) + connsm->eff_max_rx_time; + + ticks = (uint32_t)(next_sched_time - begtime); + allowed_usecs = os_cputime_ticks_to_usecs(ticks); + if ((usecs + add_usecs) >= allowed_usecs) { + rc = 0; + } + } + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) +/** + * Callback for the Authenticated payload timer. This function is called + * when the authenticated payload timer expires. When the authenticated + * payload timeout expires, we should + * -> Send the authenticated payload timeout event. + * -> Start the LE ping procedure. + * -> Restart the timer. + * + * @param arg + */ +void +ble_ll_conn_auth_pyld_timer_cb(struct ble_npl_event *ev) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)ble_npl_event_get_arg(ev); + ble_ll_auth_pyld_tmo_event_send(connsm); + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_LE_PING); + ble_ll_conn_auth_pyld_timer_start(connsm); +} + +void +ble_ll_conn_rd_features_timer_cb(struct ble_npl_event *ev) +{ + struct ble_ll_conn_sm *connsm; + + connsm = (struct ble_ll_conn_sm *)ble_npl_event_get_arg(ev); + + if (!connsm->csmflags.cfbit.pending_hci_rd_features || + !connsm->csmflags.cfbit.rxd_features) { + return; + } + + ble_ll_hci_ev_rd_rem_used_feat(connsm, BLE_ERR_SUCCESS); + connsm->csmflags.cfbit.pending_hci_rd_features = 0; +} + +/** + * Start (or restart) the authenticated payload timer + * + * @param connsm + */ +void +ble_ll_conn_auth_pyld_timer_start(struct ble_ll_conn_sm *connsm) +{ + int32_t tmo; + + /* Timeout in is in 10 msec units */ + tmo = (int32_t)BLE_LL_CONN_AUTH_PYLD_OS_TMO(connsm->auth_pyld_tmo); + ble_npl_callout_reset(&connsm->auth_pyld_timer, tmo); +} +#endif + +static void +ble_ll_conn_master_common_init(struct ble_ll_conn_sm *connsm) +{ + + /* Set master role */ + connsm->conn_role = BLE_LL_CONN_ROLE_MASTER; + + /* Set default ce parameters */ + + /* + * XXX: for now, we need twice the transmit window as our calculations + * for the transmit window offset could be off. + */ + connsm->tx_win_size = BLE_LL_CONN_TX_WIN_MIN + 1; + connsm->tx_win_off = 0; + connsm->master_sca = MYNEWT_VAL(BLE_LL_MASTER_SCA); + + /* Hop increment is a random value between 5 and 16. */ + connsm->hop_inc = (rand() % 12) + 5; + + /* Set channel map to map requested by host */ + connsm->num_used_chans = g_ble_ll_conn_params.num_used_chans; + memcpy(connsm->chanmap, g_ble_ll_conn_params.master_chan_map, + BLE_LL_CONN_CHMAP_LEN); + + /* Calculate random access address and crc initialization value */ + connsm->access_addr = ble_ll_utils_calc_access_addr(); + connsm->crcinit = rand() & 0xffffff; + + /* Set initial schedule callback */ + connsm->conn_sch.sched_cb = ble_ll_conn_event_start_cb; +} +/** + * Called when a create connection command has been received. This initializes + * a connection state machine in the master role. + * + * NOTE: Must be called before the state machine is started + * + * @param connsm + * @param hcc + */ +void +ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm, + struct hci_create_conn *hcc) +{ + + ble_ll_conn_master_common_init(connsm); + + /* Set slave latency and supervision timeout */ + connsm->slave_latency = hcc->conn_latency; + connsm->supervision_tmo = hcc->supervision_timeout; + + /* Set own address type and peer address if needed */ + connsm->own_addr_type = hcc->own_addr_type; + if (hcc->filter_policy == 0) { + memcpy(&connsm->peer_addr, &hcc->peer_addr, BLE_DEV_ADDR_LEN); + connsm->peer_addr_type = hcc->peer_addr_type; + } + + /* XXX: for now, just make connection interval equal to max */ + connsm->conn_itvl = hcc->conn_itvl_max; + + /* Check the min/max CE lengths are less than connection interval */ + if (hcc->min_ce_len > (connsm->conn_itvl * 2)) { + connsm->min_ce_len = connsm->conn_itvl * 2; + } else { + connsm->min_ce_len = hcc->min_ce_len; + } + + if (hcc->max_ce_len > (connsm->conn_itvl * 2)) { + connsm->max_ce_len = connsm->conn_itvl * 2; + } else { + connsm->max_ce_len = hcc->max_ce_len; + } +} + +static void +ble_ll_update_max_tx_octets_phy_mode(struct ble_ll_conn_sm *connsm) +{ + uint32_t usecs; + + usecs = connsm->eff_max_tx_time; + + connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_1M] = + ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_1M); + connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_2M] = + ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_2M); + connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_CODED_125KBPS] = + ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_CODED_125KBPS); + connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_CODED_500KBPS] = + ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_CODED_500KBPS); +} + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + +static void +ble_ll_conn_set_phy(struct ble_ll_conn_sm *connsm, int tx_phy, int rx_phy) +{ + + struct ble_ll_conn_phy_data *phy_data = &connsm->phy_data; + + phy_data->rx_phy_mode = ble_ll_phy_to_phy_mode(rx_phy, + BLE_HCI_LE_PHY_CODED_ANY); + phy_data->cur_rx_phy = rx_phy; + + phy_data->tx_phy_mode = ble_ll_phy_to_phy_mode(tx_phy, + BLE_HCI_LE_PHY_CODED_ANY); + phy_data->cur_tx_phy = tx_phy; + +} + +static void +ble_ll_conn_init_phy(struct ble_ll_conn_sm *connsm, int phy) +{ + struct ble_ll_conn_global_params *conngp; + + /* Always initialize symmetric PHY - controller can change this later */ + ble_ll_conn_set_phy(connsm, phy, phy); + + /* Update data length management to match initial PHY */ + conngp = &g_ble_ll_conn_params; + connsm->max_tx_octets = conngp->conn_init_max_tx_octets; + connsm->max_rx_octets = conngp->supp_max_rx_octets; + if (phy == BLE_PHY_CODED) { + connsm->max_tx_time = conngp->conn_init_max_tx_time_coded; + connsm->max_rx_time = BLE_LL_CONN_SUPP_TIME_MAX_CODED; + connsm->rem_max_tx_time = BLE_LL_CONN_SUPP_TIME_MIN_CODED; + connsm->rem_max_rx_time = BLE_LL_CONN_SUPP_TIME_MIN_CODED; + /* Assume peer does support coded */ + connsm->remote_features[0] |= (BLE_LL_FEAT_LE_CODED_PHY >> 8); + } else { + connsm->max_tx_time = conngp->conn_init_max_tx_time_uncoded; + connsm->max_rx_time = BLE_LL_CONN_SUPP_TIME_MAX_UNCODED; + connsm->rem_max_tx_time = BLE_LL_CONN_SUPP_TIME_MIN_UNCODED; + connsm->rem_max_rx_time = BLE_LL_CONN_SUPP_TIME_MIN_UNCODED; + } + connsm->eff_max_tx_time = connsm->rem_max_tx_time; + connsm->eff_max_rx_time = connsm->rem_max_rx_time; + connsm->rem_max_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->rem_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->eff_max_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->eff_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + + ble_ll_update_max_tx_octets_phy_mode(connsm); +} + +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + +void +ble_ll_conn_ext_master_init(struct ble_ll_conn_sm *connsm, + struct hci_ext_create_conn *hcc) +{ + + ble_ll_conn_master_common_init(connsm); + + /* Set own address type and peer address if needed */ + connsm->own_addr_type = hcc->own_addr_type; + if (hcc->filter_policy == 0) { + memcpy(&connsm->peer_addr, &hcc->peer_addr, BLE_DEV_ADDR_LEN); + connsm->peer_addr_type = hcc->peer_addr_type; + } + + connsm->initial_params = *hcc; +} + +void +ble_ll_conn_ext_set_params(struct ble_ll_conn_sm *connsm, + struct hci_ext_conn_params *hcc_params, int phy) +{ + /* Set slave latency and supervision timeout */ + connsm->slave_latency = hcc_params->conn_latency; + connsm->supervision_tmo = hcc_params->supervision_timeout; + + /* XXX: for now, just make connection interval equal to max */ + connsm->conn_itvl = hcc_params->conn_itvl_max; + + + /* Check the min/max CE lengths are less than connection interval */ + if (hcc_params->min_ce_len > (connsm->conn_itvl * 2)) { + connsm->min_ce_len = connsm->conn_itvl * 2; + } else { + connsm->min_ce_len = hcc_params->min_ce_len; + } + + if (hcc_params->max_ce_len > (connsm->conn_itvl * 2)) { + connsm->max_ce_len = connsm->conn_itvl * 2; + } else { + connsm->max_ce_len = hcc_params->max_ce_len; + } + + ble_ll_conn_calc_itvl_ticks(connsm); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_ll_conn_init_phy(connsm, phy); +#endif +} + + +#endif + +static void +ble_ll_conn_set_csa(struct ble_ll_conn_sm *connsm, bool chsel) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + if (chsel) { + CONN_F_CSA2_SUPP(connsm) = 1; + connsm->channel_id = ((connsm->access_addr & 0xffff0000) >> 16) ^ + (connsm->access_addr & 0x0000ffff); + + /* calculate the next data channel */ + connsm->data_chan_index = ble_ll_conn_calc_dci(connsm, 0); + return; + } +#endif + + connsm->last_unmapped_chan = 0; + + /* calculate the next data channel */ + connsm->data_chan_index = ble_ll_conn_calc_dci(connsm, 1); +} + +/** + * Create a new connection state machine. This is done once per + * connection when the HCI command "create connection" is issued to the + * controller or when a slave receives a connect request. + * + * Context: Link Layer task + * + * @param connsm + */ +void +ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm) +{ + struct ble_ll_conn_global_params *conn_params; + + /* Reset following elements */ + connsm->csmflags.conn_flags = 0; + connsm->event_cntr = 0; + connsm->conn_state = BLE_LL_CONN_STATE_IDLE; + connsm->disconnect_reason = 0; + connsm->rxd_disconnect_reason = 0; + connsm->conn_features = BLE_LL_CONN_INITIAL_FEATURES; + memset(connsm->remote_features, 0, sizeof(connsm->remote_features)); + connsm->vers_nr = 0; + connsm->comp_id = 0; + connsm->sub_vers_nr = 0; + connsm->reject_reason = BLE_ERR_SUCCESS; + connsm->conn_rssi = BLE_LL_CONN_UNKNOWN_RSSI; + connsm->rpa_index = -1; + connsm->inita_identity_used = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + connsm->sync_transfer_sync_timeout = g_ble_ll_conn_sync_transfer_params.sync_timeout_us; + connsm->sync_transfer_mode = g_ble_ll_conn_sync_transfer_params.mode; + connsm->sync_transfer_skip = g_ble_ll_conn_sync_transfer_params.max_skip; +#endif + + /* XXX: TODO set these based on PHY that started connection */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + connsm->phy_data.cur_tx_phy = BLE_PHY_1M; + connsm->phy_data.cur_rx_phy = BLE_PHY_1M; + connsm->phy_data.tx_phy_mode = BLE_PHY_MODE_1M; + connsm->phy_data.rx_phy_mode = BLE_PHY_MODE_1M; + connsm->phy_data.req_pref_tx_phys_mask = 0; + connsm->phy_data.req_pref_rx_phys_mask = 0; + connsm->phy_data.host_pref_tx_phys_mask = g_ble_ll_data.ll_pref_tx_phys; + connsm->phy_data.host_pref_rx_phys_mask = g_ble_ll_data.ll_pref_rx_phys; + connsm->phy_data.phy_options = 0; + connsm->phy_tx_transition = 0; +#endif + + /* Reset current control procedure */ + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; + connsm->pending_ctrl_procs = 0; + + /* + * Set handle in connection update procedure to 0. If the handle + * is non-zero it means that the host initiated the connection + * parameter update request and the rest of the parameters are valid. + */ + connsm->conn_param_req.handle = 0; + + /* Connection end event */ + ble_npl_event_init(&connsm->conn_ev_end, ble_ll_conn_event_end, connsm); + + /* Initialize transmit queue and ack/flow control elements */ + STAILQ_INIT(&connsm->conn_txq); + connsm->cur_tx_pdu = NULL; + connsm->tx_seqnum = 0; + connsm->next_exp_seqnum = 0; + connsm->cons_rxd_bad_crc = 0; + connsm->last_rxd_sn = 1; + connsm->completed_pkts = 0; + + /* initialize data length mgmt */ + conn_params = &g_ble_ll_conn_params; + connsm->max_tx_octets = conn_params->conn_init_max_tx_octets; + connsm->max_rx_octets = conn_params->supp_max_rx_octets; + connsm->max_tx_time = conn_params->conn_init_max_tx_time; + connsm->max_rx_time = conn_params->supp_max_rx_time; + connsm->rem_max_tx_time = BLE_LL_CONN_SUPP_TIME_MIN; + connsm->rem_max_rx_time = BLE_LL_CONN_SUPP_TIME_MIN; + connsm->eff_max_tx_time = BLE_LL_CONN_SUPP_TIME_MIN; + connsm->eff_max_rx_time = BLE_LL_CONN_SUPP_TIME_MIN; + connsm->rem_max_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->rem_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->eff_max_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + connsm->eff_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + connsm->host_req_max_tx_time = 0; +#endif + + ble_ll_update_max_tx_octets_phy_mode(connsm); + + /* Reset encryption data */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + memset(&connsm->enc_data, 0, sizeof(struct ble_ll_conn_enc_data)); + connsm->enc_data.enc_state = CONN_ENC_S_UNENCRYPTED; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + connsm->auth_pyld_tmo = BLE_LL_CONN_DEF_AUTH_PYLD_TMO; + CONN_F_LE_PING_SUPP(connsm) = 1; + ble_npl_callout_init(&connsm->auth_pyld_timer, + &g_ble_ll_data.ll_evq, + ble_ll_conn_auth_pyld_timer_cb, + connsm); +#endif + + ble_ll_conn_calc_itvl_ticks(connsm); + + /* Add to list of active connections */ + SLIST_INSERT_HEAD(&g_ble_ll_conn_active_list, connsm, act_sle); +} + +void +ble_ll_conn_update_eff_data_len(struct ble_ll_conn_sm *connsm) +{ + int send_event; + uint16_t eff_time; + uint16_t eff_bytes; + + /* Assume no event sent */ + send_event = 0; + + /* See if effective times have changed */ + eff_time = min(connsm->rem_max_tx_time, connsm->max_rx_time); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (connsm->phy_data.cur_rx_phy == BLE_PHY_CODED) { + eff_time = max(eff_time, BLE_LL_CONN_SUPP_TIME_MIN_CODED); + } +#endif + if (eff_time != connsm->eff_max_rx_time) { + connsm->eff_max_rx_time = eff_time; + send_event = 1; + } + eff_time = min(connsm->rem_max_rx_time, connsm->max_tx_time); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (connsm->phy_data.cur_tx_phy == BLE_PHY_CODED) { + eff_time = max(eff_time, BLE_LL_CONN_SUPP_TIME_MIN_CODED); + } +#endif + if (eff_time != connsm->eff_max_tx_time) { + connsm->eff_max_tx_time = eff_time; + send_event = 1; + + ble_ll_update_max_tx_octets_phy_mode(connsm); + } + eff_bytes = min(connsm->rem_max_tx_octets, connsm->max_rx_octets); + if (eff_bytes != connsm->eff_max_rx_octets) { + connsm->eff_max_rx_octets = eff_bytes; + send_event = 1; + } + eff_bytes = min(connsm->rem_max_rx_octets, connsm->max_tx_octets); + if (eff_bytes != connsm->eff_max_tx_octets) { + connsm->eff_max_tx_octets = eff_bytes; + send_event = 1; + } + + if (send_event) { + ble_ll_hci_ev_datalen_chg(connsm); + } +} + +/** + * Called when a connection is terminated + * + * Context: Link Layer task. + * + * @param connsm + * @param ble_err + */ +void +ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) +{ + struct os_mbuf *m; + struct os_mbuf_pkthdr *pkthdr; + os_sr_t sr; + + /* Remove scheduler events just in case */ + ble_ll_sched_rmv_elem(&connsm->conn_sch); + + /* In case of the supervision timeout we shall make sure + * that there is no ongoing connection event. It could happen + * because we scheduled connection event before checking connection timeout. + * If connection event managed to start, let us drop it. + */ + OS_ENTER_CRITICAL(sr); + if (g_ble_ll_conn_cur_sm == connsm) { + ble_ll_conn_halt(); + STATS_INC(ble_ll_conn_stats, conn_event_while_tmo); + } + OS_EXIT_CRITICAL(sr); + + /* Stop any control procedures that might be running */ + ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + ble_npl_callout_stop(&connsm->auth_pyld_timer); +#endif + + /* Remove from the active connection list */ + SLIST_REMOVE(&g_ble_ll_conn_active_list, connsm, ble_ll_conn_sm, act_sle); + + /* Free the current transmit pdu if there is one. */ + if (connsm->cur_tx_pdu) { + os_mbuf_free_chain(connsm->cur_tx_pdu); + connsm->cur_tx_pdu = NULL; + } + + /* Free all packets on transmit queue */ + while (1) { + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(&connsm->conn_txq); + if (!pkthdr) { + break; + } + STAILQ_REMOVE_HEAD(&connsm->conn_txq, omp_next); + + m = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf)); + os_mbuf_free_chain(m); + } + + /* Make sure events off queue */ + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &connsm->conn_ev_end); + +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + /* Remove from occupied periods */ + OS_ENTER_CRITICAL(sr); + BLE_LL_ASSERT(g_ble_ll_sched_data.sch_num_occ_periods > 0); + BLE_LL_ASSERT(g_ble_ll_sched_data.sch_occ_period_mask & connsm->period_occ_mask); + --g_ble_ll_sched_data.sch_num_occ_periods; + g_ble_ll_sched_data.sch_occ_period_mask &= ~connsm->period_occ_mask; + OS_EXIT_CRITICAL(sr); +#endif + + /* Connection state machine is now idle */ + connsm->conn_state = BLE_LL_CONN_STATE_IDLE; + + /* + * If we have features and there's pending HCI command, send an event before + * disconnection event so it does make sense to host. + */ + if (connsm->csmflags.cfbit.pending_hci_rd_features && + connsm->csmflags.cfbit.rxd_features) { + ble_ll_hci_ev_rd_rem_used_feat(connsm, BLE_ERR_SUCCESS); + connsm->csmflags.cfbit.pending_hci_rd_features = 0; + } + + /* + * If there is still pending read features request HCI command, send an + * event to complete it. + */ + if (connsm->csmflags.cfbit.pending_hci_rd_features) { + ble_ll_hci_ev_rd_rem_used_feat(connsm, ble_err); + connsm->csmflags.cfbit.pending_hci_rd_features = 0; + } + + /* + * We need to send a disconnection complete event. Connection Complete for + * canceling connection creation is sent from LE Create Connection Cancel + * Command handler. + * + * If the ble error is "success" it means that the reset command was + * received and we should not send an event. + */ + if (ble_err && (ble_err != BLE_ERR_UNK_CONN_ID || + connsm->csmflags.cfbit.terminate_ind_rxd)) { + ble_ll_disconn_comp_event_send(connsm, ble_err); + } + + /* Put connection state machine back on free list */ + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + + /* Log connection end */ + ble_ll_trace_u32x3(BLE_LL_TRACE_ID_CONN_END, connsm->conn_handle, + connsm->event_cntr, (uint32_t)ble_err); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +void +ble_ll_conn_get_anchor(struct ble_ll_conn_sm *connsm, uint16_t conn_event, + uint32_t *anchor, uint8_t *anchor_usecs) +{ + uint32_t ticks; + uint32_t itvl; + + itvl = (connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS); + + if ((int16_t)(conn_event - connsm->event_cntr) < 0) { + itvl *= connsm->event_cntr - conn_event; + ticks = os_cputime_usecs_to_ticks(itvl); + *anchor = connsm->anchor_point - ticks; + } else { + itvl *= conn_event - connsm->event_cntr; + ticks = os_cputime_usecs_to_ticks(itvl); + *anchor = connsm->anchor_point + ticks; + } + + *anchor_usecs = connsm->anchor_point_usecs; + *anchor_usecs += (itvl - os_cputime_ticks_to_usecs(ticks)); + if (*anchor_usecs >= 31) { + (*anchor)++; + *anchor_usecs -= 31; + } +} +#endif + +/** + * Called to move to the next connection event. + * + * Context: Link Layer task. + * + * @param connsm + * + * @return int + */ +static int +ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) +{ + uint16_t latency; + uint32_t itvl; + uint32_t cur_ww; + uint32_t max_ww; + struct ble_ll_conn_upd_req *upd; + uint32_t ticks; + uint32_t usecs; + + /* XXX: deal with connection request procedure here as well */ + ble_ll_conn_chk_csm_flags(connsm); + + /* If unable to start terminate procedure, start it now */ + if (connsm->disconnect_reason && !CONN_F_TERMINATE_STARTED(connsm)) { + ble_ll_ctrl_terminate_start(connsm); + } + + if (CONN_F_TERMINATE_STARTED(connsm) && (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE)) { + /* Some of the devices waits whole connection interval to ACK our + * TERMINATE_IND sent as a Slave. Since we are here it means we are still waiting for ACK. + * Make sure we catch it in next connection event. + */ + connsm->slave_latency = 0; + } + + /* + * XXX: TODO Probably want to add checks to see if we need to start + * a control procedure here as an instant may have prevented us from + * starting one. + */ + + /* + * XXX TODO: I think this is technically incorrect. We can allow slave + * latency if we are doing one of these updates as long as we + * know that the master has received the ACK to the PDU that set + * the instant + */ + /* Set event counter to the next connection event that we will tx/rx in */ + itvl = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; + latency = 1; + if (connsm->csmflags.cfbit.allow_slave_latency && + !connsm->csmflags.cfbit.conn_update_sched && + !CONN_F_PHY_UPDATE_SCHED(connsm) && + !connsm->csmflags.cfbit.chanmap_update_scheduled) { + if (connsm->csmflags.cfbit.pkt_rxd) { + latency += connsm->slave_latency; + itvl = itvl * latency; + } + } + connsm->event_cntr += latency; + + /* Set next connection event start time */ + /* We can use pre-calculated values for one interval if latency is 1. */ + if (latency == 1) { + connsm->anchor_point += connsm->conn_itvl_ticks; + connsm->anchor_point_usecs += connsm->conn_itvl_usecs; + } else { + uint32_t ticks; + ticks = os_cputime_usecs_to_ticks(itvl); + connsm->anchor_point += ticks; + connsm->anchor_point_usecs += (itvl - os_cputime_ticks_to_usecs(ticks)); + } + if (connsm->anchor_point_usecs >= 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs -= 31; + } + + /* + * If a connection update has been scheduled and the event counter + * is now equal to the instant, we need to adjust the start of the + * connection by the the transmit window offset. We also copy in the + * update parameters as they now should take effect. + */ + if (connsm->csmflags.cfbit.conn_update_sched && + (connsm->event_cntr == connsm->conn_update_req.instant)) { + + /* Set flag so we send connection update event */ + upd = &connsm->conn_update_req; + if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) || + ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && + IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) || + (connsm->conn_itvl != upd->interval) || + (connsm->slave_latency != upd->latency) || + (connsm->supervision_tmo != upd->timeout)) { + connsm->csmflags.cfbit.host_expects_upd_event = 1; + } + + connsm->supervision_tmo = upd->timeout; + connsm->slave_latency = upd->latency; + connsm->tx_win_size = upd->winsize; + connsm->slave_cur_tx_win_usecs = + connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; + connsm->tx_win_off = upd->winoffset; + connsm->conn_itvl = upd->interval; + ble_ll_conn_calc_itvl_ticks(connsm); + if (upd->winoffset != 0) { + usecs = upd->winoffset * BLE_LL_CONN_ITVL_USECS; + ticks = os_cputime_usecs_to_ticks(usecs); + connsm->anchor_point += ticks; + usecs = usecs - os_cputime_ticks_to_usecs(ticks); + connsm->anchor_point_usecs += usecs; + if (connsm->anchor_point_usecs >= 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs -= 31; + } + } + + /* Reset the starting point of the connection supervision timeout */ + connsm->last_rxd_pdu_cputime = connsm->anchor_point; + + /* Reset update scheduled flag */ + connsm->csmflags.cfbit.conn_update_sched = 0; + } + + /* + * If there is a channel map request pending and we have reached the + * instant, change to new channel map. Note there is a special case here. + * If we received a channel map update with an instant equal to the event + * counter, when we get here the event counter has already been + * incremented by 1. That is why we do a signed comparison and change to + * new channel map once the event counter equals or has passed channel + * map update instant. + */ + if (connsm->csmflags.cfbit.chanmap_update_scheduled && + ((int16_t)(connsm->chanmap_instant - connsm->event_cntr) <= 0)) { + + /* XXX: there is a chance that the control packet is still on + * the queue of the master. This means that we never successfully + * transmitted update request. Would end up killing connection + on slave side. Could ignore it or see if still enqueued. */ + connsm->num_used_chans = + ble_ll_utils_calc_num_used_chans(connsm->req_chanmap); + memcpy(connsm->chanmap, connsm->req_chanmap, BLE_LL_CONN_CHMAP_LEN); + + connsm->csmflags.cfbit.chanmap_update_scheduled = 0; + + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CHAN_MAP_UPD); + + /* XXX: host could have resent channel map command. Need to + check to make sure we dont have to restart! */ + } + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + if (CONN_F_PHY_UPDATE_SCHED(connsm) && + (connsm->event_cntr == connsm->phy_instant)) { + + /* Set cur phy to new phy */ + if (connsm->phy_data.new_tx_phy) { + connsm->phy_data.cur_tx_phy = connsm->phy_data.new_tx_phy; + connsm->phy_data.tx_phy_mode = + ble_ll_phy_to_phy_mode(connsm->phy_data.cur_tx_phy, + connsm->phy_data.phy_options); + } + + if (connsm->phy_data.new_rx_phy) { + connsm->phy_data.cur_rx_phy = connsm->phy_data.new_rx_phy; + connsm->phy_data.rx_phy_mode = + ble_ll_phy_to_phy_mode(connsm->phy_data.cur_rx_phy, + connsm->phy_data.phy_options); + } + + /* Clear flags and set flag to send event at next instant */ + CONN_F_PHY_UPDATE_SCHED(connsm) = 0; + CONN_F_PHY_UPDATE_EVENT(connsm) = 1; + + ble_ll_ctrl_phy_update_proc_complete(connsm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + /* Recalculate effective connection parameters */ + ble_ll_conn_update_eff_data_len(connsm); + + /* + * If PHY in either direction was changed to coded, we assume that peer + * does support LE Coded PHY even if features were not exchanged yet. + * This means that MaxRxTime can be updated to supported max and we need + * initiate DLE to notify peer about the change. + */ + if (((connsm->phy_data.cur_tx_phy == BLE_PHY_CODED) || + (connsm->phy_data.cur_rx_phy == BLE_PHY_CODED)) && + !(connsm->remote_features[0] & (BLE_LL_FEAT_LE_CODED_PHY >> 8))) { + connsm->remote_features[0] |= (BLE_LL_FEAT_LE_CODED_PHY >> 8); + connsm->max_rx_time = BLE_LL_CONN_SUPP_TIME_MAX_CODED; + ble_ll_ctrl_initiate_dle(connsm); + } +#endif + } +#endif + + /* Calculate data channel index of next connection event */ + connsm->data_chan_index = ble_ll_conn_calc_dci(connsm, latency); + + /* + * If we are trying to terminate connection, check if next wake time is + * passed the termination timeout. If so, no need to continue with + * connection as we will time out anyway. + */ + if (CONN_F_TERMINATE_STARTED(connsm)) { + if ((int32_t)(connsm->terminate_timeout - connsm->anchor_point) <= 0) { + return -1; + } + } + + /* + * Calculate ce end time. For a slave, we need to add window widening and + * the transmit window if we still have one. + */ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + itvl = g_ble_ll_sched_data.sch_ticks_per_period; +#else + itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; +#endif + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + + cur_ww = ble_ll_utils_calc_window_widening(connsm->anchor_point, + connsm->last_anchor_point, + connsm->master_sca); + max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS; + if (cur_ww >= max_ww) { + return -1; + } + cur_ww += BLE_LL_JITTER_USECS; + connsm->slave_cur_window_widening = cur_ww; + itvl += os_cputime_usecs_to_ticks(cur_ww + connsm->slave_cur_tx_win_usecs); + } + itvl -= g_ble_ll_sched_offset_ticks; + connsm->ce_end_time = connsm->anchor_point + itvl; + + return 0; +} + +/** + * Called when a connection has been created. This function will + * -> Set the connection state to created. + * -> Start the connection supervision timer + * -> Set the Link Layer state to connection. + * -> Send a connection complete event. + * + * See Section 4.5.2 Vol 6 Part B + * + * Context: Link Layer + * + * @param connsm + * + * @ return 0: connection NOT created. 1: connection created + */ +static int +ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr) +{ + int rc; + uint8_t *evbuf; + uint32_t endtime; + uint32_t usecs; + + /* XXX: TODO this assumes we received in 1M phy */ + + /* Set state to created */ + connsm->conn_state = BLE_LL_CONN_STATE_CREATED; + + /* Clear packet received flag */ + connsm->csmflags.cfbit.pkt_rxd = 0; + + /* Consider time created the last scheduled time */ + connsm->last_scheduled = os_cputime_get32(); + + /* + * Set the last rxd pdu time since this is where we want to start the + * supervision timer from. + */ + connsm->last_rxd_pdu_cputime = connsm->last_scheduled; + + /* + * Set first connection event time. If slave the endtime is the receive end + * time of the connect request. The actual connection starts 1.25 msecs plus + * the transmit window offset from the end of the connection request. + */ + rc = 1; + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + /* + * With a 32.768 kHz crystal we dont care about the remaining usecs + * when setting last anchor point. The only thing last anchor is used + * for is to calculate window widening. The effect of this is + * negligible. + */ + connsm->last_anchor_point = rxhdr->beg_cputime; + + usecs = rxhdr->rem_usecs + 1250 + + (connsm->tx_win_off * BLE_LL_CONN_TX_WIN_USECS) + + ble_ll_pdu_tx_time_get(BLE_CONNECT_REQ_LEN, + rxhdr->rxinfo.phy_mode); + + if (rxhdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) { + switch (rxhdr->rxinfo.phy) { + case BLE_PHY_1M: + case BLE_PHY_2M: + usecs += 1250; + break; + case BLE_PHY_CODED: + usecs += 2500; + break; + default: + BLE_LL_ASSERT(0); + break; + } + } + + /* Anchor point is cputime. */ + endtime = os_cputime_usecs_to_ticks(usecs); + connsm->anchor_point = rxhdr->beg_cputime + endtime; + connsm->anchor_point_usecs = usecs - os_cputime_ticks_to_usecs(endtime); + if (connsm->anchor_point_usecs == 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs = 0; + } + + connsm->slave_cur_tx_win_usecs = + connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + connsm->ce_end_time = connsm->anchor_point + + g_ble_ll_sched_data.sch_ticks_per_period + + os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1; + +#else + connsm->ce_end_time = connsm->anchor_point + + (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT) + + os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1; +#endif + connsm->slave_cur_window_widening = BLE_LL_JITTER_USECS; + + /* Start the scheduler for the first connection event */ + while (ble_ll_sched_slave_new(connsm)) { + if (ble_ll_conn_next_event(connsm)) { + STATS_INC(ble_ll_conn_stats, cant_set_sched); + rc = 0; + break; + } + } + } + + /* Send connection complete event to inform host of connection */ + if (rc) { +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* + * If we have default phy preferences and they are different than + * the current PHY's in use, start update procedure. + */ + /* + * XXX: should we attempt to start this without knowing if + * the other side can support it? + */ + if (!ble_ll_conn_chk_phy_upd_start(connsm)) { + CONN_F_CTRLR_PHY_UPDATE(connsm) = 1; + } +#endif + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + ble_ll_adv_send_conn_comp_ev(connsm, rxhdr); + } else { + evbuf = ble_ll_init_get_conn_comp_ev(); + ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, evbuf, NULL); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + ble_ll_hci_ev_le_csa(connsm); +#endif + + /* + * Initiate features exchange + * + * XXX we do this only as a master as it was observed that sending + * LL_SLAVE_FEATURE_REQ after connection breaks some recent iPhone + * models; for slave just assume master will initiate features xchg + * if it has some additional features to use. + */ + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG); + } + } + + return rc; +} + +/** + * Called upon end of connection event + * + * Context: Link-layer task + * + * @param void *arg Pointer to connection state machine + * + */ +static void +ble_ll_conn_event_end(struct ble_npl_event *ev) +{ + uint8_t ble_err; + uint32_t tmo; + struct ble_ll_conn_sm *connsm; + + ble_ll_rfmgmt_release(); + + /* Better be a connection state machine! */ + connsm = (struct ble_ll_conn_sm *)ble_npl_event_get_arg(ev); + BLE_LL_ASSERT(connsm); + if (connsm->conn_state == BLE_LL_CONN_STATE_IDLE) { + /* That should not happen. If it does it means connection + * is already closed. + * Make sure LL state machine is in idle + */ + STATS_INC(ble_ll_conn_stats, sched_end_in_idle); + BLE_LL_ASSERT(0); + + /* Just in case */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + + ble_ll_scan_chk_resume(); + return; + } + + /* Log event end */ + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_CONN_EV_END, connsm->conn_handle, + connsm->event_cntr); + + ble_ll_scan_chk_resume(); + + /* If we have transmitted the terminate IND successfully, we are done */ + if ((connsm->csmflags.cfbit.terminate_ind_txd) || + (connsm->csmflags.cfbit.terminate_ind_rxd && + connsm->csmflags.cfbit.terminate_ind_rxd_acked)) { + if (connsm->csmflags.cfbit.terminate_ind_txd) { + ble_err = BLE_ERR_CONN_TERM_LOCAL; + } else { + /* Make sure the disconnect reason is valid! */ + ble_err = connsm->rxd_disconnect_reason; + if (ble_err == 0) { + ble_err = BLE_ERR_REM_USER_CONN_TERM; + } + } + ble_ll_conn_end(connsm, ble_err); + return; + } + + /* Remove any connection end events that might be enqueued */ + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &connsm->conn_ev_end); + + /* + * If we have received a packet, we can set the current transmit window + * usecs to 0 since we dont need to listen in the transmit window. + */ + if (connsm->csmflags.cfbit.pkt_rxd) { + connsm->slave_cur_tx_win_usecs = 0; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + /* + * If we are encrypted and have passed the authenticated payload timeout + * we need to send an event to tell the host. Unfortunately, I think we + * need one of these per connection and we have to set this timer + * fairly accurately. So we need to another event in the connection. + * This sucks. + * + * The way this works is that whenever the timer expires it just gets reset + * and we send the autheticated payload timeout event. Note that this timer + * should run even when encryption is paused. + * XXX: what should be here? Was there code here that got deleted? + */ +#endif + + /* Move to next connection event */ + if (ble_ll_conn_next_event(connsm)) { + ble_ll_conn_end(connsm, BLE_ERR_CONN_TERM_LOCAL); + return; + } + + /* Reset "per connection event" variables */ + connsm->cons_rxd_bad_crc = 0; + connsm->csmflags.cfbit.pkt_rxd = 0; + + /* See if we need to start any control procedures */ + ble_ll_ctrl_chk_proc_start(connsm); + + /* Set initial schedule callback */ + connsm->conn_sch.sched_cb = ble_ll_conn_event_start_cb; + + /* XXX: I think all this fine for when we do connection updates, but + we may want to force the first event to be scheduled. Not sure */ + /* Schedule the next connection event */ + while (ble_ll_sched_conn_reschedule(connsm)) { + if (ble_ll_conn_next_event(connsm)) { + ble_ll_conn_end(connsm, BLE_ERR_CONN_TERM_LOCAL); + return; + } + } + + /* + * This is definitely not perfect but hopefully will be fine in regards to + * the specification. We check the supervision timer at connection event + * end. If the next connection event is going to start past the supervision + * timeout we end the connection here. I guess this goes against the spec + * in two ways: + * 1) We are actually causing a supervision timeout before the time + * specified. However, this is really a moot point because the supervision + * timeout would have expired before we could possibly receive a packet. + * 2) We may end the supervision timeout a bit later than specified as + * we only check this at event end and a bad CRC could cause us to continue + * the connection event longer than the supervision timeout. Given that two + * bad CRC's consecutively ends the connection event, I dont regard this as + * a big deal but it could cause a slightly longer supervision timeout. + */ + if (connsm->conn_state == BLE_LL_CONN_STATE_CREATED) { + tmo = (uint32_t)connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS * 6UL; + ble_err = BLE_ERR_CONN_ESTABLISHMENT; + } else { + tmo = connsm->supervision_tmo * BLE_HCI_CONN_SPVN_TMO_UNITS * 1000UL; + ble_err = BLE_ERR_CONN_SPVN_TMO; + } + /* XXX: Convert to ticks to usecs calculation instead??? */ + tmo = os_cputime_usecs_to_ticks(tmo); + if ((int32_t)(connsm->anchor_point - connsm->last_rxd_pdu_cputime) >= tmo) { + ble_ll_conn_end(connsm, ble_err); + return; + } + + /* If we have completed packets, send an event */ + ble_ll_conn_num_comp_pkts_event_send(connsm); + + /* If we have features and there's pending HCI command, send an event */ + if (connsm->csmflags.cfbit.pending_hci_rd_features && + connsm->csmflags.cfbit.rxd_features) { + ble_ll_hci_ev_rd_rem_used_feat(connsm, BLE_ERR_SUCCESS); + connsm->csmflags.cfbit.pending_hci_rd_features = 0; + } +} + +/** + * Update the connection request PDU with the address type and address of + * advertiser we are going to send connect request to. + * + * @param m + * @param adva + * @param addr_type Address type of ADVA from received advertisement. + * @param inita + * @param inita_type Address type of INITA from received advertisement. + + * @param txoffset The tx window offset for this connection + */ +static void +ble_ll_conn_connect_ind_prepare(struct ble_ll_conn_sm *connsm, + struct ble_ll_scan_pdu_data *pdu_data, + uint8_t adva_type, uint8_t *adva, + uint8_t inita_type, uint8_t *inita, + int rpa_index, uint8_t channel) +{ + uint8_t hdr; + uint8_t *addr; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + int is_rpa; + struct ble_ll_resolv_entry *rl; +#endif + + hdr = BLE_ADV_PDU_TYPE_CONNECT_IND; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) + /* We need CSA2 bit only for legacy connect */ + if (channel >= BLE_PHY_NUM_DATA_CHANS) { + hdr |= BLE_ADV_PDU_HDR_CHSEL; + } +#endif + + if (adva_type) { + /* Set random address */ + hdr |= BLE_ADV_PDU_HDR_RXADD_MASK; + } + + if (inita) { + memcpy(pdu_data->inita, inita, BLE_DEV_ADDR_LEN); + if (inita_type) { + hdr |= BLE_ADV_PDU_HDR_TXADD_RAND; + } + } else { + /* Get pointer to our device address */ + connsm = g_ble_ll_conn_create_sm; + if ((connsm->own_addr_type & 1) == 0) { + addr = g_dev_addr; + } else { + hdr |= BLE_ADV_PDU_HDR_TXADD_RAND; + addr = g_random_addr; + } + + /* XXX: do this ahead of time? Calculate the local rpa I mean */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (connsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + rl = NULL; + is_rpa = ble_ll_is_rpa(adva, adva_type); + if (is_rpa) { + if (rpa_index >= 0) { + rl = &g_ble_ll_resolv_list[rpa_index]; + } + } else { + /* we look for RL entry to generate local RPA regardless if + * resolving is enabled or not (as this is is for local RPA + * not peer RPA) + */ + rl = ble_ll_resolv_list_find(adva, adva_type); + } + + /* + * If peer in on resolving list, we use RPA generated with Local IRK + * from resolving list entry. In other case, we need to use our identity + * address (see Core 5.0, Vol 6, Part B, section 6.4). + */ + if (rl && rl->rl_has_local) { + hdr |= BLE_ADV_PDU_HDR_TXADD_RAND; + ble_ll_resolv_get_priv_addr(rl, 1, pdu_data->inita); + addr = NULL; + } + } +#endif + + if (addr) { + memcpy(pdu_data->inita, addr, BLE_DEV_ADDR_LEN); + /* Identity address used */ + connsm->inita_identity_used = 1; + } + } + + memcpy(pdu_data->adva, adva, BLE_DEV_ADDR_LEN); + + pdu_data->hdr_byte = hdr; +} + +/* Returns true if the address matches the connection peer address having in + * mind privacy mode + */ +static int +ble_ll_conn_is_peer_adv(uint8_t addr_type, uint8_t *adva, int index) +{ + int rc; + uint8_t *peer_addr = NULL; + struct ble_ll_conn_sm *connsm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + struct ble_ll_resolv_entry *rl; +#endif + + /* XXX: Deal with different types of random addresses here! */ + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { + return 0; + } + + switch (connsm->peer_addr_type) { + /* Fall-through intentional */ + case BLE_HCI_CONN_PEER_ADDR_PUBLIC: + case BLE_HCI_CONN_PEER_ADDR_RANDOM: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (ble_ll_addr_is_id(adva, addr_type)) { + /* Peer uses its identity address. Let's verify privacy mode. + * + * Note: Core Spec 5.0 Vol 6, Part B + * If the Host has added the peer device to the resolving list + * with an all-zero peer IRK, the Controller shall only accept + * the peer's identity address. + */ + if (ble_ll_resolv_enabled()) { + rl = ble_ll_resolv_list_find(adva, addr_type); + if (rl && (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && + rl->rl_has_peer) { + return 0; + } + } + } + + /* Check if peer uses RPA. If so and it match, use it as controller + * supports privacy mode + */ + if ((index >= 0) && + (g_ble_ll_resolv_list[index].rl_addr_type == connsm->peer_addr_type)) { + peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr; + } +#endif + /* + * If we are here it means we don't know the device, lets + * check if type is what we are looking for and later + * if address matches + */ + if ((connsm->peer_addr_type == addr_type) && !peer_addr) { + peer_addr = adva; + } + + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + case BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT: + if ((index < 0) || + (g_ble_ll_resolv_list[index].rl_addr_type != 0)) { + return 0; + } + peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr; + break; + case BLE_HCI_CONN_PEER_ADDR_RANDOM_IDENT: + if ((index < 0) || + (g_ble_ll_resolv_list[index].rl_addr_type != 1)) { + return 0; + } + peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr; + break; +#endif + default: + peer_addr = NULL; + break; + } + + rc = 0; + if (peer_addr) { + if (!memcmp(peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN)) { + rc = 1; + } + } + + return rc; +} + +static void +ble_ll_conn_connect_ind_txend_to_standby(void *arg) +{ + ble_ll_state_set(BLE_LL_STATE_STANDBY); +} + +static void +ble_ll_conn_connect_ind_txend_to_init(void *arg) +{ + ble_ll_state_set(BLE_LL_STATE_INITIATING); +} + +static uint8_t +ble_ll_conn_connect_ind_tx_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_conn_sm *connsm; + struct ble_ll_scan_pdu_data *pdu_data; + + connsm = pducb_arg; + /* + * pdu_data was prepared just before starting TX and is expected to be + * still valid here + */ + pdu_data = ble_ll_scan_get_pdu_data(); + + memcpy(dptr, pdu_data->inita, BLE_DEV_ADDR_LEN); + memcpy(dptr + BLE_DEV_ADDR_LEN, pdu_data->adva, BLE_DEV_ADDR_LEN); + + dptr += 2 * BLE_DEV_ADDR_LEN; + + put_le32(dptr, connsm->access_addr); + dptr[4] = (uint8_t)connsm->crcinit; + dptr[5] = (uint8_t)(connsm->crcinit >> 8); + dptr[6] = (uint8_t)(connsm->crcinit >> 16); + dptr[7] = connsm->tx_win_size; + put_le16(dptr + 8, connsm->tx_win_off); + put_le16(dptr + 10, connsm->conn_itvl); + put_le16(dptr + 12, connsm->slave_latency); + put_le16(dptr + 14, connsm->supervision_tmo); + memcpy(dptr + 16, &connsm->chanmap, BLE_LL_CONN_CHMAP_LEN); + dptr[21] = connsm->hop_inc | (connsm->master_sca << 5); + + *hdr_byte = pdu_data->hdr_byte; + + return 34; +} + +/** + * Send a connection requestion to an advertiser + * + * Context: Interrupt + * + * @param addr_type Address type of advertiser + * @param adva Address of advertiser + */ +int +ble_ll_conn_connect_ind_send(struct ble_ll_conn_sm *connsm, uint8_t end_trans) +{ + int rc; + + if (end_trans == BLE_PHY_TRANSITION_NONE) { + ble_phy_set_txend_cb(ble_ll_conn_connect_ind_txend_to_standby, NULL); + } else { + ble_phy_set_txend_cb(ble_ll_conn_connect_ind_txend_to_init, NULL); + } + + rc = ble_phy_tx(ble_ll_conn_connect_ind_tx_pducb, connsm, end_trans); + + return rc; +} + +/** + * Called when a schedule item overlaps the currently running connection + * event. This generally should not happen, but if it does we stop the + * current connection event to let the schedule item run. + * + * NOTE: the phy has been disabled as well as the wfr timer before this is + * called. + */ +void +ble_ll_conn_event_halt(void) +{ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + if (g_ble_ll_conn_cur_sm) { + g_ble_ll_conn_cur_sm->csmflags.cfbit.pkt_rxd = 0; + ble_ll_event_send(&g_ble_ll_conn_cur_sm->conn_ev_end); + g_ble_ll_conn_cur_sm = NULL; + } +} + +/** + * Process a received PDU while in the initiating state. + * + * Context: Link Layer task. + * + * @param pdu_type + * @param rxbuf + * @param ble_hdr + */ +void +ble_ll_init_rx_pkt_in(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *ble_hdr) +{ + uint8_t addr_type; + uint8_t *addr; + uint8_t *adv_addr; + uint8_t *inita; + uint8_t inita_type; + struct ble_ll_conn_sm *connsm; + int ext_adv_mode = -1; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data = NULL; + + if (ble_hdr->rxinfo.user_data) { + /* aux_data just a local helper, no need to ref + * as ble_hdr->rxinfo.user_data is unref in the end of this function + */ + aux_data = ble_hdr->rxinfo.user_data; + } +#endif + + /* Get the connection state machine we are trying to create */ + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (aux_data) { + ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data); + ble_hdr->rxinfo.user_data = NULL; + } +#endif + return; + } + + if (!BLE_MBUF_HDR_CRC_OK(ble_hdr)) { + goto scan_continue; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (BLE_MBUF_HDR_AUX_INVALID(ble_hdr)) { + goto scan_continue; + } + + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) { + if (BLE_MBUF_HDR_WAIT_AUX(ble_hdr)) { + /* Just continue scanning. We are waiting for AUX */ + if (!ble_ll_sched_aux_scan(ble_hdr, connsm->scansm, aux_data)) { + /* ref for aux ptr in the scheduler */ + ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data); + ble_hdr->rxinfo.user_data = NULL; + ble_ll_scan_chk_resume(); + return; + } + goto scan_continue; + } + } + + if (CONN_F_AUX_CONN_REQ(connsm)) { + if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) { + /* Wait for connection response, in this point of time aux is NULL */ + BLE_LL_ASSERT(ble_hdr->rxinfo.user_data == NULL); + return; + } + } +#endif + + /* If we have sent a connect request, we need to enter CONNECTION state */ + if (connsm && CONN_F_CONN_REQ_TXD(connsm)) { + /* Set address of advertiser to which we are connecting. */ + + if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr, + &adv_addr, &addr_type, + &inita, &inita_type, &ext_adv_mode)) { + /* Something got wrong, keep trying to connect */ + goto scan_continue; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* + * Did we resolve this address? If so, set correct peer address + * and peer address type. + */ + if (connsm->rpa_index >= 0) { + addr_type = g_ble_ll_resolv_list[connsm->rpa_index].rl_addr_type + 2; + addr = g_ble_ll_resolv_list[connsm->rpa_index].rl_identity_addr; + } else { + addr = adv_addr; + } +#else + addr = adv_addr; +#endif + + if (connsm->rpa_index >= 0) { + connsm->peer_addr_type = addr_type; + memcpy(connsm->peer_addr, addr, BLE_DEV_ADDR_LEN); + + ble_ll_scan_set_peer_rpa(adv_addr); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* Update resolving list with current peer RPA */ + ble_ll_resolv_set_peer_rpa(connsm->rpa_index, rxbuf + BLE_LL_PDU_HDR_LEN); + if (ble_ll_is_rpa(inita, inita_type)) { + ble_ll_resolv_set_local_rpa(connsm->rpa_index, inita); + } + +#endif + } else if (ble_ll_scan_whitelist_enabled()) { + /* if WL is used we need to store peer addr also if it was not + * resolved + */ + connsm->peer_addr_type = addr_type; + memcpy(connsm->peer_addr, addr, BLE_DEV_ADDR_LEN); + } + + /* Connection has been created. Stop scanning */ + g_ble_ll_conn_create_sm = NULL; + ble_ll_scan_sm_stop(0); + + /* For AUX Connect CSA2 is mandatory. Otherwise we need to check bit + * mask + */ + if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) { + ble_ll_conn_set_csa(connsm, 1); + } else { + ble_ll_conn_set_csa(connsm, rxbuf[0] & BLE_ADV_PDU_HDR_CHSEL_MASK); + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* Lets take last used phy */ + ble_ll_conn_init_phy(connsm, ble_hdr->rxinfo.phy); +#endif + if (aux_data) { + ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data); + ble_hdr->rxinfo.user_data = NULL; + } +#endif + ble_ll_conn_created(connsm, NULL); + return; + } + +scan_continue: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* Drop last reference and keep continue to connect */ + if (aux_data) { + ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data); + ble_hdr->rxinfo.user_data = NULL; + } +#endif + ble_ll_scan_chk_resume(); +} + +/** + * Called when a receive PDU has started and we are in the initiating state. + * + * Context: Interrupt + * + * @param pdu_type + * @param ble_hdr + * + * @return int + * 0: we will not attempt to reply to this frame + * 1: we may send a response to this frame. + */ +int +ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr) +{ + struct ble_ll_conn_sm *connsm; + + connsm = g_ble_ll_conn_create_sm; + if (!connsm) { + return 0; + } + + if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || + (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND || + pdu_type == BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP)) { + return 1; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && + connsm->scansm->ext_scanning) { + if (connsm->scansm->cur_aux_data) { + STATS_INC(ble_ll_stats, aux_received); + } + + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_EXT_ADV; + return 1; + } +#endif + + return 0; +} + +/** + * Called when a receive PDU has ended and we are in the initiating state. + * + * Context: Interrupt + * + * @param rxpdu + * @param crcok + * @param ble_hdr + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok, + struct ble_mbuf_hdr *ble_hdr) +{ + int rc; + int resolved; + int chk_wl; + int index; + uint8_t pdu_type; + uint8_t adv_addr_type; + uint8_t peer_addr_type; + uint8_t *adv_addr = NULL; + uint8_t *peer; + uint8_t *init_addr = NULL; + uint8_t init_addr_type; + uint8_t pyld_len; + uint8_t inita_is_rpa; + uint8_t conn_req_end_trans; + struct os_mbuf *rxpdu; + struct ble_ll_conn_sm *connsm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + struct ble_ll_resolv_entry *rl; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_scan_sm *scansm; + uint8_t phy; +#endif + int ext_adv_mode = -1; + + /* Get connection state machine to use if connection to be established */ + connsm = g_ble_ll_conn_create_sm; + /* This could happen if connection initialize was cancelled while isr end was + * already pending + */ + if (!connsm) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + return -1; + } + + rc = -1; + pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; + pyld_len = rxbuf[1]; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + scansm = connsm->scansm; + if (scansm->cur_aux_data) { + ble_hdr->rxinfo.user_data = scansm->cur_aux_data; + scansm->cur_aux_data = NULL; + } +#endif + + if (!crcok) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* Invalid packet - make sure we do not wait for AUX_CONNECT_RSP */ + ble_ll_conn_reset_pending_aux_conn_rsp(); +#endif + + /* Ignore this packet */ + goto init_rx_isr_exit; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* If we sent AUX_CONNECT_REQ, we only expect AUX_CONNECT_RSP here */ + if (CONN_F_AUX_CONN_REQ(connsm)) { + if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) { + STATS_INC(ble_ll_stats, aux_conn_rsp_err); + CONN_F_CONN_REQ_TXD(connsm) = 0; + CONN_F_AUX_CONN_REQ(connsm) = 0; + ble_ll_sched_rmv_elem(&connsm->conn_sch); + } + goto init_rx_isr_exit; + } +#endif + + inita_is_rpa = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) { + if (!scansm->ext_scanning) { + goto init_rx_isr_exit; + } + + rc = ble_ll_scan_update_aux_data(ble_hdr, rxbuf, NULL); + if (rc < 0) { + /* No memory or broken packet */ + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID; + goto init_rx_isr_exit; + } + } +#endif + + /* Lets get addresses from advertising report*/ + if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr, + &adv_addr, &adv_addr_type, + &init_addr, &init_addr_type, + &ext_adv_mode)) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID; +#endif + goto init_rx_isr_exit; + } + + switch (pdu_type) { + case BLE_ADV_PDU_TYPE_ADV_IND: + break; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_ADV_PDU_TYPE_ADV_EXT_IND: + rc = -1; + + /* If this is not connectable adv mode, lets skip it */ + if (!(ext_adv_mode & BLE_LL_EXT_ADV_MODE_CONN)) { + goto init_rx_isr_exit; + } + + if (!adv_addr) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT; + goto init_rx_isr_exit; + } + + if (!init_addr) { + break; + } + /* if there is direct address lets fall down and check it.*/ + // no break +#endif + case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: + inita_is_rpa = (uint8_t)ble_ll_is_rpa(init_addr, init_addr_type); + if (!inita_is_rpa) { + + /* Resolving will be done later. Check if identity InitA matches */ + if (!ble_ll_is_our_devaddr(init_addr, init_addr_type)) { + goto init_rx_isr_exit; + } + } +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + else { + /* If privacy is off - reject RPA InitA*/ + goto init_rx_isr_exit; + } +#endif + + break; + default: + goto init_rx_isr_exit; + } + + /* Should we send a connect request? */ + index = -1; + peer = adv_addr; + peer_addr_type = adv_addr_type; + + resolved = 0; + chk_wl = ble_ll_scan_whitelist_enabled(); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (ble_ll_is_rpa(adv_addr, adv_addr_type) && ble_ll_resolv_enabled()) { + index = ble_hw_resolv_list_match(); + if (index >= 0) { + rl = &g_ble_ll_resolv_list[index]; + + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED; + connsm->rpa_index = index; + peer = rl->rl_identity_addr; + peer_addr_type = rl->rl_addr_type; + resolved = 1; + + /* Assure privacy */ + if ((rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && init_addr && + !inita_is_rpa && rl->rl_has_local) { + goto init_rx_isr_exit; + } + + /* + * If the InitA is a RPA, we must see if it resolves based on the + * identity address of the resolved ADVA. + */ + if (init_addr && inita_is_rpa) { + if (!ble_ll_resolv_rpa(init_addr, + g_ble_ll_resolv_list[index].rl_local_irk)) { + goto init_rx_isr_exit; + } + + /* Core Specification Vol 6, Part B, Section 6.4: + * "The Link Layer should not set the InitA field to the same + * value as the TargetA field in the received advertising PDU." + * + * We update the received PDU directly here, so ble_ll_init_rx_pkt_in + * can process it as is. + */ + memcpy(init_addr, rl->rl_local_rpa, BLE_DEV_ADDR_LEN); + } + + } else { + if (chk_wl) { + goto init_rx_isr_exit; + } + + /* Could not resolved InitA */ + if (init_addr && inita_is_rpa) { + goto init_rx_isr_exit; + } + } + } else if (init_addr) { + + /* If resolving is off and InitA is RPA we reject advertising */ + if (inita_is_rpa && !ble_ll_resolv_enabled()) { + goto init_rx_isr_exit; + } + + /* Let's see if we have IRK with that peer.*/ + rl = ble_ll_resolv_list_find(adv_addr, adv_addr_type); + + /* Lets make sure privacy mode is correct together with InitA in case it + * is identity address + */ + if (rl && !inita_is_rpa && + (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && + rl->rl_has_local) { + goto init_rx_isr_exit; + } + + /* + * If the InitA is a RPA, we must see if it resolves based on the + * identity address of the resolved ADVA. + */ + if (inita_is_rpa) { + if (!rl || !ble_ll_resolv_rpa(init_addr, rl->rl_local_irk)) { + goto init_rx_isr_exit; + } + + /* Core Specification Vol 6, Part B, Section 6.4: + * "The Link Layer should not set the InitA field to the same + * value as the TargetA field in the received advertising PDU." + * + * We update the received PDU directly here, so ble_ll_init_rx_pkt_in + * can process it as is. + */ + memcpy(init_addr, rl->rl_local_rpa, BLE_DEV_ADDR_LEN); + } + } +#endif + + /* Check filter policy */ + if (chk_wl) { + if (!ble_ll_whitelist_match(peer, peer_addr_type, resolved)) { + goto init_rx_isr_exit; + } + } else { + /* Must match the connection address */ + if (!ble_ll_conn_is_peer_adv(adv_addr_type, adv_addr, index)) { + goto init_rx_isr_exit; + } + } + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH; + + /* For CONNECT_IND we don't go into RX state */ + conn_req_end_trans = BLE_PHY_TRANSITION_NONE; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* Check if we should send AUX_CONNECT_REQ and wait for AUX_CONNECT_RSP */ + if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) { + conn_req_end_trans = BLE_PHY_TRANSITION_TX_RX; + } + + if (connsm->scansm->ext_scanning) { + phy = ble_hdr->rxinfo.phy; + + /* Update connection state machine with appropriate parameters for + * certain PHY + */ + ble_ll_conn_ext_set_params(connsm, + &connsm->initial_params.params[phy - 1], + phy); + + } +#endif + + /* Schedule new connection */ + if (ble_ll_sched_master_new(connsm, ble_hdr, pyld_len)) { + STATS_INC(ble_ll_conn_stats, cant_set_sched); + goto init_rx_isr_exit; + } + + /* Prepare data for connect request */ + ble_ll_conn_connect_ind_prepare(connsm, + ble_ll_scan_get_pdu_data(), + adv_addr_type, adv_addr, + init_addr_type, init_addr, + index, ble_hdr->rxinfo.channel); + + /* Setup to transmit the connect request */ + rc = ble_ll_conn_connect_ind_send(connsm, conn_req_end_trans); + if (rc) { + ble_ll_sched_rmv_elem(&connsm->conn_sch); + goto init_rx_isr_exit; + } + + if (init_addr && !inita_is_rpa) { + connsm->inita_identity_used = 1; + } + + CONN_F_CONN_REQ_TXD(connsm) = 1; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) { + /* Lets wait for AUX_CONNECT_RSP */ + CONN_F_AUX_CONN_REQ(connsm) = 1; + /* Keep aux data until we get scan response */ + scansm->cur_aux_data = ble_hdr->rxinfo.user_data; + ble_hdr->rxinfo.user_data = NULL; + STATS_INC(ble_ll_stats, aux_conn_req_tx); + } +#endif + + STATS_INC(ble_ll_conn_stats, conn_req_txd); + +init_rx_isr_exit: + + /* + * We have to restart receive if we cant hand up pdu. We return 0 so that + * the phy does not get disabled. + */ + rxpdu = ble_ll_rxpdu_alloc(pyld_len + BLE_LL_PDU_HDR_LEN); + if (rxpdu == NULL) { + /* + * XXX: possible allocate the PDU when we start initiating? + * I cannot say I like this solution, but if we cannot allocate a PDU + * to hand up to the LL, we need to remove the connection we just + * scheduled since the connection state machine will not get processed + * by link layer properly. For now, just remove it from the scheduler + */ + if (CONN_F_CONN_REQ_TXD(connsm) == 1) { + CONN_F_CONN_REQ_TXD(connsm) = 0; + CONN_F_AUX_CONN_REQ(connsm) = 0; + ble_ll_sched_rmv_elem(&connsm->conn_sch); + } + ble_phy_restart_rx(); + rc = 0; + } else { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + ble_ll_rx_pdu_in(rxpdu); + } + + if (rc) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + + return rc; +} + +/** + * Function called when a timeout has occurred for a connection. There are + * two types of timeouts: a connection supervision timeout and control + * procedure timeout. + * + * Context: Link Layer task + * + * @param connsm + * @param ble_err + */ +void +ble_ll_conn_timeout(struct ble_ll_conn_sm *connsm, uint8_t ble_err) +{ + int was_current; + os_sr_t sr; + + was_current = 0; + OS_ENTER_CRITICAL(sr); + if (g_ble_ll_conn_cur_sm == connsm) { + ble_ll_conn_current_sm_over(NULL); + was_current = 1; + } + OS_EXIT_CRITICAL(sr); + + /* Check if we need to resume scanning */ + if (was_current) { + ble_ll_scan_chk_resume(); + } + + ble_ll_conn_end(connsm, ble_err); +} + +/** + * Called when a data channel PDU has started that matches the access + * address of the current connection. Note that the CRC of the PDU has not + * been checked yet. + * + * Context: Interrupt + * + * @param rxhdr + */ +int +ble_ll_conn_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa) +{ + struct ble_ll_conn_sm *connsm; + + /* + * Disable wait for response timer since we receive a response. We dont + * care if this is the response we were waiting for or not; the code + * called at receive end will deal with ending the connection event + * if needed + */ + connsm = g_ble_ll_conn_cur_sm; + if (connsm) { + /* Double check access address. Better match connection state machine */ + if (aa != connsm->access_addr) { + STATS_INC(ble_ll_conn_stats, rx_data_pdu_bad_aa); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_event_send(&connsm->conn_ev_end); + g_ble_ll_conn_cur_sm = NULL; + return -1; + } + + /* Set connection handle in mbuf header */ + rxhdr->rxinfo.handle = connsm->conn_handle; + + /* Set flag denoting we have received a packet in connection event */ + connsm->csmflags.cfbit.pkt_rxd = 1; + + /* Connection is established */ + connsm->conn_state = BLE_LL_CONN_STATE_ESTABLISHED; + + /* Set anchor point (and last) if 1st rxd frame in connection event */ + if (connsm->csmflags.cfbit.slave_set_last_anchor) { + connsm->csmflags.cfbit.slave_set_last_anchor = 0; + connsm->last_anchor_point = rxhdr->beg_cputime; + connsm->anchor_point = connsm->last_anchor_point; + connsm->anchor_point_usecs = rxhdr->rem_usecs; + } + } + return 1; +} + +/** + * Called from the Link Layer task when a data PDU has been received + * + * Context: Link layer task + * + * @param rxpdu Pointer to received pdu + * @param rxpdu Pointer to ble mbuf header of received pdu + */ +void +ble_ll_conn_rx_data_pdu(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) +{ + uint8_t hdr_byte; + uint8_t rxd_sn; + uint8_t *rxbuf; + uint8_t llid; + uint16_t acl_len; + uint16_t acl_hdr; + struct ble_ll_conn_sm *connsm; + + if (BLE_MBUF_HDR_CRC_OK(hdr)) { + /* XXX: there is a chance that the connection was thrown away and + re-used before processing packets here. Fix this. */ + /* We better have a connection state machine */ + connsm = ble_ll_conn_find_active_conn(hdr->rxinfo.handle); + if (connsm) { + /* Check state machine */ + ble_ll_conn_chk_csm_flags(connsm); + + /* Validate rx data pdu */ + rxbuf = rxpdu->om_data; + hdr_byte = rxbuf[0]; + acl_len = rxbuf[1]; + llid = hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + + /* + * Check that the LLID and payload length are reasonable. + * Empty payload is only allowed for LLID == 01b. + * */ + if ((llid == 0) || + ((acl_len == 0) && (llid != BLE_LL_LLID_DATA_FRAG))) { + STATS_INC(ble_ll_conn_stats, rx_bad_llid); + goto conn_rx_data_pdu_end; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* Check if PDU is allowed when encryption is started. If not, + * terminate connection. + * + * Reference: Core 5.0, Vol 6, Part B, 5.1.3.1 + */ + if ((connsm->enc_data.enc_state > CONN_ENC_S_PAUSE_ENC_RSP_WAIT) && + !ble_ll_ctrl_enc_allowed_pdu_rx(rxpdu)) { + ble_ll_conn_timeout(connsm, BLE_ERR_CONN_TERM_MIC); + goto conn_rx_data_pdu_end; + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + /* + * Reset authenticated payload timeout if valid MIC. NOTE: we dont + * check the MIC failure bit as that would have terminated the + * connection + */ + if ((connsm->enc_data.enc_state == CONN_ENC_S_ENCRYPTED) && + CONN_F_LE_PING_SUPP(connsm) && (acl_len != 0)) { + ble_ll_conn_auth_pyld_timer_start(connsm); + } +#endif + + /* Update RSSI */ + connsm->conn_rssi = hdr->rxinfo.rssi; + + /* + * If we are a slave, we can only start to use slave latency + * once we have received a NESN of 1 from the master + */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + if (hdr_byte & BLE_LL_DATA_HDR_NESN_MASK) { + connsm->csmflags.cfbit.allow_slave_latency = 1; + } + } + + /* + * Discard the received PDU if the sequence number is the same + * as the last received sequence number + */ + rxd_sn = hdr_byte & BLE_LL_DATA_HDR_SN_MASK; + if (rxd_sn != connsm->last_rxd_sn) { + /* Update last rxd sn */ + connsm->last_rxd_sn = rxd_sn; + + /* No need to do anything if empty pdu */ + if ((llid == BLE_LL_LLID_DATA_FRAG) && (acl_len == 0)) { + goto conn_rx_data_pdu_end; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* + * XXX: should we check to see if we are in a state where we + * might expect to get an encrypted PDU? + */ + if (BLE_MBUF_HDR_MIC_FAILURE(hdr)) { + STATS_INC(ble_ll_conn_stats, mic_failures); + ble_ll_conn_timeout(connsm, BLE_ERR_CONN_TERM_MIC); + goto conn_rx_data_pdu_end; + } +#endif + + if (llid == BLE_LL_LLID_CTRL) { + /* Process control frame */ + STATS_INC(ble_ll_conn_stats, rx_ctrl_pdus); + if (ble_ll_ctrl_rx_pdu(connsm, rxpdu)) { + STATS_INC(ble_ll_conn_stats, rx_malformed_ctrl_pdus); + } + } else { + /* Count # of received l2cap frames and byes */ + STATS_INC(ble_ll_conn_stats, rx_l2cap_pdus); + STATS_INCN(ble_ll_conn_stats, rx_l2cap_bytes, acl_len); + + /* NOTE: there should be at least two bytes available */ + BLE_LL_ASSERT(OS_MBUF_LEADINGSPACE(rxpdu) >= 2); + os_mbuf_prepend(rxpdu, 2); + rxbuf = rxpdu->om_data; + + acl_hdr = (llid << 12) | connsm->conn_handle; + put_le16(rxbuf, acl_hdr); + put_le16(rxbuf + 2, acl_len); + ble_hci_trans_ll_acl_tx(rxpdu); + } + + /* NOTE: we dont free the mbuf since we handed it off! */ + return; + } else { + STATS_INC(ble_ll_conn_stats, data_pdu_rx_dup); + } + } else { + STATS_INC(ble_ll_conn_stats, no_conn_sm); + } + } + + /* Free buffer */ +conn_rx_data_pdu_end: + os_mbuf_free_chain(rxpdu); +} + +/** + * Called when a packet has been received while in the connection state. + * + * Context: Interrupt + * + * @param rxpdu + * @param crcok + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + int rc; + int is_ctrl; + uint8_t hdr_byte; + uint8_t hdr_sn; + uint8_t hdr_nesn; + uint8_t conn_sn; + uint8_t conn_nesn; + uint8_t reply; + uint8_t rem_bytes; + uint8_t opcode = 0; + uint8_t rx_pyld_len; + uint32_t begtime; + uint32_t add_usecs; + struct os_mbuf *txpdu; + struct ble_ll_conn_sm *connsm; + struct os_mbuf *rxpdu; + struct ble_mbuf_hdr *txhdr; + int rx_phy_mode; + + /* Retrieve the header and payload length */ + hdr_byte = rxbuf[0]; + rx_pyld_len = rxbuf[1]; + + /* + * We need to attempt to allocate a buffer here. The reason we do this + * now is that we should not ack the packet if we have no receive + * buffers available. We want to free up our transmit PDU if it was + * acked, but we should not ack the received frame if we cant hand it up. + * NOTE: we hand up empty pdu's to the LL task! + */ + rxpdu = ble_ll_rxpdu_alloc(rx_pyld_len + BLE_LL_PDU_HDR_LEN); + + /* + * We should have a current connection state machine. If we dont, we just + * hand the packet to the higher layer to count it. + */ + rc = -1; + connsm = g_ble_ll_conn_cur_sm; + if (!connsm) { + STATS_INC(ble_ll_conn_stats, rx_data_pdu_no_conn); + goto conn_exit; + } + + /* + * Calculate the end time of the received PDU. NOTE: this looks strange + * but for the 32768 crystal we add the time it takes to send the packet + * to the 'additional usecs' field to save some calculations. + */ + begtime = rxhdr->beg_cputime; +#if BLE_LL_BT5_PHY_SUPPORTED + rx_phy_mode = connsm->phy_data.rx_phy_mode; +#else + rx_phy_mode = BLE_PHY_MODE_1M; +#endif + add_usecs = rxhdr->rem_usecs + + ble_ll_pdu_tx_time_get(rx_pyld_len, rx_phy_mode); + + /* + * Check the packet CRC. A connection event can continue even if the + * received PDU does not pass the CRC check. If we receive two consecutive + * CRC errors we end the conection event. + */ + if (!BLE_MBUF_HDR_CRC_OK(rxhdr)) { + /* + * Increment # of consecutively received CRC errors. If more than + * one we will end the connection event. + */ + ++connsm->cons_rxd_bad_crc; + if (connsm->cons_rxd_bad_crc >= 2) { + reply = 0; + } else { + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + reply = CONN_F_LAST_TXD_MD(connsm); + } else { + /* A slave always responds with a packet */ + reply = 1; + } + } + } else { + /* Reset consecutively received bad crcs (since this one was good!) */ + connsm->cons_rxd_bad_crc = 0; + + /* Set last valid received pdu time (resets supervision timer) */ + connsm->last_rxd_pdu_cputime = begtime + + os_cputime_usecs_to_ticks(add_usecs); + + /* + * Check for valid LLID before proceeding. We have seen some weird + * things with the PHY where the CRC is OK but we dont have a valid + * LLID. This should really never happen but if it does we will just + * bail. An error stat will get incremented at the LL. + */ + if ((hdr_byte & BLE_LL_DATA_HDR_LLID_MASK) == 0) { + goto conn_exit; + } + + /* Set last received header byte */ + connsm->last_rxd_hdr_byte = hdr_byte; + + is_ctrl = 0; + if ((hdr_byte & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) { + is_ctrl = 1; + opcode = rxbuf[2]; + } + + /* + * If SN bit from header does not match NESN in connection, this is + * a resent PDU and should be ignored. + */ + hdr_sn = hdr_byte & BLE_LL_DATA_HDR_SN_MASK; + conn_nesn = connsm->next_exp_seqnum; + if (rxpdu && ((hdr_sn && conn_nesn) || (!hdr_sn && !conn_nesn))) { + connsm->next_exp_seqnum ^= 1; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (CONN_F_ENCRYPTED(connsm) && !ble_ll_conn_is_empty_pdu(rxbuf)) { + ++connsm->enc_data.rx_pkt_cntr; + } +#endif + } + + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_CONN_RX, connsm->tx_seqnum, + !!(hdr_byte & BLE_LL_DATA_HDR_NESN_MASK)); + + /* + * Check NESN bit from header. If same as tx seq num, the transmission + * is acknowledged. Otherwise we need to resend this PDU. + */ + if (CONN_F_EMPTY_PDU_TXD(connsm) || connsm->cur_tx_pdu) { + hdr_nesn = hdr_byte & BLE_LL_DATA_HDR_NESN_MASK; + conn_sn = connsm->tx_seqnum; + if ((hdr_nesn && conn_sn) || (!hdr_nesn && !conn_sn)) { + /* We did not get an ACK. Must retry the PDU */ + STATS_INC(ble_ll_conn_stats, data_pdu_txf); + } else { + /* Transmit success */ + connsm->tx_seqnum ^= 1; + STATS_INC(ble_ll_conn_stats, data_pdu_txg); + + /* If we transmitted the empty pdu, clear flag */ + if (CONN_F_EMPTY_PDU_TXD(connsm)) { + CONN_F_EMPTY_PDU_TXD(connsm) = 0; + goto chk_rx_terminate_ind; + } + + /* + * Determine if we should remove packet from queue or if there + * are more fragments to send. + */ + txpdu = connsm->cur_tx_pdu; + if (txpdu) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->enc_data.tx_encrypted) { + ++connsm->enc_data.tx_pkt_cntr; + } +#endif + txhdr = BLE_MBUF_HDR_PTR(txpdu); + if ((txhdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK) + == BLE_LL_LLID_CTRL) { + connsm->cur_tx_pdu = NULL; + /* Note: the mbuf is freed by this call */ + rc = ble_ll_ctrl_tx_done(txpdu, connsm); + if (rc) { + /* Means we transmitted a TERMINATE_IND */ + goto conn_exit; + } else { + goto chk_rx_terminate_ind; + } + } + + /* Increment offset based on number of bytes sent */ + txhdr->txinfo.offset += txhdr->txinfo.pyld_len; + if (txhdr->txinfo.offset >= OS_MBUF_PKTLEN(txpdu)) { + /* If l2cap pdu, increment # of completed packets */ + if (txhdr->txinfo.pyld_len != 0) { +#if (BLETEST_THROUGHPUT_TEST == 1) + bletest_completed_pkt(connsm->conn_handle); +#endif + ++connsm->completed_pkts; + if (connsm->completed_pkts > 2) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, + &g_ble_ll_data.ll_comp_pkt_ev); + } + } + os_mbuf_free_chain(txpdu); + connsm->cur_tx_pdu = NULL; + } else { + rem_bytes = OS_MBUF_PKTLEN(txpdu) - txhdr->txinfo.offset; + /* Adjust payload for max TX time and octets */ + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + if (is_ctrl && + (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && + (opcode == BLE_LL_CTRL_PHY_UPDATE_IND)) { + connsm->phy_tx_transition = + ble_ll_ctrl_phy_tx_transition_get(rxbuf[3]); + } +#endif + + rem_bytes = ble_ll_conn_adjust_pyld_len(connsm, rem_bytes); + txhdr->txinfo.pyld_len = rem_bytes; + } + } + } + } + + /* Should we continue connection event? */ + /* If this is a TERMINATE_IND, we have to reply */ +chk_rx_terminate_ind: + /* If we received a terminate IND, we must set some flags */ + if (is_ctrl && (opcode == BLE_LL_CTRL_TERMINATE_IND) + && (rx_pyld_len == (1 + BLE_LL_CTRL_TERMINATE_IND_LEN))) { + connsm->csmflags.cfbit.terminate_ind_rxd = 1; + connsm->rxd_disconnect_reason = rxbuf[3]; + } + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + reply = CONN_F_LAST_TXD_MD(connsm) || (hdr_byte & BLE_LL_DATA_HDR_MD_MASK); + } else { + /* A slave always replies */ + reply = 1; + } + } + + /* If reply flag set, send data pdu and continue connection event */ + rc = -1; + if (rx_pyld_len && CONN_F_ENCRYPTED(connsm)) { + rx_pyld_len += BLE_LL_DATA_MIC_LEN; + } + if (reply && ble_ll_conn_can_send_next_pdu(connsm, begtime, add_usecs)) { + rc = ble_ll_conn_tx_pdu(connsm); + } + +conn_exit: + /* Copy the received pdu and hand it up */ + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + ble_ll_rx_pdu_in(rxpdu); + } + + /* Send link layer a connection end event if over */ + if (rc) { + ble_ll_conn_current_sm_over(connsm); + } + + return rc; +} + +/** + * Called to adjust payload length to fit into max effective octets and TX time + * on current PHY. + */ +/** + * Called to enqueue a packet on the transmit queue of a connection. Should + * only be called by the controller. + * + * Context: Link Layer + * + * + * @param connsm + * @param om + */ +void +ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om, + uint8_t hdr_byte, uint8_t length) +{ + os_sr_t sr; + struct os_mbuf_pkthdr *pkthdr; + struct ble_mbuf_hdr *ble_hdr; + int lifo; + + /* Set mbuf length and packet length if a control PDU */ + if (hdr_byte == BLE_LL_LLID_CTRL) { + om->om_len = length; + OS_MBUF_PKTHDR(om)->omp_len = length; + } + + /* Set BLE transmit header */ + ble_hdr = BLE_MBUF_HDR_PTR(om); + ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.offset = 0; + ble_hdr->txinfo.hdr_byte = hdr_byte; + + /* + * Initial payload length is calculate when packet is dequeued, there's no + * need to do this now. + */ + + lifo = 0; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->enc_data.enc_state > CONN_ENC_S_ENCRYPTED) { + uint8_t llid; + + /* + * If this is one of the following types we need to insert it at + * head of queue. + */ + llid = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + if (llid == BLE_LL_LLID_CTRL) { + switch (om->om_data[0]) { + case BLE_LL_CTRL_TERMINATE_IND: + case BLE_LL_CTRL_REJECT_IND: + case BLE_LL_CTRL_REJECT_IND_EXT: + case BLE_LL_CTRL_START_ENC_REQ: + case BLE_LL_CTRL_START_ENC_RSP: + lifo = 1; + break; + case BLE_LL_CTRL_PAUSE_ENC_RSP: + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + lifo = 1; + } + break; + case BLE_LL_CTRL_ENC_REQ: + case BLE_LL_CTRL_ENC_RSP: + /* If encryption has been paused, we don't want to send any packets from the + * TX queue, as they would go unencrypted. + */ + if (connsm->enc_data.enc_state == CONN_ENC_S_PAUSED) { + lifo = 1; + } + break; + default: + break; + } + } + } +#endif + + /* Add to transmit queue for the connection */ + pkthdr = OS_MBUF_PKTHDR(om); + OS_ENTER_CRITICAL(sr); + if (lifo) { + STAILQ_INSERT_HEAD(&connsm->conn_txq, pkthdr, omp_next); + } else { + STAILQ_INSERT_TAIL(&connsm->conn_txq, pkthdr, omp_next); + } + OS_EXIT_CRITICAL(sr); +} + +/** + * Data packet from host. + * + * Context: Link Layer task + * + * @param om + * @param handle + * @param length + * + * @return int + */ +void +ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t length) +{ + uint8_t hdr_byte; + uint16_t conn_handle; + uint16_t pb; + struct ble_ll_conn_sm *connsm; + + /* See if we have an active matching connection handle */ + conn_handle = handle & 0x0FFF; + connsm = ble_ll_conn_find_active_conn(conn_handle); + if (connsm) { + /* Construct LL header in buffer (NOTE: pb already checked) */ + pb = handle & 0x3000; + if (pb == 0) { + hdr_byte = BLE_LL_LLID_DATA_START; + } else { + hdr_byte = BLE_LL_LLID_DATA_FRAG; + } + + /* Add to total l2cap pdus enqueue */ + STATS_INC(ble_ll_conn_stats, l2cap_enqueued); + + /* Clear flags field in BLE header */ + ble_ll_conn_enqueue_pkt(connsm, om, hdr_byte, length); + } else { + /* No connection found! */ + STATS_INC(ble_ll_conn_stats, handle_not_found); + os_mbuf_free_chain(om); + } +} + +/** + * Called to set the global channel mask that we use for all connections. + * + * @param num_used_chans + * @param chanmap + */ +void +ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, const uint8_t *chanmap) +{ + struct ble_ll_conn_sm *connsm; + struct ble_ll_conn_global_params *conn_params; + + /* Do nothing if same channel map */ + conn_params = &g_ble_ll_conn_params; + if (!memcmp(conn_params->master_chan_map, chanmap, BLE_LL_CONN_CHMAP_LEN)) { + return; + } + + /* Change channel map and cause channel map update procedure to start */ + conn_params->num_used_chans = num_used_chans; + memcpy(conn_params->master_chan_map, chanmap, BLE_LL_CONN_CHMAP_LEN); + + /* Perform channel map update */ + SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_CHAN_MAP_UPD); + } + } +} + +/** + * Called when a device has received a connect request while advertising and + * the connect request has passed the advertising filter policy and is for + * us. This will start a connection in the slave role assuming that we dont + * already have a connection with this device and that the connect request + * parameters are valid. + * + * Context: Link Layer + * + * @param rxbuf Pointer to received Connect Request PDU + * + * @return 0: connection not started; 1 connecton started + */ +int +ble_ll_conn_slave_start(uint8_t *rxbuf, uint8_t pat, struct ble_mbuf_hdr *rxhdr, + bool force_csa2) +{ + int rc; + uint32_t temp; + uint32_t crcinit; + uint8_t *inita; + uint8_t *dptr; + struct ble_ll_conn_sm *connsm; + + /* Ignore the connection request if we are already connected*/ + inita = rxbuf + BLE_LL_PDU_HDR_LEN; + SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) { + if (!memcmp(&connsm->peer_addr, inita, BLE_DEV_ADDR_LEN)) { + if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) { + if (connsm->peer_addr_type & 1) { + return 0; + } + } else { + if ((connsm->peer_addr_type & 1) == 0) { + return 0; + } + } + } + } + + /* Allocate a connection. If none available, dont do anything */ + connsm = ble_ll_conn_sm_get(); + if (connsm == NULL) { + return 0; + } + + /* Set the pointer at the start of the connection data */ + dptr = rxbuf + BLE_LL_CONN_REQ_ADVA_OFF + BLE_DEV_ADDR_LEN; + + /* Set connection state machine information */ + connsm->access_addr = get_le32(dptr); + crcinit = dptr[6]; + crcinit = (crcinit << 8) | dptr[5]; + crcinit = (crcinit << 8) | dptr[4]; + connsm->crcinit = crcinit; + connsm->tx_win_size = dptr[7]; + connsm->tx_win_off = get_le16(dptr + 8); + connsm->conn_itvl = get_le16(dptr + 10); + connsm->slave_latency = get_le16(dptr + 12); + connsm->supervision_tmo = get_le16(dptr + 14); + memcpy(&connsm->chanmap, dptr + 16, BLE_LL_CONN_CHMAP_LEN); + connsm->hop_inc = dptr[21] & 0x1F; + connsm->master_sca = dptr[21] >> 5; + + /* Error check parameters */ + if ((connsm->tx_win_off > connsm->conn_itvl) || + (connsm->conn_itvl < BLE_HCI_CONN_ITVL_MIN) || + (connsm->conn_itvl > BLE_HCI_CONN_ITVL_MAX) || + (connsm->tx_win_size < BLE_LL_CONN_TX_WIN_MIN) || + (connsm->slave_latency > BLE_LL_CONN_SLAVE_LATENCY_MAX)) { + goto err_slave_start; + } + + /* Slave latency cannot cause a supervision timeout */ + temp = (connsm->slave_latency + 1) * (connsm->conn_itvl * 2) * + BLE_LL_CONN_ITVL_USECS; + if ((connsm->supervision_tmo * 10000) <= temp ) { + goto err_slave_start; + } + + /* + * The transmit window must be less than or equal to the lesser of 10 + * msecs or the connection interval minus 1.25 msecs. + */ + temp = connsm->conn_itvl - 1; + if (temp > 8) { + temp = 8; + } + if (connsm->tx_win_size > temp) { + goto err_slave_start; + } + + /* Set the address of device that we are connecting with */ + memcpy(&connsm->peer_addr, inita, BLE_DEV_ADDR_LEN); + connsm->peer_addr_type = pat; + + /* Calculate number of used channels; make sure it meets min requirement */ + connsm->num_used_chans = ble_ll_utils_calc_num_used_chans(connsm->chanmap); + if (connsm->num_used_chans < 2) { + goto err_slave_start; + } + + /* Start the connection state machine */ + connsm->conn_role = BLE_LL_CONN_ROLE_SLAVE; + ble_ll_conn_sm_new(connsm); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + /* Use the same PHY as we received CONNECT_REQ on */ + ble_ll_conn_init_phy(connsm, rxhdr->rxinfo.phy); +#endif + + ble_ll_conn_set_csa(connsm, + force_csa2 || (rxbuf[0] & BLE_ADV_PDU_HDR_CHSEL_MASK)); + + /* Set initial schedule callback */ + connsm->conn_sch.sched_cb = ble_ll_conn_event_start_cb; + rc = ble_ll_conn_created(connsm, rxhdr); + if (!rc) { + SLIST_REMOVE(&g_ble_ll_conn_active_list, connsm, ble_ll_conn_sm, act_sle); + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + } + return rc; + +err_slave_start: + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + STATS_INC(ble_ll_conn_stats, slave_rxd_bad_conn_req_params); + return 0; +} + +#define MAX_TIME_UNCODED(_maxbytes) \ + ble_ll_pdu_tx_time_get(_maxbytes + BLE_LL_DATA_MIC_LEN, \ + BLE_PHY_MODE_1M); +#define MAX_TIME_CODED(_maxbytes) \ + ble_ll_pdu_tx_time_get(_maxbytes + BLE_LL_DATA_MIC_LEN, \ + BLE_PHY_MODE_CODED_125KBPS); + +/** + * Called to reset the connection module. When this function is called the + * scheduler has been stopped and the phy has been disabled. The LL should + * be in the standby state. + * + * Context: Link Layer task + */ +void +ble_ll_conn_module_reset(void) +{ + uint8_t max_phy_pyld; + uint16_t maxbytes; + struct ble_ll_conn_sm *connsm; + struct ble_ll_conn_global_params *conn_params; + + /* Kill the current one first (if one is running) */ + if (g_ble_ll_conn_cur_sm) { + connsm = g_ble_ll_conn_cur_sm; + g_ble_ll_conn_cur_sm = NULL; + ble_ll_conn_end(connsm, BLE_ERR_SUCCESS); + } + + /* Free the global connection complete event if there is one */ + if (g_ble_ll_conn_comp_ev) { + ble_hci_trans_buf_free(g_ble_ll_conn_comp_ev); + g_ble_ll_conn_comp_ev = NULL; + } + + /* Reset connection we are attempting to create */ + g_ble_ll_conn_create_sm = NULL; + + /* Now go through and end all the connections */ + while (1) { + connsm = SLIST_FIRST(&g_ble_ll_conn_active_list); + if (!connsm) { + break; + } + ble_ll_conn_end(connsm, BLE_ERR_SUCCESS); + } + + /* Get the maximum supported PHY PDU size from the PHY */ + max_phy_pyld = ble_phy_max_data_pdu_pyld(); + + /* Configure the global LL parameters */ + conn_params = &g_ble_ll_conn_params; + + maxbytes = min(MYNEWT_VAL(BLE_LL_SUPP_MAX_RX_BYTES), max_phy_pyld); + conn_params->supp_max_rx_octets = maxbytes; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + conn_params->supp_max_rx_time = MAX_TIME_CODED(maxbytes); +#else + conn_params->supp_max_rx_time = MAX_TIME_UNCODED(maxbytes); +#endif + + maxbytes = min(MYNEWT_VAL(BLE_LL_SUPP_MAX_TX_BYTES), max_phy_pyld); + conn_params->supp_max_tx_octets = maxbytes; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + conn_params->supp_max_tx_time = MAX_TIME_CODED(maxbytes); +#else + conn_params->supp_max_tx_time = MAX_TIME_UNCODED(maxbytes); +#endif + + maxbytes = min(MYNEWT_VAL(BLE_LL_CONN_INIT_MAX_TX_BYTES), max_phy_pyld); + conn_params->conn_init_max_tx_octets = maxbytes; + conn_params->conn_init_max_tx_time = MAX_TIME_UNCODED(maxbytes); + conn_params->conn_init_max_tx_time_uncoded = MAX_TIME_UNCODED(maxbytes); + conn_params->conn_init_max_tx_time_coded = MAX_TIME_CODED(maxbytes); + + conn_params->sugg_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN; + conn_params->sugg_tx_time = BLE_LL_CONN_SUPP_TIME_MIN; + + /* Mask in all channels by default */ + conn_params->num_used_chans = BLE_PHY_NUM_DATA_CHANS; + memset(conn_params->master_chan_map, 0xff, BLE_LL_CONN_CHMAP_LEN - 1); + conn_params->master_chan_map[4] = 0x1f; + + /* Reset statistics */ + STATS_RESET(ble_ll_conn_stats); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + /* reset default sync transfer params */ + g_ble_ll_conn_sync_transfer_params.max_skip = 0; + g_ble_ll_conn_sync_transfer_params.mode = 0; + g_ble_ll_conn_sync_transfer_params.sync_timeout_us = 0; +#endif +} + +/* Initialize the connection module */ +void +ble_ll_conn_module_init(void) +{ + int rc; + uint16_t i; + struct ble_ll_conn_sm *connsm; + + /* Initialize list of active conections */ + SLIST_INIT(&g_ble_ll_conn_active_list); + STAILQ_INIT(&g_ble_ll_conn_free_list); + + /* + * Take all the connections off the free memory pool and add them to + * the free connection list, assigning handles in linear order. Note: + * the specification allows a handle of zero; we just avoid using it. + */ + connsm = &g_ble_ll_conn_sm[0]; + for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { + + memset(connsm, 0, sizeof(struct ble_ll_conn_sm)); + connsm->conn_handle = i + 1; + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + + /* Initialize fixed schedule elements */ + connsm->conn_sch.sched_type = BLE_LL_SCHED_TYPE_CONN; + connsm->conn_sch.cb_arg = connsm; + ++connsm; + } + + /* Register connection statistics */ + rc = stats_init_and_reg(STATS_HDR(ble_ll_conn_stats), + STATS_SIZE_INIT_PARMS(ble_ll_conn_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_ll_conn_stats), + "ble_ll_conn"); + BLE_LL_ASSERT(rc == 0); + + /* Call reset to finish reset of initialization */ + ble_ll_conn_module_reset(); +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_hci.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_hci.c new file mode 100644 index 0000000..19ceebf --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_hci.c @@ -0,0 +1,1898 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_utils.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_conn.h" +#include "../include/controller/ble_ll_ctrl.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_adv.h" +#include "ble_ll_conn_priv.h" + +/* + * Used to limit the rate at which we send the number of completed packets + * event to the host. This is the os time at which we can send an event. + */ +static ble_npl_time_t g_ble_ll_last_num_comp_pkt_evt; +extern uint8_t *g_ble_ll_conn_comp_ev; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static const uint8_t ble_ll_valid_conn_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + | BLE_HCI_LE_PHY_2M_PREF_MASK +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + | BLE_HCI_LE_PHY_CODED_PREF_MASK +#endif + ); +static const uint8_t ble_ll_conn_required_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + | BLE_HCI_LE_PHY_CODED_PREF_MASK +#endif + ); +#endif + +/** + * Allocate an event to send a connection complete event when initiating + * + * @return int 0: success -1: failure + */ +static int +ble_ll_init_alloc_conn_comp_ev(void) +{ + int rc; + uint8_t *evbuf; + + rc = 0; + evbuf = g_ble_ll_conn_comp_ev; + if (evbuf == NULL) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!evbuf) { + rc = -1; + } else { + g_ble_ll_conn_comp_ev = evbuf; + } + } + + return rc; +} + +/** + * Called to check that the connection parameters are within range + * + * @param itvl_min + * @param itvl_max + * @param latency + * @param spvn_tmo + * + * @return int BLE_ERR_INV_HCI_CMD_PARMS if invalid parameters, 0 otherwise + */ +int +ble_ll_conn_hci_chk_conn_params(uint16_t itvl_min, uint16_t itvl_max, + uint16_t latency, uint16_t spvn_tmo) +{ + uint32_t spvn_tmo_usecs; + uint32_t min_spvn_tmo_usecs; + + if ((itvl_min > itvl_max) || + (itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (itvl_max > BLE_HCI_CONN_ITVL_MAX) || + (latency > BLE_HCI_CONN_LATENCY_MAX) || + (spvn_tmo < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (spvn_tmo > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* + * Supervision timeout (in msecs) must be more than: + * (1 + connLatency) * connIntervalMax * 1.25 msecs * 2. + */ + spvn_tmo_usecs = spvn_tmo; + spvn_tmo_usecs *= (BLE_HCI_CONN_SPVN_TMO_UNITS * 1000); + min_spvn_tmo_usecs = (uint32_t)itvl_max * 2 * BLE_LL_CONN_ITVL_USECS; + min_spvn_tmo_usecs *= (1 + latency); + if (spvn_tmo_usecs <= min_spvn_tmo_usecs) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return BLE_ERR_SUCCESS; +} + +/** + * Send a connection complete event + * + * @param status The BLE error code associated with the event + */ +void +ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status, + uint8_t *evbuf, struct ble_ll_adv_sm *advsm) +{ + struct ble_hci_ev_le_subev_enh_conn_complete *enh_ev; + struct ble_hci_ev_le_subev_conn_complete *ev; + struct ble_hci_ev *hci_ev = (void *) evbuf; + uint8_t *rpa; + + BLE_LL_ASSERT(evbuf); + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE)) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*enh_ev); + enh_ev = (void *) hci_ev->data; + + memset(enh_ev, 0, sizeof(*enh_ev)); + + enh_ev->subev_code = BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE; + enh_ev->status = status; + + if (connsm) { + enh_ev->conn_handle = htole16(connsm->conn_handle); + enh_ev->role = connsm->conn_role - 1; + enh_ev->peer_addr_type = connsm->peer_addr_type; + memcpy(enh_ev->peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN); + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + if (connsm->inita_identity_used) { + /* We used identity address in CONNECT_IND which can be just + * fine if + * a) it was direct advertising we replied to and remote uses + * its identity address in device privacy mode or IRK is all + * zeros. + * b) peer uses RPA and this is first time we connect to him + */ + rpa = NULL; + } else if (connsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { + rpa = ble_ll_scan_get_local_rpa(); + } else { + rpa = NULL; + } + } else { + rpa = ble_ll_adv_get_local_rpa(advsm); + } + + if (rpa) { + memcpy(enh_ev->local_rpa, rpa, BLE_DEV_ADDR_LEN); + } + + /* We need to adjust peer type if device connected using RPA + * and was resolved since RPA needs to be added to HCI event. + */ + if (connsm->peer_addr_type < BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT + && (connsm->rpa_index > -1)) { + enh_ev->peer_addr_type += 2; + } + + if (enh_ev->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + rpa = ble_ll_scan_get_peer_rpa(); + } else { + rpa = ble_ll_adv_get_peer_rpa(advsm); + } + memcpy(enh_ev->peer_rpa, rpa, BLE_DEV_ADDR_LEN); + } + + enh_ev->conn_itvl = htole16(connsm->conn_itvl); + enh_ev->conn_latency = htole16(connsm->slave_latency); + enh_ev->supervision_timeout = htole16(connsm->supervision_tmo); + enh_ev->mca = connsm->master_sca; + } + + ble_ll_hci_event_send(hci_ev); + return; + } + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_COMPLETE)) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + memset(ev, 0, sizeof(*ev)); + + ev->subev_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE; + ev->status = status; + + if (connsm) { + ev->conn_handle = htole16(connsm->conn_handle); + ev->role = connsm->conn_role - 1; + ev->peer_addr_type = connsm->peer_addr_type; + + if (ev->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) { + ev->peer_addr_type -= 2; + } + memcpy(ev->peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN); + ev->conn_itvl = htole16(connsm->conn_itvl); + ev->conn_latency = htole16(connsm->slave_latency); + ev->supervision_timeout = htole16(connsm->supervision_tmo); + ev->mca = connsm->master_sca; + } + + ble_ll_hci_event_send(hci_ev); + return; + } + + ble_hci_trans_buf_free(evbuf); +} + +/** + * Called to create and send the number of completed packets event to the + * host. + */ +void +ble_ll_conn_num_comp_pkts_event_send(struct ble_ll_conn_sm *connsm) +{ + /** The maximum number of handles that will fit in an event buffer. */ + static const int max_handles = + (BLE_LL_MAX_EVT_LEN - sizeof(struct ble_hci_ev_num_comp_pkts) - 1) / 4; + struct ble_hci_ev_num_comp_pkts *ev; + struct ble_hci_ev *hci_ev; + int event_sent; + + if (connsm == NULL) { + goto skip_conn; + } + + /* + * At some periodic rate, make sure we go through all active connections + * and send the number of completed packet events. We do this mainly + * because the spec says we must update the host even though no packets + * have completed but there are data packets in the controller buffers + * (i.e. enqueued in a connection state machine). + */ + if ((ble_npl_stime_t)(ble_npl_time_get() - g_ble_ll_last_num_comp_pkt_evt) < + ble_npl_time_ms_to_ticks32(MYNEWT_VAL(BLE_LL_NUM_COMP_PKT_ITVL_MS))) { + /* + * If this connection has completed packets, send an event right away. + * We do this to increase throughput but we dont want to search the + * entire active list every time. + */ + if (connsm->completed_pkts) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_NUM_COMP_PKTS; + hci_ev->length = sizeof(*ev); + ev = (void *)hci_ev->data; + + ev->count = 1; + ev->completed[0].handle = htole16(connsm->conn_handle); + ev->completed[0].packets = htole16(connsm->completed_pkts); + hci_ev->length += sizeof(ev->completed[0]); + + connsm->completed_pkts = 0; + + ble_ll_hci_event_send(hci_ev); + } + } + return; + } + + /* Iterate through all the active, created connections */ +skip_conn: + hci_ev = NULL; + ev = NULL; + + event_sent = 0; + SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) { + /* + * Only look at connections that we have sent a connection complete + * event and that either has packets enqueued or has completed packets. + */ + if ((connsm->conn_state != BLE_LL_CONN_STATE_IDLE) && + (connsm->completed_pkts || !STAILQ_EMPTY(&connsm->conn_txq))) { + /* If no buffer, get one, If cant get one, leave. */ + if (!hci_ev) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!hci_ev) { + break; + } + + hci_ev->opcode = BLE_HCI_EVCODE_NUM_COMP_PKTS; + hci_ev->length = sizeof(*ev); + ev = (void *)hci_ev->data; + + ev->count = 0; + } + + /* Add handle and complete packets */ + ev->completed[ev->count].handle = htole16(connsm->conn_handle); + ev->completed[ev->count].packets = htole16(connsm->completed_pkts); + hci_ev->length += sizeof(ev->completed[ev->count]); + ev->count++; + + connsm->completed_pkts = 0; + + /* Send now if the buffer is full. */ + if (ev->count == max_handles) { + ble_ll_hci_event_send(hci_ev); + hci_ev = NULL; + event_sent = 1; + } + } + } + + /* Send event if there is an event to send */ + if (hci_ev) { + ble_ll_hci_event_send(hci_ev); + event_sent = 1; + } + + if (event_sent) { + g_ble_ll_last_num_comp_pkt_evt = ble_npl_time_get(); + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) +/** + * Send a authenticated payload timeout event + * + * NOTE: we currently only send this event when we have a reason to send it; + * not when it fails. + * + * @param reason The BLE error code to send as a disconnect reason + */ +void +ble_ll_auth_pyld_tmo_event_send(struct ble_ll_conn_sm *connsm) +{ + struct ble_hci_ev_auth_pyld_tmo *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_AUTH_PYLD_TMO)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_AUTH_PYLD_TMO; + hci_ev->length = sizeof(*ev); + + ev = (void *) hci_ev->data; + ev->conn_handle = htole16(connsm->conn_handle); + + ble_ll_hci_event_send(hci_ev); + } + } +} +#endif + +/** + * Send a disconnection complete event. + * + * NOTE: we currently only send this event when we have a reason to send it; + * not when it fails. + * + * @param reason The BLE error code to send as a disconnect reason + */ +void +ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t reason) +{ + struct ble_hci_ev_disconn_cmp *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_DISCONN_CMP)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_DISCONN_CMP; + hci_ev->length = sizeof(*ev); + + ev = (void *) hci_ev->data; + + ev->status = BLE_ERR_SUCCESS; + ev->conn_handle = htole16(connsm->conn_handle); + ev->reason = reason; + + ble_ll_hci_event_send(hci_ev); + } + } +} + +static int +ble_ll_conn_hci_chk_scan_params(uint16_t itvl, uint16_t window) +{ + /* Check interval and window */ + if ((itvl < BLE_HCI_SCAN_ITVL_MIN) || + (itvl > BLE_HCI_SCAN_ITVL_MAX) || + (window < BLE_HCI_SCAN_WINDOW_MIN) || + (window > BLE_HCI_SCAN_WINDOW_MAX) || + (itvl < window)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return 0; +} + +/** + * Process the HCI command to create a connection. + * + * Context: Link Layer task (HCI command processing) + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_create_conn_cp *cmd = (const void *) cmdbuf; + struct ble_ll_conn_sm *connsm; + struct hci_create_conn hcc = { 0 }; + int rc; + + if (len < sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If we are already creating a connection we should leave */ + if (g_ble_ll_conn_create_sm) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* If already enabled, we return an error */ + if (ble_ll_scan_enabled()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Retrieve command data */ + hcc.scan_itvl = le16toh(cmd->scan_itvl); + hcc.scan_window = le16toh(cmd->scan_window); + + rc = ble_ll_conn_hci_chk_scan_params(hcc.scan_itvl, hcc.scan_window); + if (rc) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check filter policy */ + hcc.filter_policy = cmd->filter_policy; + if (hcc.filter_policy > BLE_HCI_INITIATOR_FILT_POLICY_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Get peer address type and address only if no whitelist used */ + if (hcc.filter_policy == 0) { + hcc.peer_addr_type = cmd->peer_addr_type; + if (hcc.peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + memcpy(&hcc.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + } + + /* Get own address type (used in connection request) */ + hcc.own_addr_type = cmd->own_addr_type; + if (hcc.own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection interval, latency and supervision timeoout */ + hcc.conn_itvl_min = le16toh(cmd->min_conn_itvl); + hcc.conn_itvl_max = le16toh(cmd->max_conn_itvl); + hcc.conn_latency = le16toh(cmd->conn_latency); + hcc.supervision_timeout = le16toh(cmd->tmo); + rc = ble_ll_conn_hci_chk_conn_params(hcc.conn_itvl_min, + hcc.conn_itvl_max, + hcc.conn_latency, + hcc.supervision_timeout); + if (rc) { + return rc; + } + + /* Min/max connection event lengths */ + hcc.min_ce_len = le16toh(cmd->min_ce); + hcc.max_ce_len = le16toh(cmd->max_ce); + if (hcc.min_ce_len > hcc.max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Make sure we can allocate an event to send the connection complete */ + if (ble_ll_init_alloc_conn_comp_ev()) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Make sure we can accept a connection! */ + connsm = ble_ll_conn_sm_get(); + if (connsm == NULL) { + return BLE_ERR_CONN_LIMIT; + } + + /* Initialize state machine in master role and start state machine */ + ble_ll_conn_master_init(connsm, &hcc); + ble_ll_conn_sm_new(connsm); + /* CSA will be selected when advertising is received */ + + /* Start scanning */ + rc = ble_ll_scan_initiator_start(&hcc, &connsm->scansm); + if (rc) { + SLIST_REMOVE(&g_ble_ll_conn_active_list,connsm,ble_ll_conn_sm,act_sle); + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + } else { + /* Set the connection state machine we are trying to create. */ + g_ble_ll_conn_create_sm = connsm; + } + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_conn_hcc_params_set_fallback(struct hci_ext_create_conn *hcc, + const struct hci_ext_conn_params *fallback) +{ + BLE_LL_ASSERT(fallback); + + if (!(hcc->init_phy_mask & BLE_PHY_MASK_1M)) { + hcc->params[0] = *fallback; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + if (!(hcc->init_phy_mask & BLE_PHY_MASK_2M)) { + hcc->params[1] = *fallback; + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (!(hcc->init_phy_mask & BLE_PHY_MASK_CODED)) { + hcc->params[2] = *fallback; + } +#endif +} + +int +ble_ll_ext_conn_create(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_ext_create_conn_cp *cmd = (const void *) cmdbuf; + const struct conn_params *params = cmd->conn_params; + const struct hci_ext_conn_params *fallback_params = NULL; + struct hci_ext_create_conn hcc = { 0 }; + struct ble_ll_conn_sm *connsm; + int rc; + + /* validate length */ + if (len < sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + len -= sizeof(*cmd); + + /* If we are already creating a connection we should leave */ + if (g_ble_ll_conn_create_sm) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* If already enabled, we return an error */ + if (ble_ll_scan_enabled()) { + return BLE_ERR_CMD_DISALLOWED; + } + + hcc.filter_policy = cmd->filter_policy; + if (hcc.filter_policy > BLE_HCI_INITIATOR_FILT_POLICY_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + hcc.own_addr_type = cmd->own_addr_type; + if (hcc.own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Validate peer address type only if no whitelist used */ + if (hcc.filter_policy == 0) { + hcc.peer_addr_type = cmd->peer_addr_type; + + if (hcc.peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + memcpy(hcc.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + } + + hcc.init_phy_mask = cmd->init_phy_mask; + if (hcc.init_phy_mask & ~ble_ll_valid_conn_phy_mask) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!(hcc.init_phy_mask & ble_ll_conn_required_phy_mask)) { + /* At least one of those need to be set */ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (hcc.init_phy_mask & BLE_PHY_MASK_1M) { + if (len < sizeof(*params)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + len -= sizeof(*params); + + hcc.params[0].scan_itvl = le16toh(params->scan_itvl); + hcc.params[0].scan_window = le16toh(params->scan_window); + + rc = ble_ll_conn_hci_chk_scan_params(hcc.params[0].scan_itvl, + hcc.params[0].scan_window); + if (rc) { + return rc; + } + + hcc.params[0].conn_itvl_min = le16toh(params->conn_min_itvl); + hcc.params[0].conn_itvl_max = le16toh(params->conn_min_itvl); + hcc.params[0].conn_latency = le16toh(params->conn_latency); + hcc.params[0].supervision_timeout = le16toh(params->supervision_timeout); + + rc = ble_ll_conn_hci_chk_conn_params(hcc.params[0].conn_itvl_min, + hcc.params[0].conn_itvl_max, + hcc.params[0].conn_latency, + hcc.params[0].supervision_timeout); + if (rc) { + return rc; + } + + /* Min/max connection event lengths */ + hcc.params[0].min_ce_len = le16toh(params->min_ce); + hcc.params[0].max_ce_len = le16toh(params->max_ce); + if (hcc.params[0].min_ce_len > hcc.params[0].max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + fallback_params = &hcc.params[0]; + params++; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + if (hcc.init_phy_mask & BLE_PHY_MASK_2M) { + if (len < sizeof(*params)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + len -= sizeof(*params); + + hcc.params[1].conn_itvl_min = le16toh(params->conn_min_itvl); + hcc.params[1].conn_itvl_max = le16toh(params->conn_min_itvl); + hcc.params[1].conn_latency = le16toh(params->conn_latency); + hcc.params[1].supervision_timeout = le16toh(params->supervision_timeout); + + rc = ble_ll_conn_hci_chk_conn_params(hcc.params[1].conn_itvl_min, + hcc.params[1].conn_itvl_max, + hcc.params[1].conn_latency, + hcc.params[1].supervision_timeout); + if (rc) { + return rc; + } + + /* Min/max connection event lengths */ + hcc.params[1].min_ce_len = le16toh(params->min_ce); + hcc.params[1].max_ce_len = le16toh(params->max_ce); + if (hcc.params[1].min_ce_len > hcc.params[1].max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + params++; + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (hcc.init_phy_mask & BLE_PHY_MASK_CODED) { + if (len < sizeof(*params)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + len -= sizeof(*params); + + hcc.params[2].scan_itvl = le16toh(params->scan_itvl); + hcc.params[2].scan_window = le16toh(params->scan_window); + + rc = ble_ll_conn_hci_chk_scan_params(hcc.params[2].scan_itvl, + hcc.params[2].scan_window); + if (rc) { + return rc; + } + + hcc.params[2].conn_itvl_min = le16toh(params->conn_min_itvl); + hcc.params[2].conn_itvl_max = le16toh(params->conn_min_itvl); + hcc.params[2].conn_latency = le16toh(params->conn_latency); + hcc.params[2].supervision_timeout = le16toh(params->supervision_timeout); + + rc = ble_ll_conn_hci_chk_conn_params(hcc.params[2].conn_itvl_min, + hcc.params[2].conn_itvl_max, + hcc.params[2].conn_latency, + hcc.params[2].supervision_timeout); + if (rc) { + return rc; + } + + /* Min/max connection event lengths */ + hcc.params[2].min_ce_len = le16toh(params->min_ce); + hcc.params[2].max_ce_len = le16toh(params->max_ce); + if (hcc.params[2].min_ce_len > hcc.params[2].max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (!fallback_params) { + fallback_params = &hcc.params[2]; + } + params++; + } +#endif + + /* Make sure we can allocate an event to send the connection complete */ + if (ble_ll_init_alloc_conn_comp_ev()) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Make sure we can accept a connection! */ + connsm = ble_ll_conn_sm_get(); + if (connsm == NULL) { + return BLE_ERR_CONN_LIMIT; + } + + ble_ll_conn_hcc_params_set_fallback(&hcc, fallback_params); + + /* Initialize state machine in master role and start state machine */ + ble_ll_conn_ext_master_init(connsm, &hcc); + ble_ll_conn_sm_new(connsm); + + /* CSA will be selected when advertising is received */ + + /* Start scanning */ + rc = ble_ll_scan_ext_initiator_start(&hcc, &connsm->scansm); + if (rc) { + SLIST_REMOVE(&g_ble_ll_conn_active_list,connsm,ble_ll_conn_sm,act_sle); + STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); + } else { + /* Set the connection state machine we are trying to create. */ + g_ble_ll_conn_create_sm = connsm; + } + + return rc; +} +#endif + +static int +ble_ll_conn_process_conn_params(const struct ble_hci_le_rem_conn_param_rr_cp *cmd, + struct ble_ll_conn_sm *connsm) +{ + int rc; + struct hci_conn_update *hcu; + + /* Retrieve command data */ + hcu = &connsm->conn_param_req; + hcu->handle = connsm->conn_handle; + + BLE_LL_ASSERT(connsm->conn_handle == le16toh(cmd->conn_handle)); + + hcu->conn_itvl_min = le16toh(cmd->conn_itvl_min); + hcu->conn_itvl_max = le16toh(cmd->conn_itvl_max); + hcu->conn_latency = le16toh(cmd->conn_latency); + hcu->supervision_timeout = le16toh(cmd->supervision_timeout); + hcu->min_ce_len = le16toh(cmd->min_ce); + hcu->max_ce_len = le16toh(cmd->max_ce); + + /* Check that parameter values are in range */ + rc = ble_ll_conn_hci_chk_conn_params(hcu->conn_itvl_min, + hcu->conn_itvl_max, + hcu->conn_latency, + hcu->supervision_timeout); + + /* Check valid min/max ce length */ + if (rc || (hcu->min_ce_len > hcu->max_ce_len)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + return rc; +} + +/** + * Called when the host issues the read remote features command + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_read_rem_features(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rd_rem_feat_cp *cmd = (const void *) cmdbuf; + struct ble_ll_conn_sm *connsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If no connection handle exit with error */ + connsm = ble_ll_conn_find_active_conn(le16toh(cmd->conn_handle)); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* If already pending exit with error */ + if (connsm->csmflags.cfbit.pending_hci_rd_features) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* + * Start control procedure if we did not receive peer's features and did not + * start procedure already. + */ + if (!connsm->csmflags.cfbit.rxd_features && + !IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG)) { + if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && + !(ble_ll_read_supp_features() & BLE_LL_FEAT_SLAVE_INIT)) { + return BLE_ERR_CMD_DISALLOWED; + } + + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG); + } + + connsm->csmflags.cfbit.pending_hci_rd_features = 1; + + return BLE_ERR_SUCCESS; +} + +/** + * Called to process a connection update command. + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_conn_update_cp *cmd = (const void *) cmdbuf; + int rc; + uint8_t ctrl_proc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + struct hci_conn_update *hcu; + + /* + * XXX: must deal with slave not supporting this feature and using + * conn update! Right now, we only check if WE support the connection + * parameters request procedure. We dont check if the remote does. + * We should also be able to deal with sending the parameter request, + * getting an UNKOWN_RSP ctrl pdu and resorting to use normal + * connection update procedure. + */ + + /* If no connection handle exit with error */ + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* Better not have this procedure ongoing! */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ) || + IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE)) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* See if this feature is supported on both sides */ + if ((connsm->conn_features & BLE_LL_FEAT_CONN_PARM_REQ) == 0) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_UNSUPP_REM_FEATURE; + } + ctrl_proc = BLE_LL_CTRL_PROC_CONN_UPDATE; + } else { + ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ; + } + + /* + * If we are a slave and the master has initiated the procedure already + * we should deny the slave request for now. If we are a master and the + * slave has initiated the procedure, we need to send a reject to the + * slave. + */ + if (connsm->csmflags.cfbit.awaiting_host_reply) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_LMP_COLLISION; + } else { + connsm->csmflags.cfbit.awaiting_host_reply = 0; + + /* XXX: If this fails no reject ind will be sent! */ + ble_ll_ctrl_reject_ind_send(connsm, connsm->host_reply_opcode, + BLE_ERR_LMP_COLLISION); + } + } + + /* + * If we are a slave and the master has initiated the channel map + * update procedure we should deny the slave request for now. + */ + if (connsm->csmflags.cfbit.chanmap_update_scheduled) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_DIFF_TRANS_COLL; + } + } + + /* Retrieve command data */ + hcu = &connsm->conn_param_req; + hcu->conn_itvl_min = le16toh(cmd->conn_itvl_min); + hcu->conn_itvl_max = le16toh(cmd->conn_itvl_max); + hcu->conn_latency = le16toh(cmd->conn_latency); + hcu->supervision_timeout = le16toh(cmd->supervision_timeout); + hcu->min_ce_len = le16toh(cmd->min_ce_len); + hcu->max_ce_len = le16toh(cmd->max_ce_len); + if (hcu->min_ce_len > hcu->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check that parameter values are in range */ + rc = ble_ll_conn_hci_chk_conn_params(hcu->conn_itvl_min, + hcu->conn_itvl_max, + hcu->conn_latency, + hcu->supervision_timeout); + if (!rc) { + hcu->handle = handle; + + /* Start the control procedure */ + ble_ll_ctrl_proc_start(connsm, ctrl_proc); + } + + return rc; +} + +int +ble_ll_conn_hci_param_rr(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rem_conn_param_rr_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rem_conn_param_rr_rp *rsp = (void *) rspbuf; + int rc; + uint8_t *dptr; + uint8_t rsp_opcode; + uint16_t handle; + struct os_mbuf *om; + struct ble_ll_conn_sm *connsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + + /* See if we support this feature */ + if ((ble_ll_read_supp_features() & BLE_LL_FEAT_CONN_PARM_REQ) == 0) { + rc = BLE_ERR_UNKNOWN_HCI_CMD; + goto done; + } + + /* If we dont have a handle we cant do anything */ + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto done; + } + + /* Make sure connection parameters are valid */ + rc = ble_ll_conn_process_conn_params(cmd, connsm); + + /* The connection should be awaiting a reply. If not, just discard */ + if (connsm->csmflags.cfbit.awaiting_host_reply) { + /* Get a control packet buffer */ + if (rc == BLE_ERR_SUCCESS) { + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, + sizeof(struct ble_mbuf_hdr)); + if (om) { + dptr = om->om_data; + rsp_opcode = ble_ll_ctrl_conn_param_reply(connsm, dptr, + &connsm->conn_cp); + dptr[0] = rsp_opcode; + len = g_ble_ll_ctrl_pkt_lengths[rsp_opcode] + 1; + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); + } + } else { + /* XXX: check return code and deal */ + ble_ll_ctrl_reject_ind_send(connsm, connsm->host_reply_opcode, + BLE_ERR_CONN_PARMS); + } + connsm->csmflags.cfbit.awaiting_host_reply = 0; + + /* XXX: if we cant get a buffer, what do we do? We need to remember + * reason if it was a negative reply. We also would need to have + * some state to tell us this happened + */ + } + +done: + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +int +ble_ll_conn_hci_param_nrr(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rem_conn_params_nrr_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rem_conn_params_nrr_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t handle; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + + /* See if we support this feature */ + if ((ble_ll_read_supp_features() & BLE_LL_FEAT_CONN_PARM_REQ) == 0) { + rc = BLE_ERR_UNKNOWN_HCI_CMD; + goto done; + } + + /* If we dont have a handle we cant do anything */ + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto done; + } + + rc = BLE_ERR_SUCCESS; + + /* The connection should be awaiting a reply. If not, just discard */ + if (connsm->csmflags.cfbit.awaiting_host_reply) { + /* XXX: check return code and deal */ + ble_ll_ctrl_reject_ind_send(connsm, connsm->host_reply_opcode, + cmd->reason); + connsm->csmflags.cfbit.awaiting_host_reply = 0; + + /* XXX: if we cant get a buffer, what do we do? We need to remember + * reason if it was a negative reply. We also would need to have + * some state to tell us this happened + */ + } + +done: + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +/* this is called from same context after cmd complete is send so it is + * safe to use g_ble_ll_conn_comp_ev + */ +static void +ble_ll_conn_hci_cancel_conn_complete_event(void) +{ + BLE_LL_ASSERT(g_ble_ll_conn_comp_ev); + + ble_ll_conn_comp_event_send(NULL, BLE_ERR_UNK_CONN_ID, + g_ble_ll_conn_comp_ev, NULL); + g_ble_ll_conn_comp_ev = NULL; +} + +/** + * Called when HCI command to cancel a create connection command has been + * received. + * + * Context: Link Layer (HCI command parser) + * + * @return int + */ +int +ble_ll_conn_create_cancel(ble_ll_hci_post_cmd_complete_cb *post_cmd_cb) +{ + int rc; + struct ble_ll_conn_sm *connsm; + os_sr_t sr; + + /* + * If we receive this command and we have not got a connection + * create command, we have to return disallowed. The spec does not say + * what happens if the connection has already been established. We + * return disallowed as well + */ + OS_ENTER_CRITICAL(sr); + connsm = g_ble_ll_conn_create_sm; + if (connsm && (connsm->conn_state == BLE_LL_CONN_STATE_IDLE)) { + /* stop scanning and end the connection event */ + g_ble_ll_conn_create_sm = NULL; + ble_ll_scan_sm_stop(1); + ble_ll_conn_end(connsm, BLE_ERR_UNK_CONN_ID); + + *post_cmd_cb = ble_ll_conn_hci_cancel_conn_complete_event; + + rc = BLE_ERR_SUCCESS; + } else { + /* If we are not attempting to create a connection*/ + rc = BLE_ERR_CMD_DISALLOWED; + } + OS_EXIT_CRITICAL(sr); + + return rc; +} + +/** + * Called to process a HCI disconnect command + * + * Context: Link Layer task (HCI command parser). + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_disconnect_cmd(const uint8_t *cmdbuf, uint8_t len) +{ + int rc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + const struct ble_hci_lc_disconnect_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof (*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check for valid parameters */ + handle = le16toh(cmd->conn_handle); + + rc = BLE_ERR_INV_HCI_CMD_PARMS; + if (handle <= BLE_LL_CONN_MAX_CONN_HANDLE) { + /* Make sure reason is valid */ + switch (cmd->reason) { + case BLE_ERR_AUTH_FAIL: + case BLE_ERR_REM_USER_CONN_TERM: + case BLE_ERR_RD_CONN_TERM_RESRCS: + case BLE_ERR_RD_CONN_TERM_PWROFF: + case BLE_ERR_UNSUPP_REM_FEATURE: + case BLE_ERR_UNIT_KEY_PAIRING: + case BLE_ERR_CONN_PARMS: + connsm = ble_ll_conn_find_active_conn(handle); + if (connsm) { + /* Do not allow command if we are in process of disconnecting */ + if (connsm->disconnect_reason) { + rc = BLE_ERR_CMD_DISALLOWED; + } else { + /* This control procedure better not be pending! */ + BLE_LL_ASSERT(CONN_F_TERMINATE_STARTED(connsm) == 0); + + /* Record the disconnect reason */ + connsm->disconnect_reason = cmd->reason; + + /* Start this control procedure */ + ble_ll_ctrl_terminate_start(connsm); + + rc = BLE_ERR_SUCCESS; + } + } else { + rc = BLE_ERR_UNK_CONN_ID; + } + break; + default: + break; + } + } + + return rc; +} + +/** + * Called to process a HCI disconnect command + * + * Context: Link Layer task (HCI command parser). + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_rd_rem_ver_cmd(const uint8_t *cmdbuf, uint8_t len) +{ + struct ble_ll_conn_sm *connsm; + const struct ble_hci_rd_rem_ver_info_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check for valid parameters */ + connsm = ble_ll_conn_find_active_conn(le16toh(cmd->conn_handle)); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* Return error if in progress */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_VERSION_XCHG)) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* + * Start this control procedure. If we have already done this control + * procedure we set the pending bit so that the host gets an event because + * it is obviously expecting one (or would not have sent the command). + * NOTE: we cant just send the event here. That would cause the event to + * be queued before the command status. + */ + if (!connsm->csmflags.cfbit.version_ind_sent) { + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_VERSION_XCHG); + } else { + connsm->pending_ctrl_procs |= (1 << BLE_LL_CTRL_PROC_VERSION_XCHG); + } + + return BLE_ERR_SUCCESS; +} + +/** + * Called to read the RSSI for a given connection handle + * + * @param cmdbuf + * @param rspbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_rd_rssi(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, uint8_t *rsplen) +{ + + const struct ble_hci_rd_rssi_cp *cmd = (const void *) cmdbuf; + struct ble_hci_rd_rssi_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rsp->handle = cmd->handle; + + connsm = ble_ll_conn_find_active_conn(le16toh(cmd->handle)); + if (!connsm) { + rsp->rssi = 127; + rc = BLE_ERR_UNK_CONN_ID; + } else { + rsp->rssi = connsm->conn_rssi; + rc = BLE_ERR_SUCCESS; + } + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Called to read the current channel map of a connection + * + * @param cmdbuf + * @param rspbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_rd_chan_map(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rd_chan_map_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rd_chan_map_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t handle; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + memset(rsp->chan_map, 0, sizeof(rsp->chan_map)); + } else { + if (connsm->csmflags.cfbit.chanmap_update_scheduled) { + memcpy(rsp->chan_map, connsm->req_chanmap, BLE_LL_CONN_CHMAP_LEN); + } else { + memcpy(rsp->chan_map, connsm->chanmap, BLE_LL_CONN_CHMAP_LEN); + } + rc = BLE_ERR_SUCCESS; + } + + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Called when the host issues the LE command "set host channel classification" + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_set_chan_class(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_host_chan_class_cp *cmd = (const void *) cmdbuf; + uint8_t num_used_chans; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* + * The HCI command states that the host is allowed to mask in just one + * channel but the Link Layer needs minimum two channels to operate. So + * I will not allow this command if there are less than 2 channels masked. + */ + num_used_chans = ble_ll_utils_calc_num_used_chans(cmd->chan_map); + if ((num_used_chans < 2) || ((cmd->chan_map[4] & 0xe0) != 0)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Set the host channel mask */ + ble_ll_conn_set_global_chanmap(num_used_chans, cmd->chan_map); + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) +int +ble_ll_conn_hci_set_data_len(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_set_data_len_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_set_data_len_rp *rsp = (void *) rspbuf; + int rc; + uint16_t handle; + uint16_t txoctets; + uint16_t txtime; + struct ble_ll_conn_sm *connsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Find connection */ + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto done; + } + + txoctets = le16toh(cmd->tx_octets); + txtime = le16toh(cmd->tx_time); + + /* Make sure it is valid */ + if (!ble_ll_chk_txrx_octets(txoctets) || + !ble_ll_chk_txrx_time(txtime)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + /* + * Keep original value requested by host since we may want to recalculate + * MaxTxTime after PHY changes between coded and uncoded. + */ + connsm->host_req_max_tx_time = txtime; + + /* If peer does not support coded, we cannot use value larger than 2120us */ + if (!(connsm->remote_features[0] & (BLE_LL_FEAT_LE_CODED_PHY >> 8))) { + txtime = min(txtime, BLE_LL_CONN_SUPP_TIME_MAX_UNCODED); + } +#endif + + rc = BLE_ERR_SUCCESS; + if (connsm->max_tx_time != txtime || + connsm->max_tx_octets != txoctets) { + + connsm->max_tx_time = txtime; + connsm->max_tx_octets = txoctets; + + ble_ll_ctrl_initiate_dle(connsm); + } + +done: + rsp->conn_handle = htole16(handle); + *rsplen = sizeof(*rsp); + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/** + * LE start encrypt command + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_le_start_encrypt(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_start_encrypt_cp *cmd = (const void *) cmdbuf; + struct ble_ll_conn_sm *connsm; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + connsm = ble_ll_conn_find_active_conn(le16toh(cmd->conn_handle)); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + } else if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + rc = BLE_ERR_UNSPECIFIED; + } else if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_ENCRYPT) { + /* + * The specification does not say what to do here but the host should + * not be telling us to start encryption while we are in the process + * of honoring a previous start encrypt. + */ + rc = BLE_ERR_CMD_DISALLOWED; + } else { + /* Start the control procedure */ + connsm->enc_data.host_rand_num = le64toh(cmd->rand); + connsm->enc_data.enc_div = le16toh(cmd->div); + swap_buf(connsm->enc_data.enc_block.key, cmd->ltk, 16); + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_ENCRYPT); + rc = BLE_ERR_SUCCESS; + } + + return rc; +} + +/** + * Called to process the LE long term key reply. + * + * Context: Link Layer Task. + * + * @param cmdbuf + * @param rspbuf + * @param ocf + * + * @return int + */ +int +ble_ll_conn_hci_le_ltk_reply(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_lt_key_req_reply_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_lt_key_req_reply_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t handle; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Find connection handle */ + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto ltk_key_cmd_complete; + } + + /* Should never get this if we are a master! */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + rc = BLE_ERR_UNSPECIFIED; + goto ltk_key_cmd_complete; + } + + /* The connection should be awaiting a reply. If not, just discard */ + if (connsm->enc_data.enc_state != CONN_ENC_S_LTK_REQ_WAIT) { + rc = BLE_ERR_CMD_DISALLOWED; + goto ltk_key_cmd_complete; + } + + swap_buf(connsm->enc_data.enc_block.key, cmd->ltk, 16); + ble_ll_calc_session_key(connsm); + ble_ll_ctrl_start_enc_send(connsm); + rc = BLE_ERR_SUCCESS; + +ltk_key_cmd_complete: + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Called to process the LE long term key negative reply. + * + * Context: Link Layer Task. + * + * @param cmdbuf + * @param rspbuf + * @param ocf + * + * @return int + */ +int +ble_ll_conn_hci_le_ltk_neg_reply(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_lt_key_req_neg_reply_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_lt_key_req_neg_reply_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t handle; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Find connection handle */ + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto ltk_key_cmd_complete; + } + + /* Should never get this if we are a master! */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + rc = BLE_ERR_UNSPECIFIED; + goto ltk_key_cmd_complete; + } + + /* The connection should be awaiting a reply. If not, just discard */ + if (connsm->enc_data.enc_state != CONN_ENC_S_LTK_REQ_WAIT) { + rc = BLE_ERR_CMD_DISALLOWED; + goto ltk_key_cmd_complete; + } + + /* We received a negative reply! Send REJECT_IND */ + ble_ll_ctrl_reject_ind_send(connsm, BLE_LL_CTRL_ENC_REQ, + BLE_ERR_PINKEY_MISSING); + connsm->enc_data.enc_state = CONN_ENC_S_LTK_NEG_REPLY; + + rc = BLE_ERR_SUCCESS; + +ltk_key_cmd_complete: + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) +/** + * Read authenticated payload timeout (OGF=3, OCF==0x007B) + * + * @param cmdbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_rd_auth_pyld_tmo(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_cb_rd_auth_pyld_tmo_cp *cmd = (const void *) cmdbuf; + struct ble_hci_cb_rd_auth_pyld_tmo_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t handle; + int rc; + + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + rsp->tmo = 0; + } else { + rc = BLE_ERR_SUCCESS; + rsp->tmo = htole16(connsm->auth_pyld_tmo); + } + + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Write authenticated payload timeout (OGF=3, OCF=00x7C) + * + * @param cmdbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_wr_auth_pyld_tmo(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_cb_wr_auth_pyld_tmo_cp *cmd = (const void *) cmdbuf; + struct ble_hci_cb_wr_auth_pyld_tmo_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint32_t min_tmo; + uint16_t handle; + uint16_t tmo; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rc = BLE_ERR_SUCCESS; + + handle = le16toh(cmd->conn_handle); + + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + } else { + /* + * The timeout is in units of 10 msecs. We need to make sure that the + * timeout is greater than or equal to connItvl * (1 + slaveLatency) + */ + tmo = le16toh(cmd->tmo); + min_tmo = (uint32_t)connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; + min_tmo *= (connsm->slave_latency + 1); + min_tmo /= 10000; + + if (tmo < min_tmo) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + } else { + connsm->auth_pyld_tmo = tmo; + if (ble_npl_callout_is_active(&connsm->auth_pyld_timer)) { + ble_ll_conn_auth_pyld_timer_start(connsm); + } + } + } + + rsp->conn_handle = htole16(handle); + *rsplen = sizeof(*rsp); + return rc; +} +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/** + * Read current phy for connection (OGF=8, OCF==0x0030) + * + * @param cmdbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_le_rd_phy(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rd_phy_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rd_phy_rp *rsp = (void *) rspbuf; + int rc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rsp->tx_phy = 0; + rsp->rx_phy = 0; + rc = BLE_ERR_UNK_CONN_ID; + } else { + rsp->tx_phy = connsm->phy_data.cur_tx_phy; + rsp->rx_phy = connsm->phy_data.cur_rx_phy; + rc = BLE_ERR_SUCCESS; + } + + rsp->conn_handle = htole16(handle); + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Set PHY preferences for connection + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_le_set_phy(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_phy_cp *cmd = (const void *) cmdbuf; + int rc; + uint16_t phy_options; + uint8_t tx_phys; + uint8_t rx_phys; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->conn_handle); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* + * If host has requested a PHY update and we are not finished do + * not allow another one + */ + if (CONN_F_HOST_PHY_UPDATE(connsm)) { + return BLE_ERR_CMD_DISALLOWED; + } + + phy_options = le16toh(cmd->phy_options); + if (phy_options > BLE_HCI_LE_PHY_CODED_S8_PREF) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check valid parameters */ + rc = ble_ll_hci_chk_phy_masks(cmd->all_phys, cmd->tx_phys, cmd->rx_phys, + &tx_phys, &rx_phys); + if (rc) { + goto phy_cmd_param_err; + } + + connsm->phy_data.phy_options = phy_options & 0x03; + connsm->phy_data.host_pref_tx_phys_mask = tx_phys, + connsm->phy_data.host_pref_rx_phys_mask = rx_phys; + + /* + * The host preferences override the default phy preferences. Currently, + * the only reason the controller will initiate a procedure on its own + * is due to the fact that the host set default preferences. So if there + * is a pending control procedure and it has not yet started, we do not + * need to perform the default controller procedure. + */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE)) { + if (connsm->cur_ctrl_proc != BLE_LL_CTRL_PROC_PHY_UPDATE) { + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; + } + CONN_F_HOST_PHY_UPDATE(connsm) = 1; + } else { + /* + * We could be doing a peer-initiated PHY update procedure. If this + * is the case the requested phy preferences will not both be 0. If + * we are not done with a peer-initiated procedure we just set the + * pending bit but do not start the control procedure. + */ + if (CONN_F_PEER_PHY_UPDATE(connsm)) { + connsm->pending_ctrl_procs |= (1 << BLE_LL_CTRL_PROC_PHY_UPDATE); + CONN_F_HOST_PHY_UPDATE(connsm) = 1; + } else { + /* Check if we should start phy update procedure */ + if (!ble_ll_conn_chk_phy_upd_start(connsm)) { + CONN_F_HOST_PHY_UPDATE(connsm) = 1; + } else { + /* + * Set flag to send a PHY update complete event. We set flag + * even if we do not do an update procedure since we have to + * inform the host even if we decide not to change anything. + */ + CONN_F_PHY_UPDATE_EVENT(connsm) = 1; + } + } + } + +phy_cmd_param_err: + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +int +ble_ll_set_sync_transfer_params(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_periodic_adv_sync_transfer_params_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_periodic_adv_sync_transfer_params_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + uint16_t sync_timeout; + uint16_t skip; + int rc; + + if (len != sizeof(*cmd)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (cmd->mode > 0x02) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + skip = le16toh(cmd->skip); + if (skip > 0x01f3) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + sync_timeout = le16toh(cmd->sync_timeout); + if ((sync_timeout < 0x000a) || (sync_timeout > 0x4000)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + /* we don't support any CTE yet */ + if (cmd->sync_cte_type) { + rc = BLE_ERR_UNSUPPORTED; + goto done; + } + + connsm = ble_ll_conn_find_active_conn(le16toh(cmd->conn_handle)); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + goto done; + } + + /* timeout in 10ms units */ + connsm->sync_transfer_sync_timeout = sync_timeout * 10000; + connsm->sync_transfer_mode = cmd->mode; + connsm->sync_transfer_skip = skip; + + rc = BLE_ERR_SUCCESS; + +done: + rsp->conn_handle = cmd->conn_handle; + *rsplen = sizeof(*rsp); + return rc; +} + +int +ble_ll_set_default_sync_transfer_params(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_default_periodic_sync_transfer_params_cp *cmd = (const void *)cmdbuf; + uint16_t sync_timeout; + uint16_t skip; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->mode > 0x02) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + skip = le16toh(cmd->skip); + if (skip > 0x01f3) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + sync_timeout = le16toh(cmd->sync_timeout); + if ((sync_timeout < 0x000a) || (sync_timeout > 0x4000)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* we don't support any CTE yet */ + if (cmd->sync_cte_type) { + return BLE_ERR_UNSUPPORTED; + } + + /* timeout in 10ms units */ + g_ble_ll_conn_sync_transfer_params.sync_timeout_us = sync_timeout * 10000; + g_ble_ll_conn_sync_transfer_params.mode = cmd->mode; + g_ble_ll_conn_sync_transfer_params.max_skip = skip; + + return BLE_ERR_SUCCESS; +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_priv.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_priv.h new file mode 100644 index 0000000..e4ade10 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_conn_priv.h @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_CONN_PRIV_ +#define H_BLE_LL_CONN_PRIV_ + +#include "../include/controller/ble_ll_conn.h" +#include "../include/controller/ble_ll_hci.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Definitions for min/max RX/TX time/bytes values allowed for connections. + * Source: Core 5.0 specification, Vol 6, Part B, section 4.5.10 + */ +#define BLE_LL_CONN_SUPP_TIME_MIN (328) /* usecs */ +#define BLE_LL_CONN_SUPP_TIME_MAX (17040) /* usecs */ +#define BLE_LL_CONN_SUPP_TIME_MIN_UNCODED (328) /* usecs */ +#define BLE_LL_CONN_SUPP_TIME_MAX_UNCODED (2120) /* usecs */ +#define BLE_LL_CONN_SUPP_TIME_MIN_CODED (2704) /* usecs */ +#define BLE_LL_CONN_SUPP_TIME_MAX_CODED (17040) /* usecs */ +#define BLE_LL_CONN_SUPP_BYTES_MIN (27) /* bytes */ +#define BLE_LL_CONN_SUPP_BYTES_MAX (251) /* bytes */ + +/* Connection event timing */ +#define BLE_LL_CONN_INITIAL_OFFSET (1250) +#define BLE_LL_CONN_ITVL_USECS (1250) +#define BLE_LL_CONN_TX_WIN_USECS (1250) +#define BLE_LL_CONN_TX_OFF_USECS (1250) +#define BLE_LL_CONN_CE_USECS (625) +#define BLE_LL_CONN_TX_WIN_MIN (1) /* in tx win units */ +#define BLE_LL_CONN_SLAVE_LATENCY_MAX (499) + +/* Connection handle range */ +#define BLE_LL_CONN_MAX_CONN_HANDLE (0x0EFF) + +/* Offset (in bytes) of advertising address in connect request */ +#define BLE_LL_CONN_REQ_ADVA_OFF (BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN) + +/* Default authenticated payload timeout (30 seconds; in 10 msecs increments) */ +#define BLE_LL_CONN_DEF_AUTH_PYLD_TMO (3000) +#define BLE_LL_CONN_AUTH_PYLD_OS_TMO(x) ble_npl_time_ms_to_ticks32((x) * 10) + +/* Global Link Layer connection parameters */ +struct ble_ll_conn_global_params +{ + uint8_t master_chan_map[BLE_LL_CONN_CHMAP_LEN]; + uint8_t num_used_chans; + uint8_t supp_max_tx_octets; + uint8_t supp_max_rx_octets; + uint8_t conn_init_max_tx_octets; + uint8_t sugg_tx_octets; + uint16_t sugg_tx_time; + uint16_t conn_init_max_tx_time; + uint16_t conn_init_max_tx_time_uncoded; + uint16_t conn_init_max_tx_time_coded; + uint16_t supp_max_tx_time; + uint16_t supp_max_rx_time; +}; +extern struct ble_ll_conn_global_params g_ble_ll_conn_params; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +struct ble_ll_conn_sync_transfer_params +{ + uint32_t sync_timeout_us; + uint16_t max_skip; + uint8_t mode; +}; +extern struct ble_ll_conn_sync_transfer_params g_ble_ll_conn_sync_transfer_params; +#endif + +/* Some data structures used by other LL routines */ +SLIST_HEAD(ble_ll_conn_active_list, ble_ll_conn_sm); +STAILQ_HEAD(ble_ll_conn_free_list, ble_ll_conn_sm); +extern struct ble_ll_conn_active_list g_ble_ll_conn_active_list; +extern struct ble_ll_conn_free_list g_ble_ll_conn_free_list; + +/* Pointer to connection state machine we are trying to create */ +extern struct ble_ll_conn_sm *g_ble_ll_conn_create_sm; + +/* Generic interface */ +struct ble_ll_len_req; +struct ble_mbuf_hdr; +struct ble_ll_adv_sm; + +struct hci_create_conn +{ + uint16_t scan_itvl; + uint16_t scan_window; + uint8_t filter_policy; + uint8_t peer_addr_type; + uint8_t peer_addr[BLE_DEV_ADDR_LEN]; + uint8_t own_addr_type; + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +}; + +void ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm); +void ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err); +void ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om, + uint8_t hdr_byte, uint8_t length); +struct ble_ll_conn_sm *ble_ll_conn_sm_get(void); +void ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm, + struct hci_create_conn *hcc); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +void ble_ll_conn_ext_master_init(struct ble_ll_conn_sm *connsm, + struct hci_ext_create_conn *hcc); + +void ble_ll_conn_ext_set_params(struct ble_ll_conn_sm *connsm, + struct hci_ext_conn_params *hcc_params, + int phy); +#endif + +struct ble_ll_conn_sm *ble_ll_conn_find_active_conn(uint16_t handle); +void ble_ll_conn_update_eff_data_len(struct ble_ll_conn_sm *connsm); + +/* Advertising interface */ +int ble_ll_conn_slave_start(uint8_t *rxbuf, uint8_t pat, + struct ble_mbuf_hdr *rxhdr, bool force_csa2); + +/* Link Layer interface */ +void ble_ll_conn_module_init(void); +void ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, const uint8_t *chanmap); +void ble_ll_conn_module_reset(void); +void ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t len); +int ble_ll_conn_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa); +int ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); +void ble_ll_conn_rx_data_pdu(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr); +void ble_ll_init_rx_pkt_in(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *ble_hdr); +int ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr); +int ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok, + struct ble_mbuf_hdr *ble_hdr); +void ble_ll_conn_wfr_timer_exp(void); +void ble_ll_conn_init_wfr_timer_exp(void); +int ble_ll_conn_is_lru(struct ble_ll_conn_sm *s1, struct ble_ll_conn_sm *s2); +uint32_t ble_ll_conn_get_ce_end_time(void); +void ble_ll_conn_event_halt(void); +void ble_ll_conn_reset_pending_aux_conn_rsp(void); +bool ble_ll_conn_init_pending_aux_conn_rsp(void); +/* HCI */ +void ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm, + uint8_t reason); +void ble_ll_auth_pyld_tmo_event_send(struct ble_ll_conn_sm *connsm); +int ble_ll_conn_hci_disconnect_cmd(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_rd_rem_ver_cmd(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_set_chan_class(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_param_rr(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_param_nrr(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_create_cancel(ble_ll_hci_post_cmd_complete_cb *post_cmd_cb); +void ble_ll_conn_num_comp_pkts_event_send(struct ble_ll_conn_sm *connsm); +void ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status, + uint8_t *evbuf, struct ble_ll_adv_sm *advsm); +void ble_ll_conn_timeout(struct ble_ll_conn_sm *connsm, uint8_t ble_err); +int ble_ll_conn_hci_chk_conn_params(uint16_t itvl_min, uint16_t itvl_max, + uint16_t latency, uint16_t spvn_tmo); +int ble_ll_conn_hci_read_rem_features(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_rd_rssi(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, + uint8_t *rsplen); +int ble_ll_conn_hci_rd_chan_map(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_set_data_len(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_le_start_encrypt(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_hci_le_ltk_reply(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_le_ltk_neg_reply(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_wr_auth_pyld_tmo(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_rd_auth_pyld_tmo(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) +void ble_ll_conn_auth_pyld_timer_start(struct ble_ll_conn_sm *connsm); +#else +#define ble_ll_conn_auth_pyld_timer_start(x) +#endif + +int ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg); +int ble_ll_hci_acl_rx(struct os_mbuf *om, void *arg); + +int ble_ll_conn_hci_le_rd_phy(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rsp, uint8_t *rsplen); +int ble_ll_conn_hci_le_set_phy(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_conn_chk_phy_upd_start(struct ble_ll_conn_sm *connsm); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +int ble_ll_ext_conn_create(const uint8_t *cmdbuf, uint8_t cmdlen); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +int ble_ll_set_sync_transfer_params(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_set_default_sync_transfer_params(const uint8_t *cmdbuf, uint8_t len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_CONN_PRIV_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_ctrl.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_ctrl.c new file mode 100644 index 0000000..37b9e68 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_ctrl.c @@ -0,0 +1,2748 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_ctrl.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll_sync.h" +#include "ble_ll_conn_priv.h" + +/* To use spec sample data for testing */ +#undef BLE_LL_ENCRYPT_USE_TEST_DATA + +/* + * For console debug to show session key calculation. NOTE: if you define + * this the stack requirements for the LL task go up considerably. The + * default stack will not be enough and must be increased. + */ +#undef BLE_LL_ENCRYPT_DEBUG +#ifdef BLE_LL_ENCRYPT_DEBUG +#include "console/console.h" +#endif + +/* + * XXX: + * 1) Do I need to keep track of which procedures have already been done? + * Do I need to worry about repeating procedures? + * 2) Should we create pool of control pdu's?. Dont need more + * than the # of connections and can probably deal with quite a few less + * if we have lots of connections. + * 3) What about procedures that have been completed but try to restart? + * 4) NOTE: there is a supported features procedure. However, in the case + * of data length extension, if the receiving device does not understand + * the pdu or it does not support data length extension, the LL_UNKNOWN_RSP + * pdu is sent. That needs to be processed... + * 5) We are supposed to remember when we do the data length update proc if + * the device sent us an unknown rsp. We should not send it another len req. + * Implement this how? Through remote supported features? + * 8) How to count control pdus sent. DO we count enqueued + sent, or only + * sent (actually attempted to tx). Do we count failures? How? + */ + +/* + * XXX: I definitely have an issue with control procedures and connection + * param request procedure and connection update procedure. This was + * noted when receiving an unknown response. Right now, I am getting confused + * with connection parameter request and updates regarding which procedures + * are running. So I need to go look through all the code and see where I + * used the request procedure and the update procedure and make sure I am doing + * the correct thing. + */ + +/* + * This array contains the length of the CtrData field in LL control PDU's. + * Note that there is a one byte opcode which precedes this field in the LL + * control PDU, so total data channel payload length for the control pdu is + * one greater. + */ +const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES] = +{ + BLE_LL_CTRL_CONN_UPD_REQ_LEN, + BLE_LL_CTRL_CHAN_MAP_LEN, + BLE_LL_CTRL_TERMINATE_IND_LEN, + BLE_LL_CTRL_ENC_REQ_LEN, + BLE_LL_CTRL_ENC_RSP_LEN, + BLE_LL_CTRL_START_ENC_REQ_LEN, + BLE_LL_CTRL_START_ENC_RSP_LEN, + BLE_LL_CTRL_UNK_RSP_LEN, + BLE_LL_CTRL_FEATURE_LEN, + BLE_LL_CTRL_FEATURE_LEN, + BLE_LL_CTRL_PAUSE_ENC_REQ_LEN, + BLE_LL_CTRL_PAUSE_ENC_RSP_LEN, + BLE_LL_CTRL_VERSION_IND_LEN, + BLE_LL_CTRL_REJ_IND_LEN, + BLE_LL_CTRL_SLAVE_FEATURE_REQ_LEN, + BLE_LL_CTRL_CONN_PARAMS_LEN, + BLE_LL_CTRL_CONN_PARAMS_LEN, + BLE_LL_CTRL_REJECT_IND_EXT_LEN, + BLE_LL_CTRL_PING_LEN, + BLE_LL_CTRL_PING_LEN, + BLE_LL_CTRL_LENGTH_REQ_LEN, + BLE_LL_CTRL_LENGTH_REQ_LEN, + BLE_LL_CTRL_PHY_REQ_LEN, + BLE_LL_CTRL_PHY_RSP_LEN, + BLE_LL_CTRL_PHY_UPD_IND_LEN, + BLE_LL_CTRL_MIN_USED_CHAN_LEN, + BLE_LL_CTRL_CTE_REQ_LEN, + BLE_LL_CTRL_CTE_RSP_LEN, + BLE_LL_CTRL_PERIODIC_SYNC_IND_LEN, + BLE_LL_CTRL_CLOCK_ACCURACY_REQ_LEN, + BLE_LL_CTRL_CLOCK_ACCURACY_RSP_LEN, +}; + +/** + * Called to determine if a LL control procedure with an instant has + * been initiated. + * + * If the function returns a 0 it means no conflicting procedure has + * been initiated. Otherwise it returns the appropriate BLE error code to + * send. + * + * @param connsm Pointer to connection state machine. + * @param req_ctrl_proc The procedure that the peer is trying to initiate + * + * @return uint8_t + */ +uint8_t +ble_ll_ctrl_proc_with_instant_initiated(struct ble_ll_conn_sm *connsm, + uint8_t req_ctrl_proc) +{ + uint8_t err; + + switch (connsm->cur_ctrl_proc) { + case BLE_LL_CTRL_PROC_PHY_UPDATE: + case BLE_LL_CTRL_PROC_CONN_UPDATE: + case BLE_LL_CTRL_PROC_CONN_PARAM_REQ: + case BLE_LL_CTRL_PROC_CHAN_MAP_UPD: + if (req_ctrl_proc == connsm->cur_ctrl_proc) { + err = BLE_ERR_LMP_COLLISION; + } else if ((connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_CONN_UPDATE) && + (req_ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + err = BLE_ERR_LMP_COLLISION; + } else { + err = BLE_ERR_DIFF_TRANS_COLL; + } + break; + default: + err = 0; + } + + return err; +} + +/** + * Create a LL_REJECT_EXT_IND pdu. + * + * @param rej_opcode Opcode to be rejected. + * @param err: error response + * @param ctrdata: Pointer to where CtrData starts in pdu + */ +void +ble_ll_ctrl_rej_ext_ind_make(uint8_t rej_opcode, uint8_t err, uint8_t *ctrdata) +{ + ctrdata[0] = rej_opcode; + ctrdata[1] = err; +} + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/** + * Called to cancel a phy update procedure. + * + * @param connsm + * @param ble_err + */ +void +ble_ll_ctrl_phy_update_cancel(struct ble_ll_conn_sm *connsm, uint8_t ble_err) +{ + /* cancel any pending phy update procedures */ + CLR_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + + /* Check if the host wants an event */ + if (CONN_F_HOST_PHY_UPDATE(connsm)) { + ble_ll_hci_ev_phy_update(connsm, ble_err); + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + } + + /* Clear any bits for phy updates that might be in progress */ + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; +} +#endif + +static int +ble_ll_ctrl_len_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + int rc; + struct ble_ll_len_req ctrl_req; + + /* Extract parameters and check if valid */ + ctrl_req.max_rx_bytes = get_le16(dptr); + ctrl_req.max_rx_time = get_le16(dptr + 2); + ctrl_req.max_tx_bytes = get_le16(dptr + 4); + ctrl_req.max_tx_time = get_le16(dptr + 6); + + if ((ctrl_req.max_rx_bytes < BLE_LL_CONN_SUPP_BYTES_MIN) || + (ctrl_req.max_rx_time < BLE_LL_CONN_SUPP_TIME_MIN) || + (ctrl_req.max_tx_bytes < BLE_LL_CONN_SUPP_BYTES_MIN) || + (ctrl_req.max_tx_time < BLE_LL_CONN_SUPP_TIME_MIN)) { + rc = 1; + } else { + /* Update parameters */ + connsm->rem_max_rx_time = ctrl_req.max_rx_time; + connsm->rem_max_tx_time = ctrl_req.max_tx_time; + connsm->rem_max_rx_octets = ctrl_req.max_rx_bytes; + connsm->rem_max_tx_octets = ctrl_req.max_tx_bytes; + + /* Recalculate effective connection parameters */ + ble_ll_conn_update_eff_data_len(connsm); + rc = 0; + } + + return rc; +} + +/** + * Process a received LL_PING_RSP control pdu. + * + * NOTE: we dont have to reset the callout since this packet will have had a + * valid MIC and that will restart the authenticated payload timer + * + * @param connsm + */ +static void +ble_ll_ctrl_rx_ping_rsp(struct ble_ll_conn_sm *connsm) +{ + /* Stop the control procedure */ + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_LE_PING); +} + +/** + * Called when we receive either a connection parameter request or response. + * + * @param connsm + * @param dptr + * @param rspbuf + * @param opcode + * + * @return int + */ +static int +ble_ll_ctrl_conn_param_pdu_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf, uint8_t opcode) +{ + int rc; + int indicate; + uint8_t rsp_opcode; + uint8_t ble_err; + struct ble_ll_conn_params *req; + struct hci_conn_update *hcu; + + /* Extract parameters and check if valid */ + req = &connsm->conn_cp; + req->interval_min = get_le16(dptr); + req->interval_max = get_le16(dptr + 2); + req->latency = get_le16(dptr + 4); + req->timeout = get_le16(dptr + 6); + req->pref_periodicity = dptr[8]; + req->ref_conn_event_cnt = get_le16(dptr + 9); + req->offset0 = get_le16(dptr + 11); + req->offset1 = get_le16(dptr + 13); + req->offset2 = get_le16(dptr + 15); + req->offset3 = get_le16(dptr + 17); + req->offset4 = get_le16(dptr + 19); + req->offset5 = get_le16(dptr + 21); + + /* Check if parameters are valid */ + ble_err = BLE_ERR_SUCCESS; + rc = ble_ll_conn_hci_chk_conn_params(req->interval_min, + req->interval_max, + req->latency, + req->timeout); + if (rc) { + ble_err = BLE_ERR_INV_LMP_LL_PARM; + goto conn_param_pdu_exit; + } + + /* + * Check if there is a requested change to either the interval, timeout + * or latency. If not, this may just be an anchor point change and we do + * not have to notify the host. + * XXX: what if we dont like the parameters? When do we check that out? + */ + indicate = 1; + if (opcode == BLE_LL_CTRL_CONN_PARM_REQ) { + if ((connsm->conn_itvl >= req->interval_min) && + (connsm->conn_itvl <= req->interval_max) && + (connsm->supervision_tmo == req->timeout) && + (connsm->slave_latency == req->latency)) { + indicate = 0; + goto conn_parm_req_do_indicate; + } + } + + /* + * A change has been requested. Is it within the values specified by + * the host? Note that for a master we will not be processing a + * connect param request from a slave if we are currently trying to + * update the connection parameters. This means that the previous + * check is all we need for a master (when receiving a request). + */ + if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) || + (opcode == BLE_LL_CTRL_CONN_PARM_RSP)) { + /* + * Not sure what to do about the slave. It is possible that the + * current connection parameters are not the same ones as the local host + * has provided? Not sure what to do here. Do we need to remember what + * host sent us? For now, I will assume that we need to remember what + * the host sent us and check it out. + */ + hcu = &connsm->conn_param_req; + if (hcu->handle != 0) { + if (!((req->interval_min < hcu->conn_itvl_min) || + (req->interval_min > hcu->conn_itvl_max) || + (req->interval_max < hcu->conn_itvl_min) || + (req->interval_max > hcu->conn_itvl_max) || + (req->latency != hcu->conn_latency) || + (req->timeout != hcu->supervision_timeout))) { + indicate = 0; + } + } + } + +conn_parm_req_do_indicate: + /* + * XXX: are the connection update parameters acceptable? If not, we will + * need to know before we indicate to the host that they are acceptable. + */ + if (indicate) { + /* If Host masked out Remote Connection Parameter Request Event, we need to + * send Reject back to the remote device + */ + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ)){ + ble_err = BLE_ERR_UNSUPP_REM_FEATURE; + goto conn_param_pdu_exit; + } + + /* + * Send event to host. At this point we leave and wait to get + * an answer. + */ + ble_ll_hci_ev_rem_conn_parm_req(connsm, req); + connsm->host_reply_opcode = opcode; + connsm->csmflags.cfbit.awaiting_host_reply = 1; + rsp_opcode = 255; + } else { + /* Create reply to connection request */ + rsp_opcode = ble_ll_ctrl_conn_param_reply(connsm, rspbuf, req); + } + +conn_param_pdu_exit: + if (ble_err) { + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = opcode; + rspbuf[2] = ble_err; + } + return rsp_opcode; +} + +/** + * Called to make a connection update request LL control PDU + * + * Context: Link Layer + * + * @param connsm + * @param rsp + */ +static void +ble_ll_ctrl_conn_upd_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld, + struct ble_ll_conn_params *cp) +{ + uint16_t instant; + uint32_t dt; + uint32_t num_old_ce; + uint32_t new_itvl_usecs; + uint32_t old_itvl_usecs; + struct hci_conn_update *hcu; + struct ble_ll_conn_upd_req *req; + + /* + * Set instant. We set the instant to the current event counter plus + * the amount of slave latency as the slave may not be listening + * at every connection interval and we are not sure when the connect + * request will actually get sent. We add one more event plus the + * minimum as per the spec of 6 connection events. + */ + instant = connsm->event_cntr + connsm->slave_latency + 6 + 1; + + /* + * XXX: This should change in the future, but for now we will just + * start the new instant at the same anchor using win offset 0. + */ + /* Copy parameters in connection update structure */ + hcu = &connsm->conn_param_req; + req = &connsm->conn_update_req; + if (cp) { + /* XXX: so we need to make the new anchor point some time away + * from txwinoffset by some amount of msecs. Not sure how to do + that here. We dont need to, but we should. */ + /* Calculate offset from requested offsets (if any) */ + if (cp->offset0 != 0xFFFF) { + new_itvl_usecs = cp->interval_max * BLE_LL_CONN_ITVL_USECS; + old_itvl_usecs = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; + if ((int16_t)(cp->ref_conn_event_cnt - instant) >= 0) { + num_old_ce = cp->ref_conn_event_cnt - instant; + dt = old_itvl_usecs * num_old_ce; + dt += (cp->offset0 * BLE_LL_CONN_ITVL_USECS); + dt = dt % new_itvl_usecs; + } else { + num_old_ce = instant - cp->ref_conn_event_cnt; + dt = old_itvl_usecs * num_old_ce; + dt -= (cp->offset0 * BLE_LL_CONN_ITVL_USECS); + dt = dt % new_itvl_usecs; + dt = new_itvl_usecs - dt; + } + req->winoffset = dt / BLE_LL_CONN_TX_WIN_USECS; + } else { + req->winoffset = 0; + } + req->interval = cp->interval_max; + req->timeout = cp->timeout; + req->latency = cp->latency; + req->winsize = 1; + } else { + req->interval = hcu->conn_itvl_max; + req->timeout = hcu->supervision_timeout; + req->latency = hcu->conn_latency; + req->winoffset = 0; + req->winsize = connsm->tx_win_size; + } + req->instant = instant; + + /* XXX: make sure this works for the connection parameter request proc. */ + pyld[0] = req->winsize; + put_le16(pyld + 1, req->winoffset); + put_le16(pyld + 3, req->interval); + put_le16(pyld + 5, req->latency); + put_le16(pyld + 7, req->timeout); + put_le16(pyld + 9, instant); + + /* Set flag in state machine to denote we have scheduled an update */ + connsm->csmflags.cfbit.conn_update_sched = 1; +} + +/** + * Called to process and UNKNOWN_RSP LL control packet. + * + * Context: Link Layer Task + * + * @param dptr + */ +static int +ble_ll_ctrl_proc_unk_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr, uint8_t *rspdata) +{ + uint8_t ctrl_proc; + uint8_t opcode; + + /* Get opcode of unknown LL control frame */ + opcode = dptr[0]; + + /* Convert opcode to control procedure id */ + switch (opcode) { + case BLE_LL_CTRL_LENGTH_REQ: + ctrl_proc = BLE_LL_CTRL_PROC_DATA_LEN_UPD; + BLE_LL_CONN_CLEAR_FEATURE(connsm, BLE_LL_FEAT_DATA_LEN_EXT); + break; + case BLE_LL_CTRL_CONN_UPDATE_IND: + ctrl_proc = BLE_LL_CTRL_PROC_CONN_UPDATE; + break; + case BLE_LL_CTRL_SLAVE_FEATURE_REQ: + ctrl_proc = BLE_LL_CTRL_PROC_FEATURE_XCHG; + BLE_LL_CONN_CLEAR_FEATURE(connsm, BLE_LL_FEAT_SLAVE_INIT); + break; + case BLE_LL_CTRL_CONN_PARM_REQ: + BLE_LL_CONN_CLEAR_FEATURE(connsm, BLE_LL_FEAT_CONN_PARM_REQ); + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + ble_ll_ctrl_conn_upd_make(connsm, rspdata, NULL); + connsm->reject_reason = BLE_ERR_SUCCESS; + return BLE_LL_CTRL_CONN_UPDATE_IND; + } + /* Else falls through. */ + /* note: fall-through intentional */ + case BLE_LL_CTRL_CONN_PARM_RSP: + ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ; + break; + case BLE_LL_CTRL_PING_REQ: + /* LL can authenticate remote device even if remote device does not + * support LE Ping feature. + */ + ctrl_proc = BLE_LL_CTRL_PROC_LE_PING; + BLE_LL_CONN_CLEAR_FEATURE(connsm, BLE_LL_FEAT_LE_PING); + break; +#if (BLE_LL_BT5_PHY_SUPPORTED ==1) + case BLE_LL_CTRL_PHY_REQ: + ble_ll_ctrl_phy_update_cancel(connsm, BLE_ERR_UNSUPP_REM_FEATURE); + ctrl_proc = BLE_LL_CTRL_PROC_PHY_UPDATE; + break; +#endif + default: + ctrl_proc = BLE_LL_CTRL_PROC_NUM; + break; + } + + /* If we are running this one currently, stop it */ + if (connsm->cur_ctrl_proc == ctrl_proc) { + /* Stop the control procedure */ + ble_ll_ctrl_proc_stop(connsm, ctrl_proc); + if (ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + ble_ll_hci_ev_conn_update(connsm, BLE_ERR_UNSUPP_REM_FEATURE); + } else if (ctrl_proc == BLE_LL_CTRL_PROC_FEATURE_XCHG) { + if (connsm->csmflags.cfbit.pending_hci_rd_features) { + ble_ll_hci_ev_rd_rem_used_feat(connsm, + BLE_ERR_UNSUPP_REM_FEATURE); + } + connsm->csmflags.cfbit.pending_hci_rd_features = 0; + } + } + + return BLE_ERR_MAX; +} + +/** + * Callback when LL control procedure times out (for a given connection). If + * this is called, it means that we need to end the connection because it + * has not responded to a LL control request. + * + * Context: Link Layer + * + * @param arg Pointer to connection state machine. + */ +static void +ble_ll_ctrl_proc_rsp_timer_cb(struct ble_npl_event *ev) +{ + /* Control procedure has timed out. Kill the connection */ + ble_ll_conn_timeout((struct ble_ll_conn_sm *)ble_npl_event_get_arg(ev), + BLE_ERR_LMP_LL_RSP_TMO); +} + +static void +ble_ll_ctrl_start_rsp_timer(struct ble_ll_conn_sm *connsm) +{ + ble_npl_callout_init(&connsm->ctrl_proc_rsp_timer, + &g_ble_ll_data.ll_evq, + ble_ll_ctrl_proc_rsp_timer_cb, + connsm); + + /* Re-start timer. Control procedure timeout is 40 seconds */ + ble_npl_callout_reset(&connsm->ctrl_proc_rsp_timer, + ble_npl_time_ms_to_ticks32(BLE_LL_CTRL_PROC_TIMEOUT_MS)); +} + +/** + * Convert a phy mask to a numeric phy value. + * + * NOTE: only one bit should be set here and there should be at least one. + * If this function returns a 0 it is an error! + * + * @param phy_mask Bitmask of phy + * + * @return uint8_t The numeric value associated with the phy mask + * + * BLE_HCI_LE_PHY_1M (1) + * BLE_HCI_LE_PHY_2M (2) + * BLE_HCI_LE_PHY_CODED (3) + */ +uint8_t +ble_ll_ctrl_phy_from_phy_mask(uint8_t phy_mask) +{ + uint8_t phy; + + /* + * NOTE: wipe out unsupported PHYs. There should not be an unsupported + * in this mask if the other side is working correctly. + */ +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + phy_mask &= ~BLE_HCI_LE_PHY_2M_PREF_MASK; +#endif +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + phy_mask &= ~BLE_HCI_LE_PHY_CODED_PREF_MASK; +#endif + + if (phy_mask & BLE_PHY_MASK_1M) { + phy = BLE_PHY_1M; + phy_mask &= ~BLE_PHY_MASK_1M; + } else if (phy_mask & BLE_PHY_MASK_2M) { + phy = BLE_PHY_2M; + phy_mask &= ~BLE_PHY_MASK_2M; + } else if (phy_mask & BLE_PHY_MASK_CODED) { + phy = BLE_PHY_CODED; + phy_mask &= ~BLE_PHY_MASK_CODED; + } else { + phy = 0; + } + + if (phy_mask != 0) { + phy = 0; + } + + return phy; +} + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +uint8_t +ble_ll_ctrl_phy_tx_transition_get(uint8_t phy_mask) +{ + /* + * Evaluate PHYs in transition starting from the one with longest TX time + * so we select the one that allows shortest payload to be sent. This is + * to make sure we do not violate timing restriction on new PHY. + */ + if (phy_mask & BLE_PHY_MASK_CODED) { + return BLE_PHY_CODED; + } else if (phy_mask & BLE_PHY_MASK_1M) { + return BLE_PHY_1M; + } else if (phy_mask & BLE_PHY_MASK_2M) { + return BLE_PHY_2M; + } + + return 0; +} + +void +ble_ll_ctrl_phy_update_proc_complete(struct ble_ll_conn_sm *connsm) +{ + int chk_proc_stop; + int chk_host_phy; + + chk_proc_stop = 1; + chk_host_phy = 1; + + connsm->phy_tx_transition = 0; + + if (CONN_F_PEER_PHY_UPDATE(connsm)) { + CONN_F_PEER_PHY_UPDATE(connsm) = 0; + } else if (CONN_F_CTRLR_PHY_UPDATE(connsm)) { + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; + } else { + /* Must be a host-initiated update */ + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + chk_host_phy = 0; + if (CONN_F_PHY_UPDATE_EVENT(connsm) == 0) { + ble_ll_hci_ev_phy_update(connsm, BLE_ERR_SUCCESS); + } + } + + /* Must check if we need to start host procedure */ + if (chk_host_phy) { + if (CONN_F_HOST_PHY_UPDATE(connsm)) { + if (ble_ll_conn_chk_phy_upd_start(connsm)) { + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + } else { + chk_proc_stop = 0; + } + } + } + + if (chk_proc_stop) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + } +} + +/** + * + * There is probably a better way for the controller to choose which PHY use. + * There are no BER metrics and RSSI does not give you S/N so for now we will + * choose this heirarchy: + * -> if 2Mbps available, use it. + * -> If 1Mbps available, use it. + * -> otherwise use coded phy. + * + * @param prefs The mask of preferred phys + * @return uint8_t The phy to use (not a mask) + */ +static uint8_t +ble_ll_ctrl_find_new_phy(uint8_t phy_mask_prefs) +{ + uint8_t new_phy; + + new_phy = phy_mask_prefs; + if (new_phy) { + if (new_phy & BLE_PHY_MASK_2M) { + new_phy = BLE_PHY_2M; + } else if (new_phy & BLE_PHY_MASK_1M) { + new_phy = BLE_PHY_1M; + } else { + new_phy = BLE_PHY_CODED; + } + } + + return new_phy; +} + +/** + * Create a LL_PHY_UPDATE_IND pdu + * + * @param connsm Pointer to connection state machine + * @param dptr Pointer to PHY_REQ or PHY_RSP data. + * @param ctrdata: Pointer to where CtrData of UPDATE_IND pdu starts + * @param slave_req flag denoting if slave requested this. 0: no 1:yes + */ +static void +ble_ll_ctrl_phy_update_ind_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *ctrdata, int slave_req) +{ + uint8_t m_to_s; + uint8_t s_to_m; + uint8_t tx_phys; + uint8_t rx_phys; + uint16_t instant; + uint8_t is_slave_sym = 0; + + /* Get preferences from PDU */ + tx_phys = dptr[0]; + rx_phys = dptr[1]; + + /* If we are master, check if slave requested symmetric PHY */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + is_slave_sym = tx_phys == rx_phys; + is_slave_sym &= __builtin_popcount(tx_phys) == 1; + } + + /* Get m_to_s and s_to_m masks */ + if (slave_req) { + m_to_s = connsm->phy_data.host_pref_tx_phys_mask & rx_phys; + s_to_m = connsm->phy_data.host_pref_rx_phys_mask & tx_phys; + } else { + m_to_s = connsm->phy_data.req_pref_tx_phys_mask & rx_phys; + s_to_m = connsm->phy_data.req_pref_rx_phys_mask & tx_phys; + } + + if (is_slave_sym) { + /* + * If either s_to_m or m_to_s is 0, it means for at least one direction + * requested PHY is not our preferred one so make sure we keep current + * PHY in both directions + * + * Core 5.2, Vol 6, PartB, 5.1.10 + * If the slave specified a single PHY in both the TX_PHYS and + * RX_PHYS fields and both fields are the same, the master shall + * either select the PHY specified by the slave for both directions + * or shall leave both directions unchanged. + */ + if ((s_to_m == 0) || (m_to_s == 0)) { + s_to_m = 0; + m_to_s = 0; + } else { + BLE_LL_ASSERT(s_to_m == m_to_s); + } + } + + /* Calculate new PHYs to use */ + m_to_s = ble_ll_ctrl_find_new_phy(m_to_s); + s_to_m = ble_ll_ctrl_find_new_phy(s_to_m); + + /* Make sure we do not indicate PHY change if the same as current one */ + if (m_to_s == connsm->phy_data.cur_tx_phy) { + m_to_s = 0; + } + if (s_to_m == connsm->phy_data.cur_rx_phy) { + s_to_m = 0; + } + + /* At this point, m_to_s and s_to_m are not masks; they are numeric */ + + /* + * If not changing we still send update ind. Check if hosts expects + * the event and if so send it. Stop control procedure if it is the + * one running. + */ + if ((m_to_s == 0) && (s_to_m == 0)) { + if (CONN_F_PEER_PHY_UPDATE(connsm)) { + CONN_F_PEER_PHY_UPDATE(connsm) = 0; + } else if (CONN_F_CTRLR_PHY_UPDATE(connsm)) { + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + } else { + ble_ll_hci_ev_phy_update(connsm, BLE_ERR_SUCCESS); + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + } + instant = 0; + } else { + /* Determine instant we will use. 6 more is minimum */ + instant = connsm->event_cntr + connsm->slave_latency + 6 + 1; + connsm->phy_instant = instant; + CONN_F_PHY_UPDATE_SCHED(connsm) = 1; + + /* Set new phys to use when instant occurs */ + connsm->phy_data.new_tx_phy = m_to_s; + connsm->phy_data.new_rx_phy = s_to_m; + + /* Convert m_to_s and s_to_m to masks */ + if (m_to_s) { + m_to_s = 1 << (m_to_s - 1); + } + + if (s_to_m) { + s_to_m = 1 << (s_to_m - 1); + } + } + + ctrdata[0] = m_to_s; + ctrdata[1] = s_to_m; + put_le16(ctrdata + 2, instant); +} + +/** + * Create a LL_PHY_REQ or LL_PHY_RSP pdu + * + * @param connsm Pointer to connection state machine + * @param ctrdata: Pointer to where CtrData starts in pdu + */ +static void +ble_ll_ctrl_phy_req_rsp_make(struct ble_ll_conn_sm *connsm, uint8_t *ctrdata) +{ + /* If no preference we use current phy */ + if (connsm->phy_data.host_pref_tx_phys_mask == 0) { + ctrdata[0] = CONN_CUR_TX_PHY_MASK(connsm); + } else { + ctrdata[0] = connsm->phy_data.host_pref_tx_phys_mask; + } + if (connsm->phy_data.host_pref_rx_phys_mask == 0) { + ctrdata[1] = CONN_CUR_RX_PHY_MASK(connsm); + } else { + ctrdata[1] = connsm->phy_data.host_pref_rx_phys_mask; + } +} + +static uint8_t +ble_ll_ctrl_rx_phy_req(struct ble_ll_conn_sm *connsm, uint8_t *req, + uint8_t *rsp) +{ + uint8_t rsp_opcode; + uint8_t err; + + /* + * XXX: TODO if we have an instant in progress we should end connection. + * At least it seems that is the case. Need to figure out more from + * the spec here. + */ + + /* Check if we have already initiated a procedure with an instant */ + err = ble_ll_ctrl_proc_with_instant_initiated(connsm, + BLE_LL_CTRL_PROC_PHY_UPDATE); + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + if (err) { + ble_ll_ctrl_rej_ext_ind_make(BLE_LL_CTRL_PHY_REQ, err, rsp); + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + } else { + /* + * NOTE: do not change order of these two lines as the call to + * make the LL_PHY_UPDATE_IND pdu might clear the flag. + */ + CONN_F_PEER_PHY_UPDATE(connsm) = 1; + ble_ll_ctrl_phy_update_ind_make(connsm, req, rsp, 1); + rsp_opcode = BLE_LL_CTRL_PHY_UPDATE_IND; + } + } else { + /* XXX: deal with other control procedures that we need to stop */ + if (err) { + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) { + ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer); + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; + } + + /* If there is a PHY update procedure pending cancel it */ + ble_ll_ctrl_phy_update_cancel(connsm, err); + + /* XXX: ? Should not be any phy update events */ + CONN_F_PHY_UPDATE_EVENT(connsm) = 0; + } + + /* XXX: TODO: if we started another procedure with an instant + * why are we doing this? Need to look into this.*/ + + /* Respond to master's phy update procedure */ + CONN_F_PEER_PHY_UPDATE(connsm) = 1; + ble_ll_ctrl_phy_req_rsp_make(connsm, rsp); + rsp_opcode = BLE_LL_CTRL_PHY_RSP; + + connsm->phy_tx_transition = ble_ll_ctrl_phy_tx_transition_get(req[1] | rsp[0]); + + /* Start response timer */ + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_PHY_UPDATE; + ble_ll_ctrl_start_rsp_timer(connsm); + } + return rsp_opcode; +} + +/** + * Process a received LL_PHY_RSP pdu + * + * @param connsm + * @param dptr Pointer to LL_PHY_RSP ctrdata + * @param rsp Pointer to CtrData of PHY_UPDATE_IND. + * + * @return uint8_t + */ +static uint8_t +ble_ll_ctrl_rx_phy_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rsp) +{ + uint8_t rsp_opcode; + + rsp_opcode = BLE_ERR_MAX; + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) { + ble_ll_ctrl_phy_update_ind_make(connsm, dptr, rsp, 0); + ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer); + rsp_opcode = BLE_LL_CTRL_PHY_UPDATE_IND; + } + + /* + * If not in the process of doing this control procedure something + * is wrong. End connection? Assert? + * + * XXX: TODO count some stat? + */ + } else { + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* NOTE: slave should never receive one of these */ + + return rsp_opcode; +} + +/** + * Called when a LL_PHY_UPDATE_IND pdu is received + * + * NOTE: slave is the only device that should receive this. + * + * @param connsm + * @param dptr + * + * @return uint8_t + */ +static uint8_t +ble_ll_ctrl_rx_phy_update_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + int no_change; + uint8_t new_m_to_s_mask; + uint8_t new_s_to_m_mask; + uint8_t new_tx_phy; + uint8_t new_rx_phy; + uint16_t instant; + uint16_t delta; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* + * Reception stops the procedure response timer but does not + * complete the procedure + */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) { + ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer); + } + + /* + * XXX: Should we check to see if we are expecting to receive one + * of these, and if not, kill connection? Meaning we better be + * doing either a PEER, CTRLR, or HOST phy update. + */ + /* get the new phy masks and see if we need to change */ + new_m_to_s_mask = dptr[0]; + new_s_to_m_mask = dptr[1]; + instant = get_le16(dptr + 2); + + if ((new_m_to_s_mask == 0) && (new_s_to_m_mask == 0)) { + /* No change in phy */ + no_change = 1; + } else { + no_change = 0; + /* + * NOTE: from the slaves perspective, the m to s phy is the one + * that the slave will receive on; s to m is the one it will + * transmit on + */ + new_rx_phy = ble_ll_ctrl_phy_from_phy_mask(new_m_to_s_mask); + new_tx_phy = ble_ll_ctrl_phy_from_phy_mask(new_s_to_m_mask); + + if ((new_tx_phy == 0) && (new_rx_phy == 0)) { + /* XXX: this is an error! What to do??? */ + no_change = 1; + } + + if ((new_tx_phy == connsm->phy_data.cur_tx_phy) && + (new_rx_phy == connsm->phy_data.cur_rx_phy)) { + no_change = 1; + } + } + + if (!no_change) { + /* If instant is in the past, we have to end the connection */ + delta = (instant - connsm->event_cntr) & 0xFFFF; + if (delta >= 32767) { + ble_ll_conn_timeout(connsm, BLE_ERR_INSTANT_PASSED); + } else { + connsm->phy_data.new_tx_phy = new_tx_phy; + connsm->phy_data.new_rx_phy = new_rx_phy; + connsm->phy_instant = instant; + CONN_F_PHY_UPDATE_SCHED(connsm) = 1; + } + return BLE_ERR_MAX; + } + + ble_ll_ctrl_phy_update_proc_complete(connsm); + + return BLE_ERR_MAX; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +/** + * Called when a BLE_LL_CTRL_PERIODIC_SYNC_IND PDU is received + * + * @param connsm + * @param dptr + * + * @return uint8_t + */ +static uint8_t +ble_ll_ctrl_rx_periodic_sync_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + if (connsm->sync_transfer_mode) { + ble_ll_sync_periodic_ind(connsm, dptr, connsm->sync_transfer_mode == 1, + connsm->sync_transfer_skip, + connsm->sync_transfer_sync_timeout); + } + + return BLE_ERR_MAX; +} +#endif + +/** + * Create a link layer length request or length response PDU. + * + * NOTE: this function does not set the LL data pdu header nor does it + * set the opcode in the buffer. + * + * @param connsm + * @param dptr: Pointer to where control pdu payload starts + */ +static void +ble_ll_ctrl_datalen_upd_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + put_le16(dptr + 1, connsm->max_rx_octets); + put_le16(dptr + 3, connsm->max_rx_time); + put_le16(dptr + 5, connsm->max_tx_octets); + put_le16(dptr + 7, connsm->max_tx_time); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +void +ble_ll_calc_session_key(struct ble_ll_conn_sm *connsm) +{ +#ifdef BLE_LL_ENCRYPT_DEBUG + int cnt; +#endif + + /* XXX: possibly have some way out of this if this locks up */ + while (1) { + if (!ble_hw_encrypt_block(&connsm->enc_data.enc_block)) { + break; + } + } + +#ifdef BLE_LL_ENCRYPT_DEBUG + console_printf("Calculating Session Key for handle=%u", + connsm->conn_handle); + + console_printf("\nLTK:"); + for (cnt = 0; cnt < 16; ++cnt) { + console_printf("%02x", connsm->enc_data.enc_block.key[cnt]); + } + console_printf("\nSKD:"); + for (cnt = 0; cnt < 16; ++cnt) { + console_printf("%02x", connsm->enc_data.enc_block.plain_text[cnt]); + } + console_printf("\nSession Key:"); + for (cnt = 0; cnt < 16; ++cnt) { + console_printf("%02x", connsm->enc_data.enc_block.cipher_text[cnt]); + } + console_printf("\nIV:"); + for (cnt = 0; cnt < 8; ++ cnt) { + console_printf("%02x", connsm->enc_data.iv[cnt]); + } + console_printf("\n"); +#endif +} + +/** + * Called to determine if this is a control PDU we are allowed to send. This + * is called when a link is being encrypted, as only certain control PDU's + * area lowed to be sent. + * + * XXX: the current code may actually allow some control pdu's to be sent + * in states where they shouldnt. I dont expect those states to occur so I + * dont try to check for them but we could do more... for example there are + * different PDUs allowed for master/slave and TX/RX + * + * @param llid + * @param opcode + * @param len + * + * @return int + */ +static int +ble_ll_ctrl_enc_allowed_pdu(uint8_t llid, uint8_t len, uint8_t opcode) +{ + int allowed; + + allowed = 0; + + switch (llid) { + case BLE_LL_LLID_CTRL: + switch (opcode) { + case BLE_LL_CTRL_REJECT_IND: + case BLE_LL_CTRL_REJECT_IND_EXT: + case BLE_LL_CTRL_START_ENC_RSP: + case BLE_LL_CTRL_START_ENC_REQ: + case BLE_LL_CTRL_ENC_REQ: + case BLE_LL_CTRL_ENC_RSP: + case BLE_LL_CTRL_PAUSE_ENC_REQ: + case BLE_LL_CTRL_PAUSE_ENC_RSP: + case BLE_LL_CTRL_TERMINATE_IND: + allowed = 1; + break; + } + break; + case BLE_LL_LLID_DATA_FRAG: + if (len == 0) { + /* Empty PDUs are allowed */ + allowed = 1; + } + break; + } + + return allowed; +} + +int +ble_ll_ctrl_enc_allowed_pdu_rx(struct os_mbuf *rxpdu) +{ + uint8_t llid; + uint8_t len; + uint8_t opcode; + + llid = rxpdu->om_data[0] & BLE_LL_DATA_HDR_LLID_MASK; + len = rxpdu->om_data[1]; + if (llid == BLE_LL_LLID_CTRL) { + opcode = rxpdu->om_data[2]; + } else { + opcode = 0; + } + + return ble_ll_ctrl_enc_allowed_pdu(llid, len, opcode); +} + +int +ble_ll_ctrl_enc_allowed_pdu_tx(struct os_mbuf_pkthdr *pkthdr) +{ + struct os_mbuf *m; + struct ble_mbuf_hdr *ble_hdr; + uint8_t llid; + uint8_t len; + uint8_t opcode; + + m = OS_MBUF_PKTHDR_TO_MBUF(pkthdr); + ble_hdr = BLE_MBUF_HDR_PTR(m); + + llid = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + len = ble_hdr->txinfo.pyld_len; + if (llid == BLE_LL_LLID_CTRL) { + opcode = m->om_data[0]; + } else { + opcode = 0; + } + + return ble_ll_ctrl_enc_allowed_pdu(llid, len, opcode); +} + +int +ble_ll_ctrl_is_start_enc_rsp(struct os_mbuf *txpdu) +{ + int is_start_enc_rsp; + uint8_t opcode; + uint8_t llid; + struct ble_mbuf_hdr *ble_hdr; + + is_start_enc_rsp = 0; + ble_hdr = BLE_MBUF_HDR_PTR(txpdu); + + llid = ble_hdr->txinfo.hdr_byte & BLE_LL_DATA_HDR_LLID_MASK; + if (llid == BLE_LL_LLID_CTRL) { + opcode = txpdu->om_data[0]; + if (opcode == BLE_LL_CTRL_START_ENC_RSP) { + is_start_enc_rsp = 1; + } + } + + return is_start_enc_rsp; +} + +/** + * Called to create and send a LL_START_ENC_REQ + * + * @param connsm + * @param err + * + * @return int + */ +int +ble_ll_ctrl_start_enc_send(struct ble_ll_conn_sm *connsm) +{ + int rc; + struct os_mbuf *om; + + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, + sizeof(struct ble_mbuf_hdr)); + if (om) { + om->om_data[0] = BLE_LL_CTRL_START_ENC_REQ; + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, 1); + + /* Wait for LL_START_ENC_RSP. If there is already procedure in progress, + * LL response timer is already running. + */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_IDLE) { + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_ENCRYPT; + ble_ll_ctrl_start_rsp_timer(connsm); + } + + rc = 0; + } else { + rc = -1; + } + return rc; +} + +/** + * Create a link layer control "encrypt request" PDU. + * + * The LL_ENC_REQ PDU format is: + * Rand (8) + * EDIV (2) + * SKDm (8) + * IVm (4) + * + * The random number and encrypted diversifier come from the host command. + * Controller generates master portion of SDK and IV. + * + * NOTE: this function does not set the LL data pdu header nor does it + * set the opcode in the buffer. + * + * @param connsm + * @param dptr: Pointer to where control pdu payload starts + */ +static void +ble_ll_ctrl_enc_req_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + put_le64(dptr, connsm->enc_data.host_rand_num); + put_le16(dptr + 8, connsm->enc_data.enc_div); + +#ifdef BLE_LL_ENCRYPT_USE_TEST_DATA + /* IV stored LSB to MSB, IVm is LSB, IVs is MSB */ + put_le64(dptr + 10, g_bletest_SKDm); + swap_buf(connsm->enc_data.enc_block.plain_text + 8, dptr + 10, 8); + put_le32(dptr + 18, g_bletest_IVm); + memcpy(connsm->enc_data.iv, dptr + 18, 4); + return; +#endif + + ble_ll_rand_data_get(connsm->enc_data.enc_block.plain_text + 8, 8); + swap_buf(dptr + 10, connsm->enc_data.enc_block.plain_text + 8, 8); + ble_ll_rand_data_get(connsm->enc_data.iv, 4); + memcpy(dptr + 18, connsm->enc_data.iv, 4); +} + +/** + * Called when LL_ENC_RSP is received by the master. + * + * Context: Link Layer Task. + * + * Format of the LL_ENC_RSP is: + * SKDs (8) + * IVs (4) + * + * The master now has the long term key (from the start encrypt command) + * and the SKD (stored in the plain text encryption block). From this the + * sessionKey is generated. + * + * @param connsm + * @param dptr + */ +static void +ble_ll_ctrl_rx_enc_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + /* Calculate session key now that we have received the ENC_RSP */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_ENCRYPT) { + /* In case we were already encrypted we need to reset packet counters */ + connsm->enc_data.rx_pkt_cntr = 0; + connsm->enc_data.tx_pkt_cntr = 0; + connsm->enc_data.tx_encrypted = 0; + + swap_buf(connsm->enc_data.enc_block.plain_text, dptr, 8); + memcpy(connsm->enc_data.iv + 4, dptr + 8, 4); + ble_ll_calc_session_key(connsm); + connsm->enc_data.enc_state = CONN_ENC_S_START_ENC_REQ_WAIT; + } +} + +/** + * Called when we have received a LL control encryption request PDU. This + * should only be received by a slave. + * + * The LL_ENC_REQ PDU format is: + * Rand (8) + * EDIV (2) + * SKDm (8) + * IVm (4) + * + * This function returns the response opcode. Typically this will be ENC_RSP + * but it could be a reject ind. Note that the caller of this function + * will send the REJECT_IND_EXT if supported by remote. + * + * NOTE: if this is received by a master we will silently discard the PDU + * (denoted by return BLE_ERR_MAX). + * + * @param connsm + * @param dptr Pointer to start of encrypt request data. + * @param rspbuf + */ +static uint8_t +ble_ll_ctrl_rx_enc_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspdata) +{ + if (connsm->conn_role != BLE_LL_CONN_ROLE_SLAVE) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + + connsm->enc_data.enc_state = CONN_ENC_S_LTK_REQ_WAIT; + + /* In case we were already encrypted we need to reset packet counters */ + connsm->enc_data.rx_pkt_cntr = 0; + connsm->enc_data.tx_pkt_cntr = 0; + connsm->enc_data.tx_encrypted = 0; + + /* Extract information from request */ + connsm->enc_data.host_rand_num = get_le64(dptr); + connsm->enc_data.enc_div = get_le16(dptr + 8); + +#if BLE_LL_ENCRYPT_USE_TEST_DATA + swap_buf(connsm->enc_data.enc_block.plain_text + 8, dptr + 10, 8); + memcpy(connsm->enc_data.iv, dptr + 18, 4); + + put_le64(rspdata, g_bletest_SKDs); + swap_buf(connsm->enc_data.enc_block.plain_text, rspdata, 8); + put_le32(rspdata + 8, g_bletest_IVs); + memcpy(connsm->enc_data.iv + 4, rspdata + 8, 4); + return BLE_LL_CTRL_ENC_RSP; +#endif + + swap_buf(connsm->enc_data.enc_block.plain_text + 8, dptr + 10, 8); + memcpy(connsm->enc_data.iv, dptr + 18, 4); + + /* Create the ENC_RSP. Concatenate our SKD and IV */ + ble_ll_rand_data_get(connsm->enc_data.enc_block.plain_text, 8); + swap_buf(rspdata, connsm->enc_data.enc_block.plain_text, 8); + ble_ll_rand_data_get(connsm->enc_data.iv + 4, 4); + memcpy(rspdata + 8, connsm->enc_data.iv + 4, 4); + + return BLE_LL_CTRL_ENC_RSP; +} + +static uint8_t +ble_ll_ctrl_rx_start_enc_req(struct ble_ll_conn_sm *connsm) +{ + int rc; + + /* Only master should receive start enc request */ + rc = BLE_ERR_MAX; + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + /* We only want to send a START_ENC_RSP if we havent yet */ + if (connsm->enc_data.enc_state == CONN_ENC_S_START_ENC_REQ_WAIT) { + connsm->enc_data.enc_state = CONN_ENC_S_START_ENC_RSP_WAIT; + rc = BLE_LL_CTRL_START_ENC_RSP; + } + } else { + rc = BLE_LL_CTRL_UNKNOWN_RSP; + } + return rc; +} + +static uint8_t +ble_ll_ctrl_rx_pause_enc_req(struct ble_ll_conn_sm *connsm) +{ + int rc; + + /* + * The spec does not say what to do here, but if we receive a pause + * encryption request and we are not encrypted, what do we do? We + * ignore it... + */ + rc = BLE_ERR_MAX; + if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && + (connsm->enc_data.enc_state == CONN_ENC_S_ENCRYPTED)) { + rc = BLE_LL_CTRL_PAUSE_ENC_RSP; + } else { + rc = BLE_LL_CTRL_UNKNOWN_RSP; + } + + return rc; +} + +/** + * Called when a LL control pdu with opcode PAUSE_ENC_RSP is received. + * + * + * @param connsm + * + * @return uint8_t + */ +static uint8_t +ble_ll_ctrl_rx_pause_enc_rsp(struct ble_ll_conn_sm *connsm) +{ + int rc; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + rc = BLE_LL_CTRL_PAUSE_ENC_RSP; + } else if (connsm->enc_data.enc_state == CONN_ENC_S_PAUSE_ENC_RSP_WAIT) { + /* Master sends back unencrypted LL_PAUSE_ENC_RSP. + * From this moment encryption is paused. + */ + rc = BLE_ERR_MAX; + connsm->enc_data.enc_state = CONN_ENC_S_PAUSED; + } else { + rc = BLE_LL_CTRL_UNKNOWN_RSP; + } + + return rc; +} + +/** + * Called when we have received a LL_CTRL_START_ENC_RSP. + * + * Context: Link-layer task + * + * @param connsm + * + * @return uint8_t + */ +static uint8_t +ble_ll_ctrl_rx_start_enc_rsp(struct ble_ll_conn_sm *connsm) +{ + int rc; + + /* Not in proper state. Discard */ + if (connsm->enc_data.enc_state != CONN_ENC_S_START_ENC_RSP_WAIT) { + return BLE_ERR_MAX; + } + + /* If master, we are done. Stop control procedure and sent event to host */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_ENCRYPT); + + /* We are encrypted */ + connsm->enc_data.enc_state = CONN_ENC_S_ENCRYPTED; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + ble_ll_conn_auth_pyld_timer_start(connsm); +#endif + rc = BLE_ERR_MAX; + } else { + /* Procedure has completed but slave needs to send START_ENC_RSP */ + rc = BLE_LL_CTRL_START_ENC_RSP; + + /* Stop timer if it was started when sending START_ENC_REQ */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_ENCRYPT) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_ENCRYPT); + } + } + + /* + * XXX: for now, a Slave sends this event when it receivest the + * START_ENC_RSP from the master. It might be technically incorrect + * to send it before we transmit our own START_ENC_RSP. + */ + ble_ll_hci_ev_encrypt_chg(connsm, BLE_ERR_SUCCESS); + + return rc; +} + +#endif + +/** + * Called to make a connection parameter request or response control pdu. + * + * @param connsm + * @param dptr Pointer to start of data. NOTE: the opcode is not part + * of the data. + */ +static void +ble_ll_ctrl_conn_param_pdu_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + struct ble_ll_conn_params *req) +{ + uint16_t offset; + struct hci_conn_update *hcu; + + /* If we were passed in a request, we use the parameters from the request */ + if (req) { + put_le16(dptr, req->interval_min); + put_le16(dptr + 2, req->interval_max); + put_le16(dptr + 4, req->latency); + put_le16(dptr + 6, req->timeout); + } else { + hcu = &connsm->conn_param_req; + /* The host should have provided the parameters! */ + BLE_LL_ASSERT(hcu->handle != 0); + put_le16(dptr, hcu->conn_itvl_min); + put_le16(dptr + 2, hcu->conn_itvl_max); + put_le16(dptr + 4, hcu->conn_latency); + put_le16(dptr + 6, hcu->supervision_timeout); + } + + /* XXX: NOTE: if interval min and interval max are != to each + * other this value should be set to non-zero. I think this + * applies only when an offset field is set. See section 5.1.7.1 pg 103 + * Vol 6 Part B. + */ + /* XXX: for now, set periodicity to 0 */ + dptr[8] = 0; + + /* XXX: deal with reference event count. what to put here? */ + put_le16(dptr + 9, connsm->event_cntr); + + /* XXX: For now, dont use offsets */ + offset = 0xFFFF; + put_le16(dptr + 11, offset); + put_le16(dptr + 13, offset); + put_le16(dptr + 15, offset); + put_le16(dptr + 17, offset); + put_le16(dptr + 19, offset); + put_le16(dptr + 21, offset); +} + +static void +ble_ll_ctrl_version_ind_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld) +{ + /* Set flag to denote we have sent/received this */ + connsm->csmflags.cfbit.version_ind_sent = 1; + + /* Fill out response */ + pyld[0] = BLE_HCI_VER_BCS; + put_le16(pyld + 1, MYNEWT_VAL(BLE_LL_MFRG_ID)); + put_le16(pyld + 3, BLE_LL_SUB_VERS_NR); +} + +/** + * Called to make a LL control channel map request PDU. + * + * @param connsm Pointer to connection state machine + * @param pyld Pointer to payload of LL control PDU + */ +static void +ble_ll_ctrl_chanmap_req_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld) +{ + /* Copy channel map that host desires into request */ + memcpy(pyld, g_ble_ll_conn_params.master_chan_map, BLE_LL_CONN_CHMAP_LEN); + memcpy(connsm->req_chanmap, pyld, BLE_LL_CONN_CHMAP_LEN); + + /* Place instant into request */ + connsm->chanmap_instant = connsm->event_cntr + connsm->slave_latency + 6 + 1; + put_le16(pyld + BLE_LL_CONN_CHMAP_LEN, connsm->chanmap_instant); + + /* Set scheduled flag */ + connsm->csmflags.cfbit.chanmap_update_scheduled = 1; +} + +/** + * Called to respond to a LL control PDU connection parameter request or + * response. + * + * @param connsm + * @param rsp + * @param req + * + * @return uint8_t + */ +uint8_t +ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, uint8_t *rsp, + struct ble_ll_conn_params *req) +{ + uint8_t rsp_opcode; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + /* Create a connection parameter response */ + ble_ll_ctrl_conn_param_pdu_make(connsm, rsp + 1, req); + rsp_opcode = BLE_LL_CTRL_CONN_PARM_RSP; + } else { + /* Create a connection update pdu */ + ble_ll_ctrl_conn_upd_make(connsm, rsp + 1, req); + rsp_opcode = BLE_LL_CTRL_CONN_UPDATE_IND; + } + + return rsp_opcode; +} + +/** + * Called when we have received a LL_REJECT_IND or LL_REJECT_IND_EXT link + * layer control Data Channel pdu. + * + * @param connsm + * @param dptr + * @param opcode + */ +static int +ble_ll_ctrl_rx_reject_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t opcode, uint8_t *rspdata) +{ + uint8_t ble_error; + uint8_t rsp_opcode = BLE_ERR_MAX; + + /* Get error out of received PDU */ + if (opcode == BLE_LL_CTRL_REJECT_IND) { + ble_error = dptr[0]; + } else { + ble_error = dptr[1]; + } + + /* XXX: should I check to make sure the rejected opcode is sane + if we receive ind ext? */ + switch (connsm->cur_ctrl_proc) { + case BLE_LL_CTRL_PROC_CONN_PARAM_REQ: + if (opcode == BLE_LL_CTRL_REJECT_IND_EXT) { + /* As a master we should send connection update indication in this point */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + rsp_opcode = BLE_LL_CTRL_CONN_UPDATE_IND; + ble_ll_ctrl_conn_upd_make(connsm, rspdata, NULL); + connsm->reject_reason = BLE_ERR_SUCCESS; + } else { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + ble_ll_hci_ev_conn_update(connsm, ble_error); + } + } + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + case BLE_LL_CTRL_PROC_ENCRYPT: + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_ENCRYPT); + ble_ll_hci_ev_encrypt_chg(connsm, ble_error); + connsm->enc_data.enc_state = CONN_ENC_S_UNENCRYPTED; + break; +#endif +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_LL_CTRL_PROC_PHY_UPDATE: + ble_ll_ctrl_phy_update_cancel(connsm, ble_error); + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + break; +#endif + case BLE_LL_CTRL_PROC_DATA_LEN_UPD: + /* That should not happen according to Bluetooth 5.0 Vol6 Part B, 5.1.9 + * However we need this workaround as there are devices on the market + * which do send LL_REJECT on LL_LENGTH_REQ when collision happens + */ + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD); + break; + default: + break; + } + + return rsp_opcode; +} + +/** + * Called when we receive a connection update event + * + * @param connsm + * @param dptr + * + * @return int + */ +static int +ble_ll_ctrl_rx_conn_update(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + uint8_t rsp_opcode; + uint16_t conn_events; + struct ble_ll_conn_upd_req *reqdata; + + /* Only a slave should receive this */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* Retrieve parameters */ + reqdata = &connsm->conn_update_req; + reqdata->winsize = dptr[0]; + reqdata->winoffset = get_le16(dptr + 1); + reqdata->interval = get_le16(dptr + 3); + reqdata->latency = get_le16(dptr + 5); + reqdata->timeout = get_le16(dptr + 7); + reqdata->instant = get_le16(dptr + 9); + + /* XXX: validate them at some point. If they dont check out, we + return the unknown response */ + rsp_opcode = BLE_ERR_MAX; + + /* If instant is in the past, we have to end the connection */ + conn_events = (reqdata->instant - connsm->event_cntr) & 0xFFFF; + if (conn_events >= 32767) { + ble_ll_conn_timeout(connsm, BLE_ERR_INSTANT_PASSED); + } else { + connsm->csmflags.cfbit.conn_update_sched = 1; + + /* + * Errata says that receiving a connection update when the event + * counter is equal to the instant means wesimply ignore the window + * offset and window size. Anchor point has already been set based on + * first packet received in connection event. Given that we increment + * the event counter BEFORE checking to see if the instant is equal to + * the event counter what we do here is increment the instant and set + * the window offset and size to 0. + */ + if (conn_events == 0) { + reqdata->winoffset = 0; + reqdata->winsize = 0; + reqdata->instant += 1; + } + } + + return rsp_opcode; +} + +void +ble_ll_ctrl_initiate_dle(struct ble_ll_conn_sm *connsm) +{ + if (!(connsm->conn_features & BLE_LL_FEAT_DATA_LEN_EXT)) { + return; + } + + /* + * Section 4.5.10 Vol 6 PART B. If the max tx/rx time or octets + * exceeds the minimum, data length procedure needs to occur + */ + if ((connsm->max_tx_octets <= BLE_LL_CONN_SUPP_BYTES_MIN) && + (connsm->max_rx_octets <= BLE_LL_CONN_SUPP_BYTES_MIN) && + (connsm->max_tx_time <= BLE_LL_CONN_SUPP_TIME_MIN) && + (connsm->max_rx_time <= BLE_LL_CONN_SUPP_TIME_MIN)) { + return; + } + + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD); +} + +static void +ble_ll_ctrl_update_features(struct ble_ll_conn_sm *connsm, uint8_t *feat) +{ + connsm->conn_features = feat[0]; + memcpy(connsm->remote_features, feat + 1, 7); + + /* If we received peer's features for the 1st time, we should try DLE */ + if (!connsm->csmflags.cfbit.rxd_features) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + /* + * If connection was established on uncoded PHY, by default we use + * MaxTxTime and MaxRxTime applicable for that PHY since we are not + * allowed to indicate longer supported time if peer does not support + * LE Coded PHY. However, once we know that peer does support it we can + * update those values to ones applicable for coded PHY. + */ + if (connsm->remote_features[0] & (BLE_LL_FEAT_LE_CODED_PHY >> 8)) { + if (connsm->host_req_max_tx_time) { + connsm->max_tx_time = max(connsm->max_tx_time, + connsm->host_req_max_tx_time); + } else { + connsm->max_tx_time = g_ble_ll_conn_params.conn_init_max_tx_time_coded; + } + connsm->max_rx_time = BLE_LL_CONN_SUPP_TIME_MAX_CODED; + } +#endif + + connsm->csmflags.cfbit.pending_initiate_dle = 1; + connsm->csmflags.cfbit.rxd_features = 1; + } +} + +/** + * Called when we receive a feature request or a slave initiated feature + * request. + * + * + * @param connsm + * @param dptr + * @param rspbuf + * @param opcode + * @param new_features + * + * @return int + */ +static int +ble_ll_ctrl_rx_feature_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf, uint8_t opcode) +{ + uint8_t rsp_opcode; + uint64_t our_feat; + + /* + * Only accept slave feature requests if we are a master and feature + * requests if we are a slave. + */ + if (opcode == BLE_LL_CTRL_SLAVE_FEATURE_REQ) { + if (connsm->conn_role != BLE_LL_CONN_ROLE_MASTER) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + } else { + /* XXX: not sure this is correct but do it anyway */ + if (connsm->conn_role != BLE_LL_CONN_ROLE_SLAVE) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + } + + our_feat = ble_ll_read_supp_features(); + + rsp_opcode = BLE_LL_CTRL_FEATURE_RSP; + + ble_ll_ctrl_update_features(connsm, dptr); + + /* + * 1st octet of features should be common features of local and remote + * controller - we call this 'connection features' + * remaining octets are features of controller which sends PDU, in this case + * it's our controller + * + * See: Vol 6, Part B, section 2.4.2.10 + */ + connsm->conn_features &= our_feat; + + put_le64(rspbuf + 1, our_feat); + rspbuf[1] = connsm->conn_features; + + return rsp_opcode; +} + +/** + * Called when we receive a feature response + * + * @param connsm + * @param dptr + * @param new_features + * + */ +static void +ble_ll_ctrl_rx_feature_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + ble_ll_ctrl_update_features(connsm, dptr); + + /* Stop the control procedure */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG)) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG); + } + + /* Send event to host if pending features read */ + if (connsm->csmflags.cfbit.pending_hci_rd_features) { + ble_ll_hci_ev_rd_rem_used_feat(connsm, BLE_ERR_SUCCESS); + connsm->csmflags.cfbit.pending_hci_rd_features = 0; + } +} + +/** + * + * + * Context: Link Layer task + * + * @param connsm + * @param dptr + * @param rspbuf + * + * @return int + */ +static int +ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + + /* + * This is not in the specification per se but it simplifies the + * implementation. If we get a connection parameter request and we + * are awaiting a reply from the host, simply ignore the request. This + * might not be a good idea if the parameters are different, but oh + * well. This is not expected to happen anyway. A return of BLE_ERR_MAX + * means that we will simply discard the connection parameter request + */ + if (connsm->csmflags.cfbit.awaiting_host_reply) { + return BLE_ERR_MAX; + } + + /* XXX: remember to deal with this on the master: if the slave has + * initiated a procedure we may have received its connection parameter + * update request and have signaled the host with an event. If that + * is the case, we will need to drop the host command when we get it + and also clear any applicable states. */ + + /* XXX: Read 5.3 again. There are multiple control procedures that might + * be pending (a connection update) that will cause collisions and the + behavior below. */ + /* + * Check for procedure collision (Vol 6 PartB 5.3). If we are a slave + * and we receive a request we "consider the slave initiated + * procedure as complete". This means send a connection update complete + * event (with error). + * + * If a master, we send reject with a + * transaction collision error code. + */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + ble_ll_hci_ev_conn_update(connsm, BLE_ERR_LMP_COLLISION); + } else { + /* The master sends reject ind ext w/error code 0x23 */ + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = BLE_LL_CTRL_CONN_PARM_REQ; + rspbuf[2] = BLE_ERR_LMP_COLLISION; + return rsp_opcode; + } + } + + /* + * If we are a master and we currently performing a channel map + * update procedure we need to return an error + */ + if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) && + (connsm->csmflags.cfbit.chanmap_update_scheduled)) { + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = BLE_LL_CTRL_CONN_PARM_REQ; + rspbuf[2] = BLE_ERR_DIFF_TRANS_COLL; + return rsp_opcode; + } + + /* Process the received connection parameter request */ + rsp_opcode = ble_ll_ctrl_conn_param_pdu_proc(connsm, dptr, rspbuf, + BLE_LL_CTRL_CONN_PARM_REQ); + return rsp_opcode; +} + +static int +ble_ll_ctrl_rx_conn_param_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + + /* A slave should never receive this response */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* + * This case should never happen! It means that the slave initiated a + * procedure and the master initiated one as well. If we do get in this + * state just clear the awaiting reply. The slave will hopefully stop its + * procedure when we reply. + */ + if (connsm->csmflags.cfbit.awaiting_host_reply) { + connsm->csmflags.cfbit.awaiting_host_reply = 0; + } + + /* If we receive a response and no procedure is pending, just leave */ + if (!IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + return BLE_ERR_MAX; + } + + /* Process the received connection parameter response */ + rsp_opcode = ble_ll_ctrl_conn_param_pdu_proc(connsm, dptr, rspbuf, + BLE_LL_CTRL_CONN_PARM_RSP); + return rsp_opcode; +} + +/** + * Called to process the LL control PDU VERSION_IND + * + * Context: Link Layer task + * + * @param connsm + * @param dptr + * @param rspbuf + * + * @return int + */ +static int +ble_ll_ctrl_rx_version_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + + /* Process the packet */ + connsm->vers_nr = dptr[0]; + connsm->comp_id = get_le16(dptr + 1); + connsm->sub_vers_nr = get_le16(dptr + 3); + connsm->csmflags.cfbit.rxd_version_ind = 1; + + rsp_opcode = BLE_ERR_MAX; + if (!connsm->csmflags.cfbit.version_ind_sent) { + rsp_opcode = BLE_LL_CTRL_VERSION_IND; + ble_ll_ctrl_version_ind_make(connsm, rspbuf); + } + + /* Stop the control procedure */ + if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_VERSION_XCHG)) { + ble_ll_hci_ev_rd_rem_ver(connsm, BLE_ERR_SUCCESS); + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_VERSION_XCHG); + } + return rsp_opcode; +} + +/** + * Called to process a received channel map request control pdu. + * + * Context: Link Layer task + * + * @param connsm + * @param dptr + */ +static int +ble_ll_ctrl_rx_chanmap_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + uint16_t instant; + uint16_t conn_events; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + return BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* If instant is in the past, we have to end the connection */ + instant = get_le16(dptr + BLE_LL_CONN_CHMAP_LEN); + conn_events = (instant - connsm->event_cntr) & 0xFFFF; + if (conn_events >= 32767) { + ble_ll_conn_timeout(connsm, BLE_ERR_INSTANT_PASSED); + } else { + connsm->chanmap_instant = instant; + memcpy(connsm->req_chanmap, dptr, BLE_LL_CONN_CHMAP_LEN); + connsm->csmflags.cfbit.chanmap_update_scheduled = 1; + } + + return BLE_ERR_MAX; +} + +/** + * Initiate LL control procedure. + * + * This function is called to obtain a mbuf to send a LL control PDU. The data + * channel PDU header is not part of the mbuf data; it is part of the BLE + * header (which is part of the mbuf). + * + * Context: LL task. + * + * @param connsm + * @param ctrl_proc + */ +static struct os_mbuf * +ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc) +{ + uint8_t len; + uint8_t opcode; + uint8_t *dptr; + uint8_t *ctrdata; + struct os_mbuf *om; + + /* Get an mbuf for the control pdu */ + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, sizeof(struct ble_mbuf_hdr)); + + if (om) { + /* The control data starts after the opcode (1 byte) */ + dptr = om->om_data; + ctrdata = dptr + 1; + + switch (ctrl_proc) { + case BLE_LL_CTRL_PROC_CONN_UPDATE: + opcode = BLE_LL_CTRL_CONN_UPDATE_IND; + ble_ll_ctrl_conn_upd_make(connsm, ctrdata, NULL); + break; + case BLE_LL_CTRL_PROC_CHAN_MAP_UPD: + opcode = BLE_LL_CTRL_CHANNEL_MAP_REQ; + ble_ll_ctrl_chanmap_req_make(connsm, ctrdata); + break; + case BLE_LL_CTRL_PROC_FEATURE_XCHG: + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + opcode = BLE_LL_CTRL_FEATURE_REQ; + } else { + opcode = BLE_LL_CTRL_SLAVE_FEATURE_REQ; + } + put_le64(ctrdata, ble_ll_read_supp_features()); + break; + case BLE_LL_CTRL_PROC_VERSION_XCHG: + opcode = BLE_LL_CTRL_VERSION_IND; + ble_ll_ctrl_version_ind_make(connsm, ctrdata); + break; + case BLE_LL_CTRL_PROC_TERMINATE: + opcode = BLE_LL_CTRL_TERMINATE_IND; + ctrdata[0] = connsm->disconnect_reason; + break; + case BLE_LL_CTRL_PROC_CONN_PARAM_REQ: + opcode = BLE_LL_CTRL_CONN_PARM_REQ; + ble_ll_ctrl_conn_param_pdu_make(connsm, ctrdata, NULL); + break; + case BLE_LL_CTRL_PROC_LE_PING: + opcode = BLE_LL_CTRL_PING_REQ; + break; + case BLE_LL_CTRL_PROC_DATA_LEN_UPD: + opcode = BLE_LL_CTRL_LENGTH_REQ; + ble_ll_ctrl_datalen_upd_make(connsm, dptr); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* XXX: deal with already encrypted connection.*/ + case BLE_LL_CTRL_PROC_ENCRYPT: + /* If we are already encrypted we do pause procedure */ + if (connsm->enc_data.enc_state == CONN_ENC_S_ENCRYPTED) { + opcode = BLE_LL_CTRL_PAUSE_ENC_REQ; + } else { + opcode = BLE_LL_CTRL_ENC_REQ; + ble_ll_ctrl_enc_req_make(connsm, ctrdata); + } + break; +#endif +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_LL_CTRL_PROC_PHY_UPDATE: + opcode = BLE_LL_CTRL_PHY_REQ; + ble_ll_ctrl_phy_req_rsp_make(connsm, ctrdata); + break; +#endif + default: + BLE_LL_ASSERT(0); + break; + } + + /* Set llid, length and opcode */ + dptr[0] = opcode; + len = g_ble_ll_ctrl_pkt_lengths[opcode] + 1; + + /* Add packet to transmit queue of connection */ + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); + } + + return om; +} + +/** + * Called to determine if the pdu is a TERMINATE_IND + * + * @param hdr + * @param opcode + * + * @return int + */ +int +ble_ll_ctrl_is_terminate_ind(uint8_t hdr, uint8_t opcode) +{ + int rc; + + rc = 0; + if ((hdr & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) { + if (opcode == BLE_LL_CTRL_TERMINATE_IND) { + rc = 1; + } + } + return rc; +} + +/** + * Stops the LL control procedure indicated by 'ctrl_proc'. + * + * Context: Link Layer task + * + * @param connsm + * @param ctrl_proc + */ +void +ble_ll_ctrl_proc_stop(struct ble_ll_conn_sm *connsm, int ctrl_proc) +{ + if (connsm->cur_ctrl_proc == ctrl_proc) { + ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer); + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; + } + CLR_PENDING_CTRL_PROC(connsm, ctrl_proc); + + /* If there are others, start them */ + ble_ll_ctrl_chk_proc_start(connsm); +} + +/** + * Called to start the terminate procedure. + * + * Context: Link Layer task. + * + * @param connsm + */ +void +ble_ll_ctrl_terminate_start(struct ble_ll_conn_sm *connsm) +{ + int ctrl_proc; + uint32_t usecs; + struct os_mbuf *om; + + BLE_LL_ASSERT(connsm->disconnect_reason != 0); + + ctrl_proc = BLE_LL_CTRL_PROC_TERMINATE; + om = ble_ll_ctrl_proc_init(connsm, ctrl_proc); + if (om) { + CONN_F_TERMINATE_STARTED(connsm) = 1; + + /* Set terminate "timeout" */ + usecs = connsm->supervision_tmo * BLE_HCI_CONN_SPVN_TMO_UNITS * 1000; + connsm->terminate_timeout = os_cputime_get32() + + os_cputime_usecs_to_ticks(usecs); + } +} + +/** + * Called to start a LL control procedure except for the terminate procedure. We + * always set the control procedure pending bit even if the control procedure + * has been initiated. + * + * Context: Link Layer task. + * + * @param connsm Pointer to connection state machine. + */ +void +ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc) +{ + struct os_mbuf *om; + + BLE_LL_ASSERT(ctrl_proc != BLE_LL_CTRL_PROC_TERMINATE); + + om = NULL; + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_IDLE) { + /* Initiate the control procedure. */ + om = ble_ll_ctrl_proc_init(connsm, ctrl_proc); + if (om) { + /* Set the current control procedure */ + connsm->cur_ctrl_proc = ctrl_proc; + + /* Initialize the procedure response timeout */ + if (ctrl_proc != BLE_LL_CTRL_PROC_CHAN_MAP_UPD) { + ble_ll_ctrl_start_rsp_timer(connsm); + } + } + } + + /* Set bitmask denoting control procedure is pending */ + connsm->pending_ctrl_procs |= (1 << ctrl_proc); +} + +/** + * Called to determine if we need to start a LL control procedure for the given + * connection. + * + * Context: Link Layer + * + * @param connsm Pointer to connection state machine. + */ +void +ble_ll_ctrl_chk_proc_start(struct ble_ll_conn_sm *connsm) +{ + int i; + + /* XXX: TODO new rules! Cannot start certain control procedures if other + * ones are peer initiated. We need to wait. Deal with this. + */ + + /* + * If we are terminating, dont start any new procedures but start + * terminate if needed + */ + if (connsm->disconnect_reason) { + if (!CONN_F_TERMINATE_STARTED(connsm)) { + /* + * If the terminate procedure has not started it means we were not + * able to start it right away (no control pdu was available). + * Start it now. No need to start any other procedures. + */ + ble_ll_ctrl_terminate_start(connsm); + } + return; + } + + /* If there is a running procedure or no pending, do nothing */ + if ((connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_IDLE) && + (connsm->pending_ctrl_procs != 0)) { + /* + * The specification says there is no priority to control procedures + * so just start from the first one for now. + */ + for (i = 0; i < BLE_LL_CTRL_PROC_NUM; ++i) { + if (IS_PENDING_CTRL_PROC(connsm, i)) { + /* + * The version exchange is a special case. If we have already + * received the information dont start it. + */ + if ((i == BLE_LL_CTRL_PROC_VERSION_XCHG) && + (connsm->csmflags.cfbit.rxd_version_ind)) { + ble_ll_hci_ev_rd_rem_ver(connsm, BLE_ERR_SUCCESS); + CLR_PENDING_CTRL_PROC(connsm, i); + } else { + ble_ll_ctrl_proc_start(connsm, i); + break; + } + } + } + } +} + +/** + * Called when the Link Layer receives a LL control PDU. + * + * NOTE: this function uses the received PDU for the response in some cases. If + * the received PDU is not used it needs to be freed here. + * + * XXX: may want to check, for both master and slave, whether the control + * pdu should be received by that role. Might make for less code... + * Context: Link Layer + * + * @param om + * @param connsm + */ +int +ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) +{ + uint32_t features; + uint32_t feature; + uint8_t len; + uint8_t opcode; + uint8_t rsp_opcode; + uint8_t *dptr; + uint8_t *rspbuf; + uint8_t *rspdata; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + int restart_encryption; +#endif + int rc = 0; + + /* XXX: where do we validate length received and packet header length? + * do this in LL task when received. Someplace!!! What I mean + * is we should validate the over the air length with the mbuf length. + Should the PHY do that???? */ + + /* + * dptr points to om_data pointer. The first byte of om_data is the + * first byte of the Data Channel PDU header. Get length from header and + * opcode from LL control PDU. + */ + dptr = om->om_data; + len = dptr[1]; + opcode = dptr[2]; + + /* + * rspbuf points to first byte of response. The response buffer does not + * contain the Data Channel PDU. Thus, the first byte of rspbuf is the + * LL control PDU payload (the opcode of the control PDU). rspdata + * points to CtrData in the control PDU. + */ + rspbuf = dptr; + rspdata = rspbuf + 1; + + /* Move data pointer to start of control data (2 byte PDU hdr + opcode) */ + dptr += (BLE_LL_PDU_HDR_LEN + 1); + + /* + * Subtract the opcode from the length. Note that if the length was zero, + * which would be an error, we will fail the check against the length + * of the control packet. + */ + --len; + + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_CTRL_RX, opcode, len); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + restart_encryption = 0; +#endif + + /* If opcode comes from reserved value or CtrlData fields is invalid + * we shall respond with LL_UNKNOWN_RSP + */ + if ((opcode >= BLE_LL_CTRL_OPCODES) || + (len != g_ble_ll_ctrl_pkt_lengths[opcode])) { + rc = -1; + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + goto ll_ctrl_send_rsp; + } + + /* Check if the feature is supported. */ + switch (opcode) { + case BLE_LL_CTRL_LENGTH_REQ: + feature = BLE_LL_FEAT_DATA_LEN_EXT; + break; + case BLE_LL_CTRL_SLAVE_FEATURE_REQ: + feature = BLE_LL_FEAT_SLAVE_INIT; + break; + case BLE_LL_CTRL_CONN_PARM_REQ: + case BLE_LL_CTRL_CONN_PARM_RSP: + feature = BLE_LL_FEAT_CONN_PARM_REQ; + break; + case BLE_LL_CTRL_ENC_REQ: + case BLE_LL_CTRL_START_ENC_REQ: + case BLE_LL_CTRL_PAUSE_ENC_REQ: + feature = BLE_LL_FEAT_LE_ENCRYPTION; + break; + case BLE_LL_CTRL_PING_REQ: + feature = BLE_LL_FEAT_LE_PING; + break; + case BLE_LL_CTRL_PHY_REQ: + feature = BLE_LL_FEAT_LE_2M_PHY | BLE_LL_FEAT_LE_CODED_PHY; + break; + case BLE_LL_CTRL_MIN_USED_CHAN_IND: + feature = BLE_LL_FEAT_MIN_USED_CHAN; + break; + case BLE_LL_CTRL_PERIODIC_SYNC_IND: + feature = BLE_LL_FEAT_SYNC_TRANS_RECV; + break; + default: + feature = 0; + break; + } + + if (feature) { + features = ble_ll_read_supp_features(); + if ((features & feature) == 0) { + if (opcode == BLE_LL_CTRL_ENC_REQ) { + if (connsm->conn_features & BLE_LL_FEAT_EXTENDED_REJ) { + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = opcode; + rspbuf[2] = BLE_ERR_UNSUPP_REM_FEATURE; + + } else { + rsp_opcode = BLE_LL_CTRL_REJECT_IND; + rspbuf[1] = BLE_ERR_UNSUPP_REM_FEATURE; + } + } else { + /* Construct unknown rsp pdu */ + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + } + goto ll_ctrl_send_rsp; + } + } + + /* Process opcode */ + rsp_opcode = BLE_ERR_MAX; + switch (opcode) { + case BLE_LL_CTRL_CONN_UPDATE_IND: + rsp_opcode = ble_ll_ctrl_rx_conn_update(connsm, dptr); + break; + case BLE_LL_CTRL_CHANNEL_MAP_REQ: + rsp_opcode = ble_ll_ctrl_rx_chanmap_req(connsm, dptr); + break; + case BLE_LL_CTRL_LENGTH_REQ: + /* Extract parameters and check if valid */ + if (ble_ll_ctrl_len_proc(connsm, dptr)) { + rc = -1; + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + goto ll_ctrl_send_rsp; + } + + /* + * If we have not started this procedure ourselves and it is + * pending, no need to perform it. + */ + if ((connsm->cur_ctrl_proc != BLE_LL_CTRL_PROC_DATA_LEN_UPD) && + IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD)) { + CLR_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD); + } + + /* Send a response */ + rsp_opcode = BLE_LL_CTRL_LENGTH_RSP; + ble_ll_ctrl_datalen_upd_make(connsm, rspbuf); + break; + case BLE_LL_CTRL_LENGTH_RSP: + /* According to specification, process this only if we asked for it. */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_DATA_LEN_UPD) { + /* + * Process the received data. If received data is invalid, we'll + * reply with LL_UNKNOWN_RSP as per spec, but we still need to stop + * control procedure to avoid timeout. + */ + if (ble_ll_ctrl_len_proc(connsm, dptr)) { + rc = -1; + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + } + + /* Stop the control procedure */ + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD); + } + break; + case BLE_LL_CTRL_UNKNOWN_RSP: + rsp_opcode = ble_ll_ctrl_proc_unk_rsp(connsm, dptr, rspdata); + break; + case BLE_LL_CTRL_FEATURE_REQ: + rsp_opcode = ble_ll_ctrl_rx_feature_req(connsm, dptr, rspbuf, opcode); + break; + /* XXX: check to see if ctrl procedure was running? Do we care? */ + case BLE_LL_CTRL_FEATURE_RSP: + ble_ll_ctrl_rx_feature_rsp(connsm, dptr); + break; + case BLE_LL_CTRL_VERSION_IND: + rsp_opcode = ble_ll_ctrl_rx_version_ind(connsm, dptr, rspdata); + break; + case BLE_LL_CTRL_SLAVE_FEATURE_REQ: + rsp_opcode = ble_ll_ctrl_rx_feature_req(connsm, dptr, rspbuf, opcode); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + case BLE_LL_CTRL_ENC_REQ: + rsp_opcode = ble_ll_ctrl_rx_enc_req(connsm, dptr, rspdata); + break; + case BLE_LL_CTRL_ENC_RSP: + ble_ll_ctrl_rx_enc_rsp(connsm, dptr); + break; + case BLE_LL_CTRL_START_ENC_REQ: + rsp_opcode = ble_ll_ctrl_rx_start_enc_req(connsm); + break; + case BLE_LL_CTRL_START_ENC_RSP: + rsp_opcode = ble_ll_ctrl_rx_start_enc_rsp(connsm); + break; + case BLE_LL_CTRL_PAUSE_ENC_REQ: + rsp_opcode = ble_ll_ctrl_rx_pause_enc_req(connsm); + break; + case BLE_LL_CTRL_PAUSE_ENC_RSP: + rsp_opcode = ble_ll_ctrl_rx_pause_enc_rsp(connsm); + if (rsp_opcode == BLE_LL_CTRL_PAUSE_ENC_RSP) { + restart_encryption = 1; + } + break; +#endif + case BLE_LL_CTRL_PING_REQ: + rsp_opcode = BLE_LL_CTRL_PING_RSP; + break; + case BLE_LL_CTRL_PING_RSP: + ble_ll_ctrl_rx_ping_rsp(connsm); + break; + case BLE_LL_CTRL_CONN_PARM_REQ: + rsp_opcode = ble_ll_ctrl_rx_conn_param_req(connsm, dptr, rspbuf); + break; + case BLE_LL_CTRL_CONN_PARM_RSP: + rsp_opcode = ble_ll_ctrl_rx_conn_param_rsp(connsm, dptr, rspbuf); + break; + /* Fall-through intentional... */ + case BLE_LL_CTRL_REJECT_IND: + case BLE_LL_CTRL_REJECT_IND_EXT: + /* Sometimes reject triggers sending other LL CTRL msg */ + rsp_opcode = ble_ll_ctrl_rx_reject_ind(connsm, dptr, opcode, rspdata); + break; +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_LL_CTRL_PHY_REQ: + rsp_opcode = ble_ll_ctrl_rx_phy_req(connsm, dptr, rspdata); + break; + case BLE_LL_CTRL_PHY_RSP: + rsp_opcode = ble_ll_ctrl_rx_phy_rsp(connsm, dptr, rspdata); + break; + case BLE_LL_CTRL_PHY_UPDATE_IND: + rsp_opcode = ble_ll_ctrl_rx_phy_update_ind(connsm, dptr); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + case BLE_LL_CTRL_PERIODIC_SYNC_IND: + rsp_opcode = ble_ll_ctrl_rx_periodic_sync_ind(connsm, dptr); + break; +#endif + default: + /* Nothing to do here */ + break; + } + + /* Free mbuf or send response */ +ll_ctrl_send_rsp: + if (rsp_opcode == BLE_ERR_MAX) { + os_mbuf_free_chain(om); + } else { + /* + * Write the response opcode into the buffer. If this is an unknown + * response, put opcode of unknown pdu into buffer. + */ + rspbuf[0] = rsp_opcode; + if (rsp_opcode == BLE_LL_CTRL_UNKNOWN_RSP) { + rspbuf[1] = opcode; + } + len = g_ble_ll_ctrl_pkt_lengths[rsp_opcode] + 1; + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (restart_encryption) { + /* XXX: what happens if this fails? Meaning we cant allocate + mbuf? */ + ble_ll_ctrl_proc_init(connsm, BLE_LL_CTRL_PROC_ENCRYPT); + } +#endif + } + + if (connsm->csmflags.cfbit.pending_initiate_dle) { + connsm->csmflags.cfbit.pending_initiate_dle = 0; + ble_ll_ctrl_initiate_dle(connsm); + } + + return rc; +} + +/** + * Called to create and send a REJECT_IND_EXT control PDU or a REJECT_IND + * + * @param connsm + * @param rej_opcode + * @param err + * + * @return int + */ +int +ble_ll_ctrl_reject_ind_send(struct ble_ll_conn_sm *connsm, uint8_t rej_opcode, + uint8_t err) +{ + int rc; + uint8_t len; + uint8_t opcode; + uint8_t *rspbuf; + struct os_mbuf *om; + + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, + sizeof(struct ble_mbuf_hdr)); + if (om) { + rspbuf = om->om_data; + opcode = BLE_LL_CTRL_REJECT_IND_EXT; + if (rej_opcode == BLE_LL_CTRL_ENC_REQ) { + if ((connsm->conn_features & BLE_LL_FEAT_EXTENDED_REJ) == 0) { + opcode = BLE_LL_CTRL_REJECT_IND; + } + } + rspbuf[0] = opcode; + if (opcode == BLE_LL_CTRL_REJECT_IND) { + rspbuf[1] = err; + len = BLE_LL_CTRL_REJ_IND_LEN + 1; + } else { + rspbuf[1] = rej_opcode; + rspbuf[2] = err; + len = BLE_LL_CTRL_REJECT_IND_EXT_LEN + 1; + } + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); + rc = 0; + } else { + rc = 1; + } + return rc; +} + +/** + * Called when a Link Layer Control pdu has been transmitted successfully. + * This is called when we have a received a PDU during the ISR. + * + * Context: ISR + * + * @param txpdu + * + * @return int + */ +int +ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm) +{ + int rc; + uint8_t opcode; + + rc = 0; + opcode = txpdu->om_data[0]; + switch (opcode) { + case BLE_LL_CTRL_TERMINATE_IND: + connsm->csmflags.cfbit.terminate_ind_txd = 1; + rc = -1; + break; + case BLE_LL_CTRL_REJECT_IND_EXT: + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + /* If rejecting opcode is BLE_LL_CTRL_PROC_CONN_PARAM_REQ and + * reason is LMP collision that means we are master on the link and + * peer wanted to start procedure which we already started. + * Let's wait for response and do not close procedure. */ + if (txpdu->om_data[1] == BLE_LL_CTRL_CONN_PARM_REQ && + txpdu->om_data[2] != BLE_ERR_LMP_COLLISION) { + connsm->reject_reason = txpdu->om_data[2]; + connsm->csmflags.cfbit.host_expects_upd_event = 1; + } + } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (connsm->enc_data.enc_state > CONN_ENC_S_ENCRYPTED) { + connsm->enc_data.enc_state = CONN_ENC_S_UNENCRYPTED; + } +#endif + break; + case BLE_LL_CTRL_REJECT_IND: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + connsm->enc_data.enc_state = CONN_ENC_S_UNENCRYPTED; +#endif + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + case BLE_LL_CTRL_PAUSE_ENC_REQ: + /* note: fall-through intentional */ + case BLE_LL_CTRL_ENC_REQ: + connsm->enc_data.enc_state = CONN_ENC_S_ENC_RSP_WAIT; + break; + case BLE_LL_CTRL_ENC_RSP: + connsm->csmflags.cfbit.send_ltk_req = 1; + break; + case BLE_LL_CTRL_START_ENC_RSP: + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + connsm->enc_data.enc_state = CONN_ENC_S_ENCRYPTED; + if (CONN_F_LE_PING_SUPP(connsm)) { + ble_ll_conn_auth_pyld_timer_start(connsm); + } + } + break; + case BLE_LL_CTRL_PAUSE_ENC_RSP: + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + connsm->enc_data.enc_state = CONN_ENC_S_PAUSE_ENC_RSP_WAIT; + } + break; +#endif +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_LL_CTRL_PHY_REQ: + connsm->phy_tx_transition = + ble_ll_ctrl_phy_tx_transition_get(connsm->phy_data.req_pref_tx_phys_mask); + break; + case BLE_LL_CTRL_PHY_UPDATE_IND: + connsm->phy_tx_transition = + ble_ll_ctrl_phy_tx_transition_get(txpdu->om_data[2]); + break; +#endif + default: + break; + } + + os_mbuf_free_chain(txpdu); + return rc; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm.c new file mode 100644 index 0000000..baf8c25 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm.c @@ -0,0 +1,728 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/sysinit/sysinit.h" + +#if MYNEWT_VAL(BLE_LL_DTM) + +#include +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/porting/nimble/include/stats/stats.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "ble_ll_dtm_priv.h" + +STATS_SECT_START(ble_ll_dtm_stats) + STATS_SECT_ENTRY(rx_count) + STATS_SECT_ENTRY(tx_failed) + STATS_SECT_ENTRY(rx_failed) +STATS_SECT_END +STATS_SECT_DECL(ble_ll_dtm_stats) ble_ll_dtm_stats; + +STATS_NAME_START(ble_ll_dtm_stats) + STATS_NAME(ble_ll_dtm_stats, rx_count) + STATS_NAME(ble_ll_dtm_stats, tx_failed) + STATS_NAME(ble_ll_dtm_stats, rx_failed) +STATS_NAME_END(ble_phy_stats) + +struct dtm_ctx { + uint8_t payload_packet; + uint8_t itvl_rem_usec; + uint16_t num_of_packets; + uint32_t itvl_ticks; +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + uint16_t num_of_packets_max; +#endif + int active; + uint8_t rf_channel; + uint8_t phy_mode; + struct os_mbuf *om; + struct ble_npl_event evt; + struct ble_ll_sched_item sch; + uint32_t pdu_start_ticks; + uint8_t pdu_start_usecs; +}; + +static struct dtm_ctx g_ble_ll_dtm_ctx; + +static const uint8_t g_ble_ll_dtm_prbs9_data[] = +{ + 0xff, 0xc1, 0xfb, 0xe8, 0x4c, 0x90, 0x72, 0x8b, + 0xe7, 0xb3, 0x51, 0x89, 0x63, 0xab, 0x23, 0x23, + 0x02, 0x84, 0x18, 0x72, 0xaa, 0x61, 0x2f, 0x3b, + 0x51, 0xa8, 0xe5, 0x37, 0x49, 0xfb, 0xc9, 0xca, + 0x0c, 0x18, 0x53, 0x2c, 0xfd, 0x45, 0xe3, 0x9a, + 0xe6, 0xf1, 0x5d, 0xb0, 0xb6, 0x1b, 0xb4, 0xbe, + 0x2a, 0x50, 0xea, 0xe9, 0x0e, 0x9c, 0x4b, 0x5e, + 0x57, 0x24, 0xcc, 0xa1, 0xb7, 0x59, 0xb8, 0x87, + 0xff, 0xe0, 0x7d, 0x74, 0x26, 0x48, 0xb9, 0xc5, + 0xf3, 0xd9, 0xa8, 0xc4, 0xb1, 0xd5, 0x91, 0x11, + 0x01, 0x42, 0x0c, 0x39, 0xd5, 0xb0, 0x97, 0x9d, + 0x28, 0xd4, 0xf2, 0x9b, 0xa4, 0xfd, 0x64, 0x65, + 0x06, 0x8c, 0x29, 0x96, 0xfe, 0xa2, 0x71, 0x4d, + 0xf3, 0xf8, 0x2e, 0x58, 0xdb, 0x0d, 0x5a, 0x5f, + 0x15, 0x28, 0xf5, 0x74, 0x07, 0xce, 0x25, 0xaf, + 0x2b, 0x12, 0xe6, 0xd0, 0xdb, 0x2c, 0xdc, 0xc3, + 0x7f, 0xf0, 0x3e, 0x3a, 0x13, 0xa4, 0xdc, 0xe2, + 0xf9, 0x6c, 0x54, 0xe2, 0xd8, 0xea, 0xc8, 0x88, + 0x00, 0x21, 0x86, 0x9c, 0x6a, 0xd8, 0xcb, 0x4e, + 0x14, 0x6a, 0xf9, 0x4d, 0xd2, 0x7e, 0xb2, 0x32, + 0x03, 0xc6, 0x14, 0x4b, 0x7f, 0xd1, 0xb8, 0xa6, + 0x79, 0x7c, 0x17, 0xac, 0xed, 0x06, 0xad, 0xaf, + 0x0a, 0x94, 0x7a, 0xba, 0x03, 0xe7, 0x92, 0xd7, + 0x15, 0x09, 0x73, 0xe8, 0x6d, 0x16, 0xee, 0xe1, + 0x3f, 0x78, 0x1f, 0x9d, 0x09, 0x52, 0x6e, 0xf1, + 0x7c, 0x36, 0x2a, 0x71, 0x6c, 0x75, 0x64, 0x44, + 0x80, 0x10, 0x43, 0x4e, 0x35, 0xec, 0x65, 0x27, + 0x0a, 0xb5, 0xfc, 0x26, 0x69, 0x3f, 0x59, 0x99, + 0x01, 0x63, 0x8a, 0xa5, 0xbf, 0x68, 0x5c, 0xd3, + 0x3c, 0xbe, 0x0b, 0xd6, 0x76, 0x83, 0xd6, 0x57, + 0x05, 0x4a, 0x3d, 0xdd, 0x81, 0x73, 0xc9, 0xeb, + 0x8a, 0x84, 0x39, 0xf4, 0x36, 0x0b, 0xf7 +}; + +static const uint8_t g_ble_ll_dtm_prbs15_data[] = +{ + 0xff, 0x7f, 0xf0, 0x3e, 0x3a, 0x13, 0xa4, 0xdc, + 0xe2, 0xf9, 0x6c, 0x54, 0xe2, 0xd8, 0xea, 0xc8, + 0x88, 0x00, 0x21, 0x86, 0x9c, 0x6a, 0xd8, 0xcb, + 0x4e, 0x14, 0x6a, 0xf9, 0x4d, 0xd2, 0x7e, 0xb2, + 0x32, 0x03, 0xc6, 0x14, 0x4b, 0x7f, 0xd1, 0xb8, + 0xa6, 0x79, 0x7c, 0x17, 0xac, 0xed, 0x06, 0xad, + 0xaf, 0x0a, 0x94, 0x7a, 0xba, 0x03, 0xe7, 0x92, + 0xd7, 0x15, 0x09, 0x73, 0xe8, 0x6d, 0x16, 0xee, + 0xe1, 0x3f, 0x78, 0x1f, 0x9d, 0x09, 0x52, 0x6e, + 0xf1, 0x7c, 0x36, 0x2a, 0x71, 0x6c, 0x75, 0x64, + 0x44, 0x80, 0x10, 0x43, 0x4e, 0x35, 0xec, 0x65, + 0x27, 0x0a, 0xb5, 0xfc, 0x26, 0x69, 0x3f, 0x59, + 0x99, 0x01, 0x63, 0x8a, 0xa5, 0xbf, 0x68, 0x5c, + 0xd3, 0x3c, 0xbe, 0x0b, 0xd6, 0x76, 0x83, 0xd6, + 0x57, 0x05, 0x4a, 0x3d, 0xdd, 0x81, 0x73, 0xc9, + 0xeb, 0x8a, 0x84, 0x39, 0xf4, 0x36, 0x0b, 0xf7, + 0xf0, 0x1f, 0xbc, 0x8f, 0xce, 0x04, 0x29, 0xb7, + 0x78, 0x3e, 0x1b, 0x95, 0x38, 0xb6, 0x3a, 0x32, + 0x22, 0x40, 0x88, 0x21, 0xa7, 0x1a, 0xf6, 0xb2, + 0x13, 0x85, 0x5a, 0x7e, 0x93, 0xb4, 0x9f, 0xac, + 0xcc, 0x80, 0x31, 0xc5, 0xd2, 0x5f, 0x34, 0xae, + 0x69, 0x1e, 0xdf, 0x05, 0x6b, 0xbb, 0x41, 0xeb, + 0xab, 0x02, 0xa5, 0x9e, 0xee, 0xc0, 0xb9, 0xe4, + 0x75, 0x45, 0xc2, 0x1c, 0x7a, 0x9b, 0x85, 0x7b, + 0xf8, 0x0f, 0xde, 0x47, 0x67, 0x82, 0x94, 0x5b, + 0x3c, 0x9f, 0x8d, 0x4a, 0x1c, 0x5b, 0x1d, 0x19, + 0x11, 0x20, 0xc4, 0x90, 0x53, 0x0d, 0x7b, 0xd9, + 0x89, 0x42, 0x2d, 0xbf, 0x49, 0xda, 0x4f, 0x56, + 0x66, 0xc0, 0x98, 0x62, 0xe9, 0x2f, 0x1a, 0xd7, + 0x34, 0x8f, 0xef, 0x82, 0xb5, 0xdd, 0xa0, 0xf5, + 0x55, 0x81, 0x52, 0x4f, 0x77, 0xe0, 0x5c, 0xf2, + 0xba, 0x22, 0x61, 0x0e, 0xbd, 0xcd, 0xc2 +}; + +static const uint8_t channel_rf_to_index[] = { + 37, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 38, 11 ,12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 39 +}; + +#define BLE_DTM_SYNC_WORD (0x71764129) +#define BLE_DTM_CRC (0x555555) + +static void ble_ll_dtm_ctx_free(struct dtm_ctx * ctx); + +static void +ble_ll_dtm_set_next(struct dtm_ctx *ctx) +{ + struct ble_ll_sched_item *sch = &ctx->sch; + + ctx->pdu_start_ticks += ctx->itvl_ticks; + ctx->pdu_start_usecs += ctx->itvl_rem_usec; + if (ctx->pdu_start_usecs >= 31) { + ctx->pdu_start_ticks++; + ctx->pdu_start_usecs -= 31; + } + + sch->start_time = ctx->pdu_start_ticks; + sch->remainder = ctx->pdu_start_usecs; + + sch->start_time -= g_ble_ll_sched_offset_ticks; +} + +static void +ble_ll_dtm_ev_tx_resched_cb(struct ble_npl_event *evt) { + /* It is called in LL context */ + struct dtm_ctx *ctx = ble_npl_event_get_arg(evt); + int rc; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + if (!ctx->active || !ctx->om) { + OS_EXIT_CRITICAL(sr); + return; + } + OS_EXIT_CRITICAL(sr); + +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + if (g_ble_ll_dtm_ctx.num_of_packets_max && + (g_ble_ll_dtm_ctx.num_of_packets == g_ble_ll_dtm_ctx.num_of_packets_max)) { + /* + * XXX do not send more packets, but also do not stop DTM - it shall be + * stopped as usual by HCI command since there is no standard way to + * signal end of test to host. + */ + return; + } +#endif + + ble_ll_dtm_set_next(ctx); + rc = ble_ll_sched_dtm(&ctx->sch); + BLE_LL_ASSERT(rc == 0); +} + +static int ble_ll_dtm_rx_start(void); + +static void +ble_ll_dtm_ev_rx_restart_cb(struct ble_npl_event *evt) { + if (ble_ll_dtm_rx_start() != 0) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_dtm_ctx.evt); + STATS_INC(ble_ll_dtm_stats, rx_failed); + } +} + +static void +ble_ll_dtm_tx_done(void *arg) +{ + struct dtm_ctx *ctx; + + ctx = arg; + if (!ctx->active) { + return; + } + + g_ble_ll_dtm_ctx.num_of_packets++; + + /* Reschedule event in LL context */ + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &ctx->evt); + + ble_ll_state_set(BLE_LL_STATE_STANDBY); +} + +static int +ble_ll_dtm_tx_sched_cb(struct ble_ll_sched_item *sch) +{ + struct dtm_ctx *ctx = sch->cb_arg; + int rc; + + if (!ctx->active) { + return BLE_LL_SCHED_STATE_DONE; + } + + rc = ble_phy_setchan(channel_rf_to_index[ctx->rf_channel], + BLE_DTM_SYNC_WORD, BLE_DTM_CRC); + if (rc != 0) { + BLE_LL_ASSERT(0); + return BLE_LL_SCHED_STATE_DONE; + } + +#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)) + ble_phy_mode_set(ctx->phy_mode, ctx->phy_mode); +#endif + ble_phy_set_txend_cb(ble_ll_dtm_tx_done, ctx); + ble_phy_txpwr_set(0); + + sch->start_time += g_ble_ll_sched_offset_ticks; + + rc = ble_phy_tx_set_start_time(sch->start_time, sch->remainder); + if (rc) { + goto resched; + } + + rc = ble_phy_tx(ble_ll_tx_mbuf_pducb, ctx->om, BLE_PHY_TRANSITION_NONE); + if (rc) { + goto resched; + } + + ble_ll_state_set(BLE_LL_STATE_DTM); + + return BLE_LL_SCHED_STATE_DONE; + +resched: + /* Reschedule from LL task if late for this PDU */ + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &ctx->evt); + + STATS_INC(ble_ll_dtm_stats, tx_failed); + + return BLE_LL_SCHED_STATE_DONE; +} + +static void +ble_ll_dtm_calculate_itvl(struct dtm_ctx *ctx, uint8_t len, + uint16_t cmd_interval, int phy_mode) +{ + uint32_t l; + uint32_t itvl_usec; + uint32_t itvl_ticks; + + /* Calculate interval as per spec Bluetooth 5.0 Vol 6. Part F, 4.1.6 */ + l = ble_ll_pdu_tx_time_get(len + BLE_LL_PDU_HDR_LEN, phy_mode); + itvl_usec = ((l + 249 + 624) / 625) * 625; + +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + if (cmd_interval > itvl_usec) { + itvl_usec = cmd_interval; + } +#endif + + itvl_ticks = os_cputime_usecs_to_ticks(itvl_usec); + ctx->itvl_rem_usec = (itvl_usec - os_cputime_ticks_to_usecs(itvl_ticks)); + if (ctx->itvl_rem_usec == 31) { + ctx->itvl_rem_usec = 0; + ++itvl_ticks; + } + ctx->itvl_ticks = itvl_ticks; +} + +static int +ble_ll_dtm_tx_create_ctx(uint8_t packet_payload, uint8_t len, + uint8_t rf_channel, uint8_t phy_mode, + uint16_t cmd_interval, uint16_t cmd_pkt_count) +{ + int rc = 0; + uint8_t byte_pattern; + struct ble_mbuf_hdr *ble_hdr; + struct os_mbuf *m; + struct dtm_ctx *ctx = &g_ble_ll_dtm_ctx; + struct ble_ll_sched_item *sch = &ctx->sch; + + /* MSYS is big enough to get continues memory */ + m = os_msys_get_pkthdr(len, sizeof(struct ble_mbuf_hdr)); + ctx->om = m; + BLE_LL_ASSERT(g_ble_ll_dtm_ctx.om); + + ctx->phy_mode = phy_mode; + ctx->rf_channel = rf_channel; + ctx->num_of_packets = 0; +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + ctx->num_of_packets_max = cmd_pkt_count; +#endif + + /* Set BLE transmit header */ + ble_hdr = BLE_MBUF_HDR_PTR(m); + ble_hdr->txinfo.flags = 0; + ble_hdr->txinfo.offset = 0; + ble_hdr->txinfo.pyld_len = len; + ble_hdr->txinfo.hdr_byte = packet_payload; + + switch(packet_payload) { + case 0x00: + if (os_mbuf_copyinto(m, 0, &g_ble_ll_dtm_prbs9_data, len)) { + return 1; + } + goto schedule; + case 0x01: + byte_pattern = 0x0F; + break; + case 0x02: + byte_pattern = 0x55; + break; + case 0x03: + if (os_mbuf_copyinto(m, 0, &g_ble_ll_dtm_prbs15_data, len)) { + return 1; + } + goto schedule; + case 0x04: + byte_pattern = 0xFF; + break; + case 0x05: + byte_pattern = 0x00; + break; + case 0x06: + byte_pattern = 0xF0; + break; + case 0x07: + byte_pattern = 0xAA; + break; + default: + return 1; + } + + for (rc = 0; rc < len; rc++) { + if (os_mbuf_copyinto(m, rc, &byte_pattern, 1)) { + return 1; + } + } + +schedule: + ble_phy_enable_dtm(); + + sch->sched_cb = ble_ll_dtm_tx_sched_cb; + sch->cb_arg = ctx; + sch->sched_type = BLE_LL_SCHED_TYPE_DTM; + + /* Prepare os_event */ + ble_npl_event_init(&ctx->evt, ble_ll_dtm_ev_tx_resched_cb, ctx); + + ble_ll_dtm_calculate_itvl(ctx, len, cmd_interval, phy_mode); + + ctx->pdu_start_ticks = ble_ll_rfmgmt_enable_now(); + ctx->pdu_start_usecs = 0; + ble_ll_dtm_set_next(ctx); + + /* Set some start point for TX packets */ + rc = ble_ll_sched_dtm(sch); + BLE_LL_ASSERT(rc == 0); + + g_ble_ll_dtm_ctx.active = 1; + return 0; +} + +static int +ble_ll_dtm_rx_start(void) +{ + os_sr_t sr; + int rc; + + rc = ble_phy_setchan(channel_rf_to_index[g_ble_ll_dtm_ctx.rf_channel], + BLE_DTM_SYNC_WORD, BLE_DTM_CRC); + if (rc) { + return rc; + } + +#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)) + ble_phy_mode_set(g_ble_ll_dtm_ctx.phy_mode, g_ble_ll_dtm_ctx.phy_mode); +#endif + + OS_ENTER_CRITICAL(sr); + rc = ble_phy_rx_set_start_time(os_cputime_get32(), 0); + OS_EXIT_CRITICAL(sr); + if (rc && rc != BLE_PHY_ERR_RX_LATE) { + return rc; + } + + ble_ll_state_set(BLE_LL_STATE_DTM); + + return 0; +} + +static int +ble_ll_dtm_rx_sched_cb(struct ble_ll_sched_item *sch) +{ + if (ble_ll_dtm_rx_start() != 0) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_dtm_ctx.evt); + STATS_INC(ble_ll_dtm_stats, rx_failed); + } + + return BLE_LL_SCHED_STATE_DONE; +} + +static int +ble_ll_dtm_rx_create_ctx(uint8_t rf_channel, uint8_t phy_mode) +{ + struct ble_ll_sched_item *sch = &g_ble_ll_dtm_ctx.sch; + int rc; + + g_ble_ll_dtm_ctx.phy_mode = phy_mode; + g_ble_ll_dtm_ctx.rf_channel = rf_channel; + + STATS_CLEAR(ble_ll_dtm_stats, rx_count); + + ble_npl_event_init(&g_ble_ll_dtm_ctx.evt, ble_ll_dtm_ev_rx_restart_cb, + NULL); + + sch->sched_cb = ble_ll_dtm_rx_sched_cb; + sch->cb_arg = &g_ble_ll_dtm_ctx; + sch->sched_type = BLE_LL_SCHED_TYPE_DTM; + sch->start_time = ble_ll_rfmgmt_enable_now(); + + rc = ble_ll_sched_dtm(sch); + BLE_LL_ASSERT(rc == 0); + + ble_phy_enable_dtm(); + + g_ble_ll_dtm_ctx.active = 1; + return 0; +} + +static void +ble_ll_dtm_ctx_free(struct dtm_ctx * ctx) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + if (!ctx->active) { + OS_EXIT_CRITICAL(sr); + return; + } + + ble_ll_sched_rmv_elem(&ctx->sch); + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &g_ble_ll_dtm_ctx.evt); + + ble_phy_disable(); + ble_phy_disable_dtm(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_rfmgmt_release(); + + os_mbuf_free_chain(ctx->om); + memset(ctx, 0, sizeof(*ctx)); + OS_EXIT_CRITICAL(sr); +} + +static int +ble_ll_dtm_tx_test(uint8_t tx_chan, uint8_t len, uint8_t packet_payload, + uint8_t hci_phy, uint16_t interval, uint16_t pkt_count) +{ + uint8_t phy_mode; + + if (g_ble_ll_dtm_ctx.active) { + return BLE_ERR_CTLR_BUSY; + } + + switch (hci_phy) { + case BLE_HCI_LE_PHY_1M: + phy_mode = BLE_PHY_MODE_1M; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_HCI_LE_PHY_2M: + phy_mode = BLE_PHY_MODE_2M; + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_HCI_LE_PHY_CODED_S8: + phy_mode = BLE_PHY_MODE_CODED_125KBPS; + break; + case BLE_HCI_LE_PHY_CODED_S2: + phy_mode = BLE_PHY_MODE_CODED_500KBPS; + break; +#endif + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (tx_chan > 0x27 || packet_payload > 0x07) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (ble_ll_dtm_tx_create_ctx(packet_payload, len, tx_chan, phy_mode, + interval, pkt_count)) { + return BLE_ERR_UNSPECIFIED; + } + + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) +static int +ble_ll_hci_dtm_tx_test_ext(const uint8_t *cmdbuf) +{ + const struct ble_hci_le_tx_test_ext_cp *cmd = (const void *) cmdbuf; + + return ble_ll_dtm_tx_test(cmd->tx_chan, cmd->test_data_len, cmd->payload, + BLE_HCI_LE_PHY_1M, le16toh(cmd->interval), + le16toh(cmd->pkt_count)); +} + +static int +ble_ll_hci_dtm_tx_test_v2_ext(const uint8_t *cmdbuf) +{ + const struct ble_hci_le_tx_test_v2_ext_cp *cmd = (const void *) cmdbuf; + + return ble_ll_dtm_tx_test(cmd->tx_chan, cmd->test_data_len, cmd->payload, + cmd->phy, le16toh(cmd->interval), + le16toh(cmd->pkt_count)); +} +#endif + +int +ble_ll_hci_dtm_tx_test(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_tx_test_cp *cmd = (const void *) cmdbuf; + +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + if (len == sizeof(struct ble_hci_le_tx_test_ext_cp)) { + return ble_ll_hci_dtm_tx_test_ext(cmdbuf); + } +#endif + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_dtm_tx_test(cmd->tx_chan, cmd->test_data_len, cmd->payload, + BLE_HCI_LE_PHY_1M, 0, 0); +} + +int +ble_ll_hci_dtm_tx_test_v2(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_tx_test_v2_cp *cmd = (const void *) cmdbuf; + +#if MYNEWT_VAL(BLE_LL_DTM_EXTENSIONS) + if (len == sizeof(struct ble_hci_le_tx_test_v2_ext_cp)) { + return ble_ll_hci_dtm_tx_test_v2_ext(cmdbuf); + } +#endif + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_dtm_tx_test(cmd->tx_chan, cmd->test_data_len, cmd->payload, + cmd->phy, 0, 0); +} + +static int +ble_ll_dtm_rx_test(uint8_t rx_chan, uint8_t hci_phy) +{ + uint8_t phy_mode; + + if (g_ble_ll_dtm_ctx.active) { + return BLE_ERR_CTLR_BUSY; + } + + switch (hci_phy) { + case BLE_HCI_LE_PHY_1M: + phy_mode = BLE_PHY_MODE_1M; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_HCI_LE_PHY_2M: + phy_mode = BLE_PHY_MODE_2M; + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_HCI_LE_PHY_CODED: + phy_mode = BLE_PHY_MODE_CODED_500KBPS; + break; +#endif + default: + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (rx_chan > 0x27) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (ble_ll_dtm_rx_create_ctx(rx_chan, phy_mode)) { + return BLE_ERR_UNSPECIFIED; + } + + return BLE_ERR_SUCCESS; +} + +int ble_ll_hci_dtm_rx_test(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rx_test_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_dtm_rx_test(cmd->rx_chan, BLE_HCI_LE_PHY_1M); +} + +int ble_ll_hci_dtm_rx_test_v2(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rx_test_v2_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* TODO ignoring modulation index */ + + return ble_ll_dtm_rx_test(cmd->rx_chan, cmd->phy); +} + +int ble_ll_dtm_end_test(uint8_t *rsp, uint8_t *rsplen) +{ + put_le16(rsp, g_ble_ll_dtm_ctx. num_of_packets); + *rsplen = 2; + + ble_ll_dtm_ctx_free(&g_ble_ll_dtm_ctx); + return BLE_ERR_SUCCESS; +} + +int ble_ll_dtm_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa) +{ + return 0; +} + +void +ble_ll_dtm_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) +{ + if (BLE_MBUF_HDR_CRC_OK(hdr)) { + /* XXX Compare data. */ + g_ble_ll_dtm_ctx.num_of_packets++; + STATS_INC(ble_ll_dtm_stats, rx_count); + } + + if (ble_ll_dtm_rx_start() != 0) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_dtm_ctx.evt); + STATS_INC(ble_ll_dtm_stats, rx_failed); + } +} + +int +ble_ll_dtm_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + struct os_mbuf *rxpdu; + + if (!g_ble_ll_dtm_ctx.active) { + return -1; + } + + rxpdu = ble_ll_rxpdu_alloc(rxbuf[1] + BLE_LL_PDU_HDR_LEN); + + /* Copy the received pdu and hand it up */ + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + ble_ll_rx_pdu_in(rxpdu); + } + + return 0; +} + +void +ble_ll_dtm_wfr_timer_exp(void) +{ + /* Should not be needed */ + BLE_LL_ASSERT(0); +} + + +void +ble_ll_dtm_reset(void) +{ + ble_ll_dtm_ctx_free(&g_ble_ll_dtm_ctx); +} + +void +ble_ll_dtm_init(void) +{ + int rc; + + rc = stats_init_and_reg(STATS_HDR(ble_ll_dtm_stats), + STATS_SIZE_INIT_PARMS(ble_ll_dtm_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_ll_dtm_stats), + "ble_ll_dtm"); + SYSINIT_PANIC_ASSERT(rc == 0); +} +#endif +#endif \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm_priv.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm_priv.h new file mode 100644 index 0000000..e04af07 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_dtm_priv.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_TEST_PRIV_ +#define H_BLE_LL_TEST_PRIV_ + +#include +#include +#include "nimble/ble.h" + +int ble_ll_hci_dtm_tx_test(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_hci_dtm_tx_test_v2(const uint8_t *cmdbuf, uint8_t len); + +int ble_ll_hci_dtm_rx_test(const uint8_t *cmdbuf, uint8_t len); +int ble_ll_hci_dtm_rx_test_v2(const uint8_t *cmdbuf, uint8_t len); + +int ble_ll_dtm_end_test(uint8_t *rsp, uint8_t *rsplen); + +int ble_ll_dtm_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa); +int ble_ll_dtm_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr); +void ble_ll_dtm_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr); +void ble_ll_dtm_wfr_timer_exp(void); +void ble_ll_dtm_reset(void); +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci.c new file mode 100644 index 0000000..d83af2c --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci.c @@ -0,0 +1,1519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_sync.h" +#include "ble_ll_priv.h" +#include "ble_ll_conn_priv.h" + +#if MYNEWT_VAL(BLE_LL_DTM) +#include "ble_ll_dtm_priv.h" +#endif + +static void ble_ll_hci_cmd_proc(struct ble_npl_event *ev); + +/* OS event to enqueue command */ +static struct ble_npl_event g_ble_ll_hci_cmd_ev; + +/* LE event mask */ +static uint64_t g_ble_ll_hci_le_event_mask; +static uint64_t g_ble_ll_hci_event_mask; +static uint64_t g_ble_ll_hci_event_mask2; + +static int16_t rx_path_pwr_compensation; +static int16_t tx_path_pwr_compensation; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static enum { + ADV_MODE_ANY, + ADV_MODE_LEGACY, + ADV_MODE_EXT, +} hci_adv_mode; + +bool ble_ll_hci_adv_mode_ext(void) +{ + return hci_adv_mode == ADV_MODE_EXT; +} +#else +bool +ble_ll_hci_adv_mode_ext(void) +{ + return false; +} +#endif + +/** + * ll hci get num cmd pkts + * + * Returns the number of command packets that the host is allowed to send + * to the controller. + * + * @return uint8_t + */ +static uint8_t +ble_ll_hci_get_num_cmd_pkts(void) +{ + return BLE_LL_CFG_NUM_HCI_CMD_PKTS; +} + +/** + * Send an event to the host. + * + * @param evbuf Pointer to event buffer to send + * + * @return int 0: success; -1 otherwise. + */ +int +ble_ll_hci_event_send(struct ble_hci_ev *hci_ev) +{ + int rc; + + BLE_LL_DEBUG_GPIO(HCI_EV, 1); + + BLE_LL_ASSERT(sizeof(*hci_ev) + hci_ev->length <= BLE_LL_MAX_EVT_LEN); + + /* Count number of events sent */ + STATS_INC(ble_ll_stats, hci_events_sent); + + /* Send the event to the host */ + rc = ble_hci_trans_ll_evt_tx((uint8_t *)hci_ev); + + BLE_LL_DEBUG_GPIO(HCI_EV, 0); + + return rc; +} + +/** + * Created and sends a command complete event with the no-op opcode to the + * host. + */ +void +ble_ll_hci_send_noop(void) +{ + struct ble_hci_ev_command_complete_nop *ev; + struct ble_hci_ev *hci_ev; + + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + /* Create a command complete event with a NO-OP opcode */ + hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_COMPLETE; + + hci_ev->length = sizeof(*ev); + ev = (void *)hci_ev->data; + + ev->num_packets = ble_ll_hci_get_num_cmd_pkts(); + ev->opcode = BLE_HCI_OPCODE_NOP; + + ble_ll_hci_event_send(hci_ev); + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/** + * LE encrypt command + * + * @param cmdbuf + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_le_encrypt(const uint8_t *cmdbuf, uint8_t len, uint8_t *rspbuf, + uint8_t *rsplen) +{ + const struct ble_hci_le_encrypt_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_encrypt_rp *rsp = (void *)rspbuf; + struct ble_encryption_block ecb; + int rc; + + /* Call the link layer to encrypt the data */ + swap_buf(ecb.key, cmd->key, BLE_ENC_BLOCK_SIZE); + swap_buf(ecb.plain_text, cmd->data, BLE_ENC_BLOCK_SIZE); + rc = ble_hw_encrypt_block(&ecb); + if (!rc) { + swap_buf(rsp->data, ecb.cipher_text, BLE_ENC_BLOCK_SIZE); + *rsplen = sizeof(*rsp); + rc = BLE_ERR_SUCCESS; + } else { + rc = BLE_ERR_CTLR_BUSY; + } + + return rc; +} +#endif + +/** + * LE rand command + * + * @param cmdbuf + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_le_rand(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rand_rp *rsp = (void *) rspbuf; + + ble_ll_rand_data_get((uint8_t *)&rsp->random_number, + sizeof(rsp->random_number)); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Read local version + * + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_rd_local_version(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_ip_rd_local_ver_rp *rsp = (void *) rspbuf; + + rsp->hci_ver = BLE_HCI_VER_BCS; + rsp->hci_rev = 0; + rsp->lmp_ver = BLE_LMP_VER_BCS; + rsp->manufacturer = htole16(MYNEWT_VAL(BLE_LL_MFRG_ID)); + rsp->lmp_subver = 0; + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Read local supported features + * + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_rd_local_supp_feat(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_ip_rd_loc_supp_feat_rp *rsp = (void *) rspbuf; + + /* + * The only two bits we set here currently are (5th byte): + * BR/EDR not supported (bit 5) + * LE supported (controller) (bit 6) + */ + rsp->features = htole64(0x0000006000000000); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Read local supported commands + * + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_rd_local_supp_cmd(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_ip_rd_loc_supp_cmd_rp *rsp = (void *) rspbuf; + + memset(rsp->commands, 0, sizeof(rsp->commands)); + memcpy(rsp->commands, g_ble_ll_supp_cmds, sizeof(g_ble_ll_supp_cmds)); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Called to read the public device address of the device + * + * + * @param rspbuf + * @param rsplen + * + * @return int + */ +static int +ble_ll_hci_rd_bd_addr(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_ip_rd_bd_addr_rp *rsp = (void *) rspbuf; + + memcpy(rsp->addr, g_dev_addr, BLE_DEV_ADDR_LEN); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * ll hci set le event mask + * + * Called when the LL controller receives a set LE event mask command. + * + * Context: Link Layer task (HCI command parser) + * + * @param cmdbuf Pointer to command buf. + * + * @return int BLE_ERR_SUCCESS. Does not return any errors. + */ +static int +ble_ll_hci_set_le_event_mask(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_event_mask_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + g_ble_ll_hci_le_event_mask = le64toh(cmd->event_mask); + + return BLE_ERR_SUCCESS; +} + +/** + * HCI read buffer size command. Returns the ACL data packet length and + * num data packets. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_read_bufsize(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_buf_size_rp *rp = (void *) rspbuf; + + rp->data_len = htole16(g_ble_ll_data.ll_acl_pkt_size); + rp->data_packets = g_ble_ll_data.ll_num_acl_pkts; + + *rsplen = sizeof(*rp); + return BLE_ERR_SUCCESS; +} + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/** + * Checks the preferred phy masks for validity and places the preferred masks + * in the input phy masks + + * @return int BLE_ERR_SUCCESS or BLE_ERR_INV_HCI_CMD_PARMS or BLE_ERR_UNSUPPORTED + */ +int +ble_ll_hci_chk_phy_masks(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys, + uint8_t *txphy, uint8_t *rxphy) +{ + /* Check for RFU */ + if ((tx_phys & ~BLE_HCI_LE_PHY_PREF_MASK_ALL) || + (rx_phys & ~BLE_HCI_LE_PHY_PREF_MASK_ALL)) { + return BLE_ERR_UNSUPPORTED; + } + + if ((!(all_phys & BLE_HCI_LE_PHY_NO_TX_PREF_MASK) && (tx_phys == 0)) || + (!(all_phys & BLE_HCI_LE_PHY_NO_RX_PREF_MASK) && (rx_phys == 0))) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If phy not supported, return error */ +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + if((tx_phys & BLE_HCI_LE_PHY_2M_PREF_MASK) || + (rx_phys & BLE_HCI_LE_PHY_2M_PREF_MASK)) { + return BLE_ERR_UNSUPPORTED; + } +#endif +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if ((tx_phys & BLE_HCI_LE_PHY_CODED_PREF_MASK) || + (rx_phys & BLE_HCI_LE_PHY_CODED_PREF_MASK)) { + return BLE_ERR_UNSUPPORTED; + } +#endif + /* Set the default PHY preferences */ + if (all_phys & BLE_HCI_LE_PHY_NO_TX_PREF_MASK) { + tx_phys = BLE_HCI_LE_PHY_PREF_MASK_ALL; + } + *txphy = tx_phys; + + if (all_phys & BLE_HCI_LE_PHY_NO_RX_PREF_MASK) { + rx_phys = BLE_HCI_LE_PHY_PREF_MASK_ALL; + } + *rxphy = rx_phys; + + return BLE_ERR_SUCCESS; +} + +/** + * Set PHY preferences for connection + * + * @param cmdbuf + * + * @return int + */ +static int +ble_ll_hci_le_set_def_phy(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_default_phy_cp *cmd = (const void *) cmdbuf; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rc = ble_ll_hci_chk_phy_masks(cmd->all_phys, cmd->tx_phys, cmd->rx_phys, + &g_ble_ll_data.ll_pref_tx_phys, + &g_ble_ll_data.ll_pref_rx_phys); + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) +/** + * HCI write suggested default data length command. + * + * This command is used by the host to change the initial max tx octets/time + * for all connections. Note that if the controller does not support the + * requested times no error is returned; the controller simply ignores the + * request (but remembers what the host requested for the read suggested + * default data length command). The spec allows for the controller to + * disregard the host. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_wr_sugg_data_len(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_wr_sugg_def_data_len_cp *cmd = (const void*) cmdbuf; + uint16_t tx_oct; + uint16_t tx_time; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Get suggested octets and time */ + tx_oct = le16toh(cmd->max_tx_octets); + tx_time = le16toh(cmd->max_tx_time); + + /* If valid, write into suggested and change connection initial times */ + if (ble_ll_chk_txrx_octets(tx_oct) && ble_ll_chk_txrx_time(tx_time)) { + g_ble_ll_conn_params.sugg_tx_octets = (uint8_t)tx_oct; + g_ble_ll_conn_params.sugg_tx_time = tx_time; + + /* + * We can disregard host suggestion, but we are a nice controller so + * let's use host suggestion, unless they exceed max supported values + * in which case we just use our max. + */ + g_ble_ll_conn_params.conn_init_max_tx_octets = + min(tx_oct, g_ble_ll_conn_params.supp_max_tx_octets); + g_ble_ll_conn_params.conn_init_max_tx_time = + min(tx_time, g_ble_ll_conn_params.supp_max_tx_time); + + /* + * Use the same for coded and uncoded defaults. These are used when PHY + * parameters are initialized and we want to use values overridden by + * host. Make sure we do not exceed max supported time on uncoded. + */ + g_ble_ll_conn_params.conn_init_max_tx_time_uncoded = + min(BLE_LL_CONN_SUPP_TIME_MAX_UNCODED, + g_ble_ll_conn_params.conn_init_max_tx_time); + g_ble_ll_conn_params.conn_init_max_tx_time_coded = + g_ble_ll_conn_params.conn_init_max_tx_time; + + rc = BLE_ERR_SUCCESS; + } else { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + } + + return rc; +} + +/** + * HCI read suggested default data length command. Returns the controllers + * initial max tx octet/time. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_rd_sugg_data_len(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_sugg_def_data_len_rp *rsp = (void *) rspbuf; + + /* Place the data packet length and number of packets in the buffer */ + rsp->max_tx_octets = htole16(g_ble_ll_conn_params.sugg_tx_octets); + rsp->max_tx_time = htole16(g_ble_ll_conn_params.sugg_tx_time); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * HCI read maximum data length command. Returns the controllers max supported + * rx/tx octets/times. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_rd_max_data_len(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_max_data_len_rp *rsp = (void *)rspbuf; + + /* Place the data packet length and number of packets in the buffer */ + rsp->max_tx_octests = htole16(g_ble_ll_conn_params.supp_max_tx_octets); + rsp->max_tx_time = htole16(g_ble_ll_conn_params.supp_max_tx_time); + rsp->max_rx_octests = htole16(g_ble_ll_conn_params.supp_max_rx_octets); + rsp->max_rx_time = htole16(g_ble_ll_conn_params.supp_max_rx_time); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} +#endif + +/** + * HCI read local supported features command. Returns the features + * supported by the controller. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_read_local_features(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_loc_supp_feat_rp *rsp = (void *) rspbuf; + + rsp->features = htole64(ble_ll_read_supp_features()); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * HCI read local supported states command. Returns the states + * supported by the controller. + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_hci_le_read_supp_states(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_supp_states_rp *rsp = (void *) rspbuf; + + /* Add list of supported states. */ + rsp->states = htole64(ble_ll_read_supp_states()); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + + +/** + * Checks to see if a LE event has been disabled by the host. + * + * @param subev Sub-event code of the LE Meta event. Note that this can + * be a value from 1 to 64, inclusive. + * + * @return uint8_t 0: event is not enabled; otherwise event is enabled. + */ +bool +ble_ll_hci_is_le_event_enabled(unsigned int subev) +{ + /* The LE meta event must be enabled for any LE event to be enabled */ + if (g_ble_ll_hci_event_mask & (1ull << (BLE_HCI_EVCODE_LE_META - 1))) { + return g_ble_ll_hci_le_event_mask & (1ull << (subev - 1)); + } + + return false; +} + +/** + * Checks to see if an event has been disabled by the host. + * + * NOTE: there are two "pages" of event masks; the first page is for event + * codes between 0 and 63 and the second page is for event codes 64 and + * greater. + * + * @param evcode This is the event code for the event. + * + * @return uint8_t 0: event is not enabled; otherwise event is enabled. + */ +bool +ble_ll_hci_is_event_enabled(unsigned int evcode) +{ + if (evcode >= 64) { + return g_ble_ll_hci_event_mask2 & (1ull << (evcode - 64)); + } + + return g_ble_ll_hci_event_mask & (1ull << (evcode - 1)); +} + +/** + * Called to determine if the reply to the command should be a command complete + * event or a command status event. + * + * @param ocf + * + * @return int 0: return command complete; 1: return command status event + */ +static int +ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf) +{ + int rc; + + switch (ocf) { + case BLE_HCI_OCF_LE_RD_REM_FEAT: + case BLE_HCI_OCF_LE_CREATE_CONN: + case BLE_HCI_OCF_LE_EXT_CREATE_CONN: + case BLE_HCI_OCF_LE_CONN_UPDATE: + case BLE_HCI_OCF_LE_START_ENCRYPT: + case BLE_HCI_OCF_LE_RD_P256_PUBKEY: + case BLE_HCI_OCF_LE_GEN_DHKEY: + case BLE_HCI_OCF_LE_SET_PHY: + case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC: + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/** HCI LE read maximum advertising data length command. Returns the controllers +* max supported advertising data length; +* +* @param rspbuf Pointer to response buffer +* @param rsplen Length of response buffer +* +* @return int BLE error code +*/ +static int +ble_ll_adv_rd_max_adv_data_len(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_max_adv_data_len_rp *rsp = (void *) rspbuf; + + rsp->max_adv_data_len = htole16(BLE_ADV_DATA_MAX_LEN); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * HCI LE read number of supported advertising sets + * + * @param rspbuf Pointer to response buffer + * @param rsplen Length of response buffer + * + * @return int BLE error code + */ +static int +ble_ll_adv_rd_sup_adv_sets(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_num_of_adv_sets_rp *rsp = (void *)rspbuf; + + rsp->num_sets = BLE_ADV_INSTANCES; + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +static bool +ble_ll_is_valid_adv_mode(uint8_t ocf) +{ + /* + * If, since the last power-on or reset, the Host has ever issued a legacy + * advertising command and then issues an extended advertising command, or + * has ever issued an extended advertising command and then issues a legacy + * advertising command, the Controller shall return the error code Command + * Disallowed (0x0C). + */ + + switch(ocf) { + case BLE_HCI_OCF_LE_CREATE_CONN: + case BLE_HCI_OCF_LE_SET_ADV_PARAMS: + case BLE_HCI_OCF_LE_SET_ADV_ENABLE: + case BLE_HCI_OCF_LE_SET_ADV_DATA: + case BLE_HCI_OCF_LE_SET_SCAN_PARAMS: + case BLE_HCI_OCF_LE_SET_SCAN_ENABLE: + case BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA: + case BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR: + if (hci_adv_mode == ADV_MODE_EXT) { + return false; + } + + hci_adv_mode = ADV_MODE_LEGACY; + break; + case BLE_HCI_OCF_LE_EXT_CREATE_CONN: + case BLE_HCI_OCF_LE_SET_EXT_ADV_DATA: + case BLE_HCI_OCF_LE_SET_EXT_ADV_ENABLE: + case BLE_HCI_OCF_LE_SET_EXT_ADV_PARAM: + case BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE: + case BLE_HCI_OCF_LE_SET_EXT_SCAN_PARAM: + case BLE_HCI_OCF_LE_SET_EXT_SCAN_RSP_DATA: + case BLE_HCI_OCF_LE_RD_MAX_ADV_DATA_LEN: + case BLE_HCI_OCF_LE_RD_NUM_OF_ADV_SETS: + case BLE_HCI_OCF_LE_REMOVE_ADV_SET: + case BLE_HCI_OCF_LE_CLEAR_ADV_SETS: + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_PARAMS: + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_DATA: + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE: + case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC: + case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL: + case BLE_HCI_OCF_LE_PERIODIC_ADV_TERM_SYNC: + case BLE_HCI_OCF_LE_ADD_DEV_TO_PERIODIC_ADV_LIST: + case BLE_HCI_OCF_LE_REM_DEV_FROM_PERIODIC_ADV_LIST: + case BLE_HCI_OCF_LE_CLEAR_PERIODIC_ADV_LIST: + case BLE_HCI_OCF_LE_RD_PERIODIC_ADV_LIST_SIZE: +#if MYNEWT_VAL(BLE_VERSION) >= 51 + case BLE_HCI_OCF_LE_PERIODIC_ADV_RECEIVE_ENABLE: +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER: + case BLE_HCI_OCF_LE_PERIODIC_ADV_SET_INFO_TRANSFER: + case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER_PARAMS: + case BLE_HCI_OCF_LE_SET_DEFAULT_SYNC_TRANSFER_PARAMS: +#endif + if (hci_adv_mode == ADV_MODE_LEGACY) { + return false; + } + + hci_adv_mode = ADV_MODE_EXT; + break; + default: + break; + } + + return true; +} +#endif + +static int +ble_ll_read_tx_power(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_transmit_power_rp *rsp = (void *) rspbuf; + + rsp->min_tx_power = ble_phy_txpower_round(-127); + rsp->max_tx_power = ble_phy_txpower_round(126); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +static int +ble_ll_read_rf_path_compensation(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_rf_path_compensation_rp *rsp = (void *) rspbuf; + + rsp->rx_path_compensation = htole16(rx_path_pwr_compensation); + rsp->tx_path_compensation = htole16(tx_path_pwr_compensation); + + *rsplen = sizeof(*rsp);; + return BLE_ERR_SUCCESS; +} + +static int +ble_ll_write_rf_path_compensation(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_wr_rf_path_compensation_cp *cmd = (const void *)cmdbuf; + int16_t rx; + int16_t tx; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + tx = le16toh(cmd->tx_path_compensation); + rx = le16toh(cmd->rx_path_compensation); + + if ((tx < -1280) || (tx > 1280) || (rx < -1280) || (rx > 1280)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + tx_path_pwr_compensation = tx; + rx_path_pwr_compensation = rx; + + ble_phy_set_rx_pwr_compensation(rx_path_pwr_compensation / 10); + + return BLE_ERR_SUCCESS; +} + +int8_t +ble_ll_get_tx_pwr_compensation(void) +{ + return tx_path_pwr_compensation / 10; +} + +/** + * Process a LE command sent from the host to the controller. The HCI command + * has a 3 byte command header followed by data. The header is: + * -> opcode (2 bytes) + * -> Length of parameters (1 byte; does include command header bytes). + * + * @param cmdbuf Pointer to command buffer. Points to start of command header. + * @param ocf Opcode command field. + * @param *rsplen Pointer to length of response + * + * @return int This function returns a BLE error code. If a command status + * event should be returned as opposed to command complete, + * 256 gets added to the return value. + */ +static int +ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf, + uint8_t *rspbuf, uint8_t *rsplen, + ble_ll_hci_post_cmd_complete_cb *cb) +{ + int rc; + + /* Assume error; if all pass rc gets set to 0 */ + rc = BLE_ERR_INV_HCI_CMD_PARMS; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (!ble_ll_is_valid_adv_mode(ocf)) { + rc = BLE_ERR_CMD_DISALLOWED; + + /* + * This code is here because we add 256 to the return code to denote + * that the reply to this command should be command status (as opposed to + * command complete). + * + * For unknown HCI command let us return always command status as per + * specification Bluetooth 5, Vol. 2, Chapter 4.4 + */ + if (ble_ll_hci_le_cmd_send_cmd_status(ocf)) { + rc += (BLE_ERR_MAX + 1); + } + + return rc; + } +#endif + + switch (ocf) { + case BLE_HCI_OCF_LE_SET_EVENT_MASK: + rc = ble_ll_hci_set_le_event_mask(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RD_BUF_SIZE: + if (len == 0) { + rc = ble_ll_hci_le_read_bufsize(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT: + if (len == 0) { + rc = ble_ll_hci_le_read_local_features(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_SET_RAND_ADDR: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + rc = ble_ll_set_random_addr(cmdbuf, len, hci_adv_mode == ADV_MODE_EXT); +#else + rc = ble_ll_set_random_addr(cmdbuf, len, false); +#endif + break; + case BLE_HCI_OCF_LE_SET_ADV_PARAMS: + rc = ble_ll_adv_set_adv_params(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR: + if (len == 0) { + rc = ble_ll_adv_read_txpwr(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_SET_ADV_DATA: + rc = ble_ll_hci_set_adv_data(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA: + rc = ble_ll_hci_set_scan_rsp_data(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_ADV_ENABLE: + rc = ble_ll_hci_adv_set_enable(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_SCAN_PARAMS: + rc = ble_ll_scan_set_scan_params(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_SCAN_ENABLE: + rc = ble_ll_hci_scan_set_enable(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CREATE_CONN: + rc = ble_ll_conn_create(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CREATE_CONN_CANCEL: + if (len == 0) { + rc = ble_ll_conn_create_cancel(cb); + } + break; + case BLE_HCI_OCF_LE_RD_WHITE_LIST_SIZE: + if (len == 0) { + rc = ble_ll_whitelist_read_size(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_CLEAR_WHITE_LIST: + if (len == 0) { + rc = ble_ll_whitelist_clear(); + } + break; + case BLE_HCI_OCF_LE_ADD_WHITE_LIST: + rc = ble_ll_whitelist_add(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RMV_WHITE_LIST: + rc = ble_ll_whitelist_rmv(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CONN_UPDATE: + rc = ble_ll_conn_hci_update(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS: + rc = ble_ll_conn_hci_set_chan_class(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RD_CHAN_MAP: + rc = ble_ll_conn_hci_rd_chan_map(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_RD_REM_FEAT: + rc = ble_ll_conn_hci_read_rem_features(cmdbuf, len); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + case BLE_HCI_OCF_LE_ENCRYPT: + rc = ble_ll_hci_le_encrypt(cmdbuf, len, rspbuf, rsplen); + break; +#endif + case BLE_HCI_OCF_LE_RAND: + if (len == 0) { + rc = ble_ll_hci_le_rand(rspbuf, rsplen); + } + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + case BLE_HCI_OCF_LE_START_ENCRYPT: + rc = ble_ll_conn_hci_le_start_encrypt(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY: + rc = ble_ll_conn_hci_le_ltk_reply(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY: + rc = ble_ll_conn_hci_le_ltk_neg_reply(cmdbuf, len, rspbuf, rsplen); + break; +#endif + case BLE_HCI_OCF_LE_RD_SUPP_STATES : + if (len == 0) { + rc = ble_ll_hci_le_read_supp_states(rspbuf, rsplen); + } + break; +#if MYNEWT_VAL(BLE_LL_DTM) + case BLE_HCI_OCF_LE_TX_TEST: + rc = ble_ll_hci_dtm_tx_test(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RX_TEST: + rc = ble_ll_hci_dtm_rx_test(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_TEST_END: + if (len == 0) { + rc = ble_ll_dtm_end_test(rspbuf, rsplen); + } + break; +#endif + case BLE_HCI_OCF_LE_REM_CONN_PARAM_RR: + rc = ble_ll_conn_hci_param_rr(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR: + rc = ble_ll_conn_hci_param_nrr(cmdbuf, len, rspbuf, rsplen); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) + case BLE_HCI_OCF_LE_SET_DATA_LEN: + rc = ble_ll_conn_hci_set_data_len(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_RD_SUGG_DEF_DATA_LEN: + if (len == 0) { + rc = ble_ll_hci_le_rd_sugg_data_len(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_WR_SUGG_DEF_DATA_LEN: + rc = ble_ll_hci_le_wr_sugg_data_len(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + case BLE_HCI_OCF_LE_ADD_RESOLV_LIST: + rc = ble_ll_resolv_list_add(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RMV_RESOLV_LIST: + rc = ble_ll_resolv_list_rmv(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CLR_RESOLV_LIST: + if (len == 0) { + rc = ble_ll_resolv_list_clr(); + } + break; + case BLE_HCI_OCF_LE_RD_RESOLV_LIST_SIZE: + if (len == 0) { + rc = ble_ll_resolv_list_read_size(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_RD_PEER_RESOLV_ADDR: + rc = ble_ll_resolv_peer_addr_rd(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_RD_LOCAL_RESOLV_ADDR: + rc = ble_ll_resolv_local_addr_rd(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_SET_ADDR_RES_EN: + rc = ble_ll_resolv_enable_cmd(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_RPA_TMO: + rc = ble_ll_resolv_set_rpa_tmo(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) + case BLE_HCI_OCF_LE_RD_MAX_DATA_LEN: + if (len == 0) { + rc = ble_ll_hci_le_rd_max_data_len(rspbuf, rsplen); + } + break; +#endif +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_HCI_OCF_LE_RD_PHY: + rc = ble_ll_conn_hci_le_rd_phy(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_SET_DEFAULT_PHY: + rc = ble_ll_hci_le_set_def_phy(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_PHY: + rc = ble_ll_conn_hci_le_set_phy(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_DTM) + case BLE_HCI_OCF_LE_RX_TEST_V2: + rc = ble_ll_hci_dtm_rx_test_v2(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_TX_TEST_V2: + rc = ble_ll_hci_dtm_tx_test_v2(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_HCI_OCF_LE_SET_ADV_SET_RND_ADDR: + rc = ble_ll_adv_hci_set_random_addr(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_EXT_ADV_PARAM: + rc = ble_ll_adv_ext_set_param(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_SET_EXT_ADV_DATA: + rc = ble_ll_adv_ext_set_adv_data(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_EXT_SCAN_RSP_DATA: + rc = ble_ll_adv_ext_set_scan_rsp(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_EXT_ADV_ENABLE: + rc = ble_ll_adv_ext_set_enable(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_RD_MAX_ADV_DATA_LEN: + if (len == 0) { + rc = ble_ll_adv_rd_max_adv_data_len(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_RD_NUM_OF_ADV_SETS: + if (len == 0) { + rc = ble_ll_adv_rd_sup_adv_sets(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_LE_REMOVE_ADV_SET: + rc = ble_ll_adv_remove(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CLEAR_ADV_SETS: + if (len == 0) { + rc = ble_ll_adv_clear_all(); + } + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_PARAMS: + rc = ble_ll_adv_periodic_set_param(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_DATA: + rc = ble_ll_adv_periodic_set_data(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE: + rc = ble_ll_adv_periodic_enable(cmdbuf, len); + break; +#endif +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_HCI_OCF_LE_SET_EXT_SCAN_PARAM: + rc = ble_ll_set_ext_scan_params(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE: + rc = ble_ll_hci_ext_scan_set_enable(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_EXT_CREATE_CONN: + rc = ble_ll_ext_conn_create(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC: + rc = ble_ll_sync_create(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL: + if (len == 0) { + rc = ble_ll_sync_cancel(cb); + } + break; + case BLE_HCI_OCF_LE_PERIODIC_ADV_TERM_SYNC: + rc = ble_ll_sync_terminate(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_ADD_DEV_TO_PERIODIC_ADV_LIST: + rc = ble_ll_sync_list_add(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_REM_DEV_FROM_PERIODIC_ADV_LIST: + rc = ble_ll_sync_list_remove(cmdbuf, len); + break; + case BLE_HCI_OCF_LE_CLEAR_PERIODIC_ADV_LIST: + if (len == 0) { + rc = ble_ll_sync_list_clear(); + } + break; + case BLE_HCI_OCF_LE_RD_PERIODIC_ADV_LIST_SIZE: + if (len == 0) { + rc = ble_ll_sync_list_size(rspbuf, rsplen); + } + break; +#if MYNEWT_VAL(BLE_VERSION) >= 51 + case BLE_HCI_OCF_LE_PERIODIC_ADV_RECEIVE_ENABLE: + rc = ble_ll_sync_receive_enable(cmdbuf, len); + break; +#endif +#endif + case BLE_HCI_OCF_LE_RD_TRANSMIT_POWER: + rc = ble_ll_read_tx_power(rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_RD_RF_PATH_COMPENSATION: + rc = ble_ll_read_rf_path_compensation(rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_WR_RF_PATH_COMPENSATION: + rc = ble_ll_write_rf_path_compensation(cmdbuf, len); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + case BLE_HCI_OCF_LE_SET_PRIVACY_MODE: + rc = ble_ll_resolve_set_priv_mode(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER: + rc = ble_ll_sync_transfer(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_PERIODIC_ADV_SET_INFO_TRANSFER: + rc = ble_ll_adv_periodic_set_info_transfer(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER_PARAMS: + rc = ble_ll_set_sync_transfer_params(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_SET_DEFAULT_SYNC_TRANSFER_PARAMS: + rc = ble_ll_set_default_sync_transfer_params(cmdbuf, len); + break; +#endif +#if MYNEWT_VAL(BLE_VERSION) >= 52 + case BLE_HCI_OCF_LE_SET_HOST_FEAT: + rc = ble_ll_set_host_feat(cmdbuf, len); + break; +#endif + default: + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + /* + * This code is here because we add 256 to the return code to denote + * that the reply to this command should be command status (as opposed to + * command complete). + * + * For unknown HCI command let us return always command status as per + * specification Bluetooth 5, Vol. 2, Chapter 4.4 + */ + if (ble_ll_hci_le_cmd_send_cmd_status(ocf) || rc == BLE_ERR_UNKNOWN_HCI_CMD) { + rc += (BLE_ERR_MAX + 1); + } + + return rc; +} + +/** + * Process a link control command sent from the host to the controller. The HCI + * command has a 3 byte command header followed by data. The header is: + * -> opcode (2 bytes) + * -> Length of parameters (1 byte; does include command header bytes). + * + * @param cmdbuf Pointer to command buffer. Points to start of command header. + * @param ocf Opcode command field. + * + * @return int This function returns a BLE error code. If a command status + * event should be returned as opposed to command complete, + * 256 gets added to the return value. + */ +static int +ble_ll_hci_link_ctrl_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf) +{ + int rc; + + switch (ocf) { + case BLE_HCI_OCF_DISCONNECT_CMD: + rc = ble_ll_conn_hci_disconnect_cmd(cmdbuf, len); + /* Send command status instead of command complete */ + rc += (BLE_ERR_MAX + 1); + break; + + case BLE_HCI_OCF_RD_REM_VER_INFO: + rc = ble_ll_conn_hci_rd_rem_ver_cmd(cmdbuf, len); + /* Send command status instead of command complete */ + rc += (BLE_ERR_MAX + 1); + break; + + default: + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + return rc; +} + +static int +ble_ll_hci_cb_set_event_mask(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_cb_set_event_mask_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof (*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + g_ble_ll_hci_event_mask = le64toh(cmd->event_mask); + + return BLE_ERR_SUCCESS; +} + +static int +ble_ll_hci_cb_set_event_mask2(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_cb_set_event_mask2_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof (*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + g_ble_ll_hci_event_mask2 = le64toh(cmd->event_mask2); + + return BLE_ERR_SUCCESS; +} + +static int +ble_ll_hci_ctlr_bb_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf, + uint8_t *rspbuf, uint8_t *rsplen) +{ + int rc; + + /* Assume error; if all pass rc gets set to 0 */ + rc = BLE_ERR_INV_HCI_CMD_PARMS; + + switch (ocf) { + case BLE_HCI_OCF_CB_SET_EVENT_MASK: + rc = ble_ll_hci_cb_set_event_mask(cmdbuf, len); + break; + case BLE_HCI_OCF_CB_RESET: + if (len == 0) { + rc = ble_ll_reset(); + } + break; + case BLE_HCI_OCF_CB_SET_EVENT_MASK2: + rc = ble_ll_hci_cb_set_event_mask2(cmdbuf, len); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING) + case BLE_HCI_OCF_CB_RD_AUTH_PYLD_TMO: + rc = ble_ll_conn_hci_rd_auth_pyld_tmo(cmdbuf, len, rspbuf, rsplen); + break; + case BLE_HCI_OCF_CB_WR_AUTH_PYLD_TMO: + rc = ble_ll_conn_hci_wr_auth_pyld_tmo(cmdbuf, len, rspbuf, rsplen); + break; +#endif + default: + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + return rc; +} + +static int +ble_ll_hci_info_params_cmd_proc(const uint8_t *cmdbuf, uint8_t len, + uint16_t ocf, uint8_t *rspbuf, uint8_t *rsplen) +{ + int rc; + + /* Assume error; if all pass rc gets set to 0 */ + rc = BLE_ERR_INV_HCI_CMD_PARMS; + + switch (ocf) { + case BLE_HCI_OCF_IP_RD_LOCAL_VER: + if (len == 0) { + rc = ble_ll_hci_rd_local_version(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_IP_RD_LOC_SUPP_CMD: + if (len == 0) { + rc = ble_ll_hci_rd_local_supp_cmd(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_IP_RD_LOC_SUPP_FEAT: + if (len == 0) { + rc = ble_ll_hci_rd_local_supp_feat(rspbuf, rsplen); + } + break; + case BLE_HCI_OCF_IP_RD_BD_ADDR: + if (len == 0) { + rc = ble_ll_hci_rd_bd_addr(rspbuf, rsplen); + } + break; + default: + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + return rc; +} + +static int +ble_ll_hci_status_params_cmd_proc(const uint8_t *cmdbuf, uint8_t len, + uint16_t ocf, uint8_t *rspbuf, + uint8_t *rsplen) +{ + int rc; + + switch (ocf) { + case BLE_HCI_OCF_RD_RSSI: + rc = ble_ll_conn_hci_rd_rssi(cmdbuf, len, rspbuf, rsplen); + break; + default: + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + return rc; +} + +/** + * Called to process an HCI command from the host. + * + * @param ev Pointer to os event containing a pointer to command buffer + */ +static void +ble_ll_hci_cmd_proc(struct ble_npl_event *ev) +{ + int rc; + uint8_t ogf; + uint8_t rsplen; + struct ble_hci_cmd *cmd; + uint16_t opcode; + uint16_t ocf; + ble_ll_hci_post_cmd_complete_cb post_cb = NULL; + struct ble_hci_ev *hci_ev; + struct ble_hci_ev_command_status *cmd_status; + struct ble_hci_ev_command_complete *cmd_complete; + uint8_t *rspbuf; + + BLE_LL_DEBUG_GPIO(HCI_CMD, 1); + + /* The command buffer is the event argument */ + cmd = ble_npl_event_get_arg(ev); + BLE_LL_ASSERT(cmd != NULL); + + /* Get the opcode from the command buffer */ + opcode = le16toh(cmd->opcode); + ocf = BLE_HCI_OCF(opcode); + ogf = BLE_HCI_OGF(opcode); + + /* + * The command response pointer points into the same buffer as the + * command data itself. That is fine, as each command reads all the data + * before crafting a response. + * Also reuse cmd buffer for complete event + */ + hci_ev = (struct ble_hci_ev *) cmd; + rspbuf = hci_ev->data + sizeof(*cmd_complete); + + /* Assume response length is zero */ + rsplen = 0; + + switch (ogf) { + case BLE_HCI_OGF_LINK_CTRL: + rc = ble_ll_hci_link_ctrl_cmd_proc(cmd->data, cmd->length, ocf); + break; + case BLE_HCI_OGF_CTLR_BASEBAND: + rc = ble_ll_hci_ctlr_bb_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen); + break; + case BLE_HCI_OGF_INFO_PARAMS: + rc = ble_ll_hci_info_params_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen); + break; + case BLE_HCI_OGF_STATUS_PARAMS: + rc = ble_ll_hci_status_params_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen); + break; + case BLE_HCI_OGF_LE: + rc = ble_ll_hci_le_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen, &post_cb); + break; + default: + /* XXX: Need to support other OGF. For now, return unsupported */ + rc = BLE_ERR_UNKNOWN_HCI_CMD; + break; + } + + /* If no response is generated, we free the buffers */ + BLE_LL_ASSERT(rc >= 0); + if (rc <= BLE_ERR_MAX) { + /* Create a command complete event with status from command */ + hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_COMPLETE; + hci_ev->length = sizeof(*cmd_complete) + rsplen; + + cmd_complete = (void *) hci_ev->data; + cmd_complete->num_packets = ble_ll_hci_get_num_cmd_pkts(); + cmd_complete->opcode = htole16(opcode); + cmd_complete->status = (uint8_t) rc; + } else { + /* Create a command status event */ + rc -= (BLE_ERR_MAX + 1); + + hci_ev->opcode = BLE_HCI_EVCODE_COMMAND_STATUS; + hci_ev->length = sizeof(*cmd_status); + + cmd_status = (void *) hci_ev->data; + cmd_status->status = (uint8_t)rc; + cmd_status->num_packets = ble_ll_hci_get_num_cmd_pkts(); + cmd_status->opcode = htole16(opcode); + } + + /* Count commands and those in error */ + if (rc) { + STATS_INC(ble_ll_stats, hci_cmd_errs); + } else { + STATS_INC(ble_ll_stats, hci_cmds); + } + + /* Send the event (events cannot be masked) */ + ble_ll_hci_event_send(hci_ev); + + /* Call post callback if set by command handler */ + if (post_cb) { + post_cb(); + } + + BLE_LL_DEBUG_GPIO(HCI_CMD, 0); +} + +/** + * Sends an HCI command to the controller. On success, the supplied buffer is + * relinquished to the controller task. On failure, the caller must free the + * buffer. + * + * @param cmd A flat buffer containing the HCI command to + * send. + * + * @return 0 on success; + * BLE_ERR_MEM_CAPACITY on HCI buffer exhaustion. + */ +int +ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg) +{ + struct ble_npl_event *ev; + + /* Get an event structure off the queue */ + ev = &g_ble_ll_hci_cmd_ev; + if (ble_npl_event_is_queued(ev)) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Fill out the event and post to Link Layer */ + ble_npl_event_set_arg(ev, cmd); + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, ev); + + return 0; +} + +/* Send ACL data from host to contoller */ +int +ble_ll_hci_acl_rx(struct os_mbuf *om, void *arg) +{ + ble_ll_acl_data_in(om); + return 0; +} + +/** + * Initalize the LL HCI. + * + * NOTE: This function is called by the HCI RESET command so if any code + * is added here it must be OK to be executed when the reset command is used. + */ +void +ble_ll_hci_init(void) +{ + BLE_LL_DEBUG_GPIO_INIT(HCI_CMD); + BLE_LL_DEBUG_GPIO_INIT(HCI_EV); + + /* Set event callback for command processing */ + ble_npl_event_init(&g_ble_ll_hci_cmd_ev, ble_ll_hci_cmd_proc, NULL); + + /* Set defaults for LE events: Vol 2 Part E 7.8.1 */ + g_ble_ll_hci_le_event_mask = 0x1f; + + /* Set defaults for controller/baseband events: Vol 2 Part E 7.3.1 */ + g_ble_ll_hci_event_mask = 0x1fffffffffff; + + + /* Set page 2 to 0 */ + g_ble_ll_hci_event_mask2 = 0; + + /* reset RF path compensation values */ + rx_path_pwr_compensation = 0; + tx_path_pwr_compensation = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* after reset both legacy and extended advertising commands are allowed */ + hci_adv_mode = ADV_MODE_ANY; +#endif +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci_ev.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci_ev.c new file mode 100644 index 0000000..b78b919 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_hci_ev.c @@ -0,0 +1,526 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_ctrl.h" +#include "ble_ll_conn_priv.h" + +#if (BLETEST_CONCURRENT_CONN_TEST == 1) +extern void bletest_ltk_req_reply(uint16_t handle); +#endif + +/** + * Send a data length change event for a connection to the host. + * + * @param connsm Pointer to connection state machine + */ +void +ble_ll_hci_ev_datalen_chg(struct ble_ll_conn_sm *connsm) +{ + struct ble_hci_ev_le_subev_data_len_chg *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DATA_LEN_CHG)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_DATA_LEN_CHG; + ev->conn_handle = htole16(connsm->conn_handle); + + ev->max_tx_octets = htole16(connsm->eff_max_tx_octets); + ev->max_tx_time = htole16(connsm->eff_max_tx_time); + ev->max_rx_octets = htole16(connsm->eff_max_rx_octets); + ev->max_rx_time = htole16(connsm->eff_max_rx_time); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +/** + * Send a connection parameter request event for a connection to the host. + * + * @param connsm Pointer to connection state machine + */ +void +ble_ll_hci_ev_rem_conn_parm_req(struct ble_ll_conn_sm *connsm, + struct ble_ll_conn_params *cp) +{ + struct ble_hci_ev_le_subev_rem_conn_param_req *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ; + ev->conn_handle = htole16(connsm->conn_handle); + ev->min_interval = htole16(cp->interval_min); + ev->max_interval = htole16(cp->interval_max); + ev->latency = htole16(cp->latency); + ev->timeout = htole16(cp->timeout); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +/** + * Send a connection update event. + * + * @param connsm Pointer to connection state machine + * @param status The error code. + */ +void +ble_ll_hci_ev_conn_update(struct ble_ll_conn_sm *connsm, uint8_t status) +{ + struct ble_hci_ev_le_subev_conn_upd_complete *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE; + ev->status = status; + ev->conn_handle = htole16(connsm->conn_handle); + ev->conn_itvl = htole16(connsm->conn_itvl); + ev->conn_latency = htole16(connsm->slave_latency); + ev->supervision_timeout = htole16(connsm->supervision_tmo); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +void +ble_ll_hci_ev_encrypt_chg(struct ble_ll_conn_sm *connsm, uint8_t status) +{ + struct ble_hci_ev_enc_key_refresh *ev_key_refresh; + struct ble_hci_ev_enrypt_chg *ev_enc_chf; + struct ble_hci_ev *hci_ev; + + if (CONN_F_ENC_CHANGE_SENT(connsm) == 0) { + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_ENCRYPT_CHG)) { + hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_ENCRYPT_CHG; + hci_ev->length = sizeof(*ev_enc_chf); + ev_enc_chf = (void *) hci_ev->data; + + ev_enc_chf->status = status; + ev_enc_chf->connection_handle = htole16(connsm->conn_handle); + ev_enc_chf->enabled = (status == BLE_ERR_SUCCESS) ? 0x01 : 0x00; + + ble_ll_hci_event_send(hci_ev); + } + } + + CONN_F_ENC_CHANGE_SENT(connsm) = 1; + return; + } + + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_ENC_KEY_REFRESH)) { + hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_ENC_KEY_REFRESH; + hci_ev->length = sizeof(*ev_key_refresh); + ev_key_refresh = (void *) hci_ev->data; + + ev_key_refresh->status = status; + ev_key_refresh->conn_handle = htole16(connsm->conn_handle); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +/** + * Send a long term key request event for a connection to the host. + * + * @param connsm Pointer to connection state machine + */ +int +ble_ll_hci_ev_ltk_req(struct ble_ll_conn_sm *connsm) +{ + struct ble_hci_ev_le_subev_lt_key_req *ev; + struct ble_hci_ev *hci_ev; + int rc; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_LT_KEY_REQ)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_LT_KEY_REQ; + ev->conn_handle = htole16(connsm->conn_handle); + ev->rand = htole64(connsm->enc_data.host_rand_num); + ev->div = htole16(connsm->enc_data.enc_div); + + ble_ll_hci_event_send(hci_ev); + } + rc = 0; + } else { + rc = -1; + } + +#if (BLETEST_CONCURRENT_CONN_TEST == 1) + if (rc == 0) { + bletest_ltk_req_reply(connsm->conn_handle); + } +#endif + return rc; +} +#endif + +void +ble_ll_hci_ev_rd_rem_used_feat(struct ble_ll_conn_sm *connsm, uint8_t status) +{ + struct ble_hci_ev_le_subev_rd_rem_used_feat *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT; + ev->status = status; + ev->conn_handle = htole16(connsm->conn_handle); + ev->features[0] = connsm->conn_features; + memcpy(ev->features + 1, connsm->remote_features, 7); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +void +ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, uint8_t status) +{ + struct ble_hci_ev_rd_rem_ver_info_cmp *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->status = status; + ev->conn_handle = htole16(connsm->conn_handle); + ev->version = connsm->vers_nr; + ev->manufacturer = htole16(connsm->comp_id); + ev->subversion = htole16(connsm->sub_vers_nr); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +/** + * Send a HW error to the host. + * + * @param hw_err + * + * @return int 0: event masked or event sent, -1 otherwise + */ +int +ble_ll_hci_ev_hw_err(uint8_t hw_err) +{ + struct ble_hci_ev_hw_error *ev; + struct ble_hci_ev *hci_ev; + int rc; + + rc = 0; + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_HW_ERROR)) { + hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_HW_ERROR; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->hw_code = hw_err; + + ble_ll_hci_event_send(hci_ev); + } else { + rc = -1; + } + } + return rc; +} + +void +ble_ll_hci_ev_databuf_overflow(void) +{ + struct ble_hci_ev_data_buf_overflow *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_DATA_BUF_OVERFLOW)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_DATA_BUF_OVERFLOW; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->link_type = BLE_HCI_EVENT_ACL_BUF_OVERFLOW; + + ble_ll_hci_event_send(hci_ev); + } + } +} + +/** + * Send a LE Channel Selection Algorithm event. + * + * @param connsm Pointer to connection state machine + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) +void +ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm) +{ + struct ble_hci_ev_le_subev_chan_sel_alg *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CHAN_SEL_ALG)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_CHAN_SEL_ALG; + ev->conn_handle = htole16(connsm->conn_handle); + ev->csa = connsm->csmflags.cfbit.csa2_supp ? 0x01 : 0x00; + + ble_ll_hci_event_send(hci_ev); + } + } +} +#endif + +/** + * Sends the LE Scan Request Received event + * + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +void +ble_ll_hci_ev_send_scan_req_recv(uint8_t adv_handle, const uint8_t *peer, + uint8_t peer_addr_type) +{ + struct ble_hci_ev_le_subev_scan_req_rcvd *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD; + ev->adv_handle = adv_handle; + ev->peer_addr_type = peer_addr_type; + memcpy(ev->peer_addr, peer, BLE_DEV_ADDR_LEN); + + ble_ll_hci_event_send(hci_ev); + } + } +} +#endif + +/** + * Sends the LE Scan Timeout Event + * + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +void +ble_ll_hci_ev_send_scan_timeout(void) +{ + struct ble_hci_ev_le_subev_scan_timeout *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_TIMEOUT)) { + hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT; + + ble_ll_hci_event_send(hci_ev); + } + } +} +#endif + +/** + * Sends the LE Advertising Set Terminated event + * + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +void +ble_ll_hci_ev_send_adv_set_terminated(uint8_t status, uint8_t adv_handle, + uint16_t conn_handle, uint8_t events) +{ + struct ble_hci_ev_le_subev_adv_set_terminated *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED; + ev->status = status; + ev->adv_handle = adv_handle; + ev->conn_handle = htole16(conn_handle); + ev->num_events = events; + + ble_ll_hci_event_send(hci_ev); + } + } +} +#endif + +/** + * Send a PHY update complete event + * + * @param connsm Pointer to connection state machine + * @param status error status of event + */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +int +ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status) +{ + struct ble_hci_ev_le_subev_phy_update_complete *ev; + struct ble_hci_ev *hci_ev; + int rc; + + rc = 0; + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE; + ev->status = status; + ev->conn_handle = htole16(connsm->conn_handle); + ev->tx_phy = connsm->phy_data.cur_tx_phy; + ev->rx_phy = connsm->phy_data.cur_rx_phy; + + ble_ll_hci_event_send(hci_ev); + } else { + rc = BLE_ERR_MEM_CAPACITY; + } + } + return rc; +} +#endif + +void +ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line) +{ + struct ble_hci_ev_vendor_debug *ev; + struct ble_hci_ev *hci_ev; + unsigned int str_len; + bool skip = true; + uint8_t digit; + int max_len; + int i; + + /* 6 is for line number ":00000" , we assume files have no more than 64k of + * lines + */ + max_len = BLE_HCI_MAX_DATA_LEN - sizeof(*ev) - 6; + + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_VENDOR_DEBUG; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + /* Debug id for future use */ + ev->id = 0x00; + + /* snprintf would be nicer but this is heavy on flash + * len = snprintf((char *) ev->data, max_len, "%s:%u", file, line); + * if (len < 0) { + * len = 0; + * } else if (len > max_len) { + * len = max_len; + * } + * + * hci_ev->length += len; + */ + str_len = strlen(file); + if (str_len > max_len) { + str_len = max_len; + } + + memcpy(ev->data, file, str_len); + ev->data[str_len++] = ':'; + + for (i = 100000; i >= 10; i /= 10) { + digit = (line % i) / (i/10); + + if (!digit && skip) { + continue; + } + + skip = false; + ev->data[str_len++] = '0' + digit; + } + + hci_ev->length += str_len; + + ble_ll_hci_event_send(hci_ev); + } +} + +#endif \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_priv.h b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_priv.h new file mode 100644 index 0000000..900950e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_priv.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_LL_PRIV_ +#define H_BLE_LL_PRIV_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MYNEWT + +#include "syscfg/syscfg.h" +#include "hal/hal_gpio.h" + +#define BLE_LL_DEBUG_GPIO_INIT(_name) \ + if (MYNEWT_VAL(BLE_LL_DEBUG_GPIO_ ## _name) >= 0) { \ + hal_gpio_init_out(MYNEWT_VAL(BLE_LL_DEBUG_GPIO_ ## _name), 0); \ + } + +#define BLE_LL_DEBUG_GPIO(_name, _val) \ + if (MYNEWT_VAL(BLE_LL_DEBUG_GPIO_ ## _name) >= 0) { \ + hal_gpio_write(MYNEWT_VAL(BLE_LL_DEBUG_GPIO_ ## _name), !!(_val)); \ + } + +#else +#define BLE_LL_DEBUG_GPIO_INIT(_name) (void)(0) +#define BLE_LL_DEBUG_GPIO(_name, _val) (void)(0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_LL_PRIV_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rand.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rand.c new file mode 100644 index 0000000..dd89e04 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rand.c @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll.h" +#if MYNEWT_VAL(TRNG) +#include "trng/trng.h" +#endif + +#if MYNEWT_VAL(TRNG) +static struct trng_dev *g_trng; +#else +/* This is a simple circular buffer for holding N samples of random data */ +struct ble_ll_rnum_data +{ + uint8_t *rnd_in; + uint8_t *rnd_out; + volatile uint8_t rnd_size; +}; + +struct ble_ll_rnum_data g_ble_ll_rnum_data; +uint8_t g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)]; + +#define IS_RNUM_BUF_END(x) \ + (x == &g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE) - 1]) + +void +ble_ll_rand_sample(uint8_t rnum) +{ + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) { + ++g_ble_ll_rnum_data.rnd_size; + g_ble_ll_rnum_data.rnd_in[0] = rnum; + if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_in)) { + g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf; + } else { + ++g_ble_ll_rnum_data.rnd_in; + } + } else { + /* Stop generating random numbers as we are full */ + ble_hw_rng_stop(); + } + OS_EXIT_CRITICAL(sr); +} +#endif + +/* Get 'len' bytes of random data */ +int +ble_ll_rand_data_get(uint8_t *buf, uint8_t len) +{ +#if MYNEWT_VAL(TRNG) + size_t num; + + while (len) { + num = trng_read(g_trng, buf, len); + buf += num; + len -= num; + } +#else + uint8_t rnums; + os_sr_t sr; + + while (len != 0) { + OS_ENTER_CRITICAL(sr); + rnums = g_ble_ll_rnum_data.rnd_size; + if (rnums > len) { + rnums = len; + } + len -= rnums; + g_ble_ll_rnum_data.rnd_size -= rnums; + while (rnums) { + buf[0] = g_ble_ll_rnum_data.rnd_out[0]; + if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_out)) { + g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf; + } else { + ++g_ble_ll_rnum_data.rnd_out; + } + ++buf; + --rnums; + } + OS_EXIT_CRITICAL(sr); + + /* Make sure rng is started! */ + ble_hw_rng_start(); + + /* Wait till bytes are in buffer. */ + if (len) { + while ((g_ble_ll_rnum_data.rnd_size < len) && + (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE))) { + /* Spin here */ + } + } + } +#endif + return BLE_ERR_SUCCESS; +} + +/** + * Called to obtain a "prand" as defined in core V4.2 Vol 6 Part B 1.3.2.2 + * + * @param prand + */ +void +ble_ll_rand_prand_get(uint8_t *prand) +{ + uint16_t sum; + + while (1) { + /* Get 24 bits of random data */ + ble_ll_rand_data_get(prand, 3); + + /* Prand cannot be all zeros or 1's. */ + sum = prand[0] + prand[1] + prand[2]; + if ((sum != 0) && (sum != (3 * 0xff))) { + break; + } + } + + /* Upper two bits must be 01 */ + prand[2] &= ~0xc0; + prand[2] |= 0x40; +} + +/** + * Start the generation of random numbers + * + * @return int + */ +int +ble_ll_rand_start(void) +{ +#if MYNEWT_VAL(TRNG) + /* Nothing to do - this is handled by driver */ +#else + /* Start the generation of numbers if we are not full */ + if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) { + ble_hw_rng_start(); + } +#endif + return 0; +} + +/** + * Initialize LL random number generation. Should be called only once on + * initialization. + * + * @return int + */ +int +ble_ll_rand_init(void) +{ +#if MYNEWT_VAL(TRNG) + g_trng = (struct trng_dev *) os_dev_open("trng", OS_TIMEOUT_NEVER, NULL); +#else + g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf; + g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf; + ble_hw_rng_init(ble_ll_rand_sample, 1); +#endif + return 0; +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_resolv.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_resolv.c new file mode 100644 index 0000000..231ecad --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_resolv.c @@ -0,0 +1,755 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_sync.h" +#include "../include/controller/ble_hw.h" +#include "ble_ll_conn_priv.h" + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +struct ble_ll_resolv_data +{ + uint8_t addr_res_enabled; + uint8_t rl_size; + uint8_t rl_cnt_hw; + uint8_t rl_cnt; + ble_npl_time_t rpa_tmo; + struct ble_npl_callout rpa_timer; +}; +struct ble_ll_resolv_data g_ble_ll_resolv_data; + +__attribute__((aligned(4))) +struct ble_ll_resolv_entry g_ble_ll_resolv_list[MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)]; + +static int +ble_ll_is_controller_busy(void) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + if (ble_ll_sync_enabled()) { + return 1; + } +#endif + + return ble_ll_adv_enabled() || ble_ll_scan_enabled() || + g_ble_ll_conn_create_sm; +} +/** + * Called to determine if a change is allowed to the resolving list at this + * time. We are not allowed to modify the resolving list if address translation + * is enabled and we are either scanning, advertising, or attempting to create + * a connection. + * + * @return int 0: not allowed. 1: allowed. + */ +static int +ble_ll_resolv_list_chg_allowed(void) +{ + int rc; + + if (g_ble_ll_resolv_data.addr_res_enabled && + ble_ll_is_controller_busy()) { + rc = 0; + } else { + rc = 1; + } + return rc; +} + + +/** + * Called to generate a resolvable private address in rl structure + * + * @param rl + * @param local + */ +static void +ble_ll_resolv_gen_priv_addr(struct ble_ll_resolv_entry *rl, int local) +{ + uint8_t *irk; + uint8_t *prand; + struct ble_encryption_block ecb; + uint8_t *addr; + + BLE_LL_ASSERT(rl != NULL); + + if (local) { + addr = rl->rl_local_rpa; + irk = rl->rl_local_irk; + } else { + addr = rl->rl_peer_rpa; + irk = rl->rl_peer_irk; + } + + /* Get prand */ + prand = addr + 3; + ble_ll_rand_prand_get(prand); + + /* Calculate hash, hash = ah(local IRK, prand) */ + memcpy(ecb.key, irk, 16); + memset(ecb.plain_text, 0, 13); + ecb.plain_text[13] = prand[2]; + ecb.plain_text[14] = prand[1]; + ecb.plain_text[15] = prand[0]; + + /* Calculate hash */ + ble_hw_encrypt_block(&ecb); + + addr[0] = ecb.cipher_text[15]; + addr[1] = ecb.cipher_text[14]; + addr[2] = ecb.cipher_text[13]; +} + +/** + * Called when the Resolvable private address timer expires. This timer + * is used to regenerate local and peers RPA's in the resolving list. + */ +static void +ble_ll_resolv_rpa_timer_cb(struct ble_npl_event *ev) +{ + int i; + os_sr_t sr; + struct ble_ll_resolv_entry *rl; + + rl = &g_ble_ll_resolv_list[0]; + for (i = 0; i < g_ble_ll_resolv_data.rl_cnt; ++i) { + if (rl->rl_has_local) { + OS_ENTER_CRITICAL(sr); + ble_ll_resolv_gen_priv_addr(rl, 1); + OS_EXIT_CRITICAL(sr); + } + + if (rl->rl_has_peer) { + OS_ENTER_CRITICAL(sr); + ble_ll_resolv_gen_priv_addr(rl, 0); + OS_EXIT_CRITICAL(sr); + } + ++rl; + } + + ble_npl_callout_reset(&g_ble_ll_resolv_data.rpa_timer, + g_ble_ll_resolv_data.rpa_tmo); + + ble_ll_adv_rpa_timeout(); +} + +/** + * Called to determine if the IRK is all zero. + * + * @param irk + * + * @return int 0: IRK is zero . 1: IRK has non-zero value. + */ +static int +ble_ll_resolv_irk_nonzero(const uint8_t *irk) +{ + int i; + int rc; + + rc = 0; + for (i = 0; i < 16; ++i) { + if (*irk != 0) { + rc = 1; + break; + } + ++irk; + } + + return rc; +} + +/** + * Clear the resolving list + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_ll_resolv_list_clr(void) +{ + /* Check proper state */ + if (!ble_ll_resolv_list_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Sets total on list to 0. Clears HW resolve list */ + g_ble_ll_resolv_data.rl_cnt_hw = 0; + g_ble_ll_resolv_data.rl_cnt = 0; + ble_hw_resolv_list_clear(); + + /* stop RPA timer when clearing RL */ + ble_npl_callout_stop(&g_ble_ll_resolv_data.rpa_timer); + + return BLE_ERR_SUCCESS; +} + +/** + * Read the size of the resolving list. This is the total number of resolving + * list entries allowed by the controller. + * + * @param rspbuf Pointer to response buffer + * + * @return int 0: success. + */ +int +ble_ll_resolv_list_read_size(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_resolv_list_size_rp *rsp = (void *) rspbuf; + + rsp->size = g_ble_ll_resolv_data.rl_size; + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +/** + * Used to determine if the device is on the resolving list. + * + * @param addr + * @param addr_type Public address (0) or random address (1) + * + * @return int 0: device is not on resolving list; otherwise the return value + * is the 'position' of the device in the resolving list (the index of the + * element plus 1). + */ +static int +ble_ll_is_on_resolv_list(const uint8_t *addr, uint8_t addr_type) +{ + int i; + struct ble_ll_resolv_entry *rl; + + rl = &g_ble_ll_resolv_list[0]; + for (i = 0; i < g_ble_ll_resolv_data.rl_cnt; ++i) { + if ((rl->rl_addr_type == addr_type) && + (!memcmp(&rl->rl_identity_addr[0], addr, BLE_DEV_ADDR_LEN))) { + return i + 1; + } + ++rl; + } + + return 0; +} + +/** + * Used to determine if the device is on the resolving list. + * + * @param addr + * @param addr_type Public address (0) or random address (1) + * + * @return Pointer to resolving list entry or NULL if no entry found. + */ +struct ble_ll_resolv_entry * +ble_ll_resolv_list_find(const uint8_t *addr, uint8_t addr_type) +{ + int i; + struct ble_ll_resolv_entry *rl; + + rl = &g_ble_ll_resolv_list[0]; + for (i = 0; i < g_ble_ll_resolv_data.rl_cnt; ++i) { + if ((rl->rl_addr_type == addr_type) && + (!memcmp(&rl->rl_identity_addr[0], addr, BLE_DEV_ADDR_LEN))) { + return rl; + } + ++rl; + } + + return NULL; +} + +/** + * Add a device to the resolving list + * + * @return int + */ +int +ble_ll_resolv_list_add(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_add_resolv_list_cp *cmd = (const void *) cmdbuf; + struct ble_ll_resolv_entry *rl; + int rc = BLE_ERR_SUCCESS; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Must be in proper state */ + if (!ble_ll_resolv_list_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Check if we have any open entries */ + if (g_ble_ll_resolv_data.rl_cnt >= g_ble_ll_resolv_data.rl_size) { + return BLE_ERR_MEM_CAPACITY; + } + + /* spec is not clear on how to handle this but make sure host is aware + * that new keys are not used in that case + */ + if (ble_ll_is_on_resolv_list(cmd->peer_id_addr, cmd->peer_addr_type)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* we keep this sorted in a way that entries with peer_irk are first */ + if (ble_ll_resolv_irk_nonzero(cmd->peer_irk)) { + memmove(&g_ble_ll_resolv_list[g_ble_ll_resolv_data.rl_cnt_hw + 1], + &g_ble_ll_resolv_list[g_ble_ll_resolv_data.rl_cnt_hw], + (g_ble_ll_resolv_data.rl_cnt - g_ble_ll_resolv_data.rl_cnt_hw) * + sizeof(g_ble_ll_resolv_list[0])); + rl = &g_ble_ll_resolv_list[g_ble_ll_resolv_data.rl_cnt_hw]; + } else { + rl = &g_ble_ll_resolv_list[g_ble_ll_resolv_data.rl_cnt]; + } + + memset (rl, 0, sizeof(*rl)); + rl->rl_addr_type = cmd->peer_addr_type; + memcpy(rl->rl_identity_addr, cmd->peer_id_addr, BLE_DEV_ADDR_LEN); + + if (ble_ll_resolv_irk_nonzero(cmd->peer_irk)) { + swap_buf(rl->rl_peer_irk, cmd->peer_irk, 16); + rl->rl_has_peer = 1; + + /* generate peer RPA now, those will be updated by timer when + * resolution is enabled + */ + ble_ll_resolv_gen_priv_addr(rl, 0); + } + + if (ble_ll_resolv_irk_nonzero(cmd->local_irk)) { + swap_buf(rl->rl_local_irk, cmd->local_irk, 16); + rl->rl_has_local = 1; + + /* generate local RPA now, those will be updated by timer when + * resolution is enabled + */ + ble_ll_resolv_gen_priv_addr(rl, 1); + } + + /* By default use privacy network mode */ + rl->rl_priv_mode = BLE_HCI_PRIVACY_NETWORK; + + /* Add peers IRKs to HW resolving list. Should always succeed since we + * already checked if there is room for it. + */ + if (rl->rl_has_peer) { + rc = ble_hw_resolv_list_add(rl->rl_peer_irk); + BLE_LL_ASSERT(rc == BLE_ERR_SUCCESS); + g_ble_ll_resolv_data.rl_cnt_hw++; + } + + g_ble_ll_resolv_data.rl_cnt++; + + /* start RPA timer if this was first element added to RL */ + if (g_ble_ll_resolv_data.rl_cnt == 1) { + ble_npl_callout_reset(&g_ble_ll_resolv_data.rpa_timer, + g_ble_ll_resolv_data.rpa_tmo); + } + + return rc; +} + +/** + * Remove a device from the resolving list + * + * @param cmdbuf + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_ll_resolv_list_rmv(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rmv_resolve_list_cp *cmd = (const void *) cmdbuf; + int position; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Must be in proper state */ + if (!ble_ll_resolv_list_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Remove from IRK records */ + position = ble_ll_is_on_resolv_list(cmd->peer_id_addr, cmd->peer_addr_type); + if (position) { + BLE_LL_ASSERT(position <= g_ble_ll_resolv_data.rl_cnt); + + memmove(&g_ble_ll_resolv_list[position - 1], + &g_ble_ll_resolv_list[position], + (g_ble_ll_resolv_data.rl_cnt - position) * + sizeof(g_ble_ll_resolv_list[0])); + g_ble_ll_resolv_data.rl_cnt--; + + /* Remove from HW list */ + if (position <= g_ble_ll_resolv_data.rl_cnt_hw) { + ble_hw_resolv_list_rmv(position - 1); + g_ble_ll_resolv_data.rl_cnt_hw--; + } + + /* stop RPA timer if list is empty */ + if (g_ble_ll_resolv_data.rl_cnt == 0) { + ble_npl_callout_stop(&g_ble_ll_resolv_data.rpa_timer); + } + + return BLE_ERR_SUCCESS; + } + + return BLE_ERR_UNK_CONN_ID; +} + +/** + * Called to enable or disable address resolution in the controller + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_resolv_enable_cmd(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_addr_res_en_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (ble_ll_is_controller_busy()) { + return BLE_ERR_CMD_DISALLOWED; + + } + + if (cmd->enable > 1) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + g_ble_ll_resolv_data.addr_res_enabled = cmd->enable; + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_resolv_peer_addr_rd(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rd_peer_recolv_addr_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rd_peer_recolv_addr_rp *rsp = (void *) rspbuf; + struct ble_ll_resolv_entry *rl; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rl = ble_ll_resolv_list_find(cmd->peer_id_addr, cmd->peer_addr_type); + if (rl) { + memcpy(rsp->rpa, rl->rl_peer_rpa, BLE_DEV_ADDR_LEN); + rc = BLE_ERR_SUCCESS; + } else { + memset(rsp->rpa, 0, BLE_DEV_ADDR_LEN); + rc = BLE_ERR_UNK_CONN_ID; + } + + *rsplen = sizeof(*rsp); + return rc; +} + +int +ble_ll_resolv_local_addr_rd(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_rd_local_recolv_addr_cp *cmd = (const void *) cmdbuf; + struct ble_hci_le_rd_local_recolv_addr_rp *rsp = (void *) rspbuf; + struct ble_ll_resolv_entry *rl; + int rc; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rl = ble_ll_resolv_list_find(cmd->peer_id_addr, cmd->peer_addr_type); + if (rl) { + memcpy(rsp->rpa, rl->rl_local_rpa, BLE_DEV_ADDR_LEN); + rc = BLE_ERR_SUCCESS; + } else { + memset(rsp->rpa, 0, BLE_DEV_ADDR_LEN); + rc = BLE_ERR_UNK_CONN_ID; + } + + *rsplen = sizeof(*rsp); + return rc; +} + +/** + * Set the resolvable private address timeout. + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_resolv_set_rpa_tmo(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_rpa_tmo_cp *cmd = (const void *)cmdbuf; + uint16_t tmo_secs; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + tmo_secs = le16toh(cmd->rpa_timeout); + if (!((tmo_secs > 0) && (tmo_secs <= 0xA1B8))) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + g_ble_ll_resolv_data.rpa_tmo = ble_npl_time_ms_to_ticks32(tmo_secs * 1000); + + /* restart timer if there is something on RL */ + if (g_ble_ll_resolv_data.rl_cnt) { + ble_npl_callout_reset(&g_ble_ll_resolv_data.rpa_timer, + g_ble_ll_resolv_data.rpa_tmo); + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_resolve_set_priv_mode(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_privacy_mode_cp *cmd = (const void *) cmdbuf; + struct ble_ll_resolv_entry *rl; + + if (ble_ll_is_controller_busy()) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rl = ble_ll_resolv_list_find(cmd->peer_id_addr, cmd->peer_id_addr_type); + if (!rl) { + return BLE_ERR_UNK_CONN_ID; + } + + if (cmd->mode > BLE_HCI_PRIVACY_DEVICE) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + rl->rl_priv_mode = cmd->mode; + + return BLE_ERR_SUCCESS; +} + +/** + * Returns the Resolvable Private address timeout, in os ticks + * + * + * @return uint32_t + */ +uint32_t +ble_ll_resolv_get_rpa_tmo(void) +{ + return g_ble_ll_resolv_data.rpa_tmo; +} + +void +ble_ll_resolv_get_priv_addr(struct ble_ll_resolv_entry *rl, int local, + uint8_t *addr) +{ + os_sr_t sr; + + BLE_LL_ASSERT(rl != NULL); + BLE_LL_ASSERT(addr != NULL); + + OS_ENTER_CRITICAL(sr); + if (local) { + BLE_LL_ASSERT(rl->rl_has_local); + memcpy(addr, rl->rl_local_rpa, BLE_DEV_ADDR_LEN); + } else { + BLE_LL_ASSERT(rl->rl_has_peer); + memcpy(addr, rl->rl_peer_rpa, BLE_DEV_ADDR_LEN); + } + + OS_EXIT_CRITICAL(sr); +} + +void +ble_ll_resolv_set_peer_rpa(int index, uint8_t *rpa) +{ + os_sr_t sr; + struct ble_ll_resolv_entry *rl; + + OS_ENTER_CRITICAL(sr); + rl = &g_ble_ll_resolv_list[index]; + memcpy(rl->rl_peer_rpa, rpa, BLE_DEV_ADDR_LEN); + OS_EXIT_CRITICAL(sr); +} + +void +ble_ll_resolv_set_local_rpa(int index, uint8_t *rpa) +{ + os_sr_t sr; + struct ble_ll_resolv_entry *rl; + + OS_ENTER_CRITICAL(sr); + rl = &g_ble_ll_resolv_list[index]; + memcpy(rl->rl_local_rpa, rpa, BLE_DEV_ADDR_LEN); + OS_EXIT_CRITICAL(sr); +} + +/** + * Generate a resolvable private address. + * + * @param addr + * @param addr_type + * @param rpa + * + * @return int + */ +int +ble_ll_resolv_gen_rpa(uint8_t *addr, uint8_t addr_type, uint8_t *rpa, int local) +{ + struct ble_ll_resolv_entry *rl; + + rl = ble_ll_resolv_list_find(addr, addr_type); + if (rl) { + if ((local && rl->rl_has_local) || (!local && rl->rl_has_peer)) { + ble_ll_resolv_get_priv_addr(rl, local, rpa); + return 1; + } + } + + return 0; +} + +/** + * Resolve a Resolvable Private Address + * + * @param rpa + * @param index + * + * @return int + */ +int +ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk) +{ + int rc; + const uint32_t *irk32; + uint32_t *key32; + uint32_t *pt32; + struct ble_encryption_block ecb; + + irk32 = (const uint32_t *)irk; + key32 = (uint32_t *)&ecb.key[0]; + + key32[0] = irk32[0]; + key32[1] = irk32[1]; + key32[2] = irk32[2]; + key32[3] = irk32[3]; + + pt32 = (uint32_t *)&ecb.plain_text[0]; + pt32[0] = 0; + pt32[1] = 0; + pt32[2] = 0; + pt32[3] = 0; + + ecb.plain_text[15] = rpa[3]; + ecb.plain_text[14] = rpa[4]; + ecb.plain_text[13] = rpa[5]; + + ble_hw_encrypt_block(&ecb); + if ((ecb.cipher_text[15] == rpa[0]) && (ecb.cipher_text[14] == rpa[1]) && + (ecb.cipher_text[13] == rpa[2])) { + rc = 1; + } else { + rc = 0; + } + + return rc; +} + +int +ble_ll_resolv_peer_rpa_any(const uint8_t *rpa) +{ + int i; + + for (i = 0; i < g_ble_ll_resolv_data.rl_cnt_hw; i++) { + if (ble_ll_resolv_rpa(rpa, g_ble_ll_resolv_list[i].rl_peer_irk)) { + return i; + } + } + + return -1; +} + +/** + * Returns whether or not address resolution is enabled. + * + * @return uint8_t + */ +uint8_t +ble_ll_resolv_enabled(void) +{ + return g_ble_ll_resolv_data.addr_res_enabled; +} + +/** + * Called to reset private address resolution module. + */ +void +ble_ll_resolv_list_reset(void) +{ + g_ble_ll_resolv_data.addr_res_enabled = 0; + ble_npl_callout_stop(&g_ble_ll_resolv_data.rpa_timer); + ble_ll_resolv_list_clr(); + ble_ll_resolv_init(); +} + +void +ble_ll_resolv_init(void) +{ + uint8_t hw_size; + + /* Default is 15 minutes */ + g_ble_ll_resolv_data.rpa_tmo = ble_npl_time_ms_to_ticks32(15 * 60 * 1000); + + hw_size = ble_hw_resolv_list_size(); + if (hw_size > MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) { + hw_size = MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE); + } + g_ble_ll_resolv_data.rl_size = hw_size; + + ble_npl_callout_init(&g_ble_ll_resolv_data.rpa_timer, + &g_ble_ll_data.ll_evq, + ble_ll_resolv_rpa_timer_cb, + NULL); +} + +#endif /* if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rfmgmt.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rfmgmt.c new file mode 100644 index 0000000..40d79be --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_rfmgmt.c @@ -0,0 +1,349 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_rfmgmt.h" + +#if MYNEWT_VAL(BLE_LL_RFMGMT_ENABLE_TIME) > 0 + +enum ble_ll_rfmgmt_state { + RFMGMT_STATE_OFF = 0, + RFMGMT_STATE_ENABLING = 1, + RFMGMT_STATE_ENABLED = 2, +}; + +struct ble_ll_rfmgmt_data { + enum ble_ll_rfmgmt_state state; + uint16_t ticks_to_enabled; + + struct hal_timer timer; + bool timer_scheduled; + uint32_t timer_scheduled_at; + + bool enable_scan; + bool enable_sched; + uint32_t enable_scan_at; + uint32_t enable_sched_at; + + uint32_t enabled_at; + + struct ble_npl_event release_ev; +}; + +static struct ble_ll_rfmgmt_data g_ble_ll_rfmgmt_data; + +static void +ble_ll_rfmgmt_enable(void) +{ + OS_ASSERT_CRITICAL(); + + if (g_ble_ll_rfmgmt_data.state == RFMGMT_STATE_OFF) { + g_ble_ll_rfmgmt_data.state = RFMGMT_STATE_ENABLING; + g_ble_ll_rfmgmt_data.enabled_at = os_cputime_get32(); + ble_phy_rfclk_enable(); + } +} + +static void +ble_ll_rfmgmt_disable(void) +{ + OS_ASSERT_CRITICAL(); + + if (g_ble_ll_rfmgmt_data.state != RFMGMT_STATE_OFF) { + ble_phy_rfclk_disable(); + g_ble_ll_rfmgmt_data.state = RFMGMT_STATE_OFF; + } +} + +static void +ble_ll_rfmgmt_timer_reschedule(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + uint32_t enable_at; + + /* Figure out when we need to enable RF */ + if (rfmgmt->enable_scan && rfmgmt->enable_sched) { + if (CPUTIME_LT(rfmgmt->enable_scan_at, rfmgmt->enable_sched_at)) { + enable_at = rfmgmt->enable_scan_at; + } else { + enable_at = rfmgmt->enable_sched_at; + } + } else if (rfmgmt->enable_scan) { + enable_at = rfmgmt->enable_scan_at; + } else if (rfmgmt->enable_sched) { + enable_at = rfmgmt->enable_sched_at; + } else { + rfmgmt->timer_scheduled = false; + os_cputime_timer_stop(&rfmgmt->timer); + return; + } + + if (rfmgmt->timer_scheduled) { + /* + * If there is timer already scheduled at the same time we do not need + * to do anything. Otherwise we need to stop timer and schedule it again + * regardless if it's earlier or later to make sure it fires at the time + * something expects it. + */ + + if (rfmgmt->timer_scheduled_at == enable_at) { + return; + } + + rfmgmt->timer_scheduled = false; + os_cputime_timer_stop(&rfmgmt->timer); + } + + /* + * In case timer was requested to be enabled before current time, just make + * sure it's enabled and assume caller can deal with this. This will happen + * if something is scheduled "now" since "enable_at" is in the past, but in + * such case it's absolutely harmless since we already have clock enabled + * and this will do nothing. + */ + if (CPUTIME_LEQ(enable_at, os_cputime_get32())) { + ble_ll_rfmgmt_enable(); + return; + } + + rfmgmt->timer_scheduled = true; + rfmgmt->timer_scheduled_at = enable_at; + os_cputime_timer_start(&rfmgmt->timer, enable_at); +} + +static void +ble_ll_rfmgmt_timer_exp(void *arg) +{ + g_ble_ll_rfmgmt_data.timer_scheduled = false; + ble_ll_rfmgmt_enable(); +} + +static void +ble_ll_rfmgmt_release_ev(struct ble_npl_event *ev) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + uint32_t now; + bool can_disable; + uint8_t lls; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + now = os_cputime_get32(); + + can_disable = true; + lls = ble_ll_state_get(); + + if (rfmgmt->enable_scan && CPUTIME_GEQ(now, rfmgmt->enable_scan_at)) { + /* Blocked by scan */ + can_disable = false; + } else if (rfmgmt->enable_sched && CPUTIME_GEQ(now, rfmgmt->enable_sched_at)) { + /* Blocked by scheduler item */ + can_disable = false; + } else if (lls != BLE_LL_STATE_STANDBY) { + /* Blocked by LL state */ + can_disable = false; + } + + if (can_disable) { + ble_ll_rfmgmt_disable(); + } + + OS_EXIT_CRITICAL(sr); +} + +static uint32_t +ble_ll_rfmgmt_ticks_to_enabled(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + uint32_t rem_ticks; + uint32_t now; + + switch (rfmgmt->state) { + case RFMGMT_STATE_OFF: + rem_ticks = rfmgmt->ticks_to_enabled; + break; + case RFMGMT_STATE_ENABLING: + now = os_cputime_get32(); + if (CPUTIME_LT(now, rfmgmt->enabled_at + rfmgmt->ticks_to_enabled)) { + rem_ticks = rfmgmt->enabled_at + rfmgmt->ticks_to_enabled - now; + break; + } + rfmgmt->state = RFMGMT_STATE_ENABLED; + /* Else falls through. */ + /* no break */ + case RFMGMT_STATE_ENABLED: + rem_ticks = 0; + break; + default: + BLE_LL_ASSERT(0); + rem_ticks = 0; + break; + } + + return rem_ticks; +} + +void +ble_ll_rfmgmt_init(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + + rfmgmt->state = RFMGMT_STATE_OFF; + + rfmgmt->ticks_to_enabled = + ble_ll_usecs_to_ticks_round_up(MYNEWT_VAL(BLE_LL_RFMGMT_ENABLE_TIME)); + + rfmgmt->timer_scheduled = false; + os_cputime_timer_init(&rfmgmt->timer, ble_ll_rfmgmt_timer_exp, NULL); + + ble_npl_event_init(&rfmgmt->release_ev, ble_ll_rfmgmt_release_ev, NULL); +} + +void +ble_ll_rfmgmt_reset(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + + rfmgmt->timer_scheduled = false; + rfmgmt->timer_scheduled_at = 0; + os_cputime_timer_stop(&rfmgmt->timer); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &rfmgmt->release_ev); + + ble_ll_rfmgmt_disable(); + + rfmgmt->enable_scan = false; + rfmgmt->enable_scan_at = 0; + rfmgmt->enable_sched = false; + rfmgmt->enable_sched_at = 0; + + rfmgmt->enabled_at = 0; +} + +void +ble_ll_rfmgmt_scan_changed(bool enabled, uint32_t next_window) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + rfmgmt->enable_scan = enabled; + rfmgmt->enable_scan_at = next_window - rfmgmt->ticks_to_enabled; + + ble_ll_rfmgmt_timer_reschedule(); + + OS_EXIT_CRITICAL(sr); +} + +void +ble_ll_rfmgmt_sched_changed(struct ble_ll_sched_item *first) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + rfmgmt->enable_sched = (first != NULL); + if (first) { + rfmgmt->enable_sched_at = first->start_time - rfmgmt->ticks_to_enabled; + } + + ble_ll_rfmgmt_timer_reschedule(); + + OS_EXIT_CRITICAL(sr); +} + +void +ble_ll_rfmgmt_release(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &rfmgmt->release_ev); + + if (g_ble_ll_rfmgmt_data.state != RFMGMT_STATE_OFF) { + ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &rfmgmt->release_ev); + } + + OS_EXIT_CRITICAL(sr); +} + +uint32_t +ble_ll_rfmgmt_enable_now(void) +{ + struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data; + uint32_t enabled_at; + os_sr_t sr; + + OS_ENTER_CRITICAL(sr); + + ble_ll_rfmgmt_enable(); + + if (rfmgmt->state == RFMGMT_STATE_ENABLED) { + enabled_at = os_cputime_get32(); + } else { + enabled_at = rfmgmt->enabled_at + rfmgmt->ticks_to_enabled + 1; + } + + OS_EXIT_CRITICAL(sr); + + return enabled_at; +} + +bool +ble_ll_rfmgmt_is_enabled(void) +{ + bool ret; + + OS_ASSERT_CRITICAL(); + + ret = ble_ll_rfmgmt_ticks_to_enabled() == 0; + + return ret; +} + +#else + +void +ble_ll_rfmgmt_init(void) +{ + static bool enabled = false; + + if (!enabled) { + ble_phy_rfclk_enable(); + } + + enabled = true; +} + +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_scan.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_scan.c new file mode 100644 index 0000000..eac8411 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_scan.c @@ -0,0 +1,3981 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_hw.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_ll_sync.h" +#include "ble_ll_conn_priv.h" + +/* + * XXX: + * 1) I think I can guarantee that we dont process things out of order if + * I send an event when a scan request is sent. The scan_rsp_pending flag + * code might be made simpler. + * + * 2) Interleave sending scan requests to different advertisers? I guess I need + * a list of advertisers to which I sent a scan request and have yet to + * receive a scan response from? Implement this. + */ + +/* Dont allow more than 255 of these entries */ +#if MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS) > 255 + #error "Cannot have more than 255 scan response entries!" +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#define SCAN_VALID_PHY_MASK (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK) +#else +#define SCAN_VALID_PHY_MASK (BLE_HCI_LE_PHY_1M_PREF_MASK) +#endif + +/* The scanning parameters set by host */ +static struct ble_ll_scan_params g_ble_ll_scan_params[BLE_LL_SCAN_PHY_NUMBER]; + +/* The scanning state machine global object */ +static struct ble_ll_scan_sm g_ble_ll_scan_sm; + +struct ble_ll_ext_adv_hdr +{ + uint8_t mode; + uint8_t hdr_len; + uint8_t hdr[0]; +}; + +struct ble_ll_scan_addr_data { + bool adva_present; + uint8_t adva_type; + uint8_t *adva; + uint8_t targeta_type; + uint8_t *targeta; + uint8_t adv_addr_type; + uint8_t *adv_addr; + struct ble_ll_resolv_entry *rl; +}; + +/* + * Structure used to store advertisers. This is used to limit sending scan + * requests to the same advertiser and also to filter duplicate events sent + * to the host. + */ +struct ble_ll_scan_advertisers +{ + uint16_t sc_adv_flags; + uint16_t adi; + struct ble_dev_addr adv_addr; +}; + +#define BLE_LL_SC_ADV_F_RANDOM_ADDR (0x01) +#define BLE_LL_SC_ADV_F_SCAN_RSP_RXD (0x02) +#define BLE_LL_SC_ADV_F_DIRECT_RPT_SENT (0x04) +#define BLE_LL_SC_ADV_F_ADV_RPT_SENT (0x08) +#define BLE_LL_SC_ADV_F_SCAN_RSP_SENT (0x10) + +/* Contains list of advertisers that we have heard scan responses from */ +static uint8_t g_ble_ll_scan_num_rsp_advs; +struct ble_ll_scan_advertisers +g_ble_ll_scan_rsp_advs[MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)]; + +/* Duplicates filtering data */ +#define BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type) \ + ((addr_type) & 1) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi) \ + (((adi >> 8) & 0xF0) | (1 << 3) | (is_anon << 2) | (has_aux << 1) | ((addr_type) & 1)) +#endif + +#define BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT (0x01) +#define BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT (0x02) +#define BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT (0x04) + +struct ble_ll_scan_dup_entry { + uint8_t type; /* entry type, see BLE_LL_SCAN_ENTRY_TYPE_* */ + uint8_t addr[6]; + uint8_t flags; /* use BLE_LL_SCAN_DUP_F_xxx */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + uint16_t adi; +#endif + TAILQ_ENTRY(ble_ll_scan_dup_entry) link; +}; + +static os_membuf_t g_scan_dup_mem[ OS_MEMPOOL_SIZE( + MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS), + sizeof(struct ble_ll_scan_dup_entry)) ]; +static struct os_mempool g_scan_dup_pool; +static TAILQ_HEAD(ble_ll_scan_dup_list, ble_ll_scan_dup_entry) g_scan_dup_list; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#if MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT) != 0 +static os_membuf_t ext_scan_aux_mem[ OS_MEMPOOL_SIZE( + MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT), + sizeof (struct ble_ll_aux_data)) +]; +#else +#define ext_scan_aux_mem NULL +#endif + +static struct os_mempool ext_scan_aux_pool; + +static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm, + struct ble_ll_sched_item *sch); + +static void +ble_ll_aux_scan_drop_event_cb(struct ble_npl_event *ev) +{ + struct ble_ll_aux_data *aux_data = ble_npl_event_get_arg(ev); + + ble_ll_scan_end_adv_evt(aux_data); + ble_ll_scan_aux_data_unref(aux_data); +} + +static void +ble_ll_aux_scan_drop(struct ble_ll_aux_data *aux_data) +{ + BLE_LL_ASSERT(aux_data); + + STATS_INC(ble_ll_stats, aux_scan_drop); + + ble_npl_event_init(&aux_data->ev, ble_ll_aux_scan_drop_event_cb, aux_data); + ble_ll_event_send(&aux_data->ev); +} + +static int +ble_ll_aux_scan_cb(struct ble_ll_sched_item *sch) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + uint8_t lls = ble_ll_state_get(); + uint32_t wfr_usec; + + STATS_INC(ble_ll_stats, aux_sched_cb); + + /* Drop the scheduled item if scan was disable or there is aux or scan + * response pending + */ + if (!scansm->scan_enabled || scansm->cur_aux_data || + scansm->scan_rsp_pending) { + ble_ll_aux_scan_drop(sch->cb_arg); + sch->cb_arg = NULL; + goto done; + } + + /* Check if there is no aux connect sent. If so drop the sched item */ + if (lls == BLE_LL_STATE_INITIATING && ble_ll_conn_init_pending_aux_conn_rsp()) { + ble_ll_aux_scan_drop(sch->cb_arg); + sch->cb_arg = NULL; + goto done; + } + + /* This function is called only when scanner is running. This can happen + * in 3 states: + * BLE_LL_STATE_SCANNING + * BLE_LL_STATE_INITIATING + * BLE_LL_STATE_STANDBY + */ + if (lls != BLE_LL_STATE_STANDBY) { + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + + /* When doing RX for AUX pkt, cur_aux_data keeps valid aux data */ + scansm->cur_aux_data = sch->cb_arg; + sch->cb_arg = NULL; + BLE_LL_ASSERT(scansm->cur_aux_data != NULL); + scansm->cur_aux_data->scanning = 1; + + if (ble_ll_scan_start(scansm, sch)) { + ble_ll_scan_interrupted(scansm); + goto done; + } + + STATS_INC(ble_ll_stats, aux_fired_for_read); + + wfr_usec = scansm->cur_aux_data->offset_units ? 300 : 30; + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usec); + +done: + + return BLE_LL_SCHED_STATE_DONE; +} + +static int +ble_ll_scan_ext_adv_init(struct ble_ll_aux_data **aux_data) +{ + struct ble_ll_aux_data *e; + + e = os_memblock_get(&ext_scan_aux_pool); + if (!e) { + return -1; + } + + memset(e, 0, sizeof(*e)); + e->sch.sched_cb = ble_ll_aux_scan_cb; + e->sch.sched_type = BLE_LL_SCHED_TYPE_AUX_SCAN; + e->ref_cnt = 1; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + e->rpa_index = -1; +#endif + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t)e, e->ref_cnt); + + *aux_data = e; + STATS_INC(ble_ll_stats, aux_allocated); + + return 0; +} +#endif + +static inline uint32_t +ble_ll_scan_time_hci_to_ticks(uint16_t value) +{ + return os_cputime_usecs_to_ticks(value * BLE_HCI_SCAN_ITVL); +} + +/* See Vol 6 Part B Section 4.4.3.2. Active scanning backoff */ +static void +ble_ll_scan_req_backoff(struct ble_ll_scan_sm *scansm, int success) +{ + BLE_LL_ASSERT(scansm->backoff_count == 0); + BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); + + if (success) { + scansm->scan_rsp_cons_fails = 0; + ++scansm->scan_rsp_cons_ok; + if (scansm->scan_rsp_cons_ok == 2) { + scansm->scan_rsp_cons_ok = 0; + if (scansm->upper_limit > 1) { + scansm->upper_limit >>= 1; + } + } + STATS_INC(ble_ll_stats, scan_req_txg); + } else { + scansm->scan_rsp_cons_ok = 0; + ++scansm->scan_rsp_cons_fails; + if (scansm->scan_rsp_cons_fails == 2) { + scansm->scan_rsp_cons_fails = 0; + if (scansm->upper_limit < 256) { + scansm->upper_limit <<= 1; + } + } + STATS_INC(ble_ll_stats, scan_req_txf); + } + + scansm->backoff_count = rand() & (scansm->upper_limit - 1); + ++scansm->backoff_count; + BLE_LL_ASSERT(scansm->backoff_count <= 256); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +static void +ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm) +{ + ble_npl_time_t now; + + now = ble_npl_time_get(); + if ((ble_npl_stime_t)(now - scansm->scan_nrpa_timer) >= 0) { + /* Generate new NRPA */ + ble_ll_rand_data_get(scansm->scan_nrpa, BLE_DEV_ADDR_LEN); + scansm->scan_nrpa[5] &= ~0xc0; + + /* We'll use the same timeout as for RPA rotation */ + scansm->scan_nrpa_timer = now + ble_ll_resolv_get_rpa_tmo(); + } +} +#endif + +static void +ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm, + const uint8_t *adv_addr, uint8_t adv_addr_type, + struct ble_ll_resolv_entry *rl) +{ + uint8_t hdr_byte; + struct ble_ll_scan_pdu_data *pdu_data; + uint8_t *scana; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + uint8_t rpa[BLE_DEV_ADDR_LEN]; +#endif + + pdu_data = &scansm->pdu_data; + + /* Construct first PDU header byte */ + hdr_byte = BLE_ADV_PDU_TYPE_SCAN_REQ; + if (adv_addr_type) { + hdr_byte |= BLE_ADV_PDU_HDR_RXADD_RAND; + } + + /* Determine ScanA */ + if (scansm->own_addr_type & 0x01) { + hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND; + scana = g_random_addr; + } else { + scana = g_dev_addr; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (scansm->own_addr_type & 0x02) { + /* + * If device is on RL and we have local IRK, we use RPA generated using + * that IRK as ScanA. Otherwise we use NRPA as ScanA to prevent our + * device from being tracked when doing an active scan (Core 5.1, Vol 6, + * Part B, section 6.3) + */ + if (rl && rl->rl_has_local) { + ble_ll_resolv_get_priv_addr(rl, 1, rpa); + scana = rpa; + } else { + ble_ll_scan_refresh_nrpa(scansm); + scana = scansm->scan_nrpa; + } + + hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND; + } +#endif + + /* Save scan request data */ + pdu_data->hdr_byte = hdr_byte; + memcpy(pdu_data->scana, scana, BLE_DEV_ADDR_LEN); + memcpy(pdu_data->adva, adv_addr, BLE_DEV_ADDR_LEN); +} + +static uint8_t +ble_ll_scan_req_tx_pdu_cb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) +{ + struct ble_ll_scan_sm *scansm = pducb_arg; + struct ble_ll_scan_pdu_data *pdu_data = &scansm->pdu_data; + + memcpy(dptr, pdu_data->scana, BLE_DEV_ADDR_LEN); + memcpy(dptr + BLE_DEV_ADDR_LEN, pdu_data->adva, BLE_DEV_ADDR_LEN); + + *hdr_byte = pdu_data->hdr_byte; + + return BLE_DEV_ADDR_LEN * 2; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/* if copy_from is provided new report is initialized with that instead of + * defaults + */ +static struct ble_hci_ev * +ble_ll_scan_get_ext_adv_report(struct ext_adv_report *copy_from) +{ + struct ble_hci_ev_le_subev_ext_adv_rpt *ev; + struct ext_adv_report *report; + struct ble_hci_ev *hci_ev; + + hci_ev = ( void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (!hci_ev) { + return NULL; + } + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev) + sizeof(*report); + ev = (void *) hci_ev->data; + + memset(ev, 0, sizeof(*ev)); + ev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT; + /* We support only one report per event now */ + ev->num_reports = 1; + + report = ev->reports; + + if (copy_from) { + memcpy(report, copy_from, sizeof(*report)); + report->data_len = 0; + } else { + memset(report, 0, sizeof(*report)); + + report->pri_phy = BLE_PHY_1M; + /* Init SID with "Not available" which is 0xFF */ + report->sid = 0xFF; + /* Init TX Power with "Not available" which is 127 */ + report->tx_power = 127; + /* Init RSSI with "Not available" which is 127 */ + report->rssi = 127; + /* Init address type with "anonymous" which is 0xFF */ + report->addr_type = 0xFF; + } + + return hci_ev; +} + +static void +ble_ll_scan_send_truncated(struct ble_ll_aux_data *aux_data) +{ + struct ble_hci_ev_le_subev_ext_adv_rpt *ev; + struct ext_adv_report *report; + struct ble_hci_ev *hci_ev; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { + return; + } + + BLE_LL_ASSERT(aux_data); + + /* No need to send if we did not send any report or sent truncated already */ + if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || + (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)) { + return; + } + + BLE_LL_ASSERT(aux_data->evt); + hci_ev = aux_data->evt; + aux_data->evt = NULL; + + hci_ev->length = sizeof(*ev) + sizeof(*report); + + ev = (void *) hci_ev->data; + report = ev->reports; + + report->data_len = 0; + + report->evt_type = aux_data->evt_type; + report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; + + if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { + memcpy(report->addr, aux_data->adva, 6); + report->addr_type = aux_data->adva_type; + } + + if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { + memcpy(report->dir_addr, aux_data->targeta, 6); + report->dir_addr_type = aux_data->targeta_type; + } + + report->sid = aux_data->adi >> 12; + ble_ll_hci_event_send(hci_ev); + + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY; + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; +} + +static int +ble_ll_scan_get_adi(struct ble_ll_aux_data *aux_data, uint16_t *adi) +{ + if (!aux_data || !(aux_data->flags & BLE_LL_AUX_HAS_ADI)) { + return -1; + } + + *adi = aux_data->adi; + + return 0; +} + +void +ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data) +{ + /* Make sure we send report with 'truncated' data state if needed */ + ble_ll_scan_send_truncated(aux_data); +} +#endif + +static void +ble_ll_scan_clean_cur_aux_data(void) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + + /* If scanner was reading aux ptr, we need to clean it up */ + if (scansm->cur_aux_data) { + ble_ll_scan_end_adv_evt(scansm->cur_aux_data); + ble_ll_scan_aux_data_unref(scansm->cur_aux_data); + scansm->cur_aux_data = NULL; + } +#endif +} + +void +ble_ll_scan_halt(void) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + + ble_ll_scan_clean_cur_aux_data(); + + /* Update backoff if we failed to receive scan response */ + if (scansm->scan_rsp_pending) { + scansm->scan_rsp_pending = 0; + ble_ll_scan_req_backoff(scansm, 0); + } +} + +/** + * Checks to see if we have received a scan response from this advertiser. + * + * @param adv_addr Address of advertiser + * @param txadd TxAdd bit (0: public; random otherwise) + * + * @return int 0: have not received a scan response; 1 otherwise. + */ +static int +ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd, + uint8_t ext_adv, uint16_t adi) +{ + uint8_t num_advs; + struct ble_ll_scan_advertisers *adv; + + /* Do we have an address match? Must match address type */ + adv = &g_ble_ll_scan_rsp_advs[0]; + num_advs = g_ble_ll_scan_num_rsp_advs; + while (num_advs) { + if (!memcmp(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN)) { + /* Address type must match */ + if (txadd) { + if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) { + if (ext_adv) { + if (adi == adv->adi) { + return 1; + } + goto next; + } + return 1; + } + } else { + if ((adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) == 0) { + if (ext_adv) { + if (adi == adv->adi) { + return 1; + } + goto next; + } + return 1; + } + } + } +next: + ++adv; + --num_advs; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd, + uint8_t ext_adv, uint16_t adi) +{ + uint8_t num_advs; + struct ble_ll_scan_advertisers *adv; + + /* XXX: for now, if we dont have room, just leave */ + num_advs = g_ble_ll_scan_num_rsp_advs; + if (num_advs == MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)) { + return; + } + + /* Check if address is already on the list */ + if (ble_ll_scan_have_rxd_scan_rsp(addr, txadd, ext_adv, adi)) { + return; + } + + /* Add the advertiser to the array */ + adv = &g_ble_ll_scan_rsp_advs[num_advs]; + memcpy(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN); + adv->sc_adv_flags = BLE_LL_SC_ADV_F_SCAN_RSP_RXD; + if (txadd) { + adv->sc_adv_flags |= BLE_LL_SC_ADV_F_RANDOM_ADDR; + } + adv->adi = adi; + ++g_ble_ll_scan_num_rsp_advs; + + return; +} + +static int +ble_ll_hci_send_legacy_ext_adv_report(uint8_t evtype, + const uint8_t *addr, uint8_t addr_type, + uint8_t rssi, + uint8_t adv_data_len, + struct os_mbuf *adv_data, + const uint8_t *inita, uint8_t inita_type) +{ + struct ble_hci_ev_le_subev_ext_adv_rpt *ev; + struct ext_adv_report *report; + struct ble_hci_ev *hci_ev; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { + return -1; + } + + /* Drop packet if adv data doesn't fit */ + if ((sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len) > BLE_HCI_MAX_DATA_LEN) { + STATS_INC(ble_ll_stats, adv_evt_dropped); + return -1; + } + + hci_ev = ble_ll_scan_get_ext_adv_report(NULL); + if (!hci_ev) { + return -1; + } + + ev = (void *) hci_ev->data; + report = ev->reports; + + switch (evtype) { + case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND: + report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_IND; + break; + case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND: + report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_DIRECT_IND; + break; + case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND: + report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_NONCON_IND; + break; + case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP: + report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_IND; + break; + case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND: + report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_SCAN_IND; + break; + default: + BLE_LL_ASSERT(0); + ble_hci_trans_buf_free((uint8_t *) hci_ev); + return -1; + } + + report->addr_type = addr_type; + memcpy(report->addr, addr, BLE_DEV_ADDR_LEN); + report->pri_phy = BLE_PHY_1M; + report->sid = 0xFF; + report->tx_power = 127; + report->rssi = rssi; + + if (inita) { + report->dir_addr_type = inita_type; + memcpy(report->dir_addr, inita, BLE_DEV_ADDR_LEN); + } + + if (adv_data_len) { + hci_ev->length += adv_data_len; + report->data_len = adv_data_len; + os_mbuf_copydata(adv_data, 0, adv_data_len, report->data); + } + + return ble_ll_hci_event_send(hci_ev); +} +#endif + +static int +ble_ll_hci_send_adv_report(uint8_t evtype, + const uint8_t *addr, uint8_t addr_type, int8_t rssi, + uint8_t adv_data_len, struct os_mbuf *adv_data) +{ + struct ble_hci_ev_le_subev_adv_rpt *ev; + struct ble_hci_ev *hci_ev; + int8_t *ev_rssi; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ADV_RPT)) { + return -1; + } + + /* Drop packet if adv data doesn't fit, note extra 1 is for RSSI */ + if ((sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len + 1) > BLE_HCI_MAX_DATA_LEN) { + STATS_INC(ble_ll_stats, adv_evt_dropped); + return -1; + } + + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (!hci_ev) { + return -1; + } + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len + 1; + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_ADV_RPT; + ev->num_reports = 1; + + ev->reports[0].type = evtype; + ev->reports[0].addr_type = addr_type; + memcpy(ev->reports[0].addr, addr, BLE_DEV_ADDR_LEN); + ev->reports[0].data_len = adv_data_len; + os_mbuf_copydata(adv_data, 0, adv_data_len, ev->reports[0].data); + + /* RSSI is after adv data... */ + ev_rssi = (int8_t *) (hci_ev->data + sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len); + *ev_rssi = rssi; + + return ble_ll_hci_event_send(hci_ev); +} + +static int +ble_ll_hci_send_dir_adv_report(const uint8_t *addr, uint8_t addr_type, + const uint8_t *inita, uint8_t inita_type, + int8_t rssi) +{ + struct ble_hci_ev_le_subev_direct_adv_rpt *ev; + struct ble_hci_ev *hci_ev; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT)) { + return -1; + } + + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (!hci_ev) { + return -1; + } + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev) + sizeof(*(ev->reports)); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT; + ev->num_reports = 1; + + ev->reports[0].type = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; + ev->reports[0].addr_type = addr_type; + memcpy(ev->reports[0].addr, addr, BLE_DEV_ADDR_LEN); + ev->reports[0].dir_addr_type = inita_type; + memcpy(ev->reports[0].dir_addr, inita, BLE_DEV_ADDR_LEN); + ev->reports[0].rssi = rssi; + + return ble_ll_hci_event_send(hci_ev); +} + +static int +ble_ll_scan_dup_update_legacy(uint8_t addr_type, const uint8_t *addr, + uint8_t subev, uint8_t evtype) +{ + struct ble_ll_scan_dup_entry *e; + uint8_t type; + + type = BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type); + + /* + * We assume ble_ll_scan_dup_check() was called before which either matched + * some entry or allocated new one and placed in on the top of queue. + */ + + e = TAILQ_FIRST(&g_scan_dup_list); + BLE_LL_ASSERT(e && e->type == type && !memcmp(e->addr, addr, 6)); + + if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) { + e->flags |= BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT; + } else { + if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + e->flags |= BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT; + } else { + e->flags |= BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; + } + } + + return 0; +} + +/** + * Send an advertising report to the host. + * + * NOTE: while we are allowed to send multiple devices in one report, we + * will just send for one for now. + * + * @param pdu_type + * @param txadd + * @param rxbuf + * @param hdr + * @param scansm + */ +static void +ble_ll_scan_send_adv_report(uint8_t pdu_type, + const uint8_t *adva, uint8_t adva_type, + const uint8_t *inita, uint8_t inita_type, + struct os_mbuf *om, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_sm *scansm) +{ + uint8_t subev = BLE_HCI_LE_SUBEV_ADV_RPT; + uint8_t adv_data_len; + uint8_t evtype; + int rc; + + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { + if (ble_ll_is_rpa(inita, inita_type)) { + /* For resolvable we send separate subevent */ + subev = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT; + } + + evtype = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; + adv_data_len = 0; + } else { + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) { + evtype = BLE_HCI_ADV_RPT_EVTYPE_ADV_IND; + } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND) { + evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND; + } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_NONCONN_IND) { + evtype = BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND; + } else { + evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP; + } + adv_data_len = om->om_data[1] - BLE_DEV_ADDR_LEN; + os_mbuf_adj(om, BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN); + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* If RPA has been used, make sure we use correct address types + * in the advertising report. + */ + if (BLE_MBUF_HDR_RESOLVED(hdr)) { + adva_type += 2; + } + if (BLE_MBUF_HDR_TARGETA_RESOLVED(hdr)) { + inita_type += 2; + } +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (scansm->ext_scanning) { + rc = ble_ll_hci_send_legacy_ext_adv_report(evtype, + adva, adva_type, + hdr->rxinfo.rssi, + adv_data_len, om, + inita, inita_type); + goto done; + } +#endif + + if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) { + rc = ble_ll_hci_send_dir_adv_report(adva, adva_type, inita, inita_type, + hdr->rxinfo.rssi); + goto done; + } + + rc = ble_ll_hci_send_adv_report(evtype, adva, adva_type, hdr->rxinfo.rssi, + adv_data_len, om); +done: + if (!rc && scansm->scan_filt_dups) { + ble_ll_scan_dup_update_legacy(adva_type, adva, subev, evtype); + } +} + +static void +ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan, + int *phy) +{ + struct ble_ll_scan_params *scanp = scansm->scanp; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data = scansm->cur_aux_data; + + if (!scansm->ext_scanning || !aux_data || !aux_data->scanning) { + *chan = scanp->scan_chan; + *phy = scanp->phy; + return; + } + + *chan = aux_data->chan; + *phy = aux_data->aux_phy; +#else + *chan = scanp->scan_chan; + *phy = scanp->phy; +#endif +} +/** + * Called to enable the receiver for scanning. + * + * Context: Link Layer task + * + * @param sch + * + * @return int + */ +static int +ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch) +{ + int rc; + struct ble_ll_scan_params *scanp = scansm->scanp; + uint8_t scan_chan; +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + uint8_t phy_mode; +#endif + int phy; + + BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); + + ble_ll_get_chan_to_scan(scansm, &scan_chan, &phy); + + /* XXX: right now scheduled item is only present if we schedule for aux + * scan just make sanity check that we have proper combination of + * sch and resulting scan_chan + */ + BLE_LL_ASSERT(!sch || scan_chan < BLE_PHY_ADV_CHAN_START); + BLE_LL_ASSERT(sch || scan_chan >= BLE_PHY_ADV_CHAN_START); + + /* Set channel */ + rc = ble_phy_setchan(scan_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); + BLE_LL_ASSERT(rc == 0); + + /* + * Set transmit end callback to NULL in case we transmit a scan request. + * There is a callback for the connect request. + */ + ble_phy_set_txend_cb(NULL, NULL); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (ble_ll_resolv_enabled()) { + ble_phy_resolv_list_enable(); + } else { + ble_phy_resolv_list_disable(); + } +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY); + ble_phy_mode_set(phy_mode, phy_mode); +#endif + + /* XXX: probably need to make sure hfxo is running too */ + /* XXX: can make this better; want to just start asap. */ + if (sch) { + rc = ble_phy_rx_set_start_time(sch->start_time + + g_ble_ll_sched_offset_ticks, + sch->remainder); + } else { + rc = ble_phy_rx_set_start_time(os_cputime_get32() + + g_ble_ll_sched_offset_ticks, 0); + } + if (!rc || rc == BLE_PHY_ERR_RX_LATE) { + /* If we are late here, it is still OK because we keep scanning. + * Clear error + */ + rc = 0; + + /* Enable/disable whitelisting */ + if (scanp->scan_filt_policy & 1) { + ble_ll_whitelist_enable(); + } else { + ble_ll_whitelist_disable(); + } + + /* Set link layer state to scanning */ + if (scanp->scan_type == BLE_SCAN_TYPE_INITIATE) { + ble_ll_state_set(BLE_LL_STATE_INITIATING); + } else { + ble_ll_state_set(BLE_LL_STATE_SCANNING); + } + } + + return rc; +} + +static uint8_t +ble_ll_scan_get_next_adv_prim_chan(uint8_t chan) +{ + ++chan; + if (chan == BLE_PHY_NUM_CHANS) { + chan = BLE_PHY_ADV_CHAN_START; + } + + return chan; +} + +static uint32_t +ble_ll_scan_move_window_to(struct ble_ll_scan_params *scanp, uint32_t time) +{ + uint32_t end_time; + + /* + * Move window until given tick is before or inside window and move to next + * channel for each skipped interval. + */ + + end_time = scanp->timing.start_time + scanp->timing.window; + while (CPUTIME_GEQ(time, end_time)) { + scanp->timing.start_time += scanp->timing.interval; + scanp->scan_chan = ble_ll_scan_get_next_adv_prim_chan(scanp->scan_chan); + end_time = scanp->timing.start_time + scanp->timing.window; + } + + return scanp->timing.start_time; +} + +static bool +ble_ll_scan_is_inside_window(struct ble_ll_scan_params *scanp, uint32_t time) +{ + uint32_t start_time; + + /* Make sure we are checking against closest window */ + start_time = ble_ll_scan_move_window_to(scanp, time); + + if (scanp->timing.window == scanp->timing.interval) { + /* always inside window in continuous scan */ + return true; + } + + return CPUTIME_GEQ(time, start_time) && + CPUTIME_LT(time, start_time + scanp->timing.window); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_scan_aux_data_free(struct ble_ll_aux_data *aux_data) +{ + if (aux_data) { + if (aux_data->evt) { + ble_hci_trans_buf_free((uint8_t *)aux_data->evt); + aux_data->evt = NULL; + } + os_memblock_put(&ext_scan_aux_pool, aux_data); + STATS_INC(ble_ll_stats, aux_freed); + } +} + +struct ble_ll_aux_data * +ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_data) +{ + os_sr_t sr; + + BLE_LL_ASSERT(aux_data); + + OS_ENTER_CRITICAL(sr); + aux_data->ref_cnt++; + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t) aux_data, aux_data->ref_cnt); + + OS_EXIT_CRITICAL(sr); + + return aux_data; +} + +void +ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_data) +{ + os_sr_t sr; + + BLE_LL_ASSERT(aux_data); + + OS_ENTER_CRITICAL(sr); + aux_data->ref_cnt--; + ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_UNREF, (uint32_t) aux_data, aux_data->ref_cnt); + + if (aux_data->ref_cnt == 0) { + /* + * Some validation to make sure that we completed scan properly: + * - we either did not send any report or sent completed/truncated + * - we only sent one of completed/truncated + * - in case of error, we wither did not send anything or sent truncated + */ + BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || + ((aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) && + (aux_data->flags_ll & (BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED | BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)))); + BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED) || !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); + BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) || + !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || + (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); + + ble_ll_scan_aux_data_free(aux_data); + } + + OS_EXIT_CRITICAL(sr); +} + +static void +ble_ll_scan_sched_remove(struct ble_ll_sched_item *sch) +{ + ble_ll_scan_end_adv_evt(sch->cb_arg); + ble_ll_scan_aux_data_unref(sch->cb_arg); + sch->cb_arg = NULL; +} +#endif +/** + * Stop the scanning state machine + */ +void +ble_ll_scan_sm_stop(int chk_disable) +{ + os_sr_t sr; + uint8_t lls; + struct ble_ll_scan_sm *scansm; + + /* Stop the scanning timer */ + scansm = &g_ble_ll_scan_sm; + os_cputime_timer_stop(&scansm->scan_timer); + + OS_ENTER_CRITICAL(sr); + + /* Disable scanning state machine */ + scansm->scan_enabled = 0; + scansm->restart_timer_needed = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (scansm->ext_scanning) { + ble_ll_scan_clean_cur_aux_data(); + ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_AUX_SCAN, ble_ll_scan_sched_remove); + scansm->ext_scanning = 0; + } +#endif + + /* Update backoff if we failed to receive scan response */ + if (scansm->scan_rsp_pending) { + scansm->scan_rsp_pending = 0; + ble_ll_scan_req_backoff(scansm, 0); + } + OS_EXIT_CRITICAL(sr); + + /* Count # of times stopped */ + STATS_INC(ble_ll_stats, scan_stops); + + /* Only set state if we are currently in a scan window */ + if (chk_disable) { + OS_ENTER_CRITICAL(sr); + lls = ble_ll_state_get(); + + if ((lls == BLE_LL_STATE_SCANNING) || + (lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) { + /* Disable phy */ + ble_phy_disable(); + + /* Set LL state to standby */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + OS_EXIT_CRITICAL(sr); + } + + /* No need for RF anymore */ + OS_ENTER_CRITICAL(sr); + ble_ll_rfmgmt_scan_changed(false, 0); + ble_ll_rfmgmt_release(); + OS_EXIT_CRITICAL(sr); +} + +static int +ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm) +{ + struct ble_ll_scan_params *scanp; + struct ble_ll_scan_params *scanp_next; + + if (!ble_ll_is_valid_own_addr_type(scansm->own_addr_type, g_random_addr)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + BLE_LL_ASSERT(scansm->scanp); + scanp = scansm->scanp; + scanp_next = scansm->scanp_next; + + /* Count # of times started */ + STATS_INC(ble_ll_stats, scan_starts); + + /* Set flag telling us that scanning is enabled */ + scansm->scan_enabled = 1; + + /* Set first advertising channel */ + scanp->scan_chan = BLE_PHY_ADV_CHAN_START; + if (scanp_next) { + scanp_next->scan_chan = BLE_PHY_ADV_CHAN_START; + } + + /* Reset scan request backoff parameters to default */ + scansm->upper_limit = 1; + scansm->backoff_count = 1; + scansm->scan_rsp_pending = 0; + + /* Forget filtered advertisers from previous scan. */ + g_ble_ll_scan_num_rsp_advs = 0; + + os_mempool_clear(&g_scan_dup_pool); + TAILQ_INIT(&g_scan_dup_list); + + /* + * First scan window can start when RF is enabled. Add 1 tick since we are + * most likely not aligned with ticks so RF may be effectively enabled 1 + * tick later. + */ + scanp->timing.start_time = ble_ll_rfmgmt_enable_now(); + ble_ll_rfmgmt_scan_changed(true, scanp->timing.start_time); + + if (scanp_next) { + /* Schedule start time right after first phy */ + scanp_next->timing.start_time = scanp->timing.start_time + + scanp->timing.window; + } + + /* Start scan at 1st window */ + os_cputime_timer_start(&scansm->scan_timer, scanp->timing.start_time); + + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_aux_scan_rsp_failed(struct ble_ll_scan_sm *scansm) +{ + if (!scansm->cur_aux_data) { + return; + } + + STATS_INC(ble_ll_stats, aux_scan_rsp_err); + ble_ll_scan_interrupted(scansm); +} +#endif + +static void +ble_ll_scan_interrupted_event_cb(struct ble_npl_event *ev) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data; +#endif + + if (!scansm->scan_enabled) { + return; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + aux_data = ble_npl_event_get_arg(ev); + + if (aux_data) { + if (scansm->scan_rsp_pending) { + STATS_INC(ble_ll_stats, aux_scan_rsp_err); + } + ble_ll_scan_end_adv_evt(aux_data); + ble_ll_scan_aux_data_unref(aux_data); + ble_npl_event_set_arg(ev, NULL); + STATS_INC(ble_ll_stats, aux_missed_adv); + } +#endif + + /* + * If we timed out waiting for a response, the scan response pending + * flag should be set. Deal with scan backoff. Put device back into rx. + */ + + if (scansm->scan_rsp_pending) { + scansm->scan_rsp_pending = 0; + ble_ll_scan_req_backoff(scansm, 0); + } + + ble_ll_scan_chk_resume(); +} + +/** + * Called to process the scanning OS event which was posted to the LL task + * + * Context: Link Layer task. + * + * @param arg + */ +static void +ble_ll_scan_event_proc(struct ble_npl_event *ev) +{ + struct ble_ll_scan_sm *scansm; + os_sr_t sr; + bool start_scan; + bool inside_window; + struct ble_ll_scan_params *scanp; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + bool inside_window_next; + struct ble_ll_scan_params *scanp_next; +#endif + uint32_t next_proc_time; + uint32_t now; + /* + * Get the scanning state machine. If not enabled (this is possible), just + * leave and do nothing (just make sure timer is stopped). + */ + scansm = (struct ble_ll_scan_sm *)ble_npl_event_get_arg(ev); + scanp = scansm->scanp; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + scanp_next = scansm->scanp_next; +#endif + + OS_ENTER_CRITICAL(sr); + if (!scansm->scan_enabled) { + os_cputime_timer_stop(&scansm->scan_timer); + ble_ll_rfmgmt_scan_changed(false, 0); + ble_ll_rfmgmt_release(); + OS_EXIT_CRITICAL(sr); + return; + } + + if (scansm->cur_aux_data || scansm->scan_rsp_pending) { + /* Aux scan in progress. Wait */ + STATS_INC(ble_ll_stats, scan_timer_stopped); + scansm->restart_timer_needed = 1; + OS_EXIT_CRITICAL(sr); + return; + } + + now = os_cputime_get32(); + + inside_window = ble_ll_scan_is_inside_window(scanp, now); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* Update also next PHY if configured */ + if (scanp_next) { + inside_window_next = ble_ll_scan_is_inside_window(scanp_next, now); + + /* + * Switch PHY if current PHY is outside window and next PHY is either + * inside window or has next window earlier than current PHY. + */ + if (!inside_window && + ((inside_window_next || CPUTIME_LEQ(scanp_next->timing.start_time, + scanp->timing.start_time)))) { + scansm->scanp = scanp_next; + scansm->scanp_next = scanp; + scanp = scansm->scanp; + scanp_next = scansm->scanp_next; + inside_window = inside_window_next; + } + } +#endif + + /* + * At this point scanp and scanp_next point to current or closest scan + * window on both PHYs (scanp is the closer one). Make sure RF is enabled + * on time. + */ + ble_ll_rfmgmt_scan_changed(true, scanp->timing.start_time); + + /* + * If we are inside window, next scan proc should happen at the end of + * current window to either disable scan or switch to next PHY. + * If we are outside window, next scan proc should happen at the time of + * closest scan window. + */ + if (inside_window) { + next_proc_time = scanp->timing.start_time + scanp->timing.window; + } else { + next_proc_time = scanp->timing.start_time; + } + + /* + * If we are not in the standby state it means that the scheduled + * scanning event was overlapped in the schedule. In this case all we do + * is post the scan schedule end event. + */ + start_scan = inside_window; + switch (ble_ll_state_get()) { + case BLE_LL_STATE_ADV: + case BLE_LL_STATE_CONNECTION: + case BLE_LL_STATE_SYNC: + start_scan = false; + break; + case BLE_LL_STATE_INITIATING: + /* Must disable PHY since we will move to a new channel */ + ble_phy_disable(); + if (!inside_window) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */ + ble_ll_conn_reset_pending_aux_conn_rsp(); + break; + case BLE_LL_STATE_SCANNING: + /* Must disable PHY since we will move to a new channel */ + ble_phy_disable(); + if (!inside_window) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + } + break; + case BLE_LL_STATE_STANDBY: + break; + default: + BLE_LL_ASSERT(0); + break; + } + + if (start_scan) { + ble_ll_scan_start(scansm, NULL); + } else { + ble_ll_rfmgmt_release(); + } + + OS_EXIT_CRITICAL(sr); + os_cputime_timer_start(&scansm->scan_timer, next_proc_time); +} + +/** + * ble ll scan rx pdu start + * + * Called when a PDU reception has started and the Link Layer is in the + * scanning state. + * + * Context: Interrupt + * + * @param pdu_type + * @param rxflags + * + * @return int + * 0: we will not attempt to reply to this frame + * 1: we may send a response to this frame. + */ +int +ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags) +{ + int rc; + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + + rc = 0; + scansm = &g_ble_ll_scan_sm; + scanp = scansm->scanp; + + switch (scanp->scan_type) { + case BLE_SCAN_TYPE_ACTIVE: + /* If adv ind or scan ind, we may send scan request */ + if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || + (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND)) { + rc = 1; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning)) { + *rxflags |= BLE_MBUF_HDR_F_EXT_ADV; + rc = 1; + } +#endif + + if (scansm->cur_aux_data && !scansm->scan_rsp_pending ) { + STATS_INC(ble_ll_stats, aux_received); + } + + /* + * If this is the first PDU after we sent the scan response (as + * denoted by the scan rsp pending flag), we set a bit in the ble + * header so the link layer can check to see if the scan request + * was successful. We do it this way to let the Link Layer do the + * work for successful scan requests. If failed, we do the work here. + */ + if (scansm->scan_rsp_pending) { + scansm->scan_rsp_pending = 0; + + if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { + *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD; + } else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_SCAN_RSP) { + *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD; + } else { + ble_ll_scan_req_backoff(scansm, 0); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_ll_aux_scan_rsp_failed(scansm); +#endif + } + } + break; + case BLE_SCAN_TYPE_PASSIVE: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning)) { + *rxflags |= BLE_MBUF_HDR_F_EXT_ADV; + } + break; +#endif + default: + break; + } + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static uint8_t +ble_ll_ext_adv_phy_mode_to_local_phy(uint8_t adv_phy_mode) +{ + switch (adv_phy_mode) { + case 0x00: + return BLE_PHY_1M; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case 0x01: + return BLE_PHY_2M; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case 0x02: + return BLE_PHY_CODED; +#endif + } + + return 0; +} + +static int +ble_ll_ext_scan_parse_aux_ptr(struct ble_ll_aux_data *aux_data, uint8_t *buf) +{ + uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF; + + aux_data->chan = (aux_ptr_field) & 0x3F; + if (aux_data->chan >= BLE_PHY_NUM_DATA_CHANS) { + return -1; + } + + /* TODO use CA aux_ptr_field >> 6 */ + + aux_data->offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF); + + if ((aux_ptr_field >> 7) & 0x01) { + aux_data->offset *= 10; + aux_data->offset_units = 1; + } + + if (aux_data->offset < BLE_LL_MAFS) { + return -1; + } + + aux_data->aux_phy = + ble_ll_ext_adv_phy_mode_to_local_phy((aux_ptr_field >> 21) & 0x07); + if (aux_data->aux_phy == 0) { + return -1; + } + + return 0; +} + +static void +ble_ll_ext_scan_parse_adv_info(struct ext_adv_report *report, const uint8_t *buf) +{ + uint16_t adv_info = get_le16(buf); + + /* TODO Use DID */ + + report->sid = (adv_info >> 12); +} + +/** + * ble_ll_scan_update_aux_data + * + * Update aux_data stored in ble_hdr.rxinfo.user_data. If no aux_data is present + * (i.e. processing ADV_EXT_IND) this will try to allocate new aux_data. + * + * Context: Interrupt + * + * @param ble_hdr + * @param rxbuf + * + * @return int + * 1: do not scan for next AUX (no AuxPtr or malformed data) + * 0: scan for next AUX (valid AuxPtr found) + * -1: error + */ +int +ble_ll_scan_update_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf, + bool *adva_present) +{ + uint8_t pdu_hdr; + uint8_t pdu_len; + uint8_t adv_mode; + uint8_t eh_len; + uint8_t eh_flags; + uint8_t *eh; + struct ble_ll_aux_data *aux_data; + bool is_aux; + + aux_data = ble_hdr->rxinfo.user_data; + /* aux_data is initially not set only for ADV_EXT_IND */ + is_aux = aux_data; + + pdu_hdr = rxbuf[0]; + pdu_len = rxbuf[1]; + + /* PDU without at least Extended Header Length is invalid */ + if (pdu_len == 0) { + return -1; + } + + adv_mode = rxbuf[2] >> 6; + eh_len = rxbuf[2] & 0x3f; + eh_flags = rxbuf[3]; + eh = &rxbuf[4]; + + /* + * PDU without Extended Header is valid in case of last AUX_CHAIN_IND in + * chain so aux_data has to be set and advertising mode has to be 00b, + * otherwise it's an invalid PDU. + */ + if (eh_len == 0) { + if (!aux_data || adv_mode) { + return -1; + } + aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE; + return 1; + } + + /* + * If aux_data is not set, this is ADV_EXT_IND which starts new extended + * advertising event. + */ + if (!aux_data) { + if (ble_ll_scan_ext_adv_init(&aux_data)) { + return -1; + } + + aux_data->aux_primary_phy = ble_hdr->rxinfo.phy; + } else { + if (aux_data->flags_isr & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED) { + aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED; + } else { + aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED; + } + } + + /* Now parse extended header... */ + + if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + aux_data->flags |= BLE_LL_AUX_HAS_ADVA; + memcpy(aux_data->adva, eh, 6); + aux_data->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK); + eh += BLE_LL_EXT_ADV_ADVA_SIZE; + + if (adva_present) { + *adva_present = true; + } + } else if (adva_present) { + *adva_present = false; + } + + if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + aux_data->flags |= BLE_LL_AUX_HAS_TARGETA; + memcpy(aux_data->targeta, eh, 6); + aux_data->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK); + eh += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + + if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { + eh += 1; + } + + if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { + aux_data->flags |= BLE_LL_AUX_HAS_ADI; + if (is_aux) { + if (get_le16(eh) != aux_data->adi) { + aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR; + STATS_INC(ble_ll_stats, aux_chain_err); + } + } else { + aux_data->adi = get_le16(eh); + } + eh += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } + + if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + if (ble_ll_ext_scan_parse_aux_ptr(aux_data, eh)) { + aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR; + } + } else if (!(adv_mode & BLE_LL_EXT_ADV_MODE_SCAN)) { + /* No AuxPtr for scannable PDU is ignored since we can still scan it */ + aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE; + } + + ble_hdr->rxinfo.user_data = aux_data; + + /* Do not scan for next AUX if either no AuxPtr or malformed data found */ + return !(eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) || + (aux_data->flags_isr & BLE_LL_AUX_FLAG_SCAN_ERROR); +} + +/** + * Called when a receive ADV_EXT PDU has ended. + * + * Context: Interrupt + * + * @return int + * < 0 Error + * >= 0: Success (number of bytes left in PDU) + * + */ +static int +ble_ll_scan_parse_ext_hdr(struct os_mbuf *om, + const uint8_t *adva, uint8_t adva_type, + const uint8_t *inita, uint8_t inita_type, + struct ble_mbuf_hdr *ble_hdr, + struct ext_adv_report *report) +{ + uint8_t pdu_len; + uint8_t ext_hdr_len; + uint8_t ext_hdr_flags; + uint8_t *ext_hdr; + uint8_t *rxbuf = om->om_data; + int i = 1; + struct ble_ll_scan_sm *scansm; + struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data; + + BLE_LL_ASSERT(report); + + scansm = &g_ble_ll_scan_sm; + + if (!scansm->ext_scanning) { + /* Ignore ext adv if host does not want it*/ + return -1; + } + + pdu_len = rxbuf[1]; + if (pdu_len == 0) { + return -1; + } + + report->evt_type = rxbuf[2] >> 6; + if ( report->evt_type > BLE_LL_EXT_ADV_MODE_SCAN) { + return -1; + } + + if (BLE_MBUF_HDR_SCAN_RSP_RXD(ble_hdr)) { + report->evt_type |= BLE_HCI_ADV_SCAN_RSP_MASK; + } + + ext_hdr_len = rxbuf[2] & 0x3F; + os_mbuf_adj(om, 3); + + ext_hdr_flags = rxbuf[3]; + ext_hdr = &rxbuf[4]; + + if (ext_hdr_len) { + i = 0; + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + i += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + if (adva) { + memcpy(report->addr, adva, 6); + report->addr_type = adva_type; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + i += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + if (inita) { + memcpy(report->dir_addr, inita, 6); + report->dir_addr_type = inita_type; + report->evt_type |= BLE_HCI_ADV_DIRECT_MASK; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { + /* Just skip it for now*/ + i += 1; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { + ble_ll_ext_scan_parse_adv_info(report, (ext_hdr + i)); + i += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } else if (report->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) { + report->sid = (aux_data->adi >> 12); + } + + /* In this point of time we don't want to care about aux ptr */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + i += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { + report->periodic_itvl = get_le16(ext_hdr + i + 2); + i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { + report->tx_power = *(ext_hdr + i); + i += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + /* TODO Handle ACAD if needed */ + } + + /* In the event we need information on primary and secondary PHY used during + * advertising. + */ + if (!aux_data) { + report->pri_phy = ble_hdr->rxinfo.phy; + goto done; + } + + report->sec_phy = aux_data->aux_phy; + report->pri_phy = aux_data->aux_primary_phy; + + if (ext_hdr_len) { + /* Adjust mbuf to contain advertising data only */ + os_mbuf_adj(om, ext_hdr_len); + } + + /* Let us first keep update event type in aux data. + * Note that in aux chain and aux scan response packets + * we do miss original event type, which we need for advertising report. + */ + aux_data->evt_type |= report->evt_type; + report->evt_type = aux_data->evt_type; + +done: + return pdu_len - ext_hdr_len - 1; +} + +static int +ble_ll_scan_get_addr_from_ext_adv(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr, + uint8_t **addr, uint8_t *addr_type, + uint8_t **inita, uint8_t *inita_type, + int *ext_mode) +{ + uint8_t pdu_len; + uint8_t ext_hdr_len; + uint8_t ext_hdr_flags; + uint8_t *ext_hdr; + bool has_adva = false; + bool has_inita = false; + int i; + struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data; + + *addr = NULL; + *inita = NULL; + + pdu_len = rxbuf[1]; + if (pdu_len == 0) { + return -1; + } + + *ext_mode = rxbuf[2] >> 6; + if (*ext_mode > BLE_LL_EXT_ADV_MODE_SCAN) { + return -1; + } + + ext_hdr_len = rxbuf[2] & 0x3F; + if (ext_hdr_len == 0) { + goto done; + } + + ext_hdr_flags = rxbuf[3]; + ext_hdr = &rxbuf[4]; + + i = 0; + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + if (ext_hdr_len < BLE_LL_EXT_ADV_ADVA_SIZE) { + return -1; + } + + *addr = ext_hdr + i; + *addr_type = + ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); + i += BLE_LL_EXT_ADV_ADVA_SIZE; + + has_adva = true; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + *inita = ext_hdr + i; + *inita_type = + ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); + i += BLE_LL_EXT_ADV_TARGETA_SIZE; + + has_inita = true; + } + +done: + /* Check if we had address already. If yes, replace it with new one */ + + if (aux_data) { + /* If address has been provided, we do have it already in aux_data.*/ + if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { + if (!has_adva) { + *addr = aux_data->adva; + *addr_type = aux_data->adva_type; + } else { + memcpy(aux_data->adva, *addr, 6); + aux_data->adva_type = *addr_type; + } + } + + if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { + if (!has_inita) { + *inita = aux_data->targeta; + *inita_type = aux_data->targeta_type; + } else { + memcpy(aux_data->targeta, *inita, 6); + aux_data->targeta_type = *inita_type; + } + } + } + + return 0; +} +#endif + +int +ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *ble_hdr, + uint8_t **addr, uint8_t *addr_type, + uint8_t **inita, uint8_t *inita_type, + int *ext_mode) +{ + /* + * XXX this should be only used for legacy advertising, but need to refactor + * code in ble_ll_init first so it does not call this for ext + */ + + if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND && + pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) { + /* Legacy advertising */ + *addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); + *addr = rxbuf + BLE_LL_PDU_HDR_LEN; + + if (pdu_type != BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { + *inita = NULL; + *inita_type = 0; + return 0; + } + + *inita = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; + *inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); + + return 0; + } + + /* Extended advertising */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + return ble_ll_scan_get_addr_from_ext_adv(rxbuf, ble_hdr, addr, addr_type, + inita, inita_type, ext_mode); +#else + return -1; +#endif + + return 0; +} + +static void +ble_ll_scan_get_addr_data_from_legacy(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_ll_scan_addr_data *addrd) +{ + BLE_LL_ASSERT(pdu_type < BLE_ADV_PDU_TYPE_ADV_EXT_IND); + + addrd->adva_present = true; + + addrd->adva = rxbuf + BLE_LL_PDU_HDR_LEN; + addrd->adva_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); + + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { + addrd->targeta = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; + addrd->targeta_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); + } else { + addrd->targeta = NULL; + addrd->targeta_type = 0; + } +} + +/* + * Matches incoming PDU using scan filter policy and whitelist, if applicable. + * This will also resolve addresses and update flags/fields in header and + * addr_data as needed. + * + * @return 0 = no match + * 1 = match + * 2 = match, but do not scan + */ +static int +ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_ll_scan_params *scanp = scansm->scanp; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + struct ble_ll_resolv_entry *rl = NULL; +#endif + bool scan_req_allowed = true; + int resolved = 0; + + /* Use AdvA as initial advertiser address, we may try to resolve it later */ + addrd->adv_addr = addrd->adva; + addrd->adv_addr_type = addrd->adva_type; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* By default, assume AdvA is not resolved */ + rxinfo->rpa_index = -1; + + switch (ble_ll_addr_subtype(addrd->adva, addrd->adva_type)) { + case BLE_LL_ADDR_SUBTYPE_RPA: + /* + * Only resolve if packet actually contained AdvA. + * In extended advertising PDUs we may use RL index from a PDU that + * already had AdvA (e.g. ADV_EXT_IND in case of AUX_ADV_IND without + * AdvA). In legacy advertising PDUs we always need to resolve AdvA. + */ + if (addrd->adva_present) { + rxinfo->rpa_index = ble_hw_resolv_list_match(); + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + BLE_LL_ASSERT(aux_data); + rxinfo->rpa_index = aux_data->rpa_index; +#else + BLE_LL_ASSERT(false); + rxinfo->rpa_index = -1; +#endif + } + + if (rxinfo->rpa_index < 0) { + break; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (aux_data) { + aux_data->rpa_index = rxinfo->rpa_index; + } +#endif + + /* Use resolved identity address as advertiser address */ + rl = &g_ble_ll_resolv_list[rxinfo->rpa_index]; + addrd->adv_addr = rl->rl_identity_addr; + addrd->adv_addr_type = rl->rl_addr_type; + addrd->rl = rl; + + rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; + resolved = 1; + break; + case BLE_LL_ADDR_SUBTYPE_IDENTITY: + /* + * If AdvA is an identity address, we need to check if that device was + * added to RL in order to use proper privacy mode. + */ + rl = ble_ll_resolv_list_find(addrd->adva, addrd->adva_type); + if (!rl) { + break; + } + + addrd->rl = rl; + + /* Ignore device if using network privacy mode and it has IRK */ + if ((rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && rl->rl_has_peer) { + return 0; + } + break; + default: + /* NRPA goes through filtering policy directly */ + break; + } + + if (addrd->targeta) { + switch (ble_ll_addr_subtype(addrd->targeta, addrd->targeta_type)) { + case BLE_LL_ADDR_SUBTYPE_RPA: + /* Check if TargetA can be resolved using the same RL entry as AdvA */ + if (rl && ble_ll_resolv_rpa(addrd->targeta, rl->rl_local_irk)) { + rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED; + break; + } + + /* Check if scan filter policy allows unresolved RPAs to be processed */ + if (!(scanp->scan_filt_policy & 0x02)) { + return 0; + } + + /* + * We will notify host as requited by scan policy, but make sure we + * do not send scan request since we do not know if this is directed + * to us. + */ + scan_req_allowed = false; + break; + case BLE_LL_ADDR_SUBTYPE_IDENTITY: + /* We shall ignore identity in TargetA if we are using RPA */ + if ((scanp->own_addr_type & 0x02) && rl && rl->rl_has_local) { + return 0; + } + /* Ignore if not directed to us */ + if (!ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) { + return 0; + } + break; + default: + /* NRPA goes through filtering policy directly */ + break; + } + } +#else + /* Ignore if not directed to us */ + if (addrd->targeta && + !ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) { + return 0; + } +#endif + + /* Check on WL if required by scan filter policy */ + if (scanp->scan_filt_policy & 0x01) { + if (!ble_ll_whitelist_match(addrd->adv_addr, addrd->adv_addr_type, resolved)) { + return 0; + } + } + + return scan_req_allowed ? 1 : 2; +} + +static int +ble_ll_scan_rx_isr_on_legacy(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_ll_scan_params *scanp = scansm->scanp; + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + uint8_t sreq_adva_type; + uint8_t *sreq_adva; + int rc; + + ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); + + if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { + if (!BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { + /* + * We were not expecting scan response so just ignore and do not + * update backoff. + */ + return -1; + } + + sreq_adva_type = !!(scansm->pdu_data.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK); + sreq_adva = scansm->pdu_data.adva; + + /* + * Ignore scan response if AdvA does not match AdvA in request and also + * update backoff as if there was no scan response. + */ + if ((addrd->adva_type != sreq_adva_type) || + memcmp(addrd->adva, sreq_adva, BLE_DEV_ADDR_LEN)) { + ble_ll_scan_req_backoff(scansm, 0); + return -1; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* + * We are not pushing this one through filters so need to update + * rpa_index here as otherwise pkt_in won't be able to determine + * advertiser address properly. + */ + rxinfo->rpa_index = ble_hw_resolv_list_match(); + if (rxinfo->rpa_index >= 0) { + rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; + } +#endif + + rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; + + return 0; + } + + rc = ble_ll_scan_rx_filter(hdr, addrd); + if (!rc) { + return 0; + } + + rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; + + if (rc == 2) { + /* Scan request forbidden by filter policy */ + return 0; + } + + return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) && + ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || + (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND)); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +ble_ll_scan_rx_isr_on_aux(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_ll_scan_params *scanp = scansm->scanp; + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + struct ble_ll_aux_data *aux_data; + int rc; + + if (!scansm->ext_scanning) { + return -1; + } + + rc = ble_ll_scan_update_aux_data(hdr, rxbuf, &addrd->adva_present); + if (rc < 0) { + rxinfo->flags |= BLE_MBUF_HDR_F_AUX_INVALID; + return -1; + } else if (rc == 0) { + rxinfo->flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT; + } + + /* Now we can update aux_data from header since it may have just been created */ + aux_data = rxinfo->user_data; + + /* + * Restore proper header flags if filtering was already done successfully on + * some previous PDU in an event. + */ + if (aux_data->flags & BLE_LL_AUX_IS_MATCHED) { + rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; + rxinfo->rpa_index = aux_data->rpa_index; + if (rxinfo->rpa_index >= 0) { + rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; + } + if (aux_data->flags & BLE_LL_AUX_IS_TARGETA_RESOLVED) { + rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED; + } + goto done; + } + + if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { + addrd->adva = aux_data->adva; + addrd->adva_type = aux_data->adva_type; + } else { + /* Accept this PDU and wait for AdvA in aux */ + rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; + return 0; + } + if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { + addrd->targeta = aux_data->targeta; + addrd->targeta_type = aux_data->targeta_type; + } else { + addrd->targeta = NULL; + } + + rc = ble_ll_scan_rx_filter(hdr, addrd); + if (!rc) { + return 0; + } + + rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; + + /* + * Once we matched device, there's no need to go through filtering on every + * other PDU in an event so just store info required to restore state for + * subsequent PDUs in aux_data. + */ + aux_data->flags |= BLE_LL_AUX_IS_MATCHED; + if (rxinfo->flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) { + aux_data->flags |= BLE_LL_AUX_IS_TARGETA_RESOLVED; + /* AdvA state is already stored in rpa_index */ + } + + if (rc == 2) { + /* Scan request forbidden by filter policy */ + return 0; + } + +done: + return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) && + ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_SCAN); +} +#endif + +static bool +ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data = rxinfo->user_data; + uint8_t phy_mode; +#endif + bool is_ext_adv = false; + uint16_t adi = 0; + int rc; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) { + if (ble_ll_scan_get_adi(aux_data, &adi) < 0) { + return false; + } + is_ext_adv = true; + } +#endif + + /* Check if we already scanned this device successfully */ + if (ble_ll_scan_have_rxd_scan_rsp(addrd->adv_addr, addrd->adv_addr_type, + is_ext_adv, adi)) { + return false; + } + + /* Better not be a scan response pending */ + BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); + + /* We want to send a request. See if backoff allows us */ + if (scansm->backoff_count > 0) { + if (--scansm->backoff_count != 0) { + return false; + } + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + phy_mode = ble_ll_phy_to_phy_mode(rxinfo->phy, BLE_HCI_LE_PHY_CODED_ANY); + if (ble_ll_sched_scan_req_over_aux_ptr(rxinfo->channel, phy_mode)) { + return false; + } +#endif + + /* Use original AdvA in scan request (Core 5.1, Vol 6, Part B, section 6.3) */ + ble_ll_scan_req_pdu_prepare(scansm, addrd->adva, addrd->adva_type, addrd->rl); + + rc = ble_phy_tx(ble_ll_scan_req_tx_pdu_cb, scansm, BLE_PHY_TRANSITION_TX_RX); + if (rc) { + return false; + } + + scansm->scan_rsp_pending = 1; + rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_REQ_TXD; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (rxinfo->channel < BLE_PHY_NUM_DATA_CHANS) { + /* Keep aux_data for expected scan response */ + scansm->cur_aux_data = ble_ll_scan_aux_data_ref(aux_data); + STATS_INC(ble_ll_stats, aux_scan_req_tx); + } +#endif + + return true; +} + +/** + * Called when a receive PDU has ended. + * + * Context: Interrupt + * + * @param rxpdu + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_mbuf_hdr *hdr = BLE_MBUF_HDR_PTR(rxpdu); + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + uint8_t *rxbuf; + uint8_t pdu_type; + struct ble_ll_scan_addr_data addrd; + int rc; + + /* + * If buffer for incoming PDU was not allocated we need to force scan to be + * restarted since LL will not be notified. Keep PHY enabled. + */ + if (rxpdu == NULL) { + ble_ll_scan_interrupted(scansm); + return 0; + } + + rxbuf = rxpdu->om_data; + pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* + * In case aux was expected, copy aux_data for LL to use. Make sure this was + * indeed an aux as otherwise there's no need to process it and just pass to + * LL immediately. + */ + if (scansm->cur_aux_data) { + rxinfo->user_data = scansm->cur_aux_data; + scansm->cur_aux_data = NULL; + if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + return -1; + } + } +#endif + + if (!crcok) { + goto scan_rx_isr_ignore; + } + + /* + * Addresses will be always set in handlers, no need to initialize them. We + * only need to initialize rl which may not be always set, depending on how + * filtering goes. + */ + addrd.rl = NULL; + + switch (pdu_type) { + case BLE_ADV_PDU_TYPE_ADV_IND: + case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: + case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND: + case BLE_ADV_PDU_TYPE_SCAN_RSP: + case BLE_ADV_PDU_TYPE_ADV_SCAN_IND: + rc = ble_ll_scan_rx_isr_on_legacy(pdu_type, rxbuf, hdr, &addrd); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_ADV_PDU_TYPE_ADV_EXT_IND: + rc = ble_ll_scan_rx_isr_on_aux(pdu_type, rxbuf, hdr, &addrd); + break; +#endif + default: + /* This is not something we would like to process here */ + rc = -1; + break; + } + + if (rc == -1) { + goto scan_rx_isr_ignore; + } else if (rc == 1) { + if (ble_ll_scan_send_scan_req(pdu_type, rxbuf, hdr, &addrd)) { + /* Keep PHY active and LL in scanning state */ + return 0; + } + } + + /* We are done with this PDU so go to standby and let LL resume if needed */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + return -1; + +scan_rx_isr_ignore: + rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; + ble_ll_state_set(BLE_LL_STATE_STANDBY); + return -1; +} + +/** + * Called to resume scanning. This is called after an advertising event or + * connection event has ended. It is also called if we receive a packet while + * in the initiating or scanning state. + * + * If periodic advertising is enabled this is also called on sync event end + * or sync packet received if chaining + * + * Context: Link Layer task + */ +void +ble_ll_scan_chk_resume(void) +{ + os_sr_t sr; + struct ble_ll_scan_sm *scansm; + uint32_t now; + + scansm = &g_ble_ll_scan_sm; + if (scansm->scan_enabled) { + OS_ENTER_CRITICAL(sr); + if (scansm->restart_timer_needed) { + scansm->restart_timer_needed = 0; + ble_ll_event_send(&scansm->scan_sched_ev); + STATS_INC(ble_ll_stats, scan_timer_restarted); + OS_EXIT_CRITICAL(sr); + return; + } + + now = os_cputime_get32(); + if (ble_ll_state_get() == BLE_LL_STATE_STANDBY && + ble_ll_scan_is_inside_window(scansm->scanp, now)) { + /* Turn on the receiver and set state */ + ble_ll_scan_start(scansm, NULL); + } + OS_EXIT_CRITICAL(sr); + } +} + +/** + * Scan timer callback; means that the scan window timeout has been reached + * and we should perform the appropriate actions. + * + * Context: Interrupt (cputimer) + * + * @param arg Pointer to scan state machine. + */ +void +ble_ll_scan_timer_cb(void *arg) +{ + struct ble_ll_scan_sm *scansm; + + scansm = (struct ble_ll_scan_sm *)arg; + ble_ll_event_send(&scansm->scan_sched_ev); +} + +void +ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_npl_event_set_arg(&scansm->scan_interrupted_ev, scansm->cur_aux_data); + scansm->cur_aux_data = NULL; +#endif + + ble_ll_event_send(&scansm->scan_interrupted_ev); +} + +/** + * Called when the wait for response timer expires while in the scanning + * state. + * + * Context: Interrupt. + */ +void +ble_ll_scan_wfr_timer_exp(void) +{ + struct ble_ll_scan_sm *scansm; + uint8_t chan; + int phy; + int rc; +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + uint8_t phy_mode; +#endif + uint32_t now; + + scansm = &g_ble_ll_scan_sm; + + /* Update backoff if we failed to receive scan response */ + if (scansm->scan_rsp_pending) { + scansm->scan_rsp_pending = 0; + ble_ll_scan_req_backoff(scansm, 0); + } + + if (scansm->cur_aux_data) { + /* We actually care about interrupted scan only for EXT ADV because only + * then we might consider to send truncated event to the host. + */ + ble_ll_scan_interrupted(scansm); + + /* Need to disable phy since we are going to move to BLE_LL_STATE_STANDBY + * or we will need to change channel to primary one + */ + ble_phy_disable(); + + now = os_cputime_get32(); + if (!ble_ll_scan_is_inside_window(scansm->scanp, now)) { + /* Outside the window scan */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + return; + } + + ble_ll_get_chan_to_scan(scansm, &chan, &phy); +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY); + ble_phy_mode_set(phy_mode, phy_mode); +#endif + rc = ble_phy_setchan(chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); + BLE_LL_ASSERT(rc == 0); + } + + + ble_phy_restart_rx(); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/* + * Send extended advertising report + * + * @return -1 on error (data truncated or other error) + * 0 on success (data status is "completed") + * 1 on success (data status is not "completed") + */ +static int +ble_ll_hci_send_ext_adv_report(uint8_t ptype, uint8_t *adva, uint8_t adva_type, + uint8_t *inita, uint8_t inita_type, + struct os_mbuf *om, + struct ble_mbuf_hdr *hdr) +{ + struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data; + struct ble_hci_ev_le_subev_ext_adv_rpt *ev; + struct ext_adv_report *report; + struct ble_hci_ev *hci_ev; + struct ble_hci_ev *hci_ev_next; + int offset; + int datalen; + int rc; + bool need_event; + bool is_scannable_aux; + uint8_t max_data_len; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { + rc = -1; + goto done; + } + + /* + * We keep one allocated event in aux_data to be able to truncate chain + * properly in case of error. If there is no event in aux_data it means this + * is the first event for this chain. + */ + if (aux_data && aux_data->evt) { + hci_ev = aux_data->evt; + aux_data->evt = NULL; + + hci_ev->length = sizeof(*ev) + sizeof(*report); + } else { + hci_ev = ble_ll_scan_get_ext_adv_report(NULL); + if (!hci_ev) { + rc = -1; + goto done; + } + } + + ev = (void *) hci_ev->data; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* If RPA has been used, make sure we use correct address types + * in the advertising report. + */ + if (BLE_MBUF_HDR_RESOLVED(hdr)) { + adva_type += 2; + } + if (BLE_MBUF_HDR_TARGETA_RESOLVED(hdr)) { + inita_type += 2; + } +#endif + + datalen = ble_ll_scan_parse_ext_hdr(om, adva, adva_type, inita, inita_type, + hdr, ev->reports); + if (datalen < 0) { + rc = -1; + + /* Need to send truncated event if we already sent some reports */ + if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { + BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED)); + BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); + + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; + + report = ev->reports; + report->data_len = 0; + report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; + + ble_ll_hci_event_send(hci_ev); + goto done; + } + + ble_hci_trans_buf_free((uint8_t *)hci_ev); + goto done; + } + + is_scannable_aux = aux_data && + (aux_data->evt_type & BLE_HCI_ADV_SCAN_MASK) && + !(aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK); + + max_data_len = BLE_LL_MAX_EVT_LEN - sizeof(*hci_ev) - sizeof(*ev) - sizeof(*report); + offset = 0; + + do { + hci_ev_next = NULL; + + ev = (void *) hci_ev->data; + report = ev->reports; + + report->data_len = min(max_data_len, datalen - offset); + + /* adjust event length */ + hci_ev->length += report->data_len; + report->rssi = hdr->rxinfo.rssi; + + os_mbuf_copydata(om, offset, report->data_len, report->data); + offset += report->data_len; + + /* + * We need another event if either there are still some data left to + * send in current PDU or scan is not completed. The only exception is + * when this is a scannable event which is not a scan response. + */ + need_event = ((offset < datalen) || (aux_data && !(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_COMPLETE))) && !is_scannable_aux; + + if (need_event) { + /* + * We will need another event so let's try to allocate one now. If + * we cannot do this, need to mark event as truncated. + */ + hci_ev_next = ble_ll_scan_get_ext_adv_report(report); + + if (hci_ev_next) { + report->evt_type |= BLE_HCI_ADV_DATA_STATUS_INCOMPLETE; + rc = 1; + } else { + report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; + rc = -1; + } + } else if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR)) { + report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; + rc = -1; + } else { + rc = 0; + } + + if ((rc == -1) && aux_data) { + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + + if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { + ble_hci_trans_buf_free((uint8_t *)hci_ev); + goto done; + } + + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; + } else if (!is_scannable_aux) { + /* + * We do not set 'sent' flags for scannable AUX since we only care + * about scan response that will come next. + */ + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY; + if (rc == 0) { + aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED; + } + } + + ble_ll_hci_event_send(hci_ev); + + hci_ev = hci_ev_next; + } while ((offset < datalen) && hci_ev); + + BLE_LL_ASSERT(offset <= datalen); + + if (aux_data) { + /* Store any event left for later use */ + aux_data->evt = hci_ev; + } else { + /* If it is empty beacon, evt shall be NULL */ + BLE_LL_ASSERT(!hci_ev); + } + +done: + if (!aux_data) { + return rc; + } + + if (rc == 0) { + if (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) { + /* Complete scan response can be added to duplicates list */ + ble_ll_scan_add_scan_rsp_adv(aux_data->adva, aux_data->adva_type, + 1, aux_data->adi); + } else if (is_scannable_aux) { + /* + * Scannable AUX is marked as incomplete because we do not want to + * add this to duplicates list now, this should happen only after + * we receive complete scan response. The drawback here is that we + * will keep receiving reports for scannable PDUs until complete + * scan response is received. + * + * XXX ^^ extend duplicates list to fix + */ + rc = 1; + } + } else if (rc < 0) { + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + } + + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +static void +ble_ll_scan_check_periodic_sync(const struct os_mbuf *om, struct ble_mbuf_hdr *rxhdr, + uint8_t *adva, uint8_t adva_type, int rpa_index) +{ + uint8_t pdu_len; + uint8_t ext_hdr_len; + uint8_t ext_hdr_flags; + uint8_t *ext_hdr; + uint8_t *rxbuf = om->om_data; + uint8_t sid; + int i; + + pdu_len = rxbuf[1]; + if (pdu_len == 0) { + return; + } + + ext_hdr_len = rxbuf[2] & 0x3F; + + if (ext_hdr_len) { + ext_hdr_flags = rxbuf[3]; + ext_hdr = &rxbuf[4]; + i = 0; + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + i += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + i += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { + i += 1; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { + sid = (get_le16(ext_hdr + i) >> 12); + i += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } else { + /* ADI is mandatory */ + return; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + i += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { + ble_ll_sync_info_event(adva, adva_type, rpa_index, sid, rxhdr, + ext_hdr + i); + } + } +} +#endif + +static inline void +ble_ll_scan_dup_move_to_head(struct ble_ll_scan_dup_entry *e) +{ + if (e != TAILQ_FIRST(&g_scan_dup_list)) { + TAILQ_REMOVE(&g_scan_dup_list, e, link); + TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); + } +} + +static inline struct ble_ll_scan_dup_entry * +ble_ll_scan_dup_new(void) +{ + struct ble_ll_scan_dup_entry *e; + + e = os_memblock_get(&g_scan_dup_pool); + if (!e) { + e = TAILQ_LAST(&g_scan_dup_list, ble_ll_scan_dup_list); + TAILQ_REMOVE(&g_scan_dup_list, e, link); + } + + memset(e, 0, sizeof(*e)); + + return e; +} + +static int +ble_ll_scan_dup_check_legacy(uint8_t addr_type, uint8_t *addr, uint8_t pdu_type) +{ + struct ble_ll_scan_dup_entry *e; + uint8_t type; + int rc; + + type = BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type); + + TAILQ_FOREACH(e, &g_scan_dup_list, link) { + if ((e->type == type) && !memcmp(e->addr, addr, 6)) { + break; + } + } + + if (e) { + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { + rc = e->flags & BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT; + } else if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { + rc = e->flags & BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT; + } else { + rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; + } + + ble_ll_scan_dup_move_to_head(e); + } else { + rc = 0; + + e = ble_ll_scan_dup_new(); + e->flags = 0; + e->type = type; + memcpy(e->addr, addr, 6); + + TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); + } + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr, + struct ble_ll_aux_data *aux_data) +{ + struct ble_ll_scan_dup_entry *e; + bool has_aux; + bool is_anon; + uint16_t adi; + uint8_t type; + int rc; + + has_aux = aux_data != NULL; + is_anon = addr == NULL; + adi = has_aux ? aux_data->adi : 0; + + type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi); + + TAILQ_FOREACH(e, &g_scan_dup_list, link) { + if ((e->type == type) && + (is_anon || !memcmp(e->addr, addr, BLE_DEV_ADDR_LEN))) { + break; + } + } + + if (e) { + if (e->adi != adi) { + rc = 0; + + e->flags = 0; + e->adi = adi; + } else { + rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; + } + + ble_ll_scan_dup_move_to_head(e); + } else { + rc = 0; + + e = ble_ll_scan_dup_new(); + e->flags = 0; + e->type = type; + e->adi = adi; + if (!is_anon) { + memcpy(e->addr, addr, 6); + } + + TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); + } + + return rc; +} + +static int +ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr, + struct ble_ll_aux_data *aux_data) +{ + struct ble_ll_scan_dup_entry *e; + bool has_aux; + bool is_anon; + uint16_t adi; + uint8_t type; + + has_aux = aux_data != NULL; + is_anon = addr == NULL; + adi = has_aux ? aux_data->adi : 0; + + type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi); + + /* + * We assume ble_ll_scan_dup_check() was called before which either matched + * some entry or allocated new one and placed in on the top of queue. + */ + + e = TAILQ_FIRST(&g_scan_dup_list); + BLE_LL_ASSERT(e && e->type == type && (is_anon || !memcmp(e->addr, addr, 6))); + + e->flags |= BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; + + return 0; +} +#endif + +static void +ble_ll_scan_rx_pkt_in_restore_addr_data(struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + struct ble_ll_resolv_entry *rl; +#endif + + addrd->adv_addr = addrd->adva; + addrd->adv_addr_type = addrd->adva_type; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (rxinfo->rpa_index >= 0) { + rl = &g_ble_ll_resolv_list[rxinfo->rpa_index]; + addrd->adv_addr = rl->rl_identity_addr; + addrd->adv_addr_type = rl->rl_addr_type; + addrd->rl = rl; + } + if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) { + addrd->targeta = ble_ll_get_our_devaddr(scansm->own_addr_type & 1); + addrd->targeta_type = scansm->own_addr_type & 1; + } +#endif +} + +static void +ble_ll_scan_rx_pkt_in_on_legacy(uint8_t pdu_type, struct os_mbuf *om, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; + uint8_t *rxbuf = om->om_data; + bool send_hci_report; + + + if (!BLE_MBUF_HDR_DEVMATCH(hdr) || + !BLE_MBUF_HDR_CRC_OK(hdr) || + BLE_MBUF_HDR_IGNORED(hdr)) { + return; + } + + ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); + ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, addrd); + + send_hci_report = !scansm->scan_filt_dups || + !ble_ll_scan_dup_check_legacy(addrd->adv_addr_type, + addrd->adv_addr, + pdu_type); + if (send_hci_report) { + /* Sending advertising report will also update scan_dup list */ + ble_ll_scan_send_adv_report(pdu_type, + addrd->adv_addr, addrd->adv_addr_type, + addrd->targeta, addrd->targeta_type, + om, hdr, scansm); + } + + if (BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { + ble_ll_scan_req_backoff(scansm, 1); + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_scan_rx_pkt_in_on_aux(uint8_t pdu_type, struct os_mbuf *om, + struct ble_mbuf_hdr *hdr, + struct ble_ll_scan_addr_data *addrd) +{ + struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + uint8_t *rxbuf = om->om_data; +#endif + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + struct ble_ll_aux_data *aux_data = rxinfo->user_data; + bool send_hci_report; + int rc; + + if (!scansm->ext_scanning) { + goto scan_continue; + } + + if (aux_data) { + aux_data->flags_ll |= aux_data->flags_isr; + } + + /* + * For every new extended advertising event scanned, rx_isr_end will either + * allocate new aux_data or set 'invalid' flag. This means if no 'invalid' + * flag is set, aux_data is always valid. + */ + + /* Drop on scan error or if we received not what we expected to receive */ + if (!BLE_MBUF_HDR_CRC_OK(hdr) || + BLE_MBUF_HDR_IGNORED(hdr) || + BLE_MBUF_HDR_AUX_INVALID(hdr) || + (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) || + (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND)) { + if (aux_data) { + ble_ll_scan_end_adv_evt(aux_data); + ble_ll_scan_aux_data_unref(aux_data); + rxinfo->user_data = NULL; + } + return; + } + + BLE_LL_ASSERT(aux_data); + + if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { + addrd->adva = aux_data->adva; + addrd->adva_type = aux_data->adva_type; + } else { + addrd->adva = NULL; + addrd->adva_type = 0; + } + if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { + addrd->targeta = aux_data->targeta; + addrd->targeta_type = aux_data->targeta_type; + } else { + addrd->targeta = NULL; + addrd->targeta_type = 0; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + /* + * Periodic scan uses own filter list so we need to let it do own filtering + * regardless of scanner filtering. Just make sure we already have AdvA. + */ + if (ble_ll_sync_enabled() && + ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_NON_CONN) && addrd->adva && + !(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED)) { + ble_ll_scan_check_periodic_sync(om, hdr, addrd->adva, addrd->adva_type, + rxinfo->rpa_index); + } +#endif + + /* Ignore if device was not matched by either whitelist or scan policy */ + if (!BLE_MBUF_HDR_DEVMATCH(hdr)) { + goto scan_continue; + } + + ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, addrd); + + /* + * If there is AuxPtr in this PDU, we should first try to schedule scan for + * subsequent aux. + */ + if (BLE_MBUF_HDR_WAIT_AUX(hdr)) { + if (ble_ll_sched_aux_scan(hdr, scansm, aux_data)) { + rxinfo->flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT; + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + + /* Silently ignore if no HCI event was sent to host */ + if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { + goto scan_continue; + } + } + + /* Ignore if this was just ADV_EXT_IND with AuxPtr, will process aux */ + if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED)) { + goto scan_continue; + } + + STATS_INC(ble_ll_stats, aux_chain_cnt); + } + + send_hci_report = !scansm->scan_filt_dups || + !ble_ll_scan_dup_check_ext(addrd->adv_addr_type, + addrd->adv_addr, aux_data); + if (send_hci_report) { + rc = ble_ll_hci_send_ext_adv_report(pdu_type, + addrd->adv_addr, addrd->adv_addr_type, + addrd->targeta, addrd->targeta_type, + om, hdr); + if ((rc < 0) && BLE_MBUF_HDR_WAIT_AUX(hdr)) { + /* Data were truncated so stop scanning for subsequent auxes */ + aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; + + if (ble_ll_sched_rmv_elem(&aux_data->sch) == 0) { + ble_ll_scan_aux_data_unref(aux_data->sch.cb_arg); + aux_data->sch.cb_arg = NULL; + } + } else if ((rc == 0) && scansm->scan_filt_dups) { + /* Complete data were send so we can update scan_dup list */ + ble_ll_scan_dup_update_ext(addrd->adv_addr_type, addrd->adv_addr, + aux_data); + } + } + + if (BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { + /* + * For now assume success if we just received direct scan response, + * don't care about complete aux chain. + */ + ble_ll_scan_req_backoff(scansm, 1); + } + +scan_continue: + ble_ll_scan_aux_data_unref(rxinfo->user_data); + rxinfo->user_data = NULL; +} +#endif + +/** + * Process a received PDU while in the scanning state. + * + * Context: Link Layer task. + * + * @param pdu_type + * @param rxbuf + */ +void +ble_ll_scan_rx_pkt_in(uint8_t ptype, struct os_mbuf *om, struct ble_mbuf_hdr *hdr) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; + struct ble_ll_aux_data *aux_data = rxinfo->user_data; +#endif + struct ble_ll_scan_addr_data addrd; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (aux_data || (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND)) { + ble_ll_scan_rx_pkt_in_on_aux(ptype, om, hdr, &addrd); + ble_ll_scan_chk_resume(); + return; + } +#endif + + ble_ll_scan_rx_pkt_in_on_legacy(ptype, om, hdr, &addrd); + ble_ll_scan_chk_resume(); +} + +int +ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_scan_params_cp *cmd = (const void *)cmdbuf; + uint16_t scan_itvl; + uint16_t scan_window; + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* If already enabled, we return an error */ + scansm = &g_ble_ll_scan_sm; + if (scansm->scan_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Get the scan interval and window */ + scan_itvl = le16toh(cmd->scan_itvl); + scan_window = le16toh(cmd->scan_window); + + /* Check scan type */ + if ((cmd->scan_type != BLE_HCI_SCAN_TYPE_PASSIVE) && + (cmd->scan_type != BLE_HCI_SCAN_TYPE_ACTIVE)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check interval and window */ + if ((scan_itvl < BLE_HCI_SCAN_ITVL_MIN) || + (scan_itvl > BLE_HCI_SCAN_ITVL_MAX) || + (scan_window < BLE_HCI_SCAN_WINDOW_MIN) || + (scan_window > BLE_HCI_SCAN_WINDOW_MAX) || + (scan_itvl < scan_window)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check own addr type */ + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check scanner filter policy */ + if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Store scan parameters */ + scanp = &g_ble_ll_scan_params[PHY_UNCODED]; + scanp->configured = 1; + scanp->scan_type = cmd->scan_type; + scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(scan_itvl); + scanp->timing.window = ble_ll_scan_time_hci_to_ticks(scan_window); + scanp->scan_filt_policy = cmd->filter_policy; + scanp->own_addr_type = cmd->own_addr_type; + +#if (BLE_LL_SCAN_PHY_NUMBER == 2) + g_ble_ll_scan_params[PHY_CODED].configured = 0; +#endif + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static int +ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window) +{ + /* Check scan type */ + if ((type != BLE_HCI_SCAN_TYPE_PASSIVE) && + (type != BLE_HCI_SCAN_TYPE_ACTIVE)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check interval and window */ + if ((itvl < BLE_HCI_SCAN_ITVL_MIN) || + (itvl > BLE_HCI_SCAN_ITVL_MAX) || + (window < BLE_HCI_SCAN_WINDOW_MIN) || + (window > BLE_HCI_SCAN_WINDOW_MAX) || + (itvl < window)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return 0; +} + +int +ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_ext_scan_params_cp *cmd = (const void *) cmdbuf; + const struct scan_params *params = cmd->scans; + + struct ble_ll_scan_params new_params[BLE_LL_SCAN_PHY_NUMBER] = { }; + struct ble_ll_scan_params *uncoded = &new_params[PHY_UNCODED]; + struct ble_ll_scan_params *coded = &new_params[PHY_CODED]; + uint16_t interval; + uint16_t window; + int rc; + + if (len <= sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + len -= sizeof(*cmd); + + /* If already enabled, we return an error */ + if (g_ble_ll_scan_sm.scan_enabled) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Check own addr type */ + if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + coded->own_addr_type = cmd->own_addr_type; + uncoded->own_addr_type = cmd->own_addr_type; + + /* Check scanner filter policy */ + if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + coded->scan_filt_policy = cmd->filter_policy; + uncoded->scan_filt_policy = cmd->filter_policy; + + /* Check if no reserved bits in PHYS are set and that at least one valid PHY + * is set. + */ + if (!(cmd->phys & SCAN_VALID_PHY_MASK) || + (cmd->phys & ~SCAN_VALID_PHY_MASK)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->phys & BLE_HCI_LE_PHY_1M_PREF_MASK) { + if (len < sizeof(*params)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + interval = le16toh(params->itvl); + window = le16toh(params->window); + + rc = ble_ll_check_scan_params(params->type, interval, window); + if (rc) { + return rc; + } + + uncoded->scan_type = params->type; + uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(interval); + uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(window); + + /* That means user wants to use this PHY for scanning */ + uncoded->configured = 1; + params++; + len -= sizeof(*params); + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + if (cmd->phys & BLE_HCI_LE_PHY_CODED_PREF_MASK) { + if (len < sizeof(*params)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + interval = le16toh(params->itvl); + window = le16toh(params->window); + + rc = ble_ll_check_scan_params(params->type, interval, window); + if (rc) { + return rc; + } + + coded->scan_type = params->type; + coded->timing.interval = ble_ll_scan_time_hci_to_ticks(interval); + coded->timing.window = ble_ll_scan_time_hci_to_ticks(window); + + /* That means user wants to use this PHY for scanning */ + coded->configured = 1; + } +#endif + + /* if any of PHYs is configured for continuous scan we alter interval to + * fit other PHY + */ + if (coded->configured && uncoded->configured) { + if (coded->timing.interval == coded->timing.window) { + coded->timing.interval += uncoded->timing.window; + } + + if (uncoded->timing.interval == uncoded->timing.window) { + uncoded->timing.window += coded->timing.window; + } + } + + memcpy(g_ble_ll_scan_params, new_params, sizeof(new_params)); + + return 0; +} + +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_scan_duration_period_timers_restart(struct ble_ll_scan_sm *scansm) +{ + uint32_t now; + + now = os_cputime_get32(); + + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); + + if (scansm->duration_ticks) { + os_cputime_timer_start(&scansm->duration_timer, + now + scansm->duration_ticks); + + if (scansm->period_ticks) { + os_cputime_timer_start(&scansm->period_timer, + now + scansm->period_ticks); + } + } +} + +static void +ble_ll_scan_duration_timer_cb(void *arg) +{ + struct ble_ll_scan_sm *scansm; + + scansm = (struct ble_ll_scan_sm *)arg; + + ble_ll_scan_sm_stop(2); + + /* if period is set both timers get started from period cb */ + if (!scansm->period_ticks) { + ble_ll_hci_ev_send_scan_timeout(); + } +} + +static void +ble_ll_scan_period_timer_cb(void *arg) +{ + struct ble_ll_scan_sm *scansm = arg; + + ble_ll_scan_sm_start(scansm); + + /* always start timer regardless of ble_ll_scan_sm_start result + * if it failed will restart in next period + */ + ble_ll_scan_duration_period_timers_restart(scansm); +} +#endif + +/** + * ble ll scan set enable + * + * HCI scan set enable command processing function + * + * Context: Link Layer task (HCI Command parser). + * + * @return int BLE error code. + */ +static int +ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period, + uint16_t dur, bool ext) +{ + int rc; + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + struct ble_ll_scan_params *scanp_phy; + int i; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + uint32_t period_ticks = 0; + uint32_t dur_ticks = 0; +#endif + + /* Check for valid parameters */ + if ((filter_dups > 1) || (enable > 1)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + scansm = &g_ble_ll_scan_sm; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* we can do that here since value will never change until reset */ + scansm->ext_scanning = ext; + + if (ext) { + /* Period parameter is ignored when the Duration parameter is zero */ + if (!dur) { + period = 0; + } + + /* period is in 1.28 sec units + * TODO support full range, would require os_cputime milliseconds API + */ + if (period > 3355) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + period_ticks = os_cputime_usecs_to_ticks(period * 1280000); + + /* duration is in 10ms units */ + dur_ticks = os_cputime_usecs_to_ticks(dur * 10000); + + if (dur_ticks && period_ticks && (dur_ticks >= period_ticks)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } +#endif + + /* disable*/ + if (!enable) { + if (scansm->scan_enabled) { + ble_ll_scan_sm_stop(1); + } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); +#endif + + return BLE_ERR_SUCCESS; + } + + /* if already enable we just need to update parameters */ + if (scansm->scan_enabled) { + /* Controller does not allow initiating and scanning.*/ + for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { + scanp_phy = &scansm->scanp_phys[i]; + if (scanp_phy->configured && + scanp_phy->scan_type == BLE_SCAN_TYPE_INITIATE) { + return BLE_ERR_CMD_DISALLOWED; + } + } + +#if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS) + /* update filter policy */ + scansm->scan_filt_dups = filter_dups; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* restart timers according to new settings */ + scansm->duration_ticks = dur_ticks; + scansm->period_ticks = period_ticks; + ble_ll_scan_duration_period_timers_restart(scansm); +#endif + + return BLE_ERR_SUCCESS; + } + + /* we can store those upfront regardless of start scan result since scan is + * disabled now + */ + +#if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS) + scansm->scan_filt_dups = filter_dups; +#endif + scansm->scanp = NULL; + scansm->scanp_next = NULL; + + for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { + scanp_phy = &scansm->scanp_phys[i]; + scanp = &g_ble_ll_scan_params[i]; + + if (!scanp->configured) { + continue; + } + + scanp_phy->configured = scanp->configured; + scanp_phy->scan_type = scanp->scan_type; + scanp_phy->timing = scanp->timing; + scanp_phy->scan_filt_policy = scanp->scan_filt_policy; + scanp_phy->own_addr_type = scanp->own_addr_type; + + if (!scansm->scanp) { + scansm->scanp = scanp_phy; + /* Take own_addr_type from the first configured PHY. + * Note: All configured PHYs shall have the same own_addr_type + */ + scansm->own_addr_type = scanp_phy->own_addr_type; + } else { + scansm->scanp_next = scanp_phy; + } + } + + /* spec is not really clear if we should use defaults in this case + * or just disallow starting scan without explicit configuration + * For now be nice to host and just use values based on LE Set Scan + * Parameters defaults. + */ + if (!scansm->scanp) { + scansm->scanp = &scansm->scanp_phys[PHY_UNCODED]; + scansm->own_addr_type = BLE_ADDR_PUBLIC; + + scanp_phy = scansm->scanp; + scanp_phy->configured = 1; + scanp_phy->scan_type = BLE_SCAN_TYPE_PASSIVE; + scanp_phy->timing.interval = + ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF); + scanp_phy->timing.window = + ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF); + scanp_phy->scan_filt_policy = BLE_HCI_SCAN_FILT_NO_WL; + scanp_phy->own_addr_type = BLE_ADDR_PUBLIC; + } + + rc = ble_ll_scan_sm_start(scansm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (rc == BLE_ERR_SUCCESS) { + scansm->duration_ticks = dur_ticks; + scansm->period_ticks = period_ticks; + ble_ll_scan_duration_period_timers_restart(scansm); + } +#endif + + return rc; +} + +int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_scan_enable_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_scan_set_enable(cmd->enable, cmd->filter_duplicates, 0, 0, + false); +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +int ble_ll_hci_ext_scan_set_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_set_ext_scan_enable_cp *cmd = (const void *) cmdbuf; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return ble_ll_scan_set_enable(cmd->enable, cmd->filter_dup, + le16toh(cmd->period), le16toh(cmd->duration), + true); +} +#endif + +/** + * Checks if controller can change the whitelist. If scanning is enabled and + * using the whitelist the controller is not allowed to change the whitelist. + * + * @return int 0: not allowed to change whitelist; 1: change allowed. + */ +int +ble_ll_scan_can_chg_whitelist(void) +{ + int rc; + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + + scansm = &g_ble_ll_scan_sm; + scanp = scansm->scanp; + if (scansm->scan_enabled && (scanp->scan_filt_policy & 1)) { + rc = 0; + } else { + rc = 1; + } + + return rc; +} + +int +ble_ll_scan_initiator_start(struct hci_create_conn *hcc, + struct ble_ll_scan_sm **sm) +{ + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + int rc; + + scansm = &g_ble_ll_scan_sm; + scansm->own_addr_type = hcc->own_addr_type; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + scansm->ext_scanning = 0; +#endif + scansm->scanp = &scansm->scanp_phys[PHY_UNCODED]; + scansm->scanp_next = NULL; + + scanp = scansm->scanp; + scanp->scan_filt_policy = hcc->filter_policy; + scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(hcc->scan_itvl); + scanp->timing.window = ble_ll_scan_time_hci_to_ticks(hcc->scan_window); + scanp->scan_type = BLE_SCAN_TYPE_INITIATE; + + rc = ble_ll_scan_sm_start(scansm); + if (sm == NULL) { + return rc; + } + + if (rc == BLE_ERR_SUCCESS) { + *sm = scansm; + } else { + *sm = NULL; + } + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +int +ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc, + struct ble_ll_scan_sm **sm) +{ + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp_uncoded; + struct ble_ll_scan_params *scanp_coded; + struct hci_ext_conn_params *params; + int rc; + + scansm = &g_ble_ll_scan_sm; + scansm->own_addr_type = hcc->own_addr_type; + scansm->scanp = NULL; + scansm->scanp_next = NULL; + scansm->ext_scanning = 1; + + if (hcc->init_phy_mask & BLE_PHY_MASK_1M) { + params = &hcc->params[0]; + scanp_uncoded = &scansm->scanp_phys[PHY_UNCODED]; + + scanp_uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl); + scanp_uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window); + scanp_uncoded->scan_type = BLE_SCAN_TYPE_INITIATE; + scanp_uncoded->scan_filt_policy = hcc->filter_policy; + scansm->scanp = scanp_uncoded; + } + + if (hcc->init_phy_mask & BLE_PHY_MASK_CODED) { + params = &hcc->params[2]; + scanp_coded = &scansm->scanp_phys[PHY_CODED]; + + scanp_coded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl); + scanp_coded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window); + scanp_coded->scan_type = BLE_SCAN_TYPE_INITIATE; + scanp_coded->scan_filt_policy = hcc->filter_policy; + if (scansm->scanp) { + scansm->scanp_next = scanp_coded; + } else { + scansm->scanp = scanp_coded; + } + } + + /* if any of PHYs is configured for continuous scan we alter interval to + * fit other PHY + */ + if (scansm->scanp && scansm->scanp_next && scanp_coded->configured && + scanp_uncoded->configured) { + if (scanp_coded->timing.interval == scanp_coded->timing.window) { + scanp_coded->timing.interval += scanp_uncoded->timing.window; + } + + if (scanp_uncoded->timing.interval == scanp_uncoded->timing.window) { + scanp_uncoded->timing.interval += scanp_coded->timing.window; + } + } + + rc = ble_ll_scan_sm_start(scansm); + if (sm == NULL) { + return rc; + } + + if (rc == BLE_ERR_SUCCESS) { + *sm = scansm; + } else { + *sm = NULL; + } + + return rc; +} +#endif + +/** + * Checks to see if the scanner is enabled. + * + * @return int 0: not enabled; enabled otherwise + */ +int +ble_ll_scan_enabled(void) +{ + return (int)g_ble_ll_scan_sm.scan_enabled; +} + +/** + * Returns the peer resolvable private address of last device connecting to us + * + * @return uint8_t* + */ +uint8_t * +ble_ll_scan_get_peer_rpa(void) +{ + struct ble_ll_scan_sm *scansm; + + /* XXX: should this go into IRK list or connection? */ + scansm = &g_ble_ll_scan_sm; + return scansm->scan_peer_rpa; +} + +/** + * Returns the local resolvable private address currently being using by + * the scanner/initiator + * + * @return uint8_t* + */ +uint8_t * +ble_ll_scan_get_local_rpa(void) +{ + return g_ble_ll_scan_sm.pdu_data.scana; +} + +/** + * Set the Resolvable Private Address in the scanning (or initiating) state + * machine. + * + * XXX: should this go into IRK list or connection? + * + * @param rpa + */ +void +ble_ll_scan_set_peer_rpa(uint8_t *rpa) +{ + struct ble_ll_scan_sm *scansm; + + scansm = &g_ble_ll_scan_sm; + memcpy(scansm->scan_peer_rpa, rpa, BLE_DEV_ADDR_LEN); +} + +struct ble_ll_scan_pdu_data * +ble_ll_scan_get_pdu_data(void) +{ + return &g_ble_ll_scan_sm.pdu_data; +} + +/* Returns true if whitelist is enabled for scanning */ +int +ble_ll_scan_whitelist_enabled(void) +{ + return g_ble_ll_scan_sm.scanp->scan_filt_policy & 1; +} + +static void +ble_ll_scan_common_init(void) +{ + struct ble_ll_scan_sm *scansm; + struct ble_ll_scan_params *scanp; + int i; + + /* Clear state machine in case re-initialized */ + scansm = &g_ble_ll_scan_sm; + memset(scansm, 0, sizeof(struct ble_ll_scan_sm)); + + /* Clear scan parameters in case re-initialized */ + memset(g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params)); + + /* Initialize scanning window end event */ + ble_npl_event_init(&scansm->scan_sched_ev, ble_ll_scan_event_proc, scansm); + + for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { + /* Set all non-zero default parameters */ + scanp = &g_ble_ll_scan_params[i]; + scanp->timing.interval = + ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF); + scanp->timing.window = + ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF); + } + + scansm->scanp_phys[PHY_UNCODED].phy = BLE_PHY_1M; +#if (BLE_LL_SCAN_PHY_NUMBER == 2) + scansm->scanp_phys[PHY_CODED].phy = BLE_PHY_CODED; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* Make sure we'll generate new NRPA if necessary */ + scansm->scan_nrpa_timer = ble_npl_time_get(); +#endif + + /* Initialize scanning timer */ + os_cputime_timer_init(&scansm->scan_timer, ble_ll_scan_timer_cb, scansm); + + /* Initialize extended scan timers */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_init(&scansm->duration_timer, + ble_ll_scan_duration_timer_cb, scansm); + os_cputime_timer_init(&scansm->period_timer, ble_ll_scan_period_timer_cb, + scansm); +#endif + + ble_npl_event_init(&scansm->scan_interrupted_ev, ble_ll_scan_interrupted_event_cb, NULL); +} + +/** + * Called when the controller receives the reset command. Resets the + * scanning state machine to its initial state. + * + * @return int + */ +void +ble_ll_scan_reset(void) +{ + struct ble_ll_scan_sm *scansm; + + scansm = &g_ble_ll_scan_sm; + + /* If enabled, stop it. */ + if (scansm->scan_enabled) { + ble_ll_scan_sm_stop(0); + } + + /* stop extended scan timers */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); +#endif + + /* Reset duplicate advertisers and those from which we rxd a response */ + g_ble_ll_scan_num_rsp_advs = 0; + memset(&g_ble_ll_scan_rsp_advs[0], 0, sizeof(g_ble_ll_scan_rsp_advs)); + + os_mempool_clear(&g_scan_dup_pool); + TAILQ_INIT(&g_scan_dup_list); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* clear memory pool for AUX scan results */ + os_mempool_clear(&ext_scan_aux_pool); +#endif + + /* Call the common initialize function again */ + ble_ll_scan_common_init(); +} + +/** + * ble ll scan initialize + * + * Initialize a scanner. Must be called before scanning can be started. + * Expected to be called with a un-initialized scanning state machine. + */ +void +ble_ll_scan_init(void) +{ + os_error_t err; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + err = os_mempool_init(&ext_scan_aux_pool, + MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT), + sizeof (struct ble_ll_aux_data), + ext_scan_aux_mem, + "ble_ll_aux_scan_pool"); + BLE_LL_ASSERT(err == 0); +#endif + + err = os_mempool_init(&g_scan_dup_pool, + MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS), + sizeof(struct ble_ll_scan_dup_entry), + g_scan_dup_mem, + "ble_ll_scan_dup_pool"); + BLE_LL_ASSERT(err == 0); + + TAILQ_INIT(&g_scan_dup_list); + + ble_ll_scan_common_init(); +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sched.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sched.c new file mode 100644 index 0000000..77c107f --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sched.c @@ -0,0 +1,1838 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/porting/nimble/include/os/os_cputime.h" + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) +#include "nimble/nimble/drivers/nrf51/include/ble/xcvr.h" +#elif defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) +#include "nimble/nimble/drivers/nrf52/include/ble/xcvr.h" +#endif + +#include "../include/controller/ble_phy.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_rfmgmt.h" +#include "../include/controller/ble_ll_trace.h" +#include "../include/controller/ble_ll_sync.h" +#include "ble_ll_priv.h" +#include "ble_ll_conn_priv.h" + +/* XXX: this is temporary. Not sure what I want to do here */ +struct hal_timer g_ble_ll_sched_timer; + +uint8_t g_ble_ll_sched_offset_ticks; + +#define BLE_LL_SCHED_ADV_WORST_CASE_USECS \ + (BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + BLE_LL_SCHED_ADV_MAX_USECS \ + + XCVR_TX_SCHED_DELAY_USECS) + +#if (BLE_LL_SCHED_DEBUG == 1) +int32_t g_ble_ll_sched_max_late; +int32_t g_ble_ll_sched_max_early; +#endif + +/* XXX: TODO: + * 1) Add some accounting to the schedule code to see how late we are + * (min/max?) + * + * 2) Need to determine how we really want to handle the case when we execute + * a schedule item but there is a current event. We could: + * -> Reschedule the schedule item and let current event finish + * -> Kill the current event and run the scheduled item. + * -> Disable schedule timer while in an event; could cause us to be late. + * -> Wait for current event to finish hoping it does before schedule item. + */ + +/* Queue for timers */ +TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q; + +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) +struct ble_ll_sched_obj g_ble_ll_sched_data; +#endif + +/** + * Checks if two events in the schedule will overlap in time. NOTE: consecutive + * schedule items can end and start at the same time. + * + * @param s1 + * @param s2 + * + * @return int 0: dont overlap 1:overlap + */ +static int +ble_ll_sched_is_overlap(struct ble_ll_sched_item *s1, + struct ble_ll_sched_item *s2) +{ + int rc; + + rc = 1; + if ((int32_t)(s1->start_time - s2->start_time) < 0) { + /* Make sure this event does not overlap current event */ + if ((int32_t)(s1->end_time - s2->start_time) <= 0) { + rc = 0; + } + } else { + /* Check for overlap */ + if ((int32_t)(s1->start_time - s2->end_time) >= 0) { + rc = 0; + } + } + + return rc; +} + +/* + * Determines if the schedule item overlaps the currently running schedule + * item. We only care about connection schedule items + */ +static int +ble_ll_sched_overlaps_current(struct ble_ll_sched_item *sch) +{ + int rc; + uint32_t ce_end_time; + + rc = 0; + if (ble_ll_state_get() == BLE_LL_STATE_CONNECTION) { + ce_end_time = ble_ll_conn_get_ce_end_time(); + if ((int32_t)(ce_end_time - sch->start_time) > 0) { + rc = 1; + } + } + return rc; +} + +static int +ble_ll_sched_conn_overlap(struct ble_ll_sched_item *entry) +{ + int rc; + struct ble_ll_conn_sm *connsm; + + /* Should only be advertising or a connection here */ + if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN) { + connsm = (struct ble_ll_conn_sm *)entry->cb_arg; + entry->enqueued = 0; + TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link); + ble_ll_event_send(&connsm->conn_ev_end); + rc = 0; + } else { + rc = -1; + } + + return rc; +} + +static struct ble_ll_sched_item * +ble_ll_sched_insert_if_empty(struct ble_ll_sched_item *sch) +{ + struct ble_ll_sched_item *entry; + + entry = TAILQ_FIRST(&g_ble_ll_sched_q); + if (!entry) { + TAILQ_INSERT_HEAD(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 1; + } + return entry; +} + +int +ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm) +{ + int rc; + os_sr_t sr; + uint32_t usecs; + struct ble_ll_sched_item *sch; + struct ble_ll_sched_item *start_overlap; + struct ble_ll_sched_item *end_overlap; + struct ble_ll_sched_item *entry; + struct ble_ll_conn_sm *tmp; + + /* Get schedule element from connection */ + sch = &connsm->conn_sch; + + /* Set schedule start and end times */ + sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks; + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + usecs = connsm->slave_cur_window_widening; + sch->start_time -= (os_cputime_usecs_to_ticks(usecs) + 1); + sch->remainder = 0; + } else { + sch->remainder = connsm->anchor_point_usecs; + } + sch->end_time = connsm->ce_end_time; + + /* Better be past current time or we just leave */ + if ((int32_t)(sch->start_time - os_cputime_get32()) < 0) { + return -1; + } + + /* We have to find a place for this schedule */ + OS_ENTER_CRITICAL(sr); + + if (ble_ll_sched_overlaps_current(sch)) { + OS_EXIT_CRITICAL(sr); + return -1; + } + + /* Stop timer since we will add an element */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + + start_overlap = NULL; + end_overlap = NULL; + rc = 0; + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + if (ble_ll_sched_is_overlap(sch, entry)) { + if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN && + !ble_ll_conn_is_lru((struct ble_ll_conn_sm *)sch->cb_arg, + (struct ble_ll_conn_sm *)entry->cb_arg)) { + /* Only insert if this element is older than all that we + * overlap + */ + start_overlap = NULL; + rc = -1; + break; + } + + if (start_overlap == NULL) { + start_overlap = entry; + end_overlap = entry; + } else { + end_overlap = entry; + } + } else { + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + break; + } + } + } + + if (!rc) { + if (!entry) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + sch->enqueued = 1; + } + + /* Remove first to last scheduled elements */ + entry = start_overlap; + while (entry) { + start_overlap = TAILQ_NEXT(entry,link); + switch (entry->sched_type) { + case BLE_LL_SCHED_TYPE_CONN: + tmp = (struct ble_ll_conn_sm *)entry->cb_arg; + ble_ll_event_send(&tmp->conn_ev_end); + break; + case BLE_LL_SCHED_TYPE_ADV: + ble_ll_adv_event_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + case BLE_LL_SCHED_TYPE_AUX_SCAN: + ble_ll_scan_end_adv_evt((struct ble_ll_aux_data *)entry->cb_arg); + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + case BLE_LL_SCHED_TYPE_PERIODIC: + ble_ll_adv_periodic_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg); + break; + case BLE_LL_SCHED_TYPE_SYNC: + ble_ll_sync_rmvd_from_sched((struct ble_ll_sync_sm *)entry->cb_arg); + break; +#endif +#endif + default: + BLE_LL_ASSERT(0); + break; + } + + TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link); + entry->enqueued = 0; + + if (entry == end_overlap) { + break; + } + entry = start_overlap; + } + + entry = TAILQ_FIRST(&g_ble_ll_sched_q); + if (entry == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } else { + sch = entry; + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} + +/** + * Called to schedule a connection when the current role is master. + * + * Context: Interrupt + * + * @param connsm + * @param ble_hdr + * @param pyld_len + * + * @return int + */ +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) +int +ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len) +{ + int rc; + os_sr_t sr; + uint32_t initial_start; + uint32_t earliest_start; + uint32_t earliest_end; + uint32_t dur; + uint32_t itvl_t; + uint32_t adv_rxend; + int i; + uint32_t tpp; + uint32_t tse; + uint32_t np; + uint32_t cp; + uint32_t tick_in_period; + + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *sch; + + /* Better have a connsm */ + BLE_LL_ASSERT(connsm != NULL); + + /* Get schedule element from connection */ + rc = -1; + sch = &connsm->conn_sch; + + /* XXX: + * The calculations for the 32kHz crystal bear alot of explanation. The + * earliest possible time that the master can start the connection with a + * slave is 1.25 msecs from the end of the connection request. The + * connection request is sent an IFS time from the end of the advertising + * packet that was received plus the time it takes to send the connection + * request. At 1 Mbps, this is 1752 usecs, or 57.41 ticks. Using 57 ticks + * makes us off ~13 usecs. Since we dont want to actually calculate the + * receive end time tick (this would take too long), we assume the end of + * the advertising PDU is 'now' (we call os_cputime_get32). We dont know + * how much time it will take to service the ISR but if we are more than the + * rx to tx time of the chip we will not be successful transmitting the + * connect request. All this means is that we presume that the slave will + * receive the connect request later than we expect but no earlier than + * 13 usecs before (this is important). + * + * The code then attempts to schedule the connection at the + * earliest time although this may not be possible. When the actual + * schedule start time is determined, the master has to determine if this + * time is more than a transmit window offset interval (1.25 msecs). The + * master has to tell the slave how many transmit window offsets there are + * from the earliest possible time to when the actual transmit start will + * occur. Later in this function you will see the calculation. The actual + * transmission start has to occur within the transmit window. The transmit + * window interval is in units of 1.25 msecs and has to be at least 1. To + * make things a bit easier (but less power efficient for the slave), we + * use a transmit window of 2. We do this because we dont quite know the + * exact start of the transmission and if we are too early or too late we + * could miss the transmit window. A final note: the actual transmission + * start (the anchor point) is sched offset ticks from the schedule start + * time. We dont add this to the calculation when calculating the window + * offset. The reason we dont do this is we want to insure we transmit + * after the window offset we tell the slave. For example, say we think + * we are transmitting 1253 usecs from the earliest start. This would cause + * us to send a transmit window offset of 1. Since we are actually + * transmitting earlier than the slave thinks we could end up transmitting + * before the window offset. Transmitting later is fine since we have the + * transmit window to do so. Transmitting before is bad, since the slave + * wont be listening. We could do better calculation if we wanted to use + * a transmit window of 1 as opposed to 2, but for now we dont care. + */ + dur = os_cputime_usecs_to_ticks(g_ble_ll_sched_data.sch_ticks_per_period); + adv_rxend = os_cputime_get32(); + if (ble_hdr->rxinfo.channel >= BLE_PHY_NUM_DATA_CHANS) { + /* + * We received packet on advertising channel which means this is a legacy + * PDU on 1 Mbps - we do as described above. + */ + earliest_start = adv_rxend + 57; + } else { + /* + * The calculations are similar as above. + * + * We received packet on data channel which means this is AUX_ADV_IND + * received on secondary adv channel. We can schedule first packet at + * the earliest after "T_IFS + AUX_CONNECT_REQ + transmitWindowDelay". + * AUX_CONNECT_REQ and transmitWindowDelay times vary depending on which + * PHY we received on. + * + */ + if (ble_hdr->rxinfo.phy == BLE_PHY_1M) { + // 150 + 352 + 2500 = 3002us = 98.37 ticks + earliest_start = adv_rxend + 98; + } else if (ble_hdr->rxinfo.phy == BLE_PHY_2M) { + // 150 + 180 + 2500 = 2830us = 92.73 ticks + earliest_start = adv_rxend + 93; + } else if (ble_hdr->rxinfo.phy == BLE_PHY_CODED) { + // 150 + 2896 + 3750 = 6796us = 222.69 ticks + earliest_start = adv_rxend + 223; + } else { + BLE_LL_ASSERT(0); + } + } + earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) * + BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; + itvl_t = connsm->conn_itvl_ticks; + + /* We have to find a place for this schedule */ + OS_ENTER_CRITICAL(sr); + + /* + * Are there any allocated periods? If not, set epoch start to earliest + * time + */ + if (g_ble_ll_sched_data.sch_num_occ_periods == 0) { + g_ble_ll_sched_data.sch_epoch_start = earliest_start; + cp = 0; + } else { + /* + * Earliest start must occur on period boundary. + * (tse = ticks since epoch) + */ + tpp = g_ble_ll_sched_data.sch_ticks_per_period; + tse = earliest_start - g_ble_ll_sched_data.sch_epoch_start; + np = tse / tpp; + cp = np % BLE_LL_SCHED_PERIODS; + tick_in_period = tse - (np * tpp); + if (tick_in_period != 0) { + ++cp; + if (cp == BLE_LL_SCHED_PERIODS) { + cp = 0; + } + earliest_start += (tpp - tick_in_period); + } + + /* Now find first un-occupied period starting from cp */ + for (i = 0; i < BLE_LL_SCHED_PERIODS; ++i) { + if (g_ble_ll_sched_data.sch_occ_period_mask & (1 << cp)) { + ++cp; + if (cp == BLE_LL_SCHED_PERIODS) { + cp = 0; + } + earliest_start += tpp; + } else { + /* not occupied */ + break; + } + } + /* Should never happen but if it does... */ + if (i == BLE_LL_SCHED_PERIODS) { + OS_EXIT_CRITICAL(sr); + return rc; + } + } + + sch->start_time = earliest_start; + initial_start = earliest_start; + earliest_end = earliest_start + dur; + + if (!ble_ll_sched_insert_if_empty(sch)) { + /* Nothing in schedule. Schedule as soon as possible */ + rc = 0; + connsm->tx_win_off = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET); + } else { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* Set these because overlap function needs them to be set */ + sch->start_time = earliest_start; + sch->end_time = earliest_end; + + /* We can insert if before entry in list */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + if ((earliest_start - initial_start) <= itvl_t) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + } + break; + } + + /* Check for overlapping events */ + if (ble_ll_sched_is_overlap(sch, entry)) { + /* Earliest start is end of this event since we overlap */ + earliest_start = entry->end_time; + earliest_end = earliest_start + dur; + } + } + + /* Must be able to schedule within one connection interval */ + if (!entry) { + if ((earliest_start - initial_start) <= itvl_t) { + rc = 0; + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + } + + if (!rc) { + /* calculate number of window offsets. Each offset is 1.25 ms */ + sch->enqueued = 1; + /* + * NOTE: we dont add sched offset ticks as we want to under-estimate + * the transmit window slightly since the window size is currently + * 2 when using a 32768 crystal. + */ + dur = os_cputime_ticks_to_usecs(earliest_start - initial_start); + connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS; + } + } + + if (!rc) { + sch->start_time = earliest_start; + sch->end_time = earliest_end; + /* + * Since we have the transmit window to transmit in, we dont need + * to set the anchor point usecs; just transmit to the nearest tick. + */ + connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks; + connsm->anchor_point_usecs = 0; + connsm->ce_end_time = earliest_end; + connsm->period_occ_mask = (1 << cp); + g_ble_ll_sched_data.sch_occ_period_mask |= connsm->period_occ_mask; + ++g_ble_ll_sched_data.sch_num_occ_periods; + } + + + /* Get head of list to restart timer */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + ble_ll_rfmgmt_sched_changed(sch); + + OS_EXIT_CRITICAL(sr); + + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} +#else +int +ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len) +{ + int rc; + os_sr_t sr; + uint8_t req_slots; + uint32_t initial_start; + uint32_t earliest_start; + uint32_t earliest_end; + uint32_t dur; + uint32_t itvl_t; + uint32_t adv_rxend; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *sch; + + /* + * XXX: TODO this code assumes the advertisement and connect request were + * sent at 1Mbps. + */ + + /* Get schedule element from connection */ + rc = -1; + sch = &connsm->conn_sch; + req_slots = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS); + + /* XXX: + * The calculations for the 32kHz crystal bear alot of explanation. The + * earliest possible time that the master can start the connection with a + * slave is 1.25 msecs from the end of the connection request. The + * connection request is sent an IFS time from the end of the advertising + * packet that was received plus the time it takes to send the connection + * request. At 1 Mbps, this is 1752 usecs, or 57.41 ticks. Using 57 ticks + * makes us off ~13 usecs. Since we dont want to actually calculate the + * receive end time tick (this would take too long), we assume the end of + * the advertising PDU is 'now' (we call os_cputime_get32). We dont know + * how much time it will take to service the ISR but if we are more than the + * rx to tx time of the chip we will not be successful transmitting the + * connect request. All this means is that we presume that the slave will + * receive the connect request later than we expect but no earlier than + * 13 usecs before (this is important). + * + * The code then attempts to schedule the connection at the + * earliest time although this may not be possible. When the actual + * schedule start time is determined, the master has to determine if this + * time is more than a transmit window offset interval (1.25 msecs). The + * master has to tell the slave how many transmit window offsets there are + * from the earliest possible time to when the actual transmit start will + * occur. Later in this function you will see the calculation. The actual + * transmission start has to occur within the transmit window. The transmit + * window interval is in units of 1.25 msecs and has to be at least 1. To + * make things a bit easier (but less power efficient for the slave), we + * use a transmit window of 2. We do this because we dont quite know the + * exact start of the transmission and if we are too early or too late we + * could miss the transmit window. A final note: the actual transmission + * start (the anchor point) is sched offset ticks from the schedule start + * time. We dont add this to the calculation when calculating the window + * offset. The reason we dont do this is we want to insure we transmit + * after the window offset we tell the slave. For example, say we think + * we are transmitting 1253 usecs from the earliest start. This would cause + * us to send a transmit window offset of 1. Since we are actually + * transmitting earlier than the slave thinks we could end up transmitting + * before the window offset. Transmitting later is fine since we have the + * transmit window to do so. Transmitting before is bad, since the slave + * wont be listening. We could do better calculation if we wanted to use + * a transmit window of 1 as opposed to 2, but for now we dont care. + */ + dur = req_slots * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; + adv_rxend = os_cputime_get32(); + if (ble_hdr->rxinfo.channel >= BLE_PHY_NUM_DATA_CHANS) { + /* + * We received packet on advertising channel which means this is a legacy + * PDU on 1 Mbps - we do as described above. + */ + earliest_start = adv_rxend + 57; + } else { + /* + * The calculations are similar as above. + * + * We received packet on data channel which means this is AUX_ADV_IND + * received on secondary adv channel. We can schedule first packet at + * the earliest after "T_IFS + AUX_CONNECT_REQ + transmitWindowDelay". + * AUX_CONNECT_REQ and transmitWindowDelay times vary depending on which + * PHY we received on. + * + */ + if (ble_hdr->rxinfo.phy == BLE_PHY_1M) { + // 150 + 352 + 2500 = 3002us = 98.37 ticks + earliest_start = adv_rxend + 98; + } else if (ble_hdr->rxinfo.phy == BLE_PHY_2M) { + // 150 + 180 + 2500 = 2830us = 92.73 ticks + earliest_start = adv_rxend + 93; + } else if (ble_hdr->rxinfo.phy == BLE_PHY_CODED) { + // 150 + 2896 + 3750 = 6796us = 222.69 ticks + earliest_start = adv_rxend + 223; + } else { + BLE_LL_ASSERT(0); + } + } + earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) * + BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; + earliest_end = earliest_start + dur; + itvl_t = connsm->conn_itvl_ticks; + + /* We have to find a place for this schedule */ + OS_ENTER_CRITICAL(sr); + + /* The schedule item must occur after current running item (if any) */ + sch->start_time = earliest_start; + initial_start = earliest_start; + + if (!ble_ll_sched_insert_if_empty(sch)) { + /* Nothing in schedule. Schedule as soon as possible */ + rc = 0; + connsm->tx_win_off = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET); + } else { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* Set these because overlap function needs them to be set */ + sch->start_time = earliest_start; + sch->end_time = earliest_end; + + /* We can insert if before entry in list */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + if ((earliest_start - initial_start) <= itvl_t) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + } + break; + } + + /* Check for overlapping events */ + if (ble_ll_sched_is_overlap(sch, entry)) { + /* Earliest start is end of this event since we overlap */ + earliest_start = entry->end_time; + earliest_end = earliest_start + dur; + } + } + + /* Must be able to schedule within one connection interval */ + if (!entry) { + if ((earliest_start - initial_start) <= itvl_t) { + rc = 0; + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + } + + if (!rc) { + /* calculate number of window offsets. Each offset is 1.25 ms */ + sch->enqueued = 1; + /* + * NOTE: we dont add sched offset ticks as we want to under-estimate + * the transmit window slightly since the window size is currently + * 2 when using a 32768 crystal. + */ + dur = os_cputime_ticks_to_usecs(earliest_start - initial_start); + connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS; + } + } + + if (!rc) { + sch->start_time = earliest_start; + sch->end_time = earliest_end; + /* + * Since we have the transmit window to transmit in, we dont need + * to set the anchor point usecs; just transmit to the nearest tick. + */ + connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks; + connsm->anchor_point_usecs = 0; + connsm->ce_end_time = earliest_end; + } + + /* Get head of list to restart timer */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + ble_ll_rfmgmt_sched_changed(sch); + + OS_EXIT_CRITICAL(sr); + + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} +#endif + +/** + * Schedules a slave connection for the first time. + * + * Context: Link Layer + * + * @param connsm + * + * @return int + */ +int +ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm) +{ + int rc; + os_sr_t sr; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *next_sch; + struct ble_ll_sched_item *sch; + int first = 0; + + /* Get schedule element from connection */ + rc = -1; + sch = &connsm->conn_sch; + + /* Set schedule start and end times */ + /* + * XXX: for now, we dont care about anchor point usecs for the slave. It + * does not matter if we turn on the receiver up to one tick before w + * need to. We also subtract one extra tick since the conversion from + * usecs to ticks could be off by up to 1 tick. + */ + sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks - + os_cputime_usecs_to_ticks(connsm->slave_cur_window_widening) - 1; + sch->end_time = connsm->ce_end_time; + sch->remainder = 0; + + /* We have to find a place for this schedule */ + OS_ENTER_CRITICAL(sr); + + /* The schedule item must occur after current running item (if any) */ + if (ble_ll_sched_overlaps_current(sch)) { + OS_EXIT_CRITICAL(sr); + return rc; + } + + entry = ble_ll_sched_insert_if_empty(sch); + if (!entry) { + /* Nothing in schedule. Schedule as soon as possible */ + rc = 0; + first = 1; + } else { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + while (1) { + next_sch = entry->link.tqe_next; + /* Insert if event ends before next starts */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + break; + } + + if (ble_ll_sched_is_overlap(sch, entry)) { + /* If we overlap with a connection, we re-schedule */ + if (ble_ll_sched_conn_overlap(entry)) { + break; + } + } + + /* Move to next entry */ + entry = next_sch; + + /* Insert at tail if none left to check */ + if (!entry) { + rc = 0; + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + break; + } + } + + if (!rc) { + sch->enqueued = 1; + } + + next_sch = TAILQ_FIRST(&g_ble_ll_sched_q); + if (next_sch == sch) { + first = 1; + } else { + sch = next_sch; + } + } + + if (first) { + ble_ll_rfmgmt_sched_changed(sch); + } + + OS_EXIT_CRITICAL(sr); + + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +/* + * Determines if the schedule item overlaps the currently running schedule + * item. This function cares about connection and sync. + */ +static int +ble_ll_sched_sync_overlaps_current(struct ble_ll_sched_item *sch) +{ + uint32_t end_time; + uint8_t state; + + state = ble_ll_state_get(); + switch (state) { + case BLE_LL_STATE_CONNECTION: + end_time = ble_ll_conn_get_ce_end_time(); + break; + case BLE_LL_STATE_SYNC: + end_time = ble_ll_sync_get_event_end_time(); + break; + default: + return 0; + } + + return CPUTIME_GT(end_time, sch->start_time); +} + +int +ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch, + uint32_t anchor_point, uint8_t anchor_point_usecs, + uint32_t window_widening, + int8_t phy_mode) +{ + struct ble_ll_sched_item *entry; + uint8_t start_time_rem_usecs; + uint8_t window_rem_usecs; + uint32_t window_ticks; + uint32_t start_time; + uint32_t end_time; + uint32_t dur; + int rc = 0; + os_sr_t sr; + + window_ticks = os_cputime_usecs_to_ticks(window_widening); + window_rem_usecs = window_widening - os_cputime_ticks_to_usecs(window_ticks); + + /* adjust for subtraction */ + anchor_point_usecs += 31; + anchor_point--; + + start_time = anchor_point - window_ticks; + start_time_rem_usecs = anchor_point_usecs - window_rem_usecs; + if (start_time_rem_usecs >= 31) { + start_time++; + start_time_rem_usecs -= 31; + } + + dur = ble_ll_pdu_tx_time_get(MYNEWT_VAL(BLE_LL_SCHED_SCAN_SYNC_PDU_LEN), + phy_mode); + end_time = start_time + os_cputime_usecs_to_ticks(dur); + + start_time -= g_ble_ll_sched_offset_ticks; + + /* Set schedule start and end times */ + sch->start_time = start_time; + sch->remainder = start_time_rem_usecs; + sch->end_time = end_time; + + /* Better be past current time or we just leave */ + if (CPUTIME_LEQ(sch->start_time, os_cputime_get32())) { + return -1; + } + + /* We have to find a place for this schedule */ + OS_ENTER_CRITICAL(sr); + + if (ble_ll_sched_sync_overlaps_current(sch)) { + OS_EXIT_CRITICAL(sr); + return -1; + } + + /* Try to find slot for sync scan. */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if (CPUTIME_LEQ(sch->end_time, entry->start_time)) { + TAILQ_INSERT_BEFORE(entry, sch, link); + sch->enqueued = 1; + break; + } + + /* Check for overlapping events. For now drop if it overlaps with + * anything. We can make it smarter later on + */ + if (ble_ll_sched_is_overlap(sch, entry)) { + rc = -1; + break; + } + } + + if (!entry) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 1; + } + + entry = TAILQ_FIRST(&g_ble_ll_sched_q); + if (entry == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } else { + sch = entry; + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} + +int +ble_ll_sched_sync(struct ble_ll_sched_item *sch, + uint32_t beg_cputime, uint32_t rem_usecs, + uint32_t offset, int8_t phy_mode) +{ + struct ble_ll_sched_item *entry; + uint32_t start_time_rem_usecs; + uint32_t off_rem_usecs; + uint32_t start_time; + uint32_t off_ticks; + uint32_t end_time; + uint32_t dur; + os_sr_t sr; + int rc = 0; + + off_ticks = os_cputime_usecs_to_ticks(offset); + off_rem_usecs = offset - os_cputime_ticks_to_usecs(off_ticks); + + start_time = beg_cputime + off_ticks; + start_time_rem_usecs = rem_usecs + off_rem_usecs; + if (start_time_rem_usecs >= 31) { + start_time++; + start_time_rem_usecs -= 31; + } + + dur = ble_ll_pdu_tx_time_get(MYNEWT_VAL(BLE_LL_SCHED_SCAN_SYNC_PDU_LEN), + phy_mode); + end_time = start_time + os_cputime_usecs_to_ticks(dur); + + start_time -= g_ble_ll_sched_offset_ticks; + + sch->start_time = start_time; + sch->remainder = start_time_rem_usecs; + sch->end_time = end_time; + + OS_ENTER_CRITICAL(sr); + + if (!ble_ll_sched_insert_if_empty(sch)) { + /* Nothing in schedule. Schedule as soon as possible + * If we are here it means sch has been added to the scheduler */ + goto done; + } + + /* Try to find slot for scan. */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if (CPUTIME_LEQ(sch->end_time, entry->start_time)) { + TAILQ_INSERT_BEFORE(entry, sch, link); + sch->enqueued = 1; + break; + } + + /* Check for overlapping events. For now drop if it overlaps with + * anything. We can make it smarter later on + */ + if (ble_ll_sched_is_overlap(sch, entry)) { + rc = -1; + break; + } + } + + if (!entry) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 1; + } + +done: + entry = TAILQ_FIRST(&g_ble_ll_sched_q); + if (entry == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } else { + sch = entry; + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + STATS_INC(ble_ll_stats, sync_scheduled); + return rc; +} +#endif + +int +ble_ll_sched_adv_new(struct ble_ll_sched_item *sch, ble_ll_sched_adv_new_cb cb, + void *arg) +{ + os_sr_t sr; + uint32_t adv_start; + uint32_t duration; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *orig; + + /* Get length of schedule item */ + duration = sch->end_time - sch->start_time; + orig = sch; + + OS_ENTER_CRITICAL(sr); + entry = ble_ll_sched_insert_if_empty(sch); + if (!entry) { + adv_start = sch->start_time; + } else { + /* XXX: no need to stop timer if not first on list. Modify code? */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + TAILQ_INSERT_BEFORE(entry, sch, link); + break; + } + + /* Check for overlapping events */ + if (ble_ll_sched_is_overlap(sch, entry)) { + /* Earliest start is end of this event since we overlap */ + sch->start_time = entry->end_time; + sch->end_time = sch->start_time + duration; + } + } + + if (!entry) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + adv_start = sch->start_time; + + sch->enqueued = 1; + + /* Restart with head of list */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + } + + if (cb) { + cb((struct ble_ll_adv_sm *)orig->cb_arg, adv_start, arg); + } + + if (orig == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return 0; +} + +int +ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start, + bool after_overlap) +{ + int rc = 0; + os_sr_t sr; + uint32_t adv_start; + uint32_t duration; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *orig = sch; + + /* Get length of schedule item */ + duration = sch->end_time - sch->start_time; + + OS_ENTER_CRITICAL(sr); + entry = ble_ll_sched_insert_if_empty(sch); + if (!entry) { + adv_start = sch->start_time; + } else { + /* XXX: no need to stop timer if not first on list. Modify code? */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + TAILQ_INSERT_BEFORE(entry, sch, link); + break; + } + + /* Check for overlapping events */ + if (ble_ll_sched_is_overlap(sch, entry)) { + if (after_overlap) { + /* Earliest start is end of this event since we overlap */ + sch->start_time = entry->end_time; + sch->end_time = sch->start_time + duration; + } else { + rc = -1; + break; + } + } + } + + if (!entry) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + adv_start = sch->start_time; + + if (!rc) { + sch->enqueued = 1; + } + + /* Restart with head of list */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + } + + if (!rc) { + *start = adv_start; + } + + if (orig == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} + +int +ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start, + uint32_t max_delay_ticks) +{ + int rc; + os_sr_t sr; + uint32_t orig_start; + uint32_t duration; + uint32_t rand_ticks; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *next_sch; + struct ble_ll_sched_item *before; + struct ble_ll_sched_item *start_overlap; + struct ble_ll_sched_item *end_overlap; + + /* Get length of schedule item */ + duration = sch->end_time - sch->start_time; + + /* Add maximum randomization delay to end */ + rand_ticks = max_delay_ticks; + sch->end_time += max_delay_ticks; + + start_overlap = NULL; + end_overlap = NULL; + before = NULL; + rc = 0; + OS_ENTER_CRITICAL(sr); + + entry = ble_ll_sched_insert_if_empty(sch); + if (entry) { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + while (1) { + next_sch = entry->link.tqe_next; + if (ble_ll_sched_is_overlap(sch, entry)) { + if (start_overlap == NULL) { + start_overlap = entry; + end_overlap = entry; + } else { + end_overlap = entry; + } + } else { + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + before = entry; + break; + } + } + + entry = next_sch; + if (entry == NULL) { + break; + } + } + + /* + * If there is no overlap, we either insert before the 'before' entry + * or we insert at the end if there is no before entry. + */ + if (start_overlap == NULL) { + if (before) { + TAILQ_INSERT_BEFORE(before, sch, link); + } else { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } + } else { + /* + * This item will overlap with others. See if we can fit it in + * with original duration. + */ + before = NULL; + orig_start = sch->start_time; + entry = start_overlap; + sch->end_time = sch->start_time + duration; + while (1) { + next_sch = entry->link.tqe_next; + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + rand_ticks = entry->start_time - sch->end_time; + before = entry; + TAILQ_INSERT_BEFORE(before, sch, link); + break; + } else { + sch->start_time = entry->end_time; + sch->end_time = sch->start_time + duration; + } + + if (entry == end_overlap) { + rand_ticks = (orig_start + max_delay_ticks) - sch->start_time; + if (rand_ticks > max_delay_ticks) { + /* No place for advertisement. */ + rc = -1; + } else { + if (next_sch == NULL) { + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + } else { + TAILQ_INSERT_BEFORE(next_sch, sch, link); + } + } + break; + } + entry = next_sch; + BLE_LL_ASSERT(entry != NULL); + } + } + } + + if (!rc) { + sch->enqueued = 1; + if (rand_ticks) { + sch->start_time += rand() % rand_ticks; + } + sch->end_time = sch->start_time + duration; + *start = sch->start_time; + + if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) { + ble_ll_rfmgmt_sched_changed(sch); + } + } + + OS_EXIT_CRITICAL(sr); + + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} + +int +ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch) +{ + uint8_t lls; + os_sr_t sr; + struct ble_ll_sched_item *entry; + + OS_ENTER_CRITICAL(sr); + + lls = ble_ll_state_get(); + if ((lls == BLE_LL_STATE_ADV) || (lls == BLE_LL_STATE_CONNECTION) || + (lls == BLE_LL_STATE_SYNC)) { + goto adv_resched_pdu_fail; + } + + entry = ble_ll_sched_insert_if_empty(sch); + if (entry) { + /* If we overlap with the first item, simply re-schedule */ + if (ble_ll_sched_is_overlap(sch, entry)) { + goto adv_resched_pdu_fail; + } + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_INSERT_BEFORE(entry, sch, link); + sch->enqueued = 1; + } + + ble_ll_rfmgmt_sched_changed(TAILQ_FIRST(&g_ble_ll_sched_q)); + + OS_EXIT_CRITICAL(sr); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + return 0; + +adv_resched_pdu_fail: + OS_EXIT_CRITICAL(sr); + return -1; +} + +/** + * Remove a schedule element + * + * @param sched_type + * + * @return int 0 - removed, 1 - not in the list + */ +int +ble_ll_sched_rmv_elem(struct ble_ll_sched_item *sch) +{ + os_sr_t sr; + struct ble_ll_sched_item *first; + int rc = 1; + + if (!sch) { + return rc; + } + + OS_ENTER_CRITICAL(sr); + if (sch->enqueued) { + first = TAILQ_FIRST(&g_ble_ll_sched_q); + if (first == sch) { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + } + + TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 0; + rc = 0; + + if (first == sch) { + first = TAILQ_FIRST(&g_ble_ll_sched_q); + if (first) { + os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time); + } + ble_ll_rfmgmt_sched_changed(first); + } + } + OS_EXIT_CRITICAL(sr); + + return rc; +} + +void +ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb) +{ + os_sr_t sr; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *first; + + OS_ENTER_CRITICAL(sr); + first = TAILQ_FIRST(&g_ble_ll_sched_q); + + if (!first) { + OS_EXIT_CRITICAL(sr); + return; + } + + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + if (entry->sched_type == type) { + if (first == entry) { + os_cputime_timer_stop(&g_ble_ll_sched_timer); + first = NULL; + } + + TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link); + remove_cb(entry); + entry->enqueued = 0; + } + } + + if (!first) { + first = TAILQ_FIRST(&g_ble_ll_sched_q); + if (first) { + os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time); + } + ble_ll_rfmgmt_sched_changed(first); + } + + OS_EXIT_CRITICAL(sr); +} + +/** + * Executes a schedule item by calling the schedule callback function. + * + * Context: Interrupt + * + * @param sch Pointer to schedule item + * + * @return int 0: schedule item is not over; otherwise schedule item is done. + */ +static int +ble_ll_sched_execute_item(struct ble_ll_sched_item *sch) +{ + int rc; + uint8_t lls; + + lls = ble_ll_state_get(); + + ble_ll_trace_u32x3(BLE_LL_TRACE_ID_SCHED, lls, os_cputime_get32(), + sch->start_time); + + if (lls == BLE_LL_STATE_STANDBY) { + goto sched; + } + + /* If aux scan scheduled and LL is in state when scanner is running + * in 3 states: + * BLE_LL_STATE_SCANNING + * BLE_LL_STATE_INITIATING + * BLE_LL_STATE_STANDBY + * + * Let scanner to decide to disable phy or not. + */ + if (sch->sched_type == BLE_LL_SCHED_TYPE_AUX_SCAN) { + if (lls == BLE_LL_STATE_INITIATING || lls == BLE_LL_STATE_SCANNING) { + goto sched; + } + } + + /* + * This is either an advertising event or connection event start. If + * we are scanning or initiating just stop it. + */ + + /* We have to disable the PHY no matter what */ + ble_phy_disable(); + + if (lls == BLE_LL_STATE_SCANNING) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_scan_halt(); + } else if (lls == BLE_LL_STATE_INITIATING) { + ble_ll_state_set(BLE_LL_STATE_STANDBY); + ble_ll_scan_halt(); + /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */ + ble_ll_conn_reset_pending_aux_conn_rsp(); + } else if (lls == BLE_LL_STATE_ADV) { + STATS_INC(ble_ll_stats, sched_state_adv_errs); + ble_ll_adv_halt(); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + } else if (lls == BLE_LL_STATE_SYNC) { + STATS_INC(ble_ll_stats, sched_state_sync_errs); + ble_ll_sync_halt(); +#endif + } else { + STATS_INC(ble_ll_stats, sched_state_conn_errs); + ble_ll_conn_event_halt(); + } + +sched: + BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 1); + BLE_LL_ASSERT(sch->sched_cb); + rc = sch->sched_cb(sch); + BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 0); + return rc; +} + +/** + * Run the BLE scheduler. Iterate through all items on the schedule queue. + * + * Context: interrupt (scheduler) + * + * @return int + */ +static void +ble_ll_sched_run(void *arg) +{ + struct ble_ll_sched_item *sch; + + BLE_LL_DEBUG_GPIO(SCHED_RUN, 1); + + /* Look through schedule queue */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + if (sch) { +#if (BLE_LL_SCHED_DEBUG == 1) + int32_t dt; + + /* Make sure we have passed the start time of the first event */ + dt = (int32_t)(os_cputime_get32() - sch->start_time); + if (dt > g_ble_ll_sched_max_late) { + g_ble_ll_sched_max_late = dt; + } + if (dt < g_ble_ll_sched_max_early) { + g_ble_ll_sched_max_early = dt; + } +#endif + + /* Remove schedule item and execute the callback */ + TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 0; + ble_ll_sched_execute_item(sch); + + /* Restart if there is an item on the schedule */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + if (sch) { + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + } + ble_ll_rfmgmt_sched_changed(sch); + } + + BLE_LL_DEBUG_GPIO(SCHED_RUN, 0); +} + +/** + * Called to determine when the next scheduled event will occur. + * + * If there are not scheduled events this function returns 0; otherwise it + * returns 1 and *next_event_time is set to the start time of the next event. + * + * @param next_event_time + * + * @return int 0: No events are scheduled 1: there is an upcoming event + */ +int +ble_ll_sched_next_time(uint32_t *next_event_time) +{ + int rc; + os_sr_t sr; + struct ble_ll_sched_item *first; + + rc = 0; + OS_ENTER_CRITICAL(sr); + first = TAILQ_FIRST(&g_ble_ll_sched_q); + if (first) { + *next_event_time = first->start_time; + rc = 1; + } + OS_EXIT_CRITICAL(sr); + + return rc; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +/** + * Called to check if there is place for a planned scan req. + * + * @param chan + * @param phy_mode + * + * @return int 0: Clear for scan req 1: there is an upcoming event + */ +int +ble_ll_sched_scan_req_over_aux_ptr(uint32_t chan, uint8_t phy_mode) +{ + struct ble_ll_sched_item *sch; + uint32_t usec_dur; + uint32_t now = os_cputime_get32(); + + /* Lets calculate roughly how much time we need for scan req and scan rsp */ + usec_dur = ble_ll_pdu_tx_time_get(BLE_SCAN_REQ_LEN, phy_mode); + if (chan >= BLE_PHY_NUM_DATA_CHANS) { + usec_dur += ble_ll_pdu_tx_time_get(BLE_SCAN_RSP_MAX_LEN, phy_mode); + } else { + usec_dur += ble_ll_pdu_tx_time_get(BLE_SCAN_RSP_MAX_EXT_LEN, phy_mode); + } + + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + while (sch) { + /* Let's check if there is no scheduled item which want to start within + * given usecs.*/ + if ((int32_t)(sch->start_time - now + os_cputime_usecs_to_ticks(usec_dur)) > 0) { + /* We are fine. Have time for scan req */ + return 0; + } + + /* There is something in the scheduler. If it is not aux ptr we assume + * it is more important that scan req + */ + if (sch->sched_type != BLE_LL_SCHED_TYPE_AUX_SCAN) { + return 1; + } + + ble_ll_scan_end_adv_evt((struct ble_ll_aux_data *)sch->cb_arg); + TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 0; + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + } + return 0; +} + +/** + * Called to schedule a aux scan. + * + * Context: Interrupt + * + * @param ble_hdr + * @param scansm + * @param aux_scan + * + * @return 0 on success, 1 otherwise + */ +int +ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr, + struct ble_ll_scan_sm *scansm, + struct ble_ll_aux_data *aux_scan) +{ + int rc = 1; + os_sr_t sr; + uint32_t off_ticks; + uint32_t off_rem_usecs; + uint32_t start_time; + uint32_t start_time_rem_usecs; + uint32_t end_time; + uint32_t dur; + struct ble_ll_sched_item *entry; + struct ble_ll_sched_item *sch; + int phy_mode; + + sch = &aux_scan->sch; + BLE_LL_ASSERT(sch->cb_arg == NULL); + + off_ticks = os_cputime_usecs_to_ticks(aux_scan->offset); + off_rem_usecs = aux_scan->offset - os_cputime_ticks_to_usecs(off_ticks); + + start_time = ble_hdr->beg_cputime + off_ticks; + start_time_rem_usecs = ble_hdr->rem_usecs + off_rem_usecs; + if (start_time_rem_usecs >= 31) { + start_time++; + start_time_rem_usecs -= 31; + } + start_time -= g_ble_ll_sched_offset_ticks; + + /* Let's calculate time we reserve for aux packet. For now we assume to wait + * for fixed number of bytes and handle possible interrupting it in + * ble_ll_sched_execute_item(). This is because aux packet can be up to + * 256bytes and we don't want to block sched that long + */ + phy_mode = ble_ll_phy_to_phy_mode(aux_scan->aux_phy, + BLE_HCI_LE_PHY_CODED_ANY); + dur = ble_ll_pdu_tx_time_get(MYNEWT_VAL(BLE_LL_SCHED_SCAN_AUX_PDU_LEN), + phy_mode); + end_time = start_time + os_cputime_usecs_to_ticks(dur); + + sch->start_time = start_time; + sch->remainder = start_time_rem_usecs; + sch->end_time = end_time; + + OS_ENTER_CRITICAL(sr); + + if (!ble_ll_sched_insert_if_empty(sch)) { + /* Nothing in schedule. Schedule as soon as possible + * If we are here it means sch has been added to the scheduler */ + rc = 0; + goto done; + } + + /* Try to find slot for aux scan. */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if ((int32_t)(sch->end_time - entry->start_time) <= 0) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + sch->enqueued = 1; + break; + } + + /* Check for overlapping events. For now drop if it overlaps with + * anything. We can make it smarter later on + */ + if (ble_ll_sched_is_overlap(sch, entry)) { + break; + } + } + + if (!entry) { + rc = 0; + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 1; + } + +done: + + if (rc == 0) { + sch->cb_arg = ble_ll_scan_aux_data_ref(aux_scan); + STATS_INC(ble_ll_stats, aux_scheduled); + } + + /* Get head of list to restart timer */ + entry = TAILQ_FIRST(&g_ble_ll_sched_q); + if (entry == sch) { + ble_ll_rfmgmt_sched_changed(sch); + } else { + sch = entry; + } + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} +#endif + +#if MYNEWT_VAL(BLE_LL_DTM) +int ble_ll_sched_dtm(struct ble_ll_sched_item *sch) +{ + int rc; + os_sr_t sr; + struct ble_ll_sched_item *entry; + + OS_ENTER_CRITICAL(sr); + + if (!ble_ll_sched_insert_if_empty(sch)) { + /* Nothing in schedule. Schedule as soon as possible + * If we are here it means sch has been added to the scheduler */ + rc = 0; + goto done; + } + + /* Try to find slot for test. */ + os_cputime_timer_stop(&g_ble_ll_sched_timer); + TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { + /* We can insert if before entry in list */ + if (sch->end_time <= entry->start_time) { + rc = 0; + TAILQ_INSERT_BEFORE(entry, sch, link); + sch->enqueued = 1; + break; + } + + /* Check for overlapping events. For now drop if it overlaps with + * anything. We can make it smarter later on + */ + if (ble_ll_sched_is_overlap(sch, entry)) { + OS_EXIT_CRITICAL(sr); + return -1; + } + } + + if (!entry) { + rc = 0; + TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 1; + } + +done: + + /* Get head of list to restart timer */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + + ble_ll_rfmgmt_sched_changed(sch); + + OS_EXIT_CRITICAL(sr); + + /* Restart timer */ + BLE_LL_ASSERT(sch != NULL); + os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); + + return rc; +} +#endif +/** + * Stop the scheduler + * + * Context: Link Layer task + */ +void +ble_ll_sched_stop(void) +{ + os_cputime_timer_stop(&g_ble_ll_sched_timer); +} + +/** + * Initialize the scheduler. Should only be called once and should be called + * before any of the scheduler API are called. + * + * @return int + */ +int +ble_ll_sched_init(void) +{ + BLE_LL_DEBUG_GPIO_INIT(SCHED_ITEM_CB); + BLE_LL_DEBUG_GPIO_INIT(SCHED_RUN); + + /* + * Initialize max early to large negative number. This is used + * to determine the worst-case "early" time the schedule was called. Dont + * expect this to be less than -3 or -4. + */ +#if (BLE_LL_SCHED_DEBUG == 1) + g_ble_ll_sched_max_early = -50000; +#endif + + /* + * This is the offset from the start of the scheduled item until the actual + * tx/rx should occur, in ticks. We also "round up" to the nearest tick. + */ + g_ble_ll_sched_offset_ticks = + (uint8_t) os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS + 30); + + /* Initialize cputimer for the scheduler */ + os_cputime_timer_init(&g_ble_ll_sched_timer, ble_ll_sched_run, NULL); + +#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING) + memset(&g_ble_ll_sched_data, 0, sizeof(struct ble_ll_sched_obj)); + g_ble_ll_sched_data.sch_ticks_per_period = + os_cputime_usecs_to_ticks(MYNEWT_VAL(BLE_LL_USECS_PER_PERIOD)); + g_ble_ll_sched_data.sch_ticks_per_epoch = BLE_LL_SCHED_PERIODS * + g_ble_ll_sched_data.sch_ticks_per_period; +#endif + + return 0; +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_supp_cmd.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_supp_cmd.c new file mode 100644 index 0000000..6ee912f --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_supp_cmd.c @@ -0,0 +1,461 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include + +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" + +/* Octet 0 */ +#define BLE_SUPP_CMD_DISCONNECT (1 << 5) +#define BLE_LL_SUPP_CMD_OCTET_0 (BLE_SUPP_CMD_DISCONNECT) + +/* Octet 5 */ +#define BLE_SUPP_CMD_SET_EVENT_MASK (1 << 6) +#define BLE_LL_SUPP_CMD_OCTET_5 (BLE_SUPP_CMD_SET_EVENT_MASK) + +/* Octet 10 */ +#define BLE_SUPP_CMD_RD_TX_PWR (0 << 2) +#define BLE_LL_SUPP_CMD_OCTET_10 (BLE_SUPP_CMD_RD_TX_PWR) + +/* Octet 14 */ +#define BLE_SUPP_CMD_RD_LOC_VER (1 << 3) +#define BLE_SUPP_CMD_RD_LOC_SUPP_FEAT (1 << 5) +#define BLE_LL_SUPP_CMD_OCTET_14 \ +( \ + BLE_SUPP_CMD_RD_LOC_VER | \ + BLE_SUPP_CMD_RD_LOC_SUPP_FEAT \ +) + +/* Octet 15 */ +#define BLE_SUPP_CMD_RD_BD_ADDR (1 << 1) +#define BLE_SUPP_CMD_RD_RSSI (1 << 5) + +#define BLE_LL_SUPP_CMD_OCTET_15 \ +( \ + BLE_SUPP_CMD_RD_BD_ADDR | \ + BLE_SUPP_CMD_RD_RSSI \ +) + +/* Octet 25 */ +#define BLE_SUPP_CMD_LE_SET_EV_MASK (1 << 0) +#define BLE_SUPP_CMD_LE_RD_BUF_SIZE (1 << 1) +#define BLE_SUPP_CMD_LE_RD_LOC_FEAT (1 << 2) +#define BLE_SUPP_CMD_LE_SET_RAND_ADDR (1 << 4) +#define BLE_SUPP_CMD_LE_SET_ADV_PARAMS (1 << 5) +#define BLE_SUPP_CMD_LE_SET_ADV_TX_PWR (1 << 6) +#define BLE_SUPP_CMD_LE_SET_ADV_DATA (1 << 7) + +#define BLE_LL_SUPP_CMD_OCTET_25 \ +( \ + BLE_SUPP_CMD_LE_SET_EV_MASK | \ + BLE_SUPP_CMD_LE_RD_BUF_SIZE | \ + BLE_SUPP_CMD_LE_RD_LOC_FEAT | \ + BLE_SUPP_CMD_LE_SET_RAND_ADDR | \ + BLE_SUPP_CMD_LE_SET_ADV_PARAMS | \ + BLE_SUPP_CMD_LE_SET_ADV_TX_PWR | \ + BLE_SUPP_CMD_LE_SET_ADV_DATA \ +) + +/* Octet 26 */ +#define BLE_SUPP_CMD_LE_SET_SCAN_RSP_DATA (1 << 0) +#define BLE_SUPP_CMD_LE_SET_ADV_ENABLE (1 << 1) +#define BLE_SUPP_CMD_LE_SET_SCAN_PARAMS (1 << 2) +#define BLE_SUPP_CMD_LE_SET_SCAN_ENABLE (1 << 3) +#define BLE_SUPP_CMD_LE_CREATE_CONN (1 << 4) +#define BLE_SUPP_CMD_LE_CREATE_CONN_CANCEL (1 << 5) +#define BLE_SUPP_CMD_LE_RD_WHITELIST_SIZE (1 << 6) +#define BLE_SUPP_CMD_LE_CLR_WHITELIST (1 << 7) + +#define BLE_LL_SUPP_CMD_OCTET_26 \ +( \ + BLE_SUPP_CMD_LE_SET_SCAN_RSP_DATA | \ + BLE_SUPP_CMD_LE_SET_ADV_ENABLE | \ + BLE_SUPP_CMD_LE_SET_SCAN_PARAMS | \ + BLE_SUPP_CMD_LE_SET_SCAN_ENABLE | \ + BLE_SUPP_CMD_LE_CREATE_CONN | \ + BLE_SUPP_CMD_LE_CREATE_CONN_CANCEL | \ + BLE_SUPP_CMD_LE_RD_WHITELIST_SIZE | \ + BLE_SUPP_CMD_LE_CLR_WHITELIST \ +) + +/* Octet 27 */ +#define BLE_SUPP_CMD_LE_ADD_DEV_WHITELIST (1 << 0) +#define BLE_SUPP_CMD_LE_RMV_DEV_WHITELIST (1 << 1) +#define BLE_SUPP_CMD_LE_CONN_UPDATE (1 << 2) +#define BLE_SUPP_CMD_LE_SET_HOST_CHAN_CLASS (1 << 3) +#define BLE_SUPP_CMD_LE_RD_CHAN_MAP (1 << 4) +#define BLE_SUPP_CMD_LE_RD_REM_USED_FEAT (1 << 5) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +#define BLE_SUPP_CMD_LE_ENCRYPT (1 << 6) +#else +#define BLE_SUPP_CMD_LE_ENCRYPT (0 << 6) +#endif +#define BLE_SUPP_CMD_LE_RAND (1 << 7) + +#define BLE_LL_SUPP_CMD_OCTET_27 \ +( \ + BLE_SUPP_CMD_LE_ENCRYPT | \ + BLE_SUPP_CMD_LE_RAND | \ + BLE_SUPP_CMD_LE_ADD_DEV_WHITELIST | \ + BLE_SUPP_CMD_LE_RMV_DEV_WHITELIST | \ + BLE_SUPP_CMD_LE_CONN_UPDATE | \ + BLE_SUPP_CMD_LE_SET_HOST_CHAN_CLASS | \ + BLE_SUPP_CMD_LE_RD_CHAN_MAP | \ + BLE_SUPP_CMD_LE_RD_REM_USED_FEAT \ +) + +/* Octet 28 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +#define BLE_SUPP_CMD_LE_START_ENCRYPT (1 << 0) +#define BLE_SUPP_CMD_LE_LTK_REQ_REPLY (1 << 1) +#define BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY (1 << 2) +#else +#define BLE_SUPP_CMD_LE_START_ENCRYPT (0 << 0) +#define BLE_SUPP_CMD_LE_LTK_REQ_REPLY (0 << 1) +#define BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY (0 << 2) +#endif +#define BLE_SUPP_CMD_LE_READ_SUPP_STATES (1 << 3) + +#if MYNEWT_VAL(BLE_LL_DTM) +#define BLE_SUPP_CMD_LE_RX_TEST (1 << 4) +#define BLE_SUPP_CMD_LE_TX_TEST (1 << 5) +#define BLE_SUPP_CMD_LE_TEST_END (1 << 6) + +#else +#define BLE_SUPP_CMD_LE_RX_TEST (0 << 4) +#define BLE_SUPP_CMD_LE_TX_TEST (0 << 5) +#define BLE_SUPP_CMD_LE_TEST_END (0 << 6) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_28 \ +( \ + BLE_SUPP_CMD_LE_START_ENCRYPT | \ + BLE_SUPP_CMD_LE_LTK_REQ_REPLY | \ + BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY | \ + BLE_SUPP_CMD_LE_READ_SUPP_STATES | \ + BLE_SUPP_CMD_LE_RX_TEST | \ + BLE_SUPP_CMD_LE_TX_TEST | \ + BLE_SUPP_CMD_LE_TEST_END \ +) + +/* Octet 33 */ +#define BLE_SUPP_CMD_LE_REM_CONN_PRR (1 << 4) +#define BLE_SUPP_CMD_LE_REM_CONN_PRNR (1 << 5) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) +#define BLE_SUPP_CMD_LE_SET_DATALEN (1 << 6) +#define BLE_SUPP_CMD_LE_RD_SUGG_DATALEN (1 << 7) +#else +#define BLE_SUPP_CMD_LE_SET_DATALEN (0 << 6) +#define BLE_SUPP_CMD_LE_RD_SUGG_DATALEN (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_33 \ +( \ + BLE_SUPP_CMD_LE_REM_CONN_PRR | \ + BLE_SUPP_CMD_LE_REM_CONN_PRNR | \ + BLE_SUPP_CMD_LE_SET_DATALEN | \ + BLE_SUPP_CMD_LE_RD_SUGG_DATALEN \ +) + +/* Octet 34 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) +#define BLE_SUPP_CMD_LE_WR_SUGG_DATALEN (1 << 0) +#else +#define BLE_SUPP_CMD_LE_WR_SUGG_DATALEN (0 << 0) +#endif +#define BLE_SUPP_CMD_LE_READ_LOCAL_P256_PK (0 << 1) +#define BLE_SUPP_CMD_LE_GENERATE_DH_KEY (0 << 2) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +#define BLE_SUPP_CMD_LE_ADD_RESOLV_LIST (1 << 3) +#define BLE_SUPP_CMD_LE_REMOVE_RESOLV_LIST (1 << 4) +#define BLE_SUPP_CMD_LE_CLEAR_RESOLV_LIST (1 << 5) +#define BLE_SUPP_CMD_LE_RD_RESOLV_SIZE (1 << 6) +#define BLE_SUPP_CMD_LE_RD_PEER_RESV_ADDR (1 << 7) +#else +#define BLE_SUPP_CMD_LE_ADD_RESOLV_LIST (0 << 3) +#define BLE_SUPP_CMD_LE_REMOVE_RESOLV_LIST (0 << 4) +#define BLE_SUPP_CMD_LE_CLEAR_RESOLV_LIST (0 << 5) +#define BLE_SUPP_CMD_LE_RD_RESOLV_SIZE (0 << 6) +#define BLE_SUPP_CMD_LE_RD_PEER_RESV_ADDR (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_34 \ +( \ + BLE_SUPP_CMD_LE_WR_SUGG_DATALEN | \ + BLE_SUPP_CMD_LE_READ_LOCAL_P256_PK | \ + BLE_SUPP_CMD_LE_GENERATE_DH_KEY | \ + BLE_SUPP_CMD_LE_ADD_RESOLV_LIST | \ + BLE_SUPP_CMD_LE_REMOVE_RESOLV_LIST | \ + BLE_SUPP_CMD_LE_CLEAR_RESOLV_LIST | \ + BLE_SUPP_CMD_LE_RD_RESOLV_SIZE | \ + BLE_SUPP_CMD_LE_RD_PEER_RESV_ADDR \ +) + +/* Octet 35 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +#define BLE_SUPP_CMD_LE_RD_LOCAL_RESV_ADDR (1 << 0) +#define BLE_SUPP_CMD_LE_SET_ADDR_RES_EN (1 << 1) +#define BLE_SUPP_CMD_LE_SET_RESV_ADDR_TMO (1 << 2) +#else +#define BLE_SUPP_CMD_LE_RD_LOCAL_RESV_ADDR (0 << 0) +#define BLE_SUPP_CMD_LE_SET_ADDR_RES_EN (0 << 1) +#define BLE_SUPP_CMD_LE_SET_RESV_ADDR_TMO (0 << 2) +#endif +#define BLE_SUPP_CMD_LE_RD_MAX_DATALEN (1 << 3) +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +#define BLE_SUPP_CMD_LE_READ_PHY (1 << 4) +#define BLE_SUPP_CMD_LE_SET_DEFAULT_PHY (1 << 5) +#define BLE_SUPP_CMD_LE_SET_PHY (1 << 6) +#else +#define BLE_SUPP_CMD_LE_READ_PHY (0 << 4) +#define BLE_SUPP_CMD_LE_SET_DEFAULT_PHY (0 << 5) +#define BLE_SUPP_CMD_LE_SET_PHY (0 << 6) +#endif + +#if MYNEWT_VAL(BLE_LL_DTM) +#define BLE_SUPP_CMD_LE_ENHANCED_RX_TEST (1 << 7) +#else +#define BLE_SUPP_CMD_LE_ENHANCED_RX_TEST (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_35 \ +( \ + BLE_SUPP_CMD_LE_RD_LOCAL_RESV_ADDR | \ + BLE_SUPP_CMD_LE_SET_ADDR_RES_EN | \ + BLE_SUPP_CMD_LE_SET_RESV_ADDR_TMO | \ + BLE_SUPP_CMD_LE_RD_MAX_DATALEN | \ + BLE_SUPP_CMD_LE_READ_PHY | \ + BLE_SUPP_CMD_LE_SET_DEFAULT_PHY | \ + BLE_SUPP_CMD_LE_SET_PHY | \ + BLE_SUPP_CMD_LE_ENHANCED_RX_TEST \ +) + +/* Octet 36 */ +#if MYNEWT_VAL(BLE_LL_DTM) +#define BLE_SUPP_CMD_LE_ENHANCED_TX_TEST (1 << 0) +#else +#define BLE_SUPP_CMD_LE_ENHANCED_TX_TEST (0 << 0) +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_SUPP_CMD_LE_SET_ADVS_RAND_ADDR (1 << 1) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_PARAM (1 << 2) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_DATA (1 << 3) +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_RSP (1 << 4) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_ENABLE (1 << 5) +#define BLE_SUPP_CMD_LE_RD_MAX_ADV_DATA_LEN (1 << 6) +#define BLE_SUPP_CMD_LE_RD_NUM_SUPP_ADVS (1 << 7) +#else +#define BLE_SUPP_CMD_LE_SET_ADVS_RAND_ADDR (0 << 1) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_PARAM (0 << 2) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_DATA (0 << 3) +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_RSP (0 << 4) +#define BLE_SUPP_CMD_LE_SET_EXT_ADV_ENABLE (0 << 5) +#define BLE_SUPP_CMD_LE_RD_MAX_ADV_DATA_LEN (0 << 6) +#define BLE_SUPP_CMD_LE_RD_NUM_SUPP_ADVS (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_36 \ +( \ + BLE_SUPP_CMD_LE_ENHANCED_TX_TEST | \ + BLE_SUPP_CMD_LE_SET_ADVS_RAND_ADDR | \ + BLE_SUPP_CMD_LE_SET_EXT_ADV_PARAM | \ + BLE_SUPP_CMD_LE_SET_EXT_ADV_DATA | \ + BLE_SUPP_CMD_LE_SET_EXT_SCAN_RSP | \ + BLE_SUPP_CMD_LE_SET_EXT_ADV_ENABLE | \ + BLE_SUPP_CMD_LE_RD_MAX_ADV_DATA_LEN | \ + BLE_SUPP_CMD_LE_RD_NUM_SUPP_ADVS \ +) + +/* Octet 37 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_SUPP_CMD_LE_REMOVE_ADVS (1 << 0) +#define BLE_SUPP_CMD_LE_CLEAR_ADVS (1 << 1) +#else +#define BLE_SUPP_CMD_LE_REMOVE_ADVS (0 << 0) +#define BLE_SUPP_CMD_LE_CLEAR_ADVS (0 << 1) +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +#define BLE_SUPP_CMD_LE_SET_PADV_PARAM (1 << 2) +#define BLE_SUPP_CMD_LE_SET_PADV_DATA (1 << 3) +#define BLE_SUPP_CMD_LE_SET_PADV_ENABLE (1 << 4) +#else +#define BLE_SUPP_CMD_LE_SET_PADV_PARAM (0 << 2) +#define BLE_SUPP_CMD_LE_SET_PADV_DATA (0 << 3) +#define BLE_SUPP_CMD_LE_SET_PADV_ENABLE (0 << 4) +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM (1 << 5) +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE (1 << 6) +#define BLE_SUPP_CMD_LE_EXT_CREATE_CONN (1 << 7) +#else +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM (0 << 5) +#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE (0 << 6) +#define BLE_SUPP_CMD_LE_EXT_CREATE_CONN (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_37 \ +( \ + BLE_SUPP_CMD_LE_REMOVE_ADVS | \ + BLE_SUPP_CMD_LE_CLEAR_ADVS | \ + BLE_SUPP_CMD_LE_SET_PADV_PARAM | \ + BLE_SUPP_CMD_LE_SET_PADV_DATA | \ + BLE_SUPP_CMD_LE_SET_PADV_ENABLE | \ + BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM | \ + BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE | \ + BLE_SUPP_CMD_LE_EXT_CREATE_CONN \ +) + +/* Octet 38 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +#define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC (1 << 0) +#define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC_C (1 << 1) +#define BLE_SUPP_CMD_LE_PADV_TERMINATE_SYNC (1 << 2) +#define BLE_SUPP_CMD_LE_ADD_PADV_LIST (1 << 3) +#define BLE_SUPP_CMD_LE_REMOVE_PADV_LIST (1 << 4) +#define BLE_SUPP_CMD_LE_CLEAR_PADV_LIST (1 << 5) +#define BLE_SUPP_CMD_LE_RD_PADV_LIST_SIZE (1 << 6) +#else +#define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC (0 << 0) +#define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC_C (0 << 1) +#define BLE_SUPP_CMD_LE_PADV_TERMINATE_SYNC (0 << 2) +#define BLE_SUPP_CMD_LE_ADD_PADV_LIST (0 << 3) +#define BLE_SUPP_CMD_LE_REMOVE_PADV_LIST (0 << 4) +#define BLE_SUPP_CMD_LE_CLEAR_PADV_LIST (0 << 5) +#define BLE_SUPP_CMD_LE_RD_PADV_LIST_SIZE (0 << 6) +#endif +#define BLE_SUPP_CMD_LE_RD_TX_POWER (1 << 7) + +#define BLE_LL_SUPP_CMD_OCTET_38 \ +( \ + BLE_SUPP_CMD_LE_PADV_CREATE_SYNC | \ + BLE_SUPP_CMD_LE_PADV_CREATE_SYNC_C | \ + BLE_SUPP_CMD_LE_PADV_TERMINATE_SYNC | \ + BLE_SUPP_CMD_LE_ADD_PADV_LIST | \ + BLE_SUPP_CMD_LE_REMOVE_PADV_LIST | \ + BLE_SUPP_CMD_LE_CLEAR_PADV_LIST | \ + BLE_SUPP_CMD_LE_RD_PADV_LIST_SIZE | \ + BLE_SUPP_CMD_LE_RD_TX_POWER \ +) + +/* Octet 39 */ +#define BLE_SUPP_CMD_LE_RD_RF_PATH_COMP (1 << 0) +#define BLE_SUPP_CMD_LE_WR_RF_PATH_COMP (1 << 1) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +#define BLE_SUPP_CMD_LE_SET_PRIVACY_MODE (1 << 2) +#else +#define BLE_SUPP_CMD_LE_SET_PRIVACY_MODE (0 << 2) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_39 \ +( \ + BLE_SUPP_CMD_LE_RD_RF_PATH_COMP | \ + BLE_SUPP_CMD_LE_WR_RF_PATH_COMP | \ + BLE_SUPP_CMD_LE_SET_PRIVACY_MODE \ +) + +/* Octet 40 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_VERSION) >= 51 +#define BLE_SUPP_CMD_LE_PADV_RECV_ENABLE (1 << 5) +#else +#define BLE_SUPP_CMD_LE_PADV_RECV_ENABLE (0 << 5) +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +#define BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER (1 << 6) +#define BLE_SUPP_CMD_LE_PADV_SET_INFO_TRANSFER (1 << 7) +#else +#define BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER (0 << 6) +#define BLE_SUPP_CMD_LE_PADV_SET_INFO_TRANSFER (0 << 7) +#endif + +#define BLE_LL_SUPP_CMD_OCTET_40 \ +( \ + BLE_SUPP_CMD_LE_PADV_RECV_ENABLE | \ + BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER | \ + BLE_SUPP_CMD_LE_PADV_SET_INFO_TRANSFER \ +) + +/* Octet 41 */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +#define BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER_PARAMS (1 << 0) +#define BLE_SUPP_CMD_LE_PADV_DEFAULT_SYNC_TRANSFER_PARAMS (1 << 1) +#else +#define BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER_PARAMS (0 << 0) +#define BLE_SUPP_CMD_LE_PADV_DEFAULT_SYNC_TRANSFER_PARAMS (0 << 1) +#endif +#define BLE_LL_SUPP_CMD_OCTET_41 \ +( \ + BLE_SUPP_CMD_LE_PADV_SYNC_TRANSFER_PARAMS | \ + BLE_SUPP_CMD_LE_PADV_DEFAULT_SYNC_TRANSFER_PARAMS \ +) + +/* Defines the array of supported commands */ +const uint8_t g_ble_ll_supp_cmds[BLE_LL_SUPP_CMD_LEN] = +{ + BLE_LL_SUPP_CMD_OCTET_0, /* Octet 0 */ + 0, + 0, + 0, + 0, + BLE_LL_SUPP_CMD_OCTET_5, + 0, + 0, + 0, /* Octet 8 */ + 0, + BLE_LL_SUPP_CMD_OCTET_10, + 0, + 0, + 0, + BLE_LL_SUPP_CMD_OCTET_14, + BLE_LL_SUPP_CMD_OCTET_15, + 0, /* Octet 16 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, /* Octet 24 */ + BLE_LL_SUPP_CMD_OCTET_25, + BLE_LL_SUPP_CMD_OCTET_26, + BLE_LL_SUPP_CMD_OCTET_27, + BLE_LL_SUPP_CMD_OCTET_28, + 0, + 0, + 0, + 0, /* Octet 32 */ + BLE_LL_SUPP_CMD_OCTET_33, + BLE_LL_SUPP_CMD_OCTET_34, + BLE_LL_SUPP_CMD_OCTET_35, + BLE_LL_SUPP_CMD_OCTET_36, + BLE_LL_SUPP_CMD_OCTET_37, + BLE_LL_SUPP_CMD_OCTET_38, + BLE_LL_SUPP_CMD_OCTET_39, + BLE_LL_SUPP_CMD_OCTET_40, /* Octet 40 */ + BLE_LL_SUPP_CMD_OCTET_41, +}; + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sync.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sync.c new file mode 100644 index 0000000..231a4d6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_sync.c @@ -0,0 +1,2248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_sync.h" +#include "../include/controller/ble_ll_utils.h" +#include "../include/controller/ble_ll_sched.h" +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_ll_resolv.h" +#include "../include/controller/ble_ll_rfmgmt.h" + +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "nimble/nimble/include/nimble/ble_hci_trans.h" + +#include "ble_ll_conn_priv.h" + +#include "nimble/porting/nimble/include/stats/stats.h" + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) + +/* defines number of events that can be lost during sync establishment + * before failed to be established error is reported + */ +#define BLE_LL_SYNC_ESTABLISH_CNT 6 + +#define BLE_LL_SYNC_CNT MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_CNT) +#define BLE_LL_SYNC_LIST_CNT MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_LIST_CNT) + +#define BLE_LL_SYNC_SM_FLAG_RESERVED 0x0001 +#define BLE_LL_SYNC_SM_FLAG_ESTABLISHING 0x0002 +#define BLE_LL_SYNC_SM_FLAG_ESTABLISHED 0x0004 +#define BLE_LL_SYNC_SM_FLAG_SET_ANCHOR 0x0008 +#define BLE_LL_SYNC_SM_FLAG_OFFSET_300 0x0010 +#define BLE_LL_SYNC_SM_FLAG_SYNC_INFO 0x0020 +#define BLE_LL_SYNC_SM_FLAG_DISABLED 0x0040 +#define BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED 0x0080 +#define BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED 0x0100 + +#define BLE_LL_SYNC_CHMAP_LEN 5 +#define BLE_LL_SYNC_ITVL_USECS 1250 + +struct ble_ll_sync_sm { + uint16_t flags; + + uint8_t adv_sid; + uint8_t adv_addr[BLE_DEV_ADDR_LEN]; + uint8_t adv_addr_type; + + uint8_t sca; + uint8_t chanmap[BLE_LL_SYNC_CHMAP_LEN]; + uint8_t num_used_chans; + + uint8_t chan_index; + uint8_t chan_chain; + + uint8_t phy_mode; + + uint8_t sync_pending_cnt; + + uint32_t timeout; + uint16_t skip; + + uint16_t itvl; + uint8_t itvl_usecs; + uint32_t itvl_ticks; + + uint32_t crcinit; /* only 3 bytes are used */ + uint32_t access_addr; + uint16_t event_cntr; + uint16_t channel_id; + + uint32_t window_widening; + uint32_t last_anchor_point; + uint32_t anchor_point; + uint8_t anchor_point_usecs; + + struct ble_ll_sched_item sch; + + struct ble_npl_event sync_ev_end; + + uint8_t *next_report; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + struct ble_ll_conn_sm *transfer_conn; + uint8_t *transfer_received_ev; + uint16_t transfer_id; + uint16_t event_cntr_last_received; + uint8_t adv_addr_rpa[6]; +#endif +}; + +static struct ble_ll_sync_sm g_ble_ll_sync_sm[BLE_LL_SYNC_CNT]; + +static struct { + uint8_t adv_sid; + uint8_t adv_addr[BLE_DEV_ADDR_LEN]; + uint8_t adv_addr_type; +} g_ble_ll_sync_adv_list[BLE_LL_SYNC_LIST_CNT]; + +static struct { + uint32_t timeout; + uint16_t max_skip; + uint16_t options; +} g_ble_ll_sync_create_params; + +/* if this is set HCI LE Sync Create is pending */ +static uint8_t *g_ble_ll_sync_create_comp_ev; + +static struct ble_ll_sync_sm *g_ble_ll_sync_sm_current; + +static int +ble_ll_sync_on_list(const uint8_t *addr, uint8_t addr_type, uint8_t sid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + if ((g_ble_ll_sync_adv_list[i].adv_sid == sid) && + (g_ble_ll_sync_adv_list[i].adv_addr_type == addr_type) && + !memcmp(g_ble_ll_sync_adv_list[i].adv_addr, addr, BLE_DEV_ADDR_LEN)) { + return i; + } + } + + return -1; +} + +static int +ble_ll_sync_list_get_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + if (g_ble_ll_sync_adv_list[i].adv_sid == 0xff) { + return i; + } + } + + return -1; +} + +static bool +ble_ll_sync_list_empty(void) { + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + if (g_ble_ll_sync_adv_list[i].adv_sid != 0xff) { + return false; + } + } + + return true; +} + +static uint8_t +ble_ll_sync_get_handle(struct ble_ll_sync_sm *sm) +{ + /* handle number is offset in global array */ + return sm - g_ble_ll_sync_sm; +} + +static void +ble_ll_sync_sm_clear(struct ble_ll_sync_sm *sm) +{ + if (sm->flags & (BLE_LL_SYNC_SM_FLAG_ESTABLISHING | + BLE_LL_SYNC_SM_FLAG_ESTABLISHED)) { + ble_ll_sched_rmv_elem(&sm->sch); + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &sm->sync_ev_end); + } + + if (sm->next_report) { + ble_hci_trans_buf_free(sm->next_report); + } + + if (g_ble_ll_sync_sm_current == sm) { + ble_phy_disable(); + ble_ll_state_set(BLE_LL_STATE_STANDBY); + g_ble_ll_sync_sm_current = NULL; + ble_ll_scan_chk_resume(); + } + + ble_ll_rfmgmt_release(); + + BLE_LL_ASSERT(sm->sync_ev_end.ev.ev_queued == 0); + BLE_LL_ASSERT(sm->sch.enqueued == 0); + memset(sm, 0, sizeof(*sm)); +} + +static uint8_t +ble_ll_sync_phy_mode_to_hci(int8_t phy_mode) +{ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + switch (phy_mode) { + case BLE_PHY_MODE_1M: + return BLE_HCI_LE_PHY_1M; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_MODE_2M: + return BLE_HCI_LE_PHY_2M; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_MODE_CODED_125KBPS: + case BLE_PHY_MODE_CODED_500KBPS: + return BLE_HCI_LE_PHY_CODED; +#endif + default: + BLE_LL_ASSERT(false); + return BLE_PHY_MODE_1M; + } +#else + return BLE_PHY_MODE_1M; +#endif +} + +static struct ble_ll_sync_sm * +ble_ll_sync_find(const uint8_t *addr, uint8_t addr_type, uint8_t sid) +{ + struct ble_ll_sync_sm *sm; + int i; + + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + sm = &g_ble_ll_sync_sm[i]; + + if (!sm->flags) { + continue; + } + if ((sm->adv_sid == sid) && (sm->adv_addr_type == addr_type) && + !memcmp(&sm->adv_addr, addr, BLE_DEV_ADDR_LEN)) { + return sm; + } + } + + return NULL; +} + +static uint16_t +get_max_skip(uint32_t interval_us, uint32_t timeout_us) +{ + uint16_t max_skip; + + BLE_LL_ASSERT(interval_us); + BLE_LL_ASSERT(timeout_us); + + if (timeout_us <= interval_us) { + return 0; + } + + /* + * Calculate max allowed skip to receive something before timeout. We adjust + * current skip value to be no more than max_skip-6 so we have at least few + * attempts to receive an event (so we don't timeout immediately after just + * one missed event). + */ + + max_skip = (timeout_us / interval_us) - 1; + + if (max_skip < 6) { + return 0; + } + + return max_skip - 6; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +static void +ble_ll_sync_transfer_received(struct ble_ll_sync_sm *sm, uint8_t status) +{ + struct ble_hci_ev_le_subev_periodic_adv_sync_transfer *ev; + struct ble_hci_ev *hci_ev; + + BLE_LL_ASSERT(sm->transfer_received_ev); + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER)) { + hci_ev = (void *) sm->transfer_received_ev; + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + + ev = (void *) hci_ev->data; + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER; + + ev->status = status; + ev->conn_handle = htole16(sm->transfer_conn->conn_handle); + ev->service_data = htole16(sm->transfer_id); + + /* this is ignored by host on error */ + ev->sync_handle = htole16(ble_ll_sync_get_handle(sm)); + ev->sid = sm->adv_sid; + ev->peer_addr_type = sm->adv_addr_type; + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED) { + ev->peer_addr_type += 2; + } + memcpy(ev->peer_addr, sm->adv_addr, BLE_DEV_ADDR_LEN); + ev->phy = ble_ll_sync_phy_mode_to_hci(sm->phy_mode); + ev->interval = htole16(sm->itvl); + ev->aca = sm->sca; + + ble_ll_hci_event_send(hci_ev); + } else { + ble_hci_trans_buf_free(sm->transfer_received_ev); + } + + sm->transfer_received_ev = NULL; + sm->transfer_conn = NULL; +} +#endif + +static void +ble_ll_sync_est_event_success(struct ble_ll_sync_sm *sm) +{ + struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev; + struct ble_hci_ev *hci_ev; + + BLE_LL_ASSERT(g_ble_ll_sync_create_comp_ev); + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB)) { + hci_ev = (void *) g_ble_ll_sync_create_comp_ev; + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB; + ev->status = BLE_ERR_SUCCESS; + ev->sync_handle = htole16(ble_ll_sync_get_handle(sm)); + ev->sid = sm->adv_sid; + ev->peer_addr_type = sm->adv_addr_type; + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED) { + ev->peer_addr_type += 2; + } + memcpy(ev->peer_addr, sm->adv_addr, BLE_DEV_ADDR_LEN); + ev->phy = ble_ll_sync_phy_mode_to_hci(sm->phy_mode); + ev->interval = htole16(sm->itvl); + ev->aca = sm->sca; + + ble_ll_hci_event_send(hci_ev); + } else { + ble_hci_trans_buf_free(g_ble_ll_sync_create_comp_ev); + } + + g_ble_ll_sync_create_comp_ev = NULL; +} + +static void +ble_ll_sync_est_event_failed(uint8_t status) +{ + struct ble_hci_ev_le_subev_periodic_adv_sync_estab *ev; + struct ble_hci_ev *hci_ev; + + BLE_LL_ASSERT(g_ble_ll_sync_create_comp_ev); + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB)) { + hci_ev = (void *) g_ble_ll_sync_create_comp_ev; + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + memset(ev, 0, sizeof(*ev)); + + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB; + ev->status = status; + + ble_ll_hci_event_send(hci_ev); + } else { + ble_hci_trans_buf_free(g_ble_ll_sync_create_comp_ev); + } + + g_ble_ll_sync_create_comp_ev = NULL; +} + +static void +ble_ll_sync_lost_event(struct ble_ll_sync_sm *sm) +{ + struct ble_hci_ev_le_subev_periodic_adv_sync_lost *ev; + struct ble_hci_ev *hci_ev; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST)) { + hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (hci_ev) { + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST; + ev->sync_handle = htole16(ble_ll_sync_get_handle(sm)); + + ble_ll_hci_event_send(hci_ev); + } + } +} + +static void +ble_ll_sync_current_sm_over(void) +{ + /* Disable the PHY */ + ble_phy_disable(); + + /* Link-layer is in standby state now */ + ble_ll_state_set(BLE_LL_STATE_STANDBY); + + /* Set current LL sync to NULL */ + g_ble_ll_sync_sm_current = NULL; +} + +static int +ble_ll_sync_event_start_cb(struct ble_ll_sched_item *sch) +{ + struct ble_ll_sync_sm *sm; + uint32_t wfr_usecs; + uint32_t start; + int rc; + + /* Set current connection state machine */ + sm = sch->cb_arg; + BLE_LL_ASSERT(sm); + + g_ble_ll_sync_sm_current = sm; + + /* Disable whitelisting */ + ble_ll_whitelist_disable(); + + /* Set LL state */ + ble_ll_state_set(BLE_LL_STATE_SYNC); + + /* Set channel */ + ble_phy_setchan(sm->chan_index, sm->access_addr, sm->crcinit); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_phy_resolv_list_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_set(sm->phy_mode, sm->phy_mode); +#endif + + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_rx_set_start_time(start, sch->remainder); + if (rc && rc != BLE_PHY_ERR_RX_LATE) { + STATS_INC(ble_ll_stats, sync_event_failed); + rc = BLE_LL_SCHED_STATE_DONE; + ble_ll_event_send(&sm->sync_ev_end); + ble_ll_sync_current_sm_over(); + } else { + /* + * Set flag that tells to set last anchor point if a packet + * has been received. + */ + sm->flags |= BLE_LL_SYNC_SM_FLAG_SET_ANCHOR; + + /* Set WFR timer. + * If establishing we always adjust with offset unit. + * If this is first packet of sync (one that was pointed by from + * SyncInfo we don't adjust WFT with window widening. + */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + wfr_usecs = (sm->flags & BLE_LL_SYNC_SM_FLAG_OFFSET_300) ? 300 : 30; + if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_SYNC_INFO)) { + wfr_usecs += 2 * sm->window_widening; + } + } else { + wfr_usecs = 2 * sm->window_widening; + } + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usecs); + + rc = BLE_LL_SCHED_STATE_RUNNING; + } + + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_SYNC_INFO; + + return rc; +} + +/** + * Called when a receive PDU has started. + * + * Context: interrupt + * + * @return int + * < 0: A frame we dont want to receive. + * = 0: Continue to receive frame. Dont go from rx to tx + */ +int +ble_ll_sync_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr) +{ + BLE_LL_ASSERT(g_ble_ll_sync_sm_current); + + /* this also handles chains as those have same PDU type */ + if (pdu_type != BLE_ADV_PDU_TYPE_AUX_SYNC_IND) { + ble_ll_event_send(&g_ble_ll_sync_sm_current->sync_ev_end); + ble_ll_sync_current_sm_over(); + STATS_INC(ble_ll_stats, sched_invalid_pdu); + return -1; + } + + STATS_INC(ble_ll_stats, sync_received); + return 0; +} + +static int +ble_ll_sync_parse_ext_hdr(struct os_mbuf *om, uint8_t **aux, int8_t *tx_power) +{ + uint8_t *rxbuf = om->om_data; + uint8_t ext_hdr_flags; + uint8_t ext_hdr_len; + uint8_t *ext_hdr; + uint8_t pdu_len; + int i; + + pdu_len = rxbuf[1]; + if (pdu_len == 0) { + return -1; + } + ext_hdr_len = rxbuf[2] & 0x3F; + if (ext_hdr_len > (pdu_len - 1)) { + return -1; + } + + if (ext_hdr_len) { + ext_hdr_flags = rxbuf[3]; + ext_hdr = &rxbuf[4]; + + i = 0; + + /* there should be no AdvA in Sync or chain, skip it */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { + i += BLE_LL_EXT_ADV_ADVA_SIZE; + } + + /* there should be no TargetA in Sync or chain, skip it */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { + i += BLE_LL_EXT_ADV_TARGETA_SIZE; + } + + /* Ignore CTE for now */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { + i += 1; + } + + /* there should be no ADI in Sync or chain, skip it */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { + i += BLE_LL_EXT_ADV_DATA_INFO_SIZE; + } + + /* get AuXPTR if present */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { + *aux = ext_hdr + i; + i += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + } + + /* there should be no SyncInfo in Sync or chain, skip it */ + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { + i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; + } + + if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { + *tx_power = *(ext_hdr + i); + i += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + /* TODO Handle ACAD if needed */ + + /* sanity check */ + if (i > ext_hdr_len) { + return -1; + } + } + + return pdu_len - ext_hdr_len - 1; +} + +static void +ble_ll_sync_adjust_ext_hdr(struct os_mbuf *om) +{ + uint8_t *rxbuf = om->om_data; + uint8_t ext_hdr_len; + + /* this was already verified in ble_ll_sync_parse_ext_hdr() */ + ext_hdr_len = rxbuf[2] & 0x3F; + + os_mbuf_adj(om, 3 + ext_hdr_len); +} + +static void +ble_ll_sync_send_truncated_per_adv_rpt(struct ble_ll_sync_sm *sm, uint8_t *evbuf) +{ + struct ble_hci_ev_le_subev_periodic_adv_rpt *ev; + struct ble_hci_ev *hci_ev; + + if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT) || + (sm->flags & BLE_LL_SYNC_SM_FLAG_DISABLED)) { + ble_hci_trans_buf_free(evbuf); + return; + } + + hci_ev = (void *) evbuf; + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT; + ev->sync_handle = htole16(ble_ll_sync_get_handle(sm)); + ev->tx_power = 127; /* not available */ + ev->rssi = 127; /* not available */ + ev->cte_type = 0xff; + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED; + ev->data_len = 0; + + ble_ll_hci_event_send(hci_ev); +} + +static void +ble_ll_sync_send_per_adv_rpt(struct ble_ll_sync_sm *sm, struct os_mbuf *rxpdu, + int8_t rssi, int8_t tx_power, int datalen, + uint8_t *aux, bool aux_scheduled) +{ + struct ble_hci_ev_le_subev_periodic_adv_rpt *ev; + struct ble_hci_ev *hci_ev; + struct ble_hci_ev *hci_ev_next = NULL; + uint8_t max_data_len; + int offset; + + /* use next report buffer if present, this means we are chaining */ + if (sm->next_report) { + hci_ev = (void *) sm->next_report; + sm->next_report = NULL; + } else { + hci_ev = (void * )ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (!hci_ev) { + goto done; + } + } + + max_data_len = BLE_LL_MAX_EVT_LEN - sizeof(*hci_ev) - sizeof(*ev); + offset = 0; + + do { + if (hci_ev_next) { + hci_ev = hci_ev_next; + hci_ev_next = NULL; + } + + hci_ev->opcode = BLE_HCI_EVCODE_LE_META; + hci_ev->length = sizeof(*ev); + + ev = (void *) hci_ev->data; + + ev->subev_code = BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT; + ev->sync_handle = htole16(ble_ll_sync_get_handle(sm)); + ev->tx_power = tx_power; + ev->rssi = rssi; + ev->cte_type = 0xff; + + ev->data_len = min(max_data_len, datalen - offset); + /* adjust event length */ + hci_ev->length += ev->data_len; + + os_mbuf_copydata(rxpdu, offset, ev->data_len, ev->data); + offset += ev->data_len; + + /* Need another event for next fragment of this PDU */ + if (offset < datalen) { + hci_ev_next = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (hci_ev_next) { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE; + } else { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED; + } + } else { + /* last report of this PDU */ + if (aux) { + if (aux_scheduled) { + /* if we scheduled aux, we need buffer for next report */ + hci_ev_next = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); + if (hci_ev_next) { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE; + } else { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED; + } + } else { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED; + } + } else { + ev->data_status = BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE; + } + } + ble_ll_hci_event_send(hci_ev); + } while ((offset < datalen) && hci_ev_next); + +done: + /* this means that we already truncated data (or didn't sent first at all) + * in HCI report but has scheduled for next PDU in chain. In that case mark + * it so that we end event properly when next PDU is received. + * */ + if (aux_scheduled && !hci_ev_next) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED; + } + + /* store for chain */ + sm->next_report = (void *) hci_ev_next; +} + +/** + * Called when a receive PDU has ended. + * + * Context: Interrupt + * + * @param rxpdu + * + * @return int + * < 0: Disable the phy after reception. + * == 0: Success. Do not disable the PHY. + * > 0: Do not disable PHY as that has already been done. + */ +int +ble_ll_sync_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) +{ + struct ble_mbuf_hdr *ble_hdr; + struct os_mbuf *rxpdu; + + BLE_LL_ASSERT(g_ble_ll_sync_sm_current); + + /* type was verified in isr_start */ + + rxpdu = ble_ll_rxpdu_alloc(rxbuf[1] + BLE_LL_PDU_HDR_LEN); + if (rxpdu) { + ble_phy_rxpdu_copy(rxbuf, rxpdu); + + ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); + ble_hdr->rxinfo.user_data = g_ble_ll_sync_sm_current; + + ble_ll_rx_pdu_in(rxpdu); + } else { + STATS_INC(ble_ll_stats, sync_rx_buf_err); + ble_ll_event_send(&g_ble_ll_sync_sm_current->sync_ev_end); + } + + /* PHY is disabled here */ + ble_ll_sync_current_sm_over(); + + return 1; +} + +/** + * Called when the wait for response timer expires while in the sync state. + * + * Context: Interrupt. + */ +void +ble_ll_sync_wfr_timer_exp(void) +{ + struct ble_ll_sync_sm *sm = g_ble_ll_sync_sm_current; + + BLE_LL_ASSERT(g_ble_ll_sync_sm_current); + STATS_INC(ble_ll_stats, sync_missed_err); + + ble_ll_sync_current_sm_over(); + ble_ll_event_send(&sm->sync_ev_end); +} + +/** + * Called when sync event needs to be halted. This normally should not be called + * and is only called when a scheduled item executes but scanning for sync/chain + * is stil ongoing + * Context: Interrupt + */ +void +ble_ll_sync_halt(void) +{ + struct ble_ll_sync_sm *sm = g_ble_ll_sync_sm_current; + + ble_ll_sync_current_sm_over(); + + if (sm) { + ble_ll_event_send(&sm->sync_ev_end); + } +} + +uint32_t +ble_ll_sync_get_event_end_time(void) +{ + uint32_t end_time; + + if (g_ble_ll_sync_sm_current) { + end_time = g_ble_ll_sync_sm_current->sch.end_time; + } else { + end_time = os_cputime_get32(); + } + return end_time; +} + +static uint8_t +ble_ll_sync_phy_mode_to_aux_phy(uint8_t phy_mode) +{ + switch (phy_mode) { + case BLE_PHY_MODE_1M: + return 0x00; + case BLE_PHY_MODE_2M: + return 0x01; + case BLE_PHY_MODE_CODED_125KBPS: + case BLE_PHY_MODE_CODED_500KBPS: + return 0x02; + default: + BLE_LL_ASSERT(false); + return 0x00; + } +} + +static void +ble_ll_sync_parse_aux_ptr(const uint8_t *buf, uint8_t *chan, uint32_t *offset, + uint8_t *offset_units, uint8_t *phy) +{ + uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF; + + *chan = aux_ptr_field & 0x3F; + + /* TODO use CA aux_ptr_field >> 6 */ + + if ((aux_ptr_field >> 7) & 0x01) { + *offset = 300 * ((aux_ptr_field >> 8) & 0x1FFF); + *offset_units = 1; + } else { + *offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF); + *offset_units = 0; + } + + *phy = (aux_ptr_field >> 21) & 0x07; +} + +static int +ble_ll_sync_chain_start_cb(struct ble_ll_sched_item *sch) +{ + struct ble_ll_sync_sm *sm; + uint32_t wfr_usecs; + uint32_t start; + int rc; + + /* Set current connection state machine */ + sm = sch->cb_arg; + g_ble_ll_sync_sm_current = sm; + BLE_LL_ASSERT(sm); + + /* Disable whitelisting */ + ble_ll_whitelist_disable(); + + /* Set LL state */ + ble_ll_state_set(BLE_LL_STATE_SYNC); + + /* Set channel */ + ble_phy_setchan(sm->chan_chain, sm->access_addr, sm->crcinit); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + ble_phy_resolv_list_disable(); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + ble_phy_encrypt_disable(); +#endif + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_set(sm->phy_mode, sm->phy_mode); +#endif + + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_rx_set_start_time(start, sch->remainder); + if (rc && rc != BLE_PHY_ERR_RX_LATE) { + STATS_INC(ble_ll_stats, sync_chain_failed); + rc = BLE_LL_SCHED_STATE_DONE; + ble_ll_event_send(&sm->sync_ev_end); + ble_ll_sync_current_sm_over(); + } else { + /* + * Clear flag that tells to set last anchor point if a packet + * has been received, this is chain and we don't need it. + */ + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_SET_ANCHOR; + + wfr_usecs = (sm->flags & BLE_LL_SYNC_SM_FLAG_OFFSET_300) ? 300 : 30; + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usecs); + rc = BLE_LL_SCHED_STATE_RUNNING; + } + + return rc; +} + +static int +ble_ll_sync_schedule_chain(struct ble_ll_sync_sm *sm, struct ble_mbuf_hdr *hdr, + const uint8_t *aux) +{ + uint8_t offset_units; + uint32_t offset; + uint8_t chan; + uint8_t phy; + + ble_ll_sync_parse_aux_ptr(aux, &chan, &offset, &offset_units, &phy); + + if (chan >= BLE_PHY_NUM_DATA_CHANS) { + return -1; + } + + if (offset < BLE_LL_MAFS) { + return -1; + } + + /* chain should use same PHY as master PDU */ + if (phy != ble_ll_sync_phy_mode_to_aux_phy(sm->phy_mode)) { + return -1; + } + + if (offset_units) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } else { + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } + + sm->chan_chain = chan; + + sm->sch.sched_cb = ble_ll_sync_chain_start_cb; + sm->sch.cb_arg = sm; + sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC; + + return ble_ll_sched_sync(&sm->sch, hdr->beg_cputime, hdr->rem_usecs, + offset, sm->phy_mode); +} + +static void +ble_ll_sync_established(struct ble_ll_sync_sm *sm) +{ + BLE_LL_ASSERT(sm->sync_pending_cnt); + + /* mark as established */ + + sm->flags |= BLE_LL_SYNC_SM_FLAG_ESTABLISHED; + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_ESTABLISHING; + + sm->sync_pending_cnt = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + if (sm->transfer_conn) { + ble_ll_sync_transfer_received(sm, BLE_ERR_SUCCESS); + return; + } +#endif + + ble_ll_sync_est_event_success(sm); +} + +static void +ble_ll_sync_check_failed(struct ble_ll_sync_sm *sm) +{ + BLE_LL_ASSERT(sm->sync_pending_cnt); + + /* if we can retry on next event */ + if (--sm->sync_pending_cnt) { + return; + } + + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_ESTABLISHING; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + if (sm->transfer_conn) { + ble_ll_sync_transfer_received(sm, BLE_ERR_CONN_ESTABLISHMENT); + return; + } +#endif + + ble_ll_sync_est_event_failed(BLE_ERR_CONN_ESTABLISHMENT); +} + +void +ble_ll_sync_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr) +{ + struct ble_ll_sync_sm *sm = hdr->rxinfo.user_data; + bool aux_scheduled = false; + int8_t tx_power = 127; /* defaults to not available */ + uint8_t *aux = NULL; + int datalen; + + BLE_LL_ASSERT(sm); + + /* this could happen if sync was cancelled or terminated while pkt_in was + * already in LL queue, just drop in that case + */ + if (!sm->flags) { + ble_ll_scan_chk_resume(); + ble_ll_rfmgmt_release(); + return; + } + + /* Set anchor point (and last) if 1st rxd frame in sync event. + * According to spec this should be done even if CRC is not valid so we + * can store it here + */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_SET_ANCHOR) { + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_SET_ANCHOR; + + sm->anchor_point = hdr->beg_cputime; + sm->anchor_point_usecs = hdr->rem_usecs; + sm->last_anchor_point = sm->anchor_point; + } + + /* CRC error, end event */ + if (!BLE_MBUF_HDR_CRC_OK(hdr)) { + STATS_INC(ble_ll_stats, sync_crc_err); + goto end_event; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + /* save last pa counter */ + sm->event_cntr_last_received = sm->event_cntr; +#endif + + /* this means we are chaining but due to low buffers already sent data + * truncated report to host (or didn't sent any at all). If this happens + * next_buf should be already set to NULL and we just end event. + */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED) { + BLE_LL_ASSERT(!sm->next_report); + goto end_event; + } + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT) && + !(sm->flags & BLE_LL_SYNC_SM_FLAG_DISABLED)) { + /* get ext header data */ + datalen = ble_ll_sync_parse_ext_hdr(rxpdu, &aux, &tx_power); + if (datalen < 0) { + /* we got bad packet, end event */ + goto end_event; + } + + /* if aux is present, we need to schedule ASAP */ + if (aux && (ble_ll_sync_schedule_chain(sm, hdr, aux) == 0)) { + aux_scheduled = true; + } + + /* in case data reporting is enabled we need to send sync established here */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + ble_ll_sync_established(sm); + } + + /* Adjust rxpdu to contain advertising data only */ + ble_ll_sync_adjust_ext_hdr(rxpdu); + + /* send reports from this PDU */ + ble_ll_sync_send_per_adv_rpt(sm, rxpdu, hdr->rxinfo.rssi, tx_power, + datalen, aux, aux_scheduled); + } else { + /* we need to establish link even if reporting was disabled */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + ble_ll_sync_established(sm); + } + } + + /* if chain was scheduled we don't end event yet */ + /* TODO should we check resume only if offset is high? */ + if (aux_scheduled) { + ble_ll_scan_chk_resume(); + ble_ll_rfmgmt_release(); + return; + } + +end_event: + ble_ll_event_send(&sm->sync_ev_end); + ble_ll_rfmgmt_release(); +} + +static int +ble_ll_sync_next_event(struct ble_ll_sync_sm *sm, uint32_t cur_ww_adjust) +{ + uint32_t cur_ww; + uint32_t max_ww; + uint32_t ticks; + uint32_t itvl; + uint8_t usecs; + uint16_t skip = sm->skip; + + /* don't skip if are establishing sync or we missed last event */ + if (skip && ((sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) || + CPUTIME_LT(sm->last_anchor_point, sm->anchor_point))) { + skip = 0; + } + + /* Set next event start time, we can use pre-calculated values for one + * interval if not skipping + */ + if (skip == 0) { + ticks = sm->itvl_ticks; + usecs = sm->itvl_usecs; + } else { + itvl = sm->itvl * BLE_LL_SYNC_ITVL_USECS * (1 + skip); + ticks = os_cputime_usecs_to_ticks(itvl); + usecs = itvl - os_cputime_ticks_to_usecs(ticks); + } + + sm->anchor_point += ticks; + sm->anchor_point_usecs += usecs; + if (sm->anchor_point_usecs >= 31) { + sm->anchor_point++; + sm->anchor_point_usecs -= 31; + } + + /* Set event counter to the next event */ + sm->event_cntr += 1 + skip; + + /* Calculate channel index of next event */ + sm->chan_index = ble_ll_utils_calc_dci_csa2(sm->event_cntr, sm->channel_id, + sm->num_used_chans, sm->chanmap); + + cur_ww = ble_ll_utils_calc_window_widening(sm->anchor_point, + sm->last_anchor_point, + sm->sca); + + cur_ww += cur_ww_adjust; + + max_ww = (sm->itvl * (BLE_LL_SYNC_ITVL_USECS / 2)) - BLE_LL_IFS; + if (cur_ww >= max_ww) { + return -1; + } + + cur_ww += BLE_LL_JITTER_USECS; + + /* if updated anchor is pass last anchor + timeout it means we will not be + * able to get it in time and hit sync timeout + * + * note that this may result in sync timeout being sent before real + * timeout but we won't be able to fit in time anyway.. + * + * We don't do that when establishing since we try up to + * BLE_LL_SYNC_ESTABLISH_CNT events before failing regardless of timeout + */ + if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING)) { + if (CPUTIME_GT(sm->anchor_point - os_cputime_usecs_to_ticks(cur_ww), + sm->last_anchor_point + sm->timeout )) { + return -1; + } + } + + sm->window_widening = cur_ww; + + return 0; +} + +static void +ble_ll_sync_event_end(struct ble_npl_event *ev) +{ + struct ble_ll_sync_sm *sm; + + /* Better be a connection state machine! */ + sm = ble_npl_event_get_arg(ev); + BLE_LL_ASSERT(sm); + + ble_ll_rfmgmt_release(); + + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + ble_ll_sync_check_failed(sm); + } + + /* Check if we need to resume scanning */ + ble_ll_scan_chk_resume(); + + /* Remove any end events that might be enqueued */ + ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &sm->sync_ev_end); + + /* don't schedule next event if sync is not established nor establishing + * at this point SM is no longer valid + */ + if (!(sm->flags & (BLE_LL_SYNC_SM_FLAG_ESTABLISHED | + BLE_LL_SYNC_SM_FLAG_ESTABLISHING))) { + ble_ll_sync_sm_clear(sm); + return; + } + + /* if we had prepared buffer for next even it means we were chaining and + * must send truncated report to host + */ + if (sm->next_report) { + BLE_LL_ASSERT(!(sm->flags & BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED)); + ble_ll_sync_send_truncated_per_adv_rpt(sm, sm->next_report); + sm->next_report = NULL; + } + + /* Event ended so we are no longer chaining */ + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED; + + sm->sch.sched_cb = ble_ll_sync_event_start_cb; + sm->sch.cb_arg = sm; + sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC; + + do { + if (ble_ll_sync_next_event(sm, 0) < 0) { + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + /* don't allow any retry if this failed */ + sm->sync_pending_cnt = 1; + ble_ll_sync_check_failed(sm); + } else { + ble_ll_sync_lost_event(sm); + } + + /* at this point SM is no longer valid */ + ble_ll_sync_sm_clear(sm); + return; + } + } while (ble_ll_sched_sync_reschedule(&sm->sch, sm->anchor_point, + sm->anchor_point_usecs, + sm->window_widening, sm->phy_mode)); +} + +void +ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, int rpa_index, + uint8_t sid, struct ble_mbuf_hdr *rxhdr, + const uint8_t *syncinfo) +{ + struct ble_ll_sync_sm *sm = NULL; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + const uint8_t *rpa = NULL; +#endif + uint16_t max_skip; + uint32_t offset; + uint32_t usecs; + uint16_t itvl; + int i; + + /* ignore if not synchronizing */ + if (!g_ble_ll_sync_create_comp_ev) { + return; + } + + /* get reserved SM */ + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + if (g_ble_ll_sync_sm[i].flags & BLE_LL_SYNC_SM_FLAG_RESERVED) { + sm = &g_ble_ll_sync_sm[i]; + break; + } + } + + /* this means we already got sync info event and pending sync */ + if (!sm) { + return; + } + + /* check if resolved */ + if (rpa_index >= 0) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + rpa = addr; +#endif + addr = g_ble_ll_resolv_list[rpa_index].rl_identity_addr; + addr_type = g_ble_ll_resolv_list[rpa_index].rl_addr_type; + } + + /* check peer */ + if (g_ble_ll_sync_create_params.options & BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_FILTER) { + if (ble_ll_sync_on_list(addr, addr_type, sid) < 0) { + return; + } + + /* set addr and sid in sm */ + sm->adv_sid = sid; + sm->adv_addr_type = addr_type; + memcpy(sm->adv_addr, addr, BLE_DEV_ADDR_LEN); + } else { + if ((sm->adv_sid != sid) || (sm->adv_addr_type != addr_type) || + memcmp(sm->adv_addr, addr, BLE_DEV_ADDR_LEN)) { + return; + } + } + + /* Sync Packet Offset (13 bits), Offset Units (1 bit), RFU (2 bits) */ + offset = syncinfo[0]; + offset |= (uint16_t)(syncinfo[1] & 0x1f) << 8; + + /* ignore if offset is not valid */ + if (!offset) { + return; + } + + /* Interval (2 bytes), ignore if invalid */ + itvl = get_le16(&syncinfo[2]); + if (itvl < 6) { + return; + } + + if (rpa_index >= 0) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) + memcpy(sm->adv_addr_rpa, rpa, BLE_DEV_ADDR_LEN); +#endif + } + + /* set params from HCI LE Create Periodic Sync */ + sm->timeout = g_ble_ll_sync_create_params.timeout; + sm->skip = g_ble_ll_sync_create_params.max_skip; + sm->sync_pending_cnt = BLE_LL_SYNC_ESTABLISH_CNT; + + if (syncinfo[1] & 0x20) { + offset *= 300; + sm->flags |= BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } else { + offset *= 30; + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } + + /* sync end event */ + ble_npl_event_init(&sm->sync_ev_end, ble_ll_sync_event_end, sm); + + sm->itvl = itvl; + + /* precalculate interval ticks and usecs */ + usecs = sm->itvl * BLE_LL_SYNC_ITVL_USECS; + sm->itvl_ticks = os_cputime_usecs_to_ticks(usecs); + sm->itvl_usecs = (uint8_t)(usecs - + os_cputime_ticks_to_usecs(sm->itvl_ticks)); + if (sm->itvl_usecs == 31) { + sm->itvl_usecs = 0; + sm->itvl_ticks++; + } + + /* Channels Mask (37 bits) */ + sm->chanmap[0] = syncinfo[4]; + sm->chanmap[1] = syncinfo[5]; + sm->chanmap[2] = syncinfo[6]; + sm->chanmap[3] = syncinfo[7]; + sm->chanmap[4] = syncinfo[8] & 0x1f; + sm->num_used_chans = ble_ll_utils_calc_num_used_chans(sm->chanmap); + + /* SCA (3 bits) */ + sm->sca = syncinfo[8] >> 5; + + /* AA (4 bytes) */ + sm->access_addr = get_le32(&syncinfo[9]); + sm->channel_id = ((sm->access_addr & 0xffff0000) >> 16) ^ + (sm->access_addr & 0x0000ffff); + + /* CRCInit (3 bytes) */ + sm->crcinit = syncinfo[15]; + sm->crcinit = (sm->crcinit << 8) | syncinfo[14]; + sm->crcinit = (sm->crcinit << 8) | syncinfo[13]; + + /* Event Counter (2 bytes) */ + sm->event_cntr = get_le16(&syncinfo[16]); + + /* adjust skip if pass timeout */ + max_skip = get_max_skip(sm->itvl * BLE_LL_SYNC_ITVL_USECS, sm->timeout); + if (sm->skip > max_skip) { + sm->skip = max_skip; + } + + /* from now on we only need timeout in ticks */ + sm->timeout = os_cputime_usecs_to_ticks(sm->timeout); + + sm->phy_mode = rxhdr->rxinfo.phy_mode; + sm->window_widening = BLE_LL_JITTER_USECS; + + /* Calculate channel index of first event */ + sm->chan_index = ble_ll_utils_calc_dci_csa2(sm->event_cntr, sm->channel_id, + sm->num_used_chans, sm->chanmap); + + sm->sch.sched_cb = ble_ll_sync_event_start_cb; + sm->sch.cb_arg = sm; + sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC; + + if (ble_ll_sched_sync(&sm->sch, rxhdr->beg_cputime, rxhdr->rem_usecs, + offset, sm->phy_mode)) { + return; + } + + sm->anchor_point = sm->sch.start_time + g_ble_ll_sched_offset_ticks; + sm->anchor_point_usecs = sm->sch.remainder; + sm->last_anchor_point = sm->anchor_point; + +#if MYNEWT_VAL(BLE_VERSION) >= 51 + if (g_ble_ll_sync_create_params.options & BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_DISABLED) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_DISABLED; + } +#endif + + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_RESERVED; + sm->flags |= BLE_LL_SYNC_SM_FLAG_ESTABLISHING; + sm->flags |= BLE_LL_SYNC_SM_FLAG_SYNC_INFO; +} + +static struct ble_ll_sync_sm * +ble_ll_sync_reserve(void) +{ + struct ble_ll_sync_sm *sm; + int i; + + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + sm = &g_ble_ll_sync_sm[i]; + + if (!sm->flags) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_RESERVED; + return sm; + } + } + + return NULL; +} + +int +ble_ll_sync_create(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_periodic_adv_create_sync_cp *cmd = (const void *) cmdbuf; + struct ble_ll_sync_sm *sm; + uint16_t timeout; + os_sr_t sr; + + if (g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + +#if MYNEWT_VAL(BLE_VERSION) >= 51 + if (cmd->options > BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_DISABLED) { +#else + if (cmd->options > BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_FILTER) { +#endif + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->skip > 0x01f3) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + timeout = le16toh(cmd->sync_timeout); + if (timeout < 0x000a || timeout > 0x4000) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + +#if MYNEWT_VAL(BLE_VERSION) >= 51 + /* we don't support any CTE yet */ + if (cmd->sync_cte_type) { + if (cmd->sync_cte_type > 4) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return BLE_ERR_UNSUPPORTED; + } +#endif + + if (cmd->options & BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_FILTER) { + if (ble_ll_sync_list_empty()) { + return BLE_ERR_CMD_DISALLOWED; + } + } else { + if (cmd->sid > 0x0f) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + OS_ENTER_CRITICAL(sr); + sm = ble_ll_sync_find(cmd->peer_addr, cmd->peer_addr_type, cmd->sid); + OS_EXIT_CRITICAL(sr); + + if (sm) { + return BLE_ERR_ACL_CONN_EXISTS; + } + } + + /* reserve buffer for sync complete event */ + g_ble_ll_sync_create_comp_ev = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_MEM_CAPACITY; + } + + OS_ENTER_CRITICAL(sr); + + /* reserve 1 SM for created sync */ + sm = ble_ll_sync_reserve(); + if (!sm) { + ble_hci_trans_buf_free(g_ble_ll_sync_create_comp_ev); + g_ble_ll_sync_create_comp_ev = NULL; + OS_EXIT_CRITICAL(sr); + return BLE_ERR_MEM_CAPACITY; + } + + /* if we don't use list, store expected address in reserved SM */ + if (!(cmd->options & BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_FILTER)) { + sm->adv_sid = cmd->sid; + sm->adv_addr_type = cmd->peer_addr_type; + memcpy(&sm->adv_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + } + + g_ble_ll_sync_create_params.timeout = timeout * 10000; /* 10ms units, store in us */; + g_ble_ll_sync_create_params.max_skip = cmd->skip; + g_ble_ll_sync_create_params.options = cmd->options; + + OS_EXIT_CRITICAL(sr); + return BLE_ERR_SUCCESS; +} + +static void +ble_ll_sync_cancel_complete_event(void) +{ + ble_ll_sync_est_event_failed(BLE_ERR_OPERATION_CANCELLED); +} + +int +ble_ll_sync_cancel(ble_ll_hci_post_cmd_complete_cb *post_cmd_cb) +{ + struct ble_ll_sync_sm *sm; + os_sr_t sr; + int i; + + if (!g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + OS_ENTER_CRITICAL(sr); + + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + sm = &g_ble_ll_sync_sm[i]; + + /* cancelled before fist sync info packet */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_RESERVED) { + memset(sm, 0, sizeof(*sm)); + break; + } + + /* cancelled while pending sync */ + if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) { + ble_ll_sync_sm_clear(sm); + break; + } + } + + OS_EXIT_CRITICAL(sr); + + /* g_ble_ll_sync_create_comp_ev will be cleared by this callback */ + *post_cmd_cb = ble_ll_sync_cancel_complete_event; + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_terminate(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_periodic_adv_term_sync_cp *cmd = (const void *) cmdbuf; + struct ble_ll_sync_sm *sm; + uint16_t handle; + os_sr_t sr; + + if (g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->sync_handle); + if (handle > 0xeff) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (handle >= BLE_LL_SYNC_CNT) { + return BLE_ERR_UNK_ADV_INDENT; + } + + sm = &g_ble_ll_sync_sm[handle]; + + OS_ENTER_CRITICAL(sr); + + if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHED)) { + OS_EXIT_CRITICAL(sr); + return BLE_ERR_UNK_ADV_INDENT; + } + + ble_ll_sync_sm_clear(sm); + + OS_EXIT_CRITICAL(sr); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_list_add(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_add_dev_to_periodic_adv_list_cp *cmd = (const void *)cmdbuf; + int i; + + if (g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (cmd->sid > 0x0f) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + i = ble_ll_sync_on_list(cmd->peer_addr, cmd->peer_addr_type, cmd->sid); + if (i >= 0) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + i = ble_ll_sync_list_get_free(); + if (i < 0) { + return BLE_ERR_MEM_CAPACITY; + } + + g_ble_ll_sync_adv_list[i].adv_sid = cmd->sid; + g_ble_ll_sync_adv_list[i].adv_addr_type = cmd->peer_addr_type; + memcpy(&g_ble_ll_sync_adv_list[i].adv_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_list_remove(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rem_dev_from_periodic_adv_list_cp *cmd = (const void *)cmdbuf; + int i; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + if (cmd->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->sid > 0x0f) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + i = ble_ll_sync_on_list(cmd->peer_addr, cmd->peer_addr_type, cmd->sid); + if (i < 0) { + return BLE_ERR_UNK_ADV_INDENT; + } + + memset(&g_ble_ll_sync_adv_list[i], 0, sizeof(g_ble_ll_sync_adv_list[i])); + g_ble_ll_sync_adv_list[i].adv_sid = 0xff; + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_list_clear(void) +{ + int i; + + if (g_ble_ll_sync_create_comp_ev) { + return BLE_ERR_CMD_DISALLOWED; + } + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + memset(&g_ble_ll_sync_adv_list[i], 0, sizeof(g_ble_ll_sync_adv_list[i])); + g_ble_ll_sync_adv_list[i].adv_sid = 0xff; + } + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_list_size(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_periodic_adv_list_size_rp *rsp = (void *) rspbuf; + + rsp->list_size = ARRAY_SIZE(g_ble_ll_sync_adv_list); + + *rsplen = sizeof(*rsp); + return BLE_ERR_SUCCESS; +} + +#if MYNEWT_VAL(BLE_VERSION) >= 51 +int +ble_ll_sync_receive_enable(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_periodic_adv_receive_enable_cp *cmd = (const void *)cmdbuf; + struct ble_ll_sync_sm *sm; + uint16_t handle; + os_sr_t sr; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (cmd->enable > 0x01) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + handle = le16toh(cmd->sync_handle); + if (handle > 0xeff) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + if (handle >= BLE_LL_SYNC_CNT) { + return BLE_ERR_UNK_ADV_INDENT; + } + + sm = &g_ble_ll_sync_sm[handle]; + + OS_ENTER_CRITICAL(sr); + + if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHED)) { + OS_EXIT_CRITICAL(sr); + return BLE_ERR_UNK_ADV_INDENT; + } + + if (cmd->enable) { + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_DISABLED; + } else { + sm->flags |= BLE_LL_SYNC_SM_FLAG_DISABLED; + } + + OS_EXIT_CRITICAL(sr); + return BLE_ERR_SUCCESS; +} +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER) +static struct ble_ll_sync_sm * +ble_ll_sync_transfer_get(const uint8_t *addr, uint8_t addr_type, uint8_t sid) +{ + struct ble_ll_sync_sm *sm; + int i; + + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + sm = &g_ble_ll_sync_sm[i]; + + if (!sm->flags) { + /* allocate event for transfer received event */ + sm->transfer_received_ev = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!sm->transfer_received_ev) { + break; + } + + sm->adv_sid = sid; + sm->adv_addr_type = addr_type; + memcpy(&sm->adv_addr, addr, BLE_DEV_ADDR_LEN); + + sm->flags |= BLE_LL_SYNC_SM_FLAG_ESTABLISHING; + return sm; + } + } + + return NULL; +} + +void +ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm, + const uint8_t *sync_ind, bool reports_disabled, + uint16_t max_skip, uint32_t sync_timeout) +{ + const uint8_t *syncinfo = sync_ind + 2; + uint16_t sync_conn_event_count; + uint16_t last_pa_event_count; + struct ble_ll_sync_sm *sm; + uint16_t conn_event_count; + uint8_t sync_anchor_usecs; + const uint8_t *rpa = NULL; + int last_pa_diff; + uint32_t sync_anchor; + const uint8_t *addr; + uint16_t event_cntr; + uint32_t itvl_usecs; + uint32_t ww_adjust; + uint8_t addr_type; + uint8_t phy_mode; + uint32_t offset; + uint32_t future; + uint16_t itvl; + int rpa_index; + uint8_t sid; + uint8_t sca; + os_sr_t sr; + + phy_mode = ble_ll_ctrl_phy_from_phy_mask(sync_ind[25]); + itvl = get_le16(syncinfo + 2); + /* ignore if sync params are not valid */ + if ((phy_mode == 0) || (itvl < 6)) { + return; + } + + last_pa_event_count = get_le16(sync_ind + 22); + event_cntr = get_le16(syncinfo + 16); + itvl_usecs = itvl * BLE_LL_SYNC_ITVL_USECS; + + last_pa_diff = abs((int16_t)(event_cntr - last_pa_event_count)); + /* check if not 5 seconds apart, if so ignore sync transfer */ + if ((last_pa_diff * itvl_usecs) > 5000000) { + return; + } + + sid = (sync_ind[24] & 0x0f); + addr_type = (sync_ind[24] & 0x10) ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + addr = sync_ind + 26; + + rpa_index = -1; + + /* check if need to resolve */ + if (ble_ll_is_rpa(addr, addr_type)) { + rpa_index = ble_ll_resolv_peer_rpa_any(addr); + if (rpa_index >= 0) { + rpa = addr; + addr = g_ble_ll_resolv_list[rpa_index].rl_identity_addr; + addr_type = g_ble_ll_resolv_list[rpa_index].rl_addr_type; + } + } + + OS_ENTER_CRITICAL(sr); + /* check if already synchronized with this peer */ + sm = ble_ll_sync_find(addr, addr_type, sid); + if (sm) { + OS_EXIT_CRITICAL(sr); + return; + } + + /* ignore if no memory for new sync */ + sm = ble_ll_sync_transfer_get(addr, addr_type, sid); + if (!sm) { + OS_EXIT_CRITICAL(sr); + return; + } + + OS_EXIT_CRITICAL(sr); + + if (rpa_index >= 0) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED; + memcpy(sm->adv_addr_rpa, rpa, BLE_DEV_ADDR_LEN); + } + + /* set params from transfer */ + sm->timeout = os_cputime_usecs_to_ticks(sync_timeout); + sm->skip = max_skip; + sm->sync_pending_cnt = BLE_LL_SYNC_ESTABLISH_CNT; + sm->transfer_id = get_le16(sync_ind); /* first two bytes */ + sm->transfer_conn = connsm; + + /* Sync Packet Offset (13 bits), Offset Units (1 bit), Offset Adjust (1 bit), + * RFU (1 bit) + */ + offset = syncinfo[0]; + offset |= (uint16_t)(syncinfo[1] & 0x1f) << 8; + + if (syncinfo[1] & 0x20) { + if (syncinfo[1] & 0x40) { + offset += 0x2000; + } + + offset *= 300; + sm->flags |= BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } else { + offset *= 30; + sm->flags &= ~BLE_LL_SYNC_SM_FLAG_OFFSET_300; + } + + /* sync end event */ + ble_npl_event_init(&sm->sync_ev_end, ble_ll_sync_event_end, sm); + + sm->itvl = itvl; + + /* precalculate interval ticks and usecs */ + sm->itvl_ticks = os_cputime_usecs_to_ticks(itvl_usecs); + sm->itvl_usecs = (uint8_t)(itvl_usecs - + os_cputime_ticks_to_usecs(sm->itvl_ticks)); + if (sm->itvl_usecs == 31) { + sm->itvl_usecs = 0; + sm->itvl_ticks++; + } + + /* Channels Mask (37 bits) */ + sm->chanmap[0] = syncinfo[4]; + sm->chanmap[1] = syncinfo[5]; + sm->chanmap[2] = syncinfo[6]; + sm->chanmap[3] = syncinfo[7]; + sm->chanmap[4] = syncinfo[8] & 0x1f; + sm->num_used_chans = ble_ll_utils_calc_num_used_chans(sm->chanmap); + + /* SCA (3 bits) */ + sm->sca = syncinfo[8] >> 5; + + /* AA (4 bytes) */ + sm->access_addr = get_le32(syncinfo + 9); + sm->channel_id = ((sm->access_addr & 0xffff0000) >> 16) ^ + (sm->access_addr & 0x0000ffff); + + /* CRCInit (3 bytes) */ + sm->crcinit = syncinfo[13]; + sm->crcinit |= syncinfo[14] << 8; + sm->crcinit |= syncinfo[15] << 16; + + /* Event Counter (2 bytes) */ + sm->event_cntr = event_cntr; + + /* adjust skip if pass timeout */ + max_skip = get_max_skip(sm->itvl * BLE_LL_SYNC_ITVL_USECS, sync_timeout); + if (sm->skip > max_skip) { + sm->skip = max_skip; + } + + sm->phy_mode = phy_mode; + + /* Calculate channel index of first event */ + sm->chan_index = ble_ll_utils_calc_dci_csa2(sm->event_cntr, sm->channel_id, + sm->num_used_chans, sm->chanmap); + + sm->sch.sched_cb = ble_ll_sync_event_start_cb; + sm->sch.cb_arg = sm; + sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC; + + /* get anchor for specified conn event */ + conn_event_count = get_le16(sync_ind + 20); + ble_ll_conn_get_anchor(connsm, conn_event_count, &sm->anchor_point, + &sm->anchor_point_usecs); + + /* Set last anchor point */ + sm->last_anchor_point = sm->anchor_point - (last_pa_diff * sm->itvl_ticks); + + /* calculate extra window widening */ + sync_conn_event_count = get_le16(sync_ind + 32); + sca = sync_ind[24] >> 5; + ble_ll_conn_get_anchor(connsm, sync_conn_event_count, &sync_anchor, + &sync_anchor_usecs); + ww_adjust = ble_ll_utils_calc_window_widening(connsm->anchor_point, + sync_anchor, sca); + + /* spin until we get anchor in future */ + future = os_cputime_get32() + g_ble_ll_sched_offset_ticks; + while (CPUTIME_LT(sm->anchor_point, future)) { + if (ble_ll_sync_next_event(sm, ww_adjust) < 0) { + /* release SM if this failed */ + ble_ll_sync_transfer_received(sm, BLE_ERR_CONN_ESTABLISHMENT); + memset(sm, 0, sizeof(*sm)); + return; + } + } + + if (ble_ll_sched_sync(&sm->sch, sm->anchor_point, sm->anchor_point_usecs, + offset, sm->phy_mode)) { + /* release SM if this failed */ + ble_ll_sync_transfer_received(sm, BLE_ERR_CONN_ESTABLISHMENT); + memset(sm, 0, sizeof(*sm)); + return; + } + + /* Set new anchor point */ + sm->anchor_point = sm->sch.start_time + g_ble_ll_sched_offset_ticks; + sm->anchor_point_usecs = sm->sch.remainder; + + if (reports_disabled) { + sm->flags |= BLE_LL_SYNC_SM_FLAG_DISABLED; + } +} + +static void +ble_ll_sync_put_syncinfo(struct ble_ll_sync_sm *syncsm, + struct ble_ll_conn_sm *connsm, uint8_t *conn_event_cnt, + uint8_t *dptr) +{ + uint8_t anchor_usecs; + uint16_t conn_cnt; + uint32_t offset; + uint32_t anchor; + uint8_t units; + + anchor = connsm->anchor_point; + anchor_usecs = connsm->anchor_point_usecs; + conn_cnt = connsm->event_cntr; + + /* get anchor for conn event that is before periodic_adv_event_start_time */ + while (CPUTIME_GT(anchor, syncsm->anchor_point)) { + ble_ll_conn_get_anchor(connsm, --conn_cnt, &anchor, &anchor_usecs); + } + + offset = os_cputime_ticks_to_usecs(syncsm->anchor_point - anchor); + offset -= anchor_usecs; + offset += syncsm->anchor_point_usecs; + + /* connEventCount */ + put_le16(conn_event_cnt, conn_cnt); + + /* Sync Packet Offset (13 bits), Offset Units (1 bit), Offset Adjust (1 bit), + * RFU (1 bit) + */ + if (offset > 245700) { + units = 0x20; + + if (offset >= 0x2000) { + offset -= 0x2000; + units |= 0x40; + } + + offset = offset / 300; + } else { + units = 0x00; + offset = offset / 30; + } + + dptr[0] = (offset & 0x000000ff); + dptr[1] = ((offset >> 8) & 0x0000001f) | units; + + /* Interval (2 bytes) */ + put_le16(&dptr[2], syncsm->itvl); + + /* Channels Mask (37 bits) */ + dptr[4] = syncsm->chanmap[0]; + dptr[5] = syncsm->chanmap[1]; + dptr[6] = syncsm->chanmap[2]; + dptr[7] = syncsm->chanmap[3]; + dptr[8] = syncsm->chanmap[4] & 0x1f; + + /* SCA (3 bits) */ + dptr[8] |= syncsm->sca << 5; + + /* AA (4 bytes) */ + put_le32(&dptr[9], syncsm->access_addr); + + /* CRCInit (3 bytes) */ + dptr[13] = (uint8_t)syncsm->crcinit; + dptr[14] = (uint8_t)(syncsm->crcinit >> 8); + dptr[15] = (uint8_t)(syncsm->crcinit >> 16); + + /* Event Counter (2 bytes) */ + put_le16(&dptr[16], syncsm->event_cntr); +} + +static int +ble_ll_sync_send_sync_ind(struct ble_ll_sync_sm *syncsm, + struct ble_ll_conn_sm *connsm, uint16_t service_data) +{ + struct os_mbuf *om; + uint8_t *sync_ind; + + om = os_msys_get_pkthdr(BLE_LL_CTRL_MAX_PDU_LEN, + sizeof(struct ble_mbuf_hdr)); + if (!om) { + return BLE_ERR_MEM_CAPACITY; + } + + om->om_data[0] = BLE_LL_CTRL_PERIODIC_SYNC_IND; + + sync_ind = om->om_data + 1; + + /* ID (service_data), already in LE order */ + memcpy(sync_ind, &service_data, sizeof(service_data)); + + /* fill in syncinfo */ + ble_ll_sync_put_syncinfo(syncsm, connsm, sync_ind + 20, sync_ind + 2); + + /* lastPaEventCounter */ + put_le16(sync_ind + 22, syncsm->event_cntr_last_received); + + /* SID, AType, SCA */ + sync_ind[24] = syncsm->adv_sid; + + if (syncsm->flags & BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED) { + sync_ind[24] |= 1 << 4; + } else { + sync_ind[24] |= (syncsm->adv_addr_type == BLE_ADDR_RANDOM) << 4 ; + } + + sync_ind[24] |= MYNEWT_VAL(BLE_LL_MASTER_SCA) << 5; + + /* PHY */ + sync_ind[25] = (0x01 << (ble_ll_sync_phy_mode_to_hci(syncsm->phy_mode) - 1)); + + /* AdvA */ + if (syncsm->flags & BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED) { + memcpy(sync_ind + 26, syncsm->adv_addr_rpa, BLE_DEV_ADDR_LEN); + } else { + memcpy(sync_ind + 26, syncsm->adv_addr, BLE_DEV_ADDR_LEN); + } + + /* syncConnEventCount */ + put_le16(sync_ind + 32, connsm->event_cntr); + + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, + BLE_LL_CTRL_PERIODIC_SYNC_IND_LEN + 1); + + return BLE_ERR_SUCCESS; +} + +int +ble_ll_sync_transfer(const uint8_t *cmdbuf, uint8_t len, + uint8_t *rspbuf, uint8_t *rsplen) +{ + const struct ble_hci_le_periodic_adv_sync_transfer_cp *cmd = (const void *)cmdbuf; + struct ble_hci_le_periodic_adv_sync_transfer_rp *rsp = (void *) rspbuf; + struct ble_ll_conn_sm *connsm; + struct ble_ll_sync_sm *sm; + uint16_t handle; + os_sr_t sr; + int rc; + + if (len != sizeof(*cmd)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + handle = le16toh(cmd->sync_handle); + if (handle > 0xeff) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } + + if (handle >= BLE_LL_SYNC_CNT) { + rc = BLE_ERR_UNK_ADV_INDENT; + goto done; + } + + sm = &g_ble_ll_sync_sm[handle]; + + OS_ENTER_CRITICAL(sr); + + if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHED)) { + rc = BLE_ERR_UNK_ADV_INDENT; + OS_EXIT_CRITICAL(sr); + goto done; + } + + handle = le16toh(cmd->conn_handle); + if (handle > 0xeff) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + OS_EXIT_CRITICAL(sr); + goto done; + } + + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + OS_EXIT_CRITICAL(sr); + goto done; + } + + /* TODO should not need to shift + * byte 3 (0 byte is conn_feature) , bit 1 + * + * Allow initiate LL procedure only if remote supports it. + */ + if (!(connsm->remote_features[2] & (BLE_LL_FEAT_SYNC_TRANS_RECV >> (8 * 3)))) { + rc = BLE_ERR_UNSUPP_REM_FEATURE; + goto done; + } + + rc = ble_ll_sync_send_sync_ind(sm, connsm, cmd->service_data); + + OS_EXIT_CRITICAL(sr); +done: + rsp->conn_handle = cmd->conn_handle; + *rsplen = sizeof(*rsp); + return rc; +} +#endif + +/* + * Called when a sync scan event has been removed from the scheduler + * without being run. + */ +void +ble_ll_sync_rmvd_from_sched(struct ble_ll_sync_sm *sm) +{ + ble_ll_event_send(&sm->sync_ev_end); +} + +bool +ble_ll_sync_enabled(void) +{ + return g_ble_ll_sync_create_comp_ev != NULL; +} + +/** + * Called to reset the sync module. When this function is called the + * scheduler has been stopped and the phy has been disabled. The LL should + * be in the standby state. + */ +void +ble_ll_sync_reset(void) +{ + int i; + + for (i = 0; i < BLE_LL_SYNC_CNT; i++) { + ble_ll_sync_sm_clear(&g_ble_ll_sync_sm[i]); + } + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + memset(&g_ble_ll_sync_adv_list[i], 0, sizeof(g_ble_ll_sync_adv_list[i])); + g_ble_ll_sync_adv_list[i].adv_sid = 0xff; + } + + g_ble_ll_sync_create_params.timeout = 0; + g_ble_ll_sync_create_params.max_skip = 0; + g_ble_ll_sync_create_params.options = 0; + + g_ble_ll_sync_sm_current = NULL; + + if (g_ble_ll_sync_create_comp_ev) { + ble_hci_trans_buf_free(g_ble_ll_sync_create_comp_ev); + g_ble_ll_sync_create_comp_ev = NULL; + } +} + +void +ble_ll_sync_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_ll_sync_adv_list); i++) { + g_ble_ll_sync_adv_list[i].adv_sid = 0xff; + } +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_trace.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_trace.c new file mode 100644 index 0000000..c5d2b1d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_trace.c @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +#if MYNEWT_VAL(BLE_LL_SYSVIEW) + +static os_trace_module_t g_ble_ll_trace_mod; +uint32_t ble_ll_trace_off; + +static void +ble_ll_trace_module_send_desc(void) +{ + os_trace_module_desc(&g_ble_ll_trace_mod, "0 ll_sched lls=%u cputime=%u start_time=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "1 ll_rx_start lls=%u pdu_type=%x"); + os_trace_module_desc(&g_ble_ll_trace_mod, "2 ll_rx_end pdu_type=%x len=%u flags=%x"); + os_trace_module_desc(&g_ble_ll_trace_mod, "3 ll_wfr_timer_exp lls=%u xcvr=%u rx_start=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "4 ll_ctrl_rx opcode=%u len=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "5 ll_conn_ev_start conn_handle=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "6 ll_conn_ev_end conn_handle=%u event_cntr=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "7 ll_conn_end conn_handle=%u event_cntr=%u err=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "8 ll_conn_tx len=%u offset=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "9 ll_conn_rx conn_sn=%u pdu_nesn=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "10 ll_adv_txdone inst=%u chanset=%x"); + os_trace_module_desc(&g_ble_ll_trace_mod, "11 ll_adv_halt inst=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "12 ll_aux_ref aux=%p ref=%u"); + os_trace_module_desc(&g_ble_ll_trace_mod, "13 ll_aux_unref aux=%p ref=%u"); +} + +void +ble_ll_trace_init(void) +{ + ble_ll_trace_off = + os_trace_module_register(&g_ble_ll_trace_mod, "ble_ll", 12, + ble_ll_trace_module_send_desc); +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_utils.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_utils.c new file mode 100644 index 0000000..3814e58 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_utils.c @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include "nimble/nimble/include/nimble/ble.h" +#include "../include/controller/ble_ll.h" +#include "../include/controller/ble_ll_utils.h" + +/* 37 bits require 5 bytes */ +#define BLE_LL_CHMAP_LEN (5) + +/* Sleep clock accuracy table (in ppm) */ +static const uint16_t g_ble_sca_ppm_tbl[8] = { + 500, 250, 150, 100, 75, 50, 30, 20 +}; + +uint32_t +ble_ll_utils_calc_access_addr(void) +{ + uint32_t aa; + uint16_t aa_low; + uint16_t aa_high; + uint32_t temp; + uint32_t mask; + uint32_t prev_bit; + uint8_t bits_diff; + uint8_t consecutive; + uint8_t transitions; + uint8_t ones; + int tmp; + + /* Calculate a random access address */ + aa = 0; + while (1) { + /* Get two, 16-bit random numbers */ + aa_low = rand() & 0xFFFF; + aa_high = rand() & 0xFFFF; + + /* All four bytes cannot be equal */ + if (aa_low == aa_high) { + continue; + } + + /* Upper 6 bits must have 2 transitions */ + tmp = (int16_t)aa_high >> 10; + if (__builtin_popcount(tmp ^ (tmp >> 1)) < 2) { + continue; + } + + /* Cannot be access address or be 1 bit different */ + aa = aa_high; + aa = (aa << 16) | aa_low; + bits_diff = 0; + temp = aa ^ BLE_ACCESS_ADDR_ADV; + for (mask = 0x00000001; mask != 0; mask <<= 1) { + if (mask & temp) { + ++bits_diff; + if (bits_diff > 1) { + break; + } + } + } + if (bits_diff <= 1) { + continue; + } + + /* Cannot have more than 24 transitions */ + transitions = 0; + consecutive = 1; + ones = 0; + mask = 0x00000001; + while (mask < 0x80000000) { + prev_bit = aa & mask; + mask <<= 1; + if (mask & aa) { + if (prev_bit == 0) { + ++transitions; + consecutive = 1; + } else { + ++consecutive; + } + } else { + if (prev_bit == 0) { + ++consecutive; + } else { + ++transitions; + consecutive = 1; + } + } + + if (prev_bit) { + ones++; + } + + /* 8 lsb should have at least three 1 */ + if (mask == 0x00000100 && ones < 3) { + break; + } + + /* 16 lsb should have no more than 11 transitions */ + if (mask == 0x00010000 && transitions > 11) { + break; + } + + /* This is invalid! */ + if (consecutive > 6) { + /* Make sure we always detect invalid sequence below */ + mask = 0; + break; + } + } + + /* Invalid sequence found */ + if (mask != 0x80000000) { + continue; + } + + /* Cannot be more than 24 transitions */ + if (transitions > 24) { + continue; + } + + /* We have a valid access address */ + break; + } + return aa; +} + +uint8_t +ble_ll_utils_remapped_channel(uint8_t remap_index, const uint8_t *chanmap) +{ + uint8_t cntr; + uint8_t mask; + uint8_t usable_chans; + uint8_t chan; + int i, j; + + /* NOTE: possible to build a map but this would use memory. For now, + * we just calculate + * Iterate through channel map to find this channel + */ + chan = 0; + cntr = 0; + for (i = 0; i < BLE_LL_CHMAP_LEN; i++) { + usable_chans = chanmap[i]; + if (usable_chans != 0) { + mask = 0x01; + for (j = 0; j < 8; j++) { + if (usable_chans & mask) { + if (cntr == remap_index) { + return (chan + j); + } + ++cntr; + } + mask <<= 1; + } + } + chan += 8; + } + + /* we should never reach here */ + BLE_LL_ASSERT(0); + return 0; +} + +uint8_t +ble_ll_utils_calc_num_used_chans(const uint8_t *chmap) +{ + int i; + int j; + uint8_t mask; + uint8_t chanbyte; + uint8_t used_channels; + + used_channels = 0; + for (i = 0; i < BLE_LL_CHMAP_LEN; ++i) { + chanbyte = chmap[i]; + if (chanbyte) { + if (chanbyte == 0xff) { + used_channels += 8; + } else { + mask = 0x01; + for (j = 0; j < 8; ++j) { + if (chanbyte & mask) { + ++used_channels; + } + mask <<= 1; + } + } + } + } + return used_channels; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) +static uint16_t +ble_ll_utils_csa2_perm(uint16_t in) +{ + uint16_t out = 0; + int i; + + for (i = 0; i < 8; i++) { + out |= ((in >> i) & 0x00000001) << (7 - i); + } + + for (i = 8; i < 16; i++) { + out |= ((in >> i) & 0x00000001) << (15 + 8 - i); + } + + return out; +} + +static uint16_t +ble_ll_utils_csa2_prng(uint16_t counter, uint16_t ch_id) +{ + uint16_t prn_e; + + prn_e = counter ^ ch_id; + + prn_e = ble_ll_utils_csa2_perm(prn_e); + prn_e = (prn_e * 17) + ch_id; + + prn_e = ble_ll_utils_csa2_perm(prn_e); + prn_e = (prn_e * 17) + ch_id; + + prn_e = ble_ll_utils_csa2_perm(prn_e); + prn_e = (prn_e * 17) + ch_id; + + prn_e = prn_e ^ ch_id; + + return prn_e; +} + +uint8_t +ble_ll_utils_calc_dci_csa2(uint16_t event_cntr, uint16_t channel_id, + uint8_t num_used_chans, const uint8_t *chanmap) +{ + uint16_t channel_unmapped; + uint8_t remap_index; + + uint16_t prn_e; + uint8_t bitpos; + + prn_e = ble_ll_utils_csa2_prng(event_cntr, channel_id); + + channel_unmapped = prn_e % 37; + + /* + * If unmapped channel is the channel index of a used channel it is used + * as channel index. + */ + bitpos = 1 << (channel_unmapped & 0x07); + if (chanmap[channel_unmapped >> 3] & bitpos) { + return channel_unmapped; + } + + remap_index = (num_used_chans * prn_e) / 0x10000; + + return ble_ll_utils_remapped_channel(remap_index, chanmap); +} +#endif + +uint32_t +ble_ll_utils_calc_window_widening(uint32_t anchor_point, + uint32_t last_anchor_point, + uint8_t master_sca) +{ + uint32_t total_sca_ppm; + uint32_t window_widening; + int32_t time_since_last_anchor; + uint32_t delta_msec; + + window_widening = 0; + + time_since_last_anchor = (int32_t)(anchor_point - last_anchor_point); + if (time_since_last_anchor > 0) { + delta_msec = os_cputime_ticks_to_usecs(time_since_last_anchor) / 1000; + total_sca_ppm = g_ble_sca_ppm_tbl[master_sca] + + MYNEWT_VAL(BLE_LL_OUR_SCA); + window_widening = (total_sca_ppm * delta_msec) / 1000; + } + + return window_widening; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_whitelist.c b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_whitelist.c new file mode 100644 index 0000000..8b92511 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/controller/src/ble_ll_whitelist.c @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef ESP_PLATFORM + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) +#include "nimble/nimble/drivers/nrf51/include/ble/xcvr.h" +#elif defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) +#include "nimble/nimble/drivers/nrf52/include/ble/xcvr.h" +#endif + +#include "../include/controller/ble_ll_whitelist.h" +#include "../include/controller/ble_ll_hci.h" +#include "../include/controller/ble_ll_adv.h" +#include "../include/controller/ble_ll_scan.h" +#include "../include/controller/ble_hw.h" + +#if (MYNEWT_VAL(BLE_LL_WHITELIST_SIZE) < BLE_HW_WHITE_LIST_SIZE) +#define BLE_LL_WHITELIST_SIZE MYNEWT_VAL(BLE_LL_WHITELIST_SIZE) +#else +#define BLE_LL_WHITELIST_SIZE BLE_HW_WHITE_LIST_SIZE +#endif + +struct ble_ll_whitelist_entry +{ + uint8_t wl_valid; + uint8_t wl_addr_type; + uint8_t wl_dev_addr[BLE_DEV_ADDR_LEN]; +}; + +struct ble_ll_whitelist_entry g_ble_ll_whitelist[BLE_LL_WHITELIST_SIZE]; + +static int +ble_ll_whitelist_chg_allowed(void) +{ + int rc; + + /* + * This command is not allowed if: + * -> advertising uses the whitelist and we are currently advertising. + * -> scanning uses the whitelist and is enabled. + * -> initiating uses whitelist and a LE create connection command is in + * progress + */ + rc = 1; + if (!ble_ll_adv_can_chg_whitelist() || !ble_ll_scan_can_chg_whitelist()) { + rc = 0; + } + return rc; +} + +/** + * Clear the whitelist. + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_ll_whitelist_clear(void) +{ + int i; + struct ble_ll_whitelist_entry *wl; + + /* Check proper state */ + if (!ble_ll_whitelist_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Set the number of entries to 0 */ + wl = &g_ble_ll_whitelist[0]; + for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) { + wl->wl_valid = 0; + ++wl; + } + +#if (BLE_USES_HW_WHITELIST == 1) + ble_hw_whitelist_clear(); +#endif + + return BLE_ERR_SUCCESS; +} + +/** + * Read the size of the whitelist. This is the total number of whitelist + * entries allowed by the controller. + * + * @param rspbuf Pointer to response buffer + * + * @return int 0: success. + */ +int +ble_ll_whitelist_read_size(uint8_t *rspbuf, uint8_t *rsplen) +{ + struct ble_hci_le_rd_white_list_rp *rsp = (void *) rspbuf; + + rsp->size = BLE_LL_WHITELIST_SIZE; + + *rsplen = sizeof(*rsp); + + return BLE_ERR_SUCCESS; +} + +/** + * Searches the whitelist to determine if the address is present in the + * whitelist. This is an internal API that only searches the link layer + * whitelist and does not care about the hardware whitelist + * + * @param addr Device or identity address to check. + * @param addr_type Public address (0) or random address (1) + * + * @return int 0: device is not on whitelist; otherwise the return value + * is the 'position' of the device in the whitelist (the index of the element + * plus 1). + */ +static int +ble_ll_whitelist_search(const uint8_t *addr, uint8_t addr_type) +{ + int i; + struct ble_ll_whitelist_entry *wl; + + wl = &g_ble_ll_whitelist[0]; + for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) { + if ((wl->wl_valid) && (wl->wl_addr_type == addr_type) && + (!memcmp(&wl->wl_dev_addr[0], addr, BLE_DEV_ADDR_LEN))) { + return i + 1; + } + ++wl; + } + + return 0; +} + +/** + * Is there a match between the device and a device on the whitelist. + * + * NOTE: This API uses the HW, if present, to determine if there was a match + * between a received address and an address in the whitelist. If the HW does + * not support whitelisting this API is the same as the whitelist search API + * + * @param addr + * @param addr_type Public address (0) or random address (1) + * @param is_ident True if addr is an identity address; false otherwise + * + * @return int + */ +int +ble_ll_whitelist_match(uint8_t *addr, uint8_t addr_type, int is_ident) +{ + int rc; +#if (BLE_USES_HW_WHITELIST == 1) + /* + * XXX: This should be changed. This is HW specific: some HW may be able + * to both resolve a private address and perform a whitelist check. The + * current BLE hw cannot support this. + */ + if (is_ident) { + rc = ble_ll_whitelist_search(addr, addr_type); + } else { + rc = ble_hw_whitelist_match(); + } +#else + rc = ble_ll_whitelist_search(addr, addr_type); +#endif + return rc; +} + +/** + * Add a device to the whitelist + * + * @return int + */ +int +ble_ll_whitelist_add(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_add_whte_list_cp *cmd = (const void *) cmdbuf; + struct ble_ll_whitelist_entry *wl; + int rc; + int i; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Must be in proper state */ + if (!ble_ll_whitelist_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* Check if we have any open entries */ + rc = BLE_ERR_SUCCESS; + if (!ble_ll_whitelist_search(cmd->addr, cmd->addr_type)) { + wl = &g_ble_ll_whitelist[0]; + for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) { + if (wl->wl_valid == 0) { + memcpy(&wl->wl_dev_addr[0], cmd->addr, BLE_DEV_ADDR_LEN); + wl->wl_addr_type = cmd->addr_type; + wl->wl_valid = 1; + break; + } + ++wl; + } + + if (i == BLE_LL_WHITELIST_SIZE) { + rc = BLE_ERR_MEM_CAPACITY; + } else { +#if (BLE_USES_HW_WHITELIST == 1) + rc = ble_hw_whitelist_add(cmd->addr, cmd->addr_type); +#endif + } + } + + return rc; +} + +/** + * Remove a device from the whitelist + * + * @param cmdbuf + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_ll_whitelist_rmv(const uint8_t *cmdbuf, uint8_t len) +{ + const struct ble_hci_le_rmv_white_list_cp *cmd = (const void *) cmdbuf; + int position; + + if (len != sizeof(*cmd)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Must be in proper state */ + if (!ble_ll_whitelist_chg_allowed()) { + return BLE_ERR_CMD_DISALLOWED; + } + + position = ble_ll_whitelist_search(cmd->addr, cmd->addr_type); + if (position) { + g_ble_ll_whitelist[position - 1].wl_valid = 0; + } + +#if (BLE_USES_HW_WHITELIST == 1) + ble_hw_whitelist_rmv(cmd->addr, cmd->addr_type); +#endif + + return BLE_ERR_SUCCESS; +} + +/** + * Enable whitelisting. + * + * Note: This function has no effect if we are not using HW whitelisting + */ +void +ble_ll_whitelist_enable(void) +{ +#if (BLE_USES_HW_WHITELIST == 1) + ble_hw_whitelist_enable(); +#endif +} + +/** + * Disable whitelisting. + * + * Note: This function has no effect if we are not using HW whitelisting + */ +void +ble_ll_whitelist_disable(void) +{ +#if (BLE_USES_HW_WHITELIST == 1) + ble_hw_whitelist_disable(); +#endif +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/include/ble/xcvr.h b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/include/ble/xcvr.h new file mode 100644 index 0000000..6c1fb7b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/include/ble/xcvr.h @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + #if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) + +#ifndef H_BLE_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Transceiver specific defintions */ +/* NOTE: we have to account for the RTC output compare issue */ +#define XCVR_PROC_DELAY_USECS (230) + +#define XCVR_RX_START_DELAY_USECS (140) +#define XCVR_TX_START_DELAY_USECS (140) +#define XCVR_TX_SCHED_DELAY_USECS \ + (XCVR_TX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) +#define XCVR_RX_SCHED_DELAY_USECS \ + (XCVR_RX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) + +/* + * Define HW whitelist size. This is the total possible whitelist size; + * not necessarily the size that will be used (may be smaller) + */ +#define BLE_HW_WHITE_LIST_SIZE (8) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_hw.c b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_hw.c new file mode 100644 index 0000000..3d4966b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_hw.c @@ -0,0 +1,491 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "../include/ble/xcvr.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nrf.h" +#include "nimble/nimble/controller/include/controller/ble_hw.h" +#if MYNEWT +#include "mcu/cmsis_nvic.h" +#else +#include "core_cm0.h" +#include "nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h" +#endif +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +/* Total number of resolving list elements */ +#define BLE_HW_RESOLV_LIST_SIZE (16) + +/* We use this to keep track of which entries are set to valid addresses */ +static uint8_t g_ble_hw_whitelist_mask; + +/* Random number generator isr callback */ +ble_rng_isr_cb_t g_ble_rng_isr_cb; + +/* If LL privacy is enabled, allocate memory for AAR */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + +/* The NRF51 supports up to 16 IRK entries */ +#if (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE) < 16) +#define NRF_IRK_LIST_ENTRIES (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) +#else +#define NRF_IRK_LIST_ENTRIES (16) +#endif + +/* NOTE: each entry is 16 bytes long. */ +uint32_t g_nrf_irk_list[NRF_IRK_LIST_ENTRIES * 4]; + +/* Current number of IRK entries */ +uint8_t g_nrf_num_irks; + +#endif + +/* Returns public device address or -1 if not present */ +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + uint32_t addr_high; + uint32_t addr_low; + + /* Does FICR have a public address */ + if ((NRF_FICR->DEVICEADDRTYPE & 1) != 0) { + return -1; + } + + /* Copy into device address. We can do this because we know platform */ + addr_low = NRF_FICR->DEVICEADDR[0]; + addr_high = NRF_FICR->DEVICEADDR[1]; + memcpy(addr->val, &addr_low, 4); + memcpy(&addr->val[4], &addr_high, 2); + addr->type = BLE_ADDR_PUBLIC; + + return 0; +} + +/* Returns random static address or -1 if not present */ +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + int rc; + + if ((NRF_FICR->DEVICEADDRTYPE & 1) == 1) { + memcpy(addr->val, (void *)&NRF_FICR->DEVICEADDR[0], 4); + memcpy(&addr->val[4], (void *)&NRF_FICR->DEVICEADDR[1], 2); + addr->val[5] |= 0xc0; + addr->type = BLE_ADDR_RANDOM; + rc = 0; + } else { + rc = -1; + } + + return rc; +} + +/** + * Clear the whitelist + * + * @return int + */ +void +ble_hw_whitelist_clear(void) +{ + NRF_RADIO->DACNF = 0; + g_ble_hw_whitelist_mask = 0; +} + +/** + * Add a device to the hw whitelist + * + * @param addr + * @param addr_type + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint32_t mask; + + /* Find first ununsed device address match element */ + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if ((mask & g_ble_hw_whitelist_mask) == 0) { + NRF_RADIO->DAB[i] = get_le32(addr); + NRF_RADIO->DAP[i] = get_le16(addr + 4); + if (addr_type == BLE_ADDR_RANDOM) { + NRF_RADIO->DACNF |= (mask << 8); + } + g_ble_hw_whitelist_mask |= mask; + return BLE_ERR_SUCCESS; + } + mask <<= 1; + } + + return BLE_ERR_MEM_CAPACITY; +} + +/** + * Remove a device from the hw whitelist + * + * @param addr + * @param addr_type + * + */ +void +ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint8_t cfg_addr; + uint16_t dap; + uint16_t txadd; + uint32_t dab; + uint32_t mask; + + /* Find first ununsed device address match element */ + dab = get_le32(addr); + dap = get_le16(addr + 4); + txadd = NRF_RADIO->DACNF >> 8; + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if (mask & g_ble_hw_whitelist_mask) { + if ((dab == NRF_RADIO->DAB[i]) && (dap == NRF_RADIO->DAP[i])) { + cfg_addr = txadd & mask; + if (addr_type == BLE_ADDR_RANDOM) { + if (cfg_addr != 0) { + break; + } + } else { + if (cfg_addr == 0) { + break; + } + } + } + } + mask <<= 1; + } + + if (i < BLE_HW_WHITE_LIST_SIZE) { + g_ble_hw_whitelist_mask &= ~mask; + NRF_RADIO->DACNF &= ~mask; + } +} + +/** + * Returns the size of the whitelist in HW + * + * @return int Number of devices allowed in whitelist + */ +uint8_t +ble_hw_whitelist_size(void) +{ + return BLE_HW_WHITE_LIST_SIZE; +} + +/** + * Enable the whitelisted devices + */ +void +ble_hw_whitelist_enable(void) +{ + /* Enable the configured device addresses */ + NRF_RADIO->DACNF |= g_ble_hw_whitelist_mask; +} + +/** + * Disables the whitelisted devices + */ +void +ble_hw_whitelist_disable(void) +{ + /* Disable all whitelist devices */ + NRF_RADIO->DACNF &= 0x0000ff00; +} + +/** + * Boolean function which returns true ('1') if there is a match on the + * whitelist. + * + * @return int + */ +int +ble_hw_whitelist_match(void) +{ + return (int)NRF_RADIO->EVENTS_DEVMATCH; +} + +/* Encrypt data */ +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + int rc; + uint32_t end; + uint32_t err; + + /* Stop ECB */ + NRF_ECB->TASKS_STOPECB = 1; + /* XXX: does task stop clear these counters? Anyway to do this quicker? */ + NRF_ECB->EVENTS_ENDECB = 0; + NRF_ECB->EVENTS_ERRORECB = 0; + NRF_ECB->ECBDATAPTR = (uint32_t)ecb; + + /* Start ECB */ + NRF_ECB->TASKS_STARTECB = 1; + + /* Wait till error or done */ + rc = 0; + while (1) { + end = NRF_ECB->EVENTS_ENDECB; + err = NRF_ECB->EVENTS_ERRORECB; + if (end || err) { + if (err) { + rc = -1; + } + break; + } + } + + return rc; +} + +/** + * Random number generator ISR. + */ +static void +ble_rng_isr(void) +{ + uint8_t rnum; + + os_trace_isr_enter(); + + /* No callback? Clear and disable interrupts */ + if (g_ble_rng_isr_cb == NULL) { + NRF_RNG->INTENCLR = 1; + NRF_RNG->EVENTS_VALRDY = 0; + (void)NRF_RNG->SHORTS; + os_trace_isr_exit(); + return; + } + + /* If there is a value ready grab it */ + if (NRF_RNG->EVENTS_VALRDY) { + NRF_RNG->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG->VALUE; + (*g_ble_rng_isr_cb)(rnum); + } + + os_trace_isr_exit(); +} + +/** + * Initialize the random number generator + * + * @param cb + * @param bias + * + * @return int + */ +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + /* Set bias */ + if (bias) { + NRF_RNG->CONFIG = 1; + } else { + NRF_RNG->CONFIG = 0; + } + + /* If we were passed a function pointer we need to enable the interrupt */ + if (cb != NULL) { +#ifndef RIOT_VERSION + NVIC_SetPriority(RNG_IRQn, (1 << __NVIC_PRIO_BITS) - 1); +#endif +#if MYNEWT + NVIC_SetVector(RNG_IRQn, (uint32_t)ble_rng_isr); +#else + ble_npl_hw_set_isr(RNG_IRQn, ble_rng_isr); +#endif + NVIC_EnableIRQ(RNG_IRQn); + g_ble_rng_isr_cb = cb; + } + + return 0; +} + +/** + * Start the random number generator + * + * @return int + */ +int +ble_hw_rng_start(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG->EVENTS_VALRDY = 0; + if (g_ble_rng_isr_cb) { + NRF_RNG->INTENSET = 1; + } + NRF_RNG->TASKS_START = 1; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Stop the random generator + * + * @return int + */ +int +ble_hw_rng_stop(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG->INTENCLR = 1; + NRF_RNG->TASKS_STOP = 1; + NRF_RNG->EVENTS_VALRDY = 0; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t +ble_hw_rng_read(void) +{ + uint8_t rnum; + + /* Wait for a sample */ + while (NRF_RNG->EVENTS_VALRDY == 0) { + } + + NRF_RNG->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG->VALUE; + + return rnum; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +/** + * Clear the resolving list + * + * @return int + */ +void +ble_hw_resolv_list_clear(void) +{ + g_nrf_num_irks = 0; +} + +/** + * Add a device to the hw resolving list + * + * @param irk Pointer to IRK to add + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_resolv_list_add(uint8_t *irk) +{ + uint32_t *nrf_entry; + + /* Find first ununsed device address match element */ + if (g_nrf_num_irks == NRF_IRK_LIST_ENTRIES) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Copy into irk list */ + nrf_entry = &g_nrf_irk_list[4 * g_nrf_num_irks]; + memcpy(nrf_entry, irk, 16); + + /* Add to total */ + ++g_nrf_num_irks; + return BLE_ERR_SUCCESS; +} + +/** + * Remove a device from the hw resolving list + * + * @param index Index of IRK to remove + */ +void +ble_hw_resolv_list_rmv(int index) +{ + uint32_t *irk_entry; + + if (index < g_nrf_num_irks) { + --g_nrf_num_irks; + irk_entry = &g_nrf_irk_list[index]; + if (g_nrf_num_irks > index) { + memmove(irk_entry, irk_entry + 4, 16 * (g_nrf_num_irks - index)); + } + } +} + +/** + * Returns the size of the resolving list. NOTE: this returns the maximum + * allowable entries in the HW. Configuration options may limit this. + * + * @return int Number of devices allowed in resolving list + */ +uint8_t +ble_hw_resolv_list_size(void) +{ + return BLE_HW_RESOLV_LIST_SIZE; +} + +/** + * Called to determine if the address received was resolved. + * + * @return int Negative values indicate unresolved address; positive values + * indicate index in resolving list of resolved address. + */ +int +ble_hw_resolv_list_match(void) +{ + uint32_t index; + + if (NRF_AAR->EVENTS_END) { + if (NRF_AAR->EVENTS_RESOLVED) { + index = NRF_AAR->STATUS; + return (int)index; + } + } + + return -1; +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_phy.c b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_phy.c new file mode 100644 index 0000000..f9aab73 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf51/src/ble_phy.c @@ -0,0 +1,1527 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + #if defined(ARDUINO_ARCH_NRF5) && defined(NRF51) + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "../include/ble/xcvr.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/controller/include/controller/ble_phy.h" +#include "nimble/nimble/controller/include/controller/ble_phy_trace.h" +#include "nimble/nimble/controller/include/controller/ble_ll.h" +#include "nrf.h" + +#if MYNEWT +#include "mcu/nrf51_clock.h" +#include "mcu/cmsis_nvic.h" +#else +#include "core_cm0.h" +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) +#error LE 2M PHY cannot be enabled on nRF51 +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#error LE Coded PHY cannot be enabled on nRF51 +#endif + +/* XXX: 4) Make sure RF is higher priority interrupt than schedule */ + +/* + * XXX: Maximum possible transmit time is 1 msec for a 60ppm crystal + * and 16ms for a 30ppm crystal! We need to limit PDU size based on + * crystal accuracy. Look at this in the spec. + */ + +/* XXX: private header file? */ +extern uint8_t g_nrf_num_irks; +extern uint32_t g_nrf_irk_list[]; + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) + +/* + * We configure the nrf with a 1 byte S0 field, 8 bit length field, and + * zero bit S1 field. The preamble is 8 bits long. + */ +#define NRF_LFLEN_BITS (8) +#define NRF_S0_LEN (1) + +/* Maximum length of frames */ +#define NRF_MAXLEN (255) +#define NRF_BALEN (3) /* For base address of 3 bytes */ + +/* Maximum tx power */ +#define NRF_TX_PWR_MAX_DBM (4) +#define NRF_TX_PWR_MIN_DBM (-40) + +/* Max. encrypted payload length */ +#define NRF_MAX_ENCRYPTED_PYLD_LEN (27) +#define NRF_ENC_HDR_SIZE (3) +#define NRF_ENC_BUF_SIZE \ + (NRF_MAX_ENCRYPTED_PYLD_LEN + NRF_ENC_HDR_SIZE + BLE_LL_DATA_MIC_LEN) + +/* BLE PHY data structure */ +struct ble_phy_obj +{ + uint8_t phy_stats_initialized; + int8_t phy_txpwr_dbm; + uint8_t phy_chan; + uint8_t phy_state; + uint8_t phy_transition; + uint8_t phy_rx_started; + uint8_t phy_encrypted; + uint8_t phy_privacy; + uint8_t phy_tx_pyld_len; + uint8_t *rxdptr; + int8_t rx_pwr_compensation; + uint32_t phy_aar_scratch; + uint32_t phy_access_address; + struct ble_mbuf_hdr rxhdr; + void *txend_arg; + ble_phy_tx_end_func txend_cb; + uint32_t phy_start_cputime; +}; +struct ble_phy_obj g_ble_phy_data; + +/* XXX: if 27 byte packets desired we can make this smaller */ +/* Global transmit/receive buffer */ +static uint32_t g_ble_phy_tx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +static uint32_t g_ble_phy_rx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Make sure word-aligned for faster copies */ +static uint32_t g_ble_phy_enc_buf[(NRF_ENC_BUF_SIZE + 3) / 4]; +#endif + +/* RF center frequency for each channel index (offset from 2400 MHz) */ +static const uint8_t g_ble_phy_chan_freq[BLE_PHY_NUM_CHANS] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, /* 0-9 */ + 24, 28, 30, 32, 34, 36, 38, 40, 42, 44, /* 10-19 */ + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, /* 20-29 */ + 66, 68, 70, 72, 74, 76, 78, 2, 26, 80, /* 30-39 */ +}; + +/* Statistics */ +STATS_SECT_START(ble_phy_stats) + STATS_SECT_ENTRY(phy_isrs) + STATS_SECT_ENTRY(tx_good) + STATS_SECT_ENTRY(tx_fail) + STATS_SECT_ENTRY(tx_late) + STATS_SECT_ENTRY(tx_bytes) + STATS_SECT_ENTRY(rx_starts) + STATS_SECT_ENTRY(rx_aborts) + STATS_SECT_ENTRY(rx_valid) + STATS_SECT_ENTRY(rx_crc_err) + STATS_SECT_ENTRY(rx_late) + STATS_SECT_ENTRY(radio_state_errs) + STATS_SECT_ENTRY(rx_hw_err) + STATS_SECT_ENTRY(tx_hw_err) +STATS_SECT_END +STATS_SECT_DECL(ble_phy_stats) ble_phy_stats; + +STATS_NAME_START(ble_phy_stats) + STATS_NAME(ble_phy_stats, phy_isrs) + STATS_NAME(ble_phy_stats, tx_good) + STATS_NAME(ble_phy_stats, tx_fail) + STATS_NAME(ble_phy_stats, tx_late) + STATS_NAME(ble_phy_stats, tx_bytes) + STATS_NAME(ble_phy_stats, rx_starts) + STATS_NAME(ble_phy_stats, rx_aborts) + STATS_NAME(ble_phy_stats, rx_valid) + STATS_NAME(ble_phy_stats, rx_crc_err) + STATS_NAME(ble_phy_stats, rx_late) + STATS_NAME(ble_phy_stats, radio_state_errs) + STATS_NAME(ble_phy_stats, rx_hw_err) + STATS_NAME(ble_phy_stats, tx_hw_err) +STATS_NAME_END(ble_phy_stats) + +/* + * NOTE: + * Tested the following to see what would happen: + * -> NVIC has radio irq enabled (interrupt # 1, mask 0x2). + * -> Set up nrf to receive. Clear ADDRESS event register. + * -> Enable ADDRESS interrupt on nrf5 by writing to INTENSET. + * -> Enable RX. + * -> Disable interrupts globally using OS_ENTER_CRITICAL(). + * -> Wait until a packet is received and the ADDRESS event occurs. + * -> Call ble_phy_disable(). + * + * At this point I wanted to see the state of the cortex NVIC. The IRQ + * pending bit was TRUE for the radio interrupt (as expected) as we never + * serviced the radio interrupt (interrupts were disabled). + * + * What was unexpected was this: without clearing the pending IRQ in the NVIC, + * when radio interrupts were re-enabled (address event bit in INTENSET set to + * 1) and the radio ADDRESS event register read 1 (it was never cleared after + * the first address event), the radio did not enter the ISR! I would have + * expected that if the following were true, an interrupt would occur: + * -> NVIC ISER bit set to TRUE + * -> NVIC ISPR bit reads TRUE, meaning interrupt is pending. + * -> Radio peripheral interrupts are enabled for some event (or events). + * -> Corresponding event register(s) in radio peripheral read 1. + * + * Not sure what the end result of all this is. We will clear the pending + * bit in the NVIC just to be sure when we disable the PHY. + */ + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Per nordic, the number of bytes needed for scratch is 16 + MAX_PKT_SIZE. */ +#define NRF_ENC_SCRATCH_WORDS (((NRF_MAX_ENCRYPTED_PYLD_LEN + 16) + 3) / 4) + +uint32_t g_nrf_encrypt_scratchpad[NRF_ENC_SCRATCH_WORDS]; + +struct nrf_ccm_data +{ + uint8_t key[16]; + uint64_t pkt_counter; + uint8_t dir_bit; + uint8_t iv[8]; +} __attribute__((packed)); + +struct nrf_ccm_data g_nrf_ccm_data; +#endif + +/** + * Copies the data from the phy receive buffer into a mbuf chain. + * + * @param dptr Pointer to receive buffer + * @param rxpdu Pointer to already allocated mbuf chain + * + * NOTE: the packet header already has the total mbuf length in it. The + * lengths of the individual mbufs are not set prior to calling. + * + */ +void +ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu) +{ + uint32_t rem_len; + uint32_t copy_len; + uint32_t block_len; + uint32_t block_rem_len; + void *dst; + void *src; + struct os_mbuf * om; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + block_len = rxpdu->om_omp->omp_databuf_len; + rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len; + src = dptr; + + /* + * Setup for copying from first mbuf which is shorter due to packet header + * and extra leading space + */ + copy_len = block_len - rxpdu->om_pkthdr_len - 4; + om = rxpdu; + dst = om->om_data; + + while (true) { + /* + * Always copy blocks of length aligned to word size, only last mbuf + * will have remaining non-word size bytes appended. + */ + block_rem_len = copy_len; + copy_len = min(copy_len, rem_len); + copy_len &= ~3; + + dst = om->om_data; + om->om_len = copy_len; + rem_len -= copy_len; + block_rem_len -= copy_len; + + __asm__ volatile (".syntax unified \n" + " mov r4, %[len] \n" + " b 2f \n" + "1: ldr r3, [%[src], %[len]] \n" + " str r3, [%[dst], %[len]] \n" + "2: subs %[len], #4 \n" + " bpl 1b \n" + " adds %[src], %[src], r4 \n" + " adds %[dst], %[dst], r4 \n" + : [dst] "+l" (dst), [src] "+l" (src), + [len] "+l" (copy_len) + : + : "r3", "r4", "memory" + ); + + if ((rem_len < 4) && (block_rem_len >= rem_len)) { + break; + } + + /* Move to next mbuf */ + om = SLIST_NEXT(om, om_next); + copy_len = block_len; + } + + /* Copy remaining bytes, if any, to last mbuf */ + om->om_len += rem_len; + __asm__ volatile (".syntax unified \n" + " b 2f \n" + "1: ldrb r3, [%[src], %[len]] \n" + " strb r3, [%[dst], %[len]] \n" + "2: subs %[len], #1 \n" + " bpl 1b \n" + : [len] "+l" (rem_len) + : [dst] "l" (dst), [src] "l" (src) + : "r3", "memory" + ); + + /* Copy header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr, + sizeof(struct ble_mbuf_hdr)); +} + +/** + * Called when we want to wait if the radio is in either the rx or tx + * disable states. We want to wait until that state is over before doing + * anything to the radio + */ +static void +nrf_wait_disabled(void) +{ + uint32_t state; + + /* + * RX and TX states have the same values except for 3rd bit (0=RX, 1=TX) so + * we use RX symbols only. + */ + state = NRF_RADIO->STATE & 0x07; + + if (state != RADIO_STATE_STATE_Disabled) { + /* If PHY is in idle state for whatever reason, disable it now */ + if (state == RADIO_STATE_STATE_RxIdle) { + NRF_RADIO->TASKS_DISABLE = 1; + STATS_INC(ble_phy_stats, radio_state_errs); + } + + if (state == RADIO_STATE_STATE_RxDisable) { + /* This will end within a short time (6 usecs). Just poll */ + while (NRF_RADIO->STATE == state) { + /* If this fails, something is really wrong. Should last + * no more than 6 usecs */ + } + } + } +} + +/** + * + * + */ +int +ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + /* + * XXX: The TXEN time is 140 usecs but there may be additional delays + * Need to look at this. + */ + + /* + * With the 32.768 kHz crystal, we may need to adjust the RTC compare + * value by 1 tick due to the time it takes for TXEN. The code uses a 5 RTC + * tick offset, which is 152.5 usecs. The TXEN time is 140 usecs. This + * means that with a remainder of 0, TIMER0 should be set to 12 or 13 (as + * TIMER0 counts at 1MHz). A remainder of 19 or more we will need to add + * 1 tick. We dont need to add 1 tick per se, but it does give us slightly + * more time and thus less of a chance to miss a tick. Another note: we + * cant set TIMER0 CC to 0 as the compare wont occur; it must be 1 or more. + * This is why we subtract 18 (as opposed to 19) as rem_uses will be >= 1. + */ + if (rem_usecs <= 18) { + cputime -= 5; + rem_usecs += 12; + } else { + cputime -= 4; + rem_usecs -= 18; + } + + /* + * Can we set the RTC compare to start TIMER0? We can do it if: + * a) Current compare value is not N+1 or N+2 ticks from current + * counter. + * b) The value we want to set is not at least N+2 from current + * counter. + * + * NOTE: since the counter can tick 1 while we do these calculations we + * need to account for it. + */ + next_cc = cputime & 0xffffff; + cur_cc = NRF_RTC0->CC[0]; + cntr = NRF_RTC0->COUNTER; + + delta = (cur_cc - cntr) & 0xffffff; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + delta = (next_cc - cntr) & 0xffffff; + if ((delta & 0x800000) || (delta < 3)) { + return -1; + } + + /* Clear and set TIMER0 to fire off at proper time */ + NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->CC[0] = rem_usecs; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + + /* Set RTC compare to start TIMER0 */ + NRF_RTC0->EVENTS_COMPARE[0] = 0; + NRF_RTC0->CC[0] = next_cc; + NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_PPI->CHENSET = PPI_CHEN_CH31_Msk; + + /* Store the cputime at which we set the RTC */ + g_ble_phy_data.phy_start_cputime = cputime; + + return 0; +} + +/** + * Function is used to set PPI so that we can time out waiting for a reception + * to occur. This happens for two reasons: we have sent a packet and we are + * waiting for a respons (txrx should be set to ENABLE_TXRX) or we are + * starting a connection event and we are a slave and we are waiting for the + * master to send us a packet (txrx should be set to ENABLE_RX). + * + * NOTE: when waiting for a txrx turn-around, wfr_usecs is not used as there + * is no additional time to wait; we know when we should receive the address of + * the received frame. + * + * @param txrx Flag denoting if this wfr is a txrx turn-around or not. + * @param tx_phy_mode phy mode for last TX (not used on nRF51) + * @param wfr_usecs Amount of usecs to wait. + */ +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ + uint32_t end_time; + + if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { + /* + * Timeout occurs an IFS time plus time it takes to receive address + * from the transmit end. We add additional time to make sure the + * address event comes before the compare. Note that transmit end + * is captured in CC[2]. I just made up the 16 usecs I add here. + */ + end_time = NRF_TIMER0->CC[2] + BLE_LL_IFS + + ble_phy_mode_pdu_start_off(BLE_PHY_MODE_1M) + 16; + } else { + /* CC[0] is set to when RXEN occurs. */ + end_time = NRF_TIMER0->CC[0] + XCVR_RX_START_DELAY_USECS + wfr_usecs + + ble_phy_mode_pdu_start_off(BLE_PHY_MODE_1M) + BLE_LL_JITTER_USECS; + } + + /* wfr_secs is the time from rxen until timeout */ + NRF_TIMER0->CC[3] = end_time; + NRF_TIMER0->EVENTS_COMPARE[3] = 0; + + /* Enable wait for response PPI */ + NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk); + + /* Enable the disabled interrupt so we time out on events compare */ + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; +} + +/** + * Setup transceiver for receive. + */ +static void +ble_phy_rx_xcvr_setup(void) +{ + uint8_t *dptr; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + dptr += 3; + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->OUTPTR = (uint32_t)dptr; + NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; + NRF_CCM->MODE = CCM_MODE_MODE_Decryption; + NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; + NRF_CCM->SHORTS = 0; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_ENDCRYPT = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH24_Msk | PPI_CHEN_CH25_Msk; + } else { + NRF_RADIO->PACKETPTR = (uint32_t)dptr; + } +#else + NRF_RADIO->PACKETPTR = (uint32_t)dptr; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (g_ble_phy_data.phy_privacy) { + dptr += 3; + NRF_RADIO->PACKETPTR = (uint32_t)dptr; + NRF_RADIO->PCNF0 = (6 << RADIO_PCNF0_LFLEN_Pos) | + (2 << RADIO_PCNF0_S1LEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled; + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch; + NRF_AAR->EVENTS_END = 0; + NRF_AAR->EVENTS_RESOLVED = 0; + NRF_AAR->EVENTS_NOTRESOLVED = 0; + } else { + if (g_ble_phy_data.phy_encrypted == 0) { + NRF_RADIO->PCNF0 = (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + /* XXX: do I only need to do this once? Figure out what I can do + once. */ + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled; + } + } +#endif + + /* Turn off trigger TXEN on output compare match and AAR on bcmatch */ + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk | PPI_CHEN_CH23_Msk; + + /* Reset the rx started flag. Used for the wait for response */ + g_ble_phy_data.phy_rx_started = 0; + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + g_ble_phy_data.rxdptr = dptr; + + /* I want to know when 1st byte received (after address) */ + NRF_RADIO->BCC = 8; /* in bits */ + NRF_RADIO->EVENTS_ADDRESS = 0; + NRF_RADIO->EVENTS_DEVMATCH = 0; + NRF_RADIO->EVENTS_BCMATCH = 0; + NRF_RADIO->EVENTS_RSSIEND = 0; + NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_DISABLED_TXEN_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; + + NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk; +} + +/** + * Called from interrupt context when the transmit ends + * + */ +static void +ble_phy_tx_end_isr(void) +{ + uint8_t was_encrypted; + uint8_t transition; + uint8_t txlen; + uint32_t wfr_time; + + /* If this transmission was encrypted we need to remember it */ + was_encrypted = g_ble_phy_data.phy_encrypted; + (void)was_encrypted; + + /* Better be in TX state! */ + assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); + + /* Clear events and clear interrupt on disabled event */ + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk; + NRF_RADIO->EVENTS_END = 0; + wfr_time = NRF_RADIO->SHORTS; + (void)wfr_time; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* + * XXX: not sure what to do. We had a HW error during transmission. + * For now I just count a stat but continue on like all is good. + */ + if (was_encrypted) { + if (NRF_CCM->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, tx_hw_err); + NRF_CCM->EVENTS_ERROR = 0; + } + } +#endif + + /* Call transmit end callback */ + if (g_ble_phy_data.txend_cb) { + g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); + } + + transition = g_ble_phy_data.phy_transition; + if (transition == BLE_PHY_TRANSITION_TX_RX) { + /* Packet pointer needs to be reset. */ + ble_phy_rx_xcvr_setup(); + + /* + * Enable the wait for response timer. Note that cc #1 on + * timer 0 contains the transmit start time + */ + txlen = g_ble_phy_data.phy_tx_pyld_len; + if (txlen && was_encrypted) { + txlen += BLE_LL_DATA_MIC_LEN; + } + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, 0, 0); + } else { + /* + * XXX: not sure we need to stop the timer here all the time. Or that + * it should be stopped here. + */ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | + PPI_CHEN_CH20_Msk | PPI_CHEN_CH31_Msk; + assert(transition == BLE_PHY_TRANSITION_NONE); + } +} + +static void +ble_phy_rx_end_isr(void) +{ + int rc; + uint8_t *dptr; + uint8_t crcok; + struct ble_mbuf_hdr *ble_hdr; + + /* Clear events and clear interrupt */ + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->INTENCLR = RADIO_INTENCLR_END_Msk; + + /* Disable automatic RXEN */ + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + + /* Set RSSI and CRC status flag in header */ + ble_hdr = &g_ble_phy_data.rxhdr; + assert(NRF_RADIO->EVENTS_RSSIEND != 0); + ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO->RSSISAMPLE) + + g_ble_phy_data.rx_pwr_compensation; + + dptr = g_ble_phy_data.rxdptr; + + /* Count PHY crc errors and valid packets */ + crcok = (uint8_t)NRF_RADIO->CRCSTATUS; + if (!crcok) { + STATS_INC(ble_phy_stats, rx_crc_err); + } else { + STATS_INC(ble_phy_stats, rx_valid); + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only set MIC failure flag if frame is not zero length */ + if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; + } + + /* + * XXX: not sure how to deal with this. This should not + * be a MIC failure but we should not hand it up. I guess + * this is just some form of rx error and that is how we + * handle it? For now, just set CRC error flags + */ + if (NRF_CCM->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + + /* + * XXX: This is a total hack work-around for now but I dont + * know what else to do. If ENDCRYPT is not set and we are + * encrypted we need to not trust this frame and drop it. + */ + if (NRF_CCM->EVENTS_ENDCRYPT == 0) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + } +#endif + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (g_ble_phy_data.phy_encrypted || g_ble_phy_data.phy_privacy) { + /* + * XXX: This is a horrible ugly hack to deal with the RAM S1 byte. + * This should get fixed as we should not be handing up the header + * and length as part of the pdu. + */ + dptr[2] = dptr[1]; + dptr[1] = dptr[0]; + ++dptr; + } +#endif + rc = ble_ll_rx_end(dptr, ble_hdr); + if (rc < 0) { + ble_phy_disable(); + } +} + +static void +ble_phy_rx_start_isr(void) +{ + int rc; + uint32_t state; + uint32_t usecs; + uint32_t ticks; + struct ble_mbuf_hdr *ble_hdr; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + uint8_t *dptr; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; +#endif + + /* Clear events and clear interrupt */ + NRF_RADIO->EVENTS_ADDRESS = 0; + + /* Clear wfr timer channels and DISABLED interrupt */ + NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk | RADIO_INTENCLR_ADDRESS_Msk; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk; + + /* Initialize flags, channel and state in ble header at rx start */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; + ble_hdr->rxinfo.handle = 0; + ble_hdr->rxinfo.phy = BLE_PHY_1M; + ble_hdr->rxinfo.phy_mode = BLE_PHY_MODE_1M; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.user_data = NULL; +#endif + + /* + * Calculate receive start time. + * + * XXX: possibly use other routine with remainder! + */ + usecs = NRF_TIMER0->CC[1] - ble_phy_mode_pdu_start_off(BLE_PHY_MODE_1M); + ticks = os_cputime_usecs_to_ticks(usecs); + ble_hdr->rem_usecs = usecs - os_cputime_ticks_to_usecs(ticks); + if (ble_hdr->rem_usecs == 31) { + ble_hdr->rem_usecs = 0; + ++ticks; + } + ble_hdr->beg_cputime = g_ble_phy_data.phy_start_cputime + ticks; + + /* Wait to get 1st byte of frame */ + while (1) { + state = NRF_RADIO->STATE; + if (NRF_RADIO->EVENTS_BCMATCH != 0) { + break; + } + + /* + * If state is disabled, we should have the BCMATCH. If not, + * something is wrong! + */ + if (state == RADIO_STATE_STATE_Disabled) { + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO->SHORTS = 0; + return; + } + } + + /* Call Link Layer receive start function */ + rc = ble_ll_rx_start(g_ble_phy_data.rxdptr, g_ble_phy_data.phy_chan, + &g_ble_phy_data.rxhdr); + if (rc >= 0) { + /* Set rx started flag and enable rx end ISR */ + g_ble_phy_data.phy_rx_started = 1; + NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* Must start aar if we need to */ + if (g_ble_phy_data.phy_privacy) { + NRF_RADIO->EVENTS_BCMATCH = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH23_Msk; + /* + * Setup AAR to resolve AdvA and trigger it after complete address + * is received, i.e. after PDU header and AdvA is received. + * + * AdvA starts at 4th octet in receive buffer, after S0, len and S1 + * fields. + * + * In case of extended advertising AdvA is located after extended + * header (+2 octets). + */ + if (BLE_MBUF_HDR_EXT_ADV(&g_ble_phy_data.rxhdr)) { + NRF_AAR->ADDRPTR = (uint32_t)(dptr + 5); + NRF_RADIO->BCC = (BLE_DEV_ADDR_LEN + BLE_LL_PDU_HDR_LEN + 2) * 8; + + } else { + NRF_AAR->ADDRPTR = (uint32_t)(dptr + 3); + NRF_RADIO->BCC = (BLE_DEV_ADDR_LEN + BLE_LL_PDU_HDR_LEN) * 8; + } + } +#endif + } else { + /* Disable PHY */ + ble_phy_disable(); + STATS_INC(ble_phy_stats, rx_aborts); + } + + /* Count rx starts */ + STATS_INC(ble_phy_stats, rx_starts); +} + +static void +ble_phy_isr(void) +{ + uint32_t irq_en; + + os_trace_isr_enter(); + + /* Read irq register to determine which interrupts are enabled */ + irq_en = NRF_RADIO->INTENCLR; + + /* + * NOTE: order of checking is important! Possible, if things get delayed, + * we have both an ADDRESS and DISABLED interrupt in rx state. If we get + * an address, we disable the DISABLED interrupt. + */ + + /* We get this if we have started to receive a frame */ + if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) { + irq_en &= ~RADIO_INTENCLR_DISABLED_Msk; + ble_phy_rx_start_isr(); + } + + /* Check for disabled event. This only happens for transmits now */ + if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) { + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) { + NRF_RADIO->EVENTS_DISABLED = 0; + ble_ll_wfr_timer_exp(NULL); + } else { + ble_phy_tx_end_isr(); + } + } + + /* Receive packet end (we dont enable this for transmit) */ + if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) { + ble_phy_rx_end_isr(); + } + + /* Ensures IRQ is cleared */ + irq_en = NRF_RADIO->SHORTS; + + /* Count # of interrupts */ + STATS_INC(ble_phy_stats, phy_isrs); + + os_trace_isr_exit(); +} + +/** + * ble phy initialize + * + * Initialize the PHY. + * + * @return int 0: success; PHY error code otherwise + */ +int +ble_phy_init(void) +{ + int rc; + + /* Set phy channel to an invalid channel so first set channel works */ + g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; + + g_ble_phy_data.rx_pwr_compensation = 0; + + /* Toggle peripheral power to reset (just in case) */ + NRF_RADIO->POWER = 0; + NRF_RADIO->POWER = 1; + + /* Disable all interrupts */ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Set configuration registers */ + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO->PCNF0 = (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + /* XXX: should maxlen be 251 for encryption? */ + NRF_RADIO->PCNF1 = NRF_MAXLEN | + (RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos) | + (NRF_BALEN << RADIO_PCNF1_BALEN_Pos) | + RADIO_PCNF1_WHITEEN_Msk; + + /* Set base0 with the advertising access address */ + NRF_RADIO->BASE0 = (BLE_ACCESS_ADDR_ADV << 8) & 0xFFFFFF00; + NRF_RADIO->PREFIX0 = (BLE_ACCESS_ADDR_ADV >> 24) & 0xFF; + + /* Configure the CRC registers */ + NRF_RADIO->CRCCNF = RADIO_CRCCNF_SKIPADDR_Msk | RADIO_CRCCNF_LEN_Three; + + /* Configure BLE poly */ + NRF_RADIO->CRCPOLY = 0x0100065B; + + /* Configure IFS */ + NRF_RADIO->TIFS = BLE_LL_IFS; + + /* Captures tx/rx start in timer0 cc 1 and tx/rx end in timer0 cc 2 */ + NRF_PPI->CHENSET = PPI_CHEN_CH26_Msk | PPI_CHEN_CH27_Msk; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + NRF_CCM->INTENCLR = 0xffffffff; + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM->EVENTS_ERROR = 0; + memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad)); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + g_ble_phy_data.phy_aar_scratch = 0; + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR->INTENCLR = 0xffffffff; + NRF_AAR->EVENTS_END = 0; + NRF_AAR->EVENTS_RESOLVED = 0; + NRF_AAR->EVENTS_NOTRESOLVED = 0; + NRF_AAR->NIRK = 0; +#endif + + /* TIMER0 setup for PHY when using RTC */ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ + NRF_TIMER0->MODE = 0; /* Timer mode */ + NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ + + /* + * PPI setup. + * Channel 4: Captures TIMER0 in CC[3] when EVENTS_ADDRESS occurs. Used + * to cancel the wait for response timer. + * Channel 5: TIMER0 CC[3] to TASKS_DISABLE on radio. This is the wait + * for response timer. + */ + NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS); + NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[3]); + NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[3]); + NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE); + + /* Set isr in vector table and enable interrupt */ +#ifndef RIOT_VERSION + NVIC_SetPriority(RADIO_IRQn, 0); +#endif +#if MYNEWT + NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_isr); +#else + ble_npl_hw_set_isr(RADIO_IRQn, ble_phy_isr); +#endif + NVIC_EnableIRQ(RADIO_IRQn); + + /* Register phy statistics */ + if (!g_ble_phy_data.phy_stats_initialized) { + rc = stats_init_and_reg(STATS_HDR(ble_phy_stats), + STATS_SIZE_INIT_PARMS(ble_phy_stats, + STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_phy_stats), + "ble_phy"); + assert(rc == 0); + + g_ble_phy_data.phy_stats_initialized = 1; + } + + return 0; +} + +/** + * Puts the phy into receive mode. + * + * @return int 0: success; BLE Phy error code otherwise + */ +int +ble_phy_rx(void) +{ + /* Check radio state */ + nrf_wait_disabled(); + if (NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) { + ble_phy_disable(); + STATS_INC(ble_phy_stats, radio_state_errs); + return BLE_PHY_ERR_RADIO_STATE; + } + + /* Make sure all interrupts are disabled */ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Clear events prior to enabling receive */ + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + + /* Setup for rx */ + ble_phy_rx_xcvr_setup(); + + /* Start the receive task in the radio if not automatically going to rx */ + if ((NRF_PPI->CHEN & PPI_CHEN_CH21_Msk) == 0) { + NRF_RADIO->TASKS_RXEN = 1; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/** + * Called to enable encryption at the PHY. Note that this state will persist + * in the PHY; in other words, if you call this function you have to call + * disable so that future PHY transmits/receives will not be encrypted. + * + * @param pkt_counter + * @param iv + * @param key + * @param is_master + */ +void +ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master) +{ + memcpy(g_nrf_ccm_data.key, key, 16); + g_nrf_ccm_data.pkt_counter = pkt_counter; + memcpy(g_nrf_ccm_data.iv, iv, 8); + g_nrf_ccm_data.dir_bit = is_master; + g_ble_phy_data.phy_encrypted = 1; + + /* Encryption uses LFLEN=5, S1LEN = 3. */ + NRF_RADIO->PCNF0 = (5 << RADIO_PCNF0_LFLEN_Pos) | + (3 << RADIO_PCNF0_S1LEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + + /* Enable the module (AAR cannot be on while CCM on) */ + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled; + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ + g_nrf_ccm_data.pkt_counter = pkt_counter; + g_nrf_ccm_data.dir_bit = dir; +} + +void +ble_phy_encrypt_disable(void) +{ + NRF_PPI->CHENCLR = (PPI_CHEN_CH24_Msk | PPI_CHEN_CH25_Msk); + NRF_CCM->TASKS_STOP = 1; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled; + + /* Switch back to normal length */ + NRF_RADIO->PCNF0 = (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + + g_ble_phy_data.phy_encrypted = 0; +} +#endif + +void +ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) +{ + /* Set transmit end callback and arg */ + g_ble_phy_data.txend_cb = txend_cb; + g_ble_phy_data.txend_arg = arg; +} + +/** + * Called to set the start time of a transmission. + * + * This function is called to set the start time when we are not going from + * rx to tx automatically. + * + * NOTE: care must be taken when calling this function. The channel should + * already be set. + * + * @param cputime This is the tick at which the 1st bit of the preamble + * should be transmitted + * @param rem_usecs This is used only when the underlying timing uses a 32.768 + * kHz crystal. It is the # of usecs from the cputime tick + * at which the first bit of the preamble should be + * transmitted. + * @return int + */ +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + int rc; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_TX, cputime, rem_usecs); + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to RXEN since we are transmitting */ + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + + /* + * XXX: The TXEN time is 140 usecs but there may be additional delays + * Need to look at this. + */ + if (ble_phy_set_start_time(cputime, rem_usecs) != 0) { + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + rc = BLE_PHY_ERR_TX_LATE; + } else { + /* Enable PPI to automatically start TXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; + rc = 0; + } + return rc; +} +/** + * Called to set the start time of a reception + * + * This function acts a bit differently than transmit. If we are late getting + * here we will still attempt to receive. + * + * NOTE: care must be taken when calling this function. The channel should + * already be set. + * + * @param cputime + * + * @return int + */ +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + int rc; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_RX, cputime, rem_usecs); + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to TXEN since we are transmitting */ + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; + + /* + * XXX: The RXEN time is 138 usecs but there may be additional delays + * Need to look at this. + */ + if (ble_phy_set_start_time(cputime, rem_usecs) != 0) { + STATS_INC(ble_phy_stats, rx_late); + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + NRF_RADIO->TASKS_RXEN = 1; + rc = BLE_PHY_ERR_RX_LATE; + } else { + /* Enable PPI to automatically start RXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; + + /* Start rx */ + rc = ble_phy_rx(); + } + return rc; +} + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + int rc; + uint8_t *dptr; + uint8_t payload_len; + uint8_t payload_off; + uint8_t hdr_byte; + uint32_t state; + uint32_t shortcuts; + + /* + * This check is to make sure that the radio is not in a state where + * it is moving to disabled state. If so, let it get there. + */ + nrf_wait_disabled(); + + /* + * XXX: Although we may not have to do this here, I clear all the PPI + * that should not be used when transmitting. Some of them are only enabled + * if encryption and/or privacy is on, but I dont care. Better to be + * paranoid, and if you are going to clear one, might as well clear them + * all. + */ + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH23_Msk | + PPI_CHEN_CH25_Msk; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* RAM representation has S0, LENGTH and S1 fields. (3 bytes) */ + dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; + payload_off = 3; + + NRF_CCM->SHORTS = 1; + NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->OUTPTR = (uint32_t)&g_ble_phy_tx_buf[0]; + NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_MODE_Encryption; + NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; + NRF_PPI->CHENSET = PPI_CHEN_CH24_Msk; + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* Reconfigure PCNF0 */ + NRF_RADIO->PCNF0 = (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; +#endif + /* RAM representation has S0 and LENGTH fields (2 bytes) */ + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + payload_off = 2; + } +#else + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* Reconfigure PCNF0 */ + NRF_RADIO->PCNF0 = (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | + (NRF_S0_LEN << RADIO_PCNF0_S0LEN_Pos); +#endif + + /* RAM representation has S0 and LENGTH fields (2 bytes) */ + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + payload_off = 2; +#endif + + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_tx_buf[0]; + + /* Clear the ready, end and disabled events */ + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + + /* Enable shortcuts for transmit start/end. */ + shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; + if (end_trans == BLE_PHY_TRANSITION_TX_RX) { + shortcuts |= RADIO_SHORTS_DISABLED_RXEN_Msk; + } + NRF_RADIO->SHORTS = shortcuts; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + + /* Set PDU payload */ + payload_len = pducb(&dptr[payload_off], pducb_arg, &hdr_byte); + + /* Set PDU header */ + dptr[0] = hdr_byte; + dptr[1] = payload_len; + if (payload_off > 2) { + dptr[2] = 0; + } + + /* Set the PHY transition */ + g_ble_phy_data.phy_transition = end_trans; + + /* Set transmitted payload length */ + g_ble_phy_data.phy_tx_pyld_len = payload_len; + + /* If we already started transmitting, abort it! */ + state = NRF_RADIO->STATE; + if (state != RADIO_STATE_STATE_Tx) { + + /* Set phy state to transmitting and count packet statistics */ + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + STATS_INC(ble_phy_stats, tx_good); + STATS_INCN(ble_phy_stats, tx_bytes, payload_len + BLE_LL_PDU_HDR_LEN); + rc = BLE_ERR_SUCCESS; + } else { + ble_phy_disable(); + STATS_INC(ble_phy_stats, tx_late); + rc = BLE_PHY_ERR_RADIO_STATE; + } + + return rc; +} + +/** + * ble phy txpwr set + * + * Set the transmit output power (in dBm). + * + * NOTE: If the output power specified is within the BLE limits but outside + * the chip limits, we "rail" the power level so we dont exceed the min/max + * chip values. + * + * @param dbm Power output in dBm. + * + * @return int 0: success; anything else is an error + */ +int +ble_phy_txpwr_set(int dbm) +{ + /* Check valid range */ + assert(dbm <= BLE_PHY_MAX_PWR_DBM); + + /* "Rail" power level if outside supported range */ + if (dbm > NRF_TX_PWR_MAX_DBM) { + dbm = NRF_TX_PWR_MAX_DBM; + } else { + if (dbm < NRF_TX_PWR_MIN_DBM) { + dbm = NRF_TX_PWR_MIN_DBM; + } + } + + NRF_RADIO->TXPOWER = dbm; + g_ble_phy_data.phy_txpwr_dbm = dbm; + + return 0; +} + +/** + * ble phy txpwr round + * + * Get the rounded transmit output power (in dBm). + * + * @param dbm Power output in dBm. + * + * @return int Rounded power in dBm + */ +int ble_phy_txpower_round(int dbm) +{ + /* "Rail" power level if outside supported range */ + if (dbm > NRF_TX_PWR_MAX_DBM) { + dbm = NRF_TX_PWR_MAX_DBM; + } else { + if (dbm < NRF_TX_PWR_MIN_DBM) { + dbm = NRF_TX_PWR_MIN_DBM; + } + } + + return dbm; +} + +/** + * ble phy txpwr get + * + * Get the transmit power. + * + * @return int The current PHY transmit power, in dBm + */ +int +ble_phy_txpwr_get(void) +{ + return g_ble_phy_data.phy_txpwr_dbm; +} + +void +ble_phy_set_rx_pwr_compensation(int8_t compensation) +{ + g_ble_phy_data.rx_pwr_compensation = compensation; +} + +/** + * ble phy setchan + * + * Sets the logical frequency of the transceiver. The input parameter is the + * BLE channel index (0 to 39, inclusive). The NRF frequency register works like + * this: logical frequency = 2400 + FREQ (MHz). + * + * Thus, to get a logical frequency of 2402 MHz, you would program the + * FREQUENCY register to 2. + * + * @param chan This is the Data Channel Index or Advertising Channel index + * + * @return int 0: success; PHY error code otherwise + */ +int +ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) +{ + uint32_t prefix; + + assert(chan < BLE_PHY_NUM_CHANS); + + /* Check for valid channel range */ + if (chan >= BLE_PHY_NUM_CHANS) { + return BLE_PHY_ERR_INV_PARAM; + } + + /* Get correct frequency */ + if (chan < BLE_PHY_NUM_DATA_CHANS) { + /* Set current access address */ + g_ble_phy_data.phy_access_address = access_addr; + + /* Configure logical address 1 and crcinit */ + prefix = NRF_RADIO->PREFIX0; + prefix &= 0xffff00ff; + prefix |= ((access_addr >> 24) & 0xFF) << 8; + NRF_RADIO->BASE1 = (access_addr << 8) & 0xFFFFFF00; + NRF_RADIO->PREFIX0 = prefix; + NRF_RADIO->TXADDRESS = 1; + NRF_RADIO->RXADDRESSES = (1 << 1); + NRF_RADIO->CRCINIT = crcinit; + } else { + /* Logical adddress 0 preconfigured */ + NRF_RADIO->TXADDRESS = 0; + NRF_RADIO->RXADDRESSES = (1 << 0); + NRF_RADIO->CRCINIT = BLE_LL_CRCINIT_ADV; + + /* Set current access address */ + g_ble_phy_data.phy_access_address = BLE_ACCESS_ADDR_ADV; + } + + /* Set the frequency and the data whitening initial value */ + g_ble_phy_data.phy_chan = chan; + NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan]; + NRF_RADIO->DATAWHITEIV = chan; + + return 0; +} + +/** + * Stop the timer used to count microseconds when using RTC for cputime + */ +static void +ble_phy_stop_usec_timer(void) +{ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_RTC0->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk; +} + +/** + * ble phy disable irq and ppi + * + * This routine is to be called when reception was stopped due to either a + * wait for response timeout or a packet being received and the phy is to be + * restarted in receive mode. Generally, the disable routine is called to stop + * the phy. + */ +static void +ble_phy_disable_irq_and_ppi(void) +{ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO->SHORTS = 0; + NRF_RADIO->TASKS_DISABLE = 1; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH20_Msk | + PPI_CHEN_CH21_Msk | PPI_CHEN_CH23_Msk | PPI_CHEN_CH24_Msk | + PPI_CHEN_CH25_Msk | PPI_CHEN_CH31_Msk; + NVIC_ClearPendingIRQ(RADIO_IRQn); + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +void +ble_phy_restart_rx(void) +{ + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); + ble_phy_rx(); +} + +/** + * ble phy disable + * + * Disables the PHY. This should be called when an event is over. It stops + * the usec timer (if used), disables interrupts, disables the RADIO, disables + * PPI and sets state to idle. + */ +void +ble_phy_disable(void) +{ + ble_phy_trace_void(BLE_PHY_TRACE_ID_DISABLE); + + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); +} + +/* Gets the current access address */ +uint32_t ble_phy_access_addr_get(void) +{ + return g_ble_phy_data.phy_access_address; +} + +/** + * Return the phy state + * + * @return int The current PHY state. + */ +int +ble_phy_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +/** + * Called to see if a reception has started + * + * @return int + */ +int +ble_phy_rx_started(void) +{ + return g_ble_phy_data.phy_rx_started; +} + +/** + * Return the transceiver state + * + * @return int transceiver state. + */ +uint8_t +ble_phy_xcvr_state_get(void) +{ + uint32_t state; + state = NRF_RADIO->STATE; + return (uint8_t)state; +} + +/** + * Called to return the maximum data pdu payload length supported by the + * phy. For this chip, if encryption is enabled, the maximum payload is 27 + * bytes. + * + * @return uint8_t Maximum data channel PDU payload size supported + */ +uint8_t +ble_phy_max_data_pdu_pyld(void) +{ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + return NRF_MAX_ENCRYPTED_PYLD_LEN; +#else + return BLE_LL_DATA_PDU_MAX_PYLD; +#endif +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +void +ble_phy_resolv_list_enable(void) +{ + NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks; + g_ble_phy_data.phy_privacy = 1; +} + +void +ble_phy_resolv_list_disable(void) +{ + g_ble_phy_data.phy_privacy = 0; +} +#endif + +void +ble_phy_rfclk_enable(void) +{ +#if MYNEWT + nrf51_clock_hfxo_request(); +#else + NRF_CLOCK->TASKS_HFCLKSTART = 1; +#endif +} + +void +ble_phy_rfclk_disable(void) +{ +#if MYNEWT + nrf51_clock_hfxo_release(); +#else + NRF_CLOCK->TASKS_HFCLKSTOP = 1; +#endif +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/include/ble/xcvr.h b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/include/ble/xcvr.h new file mode 100644 index 0000000..fd8e1e8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/include/ble/xcvr.h @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) + +#ifndef H_BLE_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XCVR_RX_RADIO_RAMPUP_USECS (40) +#define XCVR_TX_RADIO_RAMPUP_USECS (40) + +/* + * NOTE: we have to account for the RTC output compare issue. We want it to be + * 5 ticks. + */ +#define XCVR_PROC_DELAY_USECS (153) +#define XCVR_RX_START_DELAY_USECS (XCVR_RX_RADIO_RAMPUP_USECS) +#define XCVR_TX_START_DELAY_USECS (XCVR_TX_RADIO_RAMPUP_USECS) +#define XCVR_TX_SCHED_DELAY_USECS \ + (XCVR_TX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) +#define XCVR_RX_SCHED_DELAY_USECS \ + (XCVR_RX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) + +/* + * Define HW whitelist size. This is the total possible whitelist size; + * not necessarily the size that will be used (may be smaller) + */ +#define BLE_HW_WHITE_LIST_SIZE (8) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_hw.c b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_hw.c new file mode 100644 index 0000000..400fddf --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_hw.c @@ -0,0 +1,491 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "../include/ble/xcvr.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nrf.h" +#include "nimble/nimble/controller/include/controller/ble_hw.h" +#if MYNEWT +#include "mcu/cmsis_nvic.h" +#else +#include "core_cm4.h" +#include "nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h" +#endif +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +/* Total number of resolving list elements */ +#define BLE_HW_RESOLV_LIST_SIZE (16) + +/* We use this to keep track of which entries are set to valid addresses */ +static uint8_t g_ble_hw_whitelist_mask; + +/* Random number generator isr callback */ +ble_rng_isr_cb_t g_ble_rng_isr_cb; + +/* If LL privacy is enabled, allocate memory for AAR */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + +/* The NRF51 supports up to 16 IRK entries */ +#if (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE) < 16) +#define NRF_IRK_LIST_ENTRIES (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) +#else +#define NRF_IRK_LIST_ENTRIES (16) +#endif + +/* NOTE: each entry is 16 bytes long. */ +uint32_t g_nrf_irk_list[NRF_IRK_LIST_ENTRIES * 4]; + +/* Current number of IRK entries */ +uint8_t g_nrf_num_irks; + +#endif + +/* Returns public device address or -1 if not present */ +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + uint32_t addr_high; + uint32_t addr_low; + + /* Does FICR have a public address */ + if ((NRF_FICR->DEVICEADDRTYPE & 1) != 0) { + return -1; + } + + /* Copy into device address. We can do this because we know platform */ + addr_low = NRF_FICR->DEVICEADDR[0]; + addr_high = NRF_FICR->DEVICEADDR[1]; + memcpy(addr->val, &addr_low, 4); + memcpy(&addr->val[4], &addr_high, 2); + addr->type = BLE_ADDR_PUBLIC; + + return 0; +} + +/* Returns random static address or -1 if not present */ +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + int rc; + + if ((NRF_FICR->DEVICEADDRTYPE & 1) == 1) { + memcpy(addr->val, (void *)&NRF_FICR->DEVICEADDR[0], 4); + memcpy(&addr->val[4], (void *)&NRF_FICR->DEVICEADDR[1], 2); + addr->val[5] |= 0xc0; + addr->type = BLE_ADDR_RANDOM; + rc = 0; + } else { + rc = -1; + } + + return rc; +} + +/** + * Clear the whitelist + * + * @return int + */ +void +ble_hw_whitelist_clear(void) +{ + NRF_RADIO->DACNF = 0; + g_ble_hw_whitelist_mask = 0; +} + +/** + * Add a device to the hw whitelist + * + * @param addr + * @param addr_type + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint32_t mask; + + /* Find first ununsed device address match element */ + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if ((mask & g_ble_hw_whitelist_mask) == 0) { + NRF_RADIO->DAB[i] = get_le32(addr); + NRF_RADIO->DAP[i] = get_le16(addr + 4); + if (addr_type == BLE_ADDR_RANDOM) { + NRF_RADIO->DACNF |= (mask << 8); + } + g_ble_hw_whitelist_mask |= mask; + return BLE_ERR_SUCCESS; + } + mask <<= 1; + } + + return BLE_ERR_MEM_CAPACITY; +} + +/** + * Remove a device from the hw whitelist + * + * @param addr + * @param addr_type + * + */ +void +ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type) +{ + int i; + uint8_t cfg_addr; + uint16_t dap; + uint16_t txadd; + uint32_t dab; + uint32_t mask; + + /* Find first ununsed device address match element */ + dab = get_le32(addr); + dap = get_le16(addr + 4); + txadd = NRF_RADIO->DACNF >> 8; + mask = 0x01; + for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) { + if (mask & g_ble_hw_whitelist_mask) { + if ((dab == NRF_RADIO->DAB[i]) && (dap == NRF_RADIO->DAP[i])) { + cfg_addr = txadd & mask; + if (addr_type == BLE_ADDR_RANDOM) { + if (cfg_addr != 0) { + break; + } + } else { + if (cfg_addr == 0) { + break; + } + } + } + } + mask <<= 1; + } + + if (i < BLE_HW_WHITE_LIST_SIZE) { + g_ble_hw_whitelist_mask &= ~mask; + NRF_RADIO->DACNF &= ~mask; + } +} + +/** + * Returns the size of the whitelist in HW + * + * @return int Number of devices allowed in whitelist + */ +uint8_t +ble_hw_whitelist_size(void) +{ + return BLE_HW_WHITE_LIST_SIZE; +} + +/** + * Enable the whitelisted devices + */ +void +ble_hw_whitelist_enable(void) +{ + /* Enable the configured device addresses */ + NRF_RADIO->DACNF |= g_ble_hw_whitelist_mask; +} + +/** + * Disables the whitelisted devices + */ +void +ble_hw_whitelist_disable(void) +{ + /* Disable all whitelist devices */ + NRF_RADIO->DACNF &= 0x0000ff00; +} + +/** + * Boolean function which returns true ('1') if there is a match on the + * whitelist. + * + * @return int + */ +int +ble_hw_whitelist_match(void) +{ + return (int)NRF_RADIO->EVENTS_DEVMATCH; +} + +/* Encrypt data */ +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + int rc; + uint32_t end; + uint32_t err; + + /* Stop ECB */ + NRF_ECB->TASKS_STOPECB = 1; + /* XXX: does task stop clear these counters? Anyway to do this quicker? */ + NRF_ECB->EVENTS_ENDECB = 0; + NRF_ECB->EVENTS_ERRORECB = 0; + NRF_ECB->ECBDATAPTR = (uint32_t)ecb; + + /* Start ECB */ + NRF_ECB->TASKS_STARTECB = 1; + + /* Wait till error or done */ + rc = 0; + while (1) { + end = NRF_ECB->EVENTS_ENDECB; + err = NRF_ECB->EVENTS_ERRORECB; + if (end || err) { + if (err) { + rc = -1; + } + break; + } + } + + return rc; +} + +/** + * Random number generator ISR. + */ +static void +ble_rng_isr(void) +{ + uint8_t rnum; + + os_trace_isr_enter(); + + /* No callback? Clear and disable interrupts */ + if (g_ble_rng_isr_cb == NULL) { + NRF_RNG->INTENCLR = 1; + NRF_RNG->EVENTS_VALRDY = 0; + (void)NRF_RNG->SHORTS; + os_trace_isr_exit(); + return; + } + + /* If there is a value ready grab it */ + if (NRF_RNG->EVENTS_VALRDY) { + NRF_RNG->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG->VALUE; + (*g_ble_rng_isr_cb)(rnum); + } + + os_trace_isr_exit(); +} + +/** + * Initialize the random number generator + * + * @param cb + * @param bias + * + * @return int + */ +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + /* Set bias */ + if (bias) { + NRF_RNG->CONFIG = 1; + } else { + NRF_RNG->CONFIG = 0; + } + + /* If we were passed a function pointer we need to enable the interrupt */ + if (cb != NULL) { +#ifndef RIOT_VERSION + NVIC_SetPriority(RNG_IRQn, (1 << __NVIC_PRIO_BITS) - 1); +#endif +#if MYNEWT + NVIC_SetVector(RNG_IRQn, (uint32_t)ble_rng_isr); +#else + ble_npl_hw_set_isr(RNG_IRQn, ble_rng_isr); +#endif + NVIC_EnableIRQ(RNG_IRQn); + g_ble_rng_isr_cb = cb; + } + + return 0; +} + +/** + * Start the random number generator + * + * @return int + */ +int +ble_hw_rng_start(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG->EVENTS_VALRDY = 0; + if (g_ble_rng_isr_cb) { + NRF_RNG->INTENSET = 1; + } + NRF_RNG->TASKS_START = 1; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Stop the random generator + * + * @return int + */ +int +ble_hw_rng_stop(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_RNG->INTENCLR = 1; + NRF_RNG->TASKS_STOP = 1; + NRF_RNG->EVENTS_VALRDY = 0; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t +ble_hw_rng_read(void) +{ + uint8_t rnum; + + /* Wait for a sample */ + while (NRF_RNG->EVENTS_VALRDY == 0) { + } + + NRF_RNG->EVENTS_VALRDY = 0; + rnum = (uint8_t)NRF_RNG->VALUE; + + return rnum; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +/** + * Clear the resolving list + * + * @return int + */ +void +ble_hw_resolv_list_clear(void) +{ + g_nrf_num_irks = 0; +} + +/** + * Add a device to the hw resolving list + * + * @param irk Pointer to IRK to add + * + * @return int 0: success, BLE error code otherwise + */ +int +ble_hw_resolv_list_add(uint8_t *irk) +{ + uint32_t *nrf_entry; + + /* Find first ununsed device address match element */ + if (g_nrf_num_irks == NRF_IRK_LIST_ENTRIES) { + return BLE_ERR_MEM_CAPACITY; + } + + /* Copy into irk list */ + nrf_entry = &g_nrf_irk_list[4 * g_nrf_num_irks]; + memcpy(nrf_entry, irk, 16); + + /* Add to total */ + ++g_nrf_num_irks; + return BLE_ERR_SUCCESS; +} + +/** + * Remove a device from the hw resolving list + * + * @param index Index of IRK to remove + */ +void +ble_hw_resolv_list_rmv(int index) +{ + uint32_t *irk_entry; + + if (index < g_nrf_num_irks) { + --g_nrf_num_irks; + irk_entry = &g_nrf_irk_list[index]; + if (g_nrf_num_irks > index) { + memmove(irk_entry, irk_entry + 4, 16 * (g_nrf_num_irks - index)); + } + } +} + +/** + * Returns the size of the resolving list. NOTE: this returns the maximum + * allowable entries in the HW. Configuration options may limit this. + * + * @return int Number of devices allowed in resolving list + */ +uint8_t +ble_hw_resolv_list_size(void) +{ + return BLE_HW_RESOLV_LIST_SIZE; +} + +/** + * Called to determine if the address received was resolved. + * + * @return int Negative values indicate unresolved address; positive values + * indicate index in resolving list of resolved address. + */ +int +ble_hw_resolv_list_match(void) +{ + uint32_t index; + + if (NRF_AAR->EVENTS_END) { + if (NRF_AAR->EVENTS_RESOLVED) { + index = NRF_AAR->STATUS; + return (int)index; + } + } + + return -1; +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy.c b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy.c new file mode 100644 index 0000000..b1fbe3f --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy.c @@ -0,0 +1,2120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + #if defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) + +#include +#include +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os.h" +#include "../include/ble/xcvr.h" +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/include/nimble/nimble_opt.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" +#include "nimble/nimble/controller/include/controller/ble_phy.h" +#include "nimble/nimble/controller/include/controller/ble_phy_trace.h" +#include "nimble/nimble/controller/include/controller/ble_ll.h" +#include "nrf.h" +#if MYNEWT +#include "mcu/nrf52_clock.h" +#include "mcu/cmsis_nvic.h" +#include "hal/hal_gpio.h" +#else +#include "core_cm4.h" +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811) +#error LE Coded PHY can only be enabled on nRF52811 or nRF52840 +#endif +#endif + +/* + * NOTE: This code uses a couple of PPI channels so care should be taken when + * using PPI somewhere else. + * + * Pre-programmed channels: CH20, CH21, CH23, CH25, CH31 + * Regular channels: CH4, CH5 and optionally CH17, CH18, CH19 + * - CH4 = cancel wfr timer on address match + * - CH5 = disable radio on wfr timer expiry + * - CH17 = (optional) gpio debug for radio ramp-up + * - CH18 = (optional) gpio debug for wfr timer RX enabled + * - CH19 = (optional) gpio debug for wfr timer radio disabled + * + */ + +/* XXX: 4) Make sure RF is higher priority interrupt than schedule */ + +/* + * XXX: Maximum possible transmit time is 1 msec for a 60ppm crystal + * and 16ms for a 30ppm crystal! We need to limit PDU size based on + * crystal accuracy. Look at this in the spec. + */ + +/* XXX: private header file? */ +extern uint8_t g_nrf_num_irks; +extern uint32_t g_nrf_irk_list[]; + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) + +/* + * We configure the nrf with a 1 byte S0 field, 8 bit length field, and + * zero bit S1 field. The preamble is 8 bits long. + */ +#define NRF_LFLEN_BITS (8) +#define NRF_S0LEN (1) +#define NRF_S1LEN_BITS (0) +#define NRF_CILEN_BITS (2) +#define NRF_TERMLEN_BITS (3) + +/* Maximum length of frames */ +#define NRF_MAXLEN (255) +#define NRF_BALEN (3) /* For base address of 3 bytes */ + +/* NRF_RADIO->PCNF0 configuration values */ +#define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ + (RADIO_PCNF0_S1INCL_Msk) | \ + (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ + (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) +#define NRF_PCNF0_1M (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_8bit << RADIO_PCNF0_PLEN_Pos) +#define NRF_PCNF0_2M (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_16bit << RADIO_PCNF0_PLEN_Pos) +#define NRF_PCNF0_CODED (NRF_PCNF0) | \ + (RADIO_PCNF0_PLEN_LongRange << RADIO_PCNF0_PLEN_Pos) | \ + (NRF_CILEN_BITS << RADIO_PCNF0_CILEN_Pos) | \ + (NRF_TERMLEN_BITS << RADIO_PCNF0_TERMLEN_Pos) + +/* BLE PHY data structure */ +struct ble_phy_obj +{ + uint8_t phy_stats_initialized; + int8_t phy_txpwr_dbm; + uint8_t phy_chan; + uint8_t phy_state; + uint8_t phy_transition; + uint8_t phy_transition_late; + uint8_t phy_rx_started; + uint8_t phy_encrypted; + uint8_t phy_privacy; + uint8_t phy_tx_pyld_len; + uint8_t phy_cur_phy_mode; + uint8_t phy_tx_phy_mode; + uint8_t phy_rx_phy_mode; + uint8_t phy_bcc_offset; + int8_t rx_pwr_compensation; + uint32_t phy_aar_scratch; + uint32_t phy_access_address; + struct ble_mbuf_hdr rxhdr; + void *txend_arg; + ble_phy_tx_end_func txend_cb; + uint32_t phy_start_cputime; +}; +struct ble_phy_obj g_ble_phy_data; + +/* XXX: if 27 byte packets desired we can make this smaller */ +/* Global transmit/receive buffer */ +static uint32_t g_ble_phy_tx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +static uint32_t g_ble_phy_rx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Make sure word-aligned for faster copies */ +static uint32_t g_ble_phy_enc_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +#endif + +/* RF center frequency for each channel index (offset from 2400 MHz) */ +static const uint8_t g_ble_phy_chan_freq[BLE_PHY_NUM_CHANS] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, /* 0-9 */ + 24, 28, 30, 32, 34, 36, 38, 40, 42, 44, /* 10-19 */ + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, /* 20-29 */ + 66, 68, 70, 72, 74, 76, 78, 2, 26, 80, /* 30-39 */ +}; + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/* packet start offsets (in usecs) */ +static const uint16_t g_ble_phy_mode_pkt_start_off[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 40, + [BLE_PHY_MODE_2M] = 24, + [BLE_PHY_MODE_CODED_125KBPS] = 376, + [BLE_PHY_MODE_CODED_500KBPS] = 376 +}; +#endif + +/* Various radio timings */ +/* Radio ramp-up times in usecs (fast mode) */ +#define BLE_PHY_T_TXENFAST (XCVR_TX_RADIO_RAMPUP_USECS) +#define BLE_PHY_T_RXENFAST (XCVR_RX_RADIO_RAMPUP_USECS) +/* delay between EVENTS_READY and start of tx */ +static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 4, + [BLE_PHY_MODE_2M] = 3, + [BLE_PHY_MODE_CODED_125KBPS] = 5, + [BLE_PHY_MODE_CODED_500KBPS] = 5 +}; +/* delay between EVENTS_END and end of txd packet */ +static const uint8_t g_ble_phy_t_txenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 4, + [BLE_PHY_MODE_2M] = 3, + [BLE_PHY_MODE_CODED_125KBPS] = 9, + [BLE_PHY_MODE_CODED_500KBPS] = 3 +}; +/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */ +static const uint8_t g_ble_phy_t_rxaddrdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 6, + [BLE_PHY_MODE_2M] = 2, + [BLE_PHY_MODE_CODED_125KBPS] = 17, + [BLE_PHY_MODE_CODED_500KBPS] = 17 +}; +/* delay between end of rxd packet and EVENTS_END */ +static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 6, + [BLE_PHY_MODE_2M] = 2, + [BLE_PHY_MODE_CODED_125KBPS] = 27, + [BLE_PHY_MODE_CODED_500KBPS] = 22 +}; + +/* Statistics */ +STATS_SECT_START(ble_phy_stats) + STATS_SECT_ENTRY(phy_isrs) + STATS_SECT_ENTRY(tx_good) + STATS_SECT_ENTRY(tx_fail) + STATS_SECT_ENTRY(tx_late) + STATS_SECT_ENTRY(tx_bytes) + STATS_SECT_ENTRY(rx_starts) + STATS_SECT_ENTRY(rx_aborts) + STATS_SECT_ENTRY(rx_valid) + STATS_SECT_ENTRY(rx_crc_err) + STATS_SECT_ENTRY(rx_late) + STATS_SECT_ENTRY(radio_state_errs) + STATS_SECT_ENTRY(rx_hw_err) + STATS_SECT_ENTRY(tx_hw_err) +STATS_SECT_END +STATS_SECT_DECL(ble_phy_stats) ble_phy_stats; + +STATS_NAME_START(ble_phy_stats) + STATS_NAME(ble_phy_stats, phy_isrs) + STATS_NAME(ble_phy_stats, tx_good) + STATS_NAME(ble_phy_stats, tx_fail) + STATS_NAME(ble_phy_stats, tx_late) + STATS_NAME(ble_phy_stats, tx_bytes) + STATS_NAME(ble_phy_stats, rx_starts) + STATS_NAME(ble_phy_stats, rx_aborts) + STATS_NAME(ble_phy_stats, rx_valid) + STATS_NAME(ble_phy_stats, rx_crc_err) + STATS_NAME(ble_phy_stats, rx_late) + STATS_NAME(ble_phy_stats, radio_state_errs) + STATS_NAME(ble_phy_stats, rx_hw_err) + STATS_NAME(ble_phy_stats, tx_hw_err) +STATS_NAME_END(ble_phy_stats) + +/* + * NOTE: + * Tested the following to see what would happen: + * -> NVIC has radio irq enabled (interrupt # 1, mask 0x2). + * -> Set up nrf to receive. Clear ADDRESS event register. + * -> Enable ADDRESS interrupt on nrf5 by writing to INTENSET. + * -> Enable RX. + * -> Disable interrupts globally using OS_ENTER_CRITICAL(). + * -> Wait until a packet is received and the ADDRESS event occurs. + * -> Call ble_phy_disable(). + * + * At this point I wanted to see the state of the cortex NVIC. The IRQ + * pending bit was TRUE for the radio interrupt (as expected) as we never + * serviced the radio interrupt (interrupts were disabled). + * + * What was unexpected was this: without clearing the pending IRQ in the NVIC, + * when radio interrupts were re-enabled (address event bit in INTENSET set to + * 1) and the radio ADDRESS event register read 1 (it was never cleared after + * the first address event), the radio did not enter the ISR! I would have + * expected that if the following were true, an interrupt would occur: + * -> NVIC ISER bit set to TRUE + * -> NVIC ISPR bit reads TRUE, meaning interrupt is pending. + * -> Radio peripheral interrupts are enabled for some event (or events). + * -> Corresponding event register(s) in radio peripheral read 1. + * + * Not sure what the end result of all this is. We will clear the pending + * bit in the NVIC just to be sure when we disable the PHY. + */ + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + +/* + * Per nordic, the number of bytes needed for scratch is 16 + MAX_PKT_SIZE. + * However, when I used a smaller size it still overwrote the scratchpad. Until + * I figure this out I am just going to allocate 67 words so we have enough + * space for 267 bytes of scratch. I used 268 bytes since not sure if this + * needs to be aligned and burning a byte is no big deal. + */ +//#define NRF_ENC_SCRATCH_WORDS (((MYNEWT_VAL(BLE_LL_MAX_PKT_SIZE) + 16) + 3) / 4) +#define NRF_ENC_SCRATCH_WORDS (67) + +uint32_t g_nrf_encrypt_scratchpad[NRF_ENC_SCRATCH_WORDS]; + +struct nrf_ccm_data +{ + uint8_t key[16]; + uint64_t pkt_counter; + uint8_t dir_bit; + uint8_t iv[8]; +} __attribute__((packed)); + +struct nrf_ccm_data g_nrf_ccm_data; +#endif + +static void +ble_phy_apply_errata_102_106_107(void) +{ + /* [102] RADIO: PAYLOAD/END events delayed or not triggered after ADDRESS + * [106] RADIO: Higher CRC error rates for some access addresses + * [107] RADIO: Immediate address match for access addresses containing MSBs 0x00 + */ + *(volatile uint32_t *)0x40001774 = ((*(volatile uint32_t *)0x40001774) & + 0xfffffffe) | 0x01000000; +} + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + +/* Packet start offset (in usecs). This is the preamble plus access address. + * For LE Coded PHY this also includes CI and TERM1. */ +uint32_t +ble_phy_mode_pdu_start_off(int phy_mode) +{ + return g_ble_phy_mode_pkt_start_off[phy_mode]; +} + +#if NRF52840_XXAA +static inline bool +ble_phy_mode_is_coded(uint8_t phy_mode) +{ + return (phy_mode == BLE_PHY_MODE_CODED_125KBPS) || + (phy_mode == BLE_PHY_MODE_CODED_500KBPS); +} + +static void +ble_phy_apply_nrf52840_errata(uint8_t new_phy_mode) +{ + bool new_coded = ble_phy_mode_is_coded(new_phy_mode); + bool cur_coded = ble_phy_mode_is_coded(g_ble_phy_data.phy_cur_phy_mode); + + /* + * Workarounds should be applied only when switching to/from LE Coded PHY + * so no need to apply them every time. + * + * nRF52840 Engineering A Errata v1.2 + * [164] RADIO: Low sensitivity in long range mode + * + * nRF52840 Rev 1 Errata + * [191] RADIO: High packet error rate in BLE Long Range mode + */ + if (new_coded == cur_coded) { + return; + } + + if (new_coded) { +#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_164) + /* [164] */ + *(volatile uint32_t *)0x4000173C |= 0x80000000; + *(volatile uint32_t *)0x4000173C = + ((*(volatile uint32_t *)0x4000173C & 0xFFFFFF00) | 0x5C); +#endif +#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_191) + /* [191] */ + *(volatile uint32_t *) 0x40001740 = + ((*((volatile uint32_t *) 0x40001740)) & 0x7FFF00FF) | + 0x80000000 | (((uint32_t)(196)) << 8); +#endif + } else { +#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_164) + /* [164] */ + *(volatile uint32_t *)0x4000173C &= ~0x80000000; +#endif +#if MYNEWT_VAL(BLE_PHY_NRF52840_ERRATA_191) + /* [191] */ + *(volatile uint32_t *) 0x40001740 = + ((*((volatile uint32_t *) 0x40001740)) & 0x7FFFFFFF); +#endif + } +} +#endif + +static void +ble_phy_mode_apply(uint8_t phy_mode) +{ + if (phy_mode == g_ble_phy_data.phy_cur_phy_mode) { + return; + } + +#if NRF52840_XXAA + ble_phy_apply_nrf52840_errata(phy_mode); +#endif + + switch (phy_mode) { + case BLE_PHY_MODE_1M: + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO->PCNF0 = NRF_PCNF0_1M; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_MODE_2M: + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_2Mbit; + NRF_RADIO->PCNF0 = NRF_PCNF0_2M; + break; +#endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_MODE_CODED_125KBPS: + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR125Kbit; + NRF_RADIO->PCNF0 = NRF_PCNF0_CODED; + break; + case BLE_PHY_MODE_CODED_500KBPS: + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_LR500Kbit; + NRF_RADIO->PCNF0 = NRF_PCNF0_CODED; + break; +#endif + default: + assert(0); + } + + g_ble_phy_data.phy_cur_phy_mode = phy_mode; +} + +void +ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode) +{ + g_ble_phy_data.phy_tx_phy_mode = tx_phy_mode; + g_ble_phy_data.phy_rx_phy_mode = rx_phy_mode; +} +#endif + +int +ble_phy_get_cur_phy(void) +{ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + switch (g_ble_phy_data.phy_cur_phy_mode) { + case BLE_PHY_MODE_1M: + return BLE_PHY_1M; + case BLE_PHY_MODE_2M: + return BLE_PHY_2M; + case BLE_PHY_MODE_CODED_125KBPS: + case BLE_PHY_MODE_CODED_500KBPS: + return BLE_PHY_CODED; + default: + assert(0); + return -1; + } +#else + return BLE_PHY_1M; +#endif +} + +/** + * Copies the data from the phy receive buffer into a mbuf chain. + * + * @param dptr Pointer to receive buffer + * @param rxpdu Pointer to already allocated mbuf chain + * + * NOTE: the packet header already has the total mbuf length in it. The + * lengths of the individual mbufs are not set prior to calling. + * + */ +void +ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu) +{ + uint32_t rem_len; + uint32_t copy_len; + uint32_t block_len; + uint32_t block_rem_len; + void *dst; + void *src; + struct os_mbuf * om; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + block_len = rxpdu->om_omp->omp_databuf_len; + rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len; + src = dptr; + + /* + * Setup for copying from first mbuf which is shorter due to packet header + * and extra leading space + */ + copy_len = block_len - rxpdu->om_pkthdr_len - 4; + om = rxpdu; + dst = om->om_data; + + while (true) { + /* + * Always copy blocks of length aligned to word size, only last mbuf + * will have remaining non-word size bytes appended. + */ + block_rem_len = copy_len; + copy_len = min(copy_len, rem_len); + copy_len &= ~3; + + dst = om->om_data; + om->om_len = copy_len; + rem_len -= copy_len; + block_rem_len -= copy_len; + + __asm__ volatile (".syntax unified \n" + " mov r4, %[len] \n" + " b 2f \n" + "1: ldr r3, [%[src], %[len]] \n" + " str r3, [%[dst], %[len]] \n" + "2: subs %[len], #4 \n" + " bpl 1b \n" + " adds %[src], %[src], r4 \n" + " adds %[dst], %[dst], r4 \n" + : [dst] "+r" (dst), [src] "+r" (src), + [len] "+r" (copy_len) + : + : "r3", "r4", "memory" + ); + + if ((rem_len < 4) && (block_rem_len >= rem_len)) { + break; + } + + /* Move to next mbuf */ + om = SLIST_NEXT(om, om_next); + copy_len = block_len; + } + + /* Copy remaining bytes, if any, to last mbuf */ + om->om_len += rem_len; + __asm__ volatile (".syntax unified \n" + " b 2f \n" + "1: ldrb r3, [%[src], %[len]] \n" + " strb r3, [%[dst], %[len]] \n" + "2: subs %[len], #1 \n" + " bpl 1b \n" + : [len] "+r" (rem_len) + : [dst] "r" (dst), [src] "r" (src) + : "r3", "memory" + ); + + /* Copy header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr, + sizeof(struct ble_mbuf_hdr)); +} + +/** + * Called when we want to wait if the radio is in either the rx or tx + * disable states. We want to wait until that state is over before doing + * anything to the radio + */ +static void +nrf_wait_disabled(void) +{ + uint32_t state; + + state = NRF_RADIO->STATE; + if (state != RADIO_STATE_STATE_Disabled) { + if ((state == RADIO_STATE_STATE_RxDisable) || + (state == RADIO_STATE_STATE_TxDisable)) { + /* This will end within a short time (6 usecs). Just poll */ + while (NRF_RADIO->STATE == state) { + /* If this fails, something is really wrong. Should last + * no more than 6 usecs */ + } + } + } +} + +/** + * + * + */ +static int +ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs, bool tx) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + /* + * We need to adjust start time to include radio ramp-up and TX pipeline + * delay (the latter only if applicable, so only for TX). + * + * Radio ramp-up time is 40 usecs and TX delay is 3 or 5 usecs depending on + * phy, thus we'll offset RTC by 2 full ticks (61 usecs) and then compensate + * using TIMER0 with 1 usec precision. + */ + + cputime -= 2; + rem_usecs += 61; + if (tx) { + rem_usecs -= BLE_PHY_T_TXENFAST; + rem_usecs -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; + } else { + rem_usecs -= BLE_PHY_T_RXENFAST; + } + + /* + * rem_usecs will be no more than 2 ticks, but if it is more than single + * tick then we should better count one more low-power tick rather than + * 30 high-power usecs. Also make sure we don't set TIMER0 CC to 0 as the + * compare won't occur. + */ + + if (rem_usecs > 30) { + cputime++; + rem_usecs -= 30; + } + + /* + * Can we set the RTC compare to start TIMER0? We can do it if: + * a) Current compare value is not N+1 or N+2 ticks from current + * counter. + * b) The value we want to set is not at least N+2 from current + * counter. + * + * NOTE: since the counter can tick 1 while we do these calculations we + * need to account for it. + */ + next_cc = cputime & 0xffffff; + cur_cc = NRF_RTC0->CC[0]; + cntr = NRF_RTC0->COUNTER; + + delta = (cur_cc - cntr) & 0xffffff; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + delta = (next_cc - cntr) & 0xffffff; + if ((delta & 0x800000) || (delta < 3)) { + return -1; + } + + /* Clear and set TIMER0 to fire off at proper time */ + NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->CC[0] = rem_usecs; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + + /* Set RTC compare to start TIMER0 */ + NRF_RTC0->EVENTS_COMPARE[0] = 0; + NRF_RTC0->CC[0] = next_cc; + NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_PPI->CHENSET = PPI_CHEN_CH31_Msk; + + /* Store the cputime at which we set the RTC */ + g_ble_phy_data.phy_start_cputime = cputime; + + return 0; +} + +static int +ble_phy_set_start_now(void) +{ + os_sr_t sr; + uint32_t now; + + OS_ENTER_CRITICAL(sr); + + /* + * Set TIMER0 to fire immediately. We can't set CC to 0 as compare will not + * occur in such case. + */ + NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->CC[0] = 1; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + + /* + * Set RTC compare to start TIMER0. We need to set it to at least N+2 ticks + * from current value to guarantee triggering compare event, but let's set + * it to N+3 to account for possible extra tick on RTC0 during these + * operations. + */ + now = os_cputime_get32(); + NRF_RTC0->EVENTS_COMPARE[0] = 0; + NRF_RTC0->CC[0] = now + 3; + NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_PPI->CHENSET = PPI_CHEN_CH31_Msk; + + /* + * Store the cputime at which we set the RTC + * + * XXX Compare event may be triggered on previous CC value (if it was set to + * less than N+2) so in rare cases actual start time may be 2 ticks earlier + * than what we expect. Since this is only used on RX, it may cause AUX scan + * to be scheduled 1 or 2 ticks too late so we'll miss it - it's acceptable + * for now. + */ + g_ble_phy_data.phy_start_cputime = now + 3; + + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Function is used to set PPI so that we can time out waiting for a reception + * to occur. This happens for two reasons: we have sent a packet and we are + * waiting for a respons (txrx should be set to ENABLE_TXRX) or we are + * starting a connection event and we are a slave and we are waiting for the + * master to send us a packet (txrx should be set to ENABLE_RX). + * + * NOTE: when waiting for a txrx turn-around, wfr_usecs is not used as there + * is no additional time to wait; we know when we should receive the address of + * the received frame. + * + * @param txrx Flag denoting if this wfr is a txrx turn-around or not. + * @param tx_phy_mode phy mode for last TX (only valid for TX->RX) + * @param wfr_usecs Amount of usecs to wait. + */ +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ + uint32_t end_time; + uint8_t phy; + + phy = g_ble_phy_data.phy_cur_phy_mode; + + if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { + /* RX shall start exactly T_IFS after TX end captured in CC[2] */ + end_time = NRF_TIMER0->CC[2] + BLE_LL_IFS; + /* Adjust for delay between EVENT_END and actual TX end time */ + end_time += g_ble_phy_t_txenddelay[tx_phy_mode]; + /* Wait a bit longer due to allowed active clock accuracy */ + end_time += 2; + /* + * It's possible that we'll capture PDU start time at the end of timer + * cycle and since wfr expires at the beginning of calculated timer + * cycle it can be almost 1 usec too early. Let's compensate for this + * by waiting 1 usec more. + */ + end_time += 1; +#if MYNEWT_VAL(BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN) > 0 + if ((phy == BLE_PHY_MODE_CODED_125KBPS) || + (phy == BLE_PHY_MODE_CODED_500KBPS)) { + /* + * Some controllers exceed T_IFS when transmitting on coded phy + * so let's wait a bit longer to be able to talk to them if this + * workaround is enabled. + */ + end_time += MYNEWT_VAL(BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN); + } +#endif + } else { + /* + * RX shall start no later than wfr_usecs after RX enabled. + * CC[0] is the time of RXEN so adjust for radio ram-up. + * Do not add jitter since this is already covered by LL. + */ + end_time = NRF_TIMER0->CC[0] + BLE_PHY_T_RXENFAST + wfr_usecs; + } + + /* + * Note: on LE Coded EVENT_ADDRESS is fired after TERM1 is received, so + * we are actually calculating relative to start of packet payload + * which is fine. + */ + + /* Adjust for receiving access address since this triggers EVENT_ADDRESS */ + end_time += ble_phy_mode_pdu_start_off(phy); + /* Adjust for delay between actual access address RX and EVENT_ADDRESS */ + end_time += g_ble_phy_t_rxaddrdelay[phy]; + + /* wfr_secs is the time from rxen until timeout */ + NRF_TIMER0->CC[3] = end_time; + NRF_TIMER0->EVENTS_COMPARE[3] = 0; + + /* Enable wait for response PPI */ + NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk); + + /* Enable the disabled interrupt so we time out on events compare */ + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + + /* + * It may happen that if CPU is halted for a brief moment (e.g. during flash + * erase or write), TIMER0 already counted past CC[3] and thus wfr will not + * fire as expected. In case this happened, let's just disable PPIs for wfr + * and trigger wfr manually (i.e. disable radio). + * + * Note that the same applies to RX start time set in CC[0] but since it + * should fire earlier than wfr, fixing wfr is enough. + * + * CC[1] is only used as a reference on RX start, we do not need it here so + * it can be used to read TIMER0 counter. + */ + NRF_TIMER0->TASKS_CAPTURE[1] = 1; + if (NRF_TIMER0->CC[1] > NRF_TIMER0->CC[3]) { + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk; + NRF_RADIO->TASKS_DISABLE = 1; + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +static uint32_t +ble_phy_get_ccm_datarate(void) +{ +#if BLE_LL_BT5_PHY_SUPPORTED + switch (g_ble_phy_data.phy_cur_phy_mode) { + case BLE_PHY_MODE_1M: + return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos; + case BLE_PHY_MODE_2M: + return CCM_MODE_DATARATE_2Mbit << CCM_MODE_DATARATE_Pos; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + case BLE_PHY_MODE_CODED_125KBPS: + return CCM_MODE_DATARATE_125Kbps << CCM_MODE_DATARATE_Pos; + case BLE_PHY_MODE_CODED_500KBPS: + return CCM_MODE_DATARATE_500Kbps << CCM_MODE_DATARATE_Pos; +#endif + } + + assert(0); + return 0; +#else + return CCM_MODE_DATARATE_1Mbit << CCM_MODE_DATARATE_Pos; +#endif +} +#endif + +/** + * Setup transceiver for receive. + */ +static void +ble_phy_rx_xcvr_setup(void) +{ + uint8_t *dptr; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + dptr += 3; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->OUTPTR = (uint32_t)dptr; + NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; + NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; + NRF_CCM->SHORTS = 0; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_ENDCRYPT = 0; + NRF_CCM->TASKS_KSGEN = 1; + NRF_PPI->CHENSET = PPI_CHEN_CH25_Msk; + } else { + NRF_RADIO->PACKETPTR = (uint32_t)dptr; + } +#else + NRF_RADIO->PACKETPTR = (uint32_t)dptr; +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + if (g_ble_phy_data.phy_privacy) { + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled; + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch; + NRF_AAR->EVENTS_END = 0; + NRF_AAR->EVENTS_RESOLVED = 0; + NRF_AAR->EVENTS_NOTRESOLVED = 0; + } else { + if (g_ble_phy_data.phy_encrypted == 0) { + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled; + } + } +#endif + + /* Turn off trigger TXEN on output compare match and AAR on bcmatch */ + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk | PPI_CHEN_CH23_Msk; + + /* Reset the rx started flag. Used for the wait for response */ + g_ble_phy_data.phy_rx_started = 0; + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + +#if BLE_LL_BT5_PHY_SUPPORTED + /* + * On Coded PHY there are CI and TERM1 fields before PDU starts so we need + * to take this into account when setting up BCC. + */ + if (g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_125KBPS || + g_ble_phy_data.phy_cur_phy_mode == BLE_PHY_MODE_CODED_500KBPS) { + g_ble_phy_data.phy_bcc_offset = 5; + } else { + g_ble_phy_data.phy_bcc_offset = 0; + } +#else + g_ble_phy_data.phy_bcc_offset = 0; +#endif + + /* I want to know when 1st byte received (after address) */ + NRF_RADIO->BCC = 8 + g_ble_phy_data.phy_bcc_offset; /* in bits */ + NRF_RADIO->EVENTS_ADDRESS = 0; + NRF_RADIO->EVENTS_DEVMATCH = 0; + NRF_RADIO->EVENTS_BCMATCH = 0; + NRF_RADIO->EVENTS_RSSIEND = 0; + NRF_RADIO->EVENTS_CRCOK = 0; + NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; + + NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk; +} + +/** + * Called from interrupt context when the transmit ends + * + */ +static void +ble_phy_tx_end_isr(void) +{ + uint8_t tx_phy_mode; + uint8_t was_encrypted; + uint8_t transition; + uint32_t rx_time; + uint32_t wfr_time; + + /* Store PHY on which we've just transmitted smth */ + tx_phy_mode = g_ble_phy_data.phy_cur_phy_mode; + + /* If this transmission was encrypted we need to remember it */ + was_encrypted = g_ble_phy_data.phy_encrypted; + (void)was_encrypted; + + /* Better be in TX state! */ + assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); + + /* Clear events and clear interrupt on disabled event */ + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk; + NRF_RADIO->EVENTS_END = 0; + wfr_time = NRF_RADIO->SHORTS; + (void)wfr_time; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* + * XXX: not sure what to do. We had a HW error during transmission. + * For now I just count a stat but continue on like all is good. + */ + if (was_encrypted) { + if (NRF_CCM->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, tx_hw_err); + NRF_CCM->EVENTS_ERROR = 0; + } + } +#endif + + /* Call transmit end callback */ + if (g_ble_phy_data.txend_cb) { + g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); + } + + transition = g_ble_phy_data.phy_transition; + if (transition == BLE_PHY_TRANSITION_TX_RX) { + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); +#endif + + /* Packet pointer needs to be reset. */ + ble_phy_rx_xcvr_setup(); + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, tx_phy_mode, 0); + + /* Schedule RX exactly T_IFS after TX end captured in CC[2] */ + rx_time = NRF_TIMER0->CC[2] + BLE_LL_IFS; + /* Adjust for delay between EVENT_END and actual TX end time */ + rx_time += g_ble_phy_t_txenddelay[tx_phy_mode]; + /* Adjust for radio ramp-up */ + rx_time -= BLE_PHY_T_RXENFAST; + /* Start listening a bit earlier due to allowed active clock accuracy */ + rx_time -= 2; + + NRF_TIMER0->CC[0] = rx_time; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; + } else { + /* + * XXX: not sure we need to stop the timer here all the time. Or that + * it should be stopped here. + */ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | + PPI_CHEN_CH20_Msk | PPI_CHEN_CH31_Msk; + assert(transition == BLE_PHY_TRANSITION_NONE); + } +} + +static inline uint8_t +ble_phy_get_cur_rx_phy_mode(void) +{ + uint8_t phy; + + phy = g_ble_phy_data.phy_cur_phy_mode; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + /* + * For Coded PHY mode can be set to either codings since actual coding is + * set in packet header. However, here we need actual coding of received + * packet as this determines pipeline delays so need to figure this out + * using CI field. + */ + if ((phy == BLE_PHY_MODE_CODED_125KBPS) || + (phy == BLE_PHY_MODE_CODED_500KBPS)) { + phy = NRF_RADIO->PDUSTAT & RADIO_PDUSTAT_CISTAT_Msk ? + BLE_PHY_MODE_CODED_500KBPS : + BLE_PHY_MODE_CODED_125KBPS; + } +#endif + + return phy; +} + +static void +ble_phy_rx_end_isr(void) +{ + int rc; + uint8_t *dptr; + uint8_t crcok; + uint32_t tx_time; + struct ble_mbuf_hdr *ble_hdr; + + /* Clear events and clear interrupt */ + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->INTENCLR = RADIO_INTENCLR_END_Msk; + + /* Disable automatic RXEN */ + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + + /* Set RSSI and CRC status flag in header */ + ble_hdr = &g_ble_phy_data.rxhdr; + assert(NRF_RADIO->EVENTS_RSSIEND != 0); + ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO->RSSISAMPLE) + + g_ble_phy_data.rx_pwr_compensation; + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + dptr += 3; + + /* Count PHY crc errors and valid packets */ + crcok = NRF_RADIO->EVENTS_CRCOK; + if (!crcok) { + STATS_INC(ble_phy_stats, rx_crc_err); + } else { + STATS_INC(ble_phy_stats, rx_valid); + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only set MIC failure flag if frame is not zero length */ + if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; + } + + /* + * XXX: not sure how to deal with this. This should not + * be a MIC failure but we should not hand it up. I guess + * this is just some form of rx error and that is how we + * handle it? For now, just set CRC error flags + */ + if (NRF_CCM->EVENTS_ERROR) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + + /* + * XXX: This is a total hack work-around for now but I dont + * know what else to do. If ENDCRYPT is not set and we are + * encrypted we need to not trust this frame and drop it. + */ + if (NRF_CCM->EVENTS_ENDCRYPT == 0) { + STATS_INC(ble_phy_stats, rx_hw_err); + ble_hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_CRC_OK; + } + } +#endif + } + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); +#endif + + /* + * Let's schedule TX now and we will just cancel it after processing RXed + * packet if we don't need TX. + * + * We need this to initiate connection in case AUX_CONNECT_REQ was sent on + * LE Coded S8. In this case the time we process RXed packet is roughly the + * same as the limit when we need to have TX scheduled (i.e. TIMER0 and PPI + * armed) so we may simply miss the slot and set the timer in the past. + * + * When TX is scheduled in advance, we may event process packet a bit longer + * during radio ramp-up - this gives us extra 40 usecs which is more than + * enough. + */ + + /* Schedule TX exactly T_IFS after RX end captured in CC[2] */ + tx_time = NRF_TIMER0->CC[2] + BLE_LL_IFS; + /* Adjust for delay between actual RX end time and EVENT_END */ + tx_time -= g_ble_phy_t_rxenddelay[ble_hdr->rxinfo.phy_mode]; + /* Adjust for radio ramp-up */ + tx_time -= BLE_PHY_T_TXENFAST; + /* Adjust for delay between EVENT_READY and actual TX start time */ + tx_time -= g_ble_phy_t_txdelay[g_ble_phy_data.phy_cur_phy_mode]; + + NRF_TIMER0->CC[0] = tx_time; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; + + /* + * XXX: Hack warning! + * + * It may happen (during flash erase) that CPU is stopped for a moment and + * TIMER0 already counted past CC[0]. In such case we will be stuck waiting + * for TX to start since EVENTS_COMPARE[0] will not happen any time soon. + * For now let's set a flag denoting that we are late in RX-TX transition so + * ble_phy_tx() will fail - this allows everything to cleanup nicely without + * the need for extra handling in many places. + * + * Note: CC[3] is used only for wfr which we do not need here. + */ + NRF_TIMER0->TASKS_CAPTURE[3] = 1; + if (NRF_TIMER0->CC[3] > NRF_TIMER0->CC[0]) { + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; + g_ble_phy_data.phy_transition_late = 1; + } + + /* + * XXX: This is a horrible ugly hack to deal with the RAM S1 byte + * that is not sent over the air but is present here. Simply move the + * data pointer to deal with it. Fix this later. + */ + dptr[2] = dptr[1]; + dptr[1] = dptr[0]; + rc = ble_ll_rx_end(dptr + 1, ble_hdr); + if (rc < 0) { + ble_phy_disable(); + } +} + +static bool +ble_phy_rx_start_isr(void) +{ + int rc; + uint32_t state; + uint32_t usecs; + uint32_t pdu_usecs; + uint32_t ticks; + struct ble_mbuf_hdr *ble_hdr; + uint8_t *dptr; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + int adva_offset; +#endif + + dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; + + /* Clear events and clear interrupt */ + NRF_RADIO->EVENTS_ADDRESS = 0; + + /* Clear wfr timer channels and DISABLED interrupt */ + NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk | RADIO_INTENCLR_ADDRESS_Msk; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk; + + /* Initialize the ble mbuf header */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; + ble_hdr->rxinfo.handle = 0; + ble_hdr->rxinfo.phy = ble_phy_get_cur_phy(); + ble_hdr->rxinfo.phy_mode = ble_phy_get_cur_rx_phy_mode(); +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.user_data = NULL; +#endif + + /* + * Calculate accurate packets start time (with remainder) + * + * We may start receiving packet somewhere during preamble in which case + * it is possible that actual transmission started before TIMER0 was + * running - need to take this into account. + */ + ble_hdr->beg_cputime = g_ble_phy_data.phy_start_cputime; + + usecs = NRF_TIMER0->CC[1]; + pdu_usecs = ble_phy_mode_pdu_start_off(ble_hdr->rxinfo.phy_mode) + + g_ble_phy_t_rxaddrdelay[ble_hdr->rxinfo.phy_mode]; + if (usecs < pdu_usecs) { + g_ble_phy_data.phy_start_cputime--; + usecs += 30; + } + usecs -= pdu_usecs; + + ticks = os_cputime_usecs_to_ticks(usecs); + usecs -= os_cputime_ticks_to_usecs(ticks); + if (usecs == 31) { + usecs = 0; + ++ticks; + } + + ble_hdr->beg_cputime += ticks; + ble_hdr->rem_usecs = usecs; + + /* XXX: I wonder if we always have the 1st byte. If we need to wait for + * rx chain delay, it could be 18 usecs from address interrupt. The + nrf52 may be able to get here early. */ + /* Wait to get 1st byte of frame */ + while (1) { + state = NRF_RADIO->STATE; + if (NRF_RADIO->EVENTS_BCMATCH != 0) { + break; + } + + /* + * If state is disabled, we should have the BCMATCH. If not, + * something is wrong! + */ + if (state == RADIO_STATE_STATE_Disabled) { + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO->SHORTS = 0; + return false; + } + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + /* + * If privacy is enabled and received PDU has TxAdd bit set (i.e. random + * address) we try to resolve address using AAR. + */ + if (g_ble_phy_data.phy_privacy && (dptr[3] & 0x40)) { + /* + * AdvA is located at 4th octet in RX buffer (after S0, length an S1 + * fields). In case of extended advertising PDU we need to add 2 more + * octets for extended header. + */ + adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0; + NRF_AAR->ADDRPTR = (uint32_t)(dptr + 3 + adva_offset); + + /* Trigger AAR after last bit of AdvA is received */ + NRF_RADIO->EVENTS_BCMATCH = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH23_Msk; + NRF_RADIO->BCC = (BLE_LL_PDU_HDR_LEN + adva_offset + BLE_DEV_ADDR_LEN) * 8 + + g_ble_phy_data.phy_bcc_offset; + } +#endif + + /* Call Link Layer receive start function */ + rc = ble_ll_rx_start(dptr + 3, + g_ble_phy_data.phy_chan, + &g_ble_phy_data.rxhdr); + if (rc >= 0) { + /* Set rx started flag and enable rx end ISR */ + g_ble_phy_data.phy_rx_started = 1; + NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; + } else { + /* Disable PHY */ + ble_phy_disable(); + STATS_INC(ble_phy_stats, rx_aborts); + } + + /* Count rx starts */ + STATS_INC(ble_phy_stats, rx_starts); + + return true; +} + +static void +ble_phy_isr(void) +{ + uint32_t irq_en; + + os_trace_isr_enter(); + + /* Read irq register to determine which interrupts are enabled */ + irq_en = NRF_RADIO->INTENCLR; + + /* + * NOTE: order of checking is important! Possible, if things get delayed, + * we have both an ADDRESS and DISABLED interrupt in rx state. If we get + * an address, we disable the DISABLED interrupt. + */ + + /* We get this if we have started to receive a frame */ + if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) { + /* + * wfr timer is calculated to expire at the exact time we should start + * receiving a packet (with 1 usec precision) so it is possible it will + * fire at the same time as EVENT_ADDRESS. If this happens, radio will + * be disabled while we are waiting for EVENT_BCCMATCH after 1st byte + * of payload is received and ble_phy_rx_start_isr() will fail. In this + * case we should not clear DISABLED irq mask so it will be handled as + * regular radio disabled event below. In other case radio was disabled + * on purpose and there's nothing more to handle so we can clear mask. + */ + if (ble_phy_rx_start_isr()) { + irq_en &= ~RADIO_INTENCLR_DISABLED_Msk; + } + } + + /* Check for disabled event. This only happens for transmits now */ + if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) { + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) { + NRF_RADIO->EVENTS_DISABLED = 0; + ble_ll_wfr_timer_exp(NULL); + } else if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) { + assert(0); + } else { + ble_phy_tx_end_isr(); + } + } + + /* Receive packet end (we dont enable this for transmit) */ + if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) { + ble_phy_rx_end_isr(); + } + + g_ble_phy_data.phy_transition_late = 0; + + /* Ensures IRQ is cleared */ + irq_en = NRF_RADIO->SHORTS; + + /* Count # of interrupts */ + STATS_INC(ble_phy_stats, phy_isrs); + + os_trace_isr_exit(); +} + +#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 || \ + MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \ + MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0 +static inline void +ble_phy_dbg_time_setup_gpiote(int index, int pin) +{ + NRF_GPIO_Type *port; + +#if NRF52840_XXAA + port = pin > 31 ? NRF_P1 : NRF_P0; + pin &= 0x1f; +#else + port = NRF_P0; +#endif + + /* Configure GPIO directly to avoid dependency to hal_gpio (for porting) */ + port->DIRSET = (1 << pin); + port->OUTCLR = (1 << pin); + + NRF_GPIOTE->CONFIG[index] = + (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) | + ((pin & 0x1F) << GPIOTE_CONFIG_PSEL_Pos) | +#if NRF52840_XXAA + ((port == NRF_P1) << GPIOTE_CONFIG_PORT_Pos); +#else + 0; +#endif +} +#endif + +static void +ble_phy_dbg_time_setup(void) +{ + int gpiote_idx __attribute__((unused)) = 8; + + /* + * We setup GPIOTE starting from last configuration index to minimize risk + * of conflict with GPIO setup via hal. It's not great solution, but since + * this is just debugging code we can live with this. + */ + +#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 + ble_phy_dbg_time_setup_gpiote(--gpiote_idx, + MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN)); + + NRF_PPI->CH[17].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY); + NRF_PPI->CH[17].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]); + NRF_PPI->CHENSET = PPI_CHEN_CH17_Msk; + + /* CH[20] and PPI CH[21] are on to trigger TASKS_TXEN or TASKS_RXEN */ + NRF_PPI->FORK[20].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]); + NRF_PPI->FORK[21].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]); +#endif + +#if MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 + ble_phy_dbg_time_setup_gpiote(--gpiote_idx, + MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN)); + + /* CH[26] and CH[27] are always on for EVENT_ADDRESS and EVENT_END */ + NRF_PPI->FORK[26].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]); + NRF_PPI->FORK[27].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]); +#endif + +#if MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0 + ble_phy_dbg_time_setup_gpiote(--gpiote_idx, + MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN)); + +#if NRF52840_XXAA + NRF_PPI->CH[18].EEP = (uint32_t)&(NRF_RADIO->EVENTS_RXREADY); +#else + NRF_PPI->CH[18].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY); +#endif + NRF_PPI->CH[18].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]); + NRF_PPI->CH[19].EEP = (uint32_t)&(NRF_RADIO->EVENTS_DISABLED); + NRF_PPI->CH[19].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]); + NRF_PPI->CHENSET = PPI_CHEN_CH18_Msk | PPI_CHEN_CH19_Msk; + + /* CH[4] and CH[5] are always on for wfr */ + NRF_PPI->FORK[4].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]); + NRF_PPI->FORK[5].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]); +#endif +} + +/** + * ble phy initialize + * + * Initialize the PHY. + * + * @return int 0: success; PHY error code otherwise + */ +int +ble_phy_init(void) +{ + int rc; + + /* Default phy to use is 1M */ + g_ble_phy_data.phy_cur_phy_mode = BLE_PHY_MODE_1M; + g_ble_phy_data.phy_tx_phy_mode = BLE_PHY_MODE_1M; + g_ble_phy_data.phy_rx_phy_mode = BLE_PHY_MODE_1M; + + g_ble_phy_data.rx_pwr_compensation = 0; + + /* Set phy channel to an invalid channel so first set channel works */ + g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; + + /* Toggle peripheral power to reset (just in case) */ + NRF_RADIO->POWER = 0; + NRF_RADIO->POWER = 1; + + /* Disable all interrupts */ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Set configuration registers */ + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO->PCNF0 = NRF_PCNF0; + + /* XXX: should maxlen be 251 for encryption? */ + NRF_RADIO->PCNF1 = NRF_MAXLEN | + (RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos) | + (NRF_BALEN << RADIO_PCNF1_BALEN_Pos) | + RADIO_PCNF1_WHITEEN_Msk; + + /* Enable radio fast ramp-up */ + NRF_RADIO->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) & + RADIO_MODECNF0_RU_Msk; + + /* Set logical address 1 for TX and RX */ + NRF_RADIO->TXADDRESS = 0; + NRF_RADIO->RXADDRESSES = (1 << 0); + + /* Configure the CRC registers */ + NRF_RADIO->CRCCNF = (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos) | RADIO_CRCCNF_LEN_Three; + + /* Configure BLE poly */ + NRF_RADIO->CRCPOLY = 0x0000065B; + + /* Configure IFS */ + NRF_RADIO->TIFS = BLE_LL_IFS; + + /* Captures tx/rx start in timer0 cc 1 and tx/rx end in timer0 cc 2 */ + NRF_PPI->CHENSET = PPI_CHEN_CH26_Msk | PPI_CHEN_CH27_Msk; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + NRF_CCM->INTENCLR = 0xffffffff; + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM->EVENTS_ERROR = 0; + memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad)); +#endif + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + g_ble_phy_data.phy_aar_scratch = 0; + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + NRF_AAR->INTENCLR = 0xffffffff; + NRF_AAR->EVENTS_END = 0; + NRF_AAR->EVENTS_RESOLVED = 0; + NRF_AAR->EVENTS_NOTRESOLVED = 0; + NRF_AAR->NIRK = 0; +#endif + + /* TIMER0 setup for PHY when using RTC */ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ + NRF_TIMER0->MODE = 0; /* Timer mode */ + NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ + + /* + * PPI setup. + * Channel 4: Captures TIMER0 in CC[3] when EVENTS_ADDRESS occurs. Used + * to cancel the wait for response timer. + * Channel 5: TIMER0 CC[3] to TASKS_DISABLE on radio. This is the wait + * for response timer. + */ + NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS); + NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[3]); + NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[3]); + NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE); + + /* Set isr in vector table and enable interrupt */ +#ifndef RIOT_VERSION + NVIC_SetPriority(RADIO_IRQn, 0); +#endif +#if MYNEWT + NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_isr); +#else + ble_npl_hw_set_isr(RADIO_IRQn, ble_phy_isr); +#endif + NVIC_EnableIRQ(RADIO_IRQn); + + /* Register phy statistics */ + if (!g_ble_phy_data.phy_stats_initialized) { + rc = stats_init_and_reg(STATS_HDR(ble_phy_stats), + STATS_SIZE_INIT_PARMS(ble_phy_stats, + STATS_SIZE_32), + STATS_NAME_INIT_PARMS(ble_phy_stats), + "ble_phy"); + assert(rc == 0); + + g_ble_phy_data.phy_stats_initialized = 1; + } + + ble_phy_dbg_time_setup(); + + return 0; +} + +/** + * Puts the phy into receive mode. + * + * @return int 0: success; BLE Phy error code otherwise + */ +int +ble_phy_rx(void) +{ + /* + * Check radio state. + * + * In case radio is now disabling we'll wait for it to finish, but if for + * any reason it's just in idle state we proceed with RX as usual since + * nRF52 radio can ramp-up from idle state as well. + * + * Note that TX and RX states values are the same except for 3rd bit so we + * can make a shortcut here when checking for idle state. + */ + nrf_wait_disabled(); + if ((NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) && + ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { + ble_phy_disable(); + STATS_INC(ble_phy_stats, radio_state_errs); + return BLE_PHY_ERR_RADIO_STATE; + } + + /* Make sure all interrupts are disabled */ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + + /* Clear events prior to enabling receive */ + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + + /* Setup for rx */ + ble_phy_rx_xcvr_setup(); + + /* PPI to start radio automatically shall be set here */ + assert(NRF_PPI->CHEN & PPI_CHEN_CH21_Msk); + + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/** + * Called to enable encryption at the PHY. Note that this state will persist + * in the PHY; in other words, if you call this function you have to call + * disable so that future PHY transmits/receives will not be encrypted. + * + * @param pkt_counter + * @param iv + * @param key + * @param is_master + */ +void +ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master) +{ + memcpy(g_nrf_ccm_data.key, key, 16); + g_nrf_ccm_data.pkt_counter = pkt_counter; + memcpy(g_nrf_ccm_data.iv, iv, 8); + g_nrf_ccm_data.dir_bit = is_master; + g_ble_phy_data.phy_encrypted = 1; + /* Enable the module (AAR cannot be on while CCM on) */ + NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled; + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ + g_nrf_ccm_data.pkt_counter = pkt_counter; + g_nrf_ccm_data.dir_bit = dir; +} + +void +ble_phy_encrypt_disable(void) +{ + NRF_PPI->CHENCLR = PPI_CHEN_CH25_Msk; + NRF_CCM->TASKS_STOP = 1; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled; + + g_ble_phy_data.phy_encrypted = 0; +} +#endif + +void +ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) +{ + /* Set transmit end callback and arg */ + g_ble_phy_data.txend_cb = txend_cb; + g_ble_phy_data.txend_arg = arg; +} + +/** + * Called to set the start time of a transmission. + * + * This function is called to set the start time when we are not going from + * rx to tx automatically. + * + * NOTE: care must be taken when calling this function. The channel should + * already be set. + * + * @param cputime This is the tick at which the 1st bit of the preamble + * should be transmitted + * @param rem_usecs This is used only when the underlying timing uses a 32.768 + * kHz crystal. It is the # of usecs from the cputime tick + * at which the first bit of the preamble should be + * transmitted. + * @return int + */ +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + int rc; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_TX, cputime, rem_usecs); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_tx_phy_mode); +#endif + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to RXEN since we are transmitting */ + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + + if (ble_phy_set_start_time(cputime, rem_usecs, true) != 0) { + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + rc = BLE_PHY_ERR_TX_LATE; + } else { + /* Enable PPI to automatically start TXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; + rc = 0; + } + return rc; +} + +/** + * Called to set the start time of a reception + * + * This function acts a bit differently than transmit. If we are late getting + * here we will still attempt to receive. + * + * NOTE: care must be taken when calling this function. The channel should + * already be set. + * + * @param cputime + * + * @return int + */ +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + bool late = false; + int rc = 0; + + ble_phy_trace_u32x2(BLE_PHY_TRACE_ID_START_RX, cputime, rem_usecs); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_rx_phy_mode); +#endif + + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to TXEN since we are transmitting */ + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; + + if (ble_phy_set_start_time(cputime, rem_usecs, false) != 0) { + STATS_INC(ble_phy_stats, rx_late); + + /* We're late so let's just try to start RX as soon as possible */ + ble_phy_set_start_now(); + + late = true; + } + + /* Enable PPI to automatically start RXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; + + /* Start rx */ + rc = ble_phy_rx(); + + /* + * If we enabled receiver but were late, let's return proper error code so + * caller can handle this. + */ + if (!rc && late) { + rc = BLE_PHY_ERR_RX_LATE; + } + + return rc; +} + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + int rc; + uint8_t *dptr; + uint8_t *pktptr; + uint8_t payload_len; + uint8_t hdr_byte; + uint32_t state; + uint32_t shortcuts; + + if (g_ble_phy_data.phy_transition_late) { + ble_phy_disable(); + STATS_INC(ble_phy_stats, tx_late); + return BLE_PHY_ERR_TX_LATE; + } + + /* + * This check is to make sure that the radio is not in a state where + * it is moving to disabled state. If so, let it get there. + */ + nrf_wait_disabled(); + + /* + * XXX: Although we may not have to do this here, I clear all the PPI + * that should not be used when transmitting. Some of them are only enabled + * if encryption and/or privacy is on, but I dont care. Better to be + * paranoid, and if you are going to clear one, might as well clear them + * all. + */ + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH23_Msk | + PPI_CHEN_CH25_Msk; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; + pktptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM->INPTR = (uint32_t)dptr; + NRF_CCM->OUTPTR = (uint32_t)pktptr; + NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate(); + NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; + } else { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; +#endif + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + pktptr = dptr; + } +#else + dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; + pktptr = dptr; +#endif + + /* Set PDU payload */ + payload_len = pducb(&dptr[3], pducb_arg, &hdr_byte); + + /* RAM representation has S0, LENGTH and S1 fields. (3 bytes) */ + dptr[0] = hdr_byte; + dptr[1] = payload_len; + dptr[2] = 0; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* Start key-stream generation and encryption (via short) */ + if (g_ble_phy_data.phy_encrypted) { + NRF_CCM->TASKS_KSGEN = 1; + } +#endif + + NRF_RADIO->PACKETPTR = (uint32_t)pktptr; + + /* Clear the ready, end and disabled events */ + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + + /* Enable shortcuts for transmit start/end. */ + shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; + NRF_RADIO->SHORTS = shortcuts; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + + /* Set the PHY transition */ + g_ble_phy_data.phy_transition = end_trans; + + /* Set transmitted payload length */ + g_ble_phy_data.phy_tx_pyld_len = payload_len; + + /* If we already started transmitting, abort it! */ + state = NRF_RADIO->STATE; + if (state != RADIO_STATE_STATE_Tx) { + /* Set phy state to transmitting and count packet statistics */ + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + STATS_INC(ble_phy_stats, tx_good); + STATS_INCN(ble_phy_stats, tx_bytes, payload_len + BLE_LL_PDU_HDR_LEN); + rc = BLE_ERR_SUCCESS; + } else { + ble_phy_disable(); + STATS_INC(ble_phy_stats, tx_late); + rc = BLE_PHY_ERR_RADIO_STATE; + } + + return rc; +} + +/** + * ble phy txpwr set + * + * Set the transmit output power (in dBm). + * + * NOTE: If the output power specified is within the BLE limits but outside + * the chip limits, we "rail" the power level so we dont exceed the min/max + * chip values. + * + * @param dbm Power output in dBm. + * + * @return int 0: success; anything else is an error + */ +int +ble_phy_txpwr_set(int dbm) +{ + /* "Rail" power level if outside supported range */ + dbm = ble_phy_txpower_round(dbm); + + NRF_RADIO->TXPOWER = dbm; + g_ble_phy_data.phy_txpwr_dbm = dbm; + + return 0; +} + +/** + * ble phy txpwr round + * + * Get the rounded transmit output power (in dBm). + * + * @param dbm Power output in dBm. + * + * @return int Rounded power in dBm + */ +int ble_phy_txpower_round(int dbm) +{ + /* TODO this should be per nRF52XXX */ + + /* "Rail" power level if outside supported range */ + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos4dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Pos4dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Pos3dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_0dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_0dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm; + } + + if (dbm >= (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) { + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm; + } + + return (int8_t)RADIO_TXPOWER_TXPOWER_Neg40dBm; +} + +/** + * ble phy set access addr + * + * Set access address. + * + * @param access_addr Access address + * + * @return int 0: success; PHY error code otherwise + */ +static int +ble_phy_set_access_addr(uint32_t access_addr) +{ + NRF_RADIO->BASE0 = (access_addr << 8); + NRF_RADIO->PREFIX0 = (NRF_RADIO->PREFIX0 & 0xFFFFFF00) | (access_addr >> 24); + + g_ble_phy_data.phy_access_address = access_addr; + + ble_phy_apply_errata_102_106_107(); + + return 0; +} + +/** + * ble phy txpwr get + * + * Get the transmit power. + * + * @return int The current PHY transmit power, in dBm + */ +int +ble_phy_txpwr_get(void) +{ + return g_ble_phy_data.phy_txpwr_dbm; +} + +void +ble_phy_set_rx_pwr_compensation(int8_t compensation) +{ + g_ble_phy_data.rx_pwr_compensation = compensation; +} + +/** + * ble phy setchan + * + * Sets the logical frequency of the transceiver. The input parameter is the + * BLE channel index (0 to 39, inclusive). The NRF frequency register works like + * this: logical frequency = 2400 + FREQ (MHz). + * + * Thus, to get a logical frequency of 2402 MHz, you would program the + * FREQUENCY register to 2. + * + * @param chan This is the Data Channel Index or Advertising Channel index + * + * @return int 0: success; PHY error code otherwise + */ +int +ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) +{ + assert(chan < BLE_PHY_NUM_CHANS); + + /* Check for valid channel range */ + if (chan >= BLE_PHY_NUM_CHANS) { + return BLE_PHY_ERR_INV_PARAM; + } + + /* Set current access address */ + ble_phy_set_access_addr(access_addr); + + /* Configure crcinit */ + NRF_RADIO->CRCINIT = crcinit; + + /* Set the frequency and the data whitening initial value */ + g_ble_phy_data.phy_chan = chan; + NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan]; + NRF_RADIO->DATAWHITEIV = chan; + + return 0; +} + +/** + * Stop the timer used to count microseconds when using RTC for cputime + */ +static void +ble_phy_stop_usec_timer(void) +{ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_RTC0->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk; +} + +/** + * ble phy disable irq and ppi + * + * This routine is to be called when reception was stopped due to either a + * wait for response timeout or a packet being received and the phy is to be + * restarted in receive mode. Generally, the disable routine is called to stop + * the phy. + */ +static void +ble_phy_disable_irq_and_ppi(void) +{ + NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; + NRF_RADIO->SHORTS = 0; + NRF_RADIO->TASKS_DISABLE = 1; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH20_Msk | + PPI_CHEN_CH21_Msk | PPI_CHEN_CH23_Msk | + PPI_CHEN_CH25_Msk | PPI_CHEN_CH31_Msk; + NVIC_ClearPendingIRQ(RADIO_IRQn); + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +void +ble_phy_restart_rx(void) +{ + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); + + ble_phy_set_start_now(); + /* Enable PPI to automatically start RXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; + + ble_phy_rx(); +} + +/** + * ble phy disable + * + * Disables the PHY. This should be called when an event is over. It stops + * the usec timer (if used), disables interrupts, disables the RADIO, disables + * PPI and sets state to idle. + */ +void +ble_phy_disable(void) +{ + ble_phy_trace_void(BLE_PHY_TRACE_ID_DISABLE); + + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); +} + +/* Gets the current access address */ +uint32_t ble_phy_access_addr_get(void) +{ + return g_ble_phy_data.phy_access_address; +} + +/** + * Return the phy state + * + * @return int The current PHY state. + */ +int +ble_phy_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +/** + * Called to see if a reception has started + * + * @return int + */ +int +ble_phy_rx_started(void) +{ + return g_ble_phy_data.phy_rx_started; +} + +/** + * Return the transceiver state + * + * @return int transceiver state. + */ +uint8_t +ble_phy_xcvr_state_get(void) +{ + uint32_t state; + state = NRF_RADIO->STATE; + return (uint8_t)state; +} + +/** + * Called to return the maximum data pdu payload length supported by the + * phy. For this chip, if encryption is enabled, the maximum payload is 27 + * bytes. + * + * @return uint8_t Maximum data channel PDU payload size supported + */ +uint8_t +ble_phy_max_data_pdu_pyld(void) +{ + return BLE_LL_DATA_PDU_MAX_PYLD; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +void +ble_phy_resolv_list_enable(void) +{ + NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks; + g_ble_phy_data.phy_privacy = 1; +} + +void +ble_phy_resolv_list_disable(void) +{ + g_ble_phy_data.phy_privacy = 0; +} +#endif + +#if MYNEWT_VAL(BLE_LL_DTM) +void ble_phy_enable_dtm(void) +{ + /* When DTM is enabled we need to disable whitening as per + * Bluetooth v5.0 Vol 6. Part F. 4.1.1 + */ + NRF_RADIO->PCNF1 &= ~RADIO_PCNF1_WHITEEN_Msk; +} + +void ble_phy_disable_dtm(void) +{ + /* Enable whitening */ + NRF_RADIO->PCNF1 |= RADIO_PCNF1_WHITEEN_Msk; +} +#endif + +void +ble_phy_rfclk_enable(void) +{ +#if MYNEWT + nrf52_clock_hfxo_request(); +#else + NRF_CLOCK->TASKS_HFCLKSTART = 1; +#endif +} + +void +ble_phy_rfclk_disable(void) +{ +#if MYNEWT + nrf52_clock_hfxo_release(); +#else + NRF_CLOCK->TASKS_HFCLKSTOP = 1; +#endif +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy_trace.c b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy_trace.c new file mode 100644 index 0000000..84dbf4d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/drivers/nrf52/src/ble_phy_trace.c @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(ARDUINO_ARCH_NRF5) && defined(NRF52_SERIES) + +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os_trace_api.h" + +#if MYNEWT_VAL(BLE_PHY_SYSVIEW) + +static os_trace_module_t g_ble_phy_trace_mod; +uint32_t ble_phy_trace_off; + +static void +ble_phy_trace_module_send_desc(void) +{ + os_trace_module_desc(&g_ble_phy_trace_mod, "0 phy_set_tx cputime=%u usecs=%u"); + os_trace_module_desc(&g_ble_phy_trace_mod, "1 phy_set_rx cputime=%u usecs=%u"); + os_trace_module_desc(&g_ble_phy_trace_mod, "2 phy_disable"); +} + +void +ble_phy_trace_init(void) +{ + ble_phy_trace_off = + os_trace_module_register(&g_ble_phy_trace_mod, "ble_phy", 3, + ble_phy_trace_module_send_desc); +} +#endif +#endif \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_att.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_att.h new file mode 100644 index 0000000..ceca351 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_att.h @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_ATT_ +#define H_BLE_ATT_ + +/** + * @brief Bluetooth Attribute Protocol (ATT) + * @defgroup bt_att Bluetooth Attribute Protocol (ATT) + * @ingroup bt_host + * @{ + */ + +#include "nimble/porting/nimble/include/os/queue.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +#define BLE_ATT_UUID_PRIMARY_SERVICE 0x2800 +#define BLE_ATT_UUID_SECONDARY_SERVICE 0x2801 +#define BLE_ATT_UUID_INCLUDE 0x2802 +#define BLE_ATT_UUID_CHARACTERISTIC 0x2803 + +#define BLE_ATT_ERR_INVALID_HANDLE 0x01 +#define BLE_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BLE_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BLE_ATT_ERR_INVALID_PDU 0x04 +#define BLE_ATT_ERR_INSUFFICIENT_AUTHEN 0x05 +#define BLE_ATT_ERR_REQ_NOT_SUPPORTED 0x06 +#define BLE_ATT_ERR_INVALID_OFFSET 0x07 +#define BLE_ATT_ERR_INSUFFICIENT_AUTHOR 0x08 +#define BLE_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BLE_ATT_ERR_ATTR_NOT_FOUND 0x0a +#define BLE_ATT_ERR_ATTR_NOT_LONG 0x0b +#define BLE_ATT_ERR_INSUFFICIENT_KEY_SZ 0x0c +#define BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN 0x0d +#define BLE_ATT_ERR_UNLIKELY 0x0e +#define BLE_ATT_ERR_INSUFFICIENT_ENC 0x0f +#define BLE_ATT_ERR_UNSUPPORTED_GROUP 0x10 +#define BLE_ATT_ERR_INSUFFICIENT_RES 0x11 + +#define BLE_ATT_OP_ERROR_RSP 0x01 +#define BLE_ATT_OP_MTU_REQ 0x02 +#define BLE_ATT_OP_MTU_RSP 0x03 +#define BLE_ATT_OP_FIND_INFO_REQ 0x04 +#define BLE_ATT_OP_FIND_INFO_RSP 0x05 +#define BLE_ATT_OP_FIND_TYPE_VALUE_REQ 0x06 +#define BLE_ATT_OP_FIND_TYPE_VALUE_RSP 0x07 +#define BLE_ATT_OP_READ_TYPE_REQ 0x08 +#define BLE_ATT_OP_READ_TYPE_RSP 0x09 +#define BLE_ATT_OP_READ_REQ 0x0a +#define BLE_ATT_OP_READ_RSP 0x0b +#define BLE_ATT_OP_READ_BLOB_REQ 0x0c +#define BLE_ATT_OP_READ_BLOB_RSP 0x0d +#define BLE_ATT_OP_READ_MULT_REQ 0x0e +#define BLE_ATT_OP_READ_MULT_RSP 0x0f +#define BLE_ATT_OP_READ_GROUP_TYPE_REQ 0x10 +#define BLE_ATT_OP_READ_GROUP_TYPE_RSP 0x11 +#define BLE_ATT_OP_WRITE_REQ 0x12 +#define BLE_ATT_OP_WRITE_RSP 0x13 +#define BLE_ATT_OP_PREP_WRITE_REQ 0x16 +#define BLE_ATT_OP_PREP_WRITE_RSP 0x17 +#define BLE_ATT_OP_EXEC_WRITE_REQ 0x18 +#define BLE_ATT_OP_EXEC_WRITE_RSP 0x19 +#define BLE_ATT_OP_NOTIFY_REQ 0x1b +#define BLE_ATT_OP_INDICATE_REQ 0x1d +#define BLE_ATT_OP_INDICATE_RSP 0x1e +#define BLE_ATT_OP_WRITE_CMD 0x52 + +#define BLE_ATT_ATTR_MAX_LEN 512 + +#define BLE_ATT_F_READ 0x01 +#define BLE_ATT_F_WRITE 0x02 +#define BLE_ATT_F_READ_ENC 0x04 +#define BLE_ATT_F_READ_AUTHEN 0x08 +#define BLE_ATT_F_READ_AUTHOR 0x10 +#define BLE_ATT_F_WRITE_ENC 0x20 +#define BLE_ATT_F_WRITE_AUTHEN 0x40 +#define BLE_ATT_F_WRITE_AUTHOR 0x80 + +#define HA_FLAG_PERM_RW (BLE_ATT_F_READ | BLE_ATT_F_WRITE) + +#define BLE_ATT_ACCESS_OP_READ 1 +#define BLE_ATT_ACCESS_OP_WRITE 2 + +/** Default ATT MTU. Also the minimum. */ +#define BLE_ATT_MTU_DFLT 23 + +/** + * An ATT MTU of 527 allows the largest ATT command (signed write) to contain a + * 512-byte attribute value. + */ +#define BLE_ATT_MTU_MAX 527 + +/** + * Reads a locally registered attribute. If the specified attribute handle + * corresponds to a GATT characteristic value or descriptor, the read is + * performed by calling the registered GATT access callback. + * + * @param attr_handle The 16-bit handle of the attribute to read. + * @param out_om On success, this is made to point to a + * newly-allocated mbuf containing the + * attribute data read. + * + * @return 0 on success; + * NimBLE host ATT return code if the attribute + * access callback reports failure; + * NimBLE host core return code on unexpected + * error. + */ +int ble_att_svr_read_local(uint16_t attr_handle, struct os_mbuf **out_om); + +/** + * Writes a locally registered attribute. This function consumes the supplied + * mbuf regardless of the outcome. If the specified attribute handle + * corresponds to a GATT characteristic value or descriptor, the write is + * performed by calling the registered GATT access callback. + * + * @param attr_handle The 16-bit handle of the attribute to write. + * @param om The value to write to the attribute. + * + * @return 0 on success; + * NimBLE host ATT return code if the attribute + * access callback reports failure; + * NimBLE host core return code on unexpected + * error. + */ +int ble_att_svr_write_local(uint16_t attr_handle, struct os_mbuf *om); + +/** + * Retrieves the ATT MTU of the specified connection. If an MTU exchange for + * this connection has occurred, the MTU is the lower of the two peers' + * preferred values. Otherwise, the MTU is the default value of 23. + * + * @param conn_handle The handle of the connection to query. + * + * @return The specified connection's ATT MTU, or 0 if + * there is no such connection. + */ +uint16_t ble_att_mtu(uint16_t conn_handle); + +/** + * Retrieves the preferred ATT MTU. This is the value indicated by the device + * during an ATT MTU exchange. + * + * @return The preferred ATT MTU. + */ +uint16_t ble_att_preferred_mtu(void); + +/** + * Sets the preferred ATT MTU; the device will indicate this value in all + * subsequent ATT MTU exchanges. The ATT MTU of a connection is equal to the + * lower of the two peers' preferred MTU values. The ATT MTU is what dictates + * the maximum size of any message sent during a GATT procedure. + * + * The specified MTU must be within the following range: [23, BLE_ATT_MTU_MAX]. + * 23 is a minimum imposed by the Bluetooth specification; BLE_ATT_MTU_MAX is a + * NimBLE compile-time setting. + * + * @param mtu The preferred ATT MTU. + * + * @return 0 on success; + * BLE_HS_EINVAL if the specified value is not + * within the allowed range. + */ +int ble_att_set_preferred_mtu(uint16_t mtu); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_eddystone.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_eddystone.h new file mode 100644 index 0000000..76b7e2b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_eddystone.h @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_EDDYSTONE_ +#define H_BLE_EDDYSTONE_ + +/** + * @brief Eddystone - BLE beacon from Google + * @defgroup bt_eddystone Eddystone - BLE beacon from Google + * @ingroup bt_host + * @{ + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_adv_fields; + +#define BLE_EDDYSTONE_MAX_UUIDS16 3 +#define BLE_EDDYSTONE_URL_MAX_LEN 17 + +#define BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW 0 +#define BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW 1 +#define BLE_EDDYSTONE_URL_SCHEME_HTTP 2 +#define BLE_EDDYSTONE_URL_SCHEME_HTTPS 3 + +#define BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH 0x00 +#define BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH 0x01 +#define BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH 0x02 +#define BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH 0x03 +#define BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH 0x04 +#define BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH 0x05 +#define BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH 0x06 +#define BLE_EDDYSTONE_URL_SUFFIX_COM 0x07 +#define BLE_EDDYSTONE_URL_SUFFIX_ORG 0x08 +#define BLE_EDDYSTONE_URL_SUFFIX_EDU 0x09 +#define BLE_EDDYSTONE_URL_SUFFIX_NET 0x0a +#define BLE_EDDYSTONE_URL_SUFFIX_INFO 0x0b +#define BLE_EDDYSTONE_URL_SUFFIX_BIZ 0x0c +#define BLE_EDDYSTONE_URL_SUFFIX_GOV 0x0d +#define BLE_EDDYSTONE_URL_SUFFIX_NONE 0xff + +/** + * Configures the device to advertise Eddystone UID beacons. + * + * @param adv_fields The base advertisement fields to transform into + * an eddystone beacon. All configured fields + * are preserved; you probably want to clear + * this struct before calling this function. + * @param uid The 16-byte UID to advertise. + * @param measured_power The Measured Power (RSSI value at 0 Meter). + * + * @return 0 on success; + * BLE_HS_EBUSY if advertising is in progress; + * BLE_HS_EMSGSIZE if the specified data is too + * large to fit in an advertisement; + * Other nonzero on failure. + */ +int ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields *adv_fields, + void *uid, int8_t measured_power); + +/** + * Configures the device to advertise Eddystone URL beacons. + * + * @param adv_fields The base advertisement fields to transform into + * an eddystone beacon. All configured fields + * are preserved; you probably want to clear + * this struct before calling this function. + * @param url_scheme The prefix of the URL; one of the + * BLE_EDDYSTONE_URL_SCHEME values. + * @param url_body The middle of the URL. Don't include the + * suffix if there is a suitable suffix code. + * @param url_body_len The string length of the url_body argument. + * @param url_suffix The suffix of the URL; one of the + * BLE_EDDYSTONE_URL_SUFFIX values; use + * BLE_EDDYSTONE_URL_SUFFIX_NONE if the suffix + * is embedded in the body argument. + * @param measured_power The Measured Power (RSSI value at 0 Meter). + * + * @return 0 on success; + * BLE_HS_EBUSY if advertising is in progress; + * BLE_HS_EMSGSIZE if the specified data is too + * large to fit in an advertisement; + * Other nonzero on failure. + */ +int ble_eddystone_set_adv_data_url(struct ble_hs_adv_fields *adv_fields, + uint8_t url_scheme, char *url_body, + uint8_t url_body_len, uint8_t suffix, + int8_t measured_power); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gap.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gap.h new file mode 100644 index 0000000..edaf3bc --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gap.h @@ -0,0 +1,2094 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GAP_ +#define H_BLE_GAP_ + +/** + * @brief Bluetooth Host Generic Access Profile (GAP) + * @defgroup bt_host_gap Bluetooth Host Generic Access Profile (GAP) + * @ingroup bt_host + * @{ + */ + +#include +#include "ble_hs.h" +#include "ble_hs_adv.h" +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hci_le_conn_complete; +struct hci_conn_update; + +/** 30 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL1_MIN (30 * 1000 / BLE_HCI_ADV_ITVL) + +/** 60 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL1_MAX (60 * 1000 / BLE_HCI_ADV_ITVL) + +/** 100 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL2_MIN (100 * 1000 / BLE_HCI_ADV_ITVL) + +/** 150 ms. */ +#define BLE_GAP_ADV_FAST_INTERVAL2_MAX (150 * 1000 / BLE_HCI_ADV_ITVL) + +/** 30 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_INTERVAL_MIN (30 * 1000 / BLE_HCI_ADV_ITVL) + +/** 60 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_INTERVAL_MAX (60 * 1000 / BLE_HCI_ADV_ITVL) + +/** 11.25 ms; limited discovery interval. */ +#define BLE_GAP_LIM_DISC_SCAN_INT (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 11.25 ms; limited discovery window (not from the spec). */ +#define BLE_GAP_LIM_DISC_SCAN_WINDOW (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 30 ms; active scanning. */ +#define BLE_GAP_SCAN_FAST_WINDOW (30 * 1000 / BLE_HCI_SCAN_ITVL) + +/* 30.72 seconds; active scanning. */ +#define BLE_GAP_SCAN_FAST_PERIOD (30.72 * 1000) + +/** 1.28 seconds; background scanning. */ +#define BLE_GAP_SCAN_SLOW_INTERVAL1 (1280 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 11.25 ms; background scanning. */ +#define BLE_GAP_SCAN_SLOW_WINDOW1 (11.25 * 1000 / BLE_HCI_SCAN_ITVL) + +/** 10.24 seconds. */ +#define BLE_GAP_DISC_DUR_DFLT (10.24 * 1000) + +/** 30 seconds (not from the spec). */ +#define BLE_GAP_CONN_DUR_DFLT (30 * 1000) + +/** 1 second. */ +#define BLE_GAP_CONN_PAUSE_CENTRAL (1 * 1000) + +/** 5 seconds. */ +#define BLE_GAP_CONN_PAUSE_PERIPHERAL (5 * 1000) + +/* 30 ms. */ +#define BLE_GAP_INITIAL_CONN_ITVL_MIN (30 * 1000 / BLE_HCI_CONN_ITVL) + +/* 50 ms. */ +#define BLE_GAP_INITIAL_CONN_ITVL_MAX (50 * 1000 / BLE_HCI_CONN_ITVL) + +/** Default channels mask: all three channels are used. */ +#define BLE_GAP_ADV_DFLT_CHANNEL_MAP 0x07 + +#define BLE_GAP_INITIAL_CONN_LATENCY 0 +#define BLE_GAP_INITIAL_SUPERVISION_TIMEOUT 0x0100 +#define BLE_GAP_INITIAL_CONN_MIN_CE_LEN 0x0000 +#define BLE_GAP_INITIAL_CONN_MAX_CE_LEN 0x0000 + +#define BLE_GAP_ROLE_MASTER 0 +#define BLE_GAP_ROLE_SLAVE 1 + +#define BLE_GAP_EVENT_CONNECT 0 +#define BLE_GAP_EVENT_DISCONNECT 1 +/* Reserved 2 */ +#define BLE_GAP_EVENT_CONN_UPDATE 3 +#define BLE_GAP_EVENT_CONN_UPDATE_REQ 4 +#define BLE_GAP_EVENT_L2CAP_UPDATE_REQ 5 +#define BLE_GAP_EVENT_TERM_FAILURE 6 +#define BLE_GAP_EVENT_DISC 7 +#define BLE_GAP_EVENT_DISC_COMPLETE 8 +#define BLE_GAP_EVENT_ADV_COMPLETE 9 +#define BLE_GAP_EVENT_ENC_CHANGE 10 +#define BLE_GAP_EVENT_PASSKEY_ACTION 11 +#define BLE_GAP_EVENT_NOTIFY_RX 12 +#define BLE_GAP_EVENT_NOTIFY_TX 13 +#define BLE_GAP_EVENT_SUBSCRIBE 14 +#define BLE_GAP_EVENT_MTU 15 +#define BLE_GAP_EVENT_IDENTITY_RESOLVED 16 +#define BLE_GAP_EVENT_REPEAT_PAIRING 17 +#define BLE_GAP_EVENT_PHY_UPDATE_COMPLETE 18 +#define BLE_GAP_EVENT_EXT_DISC 19 +#define BLE_GAP_EVENT_PERIODIC_SYNC 20 +#define BLE_GAP_EVENT_PERIODIC_REPORT 21 +#define BLE_GAP_EVENT_PERIODIC_SYNC_LOST 22 +#define BLE_GAP_EVENT_SCAN_REQ_RCVD 23 +#define BLE_GAP_EVENT_PERIODIC_TRANSFER 24 + +/*** Reason codes for the subscribe GAP event. */ + +/** Peer's CCCD subscription state changed due to a descriptor write. */ +#define BLE_GAP_SUBSCRIBE_REASON_WRITE 1 + +/** Peer's CCCD subscription state cleared due to connection termination. */ +#define BLE_GAP_SUBSCRIBE_REASON_TERM 2 + +/** + * Peer's CCCD subscription state changed due to restore from persistence + * (bonding restored). + */ +#define BLE_GAP_SUBSCRIBE_REASON_RESTORE 3 + +#define BLE_GAP_REPEAT_PAIRING_RETRY 1 +#define BLE_GAP_REPEAT_PAIRING_IGNORE 2 + +/** Connection security state */ +struct ble_gap_sec_state { + /** If connection is encrypted */ + unsigned encrypted:1; + + /** If connection is authenticated */ + unsigned authenticated:1; + + /** If connection is bonded (security information is stored) */ + unsigned bonded:1; + + /** Size of a key used for encryption */ + unsigned key_size:5; +}; + +/** Advertising parameters */ +struct ble_gap_adv_params { + /** Advertising mode. Can be one of following constants: + * - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2). + * - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3). + * - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4). + */ + uint8_t conn_mode; + /** Discoverable mode. Can be one of following constants: + * - BLE_GAP_DISC_MODE_NON (non-discoverable; 3.C.9.2.2). + * - BLE_GAP_DISC_MODE_LTD (limited-discoverable; 3.C.9.2.3). + * - BLE_GAP_DISC_MODE_GEN (general-discoverable; 3.C.9.2.4). + */ + uint8_t disc_mode; + + /** Minimum advertising interval, if 0 stack use sane defaults */ + uint16_t itvl_min; + /** Maximum advertising interval, if 0 stack use sane defaults */ + uint16_t itvl_max; + /** Advertising channel map , if 0 stack use sane defaults */ + uint8_t channel_map; + + /** Advertising Filter policy */ + uint8_t filter_policy; + + /** If do High Duty cycle for Directed Advertising */ + uint8_t high_duty_cycle:1; +}; + +/** @brief Connection descriptor */ +struct ble_gap_conn_desc { + /** Connection security state */ + struct ble_gap_sec_state sec_state; + + /** Local identity address */ + ble_addr_t our_id_addr; + + /** Peer identity address */ + ble_addr_t peer_id_addr; + + /** Local over-the-air address */ + ble_addr_t our_ota_addr; + + /** Peer over-the-air address */ + ble_addr_t peer_ota_addr; + + /** Connection handle */ + uint16_t conn_handle; + + /** Connection interval */ + uint16_t conn_itvl; + + /** Connection latency */ + uint16_t conn_latency; + + /** Connection supervision timeout */ + uint16_t supervision_timeout; + + /** Connection Role + * Possible values BLE_GAP_ROLE_SLAVE or BLE_GAP_ROLE_MASTER + */ + uint8_t role; + + /** Master clock accuracy */ + uint8_t master_clock_accuracy; +}; + +/** @brief Connection parameters */ +struct ble_gap_conn_params { + /** Scan interval in 0.625ms units */ + uint16_t scan_itvl; + + /** Scan window in 0.625ms units */ + uint16_t scan_window; + + /** Minimum value for connection interval in 1.25ms units */ + uint16_t itvl_min; + + /** Maximum value for connection interval in 1.25ms units */ + uint16_t itvl_max; + + /** Connection latency */ + uint16_t latency; + + /** Supervision timeout in 10ms units */ + uint16_t supervision_timeout; + + /** Minimum length of connection event in 0.625ms units */ + uint16_t min_ce_len; + + /** Maximum length of connection event in 0.625ms units */ + uint16_t max_ce_len; +}; + +/** @brief Extended discovery parameters */ +struct ble_gap_ext_disc_params { + /** Scan interval in 0.625ms units */ + uint16_t itvl; + + /** Scan window in 0.625ms units */ + uint16_t window; + + /** If passive scan should be used */ + uint8_t passive:1; +}; + +/** @brief Discovery parameters */ +struct ble_gap_disc_params { + /** Scan interval in 0.625ms units */ + uint16_t itvl; + + /** Scan window in 0.625ms units */ + uint16_t window; + + /** Scan filter policy */ + uint8_t filter_policy; + + /** If limited discovery procedure should be used */ + uint8_t limited:1; + + /** If passive scan should be used */ + uint8_t passive:1; + + /** If enable duplicates filtering */ + uint8_t filter_duplicates:1; +}; + +/** @brief Connection parameters update parameters */ +struct ble_gap_upd_params { + /** Minimum value for connection interval in 1.25ms units */ + uint16_t itvl_min; + + /** Maximum value for connection interval in 1.25ms units */ + uint16_t itvl_max; + + /** Connection latency */ + uint16_t latency; + + /** Supervision timeout in 10ms units */ + uint16_t supervision_timeout; + + /** Minimum length of connection event in 0.625ms units */ + uint16_t min_ce_len; + + /** Maximum length of connection event in 0.625ms units */ + uint16_t max_ce_len; +}; + +/** @brief Passkey query */ +struct ble_gap_passkey_params { + /** Passkey action, can be one of following constants: + * - BLE_SM_IOACT_NONE + * - BLE_SM_IOACT_OOB + * - BLE_SM_IOACT_INPUT + * - BLE_SM_IOACT_DISP + * - BLE_SM_IOACT_NUMCMP + */ + uint8_t action; + + /** Passkey to compare, valid for BLE_SM_IOACT_NUMCMP action */ + uint32_t numcmp; +}; + +#if MYNEWT_VAL(BLE_EXT_ADV) + +#define BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE 0x00 +#define BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE 0x01 +#define BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED 0x02 + +/** @brief Extended advertising report */ +struct ble_gap_ext_disc_desc { + /** Report properties bitmask + * - BLE_HCI_ADV_CONN_MASK + * - BLE_HCI_ADV_SCAN_MASK + * - BLE_HCI_ADV_DIRECT_MASK + * - BLE_HCI_ADV_SCAN_RSP_MASK + * - BLE_HCI_ADV_LEGACY_MASK + * */ + uint8_t props; + + /** Advertising data status, can be one of following constants: + * - BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE + * - BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE + * - BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED + */ + uint8_t data_status; + + /** Legacy advertising PDU type. Valid if BLE_HCI_ADV_LEGACY_MASK props is + * set. Can be one of following constants: + * - BLE_HCI_ADV_RPT_EVTYPE_ADV_IND + * - BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP + */ + uint8_t legacy_event_type; + + /** Advertiser address */ + ble_addr_t addr; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertiser transmit power in dBm (127 if unavailable) */ + int8_t tx_power; + + /** Advertising Set ID */ + uint8_t sid; + + /** Primary advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t prim_phy; + + /** Secondary advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t sec_phy; + + /** Periodic advertising interval. 0 if no periodic advertising. */ + uint16_t periodic_adv_itvl; + + /** Advertising Data length */ + uint8_t length_data; + + /** Advertising data */ + const uint8_t *data; + + /** Directed advertising address. Valid if BLE_HCI_ADV_DIRECT_MASK props is + * set (BLE_ADDR_ANY otherwise). + */ + ble_addr_t direct_addr; +}; +#endif + +/** @brief Advertising report */ +struct ble_gap_disc_desc { + /** Advertising PDU type. Can be one of following constants: + * - BLE_HCI_ADV_RPT_EVTYPE_ADV_IND + * - BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND + * - BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP + */ + uint8_t event_type; + + /** Advertising Data length */ + uint8_t length_data; + + /** Advertiser address */ + ble_addr_t addr; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertising data */ + const uint8_t *data; + + /** Directed advertising address. Valid for BLE_HCI_ADV_RPT_EVTYPE_DIR_IND + * event type (BLE_ADDR_ANY otherwise). + */ + ble_addr_t direct_addr; +}; + +struct ble_gap_repeat_pairing { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** Properties of the existing bond. */ + uint8_t cur_key_size; + uint8_t cur_authenticated:1; + uint8_t cur_sc:1; + + /** + * Properties of the imminent secure link if the pairing procedure is + * allowed to continue. + */ + uint8_t new_key_size; + uint8_t new_authenticated:1; + uint8_t new_sc:1; + uint8_t new_bonding:1; +}; + +/** + * Represents a GAP-related event. When such an event occurs, the host + * notifies the application by passing an instance of this structure to an + * application-specified callback. + */ +struct ble_gap_event { + /** + * Indicates the type of GAP event that occurred. This is one of the + * BLE_GAP_EVENT codes. + */ + uint8_t type; + + /** + * A discriminated union containing additional details concerning the GAP + * event. The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents a connection attempt. Valid for the following event + * types: + * o BLE_GAP_EVENT_CONNECT + */ + struct { + /** + * The status of the connection attempt; + * o 0: the connection was successfully established. + * o BLE host error code: the connection attempt failed for + * the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } connect; + + /** + * Represents a terminated connection. Valid for the following event + * types: + * o BLE_GAP_EVENT_DISCONNECT + */ + struct { + /** + * A BLE host return code indicating the reason for the + * disconnect. + */ + int reason; + + /** Information about the connection prior to termination. */ + struct ble_gap_conn_desc conn; + } disconnect; + + /** + * Represents an advertising report received during a discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_DISC + */ + struct ble_gap_disc_desc disc; + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** + * Represents an extended advertising report received during a discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_EXT_DISC + */ + struct ble_gap_ext_disc_desc ext_disc; +#endif + + /** + * Represents a completed discovery procedure. Valid for the following + * event types: + * o BLE_GAP_EVENT_DISC_COMPLETE + */ + struct { + /** + * The reason the discovery procedure stopped. Typical reason + * codes are: + * o 0: Duration expired. + * o BLE_HS_EPREEMPTED: Host aborted procedure to configure a + * peer's identity. + */ + int reason; + } disc_complete; + + /** + * Represents a completed advertise procedure. Valid for the following + * event types: + * o BLE_GAP_EVENT_ADV_COMPLETE + */ + struct { + /** + * The reason the advertise procedure stopped. Typical reason + * codes are: + * o 0: Terminated due to connection. + * o BLE_HS_ETIMEOUT: Duration expired. + * o BLE_HS_EPREEMPTED: Host aborted procedure to configure a + * peer's identity. + */ + int reason; + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** Advertising instance */ + uint8_t instance; + /** The handle of the relevant connection - valid if reason=0 */ + uint16_t conn_handle; + /** + * Number of completed extended advertising events + * + * This field is only valid if non-zero max_events was passed to + * ble_gap_ext_adv_start() and advertising completed due to duration + * timeout or max events transmitted. + * */ + uint8_t num_ext_adv_events; +#endif + } adv_complete; + + /** + * Represents an attempt to update a connection's parameters. If the + * attempt was successful, the connection's descriptor reflects the + * updated parameters. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_CONN_UPDATE + */ + struct { + /** + * The result of the connection update attempt; + * o 0: the connection was successfully updated. + * o BLE host error code: the connection update attempt failed + * for the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } conn_update; + + /** + * Represents a peer's request to update the connection parameters. + * This event is generated when a peer performs any of the following + * procedures: + * o L2CAP Connection Parameter Update Procedure + * o Link-Layer Connection Parameters Request Procedure + * + * To reject the request, return a non-zero HCI error code. The value + * returned is the reject reason given to the controller. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_L2CAP_UPDATE_REQ + * o BLE_GAP_EVENT_CONN_UPDATE_REQ + */ + struct { + /** + * Indicates the connection parameters that the peer would like to + * use. + */ + const struct ble_gap_upd_params *peer_params; + + /** + * Indicates the connection parameters that the local device would + * like to use. The application callback should fill this in. By + * default, this struct contains the requested parameters (i.e., + * it is a copy of 'peer_params'). + */ + struct ble_gap_upd_params *self_params; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } conn_update_req; + + /** + * Represents a failed attempt to terminate an established connection. + * Valid for the following event types: + * o BLE_GAP_EVENT_TERM_FAILURE + */ + struct { + /** + * A BLE host return code indicating the reason for the failure. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } term_failure; + + /** + * Represents an attempt to change the encrypted state of a + * connection. If the attempt was successful, the connection + * descriptor reflects the updated encrypted state. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_ENC_CHANGE + */ + struct { + /** + * Indicates the result of the encryption state change attempt; + * o 0: the encrypted state was successfully updated; + * o BLE host error code: the encryption state change attempt + * failed for the specified reason. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } enc_change; + + /** + * Represents a passkey query needed to complete a pairing procedure. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_PASSKEY_ACTION + */ + struct { + /** Contains details about the passkey query. */ + struct ble_gap_passkey_params params; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } passkey; + + /** + * Represents a received ATT notification or indication. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_NOTIFY_RX + */ + struct { + /** + * The contents of the notification or indication. If the + * application wishes to retain this mbuf for later use, it must + * set this pointer to NULL to prevent the stack from freeing it. + */ + struct os_mbuf *om; + + /** The handle of the relevant ATT attribute. */ + uint16_t attr_handle; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** + * Whether the received command is a notification or an + * indication; + * o 0: Notification; + * o 1: Indication. + */ + uint8_t indication:1; + } notify_rx; + + /** + * Represents a transmitted ATT notification or indication, or a + * completed indication transaction. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_NOTIFY_TX + */ + struct { + /** + * The status of the notification or indication transaction; + * o 0: Command successfully sent; + * o BLE_HS_EDONE: Confirmation (indication ack) received; + * o BLE_HS_ETIMEOUT: Confirmation (indication ack) never + * received; + * o Other return code: Error. + */ + int status; + + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** The handle of the relevant characteristic value. */ + uint16_t attr_handle; + + /** + * Whether the transmitted command is a notification or an + * indication; + * o 0: Notification; + * o 1: Indication. + */ + uint8_t indication:1; + } notify_tx; + + /** + * Represents a state change in a peer's subscription status. In this + * comment, the term "update" is used to refer to either a notification + * or an indication. This event is triggered by any of the following + * occurrences: + * o Peer enables or disables updates via a CCCD write. + * o Connection is about to be terminated and the peer is + * subscribed to updates. + * o Peer is now subscribed to updates after its state was restored + * from persistence. This happens when bonding is restored. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_SUBSCRIBE + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** The value handle of the relevant characteristic. */ + uint16_t attr_handle; + + /** One of the BLE_GAP_SUBSCRIBE_REASON codes. */ + uint8_t reason; + + /** Whether the peer was previously subscribed to notifications. */ + uint8_t prev_notify:1; + + /** Whether the peer is currently subscribed to notifications. */ + uint8_t cur_notify:1; + + /** Whether the peer was previously subscribed to indications. */ + uint8_t prev_indicate:1; + + /** Whether the peer is currently subscribed to indications. */ + uint8_t cur_indicate:1; + } subscribe; + + /** + * Represents a change in an L2CAP channel's MTU. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_MTU + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + + /** + * Indicates the channel whose MTU has been updated; either + * BLE_L2CAP_CID_ATT or the ID of a connection-oriented channel. + */ + uint16_t channel_id; + + /* The channel's new MTU. */ + uint16_t value; + } mtu; + + /** + * Represents a change in peer's identity. This is issued after + * successful pairing when Identity Address Information was received. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_IDENTITY_RESOLVED + */ + struct { + /** The handle of the relevant connection. */ + uint16_t conn_handle; + } identity_resolved; + + /** + * Represents a peer's attempt to pair despite a bond already existing. + * The application has two options for handling this event type: + * o Retry: Return BLE_GAP_REPEAT_PAIRING_RETRY after deleting the + * conflicting bond. The stack will verify the bond has + * been deleted and continue the pairing procedure. If + * the bond is still present, this event will be reported + * again. + * o Ignore: Return BLE_GAP_REPEAT_PAIRING_IGNORE. The stack will + * silently ignore the pairing request. + * + * Valid for the following event types: + * o BLE_GAP_EVENT_REPEAT_PAIRING + */ + struct ble_gap_repeat_pairing repeat_pairing; + + /** + * Represents a change of PHY. This is issue after successful + * change on PHY. + */ + struct { + int status; + uint16_t conn_handle; + + /** + * Indicates enabled TX/RX PHY. Possible values: + * o BLE_GAP_LE_PHY_1M + * o BLE_GAP_LE_PHY_2M + * o BLE_GAP_LE_PHY_CODED + */ + uint8_t tx_phy; + uint8_t rx_phy; + } phy_updated; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + /** + * Represents a periodic advertising sync established during discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_SYNC + */ + struct { + /** BLE_ERR_SUCCESS on success or error code on failure. Other + * fields are valid only for success + */ + uint8_t status; + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Advertising Set ID */ + uint8_t sid; + + /** Advertiser address */ + ble_addr_t adv_addr; + + /** Advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t adv_phy; + + /** Periodic advertising interval */ + uint16_t per_adv_ival; + + /** Advertiser clock accuracy */ + uint8_t adv_clk_accuracy; + } periodic_sync; + + /** + * Represents a periodic advertising report received on established + * sync. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_REPORT + */ + struct { + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Advertiser transmit power in dBm (127 if unavailable) */ + int8_t tx_power; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** Advertising data status, can be one of following constants: + * - BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE + * - BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE + * - BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED + */ + uint8_t data_status; + + /** Advertising Data length */ + uint8_t data_length; + + /** Advertising data */ + const uint8_t *data; + } periodic_report; + + /** + * Represents a periodic advertising sync lost of established sync. + * Sync lost reason can be BLE_HS_ETIMEOUT (sync timeout) or + * BLE_HS_EDONE (sync terminated locally). + * Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_SYNC_LOST + */ + struct { + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Reason for sync lost, can be BLE_HS_ETIMEOUT for timeout or + * BLE_HS_EDONE for locally terminated sync + */ + int reason; + } periodic_sync_lost; +#endif + +#if MYNEWT_VAL(BLE_EXT_ADV) + /** + * Represents a scan request for an extended advertising instance where + * scan request notifications were enabled. + * Valid for the following event types: + * o BLE_GAP_EVENT_SCAN_REQ_RCVD + */ + struct { + /** Extended advertising instance */ + uint8_t instance; + /** Address of scanner */ + ble_addr_t scan_addr; + } scan_req_rcvd; +#endif +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + /** + * Represents a periodic advertising sync transfer received. Valid for + * the following event types: + * o BLE_GAP_EVENT_PERIODIC_TRANSFER + */ + struct { + /** BLE_ERR_SUCCESS on success or error code on failure. Sync handle + * is valid only for success. + */ + uint8_t status; + + /** Periodic sync handle */ + uint16_t sync_handle; + + /** Connection handle */ + uint16_t conn_handle; + + /** Service Data */ + uint16_t service_data; + + /** Advertising Set ID */ + uint8_t sid; + + /** Advertiser address */ + ble_addr_t adv_addr; + + /** Advertising PHY, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t adv_phy; + + /** Periodic advertising interval */ + uint16_t per_adv_itvl; + + /** Advertiser clock accuracy */ + uint8_t adv_clk_accuracy; + } periodic_transfer; +#endif + }; +}; + +typedef int ble_gap_event_fn(struct ble_gap_event *event, void *arg); + +#define BLE_GAP_CONN_MODE_NON 0 +#define BLE_GAP_CONN_MODE_DIR 1 +#define BLE_GAP_CONN_MODE_UND 2 + +#define BLE_GAP_DISC_MODE_NON 0 +#define BLE_GAP_DISC_MODE_LTD 1 +#define BLE_GAP_DISC_MODE_GEN 2 + +/** + * Searches for a connection with the specified handle. If a matching + * connection is found, the supplied connection descriptor is filled + * correspondingly. + * + * @param handle The connection handle to search for. + * @param out_desc On success, this is populated with information relating to + * the matching connection. Pass NULL if you don't need this + * information. + * + * @return 0 on success, BLE_HS_ENOTCONN if no matching connection was + * found. + */ +int ble_gap_conn_find(uint16_t handle, struct ble_gap_conn_desc *out_desc); + +/** + * Searches for a connection with a peer with the specified address. + * If a matching connection is found, the supplied connection descriptor + * is filled correspondingly. + * + * @param addr The ble address of a connected peer device to search for. + * @param out_desc On success, this is populated with information relating to + * the matching connection. Pass NULL if you don't need this + * information. + * + * @return 0 on success, BLE_HS_ENOTCONN if no matching connection was + * found. + */ +int ble_gap_conn_find_by_addr(const ble_addr_t *addr, + struct ble_gap_conn_desc *out_desc); + +/** + * Configures a connection to use the specified GAP event callback. A + * connection's GAP event callback is first specified when the connection is + * created, either via advertising or initiation. This function replaces the + * callback that was last configured. + * + * @param conn_handle The handle of the connection to configure. + * @param cb The callback to associate with the connection. + * @param cb_arg An optional argument that the callback receives. + * + * @return 0 on success, BLE_HS_ENOTCONN if there is no connection + * with the specified handle. + */ +int ble_gap_set_event_cb(uint16_t conn_handle, + ble_gap_event_fn *cb, void *cb_arg); + +/** @brief Start advertising + * + * This function configures and start advertising procedure. + * + * @param own_addr_type The type of address the stack should use for itself. + * Valid values are: + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param direct_addr The peer's address for directed advertising. This + * parameter shall be non-NULL if directed advertising is + * being used. + * @param duration_ms The duration of the advertisement procedure. On + * expiration, the procedure ends and a + * BLE_GAP_EVENT_ADV_COMPLETE event is reported. Units are + * milliseconds. Specify BLE_HS_FOREVER for no expiration. + * @param adv_params Additional arguments specifying the particulars of the + * advertising procedure. + * @param cb The callback to associate with this advertising + * procedure. If advertising ends, the event is reported + * through this callback. If advertising results in a + * connection, the connection inherits this callback as its + * event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback function. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, + const struct ble_gap_adv_params *adv_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Stops the currently-active advertising procedure. A success return + * code indicates that advertising has been fully aborted and a new advertising + * procedure can be initiated immediately. + * + * NOTE: If the caller is running in the same task as the NimBLE host, or if it + * is running in a higher priority task than that of the host, care must be + * taken when restarting advertising. Under these conditions, the following is + * *not* a reliable method to restart advertising: + * ble_gap_adv_stop() + * ble_gap_adv_start() + * + * Instead, the call to `ble_gap_adv_start()` must be made in a separate event + * context. That is, `ble_gap_adv_start()` must be called asynchronously by + * enqueueing an event on the current task's event queue. See + * https://github.com/apache/mynewt-nimble/pull/211 for more information. + * + * @return 0 on success, BLE_HS_EALREADY if there is no active advertising + * procedure, other error code on failure. + */ +int ble_gap_adv_stop(void); + +/** + * Indicates whether an advertisement procedure is currently in progress. + * + * @return 0 if no advertisement procedure in progress, 1 otherwise. + */ +int ble_gap_adv_active(void); + +/** + * Configures the data to include in subsequent advertisements. + * + * @param data Buffer containing the advertising data. + * @param data_len The size of the advertising data, in bytes. + * + * @return 0 on succes, BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_adv_set_data(const uint8_t *data, int data_len); + +/** + * Configures the data to include in subsequent scan responses. + * + * @param data Buffer containing the scan response data. + * @param data_len The size of the response data, in bytes. + * + * @return 0 on succes, BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_adv_rsp_set_data(const uint8_t *data, int data_len); + +/** + * Configures the fields to include in subsequent advertisements. This is a + * convenience wrapper for ble_gap_adv_set_data(). + * + * @param adv_fields Specifies the advertisement data. + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * BLE_HS_EMSGSIZE if the specified data is too large to + * fit in an advertisement, + * other error code on failure. + */ +int ble_gap_adv_set_fields(const struct ble_hs_adv_fields *rsp_fields); + +/** + * Configures the fields to include in subsequent scan responses. This is a + * convenience wrapper for ble_gap_adv_rsp_set_data(). + * + * @param adv_fields Specifies the scan response data. + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * BLE_HS_EMSGSIZE if the specified data is too large to + * fit in a scan response, + * other error code on failure. + */ +int ble_gap_adv_rsp_set_fields(const struct ble_hs_adv_fields *rsp_fields); + +/** + * Configure LE Data Length in controller (OGF = 0x08, OCF = 0x0022). + * + * @param conn_handle Connection handle. + * @param tx_octets The preferred value of payload octets that the Controller + * should use for a new connection (Range + * 0x001B-0x00FB). + * @param tx_time The preferred maximum number of microseconds that the local Controller + * should use to transmit a single link layer packet + * (Range 0x0148-0x4290). + * + * @return 0 on success, + * other error code on failure. + */ +int ble_hs_hci_util_set_data_len(uint16_t conn_handle, uint16_t tx_octets, + uint16_t tx_time); + +#if MYNEWT_VAL(BLE_EXT_ADV) +/** @brief Extended advertising parameters */ +struct ble_gap_ext_adv_params { + /** If perform connectable advertising */ + unsigned int connectable:1; + + /** If perform scannable advertising */ + unsigned int scannable:1; + + /** If perform directed advertising */ + unsigned int directed:1; + + /** If perform high-duty directed advertising */ + unsigned int high_duty_directed:1; + + /** If use legacy PDUs for advertising */ + unsigned int legacy_pdu:1; + + /** If perform anonymous advertising */ + unsigned int anonymous:1; + + /** If include TX power in advertising PDU */ + unsigned int include_tx_power:1; + + /** If enable scan request notification */ + unsigned int scan_req_notif:1; + + /** Minimum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint32_t itvl_min; + + /** Maximum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint32_t itvl_max; + + /** Advertising channel map , if 0 stack use sane defaults */ + uint8_t channel_map; + + /** Own address type to be used by advertising instance */ + uint8_t own_addr_type; + + /** Peer address for directed advertising, valid only if directed is set */ + ble_addr_t peer; + + /** Advertising Filter policy */ + uint8_t filter_policy; + + /** Primary advertising PHY to use , can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t primary_phy; + + /** Secondary advertising PHY to use, can be one of following constants: + * - BLE_HCI_LE_PHY_1M + * - LE_HCI_LE_PHY_2M + * - BLE_HCI_LE_PHY_CODED + */ + uint8_t secondary_phy; + + /** Preferred advertiser transmit power */ + int8_t tx_power; + + /** Advertising Set ID */ + uint8_t sid; +}; + +/** + * Configure extended advertising instance + * + * @param instance Instance ID + * @param params Additional arguments specifying the particulars + * of the advertising. + * @param selected_tx_power Selected advertising transmit power will be + * stored in that param if non-NULL. + * @param cb The callback to associate with this advertising + * procedure. Advertising complete event is reported + * through this callback + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Set random address for configured advertising instance. + * + * @param instance Instance ID + * @param addr Random address to be set + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_adv_set_addr(uint8_t instance, const ble_addr_t *addr); + +/** + * Start advertising instance. + * + * @param instance Instance ID + * @param duration The duration of the advertisement procedure. On + * expiration, the procedure ends and + * a BLE_GAP_EVENT_ADV_COMPLETE event is reported. + * Units are 10 milliseconds. Specify 0 for no + * expiration. + * @params max_events Number of advertising events that should be sent + * before advertising ends and + * a BLE_GAP_EVENT_ADV_COMPLETE event is reported. + * Specify 0 for no limit. + * + * @return 0 on success, error code on failure. + */ +int ble_gap_ext_adv_start(uint8_t instance, int duration, int max_events); + +/** + * Stops advertising procedure for specified instance. + * + * @param instance Instance ID + * + * @return 0 on success, BLE_HS_EALREADY if there is no active advertising + * procedure for instance, other error code on failure. + */ +int ble_gap_ext_adv_stop(uint8_t instance); + +/** + * Configures the data to include in advertisements packets for specified + * advertising instance. + * + * @param instance Instance ID + * @param data Chain containing the advertising data. + * + * @return 0 on success or error code on failure. + */ +int ble_gap_ext_adv_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Configures the data to include in subsequent scan responses for specified + * advertisign instance. + * + * @param instance Instance ID + * @param data Chain containing the scan response data. + * + * @return 0 on success or error code on failure. + */ + +int ble_gap_ext_adv_rsp_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Remove existing advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_ext_adv_remove(uint8_t instance); + +/** + * Clear all existing advertising instances + * @return 0 on success, + * BLE_HS_EBUSY if advertising is in progress, + * other error code on failure. + */ +int ble_gap_ext_adv_clear(void); +#endif + +/* Periodic Advertising */ +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + +/** @brief Periodic advertising parameters */ +struct ble_gap_periodic_adv_params { + /** If include TX power in advertising PDU */ + unsigned int include_tx_power:1; + + /** Minimum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint16_t itvl_min; + + /** Maximum advertising interval in 0.625ms units, if 0 stack use sane + * defaults + */ + uint16_t itvl_max; +}; + +/** @brief Periodic sync parameters */ +struct ble_gap_periodic_sync_params { + /** The maximum number of periodic advertising events that controller can + * skip after a successful receive. + * */ + uint16_t skip; + + /** Synchronization timeout for the periodic advertising train in 10ms units + */ + uint16_t sync_timeout; + + /** If reports should be initially disabled when sync is created */ + unsigned int reports_disabled:1; +}; + +/** + * Configure periodic advertising for specified advertising instance + * + * This is allowed only for instances configured as non-announymous, + * non-connectable and non-scannable. + * + * @param instance Instance ID + * @param params Additional arguments specifying the particulars + * of periodic advertising. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_configure(uint8_t instance, + const struct ble_gap_periodic_adv_params *params); + +/** + * Start periodic advertising for specified advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, error code on failure. + */ +int ble_gap_periodic_adv_start(uint8_t instance); + +/** + * Stop periodic advertising for specified advertising instance. + * + * @param instance Instance ID + * + * @return 0 on success, error code on failure. + */ +int ble_gap_periodic_adv_stop(uint8_t instance); + +/** + * Configures the data to include in periodic advertisements for specified + * advertising instance. + * + * @param instance Instance ID + * @param data Chain containing the periodic advertising data. + * + * @return 0 on success or error code on failure. + */ +int ble_gap_periodic_adv_set_data(uint8_t instance, struct os_mbuf *data); + +/** + * Performs the Synchronization procedure with periodic advertiser. + * + * @param addr Peer address to synchronize with. If NULL than + * peers from periodic list are used. + * @param adv_sid Advertiser Set ID + * @param params Additional arguments specifying the particulars + * of the synchronization procedure. + * @param cb The callback to associate with this synchrnization + * procedure. BLE_GAP_EVENT_PERIODIC_REPORT events + * are reported only by this callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_create(const ble_addr_t *addr, uint8_t adv_sid, + const struct ble_gap_periodic_sync_params *params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Cancel pending synchronization procedure. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_create_cancel(void); + +/** + * Terminate synchronization procedure. + * + * @param sync_handle Handle identifying synchronization to terminate. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_terminate(uint16_t sync_handle); + +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) +/** + * Disable or enable periodic reports for specified sync. + * + * @param sync_handle Handle identifying synchronization. + * @param enable If reports should be enabled. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_reporting(uint16_t sync_handle, bool enable); + +/** + * Initialize sync transfer procedure for specified handles. + * + * This allows to transfer periodic sync to which host is synchronized. + * + * @param sync_handle Handle identifying synchronization. + * @param conn_handle Handle identifying connection. + * @param service_data Sync transfer service data + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_transfer(uint16_t sync_handle, + uint16_t conn_handle, + uint16_t service_data); + +/** + * Initialize set info transfer procedure for specified handles. + * + * This allows to transfer periodic sync which is being advertised by host. + * + * @param instance Advertising instance with periodic adv enabled. + * @param conn_handle Handle identifying connection. + * @param service_data Sync transfer service data + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_set_info(uint8_t instance, + uint16_t conn_handle, + uint16_t service_data); + +/** + * Enables or disables sync transfer reception on specified connection. + * When sync transfer arrives, BLE_GAP_EVENT_PERIODIC_TRANSFER is sent to the user. + * After that, sync transfer reception on that connection is terminated and user needs + * to call this API again when expect to receive next sync transfers. + * + * Note: If ACL connection gets disconnected before sync transfer arrived, user will + * not receive BLE_GAP_EVENT_PERIODIC_TRANSFER. Instead, sync transfer reception + * is terminated by the host automatically. + * + * @param conn_handle Handle identifying connection. + * @param params Parameters for enabled sync transfer reception. + * Specify NULL to disable reception. + * @param cb The callback to associate with this synchronization + * procedure. BLE_GAP_EVENT_PERIODIC_REPORT events + * are reported only by this callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_periodic_adv_sync_receive(uint16_t conn_handle, + const struct ble_gap_periodic_sync_params *params, + ble_gap_event_fn *cb, void *cb_arg); +#endif + +/** + * Add peer device to periodic synchronization list. + * + * @param addr Peer address to add to list. + * @param adv_sid Advertiser Set ID + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_add_dev_to_periodic_adv_list(const ble_addr_t *peer_addr, + uint8_t adv_sid); + +/** + * Remove peer device from periodic synchronization list. + * + * @param addr Peer address to remove from list. + * @param adv_sid Advertiser Set ID + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_rem_dev_from_periodic_adv_list(const ble_addr_t *peer_addr, + uint8_t adv_sid); + +/** + * Clear periodic synchrnization list. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_clear_periodic_adv_list(void); + +/** + * Get periodic synchronization list size. + * + * @param per_adv_list_size On success list size is stored here. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_read_periodic_adv_list_size(uint8_t *per_adv_list_size); +#endif + + +/** + * Performs the Limited or General Discovery Procedures. + * + * @param own_addr_type The type of address the stack should use for + * itself when sending scan requests. Valid + * values are: + * - BLE_ADDR_TYPE_PUBLIC + * - BLE_ADDR_TYPE_RANDOM + * - BLE_ADDR_TYPE_RPA_PUB_DEFAULT + * - BLE_ADDR_TYPE_RPA_RND_DEFAULT + * This parameter is ignored unless active + * scanning is being used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. Specify + * BLE_HS_FOREVER for no expiration. Specify + * 0 to use stack defaults. + * @param disc_params Additional arguments specifying the particulars + * of the discovery procedure. + * @param cb The callback to associate with this discovery + * procedure. Advertising reports and + * discovery termination events are reported + * through this callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_disc(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Performs the Limited or General Extended Discovery Procedures. + * + * @param own_addr_type The type of address the stack should use for + * itself when sending scan requests. Valid + * values are: + * - BLE_ADDR_TYPE_PUBLIC + * - BLE_ADDR_TYPE_RANDOM + * - BLE_ADDR_TYPE_RPA_PUB_DEFAULT + * - BLE_ADDR_TYPE_RPA_RND_DEFAULT + * This parameter is ignored unless active + * scanning is being used. + * @param duration The duration of the discovery procedure. + * On expiration, if period is set to 0, the + * procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are 10 milliseconds. + * Specify 0 for no expiration. + * @param period Time interval from when the Controller started + * its last Scan Duration until it begins the + * subsequent Scan Duration. Specify 0 to scan + * continuously. Units are 1.28 second. + * @param limited If limited discovery procedure should be used. + * @param uncoded_params Additional arguments specifying the particulars + * of the discovery procedure for uncoded PHY. + * If NULL is provided no scan is performed for + * this PHY. + * @param coded_params Additional arguments specifying the particulars + * of the discovery procedure for coded PHY. + * If NULL is provided no scan is performed for + * this PHY. + * @param cb The callback to associate with this discovery + * procedure. Advertising reports and discovery + * termination events are reported through this + * callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_ext_disc(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Cancels the discovery procedure currently in progress. A success return + * code indicates that scanning has been fully aborted; a new discovery or + * connect procedure can be initiated immediately. + * + * @return 0 on success; + * BLE_HS_EALREADY if there is no discovery + * procedure to cancel; + * Other nonzero on unexpected error. + */ +int ble_gap_disc_cancel(void); + +/** + * Indicates whether a discovery procedure is currently in progress. + * + * @return 0: No discovery procedure in progress; + * 1: Discovery procedure in progress. + */ +int ble_gap_disc_active(void); + +/** + * Initiates a connect procedure. + * + * @param own_addr_type The type of address the stack should use for + * itself during connection establishment. + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param peer_addr The address of the peer to connect to. + * If this parameter is NULL, the white list + * is used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. + * @param conn_params Additional arguments specifying the particulars + * of the connect procedure. Specify null for + * default values. + * @param cb The callback to associate with this connect + * procedure. When the connect procedure + * completes, the result is reported through + * this callback. If the connect procedure + * succeeds, the connection inherits this + * callback as its event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; + * BLE_HS_EALREADY if a connection attempt is + * already in progress; + * BLE_HS_EBUSY if initiating a connection is not + * possible because scanning is in progress; + * BLE_HS_EDONE if the specified peer is already + * connected; + * Other nonzero on error. + */ +int ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + const struct ble_gap_conn_params *params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Initiates an extended connect procedure. + * + * @param own_addr_type The type of address the stack should use for + * itself during connection establishment. + * - BLE_OWN_ADDR_PUBLIC + * - BLE_OWN_ADDR_RANDOM + * - BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT + * - BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + * @param peer_addr The address of the peer to connect to. + * If this parameter is NULL, the white list + * is used. + * @param duration_ms The duration of the discovery procedure. + * On expiration, the procedure ends and a + * BLE_GAP_EVENT_DISC_COMPLETE event is + * reported. Units are milliseconds. + * @param phy_mask Define on which PHYs connection attempt should + * be done + * @param phy_1m_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_1M_MASK is set in phy_mask + * this parameter can be specify to null for + * default values. + * @param phy_2m_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_2M_MASK is set in phy_mask + * this parameter can be specify to null for + * default values. + * @param phy_coded_conn_params Additional arguments specifying the + * particulars of the connect procedure. When + * BLE_GAP_LE_PHY_CODED_MASK is set in + * phy_mask this parameter can be specify to + * null for default values. + * @param cb The callback to associate with this connect + * procedure. When the connect procedure + * completes, the result is reported through + * this callback. If the connect procedure + * succeeds, the connection inherits this + * callback as its event-reporting mechanism. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; + * BLE_HS_EALREADY if a connection attempt is + * already in progress; + * BLE_HS_EBUSY if initiating a connection is not + * possible because scanning is in progress; + * BLE_HS_EDONE if the specified peer is already + * connected; + * Other nonzero on error. + */ +int ble_gap_ext_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, uint8_t phy_mask, + const struct ble_gap_conn_params *phy_1m_conn_params, + const struct ble_gap_conn_params *phy_2m_conn_params, + const struct ble_gap_conn_params *phy_coded_conn_params, + ble_gap_event_fn *cb, void *cb_arg); + +/** + * Aborts a connect procedure in progress. + * + * @return 0 on success; + * BLE_HS_EALREADY if there is no active connect + * procedure. + * Other nonzero on error. + */ +int ble_gap_conn_cancel(void); + +/** + * Indicates whether a connect procedure is currently in progress. + * + * @return 0: No connect procedure in progress; + * 1: Connect procedure in progress. + */ +int ble_gap_conn_active(void); + +/** + * Terminates an established connection. + * + * @param conn_handle The handle corresponding to the connection to + * terminate. + * @param hci_reason The HCI error code to indicate as the reason + * for termination. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if there is no connection with + * the specified handle; + * Other nonzero on failure. + */ +int ble_gap_terminate(uint16_t conn_handle, uint8_t hci_reason); + +/** + * Overwrites the controller's white list with the specified contents. + * + * @param addrs The entries to write to the white list. + * @param white_list_count The number of entries in the white list. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_wl_set(const ble_addr_t *addrs, uint8_t white_list_count); + +/** + * Removes the address from controller's white list. + * + * @param addrs The entry to be removed from the white list. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_wl_tx_rmv(const ble_addr_t *addrs); + +/** + * Initiates a connection parameter update procedure. + * + * @param conn_handle The handle corresponding to the connection to + * update. + * @param params The connection parameters to attempt to update + * to. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if a connection update + * procedure for this connection is already in + * progress; + * BLE_HS_EINVAL if requested parameters are + * invalid; + * Other nonzero on error. + */ +int ble_gap_update_params(uint16_t conn_handle, + const struct ble_gap_upd_params *params); + +/** + * Configure LE Data Length in controller (OGF = 0x08, OCF = 0x0022). + * + * @param conn_handle Connection handle. + * @param tx_octets The preferred value of payload octets that the Controller + * should use for a new connection (Range + * 0x001B-0x00FB). + * @param tx_time The preferred maximum number of microseconds that the local Controller + * should use to transmit a single link layer packet + * (Range 0x0148-0x4290). + * + * @return 0 on success, + * other error code on failure. + */ +int ble_gap_set_data_len(uint16_t conn_handle, uint16_t tx_octets, uint16_t tx_time); + +/** + * Initiates the GAP security procedure. + * + * Depending on connection role and stored security information this function + * will start appropriate security procedure (pairing or encryption). + * + * @param conn_handle The handle corresponding to the connection to + * secure. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an security procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_security_initiate(uint16_t conn_handle); + +/** + * Initiates the GAP pairing procedure as a master. This is for testing only and + * should not be used by application. Use ble_gap_security_initiate() instead. + * + * @param conn_handle The handle corresponding to the connection to + * start pairing on. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an pairing procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_pair_initiate(uint16_t conn_handle); + +/** + * Initiates the GAP encryption procedure as a master. This is for testing only + * and should not be used by application. Use ble_gap_security_initiate() + * instead. + * + * @param conn_handle The handle corresponding to the connection to + * start encryption. + * @param key_size Encryption key size + * @param ltk Long Term Key to be used for encryption. + * @param udiv Encryption Diversifier for LTK + * @param rand_val Random Value for EDIV and LTK + * @param auth If LTK provided is authenticated. + * + * @return 0 on success; + * BLE_HS_ENOTCONN if the there is no connection + * with the specified handle; + * BLE_HS_EALREADY if an encryption procedure for + * this connection is already in progress; + * Other nonzero on error. + */ +int ble_gap_encryption_initiate(uint16_t conn_handle, uint8_t key_size, + const uint8_t *ltk, uint16_t ediv, + uint64_t rand_val, int auth); + +/** + * Retrieves the most-recently measured RSSI for the specified connection. A + * connection's RSSI is updated whenever a data channel PDU is received. + * + * @param conn_handle Specifies the connection to query. + * @param out_rssi On success, the retrieved RSSI is written here. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_conn_rssi(uint16_t conn_handle, int8_t *out_rssi); + +/** + * Unpairs a device with the specified address. The keys related to that peer + * device are removed from storage and peer address is removed from the resolve + * list from the controller. If a peer is connected, the connection is terminated. + * + * @param peer_addr Address of the device to be unpaired + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair(const ble_addr_t *peer_addr); + +/** + * Unpairs the oldest bonded peer device. The keys related to that peer + * device are removed from storage and peer address is removed from the resolve + * list from the controller. If a peer is connected, the connection is terminated. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair_oldest_peer(void); + +/** + * Similar to `ble_gap_unpair_oldest_peer()`, except it makes sure that the + * peer received in input parameters is not deleted. + * + * @param peer_addr Address of the peer (not to be deleted) + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_gap_unpair_oldest_except(const ble_addr_t *peer_addr); + +#define BLE_GAP_PRIVATE_MODE_NETWORK 0 +#define BLE_GAP_PRIVATE_MODE_DEVICE 1 + +/** + * Set privacy mode for specified peer device + * + * @param peer_addr Peer device address + * @param priv_mode Privacy mode to be used. Can be one of following + * constants: + * - BLE_GAP_PRIVATE_MODE_NETWORK + * - BLE_GAP_PRIVATE_MODE_DEVICE + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_priv_mode(const ble_addr_t *peer_addr, uint8_t priv_mode); + +#define BLE_GAP_LE_PHY_1M 1 +#define BLE_GAP_LE_PHY_2M 2 +#define BLE_GAP_LE_PHY_CODED 3 +/** + * Read PHYs used for specified connection. + * + * On success output parameters are filled with information about used PHY type. + * + * @param conn_handle Connection handle + * @param tx_phy TX PHY used. Can be one of following constants: + * - BLE_GAP_LE_PHY_1M + * - BLE_GAP_LE_PHY_2M + * - BLE_GAP_LE_PHY_CODED + * @param rx_phy RX PHY used. Can be one of following constants: + * - BLE_GAP_LE_PHY_1M + * - BLE_GAP_LE_PHY_2M + * - BLE_GAP_LE_PHY_CODED + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_read_le_phy(uint16_t conn_handle, uint8_t *tx_phy, uint8_t *rx_phy); + +#define BLE_GAP_LE_PHY_1M_MASK 0x01 +#define BLE_GAP_LE_PHY_2M_MASK 0x02 +#define BLE_GAP_LE_PHY_CODED_MASK 0x04 +#define BLE_GAP_LE_PHY_ANY_MASK 0x0F +/** + * Set preferred default PHYs to be used for connections. + * + * @params tx_phys_mask Preferred TX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @params rx_phys_mask Preferred RX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_prefered_default_le_phy(uint8_t tx_phys_mask, + uint8_t rx_phys_mask); + +#define BLE_GAP_LE_PHY_CODED_ANY 0 +#define BLE_GAP_LE_PHY_CODED_S2 1 +#define BLE_GAP_LE_PHY_CODED_S8 2 +/** + * Set preferred PHYs to be used for connection. + * + * @param conn_handle Connection handle + * @params tx_phys_mask Preferred TX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @params rx_phys_mask Preferred RX PHY. Can be mask of following + * constants: + * - BLE_GAP_LE_PHY_1M_MASK + * - BLE_GAP_LE_PHY_2M_MASK + * - BLE_GAP_LE_PHY_CODED_MASK + * - BLE_GAP_LE_PHY_ANY_MASK + * @param phy_opts Additional PHY options. Valid values are: + * - BLE_GAP_LE_PHY_CODED_ANY + * - BLE_GAP_LE_PHY_CODED_S2 + * - BLE_GAP_LE_PHY_CODED_S8 + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_prefered_le_phy(uint16_t conn_handle, uint8_t tx_phys_mask, + uint8_t rx_phys_mask, uint16_t phy_opts); + +/** + * Event listener structure + * + * This should be used as an opaque structure and not modified manually. + */ +struct ble_gap_event_listener { + ble_gap_event_fn *fn; + void *arg; + SLIST_ENTRY(ble_gap_event_listener) link; +}; + +/** + * Registers listener for GAP events + * + * On success listener structure will be initialized automatically and does not + * need to be initialized prior to calling this function. To change callback + * and/or argument unregister listener first and register it again. + * + * @param listener Listener structure + * @param fn Callback function + * @param arg Callback argument + * + * @return 0 on success + * BLE_HS_EINVAL if no callback is specified + * BLE_HS_EALREADY if listener is already registered + */ +int ble_gap_event_listener_register(struct ble_gap_event_listener *listener, + ble_gap_event_fn *fn, void *arg); + +/** + * Unregisters listener for GAP events + * + * @param listener Listener structure + * + * @return 0 on success + * BLE_HS_ENOENT if listener was not registered + */ +int ble_gap_event_listener_unregister(struct ble_gap_event_listener *listener); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gatt.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gatt.h new file mode 100644 index 0000000..ee8f177 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_gatt.h @@ -0,0 +1,902 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_GATT_ +#define H_BLE_GATT_ + +/** + * @brief Bluetooth Generic Attribute Profile (GATT) + * @defgroup bt_gatt Bluetooth Generic Attribute Profile (GATT) + * @ingroup bt_host + * @{ + */ + +#include +#include "ble_att.h" +#include "ble_uuid.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_conn; +struct ble_att_error_rsp; +struct ble_hs_cfg; + +#define BLE_GATT_REGISTER_OP_SVC 1 +#define BLE_GATT_REGISTER_OP_CHR 2 +#define BLE_GATT_REGISTER_OP_DSC 3 + +#define BLE_GATT_SVC_UUID16 0x1801 +#define BLE_GATT_DSC_CLT_CFG_UUID16 0x2902 + +#define BLE_GATT_CHR_PROP_BROADCAST 0x01 +#define BLE_GATT_CHR_PROP_READ 0x02 +#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04 +#define BLE_GATT_CHR_PROP_WRITE 0x08 +#define BLE_GATT_CHR_PROP_NOTIFY 0x10 +#define BLE_GATT_CHR_PROP_INDICATE 0x20 +#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40 +#define BLE_GATT_CHR_PROP_EXTENDED 0x80 + +#define BLE_GATT_ACCESS_OP_READ_CHR 0 +#define BLE_GATT_ACCESS_OP_WRITE_CHR 1 +#define BLE_GATT_ACCESS_OP_READ_DSC 2 +#define BLE_GATT_ACCESS_OP_WRITE_DSC 3 + +#define BLE_GATT_CHR_F_BROADCAST 0x0001 +#define BLE_GATT_CHR_F_READ 0x0002 +#define BLE_GATT_CHR_F_WRITE_NO_RSP 0x0004 +#define BLE_GATT_CHR_F_WRITE 0x0008 +#define BLE_GATT_CHR_F_NOTIFY 0x0010 +#define BLE_GATT_CHR_F_INDICATE 0x0020 +#define BLE_GATT_CHR_F_AUTH_SIGN_WRITE 0x0040 +#define BLE_GATT_CHR_F_RELIABLE_WRITE 0x0080 +#define BLE_GATT_CHR_F_AUX_WRITE 0x0100 +#define BLE_GATT_CHR_F_READ_ENC 0x0200 +#define BLE_GATT_CHR_F_READ_AUTHEN 0x0400 +#define BLE_GATT_CHR_F_READ_AUTHOR 0x0800 +#define BLE_GATT_CHR_F_WRITE_ENC 0x1000 +#define BLE_GATT_CHR_F_WRITE_AUTHEN 0x2000 +#define BLE_GATT_CHR_F_WRITE_AUTHOR 0x4000 + +#define BLE_GATT_SVC_TYPE_END 0 +#define BLE_GATT_SVC_TYPE_PRIMARY 1 +#define BLE_GATT_SVC_TYPE_SECONDARY 2 + +/*** @client. */ +struct ble_gatt_error { + uint16_t status; + uint16_t att_handle; +}; + +struct ble_gatt_svc { + uint16_t start_handle; + uint16_t end_handle; + ble_uuid_any_t uuid; +}; + +struct ble_gatt_attr { + uint16_t handle; + uint16_t offset; + struct os_mbuf *om; +}; + +struct ble_gatt_chr { + uint16_t def_handle; + uint16_t val_handle; + uint8_t properties; + ble_uuid_any_t uuid; +}; + +struct ble_gatt_dsc { + uint16_t handle; + ble_uuid_any_t uuid; +}; + +typedef int ble_gatt_mtu_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg); +typedef int ble_gatt_disc_svc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, + void *arg); + +/** + * The host will free the attribute mbuf automatically after the callback is + * executed. The application can take ownership of the mbuf and prevent it + * from being freed by assigning NULL to attr->om. + */ +typedef int ble_gatt_attr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg); + +/** + * The host will free the attribute mbufs automatically after the callback is + * executed. The application can take ownership of the mbufs and prevent them + * from being freed by assigning NULL to each attribute's om field. + */ +typedef int ble_gatt_reliable_attr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, void *arg); + +typedef int ble_gatt_chr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg); + +typedef int ble_gatt_dsc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg); + +/** + * Initiates GATT procedure: Exchange MTU. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_exchange_mtu(uint16_t conn_handle, + ble_gatt_mtu_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Primary Services. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + */ +int ble_gattc_disc_all_svcs(uint16_t conn_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover Primary Service by Service UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param service_uuid128 The 128-bit UUID of the service to discover. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Find Included Services. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_disc_svc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Characteristics of a Service. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, ble_gatt_chr_fn *cb, + void *cb_arg); + +/** + * Initiates GATT procedure: Discover Characteristics by UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The handle to begin the search at (generally + * the service definition handle). + * @param end_handle The handle to end the search at (generally the + * last handle in the service). + * @param chr_uuid128 The 128-bit UUID of the characteristic to + * discover. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_chr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Discover All Characteristic Descriptors. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The handle of the characteristic value + * attribute. + * @param chr_end_handle The last handle in the characteristic + * definition. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, + ble_gatt_dsc_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Characteristic Value. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to read. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Using Characteristic UUID. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param start_handle The first handle to search (generally the + * handle of the service definition). + * @param end_handle The last handle to search (generally the + * last handle in the service definition). + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Long Characteristic Values. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param handle The handle of the characteristic value to read. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_long(uint16_t conn_handle, uint16_t handle, uint16_t offset, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Read Multiple Characteristic Values. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param handles An array of 16-bit attribute handles to read. + * @param num_handles The number of entries in the "handles" array. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_read_mult(uint16_t conn_handle, const uint16_t *handles, + uint8_t num_handles, ble_gatt_attr_fn *cb, + void *cb_arg); + +/** + * Initiates GATT procedure: Write Without Response. This function consumes + * the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); + +/** + * Initiates GATT procedure: Write Without Response. This function consumes + * the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param value The value to write to the characteristic. + * @param value_len The number of bytes to write. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_no_rsp_flat(uint16_t conn_handle, uint16_t attr_handle, + const void *data, uint16_t data_len); + +/** + * Initiates GATT procedure: Write Characteristic Value. This function + * consumes the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Write Characteristic Value (flat buffer version). + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param value The value to write to the characteristic. + * @param value_len The number of bytes to write. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_flat(uint16_t conn_handle, uint16_t attr_handle, + const void *data, uint16_t data_len, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Write Long Characteristic Values. This function + * consumes the supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attr_handle The handle of the characteristic value to write + * to. + * @param txom The value to write to the characteristic. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om, + ble_gatt_attr_fn *cb, void *cb_arg); + +/** + * Initiates GATT procedure: Reliable Writes. This function consumes the + * supplied mbufs regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param attrs An array of attribute descriptors; specifies + * which characteristics to write to and what + * data to write to them. The mbuf pointer in + * each attribute is set to NULL by this + * function. + * @param num_attrs The number of characteristics to write; equal + * to the number of elements in the 'attrs' + * array. + * @param cb The function to call to report procedure status + * updates; null for no callback. + * @param cb_arg The optional argument to pass to the callback + * function. + */ +int ble_gattc_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, + int num_attrs, ble_gatt_reliable_attr_fn *cb, + void *cb_arg); + +/** + * Sends a "free-form" characteristic notification. This function consumes the + * supplied mbuf regardless of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The attribute handle to indicate in the + * outgoing notification. + * @param txom The value to write to the characteristic. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_notify_custom(uint16_t conn_handle, uint16_t att_handle, + struct os_mbuf *om); + +/** + * Sends a characteristic notification. The content of the message is read + * from the specified characteristic. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * notification. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_handle); + +/** + * Sends a "free-form" characteristic indication. The provided mbuf contains + * the indication payload. This function consumes the supplied mbuf regardless + * of the outcome. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * indication. + * @param txom The data to include in the indication. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_indicate_custom(uint16_t conn_handle, uint16_t chr_val_handle, + struct os_mbuf *txom); + +/** + * Sends a characteristic indication. The content of the message is read from + * the specified characteristic. + * + * @param conn_handle The connection over which to execute the + * procedure. + * @param chr_val_handle The value attribute handle of the + * characteristic to include in the outgoing + * indication. + * + * @return 0 on success; nonzero on failure. + */ +int ble_gattc_indicate(uint16_t conn_handle, uint16_t chr_val_handle); + +int ble_gattc_init(void); + +/*** @server. */ + +struct ble_gatt_access_ctxt; +typedef int ble_gatt_access_fn(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +typedef uint16_t ble_gatt_chr_flags; + +struct ble_gatt_chr_def { + /** + * Pointer to characteristic UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** + * Callback that gets executed when this characteristic is read or + * written. + */ + ble_gatt_access_fn *access_cb; + + /** Optional argument for callback. */ + void *arg; + + /** + * Array of this characteristic's descriptors. NULL if no descriptors. + * Do not include CCCD; it gets added automatically if this + * characteristic's notify or indicate flag is set. + */ + struct ble_gatt_dsc_def *descriptors; + + /** Specifies the set of permitted operations for this characteristic. */ + ble_gatt_chr_flags flags; + + /** Specifies minimum required key size to access this characteristic. */ + uint8_t min_key_size; + + /** + * At registration time, this is filled in with the characteristic's value + * attribute handle. + */ + uint16_t *val_handle; +}; + +struct ble_gatt_svc_def { + /** + * One of the following: + * o BLE_GATT_SVC_TYPE_PRIMARY - primary service + * o BLE_GATT_SVC_TYPE_SECONDARY - secondary service + * o 0 - No more services in this array. + */ + uint8_t type; + + /** + * Pointer to service UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** + * Array of pointers to other service definitions. These services are + * reported as "included services" during service discovery. Terminate the + * array with NULL. + */ + const struct ble_gatt_svc_def **includes; + + /** + * Array of characteristic definitions corresponding to characteristics + * belonging to this service. + */ + const struct ble_gatt_chr_def *characteristics; +}; + +struct ble_gatt_dsc_def { + /** + * Pointer to descriptor UUID; use BLE_UUIDxx_DECLARE macros to declare + * proper UUID; NULL if there are no more characteristics in the service. + */ + const ble_uuid_t *uuid; + + /** Specifies the set of permitted operations for this descriptor. */ + uint8_t att_flags; + + /** Specifies minimum required key size to access this descriptor. */ + uint8_t min_key_size; + + /** Callback that gets executed when the descriptor is read or written. */ + ble_gatt_access_fn *access_cb; + + /** Optional argument for callback. */ + void *arg; +}; + +/** + * Context for an access to a GATT characteristic or descriptor. When a client + * reads or writes a locally registered characteristic or descriptor, an + * instance of this struct gets passed to the application callback. + */ +struct ble_gatt_access_ctxt { + /** + * Indicates the gatt operation being performed. This is equal to one of + * the following values: + * o BLE_GATT_ACCESS_OP_READ_CHR + * o BLE_GATT_ACCESS_OP_WRITE_CHR + * o BLE_GATT_ACCESS_OP_READ_DSC + * o BLE_GATT_ACCESS_OP_WRITE_DSC + */ + uint8_t op; + + /** + * A container for the GATT access data. + * o For reads: The application populates this with the value of the + * characteristic or descriptor being read. + * o For writes: This is already populated with the value being written + * by the peer. If the application wishes to retain this mbuf for + * later use, the access callback must set this pointer to NULL to + * prevent the stack from freeing it. + */ + struct os_mbuf *om; + + /** + * The GATT operation being performed dictates which field in this union is + * valid. If a characteristic is being accessed, the chr field is valid. + * Otherwise a descriptor is being accessed, in which case the dsc field + * is valid. + */ + union { + /** + * The characteristic definition corresponding to the characteristic + * being accessed. This is what the app registered at startup. + */ + const struct ble_gatt_chr_def *chr; + + /** + * The descriptor definition corresponding to the descriptor being + * accessed. This is what the app registered at startup. + */ + const struct ble_gatt_dsc_def *dsc; + }; +}; + +/** + * Context passed to the registration callback; represents the GATT service, + * characteristic, or descriptor being registered. + */ +struct ble_gatt_register_ctxt { + /** + * Indicates the gatt registration operation just performed. This is + * equal to one of the following values: + * o BLE_GATT_REGISTER_OP_SVC + * o BLE_GATT_REGISTER_OP_CHR + * o BLE_GATT_REGISTER_OP_DSC + */ + uint8_t op; + + /** + * The value of the op field determines which field in this union is valid. + */ + union { + /** Service; valid if op == BLE_GATT_REGISTER_OP_SVC. */ + struct { + /** The ATT handle of the service definition attribute. */ + uint16_t handle; + + /** + * The service definition representing the service being + * registered. + */ + const struct ble_gatt_svc_def *svc_def; + } svc; + + /** Characteristic; valid if op == BLE_GATT_REGISTER_OP_CHR. */ + struct { + /** The ATT handle of the characteristic definition attribute. */ + uint16_t def_handle; + + /** The ATT handle of the characteristic value attribute. */ + uint16_t val_handle; + + /** + * The characteristic definition representing the characteristic + * being registered. + */ + const struct ble_gatt_chr_def *chr_def; + + /** + * The service definition corresponding to the characteristic's + * parent service. + */ + const struct ble_gatt_svc_def *svc_def; + } chr; + + /** Descriptor; valid if op == BLE_GATT_REGISTER_OP_DSC. */ + struct { + /** The ATT handle of the descriptor definition attribute. */ + uint16_t handle; + + /** + * The descriptor definition corresponding to the descriptor being + * registered. + */ + const struct ble_gatt_dsc_def *dsc_def; + + /** + * The characteristic definition corresponding to the descriptor's + * parent characteristic. + */ + const struct ble_gatt_chr_def *chr_def; + + /** + * The service definition corresponding to the descriptor's + * grandparent service + */ + const struct ble_gatt_svc_def *svc_def; + } dsc; + }; +}; + +typedef void ble_gatt_register_fn(struct ble_gatt_register_ctxt *ctxt, + void *arg); + +/** + * Queues a set of service definitions for registration. All services queued + * in this manner get registered when ble_gatts_start() is called. + * + * @param svcs An array of service definitions to queue for + * registration. This array must be + * terminated with an entry whose 'type' + * equals 0. + * + * @return 0 on success; + * BLE_HS_ENOMEM on heap exhaustion. + */ +int ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs); + +/** + * Set visibility of local GATT service. Invisible services are not removed + * from database but are not discoverable by peer devices. Service Changed + * should be handled by application when needed by calling + * ble_svc_gatt_changed(). + * + * @param handle Handle of service + * @param visible non-zero if service should be visible + * + * @return 0 on success; + * BLE_HS_ENOENT if service wasn't found. + */ +int ble_gatts_svc_set_visibility(uint16_t handle, int visible); + +/** + * Adjusts a host configuration object's settings to accommodate the specified + * service definition array. This function adds the counts to the appropriate + * fields in the supplied configuration object without clearing them first, so + * it can be called repeatedly with different inputs to calculate totals. Be + * sure to zero the GATT server settings prior to the first call to this + * function. + * + * @param defs The service array containing the resource + * definitions to be counted. + * + * @return 0 on success; + * BLE_HS_EINVAL if the svcs array contains an + * invalid resource definition. + */ +int ble_gatts_count_cfg(const struct ble_gatt_svc_def *defs); + +/** + * Send notification (or indication) to any connected devices that have + * subscribed for notification (or indication) for specified characteristic. + * + * @param chr_val_handle Characteristic value handle + */ +void ble_gatts_chr_updated(uint16_t chr_val_handle); + +/** + * Retrieves the attribute handle associated with a local GATT service. + * + * @param uuid The UUID of the service to look up. + * @param out_handle On success, populated with the handle of the + * service attribute. Pass null if you don't + * need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service could + * not be found. + */ +int ble_gatts_find_svc(const ble_uuid_t *uuid, uint16_t *out_handle); + +/** + * Retrieves the pair of attribute handles associated with a local GATT + * characteristic. + * + * @param svc_uuid The UUID of the parent service. + * @param chr_uuid The UUID of the characteristic to look up. + * @param out_def_handle On success, populated with the handle + * of the characteristic definition attribute. + * Pass null if you don't need this value. + * @param out_val_handle On success, populated with the handle + * of the characteristic value attribute. + * Pass null if you don't need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service or + * characteristic could not be found. + */ +int ble_gatts_find_chr(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid, + uint16_t *out_def_handle, uint16_t *out_val_handle); + +/** + * Retrieves the attribute handle associated with a local GATT descriptor. + * + * @param svc_uuid The UUID of the grandparent service. + * @param chr_uuid The UUID of the parent characteristic. + * @param dsc_uuid The UUID of the descriptor ro look up. + * @param out_handle On success, populated with the handle + * of the descriptor attribute. Pass null if + * you don't need this value. + * + * @return 0 on success; + * BLE_HS_ENOENT if the specified service, + * characteristic, or descriptor could not be + * found. + */ +int ble_gatts_find_dsc(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid, + const ble_uuid_t *dsc_uuid, uint16_t *out_dsc_handle); + +typedef void (*ble_gatt_svc_foreach_fn)(const struct ble_gatt_svc_def *svc, + uint16_t handle, + uint16_t end_group_handle, + void *arg); + +/** + * Prints dump of local GATT database. This is useful to log local state of + * database in human readable form. + */ +void ble_gatts_show_local(void); + +/** + * Resets the GATT server to its initial state. On success, this function + * removes all supported services, characteristics, and descriptors. This + * function requires that: + * o No peers are connected, and + * o No GAP operations are active (advertise, discover, or connect). + * + * @return 0 on success; + * BLE_HS_EBUSY if the GATT server could not be + * reset due to existing connections or active + * GAP procedures. + */ +int ble_gatts_reset(void); + +/** + * Makes all registered services available to peers. This function gets called + * automatically by the NimBLE host on startup; manual calls are only necessary + * for replacing the set of supported services with a new one. This function + * requires that: + * o No peers are connected, and + * o No GAP operations are active (advertise, discover, or connect). + * + * @return 0 on success; + * A BLE host core return code on unexpected + * error. + */ +int ble_gatts_start(void); + +/** + * Resets the GATT configuration parameters and deallocates the memory of attributes. + * + */ +void ble_gatts_stop(void); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs.h new file mode 100644 index 0000000..f24b8a9 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs.h @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ +#define H_BLE_HS_ + +/** + * @brief Bluetooth Host + * @defgroup bt_host Bluetooth Host + * @{ + */ + +#include +#include "nimble/nimble/include/nimble/hci_common.h" +#include "ble_att.h" +#include "ble_eddystone.h" +#include "ble_gap.h" +#include "ble_gatt.h" +#include "ble_hs_adv.h" +#include "ble_hs_id.h" +#include "ble_hs_hci.h" +#include "ble_hs_log.h" +#include "ble_hs_mbuf.h" +#include "ble_hs_stop.h" +#include "ble_ibeacon.h" +#include "ble_l2cap.h" +#include "ble_sm.h" +#include "ble_store.h" +#include "ble_uuid.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HS_FOREVER INT32_MAX + +/** Connection handle not present */ +#define BLE_HS_CONN_HANDLE_NONE 0xffff + +/** + * @brief Bluetooth Host Error Code + * @defgroup bt_host_err Bluetooth Host Error Code + * + * Defines error codes returned by Bluetooth host. If error comes from specific + * component (eg L2CAP or Security Manager) it is shifted by base allowing to + * identify component. + * @{ + */ + +#define BLE_HS_EAGAIN 1 +#define BLE_HS_EALREADY 2 +#define BLE_HS_EINVAL 3 +#define BLE_HS_EMSGSIZE 4 +#define BLE_HS_ENOENT 5 +#define BLE_HS_ENOMEM 6 +#define BLE_HS_ENOTCONN 7 +#define BLE_HS_ENOTSUP 8 +#define BLE_HS_EAPP 9 +#define BLE_HS_EBADDATA 10 +#define BLE_HS_EOS 11 +#define BLE_HS_ECONTROLLER 12 +#define BLE_HS_ETIMEOUT 13 +#define BLE_HS_EDONE 14 +#define BLE_HS_EBUSY 15 +#define BLE_HS_EREJECT 16 +#define BLE_HS_EUNKNOWN 17 +#define BLE_HS_EROLE 18 +#define BLE_HS_ETIMEOUT_HCI 19 +#define BLE_HS_ENOMEM_EVT 20 +#define BLE_HS_ENOADDR 21 +#define BLE_HS_ENOTSYNCED 22 +#define BLE_HS_EAUTHEN 23 +#define BLE_HS_EAUTHOR 24 +#define BLE_HS_EENCRYPT 25 +#define BLE_HS_EENCRYPT_KEY_SZ 26 +#define BLE_HS_ESTORE_CAP 27 +#define BLE_HS_ESTORE_FAIL 28 +#define BLE_HS_EPREEMPTED 29 +#define BLE_HS_EDISABLED 30 +#define BLE_HS_ESTALLED 31 + +/** Error base for ATT errors */ +#define BLE_HS_ERR_ATT_BASE 0x100 + +/** Converts error to ATT base */ +#define BLE_HS_ATT_ERR(x) ((x) ? BLE_HS_ERR_ATT_BASE + (x) : 0) + +/** Error base for HCI errors */ +#define BLE_HS_ERR_HCI_BASE 0x200 + +/** Converts error to HCI base */ +#define BLE_HS_HCI_ERR(x) ((x) ? BLE_HS_ERR_HCI_BASE + (x) : 0) + +/** Error base for L2CAP errors */ +#define BLE_HS_ERR_L2C_BASE 0x300 + +/** Converts error to L2CAP base */ +#define BLE_HS_L2C_ERR(x) ((x) ? BLE_HS_ERR_L2C_BASE + (x) : 0) + +/** Error base for local Security Manager errors */ +#define BLE_HS_ERR_SM_US_BASE 0x400 + +/** Converts error to local Security Manager base */ +#define BLE_HS_SM_US_ERR(x) ((x) ? BLE_HS_ERR_SM_US_BASE + (x) : 0) + +/** Error base for remote (peer) Security Manager errors */ +#define BLE_HS_ERR_SM_PEER_BASE 0x500 + +/** Converts error to remote (peer) Security Manager base */ +#define BLE_HS_SM_PEER_ERR(x) ((x) ? BLE_HS_ERR_SM_PEER_BASE + (x) : 0) + +/** Error base for hardware errors */ +#define BLE_HS_ERR_HW_BASE 0x600 + +/** Converts error to hardware error base */ +#define BLE_HS_HW_ERR(x) (BLE_HS_ERR_HW_BASE + (x)) + +/** + * @} + */ + +/** + * @brief Bluetooth Host Configuration + * @defgroup bt_host_conf Bluetooth Host Configuration + * + * @{ + */ + +/** + * @brief Local Input-Output capabilities of device + * @defgroup bt_host_io_local Local Input-Output capabilities of device + * + * @{ + */ + +/** DisplayOnly IO capability */ +#define BLE_HS_IO_DISPLAY_ONLY 0x00 + +/** DisplayYesNo IO capability */ +#define BLE_HS_IO_DISPLAY_YESNO 0x01 + +/** KeyboardOnly IO capability */ +#define BLE_HS_IO_KEYBOARD_ONLY 0x02 + +/** NoInputNoOutput IO capability */ +#define BLE_HS_IO_NO_INPUT_OUTPUT 0x03 + +/** KeyboardDisplay Only IO capability */ +#define BLE_HS_IO_KEYBOARD_DISPLAY 0x04 + +/** + * @} + */ + +/** @brief Stack reset callback + * + * @param reason Reason code for reset + */ +typedef void ble_hs_reset_fn(int reason); + + +/** @brief Stack sync callback */ +typedef void ble_hs_sync_fn(void); + +/** @brief Bluetooth Host main configuration structure + * + * Those can be used by application to configure stack. + * + * The only reason Security Manager (sm_ members) is configurable at runtime is + * to simplify security testing. Defaults for those are configured by selecting + * proper options in application's syscfg. + */ +struct ble_hs_cfg { + /** + * An optional callback that gets executed upon registration of each GATT + * resource (service, characteristic, or descriptor). + */ + ble_gatt_register_fn *gatts_register_cb; + + /** + * An optional argument that gets passed to the GATT registration + * callback. + */ + void *gatts_register_arg; + + /** Security Manager Local Input Output Capabilities */ + uint8_t sm_io_cap; + + /** @brief Security Manager OOB flag + * + * If set proper flag in Pairing Request/Response will be set. + */ + unsigned sm_oob_data_flag:1; + + /** @brief Security Manager Bond flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in storing keys distributed during bonding. + */ + unsigned sm_bonding:1; + + /** @brief Security Manager MITM flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in requiring Man-In-The-Middle protection when pairing. + */ + unsigned sm_mitm:1; + + /** @brief Security Manager Secure Connections flag + * + * If set proper flag in Pairing Request/Response will be set. This results + * in using LE Secure Connections for pairing if also supported by remote + * device. Fallback to legacy pairing if not supported by remote. + */ + unsigned sm_sc:1; + + /** @brief Security Manager Key Press Notification flag + * + * Currently unsupported and should not be set. + */ + unsigned sm_keypress:1; + + /** @brief Security Manager Local Key Distribution Mask */ + uint8_t sm_our_key_dist; + + /** @brief Security Manager Remote Key Distribution Mask */ + uint8_t sm_their_key_dist; + + /** @brief Stack reset callback + * + * This callback is executed when the host resets itself and the controller + * due to fatal error. + */ + ble_hs_reset_fn *reset_cb; + + /** @brief Stack sync callback + * + * This callback is executed when the host and controller become synced. + * This happens at startup and after a reset. + */ + ble_hs_sync_fn *sync_cb; + + /* XXX: These need to go away. Instead, the nimble host package should + * require the host-store API (not yet implemented).. + */ + /** Storage Read callback handles read of security material */ + ble_store_read_fn *store_read_cb; + + /** Storage Write callback handles write of security material */ + ble_store_write_fn *store_write_cb; + + /** Storage Delete callback handles deletion of security material */ + ble_store_delete_fn *store_delete_cb; + + /** @brief Storage Status callback. + * + * This callback gets executed when a persistence operation cannot be + * performed or a persistence failure is imminent. For example, if is + * insufficient storage capacity for a record to be persisted, this + * function gets called to give the application the opportunity to make + * room. + */ + ble_store_status_fn *store_status_cb; + + /** An optional argument that gets passed to the storage status callback. */ + void *store_status_arg; +}; + +extern struct ble_hs_cfg ble_hs_cfg; + +/** + * @} + */ + +/** + * @brief Indicates whether the host is enabled. The host is enabled if it is + * starting or fully started. It is disabled if it is stopping or stopped. + * + * @return 1 if the host is enabled; + * 0 if the host is disabled. + */ +int ble_hs_is_enabled(void); + +/** + * Indicates whether the host has synchronized with the controller. + * Synchronization must occur before any host procedures can be performed. + * + * @return 1 if the host and controller are in sync; + * 0 if the host and controller are out of sync. + */ +int ble_hs_synced(void); + +/** + * Synchronizes the host with the controller by sending a sequence of HCI + * commands. This function must be called before any other host functionality + * is used, but it must be called after both the host and controller are + * initialized. Typically, the host-parent-task calls this function at the top + * of its task routine. This function must only be called in the host parent + * task. A safe alternative for starting the stack from any task is to call + * `ble_hs_sched_start()`. + * + * If the host fails to synchronize with the controller (if the controller is + * not fully booted, for example), the host will attempt to resynchronize every + * 100 ms. For this reason, an error return code is not necessarily fatal. + * + * @return 0 on success; nonzero on error. + */ +int ble_hs_start(void); + +/** + * Enqueues a host start event to the default event queue. The actual host + * startup is performed in the host parent task, but using the default queue + * here ensures the event won't run until the end of main() when this is + * called during system initialization. This allows the application to + * configure the host package in the meantime. + * + * If auto-start is disabled, the application should use this function to start + * the BLE stack. This function can be called at any time as long as the host + * is stopped. When the host successfully starts, the application is notified + * via the ble_hs_cfg.sync_cb callback. + */ +void ble_hs_sched_start(void); + +/** + * Causes the host to reset the NimBLE stack as soon as possible. The + * application is notified when the reset occurs via the host reset callback. + * + * @param reason The host error code that gets passed to the reset callback. + */ +void ble_hs_sched_reset(int reason); + +/** + * Designates the specified event queue for NimBLE host work. By default, the + * host uses the default event queue and runs in the main task. This function + * is useful if you want the host to run in a different task. + * + * @param evq The event queue to use for host work. + */ +void ble_hs_evq_set(struct ble_npl_eventq *evq); + +/** + * Initializes the NimBLE host. This function must be called before the OS is + * started. The NimBLE stack requires an application task to function. One + * application task in particular is designated as the "host parent task". In + * addition to application-specific work, the host parent task does work for + * NimBLE by processing events generated by the host. + */ +void ble_hs_init(void); + +/** + * Deinitializes the NimBLE host. This function must be called after the + * NimBLE host stop procedure is complete. + */ +void ble_hs_deinit(void); + +/** + * @brief Called when the system is shutting down. Stops the BLE host. + * + * @param reason The reason for the shutdown. One of the + * HAL_RESET_[...] codes or an + * implementation-defined value. + * + * @return SYSDOWN_IN_PROGRESS. + */ +int ble_hs_shutdown(int reason); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_adv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_adv.h new file mode 100644 index 0000000..ae29653 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_adv.h @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ADV_ +#define H_BLE_HS_ADV_ + +#include +#include "ble_uuid.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_HS_ADV_MAX_SZ BLE_HCI_MAX_ADV_DATA_LEN + +/** Max field payload size (account for 2-byte header). */ +#define BLE_HS_ADV_MAX_FIELD_SZ (BLE_HS_ADV_MAX_SZ - 2) + +struct ble_hs_adv_field { + uint8_t length; + uint8_t type; + uint8_t value[0]; +}; + +typedef int (* ble_hs_adv_parse_func_t) (const struct ble_hs_adv_field *, + void *); + +struct ble_hs_adv_fields { + /*** 0x01 - Flags. */ + uint8_t flags; + + /*** 0x02,0x03 - 16-bit service class UUIDs. */ + const ble_uuid16_t *uuids16; + uint8_t num_uuids16; + unsigned uuids16_is_complete:1; + + /*** 0x04,0x05 - 32-bit service class UUIDs. */ + const ble_uuid32_t *uuids32; + uint8_t num_uuids32; + unsigned uuids32_is_complete:1; + + /*** 0x06,0x07 - 128-bit service class UUIDs. */ + const ble_uuid128_t *uuids128; + uint8_t num_uuids128; + unsigned uuids128_is_complete:1; + + /*** 0x08,0x09 - Local name. */ + const uint8_t *name; + uint8_t name_len; + unsigned name_is_complete:1; + + /*** 0x0a - Tx power level. */ + int8_t tx_pwr_lvl; + unsigned tx_pwr_lvl_is_present:1; + + /*** 0x0d - Slave connection interval range. */ + const uint8_t *slave_itvl_range; + + /*** 0x16 - Service data - 16-bit UUID. */ + const uint8_t *svc_data_uuid16; + uint8_t svc_data_uuid16_len; + + /*** 0x17 - Public target address. */ + const uint8_t *public_tgt_addr; + uint8_t num_public_tgt_addrs; + + /*** 0x19 - Appearance. */ + uint16_t appearance; + unsigned appearance_is_present:1; + + /*** 0x1a - Advertising interval. */ + uint16_t adv_itvl; + unsigned adv_itvl_is_present:1; + + /*** 0x20 - Service data - 32-bit UUID. */ + const uint8_t *svc_data_uuid32; + uint8_t svc_data_uuid32_len; + + /*** 0x21 - Service data - 128-bit UUID. */ + const uint8_t *svc_data_uuid128; + uint8_t svc_data_uuid128_len; + + /*** 0x24 - URI. */ + const uint8_t *uri; + uint8_t uri_len; + + /*** 0xff - Manufacturer specific data. */ + const uint8_t *mfg_data; + uint8_t mfg_data_len; +}; + +#define BLE_HS_ADV_TYPE_FLAGS 0x01 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS16 0x02 +#define BLE_HS_ADV_TYPE_COMP_UUIDS16 0x03 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS32 0x04 +#define BLE_HS_ADV_TYPE_COMP_UUIDS32 0x05 +#define BLE_HS_ADV_TYPE_INCOMP_UUIDS128 0x06 +#define BLE_HS_ADV_TYPE_COMP_UUIDS128 0x07 +#define BLE_HS_ADV_TYPE_INCOMP_NAME 0x08 +#define BLE_HS_ADV_TYPE_COMP_NAME 0x09 +#define BLE_HS_ADV_TYPE_TX_PWR_LVL 0x0a +#define BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE 0x12 +#define BLE_HS_ADV_TYPE_SOL_UUIDS16 0x14 +#define BLE_HS_ADV_TYPE_SOL_UUIDS128 0x15 +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID16 0x16 +#define BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR 0x17 +#define BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR 0x18 +#define BLE_HS_ADV_TYPE_APPEARANCE 0x19 +#define BLE_HS_ADV_TYPE_ADV_ITVL 0x1a +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID32 0x20 +#define BLE_HS_ADV_TYPE_SVC_DATA_UUID128 0x21 +#define BLE_HS_ADV_TYPE_URI 0x24 +#define BLE_HS_ADV_TYPE_MESH_PROV 0x29 +#define BLE_HS_ADV_TYPE_MESH_MESSAGE 0x2a +#define BLE_HS_ADV_TYPE_MESH_BEACON 0x2b +#define BLE_HS_ADV_TYPE_MFG_DATA 0xff + +#define BLE_HS_ADV_FLAGS_LEN 1 +#define BLE_HS_ADV_F_DISC_LTD 0x01 +#define BLE_HS_ADV_F_DISC_GEN 0x02 +#define BLE_HS_ADV_F_BREDR_UNSUP 0x04 + +#define BLE_HS_ADV_TX_PWR_LVL_LEN 1 + +/** + * Set the tx_pwr_lvl field to this if you want the stack to fill in the tx + * power level field. + */ +#define BLE_HS_ADV_TX_PWR_LVL_AUTO (-128) + +#define BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN 4 + +#define BLE_HS_ADV_SVC_DATA_UUID16_MIN_LEN 2 + +#define BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN 6 + +#define BLE_HS_ADV_APPEARANCE_LEN 2 + +#define BLE_HS_ADV_ADV_ITVL_LEN 2 + +#define BLE_HS_ADV_SVC_DATA_UUID32_MIN_LEN 4 + +#define BLE_HS_ADV_SVC_DATA_UUID128_MIN_LEN 16 + +int ble_hs_adv_set_fields_mbuf(const struct ble_hs_adv_fields *adv_fields, + struct os_mbuf *om); + +int ble_hs_adv_set_fields(const struct ble_hs_adv_fields *adv_fields, + uint8_t *dst, uint8_t *dst_len, uint8_t max_len); + +int ble_hs_adv_parse_fields(struct ble_hs_adv_fields *adv_fields, + const uint8_t *src, uint8_t src_len); + +int ble_hs_adv_parse(const uint8_t *data, uint8_t length, + ble_hs_adv_parse_func_t func, void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_hci.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_hci.h new file mode 100644 index 0000000..e10b8e6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_hci.h @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_HCI_ +#define H_BLE_HS_HCI_ + +/** + * @brief Bluetooth Host HCI utils + * @defgroup bt_host_hci Bluetooth Host HCI utils + * @ingroup bt_host + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Queries the controller for the channel map used with the specified + * connection. The channel map is represented as an array of five bytes, with + * each bit corresponding to an individual channel. The array is interpreted + * as little-endian, such that: + * map[0] & 0x01 --> Channel 0. + * map[0] & 0x02 --> Channel 1. + * ... + * map[1] & 0x01 --> Channel 8. + * + * As there are 37 channels, only the first 37 bits get written. + * + * If a bit is 1, the corresponding channel is used. Otherwise, the channel is + * unused. + * + * @param conn_handle The handle of the connection whose channel map + * is being read. + * @param out_chan_map On success, the retrieved channel map gets + * written here. This buffer must have a size + * >= 5 bytes. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_hs_hci_read_chan_map(uint16_t conn_handle, uint8_t *out_chan_map); + +/** + * Instructs the controller to use the specified channel map. The channel map + * is represented as an array of five bytes, with each bit corresponding to an + * individual channel. The array is interpreted as little-endian, such that: + * map[0] & 0x01 --> Channel 0. + * map[0] & 0x02 --> Channel 1. + * ... + * map[1] & 0x01 --> Channel 8. + * + * As there are 37 channels, only the first 37 bits should be written are used. + * + * If a bit is 1, the corresponding channel can be used. Otherwise, the + * channel should not be used. + * + * @param chan_map The channel map to configure. This buffer + * should have a size of 5 bytes. + * + * @return 0 on success; + * A BLE host HCI return code if the controller + * rejected the request; + * A BLE host core return code on unexpected + * error. + */ +int ble_hs_hci_set_chan_class(const uint8_t *chan_map); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_id.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_id.h new file mode 100644 index 0000000..5683034 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_id.h @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_ID_ +#define H_BLE_HS_ID_ + +/** + * @brief Bluetooth Host Identity + * @defgroup bt_host_id Bluetooth Host Identity + * @ingroup bt_host + * @{ + */ + +#include +#include "nimble/nimble/include/nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generates a new random address. This function does not configure the device + * with the new address; the caller can use the address in subsequent + * operations. + * + * @param nrpa The type of random address to generate: + * 0: static + * 1: non-resolvable private + * @param out_addr On success, the generated address gets written + * here. + * + * @return 0 on success; nonzero on failure. + */ +int ble_hs_id_gen_rnd(int nrpa, ble_addr_t *out_addr); + +/** + * Sets the device's random address. The address type (static vs. + * non-resolvable private) is inferred from the most-significant byte of the + * address. The address is specified in host byte order (little-endian!). + * + * @param rnd_addr The random address to set. + * + * @return 0 on success; + * BLE_HS_EINVAL if the specified address is not a + * valid static random or non-resolvable + * private address. + * Other nonzero on error. + */ +int ble_hs_id_set_rnd(const uint8_t *rnd_addr); + +/** + * Retrieves one of the device's identity addresses. The device can have two + * identity addresses: one public and one random. The id_addr_type argument + * specifies which of these two addresses to retrieve. + * + * @param id_addr_type The type of identity address to retrieve. + * Valid values are: + * o BLE_ADDR_PUBLIC + * o BLE_ADDR_RANDOM + * @param out_id_addr On success, the requested identity address is + * copied into this buffer. The buffer must + * be at least six bytes in size. Pass NULL + * if you do not require this information. + * @param out_is_nrpa On success, the pointed-to value indicates + * whether the retrieved address is a + * non-resolvable private address. Pass NULL + * if you do not require this information. + * + * @return 0 on success; + * BLE_HS_EINVAL if an invalid address type was + * specified; + * BLE_HS_ENOADDR if the device does not have an + * identity address of the requested type; + * Other BLE host core code on error. + */ +int ble_hs_id_copy_addr(uint8_t id_addr_type, uint8_t *out_id_addr, + int *out_is_nrpa); + +/** + * Determines the best address type to use for automatic address type + * resolution. Calculation of the best address type is done as follows: + * + * if privacy requested: + * if we have a random static address: + * --> RPA with static random ID + * else + * --> RPA with public ID + * end + * else + * if we have a random static address: + * --> random static address + * else + * --> public address + * end + * end + * + * @param privacy (0/1) Whether to use a private address. + * @param out_addr_type On success, the "own addr type" code gets + * written here. + * + * @return 0 if an address type was successfully inferred. + * BLE_HS_ENOADDR if the device does not have a + * suitable address. + * Other BLE host core code on error. + */ +int ble_hs_id_infer_auto(int privacy, uint8_t *out_addr_type); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_log.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_log.h new file mode 100644 index 0000000..303b53c --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_log.h @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_LOG_ +#define H_BLE_HS_LOG_ + +#include "nimble/porting/nimble/include/modlog/modlog.h" + +/* Only include the logcfg header if this version of newt can generate it. */ +#if MYNEWT_VAL(NEWT_FEATURE_LOGCFG) +#include "nimble/porting/nimble/include/logcfg/logcfg.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +#define BLE_HS_LOG(lvl, ...) \ + BLE_HS_LOG_ ## lvl(__VA_ARGS__) + +#define BLE_HS_LOG_ADDR(lvl, addr) \ + BLE_HS_LOG_ ## lvl("%02x:%02x:%02x:%02x:%02x:%02x", \ + (addr)[5], (addr)[4], (addr)[3], \ + (addr)[2], (addr)[1], (addr)[0]) + + +void ble_hs_log_mbuf(const struct os_mbuf *om); +void ble_hs_log_flat_buf(const void *data, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_mbuf.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_mbuf.h new file mode 100644 index 0000000..a3c2c02 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_mbuf.h @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_MBUF_ +#define H_BLE_HS_MBUF_ + +/** + * @brief Bluetooth Host chained memory buffer (mbuf) + * @defgroup bt_host_mbuf Bluetooth Host chained memory buffer (mbuf) + * @ingroup bt_host + * @{ + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +/** + * Allocates an mbuf suitable for an ATT command packet. The resulting packet + * has sufficient leading space for: + * - ACL data header + * - L2CAP B-frame header + * - Largest ATT command base (prepare write request / response). + * + * @return An empty mbuf on success, NULL on error. + */ +struct os_mbuf *ble_hs_mbuf_att_pkt(void); + +/** + * Allocates an mbuf and fills it with the contents of the specified flat + * buffer. + * + * @param buf The flat buffer to copy from. + * @param len The length of the flat buffer. + * + * @return A newly-allocated mbuf on success, NULL on error. + */ +struct os_mbuf *ble_hs_mbuf_from_flat(const void *buf, uint16_t len); + +/** + * Copies the contents of an mbuf into the specified flat buffer. If the flat + * buffer is too small to contain the mbuf's contents, it is filled to capacity + * and BLE_HS_EMSGSIZE is returned. + * + * @param om The mbuf to copy from. + * @param flat The destination flat buffer. + * @param max_len The size of the flat buffer. + * @param out_copy_len The number of bytes actually copied gets written here. + * + * @return 0 on success or BLE host core return code on error. + */ +int ble_hs_mbuf_to_flat(const struct os_mbuf *om, void *flat, uint16_t max_len, + uint16_t *out_copy_len); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_pvcy.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_pvcy.h new file mode 100644 index 0000000..26450f4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_pvcy.h @@ -0,0 +1,73 @@ +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_PVCY_ +#define H_BLE_HS_PVCY_ + +#include "ble_hs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + +#define NIMBLE_HOST_DISABLE_PRIVACY 0x00 +#define NIMBLE_HOST_ENABLE_RPA 0x01 +#define NIMBLE_HOST_ENABLE_NRPA 0x02 + +/* Called to configure local(own) privacy (RPA/NRPA) when using Host based privacy. + * In Host based privacy, as controller is not aware of RPA/NRPA address is in use, + * we do it through 'BLE_ADDR_RANDOM (0x01)' addr_type route. This is necessary + * so as to set the private address as random address in controller. + * Remember to configure `BLE_SM_PAIR_KEY_DIST_ID` in our & their + * key distributions for using RPA. For NRPA part of privacy it is not + * necessary to configure key distributions in host, as anyway NRPA is non-resolvable. + * Please call this API once host-controller are synced as we set the private + * (RPA/NRPA) address using host-controller HCI commands. + * + * To give brief information on how to use this feature, + * please refer to following steps while using RPA feature: + * + * 1. Include "host/ble_hs_pvcy.h". + * 2. Set own_addr_type to `BLE_OWN_ADDR_RANDOM`. + * 3. Add `BLE_SM_PAIR_KEY_DIST_ID` to key distribution in + * `ble_hs_cfg.sm_our_key_dist` & `ble_hs_cfg.sm_their_key_dist`. + * 4. Call `ble_hs_pvcy_rpa_config(1)` in Host-Controller sync callback. + * + * In case of NRPA, steps 1, 2 and calling ble_hs_pvcy_rpa_config(2) will + * suffice. + * + * @param enable RPA when param = 1 (NIMBLE_HOST_ENABLE_RPA) + * enable NRPA when param = 2 (NIMBLE_HOST_ENABLE_NRPA) + * disable privacy when param = 0 (NIMBLE_HOST_DISABLE_PRIVACY) + * + * @return return 0 when successful. + * return appropriate error code otherwise + */ +int ble_hs_pvcy_rpa_config(uint8_t enable); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_stop.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_stop.h new file mode 100644 index 0000000..d16c9c2 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_hs_stop.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HS_STOP_ +#define H_BLE_HS_STOP_ + +/** @typedef ble_hs_stop_fn + * @brief Callback function; reports the result of a host stop procedure. + * + * @param status The result of the host stop procedure. One of + * the HAL_RESET_[...] codes or an + * implementation-defined value. + * @param arg Optional argument specified when the stop + * procedure was initiated. + * + */ +typedef void ble_hs_stop_fn(int status, void *arg); + +/** + * @brief Used to report the result of a stop procedure. + * + * This should be used as an opaque structure and not modified manually. + */ +struct ble_hs_stop_listener { + ble_hs_stop_fn *fn; + void *arg; + SLIST_ENTRY(ble_hs_stop_listener) link; +}; + +/** + * @brief Stops the BLE host. + * + * Aborts all active GAP procedures and terminates all open connections. + * Connection termination is performed asynchronously, so this function's + * result is reported via the provided listener. + * + * @param listener A listener to populate. This object's initial + * value doesn't matter, but its lifetime must + * extend until the stop procedure completes. + * @param fn The callback to execute when the stop procedure + * completes. + * @param arg Optional argument to pass to the callback. + * + * @return 0: Stop procedure successfully initiated. + * BLE_HS_EBUSY: Stop procedure already in + * progress; the provided callback gets called + * when the procedure completes. + * BLE_HS_EALREADY: Host already stopped; the + * provided callback does *not* get called. + */ +int ble_hs_stop(struct ble_hs_stop_listener *listener, + ble_hs_stop_fn *fn, void *arg); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_ibeacon.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_ibeacon.h new file mode 100644 index 0000000..fff7c57 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_ibeacon.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_IBEACON_ +#define H_BLE_IBEACON_ + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_ibeacon_set_adv_data(void *uuid128, uint16_t major, + uint16_t minor, int8_t measured_power); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_l2cap.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_l2cap.h new file mode 100644 index 0000000..9d92c08 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_l2cap.h @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_L2CAP_ +#define H_BLE_L2CAP_ + +#include "nimble/nimble/include/nimble/nimble_opt.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_l2cap_sig_update_req; +struct ble_hs_conn; + +#define BLE_L2CAP_CID_ATT 4 +#define BLE_L2CAP_CID_SIG 5 +#define BLE_L2CAP_CID_SM 6 + +#define BLE_L2CAP_SIG_OP_REJECT 0x01 +#define BLE_L2CAP_SIG_OP_CONNECT_REQ 0x02 +#define BLE_L2CAP_SIG_OP_CONNECT_RSP 0x03 +#define BLE_L2CAP_SIG_OP_CONFIG_REQ 0x04 +#define BLE_L2CAP_SIG_OP_CONFIG_RSP 0x05 +#define BLE_L2CAP_SIG_OP_DISCONN_REQ 0x06 +#define BLE_L2CAP_SIG_OP_DISCONN_RSP 0x07 +#define BLE_L2CAP_SIG_OP_ECHO_REQ 0x08 +#define BLE_L2CAP_SIG_OP_ECHO_RSP 0x09 +#define BLE_L2CAP_SIG_OP_INFO_REQ 0x0a +#define BLE_L2CAP_SIG_OP_INFO_RSP 0x0b +#define BLE_L2CAP_SIG_OP_CREATE_CHAN_REQ 0x0c +#define BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP 0x0d +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_REQ 0x0e +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP 0x0f +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_REQ 0x10 +#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP 0x11 +#define BLE_L2CAP_SIG_OP_UPDATE_REQ 0x12 +#define BLE_L2CAP_SIG_OP_UPDATE_RSP 0x13 +#define BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ 0x14 +#define BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_RSP 0x15 +#define BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT 0x16 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ 0x17 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP 0x18 +#define BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ 0x19 +#define BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP 0x1A +#define BLE_L2CAP_SIG_OP_MAX 0x1B + +#define BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD 0x0000 +#define BLE_L2CAP_SIG_ERR_MTU_EXCEEDED 0x0001 +#define BLE_L2CAP_SIG_ERR_INVALID_CID 0x0002 + +#define BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS 0x0000 +#define BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM 0x0002 +#define BLE_L2CAP_COC_ERR_NO_RESOURCES 0x0004 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN 0x0005 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR 0x0006 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ 0x0007 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC 0x0008 +#define BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID 0x0009 +#define BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED 0x000A +#define BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS 0x000B +#define BLE_L2CAP_COC_ERR_INVALID_PARAMETERS 0x000C + +#define BLE_L2CAP_ERR_RECONFIG_SUCCEED 0x0000 +#define BLE_L2CAP_ERR_RECONFIG_REDUCTION_MTU_NOT_ALLOWED 0x0001 +#define BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED 0x0002 +#define BLE_L2CAP_ERR_RECONFIG_INVALID_DCID 0x0003 +#define BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM 0x0004 + +#define BLE_L2CAP_EVENT_COC_CONNECTED 0 +#define BLE_L2CAP_EVENT_COC_DISCONNECTED 1 +#define BLE_L2CAP_EVENT_COC_ACCEPT 2 +#define BLE_L2CAP_EVENT_COC_DATA_RECEIVED 3 +#define BLE_L2CAP_EVENT_COC_TX_UNSTALLED 4 +#define BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED 5 +#define BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED 6 + +typedef void ble_l2cap_sig_update_fn(uint16_t conn_handle, int status, + void *arg); + +struct ble_l2cap_sig_update_params { + uint16_t itvl_min; + uint16_t itvl_max; + uint16_t slave_latency; + uint16_t timeout_multiplier; +}; + +int ble_l2cap_sig_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params, + ble_l2cap_sig_update_fn *cb, void *cb_arg); + +struct ble_l2cap_chan; + +/** + * Represents a L2CAP-related event. + * When such an event occurs, the host notifies the application by passing an + * instance of this structure to an application-specified callback. + */ +struct ble_l2cap_event { + /** + * Indicates the type of L2CAP event that occurred. This is one of the + * BLE_L2CAP_EVENT codes. + */ + uint8_t type; + + /** + * A discriminated union containing additional details concerning the L2CAP + * event. The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents a connection attempt. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_CONNECTED */ + struct { + /** + * The status of the connection attempt; + * o 0: the connection was successfully established. + * o BLE host error code: the connection attempt failed for + * the specified reason. + */ + int status; + + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } connect; + + /** + * Represents a terminated connection. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_DISCONNECTED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** Information about the L2CAP connection prior to termination. */ + struct ble_l2cap_chan *chan; + } disconnect; + + /** + * Represents connection accept. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_ACCEPT + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** MTU supported by peer device on the channel */ + uint16_t peer_sdu_size; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } accept; + + /** + * Represents received data. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_DATA_RECEIVED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + + /** The mbuf with received SDU. */ + struct os_mbuf *sdu_rx; + } receive; + + /** + * Represents tx_unstalled data. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_TX_UNSTALLED + */ + struct { + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + + /** + * The status of the send attempt which was stalled due to + * lack of credits; This can be non zero only if there + * is an issue with memory allocation for following SDU fragments. + * In such a case last SDU has been partially sent to peer device + * and it is up to application to decide how to handle it. + */ + int status; + } tx_unstalled; + + /** + * Represents reconfiguration done. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED + * o BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED + */ + struct { + /** + * The status of the reconfiguration attempt; + * o 0: the reconfiguration was successfully done. + * o BLE host error code: the reconfiguration attempt failed for + * the specified reason. + */ + int status; + + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } reconfigured; + }; +}; + +struct ble_l2cap_chan_info { + uint16_t scid; + uint16_t dcid; + uint16_t our_l2cap_mtu; + uint16_t peer_l2cap_mtu; + uint16_t psm; + uint16_t our_coc_mtu; + uint16_t peer_coc_mtu; +}; + +typedef int ble_l2cap_event_fn(struct ble_l2cap_event *event, void *arg); + + +uint16_t ble_l2cap_get_conn_handle(struct ble_l2cap_chan *chan); +int ble_l2cap_create_server(uint16_t psm, uint16_t mtu, + ble_l2cap_event_fn *cb, void *cb_arg); + +int ble_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, + struct os_mbuf *sdu_rx, + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_disconnect(struct ble_l2cap_chan *chan); +int ble_l2cap_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx); +int ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx); +int ble_l2cap_get_chan_info(struct ble_l2cap_chan *chan, struct ble_l2cap_chan_info *chan_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_monitor.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_monitor.h new file mode 100644 index 0000000..418d4e3 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_monitor.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_MONITOR_ +#define H_BLE_MONITOR_ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#undef BLE_MONITOR +#define BLE_MONITOR (MYNEWT_VAL(BLE_MONITOR_UART) || MYNEWT_VAL(BLE_MONITOR_RTT)) + +#ifdef __cplusplus +extern "C" { +#endif + +int ble_monitor_log(int level, const char *fmt, ...); + +int ble_monitor_out(int c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_sm.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_sm.h new file mode 100644 index 0000000..42414d5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_sm.h @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_SM_ +#define H_BLE_SM_ + +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_SM_ERR_PASSKEY 0x01 +#define BLE_SM_ERR_OOB 0x02 +#define BLE_SM_ERR_AUTHREQ 0x03 +#define BLE_SM_ERR_CONFIRM_MISMATCH 0x04 +#define BLE_SM_ERR_PAIR_NOT_SUPP 0x05 +#define BLE_SM_ERR_ENC_KEY_SZ 0x06 +#define BLE_SM_ERR_CMD_NOT_SUPP 0x07 +#define BLE_SM_ERR_UNSPECIFIED 0x08 +#define BLE_SM_ERR_REPEATED 0x09 +#define BLE_SM_ERR_INVAL 0x0a +#define BLE_SM_ERR_DHKEY 0x0b +#define BLE_SM_ERR_NUMCMP 0x0c +#define BLE_SM_ERR_ALREADY 0x0d +#define BLE_SM_ERR_CROSS_TRANS 0x0e +#define BLE_SM_ERR_MAX_PLUS_1 0x0f + +#define BLE_SM_PAIR_ALG_JW 0 +#define BLE_SM_PAIR_ALG_PASSKEY 1 +#define BLE_SM_PAIR_ALG_OOB 2 +#define BLE_SM_PAIR_ALG_NUMCMP 3 + +#define BLE_SM_PAIR_KEY_DIST_ENC 0x01 +#define BLE_SM_PAIR_KEY_DIST_ID 0x02 +#define BLE_SM_PAIR_KEY_DIST_SIGN 0x04 +#define BLE_SM_PAIR_KEY_DIST_LINK 0x08 +#define BLE_SM_PAIR_KEY_DIST_RESERVED 0xf0 + +#define BLE_SM_IO_CAP_DISP_ONLY 0x00 +#define BLE_SM_IO_CAP_DISP_YES_NO 0x01 +#define BLE_SM_IO_CAP_KEYBOARD_ONLY 0x02 +#define BLE_SM_IO_CAP_NO_IO 0x03 +#define BLE_SM_IO_CAP_KEYBOARD_DISP 0x04 +#define BLE_SM_IO_CAP_RESERVED 0x05 + +#define BLE_SM_PAIR_OOB_NO 0x00 +#define BLE_SM_PAIR_OOB_YES 0x01 +#define BLE_SM_PAIR_OOB_RESERVED 0x02 + +#define BLE_SM_PAIR_AUTHREQ_BOND 0x01 +#define BLE_SM_PAIR_AUTHREQ_MITM 0x04 +#define BLE_SM_PAIR_AUTHREQ_SC 0x08 +#define BLE_SM_PAIR_AUTHREQ_KEYPRESS 0x10 +#define BLE_SM_PAIR_AUTHREQ_RESERVED 0xe2 + +#define BLE_SM_PAIR_KEY_SZ_MIN 7 +#define BLE_SM_PAIR_KEY_SZ_MAX 16 + +/* + * The security manager asks the application to perform a key generation + * action. The application passes the passkey back to SM via + * ble_sm_inject_io(). + */ +#define BLE_SM_IOACT_NONE 0 +#define BLE_SM_IOACT_OOB 1 +#define BLE_SM_IOACT_INPUT 2 +#define BLE_SM_IOACT_DISP 3 +#define BLE_SM_IOACT_NUMCMP 4 +#define BLE_SM_IOACT_OOB_SC 5 +#define BLE_SM_IOACT_MAX_PLUS_ONE 6 + +struct ble_sm_sc_oob_data { + /** Random Number. */ + uint8_t r[16]; + + /** Confirm Value. */ + uint8_t c[16]; +}; + +struct ble_sm_io { + uint8_t action; + union { + uint32_t passkey; + uint8_t oob[16]; + uint8_t numcmp_accept; + struct { + struct ble_sm_sc_oob_data *local; + struct ble_sm_sc_oob_data *remote; + } oob_sc_data; + }; +}; + +int ble_sm_sc_oob_generate_data(struct ble_sm_sc_oob_data *oob_data); + +#if NIMBLE_BLE_SM +int ble_sm_inject_io(uint16_t conn_handle, struct ble_sm_io *pkey); +#else +#define ble_sm_inject_io(conn_handle, pkey) \ + ((void)(conn_handle), BLE_HS_ENOTSUP) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_store.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_store.h new file mode 100644 index 0000000..8e6721b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_store.h @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_STORE_ +#define H_BLE_STORE_ + +#include +#include "nimble/nimble/include/nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_STORE_OBJ_TYPE_OUR_SEC 1 +#define BLE_STORE_OBJ_TYPE_PEER_SEC 2 +#define BLE_STORE_OBJ_TYPE_CCCD 3 +#define BLE_STORE_OBJ_TYPE_PEER_DEV_REC 4 + +/** Failed to persist record; insufficient storage capacity. */ +#define BLE_STORE_EVENT_OVERFLOW 1 + +/** About to execute a procedure that may fail due to overflow. */ +#define BLE_STORE_EVENT_FULL 2 + +/** + * Used as a key for lookups of security material. This struct corresponds to + * the following store object types: + * o BLE_STORE_OBJ_TYPE_OUR_SEC + * o BLE_STORE_OBJ_TYPE_PEER_SEC + */ +struct ble_store_key_sec { + /** + * Key by peer identity address; + * peer_addr=BLE_ADDR_NONE means don't key off peer. + */ + ble_addr_t peer_addr; + + /** Key by ediv; ediv_rand_present=0 means don't key off ediv. */ + uint16_t ediv; + + /** Key by rand_num; ediv_rand_present=0 means don't key off rand_num. */ + uint64_t rand_num; + + unsigned ediv_rand_present:1; + + /** Number of results to skip; 0 means retrieve the first match. */ + uint8_t idx; +}; + +/** + * Represents stored security material. This struct corresponds to the + * following store object types: + * o BLE_STORE_OBJ_TYPE_OUR_SEC + * o BLE_STORE_OBJ_TYPE_PEER_SEC + */ +struct ble_store_value_sec { + ble_addr_t peer_addr; + + uint8_t key_size; + uint16_t ediv; + uint64_t rand_num; + uint8_t ltk[16]; + uint8_t ltk_present:1; + + uint8_t irk[16]; + uint8_t irk_present:1; + + uint8_t csrk[16]; + uint8_t csrk_present:1; + + unsigned authenticated:1; + uint8_t sc:1; +}; + +/** + * Used as a key for lookups of stored client characteristic configuration + * descriptors (CCCDs). This struct corresponds to the BLE_STORE_OBJ_TYPE_CCCD + * store object type. + */ +struct ble_store_key_cccd { + /** + * Key by peer identity address; + * peer_addr=BLE_ADDR_NONE means don't key off peer. + */ + ble_addr_t peer_addr; + + /** + * Key by characteristic value handle; + * chr_val_handle=0 means don't key off characteristic handle. + */ + uint16_t chr_val_handle; + + /** Number of results to skip; 0 means retrieve the first match. */ + uint8_t idx; +}; + +/** + * Represents a stored client characteristic configuration descriptor (CCCD). + * This struct corresponds to the BLE_STORE_OBJ_TYPE_CCCD store object type. + */ +struct ble_store_value_cccd { + ble_addr_t peer_addr; + uint16_t chr_val_handle; + uint16_t flags; + unsigned value_changed:1; +}; + +/** + * Used as a key for store lookups. This union must be accompanied by an + * object type code to indicate which field is valid. + */ +union ble_store_key { + struct ble_store_key_sec sec; + struct ble_store_key_cccd cccd; +}; + +/** + * Represents stored data. This union must be accompanied by an object type + * code to indicate which field is valid. + */ +union ble_store_value { + struct ble_store_value_sec sec; + struct ble_store_value_cccd cccd; +}; + +struct ble_store_status_event { + /** + * The type of event being reported; one of the BLE_STORE_EVENT_TYPE_[...] + * codes. + */ + int event_code; + + /** + * Additional data related to the event; the valid field is inferred from + * the obj_type,event_code pair. + */ + union { + /** + * Represents a write that failed due to storage exhaustion. Valid for + * the following event types: + * o BLE_STORE_EVENT_OVERFLOW + */ + struct { + /** The type of object that failed to be written. */ + int obj_type; + + /** The object that failed to be written. */ + const union ble_store_value *value; + } overflow; + + /** + * Represents the possibility that a scheduled write will fail due to + * storage exhaustion. Valid for the following event types: + * o BLE_STORE_EVENT_FULL + */ + struct { + /** The type of object that may fail to be written. */ + int obj_type; + + /** The handle of the connection which prompted the write. */ + uint16_t conn_handle; + } full; + }; +}; + +/** + * Searches the store for an object matching the specified criteria. If a + * match is found, it is read from the store and the dst parameter is populated + * with the retrieved object. + * + * @param obj_type The type of object to search for; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param key Specifies properties of the object to search + * for. An object is retrieved if it matches + * these criteria. + * @param dst On success, this is populated with the + * retrieved object. + * + * @return 0 if an object was successfully retreived; + * BLE_HS_ENOENT if no matching object was found; + * Other nonzero on error. + */ +typedef int ble_store_read_fn(int obj_type, const union ble_store_key *key, + union ble_store_value *dst); + +/** + * Writes the specified object to the store. If an object with the same + * identity is already in the store, it is replaced. If the store lacks + * sufficient capacity to write the object, this function may remove previously + * stored values to make room. + * + * @param obj_type The type of object being written; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param val The object to persist. + * + * @return 0 if the object was successfully written; + * Other nonzero on error. + */ +typedef int ble_store_write_fn(int obj_type, const union ble_store_value *val); + +/** + * Searches the store for the first object matching the specified criteria. If + * a match is found, it is deleted from the store. + * + * @param obj_type The type of object to delete; one of the + * BLE_STORE_OBJ_TYPE_[...] codes. + * @param key Specifies properties of the object to search + * for. An object is deleted if it matches + * these criteria. + * @return 0 if an object was successfully retrieved; + * BLE_HS_ENOENT if no matching object was found; + * Other nonzero on error. + */ +typedef int ble_store_delete_fn(int obj_type, const union ble_store_key *key); + +/** + * Indicates an inability to perform a store operation. This callback should + * do one of two things: + * o Address the problem and return 0, indicating that the store operation + * should proceed. + * o Return nonzero to indicate that the store operation should be aborted. + * + * @param event Describes the store event being reported. + * @param arg Optional user argument. + * + * @return 0 if the store operation should proceed; + * nonzero if the store operation should be + * aborted. + */ +typedef int ble_store_status_fn(struct ble_store_status_event *event, + void *arg); + +int ble_store_read(int obj_type, const union ble_store_key *key, + union ble_store_value *val); +int ble_store_write(int obj_type, const union ble_store_value *val); +int ble_store_delete(int obj_type, const union ble_store_key *key); +int ble_store_overflow_event(int obj_type, const union ble_store_value *value); +int ble_store_full_event(int obj_type, uint16_t conn_handle); + +int ble_store_read_our_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec); +int ble_store_write_our_sec(const struct ble_store_value_sec *value_sec); +int ble_store_delete_our_sec(const struct ble_store_key_sec *key_sec); +int ble_store_read_peer_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec); +int ble_store_write_peer_sec(const struct ble_store_value_sec *value_sec); +int ble_store_delete_peer_sec(const struct ble_store_key_sec *key_sec); + +int ble_store_read_cccd(const struct ble_store_key_cccd *key, + struct ble_store_value_cccd *out_value); +int ble_store_write_cccd(const struct ble_store_value_cccd *value); +int ble_store_delete_cccd(const struct ble_store_key_cccd *key); + +void ble_store_key_from_value_sec(struct ble_store_key_sec *out_key, + const struct ble_store_value_sec *value); +void ble_store_key_from_value_cccd(struct ble_store_key_cccd *out_key, + const struct ble_store_value_cccd *value); + +void ble_store_key_from_value(int obj_type, + union ble_store_key *out_key, + const union ble_store_value *value); + +typedef int ble_store_iterator_fn(int obj_type, + union ble_store_value *val, + void *cookie); + +int ble_store_iterate(int obj_type, + ble_store_iterator_fn *callback, + void *cookie); + +int ble_store_clear(void); + +/*** Utility functions. */ + +int ble_store_util_bonded_peers(ble_addr_t *out_peer_id_addrs, + int *out_num_peers, + int max_peers); +int ble_store_util_delete_all(int type, const union ble_store_key *key); +int ble_store_util_delete_peer(const ble_addr_t *peer_id_addr); +int ble_store_util_delete_oldest_peer(void); +int ble_store_util_count(int type, int *out_count); +int ble_store_util_status_rr(struct ble_store_status_event *event, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_uuid.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_uuid.h new file mode 100644 index 0000000..d3576c5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/include/host/ble_uuid.h @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_UUID_ +#define H_BLE_UUID_ + +/** + * @brief Bluetooth UUID + * @defgroup bt_uuid Bluetooth UUID + * @ingroup bt_host + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_mbuf; + +/** Type of UUID */ +enum { + /** 16-bit UUID (BT SIG assigned) */ + BLE_UUID_TYPE_16 = 16, + + /** 32-bit UUID (BT SIG assigned) */ + BLE_UUID_TYPE_32 = 32, + + /** 128-bit UUID */ + BLE_UUID_TYPE_128 = 128, +}; + +/** Generic UUID type, to be used only as a pointer */ +typedef struct { + /** Type of the UUID */ + uint8_t type; +} ble_uuid_t; + +/** 16-bit UUID */ +typedef struct { + ble_uuid_t u; + uint16_t value; +} ble_uuid16_t; + +/** 32-bit UUID */ +typedef struct { + ble_uuid_t u; + uint32_t value; +} ble_uuid32_t; + +/** 128-bit UUID */ +typedef struct { + ble_uuid_t u; + uint8_t value[16]; +} ble_uuid128_t; + +/** Universal UUID type, to be used for any-UUID static allocation */ +typedef union { + ble_uuid_t u; + ble_uuid16_t u16; + ble_uuid32_t u32; + ble_uuid128_t u128; +} ble_uuid_any_t; + +#define BLE_UUID16_INIT(uuid16) \ + { \ + .u.type = BLE_UUID_TYPE_16, \ + .value = (uuid16), \ + } + +#define BLE_UUID32_INIT(uuid32) \ + { \ + .u.type = BLE_UUID_TYPE_32, \ + .value = (uuid32), \ + } + +#define BLE_UUID128_INIT(uuid128...) \ + { \ + .u.type = BLE_UUID_TYPE_128, \ + .value = { uuid128 }, \ + } + +#define BLE_UUID16_DECLARE(uuid16) \ + ((ble_uuid_t *) (&(ble_uuid16_t) BLE_UUID16_INIT(uuid16))) + +#define BLE_UUID32_DECLARE(uuid32) \ + ((ble_uuid_t *) (&(ble_uuid32_t) BLE_UUID32_INIT(uuid32))) + +#define BLE_UUID128_DECLARE(uuid128...) \ + ((ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT(uuid128))) + +#define BLE_UUID16(u) \ + ((ble_uuid16_t *) (u)) + +#define BLE_UUID32(u) \ + ((ble_uuid32_t *) (u)) + +#define BLE_UUID128(u) \ + ((ble_uuid128_t *) (u)) + +/** Size of buffer needed to store UUID as a string. + * Includes trailing \0. + */ +#define BLE_UUID_STR_LEN (37) + +/** @brief Constructs a UUID object from a byte array. + * + * @param uuid On success, this gets populated with the constructed UUID. + * @param buf The source buffer to parse. + * @param len The size of the buffer, in bytes. + * + * @return 0 on success, BLE_HS_EINVAL if the source buffer does not contain + * a valid UUID. + */ +int ble_uuid_init_from_buf(ble_uuid_any_t *uuid, const void *buf, size_t len); + +/** @brief Compares two Bluetooth UUIDs. + * + * @param uuid1 The first UUID to compare. + * @param uuid2 The second UUID to compare. + * + * @return 0 if the two UUIDs are equal, nonzero if the UUIDs differ. + */ +int ble_uuid_cmp(const ble_uuid_t *uuid1, const ble_uuid_t *uuid2); + +/** @brief Copy Bluetooth UUID + * + * @param dst Destination UUID. + * @param src Source UUID. + */ +void ble_uuid_copy(ble_uuid_any_t *dst, const ble_uuid_t *src); + +/** @brief Converts the specified UUID to its string representation. + * + * Example string representations: + * o 16-bit: 0x1234 + * o 32-bit: 0x12345678 + * o 128-bit: 12345678-1234-1234-1234-123456789abc + * + * @param uuid The source UUID to convert. + * @param dst The destination buffer. + * + * @return A pointer to the supplied destination buffer. + */ +char *ble_uuid_to_str(const ble_uuid_t *uuid, char *dst); + +/** @brief Converts the specified 16-bit UUID to a uint16_t. + * + * @param uuid The source UUID to convert. + * + * @return The converted integer on success, NULL if the specified UUID is + * not 16 bits. + */ +uint16_t ble_uuid_u16(const ble_uuid_t *uuid); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _BLE_HOST_UUID_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/access.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/access.h new file mode 100644 index 0000000..f146bf1 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/access.h @@ -0,0 +1,656 @@ +/** @file + * @brief Bluetooth Mesh Access Layer APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_ACCESS_H +#define __BT_MESH_ACCESS_H + +/** + * @brief Bluetooth Mesh Access Layer + * @defgroup bt_mesh_access Bluetooth Mesh Access Layer + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define BT_MESH_ADDR_UNASSIGNED 0x0000 +#define BT_MESH_ADDR_ALL_NODES 0xffff +#define BT_MESH_ADDR_PROXIES 0xfffc +#define BT_MESH_ADDR_FRIENDS 0xfffd +#define BT_MESH_ADDR_RELAYS 0xfffe + +#define BT_MESH_KEY_UNUSED 0xffff +#define BT_MESH_KEY_DEV 0xfffe +#define BT_MESH_KEY_DEV_LOCAL BT_MESH_KEY_DEV +#define BT_MESH_KEY_DEV_REMOTE 0xfffd +#define BT_MESH_KEY_DEV_ANY 0xfffc + +#define BT_MESH_IS_DEV_KEY(key) (key == BT_MESH_KEY_DEV_LOCAL || \ + key == BT_MESH_KEY_DEV_REMOTE) + +/** Helper to define a mesh element within an array. + * + * In case the element has no SIG or Vendor models the helper + * macro BT_MESH_MODEL_NONE can be given instead. + * + * @param _loc Location Descriptor. + * @param _mods Array of models. + * @param _vnd_mods Array of vendor models. + */ +#define BT_MESH_ELEM(_loc, _mods, _vnd_mods) \ +{ \ + .loc = (_loc), \ + .model_count = ARRAY_SIZE(_mods), \ + .models = (_mods), \ + .vnd_model_count = ARRAY_SIZE(_vnd_mods), \ + .vnd_models = (_vnd_mods), \ +} + +/** Abstraction that describes a Mesh Element */ +struct bt_mesh_elem { + /* Unicast Address. Set at runtime during provisioning. */ + u16_t addr; + + /* Location Descriptor (GATT Bluetooth Namespace Descriptors) */ + const u16_t loc; + + const u8_t model_count; + const u8_t vnd_model_count; + + struct bt_mesh_model * const models; + struct bt_mesh_model * const vnd_models; +}; + +/* Foundation Models */ +#define BT_MESH_MODEL_ID_CFG_SRV 0x0000 +#define BT_MESH_MODEL_ID_CFG_CLI 0x0001 +#define BT_MESH_MODEL_ID_HEALTH_SRV 0x0002 +#define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003 + +/* Models from the Mesh Model Specification */ +#define BT_MESH_MODEL_ID_GEN_ONOFF_SRV 0x1000 +#define BT_MESH_MODEL_ID_GEN_ONOFF_CLI 0x1001 +#define BT_MESH_MODEL_ID_GEN_LEVEL_SRV 0x1002 +#define BT_MESH_MODEL_ID_GEN_LEVEL_CLI 0x1003 +#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV 0x1004 +#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI 0x1005 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV 0x1006 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV 0x1007 +#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI 0x1008 +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV 0x1009 +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV 0x100a +#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI 0x100b +#define BT_MESH_MODEL_ID_GEN_BATTERY_SRV 0x100c +#define BT_MESH_MODEL_ID_GEN_BATTERY_CLI 0x100d +#define BT_MESH_MODEL_ID_GEN_LOCATION_SRV 0x100e +#define BT_MESH_MODEL_ID_GEN_LOCATION_SETUPSRV 0x100f +#define BT_MESH_MODEL_ID_GEN_LOCATION_CLI 0x1010 +#define BT_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV 0x1011 +#define BT_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV 0x1012 +#define BT_MESH_MODEL_ID_GEN_USER_PROP_SRV 0x1013 +#define BT_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV 0x1014 +#define BT_MESH_MODEL_ID_GEN_PROP_CLI 0x1015 +#define BT_MESH_MODEL_ID_SENSOR_SRV 0x1100 +#define BT_MESH_MODEL_ID_SENSOR_SETUP_SRV 0x1101 +#define BT_MESH_MODEL_ID_SENSOR_CLI 0x1102 +#define BT_MESH_MODEL_ID_TIME_SRV 0x1200 +#define BT_MESH_MODEL_ID_TIME_SETUP_SRV 0x1201 +#define BT_MESH_MODEL_ID_TIME_CLI 0x1202 +#define BT_MESH_MODEL_ID_SCENE_SRV 0x1203 +#define BT_MESH_MODEL_ID_SCENE_SETUP_SRV 0x1204 +#define BT_MESH_MODEL_ID_SCENE_CLI 0x1205 +#define BT_MESH_MODEL_ID_SCHEDULER_SRV 0x1206 +#define BT_MESH_MODEL_ID_SCHEDULER_SETUP_SRV 0x1207 +#define BT_MESH_MODEL_ID_SCHEDULER_CLI 0x1208 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV 0x1300 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV 0x1301 +#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI 0x1302 +#define BT_MESH_MODEL_ID_LIGHT_CTL_SRV 0x1303 +#define BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV 0x1304 +#define BT_MESH_MODEL_ID_LIGHT_CTL_CLI 0x1305 +#define BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV 0x1306 +#define BT_MESH_MODEL_ID_LIGHT_HSL_SRV 0x1307 +#define BT_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV 0x1308 +#define BT_MESH_MODEL_ID_LIGHT_HSL_CLI 0x1309 +#define BT_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV 0x130a +#define BT_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV 0x130b +#define BT_MESH_MODEL_ID_LIGHT_XYL_SRV 0x130c +#define BT_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV 0x130d +#define BT_MESH_MODEL_ID_LIGHT_XYL_CLI 0x130e +#define BT_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f +#define BT_MESH_MODEL_ID_LIGHT_LC_SETUPSRV 0x1310 +#define BT_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311 + +/** Message sending context. */ +struct bt_mesh_msg_ctx { + /** NetKey Index of the subnet to send the message on. */ + u16_t net_idx; + + /** AppKey Index to encrypt the message with. */ + u16_t app_idx; + + /** Remote address. */ + u16_t addr; + + /** Destination address of a received message. Not used for sending. */ + u16_t recv_dst; + + /** RSSI of received packet. Not used for sending. */ + s8_t recv_rssi; + + /** Received TTL value. Not used for sending. */ + u8_t recv_ttl; + + /** Force sending reliably by using segment acknowledgement */ + bool send_rel; + + /** TTL, or BT_MESH_TTL_DEFAULT for default TTL. */ + u8_t send_ttl; +}; + +struct bt_mesh_model_op { + /* OpCode encoded using the BT_MESH_MODEL_OP_* macros */ + const u32_t opcode; + + /* Minimum required message length */ + const size_t min_len; + + /* Message handler for the opcode */ + void (*const func)(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); +}; + +#define BT_MESH_MODEL_OP_1(b0) (b0) +#define BT_MESH_MODEL_OP_2(b0, b1) (((b0) << 8) | (b1)) +#define BT_MESH_MODEL_OP_3(b0, cid) ((((b0) << 16) | 0xc00000) | (cid)) + +#define BT_MESH_MODEL_OP_END { 0, 0, NULL } +#define BT_MESH_MODEL_NO_OPS ((struct bt_mesh_model_op []) \ + { BT_MESH_MODEL_OP_END }) + +/** Helper to define an empty model array */ +#define BT_MESH_MODEL_NONE ((struct bt_mesh_model []){}) + +/** Length of a short Mesh MIC. */ +#define BT_MESH_MIC_SHORT 4 +/** Length of a long Mesh MIC. */ +#define BT_MESH_MIC_LONG 8 + +/** @def BT_MESH_MODEL_OP_LEN + * + * @brief Helper to determine the length of an opcode. + * + * @param _op Opcode. + */ +#define BT_MESH_MODEL_OP_LEN(_op) ((_op) <= 0xff ? 1 : (_op) <= 0xffff ? 2 : 3) + +/** @def BT_MESH_MODEL_BUF_LEN + * + * @brief Helper for model message buffer length. + * + * Returns the length of a Mesh model message buffer, including the opcode + * length and a short MIC. + * + * @param _op Opcode of the message. + * @param _payload_len Length of the model payload. + */ +#define BT_MESH_MODEL_BUF_LEN(_op, _payload_len) \ + (BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_SHORT) + +/** @def BT_MESH_MODEL_BUF_LEN_LONG_MIC + * + * @brief Helper for model message buffer length. + * + * Returns the length of a Mesh model message buffer, including the opcode + * length and a long MIC. + * + * @param _op Opcode of the message. + * @param _payload_len Length of the model payload. + */ +#define BT_MESH_MODEL_BUF_LEN_LONG_MIC(_op, _payload_len) \ + (BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_LONG) + +/** @def BT_MESH_MODEL_BUF_DEFINE + * + * @brief Define a Mesh model message buffer using @ref NET_BUF_SIMPLE. + * + * @param _op Opcode of the message. + * @param _payload_len Length of the model message payload. + */ +#define BT_MESH_MODEL_BUF(_op, _payload_len) \ + NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN(_op, (_payload_len))) + +/** @def BT_MESH_MODEL_CB + * + * @brief Composition data SIG model entry with callback functions. + * + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + * @param _cb Callback structure, or NULL to keep no callbacks. + */ +#define BT_MESH_MODEL_CB(_id, _op, _pub, _user_data, _cb) \ +{ \ + .id = (_id), \ + .op = _op, \ + .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \ + BT_MESH_KEY_UNUSED }, \ + .pub = _pub, \ + .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \ + BT_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ + .cb = _cb, \ +} + +/** @def BT_MESH_MODEL_VND_CB + * + * @brief Composition data vendor model entry with callback functions. + * + * @param _company Company ID. + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + * @param _cb Callback structure, or NULL to keep no callbacks. + */ +#define BT_MESH_MODEL_VND_CB(_company, _id, _op, _pub, _user_data, _cb) \ +{ \ + .vnd.company = (_company), \ + .vnd.id = (_id), \ + .op = _op, \ + .pub = _pub, \ + .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \ + BT_MESH_KEY_UNUSED }, \ + .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \ + BT_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ + .cb = _cb, \ +} + + +/** @def BT_MESH_MODEL + * + * @brief Composition data SIG model entry. + * + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + */ +#define BT_MESH_MODEL(_id, _op, _pub, _user_data) \ + BT_MESH_MODEL_CB(_id, _op, _pub, _user_data, NULL) + +/** @def BT_MESH_MODEL_VND + * + * @brief Composition data vendor model entry. + * + * @param _company Company ID. + * @param _id Model ID. + * @param _op Array of model opcode handlers. + * @param _pub Model publish parameters. + * @param _user_data User data for the model. + */ +#define BT_MESH_MODEL_VND(_company, _id, _op, _pub, _user_data) \ + BT_MESH_MODEL_VND_CB(_company, _id, _op, _pub, _user_data, NULL) + +/** @def BT_MESH_TRANSMIT + * + * @brief Encode transmission count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0, + * less than or equal to 320, and a multiple of 10. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BT_MESH_TRANSMIT(count, int_ms) ((count) | (((int_ms / 10) - 1) << 3)) + +/** @def BT_MESH_TRANSMIT_COUNT + * + * @brief Decode transmit count from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission count (actual transmissions is N + 1). + */ +#define BT_MESH_TRANSMIT_COUNT(transmit) (((transmit) & (u8_t)BIT_MASK(3))) + +/** @def BT_MESH_TRANSMIT_INT + * + * @brief Decode transmit interval from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BT_MESH_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 10) + +/** @def BT_MESH_PUB_TRANSMIT + * + * @brief Encode Publish Retransmit count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 50. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BT_MESH_PUB_TRANSMIT(count, int_ms) BT_MESH_TRANSMIT(count, \ + (int_ms) / 5) + +/** @def BT_MESH_PUB_TRANSMIT_COUNT + * + * @brief Decode Pubhlish Retransmit count from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Retransmission count (actual transmissions is N + 1). + */ +#define BT_MESH_PUB_TRANSMIT_COUNT(transmit) BT_MESH_TRANSMIT_COUNT(transmit) + +/** @def BT_MESH_PUB_TRANSMIT_INT + * + * @brief Decode Publish Retransmit interval from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BT_MESH_PUB_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 50) + +/** Model publication context. */ +struct bt_mesh_model_pub { + /** The model the context belongs to. Initialized by the stack. */ + struct bt_mesh_model *mod; + + u16_t addr; /**< Publish Address. */ + u16_t key; /**< Publish AppKey Index. */ + + u8_t ttl; /**< Publish Time to Live. */ + u8_t retransmit; /**< Retransmit Count & Interval Steps. */ + u8_t period; /**< Publish Period. */ + u8_t period_div:4, /**< Divisor for the Period. */ + cred:1, /**< Friendship Credentials Flag. */ + fast_period:1,/**< Use FastPeriodDivisor */ + count:3; /**< Retransmissions left. */ + + u32_t period_start; /**< Start of the current period. */ + + /** @brief Publication buffer, containing the publication message. + * + * The application is expected to initialize this with + * a valid net_buf_simple pointer, with the help of e.g. + * the NET_BUF_SIMPLE() macro. The publication buffer must + * contain a valid publication message before calling the + * bt_mesh_model_publish() API or after the publication's + * @ref bt_mesh_model_pub.update callback has been called + * and returned success. The buffer must be created outside + * of function context, i.e. it must not be on the stack. + * This is most conveniently acheived by creating it inline + * when declaring the publication context: + * + * static struct bt_mesh_model_pub my_pub = { + * .msg = NET_BUF_SIMPLE(size), + * }; + */ + struct os_mbuf *msg; + + /** @brief Callback for updating the publication buffer. + * + * When set to NULL, the model is assumed not to support + * periodic publishing. When set to non-NULL the callback + * will be called periodically and is expected to update + * @ref bt_mesh_model_pub.msg with a valid publication + * message. + * + * @param mod The Model the Publication Context belogs to. + * + * @return Zero on success or (negative) error code otherwise. + */ + int (*update)(struct bt_mesh_model *mod); + + /** Publish Period Timer. Only for stack-internal use. */ + struct k_delayed_work timer; +}; + +/** Model callback functions. */ +struct bt_mesh_model_cb { + /** @brief Set value handler of user data tied to the model. + * + * @sa settings_handler::h_set + * + * @param model Model to set the persistent data of. + * @param val Data from the backend. + * + * @return 0 on success, error otherwise. + */ + int (*const settings_set)(struct bt_mesh_model *model, char *val); + + /** @brief Callback called when all settings have been loaded. + * + * This handler gets called after the settings have been loaded in + * full. + * + * @sa settings_handler::h_commit + * + * @param model Model this callback belongs to. + * + * @return 0 on success, error otherwise. + */ + int (*const settings_commit)(struct bt_mesh_model *model); + + /** @brief Model initialize callback. + * + * Called on every model instance during mesh initialization. + * + * @param model Model to be initialized. + * + * @return 0 on success, error otherwise. + */ + int (*const init)(struct bt_mesh_model *model); + + /** @brief Model reset callback. + * + * Called when the mesh node is reset. All model data is deleted on + * reset, and the model should clear its state. + * + * @param model Model this callback belongs to. + */ + void (*const reset)(struct bt_mesh_model *model); +}; + +/** Abstraction that describes a Mesh Model instance */ +struct bt_mesh_model { + union { + const u16_t id; + struct { + u16_t company; + u16_t id; + } vnd; + }; + + /* Internal information, mainly for persistent storage */ + u8_t elem_idx; /* Belongs to Nth element */ + u8_t mod_idx; /* Is the Nth model in the element */ + u16_t flags; /* Model flags for internal bookkeeping */ + + /* Model Publication */ + struct bt_mesh_model_pub * const pub; + + /* AppKey List */ + u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; + + /* Subscription List (group or virtual addresses) */ + u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; + + const struct bt_mesh_model_op * const op; + + /* Model callback structure. */ + const struct bt_mesh_model_cb * const cb; + +#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS) + /* Pointer to the next model in a model extension tree. */ + struct bt_mesh_model *next; + /* Pointer to the first model this model extends. */ + struct bt_mesh_model *extends; +#endif + /* Model-specific user data */ + void *user_data; +}; + +struct bt_mesh_send_cb { + void (*start)(u16_t duration, int err, void *cb_data); + void (*end)(int err, void *cb_data); +}; + +void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode); + +/** Special TTL value to request using configured default TTL */ +#define BT_MESH_TTL_DEFAULT 0xff + +/** Maximum allowed TTL value */ +#define BT_MESH_TTL_MAX 0x7f + +/** + * @brief Send an Access Layer message. + * + * @param model Mesh (client) Model that the message belongs to. + * @param ctx Message context, includes keys, TTL, etc. + * @param msg Access Layer payload (the actual message to be sent). + * @param cb Optional "message sent" callback. + * @param cb_data User data to be passed to the callback. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, + void *cb_data); + +/** + * @brief Send a model publication message. + * + * Before calling this function, the user needs to ensure that the model + * publication message (@ref bt_mesh_model_pub.msg) contains a valid + * message to be sent. Note that this API is only to be used for + * non-period publishing. For periodic publishing the app only needs + * to make sure that @ref bt_mesh_model_pub.msg contains a valid message + * whenever the @ref bt_mesh_model_pub.update callback is called. + * + * @param model Mesh (client) Model that's publishing the message. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_publish(struct bt_mesh_model *model); + +/** + * @brief Get the element that a model belongs to. + * + * @param mod Mesh model. + * + * @return Pointer to the element that the given model belongs to. + */ +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod); + +/** @brief Find a SIG model. + * + * @param elem Element to search for the model in. + * @param id Model ID of the model. + * + * @return A pointer to the Mesh model matching the given parameters, or NULL + * if no SIG model with the given ID exists in the given element. + */ +struct bt_mesh_model *bt_mesh_model_find(const struct bt_mesh_elem *elem, + u16_t id); + +/** @brief Find a vendor model. + * + * @param elem Element to search for the model in. + * @param company Company ID of the model. + * @param id Model ID of the model. + * + * @return A pointer to the Mesh model matching the given parameters, or NULL + * if no vendor model with the given ID exists in the given element. + */ +struct bt_mesh_model *bt_mesh_model_find_vnd(const struct bt_mesh_elem *elem, + u16_t company, u16_t id); + +/** @brief Get whether the model is in the primary element of the device. + * + * @param mod Mesh model. + * + * @return true if the model is on the primary element, false otherwise. + */ +static inline bool bt_mesh_model_in_primary(const struct bt_mesh_model *mod) +{ + return (mod->elem_idx == 0); +} + +/** @brief Immediately store the model's user data in persistent storage. + * + * @param mod Mesh model. + * @param vnd This is a vendor model. + * @param data Model data to store, or NULL to delete any model data. + * @param data_len Length of the model data. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, + const void *data, size_t data_len); + +/** @brief Let a model extend another. + * + * Mesh models may be extended to reuse their functionality, forming a more + * complex model. A Mesh model may extend any number of models, in any element. + * The extensions may also be nested, ie a model that extends another may itself + * be extended. Extensions may not be cyclical, and a model can only be extended + * by one other model. + * + * A set of models that extend each other form a model extension tree. + * + * All models in an extension tree share one subscription list per element. The + * access layer will utilize the combined subscription list of all models in an + * extension tree and element, giving the models extended subscription list + * capacity. + * + * @param[in] mod Mesh model. + * @param[in] base_mod The model being extended. + * + * @retval 0 Successfully extended the base_mod model. + * @retval -EALREADY The base_mod model is already extended. + */ +int bt_mesh_model_extend(struct bt_mesh_model *mod, + struct bt_mesh_model *base_mod); + +/** Node Composition */ +struct bt_mesh_comp { + u16_t cid; + u16_t pid; + u16_t vid; + + size_t elem_count; + struct bt_mesh_elem *elem; +}; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_ACCESS_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_cli.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_cli.h new file mode 100644 index 0000000..7dc237b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_cli.h @@ -0,0 +1,234 @@ +/** @file + * @brief Bluetooth Mesh Configuration Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_CFG_CLI_H +#define __BT_MESH_CFG_CLI_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_cli Bluetooth Mesh Configuration Client Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Configuration Client Model Context */ +struct bt_mesh_cfg_cli { + struct bt_mesh_model *model; + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[]; +extern const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb; + +#define BT_MESH_MODEL_CFG_CLI(cli_data) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_CFG_CLI, bt_mesh_cfg_cli_op, NULL, \ + cli_data, &bt_mesh_cfg_cli_cb) + +int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page, + u8_t *status, struct os_mbuf *comp); + +int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status); + +int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl); + +int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl); + +int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status); + +int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status); + +int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val, + u8_t *status); + +int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status, + u8_t *transmit); + +int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay, + u8_t new_transmit, u8_t *status, u8_t *transmit); + +int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + const u8_t net_key[16], u8_t *status); + +int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16], + u8_t *status); + +int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status); + +/** @def BT_MESH_PUB_PERIOD_100MS + * + * @brief Helper macro to encode model publication period in units of 100ms + * + * @param steps Number of 100ms steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_100MS(steps) ((steps) & BIT_MASK(6)) + +/** @def BT_MESH_PUB_PERIOD_SEC + * + * @brief Helper macro to encode model publication period in units of 1 second + * + * @param steps Number of 1 second steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_SEC(steps) (((steps) & BIT_MASK(6)) | (1 << 6)) + +/** @def BT_MESH_PUB_PERIOD_10SEC + * + * @brief Helper macro to encode model publication period in units of 10 + * seconds + * + * @param steps Number of 10 second steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_10SEC(steps) (((steps) & BIT_MASK(6)) | (2 << 6)) + +/** @def BT_MESH_PUB_PERIOD_10MIN + * + * @brief Helper macro to encode model publication period in units of 10 + * minutes + * + * @param steps Number of 10 minute steps. + * + * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period + */ +#define BT_MESH_PUB_PERIOD_10MIN(steps) (((steps) & BIT_MASK(6)) | (3 << 6)) + +struct bt_mesh_cfg_mod_pub { + u16_t addr; + u16_t app_idx; + bool cred_flag; + u8_t ttl; + u8_t period; + u8_t transmit; +}; + +int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status); + +int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status); + +int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status); + +int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status); + +int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status); + +int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status); + +int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status); + +int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, u16_t sub_addr, + u16_t mod_id, u16_t cid, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status); + +int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t *virt_addr, + u8_t *status); + +int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status); + +struct bt_mesh_cfg_hb_sub { + u16_t src; + u16_t dst; + u8_t period; + u8_t count; + u8_t min; + u8_t max; +}; + +int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status); + +int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status); + +struct bt_mesh_cfg_hb_pub { + u16_t dst; + u8_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; +}; + +int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr, + const struct bt_mesh_cfg_hb_pub *pub, u8_t *status); + +int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_pub *pub, u8_t *status); + +s32_t bt_mesh_cfg_cli_timeout_get(void); +void bt_mesh_cfg_cli_timeout_set(s32_t timeout); + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* __BT_MESH_CFG_CLI_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_srv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_srv.h new file mode 100644 index 0000000..14d8a29 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/cfg_srv.h @@ -0,0 +1,78 @@ +/** @file + * @brief Bluetooth Mesh Configuration Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_CFG_SRV_H +#define __BT_MESH_CFG_SRV_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_srv Bluetooth Mesh Configuration Server Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Configuration Server Model Context */ +struct bt_mesh_cfg_srv { + struct bt_mesh_model *model; + + u8_t net_transmit; /* Network Transmit state */ + u8_t relay; /* Relay Mode state */ + u8_t relay_retransmit; /* Relay Retransmit state */ + u8_t beacon; /* Secure Network Beacon state */ + u8_t gatt_proxy; /* GATT Proxy state */ + u8_t frnd; /* Friend state */ + u8_t default_ttl; /* Default TTL */ + + /* Heartbeat Publication */ + struct bt_mesh_hb_pub { + struct k_delayed_work timer; + + u16_t dst; + u16_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; + } hb_pub; + + /* Heartbeat Subscription */ + struct bt_mesh_hb_sub { + s64_t expiry; + + u16_t src; + u16_t dst; + u16_t count; + u8_t min_hops; + u8_t max_hops; + + /* Optional subscription tracking function */ + void (*func)(u8_t hops, u16_t feat); + } hb_sub; +}; + +extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[]; +extern const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb; + +#define BT_MESH_MODEL_CFG_SRV(srv_data) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_CFG_SRV, bt_mesh_cfg_srv_op, NULL, \ + srv_data, &bt_mesh_cfg_srv_cb) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_CFG_SRV_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/glue.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/glue.h new file mode 100644 index 0000000..8b3da97 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/glue.h @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _MESH_GLUE_ +#define _MESH_GLUE_ + +#include +#include + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/logcfg/logcfg.h" +#include "nimble/porting/nimble/include/modlog/modlog.h" +#include "nimble/nimble/include/nimble/nimble_npl.h" + +#include "nimble/porting/nimble/include/os/os_mbuf.h" +#include "nimble/porting/nimble/include/os/queue.h" + +#include "nimble/nimble/include/nimble/ble.h" +#include "nimble/nimble/host/include/host/ble_hs.h" +#include "nimble/nimble/host/include/host/ble_uuid.h" +#include "nimble/nimble/host/src/ble_sm_priv.h" +#include "nimble/nimble/host/src/ble_hs_hci_priv.h" + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +#include "mbedtls/aes.h" +#include "mbedtls/cipher.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/cmac.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" + +#else +#include "nimble/ext/tinycrypt/include/tinycrypt/aes.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/constants.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/utils.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/ecc_dh.h" +#endif + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) +#include "config/config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define s16_t int16_t +#define u32_t uint32_t +#define u64_t uint64_t +#define s64_t int64_t +#define s32_t int32_t + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _data Pointer to the data field payload + * @param _data_len Number of bytes behind the _data pointer + */ +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const u8_t *)(_data), \ + } + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _bytes Variable number of single-byte parameters + */ +#define BT_DATA_BYTES(_type, _bytes...) \ + BT_DATA(_type, ((u8_t []) { _bytes }), \ + sizeof((u8_t []) { _bytes })) + +/* EIR/AD data type definitions */ +#define BT_DATA_FLAGS 0x01 /* AD flags */ +#define BT_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BT_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BT_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BT_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BT_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BT_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BT_DATA_NAME_SHORTENED 0x08 /* Shortened name */ +#define BT_DATA_NAME_COMPLETE 0x09 /* Complete name */ +#define BT_DATA_TX_POWER 0x0a /* Tx Power */ +#define BT_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BT_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BT_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BT_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BT_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BT_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BT_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BT_DATA_URI 0x24 /* URI */ +#define BT_DATA_MESH_PROV 0x29 /* Mesh Provisioning PDU */ +#define BT_DATA_MESH_MESSAGE 0x2a /* Mesh Networking PDU */ +#define BT_DATA_MESH_BEACON 0x2b /* Mesh Beacon */ + +#define BT_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BT_LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */ +#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +#define sys_put_be16(a,b) put_be16(b, a) +#define sys_put_le16(a,b) put_le16(b, a) +#define sys_put_be32(a,b) put_be32(b, a) +#define sys_get_be16(a) get_be16(a) +#define sys_get_le16(a) get_le16(a) +#define sys_get_be32(a) get_be32(a) +#define sys_cpu_to_be16(a) htobe16(a) +#define sys_cpu_to_be32(a) htobe32(a) +#define sys_be32_to_cpu(a) be32toh(a) +#define sys_be16_to_cpu(a) be16toh(a) +#define sys_le16_to_cpu(a) le16toh(a) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define CODE_UNREACHABLE __builtin_unreachable() +#define __ASSERT(code, str) \ + do { \ + if (!(code)) BT_ERR(str); \ + assert(code); \ + } while (0); + +#define __ASSERT_NO_MSG(test) __ASSERT(test, "") + +/* Mesh is designed to not use mbuf chains */ +#if BT_DBG_ENABLED +#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL) +#else +#define ASSERT_NOT_CHAIN(om) (void)(om) +#endif + +#define __packed __attribute__((__packed__)) + +#define MSEC_PER_SEC (1000) +#define K_MSEC(ms) (ms) +#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC) +#define K_MINUTES(m) K_SECONDS((m) * 60) +#define K_HOURS(h) K_MINUTES((h) * 60) + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define BIT_MASK(n) (BIT(n) - 1) + +#define BT_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */ +#define BT_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */ +#define BT_GAP_ADV_SLOW_INT_MIN 0x0640 /* 1 s */ +#define BT_GAP_ADV_SLOW_INT_MAX 0x0780 /* 1.2 s */ + +#ifndef MESH_LOG_MODULE +#define MESH_LOG_MODULE BLE_MESH_LOG +#endif + +#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) +#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ + +#define BLE_MESH_LOG(lvl, ...) CAT(MESH_LOG_MODULE, CAT(_, lvl))(__VA_ARGS__) + +#define BT_DBG(fmt, ...) BLE_MESH_LOG(DEBUG, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_INFO(fmt, ...) BLE_MESH_LOG(INFO, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_WARN(fmt, ...) BLE_MESH_LOG(WARN, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_ERR(fmt, ...) BLE_MESH_LOG(ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__); +#define BT_GATT_ERR(_att_err) (-(_att_err)) + +typedef ble_addr_t bt_addr_le_t; + +#define k_fifo_init(queue) ble_npl_eventq_init(queue) +#define net_buf_simple_tailroom(buf) OS_MBUF_TRAILINGSPACE(buf) +#define net_buf_tailroom(buf) net_buf_simple_tailroom(buf) +#define net_buf_headroom(buf) ((buf)->om_data - &(buf)->om_databuf[buf->om_pkthdr_len]) +#define net_buf_simple_headroom(buf) net_buf_headroom(buf) +#define net_buf_simple_tail(buf) ((buf)->om_data + (buf)->om_len) + +struct net_buf_simple_state { + /** Offset of the data pointer from the beginning of the storage */ + u16_t offset; + /** Length of data */ + u16_t len; +}; + +static inline struct os_mbuf * NET_BUF_SIMPLE(uint16_t size) +{ + struct os_mbuf *buf; + + buf = os_msys_get(size, 0); + assert(buf); + + return buf; +} + +#define K_NO_WAIT (0) +#define K_FOREVER (-1) + +#if MYNEWT_VAL(BLE_EXT_ADV) +#define BT_MESH_ADV_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES)) + +#if MYNEWT_VAL(BLE_MESH_PROXY) +/* Note that BLE_MULTI_ADV_INSTANCES contains number of additional instances. + * Instance 0 is always there + */ +#if MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) < 1 +#error "Mesh needs at least BLE_MULTI_ADV_INSTANCES set to 1" +#endif +#define BT_MESH_ADV_GATT_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) - 1) +#endif /* BLE_MESH_PROXY */ +#endif /* BLE_EXT_ADV */ + +/* This is by purpose */ +static inline void net_buf_simple_init(struct os_mbuf *buf, + size_t reserve_head) +{ + /* This is called in Zephyr after initialize. + * Note in Mynewt case we don't care abour reserved head*/ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head; + buf->om_len = 0; +} + +void net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *buf); +void * net_buf_ref(struct os_mbuf *om); +void net_buf_unref(struct os_mbuf *om); +uint16_t net_buf_simple_pull_le16(struct os_mbuf *om); +uint16_t net_buf_simple_pull_be16(struct os_mbuf *om); +uint32_t net_buf_simple_pull_be32(struct os_mbuf *om); +uint32_t net_buf_simple_pull_le32(struct os_mbuf *om); +uint8_t net_buf_simple_pull_u8(struct os_mbuf *om); +void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val); +void net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val); +void net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val); +void net_buf_add_zeros(struct os_mbuf *om, uint8_t len); +void net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val); +void *net_buf_simple_pull(struct os_mbuf *om, uint8_t len); +void *net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len); +void *net_buf_simple_add(struct os_mbuf *om, uint8_t len); +bool k_fifo_is_empty(struct ble_npl_eventq *q); +void *net_buf_get(struct ble_npl_eventq *fifo,s32_t t); +uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len); +void net_buf_reserve(struct os_mbuf *om, size_t reserve); + +#define net_buf_add_mem(a,b,c) os_mbuf_append(a,b,c) +#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c) +#define net_buf_add_u8(a,b) net_buf_simple_add_u8(a,b) +#define net_buf_add(a,b) net_buf_simple_add(a,b) + +#define net_buf_clone(a, b) os_mbuf_dup(a) +#define net_buf_add_be32(a, b) net_buf_simple_add_be32(a, b) +#define net_buf_add_be16(a, b) net_buf_simple_add_be16(a, b) +#define net_buf_pull(a, b) net_buf_simple_pull(a, b) +#define net_buf_pull_mem(a, b) net_buf_simple_pull_mem(a, b) +#define net_buf_pull_u8(a) net_buf_simple_pull_u8(a) +#define net_buf_pull_be16(a) net_buf_simple_pull_be16(a) +#define net_buf_skip(a, b) net_buf_simple_pull_mem(a, b) + +#define BT_GATT_CCC_NOTIFY BLE_GATT_CHR_PROP_NOTIFY + +/** Description of different data types that can be encoded into + * advertising data. Used to form arrays that are passed to the + * bt_le_adv_start() function. + */ +struct bt_data { + u8_t type; + u8_t data_len; + const u8_t *data; +}; + +struct bt_pub_key_cb { + /** @brief Callback type for Public Key generation. + * + * Used to notify of the local public key or that the local key is not + * available (either because of a failure to read it or because it is + * being regenerated). + * + * @param key The local public key, or NULL in case of no key. + */ + void (*func)(const u8_t key[64]); + + struct bt_pub_key_cb *_next; +}; + +typedef void (*bt_dh_key_cb_t)(const u8_t key[32]); +int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb); +int bt_pub_key_gen(struct bt_pub_key_cb *new_cb); +uint8_t *bt_pub_key_get(void); +int bt_rand(void *buf, size_t len); +const char * bt_hex(const void *buf, size_t len); +int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data); +void bt_mesh_register_gatt(void); +int bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len); +int bt_le_adv_stop(bool proxy); + +struct k_delayed_work { + struct ble_npl_callout work; +}; + +void k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler); +void k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f); +void k_delayed_work_cancel(struct k_delayed_work *w); +void k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms); +int64_t k_uptime_get(void); +u32_t k_uptime_get_32(void); +void k_sleep(int32_t duration); +void k_work_submit(struct ble_npl_callout *w); +void k_work_add_arg(struct ble_npl_callout *w, void *arg); +void k_delayed_work_add_arg(struct k_delayed_work *w, void *arg); +uint32_t k_delayed_work_remaining_get(struct k_delayed_work *w); + +static inline void net_buf_simple_save(struct os_mbuf *buf, + struct net_buf_simple_state *state) +{ + state->offset = net_buf_simple_headroom(buf); + state->len = buf->om_len; +} + +static inline void net_buf_simple_restore(struct os_mbuf *buf, + struct net_buf_simple_state *state) +{ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + state->offset; + buf->om_len = state->len; +} + +static inline void sys_memcpy_swap(void *dst, const void *src, size_t length) +{ + __ASSERT(((src < dst && (src + length) <= dst) || + (src > dst && (dst + length) <= src)), + "Source and destination buffers must not overlap"); + + src += length - 1; + + for (; length > 0; length--) { + *((u8_t *)dst++) = *((u8_t *)src--); + } +} + +#define popcount(x) __builtin_popcount(x) + +static inline unsigned int find_lsb_set(u32_t op) +{ + return __builtin_ffs(op); +} + +static inline unsigned int find_msb_set(u32_t op) +{ + if (!op) + return 0; + + return 32 - __builtin_clz(op); +} + +#define CONFIG_BT_MESH_FRIEND BLE_MESH_FRIEND +#define CONFIG_BT_MESH_GATT_PROXY BLE_MESH_GATT_PROXY +#define CONFIG_BT_MESH_IV_UPDATE_TEST BLE_MESH_IV_UPDATE_TEST +#define CONFIG_BT_MESH_LOW_POWER BLE_MESH_LOW_POWER +#define CONFIG_BT_MESH_LPN_AUTO BLE_MESH_LPN_AUTO +#define CONFIG_BT_MESH_LPN_ESTABLISHMENT BLE_MESH_LPN_ESTABLISHMENT +#define CONFIG_BT_MESH_PB_ADV BLE_MESH_PB_ADV +#define CONFIG_BT_MESH_PB_GATT BLE_MESH_PB_GATT +#define CONFIG_BT_MESH_PROV BLE_MESH_PROV +#define CONFIG_BT_MESH_PROXY BLE_MESH_PROXY +#define CONFIG_BT_TESTING BLE_MESH_TESTING +#define CONFIG_BT_SETTINGS BLE_MESH_SETTINGS +#define CONFIG_SETTINGS BLE_MESH_SETTINGS +#define CONFIG_BT_MESH_PROVISIONER BLE_MESH_PROVISIONER + +/* Above flags are used with IS_ENABLED macro */ +#define IS_ENABLED(config) MYNEWT_VAL(config) + +#define CONFIG_BT_MESH_LPN_GROUPS MYNEWT_VAL(BLE_MESH_LPN_GROUPS) +#define CONFIG_BT_MESH_ADV_BUF_COUNT MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT) +#define CONFIG_BT_MESH_FRIEND_QUEUE_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) +#define CONFIG_BT_MESH_FRIEND_RECV_WIN MYNEWT_VAL(BLE_MESH_FRIEND_RECV_WIN) +#define CONFIG_BT_MESH_LPN_POLL_TIMEOUT MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) +#define CONFIG_BT_MESH_MODEL_GROUP_COUNT MYNEWT_VAL(BLE_MESH_MODEL_GROUP_COUNT) +#define CONFIG_BT_MESH_MODEL_KEY_COUNT MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) +#define CONFIG_BT_MESH_NODE_ID_TIMEOUT MYNEWT_VAL(BLE_MESH_NODE_ID_TIMEOUT) +#define CONFIG_BT_MAX_CONN MYNEWT_VAL(BLE_MAX_CONNECTIONS) +#define CONFIG_BT_MESH_SEQ_STORE_RATE MYNEWT_VAL(BLE_MESH_SEQ_STORE_RATE) +#define CONFIG_BT_MESH_RPL_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_RPL_STORE_TIMEOUT) +#define CONFIG_BT_MESH_APP_KEY_COUNT MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) +#define CONFIG_BT_MESH_SUBNET_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) +#define CONFIG_BT_MESH_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_STORE_TIMEOUT) +#define CONFIG_BT_MESH_IVU_DIVIDER MYNEWT_VAL(BLE_MESH_IVU_DIVIDER) +#define CONFIG_BT_DEVICE_NAME MYNEWT_VAL(BLE_MESH_DEVICE_NAME) +#define CONFIG_BT_MESH_TX_SEG_MAX MYNEWT_VAL(BLE_MESH_TX_SEG_MAX) +#define CONFIG_BT_MESH_LABEL_COUNT MYNEWT_VAL(BLE_MESH_LABEL_COUNT) +#define CONFIG_BT_MESH_NODE_COUNT MYNEWT_VAL(BLE_MESH_NODE_COUNT) + +#define printk console_printf + +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + + +#define k_sem ble_npl_sem + +static inline void k_sem_init(struct k_sem *sem, unsigned int initial_count, + unsigned int limit) +{ + ble_npl_sem_init(sem, initial_count); +} + +static inline int k_sem_take(struct k_sem *sem, s32_t timeout) +{ + uint32_t ticks; + + ble_npl_time_ms_to_ticks(timeout, &ticks); + return - ble_npl_sem_pend(sem, ticks); +} + +static inline void k_sem_give(struct k_sem *sem) +{ + ble_npl_sem_release(sem); +} + +/* Helpers to access the storage array, since we don't have access to its + * type at this point anymore. + */ + +#define BUF_SIZE(pool) (pool->omp_pool->mp_block_size) + +static inline int net_buf_id(struct os_mbuf *buf) +{ + struct os_mbuf_pool *pool = buf->om_omp; + u8_t *pool_start = (u8_t *)pool->omp_pool->mp_membuf_addr; + u8_t *buf_ptr = (u8_t *)buf; + + return (buf_ptr - pool_start) / BUF_SIZE(pool); +} + +/* XXX: We should not use os_mbuf_pkthdr chains to represent a list of + * packets, this is a hack. For now this is not an issue, because mesh + * does not use os_mbuf chains. We should change this in the future. + */ +STAILQ_HEAD(net_buf_slist_t, os_mbuf_pkthdr); + +void net_buf_slist_init(struct net_buf_slist_t *list); +bool net_buf_slist_is_empty(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list); +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf); +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list); +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf); +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur); +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append); +#define NET_BUF_SLIST_FOR_EACH_NODE(head, var) STAILQ_FOREACH(var, head, omp_next) + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +#define settings_load conf_load +int settings_bytes_from_str(char *val_str, void *vp, int *len); +char *settings_str_from_bytes(const void *vp, int vp_len, + char *buf, int buf_len); + +#define snprintk snprintf +#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) +#define settings_save_one conf_save_one + +#else + +static inline int +settings_load(void) +{ + return 0; +} + +#endif /* MYNEWT_VAL(MYNEWT_VAL_BLE_MESH_SETTINGS) */ + +#define BUILD_ASSERT(cond) _Static_assert(cond, "") + +#ifdef __cplusplus +} +#endif + +#endif /* _MESH_GLUE_ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_cli.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_cli.h new file mode 100644 index 0000000..8ab8d6d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_cli.h @@ -0,0 +1,81 @@ +/** @file + * @brief Bluetooth Mesh Health Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_HEALTH_CLI_H +#define __BT_MESH_HEALTH_CLI_H + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_health_cli Bluetooth Mesh Health Client Model + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mesh Health Client Model Context */ +struct bt_mesh_health_cli { + struct bt_mesh_model *model; + + void (*current_status)(struct bt_mesh_health_cli *cli, u16_t addr, + u8_t test_id, u16_t cid, u8_t *faults, + size_t fault_count); + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern const struct bt_mesh_model_op bt_mesh_health_cli_op[]; +extern const struct bt_mesh_model_cb bt_mesh_health_cli_cb; + +#define BT_MESH_MODEL_HEALTH_CLI(cli_data) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_HEALTH_CLI, bt_mesh_health_cli_op, \ + NULL, cli_data, &bt_mesh_health_cli_cb) + +int bt_mesh_health_cli_set(struct bt_mesh_model *model); + +int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t test_id, u8_t *faults, + size_t *fault_count); + +int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *divisor); + +int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t divisor, u8_t *updated_divisor); + +int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *attention); + +int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t attention, u8_t *updated_attention); + +s32_t bt_mesh_health_cli_timeout_get(void); +void bt_mesh_health_cli_timeout_set(s32_t timeout); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_HEALTH_CLI_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_srv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_srv.h new file mode 100644 index 0000000..8398237 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/health_srv.h @@ -0,0 +1,100 @@ +/** @file + * @brief Bluetooth Mesh Health Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_HEALTH_SRV_H +#define __BT_MESH_HEALTH_SRV_H + +/** + * @brief Mesh Bluetooth Mesh Health Server Model + * @defgroup bt_mesh_health_srv + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_health_srv_cb { + /* Fetch current faults */ + int (*fault_get_cur)(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, + u8_t *fault_count); + + /* Fetch registered faults */ + int (*fault_get_reg)(struct bt_mesh_model *model, u16_t company_id, + u8_t *test_id, u8_t *faults, + u8_t *fault_count); + + /* Clear registered faults */ + int (*fault_clear)(struct bt_mesh_model *model, u16_t company_id); + + /* Run a specific test */ + int (*fault_test)(struct bt_mesh_model *model, u8_t test_id, + u16_t company_id); + + /* Attention on */ + void (*attn_on)(struct bt_mesh_model *model); + + /* Attention off */ + void (*attn_off)(struct bt_mesh_model *model); +}; + +/** @def BT_MESH_HEALTH_FAULT_MSG + * + * A helper to define a health fault message. + * + * @param max_faults Maximum number of faults the element can have. + * + * @return a New net_buf_simple of the needed size. + */ +#define BT_MESH_HEALTH_FAULT_MSG(max_faults) \ + NET_BUF_SIMPLE(1 + 3 + (max_faults)) + +/** Mesh Health Server Model Context */ +struct bt_mesh_health_srv { + struct bt_mesh_model *model; + + /* Optional callback struct */ + const struct bt_mesh_health_srv_cb *cb; + + /* Attention Timer state */ + struct k_delayed_work attn_timer; +}; + +int bt_mesh_fault_update(struct bt_mesh_elem *elem); + +extern const struct bt_mesh_model_op bt_mesh_health_srv_op[]; +extern const struct bt_mesh_model_cb bt_mesh_health_srv_cb; + +/** @def BT_MESH_MODEL_HEALTH_SRV + * + * Define a new health server model. Note that this API needs to be + * repeated for each element that the application wants to have a + * health server model on. Each instance also needs a unique + * bt_mesh_health_srv and bt_mesh_model_pub context. + * + * @param srv Pointer to a unique struct bt_mesh_health_srv. + * @param pub Pointer to a unique struct bt_mesh_model_pub. + * + * @return New mesh model instance. + */ +#define BT_MESH_MODEL_HEALTH_SRV(srv, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_op, \ + pub, srv, &bt_mesh_health_srv_cb) + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_HEALTH_SRV_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/main.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/main.h new file mode 100644 index 0000000..4a5bedb --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/main.h @@ -0,0 +1,441 @@ +/** @file + * @brief Bluetooth Mesh Profile APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_MAIN_H +#define __BT_MESH_MAIN_H + +/** + * @brief Bluetooth Mesh Provisioning + * @defgroup bt_mesh_prov Bluetooth Mesh Provisioning + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BT_MESH_NO_OUTPUT = 0, + BT_MESH_BLINK = BIT(0), + BT_MESH_BEEP = BIT(1), + BT_MESH_VIBRATE = BIT(2), + BT_MESH_DISPLAY_NUMBER = BIT(3), + BT_MESH_DISPLAY_STRING = BIT(4), +} bt_mesh_output_action_t; + +typedef enum { + BT_MESH_NO_INPUT = 0, + BT_MESH_PUSH = BIT(0), + BT_MESH_TWIST = BIT(1), + BT_MESH_ENTER_NUMBER = BIT(2), + BT_MESH_ENTER_STRING = BIT(3), +} bt_mesh_input_action_t; + +typedef enum { + BT_MESH_PROV_ADV = BIT(0), + BT_MESH_PROV_GATT = BIT(1), +} bt_mesh_prov_bearer_t; + +typedef enum { + BT_MESH_PROV_OOB_OTHER = BIT(0), + BT_MESH_PROV_OOB_URI = BIT(1), + BT_MESH_PROV_OOB_2D_CODE = BIT(2), + BT_MESH_PROV_OOB_BAR_CODE = BIT(3), + BT_MESH_PROV_OOB_NFC = BIT(4), + BT_MESH_PROV_OOB_NUMBER = BIT(5), + BT_MESH_PROV_OOB_STRING = BIT(6), + /* 7 - 10 are reserved */ + BT_MESH_PROV_OOB_ON_BOX = BIT(11), + BT_MESH_PROV_OOB_IN_BOX = BIT(12), + BT_MESH_PROV_OOB_ON_PAPER = BIT(13), + BT_MESH_PROV_OOB_IN_MANUAL = BIT(14), + BT_MESH_PROV_OOB_ON_DEV = BIT(15), +} bt_mesh_prov_oob_info_t; + +/** Provisioning properties & capabilities. */ +struct bt_mesh_prov { + /** The UUID that's used when advertising as unprovisioned */ + const u8_t *uuid; + + /** Optional URI. This will be advertised separately from the + * unprovisioned beacon, however the unprovisioned beacon will + * contain a hash of it so the two can be associated by the + * provisioner. + */ + const char *uri; + + /** Out of Band information field. */ + bt_mesh_prov_oob_info_t oob_info; + + /** Static OOB value */ + const u8_t *static_val; + /** Static OOB value length */ + u8_t static_val_len; + + /** Maximum size of Output OOB supported */ + u8_t output_size; + /** Supported Output OOB Actions */ + u16_t output_actions; + + /* Maximum size of Input OOB supported */ + u8_t input_size; + /** Supported Input OOB Actions */ + u16_t input_actions; + + /** @brief Output of a number is requested. + * + * This callback notifies the application that it should + * output the given number using the given action. + * + * @param act Action for outputting the number. + * @param num Number to be outputted. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_number)(bt_mesh_output_action_t act, u32_t num); + + /** @brief Output of a string is requested. + * + * This callback notifies the application that it should + * display the given string to the user. + * + * @param str String to be displayed. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_string)(const char *str); + + /** @brief Input is requested. + * + * This callback notifies the application that it should + * request input from the user using the given action. The + * requested input will either be a string or a number, and + * the application needs to consequently call the + * bt_mesh_input_string() or bt_mesh_input_number() functions + * once the data has been acquired from the user. + * + * @param act Action for inputting data. + * @param num Maximum size of the inputted data. + * + * @return Zero on success or negative error code otherwise + */ + int (*input)(bt_mesh_input_action_t act, u8_t size); + + /** @brief The other device finished their OOB input. + * + * This callback notifies the application that it should stop + * displaying its output OOB value, as the other party finished their + * OOB input. + */ + void (*input_complete)(void); + + /** @brief Unprovisioned beacon has been received. + * + * This callback notifies the application that an unprovisioned + * beacon has been received. + * + * @param uuid UUID + * @param oob_info OOB Information + * @param uri_hash Pointer to URI Hash value. NULL if no hash was + * present in the beacon. + */ + void (*unprovisioned_beacon)(u8_t uuid[16], + bt_mesh_prov_oob_info_t oob_info, + u32_t *uri_hash); + + /** @brief Provisioning link has been opened. + * + * This callback notifies the application that a provisioning + * link has been opened on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_open)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning link has been closed. + * + * This callback notifies the application that a provisioning + * link has been closed on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_close)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning is complete. + * + * This callback notifies the application that provisioning has + * been successfully completed, and that the local node has been + * assigned the specified NetKeyIndex and primary element address. + * + * @param net_idx NetKeyIndex given during provisioning. + * @param addr Primary element address. + */ + void (*complete)(u16_t net_idx, u16_t addr); + + /** @brief A new node has been added to the provisioning database. + * + * This callback notifies the application that provisioning has + * been successfully completed, and that a node has been assigned + * the specified NetKeyIndex and primary element address. + * + * @param net_idx NetKeyIndex given during provisioning. + * @param addr Primary element address. + * @param num_elem Number of elements that this node has. + */ + void (*node_added)(u16_t net_idx, u16_t addr, u8_t num_elem); + + /** @brief Node has been reset. + * + * This callback notifies the application that the local node + * has been reset and needs to be reprovisioned. The node will + * not automatically advertise as unprovisioned, rather the + * bt_mesh_prov_enable() API needs to be called to enable + * unprovisioned advertising on one or more provisioning bearers. + */ + void (*reset)(void); +}; + +/** @brief Provide provisioning input OOB string. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BT_MESH_ENTER_STRING as the action. + * + * @param str String. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_string(const char *str); + +/** @brief Provide provisioning input OOB number. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BT_MESH_ENTER_NUMBER as the action. + * + * @param num Number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_number(u32_t num); + +/** @brief Enable specific provisioning bearers + * + * Enable one or more provisioning bearers. + * + * @param bearers Bit-wise or of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers); + +/** @brief Disable specific provisioning bearers + * + * Disable one or more provisioning bearers. + * + * @param bearers Bit-wise or of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers); + +/** + * @} + */ + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh Bluetooth Mesh + * @ingroup bluetooth + * @{ + */ + +/* Primary Network Key index */ +#define BT_MESH_NET_PRIMARY 0x000 + +#define BT_MESH_RELAY_DISABLED 0x00 +#define BT_MESH_RELAY_ENABLED 0x01 +#define BT_MESH_RELAY_NOT_SUPPORTED 0x02 + +#define BT_MESH_BEACON_DISABLED 0x00 +#define BT_MESH_BEACON_ENABLED 0x01 + +#define BT_MESH_GATT_PROXY_DISABLED 0x00 +#define BT_MESH_GATT_PROXY_ENABLED 0x01 +#define BT_MESH_GATT_PROXY_NOT_SUPPORTED 0x02 + +#define BT_MESH_FRIEND_DISABLED 0x00 +#define BT_MESH_FRIEND_ENABLED 0x01 +#define BT_MESH_FRIEND_NOT_SUPPORTED 0x02 + +#define BT_MESH_NODE_IDENTITY_STOPPED 0x00 +#define BT_MESH_NODE_IDENTITY_RUNNING 0x01 +#define BT_MESH_NODE_IDENTITY_NOT_SUPPORTED 0x02 + +/* Features */ +#define BT_MESH_FEAT_RELAY BIT(0) +#define BT_MESH_FEAT_PROXY BIT(1) +#define BT_MESH_FEAT_FRIEND BIT(2) +#define BT_MESH_FEAT_LOW_POWER BIT(3) +#define BT_MESH_FEAT_SUPPORTED (BT_MESH_FEAT_RELAY | \ + BT_MESH_FEAT_PROXY | \ + BT_MESH_FEAT_FRIEND | \ + BT_MESH_FEAT_LOW_POWER) + +/** @brief Initialize Mesh support + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + * @param own_addr_type Node address type + * @param prov Node provisioning information. + * @param comp Node Composition. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_init(u8_t own_addr_type, + const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp); + +/** @brief Reset the state of the local Mesh node. + * + * Resets the state of the node, which means that it needs to be + * reprovisioned to become an active node in a Mesh network again. + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + */ +void bt_mesh_reset(void); + +/** @brief Suspend the Mesh network temporarily. + * + * This API can be used for power saving purposes, but the user should be + * aware that leaving the local node suspended for a long period of time + * may cause it to become permanently disconnected from the Mesh network. + * If at all possible, the Friendship feature should be used instead, to + * make the node into a Low Power Node. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_suspend(void); + +/** @brief Resume a suspended Mesh network. + * + * This API resumes the local node, after it has been suspended using the + * bt_mesh_suspend() API. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_resume(void); + +/** @brief Provision the local Mesh Node. + * + * This API should normally not be used directly by the application. The + * only exception is for testing purposes where manual provisioning is + * desired without an actual external provisioner. + * + * @param net_key Network Key + * @param net_idx Network Key Index + * @param flags Provisioning Flags + * @param iv_index IV Index + * @param addr Primary element address + * @param dev_key Device Key + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]); + +/** @brief Provision a Mesh Node using PB-ADV + * + * @param uuid UUID + * @param net_idx Network Key Index + * @param addr Address to assign to remote device. If addr is 0, the lowest + * available address will be chosen. + * @param attention_duration The attention duration to be send to remote device + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provision_adv(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration); + +/** @brief Check if the local node has been provisioned. + * + * This API can be used to check if the local node has been provisioned + * or not. It can e.g. be helpful to determine if there was a stored + * network in flash, i.e. if the network was restored after calling + * settings_load(). + * + * @return True if the node is provisioned. False otherwise. + */ +bool bt_mesh_is_provisioned(void); + +/** @brief Toggle the IV Update test mode + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @param enable true to enable IV Update test mode, false to disable it. + */ +void bt_mesh_iv_update_test(bool enable); + +/** @brief Toggle the IV Update state + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @return true if IV Update In Progress state was entered, false otherwise. + */ +bool bt_mesh_iv_update(void); + +/** @brief Toggle the Low Power feature of the local device + * + * Enables or disables the Low Power feature of the local device. This is + * exposed as a run-time feature, since the device might want to change + * this e.g. based on being plugged into a stable power source or running + * from a battery power source. + * + * @param enable true to enable LPN functionality, false to disable it. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_set(bool enable); + +/** @brief Send out a Friend Poll message. + * + * Send a Friend Poll message to the Friend of this node. If there is no + * established Friendship the function will return an error. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_poll(void); + +/** @brief Register a callback for Friendship changes. + * + * Registers a callback that will be called whenever Friendship gets + * established or is lost. + * + * @param cb Function to call when the Friendship status changes. + */ +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_MAIN_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/mesh.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/mesh.h new file mode 100644 index 0000000..77b2871 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/mesh.h @@ -0,0 +1,26 @@ +/** @file + * @brief Bluetooth Mesh Profile APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_H +#define __BT_MESH_H + +#include +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "nimble/porting/nimble/include/os/os_mbuf.h" + +#include "glue.h" +#include "access.h" +#include "main.h" +#include "cfg_srv.h" +#include "health_srv.h" +#include "cfg_cli.h" +#include "health_cli.h" +#include "proxy.h" + +#endif /* __BT_MESH_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_cli.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_cli.h new file mode 100644 index 0000000..f2e77a4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_cli.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MODEL_CLI_H__ +#define __MODEL_CLI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_gen_model_cli { + struct bt_mesh_model *model; + + struct k_sem op_sync; + u32_t op_pending; + void *op_param; +}; + +extern const struct bt_mesh_model_op gen_onoff_cli_op[]; +extern const struct bt_mesh_model_cb bt_mesh_gen_onoff_cli_cb; + +#define BT_MESH_MODEL_GEN_ONOFF_CLI(cli_data, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, pub,\ + cli_data, &bt_mesh_gen_onoff_cli_cb) + +extern const struct bt_mesh_model_op gen_level_cli_op[]; +extern const struct bt_mesh_model_cb bt_mesh_gen_level_cli_cb; + +#define BT_MESH_MODEL_GEN_LEVEL_CLI(cli_data, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, gen_level_cli_op, pub,\ + cli_data, &bt_mesh_gen_level_cli_cb) + +int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *state); +int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t val, u8_t *state); +int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t *level); +int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t val, s16_t *state); + +#ifdef __cplusplus +} +#endif + +#endif /* __MODEL_CLI_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_srv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_srv.h new file mode 100644 index 0000000..e498ad3 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/model_srv.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MODEL_SRV_H__ +#define __MODEL_SRV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_gen_onoff_srv { + struct bt_mesh_model *model; + + int (*get)(struct bt_mesh_model *model, u8_t *state); + int (*set)(struct bt_mesh_model *model, u8_t state); +}; + +extern const struct bt_mesh_model_op gen_onoff_srv_op[]; +extern const struct bt_mesh_model_cb gen_onoff_srv_cb; + +#define BT_MESH_MODEL_GEN_ONOFF_SRV(srv, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, \ + gen_onoff_srv_op, pub, srv, &gen_onoff_srv_cb) + +struct bt_mesh_gen_level_srv { + struct bt_mesh_model *model; + + int (*get)(struct bt_mesh_model *model, s16_t *level); + int (*set)(struct bt_mesh_model *model, s16_t level); +}; + +extern const struct bt_mesh_model_op gen_level_srv_op[]; +extern const struct bt_mesh_model_cb gen_level_srv_cb; + +#define BT_MESH_MODEL_GEN_LEVEL_SRV(srv, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, \ + gen_level_srv_op, pub, srv, &gen_level_srv_cb) + +struct bt_mesh_light_lightness_srv { + struct bt_mesh_model *model; + + int (*get)(struct bt_mesh_model *model, s16_t *level); + int (*set)(struct bt_mesh_model *model, s16_t level); +}; + +extern const struct bt_mesh_model_op light_lightness_srv_op[]; +extern const struct bt_mesh_model_cb light_lightness_srv_cb; + +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(srv, pub) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, \ + light_lightness_srv_op, pub, srv, &light_lightness_srv_cb) + +void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, u8_t *state), + int (*set)(struct bt_mesh_model *model, u8_t state)); +void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level), + int (*set)(struct bt_mesh_model *model, s16_t level)); +void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level), + int (*set)(struct bt_mesh_model *model, s16_t level)); + +#ifdef __cplusplus +} +#endif + +#endif /* __MODEL_SRV_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/porting.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/porting.h new file mode 100644 index 0000000..1667a8a --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/porting.h @@ -0,0 +1,27 @@ +/** @file + * @brief Bluetooth Mesh Porting APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_PORTING_H +#define __BT_MESH_PORTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +void mesh_adv_thread(void *args); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_PORTING_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/proxy.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/proxy.h new file mode 100644 index 0000000..63bbfa3 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/proxy.h @@ -0,0 +1,43 @@ +/** @file + * @brief Bluetooth Mesh Proxy APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_PROXY_H +#define __BT_MESH_PROXY_H + +/** + * @brief Bluetooth Mesh Proxy + * @defgroup bt_mesh_proxy Bluetooth Mesh Proxy + * @ingroup bt_mesh + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable advertising with Node Identity. + * + * This API requires that GATT Proxy support has been enabled. Once called + * each subnet will start advertising using Node Identity for the next + * 60 seconds. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_proxy_identity_enable(void); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __BT_MESH_PROXY_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/slist.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/slist.h new file mode 100644 index 0000000..8a858f8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/slist.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Single-linked list implementation + * + * Single-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + */ + +#ifndef __SLIST_H__ +#define __SLIST_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct _snode { + struct _snode *next; +}; + +typedef struct _snode sys_snode_t; + +struct _slist { + sys_snode_t *head; + sys_snode_t *tail; +}; + +typedef struct _slist sys_slist_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + */ +#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_slist_peek_head(__sl); __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * it contains the starting node, or NULL to start from the head + */ +#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \ + : sys_slist_peek_head(__sl); \ + __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __sn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * @param __sns A sys_snode_t pointer for the loop to run safely + */ +#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_slist_peek_head(__sl), \ + __sns = sys_slist_peek_next(__sn); \ + __sn; __sn = __sns, \ + __sns = sys_slist_peek_next(__sn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __ln A pointer on a sys_node_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek container of the list tail + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ + +#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \ + __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \ + __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Initialize a list + * + * @param list A pointer on the list to initialize + */ +static inline void sys_slist_init(sys_slist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} + +#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL} + +/** + * @brief Test if the given list is empty + * + * @param list A pointer on the list to test + * + * @return a boolean, true if it's empty, false otherwise + */ +static inline bool sys_slist_is_empty(sys_slist_t *list) +{ + return (!list->head); +} + +/** + * @brief Peek the first node from the list + * + * @param list A point on the list to peek the first node from + * + * @return A pointer on the first node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list) +{ + return list->head; +} + +/** + * @brief Peek the last node from the list + * + * @param list A point on the list to peek the last node from + * + * @return A pointer on the last node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) +{ + return list->tail; +} + +/** + * @brief Peek the next node from current node, node is not NULL + * + * Faster then sys_slist_peek_next() if node is known not to be NULL. + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) +{ + return node->next; +} + +/** + * @brief Peek the next node from current node + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node) +{ + return node ? sys_slist_peek_next_no_check(node) : NULL; +} + +/** + * @brief Prepend a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to prepend + */ +static inline void sys_slist_prepend(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = list->head; + list->head = node; + + if (!list->tail) { + list->tail = list->head; + } +} + +/** + * @brief Append a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to append + */ +static inline void sys_slist_append(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = NULL; + + if (!list->tail) { + list->tail = node; + list->head = node; + } else { + list->tail->next = node; + list->tail = node; + } +} + +/** + * @brief Append a list to the given list + * + * Append a singly-linked, NULL-terminated list consisting of nodes containing + * the pointer to the next node as the first element of a node, to @a list. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param head A pointer to the first element of the list to append + * @param tail A pointer to the last element of the list to append + */ +static inline void sys_slist_append_list(sys_slist_t *list, + void *head, void *tail) +{ + if (!list->tail) { + list->head = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } else { + list->tail->next = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } +} + +/** + * @brief merge two slists, appending the second one to the first + * + * When the operation is completed, the appending list is empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param list_to_append A pointer to the list to append. + */ +static inline void sys_slist_merge_slist(sys_slist_t *list, + sys_slist_t *list_to_append) +{ + sys_slist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_slist_init(list_to_append); +} + +/** + * @brief Insert a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev A pointer on the previous node + * @param node A pointer on the node to insert + */ +static inline void sys_slist_insert(sys_slist_t *list, + sys_snode_t *prev, + sys_snode_t *node) +{ + if (!prev) { + sys_slist_prepend(list, node); + } else if (!prev->next) { + sys_slist_append(list, node); + } else { + node->next = prev->next; + prev->next = node; + } +} + +/** + * @brief Fetch and remove the first node of the given list + * + * List must be known to be non-empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list + */ +static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) +{ + sys_snode_t *node = list->head; + + list->head = node->next; + if (list->tail == node) { + list->tail = list->head; + } + + return node; +} + +/** + * @brief Fetch and remove the first node of the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list (or NULL if empty) + */ +static inline sys_snode_t *sys_slist_get(sys_slist_t *list) +{ + return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); +} + +/** + * @brief Remove a node + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev_node A pointer on the previous node + * (can be NULL, which means the node is the list's head) + * @param node A pointer on the node to remove + */ +static inline void sys_slist_remove(sys_slist_t *list, + sys_snode_t *prev_node, + sys_snode_t *node) +{ + if (!prev_node) { + list->head = node->next; + + /* Was node also the tail? */ + if (list->tail == node) { + list->tail = list->head; + } + } else { + prev_node->next = node->next; + + /* Was node the tail? */ + if (list->tail == node) { + list->tail = prev_node; + } + } + + node->next = NULL; +} + +/** + * @brief Find and remove a node from a list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to remove from the list + * + * @return true if node was removed + */ +static inline bool sys_slist_find_and_remove(sys_slist_t *list, + sys_snode_t *node) +{ + sys_snode_t *prev = NULL; + sys_snode_t *test; + + SYS_SLIST_FOR_EACH_NODE(list, test) { + if (test == node) { + sys_slist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* __SLIST_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/testing.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/testing.h new file mode 100644 index 0000000..4c2b2a6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/include/mesh/testing.h @@ -0,0 +1,105 @@ +/** + * @file testing.h + * @brief Internal API for Bluetooth testing. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BT_TESTING_H +#define __BT_TESTING_H + +#include "slist.h" +#include "glue.h" +#include "access.h" + +/** + * @brief Bluetooth testing + * @defgroup bt_test_cb Bluetooth testing callbacks + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth Testing callbacks structure. + * + * Callback structure to be used for Bluetooth testing purposes. + * Allows access to Bluetooth stack internals, not exposed by public API. + */ +struct bt_test_cb { + void (*mesh_net_recv)(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, + const void *payload, size_t payload_len); + void (*mesh_model_bound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_model_unbound)(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx); + void (*mesh_prov_invalid_bearer)(u8_t opcode); + void (*mesh_trans_incomp_timer_exp)(void); + + sys_snode_t node; +}; + +/** Register callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_register(struct bt_test_cb *cb); + +/** Unregister callbacks for Bluetooth testing purposes + * + * @param cb bt_test_cb callback structure + */ +void bt_test_cb_unregister(struct bt_test_cb *cb); + +/** Send Friend Subscription List Add message. + * + * Used by Low Power node to send the group address for which messages are to + * be stored by Friend node. + * + * @param group Group address + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_lpn_group_add(u16_t group); + +/** Send Friend Subscription List Remove message. + * + * Used by Low Power node to remove the group addresses from Friend node + * subscription list. Messages sent to those addresses will not be stored + * by Friend node. + * + * @param groups Group addresses + * @param groups_count Group addresses count + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_lpn_group_remove(u16_t *groups, size_t groups_count); + +/** Clear replay protection list cache. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_test_mesh_rpl_clear(void); + +u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx); +u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store); +int cmd_mesh_init(int argc, char *argv[]); + +int bt_test_shell_init(void); +int bt_test_bind_app_key_to_model(struct bt_mesh_model *model, u16_t key_idx, u16_t id); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_TESTING_H */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.c new file mode 100644 index 0000000..37580b4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.c @@ -0,0 +1,859 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_ACCESS_LOG + +#include +#include "nimble/porting/nimble/include/os/os_mbuf.h" + +#include "../include/mesh/mesh.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +#include "../include/mesh/model_cli.h" +#endif + +static const struct bt_mesh_comp *dev_comp; +static u16_t dev_primary_addr; + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data) +{ + int i, j; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + for (j = 0; j < elem->model_count; j++) { + struct bt_mesh_model *model = &elem->models[j]; + + func(model, elem, false, i == 0, user_data); + } + + for (j = 0; j < elem->vnd_model_count; j++) { + struct bt_mesh_model *model = &elem->vnd_models[j]; + + func(model, elem, true, i == 0, user_data); + } + } +} + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod) +{ + int period; + + if (!mod->pub) { + return 0; + } + + switch (mod->pub->period >> 6) { + case 0x00: + /* 1 step is 100 ms */ + period = K_MSEC((mod->pub->period & BIT_MASK(6)) * 100); + break; + case 0x01: + /* 1 step is 1 second */ + period = K_SECONDS(mod->pub->period & BIT_MASK(6)); + break; + case 0x02: + /* 1 step is 10 seconds */ + period = K_SECONDS((mod->pub->period & BIT_MASK(6)) * 10); + break; + case 0x03: + /* 1 step is 10 minutes */ + period = K_MINUTES((mod->pub->period & BIT_MASK(6)) * 10); + break; + default: + CODE_UNREACHABLE; + } + + if (mod->pub->fast_period) { + return period >> mod->pub->period_div; + } else { + return period; + } +} + +static s32_t next_period(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + u32_t elapsed, period; + + period = bt_mesh_model_pub_period_get(mod); + if (!period) { + return 0; + } + + elapsed = k_uptime_get_32() - pub->period_start; + + BT_DBG("Publishing took %ums", (unsigned) elapsed); + + if (elapsed > period) { + BT_WARN("Publication sending took longer than the period"); + /* Return smallest positive number since 0 means disabled */ + return K_MSEC(1); + } + + return period - elapsed; +} + +static void publish_sent(int err, void *user_data) +{ + struct bt_mesh_model *mod = user_data; + s32_t delay; + + BT_DBG("err %d", err); + + if (mod->pub->count) { + delay = BT_MESH_PUB_TRANSMIT_INT(mod->pub->retransmit); + } else { + delay = next_period(mod); + } + + if (delay) { + BT_DBG("Publishing next time in %dms", (int) delay); + k_delayed_work_submit(&mod->pub->timer, delay); + } +} + +static void publish_start(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_model *mod = user_data; + struct bt_mesh_model_pub *pub = mod->pub; + + if (err) { + BT_ERR("Failed to publish: err %d", err); + return; + } + + /* Initialize the timestamp for the beginning of a new period */ + if (pub->count == BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit)) { + pub->period_start = k_uptime_get_32(); + } +} + +static const struct bt_mesh_send_cb pub_sent_cb = { + .start = publish_start, + .end = publish_sent, +}; + +static int publish_retransmit(struct bt_mesh_model *mod) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_model_pub *pub = mod->pub; + struct bt_mesh_app_key *key; + struct bt_mesh_msg_ctx ctx = { + .addr = pub->addr, + .send_ttl = pub->ttl, + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = bt_mesh_model_elem(mod)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = pub->cred, + }; + int err; + + key = bt_mesh_app_key_find(pub->key); + if (!key) { + err = -EADDRNOTAVAIL; + goto done; + } + + tx.sub = bt_mesh_subnet_get(key->net_idx); + + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + + net_buf_simple_init(sdu, 0); + net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len); + + pub->count--; + + err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod); + +done: + os_mbuf_free_chain(sdu); + return err; +} + +static void mod_publish(struct ble_npl_event *work) +{ + struct bt_mesh_model_pub *pub = ble_npl_event_get_arg(work); + s32_t period_ms; + int err; + + BT_DBG(""); + + period_ms = bt_mesh_model_pub_period_get(pub->mod); + BT_DBG("period %u ms", (unsigned) period_ms); + + if (pub->count) { + err = publish_retransmit(pub->mod); + if (err) { + BT_ERR("Failed to retransmit (err %d)", err); + + pub->count = 0; + + /* Continue with normal publication */ + if (period_ms) { + k_delayed_work_submit(&pub->timer, period_ms); + } + } + + return; + } + + if (!period_ms) { + return; + } + + __ASSERT_NO_MSG(pub->update != NULL); + + err = pub->update(pub->mod); + if (err) { + BT_ERR("Failed to update publication message"); + return; + } + + err = bt_mesh_model_publish(pub->mod); + if (err) { + BT_ERR("Publishing failed (err %d)", err); + } +} + +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod) +{ + return &dev_comp->elem[mod->elem_idx]; +} + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx) +{ + struct bt_mesh_elem *elem; + + if (elem_idx >= dev_comp->elem_count) { + BT_ERR("Invalid element index %u", elem_idx); + return NULL; + } + + elem = &dev_comp->elem[elem_idx]; + + if (vnd) { + if (mod_idx >= elem->vnd_model_count) { + BT_ERR("Invalid vendor model index %u", mod_idx); + return NULL; + } + + return &elem->vnd_models[mod_idx]; + } else { + if (mod_idx >= elem->model_count) { + BT_ERR("Invalid SIG model index %u", mod_idx); + return NULL; + } + + return &elem->models[mod_idx]; + } +} + +static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + int i; + + if (mod->pub) { + mod->pub->mod = mod; + k_delayed_work_init(&mod->pub->timer, mod_publish); + k_delayed_work_add_arg(&mod->pub->timer, mod->pub); + } + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + mod->keys[i] = BT_MESH_KEY_UNUSED; + } + + mod->elem_idx = elem - dev_comp->elem; + if (vnd) { + mod->mod_idx = mod - elem->vnd_models; + } else { + mod->mod_idx = mod - elem->models; + } + + if (mod->cb && mod->cb->init) { + mod->cb->init(mod); + } +} + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp) +{ + /* There must be at least one element */ + if (!comp->elem_count) { + return -EINVAL; + } + + dev_comp = comp; + + bt_mesh_model_foreach(mod_init, NULL); + + return 0; +} + +void bt_mesh_comp_provision(u16_t addr) +{ + int i; + + dev_primary_addr = addr; + + BT_DBG("addr 0x%04x elem_count %zu", addr, dev_comp->elem_count); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + elem->addr = addr++; + + BT_DBG("addr 0x%04x mod_count %u vnd_mod_count %u", + elem->addr, elem->model_count, elem->vnd_model_count); + } +} + +void bt_mesh_comp_unprovision(void) +{ + BT_DBG(""); + + dev_primary_addr = BT_MESH_ADDR_UNASSIGNED; + + bt_mesh_model_foreach(mod_init, NULL); +} + +u16_t bt_mesh_primary_addr(void) +{ + return dev_primary_addr; +} + +static u16_t *model_group_get(struct bt_mesh_model *mod, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == addr) { + return &mod->groups[i]; + } + } + + return NULL; +} + +struct find_group_visitor_ctx { + u16_t *entry; + struct bt_mesh_model *mod; + u16_t addr; +}; + +static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, + u32_t depth, void *user_data) +{ + struct find_group_visitor_ctx *ctx = user_data; + + if (mod->elem_idx != ctx->mod->elem_idx) { + return BT_MESH_WALK_CONTINUE; + } + + ctx->entry = model_group_get(mod, ctx->addr); + if (ctx->entry) { + ctx->mod = mod; + return BT_MESH_WALK_STOP; + } + + return BT_MESH_WALK_CONTINUE; +} + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr) +{ + struct find_group_visitor_ctx ctx = { + .mod = *mod, + .entry = NULL, + .addr = addr, + }; + + bt_mesh_model_tree_walk(bt_mesh_model_root(*mod), + find_group_mod_visitor, &ctx); + + *mod = ctx.mod; + return ctx.entry; +} + +static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, + u16_t group_addr) +{ + struct bt_mesh_model *model; + u16_t *match; + int i; + + for (i = 0; i < elem->model_count; i++) { + model = &elem->models[i]; + + match = model_group_get(model, group_addr); + if (match) { + return model; + } + } + + for (i = 0; i < elem->vnd_model_count; i++) { + model = &elem->vnd_models[i]; + + match = model_group_get(model, group_addr); + if (match) { + return model; + } + } + + return NULL; +} + +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr) +{ + u16_t index; + + if (BT_MESH_ADDR_IS_UNICAST(addr)) { + index = (addr - dev_comp->elem[0].addr); + if (index < dev_comp->elem_count) { + return &dev_comp->elem[index]; + } else { + return NULL; + } + } + + for (index = 0; index < dev_comp->elem_count; index++) { + struct bt_mesh_elem *elem = &dev_comp->elem[index]; + + if (bt_mesh_elem_find_group(elem, addr)) { + return elem; + } + } + + return NULL; +} + +u8_t bt_mesh_elem_count(void) +{ + return dev_comp->elem_count; +} + +static bool model_has_key(struct bt_mesh_model *mod, u16_t key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] == key || + (mod->keys[i] == BT_MESH_KEY_DEV_ANY && + BT_MESH_IS_DEV_KEY(key))) { + return true; + } + } + + return false; +} + +static bool model_has_dst(struct bt_mesh_model *mod, u16_t dst) +{ + if (BT_MESH_ADDR_IS_UNICAST(dst)) { + return (dev_comp->elem[mod->elem_idx].addr == dst); + } else if (BT_MESH_ADDR_IS_GROUP(dst) || BT_MESH_ADDR_IS_VIRTUAL(dst)) { + return bt_mesh_model_find_group(&mod, dst); + } + + return (mod->elem_idx == 0 && bt_mesh_fixed_group_match(dst)); +} + +static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models, + u8_t model_count, u32_t opcode, + struct bt_mesh_model **model) +{ + u8_t i; + + for (i = 0; i < model_count; i++) { + const struct bt_mesh_model_op *op; + + *model = &models[i]; + + for (op = (*model)->op; op->func; op++) { + if (op->opcode == opcode) { + return op; + } + } + } + + *model = NULL; + return NULL; +} + +static int get_opcode(struct os_mbuf *buf, u32_t *opcode) +{ + switch (buf->om_data[0] >> 6) { + case 0x00: + case 0x01: + if (buf->om_data[0] == 0x7f) { + BT_ERR("Ignoring RFU OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf); + return 0; + case 0x02: + if (buf->om_len < 2) { + BT_ERR("Too short payload for 2-octet OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_be16(buf); + return 0; + case 0x03: + if (buf->om_len < 3) { + BT_ERR("Too short payload for 3-octet OpCode"); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf) << 16; + *opcode |= net_buf_simple_pull_le16(buf); + return 0; + } + + CODE_UNREACHABLE; +} + +bool bt_mesh_fixed_group_match(u16_t addr) +{ + /* Check for fixed group addresses */ + switch (addr) { + case BT_MESH_ADDR_ALL_NODES: + return true; + case BT_MESH_ADDR_PROXIES: + return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); + case BT_MESH_ADDR_FRIENDS: + return (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED); + case BT_MESH_ADDR_RELAYS: + return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); + default: + return false; + } +} + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_model *models, *model; + const struct bt_mesh_model_op *op; + u32_t opcode; + u8_t count; + int i; + + BT_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", rx->ctx.app_idx, + rx->ctx.addr, rx->ctx.recv_dst); + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (get_opcode(buf, &opcode) < 0) { + BT_WARN("Unable to decode OpCode"); + return; + } + + BT_DBG("OpCode 0x%08x", (unsigned) opcode); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + struct net_buf_simple_state state; + + /* SIG models cannot contain 3-byte (vendor) OpCodes, and + * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so + * we only need to do the lookup in one of the model lists. + */ + if (BT_MESH_MODEL_OP_LEN(opcode) < 3) { + models = elem->models; + count = elem->model_count; + } else { + models = elem->vnd_models; + count = elem->vnd_model_count; + } + + op = find_op(models, count, opcode, &model); + if (!op) { + BT_DBG("No OpCode 0x%08x for elem %d", opcode, i); + continue; + } + + if (!model_has_key(model, rx->ctx.app_idx)) { + continue; + } + + if (!model_has_dst(model, rx->ctx.recv_dst)) { + continue; + } + + if (buf->om_len < op->min_len) { + BT_ERR("Too short message for OpCode 0x%08x", opcode); + continue; + } + + /* The callback will likely parse the buffer, so + * store the parsing state in case multiple models + * receive the message. + */ + net_buf_simple_save(buf, &state); + op->func(model, &rx->ctx, buf); + net_buf_simple_restore(buf, &state); + } +} + +void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode) +{ + net_buf_simple_init(msg, 0); + + switch (BT_MESH_MODEL_OP_LEN(opcode)) { + case 1: + net_buf_simple_add_u8(msg, opcode); + break; + case 2: + net_buf_simple_add_be16(msg, opcode); + break; + case 3: + net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff)); + net_buf_simple_add_le16(msg, opcode & 0xffff); + break; + default: + BT_WARN("Unknown opcode format"); + break; + } +} + +static int model_send(struct bt_mesh_model *model, + struct bt_mesh_net_tx *tx, bool implicit_bind, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->ctx->net_idx, + tx->ctx->app_idx, tx->ctx->addr); + BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len)); + + if (!bt_mesh_is_provisioned()) { + BT_ERR("Local node is not yet provisioned"); + return -EAGAIN; + } + + if (net_buf_simple_tailroom(msg) < 4) { + BT_ERR("Not enough tailroom for TransMIC"); + return -EINVAL; + } + + if (msg->om_len > BT_MESH_TX_SDU_MAX - 4) { + BT_ERR("Too big message"); + return -EMSGSIZE; + } + + if (!implicit_bind && !model_has_key(model, tx->ctx->app_idx)) { + BT_ERR("Model not bound to AppKey 0x%04x", tx->ctx->app_idx); + return -EINVAL; + } + + return bt_mesh_trans_send(tx, msg, cb, cb_data); +} + +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct bt_mesh_net_tx tx = { + .sub = bt_mesh_subnet_get(ctx->net_idx), + .ctx = ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = 0, + }; + + return model_send(model, &tx, false, msg, cb, cb_data); +} + +int bt_mesh_model_publish(struct bt_mesh_model *model) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_model_pub *pub = model->pub; + struct bt_mesh_app_key *key; + struct bt_mesh_msg_ctx ctx = { + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + }; + int err; + + BT_DBG(""); + + if (!pub) { + err = -ENOTSUP; + goto done; + } + + if (pub->addr == BT_MESH_ADDR_UNASSIGNED) { + err = -EADDRNOTAVAIL; + goto done; + } + + key = bt_mesh_app_key_find(pub->key); + if (!key) { + err = -EADDRNOTAVAIL; + goto done; + } + + if (pub->msg->om_len + 4 > BT_MESH_TX_SDU_MAX) { + BT_ERR("Message does not fit maximum SDU size"); + err = -EMSGSIZE; + goto done; + } + + if (pub->count) { + BT_WARN("Clearing publish retransmit timer"); + k_delayed_work_cancel(&pub->timer); + } + + net_buf_simple_init(sdu, 0); + net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len); + + ctx.addr = pub->addr; + ctx.send_ttl = pub->ttl; + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + + tx.friend_cred = pub->cred; + tx.sub = bt_mesh_subnet_get(ctx.net_idx), + + pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit); + + BT_DBG("Publish Retransmit Count %u Interval %ums", pub->count, + BT_MESH_PUB_TRANSMIT_INT(pub->retransmit)); + + err = model_send(model, &tx, true, sdu, &pub_sent_cb, model); + if (err) { + /* Don't try retransmissions for this publish attempt */ + pub->count = 0; + /* Make sure the publish timer gets reset */ + publish_sent(err, model); + } + +done: + os_mbuf_free_chain(sdu); + return err; +} + +struct bt_mesh_model *bt_mesh_model_find_vnd(const struct bt_mesh_elem *elem, + u16_t company, u16_t id) +{ + u8_t i; + + for (i = 0; i < elem->vnd_model_count; i++) { + if (elem->vnd_models[i].vnd.company == company && + elem->vnd_models[i].vnd.id == id) { + return &elem->vnd_models[i]; + } + } + + return NULL; +} + +struct bt_mesh_model *bt_mesh_model_find(const struct bt_mesh_elem *elem, + u16_t id) +{ + u8_t i; + + for (i = 0; i < elem->model_count; i++) { + if (elem->models[i].id == id) { + return &elem->models[i]; + } + } + + return NULL; +} + +const struct bt_mesh_comp *bt_mesh_comp_get(void) +{ + return dev_comp; +} + +struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod) +{ +#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS + while (mod->next) { + mod = mod->next; + } +#endif + return mod; +} + +void bt_mesh_model_tree_walk(struct bt_mesh_model *root, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + u32_t depth, + void *user_data), + void *user_data) +{ + struct bt_mesh_model *m = root; + u32_t depth = 0; + + do { + if (cb(m, depth, user_data) == BT_MESH_WALK_STOP) { + return; + } +#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS) + if (m->extends) { + m = m->extends; + depth++; + } else if (m->flags & BT_MESH_MOD_NEXT_IS_PARENT) { + m = m->next->next; + depth--; + } else { + m = m->next; + } +#endif + } while (m && m != root); +} + +#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS) +int bt_mesh_model_extend(struct bt_mesh_model *mod, + struct bt_mesh_model *base_mod) +{ + /* Form a cyclical LCRS tree: + * The extends-pointer points to the first child, and the next-pointer + * points to the next sibling. The last sibling is marked by the + * BT_MESH_MOD_NEXT_IS_PARENT flag, and its next-pointer points back to + * the parent. This way, the whole tree is accessible from any node. + * + * We add children (extend them) by inserting them as the first child. + */ + if (base_mod->next) { + return -EALREADY; + } + + if (mod->extends) { + base_mod->next = mod->extends; + } else { + base_mod->next = mod; + base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT; + } + + mod->extends = base_mod; + return 0; +} +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.h new file mode 100644 index 0000000..03081b9 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/access.h @@ -0,0 +1,67 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ACCESS_H__ +#define __ACCESS_H__ + +#include "../include/mesh/mesh.h" + +/* bt_mesh_model.flags */ +enum { + BT_MESH_MOD_BIND_PENDING = BIT(0), + BT_MESH_MOD_SUB_PENDING = BIT(1), + BT_MESH_MOD_PUB_PENDING = BIT(2), + BT_MESH_MOD_DATA_PRESENT = BIT(3), + BT_MESH_MOD_NEXT_IS_PARENT = BIT(4), +}; + +/* Tree walk return codes */ +enum bt_mesh_walk { + BT_MESH_WALK_STOP, + BT_MESH_WALK_CONTINUE, +}; + +void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count); + +u8_t bt_mesh_elem_count(void); + +/* Find local element based on unicast or group address */ +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr); + +struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod); +void bt_mesh_model_tree_walk(struct bt_mesh_model *root, + enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod, + u32_t depth, + void *user_data), + void *user_data); + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr); + +bool bt_mesh_fixed_group_match(u16_t addr); + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data); + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod); + +void bt_mesh_comp_provision(u16_t addr); +void bt_mesh_comp_unprovision(void); + +u16_t bt_mesh_primary_addr(void); + +const struct bt_mesh_comp *bt_mesh_comp_get(void); + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx); + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp); +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.c new file mode 100644 index 0000000..5af4f18 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.c @@ -0,0 +1,450 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_ADV_LOG + +#include "../include/mesh/mesh.h" +#include "nimble/nimble/host/include/host/ble_hs_adv.h" +#include "nimble/nimble/host/include/host/ble_gap.h" +#include "nimble/nimble/include/nimble/hci_common.h" +#include "../include/mesh/porting.h" +#include "nimble/porting/nimble/include/nimble/nimble_port.h" +#include "adv.h" +#include "net.h" +#include "foundation.h" +#include "beacon.h" +#include "prov.h" +#include "proxy.h" + +/* Convert from ms to 0.625ms units */ +#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5) + +/* Window and Interval are equal for continuous scanning */ +#define MESH_SCAN_INTERVAL_MS 30 +#define MESH_SCAN_WINDOW_MS 30 +#define MESH_SCAN_INTERVAL ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS) +#define MESH_SCAN_WINDOW ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS) + +/* Pre-5.0 controllers enforce a minimum interval of 100ms + * whereas 5.0+ controllers can go down to 20ms. + */ +#define ADV_INT_DEFAULT_MS 100 +#define ADV_INT_FAST_MS 20 + +static s32_t adv_int_min = ADV_INT_DEFAULT_MS; + +/* TinyCrypt PRNG consumes a lot of stack space, so we need to have + * an increased call stack whenever it's used. + */ +#if MYNEWT +#define ADV_STACK_SIZE 768 +OS_TASK_STACK_DEFINE(g_blemesh_stack, ADV_STACK_SIZE); +struct os_task adv_task; +#else +static TaskHandle_t adv_task_h; +#endif + +static struct ble_npl_eventq adv_queue; +extern u8_t g_mesh_addr_type; +static int adv_initialized = false; + +static os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE( + MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)]; + +struct os_mbuf_pool adv_os_mbuf_pool; +static struct os_mempool adv_buf_mempool; + +static const u8_t adv_type[] = { + [BT_MESH_ADV_PROV] = BLE_HS_ADV_TYPE_MESH_PROV, + [BT_MESH_ADV_DATA] = BLE_HS_ADV_TYPE_MESH_MESSAGE, + [BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON, + [BT_MESH_ADV_URI] = BLE_HS_ADV_TYPE_URI, +}; + + +static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + return &adv_pool[id]; +} + +static inline void adv_send_start(u16_t duration, int err, + const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->start) { + cb->start(duration, err, cb_data); + } +} + +static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->end) { + cb->end(err, cb_data); + } +} + +static inline void adv_send(struct os_mbuf *buf) +{ + const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb; + void *cb_data = BT_MESH_ADV(buf)->cb_data; + struct ble_gap_adv_params param = { 0 }; + u16_t duration, adv_int; + struct bt_data ad; + int err; + + adv_int = max(adv_int_min, + BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit)); +#if MYNEWT_VAL(BLE_CONTROLLER) + duration = ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * + (adv_int + 10)); +#else + duration = (MESH_SCAN_WINDOW_MS + + ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * + (adv_int + 10))); +#endif + + BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type, + buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("count %u interval %ums duration %ums", + BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int, + duration); + + ad.type = adv_type[BT_MESH_ADV(buf)->type]; + ad.data_len = buf->om_len; + ad.data = buf->om_data; + + param.itvl_min = ADV_SCAN_UNIT(adv_int); + param.itvl_max = param.itvl_min; + param.conn_mode = BLE_GAP_CONN_MODE_NON; + + err = bt_le_adv_start(¶m, &ad, 1, NULL, 0); + net_buf_unref(buf); + adv_send_start(duration, err, cb, cb_data); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return; + } + + BT_DBG("Advertising started. Sleeping %u ms", duration); + + k_sleep(K_MSEC(duration)); + + err = bt_le_adv_stop(false); + adv_send_end(err, cb, cb_data); + if (err) { + BT_ERR("Stopping advertising failed: err %d", err); + return; + } + + BT_DBG("Advertising stopped"); +} + +void +mesh_adv_thread(void *args) +{ + static struct ble_npl_event *ev; + struct os_mbuf *buf; +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + s32_t timeout; +#endif + + BT_DBG("started"); + + while (1) { +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + ev = ble_npl_eventq_get(&adv_queue, 0); + while (!ev) { + timeout = bt_mesh_proxy_adv_start(); + BT_DBG("Proxy Advertising up to %d ms", (int) timeout); + + // FIXME: should we redefine K_SECONDS macro instead in glue? + if (timeout != K_FOREVER) { + timeout = ble_npl_time_ms_to_ticks32(timeout); + } + + ev = ble_npl_eventq_get(&adv_queue, timeout); + bt_mesh_proxy_adv_stop(); + } +#else + ev = ble_npl_eventq_get(&adv_queue, BLE_NPL_TIME_FOREVER); +#endif + + if (!ev || !ble_npl_event_get_arg(ev)) { + continue; + } + + buf = ble_npl_event_get_arg(ev); + + /* busy == 0 means this was canceled */ + if (BT_MESH_ADV(buf)->busy) { + BT_MESH_ADV(buf)->busy = 0; + adv_send(buf); + } else { + net_buf_unref(buf); + } + + /* os_sched(NULL); */ + } +} + +void bt_mesh_adv_update(void) +{ + static struct ble_npl_event ev = { }; + + BT_DBG(""); + + ble_npl_eventq_put(&adv_queue, &ev); +} + +struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout) +{ + struct bt_mesh_adv *adv; + struct os_mbuf *buf; + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + BT_WARN("Refusing to allocate buffer while suspended"); + return NULL; + } + + buf = os_mbuf_get_pkthdr(pool, BT_MESH_ADV_USER_DATA_SIZE); + if (!buf) { + return NULL; + } + + adv = get_id(net_buf_id(buf)); + BT_MESH_ADV(buf) = adv; + + memset(adv, 0, sizeof(*adv)); + + adv->type = type; + adv->xmit = xmit; + + adv->ref_cnt = 1; + ble_npl_event_set_arg(&adv->ev, buf); + + return buf; +} + +struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout) +{ + return bt_mesh_adv_create_from_pool(&adv_os_mbuf_pool, adv_alloc, type, + xmit, timeout); +} + +void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + BT_DBG("buf %p, type 0x%02x len %u: %s", buf, BT_MESH_ADV(buf)->type, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + BT_MESH_ADV(buf)->cb = cb; + BT_MESH_ADV(buf)->cb_data = cb_data; + BT_MESH_ADV(buf)->busy = 1; + + net_buf_put(&adv_queue, net_buf_ref(buf)); +} + +static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi, + u8_t adv_type, struct os_mbuf *buf) +{ + if (adv_type != BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { + return; + } + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); +#endif + + while (buf->om_len > 1) { + struct net_buf_simple_state state; + u8_t len, type; + + len = net_buf_simple_pull_u8(buf); + /* Check for early termination */ + if (len == 0) { + return; + } + + if (len > buf->om_len) { + BT_WARN("AD malformed"); + return; + } + + net_buf_simple_save(buf, &state); + + type = net_buf_simple_pull_u8(buf); + + switch (type) { + case BLE_HS_ADV_TYPE_MESH_MESSAGE: + bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV); + break; +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + case BLE_HS_ADV_TYPE_MESH_PROV: + bt_mesh_pb_adv_recv(buf); + break; +#endif + case BLE_HS_ADV_TYPE_MESH_BEACON: + bt_mesh_beacon_recv(buf); + break; + default: + break; + } + + net_buf_simple_restore(buf, &state); + net_buf_simple_pull(buf, len); + } +} + +void bt_mesh_adv_init(void) +{ + int rc; + + /* Advertising should only be initialized once. Calling + * os_task initialize the second time will result in an assert. */ + if (adv_initialized) { + return; + } + + rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + adv_buf_mem, "adv_buf_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT)); + assert(rc == 0); + + ble_npl_eventq_init(&adv_queue); + +#if MYNEWT + os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL, + MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER, + g_blemesh_stack, ADV_STACK_SIZE); +#elif ESP_PLATFORM + xTaskCreatePinnedToCore(mesh_adv_thread, "mesh_adv", 2768, + NULL, (configMAX_PRIORITIES - 5), &adv_task_h, NIMBLE_CORE); +#else + xTaskCreate(mesh_adv_thread, "mesh_adv", MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE), + NULL, MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), &adv_task_h); +#endif + + /* For BT5 controllers we can have fast advertising interval */ + if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) { + adv_int_min = ADV_INT_FAST_MS; + } + + adv_initialized = true; +} + +int +ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_desc *ext_desc; +#endif + struct ble_gap_disc_desc *desc; + struct os_mbuf *buf = NULL; + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("event->type %d", event->type); +#endif + + switch (event->type) { +#if MYNEWT_VAL(BLE_EXT_ADV) + case BLE_GAP_EVENT_EXT_DISC: + ext_desc = &event->ext_disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, ext_desc->data, ext_desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + bt_mesh_scan_cb(&ext_desc->addr, ext_desc->rssi, + ext_desc->legacy_event_type, buf); + break; +#endif + case BLE_GAP_EVENT_DISC: + desc = &event->disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, desc->data, desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + + bt_mesh_scan_cb(&desc->addr, desc->rssi, desc->event_type, buf); + break; + default: + break; + } + +done: + if (buf) { + os_mbuf_free_chain(buf); + } + + return 0; +} + +int bt_mesh_scan_enable(void) +{ + int err; + +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_params uncoded_params = + { .itvl = MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW, + .passive = 1 }; + + BT_DBG(""); + + err = ble_gap_ext_disc(g_mesh_addr_type, 0, 0, 0, 0, 0, + &uncoded_params, NULL, NULL, NULL); +#else + struct ble_gap_disc_params scan_param = + { .passive = 1, .filter_duplicates = 0, .itvl = + MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW }; + + BT_DBG(""); + + err = ble_gap_disc(g_mesh_addr_type, BLE_HS_FOREVER, &scan_param, + NULL, NULL); +#endif + if (err && err != BLE_HS_EALREADY) { + BT_ERR("starting scan failed (err %d)", err); + return err; + } + + return 0; +} + +int bt_mesh_scan_disable(void) +{ + int err; + + BT_DBG(""); + + err = ble_gap_disc_cancel(); + if (err && err != BLE_HS_EALREADY) { + BT_ERR("stopping scan failed (err %d)", err); + return err; + } + + return 0; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.h new file mode 100644 index 0000000..40e65e5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/adv.h @@ -0,0 +1,79 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ADV_H__ +#define __ADV_H__ + +/* Maximum advertising data payload for a single data type */ +#include "../include/mesh/mesh.h" + +#define BT_MESH_ADV(om) (*(struct bt_mesh_adv **) OS_MBUF_USRHDR(om)) + +#define BT_MESH_ADV_DATA_SIZE 31 + +/* The user data is a pointer (4 bytes) to struct bt_mesh_adv */ +#define BT_MESH_ADV_USER_DATA_SIZE (sizeof(struct bt_mesh_adv *)) + +#define BT_MESH_MBUF_HEADER_SIZE (sizeof(struct os_mbuf_pkthdr) + \ + BT_MESH_ADV_USER_DATA_SIZE +\ + sizeof(struct os_mbuf)) + +enum bt_mesh_adv_type +{ + BT_MESH_ADV_PROV, + BT_MESH_ADV_DATA, + BT_MESH_ADV_BEACON, + BT_MESH_ADV_URI, +}; + +typedef void (*bt_mesh_adv_func_t)(struct os_mbuf *buf, u16_t duration, + int err, void *user_data); + +struct bt_mesh_adv { + const struct bt_mesh_send_cb *cb; + void *cb_data; + + u8_t type:2, + busy:1; + u8_t xmit; + + /* For transport layer segment sending */ + struct { + u8_t attempts; + } seg; + + u8_t flags; + + int ref_cnt; + struct ble_npl_event ev; +}; + +typedef struct bt_mesh_adv *(*bt_mesh_adv_alloc_t)(int id); + +/* xmit_count: Number of retransmissions, i.e. 0 == 1 transmission */ +struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout); + +struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout); + +void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data); + +void bt_mesh_adv_update(void); + +void bt_mesh_adv_init(void); + +int bt_mesh_scan_enable(void); + +int bt_mesh_scan_disable(void); + +int ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg); +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/atomic.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/atomic.h new file mode 100644 index 0000000..2c73179 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/atomic.h @@ -0,0 +1,409 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ + +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} + + /** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + + /** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + + /** + * INTERNAL_HIDDEN @endcond + */ + + /** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + + /** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_bit(const atomic_t *target, int bit) + { + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); + } + + /** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); + } + +/** +* @brief Atomically set a bit to a given value. +* +* Atomically set bit number @a bit of @a target to value @a val. +* The target may be a single atomic variable or an array of them. +* +* @param target Address of atomic variable or array. +* @param bit Bit number (starting from 0). +* @param val true for 1, false for 0. +* +* @return N/A +*/ +static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + if (val) { + (void)atomic_or(ATOMIC_ELEM(target, bit), mask); + } else { + (void)atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.c new file mode 100644 index 0000000..89ee1a0 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.c @@ -0,0 +1,444 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_BEACON_LOG + +#include +#include +#include "nimble/porting/nimble/include/os/os_mbuf.h" +#include "../include/mesh/mesh.h" + +#include "adv.h" +#include "mesh_priv.h" +#include "net.h" +#include "prov.h" +#include "crypto.h" +#include "beacon.h" +#include "foundation.h" + +#define UNPROVISIONED_INTERVAL (K_SECONDS(5)) +#define PROVISIONED_INTERVAL (K_SECONDS(10)) + +#define BEACON_TYPE_UNPROVISIONED 0x00 +#define BEACON_TYPE_SECURE 0x01 + +/* 3 transmissions, 20ms interval */ +#define UNPROV_XMIT BT_MESH_TRANSMIT(2, 20) + +/* 1 transmission, 20ms interval */ +#define PROV_XMIT BT_MESH_TRANSMIT(0, 20) + +static struct k_delayed_work beacon_timer; + +static struct bt_mesh_subnet *cache_check(u8_t data[21]) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (!memcmp(sub->beacon_cache, data, 21)) { + return sub; + } + } + + return NULL; +} + +static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub) +{ + memcpy(sub->beacon_cache, data, 21); +} + +static void beacon_complete(int err, void *user_data) +{ + struct bt_mesh_subnet *sub = user_data; + + BT_DBG("err %d", err); + + sub->beacon_sent = k_uptime_get_32(); +} + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct os_mbuf *buf) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE); + + if (sub->kr_flag) { + keys = &sub->keys[1]; + } else { + keys = &sub->keys[0]; + } + + net_buf_simple_add_u8(buf, flags); + + /* Network ID */ + net_buf_simple_add_mem(buf, keys->net_id, 8); + + /* IV Index */ + net_buf_simple_add_be32(buf, bt_mesh.iv_index); + + net_buf_simple_add_mem(buf, sub->auth, 8); + + BT_DBG("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx, + flags, bt_hex(keys->net_id, 8)); + BT_DBG("IV Index 0x%08x Auth %s", (unsigned) bt_mesh.iv_index, + bt_hex(sub->auth, 8)); +} + +/* If the interval has passed or is within 5 seconds from now send a beacon */ +#define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \ + K_SECONDS(5)) + +static int secure_beacon_send(void) +{ + static const struct bt_mesh_send_cb send_cb = { + .end = beacon_complete, + }; + u32_t now = k_uptime_get_32(); + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + struct os_mbuf *buf; + u32_t time_diff; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + time_diff = now - sub->beacon_sent; + if (time_diff < K_SECONDS(600) && + time_diff < BEACON_THRESHOLD(sub)) { + continue; + } + + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate beacon buffer"); + return -ENOBUFS; + } + + bt_mesh_beacon_create(sub, buf); + + bt_mesh_adv_send(buf, &send_cb, sub); + net_buf_unref(buf); + } + + return 0; +} + +static int unprovisioned_beacon_send(void) +{ + const struct bt_mesh_prov *prov; + u8_t uri_hash[16] = { 0 }; + struct os_mbuf *buf; + u16_t oob_info; + + BT_DBG("unprovisioned_beacon_send"); + + buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, UNPROV_XMIT, K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate beacon buffer"); + return -ENOBUFS; + } + + prov = bt_mesh_prov_get(); + + net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED); + net_buf_add_mem(buf, prov->uuid, 16); + + if (prov->uri && bt_mesh_s1(prov->uri, uri_hash) == 0) { + oob_info = prov->oob_info | BT_MESH_PROV_OOB_URI; + } else { + oob_info = prov->oob_info; + } + + net_buf_add_be16(buf, oob_info); + net_buf_add_mem(buf, uri_hash, 4); + + bt_mesh_adv_send(buf, NULL, NULL); + net_buf_unref(buf); + + if (prov->uri) { + size_t len; + + buf = bt_mesh_adv_create(BT_MESH_ADV_URI, UNPROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate URI buffer"); + return -ENOBUFS; + } + + len = strlen(prov->uri); + if (net_buf_tailroom(buf) < len) { + BT_WARN("Too long URI to fit advertising data"); + } else { + net_buf_add_mem(buf, prov->uri, len); + bt_mesh_adv_send(buf, NULL, NULL); + } + + net_buf_unref(buf); + } + + return 0; +} + +static void unprovisioned_beacon_recv(struct os_mbuf *buf) +{ +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + const struct bt_mesh_prov *prov; + u8_t *uuid; + u16_t oob_info; + u32_t uri_hash_val; + u32_t *uri_hash = NULL; + + if (buf->om_len != 18 && buf->om_len != 22) { + BT_ERR("Invalid unprovisioned beacon length (%u)", buf->om_len); + return; + } + + uuid = net_buf_simple_pull_mem(buf, 16); + oob_info = net_buf_simple_pull_be16(buf); + + if (buf->om_len == 4) { + uri_hash_val = net_buf_simple_pull_be32(buf); + uri_hash = &uri_hash_val; + } + + BT_DBG("uuid %s", bt_hex(uuid, 16)); + + prov = bt_mesh_prov_get(); + + if (prov->unprovisioned_beacon) { + prov->unprovisioned_beacon(uuid, + (bt_mesh_prov_oob_info_t)oob_info, + uri_hash); + } +#endif +} + +static void update_beacon_observation(void) +{ + static bool first_half; + int i; + + /* Observation period is 20 seconds, whereas the beacon timer + * runs every 10 seconds. We process what's happened during the + * window only after the seconnd half. + */ + first_half = !first_half; + if (first_half) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = sub->beacons_cur; + sub->beacons_cur = 0; + } +} + +static void beacon_send(struct ble_npl_event *work) +{ + /* Don't send anything if we have an active provisioning link */ + if ((MYNEWT_VAL(BLE_MESH_PROV)) && bt_prov_active()) { + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + return; + } + + BT_DBG(""); + + if (bt_mesh_is_provisioned()) { + update_beacon_observation(); + secure_beacon_send(); + + /* Only resubmit if beaconing is still enabled */ + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED || + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { + k_delayed_work_submit(&beacon_timer, + PROVISIONED_INTERVAL); + } + } else if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) { + unprovisioned_beacon_send(); + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + } +} + +static void secure_beacon_recv(struct os_mbuf *buf) +{ + u8_t *data, *net_id, *auth; + struct bt_mesh_subnet *sub; + u32_t iv_index; + bool new_key, kr_change, iv_change; + u8_t flags; + + if (buf->om_len < 21) { + BT_ERR("Too short secure beacon (len %u)", buf->om_len); + return; + } + + sub = cache_check(buf->om_data); + if (sub) { + /* We've seen this beacon before - just update the stats */ + goto update_stats; + } + + /* So we can add to the cache if auth matches */ + data = buf->om_data; + + flags = net_buf_simple_pull_u8(buf); + net_id = net_buf_simple_pull_mem(buf, 8); + iv_index = net_buf_simple_pull_be32(buf); + auth = buf->om_data; + + BT_DBG("flags 0x%02x id %s iv_index 0x%08x", + flags, bt_hex(net_id, 8), (unsigned) iv_index); + + sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key); + if (!sub) { + BT_DBG("No subnet that matched beacon"); + return; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return; + } + + cache_add(data, sub); + + /* If we have NetKey0 accept initiation only from it */ + if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) && + sub->net_idx != BT_MESH_KEY_PRIMARY) { + BT_WARN("Ignoring secure beacon on non-primary subnet"); + goto update_stats; + } + + BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x", + sub->net_idx, (unsigned) iv_index, (unsigned) bt_mesh.iv_index); + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) == + BT_MESH_IV_UPDATE(flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags)); + + kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key); + if (kr_change) { + bt_mesh_net_beacon_update(sub); + } + + if (iv_change) { + /* Update all subnets */ + bt_mesh_net_sec_update(NULL); + } else if (kr_change) { + /* Key Refresh without IV Update only impacts one subnet */ + bt_mesh_net_sec_update(sub); + } + +update_stats: + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED && + sub->beacons_cur < 0xff) { + sub->beacons_cur++; + } +} + +void bt_mesh_beacon_recv(struct os_mbuf *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_len < 1) { + BT_ERR("Too short beacon"); + return; + } + + type = net_buf_simple_pull_u8(buf); + switch (type) { + case BEACON_TYPE_UNPROVISIONED: + unprovisioned_beacon_recv(buf); + break; + case BEACON_TYPE_SECURE: + secure_beacon_recv(buf); + break; + default: + BT_WARN("Unknown beacon type 0x%02x", type); + break; + } +} + +void bt_mesh_beacon_init(void) +{ + k_delayed_work_init(&beacon_timer, beacon_send); +} + +void bt_mesh_beacon_ivu_initiator(bool enable) +{ + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_INITIATOR, enable); + + if (enable) { + k_work_submit(&beacon_timer.work); + } else if (bt_mesh_beacon_get() == BT_MESH_BEACON_DISABLED) { + k_delayed_work_cancel(&beacon_timer); + } +} + +void bt_mesh_beacon_enable(void) +{ + int i; + + if (!bt_mesh_is_provisioned()) { + k_work_submit(&beacon_timer.work); + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = 0; + sub->beacons_cur = 0; + + bt_mesh_net_beacon_update(sub); + } + + k_work_submit(&beacon_timer.work); +} + +void bt_mesh_beacon_disable(void) +{ + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { + k_delayed_work_cancel(&beacon_timer); + } +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.h new file mode 100644 index 0000000..08fd88d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/beacon.h @@ -0,0 +1,26 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BEACON_H__ +#define __BEACON_H__ + +#include "nimble/porting/nimble/include/os/os_mbuf.h" + +void bt_mesh_beacon_enable(void); +void bt_mesh_beacon_disable(void); + +void bt_mesh_beacon_ivu_initiator(bool enable); + +void bt_mesh_beacon_recv(struct os_mbuf *buf); + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct os_mbuf *buf); + +void bt_mesh_beacon_init(void); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_cli.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_cli.c new file mode 100644 index 0000000..d2596c4 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_cli.c @@ -0,0 +1,1501 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +#include "../include/mesh/mesh.h" + +#include +#include +#include + +#include "net.h" +#include "foundation.h" + +#define CID_NVAL 0xffff + +/* 2 byte dummy opcode for getting compile time buffer sizes. */ +#define DUMMY_2_BYTE_OP BT_MESH_MODEL_OP_2(0xff, 0xff) + +struct comp_data { + u8_t *status; + struct os_mbuf *comp; +}; + +static s32_t msg_timeout = K_SECONDS(5); + +static struct bt_mesh_cfg_cli *cli; + +static void comp_data_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct comp_data *param; + size_t to_copy; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_DEV_COMP_DATA_STATUS) { + BT_WARN("Unexpected Composition Data Status"); + return; + } + + param = cli->op_param; + + *(param->status) = net_buf_simple_pull_u8(buf); + to_copy = min(net_buf_simple_tailroom(param->comp), buf->om_len); + net_buf_simple_add_mem(param->comp, buf->om_data, to_copy); + + k_sem_give(&cli->op_sync); +} + +static void state_status_u8(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf, + u32_t expect_status) +{ + u8_t *status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != expect_status) { + BT_WARN("Unexpected Status (0x%08x != 0x%08x)", + (unsigned) cli->op_pending, (unsigned) expect_status); + return; + } + + status = cli->op_param; + *status = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +static void beacon_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + state_status_u8(model, ctx, buf, OP_BEACON_STATUS); +} + +static void ttl_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_DEFAULT_TTL_STATUS); +} + +static void friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_FRIEND_STATUS); +} + +static void gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + state_status_u8(model, ctx, buf, OP_GATT_PROXY_STATUS); +} + +struct relay_param { + u8_t *status; + u8_t *transmit; +}; + +static void relay_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct relay_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_RELAY_STATUS) { + BT_WARN("Unexpected Relay Status message"); + return; + } + + param = cli->op_param; + *param->status = net_buf_simple_pull_u8(buf); + *param->transmit = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +struct net_key_param { + u8_t *status; + u16_t net_idx; +}; + +static void net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct net_key_param *param; + u16_t net_idx, app_idx; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_NET_KEY_STATUS) { + BT_WARN("Unexpected Net Key Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &net_idx, &app_idx); + + param = cli->op_param; + if (param->net_idx != net_idx) { + BT_WARN("Net Key Status key index does not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct app_key_param { + u8_t *status; + u16_t net_idx; + u16_t app_idx; +}; + +static void app_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct app_key_param *param; + u16_t net_idx, app_idx; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_APP_KEY_STATUS) { + BT_WARN("Unexpected App Key Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &net_idx, &app_idx); + + param = cli->op_param; + if (param->net_idx != net_idx || param->app_idx != app_idx) { + BT_WARN("App Key Status key indices did not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct mod_app_param { + u8_t *status; + u16_t elem_addr; + u16_t mod_app_idx; + u16_t mod_id; + u16_t cid; +}; + +static void mod_app_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t elem_addr, mod_app_idx, mod_id, cid; + struct mod_app_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_APP_STATUS) { + BT_WARN("Unexpected Model App Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + elem_addr = net_buf_simple_pull_le16(buf); + mod_app_idx = net_buf_simple_pull_le16(buf); + + if (buf->om_len >= 4) { + cid = net_buf_simple_pull_le16(buf); + } else { + cid = CID_NVAL; + } + + mod_id = net_buf_simple_pull_le16(buf); + + param = cli->op_param; + if (param->elem_addr != elem_addr || + param->mod_app_idx != mod_app_idx || param->mod_id != mod_id || + param->cid != cid) { + BT_WARN("Model App Status parameters did not match"); + return; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct mod_pub_param { + u16_t mod_id; + u16_t cid; + u16_t elem_addr; + u8_t *status; + struct bt_mesh_cfg_mod_pub *pub; +}; + +static void mod_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t mod_id, cid, elem_addr; + struct mod_pub_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_PUB_STATUS) { + BT_WARN("Unexpected Model Pub Status message"); + return; + } + + param = cli->op_param; + if (param->cid != CID_NVAL) { + if (buf->om_len < 14) { + BT_WARN("Unexpected Mod Pub Status with SIG Model"); + return; + } + + cid = sys_get_le16(&buf->om_data[10]); + mod_id = sys_get_le16(&buf->om_data[12]); + } else { + if (buf->om_len > 12) { + BT_WARN("Unexpected Mod Pub Status with Vendor Model"); + return; + } + + cid = CID_NVAL; + mod_id = sys_get_le16(&buf->om_data[10]); + } + + if (mod_id != param->mod_id || cid != param->cid) { + BT_WARN("Mod Pub Model ID or Company ID mismatch"); + return; + } + + status = net_buf_simple_pull_u8(buf); + + elem_addr = net_buf_simple_pull_le16(buf); + if (elem_addr != param->elem_addr) { + BT_WARN("Model Pub Status for unexpected element (0x%04x)", + elem_addr); + return; + } + + if (param->status) { + *param->status = status; + } + + if (param->pub) { + param->pub->addr = net_buf_simple_pull_le16(buf); + param->pub->app_idx = net_buf_simple_pull_le16(buf); + param->pub->cred_flag = (param->pub->app_idx & BIT(12)); + param->pub->app_idx &= BIT_MASK(12); + param->pub->ttl = net_buf_simple_pull_u8(buf); + param->pub->period = net_buf_simple_pull_u8(buf); + param->pub->transmit = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&cli->op_sync); +} + +struct mod_sub_param { + u8_t *status; + u16_t elem_addr; + u16_t *sub_addr; + u16_t *expect_sub; + u16_t mod_id; + u16_t cid; +}; + +static void mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + struct mod_sub_param *param; + u8_t status; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_MOD_SUB_STATUS) { + BT_WARN("Unexpected Model Subscription Status message"); + return; + } + + status = net_buf_simple_pull_u8(buf); + elem_addr = net_buf_simple_pull_le16(buf); + sub_addr = net_buf_simple_pull_le16(buf); + + if (buf->om_len >= 4) { + cid = net_buf_simple_pull_le16(buf); + } else { + cid = CID_NVAL; + } + + mod_id = net_buf_simple_pull_le16(buf); + + param = cli->op_param; + if (param->elem_addr != elem_addr || param->mod_id != mod_id || + (param->expect_sub && *param->expect_sub != sub_addr) || + param->cid != cid) { + BT_WARN("Model Subscription Status parameters did not match"); + return; + } + + if (param->sub_addr) { + *param->sub_addr = sub_addr; + } + + if (param->status) { + *param->status = status; + } + + k_sem_give(&cli->op_sync); +} + +struct hb_sub_param { + u8_t *status; + struct bt_mesh_cfg_hb_sub *sub; +}; + +static void hb_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf*buf) +{ + struct hb_sub_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_HEARTBEAT_SUB_STATUS) { + BT_WARN("Unexpected Heartbeat Subscription Status message"); + return; + } + + param = cli->op_param; + + *param->status = net_buf_simple_pull_u8(buf); + + param->sub->src = net_buf_simple_pull_le16(buf); + param->sub->dst = net_buf_simple_pull_le16(buf); + param->sub->period = net_buf_simple_pull_u8(buf); + param->sub->count = net_buf_simple_pull_u8(buf); + param->sub->min = net_buf_simple_pull_u8(buf); + param->sub->max = net_buf_simple_pull_u8(buf); + + k_sem_give(&cli->op_sync); +} + +struct hb_pub_param { + u8_t *status; + struct bt_mesh_cfg_hb_pub *pub; +}; + +static void hb_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct hb_pub_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_HEARTBEAT_PUB_STATUS) { + BT_WARN("Unexpected Heartbeat Publication Status message"); + return; + } + + param = cli->op_param; + + *param->status = net_buf_simple_pull_u8(buf); + + if (param->pub) { + param->pub->dst = net_buf_simple_pull_le16(buf); + param->pub->count = net_buf_simple_pull_u8(buf); + param->pub->period = net_buf_simple_pull_u8(buf); + param->pub->ttl = net_buf_simple_pull_u8(buf); + param->pub->feat = net_buf_simple_pull_u8(buf); + param->pub->net_idx = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&cli->op_sync); +} + +const struct bt_mesh_model_op bt_mesh_cfg_cli_op[] = { + { OP_DEV_COMP_DATA_STATUS, 15, comp_data_status }, + { OP_BEACON_STATUS, 1, beacon_status }, + { OP_DEFAULT_TTL_STATUS, 1, ttl_status }, + { OP_FRIEND_STATUS, 1, friend_status }, + { OP_GATT_PROXY_STATUS, 1, gatt_proxy_status }, + { OP_RELAY_STATUS, 2, relay_status }, + { OP_NET_KEY_STATUS, 3, net_key_status }, + { OP_APP_KEY_STATUS, 4, app_key_status }, + { OP_MOD_APP_STATUS, 7, mod_app_status }, + { OP_MOD_PUB_STATUS, 12, mod_pub_status }, + { OP_MOD_SUB_STATUS, 7, mod_sub_status }, + { OP_HEARTBEAT_SUB_STATUS, 9, hb_sub_status }, + { OP_HEARTBEAT_PUB_STATUS, 10, hb_pub_status }, + BT_MESH_MODEL_OP_END, +}; + +static int cfg_cli_init(struct bt_mesh_model *model) +{ + BT_DBG(""); + + if (!bt_mesh_model_in_primary(model)) { + BT_ERR("Configuration Client only allowed in primary element"); + return -EINVAL; + } + + if (!model->user_data) { + BT_ERR("No Configuration Client context provided"); + return -EINVAL; + } + + cli = model->user_data; + cli->model = model; + + /* + * Configuration Model security is device-key based and both the local + * and remote keys are allowed to access this model. + */ + model->keys[0] = BT_MESH_KEY_DEV_ANY; + + k_sem_init(&cli->op_sync, 0, 1); + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb = { + .init = cfg_cli_init, +}; + +static int cli_prepare(void *param, u32_t op) +{ + if (!cli) { + BT_ERR("No available Configuration Client context!"); + return -EINVAL; + } + + if (cli->op_pending) { + BT_WARN("Another synchronous operation pending"); + return -EBUSY; + } + + cli->op_param = param; + cli->op_pending = op; + + return 0; +} + +static void cli_reset(void) +{ + cli->op_pending = 0; + cli->op_param = NULL; +} + +static int cli_wait(void) +{ + int err; + + err = k_sem_take(&cli->op_sync, msg_timeout); + + cli_reset(); + + return err; +} + +int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page, + u8_t *status, struct os_mbuf *comp) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEV_COMP_DATA_GET, 1); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct comp_data param = { + .status = status, + .comp = comp, + }; + int err; + + err = cli_prepare(¶m, OP_DEV_COMP_DATA_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_DEV_COMP_DATA_GET); + net_buf_simple_add_u8(msg, page); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int get_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, + u8_t *val) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(val, rsp); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int set_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp, + u8_t new_val, u8_t *val) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 1); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(val, rsp); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_u8(msg, new_val); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_BEACON_GET, OP_BEACON_STATUS, + status); +} + +int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_BEACON_SET, OP_BEACON_STATUS, + val, status); +} + +int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl) +{ + return get_state_u8(net_idx, addr, OP_DEFAULT_TTL_GET, + OP_DEFAULT_TTL_STATUS, ttl); +} + +int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl) +{ + return set_state_u8(net_idx, addr, OP_DEFAULT_TTL_SET, + OP_DEFAULT_TTL_STATUS, val, ttl); +} + +int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_FRIEND_GET, + OP_FRIEND_STATUS, status); +} + +int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_FRIEND_SET, OP_FRIEND_STATUS, + val, status); +} + +int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status) +{ + return get_state_u8(net_idx, addr, OP_GATT_PROXY_GET, + OP_GATT_PROXY_STATUS, status); +} + +int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val, + u8_t *status) +{ + return set_state_u8(net_idx, addr, OP_GATT_PROXY_SET, + OP_GATT_PROXY_STATUS, val, status); +} + +int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status, + u8_t *transmit) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_GET, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct relay_param param = { + .status = status, + .transmit = transmit, + }; + int err; + + err = cli_prepare(¶m, OP_RELAY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay, + u8_t new_transmit, u8_t *status, u8_t *transmit) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_SET, 2); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct relay_param param = { + .status = status, + .transmit = transmit, + }; + int err; + + err = cli_prepare(¶m, OP_RELAY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_SET); + net_buf_simple_add_u8(msg, new_relay); + net_buf_simple_add_u8(msg, new_transmit); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + const u8_t net_key[16], u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_ADD, 18); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct net_key_param param = { + .status = status, + .net_idx = key_net_idx, + }; + int err; + + err = cli_prepare(¶m, OP_NET_KEY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NET_KEY_ADD); + net_buf_simple_add_le16(msg, key_net_idx); + net_buf_simple_add_mem(msg, net_key, 16); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16], + u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_ADD, 19); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct app_key_param param = { + .status = status, + .net_idx = key_net_idx, + .app_idx = key_app_idx, + }; + int err; + + err = cli_prepare(¶m, OP_APP_KEY_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_APP_KEY_ADD); + key_idx_pack(msg, key_net_idx, key_app_idx); + net_buf_simple_add_mem(msg, app_key, 16); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +static int mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_BIND, 8); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_app_param param = { + .status = status, + .elem_addr = elem_addr, + .mod_app_idx = mod_app_idx, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_APP_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_APP_BIND); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, mod_app_idx); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u8_t *status) +{ + return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id, + CID_NVAL, status); +} + +int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id, cid, + status); +} + +static int mod_sub(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 8); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_sub_param param = { + .status = status, + .elem_addr = elem_addr, + .expect_sub = &sub_addr, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, sub_addr); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr, + mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr, + mod_id, cid, status); +} + +int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr, + mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid, + u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr, + mod_id, cid, status); +} + +int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u8_t *status) +{ + return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr, + sub_addr, mod_id, CID_NVAL, status); +} + +int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, u16_t sub_addr, + u16_t mod_id, u16_t cid, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr, + sub_addr, mod_id, cid, status); +} + +static int mod_sub_va(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 22); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_sub_param param = { + .status = status, + .elem_addr = elem_addr, + .sub_addr = virt_addr, + .mod_id = mod_id, + .cid = cid, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_SUB_STATUS); + if (err) { + goto done; + } + + BT_DBG("net_idx 0x%04x addr 0x%04x elem_addr 0x%04x label %s", + net_idx, addr, elem_addr, label); + BT_DBG("mod_id 0x%04x cid 0x%04x", mod_id, cid); + + bt_mesh_model_msg_init(msg, op); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_mem(msg, label, 16); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label, + mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label, + mod_id, cid, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t *virt_addr, u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label, + mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, + u16_t cid, u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label, + mod_id, cid, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t *virt_addr, + u8_t *status) +{ + return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr, + label, mod_id, CID_NVAL, virt_addr, status); +} + +int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr, + u16_t elem_addr, const u8_t label[16], + u16_t mod_id, u16_t cid, + u16_t *virt_addr, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr, + label, mod_id, cid, virt_addr, status); +} + +static int mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_GET, 6); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_pub_param param = { + .mod_id = mod_id, + .cid = cid, + .elem_addr = elem_addr, + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_GET); + + net_buf_simple_add_le16(msg, elem_addr); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status) +{ + return mod_pub_get(net_idx, addr, elem_addr, mod_id, CID_NVAL, + pub, status); +} + +int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_pub_get(net_idx, addr, elem_addr, mod_id, cid, pub, status); +} + +static int mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_SET, 13); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct mod_pub_param param = { + .mod_id = mod_id, + .cid = cid, + .elem_addr = elem_addr, + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_MOD_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_SET); + + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, pub->addr); + net_buf_simple_add_le16(msg, (pub->app_idx | (pub->cred_flag << 12))); + net_buf_simple_add_u8(msg, pub->ttl); + net_buf_simple_add_u8(msg, pub->period); + net_buf_simple_add_u8(msg, pub->transmit); + + if (cid != CID_NVAL) { + net_buf_simple_add_le16(msg, cid); + } + + net_buf_simple_add_le16(msg, mod_id); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub, + u8_t *status) +{ + return mod_pub_set(net_idx, addr, elem_addr, mod_id, CID_NVAL, + pub, status); +} + +int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub, u8_t *status) +{ + if (cid == CID_NVAL) { + return -EINVAL; + } + + return mod_pub_set(net_idx, addr, elem_addr, mod_id, cid, pub, status); +} + +int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_SET, 5); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_sub_param param = { + .status = status, + .sub = sub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_SET); + net_buf_simple_add_le16(msg, sub->src); + net_buf_simple_add_le16(msg, sub->dst); + net_buf_simple_add_u8(msg, sub->period); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_sub *sub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_GET, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_sub_param param = { + .status = status, + .sub = sub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_SUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr, + const struct bt_mesh_cfg_hb_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_SET, 9); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_pub_param param = { + .status = status, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_SET); + net_buf_simple_add_le16(msg, pub->dst); + net_buf_simple_add_u8(msg, pub->count); + net_buf_simple_add_u8(msg, pub->period); + net_buf_simple_add_u8(msg, pub->ttl); + net_buf_simple_add_le16(msg, pub->feat); + net_buf_simple_add_le16(msg, pub->net_idx); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr, + struct bt_mesh_cfg_hb_pub *pub, u8_t *status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_GET, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct hb_pub_param param = { + .status = status, + .pub = pub, + }; + int err; + + err = cli_prepare(¶m, OP_HEARTBEAT_PUB_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_GET); + + err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!status) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +s32_t bt_mesh_cfg_cli_timeout_get(void) +{ + return msg_timeout; +} + +void bt_mesh_cfg_cli_timeout_set(s32_t timeout) +{ + msg_timeout = timeout; +} + +#endif +#endif \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_srv.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_srv.c new file mode 100644 index 0000000..f5de01c --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/cfg_srv.c @@ -0,0 +1,3622 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include +#include +#include + +#include "../include/mesh/mesh.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "crypto.h" +#include "access.h" +#include "beacon.h" +#include "proxy.h" +#include "foundation.h" +#include "friend.h" +#include "testing.h" +#include "settings.h" + +#define DEFAULT_TTL 7 + +static struct bt_mesh_cfg_srv *conf; + +static struct label labels[CONFIG_BT_MESH_LABEL_COUNT]; + +static int comp_add_elem(struct os_mbuf *buf, struct bt_mesh_elem *elem, + bool primary) +{ + struct bt_mesh_model *mod; + int i; + + if (net_buf_simple_tailroom(buf) < + 4 + (elem->model_count * 2) + (elem->vnd_model_count * 2)) { + BT_ERR("Too large device composition"); + return -E2BIG; + } + + net_buf_simple_add_le16(buf, elem->loc); + + net_buf_simple_add_u8(buf, elem->model_count); + net_buf_simple_add_u8(buf, elem->vnd_model_count); + + for (i = 0; i < elem->model_count; i++) { + mod = &elem->models[i]; + net_buf_simple_add_le16(buf, mod->id); + } + + for (i = 0; i < elem->vnd_model_count; i++) { + mod = &elem->vnd_models[i]; + net_buf_simple_add_le16(buf, mod->vnd.company); + net_buf_simple_add_le16(buf, mod->vnd.id); + } + + return 0; +} + +static int comp_get_page_0(struct os_mbuf *buf) +{ + u16_t feat = 0; + const struct bt_mesh_comp *comp; + int i; + + comp = bt_mesh_comp_get(); + + if ((MYNEWT_VAL(BLE_MESH_RELAY))) { + feat |= BT_MESH_FEAT_RELAY; + } + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + feat |= BT_MESH_FEAT_PROXY; + } + + if ((MYNEWT_VAL(BLE_MESH_FRIEND))) { + feat |= BT_MESH_FEAT_FRIEND; + } + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + feat |= BT_MESH_FEAT_LOW_POWER; + } + + net_buf_simple_add_le16(buf, comp->cid); + net_buf_simple_add_le16(buf, comp->pid); + net_buf_simple_add_le16(buf, comp->vid); + net_buf_simple_add_le16(buf, MYNEWT_VAL(BLE_MESH_CRPL)); + net_buf_simple_add_le16(buf, feat); + + for (i = 0; i < comp->elem_count; i++) { + int err; + + err = comp_add_elem(buf, &comp->elem[i], i == 0); + if (err) { + return err; + } + } + + return 0; +} + +static void dev_comp_data_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + u8_t page; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + page = net_buf_simple_pull_u8(buf); + if (page != 0U) { + BT_DBG("Composition page %u not available", page); + page = 0U; + } + + bt_mesh_model_msg_init(sdu, OP_DEV_COMP_DATA_STATUS); + + net_buf_simple_add_u8(sdu, page); + if (comp_get_page_0(sdu) < 0) { + BT_ERR("Unable to get composition page 0"); + goto done; + } + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Device Composition Status response"); + } + +done: + os_mbuf_free_chain(sdu); +} + +static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem, + struct os_mbuf *buf, bool *vnd) +{ + if (buf->om_len < 4) { + u16_t id; + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("ID 0x%04x addr 0x%04x", id, elem->addr); + + *vnd = false; + + return bt_mesh_model_find(elem, id); + } else { + u16_t company, id; + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id, + elem->addr); + + *vnd = true; + + return bt_mesh_model_find_vnd(elem, company, id); + } +} + +static bool app_key_is_valid(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != BT_MESH_KEY_UNUSED && + key->app_idx == app_idx) { + return true; + } + } + + return false; +} + +static u8_t _mod_pub_set(struct bt_mesh_model *model, u16_t pub_addr, + u16_t app_idx, u8_t cred_flag, u8_t ttl, u8_t period, + u8_t retransmit, bool store) +{ + if (!model->pub) { + return STATUS_NVAL_PUB_PARAM; + } + + if (!(MYNEWT_VAL(BLE_MESH_LOW_POWER)) && cred_flag) { + return STATUS_FEAT_NOT_SUPP; + } + + if (!model->pub->update && period) { + return STATUS_NVAL_PUB_PARAM; + } + + if (pub_addr == BT_MESH_ADDR_UNASSIGNED) { + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return STATUS_SUCCESS; + } + + model->pub->addr = BT_MESH_ADDR_UNASSIGNED; + model->pub->key = 0; + model->pub->cred = 0; + model->pub->ttl = 0; + model->pub->period = 0; + model->pub->retransmit = 0; + model->pub->count = 0; + + if (model->pub->update) { + k_delayed_work_cancel(&model->pub->timer); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; + } + + if (!bt_mesh_app_key_find(app_idx)) { + return STATUS_INVALID_APPKEY; + } + + model->pub->addr = pub_addr; + model->pub->key = app_idx; + model->pub->cred = cred_flag; + model->pub->ttl = ttl; + model->pub->period = period; + model->pub->retransmit = retransmit; + + if (model->pub->update) { + s32_t period_ms; + + period_ms = bt_mesh_model_pub_period_get(model); + BT_DBG("period %u ms", (unsigned) period_ms); + + if (period_ms) { + k_delayed_work_submit(&model->pub->timer, period_ms); + } else { + k_delayed_work_cancel(&model->pub->timer); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; +} + +u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x", model, key_idx); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + /* Treat existing binding as success */ + if (model->keys[i] == key_idx) { + return STATUS_SUCCESS; + } + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] == BT_MESH_KEY_UNUSED) { + model->keys[i] = key_idx; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_bind(model); + } + + return STATUS_SUCCESS; + } + } + + return STATUS_INSUFF_RESOURCES; +} + +u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] != key_idx) { + continue; + } + + model->keys[i] = BT_MESH_KEY_UNUSED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_mod_bind(model); + } + + if (model->pub && model->pub->key == key_idx) { + _mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED, + 0, 0, 0, 0, 0, store); + } + } + + return STATUS_SUCCESS; +} + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == BT_MESH_KEY_UNUSED) { + return key; + } + } + + return NULL; +} + +static u8_t app_key_set(u16_t net_idx, u16_t app_idx, const u8_t val[16], + bool update) +{ + struct bt_mesh_app_keys *keys; + struct bt_mesh_app_key *key; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s", + net_idx, app_idx, update, bt_hex(val, 16)); + + sub = bt_mesh_subnet_get(net_idx); + if (!sub) { + return STATUS_INVALID_NETKEY; + } + + key = bt_mesh_app_key_find(app_idx); + if (update) { + if (!key) { + return STATUS_INVALID_APPKEY; + } + + if (key->net_idx != net_idx) { + return STATUS_INVALID_BINDING; + } + + keys = &key->keys[1]; + + /* The AppKey Update message shall generate an error when node + * is in normal operation, Phase 2, or Phase 3 or in Phase 1 + * when the AppKey Update message on a valid AppKeyIndex when + * the AppKey value is different. + */ + if (sub->kr_phase != BT_MESH_KR_PHASE_1) { + return STATUS_CANNOT_UPDATE; + } + + if (key->updated) { + if (memcmp(keys->val, val, 16)) { + return STATUS_CANNOT_UPDATE; + } else { + return STATUS_SUCCESS; + } + } + + key->updated = true; + } else { + if (key) { + if (key->net_idx == net_idx && + !memcmp(key->keys[0].val, val, 16)) { + return STATUS_SUCCESS; + } + + if (key->net_idx == net_idx) { + return STATUS_IDX_ALREADY_STORED; + } else { + return STATUS_INVALID_NETKEY; + } + } + + key = bt_mesh_app_key_alloc(app_idx); + if (!key) { + return STATUS_INSUFF_RESOURCES; + } + + keys = &key->keys[0]; + } + + if (bt_mesh_app_id(val, &keys->id)) { + if (update) { + key->updated = false; + } + + return STATUS_STORAGE_FAIL; + } + + BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id); + + key->net_idx = net_idx; + key->app_idx = app_idx; + memcpy(keys->val, val, 16); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing AppKey persistently"); + bt_mesh_store_app_key(key); + } + + return STATUS_SUCCESS; +} + +static void app_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->om_data, false); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +static void app_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->om_data, true); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +struct unbind_data { + u16_t app_idx; + bool store; +}; + +static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + struct unbind_data *data = user_data; + + mod_unbind(mod, data->app_idx, data->store); +} + +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store) +{ + struct unbind_data data = { .app_idx = key->app_idx, .store = store }; + + BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store); + + bt_mesh_model_foreach(_mod_unbind, &data); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_app_key(key); + } + + key->net_idx = BT_MESH_KEY_UNUSED; + memset(key->keys, 0, sizeof(key->keys)); +} + +static void app_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4); + u16_t key_net_idx, key_app_idx; + struct bt_mesh_app_key *key; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + if (!bt_mesh_subnet_get(key_net_idx)) { + status = STATUS_INVALID_NETKEY; + goto send_status; + } + + key = bt_mesh_app_key_find(key_app_idx); + if (!key) { + /* Treat as success since the client might have missed a + * previous response and is resending the request. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + if (key->net_idx != key_net_idx) { + status = STATUS_INVALID_BINDING; + goto send_status; + } + + bt_mesh_app_key_del(key, true); + status = STATUS_SUCCESS; + +send_status: + bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS); + + net_buf_simple_add_u8(msg, status); + + key_idx_pack(msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send App Key Status response"); + } + + os_mbuf_free_chain(msg); +} + +/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */ +#define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2) + +static void app_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + BT_MESH_MODEL_BUF(OP_APP_KEY_LIST, + 3 + IDX_LEN(CONFIG_BT_MESH_APP_KEY_COUNT)); + u16_t get_idx, i, prev; + u8_t status; + + get_idx = net_buf_simple_pull_le16(buf); + if (get_idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", get_idx); + goto done; + } + + BT_DBG("idx 0x%04x", get_idx); + + bt_mesh_model_msg_init(msg, OP_APP_KEY_LIST); + + if (!bt_mesh_subnet_get(get_idx)) { + status = STATUS_INVALID_NETKEY; + } else { + status = STATUS_SUCCESS; + } + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, get_idx); + + if (status != STATUS_SUCCESS) { + goto send_status; + } + + prev = BT_MESH_KEY_UNUSED; + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != get_idx) { + continue; + } + + if (prev == BT_MESH_KEY_UNUSED) { + prev = key->app_idx; + continue; + } + + key_idx_pack(msg, prev, key->app_idx); + prev = BT_MESH_KEY_UNUSED; + } + + if (prev != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, prev); + } + +send_status: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send AppKey List"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void beacon_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_BEACON_STATUS, 1); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Beacon Status response"); + } + os_mbuf_free_chain(msg); +} + +static void beacon_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_BEACON_STATUS, 1); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) { + if (buf->om_data[0] != cfg->beacon) { + cfg->beacon = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->beacon) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } + } + } else { + BT_WARN("Invalid Config Beacon value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Beacon Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void default_ttl_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Default TTL Status response"); + } + + os_mbuf_free_chain(msg); + +} + +static void default_ttl_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] <= BT_MESH_TTL_MAX && buf->om_data[0] != 0x01) { + if (cfg->default_ttl != buf->om_data[0]) { + cfg->default_ttl = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + } else { + BT_WARN("Prohibited Default TTL value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Default TTL Status response"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void send_gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_GATT_PROXY_STATUS, 1); + + bt_mesh_model_msg_init(msg, OP_GATT_PROXY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_gatt_proxy_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send GATT Proxy Status"); + } + + os_mbuf_free_chain(msg); + +} + +static void gatt_proxy_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + send_gatt_proxy_status(model, ctx); +} + +static void gatt_proxy_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) { + BT_WARN("Invalid GATT Proxy value 0x%02x", buf->om_data[0]); + return; + } + + if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY)) || + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_NOT_SUPPORTED) { + goto send_status; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("GATT Proxy 0x%02x -> 0x%02x", cfg->gatt_proxy, buf->om_data[0]); + + if (cfg->gatt_proxy == buf->om_data[0]) { + goto send_status; + } + + cfg->gatt_proxy = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + bt_mesh_adv_update(); + + if (cfg->hb_pub.feat & BT_MESH_FEAT_PROXY) { + bt_mesh_heartbeat_send(); + } + +send_status: + send_gatt_proxy_status(model, ctx); +} + +static void net_transmit_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_TRANSMIT_STATUS, 1); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Network Transmit Status"); + } + + os_mbuf_free_chain(msg); + +} + +static void net_transmit_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_TRANSMIT_STATUS, 1); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + BT_DBG("Transmit 0x%02x (count %u interval %ums)", buf->om_data[0], + BT_MESH_TRANSMIT_COUNT(buf->om_data[0]), + BT_MESH_TRANSMIT_INT(buf->om_data[0])); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else { + cfg->net_transmit = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Network Transmit Status"); + } + + os_mbuf_free_chain(msg); +} + +static void relay_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_STATUS, 2); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + bt_mesh_model_msg_init(msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Config Relay Status response"); + } + + os_mbuf_free_chain(msg); + +} + +static void relay_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_STATUS, 2); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) { + bool change; + + if (cfg->relay == BT_MESH_RELAY_NOT_SUPPORTED) { + change = false; + } else { + change = (cfg->relay != buf->om_data[0]); + cfg->relay = buf->om_data[0]; + cfg->relay_retransmit = buf->om_data[1]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + BT_DBG("Relay 0x%02x (%s) xmit 0x%02x (count %u interval %u)", + cfg->relay, change ? "changed" : "not changed", + cfg->relay_retransmit, + BT_MESH_TRANSMIT_COUNT(cfg->relay_retransmit), + BT_MESH_TRANSMIT_INT(cfg->relay_retransmit)); + + if ((cfg->hb_pub.feat & BT_MESH_FEAT_RELAY) && change) { + bt_mesh_heartbeat_send(); + } + } else { + BT_WARN("Invalid Relay value 0x%02x", buf->om_data[0]); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Relay Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void send_mod_pub_status(struct bt_mesh_model *cfg_mod, + struct bt_mesh_msg_ctx *ctx, + u16_t elem_addr, u16_t pub_addr, + bool vnd, struct bt_mesh_model *mod, + u8_t status, u8_t *mod_id) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_STATUS, 14); + + bt_mesh_model_msg_init(msg, OP_MOD_PUB_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + + if (status != STATUS_SUCCESS) { + memset(net_buf_simple_add(msg, 7), 0, 7); + } else { + u16_t idx_cred; + + net_buf_simple_add_le16(msg, pub_addr); + + idx_cred = mod->pub->key | (u16_t)mod->pub->cred << 12; + net_buf_simple_add_le16(msg, idx_cred); + net_buf_simple_add_u8(msg, mod->pub->ttl); + net_buf_simple_add_u8(msg, mod->pub->period); + net_buf_simple_add_u8(msg, mod->pub->retransmit); + } + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(cfg_mod, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Publication Status"); + } + + os_mbuf_free_chain(msg); +} + +static void mod_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, pub_addr = 0; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_SUCCESS; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +static void mod_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + pub_addr = net_buf_simple_pull_le16(buf); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u", + elem_addr, pub_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BT_MESH_PUB_TRANSMIT_COUNT(retransmit), + BT_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, pub_ttl, + pub_period, retransmit, true); + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +struct label *get_label(u16_t index) +{ + if (index >= ARRAY_SIZE(labels)) { + return NULL; + } + + return &labels[index]; +} + +#if CONFIG_BT_MESH_LABEL_COUNT > 0 +static inline void va_store(struct label *store) +{ + atomic_set_bit(store->flags, BT_MESH_VA_CHANGED); + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_label(); + } +} + +static struct label *va_find(const u8_t *label_uuid, + struct label **free_slot) +{ + struct label *match = NULL; + int i; + + if (free_slot != NULL) { + *free_slot = NULL; + } + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (labels[i].ref == 0) { + if (free_slot != NULL) { + *free_slot = &labels[i]; + } + continue; + } + + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + match = &labels[i]; + } + } + + return match; +} + +static u8_t va_add(u8_t *label_uuid, u16_t *addr) +{ + struct label *update, *free_slot = NULL; + + update = va_find(label_uuid, &free_slot); + if (update) { + update->ref++; + va_store(update); + return 0; + } + + if (!free_slot) { + return STATUS_INSUFF_RESOURCES; + } + + if (bt_mesh_virtual_addr(label_uuid, addr) < 0) { + return STATUS_UNSPECIFIED; + } + + free_slot->ref = 1; + free_slot->addr = *addr; + memcpy(free_slot->uuid, label_uuid, 16); + va_store(free_slot); + + return STATUS_SUCCESS; +} + +static u8_t va_del(u8_t *label_uuid, u16_t *addr) +{ + struct label *update; + + update = va_find(label_uuid, NULL); + if (update) { + update->ref--; + + if (addr) { + *addr = update->addr; + } + + va_store(update); + } + + if (addr) { + *addr = BT_MESH_ADDR_UNASSIGNED; + } + + return STATUS_CANNOT_REMOVE; +} + +static size_t mod_sub_list_clear(struct bt_mesh_model *mod) +{ + u8_t *label_uuid; + size_t clear_count; + int i; + + /* Unref stored labels related to this model */ + for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = BT_MESH_ADDR_UNASSIGNED; + clear_count++; + } + + continue; + } + + label_uuid = bt_mesh_label_uuid_get(mod->groups[i]); + + mod->groups[i] = BT_MESH_ADDR_UNASSIGNED; + clear_count++; + + if (label_uuid) { + va_del(label_uuid, NULL); + } else { + BT_ERR("Label UUID not found"); + } + } + + return clear_count; +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x cred_flag %u", elem_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BT_MESH_PUB_TRANSMIT_COUNT(retransmit), + BT_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + pub_addr = 0; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + pub_addr = 0; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &pub_addr); + if (status == STATUS_SUCCESS) { + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, + pub_ttl, pub_period, retransmit, true); + } + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#else +static size_t mod_sub_list_clear(struct bt_mesh_model *mod) +{ + size_t clear_count; + int i; + + /* Unref stored labels related to this model */ + for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = BT_MESH_ADDR_UNASSIGNED; + clear_count++; + } + } + + return clear_count; +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t *mod_id, status; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr, pub_addr = 0; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + mod_id = net_buf_simple_pull(buf, 4); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */ + +static void send_mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + u16_t elem_addr, u16_t sub_addr, u8_t *mod_id, + bool vnd) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_SUB_STATUS, 9); + + BT_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status, + elem_addr, sub_addr); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, sub_addr); + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Subscription Status"); + } + + os_mbuf_free_chain(msg); +} + +static void mod_sub_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + u16_t *entry; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (bt_mesh_model_find_group(&mod, sub_addr)) { + /* Tried to add existing subscription */ + BT_DBG("found existing subscription"); + status = STATUS_SUCCESS; + goto send_status; + } + + entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED); + if (!entry) { + status = STATUS_INSUFF_RESOURCES; + goto send_status; + } + + *entry = sub_addr; + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + /* An attempt to remove a non-existing address shall be treated + * as a success. + */ + status = STATUS_SUCCESS; + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(&mod, sub_addr); + if (match) { + *match = BT_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, + u32_t depth, void *user_data) +{ + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + return BT_MESH_WALK_CONTINUE; +} + +static void mod_sub_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (ARRAY_SIZE(mod->groups) > 0) { + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), + mod_sub_clear_visitor, NULL); + + mod->groups[0] = sub_addr; + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del_all(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor, + NULL); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +struct mod_sub_list_ctx { + u16_t elem_idx; + struct os_mbuf *msg; +}; + +static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, + u32_t depth, void *ctx) +{ + struct mod_sub_list_ctx *visit = ctx; + int count = 0; + int i; + + if (mod->elem_idx != visit->elem_idx) { + return BT_MESH_WALK_CONTINUE; + } + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (net_buf_simple_tailroom(visit->msg) < + 2 + BT_MESH_MIC_SHORT) { + BT_WARN("No room for all groups"); + return BT_MESH_WALK_STOP; + } + + net_buf_simple_add_le16(visit->msg, mod->groups[i]); + count++; + } + + BT_DBG("sublist: model %u:%x: %u groups", mod->elem_idx, mod->id, + count); + + return BT_MESH_WALK_CONTINUE; +} + +static void mod_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct mod_sub_list_ctx visit_ctx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t addr, id; + + addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x id 0x%04x", addr, id); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + mod = bt_mesh_model_find(elem, id); + if (!mod) { + net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, id); + + visit_ctx.msg = msg; + visit_ctx.elem_idx = mod->elem_idx; + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor, + &visit_ctx); + +send_list: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Subscription List"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void mod_sub_get_vnd(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t company, addr, id; + + addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x company 0x%04x id 0x%04x", addr, company, id); + + bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST_VND); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + mod = bt_mesh_model_find_vnd(elem, company, id); + if (!mod) { + net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + goto send_list; + } + + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(msg, addr); + net_buf_simple_add_le16(msg, company); + net_buf_simple_add_le16(msg, id); + + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor, + msg); + +send_list: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Vendor Model Subscription List"); + } + +done: + os_mbuf_free_chain(msg); + +} + +#if MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u16_t *entry; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &sub_addr); + if (status != STATUS_SUCCESS) { + goto send_status; + } + + if (bt_mesh_model_find_group(&mod, sub_addr)) { + /* Tried to add existing subscription */ + status = STATUS_SUCCESS; + goto send_status; + } + + + entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED); + if (!entry) { + status = STATUS_INSUFF_RESOURCES; + goto send_status; + } + + *entry = sub_addr; + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BT_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_del(label_uuid, &sub_addr); + if (sub_addr == BT_MESH_ADDR_UNASSIGNED) { + goto send_status; + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(&mod, sub_addr); + if (match) { + *match = BT_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + } else { + status = STATUS_CANNOT_REMOVE; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (ARRAY_SIZE(mod->groups) > 0) { + bt_mesh_model_tree_walk(bt_mesh_model_root(mod), + mod_sub_clear_visitor, NULL); + + status = va_add(label_uuid, &sub_addr); + if (status == STATUS_SUCCESS) { + mod->groups[0] = sub_addr; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} +#else +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + return; + } + + net_buf_simple_pull(buf, 18); + + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BT_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} +#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */ + +static void send_net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_STATUS, 3); + + bt_mesh_model_msg_init(msg, OP_NET_KEY_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, idx); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send NetKey Status"); + } + + os_mbuf_free_chain(msg); +} + +static void net_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) { + sub = &bt_mesh.sub[i]; + break; + } + } + + if (!sub) { + send_net_key_status(model, ctx, idx, + STATUS_INSUFF_RESOURCES); + return; + } + } + + /* Check for already existing subnet */ + if (sub->net_idx == idx) { + u8_t status; + + if (memcmp(buf->om_data, sub->keys[0].net, 16)) { + status = STATUS_IDX_ALREADY_STORED; + } else { + status = STATUS_SUCCESS; + } + + send_net_key_status(model, ctx, idx, status); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[0], buf->om_data); + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->net_idx = idx; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + bt_mesh_proxy_beacon_send(sub); + bt_mesh_adv_update(); + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void net_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY); + return; + } + + /* The node shall successfully process a NetKey Update message on a + * valid NetKeyIndex when the NetKey value is different and the Key + * Refresh procedure has not been started, or when the NetKey value is + * the same in Phase 1. The NetKey Update message shall generate an + * error when the node is in Phase 2, or Phase 3. + */ + switch (sub->kr_phase) { + case BT_MESH_KR_NORMAL: + if (!memcmp(buf->om_data, sub->keys[0].net, 16)) { + return; + } + break; + case BT_MESH_KR_PHASE_1: + if (!memcmp(buf->om_data, sub->keys[1].net, 16)) { + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); + return; + } + /* fall through */ + case BT_MESH_KR_PHASE_2: + case BT_MESH_KR_PHASE_3: + send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[1], buf->om_data); + if (!err && ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND)))) { + err = friend_cred_update(sub); + } + + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->kr_phase = BT_MESH_KR_PHASE_1; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + bt_mesh_net_beacon_update(sub); + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg) +{ + BT_DBG(""); + + cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_pub.count = 0; + cfg->hb_pub.ttl = 0; + cfg->hb_pub.period = 0; + + k_delayed_work_cancel(&cfg->hb_pub.timer); +} + +static void net_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t del_idx; + u8_t status; + + del_idx = net_buf_simple_pull_le16(buf); + if (del_idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", del_idx); + return; + } + + BT_DBG("idx 0x%04x", del_idx); + + sub = bt_mesh_subnet_get(del_idx); + if (!sub) { + /* This could be a retry of a previous attempt that had its + * response lost, so pretend that it was a success. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + /* The key that the message was encrypted with cannot be removed. + * The NetKey List must contain a minimum of one NetKey. + */ + if (ctx->net_idx == del_idx) { + status = STATUS_CANNOT_REMOVE; + goto send_status; + } + + bt_mesh_subnet_del(sub, true); + status = STATUS_SUCCESS; + +send_status: + send_net_key_status(model, ctx, del_idx, status); +} + +static void net_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = + BT_MESH_MODEL_BUF(OP_NET_KEY_LIST, + IDX_LEN(CONFIG_BT_MESH_SUBNET_COUNT)); + u16_t prev, i; + + bt_mesh_model_msg_init(msg, OP_NET_KEY_LIST); + + prev = BT_MESH_KEY_UNUSED; + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (prev == BT_MESH_KEY_UNUSED) { + prev = sub->net_idx; + continue; + } + + key_idx_pack(msg, prev, sub->net_idx); + prev = BT_MESH_KEY_UNUSED; + } + + if (prev != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, prev); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send NetKey List"); + } + + os_mbuf_free_chain(msg); +} + +static void node_identity_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY); + node_id = 0x00; + } else { + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + node_id = sub->node_id; + } + + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, node_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Identity Status"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void node_identity_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_WARN("Invalid NetKeyIndex 0x%04x", idx); + goto done; + } + + node_id = net_buf_simple_pull_u8(buf); + if (node_id != 0x00 && node_id != 0x01) { + BT_WARN("Invalid Node ID value 0x%02x", node_id); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY); + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, node_id); + } else { + net_buf_simple_add_u8(msg, STATUS_SUCCESS); + net_buf_simple_add_le16(msg, idx); + + if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) { + if (node_id) { + bt_mesh_proxy_identity_start(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + } + bt_mesh_adv_update(); + } + + net_buf_simple_add_u8(msg, sub->node_id); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Identity Status"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void create_mod_app_status(struct os_mbuf *msg, + struct bt_mesh_model *mod, bool vnd, + u16_t elem_addr, u16_t app_idx, + u8_t status, u8_t *mod_id) +{ + bt_mesh_model_msg_init(msg, OP_MOD_APP_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, app_idx); + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } +} + +static void mod_app_bind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + /* Configuration Server only allows device key based access */ + if (model == mod) { + BT_ERR("Client tried to bind AppKey to Configuration Model"); + status = STATUS_CANNOT_BIND; + goto send_status; + } + + status = mod_bind(mod, key_app_idx); + + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_bound(ctx->addr, mod, key_app_idx); + } + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model App Bind Status response"); + } + +done: + os_mbuf_free_chain(msg); + +} + +static void mod_app_unbind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->om_data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = mod_unbind(mod, key_app_idx, true); + + if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) { + bt_test_mesh_model_unbound(ctx->addr, mod, key_app_idx); + } + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model App Unbind Status response"); + } + +done: + os_mbuf_free_chain(msg); +} + +#define KEY_LIST_LEN (MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) * 2) + +static void mod_app_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(max(BT_MESH_MODEL_BUF_LEN(OP_VND_MOD_APP_LIST, + 9 + KEY_LIST_LEN), + BT_MESH_MODEL_BUF_LEN(OP_SIG_MOD_APP_LIST, + 9 + KEY_LIST_LEN))); + + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + u16_t elem_addr; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_WARN("Prohibited element address"); + goto done; + } + + mod_id = buf->om_data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->om_len == 4); + status = STATUS_INVALID_ADDRESS; + goto send_list; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_list; + } + + status = STATUS_SUCCESS; + +send_list: + if (vnd) { + bt_mesh_model_msg_init(msg, OP_VND_MOD_APP_LIST); + } else { + bt_mesh_model_msg_init(msg, OP_SIG_MOD_APP_LIST); + } + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + + if (vnd) { + net_buf_simple_add_mem(msg, mod_id, 4); + } else { + net_buf_simple_add_mem(msg, mod_id, 2); + } + + if (mod) { + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] != BT_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(msg, mod->keys[i]); + } + } + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Model Application List message"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void node_reset(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_RESET_STATUS, 0); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + + bt_mesh_model_msg_init(msg, OP_NODE_RESET_STATUS); + + /* Send the response first since we wont have any keys left to + * send it later. + */ + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Node Reset Status"); + } + + bt_mesh_reset(); + os_mbuf_free_chain(msg); +} + +static void send_friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_FRIEND_STATUS, 1); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + bt_mesh_model_msg_init(msg, OP_FRIEND_STATUS); + net_buf_simple_add_u8(msg, cfg->frnd); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Friend Status"); + } + os_mbuf_free_chain(msg); +} + +static void friend_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + send_friend_status(model, ctx); +} + +static void friend_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) { + BT_WARN("Invalid Friend value 0x%02x", buf->om_data[0]); + return; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("Friend 0x%02x -> 0x%02x", cfg->frnd, buf->om_data[0]); + + if (cfg->frnd == buf->om_data[0]) { + goto send_status; + } + + if (MYNEWT_VAL(BLE_MESH_FRIEND)) { + cfg->frnd = buf->om_data[0]; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->frnd == BT_MESH_FRIEND_DISABLED) { + bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY); + } + } + + if (cfg->hb_pub.feat & BT_MESH_FEAT_FRIEND) { + bt_mesh_heartbeat_send(); + } + +send_status: + send_friend_status(model, ctx); +} + +static void lpn_timeout_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_LPN_TIMEOUT_STATUS, 5); + struct bt_mesh_friend *frnd; + u16_t lpn_addr; + s32_t timeout; + + lpn_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x lpn_addr 0x%02x", + ctx->net_idx, ctx->app_idx, ctx->addr, lpn_addr); + + /* check if it's the address of the Low Power Node? */ + if (!BT_MESH_ADDR_IS_UNICAST(lpn_addr)) { + BT_WARN("Invalid LPNAddress; ignoring msg"); + goto done; + } + + bt_mesh_model_msg_init(msg, OP_LPN_TIMEOUT_STATUS); + net_buf_simple_add_le16(msg, lpn_addr); + + if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + timeout = 0; + goto send_rsp; + } + + frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true); + if (!frnd) { + timeout = 0; + goto send_rsp; + } + + timeout = k_delayed_work_remaining_get(&frnd->timer) / 100; + +send_rsp: + net_buf_simple_add_u8(msg, timeout); + net_buf_simple_add_u8(msg, timeout >> 8); + net_buf_simple_add_u8(msg, timeout >> 16); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send LPN PollTimeout Status"); + } + +done: + os_mbuf_free_chain(msg); +} + +static void send_krp_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t phase, u8_t status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_KRP_STATUS, 4); + + bt_mesh_model_msg_init(msg, OP_KRP_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, idx); + net_buf_simple_add_u8(msg, phase); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Key Refresh State Status"); + } + + os_mbuf_free_chain(msg); +} + +static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + } else { + send_krp_status(model, ctx, idx, sub->kr_phase, + STATUS_SUCCESS); + } +} + +static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + u8_t phase; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + phase = net_buf_simple_pull_u8(buf); + + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + BT_DBG("idx 0x%04x transition 0x%02x", idx, phase); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + return; + } + + BT_DBG("%u -> %u", sub->kr_phase, phase); + + if (phase < BT_MESH_KR_PHASE_2 || phase > BT_MESH_KR_PHASE_3 || + (sub->kr_phase == BT_MESH_KR_NORMAL && + phase == BT_MESH_KR_PHASE_2)) { + BT_WARN("Prohibited transition %u -> %u", sub->kr_phase, phase); + return; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_1 && + phase == BT_MESH_KR_PHASE_2) { + sub->kr_phase = BT_MESH_KR_PHASE_2; + sub->kr_flag = 1; + bt_mesh_net_beacon_update(sub); + } else if ((sub->kr_phase == BT_MESH_KR_PHASE_1 || + sub->kr_phase == BT_MESH_KR_PHASE_2) && + phase == BT_MESH_KR_PHASE_3) { + bt_mesh_net_revoke_keys(sub); + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND))) { + friend_cred_refresh(ctx->net_idx); + } + sub->kr_phase = BT_MESH_KR_NORMAL; + sub->kr_flag = 0; + bt_mesh_net_beacon_update(sub); + } + + send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS); +} + +static u8_t hb_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val); + } +} + +static u8_t hb_pub_count_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0x01) { + return 0x01; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val - 1) + 1; + } +} + +static u16_t hb_pwr2(u8_t val, u8_t sub) +{ + if (!val) { + return 0x0000; + } else if (val == 0xff || val == 0x11) { + return 0xffff; + } else { + return (1 << (val - sub)); + } +} + +struct hb_pub_param { + u16_t dst; + u8_t count_log; + u8_t period_log; + u8_t ttl; + u16_t feat; + u16_t net_idx; +} __packed; + +static void hb_pub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + struct hb_pub_param *orig_msg) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_STATUS, 10); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_STATUS); + + net_buf_simple_add_u8(msg, status); + + if (orig_msg) { + memcpy(net_buf_simple_add(msg, sizeof(*orig_msg)), orig_msg, + sizeof(*orig_msg)); + goto send; + } + + net_buf_simple_add_le16(msg, cfg->hb_pub.dst); + net_buf_simple_add_u8(msg, hb_pub_count_log(cfg->hb_pub.count)); + net_buf_simple_add_u8(msg, cfg->hb_pub.period); + net_buf_simple_add_u8(msg, cfg->hb_pub.ttl); + net_buf_simple_add_le16(msg, cfg->hb_pub.feat); + net_buf_simple_add_le16(msg, cfg->hb_pub.net_idx); + +send: + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Heartbeat Publication Status"); + } + + os_mbuf_free_chain(msg); +} + +static void heartbeat_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); +} + +static void heartbeat_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct hb_pub_param *param = (void *)buf->om_data; + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t dst, feat, idx; + u8_t status; + + BT_DBG("src 0x%04x", ctx->addr); + + dst = sys_le16_to_cpu(param->dst); + /* All other address types but virtual are valid */ + if (BT_MESH_ADDR_IS_VIRTUAL(dst)) { + status = STATUS_INVALID_ADDRESS; + goto failed; + } + + if (param->count_log > 0x11 && param->count_log != 0xff) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->period_log > 0x10) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) { + BT_ERR("Invalid TTL value 0x%02x", param->ttl); + return; + } + + feat = sys_le16_to_cpu(param->feat); + + idx = sys_le16_to_cpu(param->net_idx); + if (idx > 0xfff) { + BT_ERR("Invalid NetKeyIndex 0x%04x", idx); + return; + } + + if (!bt_mesh_subnet_get(idx)) { + status = STATUS_INVALID_NETKEY; + goto failed; + } + + cfg->hb_pub.dst = dst; + cfg->hb_pub.period = param->period_log; + cfg->hb_pub.feat = feat & BT_MESH_FEAT_SUPPORTED; + cfg->hb_pub.net_idx = idx; + + if (dst == BT_MESH_ADDR_UNASSIGNED) { + hb_pub_disable(cfg); + } else { + /* 2^(n-1) */ + cfg->hb_pub.count = hb_pwr2(param->count_log, 1); + cfg->hb_pub.ttl = param->ttl; + + BT_DBG("period %u ms", hb_pwr2(param->period_log, 1) * 1000); + + /* The first Heartbeat message shall be published as soon + * as possible after the Heartbeat Publication Period state + * has been configured for periodic publishing. + */ + if (param->period_log && param->count_log) { + k_work_submit(&cfg->hb_pub.timer.work); + } else { + k_delayed_work_cancel(&cfg->hb_pub.timer); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_hb_pub(); + } + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); + + return; + +failed: + hb_pub_send_status(model, ctx, status, param); +} + +static void hb_sub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_STATUS, 9); + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t period; + s64_t uptime; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + uptime = k_uptime_get(); + if (uptime > cfg->hb_sub.expiry) { + period = 0; + } else { + period = (cfg->hb_sub.expiry - uptime) / 1000; + } + + bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_STATUS); + + net_buf_simple_add_u8(msg, status); + + net_buf_simple_add_le16(msg, cfg->hb_sub.src); + net_buf_simple_add_le16(msg, cfg->hb_sub.dst); + + net_buf_simple_add_u8(msg, hb_log(period)); + net_buf_simple_add_u8(msg, hb_log(cfg->hb_sub.count)); + net_buf_simple_add_u8(msg, cfg->hb_sub.min_hops); + net_buf_simple_add_u8(msg, cfg->hb_sub.max_hops); + + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Heartbeat Subscription Status"); + } + + os_mbuf_free_chain(msg); +} + +static void heartbeat_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); +} + +static void heartbeat_sub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t sub_src, sub_dst; + u8_t sub_period; + s32_t period_ms; + + BT_DBG("src 0x%04x", ctx->addr); + + sub_src = net_buf_simple_pull_le16(buf); + sub_dst = net_buf_simple_pull_le16(buf); + sub_period = net_buf_simple_pull_u8(buf); + + BT_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x", + sub_src, sub_dst, sub_period); + + if (sub_src != BT_MESH_ADDR_UNASSIGNED && + !BT_MESH_ADDR_IS_UNICAST(sub_src)) { + BT_WARN("Prohibited source address"); + return; + } + + if (BT_MESH_ADDR_IS_VIRTUAL(sub_dst) || BT_MESH_ADDR_IS_RFU(sub_dst) || + (BT_MESH_ADDR_IS_UNICAST(sub_dst) && + sub_dst != bt_mesh_primary_addr())) { + BT_WARN("Prohibited destination address"); + return; + } + + if (sub_period > 0x11) { + BT_WARN("Prohibited subscription period 0x%02x", sub_period); + return; + } + + if (sub_src == BT_MESH_ADDR_UNASSIGNED || + sub_dst == BT_MESH_ADDR_UNASSIGNED || + sub_period == 0x00) { + /* Only an explicit address change to unassigned should + * trigger clearing of the values according to + * MESH/NODE/CFG/HBS/BV-02-C. + */ + if (sub_src == BT_MESH_ADDR_UNASSIGNED || + sub_dst == BT_MESH_ADDR_UNASSIGNED) { + cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.min_hops = BT_MESH_TTL_MAX; + cfg->hb_sub.max_hops = 0; + cfg->hb_sub.count = 0; + } + + period_ms = 0; + } else { + cfg->hb_sub.src = sub_src; + cfg->hb_sub.dst = sub_dst; + cfg->hb_sub.min_hops = BT_MESH_TTL_MAX; + cfg->hb_sub.max_hops = 0; + cfg->hb_sub.count = 0; + period_ms = hb_pwr2(sub_period, 1) * 1000; + } + + /* Let the transport layer know it needs to handle this address */ + bt_mesh_set_hb_sub_dst(cfg->hb_sub.dst); + + BT_DBG("period_ms %u", (unsigned) period_ms); + + if (period_ms) { + cfg->hb_sub.expiry = k_uptime_get() + period_ms; + } else { + cfg->hb_sub.expiry = 0; + } + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); + + /* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after + * disabling subscription, but 0x00 for subsequent Get requests. + */ + if (!period_ms) { + cfg->hb_sub.min_hops = 0; + } +} + +const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = { + { OP_DEV_COMP_DATA_GET, 1, dev_comp_data_get }, + { OP_APP_KEY_ADD, 19, app_key_add }, + { OP_APP_KEY_UPDATE, 19, app_key_update }, + { OP_APP_KEY_DEL, 3, app_key_del }, + { OP_APP_KEY_GET, 2, app_key_get }, + { OP_BEACON_GET, 0, beacon_get }, + { OP_BEACON_SET, 1, beacon_set }, + { OP_DEFAULT_TTL_GET, 0, default_ttl_get }, + { OP_DEFAULT_TTL_SET, 1, default_ttl_set }, + { OP_GATT_PROXY_GET, 0, gatt_proxy_get }, + { OP_GATT_PROXY_SET, 1, gatt_proxy_set }, + { OP_NET_TRANSMIT_GET, 0, net_transmit_get }, + { OP_NET_TRANSMIT_SET, 1, net_transmit_set }, + { OP_RELAY_GET, 0, relay_get }, + { OP_RELAY_SET, 2, relay_set }, + { OP_MOD_PUB_GET, 4, mod_pub_get }, + { OP_MOD_PUB_SET, 11, mod_pub_set }, + { OP_MOD_PUB_VA_SET, 24, mod_pub_va_set }, + { OP_MOD_SUB_ADD, 6, mod_sub_add }, + { OP_MOD_SUB_VA_ADD, 20, mod_sub_va_add }, + { OP_MOD_SUB_DEL, 6, mod_sub_del }, + { OP_MOD_SUB_VA_DEL, 20, mod_sub_va_del }, + { OP_MOD_SUB_OVERWRITE, 6, mod_sub_overwrite }, + { OP_MOD_SUB_VA_OVERWRITE, 20, mod_sub_va_overwrite }, + { OP_MOD_SUB_DEL_ALL, 4, mod_sub_del_all }, + { OP_MOD_SUB_GET, 4, mod_sub_get }, + { OP_MOD_SUB_GET_VND, 6, mod_sub_get_vnd }, + { OP_NET_KEY_ADD, 18, net_key_add }, + { OP_NET_KEY_UPDATE, 18, net_key_update }, + { OP_NET_KEY_DEL, 2, net_key_del }, + { OP_NET_KEY_GET, 0, net_key_get }, + { OP_NODE_IDENTITY_GET, 2, node_identity_get }, + { OP_NODE_IDENTITY_SET, 3, node_identity_set }, + { OP_MOD_APP_BIND, 6, mod_app_bind }, + { OP_MOD_APP_UNBIND, 6, mod_app_unbind }, + { OP_SIG_MOD_APP_GET, 4, mod_app_get }, + { OP_VND_MOD_APP_GET, 6, mod_app_get }, + { OP_NODE_RESET, 0, node_reset }, + { OP_FRIEND_GET, 0, friend_get }, + { OP_FRIEND_SET, 1, friend_set }, + { OP_LPN_TIMEOUT_GET, 2, lpn_timeout_get }, + { OP_KRP_GET, 2, krp_get }, + { OP_KRP_SET, 3, krp_set }, + { OP_HEARTBEAT_PUB_GET, 0, heartbeat_pub_get }, + { OP_HEARTBEAT_PUB_SET, 9, heartbeat_pub_set }, + { OP_HEARTBEAT_SUB_GET, 0, heartbeat_sub_get }, + { OP_HEARTBEAT_SUB_SET, 5, heartbeat_sub_set }, + BT_MESH_MODEL_OP_END, +}; + +static void hb_publish(struct ble_npl_event *work) +{ + struct bt_mesh_cfg_srv *cfg = ble_npl_event_get_arg(work); + struct bt_mesh_subnet *sub; + u16_t period_ms; + + BT_DBG("hb_pub.count: %u", cfg->hb_pub.count); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if (!sub) { + BT_ERR("No matching subnet for idx 0x%02x", + cfg->hb_pub.net_idx); + cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED; + return; + } + + if (cfg->hb_pub.count == 0) { + return; + } + + period_ms = hb_pwr2(cfg->hb_pub.period, 1) * 1000; + if (period_ms && cfg->hb_pub.count > 1) { + k_delayed_work_submit(&cfg->hb_pub.timer, period_ms); + } + + bt_mesh_heartbeat_send(); + + if (cfg->hb_pub.count != 0xffff) { + cfg->hb_pub.count--; + } +} + +static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg) +{ + if (cfg->relay > 0x02) { + return false; + } + + if (cfg->beacon > 0x01) { + return false; + } + + if (cfg->default_ttl > BT_MESH_TTL_MAX) { + return false; + } + + return true; +} + +static int cfg_srv_init(struct bt_mesh_model *model) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG(""); + + if (!bt_mesh_model_in_primary(model)) { + BT_ERR("Configuration Server only allowed in primary element"); + return -EINVAL; + } + + if (!cfg) { + BT_ERR("No Configuration Server context provided"); + return -EINVAL; + } + + if (!conf_is_valid(cfg)) { + BT_ERR("Invalid values in configuration"); + return -EINVAL; + } + + /* + * Configuration Model security is device-key based and only the local + * device-key is allowed to access this model. + */ + model->keys[0] = BT_MESH_KEY_DEV_LOCAL; + + if (!(MYNEWT_VAL(BLE_MESH_RELAY))) { + cfg->relay = BT_MESH_RELAY_NOT_SUPPORTED; + } + + if (!(MYNEWT_VAL(BLE_MESH_FRIEND))) { + cfg->frnd = BT_MESH_FRIEND_NOT_SUPPORTED; + } + + if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + cfg->gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED; + } + + k_delayed_work_init(&cfg->hb_pub.timer, hb_publish); + k_delayed_work_add_arg(&cfg->hb_pub.timer, cfg); + cfg->hb_pub.net_idx = BT_MESH_KEY_UNUSED; + cfg->hb_sub.expiry = 0; + + cfg->model = model; + + conf = cfg; + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb = { + .init = cfg_srv_init, +}; + +static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + size_t clear_count; + + /* Clear model state that isn't otherwise cleared. E.g. AppKey + * binding and model publication is cleared as a consequence + * of removing all app keys, however model subscription and user data + * clearing must be taken care of here. + */ + + clear_count = mod_sub_list_clear(mod); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + if (clear_count) { + bt_mesh_store_mod_sub(mod); + } + + bt_mesh_model_data_store(mod, vnd, NULL, 0); + } + + if (mod->cb && mod->cb->reset) { + mod->cb->reset(mod); + } +} + +void bt_mesh_cfg_reset(void) +{ + struct bt_mesh_cfg_srv *cfg = conf; + int i; + + BT_DBG(""); + + if (!cfg) { + return; + } + + bt_mesh_set_hb_sub_dst(BT_MESH_ADDR_UNASSIGNED); + + cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.expiry = 0; + + /* Delete all net keys, which also takes care of all app keys which + * are associated with each net key. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_subnet_del(sub, true); + } + } + + bt_mesh_model_foreach(mod_reset, NULL); + + memset(labels, 0, sizeof(labels)); +} + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat) +{ + struct bt_mesh_cfg_srv *cfg = conf; + + if (!cfg) { + BT_WARN("No configuaration server context available"); + return; + } + + if (src != cfg->hb_sub.src || dst != cfg->hb_sub.dst) { + BT_WARN("No subscription for received heartbeat"); + return; + } + + if (k_uptime_get() > cfg->hb_sub.expiry) { + BT_WARN("Heartbeat subscription period expired"); + return; + } + + cfg->hb_sub.min_hops = min(cfg->hb_sub.min_hops, hops); + cfg->hb_sub.max_hops = max(cfg->hb_sub.max_hops, hops); + + if (cfg->hb_sub.count < 0xffff) { + cfg->hb_sub.count++; + } + + BT_DBG("src 0x%04x dst 0x%04x hops %u min %u max %u count %u", src, + dst, hops, cfg->hb_sub.min_hops, cfg->hb_sub.max_hops, + cfg->hb_sub.count); + + if (cfg->hb_sub.func) { + cfg->hb_sub.func(hops, feat); + } +} + +u8_t bt_mesh_net_transmit_get(void) +{ + if (conf) { + return conf->net_transmit; + } + + return 0; +} + +u8_t bt_mesh_relay_get(void) +{ + if (conf) { + return conf->relay; + } + + return BT_MESH_RELAY_NOT_SUPPORTED; +} + +u8_t bt_mesh_friend_get(void) +{ + BT_DBG("conf %p conf->frnd 0x%02x", conf, conf->frnd); + + if (conf) { + return conf->frnd; + } + + return BT_MESH_FRIEND_NOT_SUPPORTED; +} + +u8_t bt_mesh_relay_retransmit_get(void) +{ + if (conf) { + return conf->relay_retransmit; + } + + return 0; +} + +u8_t bt_mesh_beacon_get(void) +{ + if (conf) { + return conf->beacon; + } + + return BT_MESH_BEACON_DISABLED; +} + +u8_t bt_mesh_gatt_proxy_get(void) +{ + if (conf) { + return conf->gatt_proxy; + } + + return BT_MESH_GATT_PROXY_NOT_SUPPORTED; +} + +u8_t bt_mesh_default_ttl_get(void) +{ + if (conf) { + return conf->default_ttl; + } + + return DEFAULT_TTL; +} + +u8_t *bt_mesh_label_uuid_get(u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (labels[i].addr == addr) { + BT_DBG("Found Label UUID for 0x%04x: %s", addr, + bt_hex(labels[i].uuid, 16)); + return labels[i].uuid; + } + } + + BT_WARN("No matching Label UUID for 0x%04x", addr); + + return NULL; +} + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void) +{ + if (!conf) { + return NULL; + } + + return &conf->hb_pub; +} + +void bt_mesh_hb_pub_disable(void) +{ + if (conf) { + hb_pub_disable(conf); + } +} + +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void) +{ + return conf; +} + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store) +{ + int i; + + BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store); + + if (conf && conf->hb_pub.net_idx == sub->net_idx) { + hb_pub_disable(conf); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_store_hb_pub(); + } + } + + /* Delete any app keys bound to this NetKey index */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == sub->net_idx) { + bt_mesh_app_key_del(key, store); + } + } + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_clear_net_idx(sub->net_idx); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_subnet(sub); + } + + memset(sub, 0, sizeof(*sub)); + sub->net_idx = BT_MESH_KEY_UNUSED; +} +#endif \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.c new file mode 100644 index 0000000..4258976 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.c @@ -0,0 +1,969 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG + +#include +#include +#include + +#if (MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS)) +#include "mbedtls/aes.h" +#include "mbedtls/cipher.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/cmac.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" + +#else +#include "nimble/ext/tinycrypt/include/tinycrypt/constants.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/utils.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/aes.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/cmac_mode.h" +#include "nimble/ext/tinycrypt/include/tinycrypt/ccm_mode.h" +#endif + +#include "crypto.h" + +#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4) +#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]) +{ + int rc = BLE_HS_EUNKNOWN; + mbedtls_cipher_context_t ctx = {0}; + const mbedtls_cipher_info_t *cipher_info; + + mbedtls_cipher_init(&ctx); + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + if (cipher_info == NULL) { + goto exit; + } + + if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + goto exit; + } + + rc = mbedtls_cipher_cmac_starts(&ctx, key, 128); + if (rc != 0) { + goto exit; + } + + for (; sg_len; sg_len--, sg++) { + if (sg->len != 0 && sg->data != NULL) { + if ((rc = mbedtls_cipher_cmac_update(&ctx, sg->data, sg->len)) != 0) { + goto exit; + } + } + } + rc = mbedtls_cipher_cmac_finish(&ctx, mac); + +exit: + mbedtls_cipher_free(&ctx); + if (rc != 0) { + return -EIO; + } + + return 0; +} + +#else +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]) +{ + struct tc_aes_key_sched_struct sched; + struct tc_cmac_struct state; + + if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) { + return -EIO; + } + + for (; sg_len; sg_len--, sg++) { + if (tc_cmac_update(&state, sg->data, + sg->len) == TC_CRYPTO_FAIL) { + return -EIO; + } + } + + if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) { + return -EIO; + } + + return 0; +} +#endif + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]) +{ + int err; + + err = bt_mesh_aes_cmac_one(salt, ikm, ikm_len, okm); + if (err < 0) { + return err; + } + + return bt_mesh_aes_cmac_one(okm, info, strlen(info), okm); +} + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]) +{ + struct bt_mesh_sg sg[3]; + u8_t salt[16]; + u8_t out[16]; + u8_t t[16]; + u8_t pad; + int err; + + BT_DBG("n %s", bt_hex(n, 16)); + BT_DBG("p %s", bt_hex(p, p_len)); + + err = bt_mesh_s1("smk2", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, n, 16, t); + if (err) { + return err; + } + + pad = 0x01; + + sg[0].data = NULL; + sg[0].len = 0; + sg[1].data = p; + sg[1].len = p_len; + sg[2].data = &pad; + sg[2].len = sizeof(pad); + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + net_id[0] = out[15] & 0x7f; + + sg[0].data = out; + sg[0].len = sizeof(out); + pad = 0x02; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(enc_key, out, 16); + + pad = 0x03; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(priv_key, out, 16); + + BT_DBG("NID 0x%02x enc_key %s", net_id[0], bt_hex(enc_key, 16)); + BT_DBG("priv_key %s", bt_hex(priv_key, 16)); + + return 0; +} + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]) +{ + u8_t id64[] = { 'i', 'd', '6', '4', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk3", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id64, sizeof(id64), tmp); + if (err) { + return err; + } + + memcpy(out, tmp + 8, 8); + + return 0; +} + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]) +{ + u8_t id6[] = { 'i', 'd', '6', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk4", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id6, sizeof(id6), tmp); + if (err) { + return err; + } + + out[0] = tmp[15] & BIT_MASK(6); + + return 0; +} + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]) +{ + const char *id128 = "id128\x01"; + u8_t salt[16]; + int err; + + err = bt_mesh_s1(s, salt); + if (err) { + return err; + } + + return bt_mesh_k1(n, 16, salt, id128, out); +} + +static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *enc_msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16], mic[16]; + u16_t last_blk, blk_cnt; + size_t i, j; + int err; + + if (msg_len < 1 || aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, last_blk); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + } else { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < 16; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, 16); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + } + + if (memcmp(mic, enc_msg + msg_len, mic_size)) { + return -EBADMSG; + } + + return 0; +} + +static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t pmsg[16], cmic[16], cmsg[16], mic[16], Xn[16]; + u16_t blk_cnt, last_blk; + size_t i, j; + int err; + + BT_DBG("key %s", bt_hex(key, 16)); + BT_DBG("nonce %s", bt_hex(nonce, 13)); + BT_DBG("msg (len %zu) %s", msg_len, bt_hex(msg, msg_len)); + BT_DBG("aad_len %zu mic_size %zu", aad_len, mic_size); + + /* Unsupported AAD size */ + if (aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + } else { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + + err = bt_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_N */ + for (i = 0; i < 16; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + + } + } + + memcpy(out_msg + msg_len, mic, mic_size); + + return 0; +} + +static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x03; + + /* Pad */ + nonce[1] = 0x00; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0; + nonce[8] = 0; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} + +static void create_net_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x00; + + /* FRND + TTL */ + nonce[1] = pdu[1]; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0; + nonce[8] = 0; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]) +{ + u8_t priv_rand[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, }; + u8_t tmp[16]; + int err, i; + + BT_DBG("IVIndex %u, PrivacyKey %s", (unsigned) iv_index, + bt_hex(privacy_key, 16)); + + sys_put_be32(iv_index, &priv_rand[5]); + memcpy(&priv_rand[9], &pdu[7], 7); + + BT_DBG("PrivacyRandom %s", bt_hex(priv_rand, 16)); + + err = bt_encrypt_be(privacy_key, priv_rand, tmp); + if (err) { + return err; + } + + for (i = 0; i < 6; i++) { + pdu[1 + i] ^= tmp[i]; + } + + return 0; +} + +int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->om_data); + u8_t nonce[13]; + int err; + + BT_DBG("IVIndex %u EncKey %s mic_len %u", (unsigned) iv_index, + bt_hex(key, 16), mic_len); + BT_DBG("PDU (len %u) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (IS_ENABLED(CONFIG_BT_MESH_PROXY) && proxy) { + create_proxy_nonce(nonce, buf->om_data, iv_index); + } else { + create_net_nonce(nonce, buf->om_data, iv_index); + } + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_encrypt(key, nonce, &buf->om_data[7], buf->om_len - 7, + NULL, 0, &buf->om_data[7], mic_len); + if (!err) { + net_buf_simple_add(buf, mic_len); + } + + return err; +} + +int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->om_data); + u8_t nonce[13]; + + BT_DBG("PDU (%u bytes) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("iv_index %u, key %s mic_len %u", (unsigned) iv_index, + bt_hex(key, 16), mic_len); + + if (IS_ENABLED(CONFIG_BT_MESH_PROXY) && proxy) { + create_proxy_nonce(nonce, buf->om_data, iv_index); + } else { + create_net_nonce(nonce, buf->om_data, iv_index); + } + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + buf->om_len -= mic_len; + + return bt_mesh_ccm_decrypt(key, nonce, &buf->om_data[7], buf->om_len - 7, + NULL, 0, &buf->om_data[7], mic_len); +} + +static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic, + u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + if (dev_key) { + nonce[0] = 0x02; + } else { + nonce[0] = 0x01; + } + + sys_put_be32((seq_num | ((u32_t)aszmic << 31)), &nonce[1]); + + sys_put_be16(src, &nonce[5]); + sys_put_be16(dst, &nonce[7]); + + sys_put_be32(iv_index, &nonce[9]); +} + +static int mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index) +{ + u8_t nonce[13]; + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst); + BT_DBG("seq_num 0x%08x iv_index 0x%08x", (unsigned) seq_num, + (unsigned) iv_index); + BT_DBG("Clear: %s", bt_hex(buf->om_data, buf->om_len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + return bt_mesh_ccm_encrypt(key, nonce, buf->om_data, buf->om_len, ad, + ad ? 16 : 0, buf->om_data, + APP_MIC_LEN(aszmic)); +} + +int bt_mesh_app_encrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, u16_t src, + u16_t dst, u32_t seq_num, u32_t iv_index) +{ + int err; + + err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst, + seq_num, iv_index); + if (!err) { + BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len)); + } + + return err; +} + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index) +{ + int err; + + err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst, + seq_num, iv_index); + + if (!err) { + net_buf_simple_add(buf, APP_MIC_LEN(aszmic)); + BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len)); + } + + return err; +} + +static int mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, struct os_mbuf *out, + const u8_t *ad, u16_t src, u16_t dst, + u32_t seq_num, u32_t iv_index) +{ + u8_t nonce[13]; + + BT_DBG("EncData (len %u) %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + return bt_mesh_ccm_decrypt(key, nonce, buf->om_data, buf->om_len, ad, + ad ? 16 : 0, out->om_data, + APP_MIC_LEN(aszmic)); +} + +int bt_mesh_app_decrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, u16_t src, + u16_t dst, u32_t seq_num, u32_t iv_index) +{ + return mesh_app_decrypt(key, dev_key, aszmic, buf, buf, + ad, src, dst, seq_num, iv_index); +} + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, struct os_mbuf *out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + int err; + + err = mesh_app_decrypt(key, dev_key, aszmic, buf, out, + ad, src, dst, seq_num, iv_index); + if (!err) { + net_buf_simple_add(out, buf->om_len); + } + + return err; +} + +/* reversed, 8-bit, poly=0x07 */ +static const u8_t crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len) +{ + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + BT_DBG("fcs 0x%02x", 0xff - fcs); + + return 0xff - fcs; +} + +bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs) +{ + const u8_t *data = buf->om_data; + u16_t data_len = buf->om_len; + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + return crc_table[fcs ^ received_fcs] == 0xcf; +} + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr) +{ + u8_t salt[16]; + u8_t tmp[16]; + int err; + + err = bt_mesh_s1("vtad", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, virtual_label, 16, tmp); + if (err) { + return err; + } + + *addr = (sys_get_be16(&tmp[14]) & 0x3fff) | 0x8000; + + return 0; +} + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]) +{ + const u8_t conf_salt_key[16] = { 0 }; + + return bt_mesh_aes_cmac_one(conf_salt_key, conf_inputs, 145, salt); +} + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]) +{ + return bt_mesh_k1(dhkey, 32, conf_salt, "prck", conf_key); +} + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]) +{ + struct bt_mesh_sg sg[] = { { rand, 16 }, { auth, 16 } }; + + BT_DBG("ConfirmationKey %s", bt_hex(conf_key, 16)); + BT_DBG("RandomDevice %s", bt_hex(rand, 16)); + BT_DBG("AuthValue %s", bt_hex(auth, 16)); + + return bt_mesh_aes_cmac(conf_key, sg, ARRAY_SIZE(sg), conf); +} + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]) +{ + return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8); +} + +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[25 + 8]) +{ + return bt_mesh_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8); +} + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[8], u32_t iv_index, + u8_t auth[8]) +{ + u8_t msg[13], tmp[16]; + int err; + + BT_DBG("BeaconKey %s", bt_hex(beacon_key, 16)); + BT_DBG("NetId %s", bt_hex(net_id, 8)); + BT_DBG("IV Index 0x%08x", (unsigned) iv_index); + + msg[0] = flags; + memcpy(&msg[1], net_id, 8); + sys_put_be32(iv_index, &msg[9]); + + BT_DBG("BeaconMsg %s", bt_hex(msg, sizeof(msg))); + + err = bt_mesh_aes_cmac_one(beacon_key, msg, sizeof(msg), tmp); + if (!err) { + memcpy(auth, tmp, 8); + } + + return err; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.h new file mode 100644 index 0000000..8af371c --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/crypto.h @@ -0,0 +1,170 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ + +#include "../include/mesh/mesh.h" + +struct bt_mesh_sg { + const void *data; + size_t len; +}; + +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]); + +static inline int bt_mesh_aes_cmac_one(const u8_t key[16], const void *m, + size_t len, u8_t mac[16]) +{ + struct bt_mesh_sg sg = { m, len }; + + return bt_mesh_aes_cmac(key, &sg, 1, mac); +} + +static inline bool bt_mesh_s1(const char *m, u8_t salt[16]) +{ + const u8_t zero[16] = { 0 }; + + return bt_mesh_aes_cmac_one(zero, m, strlen(m), salt); +} + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]); + +#define bt_mesh_k1_str(ikm, ikm_len, salt_str, info, okm) \ +({ \ + const u8_t salt[16] = salt_str; \ + bt_mesh_k1(ikm, ikm_len, salt, info, okm); \ +}) + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]); + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]); + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]); + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]); + +static inline int bt_mesh_id_resolving_key(const u8_t net_key[16], + u8_t resolving_key[16]) +{ + return bt_mesh_k1_str(net_key, 16, "smbt", "smbi", resolving_key); +} + +static inline int bt_mesh_identity_key(const u8_t net_key[16], + u8_t identity_key[16]) +{ + return bt_mesh_id128(net_key, "nkik", identity_key); +} + +static inline int bt_mesh_beacon_key(const u8_t net_key[16], + u8_t beacon_key[16]) +{ + return bt_mesh_id128(net_key, "nkbk", beacon_key); +} + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[16], u32_t iv_index, + u8_t auth[8]); + +static inline int bt_mesh_app_id(const u8_t app_key[16], u8_t app_id[1]) +{ + return bt_mesh_k4(app_key, app_id); +} + +static inline int bt_mesh_session_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t session_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prsk", session_key); +} + +static inline int bt_mesh_prov_nonce(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t nonce[13]) +{ + u8_t tmp[16]; + int err; + + err = bt_mesh_k1(dhkey, 32, prov_salt, "prsn", tmp); + if (!err) { + memcpy(nonce, tmp + 3, 13); + } + + return err; +} + +static inline int bt_mesh_dev_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t dev_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prdk", dev_key); +} + +static inline int bt_mesh_prov_salt(const u8_t conf_salt[16], + const u8_t prov_rand[16], + const u8_t dev_rand[16], + u8_t prov_salt[16]) +{ + const u8_t prov_salt_key[16] = { 0 }; + struct bt_mesh_sg sg[] = { + { conf_salt, 16 }, + { prov_rand, 16 }, + { dev_rand, 16 }, + }; + + return bt_mesh_aes_cmac(prov_salt_key, sg, ARRAY_SIZE(sg), prov_salt); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]); + +int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_app_encrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf*buf, const u8_t *ad, u16_t src, + u16_t dst, u32_t seq_num, u32_t iv_index); + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf*buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index); + +int bt_mesh_app_decrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf *buf, const u8_t *ad, u16_t src, + u16_t dst, u32_t seq_num, u32_t iv_index); + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct os_mbuf*buf, struct os_mbuf*out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index); + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len); + +bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs); + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr); + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]); + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]); + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]); + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]); + +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[25 + 8]); +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/foundation.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/foundation.h new file mode 100644 index 0000000..ee615ae --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/foundation.h @@ -0,0 +1,171 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FUNDATION_H__ +#define __FUNDATION_H__ + +#define OP_APP_KEY_ADD BT_MESH_MODEL_OP_1(0x00) +#define OP_APP_KEY_UPDATE BT_MESH_MODEL_OP_1(0x01) +#define OP_DEV_COMP_DATA_STATUS BT_MESH_MODEL_OP_1(0x02) +#define OP_MOD_PUB_SET BT_MESH_MODEL_OP_1(0x03) +#define OP_HEALTH_CURRENT_STATUS BT_MESH_MODEL_OP_1(0x04) +#define OP_HEALTH_FAULT_STATUS BT_MESH_MODEL_OP_1(0x05) +#define OP_HEARTBEAT_PUB_STATUS BT_MESH_MODEL_OP_1(0x06) +#define OP_APP_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x00) +#define OP_APP_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x01) +#define OP_APP_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x02) +#define OP_APP_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x03) +#define OP_ATTENTION_GET BT_MESH_MODEL_OP_2(0x80, 0x04) +#define OP_ATTENTION_SET BT_MESH_MODEL_OP_2(0x80, 0x05) +#define OP_ATTENTION_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x06) +#define OP_ATTENTION_STATUS BT_MESH_MODEL_OP_2(0x80, 0x07) +#define OP_DEV_COMP_DATA_GET BT_MESH_MODEL_OP_2(0x80, 0x08) +#define OP_BEACON_GET BT_MESH_MODEL_OP_2(0x80, 0x09) +#define OP_BEACON_SET BT_MESH_MODEL_OP_2(0x80, 0x0a) +#define OP_BEACON_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0b) +#define OP_DEFAULT_TTL_GET BT_MESH_MODEL_OP_2(0x80, 0x0c) +#define OP_DEFAULT_TTL_SET BT_MESH_MODEL_OP_2(0x80, 0x0d) +#define OP_DEFAULT_TTL_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0e) +#define OP_FRIEND_GET BT_MESH_MODEL_OP_2(0x80, 0x0f) +#define OP_FRIEND_SET BT_MESH_MODEL_OP_2(0x80, 0x10) +#define OP_FRIEND_STATUS BT_MESH_MODEL_OP_2(0x80, 0x11) +#define OP_GATT_PROXY_GET BT_MESH_MODEL_OP_2(0x80, 0x12) +#define OP_GATT_PROXY_SET BT_MESH_MODEL_OP_2(0x80, 0x13) +#define OP_GATT_PROXY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x14) +#define OP_KRP_GET BT_MESH_MODEL_OP_2(0x80, 0x15) +#define OP_KRP_SET BT_MESH_MODEL_OP_2(0x80, 0x16) +#define OP_KRP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x17) +#define OP_MOD_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x18) +#define OP_MOD_PUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x19) +#define OP_MOD_PUB_VA_SET BT_MESH_MODEL_OP_2(0x80, 0x1a) +#define OP_MOD_SUB_ADD BT_MESH_MODEL_OP_2(0x80, 0x1b) +#define OP_MOD_SUB_DEL BT_MESH_MODEL_OP_2(0x80, 0x1c) +#define OP_MOD_SUB_DEL_ALL BT_MESH_MODEL_OP_2(0x80, 0x1d) +#define OP_MOD_SUB_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x1e) +#define OP_MOD_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x1f) +#define OP_MOD_SUB_VA_ADD BT_MESH_MODEL_OP_2(0x80, 0x20) +#define OP_MOD_SUB_VA_DEL BT_MESH_MODEL_OP_2(0x80, 0x21) +#define OP_MOD_SUB_VA_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x22) +#define OP_NET_TRANSMIT_GET BT_MESH_MODEL_OP_2(0x80, 0x23) +#define OP_NET_TRANSMIT_SET BT_MESH_MODEL_OP_2(0x80, 0x24) +#define OP_NET_TRANSMIT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x25) +#define OP_RELAY_GET BT_MESH_MODEL_OP_2(0x80, 0x26) +#define OP_RELAY_SET BT_MESH_MODEL_OP_2(0x80, 0x27) +#define OP_RELAY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x28) +#define OP_MOD_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x29) +#define OP_MOD_SUB_LIST BT_MESH_MODEL_OP_2(0x80, 0x2a) +#define OP_MOD_SUB_GET_VND BT_MESH_MODEL_OP_2(0x80, 0x2b) +#define OP_MOD_SUB_LIST_VND BT_MESH_MODEL_OP_2(0x80, 0x2c) +#define OP_LPN_TIMEOUT_GET BT_MESH_MODEL_OP_2(0x80, 0x2d) +#define OP_LPN_TIMEOUT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x2e) +#define OP_HEALTH_FAULT_CLEAR BT_MESH_MODEL_OP_2(0x80, 0x2f) +#define OP_HEALTH_FAULT_CLEAR_UNREL BT_MESH_MODEL_OP_2(0x80, 0x30) +#define OP_HEALTH_FAULT_GET BT_MESH_MODEL_OP_2(0x80, 0x31) +#define OP_HEALTH_FAULT_TEST BT_MESH_MODEL_OP_2(0x80, 0x32) +#define OP_HEALTH_FAULT_TEST_UNREL BT_MESH_MODEL_OP_2(0x80, 0x33) +#define OP_HEALTH_PERIOD_GET BT_MESH_MODEL_OP_2(0x80, 0x34) +#define OP_HEALTH_PERIOD_SET BT_MESH_MODEL_OP_2(0x80, 0x35) +#define OP_HEALTH_PERIOD_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x36) +#define OP_HEALTH_PERIOD_STATUS BT_MESH_MODEL_OP_2(0x80, 0x37) +#define OP_HEARTBEAT_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x38) +#define OP_HEARTBEAT_PUB_SET BT_MESH_MODEL_OP_2(0x80, 0x39) +#define OP_HEARTBEAT_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x3a) +#define OP_HEARTBEAT_SUB_SET BT_MESH_MODEL_OP_2(0x80, 0x3b) +#define OP_HEARTBEAT_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3c) +#define OP_MOD_APP_BIND BT_MESH_MODEL_OP_2(0x80, 0x3d) +#define OP_MOD_APP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3e) +#define OP_MOD_APP_UNBIND BT_MESH_MODEL_OP_2(0x80, 0x3f) +#define OP_NET_KEY_ADD BT_MESH_MODEL_OP_2(0x80, 0x40) +#define OP_NET_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x41) +#define OP_NET_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x42) +#define OP_NET_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x43) +#define OP_NET_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x44) +#define OP_NET_KEY_UPDATE BT_MESH_MODEL_OP_2(0x80, 0x45) +#define OP_NODE_IDENTITY_GET BT_MESH_MODEL_OP_2(0x80, 0x46) +#define OP_NODE_IDENTITY_SET BT_MESH_MODEL_OP_2(0x80, 0x47) +#define OP_NODE_IDENTITY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x48) +#define OP_NODE_RESET BT_MESH_MODEL_OP_2(0x80, 0x49) +#define OP_NODE_RESET_STATUS BT_MESH_MODEL_OP_2(0x80, 0x4a) +#define OP_SIG_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4b) +#define OP_SIG_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4c) +#define OP_VND_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4d) +#define OP_VND_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4e) + +#define STATUS_SUCCESS 0x00 +#define STATUS_INVALID_ADDRESS 0x01 +#define STATUS_INVALID_MODEL 0x02 +#define STATUS_INVALID_APPKEY 0x03 +#define STATUS_INVALID_NETKEY 0x04 +#define STATUS_INSUFF_RESOURCES 0x05 +#define STATUS_IDX_ALREADY_STORED 0x06 +#define STATUS_NVAL_PUB_PARAM 0x07 +#define STATUS_NOT_SUB_MOD 0x08 +#define STATUS_STORAGE_FAIL 0x09 +#define STATUS_FEAT_NOT_SUPP 0x0a +#define STATUS_CANNOT_UPDATE 0x0b +#define STATUS_CANNOT_REMOVE 0x0c +#define STATUS_CANNOT_BIND 0x0d +#define STATUS_TEMP_STATE_CHG_FAIL 0x0e +#define STATUS_CANNOT_SET 0x0f +#define STATUS_UNSPECIFIED 0x10 +#define STATUS_INVALID_BINDING 0x11 + +enum { + BT_MESH_VA_CHANGED, /* Label information changed */ +}; + +struct label { + u16_t ref; + u16_t addr; + u8_t uuid[16]; + atomic_t flags[1]; +}; + +void bt_mesh_cfg_reset(void); + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat); + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time); + +struct label *get_label(u16_t index); + +u8_t *bt_mesh_label_uuid_get(u16_t addr); + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void); +void bt_mesh_hb_pub_disable(void); +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void); + +u8_t bt_mesh_net_transmit_get(void); +u8_t bt_mesh_relay_get(void); +u8_t bt_mesh_friend_get(void); +u8_t bt_mesh_relay_retransmit_get(void); +u8_t bt_mesh_beacon_get(void); +u8_t bt_mesh_gatt_proxy_get(void); +u8_t bt_mesh_default_ttl_get(void); + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store); + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx); +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store); + +static inline void key_idx_pack(struct os_mbuf *buf, + u16_t idx1, u16_t idx2) +{ + net_buf_simple_add_le16(buf, idx1 | ((idx2 & 0x00f) << 12)); + net_buf_simple_add_u8(buf, idx2 >> 4); +} + +static inline void key_idx_unpack(struct os_mbuf *buf, + u16_t *idx1, u16_t *idx2) +{ + *idx1 = sys_get_le16(&buf->om_data[0]) & 0xfff; + *idx2 = sys_get_le16(&buf->om_data[1]) >> 4; + net_buf_simple_pull(buf, 3); +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.c new file mode 100644 index 0000000..c10a93e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.c @@ -0,0 +1,1654 @@ + /* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG + +#if MYNEWT_VAL(BLE_MESH_FRIEND) + +#include +#include +#include + +#include "../include/mesh/mesh.h" +#include "../include/mesh/slist.h" +#include "../include/mesh_priv.h" +#include "crypto.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "friend.h" + +/* We reserve one extra buffer for each friendship, since we need to be able + * to resend the last sent PDU, which sits separately outside of the queue. + */ +#define FRIEND_BUF_COUNT ((MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) + 1) * MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)) + +static os_membuf_t friend_buf_mem[OS_MEMPOOL_SIZE( + FRIEND_BUF_COUNT, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)]; + +struct os_mbuf_pool friend_os_mbuf_pool; +static struct os_mempool friend_buf_mempool; + +#define NET_BUF_FRAGS BIT(0) + +#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), struct friend_adv, adv) + +/* PDUs from Friend to the LPN should only be transmitted once with the + * smallest possible interval (20ms). + */ +#define FRIEND_XMIT BT_MESH_TRANSMIT(0, 20) + +struct friend_pdu_info { + u16_t src; + u16_t dst; + + u8_t seq[3]; + + u8_t ttl:7, + ctl:1; + + u32_t iv_index; +}; + +static struct friend_adv { + struct bt_mesh_adv adv; + u16_t app_idx; +} adv_pool[FRIEND_BUF_COUNT]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + adv_pool[id].app_idx = BT_MESH_KEY_UNUSED; + return &adv_pool[id].adv; +} + +static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr) +{ + if (frnd->lpn == BT_MESH_ADDR_UNASSIGNED) { + return false; + } + + return (addr >= frnd->lpn && addr < (frnd->lpn + frnd->num_elem)); +} + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established) +{ + int i; + + BT_DBG("net_idx 0x%04x lpn_addr 0x%04x", net_idx, lpn_addr); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (valid && !frnd->valid) { + continue; + } + + if (established && !frnd->established) { + continue; + } + + if (net_idx != BT_MESH_KEY_ANY && frnd->net_idx != net_idx) { + continue; + } + + if (is_lpn_unicast(frnd, lpn_addr)) { + return frnd; + } + } + + return NULL; +} + +static void purge_buffers(struct net_buf_slist_t *list) +{ + struct os_mbuf *buf; + + while (!net_buf_slist_is_empty(list)) { + buf = (void *)net_buf_slist_get(list); + BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS; + net_buf_unref(buf); + } +} + +/* Intentionally start a little bit late into the ReceiveWindow when + * it's large enough. This may improve reliability with some platforms, + * like the PTS, where the receiver might not have sufficiently compensated + * for internal latencies required to start scanning. + */ +static s32_t recv_delay(struct bt_mesh_friend *frnd) +{ +#if CONFIG_BT_MESH_FRIEND_RECV_WIN > 50 + return (s32_t)frnd->recv_delay + (CONFIG_BT_MESH_FRIEND_RECV_WIN / 5); +#else + return frnd->recv_delay; +#endif +} + +static void friend_clear(struct bt_mesh_friend *frnd) +{ + int i; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + k_delayed_work_cancel(&frnd->timer); + + friend_cred_del(frnd->net_idx, frnd->lpn); + + if (frnd->last) { + /* Cancel the sending if necessary */ + if (frnd->pending_buf) { + BT_MESH_ADV(frnd->last)->busy = 0; + } + + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + purge_buffers(&frnd->queue); + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + purge_buffers(&seg->queue); + seg->seg_count = 0U; + } + + frnd->valid = 0; + frnd->established = 0; + frnd->pending_buf = 0; + frnd->fsn = 0; + frnd->queue_size = 0; + frnd->pending_req = 0; + memset(frnd->sub_list, 0, sizeof(frnd->sub_list)); +} + +void bt_mesh_friend_clear_net_idx(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + friend_clear(frnd); + } + } +} + +void bt_mesh_friend_sec_update(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) { + frnd->sec_update = 1; + } + } +} + +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear_confirm cfm; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear"); + return -EINVAL; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter); + + frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, false, false); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", lpn_addr); + return 0; + } + + /* A Friend Clear message is considered valid if the result of the + * subtraction of the value of the LPNCounter field of the Friend + * Request message (the one that initiated the friendship) from the + * value of the LPNCounter field of the Friend Clear message, modulo + * 65536, is in the range 0 to 255 inclusive. + */ + if (lpn_counter - frnd->lpn_counter > 255) { + BT_WARN("LPN Counter out of range (old %u new %u)", + frnd->lpn_counter, lpn_counter); + return 0; + } + + tx.ctx->send_ttl = BT_MESH_TTL_MAX; + + cfm.lpn_addr = msg->lpn_addr; + cfm.lpn_counter = msg->lpn_counter; + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm, + sizeof(cfm), NULL, NULL, NULL); + + friend_clear(frnd); + + return 0; +} + +static void friend_sub_add(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == BT_MESH_ADDR_UNASSIGNED) { + frnd->sub_list[i] = addr; + return; + } + } + + BT_WARN("No space in friend subscription list"); +} + +static void friend_sub_rem(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + frnd->sub_list[i] = BT_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static struct os_mbuf *create_friend_pdu(struct bt_mesh_friend *frnd, + struct friend_pdu_info *info, + struct os_mbuf *sdu) +{ + struct os_mbuf *buf; + + buf = bt_mesh_adv_create_from_pool(&friend_os_mbuf_pool, adv_alloc, + BT_MESH_ADV_DATA, + FRIEND_XMIT, K_NO_WAIT); + if (!buf) { + return NULL; + } + + net_buf_add_u8(buf, (info->iv_index & 1) << 7); /* Will be reset in encryption */ + + if (info->ctl) { + net_buf_add_u8(buf, info->ttl | 0x80); + } else { + net_buf_add_u8(buf, info->ttl); + } + + net_buf_add_mem(buf, info->seq, sizeof(info->seq)); + + net_buf_add_be16(buf, info->src); + net_buf_add_be16(buf, info->dst); + + net_buf_add_mem(buf, sdu->om_data, sdu->om_len); + + return buf; +} + +struct unseg_app_sdu_meta { + struct bt_mesh_net_rx net; + const u8_t *key; + struct bt_mesh_subnet *subnet; + bool is_dev_key; + u8_t aid; + u8_t *ad; +}; + +static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd, + struct os_mbuf *buf, + struct unseg_app_sdu_meta *meta) +{ + u16_t app_idx = FRIEND_ADV(buf)->app_idx; + int err; + + meta->subnet = bt_mesh_subnet_get(frnd->net_idx); + meta->is_dev_key = (app_idx == BT_MESH_KEY_DEV); + meta->is_dev_key = BT_MESH_IS_DEV_KEY(app_idx); + bt_mesh_net_header_parse(buf, &meta->net); + err = bt_mesh_app_key_get(meta->subnet, app_idx, meta->net.ctx.recv_dst, + &meta->key, &meta->aid); + if (err) { + return err; + } + + if (BT_MESH_ADDR_IS_VIRTUAL(meta->net.ctx.recv_dst)) { + meta->ad = bt_mesh_label_uuid_get(meta->net.ctx.recv_dst); + if (!meta->ad) { + return -ENOENT; + } + } else { + meta->ad = NULL; + } + + return 0; +} + +static int unseg_app_sdu_decrypt(struct bt_mesh_friend *frnd, + struct os_mbuf *buf, + const struct unseg_app_sdu_meta *meta) +{ + struct net_buf_simple_state state; + int err; + + BT_DBG(""); + + net_buf_simple_save(buf, &state); + net_buf_simple_pull_mem(buf, 10); + buf->om_len -= 4; + + err = bt_mesh_app_decrypt_in_place(meta->key, meta->is_dev_key, + 0, buf, meta->ad, meta->net.ctx.addr, + meta->net.ctx.recv_dst, meta->net.seq, + BT_MESH_NET_IVI_TX); + + net_buf_simple_restore(buf, &state); + return err; +} + +static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd, + struct os_mbuf *buf, + const struct unseg_app_sdu_meta *meta) +{ + struct net_buf_simple_state state; + int err; + + BT_DBG(""); + + net_buf_simple_save(buf, &state); + net_buf_simple_pull_mem(buf, 10); + buf->om_len -= 4; + + err = bt_mesh_app_encrypt_in_place(meta->key, meta->is_dev_key, 0, buf, + meta->ad, meta->net.ctx.addr, + meta->net.ctx.recv_dst, bt_mesh.seq, + BT_MESH_NET_IVI_TX); + + net_buf_simple_restore(buf, &state); + return err; +} + +static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd, + struct os_mbuf *buf) +{ + struct unseg_app_sdu_meta meta; + int err; + + BT_DBG(""); + + if (FRIEND_ADV(buf)->app_idx == BT_MESH_KEY_UNUSED) { + return 0; + } + + err = unseg_app_sdu_unpack(frnd, buf, &meta); + if (err) { + return err; + } + + /* No need to reencrypt the message if the sequence number is + * unchanged. + */ + if (meta.net.seq == bt_mesh.seq) { + return 0; + } + + err = unseg_app_sdu_decrypt(frnd, buf, &meta); + if (err) { + BT_WARN("Decryption failed! %d", err); + return err; + } + + err = unseg_app_sdu_encrypt(frnd, buf, &meta); + if (err) { + BT_WARN("Re-encryption failed! %d", err); + } + + return err; +} + +static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct os_mbuf *buf, + bool master_cred) +{ + struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx); + const u8_t *enc, *priv; + u32_t iv_index; + u16_t src; + u8_t nid; + int err; + + if (master_cred) { + enc = sub->keys[sub->kr_flag].enc; + priv = sub->keys[sub->kr_flag].privacy; + nid = sub->keys[sub->kr_flag].nid; + } else { + if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) { + BT_ERR("friend_cred_get failed"); + return -ENOENT; + } + } + + src = sys_get_be16(&buf->om_data[5]); + + if (bt_mesh_elem_find(src)) { + u32_t seq; + + if (FRIEND_ADV(buf)->app_idx != BT_MESH_KEY_UNUSED) { + err = unseg_app_sdu_prepare(frnd, buf); + if (err) { + return err; + } + } + + seq = bt_mesh_next_seq(); + buf->om_data[2] = seq >> 16; + buf->om_data[3] = seq >> 8; + buf->om_data[4] = seq; + + iv_index = BT_MESH_NET_IVI_TX; + FRIEND_ADV(buf)->app_idx = BT_MESH_KEY_UNUSED; + } else { + u8_t ivi = (buf->om_data[0] >> 7); + iv_index = (bt_mesh.iv_index - ((bt_mesh.iv_index & 1) != ivi)); + } + + buf->om_data[0] = (nid | (iv_index & 1) << 7); + + if (bt_mesh_net_encrypt(enc, buf, iv_index, false)) { + BT_ERR("Encrypting failed"); + return -EINVAL; + } + + if (bt_mesh_net_obfuscate(buf->om_data, iv_index, priv)) { + BT_ERR("Obfuscating failed"); + return -EINVAL; + } + + return 0; +} + +static struct os_mbuf *encode_friend_ctl(struct bt_mesh_friend *frnd, + u8_t ctl_op, + struct os_mbuf *sdu) +{ + struct friend_pdu_info info; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + net_buf_simple_push_u8(sdu, TRANS_CTL_HDR(ctl_op, 0)); + + info.src = bt_mesh_primary_addr(); + info.dst = frnd->lpn; + + info.ctl = 1; + info.ttl = 0; + + memset(info.seq, 0, sizeof(info.seq)); + + info.iv_index = BT_MESH_NET_IVI_TX; + + return create_friend_pdu(frnd, &info, sdu); +} + +static struct os_mbuf *encode_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct bt_mesh_ctl_friend_update *upd; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd)); + struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx); + struct os_mbuf *buf; + + __ASSERT_NO_MSG(sub != NULL); + + BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md); + + net_buf_simple_init(sdu, 1); + + upd = net_buf_simple_add(sdu, sizeof(*upd)); + upd->flags = bt_mesh_net_flags(sub); + upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index); + upd->md = md; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_UPDATE, sdu); + + os_mbuf_free_chain(sdu); + return buf; +} + +static void enqueue_sub_cfm(struct bt_mesh_friend *frnd, u8_t xact) +{ + struct bt_mesh_ctl_friend_sub_confirm *cfm; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*cfm)); + struct os_mbuf *buf; + + BT_DBG("lpn 0x%04x xact 0x%02x", frnd->lpn, xact); + + net_buf_simple_init(sdu, 1); + + cfm = net_buf_simple_add(sdu, sizeof(*cfm)); + cfm->xact = xact; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_SUB_CFM, sdu); + if (!buf) { + BT_ERR("Unable to encode Subscription List Confirmation"); + goto done; + } + + if (encrypt_friend_pdu(frnd, buf, false)) { + return; + } + + if (frnd->last) { + BT_DBG("Discarding last PDU"); + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1; + +done: + os_mbuf_free_chain(sdu); +} + +static void friend_recv_delay(struct bt_mesh_friend *frnd) +{ + frnd->pending_req = 1; + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + BT_DBG("Waiting RecvDelay of %d ms", (int) recv_delay(frnd)); +} + +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Add"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->om_len >= 2) { + friend_sub_add(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("Too short Friend Subscription Remove"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent!"); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->om_len >= 2) { + friend_sub_rem(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +static void enqueue_buf(struct bt_mesh_friend *frnd, struct os_mbuf *buf) +{ + net_buf_slist_put(&frnd->queue, buf); + frnd->queue_size++; +} + +static void enqueue_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct os_mbuf *buf; + + buf = encode_update(frnd, md); + if (!buf) { + BT_ERR("Unable to encode Friend Update"); + return; + } + + frnd->sec_update = 0; + enqueue_buf(frnd, buf); +} + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_poll *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Poll"); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (!frnd) { + BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr); + return 0; + } + + if (msg->fsn & ~1) { + BT_WARN("Prohibited (non-zero) padding bits"); + return -EINVAL; + } + + if (frnd->pending_buf) { + BT_WARN("Previous buffer not yet sent"); + return 0; + } + + BT_DBG("msg->fsn %u frnd->fsn %u", (msg->fsn & 1), frnd->fsn); + + friend_recv_delay(frnd); + + if (!frnd->established) { + BT_DBG("Friendship established with 0x%04x", frnd->lpn); + frnd->established = 1; + } + + if (msg->fsn == frnd->fsn && frnd->last) { + BT_DBG("Re-sending last PDU"); + frnd->send_last = 1; + } else { + if (frnd->last) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + frnd->fsn = msg->fsn; + + if (net_buf_slist_is_empty(&frnd->queue)) { + enqueue_update(frnd, 0); + BT_DBG("Enqueued Friend Update to empty queue"); + } + } + + return 0; +} + +static struct bt_mesh_friend *find_clear(u16_t prev_friend) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->clear.frnd == prev_friend) { + return frnd; + } + } + + return NULL; +} + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + k_delayed_work_submit(&frnd->clear.timer, + K_SECONDS(frnd->clear.repeat_sec)); + frnd->clear.repeat_sec *= 2; +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static void send_friend_clear(struct bt_mesh_friend *frnd) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = frnd->net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = frnd->clear.frnd, + .send_ttl = BT_MESH_TTL_MAX, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(frnd->lpn), + .lpn_counter = sys_cpu_to_be16(frnd->lpn_counter), + }; + + BT_DBG(""); + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, frnd); +} + +static void clear_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work); + u32_t duration; + + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + duration = k_uptime_get_32() - frnd->clear.start; + if (duration > 2 * frnd->poll_to) { + BT_DBG("Clear Procedure timer expired"); + frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED; + return; + } + + send_friend_clear(frnd); +} + +static void clear_procedure_start(struct bt_mesh_friend *frnd) +{ + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + frnd->clear.start = k_uptime_get_32(); + frnd->clear.repeat_sec = 1U; + + send_friend_clear(frnd); +} + +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + + BT_DBG(""); + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear Confirm"); + return -EINVAL; + } + + frnd = find_clear(rx->ctx.addr); + if (!frnd) { + BT_WARN("No pending clear procedure for 0x%02x", rx->ctx.addr); + return 0; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + if (lpn_addr != frnd->lpn) { + BT_WARN("LPN address mismatch (0x%04x != 0x%04x)", + lpn_addr, frnd->lpn); + return 0; + } + + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + if (lpn_counter != frnd->lpn_counter) { + BT_WARN("LPN counter mismatch (0x%04x != 0x%04x)", + lpn_counter, frnd->lpn_counter); + return 0; + } + + k_delayed_work_cancel(&frnd->clear.timer); + frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED; + + return 0; +} + +static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi) +{ + struct bt_mesh_ctl_friend_offer *off; + struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*off)); + struct os_mbuf *buf; + + BT_DBG(""); + + net_buf_simple_init(sdu, 1); + + off = net_buf_simple_add(sdu, sizeof(*off)); + + off->recv_win = CONFIG_BT_MESH_FRIEND_RECV_WIN, + off->queue_size = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + off->sub_list_size = ARRAY_SIZE(frnd->sub_list), + off->rssi = rssi, + off->frnd_counter = sys_cpu_to_be16(frnd->counter); + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_OFFER, sdu); + if (!buf) { + BT_ERR("Unable to encode Friend Offer"); + goto done; + } + + if (encrypt_friend_pdu(frnd, buf, true)) { + return; + } + + frnd->counter++; + + if (frnd->last) { + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1; + +done: + os_mbuf_free_chain(sdu); +} + +#define RECV_WIN CONFIG_BT_MESH_FRIEND_RECV_WIN +#define RSSI_FACT(crit) (((crit) >> 5) & (u8_t)BIT_MASK(2)) +#define RECV_WIN_FACT(crit) (((crit) >> 3) & (u8_t)BIT_MASK(2)) +#define MIN_QUEUE_SIZE_LOG(crit) ((crit) & (u8_t)BIT_MASK(3)) +#define MIN_QUEUE_SIZE(crit) ((u32_t)BIT(MIN_QUEUE_SIZE_LOG(crit))) + +static s32_t offer_delay(struct bt_mesh_friend *frnd, s8_t rssi, u8_t crit) +{ + /* Scaling factors. The actual values are 1, 1.5, 2 & 2.5, but we + * want to avoid floating-point arithmetic. + */ + static const u8_t fact[] = { 10, 15, 20, 25 }; + s32_t delay; + + BT_DBG("ReceiveWindowFactor %u ReceiveWindow %u RSSIFactor %u RSSI %d", + fact[RECV_WIN_FACT(crit)], RECV_WIN, + fact[RSSI_FACT(crit)], rssi); + + /* Delay = ReceiveWindowFactor * ReceiveWindow - RSSIFactor * RSSI */ + delay = (s32_t)fact[RECV_WIN_FACT(crit)] * RECV_WIN; + delay -= (s32_t)fact[RSSI_FACT(crit)] * rssi; + delay /= 10; + + BT_DBG("Local Delay calculated as %d ms", (int) delay); + + if (delay < 100) { + return K_MSEC(100); + } + + return K_MSEC(delay); +} + +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_req *msg = (void *)buf->om_data; + struct bt_mesh_friend *frnd = NULL; + u32_t poll_to; + int i; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Request"); + return -EINVAL; + } + + if (msg->recv_delay <= 0x09) { + BT_WARN("Prohibited ReceiveDelay (0x%02x)", msg->recv_delay); + return -EINVAL; + } + + poll_to = (((u32_t)msg->poll_to[0] << 16) | + ((u32_t)msg->poll_to[1] << 8) | + ((u32_t)msg->poll_to[2])); + + if (poll_to <= 0x000009 || poll_to >= 0x34bc00) { + BT_WARN("Prohibited PollTimeout (0x%06x)", (unsigned) poll_to); + return -EINVAL; + } + + if (msg->num_elem == 0x00) { + BT_WARN("Prohibited NumElements value (0x00)"); + return -EINVAL; + } + + if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr + msg->num_elem - 1)) { + BT_WARN("LPN elements stretch outside of unicast range"); + return -EINVAL; + } + + if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) { + BT_WARN("Prohibited Minimum Queue Size in Friend Request"); + return -EINVAL; + } + + if (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) { + BT_WARN("We have a too small Friend Queue size (%u < %u)", + CONFIG_BT_MESH_FRIEND_QUEUE_SIZE, + (unsigned) MIN_QUEUE_SIZE(msg->criteria)); + return 0; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (frnd) { + BT_WARN("Existing LPN re-requesting Friendship"); + friend_clear(frnd); + goto init_friend; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (!bt_mesh.frnd[i].valid) { + frnd = &bt_mesh.frnd[i]; + frnd->valid = 1; + break; + } + } + + if (!frnd) { + BT_WARN("No free Friend contexts for new LPN"); + return -ENOMEM; + } + +init_friend: + frnd->lpn = rx->ctx.addr; + frnd->num_elem = msg->num_elem; + frnd->net_idx = rx->sub->net_idx; + frnd->recv_delay = msg->recv_delay; + frnd->poll_to = poll_to * 100; + frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr); + + BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums", + frnd->lpn, rx->ctx.recv_rssi, frnd->recv_delay, + (unsigned) frnd->poll_to); + + if (BT_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) && + !bt_mesh_elem_find(frnd->clear.frnd)) { + clear_procedure_start(frnd); + } + + k_delayed_work_submit(&frnd->timer, + offer_delay(frnd, rx->ctx.recv_rssi, + msg->criteria)); + + friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter, + frnd->counter); + + enqueue_offer(frnd, rx->ctx.recv_rssi); + + return 0; +} + +static bool is_seg(struct bt_mesh_friend_seg *seg, u16_t src, u16_t seq_zero) +{ + struct os_mbuf *buf = (void *)net_buf_slist_peek_head(&seg->queue); + struct net_buf_simple_state state; + u16_t buf_seq_zero; + u16_t buf_src; + + if (!buf) { + return false; + } + + net_buf_simple_save(buf, &state); + net_buf_skip(buf, 5); /* skip IVI, NID, CTL, TTL, SEQ */ + buf_src = net_buf_pull_be16(buf); + net_buf_skip(buf, 3); /* skip DST, OP/AID */ + buf_seq_zero = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK); + net_buf_simple_restore(buf, &state); + + return ((src == buf_src) && (seq_zero == buf_seq_zero)); +} + +static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd, + u16_t src, u16_t seq_zero, + u8_t seg_count) +{ + struct bt_mesh_friend_seg *unassigned = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + if (is_seg(seg, src, seq_zero)) { + return seg; + } + + if (!unassigned && !net_buf_slist_peek_head(&seg->queue)) { + unassigned = seg; + } + } + + if (unassigned) { + unassigned->seg_count = seg_count; + } + + return unassigned; +} + +static void enqueue_friend_pdu(struct bt_mesh_friend *frnd, + enum bt_mesh_friend_pdu_type type, + u16_t src, u8_t seg_count, + struct os_mbuf *buf) +{ + struct bt_mesh_friend_seg *seg; + + BT_DBG("type %u", type); + + if (type == BT_MESH_FRIEND_PDU_SINGLE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + enqueue_buf(frnd, buf); + return; + } + + u16_t seq_zero = (((buf->om_data[10] << 8 | buf->om_data[11]) >> 2) & TRANS_SEQ_ZERO_MASK); + + seg = get_seg(frnd, src, seq_zero, seg_count); + if (!seg) { + BT_ERR("No free friend segment RX contexts for 0x%04x", src); + net_buf_unref(buf); + return; + } + + net_buf_slist_put(&seg->queue, buf); + + if (type == BT_MESH_FRIEND_PDU_COMPLETE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + net_buf_slist_merge_slist(&frnd->queue, &seg->queue); + + frnd->queue_size += seg->seg_count; + seg->seg_count = 0U; + } else { + /* Mark the buffer as having more to come after it */ + BT_MESH_ADV(buf)->flags |= NET_BUF_FRAGS; + } +} + +static void buf_send_start(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + frnd->pending_buf = 0; + + /* Friend Offer doesn't follow the re-sending semantics */ + if (!frnd->established) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } +} + +static void buf_send_end(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + if (frnd->pending_req) { + BT_WARN("Another request before previous completed sending"); + return; + } + + if (frnd->established) { + k_delayed_work_submit(&frnd->timer, frnd->poll_to); + BT_DBG("Waiting %u ms for next poll", + (unsigned) frnd->poll_to); + } else { + /* Friend offer timeout is 1 second */ + k_delayed_work_submit(&frnd->timer, K_SECONDS(1)); + BT_DBG("Waiting for first poll"); + } +} + +static void friend_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work); + static const struct bt_mesh_send_cb buf_sent_cb = { + .start = buf_send_start, + .end = buf_send_end, + }; + + __ASSERT_NO_MSG(frnd->pending_buf == 0); + + BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn, + frnd->send_last, frnd->last); + + if (frnd->send_last && frnd->last) { + BT_DBG("Sending frnd->last %p", frnd->last); + frnd->send_last = 0; + goto send_last; + } + + if (frnd->established && !frnd->pending_req) { + BT_WARN("Friendship lost with 0x%04x", frnd->lpn); + friend_clear(frnd); + return; + } + + frnd->last = (void *)net_buf_slist_get(&frnd->queue); + if (!frnd->last) { + BT_WARN("Friendship not established with 0x%04x", + frnd->lpn); + friend_clear(frnd); + return; + } + + if (encrypt_friend_pdu(frnd, frnd->last, false)) { + return; + } + + /* Clear the flag we use for segment tracking */ + BT_MESH_ADV(frnd->last)->flags &= ~NET_BUF_FRAGS; + + BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x", + frnd->last, frnd->lpn); + frnd->queue_size--; + +send_last: + frnd->pending_req = 0; + frnd->pending_buf = 1; + bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd); +} + +int bt_mesh_friend_init(void) +{ + int rc; + int i; + + rc = os_mempool_init(&friend_buf_mempool, FRIEND_BUF_COUNT, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + friend_buf_mem, "friend_buf_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&friend_os_mbuf_pool, &friend_buf_mempool, + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, + FRIEND_BUF_COUNT); + assert(rc == 0); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + frnd->net_idx = BT_MESH_KEY_UNUSED; + + net_buf_slist_init(&frnd->queue); + + k_delayed_work_init(&frnd->timer, friend_timeout); + k_delayed_work_add_arg(&frnd->timer, frnd); + k_delayed_work_init(&frnd->clear.timer, clear_timeout); + k_delayed_work_add_arg(&frnd->clear.timer, frnd); + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + net_buf_slist_init(&frnd->seg[j].queue); + } + } + + return 0; +} + +static bool is_segack(struct os_mbuf *buf, u64_t *seqauth, u16_t src) +{ + struct net_buf_simple_state state; + bool found = false; + + if (buf->om_len != 16) { + return false; + } + + net_buf_simple_save(buf, &state); + + net_buf_skip(buf, 1); /* skip IVI, NID */ + + if (!(net_buf_pull_u8(buf) >> 7)) { + goto end; + } + + net_buf_pull(buf, 3); /* skip SEQNUM */ + + if (src != net_buf_pull_be16(buf)) { + goto end; + } + + net_buf_skip(buf, 2); /* skip dst */ + + if (TRANS_CTL_OP((u8_t *) net_buf_pull_mem(buf, 1)) != TRANS_CTL_OP_ACK) { + goto end; + } + + found = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK) == + (*seqauth & TRANS_SEQ_ZERO_MASK); +end: + net_buf_simple_restore(buf, &state); + return found; +} + +static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth, + u16_t src) +{ + struct os_mbuf *cur, *prev = NULL; + + BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src); + + for (cur = net_buf_slist_peek_head(&frnd->queue); + cur != NULL; prev = cur, cur = net_buf_slist_peek_next(cur)) { + struct os_mbuf *buf = (void *)cur; + + if (is_segack(buf, seq_auth, src)) { + BT_DBG("Removing old ack from Friend Queue"); + + net_buf_slist_remove(&frnd->queue, prev, cur); + frnd->queue_size--; + + net_buf_unref(buf); + break; + } + } +} + +static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf) +{ + struct friend_pdu_info info; + struct os_mbuf *buf; + + /* Because of network loopback, tx packets will also be passed into + * this rx function. These packets have already been added to the + * queue, and should be ignored. + */ + if (bt_mesh_elem_find(rx->ctx.addr)) { + return; + } + + BT_DBG("LPN 0x%04x queue_size %u", frnd->lpn, + (unsigned) frnd->queue_size); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, rx->ctx.addr); + } + + info.src = rx->ctx.addr; + info.dst = rx->ctx.recv_dst; + + if (rx->net_if == BT_MESH_NET_IF_LOCAL) { + info.ttl = rx->ctx.recv_ttl; + } else { + info.ttl = rx->ctx.recv_ttl - 1; + } + + info.ctl = rx->ctl; + + info.seq[0] = (rx->seq >> 16); + info.seq[1] = (rx->seq >> 8); + info.seq[2] = rx->seq; + + info.iv_index = BT_MESH_NET_IVI_RX(rx); + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + enqueue_friend_pdu(frnd, type, info.src, seg_count, buf); + + BT_DBG("Queued message for LPN 0x%04x, queue_size %u", + frnd->lpn, (unsigned) frnd->queue_size); +} + +static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf) +{ + struct friend_pdu_info info; + struct os_mbuf *buf; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, tx->src); + } + + info.src = tx->src; + info.dst = tx->ctx->addr; + + info.ttl = tx->ctx->send_ttl; + info.ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); + + info.seq[0] = (bt_mesh.seq >> 16); + info.seq[1] = (bt_mesh.seq >> 8); + info.seq[2] = bt_mesh.seq; + + info.iv_index = BT_MESH_NET_IVI_TX; + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("Failed to encode Friend buffer"); + return; + } + + if (type == BT_MESH_FRIEND_PDU_SINGLE && !info.ctl) { + /* Unsegmented application packets may be reencrypted later, + * as they depend on the the sequence number being the same + * when encrypting in transport and network. + */ + FRIEND_ADV(buf)->app_idx = tx->ctx->app_idx; + } + + enqueue_friend_pdu(frnd, type, info.src, seg_count, buf); + + BT_DBG("Queued message for LPN 0x%04x", frnd->lpn); +} + +static bool friend_lpn_matches(struct bt_mesh_friend *frnd, u16_t net_idx, + u16_t addr) +{ + int i; + + if (!frnd->established) { + return false; + } + + if (net_idx != frnd->net_idx) { + return false; + } + + if (BT_MESH_ADDR_IS_UNICAST(addr)) { + return is_lpn_unicast(frnd, addr); + } + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + return true; + } + } + + return false; +} + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, net_idx, addr)) { + BT_DBG("LPN 0x%04x matched address 0x%04x", + frnd->lpn, addr); + return true; + } + } + + BT_DBG("No matching LPN for address 0x%04x", addr); + + return false; +} + +static bool friend_queue_has_space(struct bt_mesh_friend *frnd, u16_t addr, + u64_t *seq_auth, u8_t seg_count) +{ + u32_t total = 0; + int i; + + if (seg_count > CONFIG_BT_MESH_FRIEND_QUEUE_SIZE) { + return false; + } + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + if (seq_auth && is_seg(seg, addr, *seq_auth & TRANS_SEQ_ZERO_MASK)) { + /* If there's a segment queue for this message then the + * space verification has already happened. + */ + return true; + } + + total += seg->seg_count; + } + + /* If currently pending segments combined with this segmented message + * are more than the Friend Queue Size, then there's no space. This + * is because we don't have a mechanism of aborting already pending + * segmented messages to free up buffers. + */ + return (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - total) > seg_count; +} + +bool bt_mesh_friend_queue_has_space(u16_t net_idx, u16_t src, u16_t dst, + u64_t *seq_auth, u8_t seg_count) +{ + bool someone_has_space = false, friend_match = false; + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (!friend_lpn_matches(frnd, net_idx, dst)) { + continue; + } + + friend_match = true; + + if (friend_queue_has_space(frnd, src, seq_auth, seg_count)) { + someone_has_space = true; + } + } + + /* If there were no matched LPNs treat this as success, so the + * transport layer can continue its work. + */ + if (!friend_match) { + return true; + } + + /* From the transport layers perspective it's good enough that at + * least one Friend Queue has space. If there were multiple Friend + * matches then the destination must be a group address, in which + * case e.g. segment acks are not sent. + */ + return someone_has_space; +} + +static bool friend_queue_prepare_space(struct bt_mesh_friend *frnd, u16_t addr, + u64_t *seq_auth, u8_t seg_count) +{ + bool pending_segments; + u8_t avail_space; + + if (!friend_queue_has_space(frnd, addr, seq_auth, seg_count)) { + return false; + } + + avail_space = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - frnd->queue_size; + pending_segments = false; + + while (pending_segments || avail_space < seg_count) { + struct os_mbuf *buf = (void *)net_buf_slist_get(&frnd->queue); + + if (!buf) { + BT_ERR("Unable to free up enough buffers"); + return false; + } + + frnd->queue_size--; + avail_space++; + + pending_segments = (BT_MESH_ADV(buf)->flags & NET_BUF_FRAGS); + BT_DBG("PENDING SEGMENTS %d", pending_segments); + + /* Make sure old slist entry state doesn't remain */ + BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS; + + net_buf_unref(buf); + } + + return true; +} + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf) +{ + int i; + + if (!rx->friend_match || + (rx->ctx.recv_ttl <= 1 && rx->net_if != BT_MESH_NET_IF_LOCAL) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return; + } + + BT_DBG("recv_ttl %u net_idx 0x%04x src 0x%04x dst 0x%04x", + rx->ctx.recv_ttl, rx->sub->net_idx, rx->ctx.addr, + rx->ctx.recv_dst); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (!friend_lpn_matches(frnd, rx->sub->net_idx, + rx->ctx.recv_dst)) { + continue; + } + + if (!friend_queue_prepare_space(frnd, rx->ctx.addr, seq_auth, + seg_count)) { + continue; + } + + friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, seg_count, + sbuf); + } +} + +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf) +{ + bool matched = false; + int i; + + if (!bt_mesh_friend_match(tx->sub->net_idx, tx->ctx->addr) || + bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) { + return matched; + } + + BT_DBG("net_idx 0x%04x dst 0x%04x src 0x%04x", tx->sub->net_idx, + tx->ctx->addr, tx->src); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (!friend_lpn_matches(frnd, tx->sub->net_idx, + tx->ctx->addr)) { + continue; + } + + if (!friend_queue_prepare_space(frnd, tx->src, seq_auth, + seg_count)) { + continue; + } + + friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, seg_count, + sbuf); + matched = true; + } + + return matched; +} + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + if (!friend_lpn_matches(frnd, sub->net_idx, dst)) { + continue; + } + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[j]; + + if (!is_seg(seg, src, *seq_auth & TRANS_SEQ_ZERO_MASK)) { + continue; + } + + BT_WARN("Clearing incomplete segments for 0x%04x", src); + + purge_buffers(&seg->queue); + seg->seg_count = 0U; + break; + } + } +} + +#endif /* MYNEWT_VAL(BLE_MESH_FRIEND) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.h new file mode 100644 index 0000000..361c1f0 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/friend.h @@ -0,0 +1,56 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FRIEND_H__ +#define __FRIEND_H__ + +#include "../include/mesh/mesh.h" + +enum bt_mesh_friend_pdu_type { + BT_MESH_FRIEND_PDU_SINGLE, + BT_MESH_FRIEND_PDU_PARTIAL, + BT_MESH_FRIEND_PDU_COMPLETE, +}; + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr); + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established); + +bool bt_mesh_friend_queue_has_space(u16_t net_idx, u16_t src, u16_t dst, + u64_t *seq_auth, u8_t seg_count); + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf); +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, u8_t seg_count, + struct os_mbuf *sbuf); + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth); + +void bt_mesh_friend_sec_update(u16_t net_idx); + +void bt_mesh_friend_clear_net_idx(u16_t net_idx); + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf); +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); + +int bt_mesh_friend_init(void); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/glue.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/glue.c new file mode 100644 index 0000000..14e515b --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/glue.c @@ -0,0 +1,895 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include "../include/mesh/glue.h" +#include "adv.h" +#ifndef MYNEWT +#include "nimble/porting/nimble/include/nimble/nimble_port.h" +#endif + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) +#include "base64/base64.h" +#endif + +extern u8_t g_mesh_addr_type; + +#if MYNEWT_VAL(BLE_EXT_ADV) +/* Store configuration for different bearers */ +#define BT_MESH_ADV_IDX (0) +#define BT_MESH_GATT_IDX (1) +static struct ble_gap_adv_params ble_adv_cur_conf[2]; +#endif + +const char * +bt_hex(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][137]; + static u8_t curbuf; + const u8_t *b = buf; + char *str; + int i; + + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + + len = min(len, (sizeof(hexbufs[0]) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +void +net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *om) +{ + struct ble_npl_event *ev; + + assert(OS_MBUF_IS_PKTHDR(om)); + ev = &BT_MESH_ADV(om)->ev; + assert(ev); + assert(ble_npl_event_get_arg(ev)); + + ble_npl_eventq_put(fifo, ev); +} + +void * +net_buf_ref(struct os_mbuf *om) +{ + struct bt_mesh_adv *adv; + + /* For bufs with header we count refs*/ + if (OS_MBUF_USRHDR_LEN(om) == 0) { + return om; + } + + adv = BT_MESH_ADV(om); + adv->ref_cnt++; + + return om; +} + +void +net_buf_unref(struct os_mbuf *om) +{ + struct bt_mesh_adv *adv; + + /* For bufs with header we count refs*/ + if (OS_MBUF_USRHDR_LEN(om) == 0) { + goto free; + } + + adv = BT_MESH_ADV(om); + if (--adv->ref_cnt > 0) { + return; + } + +free: + os_mbuf_free_chain(om); +} + +#if MYNEWT_VAL(BLE_CRYPTO_STACK_MBEDTLS) +int +bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) +{ + mbedtls_aes_context s = {0}; + mbedtls_aes_init(&s); + + if (mbedtls_aes_setkey_enc(&s, key, 128) != 0) { + mbedtls_aes_free(&s); + return BLE_HS_EUNKNOWN; + } + + if (mbedtls_aes_crypt_ecb(&s, MBEDTLS_AES_ENCRYPT, plaintext, enc_data) != 0) { + mbedtls_aes_free(&s); + return BLE_HS_EUNKNOWN; + } + + mbedtls_aes_free(&s); + return 0; +} + +#else +int +bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) +{ + struct tc_aes_key_sched_struct s = {0}; + + if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) { + return BLE_HS_EUNKNOWN; + } + + if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) { + return BLE_HS_EUNKNOWN; + } + + return 0; +} +#endif + +uint16_t +net_buf_simple_pull_le16(struct os_mbuf *om) +{ + uint16_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_le16(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint16_t +net_buf_simple_pull_be16(struct os_mbuf *om) +{ + uint16_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_be16(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint32_t +net_buf_simple_pull_be32(struct os_mbuf *om) +{ + uint32_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_be32(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint32_t +net_buf_simple_pull_le32(struct os_mbuf *om) +{ + uint32_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = get_le32(om->om_data); + os_mbuf_adj(om, sizeof(val)); + + return val; +} + +uint8_t +net_buf_simple_pull_u8(struct os_mbuf *om) +{ + uint8_t val; + struct os_mbuf *old = om; + + om = os_mbuf_pullup(om, sizeof(val)); + assert(om == old); + val = om->om_data[0]; + os_mbuf_adj(om, 1); + + return val; +} + +void +net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val) +{ + val = htole16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val) +{ + val = htobe16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val) +{ + val = htobe32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val) +{ + val = htole32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val) +{ + os_mbuf_append(om, &val, 1); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 2); + om->om_data -= 2; + put_le16(om->om_data, val); + om->om_len += 2; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 2; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 2); + om->om_data -= 2; + put_be16(om->om_data, val); + om->om_len += 2; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 2; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= 1); + om->om_data -= 1; + om->om_data[0] = val; + om->om_len += 1; + + if (om->om_pkthdr_len) { + OS_MBUF_PKTHDR(om)->omp_len += 1; + } + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_add_zeros(struct os_mbuf *om, uint8_t len) +{ + uint8_t z[len]; + int rc; + + memset(z, 0, len); + + rc = os_mbuf_append(om, z, len); + if(rc) { + assert(0); + } + ASSERT_NOT_CHAIN(om); +} + +void * +net_buf_simple_pull(struct os_mbuf *om, uint8_t len) +{ + os_mbuf_adj(om, len); + return om->om_data; +} + +void * +net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len) +{ + void *data = om->om_data; + + net_buf_simple_pull(om, len); + return data; +} + +void* +net_buf_simple_add(struct os_mbuf *om, uint8_t len) +{ + void * tmp; + + tmp = os_mbuf_extend(om, len); + ASSERT_NOT_CHAIN(om); + + return tmp; +} + +bool +k_fifo_is_empty(struct ble_npl_eventq *q) +{ + return ble_npl_eventq_is_empty(q); +} + +void * net_buf_get(struct ble_npl_eventq *fifo, s32_t t) +{ + struct ble_npl_event *ev = ble_npl_eventq_get(fifo, 0); + + if (ev) { + return ble_npl_event_get_arg(ev); + } + + return NULL; +} + +uint8_t * +net_buf_simple_push(struct os_mbuf *om, uint8_t len) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= len); + om->om_data -= len; + om->om_len += len; + + return om->om_data; +} + +void +net_buf_reserve(struct os_mbuf *om, size_t reserve) +{ + /* We need reserve to be done on fresh buf */ + assert(om->om_len == 0); + om->om_data += reserve; +} + +void +k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler) +{ +#ifndef MYNEWT + ble_npl_callout_init(work, nimble_port_get_dflt_eventq(), handler, NULL); +#else + ble_npl_callout_init(work, ble_npl_eventq_dflt_get(), handler, NULL); +#endif +} + +void +k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f) +{ +#ifndef MYNEWT + ble_npl_callout_init(&w->work, nimble_port_get_dflt_eventq(), f, NULL); +#else + ble_npl_callout_init(&w->work, ble_npl_eventq_dflt_get(), f, NULL); +#endif +} + +void +k_delayed_work_cancel(struct k_delayed_work *w) +{ + ble_npl_callout_stop(&w->work); +} + +void +k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms) +{ + uint32_t ticks; + + if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) { + assert(0); + } + ble_npl_callout_reset(&w->work, ticks); +} + +void +k_work_submit(struct ble_npl_callout *w) +{ + ble_npl_callout_reset(w, 0); +} + +void +k_work_add_arg(struct ble_npl_callout *w, void *arg) +{ + ble_npl_callout_set_arg(w, arg); +} + +void +k_delayed_work_add_arg(struct k_delayed_work *w, void *arg) +{ + k_work_add_arg(&w->work, arg); +} + +uint32_t +k_delayed_work_remaining_get (struct k_delayed_work *w) +{ + int sr; + ble_npl_time_t t; + + OS_ENTER_CRITICAL(sr); + + t = ble_npl_callout_remaining_ticks(&w->work, ble_npl_time_get()); + + OS_EXIT_CRITICAL(sr); + + return ble_npl_time_ticks_to_ms32(t); +} + +int64_t k_uptime_get(void) +{ + /* We should return ms */ + return ble_npl_time_ticks_to_ms32(ble_npl_time_get()); +} + +u32_t k_uptime_get_32(void) +{ + return k_uptime_get(); +} + +void k_sleep(int32_t duration) +{ + uint32_t ticks; + + ticks = ble_npl_time_ms_to_ticks32(duration); + + ble_npl_time_delay(ticks); +} + +static uint8_t pub[64]; +static uint8_t priv[32]; +static bool has_pub = false; + +int +bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb) +{ + uint8_t dh[32]; + + if (ble_sm_alg_gen_dhkey((uint8_t *)&remote_pk[0], (uint8_t *)&remote_pk[32], + priv, dh)) { + return -1; + } + + cb(dh); + return 0; +} + +int +bt_rand(void *buf, size_t len) +{ + int rc; + rc = ble_hs_hci_util_rand(buf, len); + if (rc != 0) { + return -1; + } + + return 0; +} + +int +bt_pub_key_gen(struct bt_pub_key_cb *new_cb) +{ + + if (ble_sm_alg_gen_key_pair(pub, priv)) { + assert(0); + return -1; + } + + new_cb->func(pub); + has_pub = true; + + return 0; +} + +uint8_t * +bt_pub_key_get(void) +{ + if (!has_pub) { + return NULL; + } + + return pub; +} + +static int +set_ad(const struct bt_data *ad, size_t ad_len, u8_t *buf, u8_t *buf_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + buf[(*buf_len)++] = ad[i].data_len + 1; + buf[(*buf_len)++] = ad[i].type; + + memcpy(&buf[*buf_len], ad[i].data, + ad[i].data_len); + *buf_len += ad[i].data_len; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +ble_adv_copy_to_ext_param(struct ble_gap_ext_adv_params *ext_param, + const struct ble_gap_adv_params *param) +{ + memset(ext_param, 0, sizeof(*ext_param)); + + ext_param->legacy_pdu = 1; + + if (param->conn_mode != BLE_GAP_CONN_MODE_NON) { + ext_param->connectable = 1; + ext_param->scannable = 1; + } + + ext_param->itvl_max = param->itvl_max; + ext_param->itvl_min = param->itvl_min; + ext_param->channel_map = param->channel_map; + ext_param->high_duty_directed = param->high_duty_cycle; + ext_param->own_addr_type = g_mesh_addr_type; +} + +static int +ble_adv_conf_adv_instance(const struct ble_gap_adv_params *param, int *instance) +{ + struct ble_gap_ext_adv_params ext_params; + struct ble_gap_adv_params *cur_conf; + int err = 0; + + if (param->conn_mode == BLE_GAP_CONN_MODE_NON) { + *instance = BT_MESH_ADV_INST; + cur_conf = &ble_adv_cur_conf[BT_MESH_ADV_IDX]; + } else { +#if MYNEWT_VAL(BLE_MESH_PROXY) + *instance = BT_MESH_ADV_GATT_INST; + cur_conf = &ble_adv_cur_conf[BT_MESH_GATT_IDX]; +#else + assert(0); +#endif + } + + /* Checking interval max as it has to be in place if instance was configured + * before. + */ + if (cur_conf->itvl_max == 0) { + goto configure; + } + + if (memcmp(param, cur_conf, sizeof(*cur_conf)) == 0) { + /* Same parameters - skip reconfiguring */ + goto done; + } + + ble_gap_ext_adv_stop(*instance); + err = ble_gap_ext_adv_remove(*instance); + if (err) { + assert(0); + goto done; + } + +configure: + ble_adv_copy_to_ext_param(&ext_params, param); + + err = ble_gap_ext_adv_configure(*instance, &ext_params, 0, + ble_adv_gap_mesh_cb, NULL); + if (!err) { + memcpy(cur_conf, param, sizeof(*cur_conf)); + } + +done: + return err; +} + +int +bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + struct os_mbuf *data; + int instance; + int err; + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + + err = ble_adv_conf_adv_instance(param, &instance); + if (err) { + return err; + } + + if (ad_len > 0) { + err = set_ad(ad, ad_len, buf, &buf_len); + if (err) { + return err; + } + + /* For now let's use msys pool. We are not putting more then legacy */ + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0); + if (!data) { + return OS_ENOMEM; + } + + err = os_mbuf_append(data, buf, buf_len); + if (err) { + goto error; + } + + err = ble_gap_ext_adv_set_data(instance, data); + if (err) { + return err; + } + + data = NULL; + } + + if (sd_len > 0) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + return err; + } + + /* For now let's use msys pool. We are not putting more then legace*/ + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0); + if (!data) { + return OS_ENOMEM; + } + + err = os_mbuf_append(data, buf, buf_len); + if (err) { + goto error; + } + + err = ble_gap_ext_adv_rsp_set_data(instance, data); + if (err) { + goto error; + } + } + + /*TODO: We could use duration and max events in the future */ + err = ble_gap_ext_adv_start(instance, 0, 0); + return err; + +error: + if (data) { + os_mbuf_free_chain(data); + } + + return err; +} + +int bt_le_adv_stop(bool proxy) +{ +#if MYNEWT_VAL(BLE_MESH_PROXY) + int rc; + + if (proxy) { + rc = ble_gap_ext_adv_stop(BT_MESH_ADV_GATT_INST); + } else { + rc = ble_gap_ext_adv_stop(BT_MESH_ADV_INST); + } + + return rc; +#else + return ble_gap_ext_adv_stop(BT_MESH_ADV_INST); +#endif +} + +#else + +int +bt_le_adv_start(const struct ble_gap_adv_params *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + int err; + + err = set_ad(ad, ad_len, buf, &buf_len); + if (err) { + return err; + } + + err = ble_gap_adv_set_data(buf, buf_len); + if (err != 0) { + return err; + } + + if (sd) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + + err = ble_gap_adv_rsp_set_data(buf, buf_len); + if (err != 0) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + } + + err = ble_gap_adv_start(g_mesh_addr_type, NULL, BLE_HS_FOREVER, param, + NULL, NULL); + if (err) { + BT_ERR("Advertising failed: err %d", err); + return err; + } + + return 0; +} + +int bt_le_adv_stop(bool proxy) +{ + return ble_gap_adv_stop(); +} + +#endif + +#if MYNEWT_VAL(BLE_MESH_PROXY) +int bt_mesh_proxy_svcs_register(void); +#endif + +void +bt_mesh_register_gatt(void) +{ +#if MYNEWT_VAL(BLE_MESH_PROXY) + bt_mesh_proxy_svcs_register(); +#endif +} + +void net_buf_slist_init(struct net_buf_slist_t *list) +{ + STAILQ_INIT(list); +} + +bool net_buf_slist_is_empty(struct net_buf_slist_t *list) +{ + return STAILQ_EMPTY(list); +} + +struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = STAILQ_FIRST(list); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + /* Get mbuf pointer from packet header pointer */ + pkthdr = OS_MBUF_PKTHDR(buf); + pkthdr = STAILQ_NEXT(pkthdr, omp_next); + if (!pkthdr) { + return NULL; + } + + return OS_MBUF_PKTHDR_TO_MBUF(pkthdr); +} + +struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list) +{ + os_sr_t sr; + struct os_mbuf *m; + + m = net_buf_slist_peek_head(list); + if (!m) { + return NULL; + } + + /* Remove from queue */ + OS_ENTER_CRITICAL(sr); + STAILQ_REMOVE_HEAD(list, omp_next); + OS_EXIT_CRITICAL(sr); + return m; +} + +void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf) +{ + struct os_mbuf_pkthdr *pkthdr; + + pkthdr = OS_MBUF_PKTHDR(buf); + STAILQ_INSERT_TAIL(list, pkthdr, omp_next); +} + +void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev, + struct os_mbuf *cur) +{ + struct os_mbuf_pkthdr *pkthdr, *cur_pkthdr; + + cur_pkthdr = OS_MBUF_PKTHDR(cur); + + STAILQ_FOREACH(pkthdr, list, omp_next) { + if (cur_pkthdr == pkthdr) { + STAILQ_REMOVE(list, cur_pkthdr, os_mbuf_pkthdr, omp_next); + break; + } + } +} + +void net_buf_slist_merge_slist(struct net_buf_slist_t *list, + struct net_buf_slist_t *list_to_append) +{ + if (!STAILQ_EMPTY(list_to_append)) { + *(list)->stqh_last = list_to_append->stqh_first; + (list)->stqh_last = list_to_append->stqh_last; + STAILQ_INIT(list_to_append); + } +} + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +int settings_bytes_from_str(char *val_str, void *vp, int *len) +{ + *len = base64_decode(val_str, vp); + return 0; +} + +char *settings_str_from_bytes(const void *vp, int vp_len, + char *buf, int buf_len) +{ + if (BASE64_ENCODE_SIZE(vp_len) > buf_len) { + return NULL; + } + + base64_encode(vp, vp_len, buf, 1); + + return buf; +} + +#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_cli.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_cli.c new file mode 100644 index 0000000..3fc29e5 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_cli.c @@ -0,0 +1,559 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include +#include +#include + +#include "../include/mesh/mesh.h" +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "foundation.h" +#include "../include/mesh/health_cli.h" + +static s32_t msg_timeout = K_SECONDS(5); + +static struct bt_mesh_health_cli *health_cli; + +struct health_fault_param { + u16_t cid; + u8_t *expect_test_id; + u8_t *test_id; + u8_t *faults; + size_t *fault_count; +}; + +static void health_fault_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_fault_param *param; + u8_t test_id; + u16_t cid; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_HEALTH_FAULT_STATUS) { + BT_WARN("Unexpected Health Fault Status message"); + return; + } + + param = health_cli->op_param; + + test_id = net_buf_simple_pull_u8(buf); + if (param->expect_test_id && test_id != *param->expect_test_id) { + BT_WARN("Health fault with unexpected Test ID"); + return; + } + + cid = net_buf_simple_pull_le16(buf); + if (cid != param->cid) { + BT_WARN("Health fault with unexpected Company ID"); + return; + } + + if (param->test_id) { + *param->test_id = test_id; + } + + if (buf->om_len > *param->fault_count) { + BT_WARN("Got more faults than there's space for"); + } else { + *param->fault_count = buf->om_len; + } + + memcpy(param->faults, buf->om_data, *param->fault_count); + + k_sem_give(&health_cli->op_sync); +} + +static void health_current_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_cli *cli = model->user_data; + u8_t test_id; + u16_t cid; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + test_id = net_buf_simple_pull_u8(buf); + cid = net_buf_simple_pull_le16(buf); + + BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u", + test_id, cid, buf->om_len); + + if (!cli->current_status) { + BT_WARN("No Current Status callback available"); + return; + } + + cli->current_status(cli, ctx->addr, test_id, cid, buf->om_data, buf->om_len); +} + +struct health_period_param { + u8_t *divisor; +}; + +static void health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_period_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_HEALTH_PERIOD_STATUS) { + BT_WARN("Unexpected Health Period Status message"); + return; + } + + param = health_cli->op_param; + + *param->divisor = net_buf_simple_pull_u8(buf); + + k_sem_give(&health_cli->op_sync); +} + +struct health_attention_param { + u8_t *attention; +}; + +static void health_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct health_attention_param *param; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (health_cli->op_pending != OP_ATTENTION_STATUS) { + BT_WARN("Unexpected Health Attention Status message"); + return; + } + + param = health_cli->op_param; + + if (param->attention) { + *param->attention = net_buf_simple_pull_u8(buf); + } + + k_sem_give(&health_cli->op_sync); +} + +const struct bt_mesh_model_op bt_mesh_health_cli_op[] = { + { OP_HEALTH_FAULT_STATUS, 3, health_fault_status }, + { OP_HEALTH_CURRENT_STATUS, 3, health_current_status }, + { OP_HEALTH_PERIOD_STATUS, 1, health_period_status }, + { OP_ATTENTION_STATUS, 1, health_attention_status }, + BT_MESH_MODEL_OP_END, +}; + +static int cli_prepare(void *param, u32_t op) +{ + if (!health_cli) { + BT_ERR("No available Health Client context!"); + return -EINVAL; + } + + if (health_cli->op_pending) { + BT_WARN("Another synchronous operation pending"); + return -EBUSY; + } + + health_cli->op_param = param; + health_cli->op_pending = op; + + return 0; +} + +static void cli_reset(void) +{ + health_cli->op_pending = 0; + health_cli->op_param = NULL; +} + +static int cli_wait(void) +{ + int err; + + err = k_sem_take(&health_cli->op_sync, msg_timeout); + + cli_reset(); + + return err; +} + +int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *attention) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_GET, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_attention_param param = { + .attention = attention, + }; + int err; + + err = cli_prepare(¶m, OP_ATTENTION_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_ATTENTION_GET); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t attention, u8_t *updated_attention) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_SET, 1); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_attention_param param = { + .attention = updated_attention, + }; + int err; + + err = cli_prepare(¶m, OP_ATTENTION_STATUS); + if (err) { + goto done; + } + + if (updated_attention) { + bt_mesh_model_msg_init(msg, OP_ATTENTION_SET); + } else { + bt_mesh_model_msg_init(msg, OP_ATTENTION_SET_UNREL); + } + + net_buf_simple_add_u8(msg, attention); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!updated_attention) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *divisor) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_GET, 0); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_period_param param = { + .divisor = divisor, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_GET); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t divisor, u8_t *updated_divisor) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_SET, 1); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_period_param param = { + .divisor = updated_divisor, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); + if (err) { + goto done; + } + + if (updated_divisor) { + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET_UNREL); + } + + net_buf_simple_add_u8(msg, divisor); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!updated_divisor) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_TEST, 3); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .expect_test_id = &test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + if (faults) { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST_UNREL); + } + + net_buf_simple_add_u8(msg, test_id); + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!faults) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_CLEAR, 2); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .test_id = test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + if (test_id) { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR); + } else { + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR_UNREL); + } + + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + if (!test_id) { + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u16_t cid, u8_t *test_id, u8_t *faults, + size_t *fault_count) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_GET, 2); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct health_fault_param param = { + .cid = cid, + .test_id = test_id, + .faults = faults, + .fault_count = fault_count, + }; + int err; + + err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); + if (err) { + goto done; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_GET); + net_buf_simple_add_le16(msg, cid); + + err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + cli_reset(); + goto done; + } + + err = cli_wait(); +done: + os_mbuf_free_chain(msg); + return err; +} + +s32_t bt_mesh_health_cli_timeout_get(void) +{ + return msg_timeout; +} + +void bt_mesh_health_cli_timeout_set(s32_t timeout) +{ + msg_timeout = timeout; +} + +int bt_mesh_health_cli_set(struct bt_mesh_model *model) +{ + if (!model->user_data) { + BT_ERR("No Health Client context for given model"); + return -EINVAL; + } + + health_cli = model->user_data; + + return 0; +} + +static int health_cli_init(struct bt_mesh_model *model) +{ + struct bt_mesh_health_cli *cli = model->user_data; + + BT_DBG("primary %u", bt_mesh_model_in_primary(model)); + + if (!cli) { + BT_ERR("No Health Client context provided"); + return -EINVAL; + } + + cli = model->user_data; + cli->model = model; + + k_sem_init(&cli->op_sync, 0, 1); + + /* Set the default health client pointer */ + if (!health_cli) { + health_cli = cli; + } + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_health_cli_cb = { + .init = health_cli_init, +}; +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_srv.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_srv.c new file mode 100644 index 0000000..7063d92 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/health_srv.c @@ -0,0 +1,456 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include +#include +#include + +#include "../include/mesh/mesh.h" +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" + +#define HEALTH_TEST_STANDARD 0x00 + +/* Health Server context of the primary element */ +struct bt_mesh_health_srv *health_srv; + +static void health_get_registered(struct bt_mesh_model *mod, + u16_t company_id, + struct os_mbuf *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + u8_t *test_id; + + BT_DBG("Company ID 0x%04x", company_id); + + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + net_buf_simple_add_le16(msg, company_id); + + if (srv->cb && srv->cb->fault_get_reg) { + u8_t fault_count = net_buf_simple_tailroom(msg) - 4; + int err; + + err = srv->cb->fault_get_reg(mod, company_id, test_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("Failed to get faults (err %d)", err); + *test_id = HEALTH_TEST_STANDARD; + } else { + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + *test_id = HEALTH_TEST_STANDARD; + } +} + +static size_t health_get_current(struct bt_mesh_model *mod, + struct os_mbuf *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + const struct bt_mesh_comp *comp; + u8_t *test_id, *company_ptr; + u16_t company_id; + u8_t fault_count; + int err; + + bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + company_ptr = net_buf_simple_add(msg, sizeof(company_id)); + comp = bt_mesh_comp_get(); + + if (srv->cb && srv->cb->fault_get_cur) { + fault_count = net_buf_simple_tailroom(msg); + err = srv->cb->fault_get_cur(mod, test_id, &company_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("Failed to get faults (err %d)", err); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0; + } else { + sys_put_le16(company_id, company_ptr); + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0; + } + + return fault_count; +} + +static void health_fault_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + + os_mbuf_free_chain(sdu); +} + +static void health_fault_clear_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } +} + +static void health_fault_clear(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + + os_mbuf_free_chain(sdu); +} + +static void health_fault_test_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + u8_t test_id; + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + srv->cb->fault_test(model, test_id, company_id); + } +} + +static void health_fault_test(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX); + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + u8_t test_id; + + BT_DBG(""); + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + int err; + + err = srv->cb->fault_test(model, test_id, company_id); + if (err) { + BT_WARN("Running fault test failed with err %d", err); + goto done; + } + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("Unable to send Health Current Status response"); + } + +done: + os_mbuf_free_chain(sdu); +} + +static void send_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_STATUS, 1); + struct bt_mesh_health_srv *srv = model->user_data; + u8_t time; + + time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000; + BT_DBG("%u second%s", time, (time == 1) ? "" : "s"); + + bt_mesh_model_msg_init(msg, OP_ATTENTION_STATUS); + + net_buf_simple_add_u8(msg, time); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Attention Status"); + } + + os_mbuf_free_chain(msg); +} + +static void attention_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + send_attention_status(model, ctx); +} + +static void attention_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t time; + + time = net_buf_simple_pull_u8(buf); + + BT_DBG("%u second%s", time, (time == 1) ? "" : "s"); + + bt_mesh_attention(model, time); +} + +static void attention_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + attention_set_unrel(model, ctx, buf); + + send_attention_status(model, ctx); +} + +static void send_health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_STATUS, 1); + + bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_STATUS); + + net_buf_simple_add_u8(msg, model->pub->period_div); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send Health Period Status"); + } + + os_mbuf_free_chain(msg); +} + +static void health_period_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + send_health_period_status(model, ctx); +} + +static void health_period_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t period; + + period = net_buf_simple_pull_u8(buf); + if (period > 15) { + BT_WARN("Prohibited period value %u", period); + return; + } + + BT_DBG("period %u", period); + + model->pub->period_div = period; +} + +static void health_period_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + health_period_set_unrel(model, ctx, buf); + + send_health_period_status(model, ctx); +} + +const struct bt_mesh_model_op bt_mesh_health_srv_op[] = { + { OP_HEALTH_FAULT_GET, 2, health_fault_get }, + { OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear }, + { OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear_unrel }, + { OP_HEALTH_FAULT_TEST, 3, health_fault_test }, + { OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test_unrel }, + { OP_HEALTH_PERIOD_GET, 0, health_period_get }, + { OP_HEALTH_PERIOD_SET, 1, health_period_set }, + { OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set_unrel }, + { OP_ATTENTION_GET, 0, attention_get }, + { OP_ATTENTION_SET, 1, attention_set }, + { OP_ATTENTION_SET_UNREL, 1, attention_set_unrel }, + BT_MESH_MODEL_OP_END, +}; + +static int health_pub_update(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + size_t count; + + BT_DBG(""); + + count = health_get_current(mod, pub->msg); + if (count) { + pub->fast_period = 1U; + } else { + pub->fast_period = 0U; + } + + return 0; +} + +int bt_mesh_fault_update(struct bt_mesh_elem *elem) +{ + struct bt_mesh_model *mod; + + mod = bt_mesh_model_find(elem, BT_MESH_MODEL_ID_HEALTH_SRV); + if (!mod) { + return -EINVAL; + } + + /* Let periodic publishing, if enabled, take care of sending the + * Health Current Status. + */ + if (bt_mesh_model_pub_period_get(mod)) { + return 0; + } + + health_pub_update(mod); + + return bt_mesh_model_publish(mod); +} + +static void attention_off(struct ble_npl_event *work) +{ + struct bt_mesh_health_srv *srv = ble_npl_event_get_arg(work); + BT_DBG(""); + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(srv->model); + } +} + +static int health_srv_init(struct bt_mesh_model *model) +{ + struct bt_mesh_health_srv *srv = model->user_data; + + if (!srv) { + if (!bt_mesh_model_in_primary(model)) { + return 0; + } + + BT_ERR("No Health Server context provided"); + return -EINVAL; + } + + if (!model->pub) { + BT_ERR("Health Server has no publication support"); + return -EINVAL; + } + + model->pub->update = health_pub_update; + + k_delayed_work_init(&srv->attn_timer, attention_off); + k_delayed_work_add_arg(&srv->attn_timer, srv); + + srv->model = model; + + if (bt_mesh_model_in_primary(model)) { + health_srv = srv; + } + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_health_srv_cb = { + .init = health_srv_init, +}; + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time) +{ + struct bt_mesh_health_srv *srv; + + BT_DBG("bt_mesh_attention"); + if (!model) { + srv = health_srv; + if (!srv) { + BT_WARN("No Health Server available"); + return; + } + + model = srv->model; + } else { + srv = model->user_data; + } + + if (time) { + if (srv->cb && srv->cb->attn_on) { + srv->cb->attn_on(model); + } + + k_delayed_work_submit(&srv->attn_timer, time * 1000); + } else { + k_delayed_work_cancel(&srv->attn_timer); + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(model); + } + } +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.c new file mode 100644 index 0000000..a390bf8 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.c @@ -0,0 +1,59 @@ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#include "../include/mesh/mesh.h" +#include "nimble/console/console.h" +#include "light_model.h" + + +static u8_t gen_onoff_state; +static s16_t gen_level_state; + +static void update_light_state(void) +{ + console_printf("Light state: onoff=%d lvl=0x%04x\n", gen_onoff_state, (u16_t)gen_level_state); +} + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state) +{ + *state = gen_onoff_state; + return 0; +} + +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state) +{ + gen_onoff_state = state; + update_light_state(); + return 0; +} + +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level) +{ + *level = gen_level_state; + return 0; +} + +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level) +{ + gen_level_state = level; + if ((u16_t)gen_level_state > 0x0000) { + gen_onoff_state = 1; + } + if ((u16_t)gen_level_state == 0x0000) { + gen_onoff_state = 0; + } + update_light_state(); + return 0; +} + +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness) +{ + return light_model_gen_level_get(model, lightness); +} + +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness) +{ + return light_model_gen_level_set(model, lightness); +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.h new file mode 100644 index 0000000..f3b1f7c --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/light_model.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_MESH_LIGHT_MODEL_H +#define __BT_MESH_LIGHT_MODEL_H + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#include "../include/mesh/mesh.h" + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state); +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state); +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level); +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level); +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness); +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.c new file mode 100644 index 0000000..5bb64b9 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.c @@ -0,0 +1,1059 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_LOW_POWER_LOG + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) + +#include + +#include "../include/mesh/mesh.h" +#include "mesh_priv.h" +#include "crypto.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "beacon.h" +#include "foundation.h" +#include "lpn.h" + +#if MYNEWT_VAL(BLE_MESH_LPN_AUTO) +#define LPN_AUTO_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_AUTO_TIMEOUT)) +#else +#define LPN_AUTO_TIMEOUT 0 +#endif + +#define LPN_RECV_DELAY MYNEWT_VAL(BLE_MESH_LPN_RECV_DELAY) +#define SCAN_LATENCY min(MYNEWT_VAL(BLE_MESH_LPN_SCAN_LATENCY), \ + LPN_RECV_DELAY) + +#define FRIEND_REQ_RETRY_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_RETRY_TIMEOUT)) + +#define FRIEND_REQ_WAIT K_MSEC(100) +#define FRIEND_REQ_SCAN K_SECONDS(1) +#define FRIEND_REQ_TIMEOUT (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN) + +#define POLL_RETRY_TIMEOUT K_MSEC(100) + +#define REQ_RETRY_DURATION(lpn) (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \ + (lpn)->recv_win + POLL_RETRY_TIMEOUT)) + +#define POLL_TIMEOUT_INIT (MYNEWT_VAL(BLE_MESH_LPN_INIT_POLL_TIMEOUT) * 100) +#define POLL_TIMEOUT_MAX(lpn) ((MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) * 100) - \ + REQ_RETRY_DURATION(lpn)) +#define REQ_ATTEMPTS(lpn) (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 4) + +#define CLEAR_ATTEMPTS 2 + +#define LPN_CRITERIA ((MYNEWT_VAL(BLE_MESH_LPN_MIN_QUEUE_SIZE)) | \ + (MYNEWT_VAL(BLE_MESH_LPN_RSSI_FACTOR) << 3) | \ + (MYNEWT_VAL(BLE_MESH_LPN_RECV_WIN_FACTOR) << 5)) + +#define POLL_TO(to) { (u8_t)((to) >> 16), (u8_t)((to) >> 8), (u8_t)(to) } +#define LPN_POLL_TO POLL_TO(MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT)) + +/* 2 transmissions, 20ms interval */ +#define POLL_XMIT BT_MESH_TRANSMIT(1, 20) + +static void (*lpn_cb)(u16_t friend_addr, bool established); + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG +static const char *state2str(int state) +{ + switch (state) { + case BT_MESH_LPN_DISABLED: + return "disabled"; + case BT_MESH_LPN_CLEAR: + return "clear"; + case BT_MESH_LPN_TIMER: + return "timer"; + case BT_MESH_LPN_ENABLED: + return "enabled"; + case BT_MESH_LPN_REQ_WAIT: + return "req wait"; + case BT_MESH_LPN_WAIT_OFFER: + return "wait offer"; + case BT_MESH_LPN_ESTABLISHED: + return "established"; + case BT_MESH_LPN_RECV_DELAY: + return "recv delay"; + case BT_MESH_LPN_WAIT_UPDATE: + return "wait update"; + default: + return "(unknown)"; + } +} +#endif + +static inline void lpn_set_state(int state) +{ +#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG + BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state)); +#endif + bt_mesh.lpn.state = state; +} + +static inline void group_zero(atomic_t *target) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_set(&target[i], 0); + } +#else + atomic_set(target, 0); +#endif +} + +static inline void group_set(atomic_t *target, atomic_t *source) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_or(&target[i], atomic_get(&source[i])); + } +#else + atomic_or(target, atomic_get(source)); +#endif +} + +static inline void group_clear(atomic_t *target, atomic_t *source) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + atomic_and(&target[i], ~atomic_get(&source[i])); + } +#else + atomic_and(target, ~atomic_get(source)); +#endif +} + +static void clear_friendship(bool force, bool disable); + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + /* We're switching away from Low Power behavior, so permanently + * enable scanning. + */ + bt_mesh_scan_enable(); + + lpn->req_attempts++; + + if (err) { + BT_ERR("Sending Friend Request failed (err %d)", err); + lpn_set_state(BT_MESH_LPN_ENABLED); + clear_friendship(false, lpn->disable); + return; + } + + lpn_set_state(BT_MESH_LPN_CLEAR); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT); +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static int send_friend_clear(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(tx.src), + .lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter), + }; + + BT_DBG(""); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, NULL); +} + +static void clear_friendship(bool force, bool disable) +{ + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG("force %u disable %u", force, disable); + + if (!force && lpn->established && !lpn->clear_success && + lpn->req_attempts < CLEAR_ATTEMPTS) { + send_friend_clear(); + lpn->disable = disable; + return; + } + + bt_mesh_rx_reset(); + + k_delayed_work_cancel(&lpn->timer); + + friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd); + + if (lpn->clear_success) { + lpn->old_friend = BT_MESH_ADDR_UNASSIGNED; + } else { + lpn->old_friend = lpn->frnd; + } + + if (lpn_cb && lpn->frnd != BT_MESH_ADDR_UNASSIGNED) { + lpn_cb(lpn->frnd, false); + } + + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + lpn->fsn = 0; + lpn->req_attempts = 0; + lpn->recv_win = 0; + lpn->queue_size = 0; + lpn->disable = 0; + lpn->sent_req = 0; + lpn->established = 0; + lpn->clear_success = 0; + + group_zero(lpn->added); + group_zero(lpn->pending); + group_zero(lpn->to_remove); + + /* Set this to 1 to force group subscription when the next + * Friendship is created, in case lpn->groups doesn't get + * modified meanwhile. + */ + lpn->groups_changed = 1; + + if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) { + bt_mesh_heartbeat_send(); + } + + if (disable) { + lpn_set_state(BT_MESH_LPN_DISABLED); + return; + } + + lpn_set_state(BT_MESH_LPN_ENABLED); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); +} + +static void friend_req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (err) { + BT_ERR("Sending Friend Request failed (err %d)", err); + return; + } + + lpn->adv_duration = duration; + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT); + lpn_set_state(BT_MESH_LPN_REQ_WAIT); + } else { + k_delayed_work_submit(&lpn->timer, + duration + FRIEND_REQ_TIMEOUT); + lpn_set_state(BT_MESH_LPN_WAIT_OFFER); + } +} + +static const struct bt_mesh_send_cb friend_req_sent_cb = { + .start = friend_req_sent, +}; + +static int send_friend_req(struct bt_mesh_lpn *lpn) +{ + const struct bt_mesh_comp *comp = bt_mesh_comp_get(); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = BT_MESH_ADDR_FRIENDS, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + }; + struct bt_mesh_ctl_friend_req req = { + .criteria = LPN_CRITERIA, + .recv_delay = LPN_RECV_DELAY, + .poll_to = LPN_POLL_TO, + .prev_addr = lpn->old_friend, + .num_elem = comp->elem_count, + .lpn_counter = sys_cpu_to_be16(lpn->counter), + }; + + BT_DBG(""); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req, + sizeof(req), NULL, &friend_req_sent_cb, NULL); +} + +static void req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG + BT_DBG("req 0x%02x duration %u err %d state %s", + lpn->sent_req, duration, err, state2str(lpn->state)); +#endif + + if (err) { + BT_ERR("Sending request failed (err %d)", err); + lpn->sent_req = 0; + group_zero(lpn->pending); + return; + } + + lpn->req_attempts++; + lpn->adv_duration = duration; + + if (lpn->established || IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + lpn_set_state(BT_MESH_LPN_RECV_DELAY); + /* We start scanning a bit early to elimitate risk of missing + * response data due to HCI and other latencies. + */ + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY - SCAN_LATENCY); + } else { + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY + duration + + lpn->recv_win); + } +} + +static const struct bt_mesh_send_cb req_sent_cb = { + .start = req_sent, +}; + +static int send_friend_poll(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u8_t fsn = lpn->fsn; + int err; + + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req) { + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + lpn->pending_poll = 1; + } + + return 0; + } + + err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1, + NULL, &req_sent_cb, NULL); + if (err == 0) { + lpn->pending_poll = 0; + lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL; + } + + return err; +} + +void bt_mesh_lpn_disable(bool force) +{ + if (bt_mesh.lpn.state == BT_MESH_LPN_DISABLED) { + return; + } + + clear_friendship(force, true); +} + +int bt_mesh_lpn_set(bool enable) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (enable) { + if (lpn->state != BT_MESH_LPN_DISABLED) { + return 0; + } + } else { + if (lpn->state == BT_MESH_LPN_DISABLED) { + return 0; + } + } + + if (!bt_mesh_is_provisioned()) { + if (enable) { + lpn_set_state(BT_MESH_LPN_ENABLED); + } else { + lpn_set_state(BT_MESH_LPN_DISABLED); + } + + return 0; + } + + if (enable) { + lpn_set_state(BT_MESH_LPN_ENABLED); + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + send_friend_req(lpn); + } else { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO) && + lpn->state == BT_MESH_LPN_TIMER) { + k_delayed_work_cancel(&lpn->timer); + lpn_set_state(BT_MESH_LPN_DISABLED); + } else { + bt_mesh_lpn_disable(false); + } + } + + return 0; +} + +static void friend_response_received(struct bt_mesh_lpn *lpn) +{ + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) { + lpn->fsn++; + } + + k_delayed_work_cancel(&lpn->timer); + bt_mesh_scan_disable(); + lpn_set_state(BT_MESH_LPN_ESTABLISHED); + lpn->req_attempts = 0; + lpn->sent_req = 0; +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (lpn->state == BT_MESH_LPN_TIMER) { + BT_DBG("Restarting establishment timer"); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + return; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected message withouth a preceding Poll"); + return; + } + + friend_response_received(lpn); + + BT_DBG("Requesting more messages from Friend"); + + send_friend_poll(); +} + +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_offer *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + struct friend_cred *cred; + u16_t frnd_counter; + int err; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Offer"); + return -EINVAL; + } + + if (lpn->state != BT_MESH_LPN_WAIT_OFFER) { + BT_WARN("Ignoring unexpected Friend Offer"); + return 0; + } + + if (!msg->recv_win) { + BT_WARN("Prohibited ReceiveWindow value"); + return -EINVAL; + } + + frnd_counter = sys_be16_to_cpu(msg->frnd_counter); + + BT_DBG("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u", + msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi, + frnd_counter); + + lpn->frnd = rx->ctx.addr; + + cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter); + if (!cred) { + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + return -ENOMEM; + } + + /* TODO: Add offer acceptance criteria check */ + + k_delayed_work_cancel(&lpn->timer); + + lpn->recv_win = msg->recv_win; + lpn->queue_size = msg->queue_size; + + err = send_friend_poll(); + if (err) { + friend_cred_clear(cred); + lpn->frnd = BT_MESH_ADDR_UNASSIGNED; + lpn->recv_win = 0; + lpn->queue_size = 0; + return err; + } + + lpn->counter++; + + return 0; +} + +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t addr, counter; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear Confirm"); + return -EINVAL; + } + + if (lpn->state != BT_MESH_LPN_CLEAR) { + BT_WARN("Ignoring unexpected Friend Clear Confirm"); + return 0; + } + + addr = sys_be16_to_cpu(msg->lpn_addr); + counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter); + + if (addr != bt_mesh_primary_addr() || counter != lpn->counter) { + BT_WARN("Invalid parameters in Friend Clear Confirm"); + return 0; + } + + lpn->clear_success = 1; + clear_friendship(false, lpn->disable); + + return 0; +} + +static void lpn_group_add(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t *free_slot = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + atomic_clear_bit(lpn->to_remove, i); + return; + } + + if (!free_slot && lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + free_slot = &lpn->groups[i]; + } + } + + if (!free_slot) { + BT_WARN("Friend Subscription List exceeded!"); + return; + } + + *free_slot = group; + lpn->groups_changed = 1; +} + +static void lpn_group_del(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + if (atomic_test_bit(lpn->added, i) || + atomic_test_bit(lpn->pending, i)) { + atomic_set_bit(lpn->to_remove, i); + lpn->groups_changed = 1; + } else { + lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED; + } + } + } +} + +static inline int group_popcount(atomic_t *target) +{ +#if CONFIG_BT_MESH_LPN_GROUPS > 32 + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + count += popcount(atomic_get(&target[i])); + } +#else + return popcount(atomic_get(target)); +#endif +} + +static bool sub_update(u8_t op) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int added_count = group_popcount(lpn->added); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BT_MESH_KEY_UNUSED, + .addr = lpn->frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_ctl_friend_sub req; + size_t i, g; + + BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req); + + if (lpn->sent_req) { + return false; + } + + for (i = 0, g = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) { + if (atomic_test_bit(lpn->added, i)) { + continue; + } + } else { + if (!atomic_test_bit(lpn->to_remove, i)) { + continue; + } + } + + if (added_count + g >= lpn->queue_size) { + BT_WARN("Friend Queue Size exceeded"); + break; + } + + req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]); + atomic_set_bit(lpn->pending, i); + + if (g == ARRAY_SIZE(req.addr_list)) { + break; + } + } + + if (g == 0) { + group_zero(lpn->pending); + return false; + } + + req.xact = lpn->xact_next++; + + if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL, + &req_sent_cb, NULL) < 0) { + group_zero(lpn->pending); + return false; + } + + lpn->xact_pending = req.xact; + lpn->sent_req = op; + return true; +} + +static void update_timeout(struct bt_mesh_lpn *lpn) +{ + if (lpn->established) { + BT_WARN("No response from Friend during ReceiveWindow"); + bt_mesh_scan_disable(); + lpn_set_state(BT_MESH_LPN_ESTABLISHED); + k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT); + } else { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + if (lpn->req_attempts < 6) { + BT_WARN("Retrying first Friend Poll"); + lpn->sent_req = 0; + if (send_friend_poll() == 0) { + return; + } + } + + BT_ERR("Timed out waiting for first Friend Update"); + clear_friendship(false, false); + } +} + +static void lpn_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG + BT_DBG("state: %s", state2str(lpn->state)); +#endif + + switch (lpn->state) { + case BT_MESH_LPN_DISABLED: + break; + case BT_MESH_LPN_CLEAR: + clear_friendship(false, bt_mesh.lpn.disable); + break; + case BT_MESH_LPN_TIMER: + BT_DBG("Starting to look for Friend nodes"); + lpn_set_state(BT_MESH_LPN_ENABLED); + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + /* fall through */ + case BT_MESH_LPN_ENABLED: + send_friend_req(lpn); + break; + case BT_MESH_LPN_REQ_WAIT: + bt_mesh_scan_enable(); + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + FRIEND_REQ_SCAN); + lpn_set_state(BT_MESH_LPN_WAIT_OFFER); + break; + case BT_MESH_LPN_WAIT_OFFER: + BT_WARN("No acceptable Friend Offers received"); + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + lpn->counter++; + lpn_set_state(BT_MESH_LPN_ENABLED); + lpn->sent_req = 0U; + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); + break; + case BT_MESH_LPN_ESTABLISHED: + if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) { + u8_t req = lpn->sent_req; + + lpn->sent_req = 0; + + if (!req || req == TRANS_CTL_OP_FRIEND_POLL) { + send_friend_poll(); + } else { + sub_update(req); + } + + break; + } + + BT_ERR("No response from Friend after %u retries", + lpn->req_attempts); + lpn->req_attempts = 0; + clear_friendship(false, false); + break; + case BT_MESH_LPN_RECV_DELAY: + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + SCAN_LATENCY + + lpn->recv_win); + bt_mesh_scan_enable(); + lpn_set_state(BT_MESH_LPN_WAIT_UPDATE); + break; + case BT_MESH_LPN_WAIT_UPDATE: + update_timeout(lpn); + break; + default: + __ASSERT(0, "Unhandled LPN state"); + break; + } +} + +void bt_mesh_lpn_group_add(u16_t group) +{ + BT_DBG("group 0x%04x", group); + + lpn_group_add(group); + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); +} + +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count) +{ + int i; + + for (i = 0; i < group_count; i++) { + if (groups[i] != BT_MESH_ADDR_UNASSIGNED) { + BT_DBG("group 0x%04x", groups[i]); + lpn_group_del(groups[i]); + } + } + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); +} + +static s32_t poll_timeout(struct bt_mesh_lpn *lpn) +{ + /* If we're waiting for segment acks keep polling at high freq */ + if (bt_mesh_tx_in_progress()) { + return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1)); + } + + if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) { + lpn->poll_timeout *= 2; + lpn->poll_timeout = min(lpn->poll_timeout, + POLL_TIMEOUT_MAX(lpn)); + } + + BT_DBG("Poll Timeout is %ums", (unsigned) lpn->poll_timeout); + + return lpn->poll_timeout; +} + +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Subscription Confirm"); + return -EINVAL; + } + + BT_DBG("xact 0x%02x", msg->xact); + + if (!lpn->sent_req) { + BT_WARN("No pending subscription list message"); + return 0; + } + + if (msg->xact != lpn->xact_pending) { + BT_WARN("Transaction mismatch (0x%02x != 0x%02x)", + msg->xact, lpn->xact_pending); + return 0; + } + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) { + group_set(lpn->added, lpn->pending); + group_zero(lpn->pending); + } else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) { + int i; + + group_clear(lpn->added, lpn->pending); + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (atomic_test_and_clear_bit(lpn->pending, i) && + atomic_test_and_clear_bit(lpn->to_remove, i)) { + lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED; + } + } + } else { + BT_WARN("Unexpected Friend Subscription Confirm"); + return 0; + } + + friend_response_received(lpn); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0; + } + } + + if (lpn->pending_poll) { + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_ctl_friend_update *msg = (void *)buf->om_data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + u32_t iv_index; + + if (buf->om_len < sizeof(*msg)) { + BT_WARN("Too short Friend Update"); + return -EINVAL; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected friend update"); + return 0; + } + + if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !rx->new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return 0; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) && + (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) == + BT_MESH_IV_UPDATE(msg->flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + if (!lpn->established) { + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + + /* This is normally checked on the transport layer, however + * in this state we're also still accepting master + * credentials so we need to ensure the right ones (Friend + * Credentials) were used for this message. + */ + if (!rx->friend_cred) { + BT_WARN("Friend Update with wrong credentials"); + return -EINVAL; + } + + lpn->established = 1; + + BT_INFO("Friendship established with 0x%04x", lpn->frnd); + + if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) { + bt_mesh_heartbeat_send(); + } + + if (lpn_cb) { + lpn_cb(lpn->frnd, true); + } + + /* Set initial poll timeout */ + lpn->poll_timeout = min(POLL_TIMEOUT_MAX(lpn), + POLL_TIMEOUT_INIT); + } + + friend_response_received(lpn); + + iv_index = sys_be32_to_cpu(msg->iv_index); + + BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags, + (unsigned) iv_index, msg->md); + + if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags), + rx->new_key)) { + bt_mesh_net_beacon_update(sub); + } + + bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags)); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0; + } + } + + if (msg->md) { + BT_DBG("Requesting for more messages"); + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_poll(void) +{ + if (!bt_mesh.lpn.established) { + return -EAGAIN; + } + + BT_DBG("Requesting more messages"); + + return send_friend_poll(); +} + +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)) +{ + lpn_cb = cb; +} + +int bt_mesh_lpn_init(void) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG(""); + + k_delayed_work_init(&lpn->timer, lpn_timeout); + + if (lpn->state == BT_MESH_LPN_ENABLED) { + if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } else { + bt_mesh_scan_enable(); + } + + send_friend_req(lpn); + } else { + bt_mesh_scan_enable(); + + if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO)) { + BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT); + lpn_set_state(BT_MESH_LPN_TIMER); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + } + } + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_LOW_POWER) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.h new file mode 100644 index 0000000..e53c81d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/lpn.h @@ -0,0 +1,68 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __LPN_H__ +#define __LPN_H__ + +#include "../include/mesh/mesh.h" + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct os_mbuf *buf); + +static inline bool bt_mesh_lpn_established(void) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + return bt_mesh.lpn.established; +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_match(u16_t addr) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + if (bt_mesh_lpn_established()) { + return (addr == bt_mesh.lpn.frnd); + } +#endif + return false; +} + +static inline bool bt_mesh_lpn_waiting_update(void) +{ +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + return (bt_mesh.lpn.state == BT_MESH_LPN_WAIT_UPDATE); +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_timer(void) +{ +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) && MYNEWT_VAL(BLE_MESH_LPN_AUTO) + return (bt_mesh.lpn.state == BT_MESH_LPN_TIMER); +#else + return false; +#endif +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx); + +void bt_mesh_lpn_group_add(u16_t group); +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count); + +void bt_mesh_lpn_disable(bool force); + +int bt_mesh_lpn_init(void); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh.c new file mode 100644 index 0000000..7ad1264 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh.c @@ -0,0 +1,364 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include +#include + +#include "nimble/porting/nimble/include/os/os_mbuf.h" +#include "../include/mesh/mesh.h" +#include "nimble/nimble/host/include/host/ble_uuid.h" + +#include "adv.h" +#include "prov.h" +#include "net.h" +#include "beacon.h" +#include "lpn.h" +#include "friend.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "shell.h" +#include "mesh_priv.h" +#include "settings.h" + +u8_t g_mesh_addr_type; +static struct ble_gap_event_listener mesh_event_listener; + +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]) +{ + bool pb_gatt_enabled; + int err; + + BT_INFO("Primary Element: 0x%04x", addr); + BT_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x", + net_idx, flags, (unsigned) iv_index); + + if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EALREADY; + } + + if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) { + if (bt_mesh_proxy_prov_disable(false) == 0) { + pb_gatt_enabled = true; + } else { + pb_gatt_enabled = false; + } + } else { + pb_gatt_enabled = false; + } + + err = bt_mesh_net_create(net_idx, flags, net_key, iv_index); + if (err) { + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + + if (MYNEWT_VAL(BLE_MESH_PB_GATT) && pb_gatt_enabled) { + bt_mesh_proxy_prov_enable(); + } + + return err; + } + + bt_mesh.seq = 0; + + bt_mesh_comp_provision(addr); + + memcpy(bt_mesh.dev_key, dev_key, 16); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Storing network information persistently"); + bt_mesh_store_net(); + bt_mesh_store_subnet(&bt_mesh.sub[0]); + bt_mesh_store_iv(false); + } + + bt_mesh_net_start(); + + return 0; +} + +int bt_mesh_provision_adv(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration) +{ + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (bt_mesh_subnet_get(net_idx) == NULL) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) { + return bt_mesh_pb_adv_open(uuid, net_idx, addr, + attention_duration); + } + + return -ENOTSUP; +} + +void bt_mesh_reset(void) +{ + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return; + } + + bt_mesh.iv_index = 0U; + bt_mesh.seq = 0U; + + memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags)); + + k_delayed_work_cancel(&bt_mesh.ivu_timer); + + bt_mesh_cfg_reset(); + + bt_mesh_rx_reset(); + bt_mesh_tx_reset(); + + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) { + bt_mesh_lpn_disable(true); + } + + if ((MYNEWT_VAL(BLE_MESH_FRIEND))) { + bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY); + } + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + bt_mesh_proxy_gatt_disable(); + } + + if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) { + bt_mesh_proxy_prov_enable(); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_clear_net(); + } + + memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + + bt_mesh_scan_disable(); + bt_mesh_beacon_disable(); + + bt_mesh_comp_unprovision(); + + if (IS_ENABLED(CONFIG_BT_MESH_PROV)) { + bt_mesh_prov_reset(); + } +} + +bool bt_mesh_is_provisioned(void) +{ + return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID); +} + +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + char uuid_buf[BLE_UUID_STR_LEN]; + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + ble_uuid_t *uuid = BLE_UUID128_DECLARE(); + + memcpy(BLE_UUID128(uuid)->value, prov->uuid, 16); + BT_INFO("Device UUID: %s", ble_uuid_to_str(uuid, uuid_buf)); + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + bt_mesh_beacon_enable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_enable(); + bt_mesh_adv_update(); + } + + return 0; +} + +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && + (bearers & BT_MESH_PROV_ADV)) { + bt_mesh_beacon_disable(); + bt_mesh_scan_disable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) && + (bearers & BT_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_disable(true); + } + + return 0; +} + +static int bt_mesh_gap_event(struct ble_gap_event *event, void *arg) +{ + ble_adv_gap_mesh_cb(event, arg); + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + ble_mesh_proxy_gap_event(event, arg); +#endif + + return 0; +} + +static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + mod->pub->count = 0; + k_delayed_work_cancel(&mod->pub->timer); + } +} + +int bt_mesh_suspend(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_disable(); + if (err) { + atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + BT_WARN("Disabling scanning failed (err %d)", err); + return err; + } + + bt_mesh_hb_pub_disable(); + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_disable(); + } + + bt_mesh_model_foreach(model_suspend, NULL); + + return 0; +} + +static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + s32_t period_ms = bt_mesh_model_pub_period_get(mod); + + if (period_ms) { + k_delayed_work_submit(&mod->pub->timer, period_ms); + } + } +} + +int bt_mesh_resume(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_enable(); + if (err) { + BT_WARN("Re-enabling scanning failed (err %d)", err); + atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + return err; + } + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } + + bt_mesh_model_foreach(model_resume, NULL); + + return err; +} + +int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp) +{ + int err; + + g_mesh_addr_type = own_addr_type; + + /* initialize SM alg ECC subsystem (it is used directly from mesh code) */ + ble_sm_alg_ecc_init(); + + err = bt_mesh_comp_register(comp); + if (err) { + return err; + } + +#if (MYNEWT_VAL(BLE_MESH_PROV)) + err = bt_mesh_prov_init(prov); + if (err) { + return err; + } +#endif + +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + bt_mesh_proxy_init(); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PROV)) + /* Need this to proper link.rx.buf allocation */ + bt_mesh_prov_reset_link(); +#endif + + bt_mesh_net_init(); + bt_mesh_trans_init(); + bt_mesh_beacon_init(); + bt_mesh_adv_init(); + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + + bt_mesh_beacon_enable(); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + bt_mesh_proxy_prov_enable(); +#endif + + ble_gap_event_listener_register(&mesh_event_listener, + bt_mesh_gap_event, NULL); + +#if (MYNEWT_VAL(BLE_MESH_SETTINGS)) + bt_mesh_settings_init(); +#endif + + return 0; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh_priv.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh_priv.h new file mode 100644 index 0000000..f09bb23 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/mesh_priv.h @@ -0,0 +1,39 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __MESH_PRIV_H +#define __MESH_PRIV_H + +#define BT_MESH_KEY_PRIMARY 0x0000 +#define BT_MESH_KEY_ANY 0xffff + +#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000) +#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00) +#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000) +#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb) +struct bt_mesh_net; + +#define OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) +#define OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05) +#define OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06) +#define OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07) +#define OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08) +#define OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09) +#define OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0a) +#define OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0b) +#define OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0c) +#define OP_LIGHT_LIGHTNESS_GET BT_MESH_MODEL_OP_2(0x82, 0x4b) +#define OP_LIGHT_LIGHTNESS_SET BT_MESH_MODEL_OP_2(0x82, 0x4c) +#define OP_LIGHT_LIGHTNESS_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x4d) +#define OP_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4e) + +bool bt_mesh_is_provisioned(void); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_cli.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_cli.c new file mode 100644 index 0000000..c8b10df --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_cli.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include "../include/mesh/mesh.h" +#include "../include/mesh/model_cli.h" +#include "mesh_priv.h" + +static s32_t msg_timeout = K_SECONDS(5); + +static struct bt_mesh_gen_model_cli *gen_onoff_cli; +static struct bt_mesh_gen_model_cli *gen_level_cli; + +static u8_t transaction_id = 0; + +struct gen_onoff_param { + u8_t *state; +}; + +struct gen_level_param { + s16_t *level; +}; + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_model_cli *cli = model->user_data; + struct gen_onoff_param *param; + u8_t state; + + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_GEN_ONOFF_STATUS) { + BT_WARN("Unexpected Generic OnOff Status message"); + return; + } + + param = cli->op_param; + + state = net_buf_simple_pull_u8(buf); + if (param->state) { + *param->state = state; + } + + BT_DBG("state: %d", state); + + k_sem_give(&cli->op_sync); +} + +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_model_cli *cli = model->user_data; + struct gen_level_param *param; + s16_t level; + + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (cli->op_pending != OP_GEN_LEVEL_STATUS) { + BT_WARN("Unexpected Generic LEVEL Status message"); + return; + } + + param = cli->op_param; + + level = net_buf_simple_pull_le16(buf); + if (param->level) { + *param->level = level; + } + + BT_DBG("level: %d", level); + + k_sem_give(&cli->op_sync); +} + +const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { OP_GEN_ONOFF_STATUS, 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +static int onoff_cli_init(struct bt_mesh_model *model) +{ + BT_DBG(""); + + if (!model->user_data) { + BT_ERR("No Generic OnOff Client context provided"); + return -EINVAL; + } + + gen_onoff_cli = model->user_data; + gen_onoff_cli->model = model; + + k_sem_init(&gen_onoff_cli->op_sync, 0, 1); + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_gen_onoff_cli_cb = { + .init = onoff_cli_init, +}; + +const struct bt_mesh_model_op gen_level_cli_op[] = { + { OP_GEN_LEVEL_STATUS, 2, gen_level_status }, + BT_MESH_MODEL_OP_END, +}; + +static int level_cli_init(struct bt_mesh_model *model) +{ + BT_DBG(""); + + if (!model->user_data) { + BT_ERR("No Generic Level Client context provided"); + return -EINVAL; + } + + gen_level_cli = model->user_data; + gen_level_cli->model = model; + + k_sem_init(&gen_level_cli->op_sync, 0, 1); + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_gen_level_cli_cb = { + .init = level_cli_init, +}; + +static int cli_wait(struct bt_mesh_gen_model_cli *cli, void *param, u32_t op) +{ + int err; + + BT_DBG(""); + + cli->op_param = param; + cli->op_pending = op; + + err = k_sem_take(&cli->op_sync, msg_timeout); + + cli->op_pending = 0; + cli->op_param = NULL; + + return err; +} + +int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_onoff_param param = { + .state = state, + }; + int err; + + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_GET); + + err = bt_mesh_model_send(gen_onoff_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + err = cli_wait(gen_onoff_cli, ¶m, OP_GEN_ONOFF_STATUS); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx, + u8_t val, u8_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_onoff_param param = { + .state = state, + }; + int err; + + if (state) { + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET); + } else { + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET_UNACK); + } + + net_buf_simple_add_u8(msg, val); + net_buf_simple_add_u8(msg, transaction_id); + + err = bt_mesh_model_send(gen_onoff_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + if (!state) { + goto done; + } + + err = cli_wait(gen_onoff_cli, ¶m, OP_GEN_ONOFF_STATUS); +done: + if (err == 0) { + transaction_id++; + } + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t *level) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_level_param param = { + .level = level, + }; + int err; + + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_GET); + + err = bt_mesh_model_send(gen_level_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + err = cli_wait(gen_level_cli, ¶m, OP_GEN_LEVEL_STATUS); +done: + os_mbuf_free_chain(msg); + return err; +} + +int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx, + s16_t val, s16_t *state) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct gen_level_param param = { + .level = state, + }; + int err; + + if (state) { + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET); + } else { + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET_UNACK); + } + + net_buf_simple_add_le16(msg, val); + net_buf_simple_add_u8(msg, transaction_id); + + err = bt_mesh_model_send(gen_level_cli->model, &ctx, msg, NULL, NULL); + if (err) { + BT_ERR("model_send() failed (err %d)", err); + goto done; + } + + if (!state) { + goto done; + } + + err = cli_wait(gen_level_cli, ¶m, OP_GEN_LEVEL_STATUS); +done: + if (err == 0) { + transaction_id++; + } + os_mbuf_free_chain(msg); + return err; +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_srv.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_srv.c new file mode 100644 index 0000000..e7bbf38 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/model_srv.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include "../include/mesh/mesh.h" +#include "../include/mesh/model_srv.h" +#include "mesh_priv.h" + +static struct bt_mesh_gen_onoff_srv *gen_onoff_srv; +static struct bt_mesh_gen_level_srv *gen_level_srv; +static struct bt_mesh_light_lightness_srv *light_lightness_srv; + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_gen_onoff_srv *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + u8_t *state; + + bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_STATUS); + state = net_buf_simple_add(msg, 1); + if (cb && cb->get) { + cb->get(model, state); + } + + BT_DBG("state: %d", *state); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_onoff_status(model, ctx); +} + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct bt_mesh_gen_onoff_srv *cb = model->user_data; + u8_t state; + + state = buf->om_data[0]; + + BT_DBG("state: %d", state); + + if (cb && cb->set) { + cb->set(model, state); + } +} + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_onoff_set_unack(model, ctx, buf); + gen_onoff_status(model, ctx); +} + +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_gen_level_srv *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + s16_t *level; + + bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_STATUS); + level = net_buf_simple_add(msg, 2); + if (cb && cb->get) { + cb->get(model, level); + } + + BT_DBG("level: %d", *level); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + gen_level_status(model, ctx); +} + +static void gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) { + struct bt_mesh_gen_level_srv *cb = model->user_data; + s16_t level; + + level = (s16_t) net_buf_simple_pull_le16(buf); + BT_DBG("level: %d", level); + + if (cb && cb->set) { + cb->set(model, level); + } +} + +static void gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + gen_level_set_unack(model, ctx, buf); + gen_level_status(model, ctx); +} + +static void light_lightness_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct bt_mesh_light_lightness_srv *cb = model->user_data; + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + s16_t *lightness; + + bt_mesh_model_msg_init(msg, OP_LIGHT_LIGHTNESS_STATUS); + lightness = net_buf_simple_add(msg, 2); + if (cb && cb->get) { + cb->get(model, lightness); + } + + BT_DBG("lightness: %d", *lightness); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Send status failed"); + } + + os_mbuf_free_chain(msg); +} + +static void light_lightness_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_DBG(""); + + light_lightness_status(model, ctx); +} + +static void light_lightness_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) { + struct bt_mesh_light_lightness_srv *cb = model->user_data; + s16_t lightness; + + lightness = (s16_t) net_buf_simple_pull_le16(buf); + BT_DBG("lightness: %d", lightness); + + if (cb && cb->set) { + cb->set(model, lightness); + } +} + +static void light_lightness_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + light_lightness_set_unack(model, ctx, buf); + light_lightness_status(model, ctx); +} + +const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { OP_GEN_ONOFF_GET, 0, gen_onoff_get }, + { OP_GEN_ONOFF_SET, 2, gen_onoff_set }, + { OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_level_srv_op[] = { + { OP_GEN_LEVEL_GET, 0, gen_level_get }, + { OP_GEN_LEVEL_SET, 3, gen_level_set }, + { OP_GEN_LEVEL_SET_UNACK, 3, gen_level_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_lightness_srv_op[] = { + { OP_LIGHT_LIGHTNESS_GET, 0, light_lightness_get }, + { OP_LIGHT_LIGHTNESS_SET, 3, light_lightness_set }, + { OP_LIGHT_LIGHTNESS_SET_UNACK, 3, light_lightness_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +static int onoff_srv_init(struct bt_mesh_model *model) +{ + struct bt_mesh_gen_onoff_srv *cfg = model->user_data; + + BT_DBG(""); + + if (!cfg) { + BT_ERR("No Generic OnOff Server context provided"); + return -EINVAL; + } + + cfg->model = model; + + gen_onoff_srv = cfg; + + return 0; +} + +const struct bt_mesh_model_cb gen_onoff_srv_cb = { + .init = onoff_srv_init, +}; + +static int level_srv_init(struct bt_mesh_model *model) +{ + struct bt_mesh_gen_level_srv *cfg = model->user_data; + + BT_DBG(""); + + if (!cfg) { + BT_ERR("No Generic Level Server context provided"); + return -EINVAL; + } + + cfg->model = model; + + gen_level_srv = cfg; + + return 0; +} + +const struct bt_mesh_model_cb gen_level_srv_cb = { + .init = level_srv_init, +}; + +static int lightness_srv_init(struct bt_mesh_model *model) +{ + struct bt_mesh_light_lightness_srv *cfg = model->user_data; + + BT_DBG(""); + + if (!cfg) { + BT_ERR("No Light Lightness Server context provided"); + return -EINVAL; + } + + cfg->model = model; + + light_lightness_srv = cfg; + + return 0; +} + +const struct bt_mesh_model_cb light_lightness_srv_cb = { + .init = lightness_srv_init, +}; +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.c new file mode 100644 index 0000000..b8e48d6 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.c @@ -0,0 +1,1436 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_NET_LOG + +#include +#include +#include + +#include "nimble/porting/nimble/include/os/os_mbuf.h" +#include "../include/mesh/mesh.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh_priv.h" +#include "net.h" +#include "lpn.h" +#include "friend.h" +#include "proxy.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "beacon.h" +#include "settings.h" +#include "prov.h" + +/* Minimum valid Mesh Network PDU length. The Network headers + * themselves take up 9 bytes. After that there is a minumum of 1 byte + * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1 + * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least + * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes. + */ +#define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8) + +/* Seq limit after IV Update is triggered */ +#define IV_UPDATE_SEQ_LIMIT 8000000 + +#define IVI(pdu) ((pdu)[0] >> 7) +#define NID(pdu) ((pdu)[0] & 0x7f) +#define CTL(pdu) ((pdu)[1] >> 7) +#define TTL(pdu) ((pdu)[1] & 0x7f) +#define SEQ(pdu) (((u32_t)(pdu)[2] << 16) | \ + ((u32_t)(pdu)[3] << 8) | (u32_t)(pdu)[4]); +#define SRC(pdu) (sys_get_be16(&(pdu)[5])) +#define DST(pdu) (sys_get_be16(&(pdu)[7])) + +/* Determine how many friendship credentials we need */ +#if (MYNEWT_VAL(BLE_MESH_FRIEND)) +#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT) +#elif (MYNEWT_VAL(BLE_MESH_LOW_POWER)) +#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) +#else +#define FRIEND_CRED_COUNT 0 +#endif + +static struct friend_cred friend_cred[FRIEND_CRED_COUNT]; + +static u64_t msg_cache[MYNEWT_VAL(BLE_MESH_MSG_CACHE_SIZE)]; +static u16_t msg_cache_next; + +/* Singleton network context (the implementation only supports one) */ +struct bt_mesh_net bt_mesh = { + .local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue), + .sub = { + [0 ... (MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, + .app_keys = { + [0 ... (MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) + .nodes = { + [0 ... (CONFIG_BT_MESH_NODE_COUNT - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, +#endif +}; + +static u32_t dup_cache[4]; +static int dup_cache_next; + +static bool check_dup(struct os_mbuf *data) +{ + const u8_t *tail = net_buf_simple_tail(data); + u32_t val; + int i; + + val = sys_get_be32(tail - 4) ^ sys_get_be32(tail - 8); + + for (i = 0; i < ARRAY_SIZE(dup_cache); i++) { + if (dup_cache[i] == val) { + return true; + } + } + + dup_cache[dup_cache_next++] = val; + dup_cache_next %= ARRAY_SIZE(dup_cache); + + return false; +} + +static u64_t msg_hash(struct bt_mesh_net_rx *rx, struct os_mbuf *pdu) +{ + u32_t hash1, hash2; + + /* Three least significant bytes of IVI + first byte of SEQ */ + hash1 = (BT_MESH_NET_IVI_RX(rx) << 8) | pdu->om_data[2]; + + /* Two last bytes of SEQ + SRC */ + memcpy(&hash2, &pdu->om_data[3], 4); + + return (u64_t)hash1 << 32 | (u64_t)hash2; +} + +static bool msg_cache_match(struct bt_mesh_net_rx *rx, + struct os_mbuf *pdu) +{ + u64_t hash = msg_hash(rx, pdu); + u16_t i; + + for (i = 0; i < ARRAY_SIZE(msg_cache); i++) { + if (msg_cache[i] == hash) { + return true; + } + } + + /* Add to the cache */ + rx->msg_cache_idx = msg_cache_next++; + msg_cache[rx->msg_cache_idx] = hash; + msg_cache_next %= ARRAY_SIZE(msg_cache); + + return false; +} + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx) +{ + int i; + + if (net_idx == BT_MESH_KEY_ANY) { + return &bt_mesh.sub[0]; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == net_idx) { + return &bt_mesh.sub[i]; + } + } + + return NULL; +} + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]) +{ + u8_t p[] = { 0 }; + u8_t nid; + int err; + + err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy); + if (err) { + BT_ERR("Unable to generate NID, EncKey & PrivacyKey"); + return err; + } + + memcpy(keys->net, key, 16); + + keys->nid = nid; + + BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16)); + BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16)); + + err = bt_mesh_k3(key, keys->net_id); + if (err) { + BT_ERR("Unable to generate Net ID"); + return err; + } + + BT_DBG("NetID %s", bt_hex(keys->net_id, 8)); + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + err = bt_mesh_identity_key(key, keys->identity); + if (err) { + BT_ERR("Unable to generate IdentityKey"); + return err; + } + + BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16)); +#endif /* GATT_PROXY */ + + err = bt_mesh_beacon_key(key, keys->beacon); + if (err) { + BT_ERR("Unable to generate beacon key"); + return err; + } + + BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16)); + + return 0; +} + +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]) +{ + u16_t lpn_addr, frnd_addr; + int err; + u8_t p[9]; + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + if (cred->addr == bt_mesh.lpn.frnd) { + lpn_addr = bt_mesh_primary_addr(); + frnd_addr = cred->addr; + } else { + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); + } +#else + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); +#endif + + BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr); + BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter, + cred->frnd_counter); + + p[0] = 0x01; + sys_put_be16(lpn_addr, p + 1); + sys_put_be16(frnd_addr, p + 3); + sys_put_be16(cred->lpn_counter, p + 5); + sys_put_be16(cred->frnd_counter, p + 7); + + err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid, + cred->cred[idx].enc, cred->cred[idx].privacy); + if (err) { + BT_ERR("Unable to generate NID, EncKey & PrivacyKey"); + return err; + } + + BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid, + bt_hex(cred->cred[idx].enc, 16)); + BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16)); + + return 0; +} + +void friend_cred_refresh(u16_t net_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr != BT_MESH_ADDR_UNASSIGNED && + cred->net_idx == net_idx) { + memcpy(&cred->cred[0], &cred->cred[1], + sizeof(cred->cred[0])); + } + } +} + +int friend_cred_update(struct bt_mesh_subnet *sub) +{ + int err, i; + + BT_DBG("net_idx 0x%04x", sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == BT_MESH_ADDR_UNASSIGNED || + cred->net_idx != sub->net_idx) { + continue; + } + + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + return err; + } + } + + return 0; +} + +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter) +{ + struct friend_cred *cred; + int i, err; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) { + if ((friend_cred[i].addr == BT_MESH_ADDR_UNASSIGNED) || + (friend_cred[i].addr == addr && + friend_cred[i].net_idx == sub->net_idx)) { + cred = &friend_cred[i]; + break; + } + } + + if (!cred) { + BT_WARN("No free friend credential slots"); + return NULL; + } + + cred->net_idx = sub->net_idx; + cred->addr = addr; + cred->lpn_counter = lpn_counter; + cred->frnd_counter = frnd_counter; + + err = friend_cred_set(cred, 0, sub->keys[0].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + + if (sub->kr_flag) { + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + } + + return cred; +} + +void friend_cred_clear(struct friend_cred *cred) +{ + cred->net_idx = BT_MESH_KEY_UNUSED; + cred->addr = BT_MESH_ADDR_UNASSIGNED; + cred->lpn_counter = 0; + cred->frnd_counter = 0; + memset(cred->cred, 0, sizeof(cred->cred)); +} + +int friend_cred_del(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == addr && cred->net_idx == net_idx) { + friend_cred_clear(cred); + return 0; + } + } + + return -ENOENT; +} + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv) +{ + int i; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (addr != BT_MESH_ADDR_UNASSIGNED && cred->addr != addr) { + continue; + } + + if (nid) { + *nid = cred->cred[sub->kr_flag].nid; + } + + if (enc) { + *enc = cred->cred[sub->kr_flag].enc; + } + + if (priv) { + *priv = cred->cred[sub->kr_flag].privacy; + } + + return 0; + } + + return -ENOENT; +} + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub) +{ + u8_t flags = 0x00; + + if (sub && sub->kr_flag) { + flags |= BT_MESH_NET_FLAG_KR; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + flags |= BT_MESH_NET_FLAG_IVU; + } + + return flags; +} + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + if (sub->kr_flag) { + BT_DBG("NetIndex %u Using new key", sub->net_idx); + keys = &sub->keys[1]; + } else { + BT_DBG("NetIndex %u Using current key", sub->net_idx); + keys = &sub->keys[0]; + } + + BT_DBG("flags 0x%02x, IVI 0x%08x", flags, (unsigned) bt_mesh.iv_index); + + return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, + bt_mesh.iv_index, sub->auth); +} + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index) +{ + struct bt_mesh_subnet *sub; + int err; + + BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, + (unsigned) iv_index); + + BT_DBG("NetKey %s", bt_hex(key, 16)); + + (void)memset(msg_cache, 0, sizeof(msg_cache)); + msg_cache_next = 0U; + + sub = &bt_mesh.sub[0]; + + sub->kr_flag = BT_MESH_KEY_REFRESH(flags); + if (sub->kr_flag) { + err = bt_mesh_net_keys_create(&sub->keys[1], key); + if (err) { + return -EIO; + } + + sub->kr_phase = BT_MESH_KR_PHASE_2; + } else { + err = bt_mesh_net_keys_create(&sub->keys[0], key); + if (err) { + return -EIO; + } + } + + sub->net_idx = idx; + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + bt_mesh.iv_index = iv_index; + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, + BT_MESH_IV_UPDATE(flags)); + + /* Set minimum required hours, since the 96-hour minimum requirement + * doesn't apply straight after provisioning (since we can't know how + * long has actually passed since the network changed its state). + */ + bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS; + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub) +{ + int i; + + BT_DBG("idx 0x%04x", sub->net_idx); + + memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0])); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != sub->net_idx || !key->updated) { + continue; + } + + memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0])); + key->updated = false; + } +} + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key) +{ + if (new_kr != sub->kr_flag && sub->kr_phase == BT_MESH_KR_NORMAL) { + BT_WARN("KR change in normal operation. Are we blacklisted?"); + return false; + } + + sub->kr_flag = new_kr; + + if (sub->kr_flag) { + if (sub->kr_phase == BT_MESH_KR_PHASE_1) { + BT_DBG("Phase 1 -> Phase 2"); + sub->kr_phase = BT_MESH_KR_PHASE_2; + return true; + } + } else { + switch (sub->kr_phase) { + case BT_MESH_KR_PHASE_1: + if (!new_key) { + /* Ignore */ + break; + } + /* Upon receiving a Secure Network beacon with the KR flag set + * to 0 using the new NetKey in Phase 1, the node shall + * immediately transition to Phase 3, which effectively skips + * Phase 2. + * + */ + /* falls through */ + case BT_MESH_KR_PHASE_2: + BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase); + bt_mesh_net_revoke_keys(sub); + if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) || + (MYNEWT_VAL(BLE_MESH_FRIEND))) { + friend_cred_refresh(sub->net_idx); + } + sub->kr_phase = BT_MESH_KR_NORMAL; + return true; + } + } + + return false; +} + +void bt_mesh_rpl_reset(void) +{ + int i; + + /* Discard "old old" IV Index entries from RPL and flag + * any other ones (which are valid) as old. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->src) { + if (rpl->old_iv) { + memset(rpl, 0, sizeof(*rpl)); + } else { + rpl->old_iv = true; + } + } + } +} + +#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST) +void bt_mesh_iv_update_test(bool enable) +{ + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_TEST, enable); + /* Reset the duration variable - needed for some PTS tests */ + bt_mesh.ivu_duration = 0; +} + +bool bt_mesh_iv_update(void) +{ + if (!bt_mesh_is_provisioned()) { + BT_ERR("Not yet provisioned"); + return false; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else { + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + } + + bt_mesh_net_sec_update(NULL); + + return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); +} +#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */ + +/* Used for sending immediate beacons to Friend queues and GATT clients */ +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub) +{ + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_sec_update(sub ? sub->net_idx : BT_MESH_KEY_ANY); + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + bt_mesh_proxy_beacon_send(sub); + } +} + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update) +{ + int i; + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + /* We're currently in IV Update mode */ + + if (iv_index != bt_mesh.iv_index) { + BT_WARN("IV Index mismatch: 0x%08x != 0x%08x", + (unsigned) iv_index, + (unsigned) bt_mesh.iv_index); + return false; + } + + if (iv_update) { + /* Nothing to do */ + BT_DBG("Already in IV Update in Progress state"); + return false; + } + } else { + /* We're currently in Normal mode */ + + if (iv_index == bt_mesh.iv_index) { + BT_DBG("Same IV Index in normal mode"); + return false; + } + + if (iv_index < bt_mesh.iv_index || + iv_index > bt_mesh.iv_index + 42) { + BT_ERR("IV Index out of sync: 0x%08x != 0x%08x", + (unsigned) iv_index, + (unsigned) bt_mesh.iv_index); + return false; + } + + if (iv_index > bt_mesh.iv_index + 1) { + BT_WARN("Performing IV Index Recovery"); + memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl)); + bt_mesh.iv_index = iv_index; + bt_mesh.seq = 0; + goto do_update; + } + + if (iv_index == bt_mesh.iv_index + 1 && !iv_update) { + BT_WARN("Ignoring new index in normal mode"); + return false; + } + + if (!iv_update) { + /* Nothing to do */ + BT_DBG("Already in Normal state"); + return false; + } + } + + if (!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) && + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_TEST))) { + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + BT_WARN("IV Update before minimum duration"); + return false; + } + } + + /* Defer change to Normal Operation if there are pending acks */ + if (!iv_update && bt_mesh_tx_in_progress()) { + BT_WARN("IV Update deferred because of pending transfer"); + atomic_set_bit(bt_mesh.flags, BT_MESH_IVU_PENDING); + return false; + } + +do_update: + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update); + bt_mesh.ivu_duration = 0U; + + if (iv_update) { + bt_mesh.iv_index = iv_index; + BT_DBG("IV Update state entered. New index 0x%08x", + (unsigned) bt_mesh.iv_index); + + bt_mesh_rpl_reset(); + } else { + BT_DBG("Normal mode entered"); + bt_mesh.seq = 0; + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_net_beacon_update(&bt_mesh.sub[i]); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(false); + } + + return true; +} + +u32_t bt_mesh_next_seq(void) +{ + u32_t seq = bt_mesh.seq++; + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_seq(); + } + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) && + bt_mesh.seq > IV_UPDATE_SEQ_LIMIT && + bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) { + bt_mesh_beacon_ivu_initiator(true); + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + bt_mesh_net_sec_update(NULL); + } + + return seq; +} + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + const u8_t *enc, *priv; + u32_t seq; + u16_t dst; + int err; + + BT_DBG("net_idx 0x%04x new_key %u len %u", sub->net_idx, new_key, + buf->om_len); + + enc = sub->keys[new_key].enc; + priv = sub->keys[new_key].privacy; + + err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("deobfuscate failed (err %d)", err); + return err; + } + + err = bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("decrypt failed (err %d)", err); + return err; + } + + seq = bt_mesh_next_seq(); + buf->om_data[2] = seq >> 16; + buf->om_data[3] = seq >> 8; + buf->om_data[4] = seq; + + /* Get destination, in case it's a proxy client */ + dst = DST(buf->om_data); + + err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("encrypt failed (err %d)", err); + return err; + } + + err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("obfuscate failed (err %d)", err); + return err; + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_proxy_relay(buf, dst)) { + send_cb_finalize(cb, cb_data); + } else { + bt_mesh_adv_send(buf, cb, cb_data); + } + + return 0; +} + +static void bt_mesh_net_local(struct ble_npl_event *work) +{ + struct os_mbuf *buf; + + while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) { + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + bt_mesh_net_recv(buf, 0, BT_MESH_NET_IF_LOCAL); + net_buf_unref(buf); + } +} + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + bool proxy) +{ + const bool ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); + u32_t seq_val; + u8_t nid; + const u8_t *enc, *priv; + u8_t *seq; + int err; + + if (ctl && net_buf_simple_tailroom(buf) < 8) { + BT_ERR("Insufficient MIC space for CTL PDU"); + return -EINVAL; + } else if (net_buf_simple_tailroom(buf) < 4) { + BT_ERR("Insufficient MIC space for PDU"); + return -EINVAL; + } + + BT_DBG("src 0x%04x dst 0x%04x ctl %u seq 0x%06x", + tx->src, tx->ctx->addr, ctl, bt_mesh.seq); + + net_buf_simple_push_be16(buf, tx->ctx->addr); + net_buf_simple_push_be16(buf, tx->src); + + seq = net_buf_simple_push(buf, 3); + seq_val = bt_mesh_next_seq(); + seq[0] = seq_val >> 16; + seq[1] = seq_val >> 8; + seq[2] = seq_val; + + if (ctl) { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl | 0x80); + } else { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && tx->friend_cred) { + if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, + &nid, &enc, &priv)) { + BT_WARN("Falling back to master credentials"); + + tx->friend_cred = 0; + + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + } else { + tx->friend_cred = 0; + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + + net_buf_simple_push_u8(buf, (nid | (BT_MESH_NET_IVI_TX & 1) << 7)); + + err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy); + if (err) { + return err; + } + + return bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv); +} + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + int err; + + BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu", + tx->src, tx->ctx->addr, buf->om_len, net_buf_headroom(buf), + net_buf_tailroom(buf)); + BT_DBG("Payload len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + BT_DBG("Seq 0x%06x", bt_mesh.seq); + + if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) { + tx->ctx->send_ttl = bt_mesh_default_ttl_get(); + } + + err = bt_mesh_net_encode(tx, buf, false); + if (err) { + goto done; + } + + BT_DBG("encoded %u bytes: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + /* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2: + * "The output filter of the interface connected to advertising or + * GATT bearers shall drop all messages with TTL value set to 1." + */ + if (MYNEWT_VAL(BLE_MESH_GATT_PROXY) && + tx->ctx->send_ttl != 1) { + if (bt_mesh_proxy_relay(buf, tx->ctx->addr) && + BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* Notify completion if this only went + * through the Mesh Proxy. + */ + send_cb_finalize(cb, cb_data); + + err = 0; + goto done; + } + } + + /* Deliver to local network interface if necessary */ + if (bt_mesh_fixed_group_match(tx->ctx->addr) || + bt_mesh_elem_find(tx->ctx->addr)) { + if (cb && cb->start) { + cb->start(0, 0, cb_data); + } + net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf)); + if (cb && cb->end) { + cb->end(0, cb_data); + } + k_work_submit(&bt_mesh.local_work); + } else if (tx->ctx->send_ttl != 1) { + /* Deliver to to the advertising network interface. Mesh spec + * 3.4.5.2: "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + bt_mesh_adv_send(buf, cb, cb_data); + } + +done: + net_buf_unref(buf); + return err; +} + +static bool auth_match(struct bt_mesh_subnet_keys *keys, + const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8]) +{ + u8_t net_auth[8]; + + if (memcmp(net_id, keys->net_id, 8)) { + return false; + } + + bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index, + net_auth); + + if (memcmp(auth, net_auth, 8)) { + BT_WARN("Authentication Value %s != %s", + bt_hex(auth, 8), bt_hex(net_auth, 8)); + return false; + } + + return true; +} + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) { + *new_key = false; + return sub; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) { + *new_key = true; + return sub; + } + } + + return NULL; +} + +static int net_decrypt(struct bt_mesh_subnet *sub, const u8_t *enc, + const u8_t *priv, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data), + (unsigned) bt_mesh.iv_index); + + rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01)); + + net_buf_simple_init(buf, 0); + memcpy(net_buf_simple_add(buf, data_len), data, data_len); + + if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) { + return -ENOENT; + } + + if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(rx, buf)) { + BT_WARN("Duplicate found in Network Message Cache"); + return -EALREADY; + } + + rx->ctx.addr = SRC(buf->om_data); + if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) { + BT_WARN("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr); + return -EINVAL; + } + + BT_DBG("src 0x%04x", rx->ctx.addr); + + if ((MYNEWT_VAL(BLE_MESH_PROXY)) && + rx->net_if == BT_MESH_NET_IF_PROXY_CFG) { + return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), + true); + } + + return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false); +} + +static int friend_decrypt(struct bt_mesh_subnet *sub, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + int i; + + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (NID(data) == cred->cred[0].nid && + !net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy, + data, data_len, rx, buf)) { + return 0; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == cred->cred[1].nid && + !net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1; + return 0; + } + } + + return -ENOENT; +} + +static bool net_find_and_decrypt(const u8_t *data, size_t data_len, + struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_subnet *sub; + unsigned int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + sub = &bt_mesh.sub[i]; + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if ((IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) || + IS_ENABLED(CONFIG_BT_MESH_FRIEND)) && + !friend_decrypt(sub, data, data_len, rx, buf)) { + rx->friend_cred = 1U; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + + if (NID(data) == sub->keys[0].nid && + !net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy, + data, data_len, rx, buf)) { + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + + if (sub->kr_phase == BT_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == sub->keys[1].nid && + !net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + } + + return false; +} + +/* Relaying from advertising to the advertising bearer should only happen + * if the Relay state is set to enabled. Locally originated packets always + * get sent to the advertising bearer. If the packet came in through GATT, + * then we should only relay it if the GATT Proxy state is enabled. + */ +static bool relay_to_adv(enum bt_mesh_net_if net_if) +{ + switch (net_if) { + case BT_MESH_NET_IF_LOCAL: + return true; + case BT_MESH_NET_IF_ADV: + return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); + case BT_MESH_NET_IF_PROXY: + return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); + default: + return false; + } +} + +static void bt_mesh_net_relay(struct os_mbuf *sbuf, + struct bt_mesh_net_rx *rx) +{ + const u8_t *enc, *priv; + struct os_mbuf *buf; + u8_t nid, transmit; + + if (rx->net_if == BT_MESH_NET_IF_LOCAL) { + /* Locally originated PDUs with TTL=1 will only be delivered + * to local elements as per Mesh Profile 1.0 section 3.4.5.2: + * "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + if (rx->ctx.recv_ttl == 1) { + return; + } + } else { + if (rx->ctx.recv_ttl <= 1) { + return; + } + } + + if (rx->net_if == BT_MESH_NET_IF_ADV && + bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED && + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) { + return; + } + + BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl, + rx->ctx.recv_dst); + + /* The Relay Retransmit state is only applied to adv-adv relaying. + * Anything else (like GATT to adv, or locally originated packets) + * use the Network Transmit state. + */ + if (rx->net_if == BT_MESH_NET_IF_ADV) { + transmit = bt_mesh_relay_retransmit_get(); + } else { + transmit = bt_mesh_net_transmit_get(); + } + + buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, transmit, K_NO_WAIT); + if (!buf) { + BT_ERR("Out of relay buffers"); + return; + } + + /* Only decrement TTL for non-locally originated packets */ + if (rx->net_if != BT_MESH_NET_IF_LOCAL) { + /* Leave CTL bit intact */ + sbuf->om_data[1] &= 0x80; + sbuf->om_data[1] |= rx->ctx.recv_ttl - 1; + } + + net_buf_add_mem(buf, sbuf->om_data, sbuf->om_len); + + enc = rx->sub->keys[rx->sub->kr_flag].enc; + priv = rx->sub->keys[rx->sub->kr_flag].privacy; + nid = rx->sub->keys[rx->sub->kr_flag].nid; + + BT_DBG("Relaying packet. TTL is now %u", TTL(buf->om_data)); + + /* Update NID if RX or RX was with friend credentials */ + if (rx->friend_cred) { + buf->om_data[0] &= 0x80; /* Clear everything except IVI */ + buf->om_data[0] |= nid; + } + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false)) { + BT_ERR("Re-encrypting failed"); + goto done; + } + + if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) { + BT_ERR("Re-obfuscating failed"); + goto done; + } + + BT_DBG("encoded %u bytes: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + /* Sending to the GATT bearer should only happen if GATT Proxy + * is enabled or the message originates from the local node. + */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || + rx->net_if == BT_MESH_NET_IF_LOCAL)) { + if (bt_mesh_proxy_relay(buf, rx->ctx.recv_dst) && + BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { + goto done; + } + } + + if (relay_to_adv(rx->net_if)) { + bt_mesh_adv_send(buf, NULL, NULL); + } + +done: + net_buf_unref(buf); +} + +void bt_mesh_net_header_parse(struct os_mbuf *buf, + struct bt_mesh_net_rx *rx) +{ + rx->old_iv = (IVI(buf->om_data) != (bt_mesh.iv_index & 0x01)); + rx->ctl = CTL(buf->om_data); + rx->ctx.recv_ttl = TTL(buf->om_data); + rx->seq = SEQ(buf->om_data); + rx->ctx.addr = SRC(buf->om_data); + rx->ctx.recv_dst = DST(buf->om_data); +} + +int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct os_mbuf *buf) +{ + if (data->om_len < BT_MESH_NET_MIN_PDU_LEN) { + BT_WARN("Dropping too short mesh packet (len %u)", data->om_len); + BT_WARN("%s", bt_hex(data->om_data, data->om_len)); + return -EINVAL; + } + + if (net_if == BT_MESH_NET_IF_ADV && check_dup(data)) { + BT_DBG("duplicate packet; dropping %u bytes: %s", data->om_len, + bt_hex(data->om_data, data->om_len)); + return -EINVAL; + } + + BT_DBG("%u bytes: %s", data->om_len, bt_hex(data->om_data, data->om_len)); + + rx->net_if = net_if; + + if (!net_find_and_decrypt(data->om_data, data->om_len, rx, buf)) { + BT_DBG("Unable to find matching net for packet"); + return -ENOENT; + } + + /* Initialize AppIdx to a sane value */ + rx->ctx.app_idx = BT_MESH_KEY_UNUSED; + + rx->ctx.recv_ttl = TTL(buf->om_data); + + /* Default to responding with TTL 0 for non-routed messages */ + if (rx->ctx.recv_ttl == 0) { + rx->ctx.send_ttl = 0; + } else { + rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT; + } + + rx->ctl = CTL(buf->om_data); + rx->seq = SEQ(buf->om_data); + rx->ctx.recv_dst = DST(buf->om_data); + + BT_DBG("Decryption successful. Payload len %u: %s", buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + if (net_if != BT_MESH_NET_IF_PROXY_CFG && + rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) { + BT_ERR("Destination address is unassigned; dropping packet"); + return -EBADMSG; + } + + if (BT_MESH_ADDR_IS_RFU(rx->ctx.recv_dst)) { + BT_ERR("Destination address is RFU; dropping packet"); + return -EBADMSG; + } + + if (net_if != BT_MESH_NET_IF_LOCAL && bt_mesh_elem_find(rx->ctx.addr)) { + BT_DBG("Dropping locally originated packet"); + return -EBADMSG; + } + + BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst, + rx->ctx.recv_ttl); + BT_DBG("PDU: %s", bt_hex(buf->om_data, buf->om_len)); + + return 0; +} + +void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi, + enum bt_mesh_net_if net_if) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(29); + struct bt_mesh_net_rx rx = { .ctx.recv_rssi = rssi }; + struct net_buf_simple_state state; + + BT_DBG("rssi %d net_if %u", rssi, net_if); + + if (!bt_mesh_is_provisioned()) { + BT_ERR("Not provisioned; dropping packet"); + goto done; + } + + if (bt_mesh_net_decode(data, net_if, &rx, buf)) { + goto done; + } + + /* Save the state so the buffer can later be relayed */ + net_buf_simple_save(buf, &state); + + rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) || + bt_mesh_elem_find(rx.ctx.recv_dst)); + + if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY)) && + net_if == BT_MESH_NET_IF_PROXY) { + bt_mesh_proxy_addr_add(data, rx.ctx.addr); + + if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_DISABLED && + !rx.local_match) { + BT_INFO("Proxy is disabled; ignoring message"); + goto done; + } + } + + /* The transport layer has indicated that it has rejected the message, + * but would like to see it again if it is received in the future. + * This can happen if a message is received when the device is in + * Low Power mode, but the message was not encrypted with the friend + * credentials. Remove it from the message cache so that we accept + * it again in the future. + */ + if (bt_mesh_trans_recv(buf, &rx) == -EAGAIN) { + BT_WARN("Removing rejected message from Network Message Cache"); + msg_cache[rx.msg_cache_idx] = 0ULL; + /* Rewind the next index now that we're not using this entry */ + msg_cache_next = rx.msg_cache_idx; + } + + /* Relay if this was a group/virtual address, or if the destination + * was neither a local element nor an LPN we're Friends for. + */ + if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) || + (!rx.local_match && !rx.friend_match)) { + net_buf_simple_restore(buf, &state); + bt_mesh_net_relay(buf, &rx); + } + +done: + os_mbuf_free_chain(buf); +} + +static void ivu_refresh(struct ble_npl_event *work) +{ + bt_mesh.ivu_duration += BT_MESH_IVU_HOURS; + + BT_DBG("%s for %u hour%s", + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ? + "IVU in Progress" : "IVU Normal mode", + bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1 ? "" : "s"); + + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(true); + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + return; + } + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) { + bt_mesh_beacon_ivu_initiator(true); + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_iv(true); + } +} + +void bt_mesh_net_start(void) +{ + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED) { + bt_mesh_proxy_gatt_enable(); + bt_mesh_adv_update(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { + bt_mesh_lpn_init(); + } else { + bt_mesh_scan_enable(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { + bt_mesh_friend_init(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PROV)) { + u16_t net_idx = bt_mesh.sub[0].net_idx; + u16_t addr = bt_mesh_primary_addr(); + + bt_mesh_prov_complete(net_idx, addr); + } +} + +void bt_mesh_net_init(void) +{ + k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh); + + k_work_init(&bt_mesh.local_work, bt_mesh_net_local); + net_buf_slist_init(&bt_mesh.local_queue); +} +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.h new file mode 100644 index 0000000..1a7e3aa --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/net.h @@ -0,0 +1,412 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_H__ +#define __NET_H__ + +#define BT_MESH_NET_FLAG_KR BIT(0) +#define BT_MESH_NET_FLAG_IVU BIT(1) + +#define BT_MESH_KR_NORMAL 0x00 +#define BT_MESH_KR_PHASE_1 0x01 +#define BT_MESH_KR_PHASE_2 0x02 +#define BT_MESH_KR_PHASE_3 0x03 + +#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01) +#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01) + +#include +#include "atomic.h" +#include "../include/mesh/mesh.h" +#include "../include/mesh/glue.h" + +/* How many hours in between updating IVU duration */ +#define BT_MESH_IVU_MIN_HOURS 96 +#define BT_MESH_IVU_HOURS (BT_MESH_IVU_MIN_HOURS / \ + CONFIG_BT_MESH_IVU_DIVIDER) +#define BT_MESH_IVU_TIMEOUT K_HOURS(BT_MESH_IVU_HOURS) + +struct bt_mesh_app_key { + u16_t net_idx; + u16_t app_idx; + bool updated; + struct bt_mesh_app_keys { + u8_t id; + u8_t val[16]; + } keys[2]; +}; + +struct bt_mesh_node { + u16_t addr; + u16_t net_idx; + u8_t dev_key[16]; + u8_t num_elem; +}; + +struct bt_mesh_subnet { + u32_t beacon_sent; /* Timestamp of last sent beacon */ + u8_t beacons_last; /* Number of beacons during last + * observation window + */ + u8_t beacons_cur; /* Number of beaconds observed during + * currently ongoing window. + */ + + u8_t beacon_cache[21]; /* Cached last authenticated beacon */ + + u16_t net_idx; /* NetKeyIndex */ + + bool kr_flag; /* Key Refresh Flag */ + u8_t kr_phase; /* Key Refresh Phase */ + + u8_t node_id; /* Node Identity State */ + u32_t node_id_start; /* Node Identity started timestamp */ + + u8_t auth[8]; /* Beacon Authentication Value */ + + struct bt_mesh_subnet_keys { + u8_t net[16]; /* NetKey */ + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t net_id[8]; /* Network ID */ +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + u8_t identity[16]; /* IdentityKey */ +#endif + u8_t privacy[16]; /* PrivacyKey */ + u8_t beacon[16]; /* BeaconKey */ + } keys[2]; +}; + +struct bt_mesh_rpl { + u16_t src; + bool old_iv; +#if (MYNEWT_VAL(BLE_MESH_SETTINGS)) + bool store; +#endif + u32_t seq; +}; + +#if MYNEWT_VAL(BLE_MESH_FRIEND) +#define FRIEND_SEG_RX MYNEWT_VAL(BLE_MESH_FRIEND_SEG_RX) +#define FRIEND_SUB_LIST_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_SUB_LIST_SIZE) +#else +#define FRIEND_SEG_RX 0 +#define FRIEND_SUB_LIST_SIZE 0 +#endif + +struct bt_mesh_friend { + u16_t lpn; + u8_t recv_delay; + u8_t fsn:1, + send_last:1, + pending_req:1, + sec_update:1, + pending_buf:1, + valid:1, + established:1; + s32_t poll_to; + u8_t num_elem; + u16_t lpn_counter; + u16_t counter; + + u16_t net_idx; + + u16_t sub_list[FRIEND_SUB_LIST_SIZE]; + + struct k_delayed_work timer; + + struct bt_mesh_friend_seg { + struct net_buf_slist_t queue; + + /* The target number of segments, i.e. not necessarily + * the current number of segments, in the queue. This is + * used for Friend Queue free space calculations. + */ + u8_t seg_count; + } seg[FRIEND_SEG_RX]; + + struct os_mbuf *last; + + struct net_buf_slist_t queue; + u32_t queue_size; + + /* Friend Clear Procedure */ + struct { + u32_t start; /* Clear Procedure start */ + u16_t frnd; /* Previous Friend's address */ + u16_t repeat_sec; /* Repeat timeout in seconds */ + struct k_delayed_work timer; /* Repeat timer */ + } clear; +}; + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) +#define LPN_GROUPS CONFIG_BT_MESH_LPN_GROUPS +#else +#define LPN_GROUPS 0 +#endif + +/* Low Power Node state */ +struct bt_mesh_lpn { + enum __packed { + BT_MESH_LPN_DISABLED, /* LPN feature is disabled */ + BT_MESH_LPN_CLEAR, /* Clear in progress */ + BT_MESH_LPN_TIMER, /* Waiting for auto timer expiry */ + BT_MESH_LPN_ENABLED, /* LPN enabled, but no Friend */ + BT_MESH_LPN_REQ_WAIT, /* Wait before scanning for offers */ + BT_MESH_LPN_WAIT_OFFER, /* Friend Req sent */ + BT_MESH_LPN_ESTABLISHED, /* Friendship established */ + BT_MESH_LPN_RECV_DELAY, /* Poll sent, waiting ReceiveDelay */ + BT_MESH_LPN_WAIT_UPDATE, /* Waiting for Update or message */ + } state; + + /* Transaction Number (used for subscription list) */ + u8_t xact_next; + u8_t xact_pending; + u8_t sent_req; + + /* Address of our Friend when we're a LPN. Unassigned if we don't + * have a friend yet. + */ + u16_t frnd; + + /* Value from the friend offer */ + u8_t recv_win; + + u8_t req_attempts; /* Number of Request attempts */ + + s32_t poll_timeout; + + u8_t groups_changed:1, /* Friend Subscription List needs updating */ + pending_poll:1, /* Poll to be sent after subscription */ + disable:1, /* Disable LPN after clearing */ + fsn:1, /* Friend Sequence Number */ + established:1, /* Friendship established */ + clear_success:1; /* Friend Clear Confirm received */ + + /* Friend Queue Size */ + u8_t queue_size; + + /* LPNCounter */ + u16_t counter; + + /* Previous Friend of this LPN */ + u16_t old_friend; + + /* Duration reported for last advertising packet */ + u16_t adv_duration; + + /* Next LPN related action timer */ + struct k_delayed_work timer; + + /* Subscribed groups */ + u16_t groups[LPN_GROUPS]; + + /* Bit fields for tracking which groups the Friend knows about */ + ATOMIC_DEFINE(added, LPN_GROUPS); + ATOMIC_DEFINE(pending, LPN_GROUPS); + ATOMIC_DEFINE(to_remove, LPN_GROUPS); +}; + +/* bt_mesh_net.flags */ +enum { + BT_MESH_VALID, /* We have been provisioned */ + BT_MESH_SUSPENDED, /* Network is temporarily suspended */ + BT_MESH_IVU_IN_PROGRESS, /* IV Update in Progress */ + BT_MESH_IVU_INITIATOR, /* IV Update initiated by us */ + BT_MESH_IVU_TEST, /* IV Update test mode */ + BT_MESH_IVU_PENDING, /* Update blocked by SDU in progress */ + + /* pending storage actions, must reside within first 32 flags */ + BT_MESH_RPL_PENDING, + BT_MESH_KEYS_PENDING, + BT_MESH_NET_PENDING, + BT_MESH_IV_PENDING, + BT_MESH_SEQ_PENDING, + BT_MESH_HB_PUB_PENDING, + BT_MESH_CFG_PENDING, + BT_MESH_MOD_PENDING, + BT_MESH_VA_PENDING, + BT_MESH_NODES_PENDING, + + /* Don't touch - intentionally last */ + BT_MESH_FLAG_COUNT, +}; + +struct bt_mesh_net { + u32_t iv_index; /* Current IV Index */ + u32_t seq; /* Next outgoing sequence number (24 bits) */ + + ATOMIC_DEFINE(flags, BT_MESH_FLAG_COUNT); + + /* Local network interface */ + struct ble_npl_callout local_work; + struct net_buf_slist_t local_queue; + +#if MYNEWT_VAL(BLE_MESH_FRIEND) + /* Friend state, unique for each LPN that we're Friends for */ + struct bt_mesh_friend frnd[MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)]; +#endif + +#if (MYNEWT_VAL(BLE_MESH_LOW_POWER)) + struct bt_mesh_lpn lpn; /* Low Power Node state */ +#endif + + /* Number of hours in current IV Update state */ + u8_t ivu_duration; + + /* Timer to track duration in current IV Update state */ + struct k_delayed_work ivu_timer; + + u8_t dev_key[16]; + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) + struct bt_mesh_node nodes[MYNEWT_VAL(BLE_MESH_NODE_COUNT)]; +#endif + + struct bt_mesh_app_key app_keys[MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT)]; + + struct bt_mesh_subnet sub[MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)]; + + struct bt_mesh_rpl rpl[MYNEWT_VAL(BLE_MESH_CRPL)]; +}; + +/* Network interface */ +enum bt_mesh_net_if { + BT_MESH_NET_IF_ADV, + BT_MESH_NET_IF_LOCAL, + BT_MESH_NET_IF_PROXY, + BT_MESH_NET_IF_PROXY_CFG, +}; + +/* Decoding context for Network/Transport data */ +struct bt_mesh_net_rx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx ctx; + u32_t seq; /* Sequence Number */ + u8_t old_iv:1, /* iv_index - 1 was used */ + new_key:1, /* Data was encrypted with updated key */ + friend_cred:1, /* Data was encrypted with friend cred */ + ctl:1, /* Network Control */ + net_if:2, /* Network interface */ + local_match:1, /* Matched a local element */ + friend_match:1; /* Matched an LPN we're friends for */ + u16_t msg_cache_idx; /* Index of entry in message cache */ +}; + +/* Encoding context for Network/Transport data */ +struct bt_mesh_net_tx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx *ctx; + u16_t src; + u8_t xmit; + u8_t friend_cred:1, + aszmic:1, + aid:6; +}; + +extern struct bt_mesh_net bt_mesh; + +#define BT_MESH_NET_IVI_TX (bt_mesh.iv_index - \ + atomic_test_bit(bt_mesh.flags, \ + BT_MESH_IVU_IN_PROGRESS)) +#define BT_MESH_NET_IVI_RX(rx) (bt_mesh.iv_index - (rx)->old_iv) + +#define BT_MESH_NET_HDR_LEN 9 + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]); + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index); + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub); + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key); + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub); + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub); + +void bt_mesh_rpl_reset(void); + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update); + +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub); + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx); + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key); + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + bool proxy); + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data); + +int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct os_mbuf *buf); + +void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi, + enum bt_mesh_net_if net_if); + +u32_t bt_mesh_next_seq(void); + +void bt_mesh_net_start(void); + +void bt_mesh_net_init(void); +void bt_mesh_net_header_parse(struct os_mbuf *buf, + struct bt_mesh_net_rx *rx); + +/* Friendship Credential Management */ +struct friend_cred { + u16_t net_idx; + u16_t addr; + + u16_t lpn_counter; + u16_t frnd_counter; + + struct { + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t privacy[16]; /* PrivacyKey */ + } cred[2]; +}; + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv); +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]); +void friend_cred_refresh(u16_t net_idx); +int friend_cred_update(struct bt_mesh_subnet *sub); +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter); +void friend_cred_clear(struct friend_cred *cred); +int friend_cred_del(u16_t net_idx, u16_t addr); + +static inline void send_cb_finalize(const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (!cb) { + return; + } + + if (cb->start) { + cb->start(0, 0, cb_data); + } + + if (cb->end) { + cb->end(0, cb_data); + } +} + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.c new file mode 100644 index 0000000..de41f40 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_PROV_LOG + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) + +#include "../include/mesh/mesh.h" + +#include "mesh_priv.h" +#include "net.h" +#include "access.h" +#include "settings.h" + +/* + * Check if an address range from addr_start for addr_start + num_elem - 1 is + * free for use. When a conflict is found, next will be set to the next address + * available after the conflicting range and -EAGAIN will be returned. + */ +static int addr_is_free(u16_t addr_start, u8_t num_elem, u16_t *next) +{ + const struct bt_mesh_comp *comp = bt_mesh_comp_get(); + u16_t addr_end = addr_start + num_elem - 1; + u16_t other_start, other_end; + int i; + + if (comp == NULL) { + return -EINVAL; + } + + if (!BT_MESH_ADDR_IS_UNICAST(addr_start) || + !BT_MESH_ADDR_IS_UNICAST(addr_end) || + num_elem == 0 || next == NULL) { + return -EINVAL; + } + + other_start = bt_mesh_primary_addr(); + other_end = other_start + comp->elem_count - 1; + + /* Compare with local element addresses */ + if (!(addr_end < other_start || addr_start > other_end)) { + *next = other_end + 1; + return -EAGAIN; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (node->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + other_start = node->addr; + other_end = other_start + node->num_elem - 1; + + if (!(addr_end < other_start || addr_start > other_end)) { + *next = other_end + 1; + return -EAGAIN; + } + } + + return 0; +} + +/* + * Find the lowest possible starting address that can fit num_elem elements. If + * a free address range cannot be found, BT_MESH_ADDR_UNASSIGNED will be + * returned. Otherwise the first address in the range is returned. + * + * NOTE: This is quite an ineffective algorithm as it might need to look + * through the array of nodes N+2 times. A more effective algorithm + * could be used if the nodes were stored in a sorted list. + */ +static u16_t find_lowest_free_addr(u8_t num_elem) +{ + u16_t addr = 1, next; + int err, i; + + /* + * It takes a maximum of node count + 2 to find a free address if there + * is any. +1 for our own address and +1 for making sure that the + * address range is valid. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes) + 2; ++i) { + err = addr_is_free(addr, num_elem, &next); + if (err == 0) { + break; + } else if (err != -EAGAIN) { + addr = BT_MESH_ADDR_UNASSIGNED; + break; + } + + addr = next; + } + + return addr; +} + +struct bt_mesh_node *bt_mesh_node_find(u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (addr >= node->addr && + addr <= node->addr + node->num_elem - 1) { + return node; + } + } + + return NULL; +} + +struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem, + u16_t net_idx) +{ + int i; + + BT_DBG(""); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + addr = find_lowest_free_addr(num_elem); + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return NULL; + } + } else if (!addr_is_free(addr, num_elem, NULL)) { + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) { + struct bt_mesh_node *node = &bt_mesh.nodes[i]; + + if (node->addr == BT_MESH_ADDR_UNASSIGNED) { + node->addr = addr; + node->num_elem = num_elem; + node->net_idx = net_idx; + return node; + } + } + + return NULL; +} + +void bt_mesh_node_del(struct bt_mesh_node *node, bool store) +{ + BT_DBG("Node addr 0x%04x store %u", node->addr, store); + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + bt_mesh_clear_node(node); + } + + node->addr = BT_MESH_ADDR_UNASSIGNED; + (void)memset(node->dev_key, 0, sizeof(node->dev_key)); +} + +#endif +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.h new file mode 100644 index 0000000..f86193d --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/nodes.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +struct bt_mesh_node *bt_mesh_node_find(u16_t addr); +struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem, + u16_t net_idx); +void bt_mesh_node_del(struct bt_mesh_node *node, bool store); \ No newline at end of file diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.c new file mode 100644 index 0000000..f8f22fa --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.c @@ -0,0 +1,2011 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_PROV_LOG + +#if MYNEWT_VAL(BLE_MESH_PROV) + +#include + +#include "../include/mesh/mesh.h" +#include "mesh_priv.h" + +#include "crypto.h" +#include "atomic.h" +#include "adv.h" +#include "net.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "prov.h" +#include "testing.h" +#include "settings.h" +#include "nodes.h" + +/* 3 transmissions, 20ms interval */ +#define PROV_XMIT BT_MESH_TRANSMIT(2, 20) + +#define AUTH_METHOD_NO_OOB 0x00 +#define AUTH_METHOD_STATIC 0x01 +#define AUTH_METHOD_OUTPUT 0x02 +#define AUTH_METHOD_INPUT 0x03 + +#define OUTPUT_OOB_BLINK 0x00 +#define OUTPUT_OOB_BEEP 0x01 +#define OUTPUT_OOB_VIBRATE 0x02 +#define OUTPUT_OOB_NUMBER 0x03 +#define OUTPUT_OOB_STRING 0x04 + +#define INPUT_OOB_PUSH 0x00 +#define INPUT_OOB_TWIST 0x01 +#define INPUT_OOB_NUMBER 0x02 +#define INPUT_OOB_STRING 0x03 + +#define PUB_KEY_NO_OOB 0x00 +#define PUB_KEY_OOB 0x01 + +#define PROV_ERR_NONE 0x00 +#define PROV_ERR_NVAL_PDU 0x01 +#define PROV_ERR_NVAL_FMT 0x02 +#define PROV_ERR_UNEXP_PDU 0x03 +#define PROV_ERR_CFM_FAILED 0x04 +#define PROV_ERR_RESOURCES 0x05 +#define PROV_ERR_DECRYPT 0x06 +#define PROV_ERR_UNEXP_ERR 0x07 +#define PROV_ERR_ADDR 0x08 + +#define PROV_INVITE 0x00 +#define PROV_CAPABILITIES 0x01 +#define PROV_START 0x02 +#define PROV_PUB_KEY 0x03 +#define PROV_INPUT_COMPLETE 0x04 +#define PROV_CONFIRM 0x05 +#define PROV_RANDOM 0x06 +#define PROV_DATA 0x07 +#define PROV_COMPLETE 0x08 +#define PROV_FAILED 0x09 + +#define PROV_NO_PDU 0xff + +#define PROV_ALG_P256 0x00 + +#define GPCF(gpc) (gpc & 0x03) +#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) +#define GPC_ACK 0x01 +#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) +#define GPC_CTL(op) (((op) << 2) | 0x03) + +#define START_PAYLOAD_MAX 20 +#define CONT_PAYLOAD_MAX 23 + +#define START_LAST_SEG(gpc) (gpc >> 2) +#define CONT_SEG_INDEX(gpc) (gpc >> 2) + +#define BEARER_CTL(gpc) (gpc >> 2) +#define LINK_OPEN 0x00 +#define LINK_ACK 0x01 +#define LINK_CLOSE 0x02 + +#define CLOSE_REASON_SUCCESS 0x00 +#define CLOSE_REASON_TIMEOUT 0x01 +#define CLOSE_REASON_FAILED 0x02 + +#define XACT_SEG_DATA(_seg) (&link.rx.buf->om_data[20 + ((_seg - 1) * 23)]) +#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) + +#define XACT_NVAL 0xff + +enum { + WAIT_PUB_KEY, /* Waiting for local PubKey to be generated */ + LINK_ACTIVE, /* Link has been opened */ + LINK_ACK_RECVD, /* Ack for link has been received */ + LINK_CLOSING, /* Link is closing down */ + SEND_PUB_KEY, /* Waiting to send PubKey */ + WAIT_NUMBER, /* Waiting for number input from user */ + WAIT_STRING, /* Waiting for string input from user */ + NOTIFY_INPUT_COMPLETE, /* Notify that input has been completed. */ + LINK_INVALID, /* Error occurred during provisioning */ + PROVISIONER, /* The link was opened as provisioner */ + + NUM_FLAGS, +}; + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) +#define PROVISIONER_LINK 1 +#else +#define PROVISIONER_LINK 0 +#endif + +struct provisioner_link { + struct bt_mesh_node *node; + u16_t addr; + u16_t net_idx; + u8_t attention_duration; +}; + +struct prov_link { + ATOMIC_DEFINE(flags, NUM_FLAGS); +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + uint16_t conn_handle; /* GATT connection */ +#endif + struct provisioner_link provisioner[PROVISIONER_LINK]; + + u8_t dhkey[32]; /* Calculated DHKey */ + u8_t expect; /* Next expected PDU */ + + u8_t oob_method; + u8_t oob_action; + u8_t oob_size; + + u8_t conf[16]; /* Remote Confirmation */ + u8_t rand[16]; /* Local Random */ + u8_t auth[16]; /* Authentication Value */ + + u8_t conf_salt[16]; /* ConfirmationSalt */ + u8_t conf_key[16]; /* ConfirmationKey */ + u8_t conf_inputs[145]; /* ConfirmationInputs */ + u8_t prov_salt[16]; /* Provisioning Salt */ + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + u32_t id; /* Link ID */ + + struct { + u8_t id; /* Transaction ID */ + u8_t prev_id; /* Previous Transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + struct os_mbuf *buf; + } rx; + + struct { + /* Start timestamp of the transaction */ + s64_t start; + + /* Transaction id*/ + u8_t id; + + /* Pending outgoing buffer(s) */ + struct os_mbuf *buf[3]; + + /* Retransmit timer */ + struct k_delayed_work retransmit; + } tx; +#endif + + struct k_delayed_work prot_timer; +}; + +struct prov_rx { + u32_t link_id; + u8_t xact_id; + u8_t gpc; +}; + +#define RETRANSMIT_TIMEOUT K_MSEC(500) +#define BUF_TIMEOUT K_MSEC(400) +#define CLOSING_TIMEOUT K_SECONDS(3) +#define TRANSACTION_TIMEOUT K_SECONDS(30) +#define PROTOCOL_TIMEOUT K_SECONDS(60) + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +#define PROV_BUF_HEADROOM 5 +#else +#define PROV_BUF_HEADROOM 0 +static struct os_mbuf *rx_buf; +#endif + +#define PROV_BUF(len) NET_BUF_SIMPLE(PROV_BUF_HEADROOM + len) + +static struct prov_link link; + +static const struct bt_mesh_prov *prov; + +static void pub_key_ready(const u8_t *pkey); + +static int reset_state(void) +{ + static struct bt_pub_key_cb pub_key_cb = { + .func = pub_key_ready, + }; + int err; + + k_delayed_work_cancel(&link.prot_timer); + + /* Disable Attention Timer if it was set */ + if (link.conf_inputs[0]) { + bt_mesh_attention(NULL, 0); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + link.provisioner->node != NULL) { + bt_mesh_node_del(link.provisioner->node, false); + } + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + /* Clear everything except the retransmit and protocol timer + * delayed work objects. + */ + (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit)); + link.rx.prev_id = XACT_NVAL; + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.rx.buf = bt_mesh_proxy_get_buf(); +#else + if (!rx_buf) { + rx_buf = NET_BUF_SIMPLE(65); + } + net_buf_simple_init(rx_buf, 0); + link.rx.buf = rx_buf; +#endif /* PB_GATT */ + +#else /* !PB_ADV */ + /* Clear everything except the protocol timer (k_delayed_work) */ + (void)memset(&link, 0, offsetof(struct prov_link, prot_timer)); +#endif /* PB_ADV */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.conn_handle = BLE_HS_CONN_HANDLE_NONE; +#endif + + err = bt_pub_key_gen(&pub_key_cb); + if (err) { + BT_ERR("Failed to generate public key (%d)", err); + return err; + } + + return 0; +} + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +static void buf_sent(int err, void *user_data) +{ + BT_DBG("buf_sent"); + + if (!link.tx.buf[0]) { + return; + } + + k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); +} + +static struct bt_mesh_send_cb buf_sent_cb = { + .end = buf_sent, +}; + +static void free_segments(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + link.tx.buf[i] = NULL; + /* Mark as canceled */ + BT_MESH_ADV(buf)->busy = 0; + net_buf_unref(buf); + } +} + +static void prov_clear_tx(void) +{ + BT_DBG(""); + + k_delayed_work_cancel(&link.tx.retransmit); + + free_segments(); +} + +static void reset_adv_link(void) +{ + prov_clear_tx(); + + if (prov->link_close) { + prov->link_close(BT_MESH_PROV_ADV); + } + + reset_state(); +} + +static struct os_mbuf *adv_buf_create(void) +{ + struct os_mbuf *buf; + + buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); + if (!buf) { + BT_ERR("Out of provisioning buffers"); + assert(0); + return NULL; + } + + return buf; +} + +static u8_t pending_ack = XACT_NVAL; + +static void ack_complete(u16_t duration, int err, void *user_data) +{ + BT_DBG("xact %u complete", (u8_t)pending_ack); + pending_ack = XACT_NVAL; +} + +static void gen_prov_ack_send(u8_t xact_id) +{ + static const struct bt_mesh_send_cb cb = { + .start = ack_complete, + }; + const struct bt_mesh_send_cb *complete; + struct os_mbuf *buf; + + BT_DBG("xact_id %u", xact_id); + + if (pending_ack == xact_id) { + BT_DBG("Not sending duplicate ack"); + return; + } + + buf = adv_buf_create(); + if (!buf) { + return; + } + + if (pending_ack == XACT_NVAL) { + pending_ack = xact_id; + complete = &cb; + } else { + complete = NULL; + } + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_ACK); + + bt_mesh_adv_send(buf, complete, NULL); + net_buf_unref(buf); +} + +static void send_reliable(void) +{ + int i; + + link.tx.start = k_uptime_get(); + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + } +} + +static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len) +{ + struct os_mbuf *buf; + + BT_DBG("op 0x%02x data_len %u", op, data_len); + + prov_clear_tx(); + + buf = adv_buf_create(); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_be32(buf, link.id); + /* Transaction ID, always 0 for Bearer messages */ + net_buf_add_u8(buf, 0x00); + net_buf_add_u8(buf, GPC_CTL(op)); + net_buf_add_mem(buf, data, data_len); + + link.tx.buf[0] = buf; + send_reliable(); + + return 0; +} + +static u8_t last_seg(u8_t len) +{ + if (len <= START_PAYLOAD_MAX) { + return 0; + } + + len -= START_PAYLOAD_MAX; + + return 1 + (len / CONT_PAYLOAD_MAX); +} + +static inline u8_t next_transaction_id(void) +{ + if (atomic_test_bit(link.flags, PROVISIONER)) { + if (link.tx.id != 0x7F) { + link.tx.id++; + } else { + link.tx.id = 0; + } + } else { + if (link.tx.id != 0U && link.tx.id != 0xFF) { + link.tx.id++; + } else { + link.tx.id = 0x80; + } + } + + return link.tx.id; +} + +static int prov_send_adv(struct os_mbuf *msg) +{ + struct os_mbuf *start, *buf; + u8_t seg_len, seg_id; + u8_t xact_id; + + BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len)); + + prov_clear_tx(); + + start = adv_buf_create(); + if (!start) { + return -ENOBUFS; + } + + xact_id = next_transaction_id(); + net_buf_add_be32(start, link.id); + net_buf_add_u8(start, xact_id); + + net_buf_add_u8(start, GPC_START(last_seg(msg->om_len))); + net_buf_add_be16(start, msg->om_len); + net_buf_add_u8(start, bt_mesh_fcs_calc(msg->om_data, msg->om_len)); + + link.tx.buf[0] = start; + + seg_len = min(msg->om_len, START_PAYLOAD_MAX); + BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len)); + net_buf_add_mem(start, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + + buf = start; + for (seg_id = 1; msg->om_len > 0; seg_id++) { + if (seg_id >= ARRAY_SIZE(link.tx.buf)) { + BT_ERR("Too big message"); + free_segments(); + return -E2BIG; + } + + buf = adv_buf_create(); + if (!buf) { + free_segments(); + return -ENOBUFS; + } + + link.tx.buf[seg_id] = buf; + + seg_len = min(msg->om_len, CONT_PAYLOAD_MAX); + + BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, + bt_hex(msg->om_data, seg_len)); + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_CONT(seg_id)); + net_buf_add_mem(buf, msg->om_data, seg_len); + net_buf_simple_pull(msg, seg_len); + } + + send_reliable(); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static int prov_send_gatt(struct os_mbuf *msg) +{ + if (link.conn_handle == BLE_HS_CONN_HANDLE_NONE) { + BT_ERR("No connection handle!?"); + return -ENOTCONN; + } + + return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, msg); +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +static inline int prov_send(struct os_mbuf *buf) +{ + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { + return prov_send_gatt(buf); + } +#endif +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + return prov_send_adv(buf); +#else + return 0; +#endif +} + +static void prov_buf_init(struct os_mbuf *buf, u8_t type) +{ + net_buf_simple_init(buf, PROV_BUF_HEADROOM); + net_buf_simple_add_u8(buf, type); +} + +static void prov_send_fail_msg(u8_t err) +{ + struct os_mbuf *buf = PROV_BUF(2); + + prov_buf_init(buf, PROV_FAILED); + net_buf_simple_add_u8(buf, err); + + if (prov_send(buf)) { + BT_ERR("Failed to send Provisioning Failed message"); + } + + atomic_set_bit(link.flags, LINK_INVALID); + + os_mbuf_free_chain(buf); +} + +static void prov_invite(const u8_t *data) +{ + struct os_mbuf *buf = PROV_BUF(12); + + BT_DBG("Attention Duration: %u seconds", data[0]); + + if (data[0]) { + bt_mesh_attention(NULL, data[0]); + } + + link.conf_inputs[0] = data[0]; + + prov_buf_init(buf, PROV_CAPABILITIES); + + /* Number of Elements supported */ + net_buf_simple_add_u8(buf, bt_mesh_elem_count()); + + /* Supported algorithms - FIPS P-256 Eliptic Curve */ + net_buf_simple_add_be16(buf, BIT(PROV_ALG_P256)); + + /* Public Key Type, Only "No OOB" Public Key is supported*/ + net_buf_simple_add_u8(buf, PUB_KEY_NO_OOB); + + /* Static OOB Type */ + net_buf_simple_add_u8(buf, prov->static_val ? BIT(0) : 0x00); + + /* Output OOB Size */ + net_buf_simple_add_u8(buf, prov->output_size); + + /* Output OOB Action */ + net_buf_simple_add_be16(buf, prov->output_actions); + + /* Input OOB Size */ + net_buf_simple_add_u8(buf, prov->input_size); + + /* Input OOB Action */ + net_buf_simple_add_be16(buf, prov->input_actions); + + memcpy(&link.conf_inputs[1], &buf->om_data[1], 11); + + if (prov_send(buf)) { + BT_ERR("Failed to send capabilities"); + goto done; + } + + link.expect = PROV_START; + +done: + os_mbuf_free_chain(buf); +} + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) +static void send_invite(void) +{ + struct os_mbuf *inv = PROV_BUF(2); + + BT_DBG(""); + + prov_buf_init(inv, PROV_INVITE); + net_buf_simple_add_u8(inv, link.provisioner->attention_duration); + + link.conf_inputs[0] = link.provisioner->attention_duration; + + if (prov_send(inv)) { + BT_ERR("Failed to send invite"); + goto done; + } + + link.expect = PROV_CAPABILITIES; + +done: + os_mbuf_free_chain(inv); +} +#endif + +static void send_start(void) +{ + struct os_mbuf *start = PROV_BUF(6); + + BT_DBG(""); + + prov_buf_init(start, PROV_START); + + net_buf_simple_add_u8(start, PROV_ALG_P256); + net_buf_simple_add_u8(start, PUB_KEY_NO_OOB); + net_buf_simple_add_u8(start, AUTH_METHOD_NO_OOB); + memset(link.auth, 0, sizeof(link.auth)); + + net_buf_simple_add_u8(start, 0); /* Auth Action */ + net_buf_simple_add_u8(start, 0); /* Auth Size */ + + memcpy(&link.conf_inputs[12], &start->om_data[1], 5); + + if (prov_send(start)) { + BT_ERR("Failed to send start"); + } + + os_mbuf_free_chain(start); +} + +static void prov_capabilities(const u8_t *data) +{ + u16_t algorithms, output_action, input_action; + + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return; + } + + BT_DBG("Elements: %u", data[0]); + + algorithms = sys_get_be16(&data[1]); + BT_DBG("Algorithms: %u", algorithms); + + BT_DBG("Public Key Type: 0x%02x", data[3]); + BT_DBG("Static OOB Type: 0x%02x", data[4]); + BT_DBG("Output OOB Size: %u", data[5]); + + output_action = sys_get_be16(&data[6]); + BT_DBG("Output OOB Action: 0x%04x", output_action); + + BT_DBG("Input OOB Size: %u", data[8]); + + input_action = sys_get_be16(&data[9]); + BT_DBG("Input OOB Action: 0x%04x", input_action); + + if (data[0] == 0) { + BT_ERR("Invalid number of elements"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + link.provisioner->node = bt_mesh_node_alloc(link.provisioner->addr, + data[0], + link.provisioner->net_idx); + if (link.provisioner->node == NULL) { + prov_send_fail_msg(PROV_ERR_RESOURCES); + return; + } + + memcpy(&link.conf_inputs[1], data, 11); + + atomic_set_bit(link.flags, SEND_PUB_KEY); + + send_start(); +} + +static bt_mesh_output_action_t output_action(u8_t action) +{ + switch (action) { + case OUTPUT_OOB_BLINK: + return BT_MESH_BLINK; + case OUTPUT_OOB_BEEP: + return BT_MESH_BEEP; + case OUTPUT_OOB_VIBRATE: + return BT_MESH_VIBRATE; + case OUTPUT_OOB_NUMBER: + return BT_MESH_DISPLAY_NUMBER; + case OUTPUT_OOB_STRING: + return BT_MESH_DISPLAY_STRING; + default: + return BT_MESH_NO_OUTPUT; + } +} + +static bt_mesh_input_action_t input_action(u8_t action) +{ + switch (action) { + case INPUT_OOB_PUSH: + return BT_MESH_PUSH; + case INPUT_OOB_TWIST: + return BT_MESH_TWIST; + case INPUT_OOB_NUMBER: + return BT_MESH_ENTER_NUMBER; + case INPUT_OOB_STRING: + return BT_MESH_ENTER_STRING; + default: + return BT_MESH_NO_INPUT; + } +} + +static int prov_auth(u8_t method, u8_t action, u8_t size) +{ + bt_mesh_output_action_t output; + bt_mesh_input_action_t input; + + switch (method) { + case AUTH_METHOD_NO_OOB: + if (action || size) { + return -EINVAL; + } + + memset(link.auth, 0, sizeof(link.auth)); + return 0; + case AUTH_METHOD_STATIC: + if (action || size) { + return -EINVAL; + } + + memcpy(link.auth + 16 - prov->static_val_len, + prov->static_val, prov->static_val_len); + memset(link.auth, 0, sizeof(link.auth) - prov->static_val_len); + return 0; + + case AUTH_METHOD_OUTPUT: + output = output_action(action); + if (!output) { + return -EINVAL; + } + + if (!(prov->output_actions & output)) { + return -EINVAL; + } + + if (size > prov->output_size) { + return -EINVAL; + } + + atomic_set_bit(link.flags, NOTIFY_INPUT_COMPLETE); + + if (output == BT_MESH_DISPLAY_STRING) { + unsigned char str[9]; + u8_t i; + + bt_rand(str, size); + + /* Normalize to '0' .. '9' & 'A' .. 'Z' */ + for (i = 0; i < size; i++) { + str[i] %= 36; + if (str[i] < 10) { + str[i] += '0'; + } else { + str[i] += 'A' - 10; + } + } + str[size] = '\0'; + + memcpy(link.auth, str, size); + memset(link.auth + size, 0, sizeof(link.auth) - size); + + return prov->output_string((char *)str); + } else { + u32_t div[8] = { 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000 }; + u32_t num; + + bt_rand(&num, sizeof(num)); + num %= div[size - 1]; + + sys_put_be32(num, &link.auth[12]); + memset(link.auth, 0, 12); + + return prov->output_number(output, num); + } + + case AUTH_METHOD_INPUT: + input = input_action(action); + if (!input) { + return -EINVAL; + } + + if (!(prov->input_actions & input)) { + return -EINVAL; + } + + if (size > prov->input_size) { + return -EINVAL; + } + + if (input == BT_MESH_ENTER_STRING) { + atomic_set_bit(link.flags, WAIT_STRING); + } else { + atomic_set_bit(link.flags, WAIT_NUMBER); + } + + return prov->input(input, size); + + default: + return -EINVAL; + } +} + +static void prov_start(const u8_t *data) +{ + BT_DBG("Algorithm: 0x%02x", data[0]); + BT_DBG("Public Key: 0x%02x", data[1]); + BT_DBG("Auth Method: 0x%02x", data[2]); + BT_DBG("Auth Action: 0x%02x", data[3]); + BT_DBG("Auth Size: 0x%02x", data[4]); + + if (data[0] != PROV_ALG_P256) { + BT_ERR("Unknown algorithm 0x%02x", data[0]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (data[1] != PUB_KEY_NO_OOB) { + BT_ERR("Invalid public key type: 0x%02x", data[1]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + memcpy(&link.conf_inputs[12], data, 5); + + /* TODO: reset link when auth fails? */ + link.expect = PROV_PUB_KEY; + + if (prov_auth(data[2], data[3], data[4]) < 0) { + BT_ERR("Invalid authentication method: 0x%02x; " + "action: 0x%02x; size: 0x%02x", data[2], data[3], + data[4]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + } +} + +static void send_confirm(void) +{ + struct os_mbuf *cfm = PROV_BUF(17); + + BT_DBG("ConfInputs[0] %s", bt_hex(link.conf_inputs, 64)); + BT_DBG("ConfInputs[64] %s", bt_hex(&link.conf_inputs[64], 64)); + BT_DBG("ConfInputs[128] %s", bt_hex(&link.conf_inputs[128], 17)); + + if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) { + BT_ERR("Unable to generate confirmation salt"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16)); + + if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) { + BT_ERR("Unable to generate confirmation key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16)); + + if (bt_rand(link.rand, 16)) { + BT_ERR("Unable to generate random number"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("LocalRandom: %s", bt_hex(link.rand, 16)); + + prov_buf_init(cfm, PROV_CONFIRM); + + if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth, + net_buf_simple_add(cfm, 16))) { + BT_ERR("Unable to generate confirmation value"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + if (prov_send(cfm)) { + BT_ERR("Failed to send Provisioning Confirm"); + goto done; + } + + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_CONFIRM; + } else { + link.expect = PROV_RANDOM; + } + +done: + os_mbuf_free_chain(cfm); +} + +static void send_input_complete(void) +{ + struct os_mbuf *buf = PROV_BUF(1); + + prov_buf_init(buf, PROV_INPUT_COMPLETE); + if (prov_send(buf)) { + BT_ERR("Failed to send Provisioning Input Complete"); + } + link.expect = PROV_CONFIRM; + + os_mbuf_free_chain(buf); +} + +int bt_mesh_input_number(u32_t num) +{ + BT_DBG("%u", (unsigned) num); + + if (!atomic_test_and_clear_bit(link.flags, WAIT_NUMBER)) { + return -EINVAL; + } + + sys_put_be32(num, &link.auth[12]); + + send_input_complete(); + + return 0; +} + +int bt_mesh_input_string(const char *str) +{ + BT_DBG("%s", str); + + if (!atomic_test_and_clear_bit(link.flags, WAIT_STRING)) { + return -EINVAL; + } + + strncpy((char *)link.auth, str, prov->input_size); + + send_input_complete(); + + return 0; +} + +static void send_pub_key(void) +{ + struct os_mbuf *buf = PROV_BUF(65); + const u8_t *key; + + key = bt_pub_key_get(); + if (!key) { + BT_ERR("No public key available"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("Local Public Key: %s", bt_hex(key, 64)); + + prov_buf_init(buf, PROV_PUB_KEY); + + /* Swap X and Y halves independently to big-endian */ + sys_memcpy_swap(net_buf_simple_add(buf, 32), key, 32); + sys_memcpy_swap(net_buf_simple_add(buf, 32), &key[32], 32); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + /* PublicKeyProvisioner */ + memcpy(&link.conf_inputs[17], &buf->om_data[1], 64); + } else { + /* PublicKeyRemote */ + memcpy(&link.conf_inputs[81], &buf->om_data[1], 64); + } + + if (prov_send(buf)) { + BT_ERR("Failed to send Public Key"); + goto done; + } + + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_PUB_KEY; + } else { + if (atomic_test_bit(link.flags, WAIT_NUMBER) || + atomic_test_bit(link.flags, WAIT_STRING)) { + link.expect = PROV_NO_PDU; /* Wait for input */ + } else { + link.expect = PROV_CONFIRM; + } + } + +done: + os_mbuf_free_chain(buf); +} + +static void prov_dh_key_cb(const u8_t dhkey[32]) +{ + BT_DBG("%p", dhkey); + + if (!dhkey) { + BT_ERR("DHKey generation failed"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + sys_memcpy_swap(link.dhkey, dhkey, 32); + + BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_confirm(); + } else { + send_pub_key(); + } +} + +static void prov_dh_key_gen(void) +{ + u8_t remote_pk_le[64], *remote_pk; + + if (atomic_test_bit(link.flags, PROVISIONER)) { + remote_pk = &link.conf_inputs[81]; + } else { + remote_pk = &link.conf_inputs[17]; + } + + /* Copy remote key in little-endian for bt_dh_key_gen(). + * X and Y halves are swapped independently. The bt_dh_key_gen() + * will also take care of validating the remote public key. + */ + sys_memcpy_swap(remote_pk_le, remote_pk, 32); + sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32); + + if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) { + BT_ERR("Failed to generate DHKey"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + } +} + +static void prov_pub_key(const u8_t *data) +{ + BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + /* PublicKeyDevice */ + memcpy(&link.conf_inputs[81], data, 64); + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + prov_clear_tx(); +#endif + } else { + /* PublicKeyProvisioner */ + memcpy(&link.conf_inputs[17], data, 64); + + if (!bt_pub_key_get()) { + /* Clear retransmit timer */ +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) + prov_clear_tx(); +#endif + + atomic_set_bit(link.flags, WAIT_PUB_KEY); + BT_WARN("Waiting for local public key"); + return; + } + } + + prov_dh_key_gen(); +} + +static void pub_key_ready(const u8_t *pkey) +{ + if (!pkey) { + BT_WARN("Public key not available"); + return; + } + + BT_DBG("Local public key ready"); + + if (atomic_test_and_clear_bit(link.flags, WAIT_PUB_KEY)) { + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_pub_key(); + } else { + prov_dh_key_gen(); + } + } +} + +static void notify_input_complete(void) +{ + if (atomic_test_and_clear_bit(link.flags, NOTIFY_INPUT_COMPLETE) && + prov->input_complete) { + prov->input_complete(); + } +} + +static void prov_input_complete(const u8_t *data) +{ + BT_DBG(""); + notify_input_complete(); +} + +static void send_prov_data(void) +{ + struct os_mbuf *pdu = PROV_BUF(34); + struct bt_mesh_subnet *sub; + u8_t session_key[16]; + u8_t nonce[13]; + int err; + + err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); + if (err) { + BT_ERR("Unable to generate session key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); + if (err) { + BT_ERR("Unable to generate session nonce"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + err = bt_mesh_dev_key(link.dhkey, link.prov_salt, + link.provisioner->node->dev_key); + if (err) { + BT_ERR("Unable to generate device key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("DevKey: %s", bt_hex(link.provisioner->node->dev_key, 16)); + + sub = bt_mesh_subnet_get(link.provisioner->node->net_idx); + if (sub == NULL) { + BT_ERR("No subnet with net_idx %u", + link.provisioner->node->net_idx); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + prov_buf_init(pdu, PROV_DATA); + net_buf_simple_add_mem(pdu, sub->keys[sub->kr_flag].net, 16); + net_buf_simple_add_be16(pdu, link.provisioner->node->net_idx); + net_buf_simple_add_u8(pdu, bt_mesh_net_flags(sub)); + net_buf_simple_add_be32(pdu, bt_mesh.iv_index); + net_buf_simple_add_be16(pdu, link.provisioner->node->addr); + net_buf_simple_add(pdu, 8); /* For MIC */ + + BT_DBG("net_idx %u, iv_index 0x%08x, addr 0x%04x", + link.provisioner->node->net_idx, bt_mesh.iv_index, + link.provisioner->node->addr); + + err = bt_mesh_prov_encrypt(session_key, nonce, &pdu->om_data[1], + &pdu->om_data[1]); + if (err) { + BT_ERR("Unable to encrypt provisioning data"); + prov_send_fail_msg(PROV_ERR_DECRYPT); + goto done; + } + + if (prov_send(pdu)) { + BT_ERR("Failed to send Provisioning Data"); + goto done; + } + + link.expect = PROV_COMPLETE; + +done: + os_mbuf_free_chain(pdu); +} + +static void prov_complete(const u8_t *data) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + return; + } + + struct bt_mesh_node *node = link.provisioner->node; +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + u8_t reason = CLOSE_REASON_SUCCESS; +#endif + + BT_DBG("key %s, net_idx %u, num_elem %u, addr 0x%04x", + bt_hex(node->dev_key, 16), node->net_idx, node->num_elem, + node->addr); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_node(node); + } + + link.provisioner->node = NULL; + link.expect = PROV_NO_PDU; + atomic_set_bit(link.flags, LINK_CLOSING); + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); +#endif + + bt_mesh_prov_node_added(node->net_idx, node->addr, node->num_elem); + + /* + * According to mesh profile spec (5.3.1.4.3), the close message should + * be restransmitted at least three times. Retransmit the LINK_CLOSE + * message until CLOSING_TIMEOUT has elapsed instead of resetting the + * link here. + */ +} + +static void send_random(void) +{ + struct os_mbuf *rnd = PROV_BUF(17); + + prov_buf_init(rnd, PROV_RANDOM); + net_buf_simple_add_mem(rnd, link.rand, 16); + + if (prov_send(rnd)) { + BT_ERR("Failed to send Provisioning Random"); + goto done; + } + + if (atomic_test_bit(link.flags, PROVISIONER)) { + link.expect = PROV_RANDOM; + } else { + link.expect = PROV_DATA; + } + +done: + os_mbuf_free_chain(rnd); +} + +static void prov_random(const u8_t *data) +{ + u8_t conf_verify[16]; + const u8_t *prov_rand, *dev_rand; + + BT_DBG("Remote Random: %s", bt_hex(data, 16)); + + if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) { + BT_ERR("Unable to calculate confirmation verification"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + if (memcmp(conf_verify, link.conf, 16)) { + BT_ERR("Invalid confirmation value"); + BT_DBG("Received: %s", bt_hex(link.conf, 16)); + BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); + prov_send_fail_msg(PROV_ERR_CFM_FAILED); + return; + } + + if (atomic_test_bit(link.flags, PROVISIONER)) { + prov_rand = link.rand; + dev_rand = data; + } else { + prov_rand = data; + dev_rand = link.rand; + } + + if (bt_mesh_prov_salt(link.conf_salt, prov_rand, dev_rand, + link.prov_salt)) { + BT_ERR("Failed to generate provisioning salt"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + return; + } + + BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16)); + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_bit(link.flags, PROVISIONER)) { + send_prov_data(); + } else { + send_random(); + } +} + +static void prov_confirm(const u8_t *data) +{ + BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + + memcpy(link.conf, data, 16); + + notify_input_complete(); + + if (atomic_test_bit(link.flags, PROVISIONER)) { + send_random(); + } else { + send_confirm(); + } +} + +static inline bool is_pb_gatt(void) +{ +#if MYNEWT_VAL(BLE_MESH_PB_GATT) + return (link.conn_handle != BLE_HS_CONN_HANDLE_NONE); +#else + return false; +#endif +} + +static void prov_data(const u8_t *data) +{ + struct os_mbuf *msg = PROV_BUF(1); + u8_t session_key[16]; + u8_t nonce[13]; + u8_t dev_key[16]; + u8_t pdu[25]; + u8_t flags; + u32_t iv_index; + u16_t addr; + u16_t net_idx; + int err; + bool identity_enable; + + BT_DBG(""); + + err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); + if (err) { + BT_ERR("Unable to generate session key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); + if (err) { + BT_ERR("Unable to generate session nonce"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu); + if (err) { + BT_ERR("Unable to decrypt provisioning data"); + prov_send_fail_msg(PROV_ERR_DECRYPT); + goto done; + } + + err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key); + if (err) { + BT_ERR("Unable to generate device key"); + prov_send_fail_msg(PROV_ERR_UNEXP_ERR); + goto done; + } + + BT_DBG("DevKey: %s", bt_hex(dev_key, 16)); + + net_idx = sys_get_be16(&pdu[16]); + flags = pdu[18]; + iv_index = sys_get_be32(&pdu[19]); + addr = sys_get_be16(&pdu[23]); + + BT_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x", + net_idx, (unsigned) iv_index, addr); + + prov_buf_init(msg, PROV_COMPLETE); + if (prov_send(msg)) { + BT_ERR("Failed to send Provisioning Complete"); + goto done; + } + + /* Ignore any further PDUs on this link */ + link.expect = PROV_NO_PDU; + + /* Store info, since bt_mesh_provision() will end up clearing it */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { + identity_enable = is_pb_gatt(); + } else { + identity_enable = false; + } + + err = bt_mesh_provision(pdu, net_idx, flags, iv_index, addr, dev_key); + if (err) { + BT_ERR("Failed to provision (err %d)", err); + goto done; + } + + /* After PB-GATT provisioning we should start advertising + * using Node Identity. + */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && identity_enable) { + bt_mesh_proxy_identity_enable(); + } + +done: + os_mbuf_free_chain(msg); +} + +static void prov_failed(const u8_t *data) +{ + BT_WARN("Error: 0x%02x", data[0]); +} + +static const struct { + void (*func)(const u8_t *data); + u16_t len; +} prov_handlers[] = { + { prov_invite, 1 }, + { prov_capabilities, 11 }, + { prov_start, 5, }, + { prov_pub_key, 64 }, + { prov_input_complete, 0 }, + { prov_confirm, 16 }, + { prov_random, 16 }, + { prov_data, 33 }, + { prov_complete, 0 }, + { prov_failed, 1 }, +}; + +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +static void prov_retransmit(struct ble_npl_event *work) +{ + int i, timeout; + + BT_DBG(""); + + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + BT_WARN("Link not active"); + return; + } + + if (atomic_test_bit(link.flags, LINK_CLOSING)) { + timeout = CLOSING_TIMEOUT; + } else { + timeout = TRANSACTION_TIMEOUT; + } + + if (k_uptime_get() - link.tx.start > timeout) { + BT_WARN("Giving up transaction"); + reset_adv_link(); + return; + } + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct os_mbuf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (BT_MESH_ADV(buf)->busy) { + continue; + } + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + + } +} + +static void link_open(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("link open: len %u", buf->om_len); + + if (buf->om_len < 16) { + BT_ERR("Too short bearer open message (len %u)", buf->om_len); + return; + } + + if (atomic_test_bit(link.flags, LINK_ACTIVE)) { + /* Send another link ack if the provisioner missed the last */ + if (link.id == rx->link_id && link.expect == PROV_INVITE) { + BT_DBG("Resending link ack"); + bearer_ctl_send(LINK_ACK, NULL, 0); + } else { + BT_WARN("Ignoring bearer open: link already active"); + } + + return; + } + + if (memcmp(buf->om_data, prov->uuid, 16)) { + BT_DBG("Bearer open message not for us"); + return; + } + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_ADV); + } + + link.id = rx->link_id; + atomic_set_bit(link.flags, LINK_ACTIVE); + net_buf_simple_init(link.rx.buf, 0); + + bearer_ctl_send(LINK_ACK, NULL, 0); + + link.expect = PROV_INVITE; +} + +static void link_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("Link ack: len %u", buf->om_len); + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_bit(link.flags, PROVISIONER)) { + if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) { + return; + } + + prov_clear_tx(); + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_ADV); + } + + send_invite(); + } +} + +static void link_close(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("Link close: len %u", buf->om_len); + + reset_adv_link(); +} + +static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->om_len); + + switch (BEARER_CTL(rx->gpc)) { + case LINK_OPEN: + link_open(rx, buf); + break; + case LINK_ACK: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_ack(rx, buf); + break; + case LINK_CLOSE: + if (!atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_close(rx, buf); + break; + default: + BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); + } + + return; + } +} + +static void prov_msg_recv(void) +{ + u8_t type = link.rx.buf->om_data[0]; + + BT_DBG("type 0x%02x len %u", type, link.rx.buf->om_len); + + k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); + + if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { + BT_ERR("Incorrect FCS"); + return; + } + + gen_prov_ack_send(link.rx.id); + link.rx.prev_id = link.rx.id; + link.rx.id = 0; + + if (atomic_test_bit(link.flags, LINK_INVALID)) { + BT_WARN("Unexpected msg 0x%02x on invalidated link", type); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("Unknown provisioning PDU type 0x%02x", type); + prov_send_fail_msg(PROV_ERR_NVAL_PDU); + return; + } + + if (1 + prov_handlers[type].len != link.rx.buf->om_len) { + BT_ERR("Invalid length %u for type 0x%02x", + link.rx.buf->om_len, type); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + prov_handlers[type].func(&link.rx.buf->om_data[1]); +} + +static void gen_prov_cont(struct prov_rx *rx, struct os_mbuf *buf) +{ + u8_t seg = CONT_SEG_INDEX(rx->gpc); + + BT_DBG("len %u, seg_index %u", buf->om_len, seg); + + if (!link.rx.seg && link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + if (rx->xact_id != link.rx.id) { + BT_WARN("Data for unknown transaction (%u != %u)", + rx->xact_id, link.rx.id); + return; + } + + if (seg > link.rx.last_seg) { + BT_ERR("Invalid segment index %u", seg); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } else if (seg == link.rx.last_seg) { + u8_t expect_len; + + expect_len = (link.rx.buf->om_len - 20 - + ((link.rx.last_seg - 1) * 23)); + if (expect_len != buf->om_len) { + BT_ERR("Incorrect last seg len: %u != %u", + expect_len, buf->om_len); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + } + + if (!(link.rx.seg & BIT(seg))) { + BT_WARN("Ignoring already received segment"); + return; + } + + memcpy(XACT_SEG_DATA(seg), buf->om_data, buf->om_len); + XACT_SEG_RECV(seg); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static void gen_prov_ack(struct prov_rx *rx, struct os_mbuf *buf) +{ + BT_DBG("len %u", buf->om_len); + + if (!link.tx.buf[0]) { + return; + } + + if (rx->xact_id == link.tx.id) { + /* Don't clear resending of LINK_CLOSE messages */ + if (!atomic_test_bit(link.flags, LINK_CLOSING)) { + prov_clear_tx(); + } + + /* Send the PubKey when the the Start message is ACK'ed */ + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_and_clear_bit(link.flags, SEND_PUB_KEY)) { + if (!bt_pub_key_get()) { + atomic_set_bit(link.flags, WAIT_PUB_KEY); + BT_WARN("Waiting for local public key"); + } else { + send_pub_key(); + } + } + } +} + +static void gen_prov_start(struct prov_rx *rx, struct os_mbuf *buf) +{ + u16_t trailing_space = 0; + + if (link.rx.seg) { + BT_WARN("Got Start while there are unreceived segments"); + return; + } + + if (link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + trailing_space = OS_MBUF_TRAILINGSPACE(link.rx.buf); + + link.rx.buf->om_len = net_buf_simple_pull_be16(buf); + link.rx.id = rx->xact_id; + link.rx.fcs = net_buf_simple_pull_u8(buf); + + BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->om_len, + START_LAST_SEG(rx->gpc), link.rx.buf->om_len, link.rx.fcs); + + if (link.rx.buf->om_len < 1) { + BT_ERR("Ignoring zero-length provisioning PDU"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (link.rx.buf->om_len > trailing_space) { + BT_ERR("Too large provisioning PDU (%u bytes)", + link.rx.buf->om_len); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->om_len <= 20) { + BT_ERR("Too small total length for multi-segment PDU"); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; + link.rx.last_seg = START_LAST_SEG(rx->gpc); + memcpy(link.rx.buf->om_data, buf->om_data, buf->om_len); + XACT_SEG_RECV(0); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static const struct { + void (*func)(struct prov_rx *rx, struct os_mbuf *buf); + bool require_link; + u8_t min_len; +} gen_prov[] = { + { gen_prov_start, true, 3 }, + { gen_prov_ack, true, 0 }, + { gen_prov_cont, true, 0 }, + { gen_prov_ctl, false, 0 }, +}; + +static void gen_prov_recv(struct prov_rx *rx, struct os_mbuf *buf) +{ + if (buf->om_len < gen_prov[GPCF(rx->gpc)].min_len) { + BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); + return; + } + + if (!atomic_test_bit(link.flags, LINK_ACTIVE) && + gen_prov[GPCF(rx->gpc)].require_link) { + BT_DBG("Ignoring message that requires active link"); + return; + } + + BT_DBG("prov_action: %d", GPCF(rx->gpc)); + gen_prov[GPCF(rx->gpc)].func(rx, buf); +} + +void bt_mesh_pb_adv_recv(struct os_mbuf *buf) +{ + struct prov_rx rx; + + if (!bt_prov_active() && bt_mesh_is_provisioned()) { + BT_DBG("Ignoring provisioning PDU - already provisioned"); + return; + } + + if (buf->om_len < 6) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return; + } + + rx.link_id = net_buf_simple_pull_be32(buf); + rx.xact_id = net_buf_simple_pull_u8(buf); + rx.gpc = net_buf_simple_pull_u8(buf); + + BT_DBG("link_id 0x%08x xact_id %u", (unsigned) rx.link_id, rx.xact_id); + + if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { + BT_DBG("Ignoring mesh beacon for unknown link"); + return; + } + + gen_prov_recv(&rx, buf); +} + +int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration) +{ + BT_DBG("uuid %s", bt_hex(uuid, 16)); + + if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + atomic_set_bit(link.flags, PROVISIONER); + + bt_rand(&link.id, sizeof(link.id)); + link.tx.id = 0x7F; + link.provisioner->addr = addr; + link.provisioner->net_idx = net_idx; + link.provisioner->attention_duration = attention_duration; + + net_buf_simple_init(link.rx.buf, 0); + + bearer_ctl_send(LINK_OPEN, uuid, 16); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (link.conn_handle != conn_handle) { + BT_WARN("Data for unexpected connection"); + return -ENOTCONN; + } + + if (buf->om_len < 1) { + BT_WARN("Too short provisioning packet (len %u)", buf->om_len); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return -EINVAL; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("Unknown provisioning PDU type 0x%02x", type); + return -EINVAL; + } + + if (prov_handlers[type].len != buf->om_len) { + BT_ERR("Invalid length %u for type 0x%02x", buf->om_len, type); + return -EINVAL; + } + + prov_handlers[type].func(buf->om_data); + + return 0; +} + +int bt_mesh_pb_gatt_open(uint16_t conn_handle) +{ + BT_DBG("conn_handle %d", conn_handle); + + if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + BT_ERR("Link already opened?"); + return -EBUSY; + } + + link.conn_handle = conn_handle; + link.expect = PROV_INVITE; + + if (prov->link_open) { + prov->link_open(BT_MESH_PROV_GATT); + } + + return 0; +} + +int bt_mesh_pb_gatt_close(uint16_t conn_handle) +{ + BT_DBG("conn_handle %d", conn_handle); + + if (link.conn_handle != conn_handle) { + BT_ERR("Not connected"); + return -ENOTCONN; + } + + if (prov->link_close) { + prov->link_close(BT_MESH_PROV_GATT); + } + + return reset_state(); +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +const struct bt_mesh_prov *bt_mesh_prov_get(void) +{ + return prov; +} + +bool bt_prov_active(void) +{ + return atomic_test_bit(link.flags, LINK_ACTIVE); +} + +static void protocol_timeout(struct ble_npl_event *work) +{ + BT_DBG("Protocol timeout"); + +#if MYNEWT_VAL(BLE_MESH_PB_GATT) + if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) { + bt_mesh_pb_gatt_close(link.conn_handle); + return; + } +#endif + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + u8_t reason = CLOSE_REASON_TIMEOUT; + + link.rx.seg = 0U; + bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); + + reset_state(); +#endif +} + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) +{ + if (!prov_info) { + BT_ERR("No provisioning context provided"); + return -EINVAL; + } + + k_delayed_work_init(&link.prot_timer, protocol_timeout); + + prov = prov_info; + +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + k_delayed_work_init(&link.tx.retransmit, prov_retransmit); +#endif + + return reset_state(); +} + +void bt_mesh_prov_reset_link(void) +{ +#if (MYNEWT_VAL(BLE_MESH_PB_ADV)) +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + link.rx.buf = bt_mesh_proxy_get_buf(); +#else + net_buf_simple_init(rx_buf, 0); + link.rx.buf = rx_buf; +#endif +#endif +} + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) +{ + if (prov->complete) { + prov->complete(net_idx, addr); + } +} + +void bt_mesh_prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem) +{ + if (prov->node_added) { + prov->node_added(net_idx, addr, num_elem); + } +} + +void bt_mesh_prov_reset(void) +{ + if (prov->reset) { + prov->reset(); + } +} + +#endif /* MYNEWT_VAL(BLE_MESH_PROV) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.h new file mode 100644 index 0000000..c6866c9 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/prov.h @@ -0,0 +1,37 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PROV_H__ +#define __PROV_H__ + +#include "nimble/porting/nimble/include/os/os_mbuf.h" +#include "../include/mesh/mesh.h" +#include "nimble/nimble/host/src/ble_hs_conn_priv.h" + +int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, + u8_t attention_duration); + +void bt_mesh_pb_adv_recv(struct os_mbuf *buf); + +bool bt_prov_active(void); + +int bt_mesh_pb_gatt_open(uint16_t conn_handle); +int bt_mesh_pb_gatt_close(uint16_t conn_handle); +int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf); + +const struct bt_mesh_prov *bt_mesh_prov_get(void); + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov); + +void bt_mesh_prov_reset_link(void); + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr); +void bt_mesh_prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem); +void bt_mesh_prov_reset(void); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.c new file mode 100644 index 0000000..9825815 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.c @@ -0,0 +1,1502 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG + +#if MYNEWT_VAL(BLE_MESH_PROXY) + +#include "../include/mesh/mesh.h" +#include "nimble/nimble/host/include/host/ble_att.h" +#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h" +#include "nimble/nimble/host/src/ble_hs_priv.h" + +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "prov.h" +#include "beacon.h" +#include "foundation.h" +#include "access.h" +#include "proxy.h" + +#define PDU_TYPE(data) (data[0] & BIT_MASK(6)) +#define PDU_SAR(data) (data[0] >> 6) + +/* Mesh Profile 1.0 Section 6.6: + * "The timeout for the SAR transfer is 20 seconds. When the timeout + * expires, the Proxy Server shall disconnect." + */ +#define PROXY_SAR_TIMEOUT K_SECONDS(20) + +#define SAR_COMPLETE 0x00 +#define SAR_FIRST 0x01 +#define SAR_CONT 0x02 +#define SAR_LAST 0x03 + +#define CFG_FILTER_SET 0x00 +#define CFG_FILTER_ADD 0x01 +#define CFG_FILTER_REMOVE 0x02 +#define CFG_FILTER_STATUS 0x03 + +/** @def BT_UUID_MESH_PROV + * @brief Mesh Provisioning Service + */ +ble_uuid16_t BT_UUID_MESH_PROV = BLE_UUID16_INIT(0x1827); +#define BT_UUID_MESH_PROV_VAL 0x1827 +/** @def BT_UUID_MESH_PROXY + * @brief Mesh Proxy Service + */ +ble_uuid16_t BT_UUID_MESH_PROXY = BLE_UUID16_INIT(0x1828); +#define BT_UUID_MESH_PROXY_VAL 0x1828 +/** @def BT_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +ble_uuid16_t BT_UUID_GATT_CCC = BLE_UUID16_INIT(0x2902); +#define BT_UUID_GATT_CCC_VAL 0x2902 +/** @def BT_UUID_MESH_PROV_DATA_IN + * @brief Mesh Provisioning Data In + */ +ble_uuid16_t BT_UUID_MESH_PROV_DATA_IN = BLE_UUID16_INIT(0x2adb); +#define BT_UUID_MESH_PROV_DATA_IN_VAL 0x2adb +/** @def BT_UUID_MESH_PROV_DATA_OUT + * @brief Mesh Provisioning Data Out + */ +ble_uuid16_t BT_UUID_MESH_PROV_DATA_OUT = BLE_UUID16_INIT(0x2adc); +#define BT_UUID_MESH_PROV_DATA_OUT_VAL 0x2adc +/** @def BT_UUID_MESH_PROXY_DATA_IN + * @brief Mesh Proxy Data In + */ +ble_uuid16_t BT_UUID_MESH_PROXY_DATA_IN = BLE_UUID16_INIT(0x2add); +#define BT_UUID_MESH_PROXY_DATA_IN_VAL 0x2add +/** @def BT_UUID_MESH_PROXY_DATA_OUT + * @brief Mesh Proxy Data Out + */ +ble_uuid16_t BT_UUID_MESH_PROXY_DATA_OUT = BLE_UUID16_INIT(0x2ade); +#define BT_UUID_MESH_PROXY_DATA_OUT_VAL 0x2ade + +#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6))) + +#define CLIENT_BUF_SIZE 68 + +static const struct ble_gap_adv_params slow_adv_param = { + .conn_mode = (BLE_GAP_CONN_MODE_UND), + .disc_mode = (BLE_GAP_DISC_MODE_GEN), + .itvl_min = BT_GAP_ADV_SLOW_INT_MIN, + .itvl_max = BT_GAP_ADV_SLOW_INT_MAX, +}; + +static const struct ble_gap_adv_params fast_adv_param = { + .conn_mode = (BLE_GAP_CONN_MODE_UND), + .disc_mode = (BLE_GAP_DISC_MODE_GEN), + .itvl_min = BT_GAP_ADV_FAST_INT_MIN_2, + .itvl_max = BT_GAP_ADV_FAST_INT_MAX_2, +}; + +static bool proxy_adv_enabled; + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +static void proxy_send_beacons(struct ble_npl_event *work); +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static bool prov_fast_adv; +#endif + +static struct bt_mesh_proxy_client { + uint16_t conn_handle; + u16_t filter[MYNEWT_VAL(BLE_MESH_PROXY_FILTER_SIZE)]; + enum __packed { + NONE, + WHITELIST, + BLACKLIST, + PROV, + } filter_type; + u8_t msg_type; +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + struct ble_npl_callout send_beacons; +#endif + struct k_delayed_work sar_timer; + struct os_mbuf *buf; +} clients[MYNEWT_VAL(BLE_MAX_CONNECTIONS)] = { + [0 ... (MYNEWT_VAL(BLE_MAX_CONNECTIONS) - 1)] = { 0 }, +}; + +/* Track which service is enabled */ +static enum { + MESH_GATT_NONE, + MESH_GATT_PROV, + MESH_GATT_PROXY, +} gatt_svc = MESH_GATT_NONE; + +static struct { + uint16_t proxy_h; + uint16_t proxy_data_out_h; + uint16_t prov_h; + uint16_t prov_data_in_h; + uint16_t prov_data_out_h; +} svc_handles; + +static void resolve_svc_handles(void) +{ + int rc; + + /* Either all handles are already resolved, or none of them */ + if (svc_handles.prov_data_out_h) { + return; + } + + /* + * We assert if attribute is not found since at this stage all attributes + * shall be already registered and thus shall be found. + */ + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + &svc_handles.proxy_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL), + NULL, &svc_handles.proxy_data_out_h); + assert(rc == 0); + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + &svc_handles.prov_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL), + NULL, &svc_handles.prov_data_in_h); + assert(rc == 0); + + rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL), + NULL, &svc_handles.prov_data_out_h); + assert(rc == 0); +} + +static struct bt_mesh_proxy_client *find_client(uint16_t conn_handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle == conn_handle) { + return &clients[i]; + } + } + + return NULL; +} + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +/* Next subnet in queue to be advertised */ +static int next_idx; + +static int proxy_segment_and_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg); + +static int filter_set(struct bt_mesh_proxy_client *client, + struct os_mbuf *buf) +{ + u8_t type; + + if (buf->om_len < 1) { + BT_WARN("Too short Filter Set message"); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + BT_DBG("type 0x%02x", type); + + switch (type) { + case 0x00: + memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = WHITELIST; + break; + case 0x01: + memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = BLACKLIST; + break; + default: + BT_WARN("Prohibited Filter Type 0x%02x", type); + return -EINVAL; + } + + return 0; +} + +static void filter_add(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return; + } + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == BT_MESH_ADDR_UNASSIGNED) { + client->filter[i] = addr; + return; + } + } +} + +static void filter_remove(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + client->filter[i] = BT_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static void send_filter_status(struct bt_mesh_proxy_client *client, + struct bt_mesh_net_rx *rx, + struct os_mbuf *buf) +{ + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + }; + u16_t filter_size; + int i, err; + + /* Configuration messages always have dst unassigned */ + tx.ctx->addr = BT_MESH_ADDR_UNASSIGNED; + + net_buf_simple_init(buf, 10); + + net_buf_simple_add_u8(buf, CFG_FILTER_STATUS); + + if (client->filter_type == WHITELIST) { + net_buf_simple_add_u8(buf, 0x00); + } else { + net_buf_simple_add_u8(buf, 0x01); + } + + for (filter_size = 0, i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] != BT_MESH_ADDR_UNASSIGNED) { + filter_size++; + } + } + + net_buf_simple_add_be16(buf, filter_size); + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + err = bt_mesh_net_encode(&tx, buf, true); + if (err) { + BT_ERR("Encoding Proxy cfg message failed (err %d)", err); + return; + } + + err = proxy_segment_and_send(client->conn_handle, BT_MESH_PROXY_CONFIG, buf); + if (err) { + BT_ERR("Failed to send proxy cfg message (err %d)", err); + } +} + +static void proxy_cfg(struct bt_mesh_proxy_client *client) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(29); + struct bt_mesh_net_rx rx; + u8_t opcode; + int err; + + err = bt_mesh_net_decode(client->buf, BT_MESH_NET_IF_PROXY_CFG, + &rx, buf); + if (err) { + BT_ERR("Failed to decode Proxy Configuration (err %d)", err); + goto done; + } + + /* Remove network headers */ + net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN); + + BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); + + if (buf->om_len < 1) { + BT_WARN("Too short proxy configuration PDU"); + goto done; + } + + opcode = net_buf_simple_pull_u8(buf); + switch (opcode) { + case CFG_FILTER_SET: + filter_set(client, buf); + send_filter_status(client, &rx, buf); + break; + case CFG_FILTER_ADD: + while (buf->om_len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(buf); + filter_add(client, addr); + } + send_filter_status(client, &rx, buf); + break; + case CFG_FILTER_REMOVE: + while (buf->om_len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(buf); + filter_remove(client, addr); + } + send_filter_status(client, &rx, buf); + break; + default: + BT_WARN("Unhandled configuration OpCode 0x%02x", opcode); + break; + } + +done: + os_mbuf_free_chain(buf); +} + +static int beacon_send(uint16_t conn_handle, struct bt_mesh_subnet *sub) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(23); + int rc; + + net_buf_simple_init(buf, 1); + bt_mesh_beacon_create(sub, buf); + + rc = proxy_segment_and_send(conn_handle, BT_MESH_PROXY_BEACON, buf); + os_mbuf_free_chain(buf); + return rc; +} + +static void proxy_send_beacons(struct ble_npl_event *work) +{ + struct bt_mesh_proxy_client *client; + int i; + + + client = ble_npl_event_get_arg(work); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BT_MESH_KEY_UNUSED) { + beacon_send(client->conn_handle, sub); + } + } +} + +static void proxy_sar_timeout(struct ble_npl_event *work) +{ + struct bt_mesh_proxy_client *client; + int rc; + + BT_WARN("Proxy SAR timeout"); + + client = ble_npl_event_get_arg(work); + assert(client != NULL); + + if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE)) { + rc = ble_gap_terminate(client->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + assert(rc == 0); + } +} + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub) +{ + int i; + + if (!sub) { + /* NULL means we send on all subnets */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) { + bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]); + } + } + + return; + } + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + beacon_send(clients[i].conn_handle, sub); + } + } +} + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub) +{ + sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING; + sub->node_id_start = k_uptime_get_32(); + + /* Prioritize the recently enabled subnet */ + next_idx = sub - bt_mesh.sub; +} + +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub) +{ + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + sub->node_id_start = 0; +} + +int bt_mesh_proxy_identity_enable(void) +{ + int i, count = 0; + + BT_DBG(""); + + if (!bt_mesh_is_provisioned()) { + return -EAGAIN; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) { + continue; + } + + bt_mesh_proxy_identity_start(sub); + count++; + } + + if (count) { + bt_mesh_adv_update(); + } + + return 0; +} + +#endif /* GATT_PROXY */ + +static void proxy_complete_pdu(struct bt_mesh_proxy_client *client) +{ + switch (client->msg_type) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + case BT_MESH_PROXY_NET_PDU: + BT_INFO("Mesh Network PDU"); + bt_mesh_net_recv(client->buf, 0, BT_MESH_NET_IF_PROXY); + break; + case BT_MESH_PROXY_BEACON: + BT_INFO("Mesh Beacon PDU"); + bt_mesh_beacon_recv(client->buf); + break; + case BT_MESH_PROXY_CONFIG: + BT_INFO("Mesh Configuration PDU"); + proxy_cfg(client); + break; +#endif +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + case BT_MESH_PROXY_PROV: + BT_INFO("Mesh Provisioning PDU"); + bt_mesh_pb_gatt_recv(client->conn_handle, client->buf); + break; +#endif + default: + BT_WARN("Unhandled Message Type 0x%02x", client->msg_type); + break; + } + + net_buf_simple_init(client->buf, 0); +} + +static int proxy_recv(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + struct bt_mesh_proxy_client *client; + const u8_t *data = ctxt->om->om_data; + u16_t len = ctxt->om->om_len; + + client = find_client(conn_handle); + + if (!client) { + return -ENOTCONN; + } + + if (len < 1) { + BT_WARN("Too small Proxy PDU"); + return -EINVAL; + } + + if ((attr_handle == svc_handles.prov_data_in_h) != + (PDU_TYPE(data) == BT_MESH_PROXY_PROV)) { + BT_WARN("Proxy PDU type doesn't match GATT service"); + return -EINVAL; + } + + if (len - 1 > net_buf_simple_tailroom(client->buf)) { + BT_WARN("Too big proxy PDU"); + return -EINVAL; + } + + switch (PDU_SAR(data)) { + case SAR_COMPLETE: + if (client->buf->om_len) { + BT_WARN("Complete PDU while a pending incomplete one"); + return -EINVAL; + } + + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + + case SAR_FIRST: + if (client->buf->om_len) { + BT_WARN("First PDU while a pending incomplete one"); + return -EINVAL; + } + + k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT); + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + break; + + case SAR_CONT: + if (!client->buf->om_len) { + BT_WARN("Continuation with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in continuation"); + return -EINVAL; + } + + k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + break; + + case SAR_LAST: + if (!client->buf->om_len) { + BT_WARN("Last SAR PDU with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in last SAR PDU"); + return -EINVAL; + } + + k_delayed_work_cancel(&client->sar_timer); + net_buf_simple_add_mem(client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + } + + return len; +} + +static int conn_count; + +static void proxy_connected(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + int i; + + BT_INFO("conn_handle %d", conn_handle); + + conn_count++; + + /* Since we use ADV_OPT_ONE_TIME */ + proxy_adv_enabled = false; + + /* Try to re-enable advertising in case it's possible */ + if (conn_count < CONFIG_BT_MAX_CONN) { + bt_mesh_adv_update(); + } + + for (client = NULL, i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle == BLE_HS_CONN_HANDLE_NONE) { + client = &clients[i]; + break; + } + } + + if (!client) { + BT_ERR("No free Proxy Client objects"); + return; + } + + client->conn_handle = conn_handle; + client->filter_type = NONE; + memset(client->filter, 0, sizeof(client->filter)); + net_buf_simple_init(client->buf, 0); +} + +static void proxy_disconnected(uint16_t conn_handle, int reason) +{ + int i; + bool disconnected = false; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if (client->conn_handle == conn_handle) { + if ((MYNEWT_VAL(BLE_MESH_PB_GATT)) && + client->filter_type == PROV) { + bt_mesh_pb_gatt_close(conn_handle); + } + + k_delayed_work_cancel(&client->sar_timer); + client->conn_handle = BLE_HS_CONN_HANDLE_NONE; + conn_count--; + disconnected = true; + break; + } + } + + if (disconnected) { + BT_INFO("conn_handle %d reason %d", conn_handle, reason); + bt_mesh_adv_update(); + } +} + +struct os_mbuf *bt_mesh_proxy_get_buf(void) +{ + struct os_mbuf *buf = clients[0].buf; + + if (buf != NULL) { + net_buf_simple_init(buf, 0); + } + + return buf; +} + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static void prov_ccc_write(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + + BT_DBG("conn_handle %d", conn_handle); + + /* If a connection exists there must be a client */ + client = find_client(conn_handle); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = PROV; + bt_mesh_pb_gatt_open(conn_handle); + } +} + +int bt_mesh_proxy_prov_enable(void) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_PROV) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 1); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.prov_h, 0xffff); + + gatt_svc = MESH_GATT_PROV; + prov_fast_adv = true; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + clients[i].filter_type = PROV; + } + } + + + return 0; +} + +int bt_mesh_proxy_prov_disable(bool disconnect) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROV) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 0); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.prov_h, 0xffff); + + gatt_svc = MESH_GATT_NONE; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if ((client->conn_handle == BLE_HS_CONN_HANDLE_NONE) + || (client->filter_type != PROV)) { + continue; + } + + if (disconnect) { + rc = ble_gap_terminate(client->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + assert(rc == 0); + } else { + bt_mesh_pb_gatt_close(client->conn_handle); + client->filter_type = NONE; + } + } + + bt_mesh_adv_update(); + + return 0; +} +#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) +static void proxy_ccc_write(uint16_t conn_handle) +{ + struct bt_mesh_proxy_client *client; + + BT_DBG("conn_handle %d", conn_handle); + + client = find_client(conn_handle); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = WHITELIST; + k_work_add_arg(&client->send_beacons, client); + k_work_submit(&client->send_beacons); + } +} + +int bt_mesh_proxy_gatt_enable(void) +{ + uint16_t handle; + int rc; + int i; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_PROXY) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + return -EBUSY; + } + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 1); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff); + + gatt_svc = MESH_GATT_PROXY; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) { + clients[i].filter_type = WHITELIST; + } + } + + return 0; +} + +void bt_mesh_proxy_gatt_disconnect(void) +{ + int rc; + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE) && + (client->filter_type == WHITELIST || + client->filter_type == BLACKLIST)) { + client->filter_type = NONE; + rc = ble_gap_terminate(client->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + assert(rc == 0); + } + } +} + +int bt_mesh_proxy_gatt_disable(void) +{ + uint16_t handle; + int rc; + + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROXY) { + return -EBUSY; + } + + bt_mesh_proxy_gatt_disconnect(); + + rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle); + assert(rc == 0); + ble_gatts_svc_set_visibility(handle, 0); + /* FIXME: figure out end handle */ + ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff); + + gatt_svc = MESH_GATT_NONE; + + return 0; +} + +void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr) +{ + struct bt_mesh_proxy_client *client = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + client = &clients[i]; + if (client->buf == buf) { + break; + } + } + + assert(client); + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == WHITELIST) { + filter_add(client, addr); + } else if (client->filter_type == BLACKLIST) { + filter_remove(client, addr); + } +} + +static bool client_filter_match(struct bt_mesh_proxy_client *client, + u16_t addr) +{ + int i; + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == BLACKLIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return false; + } + } + + return true; + } + + if (addr == BT_MESH_ADDR_ALL_NODES) { + return true; + } + + if (client->filter_type == WHITELIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return true; + } + } + } + + return false; +} + +bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst) +{ + bool relayed = false; + int i; + + BT_DBG("%u bytes to dst 0x%04x", buf->om_len, dst); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + struct os_mbuf *msg; + + if (client->conn_handle == BLE_HS_CONN_HANDLE_NONE) { + continue; + } + + if (!client_filter_match(client, dst)) { + continue; + } + + /* Proxy PDU sending modifies the original buffer, + * so we need to make a copy. + */ + msg = NET_BUF_SIMPLE(32); + net_buf_simple_init(msg, 1); + net_buf_simple_add_mem(msg, buf->om_data, buf->om_len); + + bt_mesh_proxy_send(client->conn_handle, BT_MESH_PROXY_NET_PDU, msg); + os_mbuf_free_chain(msg); + relayed = true; + } + + return relayed; +} + +#endif /* MYNEWT_VAL(BLE_MESH_GATT_PROXY) */ + +static int proxy_send(uint16_t conn_handle, const void *data, u16_t len) +{ + struct os_mbuf *om; + + BT_DBG("%u bytes: %s", len, bt_hex(data, len)); + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + if (gatt_svc == MESH_GATT_PROXY) { + om = ble_hs_mbuf_from_flat(data, len); + assert(om); + ble_gattc_notify_custom(conn_handle, svc_handles.proxy_data_out_h, om); + } +#endif + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (gatt_svc == MESH_GATT_PROV) { + om = ble_hs_mbuf_from_flat(data, len); + assert(om); + ble_gattc_notify_custom(conn_handle, svc_handles.prov_data_out_h, om); + } +#endif + + return 0; +} + +static int proxy_segment_and_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg) +{ + u16_t mtu; + + BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len, + bt_hex(msg->om_data, msg->om_len)); + + /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */ + mtu = ble_att_mtu(conn_handle) - 3; + if (mtu > msg->om_len) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type)); + return proxy_send(conn_handle, msg->om_data, msg->om_len); + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type)); + proxy_send(conn_handle, msg->om_data, mtu); + net_buf_simple_pull(msg, mtu); + + while (msg->om_len) { + if (msg->om_len + 1 < mtu) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type)); + proxy_send(conn_handle, msg->om_data, msg->om_len); + break; + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type)); + proxy_send(conn_handle, msg->om_data, mtu); + net_buf_simple_pull(msg, mtu); + } + + return 0; +} + +int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type, + struct os_mbuf *msg) +{ + struct bt_mesh_proxy_client *client = find_client(conn_handle); + + if (!client) { + BT_ERR("No Proxy Client found"); + return -ENOTCONN; + } + + if ((client->filter_type == PROV) != (type == BT_MESH_PROXY_PROV)) { + BT_ERR("Invalid PDU type for Proxy Client"); + return -EINVAL; + } + + return proxy_segment_and_send(conn_handle, type, msg); +} + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static u8_t prov_svc_data[20] = { 0x27, 0x18, }; + +static const struct bt_data prov_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x27, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)), +}; +#endif /* PB_GATT */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + +#define ID_TYPE_NET 0x00 +#define ID_TYPE_NODE 0x01 + +#define NODE_ID_LEN 19 +#define NET_ID_LEN 11 + +#define NODE_ID_TIMEOUT K_SECONDS(CONFIG_BT_MESH_NODE_ID_TIMEOUT) + +static u8_t proxy_svc_data[NODE_ID_LEN] = { 0x28, 0x18, }; + +static const struct bt_data node_id_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN), +}; + +static const struct bt_data net_id_ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18), + BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN), +}; + +static int node_id_adv(struct bt_mesh_subnet *sub) +{ + u8_t tmp[16]; + int err; + + BT_DBG(""); + + proxy_svc_data[2] = ID_TYPE_NODE; + + err = bt_rand(proxy_svc_data + 11, 8); + if (err) { + return err; + } + + memset(tmp, 0, 6); + memcpy(tmp + 6, proxy_svc_data + 11, 8); + sys_put_be16(bt_mesh_primary_addr(), tmp + 14); + + err = bt_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp); + if (err) { + return err; + } + + memcpy(proxy_svc_data + 3, tmp + 8, 8); + + err = bt_le_adv_start(&fast_adv_param, node_id_ad, + ARRAY_SIZE(node_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Node ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static int net_id_adv(struct bt_mesh_subnet *sub) +{ + int err; + + BT_DBG(""); + + proxy_svc_data[2] = ID_TYPE_NET; + + BT_DBG("Advertising with NetId %s", + bt_hex(sub->keys[sub->kr_flag].net_id, 8)); + + memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8); + + err = bt_le_adv_start(&slow_adv_param, net_id_ad, + ARRAY_SIZE(net_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Network ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static bool advertise_subnet(struct bt_mesh_subnet *sub) +{ + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + return false; + } + + return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING || + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED); +} + +static struct bt_mesh_subnet *next_sub(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub; + + sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)]; + if (advertise_subnet(sub)) { + next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub); + return sub; + } + } + + return NULL; +} + +static int sub_count(void) +{ + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (advertise_subnet(sub)) { + count++; + } + } + + return count; +} + +static s32_t gatt_proxy_advertise(struct bt_mesh_subnet *sub) +{ + s32_t remaining = K_FOREVER; + int subnet_count; + + BT_DBG(""); + + if (conn_count == CONFIG_BT_MAX_CONN) { + BT_DBG("Connectable advertising deferred (max connections)"); + return remaining; + } + + if (!sub) { + BT_WARN("No subnets to advertise on"); + return remaining; + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) { + u32_t active = k_uptime_get_32() - sub->node_id_start; + + if (active < NODE_ID_TIMEOUT) { + remaining = NODE_ID_TIMEOUT - active; + BT_DBG("Node ID active for %u ms, %d ms remaining", + (unsigned) active, (int) remaining); + node_id_adv(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + BT_DBG("Node ID stopped"); + } + } + + if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) { + net_id_adv(sub); + } + + subnet_count = sub_count(); + BT_DBG("sub_count %u", subnet_count); + if (subnet_count > 1) { + s32_t max_timeout; + + /* We use NODE_ID_TIMEOUT as a starting point since it may + * be less than 60 seconds. Divide this period into at least + * 6 slices, but make sure that a slice is at least one + * second long (to avoid excessive rotation). + */ + max_timeout = NODE_ID_TIMEOUT / max(subnet_count, 6); + max_timeout = max(max_timeout, K_SECONDS(1)); + + if (remaining > max_timeout || remaining < 0) { + remaining = max_timeout; + } + } + + BT_DBG("Advertising %d ms for net_idx 0x%04x", + (int) remaining, sub->net_idx); + + return remaining; +} +#endif /* GATT_PROXY */ + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) +static size_t gatt_prov_adv_create(struct bt_data prov_sd[2]) +{ + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + const char *name = CONFIG_BT_DEVICE_NAME; + size_t name_len = strlen(name); + size_t prov_sd_len = 0; + size_t sd_space = 31; + + memcpy(prov_svc_data + 2, prov->uuid, 16); + sys_put_be16(prov->oob_info, prov_svc_data + 18); + + if (prov->uri) { + size_t uri_len = strlen(prov->uri); + + if (uri_len > 29) { + /* There's no way to shorten an URI */ + BT_WARN("Too long URI to fit advertising packet"); + } else { + prov_sd[0].type = BT_DATA_URI; + prov_sd[0].data_len = uri_len; + prov_sd[0].data = (void *)prov->uri; + sd_space -= 2 + uri_len; + prov_sd_len++; + } + } + + if (sd_space > 2 && name_len > 0) { + sd_space -= 2; + + if (sd_space < name_len) { + prov_sd[prov_sd_len].type = BT_DATA_NAME_SHORTENED; + prov_sd[prov_sd_len].data_len = sd_space; + } else { + prov_sd[prov_sd_len].type = BT_DATA_NAME_COMPLETE; + prov_sd[prov_sd_len].data_len = name_len; + } + + prov_sd[prov_sd_len].data = (void *)name; + prov_sd_len++; + } + + return prov_sd_len; +} +#endif /* PB_GATT */ + +s32_t bt_mesh_proxy_adv_start(void) +{ + BT_DBG(""); + + if (gatt_svc == MESH_GATT_NONE) { + return K_FOREVER; + } + +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + if (!bt_mesh_is_provisioned()) { + const struct ble_gap_adv_params *param; + struct bt_data prov_sd[2]; + size_t prov_sd_len; + + if (prov_fast_adv) { + param = &fast_adv_param; + } else { + param = &slow_adv_param; + } + + prov_sd_len = gatt_prov_adv_create(prov_sd); + + if (bt_le_adv_start(param, prov_ad, ARRAY_SIZE(prov_ad), + prov_sd, prov_sd_len) == 0) { + proxy_adv_enabled = true; + + /* Advertise 60 seconds using fast interval */ + if (prov_fast_adv) { + prov_fast_adv = false; + return K_SECONDS(60); + } + } + } +#endif /* PB_GATT */ + +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + if (bt_mesh_is_provisioned()) { + return gatt_proxy_advertise(next_sub()); + } +#endif /* GATT_PROXY */ + + return K_FOREVER; +} + +void bt_mesh_proxy_adv_stop(void) +{ + int err; + + BT_DBG("adv_enabled %u", proxy_adv_enabled); + + if (!proxy_adv_enabled) { + return; + } + + err = bt_le_adv_stop(true); + if (err) { + BT_ERR("Failed to stop advertising (err %d)", err); + } else { + proxy_adv_enabled = false; + } +} + +static void ble_mesh_handle_connect(struct ble_gap_event *event, void *arg) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + /* When EXT ADV is enabled then mesh proxy is connected + * when proxy advertising instance is completed. + * Therefore no need to handle BLE_GAP_EVENT_CONNECT + */ + if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) { + /* Reason 0 means advertising has been completed because + * connection has been established + */ + if (event->adv_complete.reason != 0) { + return; + } + + if (event->adv_complete.instance != BT_MESH_ADV_GATT_INST) { + return; + } + + proxy_connected(event->adv_complete.conn_handle); + } +#else + if (event->type == BLE_GAP_EVENT_CONNECT) { + proxy_connected(event->connect.conn_handle); + } +#endif +} + +int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg) +{ + if ((event->type == BLE_GAP_EVENT_CONNECT) || + (event->type == BLE_GAP_EVENT_ADV_COMPLETE)) { + ble_mesh_handle_connect(event, arg); + } else if (event->type == BLE_GAP_EVENT_DISCONNECT) { + proxy_disconnected(event->disconnect.conn.conn_handle, + event->disconnect.reason); + } else if (event->type == BLE_GAP_EVENT_SUBSCRIBE) { + if (event->subscribe.attr_handle == svc_handles.proxy_data_out_h) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + proxy_ccc_write(event->subscribe.conn_handle); +#endif + } else if (event->subscribe.attr_handle == + svc_handles.prov_data_out_h) { +#if (MYNEWT_VAL(BLE_MESH_PB_GATT)) + prov_ccc_write(event->subscribe.conn_handle); +#endif + } + } + + return 0; +} + +static int +dummy_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* + * We should never never enter this callback - it's attached to notify-only + * characteristic which are notified directly from mbuf. And we can't pass + * NULL as access_cb because gatts will assert on initialize... + */ + BLE_HS_DBG_ASSERT(0); + return 0; +} + +static const struct ble_gatt_svc_def svc_defs [] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_IN_VAL), + .access_cb = proxy_recv, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL), + .access_cb = dummy_access_cb, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL), + .access_cb = proxy_recv, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL), + .access_cb = dummy_access_cb, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, { + 0, /* No more services. */ + }, +}; + +int bt_mesh_proxy_svcs_register(void) +{ + int rc; + + rc = ble_gatts_count_cfg(svc_defs); + assert(rc == 0); + + rc = ble_gatts_add_svcs(svc_defs); + assert(rc == 0); + + return 0; +} + +int bt_mesh_proxy_init(void) +{ + int i; + + for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { +#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) + k_work_init(&clients[i].send_beacons, proxy_send_beacons); +#endif + clients[i].buf = NET_BUF_SIMPLE(CLIENT_BUF_SIZE); + clients[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + + k_delayed_work_init(&clients[i].sar_timer, proxy_sar_timeout); + k_delayed_work_add_arg(&clients[i].sar_timer, &clients[i]); + } + + resolve_svc_handles(); + + ble_gatts_svc_set_visibility(svc_handles.proxy_h, 0); + ble_gatts_svc_set_visibility(svc_handles.prov_h, 0); + + return 0; +} + +#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.h new file mode 100644 index 0000000..e099947 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/proxy.h @@ -0,0 +1,45 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __PROXY_H__ +#define __PROXY_H__ + +#define BT_MESH_PROXY_NET_PDU 0x00 +#define BT_MESH_PROXY_BEACON 0x01 +#define BT_MESH_PROXY_CONFIG 0x02 +#define BT_MESH_PROXY_PROV 0x03 + +#include "../include/mesh/mesh.h" + +int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type, struct os_mbuf *msg); + +int bt_mesh_proxy_prov_enable(void); +int bt_mesh_proxy_prov_disable(bool disconnect); + +int bt_mesh_proxy_gatt_enable(void); +int bt_mesh_proxy_gatt_disable(void); +void bt_mesh_proxy_gatt_disconnect(void); + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub); + +struct os_mbuf *bt_mesh_proxy_get_buf(void); + +s32_t bt_mesh_proxy_adv_start(void); +void bt_mesh_proxy_adv_stop(void); + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub); +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub); + +bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst); +void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr); + +int bt_mesh_proxy_init(void); + +int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg); + +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.c new file mode 100644 index 0000000..2130e9e --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.c @@ -0,0 +1,2086 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#define MESH_LOG_MODULE BLE_MESH_SETTINGS_LOG + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +#include "../include/mesh/mesh.h" +#include "../include/mesh/glue.h" +#include "net.h" +#include "crypto.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "settings.h" +#include "nodes.h" + +#include "config/config.h" + +/* Tracking of what storage changes are pending for App and Net Keys. We + * track this in a separate array here instead of within the respective + * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key + * gets deleted its struct becomes invalid and may be reused for other keys. + */ +static struct key_update { + u16_t key_idx:12, /* AppKey or NetKey Index */ + valid:1, /* 1 if this entry is valid, 0 if not */ + app_key:1, /* 1 if this is an AppKey, 0 if a NetKey */ + clear:1; /* 1 if key needs clearing, 0 if storing */ +} key_updates[CONFIG_BT_MESH_APP_KEY_COUNT + CONFIG_BT_MESH_SUBNET_COUNT]; + +static struct k_delayed_work pending_store; + +/* Mesh network storage information */ +struct net_val { + u16_t primary_addr; + u8_t dev_key[16]; +} __packed; + +/* Sequence number storage */ +struct seq_val { + u8_t val[3]; +} __packed; + +/* Heartbeat Publication storage */ +struct hb_pub_val { + u16_t dst; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx:12, + indefinite:1; +}; + +/* Miscelaneous configuration server model states */ +struct cfg_val { + u8_t net_transmit; + u8_t relay; + u8_t relay_retransmit; + u8_t beacon; + u8_t gatt_proxy; + u8_t frnd; + u8_t default_ttl; +}; + +/* IV Index & IV Update storage */ +struct iv_val { + u32_t iv_index; + u8_t iv_update:1, + iv_duration:7; +} __packed; + +/* Replay Protection List storage */ +struct rpl_val { + u32_t seq:24, + old_iv:1; +}; + +/* NetKey storage information */ +struct net_key_val { + u8_t kr_flag:1, + kr_phase:7; + u8_t val[2][16]; +} __packed; + +/* AppKey storage information */ +struct app_key_val { + u16_t net_idx; + bool updated; + u8_t val[2][16]; +} __packed; + +struct mod_pub_val { + u16_t addr; + u16_t key; + u8_t ttl; + u8_t retransmit; + u8_t period; + u8_t period_div:4, + cred:1; +}; + +/* Virtual Address information */ +struct va_val { + u16_t ref; + u16_t addr; + u8_t uuid[16]; +} __packed; + +/* Node storage information */ +struct node_val { + u16_t net_idx; + u8_t dev_key[16]; + u8_t num_elem; +} __packed; + +struct node_update { + u16_t addr; + bool clear; +}; + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) +static struct node_update node_updates[CONFIG_BT_MESH_NODE_COUNT]; +#else +static struct node_update node_updates[0]; +#endif + +/* We need this so we don't overwrite app-hardcoded values in case FCB + * contains a history of changes but then has a NULL at the end. + */ +static struct { + bool valid; + struct cfg_val cfg; +} stored_cfg; + +static int net_set(int argc, char **argv, char *val) +{ + struct net_val net; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh_comp_unprovision(); + memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + return 0; + } + + len = sizeof(net); + err = settings_bytes_from_str(val, &net, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(net)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net)); + return -EINVAL; + } + + memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key)); + bt_mesh_comp_provision(net.primary_addr); + + BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr); + BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16)); + + return 0; +} + +static int iv_set(int argc, char **argv, char *val) +{ + struct iv_val iv; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh.iv_index = 0U; + atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); + return 0; + } + + len = sizeof(iv); + err = settings_bytes_from_str(val, &iv, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(iv)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv)); + return -EINVAL; + } + + bt_mesh.iv_index = iv.iv_index; + atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update); + bt_mesh.ivu_duration = iv.iv_duration; + + BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours", + (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration); + + return 0; +} + +static int seq_set(int argc, char **argv, char *val) +{ + struct seq_val seq; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!val) { + bt_mesh.seq = 0; + return 0; + } + + len = sizeof(seq); + err = settings_bytes_from_str(val, &seq, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(seq)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq)); + return -EINVAL; + } + + bt_mesh.seq = ((u32_t)seq.val[0] | ((u32_t)seq.val[1] << 8) | + ((u32_t)seq.val[2] << 16)); + + if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) { + /* Make sure we have a large enough sequence number. We + * subtract 1 so that the first transmission causes a write + * to the settings storage. + */ + bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE - + (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)); + bt_mesh.seq--; + } + + BT_DBG("Sequence Number 0x%06x", bt_mesh.seq); + + return 0; +} + +static struct bt_mesh_rpl *rpl_find(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (bt_mesh.rpl[i].src == src) { + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static struct bt_mesh_rpl *rpl_alloc(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (!bt_mesh.rpl[i].src) { + bt_mesh.rpl[i].src = src; + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static int rpl_set(int argc, char **argv, char *val) +{ + struct bt_mesh_rpl *entry; + struct rpl_val rpl; + int len, err; + u16_t src; + + if (argc < 1) { + BT_ERR("Invalid argc (%d)", argc); + return -ENOENT; + } + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + src = strtol(argv[0], NULL, 16); + entry = rpl_find(src); + + if (!val) { + if (entry) { + memset(entry, 0, sizeof(*entry)); + } else { + BT_WARN("Unable to find RPL entry for 0x%04x", src); + } + + return 0; + } + + if (!entry) { + entry = rpl_alloc(src); + if (!entry) { + BT_ERR("Unable to allocate RPL entry for 0x%04x", src); + return -ENOMEM; + } + } + + len = sizeof(rpl); + err = settings_bytes_from_str(val, &rpl, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(rpl)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl)); + return -EINVAL; + } + + entry->seq = rpl.seq; + entry->old_iv = rpl.old_iv; + + BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src, + (unsigned) entry->seq, entry->old_iv); + + return 0; +} + +static int net_key_set(int argc, char **argv, char *val) +{ + struct bt_mesh_subnet *sub; + struct net_key_val key; + int len, i, err; + u16_t net_idx; + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + net_idx = strtol(argv[0], NULL, 16); + sub = bt_mesh_subnet_get(net_idx); + + if (!val) { + if (!sub) { + BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx); + return -ENOENT; + } + + BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx); + bt_mesh_subnet_del(sub, false); + return 0; + } + + len = sizeof(key); + err = settings_bytes_from_str(val, &key, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(key)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key)); + return -EINVAL; + } + + if (sub) { + BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx); + + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net, &key.val[0], 16); + memcpy(sub->keys[1].net, &key.val[1], 16); + + return 0; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) { + sub = &bt_mesh.sub[i]; + break; + } + } + + if (!sub) { + BT_ERR("No space to allocate a new subnet"); + return -ENOMEM; + } + + sub->net_idx = net_idx; + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net, &key.val[0], 16); + memcpy(sub->keys[1].net, &key.val[1], 16); + + BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); + + return 0; +} + +static int app_key_set(int argc, char **argv, char *val) +{ + struct bt_mesh_app_key *app; + struct app_key_val key; + u16_t app_idx; + int len, err; + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + app_idx = strtol(argv[0], NULL, 16); + + if (!val) { + BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx); + + app = bt_mesh_app_key_find(app_idx); + if (app) { + bt_mesh_app_key_del(app, false); + } + + return 0; + } + + len = sizeof(key); + err = settings_bytes_from_str(val, &key, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(key)) { + BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key)); + return -EINVAL; + } + + app = bt_mesh_app_key_find(app_idx); + if (!app) { + app = bt_mesh_app_key_alloc(app_idx); + } + + if (!app) { + BT_ERR("No space for a new app key"); + return -ENOMEM; + } + + app->net_idx = key.net_idx; + app->app_idx = app_idx; + app->updated = key.updated; + memcpy(app->keys[0].val, key.val[0], 16); + memcpy(app->keys[1].val, key.val[1], 16); + + bt_mesh_app_id(app->keys[0].val, &app->keys[0].id); + bt_mesh_app_id(app->keys[1].val, &app->keys[1].id); + + BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); + + return 0; +} + +static int hb_pub_set(int argc, char **argv, char *val) +{ + struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get(); + struct hb_pub_val hb_val; + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!pub) { + return -ENOENT; + } + + if (!val) { + pub->dst = BT_MESH_ADDR_UNASSIGNED; + pub->count = 0; + pub->ttl = 0; + pub->period = 0; + pub->feat = 0; + + BT_DBG("Cleared heartbeat publication"); + return 0; + } + + len = sizeof(hb_val); + err = settings_bytes_from_str(val, &hb_val, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(hb_val)) { + BT_ERR("Unexpected value length (%d != %zu)", len, + sizeof(hb_val)); + return -EINVAL; + } + + pub->dst = hb_val.dst; + pub->period = hb_val.period; + pub->ttl = hb_val.ttl; + pub->feat = hb_val.feat; + pub->net_idx = hb_val.net_idx; + + if (hb_val.indefinite) { + pub->count = 0xffff; + } else { + pub->count = 0; + } + + BT_DBG("Restored heartbeat publication"); + + return 0; +} + +static int cfg_set(int argc, char **argv, char *val) +{ + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + int len, err; + + BT_DBG("val %s", val ? val : "(null)"); + + if (!cfg) { + return -ENOENT; + } + + if (!val) { + stored_cfg.valid = false; + BT_DBG("Cleared configuration state"); + return 0; + } + + len = sizeof(stored_cfg.cfg); + err = settings_bytes_from_str(val, &stored_cfg.cfg, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return err; + } + + if (len != sizeof(stored_cfg.cfg)) { + BT_ERR("Unexpected value length (%d != %zu)", len, + sizeof(stored_cfg.cfg)); + return -EINVAL; + } + + stored_cfg.valid = true; + BT_DBG("Restored configuration state"); + + return 0; +} + +static int mod_set_bind(struct bt_mesh_model *mod, char *val) +{ + int len, err, i; + + /* Start with empty array regardless of cleared or set value */ + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + mod->keys[i] = BT_MESH_KEY_UNUSED; + } + + if (!val) { + BT_DBG("Cleared bindings for model"); + return 0; + } + + len = sizeof(mod->keys); + err = settings_bytes_from_str(val, mod->keys, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0])); + return 0; +} + +static int mod_set_sub(struct bt_mesh_model *mod, char *val) +{ + int len, err; + + /* Start with empty array regardless of cleared or set value */ + memset(mod->groups, 0, sizeof(mod->groups)); + + if (!val) { + BT_DBG("Cleared subscriptions for model"); + return 0; + } + + len = sizeof(mod->groups); + err = settings_bytes_from_str(val, mod->groups, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + BT_DBG("Decoded %u subscribed group addresses for model", + len / sizeof(mod->groups[0])); + return 0; +} + +static int mod_set_pub(struct bt_mesh_model *mod, char *val) +{ + struct mod_pub_val pub; + int len, err; + + if (!mod->pub) { + BT_WARN("Model has no publication context!"); + return -EINVAL; + } + + if (!val) { + mod->pub->addr = BT_MESH_ADDR_UNASSIGNED; + mod->pub->key = 0; + mod->pub->cred = 0; + mod->pub->ttl = 0; + mod->pub->period = 0; + mod->pub->retransmit = 0; + mod->pub->count = 0; + + BT_DBG("Cleared publication for model"); + return 0; + } + + len = sizeof(pub); + err = settings_bytes_from_str(val, &pub, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + if (len != sizeof(pub)) { + BT_ERR("Invalid length for model publication"); + return -EINVAL; + } + + mod->pub->addr = pub.addr; + mod->pub->key = pub.key; + mod->pub->cred = pub.cred; + mod->pub->ttl = pub.ttl; + mod->pub->period = pub.period; + mod->pub->retransmit = pub.retransmit; + mod->pub->count = 0; + + BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x", + pub.addr, pub.key); + + return 0; +} + +static int mod_set(bool vnd, int argc, char **argv, char *val) +{ + struct bt_mesh_model *mod; + u8_t elem_idx, mod_idx; + u16_t mod_key; + + if (argc < 2) { + BT_ERR("Too small argc (%d)", argc); + return -ENOENT; + } + + mod_key = strtol(argv[0], NULL, 16); + elem_idx = mod_key >> 8; + mod_idx = mod_key; + + BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u", + mod_key, elem_idx, mod_idx); + + mod = bt_mesh_model_get(vnd, elem_idx, mod_idx); + if (!mod) { + BT_ERR("Failed to get model for elem_idx %u mod_idx %u", + elem_idx, mod_idx); + return -ENOENT; + } + + if (!strcmp(argv[1], "bind")) { + return mod_set_bind(mod, val); + } + + if (!strcmp(argv[1], "sub")) { + return mod_set_sub(mod, val); + } + + if (!strcmp(argv[1], "pub")) { + return mod_set_pub(mod, val); + } + + if (!strcmp(argv[1], "data")) { + mod->flags |= BT_MESH_MOD_DATA_PRESENT; + + if (mod->cb && mod->cb->settings_set) { + return mod->cb->settings_set(mod, val); + } + } + + BT_WARN("Unknown module key %s", argv[1]); + return -ENOENT; +} + +static int sig_mod_set(int argc, char **argv, char *val) +{ + return mod_set(false, argc, argv, val); +} + +static int vnd_mod_set(int argc, char **argv, char *val) +{ + return mod_set(true, argc, argv, val); +} + +#if CONFIG_BT_MESH_LABEL_COUNT > 0 +static int va_set(int argc, char **argv, char *val) +{ + struct va_val va; + struct label *lab; + u16_t index; + int len, err; + + if (argc < 1) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + index = strtol(argv[0], NULL, 16); + + if (val == NULL) { + BT_WARN("Mesh Virtual Address length = 0"); + return 0; + } + + err = settings_bytes_from_str(val, &va, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + if (len != sizeof(struct va_val)) { + BT_ERR("Invalid length for virtual address"); + return -EINVAL; + } + + if (va.ref == 0) { + BT_WARN("Ignore Mesh Virtual Address ref = 0"); + return 0; + } + + lab = get_label(index); + if (lab == NULL) { + BT_WARN("Out of labels buffers"); + return -ENOBUFS; + } + + memcpy(lab->uuid, va.uuid, 16); + lab->addr = va.addr; + lab->ref = va.ref; + + BT_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x", + lab->addr, lab->ref); + + return 0; +} +#endif + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) +static int node_set(int argc, char **argv, char *str) +{ + struct bt_mesh_node *node; + struct node_val val; + u16_t addr; + int len, err; + + if (argc < 1) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + addr = strtol(argv[0], NULL, 16); + + if (str == NULL) { + BT_DBG("val (null)"); + BT_DBG("Deleting node 0x%04x", addr); + + node = bt_mesh_node_find(addr); + if (node) { + bt_mesh_node_del(node, false); + } + + return 0; + } + + err = settings_bytes_from_str(str, &val, &len); + if (err) { + BT_ERR("Failed to decode value %s (err %d)", val, err); + return -EINVAL; + } + + if (len != sizeof(struct node_val)) { + BT_ERR("Invalid length for node_val"); + return -EINVAL; + } + + node = bt_mesh_node_find(addr); + if (!node) { + node = bt_mesh_node_alloc(addr, val.num_elem, val.net_idx); + } + + if (!node) { + BT_ERR("No space for a new node"); + return -ENOMEM; + } + + memcpy(node->dev_key, &val.dev_key, 16); + + BT_DBG("Node 0x%04x recovered from storage", addr); + + return 0; +} +#endif + +const struct mesh_setting { + const char *name; + int (*func)(int argc, char **argv, char *val); +} settings[] = { + { "Net", net_set }, + { "IV", iv_set }, + { "Seq", seq_set }, + { "RPL", rpl_set }, + { "NetKey", net_key_set }, + { "AppKey", app_key_set }, + { "HBPub", hb_pub_set }, + { "Cfg", cfg_set }, + { "s", sig_mod_set }, + { "v", vnd_mod_set }, +#if CONFIG_BT_MESH_LABEL_COUNT > 0 + { "Va", va_set }, +#endif +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) + { "Node", node_set }, +#endif +}; + +static int mesh_set(int argc, char **argv, char *val) +{ + int i; + + if (argc < 1) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); + + for (i = 0; i < ARRAY_SIZE(settings); i++) { + if (!strcmp(settings[i].name, argv[0])) { + argc--; + argv++; + + return settings[i].func(argc, argv, val); + } + } + + BT_WARN("No matching handler for key %s", argv[0]); + + return -ENOENT; +} + +static int subnet_init(struct bt_mesh_subnet *sub) +{ + int err; + + err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net); + if (err) { + BT_ERR("Unable to generate keys for subnet"); + return -EIO; + } + + if (sub->kr_phase != BT_MESH_KR_NORMAL) { + err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net); + if (err) { + BT_ERR("Unable to generate keys for subnet"); + memset(&sub->keys[0], 0, sizeof(sub->keys[0])); + return -EIO; + } + } + + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { + sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update && + mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) { + s32_t ms = bt_mesh_model_pub_period_get(mod); + if (ms) { + BT_DBG("Starting publish timer (period %u ms)", + (unsigned) ms); + k_delayed_work_submit(&mod->pub->timer, ms); + } + } + + if (mod->cb && mod->cb->settings_commit) { + mod->cb->settings_commit(mod); + } +} + +static int mesh_commit(void) +{ + struct bt_mesh_hb_pub *hb_pub; + struct bt_mesh_cfg_srv *cfg; + int i; + + BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx); + + if (bt_mesh.sub[0].net_idx == BT_MESH_KEY_UNUSED) { + /* Nothing to do since we're not yet provisioned */ + return 0; + } + + if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { + bt_mesh_proxy_prov_disable(true); + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + int err; + + if (sub->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + err = subnet_init(sub); + if (err) { + BT_ERR("Failed to init subnet 0x%03x", sub->net_idx); + } + } + + if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { + k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); + } + + bt_mesh_model_foreach(commit_mod, NULL); + + hb_pub = bt_mesh_hb_pub_get(); + if (hb_pub && hb_pub->dst != BT_MESH_ADDR_UNASSIGNED && + hb_pub->count && hb_pub->period) { + BT_DBG("Starting heartbeat publication"); + k_work_submit(&hb_pub->timer.work); + } + + cfg = bt_mesh_cfg_get(); + if (cfg && stored_cfg.valid) { + cfg->net_transmit = stored_cfg.cfg.net_transmit; + cfg->relay = stored_cfg.cfg.relay; + cfg->relay_retransmit = stored_cfg.cfg.relay_retransmit; + cfg->beacon = stored_cfg.cfg.beacon; + cfg->gatt_proxy = stored_cfg.cfg.gatt_proxy; + cfg->frnd = stored_cfg.cfg.frnd; + cfg->default_ttl = stored_cfg.cfg.default_ttl; + } + + atomic_set_bit(bt_mesh.flags, BT_MESH_VALID); + + bt_mesh_net_start(); + + return 0; +} + +/* Pending flags that use K_NO_WAIT as the storage timeout */ +#define NO_WAIT_PENDING_BITS (BIT(BT_MESH_NET_PENDING) | \ + BIT(BT_MESH_IV_PENDING) | \ + BIT(BT_MESH_SEQ_PENDING)) + +/* Pending flags that use CONFIG_BT_MESH_STORE_TIMEOUT */ +#define GENERIC_PENDING_BITS (BIT(BT_MESH_KEYS_PENDING) | \ + BIT(BT_MESH_HB_PUB_PENDING) | \ + BIT(BT_MESH_CFG_PENDING) | \ + BIT(BT_MESH_MOD_PENDING) | \ + BIT(BT_MESH_NODES_PENDING)) + +static void schedule_store(int flag) +{ + s32_t timeout, remaining; + + atomic_set_bit(bt_mesh.flags, flag); + + if (atomic_get(bt_mesh.flags) & NO_WAIT_PENDING_BITS) { + timeout = K_NO_WAIT; + } else if (atomic_test_bit(bt_mesh.flags, BT_MESH_RPL_PENDING) && + (!(atomic_get(bt_mesh.flags) & GENERIC_PENDING_BITS) || + (CONFIG_BT_MESH_RPL_STORE_TIMEOUT < + CONFIG_BT_MESH_STORE_TIMEOUT))) { + timeout = K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT); + } else { + timeout = K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT); + } + + remaining = k_delayed_work_remaining_get(&pending_store); + if (remaining && remaining < timeout) { + BT_DBG("Not rescheduling due to existing earlier deadline"); + return; + } + + BT_DBG("Waiting %d seconds", (int) (timeout / MSEC_PER_SEC)); + + k_delayed_work_submit(&pending_store, timeout); +} + +static void clear_iv(void) +{ + int err; + + err = settings_save_one("bt_mesh/IV", NULL); + if (err) { + BT_ERR("Failed to clear IV"); + } else { + BT_DBG("Cleared IV"); + } +} + +static void clear_net(void) +{ + int err; + + err = settings_save_one("bt_mesh/Net", NULL); + if (err) { + BT_ERR("Failed to clear Network"); + } else { + BT_DBG("Cleared Network"); + } +} + +static void store_pending_net(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))]; + struct net_val net; + char *str; + int err; + + BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(), + bt_hex(bt_mesh.dev_key, 16)); + + net.primary_addr = bt_mesh_primary_addr(); + memcpy(net.dev_key, bt_mesh.dev_key, 16); + + str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Network as value"); + return; + } + + BT_DBG("Saving Network as value %s", str); + err = settings_save_one("bt_mesh/Net", str); + if (err) { + BT_ERR("Failed to store Network"); + } else { + BT_DBG("Stored Network"); + } +} + +void bt_mesh_store_net(void) +{ + schedule_store(BT_MESH_NET_PENDING); +} + +static void store_pending_iv(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))]; + struct iv_val iv; + char *str; + int err; + + iv.iv_index = bt_mesh.iv_index; + iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); + iv.iv_duration = bt_mesh.ivu_duration; + + str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode IV as value"); + return; + } + + BT_DBG("Saving IV as value %s", str); + err = settings_save_one("bt_mesh/IV", str); + if (err) { + BT_ERR("Failed to store IV"); + } else { + BT_DBG("Stored IV"); + } +} + +void bt_mesh_store_iv(bool only_duration) +{ + schedule_store(BT_MESH_IV_PENDING); + + if (!only_duration) { + /* Always update Seq whenever IV changes */ + schedule_store(BT_MESH_SEQ_PENDING); + } +} + +static void store_pending_seq(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))]; + struct seq_val seq; + char *str; + int err; + + seq.val[0] = bt_mesh.seq; + seq.val[1] = bt_mesh.seq >> 8; + seq.val[2] = bt_mesh.seq >> 16; + + str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Seq as value"); + return; + } + + BT_DBG("Saving Seq as value %s", str); + err = settings_save_one("bt_mesh/Seq", str); + if (err) { + BT_ERR("Failed to store Seq"); + } else { + BT_DBG("Stored Seq"); + } +} + +void bt_mesh_store_seq(void) +{ + if (CONFIG_BT_MESH_SEQ_STORE_RATE && + (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) { + return; + } + + schedule_store(BT_MESH_SEQ_PENDING); +} + +static void store_rpl(struct bt_mesh_rpl *entry) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))]; + struct rpl_val rpl; + char path[18]; + char *str; + int err; + + BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src, + (unsigned) entry->seq, entry->old_iv); + + rpl.seq = entry->seq; + rpl.old_iv = entry->old_iv; + + str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode RPL as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src); + + BT_DBG("Saving RPL %s as value %s", path, str); + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store RPL"); + } else { + BT_DBG("Stored RPL"); + } +} + +static void clear_rpl(void) +{ + int i, err; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + char path[18]; + + if (!rpl->src) { + continue; + } + + snprintk(path, sizeof(path), "bt_mesh/RPL/%x", rpl->src); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear RPL"); + } else { + BT_DBG("Cleared RPL"); + } + + memset(rpl, 0, sizeof(*rpl)); + } +} + +static void store_pending_rpl(void) +{ + int i; + + BT_DBG(""); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->store) { + rpl->store = false; + store_rpl(rpl); + } + } +} + +static void store_pending_hb_pub(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))]; + struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get(); + struct hb_pub_val val; + char *str; + int err; + + if (!pub) { + return; + } + + if (pub->dst == BT_MESH_ADDR_UNASSIGNED) { + str = NULL; + } else { + val.indefinite = (pub->count == 0xffff); + val.dst = pub->dst; + val.period = pub->period; + val.ttl = pub->ttl; + val.feat = pub->feat; + val.net_idx = pub->net_idx; + + str = settings_str_from_bytes(&val, sizeof(val), + buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode hb pub as value"); + return; + } + } + + BT_DBG("Saving Heartbeat Publication as value %s", + str ? str : "(null)"); + err = settings_save_one("bt_mesh/HBPub", str); + if (err) { + BT_ERR("Failed to store Heartbeat Publication"); + } else { + BT_DBG("Stored Heartbeat Publication"); + } +} + +static void store_pending_cfg(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))]; + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + struct cfg_val val; + char *str; + int err; + + if (!cfg) { + return; + } + + val.net_transmit = cfg->net_transmit; + val.relay = cfg->relay; + val.relay_retransmit = cfg->relay_retransmit; + val.beacon = cfg->beacon; + val.gatt_proxy = cfg->gatt_proxy; + val.frnd = cfg->frnd; + val.default_ttl = cfg->default_ttl; + + str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode configuration as value"); + return; + } + + BT_DBG("Saving configuration as value %s", str); + err = settings_save_one("bt_mesh/Cfg", str); + if (err) { + BT_ERR("Failed to store configuration"); + } else { + BT_DBG("Stored configuration"); + } +} + +static void clear_cfg(void) +{ + int err; + + err = settings_save_one("bt_mesh/Cfg", NULL); + if (err) { + BT_ERR("Failed to clear configuration"); + } else { + BT_DBG("Cleared configuration"); + } +} + +static void clear_app_key(u16_t app_idx) +{ + char path[20]; + int err; + + BT_DBG("AppKeyIndex 0x%03x", app_idx); + + snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx); + } else { + BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx); + } +} + +static void clear_net_key(u16_t net_idx) +{ + char path[20]; + int err; + + BT_DBG("NetKeyIndex 0x%03x", net_idx); + + snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx); + } else { + BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx); + } +} + +static void store_net_key(struct bt_mesh_subnet *sub) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))]; + struct net_key_val key; + char path[20]; + char *str; + int err; + + BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx, + bt_hex(sub->keys[0].net, 16)); + + memcpy(&key.val[0], sub->keys[0].net, 16); + memcpy(&key.val[1], sub->keys[1].net, 16); + key.kr_flag = sub->kr_flag; + key.kr_phase = sub->kr_phase; + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode NetKey as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", sub->net_idx); + + BT_DBG("Saving NetKey %s as value %s", path, str); + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store NetKey"); + } else { + BT_DBG("Stored NetKey"); + } +} + +static void store_app_key(struct bt_mesh_app_key *app) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; + struct app_key_val key; + char path[20]; + char *str; + int err; + + key.net_idx = app->net_idx; + key.updated = app->updated; + memcpy(key.val[0], app->keys[0].val, 16); + memcpy(key.val[1], app->keys[1].val, 16); + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode AppKey as value"); + return; + } + + snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app->app_idx); + + BT_DBG("Saving AppKey %s as value %s", path, str); + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store AppKey"); + } else { + BT_DBG("Stored AppKey"); + } +} + +static void store_pending_keys(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + continue; + } + + if (update->clear) { + if (update->app_key) { + clear_app_key(update->key_idx); + } else { + clear_net_key(update->key_idx); + } + } else { + if (update->app_key) { + struct bt_mesh_app_key *key; + + key = bt_mesh_app_key_find(update->key_idx); + if (key) { + store_app_key(key); + } else { + BT_WARN("AppKeyIndex 0x%03x not found", + update->key_idx); + } + + } else { + struct bt_mesh_subnet *sub; + + sub = bt_mesh_subnet_get(update->key_idx); + if (sub) { + store_net_key(sub); + } else { + BT_WARN("NetKeyIndex 0x%03x not found", + update->key_idx); + } + } + } + + update->valid = 0; + } +} + +static void store_node(struct bt_mesh_node *node) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))]; + struct node_val val; + char path[20]; + char *str; + int err; + + val.net_idx = node->net_idx; + val.num_elem = node->num_elem; + memcpy(val.dev_key, node->dev_key, 16); + + snprintk(path, sizeof(path), "bt_mesh/Node/%x", node->addr); + + str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Node as value"); + return; + } + + + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store Node %s value", path); + } else { + BT_DBG("Stored Node %s value", path); + } +} + +static void clear_node(u16_t addr) +{ + char path[20]; + int err; + + BT_DBG("Node 0x%04x", addr); + + snprintk(path, sizeof(path), "bt_mesh/Node/%x", addr); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear Node 0x%04x", addr); + } else { + BT_DBG("Cleared Node 0x%04x", addr); + } +} + +static void store_pending_nodes(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(node_updates); ++i) { + struct node_update *update = &node_updates[i]; + + if (update->addr == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (update->clear) { + clear_node(update->addr); + } else { + struct bt_mesh_node *node; + + node = bt_mesh_node_find(update->addr); + if (node) { + store_node(node); + } else { + BT_WARN("Node 0x%04x not found", update->addr); + } + } + + update->addr = BT_MESH_ADDR_UNASSIGNED; + } +} + +static struct node_update *node_update_find(u16_t addr, + struct node_update **free_slot) +{ + struct node_update *match; + int i; + + match = NULL; + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(node_updates); i++) { + struct node_update *update = &node_updates[i]; + + if (update->addr == BT_MESH_ADDR_UNASSIGNED) { + *free_slot = update; + continue; + } + + if (update->addr == addr) { + match = update; + } + } + + return match; +} + +static void encode_mod_path(struct bt_mesh_model *mod, bool vnd, + const char *key, char *path, size_t path_len) +{ + u16_t mod_key = (((u16_t)mod->elem_idx << 8) | mod->mod_idx); + + if (vnd) { + snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key); + } else { + snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key); + } +} + +static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd) +{ + u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; + char buf[BT_SETTINGS_SIZE(sizeof(keys))]; + char path[20]; + int i, count, err; + char *val; + + for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] != BT_MESH_KEY_UNUSED) { + keys[count++] = mod->keys[i]; + } + } + + if (count) { + val = settings_str_from_bytes(keys, count * sizeof(keys[0]), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model bindings as value"); + return; + } + } else { + val = NULL; + } + + encode_mod_path(mod, vnd, "bind", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + err = settings_save_one(path, val); + if (err) { + BT_ERR("Failed to store bind"); + } else { + BT_DBG("Stored bind"); + } +} + +static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd) +{ + u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; + char buf[BT_SETTINGS_SIZE(sizeof(groups))]; + char path[20]; + int i, count, err; + char *val; + + for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) { + if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { + groups[count++] = mod->groups[i]; + } + } + + if (count) { + val = settings_str_from_bytes(groups, count * sizeof(groups[0]), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model subscription as value"); + return; + } + } else { + val = NULL; + } + + encode_mod_path(mod, vnd, "sub", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + err = settings_save_one(path, val); + if (err) { + BT_ERR("Failed to store sub"); + } else { + BT_DBG("Stored sub"); + } +} + +static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))]; + struct mod_pub_val pub; + char path[20]; + char *val; + int err; + + if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + val = NULL; + } else { + pub.addr = mod->pub->addr; + pub.key = mod->pub->key; + pub.ttl = mod->pub->ttl; + pub.retransmit = mod->pub->retransmit; + pub.period = mod->pub->period; + pub.period_div = mod->pub->period_div; + pub.cred = mod->pub->cred; + + val = settings_str_from_bytes(&pub, sizeof(pub), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model publication as value"); + return; + } + } + + encode_mod_path(mod, vnd, "pub", path, sizeof(path)); + + BT_DBG("Saving %s as %s", path, val ? val : "(null)"); + err = settings_save_one(path, val); + if (err) { + BT_ERR("Failed to store pub"); + } else { + BT_DBG("Stored pub"); + } +} + +static void store_pending_mod(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, bool vnd, + bool primary, void *user_data) +{ + if (!mod->flags) { + return; + } + + if (mod->flags & BT_MESH_MOD_BIND_PENDING) { + mod->flags &= ~BT_MESH_MOD_BIND_PENDING; + store_pending_mod_bind(mod, vnd); + } + + if (mod->flags & BT_MESH_MOD_SUB_PENDING) { + mod->flags &= ~BT_MESH_MOD_SUB_PENDING; + store_pending_mod_sub(mod, vnd); + } + + if (mod->flags & BT_MESH_MOD_PUB_PENDING) { + mod->flags &= ~BT_MESH_MOD_PUB_PENDING; + store_pending_mod_pub(mod, vnd); + } +} + +#define IS_VA_DEL(_label) ((_label)->ref == 0) +static void store_pending_va(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct va_val))]; + struct label *lab; + struct va_val va; + char path[18]; + char *val; + u16_t i; + int err = 0; + + for (i = 0; (lab = get_label(i)) != NULL; i++) { + if (!atomic_test_and_clear_bit(lab->flags, + BT_MESH_VA_CHANGED)) { + continue; + } + + snprintk(path, sizeof(path), "bt_mesh/Va/%x", i); + + if (IS_VA_DEL(lab)) { + val = NULL; + } else { + va.ref = lab->ref; + va.addr = lab->addr; + memcpy(va.uuid, lab->uuid, 16); + + val = settings_str_from_bytes(&va, sizeof(va), + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model publication as value"); + return; + } + + err = settings_save_one(path, val); + } + + if (err) { + BT_ERR("Failed to %s %s value (err %d)", + IS_VA_DEL(lab) ? "delete" : "store", path, err); + } else { + BT_DBG("%s %s value", + IS_VA_DEL(lab) ? "Deleted" : "Stored", path); + } + } +} + +static void store_pending(struct ble_npl_event *work) +{ + BT_DBG(""); + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_RPL_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_rpl(); + } else { + clear_rpl(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_KEYS_PENDING)) { + store_pending_keys(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NET_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_net(); + } else { + clear_net(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IV_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_iv(); + } else { + clear_iv(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) { + store_pending_seq(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_HB_PUB_PENDING)) { + store_pending_hb_pub(); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_CFG_PENDING)) { + if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + store_pending_cfg(); + } else { + clear_cfg(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_MOD_PENDING)) { + bt_mesh_model_foreach(store_pending_mod, NULL); + } + + if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_VA_PENDING)) { + store_pending_va(); + } + + if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && + atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NODES_PENDING)) { + store_pending_nodes(); + } +} + +void bt_mesh_store_rpl(struct bt_mesh_rpl *entry) +{ + entry->store = true; + schedule_store(BT_MESH_RPL_PENDING); +} + +static struct key_update *key_update_find(bool app_key, u16_t key_idx, + struct key_update **free_slot) +{ + struct key_update *match; + int i; + + match = NULL; + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + *free_slot = update; + continue; + } + + if (update->app_key != app_key) { + continue; + } + + if (update->key_idx == key_idx) { + match = update; + } + } + + return match; +} + +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 0; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_net_key(sub); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0; + free_slot->clear = 0; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_store_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 0; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_app_key(key); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1; + free_slot->clear = 0; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_store_hb_pub(void) +{ + schedule_store(BT_MESH_HB_PUB_PENDING); +} + +void bt_mesh_store_cfg(void) +{ + schedule_store(BT_MESH_CFG_PENDING); +} + +void bt_mesh_clear_net(void) +{ + schedule_store(BT_MESH_NET_PENDING); + schedule_store(BT_MESH_IV_PENDING); + schedule_store(BT_MESH_CFG_PENDING); +} + +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 1; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_net_key(sub->net_idx); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0; + free_slot->clear = 1; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 1; + schedule_store(BT_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_app_key(key->app_idx); + return; + } + + free_slot->valid = 1; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1; + free_slot->clear = 1; + + schedule_store(BT_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_rpl(void) +{ + schedule_store(BT_MESH_RPL_PENDING); +} + +void bt_mesh_store_mod_bind(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_BIND_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_sub(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_SUB_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_pub(struct bt_mesh_model *mod) +{ + mod->flags |= BT_MESH_MOD_PUB_PENDING; + schedule_store(BT_MESH_MOD_PENDING); +} + + +void bt_mesh_store_label(void) +{ + schedule_store(BT_MESH_VA_PENDING); +} + +void bt_mesh_store_node(struct bt_mesh_node *node) +{ + struct node_update *update, *free_slot; + + BT_DBG("Node 0x%04x", node->addr); + + update = node_update_find(node->addr, &free_slot); + if (update) { + update->clear = false; + schedule_store(BT_MESH_NODES_PENDING); + return; + } + + if (!free_slot) { + store_node(node); + return; + } + + free_slot->addr = node->addr; + + schedule_store(BT_MESH_NODES_PENDING); +} + +void bt_mesh_clear_node(struct bt_mesh_node *node) +{ + struct node_update *update, *free_slot; + + BT_DBG("Node 0x%04x", node->addr); + + update = node_update_find(node->addr, &free_slot); + if (update) { + update->clear = true; + schedule_store(BT_MESH_NODES_PENDING); + return; + } + + if (!free_slot) { + clear_node(node->addr); + return; + } + + free_slot->addr = node->addr; + + schedule_store(BT_MESH_NODES_PENDING); +} + +int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, + const void *data, size_t data_len) +{ + char path[20]; + char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))]; + char *val; + int err; + + encode_mod_path(mod, vnd, "data", path, sizeof(path)); + + if (data_len) { + mod->flags |= BT_MESH_MOD_DATA_PRESENT; + val = settings_str_from_bytes(data, data_len, + buf, sizeof(buf)); + if (!val) { + BT_ERR("Unable to encode model publication as value"); + return -EINVAL; + } + err = settings_save_one(path, val); + } else if (mod->flags & BT_MESH_MOD_DATA_PRESENT) { + mod->flags &= ~BT_MESH_MOD_DATA_PRESENT; + err = settings_save_one(path, NULL); + } else { + /* Nothing to delete */ + err = 0; + } + + if (err) { + BT_ERR("Failed to store %s value", path); + } else { + BT_DBG("Stored %s value", path); + } + return err; +} + +static struct conf_handler bt_mesh_settings_conf_handler = { + .ch_name = "bt_mesh", + .ch_get = NULL, + .ch_set = mesh_set, + .ch_commit = mesh_commit, + .ch_export = NULL, +}; + +void bt_mesh_settings_init(void) +{ + int rc; + + rc = conf_register(&bt_mesh_settings_conf_handler); + + SYSINIT_PANIC_ASSERT_MSG(rc == 0, + "Failed to register bt_mesh_settings conf"); + + k_delayed_work_init(&pending_store, store_pending); +} + +#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */ +#endif diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.h b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.h new file mode 100644 index 0000000..c630814 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/settings.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_mesh_store_net(void); +void bt_mesh_store_iv(bool only_duration); +void bt_mesh_store_seq(void); +void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl); +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_store_app_key(struct bt_mesh_app_key *key); +void bt_mesh_store_hb_pub(void); +void bt_mesh_store_cfg(void); +void bt_mesh_store_mod_bind(struct bt_mesh_model *mod); +void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); +void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); +void bt_mesh_store_label(void); +void bt_mesh_store_node(struct bt_mesh_node *node); + +void bt_mesh_clear_net(void); +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key); +void bt_mesh_clear_rpl(void); +void bt_mesh_clear_node(struct bt_mesh_node *node); + +void bt_mesh_settings_init(void); diff --git a/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/shell.c b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/shell.c new file mode 100644 index 0000000..c193427 --- /dev/null +++ b/lib/NimBLE-Arduino/src/nimble/nimble/host/mesh/src/shell.c @@ -0,0 +1,2821 @@ +/** @file + * @brief Bluetooth Mesh shell + * + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nimble/porting/nimble/include/syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_MESH) + +#if MYNEWT_VAL(BLE_MESH_SHELL) + +#include +#include +#include +#include "shell/shell.h" +#include "console/console.h" +#include "mesh/mesh.h" +#include "mesh/main.h" +#include "mesh/glue.h" +#include "mesh/testing.h" + +/* Private includes for raw Network & Transport layer access */ +#include "net.h" +#include "access.h" +#include "mesh_priv.h" +#include "lpn.h" +#include "transport.h" +#include "foundation.h" +#include "testing.h" +#include "settings.h" + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +#include "mesh/model_srv.h" +#include "mesh/model_cli.h" +#include "light_model.h" +#endif + +/* This should be higher priority (lower value) than main task priority */ +#define BLE_MESH_SHELL_TASK_PRIO 126 +#define BLE_MESH_SHELL_STACK_SIZE 768 + +OS_TASK_STACK_DEFINE(g_blemesh_shell_stack, BLE_MESH_SHELL_STACK_SIZE); + +struct os_task mesh_shell_task; +static struct os_eventq mesh_shell_queue; + +#define CID_NVAL 0xffff +#define CID_VENDOR 0x05C3 + +/* Vendor Model data */ +#define VND_MODEL_ID_1 0x1234 + +/* Default net, app & dev key values, unless otherwise specified */ +static const u8_t default_key[16] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, +}; + +static struct { + u16_t local; + u16_t dst; + u16_t net_idx; + u16_t app_idx; +} net = { + .local = BT_MESH_ADDR_UNASSIGNED, + .dst = BT_MESH_ADDR_UNASSIGNED, +}; + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_DISABLED, + .beacon = BT_MESH_BEACON_ENABLED, +#if MYNEWT_VAL(BLE_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_DISABLED, +#else + .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_DISABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + .relay_retransmit = BT_MESH_TRANSMIT(2, 20), +}; + +#define CUR_FAULTS_MAX 4 + +static u8_t cur_faults[CUR_FAULTS_MAX]; +static u8_t reg_faults[CUR_FAULTS_MAX * 2]; + +static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count) +{ + u8_t i, limit = *count; + + for (i = 0, *count = 0; i < faults_size && *count < limit; i++) { + if (faults[i]) { + *dst++ = faults[i]; + (*count)++; + } + } +} + +static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, u8_t *fault_count) +{ + printk("Sending current faults\n"); + + *test_id = 0x00; + *company_id = CID_VENDOR; + + get_faults(cur_faults, sizeof(cur_faults), faults, fault_count); + + return 0; +} + +static int fault_get_reg(struct bt_mesh_model *model, u16_t cid, + u8_t *test_id, u8_t *faults, u8_t *fault_count) +{ + if (cid != CID_VENDOR) { + printk("Faults requested for unknown Company ID 0x%04x\n", cid); + return -EINVAL; + } + + printk("Sending registered faults\n"); + + *test_id = 0x00; + + get_faults(reg_faults, sizeof(reg_faults), faults, fault_count); + + return 0; +} + +static int fault_clear(struct bt_mesh_model *model, uint16_t cid) +{ + if (cid != CID_VENDOR) { + return -EINVAL; + } + + memset(reg_faults, 0, sizeof(reg_faults)); + + return 0; +} + +static int fault_test(struct bt_mesh_model *model, uint8_t test_id, + uint16_t cid) +{ + if (cid != CID_VENDOR) { + return -EINVAL; + } + + if (test_id != 0x00) { + return -EINVAL; + } + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = fault_get_cur, + .fault_get_reg = fault_get_reg, + .fault_clear = fault_clear, + .fault_test = fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX); +} +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */ + +#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) +void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count) +{ + size_t i; + + if (!fault_count) { + printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n", + test_id, cid); + return; + } + + printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n", + test_id, cid, fault_count); + + for (i = 0; i < fault_count; i++) { + printk("\t0x%02x\n", faults[i]); + } +} + +static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr, + u8_t test_id, u16_t cid, u8_t *faults, + size_t fault_count) +{ + printk("Health Current Status from 0x%04x\n", addr); + show_faults(test_id, cid, faults, fault_count); +} + +static struct bt_mesh_health_cli health_cli = { + .current_status = health_current_status, +}; + +#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */ + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) +static struct bt_mesh_gen_model_cli gen_onoff_cli; +static struct bt_mesh_model_pub gen_onoff_cli_pub; +static struct bt_mesh_model_pub gen_onoff_srv_pub; +static struct bt_mesh_gen_model_cli gen_level_cli; +static struct bt_mesh_model_pub gen_level_cli_pub; +static struct bt_mesh_model_pub gen_level_srv_pub; +static struct bt_mesh_model_pub light_lightness_pub; +static struct bt_mesh_gen_onoff_srv gen_onoff_srv = { + .get = light_model_gen_onoff_get, + .set = light_model_gen_onoff_set, +}; +static struct bt_mesh_gen_level_srv gen_level_srv = { + .get = light_model_gen_level_get, + .set = light_model_gen_level_set, +}; +static struct bt_mesh_light_lightness_srv light_lightness_srv = { + .get = light_model_light_lightness_get, + .set = light_model_light_lightness_set, +}; + +void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, u8_t *state), + int (*set)(struct bt_mesh_model *model, u8_t state)) +{ + gen_onoff_srv.get = get; + gen_onoff_srv.set = set; +} + +void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level), + int (*set)(struct bt_mesh_model *model, s16_t level)) +{ + gen_level_srv.get = get; + gen_level_srv.set = set; +} + +void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level), + int (*set)(struct bt_mesh_model *model, s16_t level)) +{ + light_lightness_srv.get = get; + light_lightness_srv.set = set; +} +#endif + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + BT_MESH_MODEL_CFG_CLI(&cfg_cli), +#endif +#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) + BT_MESH_MODEL_HEALTH_CLI(&health_cli), +#endif +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + BT_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_srv, &gen_onoff_srv_pub), + BT_MESH_MODEL_GEN_ONOFF_CLI(&gen_onoff_cli, &gen_onoff_cli_pub), + BT_MESH_MODEL_GEN_LEVEL_SRV(&gen_level_srv, &gen_level_srv_pub), + BT_MESH_MODEL_GEN_LEVEL_CLI(&gen_level_cli, &gen_level_cli_pub), + BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(&light_lightness_srv, &light_lightness_pub), +#endif +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_VENDOR, VND_MODEL_ID_1, + BT_MESH_MODEL_NO_OPS, NULL, NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_VENDOR, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static u8_t hex2val(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } else { + return 0; + } +} + +static size_t hex2bin(const char *hex, u8_t *bin, size_t bin_len) +{ + size_t len = 0; + + while (*hex && len < bin_len) { + bin[len] = hex2val(*hex++) << 4; + + if (!*hex) { + len++; + break; + } + + bin[len++] |= hex2val(*hex++); + } + + return len; +} + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + printk("Local node provisioned, net_idx 0x%04x address 0x%04x\n", + net_idx, addr); + net.local = addr; + net.net_idx = net_idx, + net.dst = addr; +} + +static void prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem) +{ + printk("Node provisioned, net_idx 0x%04x address " + "0x%04x elements %d", net_idx, addr, num_elem); + + net.net_idx = net_idx, + net.dst = addr; +} + +static void prov_input_complete(void) +{ + printk("Input complete"); +} + +static void prov_reset(void) +{ + printk("The local node has been reset and needs reprovisioning\n"); +} + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + printk("OOB Number: %lu\n", number); + return 0; +} + +static int output_string(const char *str) +{ + printk("OOB String: %s\n", str); + return 0; +} + +static bt_mesh_input_action_t input_act; +static u8_t input_size; + +static int cmd_input_num(int argc, char *argv[]) +{ + int err; + + if (argc < 2) { + return -EINVAL; + } + + if (input_act != BT_MESH_ENTER_NUMBER) { + printk("A number hasn't been requested!\n"); + return 0; + } + + if (strlen(argv[1]) < input_size) { + printk("Too short input (%u digits required)\n", + input_size); + return 0; + } + + err = bt_mesh_input_number(strtoul(argv[1], NULL, 10)); + if (err) { + printk("Numeric input failed (err %d)\n", err); + return 0; + } + + input_act = BT_MESH_NO_INPUT; + return 0; +} + +struct shell_cmd_help cmd_input_num_help = { + NULL, "", NULL +}; + +static int cmd_input_str(int argc, char *argv[]) +{ + int err; + + if (argc < 2) { + return -EINVAL; + } + + if (input_act != BT_MESH_ENTER_STRING) { + printk("A string hasn't been requested!\n"); + return 0; + } + + if (strlen(argv[1]) < input_size) { + printk("Too short input (%u characters required)\n", + input_size); + return 0; + } + + err = bt_mesh_input_string(argv[1]); + if (err) { + printk("String input failed (err %d)\n", err); + return 0; + } + + input_act = BT_MESH_NO_INPUT; + return 0; +} + +struct shell_cmd_help cmd_input_str_help = { + NULL, "", NULL +}; + +static int input(bt_mesh_input_action_t act, u8_t size) +{ + switch (act) { + case BT_MESH_ENTER_NUMBER: + printk("Enter a number (max %u digits) with: input-num \n", + size); + break; + case BT_MESH_ENTER_STRING: + printk("Enter a string (max %u chars) with: input-str \n", + size); + break; + default: + printk("Unknown input action %u (size %u) requested!\n", + act, size); + return -EINVAL; + } + + input_act = act; + input_size = size; + return 0; +} + +static const char *bearer2str(bt_mesh_prov_bearer_t bearer) +{ + switch (bearer) { + case BT_MESH_PROV_ADV: + return "PB-ADV"; + case BT_MESH_PROV_GATT: + return "PB-GATT"; + default: + return "unknown"; + } +} + +static void link_open(bt_mesh_prov_bearer_t bearer) +{ + printk("Provisioning link opened on %s\n", bearer2str(bearer)); +} + +static void link_close(bt_mesh_prov_bearer_t bearer) +{ + printk("Provisioning link closed on %s\n", bearer2str(bearer)); +} + +static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static u8_t static_val[16]; + +static struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .link_open = link_open, + .link_close = link_close, + .complete = prov_complete, + .node_added = prov_node_added, + .reset = prov_reset, + .static_val = NULL, + .static_val_len = 0, + .output_size = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_SIZE), + .output_actions = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_ACTIONS), + .output_number = output_number, + .output_string = output_string, + .input_size = MYNEWT_VAL(BLE_MESH_OOB_INPUT_SIZE), + .input_actions = MYNEWT_VAL(BLE_MESH_OOB_INPUT_ACTIONS), + .input = input, + .input_complete = prov_input_complete, +}; + +static int cmd_static_oob(int argc, char *argv[]) +{ + if (argc < 2) { + prov.static_val = NULL; + prov.static_val_len = 0; + } else { + prov.static_val_len = hex2bin(argv[1], static_val, 16); + if (prov.static_val_len) { + prov.static_val = static_val; + } else { + prov.static_val = NULL; + } + } + + if (prov.static_val) { + printk("Static OOB value set (length %u)\n", + prov.static_val_len); + } else { + printk("Static OOB value cleared\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_static_oob_help = { + NULL, "[val: 1-16 hex values]", NULL +}; + +static int cmd_uuid(int argc, char *argv[]) +{ + u8_t uuid[16]; + size_t len; + + if (argc < 2) { + return -EINVAL; + } + + len = hex2bin(argv[1], uuid, sizeof(uuid)); + if (len < 1) { + return -EINVAL; + } + + memcpy(dev_uuid, uuid, len); + memset(dev_uuid + len, 0, sizeof(dev_uuid) - len); + + printk("Device UUID set\n"); + + return 0; +} + +struct shell_cmd_help cmd_uuid_help = { + NULL, "", NULL +}; + +static int cmd_reset(int argc, char *argv[]) +{ + bt_mesh_reset(); + printk("Local node reset complete\n"); + return 0; +} + +static u8_t str2u8(const char *str) +{ + if (isdigit(str[0])) { + return strtoul(str, NULL, 0); + } + + return (!strcmp(str, "on") || !strcmp(str, "enable")); +} + +static bool str2bool(const char *str) +{ + return str2u8(str); +} + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) +static int cmd_lpn(int argc, char *argv[]) +{ + static bool enabled; + int err; + + if (argc < 2) { + printk("%s\n", enabled ? "enabled" : "disabled"); + return 0; + } + + if (str2bool(argv[1])) { + if (enabled) { + printk("LPN already enabled\n"); + return 0; + } + + err = bt_mesh_lpn_set(true); + if (err) { + printk("Enabling LPN failed (err %d)\n", err); + } else { + enabled = true; + } + } else { + if (!enabled) { + printk("LPN already disabled\n"); + return 0; + } + + err = bt_mesh_lpn_set(false); + if (err) { + printk("Enabling LPN failed (err %d)\n", err); + } else { + enabled = false; + } + } + + return 0; +} + +static int cmd_poll(int argc, char *argv[]) +{ + int err; + + err = bt_mesh_lpn_poll(); + if (err) { + printk("Friend Poll failed (err %d)\n", err); + } + + return 0; +} + +static void lpn_cb(u16_t friend_addr, bool established) +{ + if (established) { + printk("Friendship (as LPN) established to Friend 0x%04x\n", + friend_addr); + } else { + printk("Friendship (as LPN) lost with Friend 0x%04x\n", + friend_addr); + } +} + +struct shell_cmd_help cmd_lpn_help = { + NULL, "", NULL +}; + +#endif /* MESH_LOW_POWER */ + +static int check_pub_addr_unassigned(void) +{ +#ifdef ARCH_sim + return 0; +#else + uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 }; + + return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), + zero_addr, BLE_DEV_ADDR_LEN) == 0; +#endif +} + +int cmd_mesh_init(int argc, char *argv[]) +{ + int err; + ble_addr_t addr; + + if (check_pub_addr_unassigned()) { + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + } + else { + err = bt_mesh_init(0, &prov, &comp); + } + + if (err) { + printk("Mesh initialization failed (err %d)\n", err); + } + + printk("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } else { + printk("Use \"pb-adv on\" or \"pb-gatt on\" to enable" + " advertising\n"); + } + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) + bt_mesh_lpn_set_cb(lpn_cb); +#endif + + return 0; +} + +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) +static int cmd_ident(int argc, char *argv[]) +{ + int err; + + err = bt_mesh_proxy_identity_enable(); + if (err) { + printk("Failed advertise using Node Identity (err %d)\n", err); + } + + return 0; +} +#endif /* MESH_GATT_PROXY */ + +static int cmd_dst(int argc, char *argv[]) +{ + if (argc < 2) { + printk("Destination address: 0x%04x%s\n", net.dst, + net.dst == net.local ? " (local)" : ""); + return 0; + } + + if (!strcmp(argv[1], "local")) { + net.dst = net.local; + } else { + net.dst = strtoul(argv[1], NULL, 0); + } + + printk("Destination address set to 0x%04x%s\n", net.dst, + net.dst == net.local ? " (local)" : ""); + return 0; +} + +struct shell_cmd_help cmd_dst_help = { + NULL, "[destination address]", NULL +}; + +static int cmd_netidx(int argc, char *argv[]) +{ + if (argc < 2) { + printk("NetIdx: 0x%04x\n", net.net_idx); + return 0; + } + + net.net_idx = strtoul(argv[1], NULL, 0); + printk("NetIdx set to 0x%04x\n", net.net_idx); + return 0; +} + +struct shell_cmd_help cmd_netidx_help = { + NULL, "[NetIdx]", NULL +}; + +static int cmd_appidx(int argc, char *argv[]) +{ + if (argc < 2) { + printk("AppIdx: 0x%04x\n", net.app_idx); + return 0; + } + + net.app_idx = strtoul(argv[1], NULL, 0); + printk("AppIdx set to 0x%04x\n", net.app_idx); + return 0; +} + +struct shell_cmd_help cmd_appidx_help = { + NULL, "[AppIdx]", NULL +}; + +static int cmd_net_send(int argc, char *argv[]) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(32); + struct bt_mesh_msg_ctx ctx = { + .send_ttl = BT_MESH_TTL_DEFAULT, + .net_idx = net.net_idx, + .addr = net.dst, + .app_idx = net.app_idx, + + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = net.local, + .xmit = bt_mesh_net_transmit_get(), + .sub = bt_mesh_subnet_get(net.net_idx), + }; + size_t len; + int err = 0; + + if (argc < 2) { + err = -EINVAL; + goto done; + } + + if (!tx.sub) { + printk("No matching subnet for NetKey Index 0x%04x\n", + net.net_idx); + goto done; + } + + net_buf_simple_init(msg, 0); + len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4); + net_buf_simple_add(msg, len); + + err = bt_mesh_trans_send(&tx, msg, NULL, NULL); + if (err) { + printk("Failed to send (err %d)\n", err); + } + +done: + os_mbuf_free_chain(msg); + return err; +} + +struct shell_cmd_help cmd_net_send_help = { + NULL, "", NULL +}; + +static int cmd_rpl_clear(int argc, char *argv[]) +{ + bt_mesh_rpl_clear(); + return 0; +} + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) +static int cmd_lpn_subscribe(int argc, char *argv[]) +{ + u16_t address; + + if (argc < 2) { + return -EINVAL; + } + + address = strtoul(argv[1], NULL, 0); + + printk("address 0x%04x", address); + + bt_mesh_lpn_group_add(address); + + return 0; +} + +struct shell_cmd_help cmd_lpn_subscribe_help = { + NULL, "", NULL +}; + +static int cmd_lpn_unsubscribe(int argc, char *argv[]) +{ + u16_t address; + + if (argc < 2) { + return -EINVAL; + } + + address = strtoul(argv[1], NULL, 0); + + printk("address 0x%04x", address); + + bt_mesh_lpn_group_del(&address, 1); + + return 0; +} + +struct shell_cmd_help cmd_lpn_unsubscribe_help = { + NULL, "", NULL +}; +#endif + +#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST) +static int cmd_iv_update(int argc, char *argv[]) +{ + if (bt_mesh_iv_update()) { + printk("Transitioned to IV Update In Progress state\n"); + } else { + printk("Transitioned to IV Update Normal state\n"); + } + + printk("IV Index is 0x%08lx\n", bt_mesh.iv_index); + + return 0; +} + +static int cmd_iv_update_test(int argc, char *argv[]) +{ + bool enable; + + if (argc < 2) { + return -EINVAL; + } + + enable = str2bool(argv[1]); + if (enable) { + printk("Enabling IV Update test mode\n"); + } else { + printk("Disabling IV Update test mode\n"); + } + + bt_mesh_iv_update_test(enable); + + return 0; +} + +struct shell_cmd_help cmd_iv_update_test_help = { + NULL, "", NULL +}; +#endif + +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +int cmd_timeout(int argc, char *argv[]) +{ + s32_t timeout; + + if (argc < 2) { + timeout = bt_mesh_cfg_cli_timeout_get(); + if (timeout == K_FOREVER) { + printk("Message timeout: forever\n"); + } else { + printk("Message timeout: %lu seconds\n", + timeout / 1000); + } + + return 0; + } + + timeout = strtol(argv[1], NULL, 0); + if (timeout < 0 || timeout > (INT32_MAX / 1000)) { + timeout = K_FOREVER; + } else { + timeout = timeout * 1000; + } + + bt_mesh_cfg_cli_timeout_set(timeout); + if (timeout == K_FOREVER) { + printk("Message timeout: forever\n"); + } else { + printk("Message timeout: %lu seconds\n", + timeout / 1000); + } + + return 0; +} + +struct shell_cmd_help cmd_timeout_help = { + NULL, "[timeout in seconds]", NULL +}; + + +static int cmd_get_comp(int argc, char *argv[]) +{ + struct os_mbuf *comp = NET_BUF_SIMPLE(32); + u8_t status, page = 0x00; + int err = 0; + + if (argc > 1) { + page = strtol(argv[1], NULL, 0); + } + + net_buf_simple_init(comp, 0); + err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page, + &status, comp); + if (err) { + printk("Getting composition failed (err %d)\n", err); + goto done; + } + + if (status != 0x00) { + printk("Got non-success status 0x%02x\n", status); + goto done; + } + + printk("Got Composition Data for 0x%04x:\n", net.dst); + printk("\tCID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tPID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tVID 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tCRPL 0x%04x\n", net_buf_simple_pull_le16(comp)); + printk("\tFeatures 0x%04x\n", net_buf_simple_pull_le16(comp)); + + while (comp->om_len > 4) { + u8_t sig, vnd; + u16_t loc; + int i; + + loc = net_buf_simple_pull_le16(comp); + sig = net_buf_simple_pull_u8(comp); + vnd = net_buf_simple_pull_u8(comp); + + printk("\n\tElement @ 0x%04x:\n", loc); + + if (comp->om_len < ((sig * 2) + (vnd * 4))) { + printk("\t\t...truncated data!\n"); + break; + } + + if (sig) { + printk("\t\tSIG Models:\n"); + } else { + printk("\t\tNo SIG Models\n"); + } + + for (i = 0; i < sig; i++) { + u16_t mod_id = net_buf_simple_pull_le16(comp); + + printk("\t\t\t0x%04x\n", mod_id); + } + + if (vnd) { + printk("\t\tVendor Models:\n"); + } else { + printk("\t\tNo Vendor Models\n"); + } + + for (i = 0; i < vnd; i++) { + u16_t cid = net_buf_simple_pull_le16(comp); + u16_t mod_id = net_buf_simple_pull_le16(comp); + + printk("\t\t\tCompany 0x%04x: 0x%04x\n", cid, mod_id); + } + } + +done: + os_mbuf_free_chain(comp); + return err; +} + +struct shell_cmd_help cmd_get_comp_help = { + NULL, "[page]", NULL +}; + +static int cmd_beacon(int argc, char *argv[]) +{ + u8_t status; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_beacon_get(net.net_idx, net.dst, &status); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_beacon_set(net.net_idx, net.dst, val, + &status); + } + + if (err) { + printk("Unable to send Beacon Get/Set message (err %d)\n", err); + return 0; + } + + printk("Beacon state is 0x%02x\n", status); + + return 0; +} + +struct shell_cmd_help cmd_beacon_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_ttl(int argc, char *argv[]) +{ + u8_t ttl; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_ttl_get(net.net_idx, net.dst, &ttl); + } else { + u8_t val = strtoul(argv[1], NULL, 0); + + err = bt_mesh_cfg_ttl_set(net.net_idx, net.dst, val, &ttl); + } + + if (err) { + printk("Unable to send Default TTL Get/Set (err %d)\n", err); + return 0; + } + + printk("Default TTL is 0x%02x\n", ttl); + + return 0; +} + +struct shell_cmd_help cmd_ttl_help = { + NULL, "[ttl: 0x00, 0x02-0x7f]", NULL +}; + +static int cmd_friend(int argc, char *argv[]) +{ + u8_t frnd; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_friend_get(net.net_idx, net.dst, &frnd); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_friend_set(net.net_idx, net.dst, val, &frnd); + } + + if (err) { + printk("Unable to send Friend Get/Set (err %d)\n", err); + return 0; + } + + printk("Friend is set to 0x%02x\n", frnd); + + return 0; +} + +struct shell_cmd_help cmd_friend_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_gatt_proxy(int argc, char *argv[]) +{ + u8_t proxy; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_gatt_proxy_get(net.net_idx, net.dst, &proxy); + } else { + u8_t val = str2u8(argv[1]); + + err = bt_mesh_cfg_gatt_proxy_set(net.net_idx, net.dst, val, + &proxy); + } + + if (err) { + printk("Unable to send GATT Proxy Get/Set (err %d)\n", err); + return 0; + } + + printk("GATT Proxy is set to 0x%02x\n", proxy); + + return 0; +} + +struct shell_cmd_help cmd_gatt_proxy_help = { + NULL, "[val: off, on]", NULL +}; + +static int cmd_relay(int argc, char *argv[]) +{ + u8_t relay, transmit; + int err; + + if (argc < 2) { + err = bt_mesh_cfg_relay_get(net.net_idx, net.dst, &relay, + &transmit); + } else { + u8_t val = str2u8(argv[1]); + u8_t count, interval, new_transmit; + + if (val) { + if (argc > 2) { + count = strtoul(argv[2], NULL, 0); + } else { + count = 2; + } + + if (argc > 3) { + interval = strtoul(argv[3], NULL, 0); + } else { + interval = 20; + } + + new_transmit = BT_MESH_TRANSMIT(count, interval); + } else { + new_transmit = 0; + } + + err = bt_mesh_cfg_relay_set(net.net_idx, net.dst, val, + new_transmit, &relay, &transmit); + } + + if (err) { + printk("Unable to send Relay Get/Set (err %d)\n", err); + return 0; + } + + printk("Relay is 0x%02x, Transmit 0x%02x (count %u interval %ums)\n", + relay, transmit, BT_MESH_TRANSMIT_COUNT(transmit), + BT_MESH_TRANSMIT_INT(transmit)); + + return 0; +} + +struct shell_cmd_help cmd_relay_help = { + NULL, "[val: off, on] [count: 0-7] [interval: 0-32]", NULL +}; + +static int cmd_net_key_add(int argc, char *argv[]) +{ + u8_t key_val[16]; + u16_t key_net_idx; + u8_t status; + int err; + + if (argc < 2) { + return -EINVAL; + } + + key_net_idx = strtoul(argv[1], NULL, 0); + + if (argc > 2) { + size_t len; + + len = hex2bin(argv[3], key_val, sizeof(key_val)); + memset(key_val, 0, sizeof(key_val) - len); + } else { + memcpy(key_val, default_key, sizeof(key_val)); + } + + err = bt_mesh_cfg_net_key_add(net.net_idx, net.dst, key_net_idx, + key_val, &status); + if (err) { + printk("Unable to send NetKey Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("NetKeyAdd failed with status 0x%02x\n", status); + } else { + printk("NetKey added with NetKey Index 0x%03x\n", key_net_idx); + } + + return 0; +} + +struct shell_cmd_help cmd_net_key_add_help = { + NULL, " [val]", NULL +}; + +static int cmd_app_key_add(int argc, char *argv[]) +{ + u8_t key_val[16]; + u16_t key_net_idx, key_app_idx; + u8_t status; + int err; + + if (argc < 3) { + return -EINVAL; + } + + key_net_idx = strtoul(argv[1], NULL, 0); + key_app_idx = strtoul(argv[2], NULL, 0); + + if (argc > 3) { + size_t len; + + len = hex2bin(argv[3], key_val, sizeof(key_val)); + memset(key_val, 0, sizeof(key_val) - len); + } else { + memcpy(key_val, default_key, sizeof(key_val)); + } + + err = bt_mesh_cfg_app_key_add(net.net_idx, net.dst, key_net_idx, + key_app_idx, key_val, &status); + if (err) { + printk("Unable to send App Key Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("AppKeyAdd failed with status 0x%02x\n", status); + } else { + printk("AppKey added, NetKeyIndex 0x%04x AppKeyIndex 0x%04x\n", + key_net_idx, key_app_idx); + } + + return 0; +} + +struct shell_cmd_help cmd_app_key_add_help = { + NULL, " [val]", NULL +}; + +static int cmd_mod_app_bind(int argc, char *argv[]) +{ + u16_t elem_addr, mod_app_idx, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + mod_app_idx = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_app_bind_vnd(net.net_idx, net.dst, + elem_addr, mod_app_idx, + mod_id, cid, &status); + } else { + err = bt_mesh_cfg_mod_app_bind(net.net_idx, net.dst, elem_addr, + mod_app_idx, mod_id, &status); + } + + if (err) { + printk("Unable to send Model App Bind (err %d)\n", err); + return 0; + } + + if (status) { + printk("Model App Bind failed with status 0x%02x\n", status); + } else { + printk("AppKey successfully bound\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_app_bind_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_add(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + sub_addr = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_add_vnd(net.net_idx, net.dst, + elem_addr, sub_addr, mod_id, + cid, &status); + } else { + err = bt_mesh_cfg_mod_sub_add(net.net_idx, net.dst, elem_addr, + sub_addr, mod_id, &status); + } + + if (err) { + printk("Unable to send Model Subscription Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("Model Subscription Add failed with status 0x%02x\n", + status); + } else { + printk("Model subscription was successful\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_add_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_del(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t status; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + sub_addr = strtoul(argv[2], NULL, 0); + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_del_vnd(net.net_idx, net.dst, + elem_addr, sub_addr, mod_id, + cid, &status); + } else { + err = bt_mesh_cfg_mod_sub_del(net.net_idx, net.dst, elem_addr, + sub_addr, mod_id, &status); + } + + if (err) { + printk("Unable to send Model Subscription Delete (err %d)\n", + err); + return 0; + } + + if (status) { + printk("Model Subscription Delete failed with status 0x%02x\n", + status); + } else { + printk("Model subscription deltion was successful\n"); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_del_help = { + NULL, " [Company ID]", NULL +}; + +static int cmd_mod_sub_add_va(int argc, char *argv[]) +{ + u16_t elem_addr, sub_addr, mod_id, cid; + u8_t label[16]; + u8_t status; + size_t len; + int err; + + if (argc < 4) { + return -EINVAL; + } + + elem_addr = strtoul(argv[1], NULL, 0); + + len = hex2bin(argv[2], label, sizeof(label)); + memset(label + len, 0, sizeof(label) - len); + + mod_id = strtoul(argv[3], NULL, 0); + + if (argc > 4) { + cid = strtoul(argv[4], NULL, 0); + err = bt_mesh_cfg_mod_sub_va_add_vnd(net.net_idx, net.dst, + elem_addr, label, mod_id, + cid, &sub_addr, &status); + } else { + err = bt_mesh_cfg_mod_sub_va_add(net.net_idx, net.dst, + elem_addr, label, mod_id, + &sub_addr, &status); + } + + if (err) { + printk("Unable to send Mod Sub VA Add (err %d)\n", err); + return 0; + } + + if (status) { + printk("Mod Sub VA Add failed with status 0x%02x\n", + status); + } else { + printk("0x%04x subscribed to Label UUID %s (va 0x%04x)\n", + elem_addr, argv[2], sub_addr); + } + + return 0; +} + +struct shell_cmd_help cmd_mod_sub_add_va_help = { + NULL, "