From 67844daa247b1f4a4b202f0a4e2aaea802186703 Mon Sep 17 00:00:00 2001 From: Ahmed Shokry Date: Wed, 28 Nov 2018 14:37:55 +0530 Subject: [PATCH] nimble/host: Add initial periodic adverting host support This patch adds the implementation for periodic advertising nd discovery, which is available when BLE_PERIODIC_ADV is enabled --- nimble/controller/src/ble_ll_adv.c | 26 +- nimble/controller/src/ble_ll_hci.c | 24 +- nimble/host/include/host/ble_gap.h | 74 ++ nimble/host/src/ble_gap.c | 687 +++++++++++++++++- nimble/host/src/ble_gap_priv.h | 5 + nimble/host/src/ble_hs.c | 3 + nimble/host/src/ble_hs_dbg.c | 15 +- nimble/host/src/ble_hs_hci_cmd.c | 158 ++++ nimble/host/src/ble_hs_hci_evt.c | 87 ++- nimble/host/src/ble_hs_hci_priv.h | 42 ++ nimble/host/src/ble_hs_periodic_disc.c | 230 ++++++ nimble/host/src/ble_hs_periodic_disc_priv.h | 61 ++ nimble/host/src/ble_hs_priv.h | 1 + nimble/host/src/ble_hs_startup.c | 5 +- nimble/host/src/ble_hs_stop.c | 48 +- nimble/include/nimble/hci_common.h | 86 ++- nimble/syscfg.yml | 9 + .../examples/linux/include/syscfg/syscfg.h | 4 + .../linux_blemesh/include/syscfg/syscfg.h | 4 + porting/nimble/Makefile.defs | 1 + porting/nimble/include/syscfg/syscfg.h | 9 + porting/npl/riot/include/syscfg/syscfg.h | 4 + 22 files changed, 1486 insertions(+), 97 deletions(-) create mode 100644 nimble/host/src/ble_hs_periodic_disc.c create mode 100644 nimble/host/src/ble_hs_periodic_disc_priv.h diff --git a/nimble/controller/src/ble_ll_adv.c b/nimble/controller/src/ble_ll_adv.c index 3efe5adaa..7e32814da 100644 --- a/nimble/controller/src/ble_ll_adv.c +++ b/nimble/controller/src/ble_ll_adv.c @@ -2106,7 +2106,7 @@ ble_ll_adv_set_scan_rsp_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, } switch (operation) { - case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE: + 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; @@ -2115,10 +2115,10 @@ ble_ll_adv_set_scan_rsp_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, break; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) - case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_LAST: + case BLE_HCI_LE_SET_DATA_OPER_LAST: /* TODO mark scan rsp as complete? */ /* fall through */ - case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_INT: + case BLE_HCI_LE_SET_DATA_OPER_INT: if (!advsm->scan_rsp_data) { return BLE_ERR_INV_HCI_CMD_PARMS; } @@ -2135,7 +2135,7 @@ ble_ll_adv_set_scan_rsp_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, return BLE_ERR_INV_HCI_CMD_PARMS; } break; - case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_FIRST: + 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; } @@ -2154,8 +2154,8 @@ ble_ll_adv_set_scan_rsp_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, return BLE_ERR_INV_HCI_CMD_PARMS; } - new_data = (operation == BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE) || - (operation == BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_FIRST); + 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) { @@ -2234,7 +2234,7 @@ ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, } switch (operation) { - case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE: + 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; @@ -2245,7 +2245,7 @@ ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, break; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) - case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_UNCHANGED: + 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; } @@ -2257,10 +2257,10 @@ ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, /* update DID only */ ble_ll_adv_update_did(advsm); return BLE_ERR_SUCCESS; - case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_LAST: + 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_EXT_ADV_DATA_OPER_INT: + case BLE_HCI_LE_SET_DATA_OPER_INT: if (!advsm->adv_data) { return BLE_ERR_INV_HCI_CMD_PARMS; } @@ -2277,7 +2277,7 @@ ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, return BLE_ERR_CMD_DISALLOWED; } break; - case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST: + 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; } @@ -2297,8 +2297,8 @@ ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance, return BLE_ERR_INV_HCI_CMD_PARMS; } - new_data = (operation == BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE) || - (operation == BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST); + 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) { diff --git a/nimble/controller/src/ble_ll_hci.c b/nimble/controller/src/ble_ll_hci.c index 8bad2d8a7..c00982dd3 100644 --- a/nimble/controller/src/ble_ll_hci.c +++ b/nimble/controller/src/ble_ll_hci.c @@ -696,16 +696,16 @@ ble_ll_is_valid_adv_mode(uint8_t ocf) 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_PER_ADV_PARAMS: - case BLE_HCI_OCF_LE_SET_PER_ADV_DATA: - case BLE_HCI_OCF_LE_SET_PER_ADV_ENABLE: - case BLE_HCI_OCF_LE_PER_ADV_CREATE_SYNC: - case BLE_HCI_OCF_LE_PER_ADV_CREATE_SYNC_CANCEL: - case BLE_HCI_OCF_LE_PER_ADV_TERM_SYNC: - case BLE_HCI_OCF_LE_ADD_DEV_TO_PER_ADV_LIST: - case BLE_HCI_OCF_LE_REM_DEV_FROM_PER_ADV_LIST: - case BLE_HCI_OCF_LE_CLEAR_PER_ADV_LIST: - case BLE_HCI_OCF_LE_RD_PER_ADV_LIST_SIZE: + 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 (hci_adv_mode == ADV_MODE_LEGACY) { return false; } @@ -859,13 +859,13 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen, case BLE_HCI_OCF_LE_SET_ADV_DATA: if (len == BLE_HCI_SET_ADV_DATA_LEN) { rc = ble_ll_adv_set_adv_data(cmdbuf, len, 0, - BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE); + BLE_HCI_LE_SET_DATA_OPER_COMPLETE); } break; case BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA: if (len == BLE_HCI_SET_SCAN_RSP_DATA_LEN) { rc = ble_ll_adv_set_scan_rsp_data(cmdbuf, len, 0, - BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE); + BLE_HCI_LE_SET_DATA_OPER_COMPLETE); } break; case BLE_HCI_OCF_LE_SET_ADV_ENABLE: diff --git a/nimble/host/include/host/ble_gap.h b/nimble/host/include/host/ble_gap.h index 616ad91bc..3aabf2069 100644 --- a/nimble/host/include/host/ble_gap.h +++ b/nimble/host/include/host/ble_gap.h @@ -124,6 +124,9 @@ struct hci_conn_update; #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_ADV_SYNC_ESTAB 20 +#define BLE_GAP_EVENT_PERIODIC_DISC 21 +#define BLE_GAP_EVENT_PERIODIC_ADV_SYNC_LOST 22 /*** Reason codes for the subscribe GAP event. */ @@ -708,6 +711,46 @@ struct ble_gap_event { uint8_t tx_phy; uint8_t rx_phy; } phy_updated; +#if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_PERIODIC_ADV) + /** + * Represents an periodic advertising sync established during discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_ADV_SYNC_ESTAB + */ + struct { + uint8_t status; + uint16_t sync_handle; + uint8_t sid; + uint8_t adv_addr_type; + uint8_t adv_addr[6]; + uint8_t adv_phy; + uint16_t per_adv_ival; + uint8_t adv_clk_accuracy; + } periodic_adv_sync_estab; + + /** + * Represents a Periodic advertising report received during discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_DISC + */ + struct { + uint16_t sync_handle; + uint8_t tx_power; + int8_t rssi; + uint8_t data_status; + uint8_t data_length; + uint8_t *data; + } periodic_disc; + + /** + * Represents an periodic advertising sync Lost during discovery + * procedure. Valid for the following event types: + * o BLE_GAP_EVENT_PERIODIC_ADV_SYNC_LOST + */ + struct { + uint16_t sync_handle; + } periodic_adv_sync_lost; +#endif }; }; @@ -903,6 +946,15 @@ struct ble_gap_ext_adv_params { uint8_t sid; }; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +struct ble_gap_periodic_adv_params { + unsigned int include_tx_power:1; + + uint16_t itvl_min; + uint16_t itvl_max; +}; +#endif + int ble_gap_ext_adv_configure(uint8_t instance, const struct ble_gap_ext_adv_params *params, int8_t *selected_tx_power, @@ -913,6 +965,28 @@ int ble_gap_ext_adv_stop(uint8_t instance); int ble_gap_ext_adv_set_data(uint8_t instance, struct os_mbuf *data); int ble_gap_ext_adv_rsp_set_data(uint8_t instance, struct os_mbuf *data); int ble_gap_ext_adv_remove(uint8_t instance); + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +/* Periodic Advertising */ +int ble_gap_periodic_adv_configure(uint8_t instance, const struct ble_gap_periodic_adv_params *params); +int ble_gap_periodic_adv_start(uint8_t instance); +int ble_gap_periodic_adv_stop(uint8_t instance); +int ble_gap_periodic_adv_set_data(uint8_t instance, struct os_mbuf *data); +int ble_gap_periodic_adv_create_sync (uint8_t filter_policy, + uint8_t adv_sid, uint8_t adv_add_type, + const uint8_t *adv_addr, uint16_t skip, + uint16_t sync_timeout); +int ble_gap_periodic_adv_create_sync_cancel(void); +int ble_gap_periodic_adv_terminate_sync(uint16_t sync_handle); +int ble_gap_add_dev_to_periodic_adv_list(uint8_t adv_add_type, const uint8_t *adv_add, + uint8_t adv_sid); +int ble_gap_rem_dev_from_periodic_adv_list(uint8_t adv_addr_type, + const uint8_t *adv_addr, + uint8_t adv_sid); +int ble_gap_clear_periodic_adv_list(void); +int ble_gap_read_periodic_adv_list_size(uint8_t *per_adv_list_size); +#endif + #endif /** diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index 216ca283f..64b65c667 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -74,10 +74,11 @@ */ /** GAP procedure op codes. */ -#define BLE_GAP_OP_NULL 0 -#define BLE_GAP_OP_M_DISC 1 -#define BLE_GAP_OP_M_CONN 2 -#define BLE_GAP_OP_S_ADV 1 +#define BLE_GAP_OP_NULL 0 +#define BLE_GAP_OP_M_DISC 1 +#define BLE_GAP_OP_M_CONN 2 +#define BLE_GAP_OP_S_ADV 1 +#define BLE_GAP_OP_S_PERIODIC_ADV 2 /** * If an attempt to cancel an active procedure fails, the attempt is retried @@ -128,6 +129,13 @@ struct ble_gap_master_state { uint8_t limited:1; } disc; }; + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + /* Tells if create_sync was called without receiving sync + * established event + */ + uint8_t pending_create_sync; +#endif }; static bssnz_t struct ble_gap_master_state ble_gap_master; @@ -148,6 +156,10 @@ struct ble_gap_slave_state { unsigned int directed:1; unsigned int legacy_pdu:1; unsigned int rnd_addr_set:1; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + unsigned int periodic_configured:1; + uint8_t periodic_op; +#endif uint8_t rnd_addr[6]; #else /* timer is used only with legacy advertising */ @@ -176,7 +188,6 @@ struct ble_gap_snapshot { }; static SLIST_HEAD(ble_gap_hook_list, ble_gap_event_listener) ble_gap_event_listener_list; - static os_membuf_t ble_gap_update_entry_mem[ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE), sizeof (struct ble_gap_update_entry))]; @@ -433,22 +444,22 @@ int ble_gap_conn_find_by_addr(const ble_addr_t *addr, struct ble_gap_conn_desc *out_desc) { - struct ble_hs_conn *conn; + struct ble_hs_conn *conn; - ble_hs_lock(); + ble_hs_lock(); - conn = ble_hs_conn_find_by_addr(addr); - if (conn != NULL && out_desc != NULL) { - ble_gap_fill_conn_desc(conn, out_desc); - } + conn = ble_hs_conn_find_by_addr(addr); + if (conn != NULL && out_desc != NULL) { + ble_gap_fill_conn_desc(conn, out_desc); + } - ble_hs_unlock(); + ble_hs_unlock(); - if (conn == NULL) { - return BLE_HS_ENOTCONN; - } + if (conn == NULL) { + return BLE_HS_ENOTCONN; + } - return 0; + return 0; } static int @@ -1283,6 +1294,111 @@ ble_gap_rx_adv_set_terminated(struct hci_le_adv_set_terminated *evt) } #endif +/* Periodic adv events */ +#if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_PERIODIC_ADV) +void +ble_gap_rx_peroidic_adv_sync_estab( + struct hci_le_subev_periodic_adv_sync_estab *evt) +{ + struct ble_gap_event event; + struct ble_gap_master_state state; + struct ble_hs_periodic_sync * psync; + + memset(&event, 0, sizeof event); + + /* There must be memory for psync as the check was done when creating + * sync + */ + psync = ble_hs_periodic_sync_alloc(); + BLE_HS_DBG_ASSERT(psync != NULL); + + psync->sync_handle = evt->sync_handle; + psync->adv_sid = evt->sid; + psync->advertiser_addr_type = evt->adv_addr_type; + + memcpy(psync->advertiser_addr.val, evt->adv_addr, 6); + + psync->advertiser_phy = evt->adv_phy; + psync->periodic_adv_itvl = evt->per_adv_ival; + psync->advertiser_clock_accuracy = evt->adv_clk_accuracy; + + ble_hs_lock(); + ble_hs_periodic_sync_insert(psync); + ble_hs_unlock(); + + event.type = BLE_GAP_EVENT_PERIODIC_ADV_SYNC_ESTAB; + event.periodic_adv_sync_estab.status = evt->status; + event.periodic_adv_sync_estab.sync_handle = evt->sync_handle; + event.periodic_adv_sync_estab.sid = evt->sid; + event.periodic_adv_sync_estab.adv_addr_type = evt->adv_addr_type; + + memcpy(event.periodic_adv_sync_estab.adv_addr, evt->adv_addr, 6); + + event.periodic_adv_sync_estab.adv_phy = evt->adv_phy; + event.periodic_adv_sync_estab.per_adv_ival = evt->per_adv_ival; + event.periodic_adv_sync_estab.adv_clk_accuracy = evt->adv_clk_accuracy; + + ble_gap_master.pending_create_sync = 0; + + ble_gap_master_extract_state(&state, 0); + if (ble_gap_has_client(&state)) { + state.cb(&event, state.cb_arg); + } +} + +void +ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt) +{ + struct ble_gap_event event; + struct ble_gap_master_state state; + + memset(&event, 0, sizeof event); + + event.type = BLE_GAP_EVENT_PERIODIC_DISC; + event.periodic_disc.sync_handle = evt->sync_handle; + event.periodic_disc.tx_power = evt->tx_power; + event.periodic_disc.rssi = evt->rssi; + event.periodic_disc.data_status = evt->data_status; + event.periodic_disc.data_length = evt->data_length; + event.periodic_disc.data = evt->data; + + ble_gap_master_extract_state(&state, 0); + if (ble_gap_has_client(&state)) { + state.cb(&event, state.cb_arg); + } +} + +void +ble_gap_rx_periodic_adv_sync_lost( + struct hci_le_subev_periodic_adv_sync_lost *evt) +{ + struct ble_gap_event event; + struct ble_gap_master_state state; + struct ble_hs_periodic_sync *psync; + + memset(&event, 0, sizeof event); + + event.type = BLE_GAP_EVENT_PERIODIC_ADV_SYNC_LOST; + event.periodic_adv_sync_lost.sync_handle = evt->sync_handle; + + ble_hs_lock(); + /* The handle must be in the list */ + psync = ble_hs_periodic_sync_find_assert(evt->sync_handle); + + /* Remove the handle from the list */ + ble_hs_periodic_sync_remove(psync); + + /* Free the memory occupied by psync as it is no longer needed */ + ble_hs_periodic_sync_free(psync); + ble_hs_unlock(); + + ble_gap_master_extract_state(&state, 0); + if (ble_gap_has_client(&state)) { + state.cb(&event, state.cb_arg); + } +} +#endif + static int ble_gap_rd_rem_sup_feat_tx(uint16_t handle) { @@ -2509,7 +2625,7 @@ ble_gap_ext_adv_start(uint8_t instance, int duration, int max_events) if (ble_gap_slave[instance].rnd_addr_set) { break; } - /* fall through */ + /* fall through */ case BLE_OWN_ADDR_PUBLIC: case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: default: @@ -2680,7 +2796,7 @@ ble_gap_ext_adv_set(uint8_t instance, uint16_t opcode, struct os_mbuf **data) opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, opcode); rc = ble_hs_hci_cmd_build_le_ext_adv_data(instance, - BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE, 0, *data, len, buf, sizeof(buf)); if (rc) { return rc; @@ -2703,7 +2819,7 @@ ble_gap_ext_adv_set(uint8_t instance, uint16_t opcode, struct os_mbuf **data) /* complete data */ if (len <= BLE_HCI_MAX_EXT_ADV_DATA_LEN) { rc = ble_hs_hci_cmd_build_le_ext_adv_data(instance, - BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE, 0, *data, len, buf,sizeof(buf)); if (rc) { return rc; @@ -2717,7 +2833,7 @@ ble_gap_ext_adv_set(uint8_t instance, uint16_t opcode, struct os_mbuf **data) } /* first fragment */ - op = BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST; + op = BLE_HCI_LE_SET_DATA_OPER_FIRST; do { rc = ble_hs_hci_cmd_build_le_ext_adv_data(instance, op, 0, *data, @@ -2736,12 +2852,12 @@ ble_gap_ext_adv_set(uint8_t instance, uint16_t opcode, struct os_mbuf **data) } len -= BLE_HCI_MAX_EXT_ADV_DATA_LEN; - op = BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_INT; + op = BLE_HCI_LE_SET_DATA_OPER_INT; } while (len > BLE_HCI_MAX_EXT_ADV_DATA_LEN); /* last fragment */ rc = ble_hs_hci_cmd_build_le_ext_adv_data(instance, - BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_LAST, + BLE_HCI_LE_SET_DATA_OPER_LAST, 0, *data, len, buf, sizeof(buf)); if (rc) { return rc; @@ -2891,6 +3007,529 @@ ble_gap_ext_adv_remove(uint8_t instance) return 0; } +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +static int +ble_gap_periodic_adv_params_tx(uint8_t instance, + const struct ble_gap_periodic_adv_params *params) + +{ + struct hci_periodic_adv_params hci_adv_params; + uint8_t buf[BLE_HCI_LE_SET_PERIODIC_ADV_PARAMS_LEN]; + int rc; + + memset(&hci_adv_params, 0, sizeof(hci_adv_params)); + + if (params->include_tx_power) { + hci_adv_params.properties |= BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR; + } + + /* Fill optional fields if application did not specify them. */ + if (params->itvl_min == 0 && params->itvl_max == 0) { + hci_adv_params.min_interval = 30 / 1.25; //30 ms + hci_adv_params.max_interval = 60 / 1.25; //150 ms + + } else { + hci_adv_params.min_interval = params->itvl_min; + hci_adv_params.max_interval = params->itvl_max; + } + + rc = ble_hs_hci_cmd_build_le_periodic_adv_params(instance, &hci_adv_params, buf, + sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack( + BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PERIODIC_ADV_PARAMS), buf, + sizeof(buf)); + + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_gap_periodic_adv_params_validate( + const struct ble_gap_periodic_adv_params *params) +{ + if (!params) { + return BLE_HS_EINVAL; + } + + if (params->itvl_min > 0xffff || params->itvl_min < 6) { + return BLE_HS_EINVAL; + } + if (params->itvl_max > 0xffff || params->itvl_max < 6) { + return BLE_HS_EINVAL; + } + return 0; +} + +int +ble_gap_periodic_adv_configure(uint8_t instance, + const struct ble_gap_periodic_adv_params *params) +{ + int rc; + + if (instance >= BLE_ADV_INSTANCES) { + return BLE_HS_EINVAL; + } + + rc = ble_gap_periodic_adv_params_validate(params); + if (rc) { + return rc; + } + + ble_hs_lock(); + + /* The corresponding extended advertising instance should be configured */ + if (!ble_gap_slave[instance].configured) { + ble_hs_unlock(); + return ENOMEM; + } + + /* Periodic advertising shall not be configured while it is already + * running. + * Bluetooth Core Specification, Section 7.8.61 + */ + if (ble_gap_slave[instance].periodic_op == BLE_GAP_OP_S_PERIODIC_ADV) { + ble_hs_unlock(); + return BLE_HS_EINVAL; + } + + rc = ble_gap_periodic_adv_params_tx(instance, params); + if (rc) { + ble_hs_unlock(); + return rc; + } + + ble_gap_slave[instance].periodic_configured = 1; + + ble_hs_unlock(); + + return 0; +} + +int +ble_gap_periodic_adv_start(uint8_t instance) +{ + uint8_t buf[BLE_HCI_LE_SET_PERIODIC_ADV_ENABLE_LEN]; + uint16_t opcode; + int rc; + + if (instance >= BLE_ADV_INSTANCES) { + return BLE_HS_EINVAL; + } + + ble_hs_lock(); + + /* Periodic advertising cannot start unless it is configured before */ + if (!ble_gap_slave[instance].periodic_configured) { + ble_hs_unlock(); + return BLE_HS_EINVAL; + } + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE); + rc = ble_hs_hci_cmd_build_le_periodic_adv_enable(1, instance, buf, + sizeof(buf)); + if (rc != 0) { + ble_hs_unlock(); + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + if (rc != 0) { + ble_hs_unlock(); + return rc; + } + + ble_gap_slave[instance].periodic_op = BLE_GAP_OP_S_PERIODIC_ADV; + + ble_hs_unlock(); + return 0; +} + +static int +ble_gap_periodic_adv_set(uint8_t instance, uint16_t opcode, + struct os_mbuf **data) +{ + /* In that case we always fit all data in single HCI command */ +#if MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) <= BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN + + static uint8_t buf[BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN + + MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)]; + uint16_t len = OS_MBUF_PKTLEN(*data); + int rc; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, opcode); + + rc = ble_hs_hci_cmd_build_le_periodic_adv_data( + instance, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE, + *data, len, + buf, sizeof(buf)); + + if (rc) { + return rc; + } + + os_mbuf_adj(*data, MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)); + *data = os_mbuf_trim_front(*data); + + return ble_hs_hci_cmd_tx_empty_ack(opcode, buf, + BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN + len); +#else + static uint8_t buf[BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN + + BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN]; + uint16_t len = OS_MBUF_PKTLEN(*data); + uint8_t op; + int rc; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, opcode); + + /* Complete data */ + if (len <= BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN) { + rc = ble_hs_hci_cmd_build_le_periodic_adv_data(instance, + BLE_HCI_LE_SET_DATA_OPER_COMPLETE, *data, len, buf, + sizeof(buf)); + if (rc) { + return rc; + } + + os_mbuf_adj(*data, len); + *data = os_mbuf_trim_front(*data); + + return ble_hs_hci_cmd_tx_empty_ack(opcode, buf, + BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN + len); + } + + /* If the periodic advertising is already enabled, the periodic advertising + * the op code shall be nothing but 0x03 + * Bluetooth Core Specification, section 7.8.62 + */ + if (ble_gap_slave[instance].periodic_op == BLE_GAP_OP_S_PERIODIC_ADV) { + return BLE_HS_EINVAL; + } + + /* First fragment */ + op = BLE_HCI_LE_SET_DATA_OPER_FIRST; + + do{ + rc = ble_hs_hci_cmd_build_le_periodic_adv_data(instance, op, *data, + BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN, buf, sizeof(buf)); + if (rc) { + return rc; + } + + os_mbuf_adj(*data, BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN); + *data = os_mbuf_trim_front(*data); + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + if (rc) { + return rc; + } + + len -= BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN; + op = BLE_HCI_LE_SET_DATA_OPER_INT; + } while (len > BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN); + + /* Last fragment */ + rc = ble_hs_hci_cmd_build_le_periodic_adv_data( + instance, + BLE_HCI_LE_SET_DATA_OPER_LAST, + *data, len, buf, sizeof(buf)); + if (rc) { + return rc; + } + + os_mbuf_adj(*data, len); + *data = os_mbuf_trim_front(*data); + + return ble_hs_hci_cmd_tx_empty_ack(opcode, buf, + BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN + len); +#endif +} + +static int +ble_gap_periodic_adv_set_data_validate(uint8_t instance, + struct os_mbuf *data) +{ + /* The corresponding extended advertising instance should be configured */ + if (!ble_gap_slave[instance].configured) { + return BLE_HS_EINVAL; + } + + if (ble_gap_slave[instance].legacy_pdu) { + return BLE_HS_EINVAL; + } + + /* One more check states that if the periodic advertising is already + * enabled, the operation shall be 0x03 (Complete). + * This check is handled during sending the data to the controller, as the + * length checks are already checked there, so this saves duplicate code + */ + + return 0; +} + +int +ble_gap_periodic_adv_set_data(uint8_t instance, struct os_mbuf *data) +{ + int rc; + if (instance >= BLE_ADV_INSTANCES) { + rc = BLE_HS_EINVAL; + goto done; + } + + ble_hs_lock(); + + rc = ble_gap_periodic_adv_set_data_validate(instance, data); + if (rc != 0) { + ble_hs_unlock(); + goto done; + } + + rc = ble_gap_periodic_adv_set( + instance, + BLE_HCI_OCF_LE_SET_PERIODIC_ADV_DATA, + &data); + + ble_hs_unlock(); + +done: + os_mbuf_free_chain(data); + return rc; +} + +static int +ble_gap_periodic_adv_stop_no_lock(uint8_t instance) +{ + uint8_t buf[BLE_HCI_LE_SET_PERIODIC_ADV_ENABLE_LEN]; + uint16_t opcode; + int rc; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE); + + rc = ble_hs_hci_cmd_build_le_periodic_adv_enable(0, instance, buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + ble_gap_slave[instance].periodic_op = BLE_GAP_OP_NULL; + + return 0; +} + +int +ble_gap_periodic_adv_stop(uint8_t instance) +{ + int rc; + + if (instance >= BLE_ADV_INSTANCES) { + return BLE_HS_EINVAL; + } + + ble_hs_lock(); + rc = ble_gap_periodic_adv_stop_no_lock(instance); + ble_hs_unlock(); + + return rc; +} + +int +ble_gap_periodic_adv_create_sync(uint8_t filter_policy, uint8_t adv_sid, + uint8_t adv_addr_type, const uint8_t *adv_addr, uint16_t skip, + uint16_t sync_timeout) +{ + uint8_t buf[BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_LEN]; + uint16_t opcode; + int rc = 0; + + if (!ble_hs_periodic_sync_can_alloc()) { + return BLE_HS_ENOMEM; + } + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC); + + /* No sync can be created if another sync is still pending */ + if (ble_gap_master.pending_create_sync) { + return BLE_HS_EINVAL; + } + + rc = ble_hs_hci_cmd_build_le_periodic_adv_create_sync(filter_policy, + adv_sid, + adv_addr_type, + adv_addr, + skip, sync_timeout, + buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + /* This shall be reset upon receiving sync_established event, + * or if the sync is cancelled before receiving that event. + */ + ble_gap_master.pending_create_sync = 1; + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + + return rc; +} + +int +ble_gap_periodic_adv_create_sync_cancel(void) +{ + uint16_t opcode; + int rc = 0; + + if (!ble_gap_master.pending_create_sync) { + return BLE_HS_EINVAL; + } + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL); + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, NULL, 0); + + if (!rc) { + ble_gap_master.pending_create_sync = 0; + } + return rc; +} + +int +ble_gap_periodic_adv_terminate_sync(uint16_t sync_handle) +{ + uint8_t buf[BLE_HCI_LE_PERIODIC_ADV_TERM_SYNC_LEN]; + uint16_t opcode; + struct ble_hs_periodic_sync *psync; + int rc = 0; + + if (ble_gap_master.pending_create_sync) { + return BLE_HS_EINVAL; + } + + ble_hs_lock(); + /* The handle must be in the list. If it doesn't exist, it means + * that the sync may have been lost at the same moment in which + * the app wants to terminate that sync handle */ + psync = ble_hs_periodic_sync_find(sync_handle); + if (!psync) { + /* Sync already terminated. TODO: Not sure what error code + * describes this scnario the best */ + rc = BLE_HS_EALREADY; + goto done; + } + + /* Remove the handle from the list */ + ble_hs_periodic_sync_remove(psync); + + /* Free the memory occupied by psync as it is no longer needed */ + ble_hs_periodic_sync_free(psync); + ble_hs_unlock(); + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_PERIODIC_ADV_TERM_SYNC); + + rc = ble_hs_hci_cmd_build_le_periodic_adv_terminate_sync(sync_handle, + buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + +done: + return rc; +} + +int +ble_gap_add_dev_to_periodic_adv_list(uint8_t adv_addr_type, + const uint8_t *adv_addr, uint8_t adv_sid) +{ + uint8_t buf[BLE_HCI_LE_ADD_DEV_TO_PERIODIC_ADV_LIST_LEN]; + uint16_t opcode; + int rc = 0; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_DEV_TO_PERIODIC_ADV_LIST); + + rc = ble_hs_hci_cmd_build_le_add_dev_to_periodic_adv_list(adv_addr_type, + adv_addr, adv_sid, buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + + return rc; +} + +int +ble_gap_rem_dev_from_periodic_adv_list(uint8_t adv_addr_type, + const uint8_t *adv_addr, uint8_t adv_sid) +{ + uint8_t buf[BLE_HCI_LE_REM_DEV_FROM_PERIODIC_ADV_LIST_LEN]; + uint16_t opcode; + int rc = 0; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_REM_DEV_FROM_PERIODIC_ADV_LIST); + + rc = ble_hs_hci_cmd_build_le_rem_dev_from_periodic_adv_list(adv_addr_type, + adv_addr, adv_sid, buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, buf, sizeof(buf)); + + return rc; +} + +int +ble_gap_clear_periodic_adv_list(void) +{ + uint16_t opcode; + int rc = 0; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLEAR_PERIODIC_ADV_LIST); + + rc = ble_hs_hci_cmd_tx_empty_ack(opcode, NULL, 0); + + return rc; +} + +int +ble_gap_read_periodic_adv_list_size(uint8_t *per_adv_list_size) +{ + uint8_t rspbuf[1]; + uint8_t rsplen; + uint16_t opcode; + int rc = 0; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_PERIODIC_ADV_LIST_SIZE); + + rc = ble_hs_hci_cmd_tx(opcode, NULL, 0, rspbuf, sizeof(rspbuf), &rsplen); + if (rc != 0) { + return rc; + } + + if (rsplen != sizeof(rspbuf)) { + return BLE_HS_ECONTROLLER; + } + + *per_adv_list_size = rspbuf[0]; + + return 0; +} + #endif /***************************************************************************** @@ -2957,7 +3596,7 @@ ble_gap_ext_disc_enable_tx(uint8_t enable, uint8_t filter_duplicates, buf, sizeof(buf)); } #endif - +#endif #if NIMBLE_BLE_SCAN #if !MYNEWT_VAL(BLE_EXT_ADV) static int @@ -4702,7 +5341,7 @@ ble_gap_preempt_done(void) disc_preempted = 0; - /* protects slaves from accessing by multiple threads */ + /* Protects slaves from accessing by multiple threads */ ble_npl_mutex_pend(&preempt_done_mutex, 0xFFFFFFFF); memset(slaves, 0, sizeof(slaves)); diff --git a/nimble/host/src/ble_gap_priv.h b/nimble/host/src/ble_gap_priv.h index a789f3976..c2aba24f5 100644 --- a/nimble/host/src/ble_gap_priv.h +++ b/nimble/host/src/ble_gap_priv.h @@ -82,6 +82,11 @@ void ble_gap_rx_le_scan_timeout(void); #if MYNEWT_VAL(BLE_EXT_ADV) void ble_gap_rx_ext_adv_report(struct ble_gap_ext_disc_desc *desc); void ble_gap_rx_adv_set_terminated(struct hci_le_adv_set_terminated *evt); +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +void ble_gap_rx_peroidic_adv_sync_estab(struct hci_le_subev_periodic_adv_sync_estab *evt); +void ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt); +void ble_gap_rx_periodic_adv_sync_lost(struct hci_le_subev_periodic_adv_sync_lost *evt); +#endif #endif void ble_gap_rx_adv_report(struct ble_gap_disc_desc *desc); void ble_gap_rx_rd_rem_sup_feat_complete(struct hci_le_rd_rem_supp_feat_complete *evt); diff --git a/nimble/host/src/ble_hs.c b/nimble/host/src/ble_hs.c index 3036746f0..c311847c3 100644 --- a/nimble/host/src/ble_hs.c +++ b/nimble/host/src/ble_hs.c @@ -744,6 +744,9 @@ ble_hs_init(void) rc = ble_hs_conn_init(); SYSINIT_PANIC_ASSERT(rc == 0); + rc = ble_hs_periodic_sync_init(); + SYSINIT_PANIC_ASSERT(rc == 0); + rc = ble_l2cap_init(); SYSINIT_PANIC_ASSERT(rc == 0); diff --git a/nimble/host/src/ble_hs_dbg.c b/nimble/host/src/ble_hs_dbg.c index 4529ee0b0..2f5c15f05 100644 --- a/nimble/host/src/ble_hs_dbg.c +++ b/nimble/host/src/ble_hs_dbg.c @@ -224,10 +224,10 @@ ble_hs_dbg_le_event_disp(uint8_t subev, uint8_t len, uint8_t *evdata) } break; } - - case BLE_HCI_LE_SUBEV_PER_ADV_SYNC_ESTAB: +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + case BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB: { - struct hci_le_subev_per_adv_sync_estab *data = (void *) evdata; + struct hci_le_subev_periodic_adv_sync_estab *data = (void *) evdata; if (len != sizeof(*data)) { BLE_HS_LOG(DEBUG, "Corrupted LE Periodic Advertising Sync " @@ -241,9 +241,9 @@ ble_hs_dbg_le_event_disp(uint8_t subev, uint8_t len, uint8_t *evdata) break; } - case BLE_HCI_LE_SUBEV_PER_ADV_RPT: + case BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT: { - struct hci_le_subev_per_adv_rpt *data = (void *) evdata; + struct hci_le_subev_periodic_adv_rpt *data = (void *) evdata; if (len < sizeof(*data) || len != sizeof(*data) + data->data_length) { BLE_HS_LOG(DEBUG, "Corrupted LE Periodic Advertising Report " @@ -258,9 +258,9 @@ ble_hs_dbg_le_event_disp(uint8_t subev, uint8_t len, uint8_t *evdata) break; } - case BLE_HCI_LE_SUBEV_PER_ADV_SYNC_LOST: + case BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST: { - struct hci_le_subev_per_adv_sync_lost *data = (void *) evdata; + struct hci_le_subev_periodic_adv_sync_lost *data = (void *) evdata; if (len != sizeof(*data)) { BLE_HS_LOG(DEBUG, "Corrupted LE Periodic Advertising Sync Lost " @@ -272,6 +272,7 @@ ble_hs_dbg_le_event_disp(uint8_t subev, uint8_t len, uint8_t *evdata) "len=%u handle=0x%04x", len, le16toh(data->sync_handle)); break; } +#endif case BLE_HCI_LE_SUBEV_SCAN_TIMEOUT: if (len) { diff --git a/nimble/host/src/ble_hs_hci_cmd.c b/nimble/host/src/ble_hs_hci_cmd.c index 47eea6df0..036a4af77 100644 --- a/nimble/host/src/ble_hs_hci_cmd.c +++ b/nimble/host/src/ble_hs_hci_cmd.c @@ -1605,6 +1605,164 @@ ble_hs_hci_cmd_build_le_ext_adv_remove(uint8_t handle, return 0; } + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +int +ble_hs_hci_cmd_build_le_periodic_adv_params(uint8_t handle, + const struct hci_periodic_adv_params *params, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_SET_PERIODIC_ADV_PARAMS_LEN); + + if (handle > 0xEF){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (params->max_interval > 0xFFFF || params->max_interval < 6){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (params->min_interval > 0xFFFF || params->min_interval < 6){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = handle; + + put_le16(&cmd[1], params->min_interval); + put_le16(&cmd[3], params->max_interval); + put_le16(&cmd[5], params->properties); + + return 0; +} + +int +ble_hs_hci_cmd_build_le_periodic_adv_enable(uint8_t enable, + uint8_t handle, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_SET_PERIODIC_ADV_ENABLE_LEN); + + if (enable > 1){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = enable; + cmd[1] = handle; + + return 0; +} + +int +ble_hs_hci_cmd_build_le_periodic_adv_data(uint8_t handle, + uint8_t operation, + struct os_mbuf *data, + uint8_t data_len, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= 3 + data_len); + + if (!data){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (!data->om_data){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (handle > 0xEF){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = handle; + cmd[1] = operation; + cmd[2] = data_len; + os_mbuf_copydata(data, 0, data_len, cmd + 3); + + return 0; +} + +int +ble_hs_hci_cmd_build_le_periodic_adv_create_sync(uint8_t filter_policy, + uint8_t adv_sid, + uint8_t adv_add_type, + const uint8_t *adv_addr, + uint16_t skip, + uint16_t sync_timeout, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_LEN); + + if (filter_policy > 1 || adv_add_type > 1){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (adv_sid > 0x0f){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + if (skip > 0x1f3 || sync_timeout > 0x4000 || sync_timeout < 0x0A){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + cmd[0] = filter_policy; + cmd[1] = adv_sid; + cmd[2] = adv_add_type; + memcpy(&cmd[3], adv_addr, 6); + put_le16(&cmd[9], skip); + put_le16(&cmd[11], sync_timeout); + cmd[13] = 0x00; + return 0; +} + +int +ble_hs_hci_cmd_build_le_periodic_adv_terminate_sync(uint16_t sync_handle, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_PERIODIC_ADV_TERM_SYNC_LEN); + + if (sync_handle > 0xEF){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = sync_handle; + + return 0; +} + +int +ble_hs_hci_cmd_build_le_add_dev_to_periodic_adv_list( + uint8_t adv_add_type, + const uint8_t *adv_addr, + uint8_t adv_sid, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_ADD_DEV_TO_PERIODIC_ADV_LIST_LEN); + + if (adv_add_type > 1 || adv_sid > 0x0f){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = adv_add_type; + memcpy(&cmd[1], adv_addr, 6); + cmd[7] = adv_sid; + + return 0; +} + +int +ble_hs_hci_cmd_build_le_rem_dev_from_periodic_adv_list( + uint8_t adv_add_type, + const uint8_t *adv_addr, + uint8_t adv_sid, + uint8_t *cmd, int cmd_len) +{ + BLE_HS_DBG_ASSERT(cmd_len >= BLE_HCI_LE_REM_DEV_FROM_PERIODIC_ADV_LIST_LEN); + + if (adv_add_type > 1 || adv_sid > 0x0f){ + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + cmd[0] = adv_add_type; + memcpy(&cmd[1], adv_addr, 6); + cmd[7] = adv_sid; + + return 0; +} +#endif + #endif static int diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index 0ccd9e8d4..03d6e8af7 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -51,7 +51,9 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_ext_adv_rpt; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_rd_rem_used_feat_complete; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_scan_timeout; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_adv_set_terminated; - +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_estab; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_rpt; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_lost; /* Statistics */ struct host_hci_stats { @@ -100,6 +102,11 @@ static const struct ble_hs_hci_evt_le_dispatch_entry { BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE, ble_hs_hci_evt_le_phy_update_complete }, { BLE_HCI_LE_SUBEV_EXT_ADV_RPT, ble_hs_hci_evt_le_ext_adv_rpt }, + { BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB, + ble_hs_hci_evt_le_periodic_adv_sync_estab}, + { BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT, ble_hs_hci_evt_le_periodic_adv_rpt}, + { BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST, + ble_hs_hci_evt_le_periodic_adv_sync_lost}, { BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT, ble_hs_hci_evt_le_rd_rem_used_feat_complete }, { BLE_HCI_LE_SUBEV_SCAN_TIMEOUT, @@ -226,7 +233,8 @@ ble_hs_hci_evt_enc_key_refresh(uint8_t event_code, uint8_t *data, int len) } static int -ble_hs_hci_evt_num_completed_pkts(uint8_t event_code, uint8_t *data, int len) +ble_hs_hci_evt_num_completed_pkts(uint8_t event_code, uint8_t *data, + int len) { struct ble_hs_conn *conn; uint16_t num_pkts; @@ -501,8 +509,9 @@ ble_hs_hci_evt_le_dir_adv_rpt(uint8_t subevent, uint8_t *data, int len) } static int -ble_hs_hci_evt_le_rd_rem_used_feat_complete(uint8_t subevent, uint8_t *data, - int len) +ble_hs_hci_evt_le_rd_rem_used_feat_complete(uint8_t subevent, + uint8_t *data, + int len) { struct hci_le_rd_rem_supp_feat_complete evt; @@ -614,6 +623,76 @@ ble_hs_hci_evt_le_ext_adv_rpt(uint8_t subevent, uint8_t *data, int len) return 0; } +static int +ble_hs_hci_evt_le_periodic_adv_sync_estab(uint8_t subevent, uint8_t *data, + int len) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_PERIODIC_ADV) + struct hci_le_subev_periodic_adv_sync_estab evt; + + if (len < BLE_HCI_LE_PERIODIC_ADV_SYNC_ESTAB_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.status = data[1]; + evt.sync_handle = get_le16(data + 2); + evt.sid = data[4]; + evt.adv_addr_type = data[5]; + memcpy(evt.adv_addr, &data[6], 6); + evt.adv_phy = data[12]; + evt.per_adv_ival = get_le16(data + 13); + evt.adv_clk_accuracy = data[15]; + + ble_gap_rx_peroidic_adv_sync_estab(&evt); +#endif + + return 0; +} + +static int +ble_hs_hci_evt_le_periodic_adv_rpt(uint8_t subevent, uint8_t *data, + int len) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_PERIODIC_ADV) + struct hci_le_subev_periodic_adv_rpt evt; + + if (len < BLE_HCI_LE_PERIODIC_ADV_RPT_LEN) { + return BLE_HS_EBADDATA; + } + + evt.sync_handle = get_le16(data + 1); + evt.tx_power = data[3]; + evt.rssi = data[4]; + evt.data_status = data[6]; + evt.data_length = data[7]; + memcpy(evt.data, &data[8], evt.data_length); + + ble_gap_rx_periodic_adv_rpt(&evt); +#endif + +return 0; +} + +static int +ble_hs_hci_evt_le_periodic_adv_sync_lost(uint8_t subevent, uint8_t *data, + int len) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) && MYNEWT_VAL(BLE_PERIODIC_ADV) + struct hci_le_subev_periodic_adv_sync_lost evt; + + if (len < BLE_HCI_LE_PERIODIC_ADV_SYNC_LOST_LEN) { + return BLE_HS_EBADDATA; + } + + evt.sync_handle = get_le16(data + 1); + + ble_gap_rx_periodic_adv_sync_lost(&evt); + +#endif + return 0; +} + + static int ble_hs_hci_evt_le_scan_timeout(uint8_t subevent, uint8_t *data, int len) { diff --git a/nimble/host/src/ble_hs_hci_priv.h b/nimble/host/src/ble_hs_hci_priv.h index dc4d676d3..e6c91c745 100644 --- a/nimble/host/src/ble_hs_hci_priv.h +++ b/nimble/host/src/ble_hs_hci_priv.h @@ -235,6 +235,48 @@ ble_hs_hci_cmd_build_le_ext_adv_params(uint8_t handle, int ble_hs_hci_cmd_build_le_ext_adv_remove(uint8_t handle, uint8_t *cmd, int cmd_len); + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +int +ble_hs_hci_cmd_build_le_periodic_adv_params(uint8_t handle, + const struct hci_periodic_adv_params *params, + uint8_t *cmd, int cmd_len); + +int +ble_hs_hci_cmd_build_le_periodic_adv_enable(uint8_t enable, + uint8_t handle, + uint8_t *cmd, int cmd_len); + +int +ble_hs_hci_cmd_build_le_periodic_adv_data(uint8_t handle, uint8_t operation, + struct os_mbuf *data, + uint8_t data_len, + uint8_t *cmd, int cmd_len); +int +ble_hs_hci_cmd_build_le_periodic_adv_create_sync(uint8_t filter_policy, + uint8_t adv_sid, + uint8_t adv_add_type, + const uint8_t *adv_addr, + uint16_t skip, + uint16_t sync_timeout, + uint8_t *cmd, int cmd_len); + +int +ble_hs_hci_cmd_build_le_periodic_adv_terminate_sync(uint16_t sync_handle, + uint8_t *cmd, int cmd_len); + +int +ble_hs_hci_cmd_build_le_add_dev_to_periodic_adv_list(uint8_t adv_add_type, + const uint8_t *adv_addr, + uint8_t adv_sid, + uint8_t *cmd, int cmd_len); +int +ble_hs_hci_cmd_build_le_rem_dev_from_periodic_adv_list(uint8_t adv_add_type, + const uint8_t *adv_addr, + uint8_t adv_sid, + uint8_t *cmd, int cmd_len); +#endif + #endif int ble_hs_hci_cmd_build_le_enh_recv_test(uint8_t rx_chan, uint8_t phy, diff --git a/nimble/host/src/ble_hs_periodic_disc.c b/nimble/host/src/ble_hs_periodic_disc.c new file mode 100644 index 000000000..3dcb2bdd6 --- /dev/null +++ b/nimble/host/src/ble_hs_periodic_disc.c @@ -0,0 +1,230 @@ +/* + * 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 "syscfg/syscfg.h" +#include "os/os.h" +#include "host/ble_hs_id.h" +#include "ble_hs_priv.h" + +static SLIST_HEAD(, ble_hs_periodic_sync) ble_hs_periodic_sync_handles; +static struct os_mempool ble_hs_periodic_sync_pool; + +static os_membuf_t ble_hs_psync_elem_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_PERIODIC_SCANNING_SM), + sizeof (struct ble_hs_periodic_sync)) +]; + +int +ble_hs_periodic_sync_can_alloc(void) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return 0; +#endif + + return (ble_hs_periodic_sync_pool.mp_num_free >= 1); +} + +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_alloc(void) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return NULL; +#endif + + struct ble_hs_periodic_sync *psync; + + psync = os_memblock_get(&ble_hs_periodic_sync_pool); + if (psync == NULL) { + goto err; + } + memset(psync, 0, sizeof *psync); + + return psync; + +err: + ble_hs_periodic_sync_free(psync); + return NULL; +} + +void +ble_hs_periodic_sync_free(struct ble_hs_periodic_sync *psync) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return; +#endif + + int rc; + if (psync == NULL) { + return; + } + +#if MYNEWT_VAL(BLE_HS_DEBUG) + memset(psync, 0xff, sizeof *psync); +#endif + rc = os_memblock_put(&ble_hs_periodic_sync_pool, psync); + BLE_HS_DBG_ASSERT_EVAL(rc == 0); +} + +void +ble_hs_periodic_sync_insert(struct ble_hs_periodic_sync *psync) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return; +#endif + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + BLE_HS_DBG_ASSERT_EVAL( + ble_hs_periodic_sync_find(psync->sync_handle) == NULL); + + SLIST_INSERT_HEAD(&ble_hs_periodic_sync_handles, psync, bhc_next); +} + +void +ble_hs_periodic_sync_remove(struct ble_hs_periodic_sync *psync) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return; +#endif + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + SLIST_REMOVE(&ble_hs_periodic_sync_handles, + psync, + ble_hs_periodic_sync, + bhc_next); +} + +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_find(uint16_t sync_handle) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return NULL; +#endif + + struct ble_hs_periodic_sync *psync; + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + SLIST_FOREACH(psync, &ble_hs_periodic_sync_handles, bhc_next) { + if (psync->sync_handle == sync_handle) { + return psync; + } + } + + return NULL; +} + +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_find_assert(uint16_t sync_handle) +{ + struct ble_hs_periodic_sync *psync; + + psync = ble_hs_periodic_sync_find(sync_handle); + BLE_HS_DBG_ASSERT(psync != NULL); + + return psync; +} + +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_find_by_adv_addr(const ble_addr_t *addr) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return NULL; +#endif + + struct ble_hs_periodic_sync *psync; + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + if (!addr) { + return NULL; + } + + SLIST_FOREACH(psync, &ble_hs_periodic_sync_handles, bhc_next) { + if (ble_addr_cmp(&psync->advertiser_addr, addr) == 0) { + return psync; + } + } + + return NULL; +} + +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_find_by_adv_sid(uint16_t sid) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return 0; +#endif + + struct ble_hs_periodic_sync *psync; + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + + SLIST_FOREACH(psync, &ble_hs_periodic_sync_handles, bhc_next) { + if (psync->adv_sid == sid) { + return psync; + } + } + + return NULL; +} + +int +ble_hs_periodic_sync_exists(uint16_t sync_handle) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return 0; +#endif + return ble_hs_periodic_sync_find(sync_handle) != NULL; +} + +/** + * Retrieves the first periodic discovery handle in the list. + */ +struct ble_hs_periodic_sync * +ble_hs_periodic_sync_first(void) +{ +#if !MYNEWT_VAL(BLE_PERIODIC_ADV) + return NULL; +#endif + + BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task()); + return SLIST_FIRST(&ble_hs_periodic_sync_handles); +} + +int +ble_hs_periodic_sync_init(void) +{ + int rc; + + rc = os_mempool_init(&ble_hs_periodic_sync_pool, + MYNEWT_VAL(BLE_MAX_PERIODIC_SCANNING_SM), + sizeof (struct ble_hs_periodic_sync), + ble_hs_psync_elem_mem, "ble_hs_periodic_disc_pool"); + if (rc != 0) { + return BLE_HS_EOS; + } + + SLIST_INIT(&ble_hs_periodic_sync_handles); + + return 0; +} diff --git a/nimble/host/src/ble_hs_periodic_disc_priv.h b/nimble/host/src/ble_hs_periodic_disc_priv.h new file mode 100644 index 000000000..5cf2bfa98 --- /dev/null +++ b/nimble/host/src/ble_hs_periodic_disc_priv.h @@ -0,0 +1,61 @@ +/* + * 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_PERIODIC_DISC_ +#define H_BLE_HS_PERIODIC_DISC_ + +#include +#include "os/queue.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_periodic_sync { + SLIST_ENTRY(ble_hs_periodic_sync) bhc_next; + uint16_t sync_handle; + uint8_t adv_sid; + uint8_t advertiser_addr_type; + ble_addr_t advertiser_addr; + uint8_t advertiser_phy; + uint16_t periodic_adv_itvl; + uint8_t advertiser_clock_accuracy; +}; + +int ble_hs_periodic_sync_can_alloc(void); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_alloc(void); +void ble_hs_periodic_sync_free(struct ble_hs_periodic_sync *psync); +void ble_hs_periodic_sync_insert(struct ble_hs_periodic_sync *psync); +void ble_hs_periodic_sync_remove(struct ble_hs_periodic_sync *psync); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_find( + uint16_t sync_handle); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_find_assert( + uint16_t sync_handle); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_find_by_adv_addr( + const ble_addr_t *addr); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_find_by_adv_sid( + uint16_t sid); +int ble_hs_periodic_sync_exists(uint16_t sync_handle); +struct ble_hs_periodic_sync *ble_hs_periodic_sync_first(void); +int ble_hs_periodic_sync_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/nimble/host/src/ble_hs_priv.h b/nimble/host/src/ble_hs_priv.h index f7d3cd43a..dec12ff80 100644 --- a/nimble/host/src/ble_hs_priv.h +++ b/nimble/host/src/ble_hs_priv.h @@ -30,6 +30,7 @@ #include "ble_hs_hci_priv.h" #include "ble_hs_atomic_priv.h" #include "ble_hs_conn_priv.h" +#include "ble_hs_periodic_disc_priv.h" #include "ble_hs_atomic_priv.h" #include "ble_hs_mbuf_priv.h" #include "ble_hs_startup_priv.h" diff --git a/nimble/host/src/ble_hs_startup.c b/nimble/host/src/ble_hs_startup.c index 2253e6382..b21db7513 100644 --- a/nimble/host/src/ble_hs_startup.c +++ b/nimble/host/src/ble_hs_startup.c @@ -256,12 +256,15 @@ ble_hs_startup_le_set_evmask_tx(void) * Enable the following LE events: * 0x0000000000000800 LE PHY Update Complete Event * 0x0000000000001000 LE Extended Advertising Report Event + * 0x0000000000002000 LE Periodic Advertising Sync Established Event + * 0x0000000000004000 LE Periodic Advertising Report Event + * 0x0000000000008000 LE Periodic Advertising Sync Lost Event * 0x0000000000010000 LE Extended Scan Timeout Event * 0x0000000000020000 LE Extended Advertising Set Terminated Event * 0x0000000000040000 LE Scan Request Received Event * 0x0000000000080000 LE Channel Selection Algorithm Event */ - mask |= 0x00000000000f1800; + mask |= 0x00000000000ff800; } ble_hs_hci_cmd_build_le_set_event_mask(mask, buf, sizeof buf); diff --git a/nimble/host/src/ble_hs_stop.c b/nimble/host/src/ble_hs_stop.c index 31480d77e..3e15bca43 100644 --- a/nimble/host/src/ble_hs_stop.c +++ b/nimble/host/src/ble_hs_stop.c @@ -58,10 +58,52 @@ ble_hs_stop_done(int status) } } +/** + * Terminates all active periodic sync handles + * + * If there are no active periodic sync handles, signals completion of the + * close procedure. + */ +static int +ble_hs_stop_terminate_next_periodic_sync(void) +{ + int rc = 0; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + struct ble_hs_periodic_sync *psync; + + while((psync = ble_hs_periodic_sync_first())){ + /* Terminate sync command waits a command complete event, so there + * is no need to wait for GAP event, as the calling thread will be + * blocked on the hci semaphore until the command complete is received. + * + * Also, once the sync is terminated, the psync will be freed and + * removed from the list such that the next call to + * ble_hs_periodic_sync_first yields the next psync handle + */ + rc = ble_gap_periodic_adv_terminate_sync(psync->sync_handle); + + if (!rc){ + BLE_HS_LOG(ERROR, + "ble_hs_stop: failed to terminate connection; rc=%d\n", + rc); + ble_hs_stop_done(rc); + } + } + +#endif + /* No more active periodic sync handles (Or periodic advertising + * is not supported), Signal completion of the stop procedure. + */ + ble_hs_stop_done(0); + + return rc; +} + /** * Terminates the first open connection. * - * If there are no open connections, signals completion of the close procedure. + * If there are no open connections, Check for any active periodic sync + * handles. */ static void ble_hs_stop_terminate_next_conn(void) @@ -71,8 +113,8 @@ ble_hs_stop_terminate_next_conn(void) handle = ble_hs_atomic_first_conn_handle(); if (handle == BLE_HS_CONN_HANDLE_NONE) { - /* No open connections. Signal completion of the stop procedure. */ - ble_hs_stop_done(0); + /* No open connections. Check for any active periodic sync */ + ble_hs_stop_terminate_next_periodic_sync(); return; } diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index 420c9fc12..4d6fa2985 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -149,19 +149,19 @@ extern "C" { #define BLE_HCI_OCF_LE_RD_NUM_OF_ADV_SETS (0x003B) #define BLE_HCI_OCF_LE_REMOVE_ADV_SET (0x003C) #define BLE_HCI_OCF_LE_CLEAR_ADV_SETS (0x003D) -#define BLE_HCI_OCF_LE_SET_PER_ADV_PARAMS (0x003E) -#define BLE_HCI_OCF_LE_SET_PER_ADV_DATA (0x003F) -#define BLE_HCI_OCF_LE_SET_PER_ADV_ENABLE (0x0040) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_PARAMS (0x003E) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_DATA (0x003F) +#define BLE_HCI_OCF_LE_SET_PERIODIC_ADV_ENABLE (0x0040) #define BLE_HCI_OCF_LE_SET_EXT_SCAN_PARAM (0x0041) #define BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE (0x0042) #define BLE_HCI_OCF_LE_EXT_CREATE_CONN (0x0043) -#define BLE_HCI_OCF_LE_PER_ADV_CREATE_SYNC (0x0044) -#define BLE_HCI_OCF_LE_PER_ADV_CREATE_SYNC_CANCEL (0x0045) -#define BLE_HCI_OCF_LE_PER_ADV_TERM_SYNC (0x0046) -#define BLE_HCI_OCF_LE_ADD_DEV_TO_PER_ADV_LIST (0x0047) -#define BLE_HCI_OCF_LE_REM_DEV_FROM_PER_ADV_LIST (0x0048) -#define BLE_HCI_OCF_LE_CLEAR_PER_ADV_LIST (0x0049) -#define BLE_HCI_OCF_LE_RD_PER_ADV_LIST_SIZE (0x004A) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC (0x0044) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL (0x0045) +#define BLE_HCI_OCF_LE_PERIODIC_ADV_TERM_SYNC (0x0046) +#define BLE_HCI_OCF_LE_ADD_DEV_TO_PERIODIC_ADV_LIST (0x0047) +#define BLE_HCI_OCF_LE_REM_DEV_FROM_PERIODIC_ADV_LIST (0x0048) +#define BLE_HCI_OCF_LE_CLEAR_PERIODIC_ADV_LIST (0x0049) +#define BLE_HCI_OCF_LE_RD_PERIODIC_ADV_LIST_SIZE (0x004A) #define BLE_HCI_OCF_LE_RD_TRANSMIT_POWER (0x004B) #define BLE_HCI_OCF_LE_RD_RF_PATH_COMPENSATION (0x004C) #define BLE_HCI_OCF_LE_WR_RF_PATH_COMPENSATION (0x004D) @@ -522,21 +522,18 @@ extern "C" { #define BLE_HCI_SET_EXT_ADV_DATA_HDR_LEN (4) #define BLE_HCI_LE_SET_EXT_ADV_DATA_LEN BLE_HCI_VARIABLE_LEN -#define BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_INT (0) -#define BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST (1) -#define BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_LAST (2) -#define BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE (3) -#define BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_UNCHANGED (4) +#define BLE_HCI_LE_SET_DATA_OPER_INT (0) +#define BLE_HCI_LE_SET_DATA_OPER_FIRST (1) +#define BLE_HCI_LE_SET_DATA_OPER_LAST (2) +#define BLE_HCI_LE_SET_DATA_OPER_COMPLETE (3) +#define BLE_HCI_LE_SET_DATA_OPER_UNCHANGED (4) /* --- LE set extended scan response data (OCF 0x0038) */ #define BLE_HCI_MAX_EXT_SCAN_RSP_DATA_LEN (251) #define BLE_HCI_SET_EXT_SCAN_RSP_DATA_HDR_LEN (4) #define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_LEN BLE_HCI_VARIABLE_LEN -#define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_INT (0) -#define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_FIRST (1) -#define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_LAST (2) -#define BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE (3) + /* --- LE set extended advertising enable (OCF 0x0039) */ #define BLE_HCI_LE_SET_EXT_ADV_ENABLE_LEN BLE_HCI_VARIABLE_LEN @@ -551,13 +548,18 @@ extern "C" { #define BLE_HCI_RD_NR_SUP_ADV_SETS (1) /* --- LE set periodic advertising parameters (OCF 0x003E) */ -#define BLE_HCI_LE_SET_PER_ADV_PARAMS_LEN (7) +#define BLE_HCI_LE_SET_PERIODIC_ADV_PARAMS_LEN (7) +#define BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR (0x0040) /* --- LE set periodic advertising data (OCF 0x003F) */ -#define BLE_HCI_LE_SET_PER_ADV_DATA_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_LE_SET_PERIODIC_ADV_DATA_LEN BLE_HCI_VARIABLE_LEN +#define BLE_HCI_MAX_PERIODIC_ADV_DATA_LEN (252) +#define BLE_HCI_SET_PERIODIC_ADV_DATA_HDR_LEN (3) + + /* --- LE periodic advertising enable (OCF 0x0040) */ -#define BLE_HCI_LE_SET_PER_ADV_ENABLE_LEN (2) +#define BLE_HCI_LE_SET_PERIODIC_ADV_ENABLE_LEN (2) /* --- LE set extended scan parameters (OCF 0x0041) */ #define BLE_HCI_LE_SET_EXT_SCAN_PARAM_LEN BLE_HCI_VARIABLE_LEN @@ -572,16 +574,16 @@ extern "C" { #define BLE_HCI_LE_EXT_CREATE_CONN_BASE_LEN (10) /* --- LE periodic advertising create sync (OCF 0x0044) */ -#define BLE_HCI_LE_PER_ADV_CREATE_SYNC_LEN (14) +#define BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_LEN (14) /* --- LE periodic advertising terminate (OCF 0x0046) */ -#define BLE_HCI_LE_PER_ADV_TERM_SYNC_LEN (2) +#define BLE_HCI_LE_PERIODIC_ADV_TERM_SYNC_LEN (2) /* --- LE add device to periodic advertising list (OCF 0x0047) */ -#define BLE_HCI_LE_ADD_DEV_TO_PER_ADV_LIST_LEN (8) +#define BLE_HCI_LE_ADD_DEV_TO_PERIODIC_ADV_LIST_LEN (8) /* --- LE remove device from periodic advertising list (OCF 0x0048) */ -#define BLE_HCI_LE_REM_DEV_FROM_PER_ADV_LIST_LEN (8) +#define BLE_HCI_LE_REM_DEV_FROM_PERIODIC_ADV_LIST_LEN (8) /* --- LE write RF path (OCF 0x004D) */ #define BLE_HCI_LE_WR_RF_PATH_COMPENSATION_LEN (4) @@ -685,9 +687,9 @@ extern "C" { #define BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT (0x0B) #define BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE (0x0C) #define BLE_HCI_LE_SUBEV_EXT_ADV_RPT (0x0D) -#define BLE_HCI_LE_SUBEV_PER_ADV_SYNC_ESTAB (0x0E) -#define BLE_HCI_LE_SUBEV_PER_ADV_RPT (0x0F) -#define BLE_HCI_LE_SUBEV_PER_ADV_SYNC_LOST (0x10) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_ESTAB (0x0E) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_RPT (0x0F) +#define BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_LOST (0x10) #define BLE_HCI_LE_SUBEV_SCAN_TIMEOUT (0x11) #define BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED (0x12) #define BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD (0x13) @@ -780,6 +782,15 @@ extern "C" { /* LE PHY update complete event (sub event 0x0C) */ #define BLE_HCI_LE_PHY_UPD_LEN (6) +/* LE Periodic Advertising Sync Established Event (sub event 0x0e) */ +#define BLE_HCI_LE_PERIODIC_ADV_SYNC_ESTAB_LEN (16) + +/* LE Periodic Advertising Report Event (sub event 0x0f) */ +#define BLE_HCI_LE_PERIODIC_ADV_RPT_LEN (8) + +/* LE Periodic Advertising Sync Lost Event (sub event 0x10) */ +#define BLE_HCI_LE_PERIODIC_ADV_SYNC_LOST_LEN (3) + /* LE Scan Timeout Event (sub event 0x11) */ #define BLE_HCI_LE_SUBEV_SCAN_TIMEOUT_LEN (1) @@ -990,8 +1001,17 @@ struct hci_le_subev_ext_adv_rpt { struct hci_ext_adv_report_param params[0]; } __attribute__((packed)); +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +/* Periodic Advertising Parameters */ +struct hci_periodic_adv_params +{ + uint16_t min_interval; + uint16_t max_interval; + uint16_t properties; +}; + /* LE Periodic Advertising Sync Established Event */ -struct hci_le_subev_per_adv_sync_estab { +struct hci_le_subev_periodic_adv_sync_estab { uint8_t status; uint16_t sync_handle; uint8_t sid; @@ -1003,20 +1023,20 @@ struct hci_le_subev_per_adv_sync_estab { } __attribute__((packed)); /* LE Periodic Advertising Report Event */ -struct hci_le_subev_per_adv_rpt { +struct hci_le_subev_periodic_adv_rpt { uint16_t sync_handle; uint8_t tx_power; int8_t rssi; - uint8_t _unused; uint8_t data_status; uint8_t data_length; uint8_t data[0]; } __attribute__((packed)); /* LE Periodic Advertising Sync Lost Event */ -struct hci_le_subev_per_adv_sync_lost { +struct hci_le_subev_periodic_adv_sync_lost { uint16_t sync_handle; } __attribute__((packed)); +#endif /* LE Advertising Set Terminated Event */ struct hci_le_subev_adv_set_terminated { diff --git a/nimble/syscfg.yml b/nimble/syscfg.yml index 4010002b5..93cfb6a0d 100644 --- a/nimble/syscfg.yml +++ b/nimble/syscfg.yml @@ -34,6 +34,11 @@ syscfg.defs: BLE_MAX_CONNECTIONS: description: 'The maximum number of concurrent connections.' value: 1 + BLE_MAX_PERIODIC_SCANNING_SM: + description: > + The maximum number of concurrent periodic syncs that can + be created + value: 1 BLE_WHITELIST: description: > Enables the BLE whitelist for controlling who to connect to or @@ -50,6 +55,10 @@ syscfg.defs: description: > This enables extended advertising feature. value: 0 + BLE_PERIODIC_ADV: + description: > + This enables periodic advertising feature. + value: 0 BLE_EXT_ADV_MAX_SIZE: description: > This allows to configure maximum size of advertising data and diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index 284eb979c..648af5c13 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -247,6 +247,10 @@ #define MYNEWT_VAL_BLE_MAX_CONNECTIONS (1) #endif +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM (0) +#endif + #ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES #define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) #endif diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index d83570403..416cfbf91 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -247,6 +247,10 @@ #define MYNEWT_VAL_BLE_MAX_CONNECTIONS (1) #endif +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM (0) +#endif + #ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES #define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) #endif diff --git a/porting/nimble/Makefile.defs b/porting/nimble/Makefile.defs index ffb531fb6..71c536c31 100644 --- a/porting/nimble/Makefile.defs +++ b/porting/nimble/Makefile.defs @@ -50,6 +50,7 @@ NIMBLE_SRC := \ $(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/lls/src/*.c)) \ $(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/tps/src/*.c)) \ $(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/store/ram/src/*.c)) \ + $(NIMBLE_ROOT)/nimble/host/src/ble_hs_periodic_disc.c \ $(NULL) ifneq (,$(NIMBLE_CFG_CONTROLLER)) diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h index c9b375574..72fca0484 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -389,6 +389,10 @@ #define MYNEWT_VAL_BLE_MAX_CONNECTIONS (1) #endif +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM (0) +#endif + #ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES #define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) #endif @@ -418,6 +422,11 @@ #define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU (256) #endif + +#ifndef MYNEWT_VAL_BLE_PERIODIC_ADV +#define MYNEWT_VAL_BLE_PERIODIC_ADV (0) +#endif + #ifndef MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO #define MYNEWT_VAL_BLE_ATT_SVR_FIND_INFO (1) #endif diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h index d7cd0a030..a41dfa316 100644 --- a/porting/npl/riot/include/syscfg/syscfg.h +++ b/porting/npl/riot/include/syscfg/syscfg.h @@ -410,6 +410,10 @@ #define MYNEWT_VAL_BLE_MAX_CONNECTIONS (1) #endif +#ifndef MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM +#define MYNEWT_VAL_BLE_MAX_PERIODIC_SCANNING_SM (0) +#endif + #ifndef MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES #define MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES (0) #endif