From bdc10744fb338ae197692713a0b48a7ccc36f566 Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 26 Apr 2020 10:25:59 +0200 Subject: Add Nimble in libs directory --- .../nimble/controller/include/controller/ble_hw.h | 116 + .../nimble/controller/include/controller/ble_ll.h | 587 ++ .../controller/include/controller/ble_ll_adv.h | 209 + .../controller/include/controller/ble_ll_conn.h | 425 ++ .../controller/include/controller/ble_ll_ctrl.h | 313 + .../controller/include/controller/ble_ll_hci.h | 75 + .../controller/include/controller/ble_ll_resolv.h | 116 + .../controller/include/controller/ble_ll_rfmgmt.h | 63 + .../controller/include/controller/ble_ll_scan.h | 293 + .../controller/include/controller/ble_ll_sched.h | 216 + .../controller/include/controller/ble_ll_sync.h | 74 + .../controller/include/controller/ble_ll_test.h | 35 + .../controller/include/controller/ble_ll_trace.h | 96 + .../controller/include/controller/ble_ll_utils.h | 29 + .../include/controller/ble_ll_whitelist.h | 52 + .../nimble/controller/include/controller/ble_phy.h | 242 + .../controller/include/controller/ble_phy_trace.h | 96 + src/libs/mynewt-nimble/nimble/controller/pkg.yml | 38 + .../mynewt-nimble/nimble/controller/src/ble_ll.c | 1714 ++++++ .../nimble/controller/src/ble_ll_adv.c | 5136 +++++++++++++++++ .../nimble/controller/src/ble_ll_conn.c | 4270 ++++++++++++++ .../nimble/controller/src/ble_ll_conn_hci.c | 1896 ++++++ .../nimble/controller/src/ble_ll_conn_priv.h | 226 + .../nimble/controller/src/ble_ll_ctrl.c | 2744 +++++++++ .../nimble/controller/src/ble_ll_dtm.c | 726 +++ .../nimble/controller/src/ble_ll_dtm_priv.h | 40 + .../nimble/controller/src/ble_ll_hci.c | 1515 +++++ .../nimble/controller/src/ble_ll_hci_ev.c | 522 ++ .../nimble/controller/src/ble_ll_priv.h | 51 + .../nimble/controller/src/ble_ll_rand.c | 185 + .../nimble/controller/src/ble_ll_resolv.c | 753 +++ .../nimble/controller/src/ble_ll_rfmgmt.c | 346 ++ .../nimble/controller/src/ble_ll_scan.c | 3979 +++++++++++++ .../nimble/controller/src/ble_ll_sched.c | 1828 ++++++ .../nimble/controller/src/ble_ll_supp_cmd.c | 458 ++ .../nimble/controller/src/ble_ll_sync.c | 2246 ++++++++ .../nimble/controller/src/ble_ll_trace.c | 55 + .../nimble/controller/src/ble_ll_utils.c | 301 + .../nimble/controller/src/ble_ll_whitelist.c | 287 + .../mynewt-nimble/nimble/controller/syscfg.yml | 434 ++ .../mynewt-nimble/nimble/controller/test/pkg.yml | 34 + .../nimble/controller/test/src/ble_ll_csa2_test.c | 115 + .../nimble/controller/test/src/ble_ll_csa2_test.h | 27 + .../nimble/controller/test/src/ble_ll_test.c | 36 + .../nimble/controller/test/syscfg.yml | 25 + .../nimble/drivers/native/include/ble/xcvr.h | 46 + .../mynewt-nimble/nimble/drivers/native/pkg.yml | 30 + .../nimble/drivers/native/src/ble_hw.c | 239 + .../nimble/drivers/native/src/ble_phy.c | 652 +++ .../nimble/drivers/nrf51/include/ble/xcvr.h | 48 + .../mynewt-nimble/nimble/drivers/nrf51/pkg.yml | 31 + .../nimble/drivers/nrf51/src/ble_hw.c | 488 ++ .../nimble/drivers/nrf51/src/ble_phy.c | 1524 +++++ .../nimble/drivers/nrf52/include/ble/xcvr.h | 52 + .../mynewt-nimble/nimble/drivers/nrf52/pkg.yml | 31 + .../nimble/drivers/nrf52/src/ble_hw.c | 488 ++ .../nimble/drivers/nrf52/src/ble_phy.c | 2116 +++++++ .../nimble/drivers/nrf52/src/ble_phy_trace.c | 44 + .../mynewt-nimble/nimble/drivers/nrf52/syscfg.yml | 75 + .../nimble/host/include/host/ble_att.h | 194 + .../nimble/host/include/host/ble_eddystone.h | 117 + .../nimble/host/include/host/ble_gap.h | 2052 +++++++ .../nimble/host/include/host/ble_gatt.h | 896 +++ .../nimble/host/include/host/ble_hs.h | 386 ++ .../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_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/host/include/host/ble_sm.h | 124 + .../nimble/host/include/host/ble_store.h | 303 + .../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 | 502 ++ .../nimble/host/mesh/include/mesh/health_cli.h | 81 + .../nimble/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/libs/mynewt-nimble/nimble/host/mesh/pkg.yml | 49 + .../mynewt-nimble/nimble/host/mesh/src/access.c | 856 +++ .../mynewt-nimble/nimble/host/mesh/src/access.h | 67 + src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c | 439 ++ src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h | 79 + .../mynewt-nimble/nimble/host/mesh/src/atomic.h | 409 ++ .../mynewt-nimble/nimble/host/mesh/src/beacon.c | 441 ++ .../mynewt-nimble/nimble/host/mesh/src/beacon.h | 26 + .../mynewt-nimble/nimble/host/mesh/src/cfg_cli.c | 1498 +++++ .../mynewt-nimble/nimble/host/mesh/src/cfg_srv.c | 3619 ++++++++++++ .../mynewt-nimble/nimble/host/mesh/src/crypto.c | 911 +++ .../mynewt-nimble/nimble/host/mesh/src/crypto.h | 170 + .../nimble/host/mesh/src/foundation.h | 171 + .../mynewt-nimble/nimble/host/mesh/src/friend.c | 1651 ++++++ .../mynewt-nimble/nimble/host/mesh/src/friend.h | 56 + src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c | 870 +++ .../nimble/host/mesh/src/health_cli.c | 556 ++ .../nimble/host/mesh/src/health_srv.c | 453 ++ .../nimble/host/mesh/src/light_model.c | 58 + .../nimble/host/mesh/src/light_model.h | 19 + src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c | 1056 ++++ src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h | 68 + src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c | 361 ++ .../mynewt-nimble/nimble/host/mesh/src/mesh_priv.h | 39 + .../mynewt-nimble/nimble/host/mesh/src/model_cli.c | 301 + .../mynewt-nimble/nimble/host/mesh/src/model_srv.c | 266 + src/libs/mynewt-nimble/nimble/host/mesh/src/net.c | 1433 +++++ src/libs/mynewt-nimble/nimble/host/mesh/src/net.h | 412 ++ .../mynewt-nimble/nimble/host/mesh/src/nodes.c | 161 + .../mynewt-nimble/nimble/host/mesh/src/nodes.h | 10 + src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c | 2008 +++++++ src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h | 37 + .../mynewt-nimble/nimble/host/mesh/src/proxy.c | 1499 +++++ .../mynewt-nimble/nimble/host/mesh/src/proxy.h | 45 + .../mynewt-nimble/nimble/host/mesh/src/settings.c | 2083 +++++++ .../mynewt-nimble/nimble/host/mesh/src/settings.h | 27 + .../mynewt-nimble/nimble/host/mesh/src/shell.c | 2819 +++++++++ .../mynewt-nimble/nimble/host/mesh/src/shell.h | 6 + .../mynewt-nimble/nimble/host/mesh/src/testing.c | 200 + .../mynewt-nimble/nimble/host/mesh/src/testing.h | 23 + .../mynewt-nimble/nimble/host/mesh/src/transport.c | 1668 ++++++ .../mynewt-nimble/nimble/host/mesh/src/transport.h | 105 + src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml | 661 +++ src/libs/mynewt-nimble/nimble/host/pkg.yml | 55 + src/libs/mynewt-nimble/nimble/host/pts/README.txt | 8 + src/libs/mynewt-nimble/nimble/host/pts/pts-gap.txt | 367 ++ .../mynewt-nimble/nimble/host/pts/pts-gatt.txt | 508 ++ .../mynewt-nimble/nimble/host/pts/pts-l2cap.txt | 304 + src/libs/mynewt-nimble/nimble/host/pts/pts-sm.txt | 310 + .../host/pts/tpg/94654-20170317-085122560.tpg | 1026 ++++ .../host/pts/tpg/94654-20170317-085441153.pts | 289 + .../ans/include/services/ans/ble_svc_ans.h | 85 + .../mynewt-nimble/nimble/host/services/ans/pkg.yml | 34 + .../nimble/host/services/ans/src/ble_svc_ans.c | 463 ++ .../nimble/host/services/ans/syscfg.yml | 30 + .../bas/include/services/bas/ble_svc_bas.h | 31 + .../mynewt-nimble/nimble/host/services/bas/pkg.yml | 34 + .../nimble/host/services/bas/src/ble_svc_bas.c | 127 + .../nimble/host/services/bas/syscfg.yml | 34 + .../services/bleuart/include/bleuart/bleuart.h | 42 + .../nimble/host/services/bleuart/pkg.yml | 37 + .../nimble/host/services/bleuart/src/bleuart.c | 203 + .../nimble/host/services/bleuart/syscfg.yml | 28 + .../dis/include/services/dis/ble_svc_dis.h | 113 + .../mynewt-nimble/nimble/host/services/dis/pkg.yml | 34 + .../nimble/host/services/dis/src/ble_svc_dis.c | 331 ++ .../nimble/host/services/dis/syscfg.yml | 109 + .../gap/include/services/gap/ble_svc_gap.h | 54 + .../mynewt-nimble/nimble/host/services/gap/pkg.yml | 34 + .../nimble/host/services/gap/src/ble_svc_gap.c | 298 + .../nimble/host/services/gap/syscfg.yml | 83 + .../gatt/include/services/gatt/ble_svc_gatt.h | 40 + .../nimble/host/services/gatt/pkg.yml | 34 + .../nimble/host/services/gatt/src/ble_svc_gatt.c | 108 + .../nimble/host/services/gatt/syscfg.yml | 24 + .../ias/include/services/ias/ble_svc_ias.h | 38 + .../mynewt-nimble/nimble/host/services/ias/pkg.yml | 34 + .../nimble/host/services/ias/src/ble_svc_ias.c | 149 + .../nimble/host/services/ias/syscfg.yml | 23 + .../ipss/include/services/ipss/ble_svc_ipss.h | 38 + .../nimble/host/services/ipss/pkg.yml | 35 + .../nimble/host/services/ipss/src/ble_svc_ipss.c | 51 + .../nimble/host/services/ipss/syscfg.yml | 24 + .../lls/include/services/lls/ble_svc_lls.h | 51 + .../mynewt-nimble/nimble/host/services/lls/pkg.yml | 34 + .../nimble/host/services/lls/src/ble_svc_lls.c | 192 + .../nimble/host/services/lls/syscfg.yml | 22 + .../tps/include/services/tps/ble_svc_tps.h | 31 + .../mynewt-nimble/nimble/host/services/tps/pkg.yml | 34 + .../nimble/host/services/tps/src/ble_svc_tps.c | 107 + .../nimble/host/services/tps/syscfg.yml | 23 + src/libs/mynewt-nimble/nimble/host/src/ble_att.c | 589 ++ .../mynewt-nimble/nimble/host/src/ble_att_clt.c | 956 +++ .../mynewt-nimble/nimble/host/src/ble_att_cmd.c | 637 ++ .../nimble/host/src/ble_att_cmd_priv.h | 449 ++ .../mynewt-nimble/nimble/host/src/ble_att_priv.h | 300 + .../mynewt-nimble/nimble/host/src/ble_att_svr.c | 2729 +++++++++ .../mynewt-nimble/nimble/host/src/ble_eddystone.c | 178 + src/libs/mynewt-nimble/nimble/host/src/ble_gap.c | 6073 ++++++++++++++++++++ .../mynewt-nimble/nimble/host/src/ble_gap_priv.h | 153 + .../mynewt-nimble/nimble/host/src/ble_gatt_priv.h | 199 + src/libs/mynewt-nimble/nimble/host/src/ble_gattc.c | 4806 ++++++++++++++++ src/libs/mynewt-nimble/nimble/host/src/ble_gatts.c | 2184 +++++++ .../mynewt-nimble/nimble/host/src/ble_gatts_lcl.c | 211 + src/libs/mynewt-nimble/nimble/host/src/ble_hs.c | 808 +++ .../mynewt-nimble/nimble/host/src/ble_hs_adv.c | 803 +++ .../nimble/host/src/ble_hs_adv_priv.h | 36 + .../mynewt-nimble/nimble/host/src/ble_hs_atomic.c | 120 + .../nimble/host/src/ble_hs_atomic_priv.h | 41 + .../mynewt-nimble/nimble/host/src/ble_hs_cfg.c | 33 + .../mynewt-nimble/nimble/host/src/ble_hs_conn.c | 576 ++ .../nimble/host/src/ble_hs_conn_priv.h | 148 + .../mynewt-nimble/nimble/host/src/ble_hs_flow.c | 267 + .../nimble/host/src/ble_hs_flow_priv.h | 36 + .../mynewt-nimble/nimble/host/src/ble_hs_hci.c | 622 ++ .../mynewt-nimble/nimble/host/src/ble_hs_hci_cmd.c | 102 + .../mynewt-nimble/nimble/host/src/ble_hs_hci_evt.c | 884 +++ .../nimble/host/src/ble_hs_hci_priv.h | 125 + .../nimble/host/src/ble_hs_hci_util.c | 215 + src/libs/mynewt-nimble/nimble/host/src/ble_hs_id.c | 306 + .../mynewt-nimble/nimble/host/src/ble_hs_id_priv.h | 40 + .../mynewt-nimble/nimble/host/src/ble_hs_log.c | 47 + .../mynewt-nimble/nimble/host/src/ble_hs_mbuf.c | 161 + .../nimble/host/src/ble_hs_mbuf_priv.h | 38 + .../mynewt-nimble/nimble/host/src/ble_hs_misc.c | 145 + .../mynewt-nimble/nimble/host/src/ble_hs_mqueue.c | 82 + .../nimble/host/src/ble_hs_periodic_sync.c | 154 + .../nimble/host/src/ble_hs_periodic_sync_priv.h | 55 + .../mynewt-nimble/nimble/host/src/ble_hs_priv.h | 157 + .../mynewt-nimble/nimble/host/src/ble_hs_pvcy.c | 248 + .../nimble/host/src/ble_hs_pvcy_priv.h | 43 + .../nimble/host/src/ble_hs_shutdown.c | 69 + .../mynewt-nimble/nimble/host/src/ble_hs_startup.c | 367 ++ .../nimble/host/src/ble_hs_startup_priv.h | 33 + .../mynewt-nimble/nimble/host/src/ble_hs_stop.c | 283 + .../mynewt-nimble/nimble/host/src/ble_ibeacon.c | 82 + src/libs/mynewt-nimble/nimble/host/src/ble_l2cap.c | 506 ++ .../mynewt-nimble/nimble/host/src/ble_l2cap_coc.c | 616 ++ .../nimble/host/src/ble_l2cap_coc_priv.h | 106 + .../mynewt-nimble/nimble/host/src/ble_l2cap_priv.h | 144 + .../mynewt-nimble/nimble/host/src/ble_l2cap_sig.c | 1941 +++++++ .../nimble/host/src/ble_l2cap_sig_cmd.c | 112 + .../nimble/host/src/ble_l2cap_sig_priv.h | 184 + .../mynewt-nimble/nimble/host/src/ble_monitor.c | 473 ++ .../nimble/host/src/ble_monitor_priv.h | 100 + src/libs/mynewt-nimble/nimble/host/src/ble_sm.c | 2813 +++++++++ .../mynewt-nimble/nimble/host/src/ble_sm_alg.c | 530 ++ .../mynewt-nimble/nimble/host/src/ble_sm_cmd.c | 63 + .../mynewt-nimble/nimble/host/src/ble_sm_lgcy.c | 254 + .../mynewt-nimble/nimble/host/src/ble_sm_priv.h | 423 ++ src/libs/mynewt-nimble/nimble/host/src/ble_sm_sc.c | 909 +++ src/libs/mynewt-nimble/nimble/host/src/ble_store.c | 420 ++ .../mynewt-nimble/nimble/host/src/ble_store_util.c | 256 + src/libs/mynewt-nimble/nimble/host/src/ble_uuid.c | 260 + .../mynewt-nimble/nimble/host/src/ble_uuid_priv.h | 45 + .../config/include/store/config/ble_store_config.h | 39 + .../mynewt-nimble/nimble/host/store/config/pkg.yml | 38 + .../host/store/config/src/ble_store_config.c | 533 ++ .../host/store/config/src/ble_store_config_conf.c | 231 + .../host/store/config/src/ble_store_config_priv.h | 59 + .../nimble/host/store/config/syscfg.yml | 27 + .../store/ram/include/store/ram/ble_store_ram.h | 39 + .../mynewt-nimble/nimble/host/store/ram/pkg.yml | 37 + .../nimble/host/store/ram/src/ble_store_ram.c | 497 ++ .../mynewt-nimble/nimble/host/store/ram/syscfg.yml | 23 + src/libs/mynewt-nimble/nimble/host/syscfg.yml | 471 ++ src/libs/mynewt-nimble/nimble/host/test/pkg.yml | 34 + .../nimble/host/test/src/ble_att_clt_test.c | 548 ++ .../nimble/host/test/src/ble_att_svr_test.c | 2138 +++++++ .../nimble/host/test/src/ble_gap_test.c | 3168 ++++++++++ .../nimble/host/test/src/ble_gatt_conn_test.c | 746 +++ .../nimble/host/test/src/ble_gatt_disc_c_test.c | 722 +++ .../nimble/host/test/src/ble_gatt_disc_d_test.c | 446 ++ .../nimble/host/test/src/ble_gatt_disc_s_test.c | 631 ++ .../nimble/host/test/src/ble_gatt_find_s_test.c | 433 ++ .../nimble/host/test/src/ble_gatt_read_test.c | 923 +++ .../nimble/host/test/src/ble_gatt_write_test.c | 832 +++ .../nimble/host/test/src/ble_gatts_notify_test.c | 1090 ++++ .../nimble/host/test/src/ble_gatts_read_test.c | 257 + .../nimble/host/test/src/ble_gatts_reg_test.c | 719 +++ .../nimble/host/test/src/ble_hs_adv_test.c | 1280 +++++ .../nimble/host/test/src/ble_hs_conn_test.c | 224 + .../nimble/host/test/src/ble_hs_hci_test.c | 342 ++ .../nimble/host/test/src/ble_hs_id_test.c | 124 + .../nimble/host/test/src/ble_hs_pvcy_test.c | 509 ++ .../nimble/host/test/src/ble_hs_stop_test.c | 203 + .../nimble/host/test/src/ble_hs_test.c | 83 + .../nimble/host/test/src/ble_hs_test.h | 62 + .../nimble/host/test/src/ble_hs_test_util.c | 2048 +++++++ .../nimble/host/test/src/ble_hs_test_util.h | 305 + .../nimble/host/test/src/ble_hs_test_util_hci.c | 616 ++ .../nimble/host/test/src/ble_hs_test_util_hci.h | 105 + .../nimble/host/test/src/ble_l2cap_test.c | 1500 +++++ .../nimble/host/test/src/ble_os_test.c | 388 ++ .../nimble/host/test/src/ble_sm_lgcy_test.c | 849 +++ .../nimble/host/test/src/ble_sm_sc_test.c | 4938 ++++++++++++++++ .../nimble/host/test/src/ble_sm_test.c | 414 ++ .../nimble/host/test/src/ble_sm_test_util.c | 2967 ++++++++++ .../nimble/host/test/src/ble_sm_test_util.h | 128 + .../nimble/host/test/src/ble_store_test.c | 435 ++ .../nimble/host/test/src/ble_uuid_test.c | 76 + src/libs/mynewt-nimble/nimble/host/test/syscfg.yml | 31 + .../mynewt-nimble/nimble/host/tools/log2smtest.rb | 1029 ++++ .../nimble/host/util/include/host/util/util.h | 46 + src/libs/mynewt-nimble/nimble/host/util/pkg.yml | 29 + src/libs/mynewt-nimble/nimble/host/util/src/addr.c | 95 + src/libs/mynewt-nimble/nimble/host/util/syscfg.yml | 19 + src/libs/mynewt-nimble/nimble/include/nimble/ble.h | 304 + .../nimble/include/nimble/ble_hci_trans.h | 192 + .../nimble/include/nimble/hci_common.h | 1536 +++++ .../nimble/include/nimble/nimble_npl.h | 174 + .../nimble/include/nimble/nimble_opt.h | 34 + .../nimble/include/nimble/nimble_opt_auto.h | 117 + src/libs/mynewt-nimble/nimble/pkg.yml | 30 + src/libs/mynewt-nimble/nimble/syscfg.yml | 83 + .../nimble/transport/da1469x/.gitignore | 2 + .../mynewt-nimble/nimble/transport/da1469x/README | 13 + .../cmac_driver/include/cmac_driver/cmac_host.h | 37 + .../nimble/transport/da1469x/cmac_driver/pkg.yml | 27 + .../da1469x/cmac_driver/scripts/build_libcmac.sh | 51 + .../transport/da1469x/cmac_driver/src/cmac_host.c | 326 ++ .../transport/da1469x/cmac_driver/syscfg.yml | 23 + .../mynewt-nimble/nimble/transport/da1469x/pkg.yml | 38 + .../nimble/transport/da1469x/src/da1469x_ble_hci.c | 368 ++ .../nimble/transport/da1469x/syscfg.yml | 43 + .../emspi/include/transport/emspi/ble_hci_emspi.h | 33 + .../mynewt-nimble/nimble/transport/emspi/pkg.yml | 36 + .../nimble/transport/emspi/src/ble_hci_emspi.c | 965 ++++ .../nimble/transport/emspi/syscfg.yml | 99 + src/libs/mynewt-nimble/nimble/transport/pkg.yml | 45 + .../ram/include/transport/ram/ble_hci_ram.h | 35 + .../mynewt-nimble/nimble/transport/ram/pkg.yml | 36 + .../nimble/transport/ram/src/ble_hci_ram.c | 238 + .../mynewt-nimble/nimble/transport/ram/syscfg.yml | 48 + .../socket/include/socket/ble_hci_socket.h | 34 + .../mynewt-nimble/nimble/transport/socket/pkg.yml | 38 + .../nimble/transport/socket/src/ble_hci_socket.c | 886 +++ .../nimble/transport/socket/syscfg.yml | 79 + src/libs/mynewt-nimble/nimble/transport/syscfg.yml | 68 + .../uart/include/transport/uart/ble_hci_uart.h | 33 + .../mynewt-nimble/nimble/transport/uart/pkg.yml | 38 + .../nimble/transport/uart/src/ble_hci_uart.c | 1187 ++++ .../mynewt-nimble/nimble/transport/uart/syscfg.yml | 72 + 333 files changed, 158458 insertions(+) create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_hw.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_adv.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_conn.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_ctrl.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_hci.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_resolv.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_scan.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sched.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sync.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_test.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_trace.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_utils.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_whitelist.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy_trace.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_adv.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_hci.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_ctrl.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_resolv.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_scan.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sched.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sync.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_trace.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_utils.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/src/ble_ll_whitelist.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/controller/test/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.h create mode 100644 src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_test.c create mode 100644 src/libs/mynewt-nimble/nimble/controller/test/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/drivers/native/include/ble/xcvr.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/native/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/native/src/ble_phy.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf51/include/ble/xcvr.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf51/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_hw.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_phy.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/include/ble/xcvr.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy_trace.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/nrf52/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_att.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_eddystone.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_gap.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_gatt.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_adv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_hci.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_id.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_log.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_mbuf.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_stop.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_ibeacon.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_l2cap.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_monitor.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_sm.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_store.h create mode 100644 src/libs/mynewt-nimble/nimble/host/include/host/ble_uuid.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/access.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/access.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/net.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/net.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/README.txt create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/pts-gap.txt create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/pts-gatt.txt create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/pts-l2cap.txt create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/pts-sm.txt create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/tpg/94654-20170317-085122560.tpg create mode 100644 src/libs/mynewt-nimble/nimble/host/pts/tpg/94654-20170317-085441153.pts create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ans/include/services/ans/ble_svc_ans.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ans/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ans/src/ble_svc_ans.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ans/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bas/include/services/bas/ble_svc_bas.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bas/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bas/src/ble_svc_bas.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bas/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bleuart/include/bleuart/bleuart.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bleuart/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bleuart/src/bleuart.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/bleuart/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/dis/include/services/dis/ble_svc_dis.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/dis/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/dis/src/ble_svc_dis.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/dis/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gap/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gap/src/ble_svc_gap.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gap/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gatt/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gatt/src/ble_svc_gatt.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/gatt/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ias/include/services/ias/ble_svc_ias.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ias/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ias/src/ble_svc_ias.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ias/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ipss/include/services/ipss/ble_svc_ipss.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ipss/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ipss/src/ble_svc_ipss.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/ipss/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/lls/include/services/lls/ble_svc_lls.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/lls/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/lls/src/ble_svc_lls.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/lls/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/tps/include/services/tps/ble_svc_tps.h create mode 100644 src/libs/mynewt-nimble/nimble/host/services/tps/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/services/tps/src/ble_svc_tps.c create mode 100644 src/libs/mynewt-nimble/nimble/host/services/tps/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att_clt.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att_cmd.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att_cmd_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_att_svr.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_eddystone.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gap.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gap_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gatt_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gattc.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gatts.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_gatts_lcl.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_adv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_adv_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_atomic.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_atomic_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_cfg.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_conn.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_conn_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_flow.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_flow_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_hci.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_hci_cmd.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_hci_evt.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_hci_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_hci_util.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_id.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_id_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_log.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_mbuf.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_mbuf_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_misc.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_mqueue.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_periodic_sync.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_periodic_sync_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_pvcy.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_pvcy_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_shutdown.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_startup.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_startup_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_hs_stop.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_ibeacon.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_coc.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_coc_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig_cmd.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_monitor.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_monitor_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm_alg.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm_cmd.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm_lgcy.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_sm_sc.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_store.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_store_util.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_uuid.c create mode 100644 src/libs/mynewt-nimble/nimble/host/src/ble_uuid_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/include/store/config/ble_store_config.h create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/src/ble_store_config.c create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/src/ble_store_config_conf.c create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/src/ble_store_config_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/store/config/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/store/ram/include/store/ram/ble_store_ram.h create mode 100644 src/libs/mynewt-nimble/nimble/host/store/ram/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/store/ram/src/ble_store_ram.c create mode 100644 src/libs/mynewt-nimble/nimble/host/store/ram/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/test/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_att_clt_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_att_svr_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gap_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_conn_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_disc_c_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_disc_d_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_disc_s_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_find_s_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_read_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatt_write_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatts_notify_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatts_read_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_gatts_reg_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_adv_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_conn_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_hci_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_id_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_pvcy_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_stop_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test.h create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test_util.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test_util.h create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test_util_hci.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_hs_test_util_hci.h create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_l2cap_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_os_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_sm_lgcy_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_sm_sc_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_sm_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_sm_test_util.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_sm_test_util.h create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_store_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/src/ble_uuid_test.c create mode 100644 src/libs/mynewt-nimble/nimble/host/test/syscfg.yml create mode 100755 src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb create mode 100644 src/libs/mynewt-nimble/nimble/host/util/include/host/util/util.h create mode 100644 src/libs/mynewt-nimble/nimble/host/util/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/util/src/addr.c create mode 100644 src/libs/mynewt-nimble/nimble/host/util/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/ble.h create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/ble_hci_trans.h create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/hci_common.h create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/nimble_npl.h create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/nimble_opt.h create mode 100644 src/libs/mynewt-nimble/nimble/include/nimble/nimble_opt_auto.h create mode 100644 src/libs/mynewt-nimble/nimble/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/.gitignore create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/README create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/include/cmac_driver/cmac_host.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/pkg.yml create mode 100755 src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/scripts/build_libcmac.sh create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/src/cmac_host.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/cmac_driver/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/src/da1469x_ble_hci.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/da1469x/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/ram/include/transport/ram/ble_hci_ram.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/ram/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/ram/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/syscfg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml (limited to 'src/libs/mynewt-nimble/nimble') diff --git a/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_hw.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_hw.h new file mode 100644 index 00000000..cf293076 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll.h new file mode 100644 index 00000000..e515fb98 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll.h @@ -0,0 +1,587 @@ +/* + * 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 "stats/stats.h" +#include "os/os_cputime.h" +#include "nimble/nimble_opt.h" +#include "nimble/nimble_npl.h" +#include "controller/ble_phy.h" + +#ifdef MYNEWT +#include "controller/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 init (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); + +// TODO added by JF, don't know why I need this? +void ble_ll_task(void *arg); + +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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_adv.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_adv.h new file mode 100644 index 00000000..4afaadd0 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_conn.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_conn.h new file mode 100644 index 00000000..26c99265 --- /dev/null +++ b/src/libs/mynewt-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 "os/os.h" +#include "nimble/ble.h" +#include "nimble/hci_common.h" +#include "nimble/nimble_npl.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_ctrl.h" +#include "controller/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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_ctrl.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_ctrl.h new file mode 100644 index 00000000..b0da1e73 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_hci.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_hci.h new file mode 100644 index 00000000..abef8746 --- /dev/null +++ b/src/libs/mynewt-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/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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_resolv.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_resolv.h new file mode 100644 index 00000000..228e0a37 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_rfmgmt.h new file mode 100644 index 00000000..37b81a88 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_scan.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_scan.h new file mode 100644 index 00000000..139ad5e1 --- /dev/null +++ b/src/libs/mynewt-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 "controller/ble_ll_sched.h" +#include "hal/hal_timer.h" +#include "syscfg/syscfg.h" +#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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sched.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sched.h new file mode 100644 index 00000000..a614cf09 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sync.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_sync.h new file mode 100644 index 00000000..712af6df --- /dev/null +++ b/src/libs/mynewt-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/ble.h" +#include "controller/ble_ll_hci.h" +#include "controller/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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_test.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_test.h new file mode 100644 index 00000000..32984c6b --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_trace.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_trace.h new file mode 100644 index 00000000..7545b570 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_utils.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_utils.h new file mode 100644 index 00000000..24830900 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_whitelist.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_ll_whitelist.h new file mode 100644 index 00000000..2d3b6c5d --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy.h new file mode 100644 index 00000000..cabb0adb --- /dev/null +++ b/src/libs/mynewt-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/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/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy_trace.h b/src/libs/mynewt-nimble/nimble/controller/include/controller/ble_phy_trace.h new file mode 100644 index 00000000..64e55118 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/controller/pkg.yml b/src/libs/mynewt-nimble/nimble/controller/pkg.yml new file mode 100644 index 00000000..96c63679 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/pkg.yml @@ -0,0 +1,38 @@ +# +# 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. +# + +pkg.name: nimble/controller +pkg.description: Controller side of the nimble Bluetooth Smart stack. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.req_apis: + - ble_driver + - ble_transport + - stats + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - nimble + +pkg.init: + ble_ll_init: 'MYNEWT_VAL(BLE_LL_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll.c new file mode 100644 index 00000000..996ad9c3 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll.c @@ -0,0 +1,1714 @@ +/* + * 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 +#include +#include +#include +#include "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "os/os_cputime.h" +#include "stats/stats.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_hw.h" +#include "controller/ble_phy.h" +#include "controller/ble_phy_trace.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_rfmgmt.h" +#include "controller/ble_ll_trace.h" +#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 + */ +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)); + } +} + +/** + * 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 init */ + 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 */ + 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 +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_adv.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_adv.c new file mode 100644 index 00000000..8b661f8c --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_adv.c @@ -0,0 +1,5136 @@ +/* + * 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 +#include +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "os/os_cputime.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_phy.h" +#include "controller/ble_hw.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_trace.h" +#include "controller/ble_ll_utils.h" +#include "controller/ble_ll_rfmgmt.h" +#include "ble_ll_conn_priv.h" + +/* 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; + uint8_t aux_dropped : 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_aux_dropped; + } + +#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_aux_dropped; + } + + /* 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_aux_dropped: + advsm->aux_dropped = 1; + 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; + advsm->aux_dropped = 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_dropped) { + ble_ll_adv_drop_event(advsm); + return; + } + + 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]); + } +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c new file mode 100644 index 00000000..1b17a0d2 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c @@ -0,0 +1,4270 @@ +/* + * 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 +#include +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "os/os_cputime.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "ble/xcvr.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_ctrl.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_trace.h" +#include "controller/ble_ll_rfmgmt.h" +#include "controller/ble_phy.h" +#include "controller/ble_hw.h" +#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 init 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(); +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_hci.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_hci.c new file mode 100644 index 00000000..1350fdc0 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_hci.c @@ -0,0 +1,1896 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_utils.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_conn.h" +#include "controller/ble_ll_ctrl.h" +#include "controller/ble_ll_scan.h" +#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 diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_priv.h b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_priv.h new file mode 100644 index 00000000..f2f72d17 --- /dev/null +++ b/src/libs/mynewt-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 "controller/ble_ll_conn.h" +#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/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_ctrl.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_ctrl.c new file mode 100644 index 00000000..ea2ba834 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_ctrl.c @@ -0,0 +1,2744 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_ctrl.h" +#include "controller/ble_ll_trace.h" +#include "controller/ble_hw.h" +#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; + } + /* 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; +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm.c new file mode 100644 index 00000000..de3b168b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm.c @@ -0,0 +1,726 @@ +/* + * 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 "syscfg/syscfg.h" +#include "sysinit/sysinit.h" + +#if MYNEWT_VAL(BLE_LL_DTM) + +#include +#include "os/os.h" +#include "stats/stats.h" +#include "controller/ble_ll.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll_sched.h" +#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 diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm_priv.h b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_dtm_priv.h new file mode 100644 index 00000000..e04af07b --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci.c new file mode 100644 index 00000000..b82adc2e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci.c @@ -0,0 +1,1515 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_hw.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_resolv.h" +#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 +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c new file mode 100644 index 00000000..dbc50db9 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c @@ -0,0 +1,522 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "nimble/ble.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#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); + } +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_priv.h b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_priv.h new file mode 100644 index 00000000..900950ef --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c new file mode 100644 index 00000000..7b384e9d --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c @@ -0,0 +1,185 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "controller/ble_hw.h" +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_resolv.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_resolv.c new file mode 100644 index 00000000..02af9304 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_resolv.c @@ -0,0 +1,753 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_sync.h" +#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) */ + diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c new file mode 100644 index 00000000..3bf5d5fa --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c @@ -0,0 +1,346 @@ +/* + * 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 +#include +#include +#include +#include "syscfg/syscfg.h" +#include "os/os_cputime.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_sched.h" +#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; + /* 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 diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_scan.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_scan.c new file mode 100644 index 00000000..2e93ba23 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_scan.c @@ -0,0 +1,3979 @@ +/* + * 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 +#include +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "os/os_cputime.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "controller/ble_phy.h" +#include "controller/ble_hw.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_rfmgmt.h" +#include "controller/ble_ll_trace.h" +#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, + int8_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_PRIVACY) +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data; +#endif + 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; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) + 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; + } +#endif + 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 init function again */ + ble_ll_scan_common_init(); +} + +/** + * ble ll scan init + * + * 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(); +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sched.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sched.c new file mode 100644 index 00000000..370faddf --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sched.c @@ -0,0 +1,1828 @@ +/* + * 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 +#include +#include +#include +#include "os/os.h" +#include "os/os_cputime.h" +#include "ble/xcvr.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_rfmgmt.h" +#include "controller/ble_ll_trace.h" +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c new file mode 100644 index 00000000..834e0095 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c @@ -0,0 +1,458 @@ +/* + * 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 +#include + +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "controller/ble_ll.h" +#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, +}; diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sync.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sync.c new file mode 100644 index 00000000..75f18bf2 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_sync.c @@ -0,0 +1,2246 @@ +/* + * 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 +#include +#include + +#include "syscfg/syscfg.h" + +#include "controller/ble_ll.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_sync.h" +#include "controller/ble_ll_utils.h" +#include "controller/ble_ll_sched.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_scan.h" +#include "controller/ble_ll_resolv.h" +#include "controller/ble_ll_rfmgmt.h" + +#include "nimble/ble.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" + +#include "ble_ll_conn_priv.h" + +#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 diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_trace.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_trace.c new file mode 100644 index 00000000..330b3d4c --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_trace.c @@ -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. + */ + +#include +#include "syscfg/syscfg.h" +#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 diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_utils.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_utils.c new file mode 100644 index 00000000..7fbb18f1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_utils.c @@ -0,0 +1,301 @@ +/* + * 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 +#include +#include "nimble/ble.h" +#include "controller/ble_ll.h" +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_whitelist.c b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_whitelist.c new file mode 100644 index 00000000..04ec6428 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/src/ble_ll_whitelist.c @@ -0,0 +1,287 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "ble/xcvr.h" +#include "controller/ble_ll_whitelist.h" +#include "controller/ble_ll_hci.h" +#include "controller/ble_ll_adv.h" +#include "controller/ble_ll_scan.h" +#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 +} diff --git a/src/libs/mynewt-nimble/nimble/controller/syscfg.yml b/src/libs/mynewt-nimble/nimble/controller/syscfg.yml new file mode 100644 index 00000000..85049cb0 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/syscfg.yml @@ -0,0 +1,434 @@ +# 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. +# + +syscfg.defs: + BLE_CONTROLLER: + description: > + Indicates that NimBLE controller is present. The default value for + this setting shall not be overriden. + value: 1 + + BLE_HW_WHITELIST_ENABLE: + description: > + Used to enable hardware white list + value: 1 + + BLE_LL_SYSVIEW: + description: > + Enable SystemView tracing module for controller. + value: 0 + + BLE_LL_PRIO: + description: 'The priority of the LL task' + type: 'task_priority' + value: 0 + + # Sleep clock accuracy (sca). This is the amount of drift in the system + # during when the device is sleeping (in parts per million). + # + # NOTE: 'the' master sca is an enumerated value based on the sca. Rather + # than have a piece of code calculate this value, the developer must set + # this value based on the value of the SCA using the following table: + # + # SCA between 251 and 500 ppm (inclusive); master sca = 0 + # SCA between 151 and 250 ppm (inclusive); master sca = 1 + # SCA between 101 and 150 ppm (inclusive); master sca = 2 + # SCA between 76 and 100 ppm (inclusive); master sca = 3 + # SCA between 51 and 75 ppm (inclusive); master sca = 4 + # SCA between 31 and 50 ppm (inclusive); master sca = 5 + # SCA between 21 and 30 ppm (inclusive); master sca = 6 + # SCA between 0 and 20 ppm (inclusive); master sca = 7 + # + # For example: + # if your clock drift is 101 ppm, your master should be set to 2. + # if your clock drift is 20, your master sca should be set to 7. + # + # The values provided below are merely meant to be an example and should + # be replaced by values appropriate for your platform. + BLE_LL_OUR_SCA: + description: 'The system clock accuracy of the device.' + value: '60' # in ppm + + BLE_LL_MASTER_SCA: + description: 'Enumerated value based on our sca' + value: '4' + + BLE_LL_TX_PWR_DBM: + description: 'Transmit power level.' + value: '0' + + BLE_LL_NUM_COMP_PKT_ITVL_MS: + description: > + Determines the interval at which the controller will send the + number of completed packets event to the host. Rate is in milliseconds. + value: 2000 + + BLE_LL_MFRG_ID: + description: > + Manufacturer ID. Should be set to unique ID per manufacturer. + value: '0xFFFF' + + # Configuration items for the number of duplicate advertisers and the + # number of advertisers from which we have heard a scan response. + BLE_LL_NUM_SCAN_DUP_ADVS: + description: 'The number of duplicate advertisers stored.' + value: '8' + BLE_LL_NUM_SCAN_RSP_ADVS: + description: > + The number of advertisers from which we have heard a scan + response. Prevents sending duplicate events to host. + value: '8' + + BLE_LL_WHITELIST_SIZE: + description: 'Size of the LL whitelist.' + value: '8' + + BLE_LL_RESOLV_LIST_SIZE: + description: 'Size of the resolving list.' + value: '4' + + # Data length management definitions for connections. These define the + # maximum size of the PDU's that will be sent and/or received in a + # connection. + BLE_LL_MAX_PKT_SIZE: + description: 'The maximum PDU size that can be sent/received' + value: '251' + BLE_LL_SUPP_MAX_RX_BYTES: + description: 'The maximum supported received PDU size' + value: MYNEWT_VAL(BLE_LL_MAX_PKT_SIZE) + BLE_LL_SUPP_MAX_TX_BYTES: + description: 'The maximum supported transmit PDU size' + value: MYNEWT_VAL(BLE_LL_MAX_PKT_SIZE) + BLE_LL_CONN_INIT_MAX_TX_BYTES: + description: > + Used to set the initial maximum transmit PDU size in a + connection. If this is set to a value greater than 27, + the controller will automatically attempt to do the + data length update procedure. The host can always tell + the controller to update this value. + value: '27' + + # The number of slots that will be allocated to each connection + BLE_LL_CONN_INIT_SLOTS: + description: > + This is the number of "slots" allocated to a connection when scheduling + connections. Each slot is 1.25 msecs long. Note that a connection event may + last longer than the number of slots allocated here and may also end earlier + (depending on when the next scheduled event occurs and how much data needs + to be transferred in the connection). However, you will be guaranteed that + a connection event will be given this much time, if needed. Consecutively + scheduled items will be at least this far apart + value: '4' + + BLE_LL_CONN_INIT_MIN_WIN_OFFSET: + description: > + This is the minimum number of "slots" for WindowOffset value used for + CONNECT_IND when creating new connection as a master. Each slot is 1.25 + msecs long. Increasing this value will delay first connection event after + connection is created. However, older TI CC254x controllers cannot change + connection parameters later if WindowOffset was set to 0 in CONNECT_IND. To + ensure interoperability with such devices set this value to 2 (or more). + value: '0' + + # Strict scheduling + BLE_LL_STRICT_CONN_SCHEDULING: + description: > + Forces the scheduler on a central to schedule connections in fixed + time intervals called periods. If set to 0, the scheduler is not forced + to do this. If set to 1, the scheduler will only schedule connections at + period boundaries. See comments in ble_ll_sched.h for more details. + value: '0' + + BLE_LL_ADD_STRICT_SCHED_PERIODS: + description: > + The number of additional periods that will be allocated for strict + scheduling. The total # of periods allocated for strict scheduling + will be equal to the number of connections plus this number. + value: '0' + + BLE_LL_USECS_PER_PERIOD: + description: > + The number of usecs per period. + value: '3250' + + # The number of random bytes to store + BLE_LL_RNG_BUFSIZE: + description: > + The number of random bytes that the link layer will try to + always have available for the host to use. Decreasing this + value may cause host delays if the host needs lots of random + material often. + value: '32' + + BLE_LL_RFMGMT_ENABLE_TIME: + description: > + Time required for radio and/or related components to be fully + enabled before any request from LL is sent. This value is used + by rfmgmt to enable PHY in advance, before request from LL is + made. It depends on radio driver selected and may also depend + on hardware used: + - nrf51 - time required for XTAL to settle + - nrf52 - time required for XTAL to settle + Value is specified in microseconds. If set to 0, rfmgmt keeps + PHY enabled all the time. + value: MYNEWT_VAL(BLE_XTAL_SETTLE_TIME) + + # Configuration for LL supported features. + # + # There are a total 8 features that the LL can support. These can be found + # in v4.2, Vol 6 Part B Section 4.6. + # + # These feature definitions are used to inform a host or other controller + # about the LL features supported by the controller. + # + # NOTE: 'the' controller always supports extended reject indicate and thus + # is not listed here. + + + BLE_LL_CFG_FEAT_LE_ENCRYPTION: + description: > + This option enables/disables encryption support in the controller. + This option saves both both code and RAM. + value: '1' + + BLE_LL_CFG_FEAT_CONN_PARAM_REQ: + description: > + This option enables/disables the connection parameter request + procedure. This is implemented in the controller but is disabled + by default. + value: '1' + + BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG: + description: > + This option allows a slave to initiate the feature exchange + procedure. This feature is implemented but currently has no impact + on code or ram size + value: '1' + + BLE_LL_CFG_FEAT_LE_PING: + description: > + This option allows a controller to send/receive LE pings. + Currently, this feature is not implemented by the controller so + turning it on or off has no effect. + value: 'MYNEWT_VAL_BLE_LL_CFG_FEAT_LE_ENCRYPTION' + + BLE_LL_CFG_FEAT_DATA_LEN_EXT: + description: > + This option enables/disables the data length update procedure in + the controller. If enabled, the controller is allowed to change the + size of tx/rx pdu's used in a connection. This option has only + minor impact on code size and non on RAM. + value: '1' + + BLE_LL_CFG_FEAT_LL_PRIVACY: + description: > + This option is used to enable/disable LL privacy. + value: '1' + + BLE_LL_CFG_FEAT_LE_CSA2: + description: > + This option is used to enable/disable support for LE Channel + Selection Algorithm #2. + value: '0' + + BLE_LL_CFG_FEAT_LE_2M_PHY: + description: > + This option is used to enable/disable support for the 2Mbps PHY. + value: '0' + + BLE_LL_CFG_FEAT_LE_CODED_PHY: + description: > + This option is used to enable/disable support for the coded PHY. + value: '0' + + BLE_LL_CFG_FEAT_LL_EXT_ADV: + description: > + This option is used to enable/disable support for Extended + Advertising Feature. That means extended scanner, advertiser + and connect. + value: MYNEWT_VAL(BLE_EXT_ADV) + + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV: + description: > + This option is used to enable/disable support for Periodic + Advertising Feature. + value: MYNEWT_VAL(BLE_PERIODIC_ADV) + + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_CNT: + description: > + This option is used to configure number of supported periodic syncs. + value: MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS) + + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_LIST_CNT: + description: > + Size of Periodic Advertiser sync list. + value: MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS) + + BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER: + description: > + This option is use to enable/disable support for Periodic + Advertising Sync Transfer Feature. + value: MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + + BLE_LL_EXT_ADV_AUX_PTR_CNT: + description: > + This option configure a max number of scheduled outstanding auxiliary + packets for receive on secondary advertising channel. + value: 0 + + BLE_PUBLIC_DEV_ADDR: + description: > + Allows the target or app to override the public device address + used by the controller. If all zero, the controller will + attempt to retrieve the public device address from its + chip specific location. If non-zero, this address will + be used. + value: "(uint8_t[6]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}" + + BLE_LL_DTM: + description: > + Enables HCI Test commands needed for Bluetooth SIG certification + value: MYNEWT_VAL(BLE_LL_DIRECT_TEST_MODE) + BLE_LL_DTM_EXTENSIONS: + description: > + Enables non-standard extensions to HCI test commands. Once enabled, + HCI_LE_Transmitter_Test accepts extra parameters in addition to + those defined in Core specification + interval (2 octets) interval between packets (usecs), overrides + standard interval + pkt_count (2 octets) number of packets to transmit, controller + will automatically stop sending packets + after given number of packets was sent + Setting either of these parameters to 0 will configure for default + behavior, as per Core specification. + If specified interval is shorter then allowed by specification it + will be ignored. + Extended parameters shall immediately follow standard parameters. + Controller can accept both standard and extended version of command + depending on specified HCI command length. + value: 0 + + BLE_LL_VND_EVENT_ON_ASSERT: + description: > + This options enables controller to send a vendor-specific event on + an assertion in controller code. The event contains file name and + line number where assertion occured. + value: 0 + + BLE_LL_SYSINIT_STAGE: + description: > + Sysinit stage for the NimBLE controller. + value: 250 + + BLE_LL_DEBUG_GPIO_HCI_CMD: + description: > + GPIO pin number to debug HCI commands flow. Pin is set to high state + when HCI command is being processed. + value: -1 + BLE_LL_DEBUG_GPIO_HCI_EV: + description: > + GPIO pin number to debug HCI events flow. Pin is set to high state + when HCI event is being sent. + value: -1 + BLE_LL_DEBUG_GPIO_SCHED_RUN: + description: > + GPIO pin number to debug scheduler running (on timer). Pin is set + to high state while scheduler is running. + value: -1 + BLE_LL_DEBUG_GPIO_SCHED_ITEM_CB: + description: > + GPIO pin number to debug scheduler item execution times. Pin is set + to high state while item is executed. + value: -1 + +# Below settings allow to change scheduler timings. These should be left at +# default values unless you know what you are doing! + BLE_LL_SCHED_AUX_MAFS_DELAY: + description: > + Additional delay [us] between last ADV_EXT_IND and AUX_ADV_IND PDUs + when scheduling extended advertising event. This extends T_MAFS. + value: 0 + BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY: + description: > + Additional delay [us] between consecutive AUX_CHAIN_IND PDUs + when scheduling extended or periodic advertising event. This extends + T_MAFS. + value: 0 + BLE_LL_SCHED_SCAN_AUX_PDU_LEN: + description: > + This is expected PDU len for AUX_ADV_IND and subsequent + AUX_CHAIN_IND. When scheduling scan scheduler will reserve time for + receiving this amount of time. Setting this to high value improves + reception of large PDUs but results in wasting scheduler space when + receiving small PDUs only. On the other hand too low value can + result in not being able to scan whole PDU due to being preempted + by next scheduled item. By default size matching legacy ADV_IND PDU + payload is used: ExtHeader (Flags, AdvA, ADI) + 31 bytes of data. + range: 1..257 + value: 41 + + BLE_LL_SCHED_SCAN_SYNC_PDU_LEN: + description: > + This is expected PDU len for AUX_SYNC_IND and subsequent + AUX_CHAIN_IND. When scheduling scan scheduler will reserve time for + receiving this amount of time. Setting this to high value improves + reception of large PDUs but results in wasting scheduler space when + receiving small PDUs only. On the other hand too low value can + result in not being able to scan whole PDU due to being preempted + by next scheduled item. By default size matching PDU with legacy + data size is used: ExtHeader + 31 bytes of data. + range: 1..257 + value: 32 + +# deprecated settings (to be defunct/removed eventually) + BLE_LL_DIRECT_TEST_MODE: + description: use BLE_LL_DTM instead + value: 0 + deprecated: 1 + BLE_XTAL_SETTLE_TIME: + description: use BLE_LL_RFMGMT_ENABLE_TIME instead + value: 0 + deprecated: 1 + +# defunct settings (to be removed eventually) + BLE_DEVICE: + description: Superseded by BLE_CONTROLLER + value: 1 + defunct: 1 + BLE_LP_CLOCK: + description: Superseded by BLE_CONTROLLER + value: 1 + defunct: 1 + BLE_NUM_COMP_PKT_RATE: + description: Superseded by BLE_LL_NUM_COMP_PKT_ITVL_MS + value: '(2 * OS_TICKS_PER_SEC)' + defunct: 1 + + +syscfg.vals.BLE_LL_CFG_FEAT_LL_EXT_ADV: + BLE_LL_CFG_FEAT_LE_CSA2: 1 + BLE_HW_WHITELIST_ENABLE: 0 + BLE_LL_EXT_ADV_AUX_PTR_CNT: 5 + +# Enable vendor event on assert in standalone build to make failed assertions in +# controller code visible when connected to external host +syscfg.vals.!BLE_HOST: + BLE_LL_VND_EVENT_ON_ASSERT: 1 + +syscfg.restrictions: + - OS_CPUTIME_FREQ == 32768 diff --git a/src/libs/mynewt-nimble/nimble/controller/test/pkg.yml b/src/libs/mynewt-nimble/nimble/controller/test/pkg.yml new file mode 100644 index 00000000..ea728811 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/test/pkg.yml @@ -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. +# +pkg.name: nimble/controller/test +pkg.type: unittest +pkg.description: "NimBLE controller unit tests." +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/test/testutil" + - nimble/controller + +pkg.deps.SELFTEST: + - "@apache-mynewt-core/sys/console/stub" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/stub" + - nimble/drivers/native + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.c b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.c new file mode 100644 index 00000000..5261eb5b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.c @@ -0,0 +1,115 @@ +/* + * 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 +#include +#include "testutil/testutil.h" +#include "controller/ble_ll_test.h" +#include "controller/ble_ll_conn.h" +#include "ble_ll_csa2_test.h" + +TEST_CASE_SELF(ble_ll_csa2_test_1) +{ + struct ble_ll_conn_sm conn; + uint8_t rc; + + /* + * Note: This test only verified mapped channel. Sample data also specifies + * prn_e and unmapped channel values but those would require extra access + * to internal state of algorithm which is not exposed. + */ + + memset(&conn, 0, sizeof(conn)); + + CONN_F_CSA2_SUPP(&conn) = 1; + + /* + * based on sample data from CoreSpec 5.0 Vol 6 Part C 3.1 + * (all channels used) + */ + conn.channel_id = ((0x8e89bed6 & 0xffff0000) >> 16) ^ + (0x8e89bed6 & 0x0000ffff); + + conn.num_used_chans = 37; + conn.chanmap[0] = 0xff; + conn.chanmap[1] = 0xff; + conn.chanmap[2] = 0xff; + conn.chanmap[3] = 0xff; + conn.chanmap[4] = 0x1f; + + conn.event_cntr = 1; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 20); + + conn.event_cntr = 2; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 6); + + conn.event_cntr = 3; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 21); +} + +TEST_CASE_SELF(ble_ll_csa2_test_2) +{ + struct ble_ll_conn_sm conn; + uint8_t rc; + + /* + * Note: This test only verified mapped channel. Sample data also specifies + * prn_e and unmapped channel values but those would require extra access + * to internal state of algorithm which is not exposed. + */ + + memset(&conn, 0, sizeof(conn)); + + CONN_F_CSA2_SUPP(&conn) = 1; + + /* + * based on sample data from CoreSpec 5.0 Vol 6 Part C 3.2 + * (9 channels used) + */ + conn.channel_id = ((0x8e89bed6 & 0xffff0000) >> 16) ^ + (0x8e89bed6 & 0x0000ffff); + + conn.num_used_chans = 9; + conn.chanmap[0] = 0x00; + conn.chanmap[1] = 0x06; + conn.chanmap[2] = 0xe0; + conn.chanmap[3] = 0x00; + conn.chanmap[4] = 0x1e; + + conn.event_cntr = 6; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 23); + + conn.event_cntr = 7; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 9); + + conn.event_cntr = 8; + rc = ble_ll_conn_calc_dci(&conn, 0); + TEST_ASSERT(rc == 34); +} + +TEST_SUITE(ble_ll_csa2_test_suite) +{ + ble_ll_csa2_test_1(); + ble_ll_csa2_test_2(); +} diff --git a/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.h b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.h new file mode 100644 index 00000000..5bcc142b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_csa2_test.h @@ -0,0 +1,27 @@ +/* + * 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_CSA2_TEST_ +#define H_BLE_LL_CSA2_TEST_ + +#include "testutil/testutil.h" + +TEST_SUITE_DECL(ble_ll_csa2_test_suite); + +#endif diff --git a/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_test.c b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_test.c new file mode 100644 index 00000000..ee089afe --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/test/src/ble_ll_test.c @@ -0,0 +1,36 @@ +/* + * 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 "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "controller/ble_ll_test.h" +#include "os/os.h" +#include "testutil/testutil.h" +#include "ble_ll_csa2_test.h" + +#if MYNEWT_VAL(SELFTEST) + +int +main(int argc, char **argv) +{ + ble_ll_csa2_test_suite(); + return tu_any_failed; +} + +#endif diff --git a/src/libs/mynewt-nimble/nimble/controller/test/syscfg.yml b/src/libs/mynewt-nimble/nimble/controller/test/syscfg.yml new file mode 100644 index 00000000..6edad438 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/controller/test/syscfg.yml @@ -0,0 +1,25 @@ +# 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. +# + +syscfg.vals: + BLE_LL_CFG_FEAT_LE_CSA2: 1 + + # Prevent priority conflict with controller task. + MCU_TIMER_POLLER_PRIO: 1 + MCU_UART_POLLER_PRIO: 2 + NATIVE_SOCKETS_PRIO: 3 diff --git a/src/libs/mynewt-nimble/nimble/drivers/native/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/native/include/ble/xcvr.h new file mode 100644 index 00000000..04741dd3 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/native/include/ble/xcvr.h @@ -0,0 +1,46 @@ +/* + * 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_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Transceiver specific defintions */ +#define XCVR_RX_START_DELAY_USECS (140) +#define XCVR_TX_START_DELAY_USECS (140) +#define XCVR_PROC_DELAY_USECS (100) +#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 (0) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/native/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/native/pkg.yml new file mode 100644 index 00000000..d0af185b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/native/pkg.yml @@ -0,0 +1,30 @@ +# +# 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. +# + +pkg.name: nimble/drivers/native +pkg.description: BLE driver for simulations. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.apis: ble_driver +pkg.deps: + - nimble/controller diff --git a/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c new file mode 100644 index 00000000..5eb1eb95 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_hw.c @@ -0,0 +1,239 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "controller/ble_hw.h" + +/* Total number of white list elements supported by nrf52 */ +#define BLE_HW_WHITE_LIST_SIZE (0) + +/* We use this to keep track of which entries are set to valid addresses */ +static uint8_t g_ble_hw_whitelist_mask; + +/* Returns public device address or -1 if not present */ +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + return -1; +} + +/* Returns random static address or -1 if not present */ +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + return -1; +} + +/** + * Clear the whitelist + * + * @return int + */ +void +ble_hw_whitelist_clear(void) +{ + 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) +{ + 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) +{ + return; +} + +/** + * 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) +{ + return; +} + +/** + * Disables the whitelisted devices + */ +void +ble_hw_whitelist_disable(void) +{ + return; +} + +/** + * Boolean function which returns true ('1') if there is a match on the + * whitelist. + * + * @return int + */ +int +ble_hw_whitelist_match(void) +{ + return 0; +} + +/* Encrypt data */ +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + return -1; +} + +/** + * Initialize the random number generator + * + * @param cb + * @param bias + * + * @return int + */ +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + return -1; +} + +/** + * Start the random number generator + * + * @return int + */ +int +ble_hw_rng_start(void) +{ + return -1; +} + +/** + * Stop the random generator + * + * @return int + */ +int +ble_hw_rng_stop(void) +{ + return -1; +} + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t +ble_hw_rng_read(void) +{ + return 0; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +/** + * Clear the resolving list + * + * @return int + */ +void +ble_hw_resolv_list_clear(void) +{ +} + +/** + * 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) +{ + return BLE_ERR_MEM_CAPACITY; +} + +/** + * Remove a device from the hw resolving list + * + * @param index Index of IRK to remove + */ +void +ble_hw_resolv_list_rmv(int 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 0; +} + +/** + * 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) +{ + return -1; +} +#endif diff --git a/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_phy.c new file mode 100644 index 00000000..f9ab0fcb --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/native/src/ble_phy.c @@ -0,0 +1,652 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" + +/* BLE PHY data structure */ +struct ble_phy_obj +{ + uint8_t phy_stats_initialized; + int8_t phy_txpwr_dbm; + int16_t rx_pwr_compensation; + 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; + uint32_t phy_aar_scratch; + uint32_t phy_access_address; + struct ble_mbuf_hdr rxhdr; + void *txend_arg; + uint8_t *rxdptr; + ble_phy_tx_end_func txend_cb; +}; +struct ble_phy_obj g_ble_phy_data; + +/* Statistics */ +struct ble_phy_statistics +{ + uint32_t tx_good; + uint32_t tx_fail; + uint32_t tx_late; + uint32_t tx_bytes; + uint32_t rx_starts; + uint32_t rx_aborts; + uint32_t rx_valid; + uint32_t rx_crc_err; + uint32_t phy_isrs; + uint32_t radio_state_errs; + uint32_t no_bufs; +}; + +struct ble_phy_statistics g_ble_phy_stats; + +static uint8_t g_ble_phy_tx_buf[BLE_PHY_MAX_PDU_LEN]; + +/* XCVR object to emulate transceiver */ +struct xcvr_data +{ + uint32_t irq_status; +}; +static struct xcvr_data g_xcvr_data; + +#define BLE_XCVR_IRQ_F_RX_START (0x00000001) +#define BLE_XCVR_IRQ_F_RX_END (0x00000002) +#define BLE_XCVR_IRQ_F_TX_START (0x00000004) +#define BLE_XCVR_IRQ_F_TX_END (0x00000008) +#define BLE_XCVR_IRQ_F_BYTE_CNTR (0x00000010) + +/* "Rail" power level if outside supported range */ +#define BLE_XCVR_TX_PWR_MAX_DBM (30) +#define BLE_XCVR_TX_PWR_MIN_DBM (-20) + +/* 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(no_bufs) + 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, no_bufs) + 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) + +/* XXX: TODO: + + * 1) Test the following to make sure it works: suppose an event is already + * set to 1 and the interrupt is not enabled. What happens if you enable the + * interrupt with the event bit already set to 1 + * 2) how to deal with interrupts? + */ +static uint32_t +ble_xcvr_get_irq_status(void) +{ + return g_xcvr_data.irq_status; +} + +static void +ble_xcvr_clear_irq(uint32_t mask) +{ + g_xcvr_data.irq_status &= ~mask; +} + +/** + * 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) +{ + uint16_t rem_bytes; + uint16_t mb_bytes; + uint16_t copylen; + uint32_t *dst; + uint32_t *src; + struct os_mbuf *m; + struct ble_mbuf_hdr *ble_hdr; + struct os_mbuf_pkthdr *pkthdr; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + pkthdr = OS_MBUF_PKTHDR(rxpdu); + rem_bytes = pkthdr->omp_len; + + /* Fill in the mbuf pkthdr first. */ + dst = (uint32_t *)(rxpdu->om_data); + src = (uint32_t *)dptr; + + mb_bytes = (rxpdu->om_omp->omp_databuf_len - rxpdu->om_pkthdr_len - 4); + copylen = min(mb_bytes, rem_bytes); + copylen &= 0xFFFC; + rem_bytes -= copylen; + mb_bytes -= copylen; + rxpdu->om_len = copylen; + while (copylen > 0) { + *dst = *src; + ++dst; + ++src; + copylen -= 4; + } + + /* Copy remaining bytes */ + m = rxpdu; + while (rem_bytes > 0) { + /* If there are enough bytes in the mbuf, copy them and leave */ + if (rem_bytes <= mb_bytes) { + memcpy(m->om_data + m->om_len, src, rem_bytes); + m->om_len += rem_bytes; + break; + } + + m = SLIST_NEXT(m, om_next); + assert(m != NULL); + + mb_bytes = m->om_omp->omp_databuf_len; + copylen = min(mb_bytes, rem_bytes); + copylen &= 0xFFFC; + rem_bytes -= copylen; + mb_bytes -= copylen; + m->om_len = copylen; + dst = (uint32_t *)m->om_data; + while (copylen > 0) { + *dst = *src; + ++dst; + ++src; + copylen -= 4; + } + } + + /* Copy ble header */ + ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); + memcpy(ble_hdr, &g_ble_phy_data.rxhdr, sizeof(struct ble_mbuf_hdr)); +} + +void +ble_phy_isr(void) +{ + int rc; + uint8_t transition; + uint32_t irq_en; + struct ble_mbuf_hdr *ble_hdr; + + /* Check for disabled event. This only happens for transmits now */ + irq_en = ble_xcvr_get_irq_status(); + if (irq_en & BLE_XCVR_IRQ_F_TX_END) { + + /* Better be in TX state! */ + assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); + ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_TX_END); + + transition = g_ble_phy_data.phy_transition; + if (transition == BLE_PHY_TRANSITION_TX_RX) { + /* Disable the phy */ + /* XXX: count no bufs? */ + ble_phy_disable(); + } else { + /* Better not be going from rx to tx! */ + assert(transition == BLE_PHY_TRANSITION_NONE); + } + } + + /* We get this if we have started to receive a frame */ + if (irq_en & BLE_XCVR_IRQ_F_RX_START) { + + ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_RX_START); + + /* 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) { + /* XXX: set rx end enable isr */ + } else { + /* Disable PHY */ + ble_phy_disable(); + irq_en = 0; + ++g_ble_phy_stats.rx_aborts; + } + + /* Count rx starts */ + ++g_ble_phy_stats.rx_starts; + } + + /* Receive packet end (we dont enable this for transmit) */ + if (irq_en & BLE_XCVR_IRQ_F_RX_END) { + + ble_xcvr_clear_irq(BLE_XCVR_IRQ_F_RX_END); + + /* Construct BLE header before handing up */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = 0; + /* XXX: dummy rssi */ + ble_hdr->rxinfo.rssi = -77 + g_ble_phy_data.rx_pwr_compensation; + ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; + ble_hdr->rxinfo.phy = BLE_PHY_1M; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.aux_data = NULL; +#endif + + /* Count PHY valid packets */ + ++g_ble_phy_stats.rx_valid; + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; + + /* Call Link Layer receive payload function */ + rc = ble_ll_rx_end(g_ble_phy_data.rxdptr, ble_hdr); + if (rc < 0) { + /* Disable the PHY. */ + ble_phy_disable(); + } + } + + /* Count # of interrupts */ + ++g_ble_phy_stats.phy_isrs; +} + +/** + * ble phy init + * + * Initialize the PHY. This is expected to be called once. + * + * @return int 0: success; PHY error code otherwise + */ +int +ble_phy_init(void) +{ + /* Set phy channel to an invalid channel so first set channel works */ + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; + g_ble_phy_data.phy_chan = BLE_PHY_NUM_CHANS; + + g_ble_phy_data.rx_pwr_compensation = 0; + + /* XXX: emulate ISR? */ + + return 0; +} + +int +ble_phy_rx(void) +{ + /* Check radio state */ + if (ble_phy_state_get() != BLE_PHY_STATE_IDLE) { + ble_phy_disable(); + ++g_ble_phy_stats.radio_state_errs; + return BLE_PHY_ERR_RADIO_STATE; + } + + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + + return 0; +} + +void +ble_phy_restart_rx(void) +{ +} + +#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) +{ +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ +} + +void +ble_phy_encrypt_disable(void) +{ +} +#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 + * @param rem_usecs + * + * @return int + */ +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + return 0; +} + +/** + * 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 + * @param rem_usecs + * + * @return int + */ +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + return 0; +} + + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + uint8_t hdr_byte; + int rc; + + if (ble_phy_state_get() != BLE_PHY_STATE_IDLE) { + ble_phy_disable(); + ++g_ble_phy_stats.radio_state_errs; + return BLE_PHY_ERR_RADIO_STATE; + } + + /* Select tx address */ + if (g_ble_phy_data.phy_chan < BLE_PHY_NUM_DATA_CHANS) { + /* XXX: fix this */ + assert(0); + } else { + } + + /* Set the PHY transition */ + g_ble_phy_data.phy_transition = end_trans; + + /* Set phy state to transmitting and count packet statistics */ + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + ++g_ble_phy_stats.tx_good; + g_ble_phy_stats.tx_bytes += pducb(g_ble_phy_tx_buf, pducb_arg, &hdr_byte) + + BLE_LL_PDU_HDR_LEN; + rc = BLE_ERR_SUCCESS; + + 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 > BLE_XCVR_TX_PWR_MAX_DBM) { + dbm = BLE_XCVR_TX_PWR_MAX_DBM; + } else { + if (dbm < BLE_XCVR_TX_PWR_MIN_DBM) { + dbm = BLE_XCVR_TX_PWR_MIN_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 > BLE_XCVR_TX_PWR_MAX_DBM) { + dbm = BLE_XCVR_TX_PWR_MAX_DBM; + } else { + if (dbm < BLE_XCVR_TX_PWR_MIN_DBM) { + dbm = BLE_XCVR_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 NRF52 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; + } + + g_ble_phy_data.phy_access_address = access_addr; + + g_ble_phy_data.phy_chan = chan; + + return 0; +} + +/** + * Disable the PHY. This will do the following: + * -> Turn off all phy interrupts. + * -> Disable internal shortcuts. + * -> Disable the radio. + * -> Sets phy state to idle. + */ +void +ble_phy_disable(void) +{ + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +/* 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; +} + +/** + * 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) +{ + g_ble_phy_data.phy_privacy = 1; +} + +void +ble_phy_resolv_list_disable(void) +{ + g_ble_phy_data.phy_privacy = 0; +} + +/** + * Return the transceiver state + * + * @return int transceiver state. + */ +uint8_t +ble_phy_xcvr_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +#endif + +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ +} + +void +ble_phy_rfclk_enable(void) +{ +} + +void +ble_phy_rfclk_disable(void) +{ +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf51/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/nrf51/include/ble/xcvr.h new file mode 100644 index 00000000..848c8cc8 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf51/include/ble/xcvr.h @@ -0,0 +1,48 @@ +/* + * 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_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_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf51/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/nrf51/pkg.yml new file mode 100644 index 00000000..816a5635 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf51/pkg.yml @@ -0,0 +1,31 @@ +# +# 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. +# + +pkg.name: nimble/drivers/nrf51 +pkg.description: BLE driver for nRF51 systems. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.apis: ble_driver +pkg.deps: + - nimble + - nimble/controller diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_hw.c new file mode 100644 index 00000000..3c7296b8 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_hw.c @@ -0,0 +1,488 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nrfx.h" +#include "controller/ble_hw.h" +#if MYNEWT +#include "mcu/cmsis_nvic.h" +#else +#include "core_cm0.h" +#include +#endif +#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 diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_phy.c new file mode 100644 index 00000000..b7e63297 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf51/src/ble_phy.c @@ -0,0 +1,1524 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "controller/ble_phy.h" +#include "controller/ble_phy_trace.h" +#include "controller/ble_ll.h" +#include "nrfx.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 init + * + * 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 +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/nrf52/include/ble/xcvr.h new file mode 100644 index 00000000..757bb80f --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/include/ble/xcvr.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_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_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/nrf52/pkg.yml new file mode 100644 index 00000000..a1ff457e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/pkg.yml @@ -0,0 +1,31 @@ +# +# 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. +# + +pkg.name: nimble/drivers/nrf52 +pkg.description: BLE driver for nRF52 systems. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.apis: ble_driver +pkg.deps: + - nimble + - nimble/controller diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c new file mode 100644 index 00000000..82e6d17e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c @@ -0,0 +1,488 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nrfx.h" +#include "controller/ble_hw.h" +#if MYNEWT +#include "mcu/cmsis_nvic.h" +#else +#include "core_cm4.h" +#include +#endif +#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 diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c new file mode 100644 index 00000000..2f6ce08e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c @@ -0,0 +1,2116 @@ +/* + * 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 +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "ble/xcvr.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/nimble_npl.h" +#include "controller/ble_phy.h" +#include "controller/ble_phy_trace.h" +#include "controller/ble_ll.h" +#include "nrfx.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 init + * + * 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 +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy_trace.c b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy_trace.c new file mode 100644 index 00000000..93b2eb32 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy_trace.c @@ -0,0 +1,44 @@ +/* + * 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 +#include "syscfg/syscfg.h" +#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 diff --git a/src/libs/mynewt-nimble/nimble/drivers/nrf52/syscfg.yml b/src/libs/mynewt-nimble/nimble/drivers/nrf52/syscfg.yml new file mode 100644 index 00000000..ce512372 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/nrf52/syscfg.yml @@ -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. +# + +syscfg.defs: + BLE_PHY_SYSVIEW: + description: > + Enable SystemView tracing module for radio driver. + value: 0 + + BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN: + description: > + This defines additional margin for T_IFS tolerance while in + RX on coded phy to allow maintaining connections with some + controllers that exceed proper T_IFS (150 usecs) by more + than allowed 2 usecs. + This value shall be only used for debugging purposes. It is + strongly recommended to keep this settings at default value + to ensure compliance with specification. + value: 0 + BLE_PHY_DBG_TIME_TXRXEN_READY_PIN: + description: > + When set to proper GPIO pin number, this pin will be set + to high state when radio is enabled using PPI channels + 20 or 21 and back to low state on radio EVENTS_READY. + This can be used to measure radio ram-up time. + value: -1 + + BLE_PHY_DBG_TIME_ADDRESS_END_PIN: + description: > + When set to proper GPIO pin number, this pin will be set + to high state on radio EVENTS_ADDRESS and back to low state + on radio EVENTS_END. + This can be used to measure radio pipeline delays. + value: -1 + + BLE_PHY_DBG_TIME_WFR_PIN: + description: > + When set to proper GPIO pin number, this pin will be set + to high state on radio EVENTS_RXREADY and back to low + state when wfr timer expires. + This can be used to check if wfr is calculated properly. + value: -1 + + BLE_PHY_NRF52840_ERRATA_164: + description: > + Enable workaround for anomaly 164 found in nRF52840. + "[164] RADIO: Low selectivity in long range mode" + This shall be only enabled for: + - nRF52840 Engineering A + value: 0 + + BLE_PHY_NRF52840_ERRATA_191: + description: > + Enable workaround for anomaly 191 found in nRF52840. + "[191] RADIO: High packet error rate in BLE Long Range mode" + This shall be only enabled for: + - nRF52840 Engineering B + - nRF52840 Engineering C + - nRF52840 Rev 1 (final silicon) + value: 1 diff --git a/src/libs/mynewt-nimble/nimble/host/include/host/ble_att.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_att.h new file mode 100644 index 00000000..391a992a --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/include/host/ble_eddystone.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_eddystone.h new file mode 100644 index 00000000..76b7e2b0 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/include/host/ble_gap.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_gap.h new file mode 100644 index 00000000..b58f350f --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/include/host/ble_gap.h @@ -0,0 +1,2052 @@ +/* + * 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 "host/ble_hs.h" +#include "host/ble_hs_adv.h" +#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 0x0010 +#define BLE_GAP_INITIAL_CONN_MAX_CE_LEN 0x0300 + +#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); + +#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); + +/** + * 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); + +/** + * 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/src/libs/mynewt-nimble/nimble/host/include/host/ble_gatt.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_gatt.h new file mode 100644 index 00000000..b06344fc --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/include/host/ble_gatt.h @@ -0,0 +1,896 @@ +/* + * 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 "host/ble_att.h" +#include "host/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); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs.h new file mode 100644 index 00000000..6c2acfd4 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs.h @@ -0,0 +1,386 @@ +/* + * 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/hci_common.h" +#include "host/ble_att.h" +#include "host/ble_eddystone.h" +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "host/ble_hs_adv.h" +#include "host/ble_hs_id.h" +#include "host/ble_hs_hci.h" +#include "host/ble_hs_log.h" +#include "host/ble_hs_mbuf.h" +#include "host/ble_hs_stop.h" +#include "host/ble_ibeacon.h" +#include "host/ble_l2cap.h" +#include "host/ble_sm.h" +#include "host/ble_store.h" +#include "host/ble_uuid.h" +#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); + +/** + * @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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_adv.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_adv.h new file mode 100644 index 00000000..e3b6ea70 --- /dev/null +++ b/src/libs/mynewt-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 "host/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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_hci.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_hci.h new file mode 100644 index 00000000..e10b8e62 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_id.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_id.h new file mode 100644 index 00000000..c96bd20f --- /dev/null +++ b/src/libs/mynewt-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/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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_log.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_log.h new file mode 100644 index 00000000..8d0a4596 --- /dev/null +++ b/src/libs/mynewt-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 "modlog/modlog.h" + +/* Only include the logcfg header if this version of newt can generate it. */ +#if MYNEWT_VAL(NEWT_FEATURE_LOGCFG) +#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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_mbuf.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_mbuf.h new file mode 100644 index 00000000..a3c2c029 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_stop.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_hs_stop.h new file mode 100644 index 00000000..d16c9c27 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/include/host/ble_ibeacon.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_ibeacon.h new file mode 100644 index 00000000..fff7c57a --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/include/host/ble_l2cap.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_l2cap.h new file mode 100644 index 00000000..aef9682c --- /dev/null +++ b/src/libs/mynewt-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_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/src/libs/mynewt-nimble/nimble/host/include/host/ble_monitor.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_monitor.h new file mode 100644 index 00000000..61722f7d --- /dev/null +++ b/src/libs/mynewt-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 + +#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/src/libs/mynewt-nimble/nimble/host/include/host/ble_sm.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_sm.h new file mode 100644 index 00000000..ceebb856 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/include/host/ble_store.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_store.h new file mode 100644 index 00000000..30a5666c --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/include/host/ble_store.h @@ -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 H_BLE_STORE_ +#define H_BLE_STORE_ + +#include +#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 + +/** 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/src/libs/mynewt-nimble/nimble/host/include/host/ble_uuid.h b/src/libs/mynewt-nimble/nimble/host/include/host/ble_uuid.h new file mode 100644 index 00000000..d3576c59 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h new file mode 100644 index 00000000..1f99f412 --- /dev/null +++ b/src/libs/mynewt-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 init 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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h new file mode 100644 index 00000000..7dc237be --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h new file mode 100644 index 00000000..14d8a295 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h new file mode 100644 index 00000000..e37fcfbc --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h @@ -0,0 +1,502 @@ +/* + * 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 "syscfg/syscfg.h" +#include "logcfg/logcfg.h" +#include "modlog/modlog.h" +#include "nimble/nimble_npl.h" + +#include "os/os_mbuf.h" +#include "os/queue.h" + +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "../src/ble_sm_priv.h" +#include "../src/ble_hs_hci_priv.h" + +#include "tinycrypt/aes.h" +#include "tinycrypt/constants.h" +#include "tinycrypt/utils.h" +#include "tinycrypt/cmac_mode.h" +#include "tinycrypt/ecc_dh.h" + +#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 init. + * 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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h new file mode 100644 index 00000000..8ab8d6d5 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h new file mode 100644 index 00000000..83982376 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h new file mode 100644 index 00000000..4a5bedba --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h new file mode 100644 index 00000000..9ba63ef0 --- /dev/null +++ b/src/libs/mynewt-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 "syscfg/syscfg.h" +#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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h new file mode 100644 index 00000000..f2e77a47 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h new file mode 100644 index 00000000..e498ad34 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h new file mode 100644 index 00000000..1667a8a0 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h new file mode 100644 index 00000000..63bbfa3e --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h new file mode 100644 index 00000000..8a858f83 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h new file mode 100644 index 00000000..4c2b2a61 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml b/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml new file mode 100644 index 00000000..44cc0c73 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml @@ -0,0 +1,49 @@ +# +# 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. +# + +pkg.name: nimble/host/mesh +pkg.description: Bluetooth Mesh +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - mesh + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/util/mem" + - "@apache-mynewt-core/crypto/tinycrypt" + - nimble + - nimble/host + +pkg.deps.BLE_MESH_SHELL: + - "@apache-mynewt-core/sys/shell" + +pkg.deps.BLE_MESH_SETTINGS: + - "@apache-mynewt-core/encoding/base64" + - "@apache-mynewt-core/sys/config" + +pkg.req_apis: + - log + - stats + +pkg.init: + bt_mesh_register_gatt: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE)' + ble_mesh_shell_init: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE_SHELL)' diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c new file mode 100644 index 00000000..ff8e9999 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c @@ -0,0 +1,856 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_ACCESS_LOG + +#include +#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 "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 diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/access.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.h new file mode 100644 index 00000000..48514983 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c new file mode 100644 index 00000000..4bd51cc1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c @@ -0,0 +1,439 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_ADV_LOG + +#include "mesh/mesh.h" +#include "host/ble_hs_adv.h" +#include "host/ble_gap.h" +#include "nimble/hci_common.h" +#include "mesh/porting.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; +#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 init 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); +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h new file mode 100644 index 00000000..4d0f7d8b --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h new file mode 100644 index 00000000..2c731794 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c new file mode 100644 index 00000000..cd540aa8 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c @@ -0,0 +1,441 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_BEACON_LOG + +#include +#include +#include "os/os_mbuf.h" +#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); + } +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h new file mode 100644 index 00000000..ac4bfed8 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c new file mode 100644 index 00000000..2c2f6c3f --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c @@ -0,0 +1,1498 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG +#if MYNEWT_VAL(BLE_MESH_CFG_CLI) + +#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 diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c new file mode 100644 index 00000000..57aac90a --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c @@ -0,0 +1,3619 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c new file mode 100644 index 00000000..b6a0ba21 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c @@ -0,0 +1,911 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "crypto.h" + +#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4) +#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) + +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; +} + +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; +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h new file mode 100644 index 00000000..745cf324 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h new file mode 100644 index 00000000..ee615ae9 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c new file mode 100644 index 00000000..9056a865 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c @@ -0,0 +1,1651 @@ + /* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG + +#if MYNEWT_VAL(BLE_MESH_FRIEND) + +#include +#include +#include + +#include "mesh/mesh.h" +#include "mesh/slist.h" +#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) */ diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h new file mode 100644 index 00000000..10ffa819 --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c new file mode 100644 index 00000000..896f3d1a --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c @@ -0,0 +1,870 @@ +/* + * 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 "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include "mesh/glue.h" +#include "adv.h" +#ifndef MYNEWT +#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); +} + +int +bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) +{ + struct tc_aes_key_sched_struct s; + + 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; +} + +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) */ + diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c new file mode 100644 index 00000000..193279c2 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c @@ -0,0 +1,556 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include +#include +#include + +#include "mesh/mesh.h" +#include "mesh_priv.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "foundation.h" +#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, +}; diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c new file mode 100644 index 00000000..16de83a9 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c @@ -0,0 +1,453 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#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); + } + } +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c new file mode 100644 index 00000000..b6d83818 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c @@ -0,0 +1,58 @@ + +#include "syscfg/syscfg.h" + +#include "mesh/mesh.h" +#include "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); +} + diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h new file mode 100644 index 00000000..95fcdb78 --- /dev/null +++ b/src/libs/mynewt-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 "syscfg/syscfg.h" +#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/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c new file mode 100644 index 00000000..ec012a5f --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c @@ -0,0 +1,1056 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_LOW_POWER_LOG + +#if MYNEWT_VAL(BLE_MESH_LOW_POWER) + +#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) */ diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h new file mode 100644 index 00000000..0ff6c9cf --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c new file mode 100644 index 00000000..52fbdbf6 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c @@ -0,0 +1,361 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include +#include + +#include "os/os_mbuf.h" +#include "mesh/mesh.h" +#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; +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h new file mode 100644 index 00000000..f09bb230 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c new file mode 100644 index 00000000..b00cfa52 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include "mesh/mesh.h" +#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; +} + diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c new file mode 100644 index 00000000..5f5a8df4 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG + +#include "mesh/mesh.h" +#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, +}; diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c new file mode 100644 index 00000000..240314d4 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c @@ -0,0 +1,1433 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_NET_LOG + +#include +#include +#include + +#include "os/os_mbuf.h" +#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. + * + * Intentional fall-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); +} diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/net.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.h new file mode 100644 index 00000000..976da005 --- /dev/null +++ b/src/libs/mynewt-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 "mesh/mesh.h" +#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/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c new file mode 100644 index 00000000..127ef21e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_PROV_LOG + +#if MYNEWT_VAL(BLE_MESH_PROVISIONER) + +#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 diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h new file mode 100644 index 00000000..f86193d9 --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c new file mode 100644 index 00000000..fe92c0e3 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c @@ -0,0 +1,2008 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_PROV_LOG + +#if MYNEWT_VAL(BLE_MESH_PROV) + +#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) */ diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h new file mode 100644 index 00000000..96e5a447 --- /dev/null +++ b/src/libs/mynewt-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 "os/os_mbuf.h" +#include "mesh/mesh.h" +#include "../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/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c new file mode 100644 index 00000000..134a36dd --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c @@ -0,0 +1,1499 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG + +#if MYNEWT_VAL(BLE_MESH_PROXY) + +#include "mesh/mesh.h" +#include "host/ble_att.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../../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 init... + */ + 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) */ diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h new file mode 100644 index 00000000..64338a0a --- /dev/null +++ b/src/libs/mynewt-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 "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/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c new file mode 100644 index 00000000..88d9b302 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c @@ -0,0 +1,2083 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_SETTINGS_LOG + +#if MYNEWT_VAL(BLE_MESH_SETTINGS) + +#include "mesh/mesh.h" +#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) */ diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h new file mode 100644 index 00000000..c630814e --- /dev/null +++ b/src/libs/mynewt-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/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c new file mode 100644 index 00000000..91fbd978 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c @@ -0,0 +1,2819 @@ +/** @file + * @brief Bluetooth Mesh shell + * + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#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, "