MYNEWT-723: Add control procedure code and HCI command/event code.
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/4e347012 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/4e347012 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/4e347012 Branch: refs/heads/bluetooth5 Commit: 4e347012011204fd92bf1c3512debe93ae802b2f Parents: 46d67b0 Author: William San Filippo <[email protected]> Authored: Sun Apr 23 15:02:14 2017 -0700 Committer: Åukasz Rymanowski <[email protected]> Committed: Thu Apr 27 23:38:58 2017 +0200 ---------------------------------------------------------------------- .../controller/include/controller/ble_ll.h | 20 +- .../controller/include/controller/ble_ll_conn.h | 57 +- .../controller/include/controller/ble_ll_ctrl.h | 28 +- .../controller/include/controller/ble_ll_hci.h | 2 + net/nimble/controller/src/ble_ll.c | 12 + net/nimble/controller/src/ble_ll_conn.c | 94 ++++ net/nimble/controller/src/ble_ll_conn_hci.c | 128 ++++- net/nimble/controller/src/ble_ll_conn_priv.h | 4 + net/nimble/controller/src/ble_ll_ctrl.c | 543 ++++++++++++++++++- net/nimble/controller/src/ble_ll_hci.c | 83 +++ net/nimble/controller/src/ble_ll_hci_ev.c | 33 ++ net/nimble/controller/syscfg.yml | 10 + net/nimble/include/nimble/hci_common.h | 10 +- 13 files changed, 988 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/include/controller/ble_ll.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll.h b/net/nimble/controller/include/controller/ble_ll.h index b2cff88..619d1a9 100644 --- a/net/nimble/controller/include/controller/ble_ll.h +++ b/net/nimble/controller/include/controller/ble_ll.h @@ -57,6 +57,12 @@ extern "C" { #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) @@ -90,18 +96,16 @@ struct ble_ll_obj /* Number of ACL data packets supported */ uint8_t ll_num_acl_pkts; -#ifdef BLE_XCVR_RFCLK - uint8_t ll_rfclk_state; - uint16_t ll_xtal_ticks; -#else - uint8_t _pad; - uint16_t _pad16; -#endif - /* 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; + #ifdef BLE_XCVR_RFCLK + uint8_t ll_rfclk_state; + uint16_t ll_xtal_ticks; uint32_t ll_rfclk_start_time; struct hal_timer ll_rfclk_timer; #endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/include/controller/ble_ll_conn.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_conn.h b/net/nimble/controller/include/controller/ble_ll_conn.h index 7817c65..d3d7639 100644 --- a/net/nimble/controller/include/controller/ble_ll_conn.h +++ b/net/nimble/controller/include/controller/ble_ll_conn.h @@ -116,10 +116,40 @@ union ble_ll_conn_sm_flags { 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? */ } cfbit; uint32_t conn_flags; } __attribute__((packed)); +/** + * Structure used for PHY data inside a connection. + * + * cur_tx_phy: value denoting current tx_phy (not a bitmask!) + * cur_rx_phy: value denoting current rx phy (not a bitmask) + * pref_tx_phys: bitmask of preferred transmit PHYs + * pref_rx_phys: bitmask of preferred receive PHYs + * phy_options: preferred phy options for coded phy + */ +struct ble_ll_conn_phy_data +{ + uint8_t cur_tx_phy: 2; + uint8_t cur_rx_phy: 2; + uint8_t new_tx_phy: 2; + uint8_t new_rx_phy: 2; + uint16_t host_pref_tx_phys: 3; + uint16_t host_pref_rx_phys: 3; + uint16_t req_pref_tx_phys: 3; + uint16_t req_pref_rx_phys: 3; + uint16_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)) + /* Connection state machine */ struct ble_ll_conn_sm { @@ -131,6 +161,12 @@ struct ble_ll_conn_sm 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; @@ -145,6 +181,10 @@ struct ble_ll_conn_sm uint16_t eff_max_tx_time; uint16_t eff_max_rx_time; + /* XXX: TODO: could make this conditional */ + struct ble_ll_conn_phy_data phy_data; + uint16_t phy_instant; + /* 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]; @@ -155,9 +195,6 @@ struct ble_ll_conn_sm uint8_t last_unmapped_chan; uint8_t num_used_chans; - /* RSSI */ - int8_t conn_rssi; - /* Ack/Flow Control */ uint8_t tx_seqnum; /* note: can be 1 bit */ uint8_t next_exp_seqnum; /* note: can be 1 bit */ @@ -166,9 +203,6 @@ struct ble_ll_conn_sm uint8_t last_rxd_hdr_byte; /* note: possibly can make 1 bit since we only use the MD bit now */ - /* For privacy */ - int8_t rpa_index; - /* connection event mgmt */ uint8_t reject_reason; uint8_t host_reply_opcode; @@ -177,8 +211,8 @@ struct ble_ll_conn_sm uint8_t cur_ctrl_proc; uint8_t disconnect_reason; uint8_t rxd_disconnect_reason; - uint32_t common_features; uint8_t vers_nr; + uint32_t common_features; uint16_t pending_ctrl_procs; uint16_t event_cntr; uint16_t completed_pkts; @@ -216,6 +250,10 @@ struct ble_ll_conn_sm 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 os_event conn_ev_end; @@ -274,6 +312,11 @@ struct ble_ll_conn_sm #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_TERMINATE_STARTED(csm) ((csm)->csmflags.cfbit.terminate_started) +#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) /* Role */ #define CONN_IS_MASTER(csm) (csm->conn_role == BLE_LL_CONN_ROLE_MASTER) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/include/controller/ble_ll_ctrl.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_ctrl.h b/net/nimble/controller/include/controller/ble_ll_ctrl.h index 23da308..95f56c4 100644 --- a/net/nimble/controller/include/controller/ble_ll_ctrl.h +++ b/net/nimble/controller/include/controller/ble_ll_ctrl.h @@ -38,7 +38,8 @@ extern "C" { #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_NUM (9) +#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 */ @@ -75,9 +76,13 @@ extern "C" { #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) /* Maximum opcode value */ -#define BLE_LL_CTRL_OPCODES (BLE_LL_CTRL_LENGTH_RSP + 1) +#define BLE_LL_CTRL_OPCODES (BLE_LL_CTRL_MIN_USED_CHAN_IND + 1) extern const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES]; @@ -131,6 +136,12 @@ struct ble_ll_enc_rsp #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. @@ -190,7 +201,7 @@ struct ble_ll_conn_params uint16_t offset5; }; -#define BLE_LL_CTRL_CONN_PARAMS_LEN (24) +#define BLE_LL_CTRL_CONN_PARAMS_LEN (23) /* LL control reject ind ext */ struct ble_ll_reject_ind_ext @@ -221,6 +232,14 @@ struct ble_ll_len_req #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) + /* API */ struct ble_ll_conn_sm; void ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc); @@ -251,8 +270,9 @@ 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); - +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); #ifdef __cplusplus } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/include/controller/ble_ll_hci.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_hci.h b/net/nimble/controller/include/controller/ble_ll_hci.h index c2c4542..e54ec7e 100644 --- a/net/nimble/controller/include/controller/ble_ll_hci.h +++ b/net/nimble/controller/include/controller/ble_ll_hci.h @@ -54,6 +54,8 @@ int ble_ll_hci_event_send(uint8_t *evbuf); /* Sends a command complete with a no-op opcode to host */ int 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 *cmdbuf, uint8_t *txphy, uint8_t *rxphy); #ifdef __cplusplus } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll.c b/net/nimble/controller/src/ble_ll.c index 5fe03d0..eb1534b 100644 --- a/net/nimble/controller/src/ble_ll.c +++ b/net/nimble/controller/src/ble_ll.c @@ -1174,6 +1174,10 @@ ble_ll_reset(void) memset(&g_ble_ll_log, 0, sizeof(g_ble_ll_log)); #endif + /* 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(); @@ -1344,6 +1348,14 @@ ble_ll_init(void) features |= BLE_LL_FEAT_CSA2; #endif +#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) == 1) + features |= BLE_LL_FEAT_LE_2M_PHY; +#endif + +#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) == 1) + features |= BLE_LL_FEAT_LE_CODED_PHY; +#endif + /* Initialize random number generation */ ble_ll_rand_init(); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_conn.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn.c b/net/nimble/controller/src/ble_ll_conn.c index e981cd7..0efbba2 100644 --- a/net/nimble/controller/src/ble_ll_conn.c +++ b/net/nimble/controller/src/ble_ll_conn.c @@ -229,6 +229,42 @@ STATS_NAME_END(ble_ll_conn_stats) static void ble_ll_conn_event_end(struct os_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 == 0) && + (csm->phy_data.host_pref_rx_phys == 0)) || + ((csm->phy_data.host_pref_tx_phys & CONN_CUR_TX_PHY_MASK(csm)) && + (csm->phy_data.host_pref_rx_phys & CONN_CUR_RX_PHY_MASK(csm)))) { + rc = -1; + } else { + csm->phy_data.req_pref_tx_phys = csm->phy_data.host_pref_tx_phys; + csm->phy_data.req_pref_rx_phys = csm->phy_data.host_pref_rx_phys; + ble_ll_ctrl_proc_start(csm, BLE_LL_CTRL_PROC_PHY_UPDATE); + rc = 0; + } + + return rc; +} +#endif + #if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 static void ble_ll_conn_calc_itvl_ticks(struct ble_ll_conn_sm *connsm) @@ -905,6 +941,16 @@ ble_ll_conn_chk_csm_flags(struct ble_ll_conn_sm *connsm) 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 } /** @@ -1640,6 +1686,17 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm) connsm->conn_rssi = BLE_LL_CONN_UNKNOWN_RSSI; connsm->rpa_index = -1; + /* XXX: TODO set these based on PHY that started connection */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + connsm->phy_data.cur_tx_phy = BLE_HCI_LE_PHY_1M; + connsm->phy_data.cur_rx_phy = BLE_HCI_LE_PHY_1M; + connsm->phy_data.req_pref_tx_phys = 0; + connsm->phy_data.req_pref_rx_phys = 0; + connsm->phy_data.host_pref_tx_phys = g_ble_ll_data.ll_pref_tx_phys; + connsm->phy_data.host_pref_rx_phys = g_ble_ll_data.ll_pref_rx_phys; + connsm->phy_data.phy_options = 0; +#endif + /* Reset current control procedure */ connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; connsm->pending_ctrl_procs = 0; @@ -1836,6 +1893,8 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) /** * Called to move to the next connection event. * + * Context: Link Layer task. + * * @param connsm * * @return int @@ -1861,11 +1920,18 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) ble_ll_ctrl_terminate_start(connsm); } + /* + * 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; @@ -1974,6 +2040,21 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) 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 */ + connsm->phy_data.cur_tx_phy = connsm->phy_data.new_tx_phy; + connsm->phy_data.cur_rx_phy = connsm->phy_data.new_rx_phy; + + /* 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); + } +#endif + /* Calculate data channel index of next connection event */ connsm->data_chan_index = ble_ll_conn_calc_dci(connsm, latency); @@ -2125,6 +2206,19 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr) /* 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 /* * 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 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_conn_hci.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn_hci.c b/net/nimble/controller/src/ble_ll_conn_hci.c index 429fcb3..4f7390c 100644 --- a/net/nimble/controller/src/ble_ll_conn_hci.c +++ b/net/nimble/controller/src/ble_ll_conn_hci.c @@ -236,13 +236,6 @@ ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status, /** * Called to create and send the number of completed packets event to the * host. - * - * Because of the ridiculous spec, all the connection handles are contiguous - * and then all the completed packets are contiguous. In order to avoid - * multiple passes through the connection list or allocating a large stack - * variable or malloc, I just use the event buffer and place the completed - * packets after the last possible handle. I then copy the completed packets - * to make it contiguous with the handles. */ void ble_ll_conn_num_comp_pkts_event_send(struct ble_ll_conn_sm *connsm) @@ -1021,7 +1014,8 @@ ble_ll_conn_hci_set_data_len(uint8_t *cmdbuf, uint8_t *rspbuf, uint8_t *rsplen) /* * XXX: For now; we will simply ignore what the host asks as we are - * allowed to do so by the spec. + * allowed to do so by the spec. If we implement this and something + * changes we need to send data length change event. */ } @@ -1204,3 +1198,121 @@ wr_auth_exit: 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(uint8_t *cmdbuf, uint8_t *rsp, uint8_t *rsplen) +{ + int rc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + + handle = get_le16(cmdbuf); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + } else { + rsp[2] = connsm->phy_data.cur_tx_phy; + rsp[3] = connsm->phy_data.cur_rx_phy; + rc = BLE_ERR_SUCCESS; + } + + put_le16(rsp, handle); + *rsplen = BLE_HCI_LE_RD_PHY_RSPLEN; + return rc; +} + +/** + * Set PHY preferences for connection + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_le_set_phy(uint8_t *cmdbuf) +{ + int rc; + uint8_t phy_options; + uint8_t tx_phys; + uint8_t rx_phys; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + + handle = get_le16(cmdbuf); + 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 = cmdbuf[5]; + if (phy_options > BLE_HCI_LE_PHY_CODED_S8_PREF) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + connsm->phy_data.phy_options = phy_options; + + /* Check valid parameters */ + rc = ble_ll_hci_chk_phy_masks(cmdbuf + 2, &tx_phys, &rx_phys); + if (rc) { + goto phy_cmd_param_err; + } + + connsm->phy_data.host_pref_tx_phys = tx_phys, + connsm->phy_data.host_pref_rx_phys = 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 |= BLE_LL_CTRL_PROC_PHY_UPDATE; + } 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 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_conn_priv.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn_priv.h b/net/nimble/controller/src/ble_ll_conn_priv.h index 02cac60..13e03cf 100644 --- a/net/nimble/controller/src/ble_ll_conn_priv.h +++ b/net/nimble/controller/src/ble_ll_conn_priv.h @@ -162,6 +162,10 @@ void ble_ll_conn_auth_pyld_timer_start(struct ble_ll_conn_sm *connsm); 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(uint8_t *cmdbuf, uint8_t *rsp, uint8_t *rsplen); +int ble_ll_conn_hci_le_set_phy(uint8_t *cmdbuf); +int ble_ll_conn_chk_phy_upd_start(struct ble_ll_conn_sm *connsm); + #ifdef __cplusplus } #endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_ctrl.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_ctrl.c b/net/nimble/controller/src/ble_ll_ctrl.c index fe0831b..ed9f336 100644 --- a/net/nimble/controller/src/ble_ll_ctrl.c +++ b/net/nimble/controller/src/ble_ll_ctrl.c @@ -79,9 +79,88 @@ */ const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES] = { - 11, 7, 1, 22, 12, 0, 0, 1, 8, 8, 0, 0, 5, 1, 8, 23, 23, 2, 0, 0, 8, 8 + 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 }; +/** + * 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; +} + static int ble_ll_ctrl_chk_supp_bytes(uint16_t bytes) { @@ -111,6 +190,32 @@ ble_ll_ctrl_chk_supp_time(uint16_t t) return rc; } +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +/** + * Called when an unknown response or ext reject received while performing + * a PHY update procedure + * + * @param connsm + * @param ble_err + */ +void +ble_ll_ctrl_phy_update_rejected(strcut 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) == 1) { + 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_PEER_PHY_UPDATE(connsm) = 0; + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; +} +#endif + static int ble_ll_ctrl_len_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr) { @@ -312,6 +417,12 @@ ble_ll_ctrl_proc_unk_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr) #endif ctrl_proc = BLE_LL_CTRL_PROC_LE_PING; break; +#if (BLE_LL_BT5_PHY_SUPPORTED ==1) + case BLE_LL_CTRL_PHY_REQ: + ble_ll_ctrl_phy_update_rejected(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; @@ -329,6 +440,394 @@ ble_ll_ctrl_proc_unk_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr) } } +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) +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; + + if (CONN_F_PEER_PHY_UPDATE(connsm)) { + CONN_F_PEER_PHY_UPDATE(connsm) = 0; + /* Must check if we need to start procedure */ + chk_proc_stop = 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) == 1) { + if (ble_ll_conn_chk_phy_upd_start(connsm)) { + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + } + } + } + + if (chk_proc_stop) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + } +} + +/** + * 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) + */ +static uint8_t +ble_ll_ctrl_phy_mask_to_numeric(uint8_t phy_mask) +{ + uint8_t numeric; + + /* + * 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_HCI_LE_PHY_1M_PREF_MASK) { + numeric = BLE_HCI_LE_PHY_1M; + phy_mask &= ~BLE_HCI_LE_PHY_1M_PREF_MASK; + } else if (phy_mask & BLE_HCI_LE_PHY_2M_PREF_MASK) { + numeric = BLE_HCI_LE_PHY_2M; + phy_mask &= ~BLE_HCI_LE_PHY_2M_PREF_MASK; + } else if (phy_mask & BLE_HCI_LE_PHY_CODED_PREF_MASK) { + numeric = BLE_HCI_LE_PHY_CODED; + phy_mask &= ~BLE_HCI_LE_PHY_CODED_PREF_MASK; + } else { + numeric = 0; + } + + if (phy_mask != 0) { + numeric = 0; + } + + return numeric; +} + +/** + * + * 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 prefs) +{ + uint8_t new_phy; + + new_phy = prefs; + if (new_phy) { + if (new_phy & BLE_HCI_LE_PHY_2M_PREF_MASK) { + new_phy = BLE_HCI_LE_PHY_2M; + } else if (new_phy & BLE_HCI_LE_PHY_1M_PREF_MASK) { + new_phy = BLE_HCI_LE_PHY_1M; + } else { + new_phy = BLE_HCI_LE_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; + + /* Get preferences from PDU */ + tx_phys = dptr[0]; + rx_phys = dptr[1]; + + /* Get m_to_s and s_to_m masks */ + if (slave_req) { + m_to_s = connsm->phy_data.host_pref_tx_phys & rx_phys; + s_to_m = connsm->phy_data.host_pref_rx_phys & tx_phys; + } else { + m_to_s = connsm->phy_data.req_pref_tx_phys & rx_phys; + s_to_m = connsm->phy_data.req_pref_rx_phys & tx_phys; + } + + /* Find new phys. If not different than current, set to 0 */ + m_to_s = ble_ll_ctrl_find_new_phy(m_to_s); + if (m_to_s == connsm->phy_data.cur_tx_phy) { + m_to_s = 0; + } + + s_to_m = ble_ll_ctrl_find_new_phy(s_to_m); + 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; + + /* Convert m_to_s and s_to_m to masks */ + m_to_s = 1 << (m_to_s - 1); + s_to_m = 1 << (s_to_m - 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; + } + + 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) +{ + ctrdata[0] = connsm->phy_data.host_pref_tx_phys; + ctrdata[1] = connsm->phy_data.host_pref_rx_phys; +} + +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) { + os_callout_stop(&connsm->ctrl_proc_rsp_timer); + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; + } + + /* If there is a PHY update procedure pending clear it */ + CLR_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + + /* Need to send event to host if the host phy update is pending */ + if (CONN_F_HOST_PHY_UPDATE(connsm)) { + CONN_F_HOST_PHY_UPDATE(connsm) = 0; + ble_ll_hci_ev_phy_update(connsm, err); + } + + /* Clear any flags we do not want around */ + CONN_F_CTRLR_PHY_UPDATE(connsm) = 0; + 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; + } + 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); + 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? + */ + } + + /* 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 + */ +void +ble_ll_ctrl_rx_phy_update_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + uint8_t new_tx_phy_mask; + uint8_t new_rx_phy_mask; + uint8_t new_tx_phy; + uint8_t new_rx_phy; + int no_change; + uint16_t instant; + uint16_t delta; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + /* + * Reception stops the procedure response timer but does not + * complete the procedure + */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) { + os_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_tx_phy_mask = dptr[0]; + new_rx_phy_mask = dptr[1]; + instant = get_le16(dptr + 2); + + no_change = 0; + if ((new_tx_phy_mask == 0) && (new_rx_phy_mask == 0)) { + /* No change in phy */ + no_change = 1; + } else { + new_tx_phy = ble_ll_ctrl_phy_mask_to_numeric(new_tx_phy_mask); + new_rx_phy = ble_ll_ctrl_phy_mask_to_numeric(new_rx_phy_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_tx_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_ll_ctrl_phy_update_proc_complete(connsm); + } +} +#endif + /** * Create a link layer length request or length response PDU. * @@ -461,7 +960,6 @@ ble_ll_ctrl_is_start_enc_rsp(struct os_mbuf *txpdu) * Called to create and send a LL_START_ENC_REQ or LL_START_ENC_RSP * * @param connsm - * @param rej_opcode * @param err * * @return int @@ -919,7 +1417,7 @@ ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, uint8_t *rsp, /** * Called when we have received a LL_REJECT_IND or LL_REJECT_IND_EXT link - * layer control Dat Channel pdu. + * layer control Data Channel pdu. * * @param connsm * @param dptr @@ -954,6 +1452,12 @@ ble_ll_ctrl_rx_reject_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr, 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_rejected(connsm, ble_error); + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_PHY_UPDATE); + break; +#endif default: break; } @@ -964,13 +1468,11 @@ ble_ll_ctrl_rx_reject_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr, * * @param connsm * @param dptr - * @param rspbuf * * @return int */ static int -ble_ll_ctrl_rx_conn_update(struct ble_ll_conn_sm *connsm, uint8_t *dptr, - uint8_t *rspbuf) +ble_ll_ctrl_rx_conn_update(struct ble_ll_conn_sm *connsm, uint8_t *dptr) { uint8_t rsp_opcode; uint16_t conn_events; @@ -1320,6 +1822,12 @@ ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc) } 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, dptr); + break; +#endif default: assert(0); break; @@ -1462,6 +1970,13 @@ ble_ll_ctrl_chk_proc_start(struct ble_ll_conn_sm *connsm) { int i; + /* WWW: new rules! Cannot start certain control procedures if other + * ones are peer initiated. We need to wait. Deal with this. + * + * WWW: Do not forget code that when some of these things end we need + * to check to start other control procedures + */ + /* * If we are terminating, dont start any new procedures but start * terminate if needed @@ -1602,6 +2117,9 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) 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; default: feature = 0; break; @@ -1632,7 +2150,7 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) rsp_opcode = BLE_ERR_MAX; switch (opcode) { case BLE_LL_CTRL_CONN_UPDATE_REQ: - rsp_opcode = ble_ll_ctrl_rx_conn_update(connsm, dptr, rspbuf); + rsp_opcode = ble_ll_ctrl_rx_conn_update(connsm, dptr); break; case BLE_LL_CTRL_CHANNEL_MAP_REQ: ble_ll_ctrl_rx_chanmap_req(connsm, dptr); @@ -1733,6 +2251,17 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) case BLE_LL_CTRL_REJECT_IND_EXT: ble_ll_ctrl_rx_reject_ind(connsm, dptr, opcode); 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: + ble_ll_ctrl_rx_phy_update_ind(connsm, dptr); + break; +#endif default: /* Nothing to do here */ break; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_hci.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_hci.c b/net/nimble/controller/src/ble_ll_hci.c index 692c0c9..a6e4c56 100644 --- a/net/nimble/controller/src/ble_ll_hci.c +++ b/net/nimble/controller/src/ble_ll_hci.c @@ -290,6 +290,75 @@ ble_ll_hci_le_read_bufsize(uint8_t *rspbuf, uint8_t *rsplen) 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 + * + * @param cmdbuf Pointer to command buffer where phy masks are located + * @param txphy Pointer to output tx phy mask + * @param rxphy Pointer to output rx phy mask + * + * @return int BLE_ERR_SUCCESS or BLE_ERR_INV_HCI_CMD_PARMS + */ +int +ble_ll_hci_chk_phy_masks(uint8_t *cmdbuf, uint8_t *txphy, uint8_t *rxphy) +{ + int rc; + uint8_t all_phys; + uint8_t rx_phys; + uint8_t tx_phys; + + /* Check for valid values */ + all_phys = cmdbuf[0]; + tx_phys = cmdbuf[1] & BLE_HCI_LE_PHY_PREF_MASK_ALL; + rx_phys = cmdbuf[2] & BLE_HCI_LE_PHY_PREF_MASK_ALL; + + if (((all_phys & BLE_HCI_LE_PHY_TX_PREF) && (tx_phys == 0)) || + ((all_phys & BLE_HCI_LE_PHY_RX_PREF) && (rx_phys == 0))) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + } else { + /* If phy not supported, wipe its bit */ +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + tx_phys &= ~BLE_HCI_LE_PHY_2M_PREF_MASK; + rx_phys &= ~BLE_HCI_LE_PHY_2M_PREF_MASK; +#endif +#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + tx_phys &= ~BLE_HCI_LE_PHY_CODED_PREF_MASK; + rx_phys &= ~BLE_HCI_LE_PHY_CODED_PREF_MASK; +#endif + /* Set the default PHY preferences */ + if ((all_phys & BLE_HCI_LE_PHY_TX_PREF) == 0) { + tx_phys = 0; + } + *txphy = tx_phys; + if ((all_phys & BLE_HCI_LE_PHY_RX_PREF) == 0) { + rx_phys = 0; + } + *rxphy = rx_phys; + rc = BLE_ERR_SUCCESS; + } + return rc; +} + +/** + * Set PHY preferences for connection + * + * @param cmdbuf + * + * @return int + */ +static int +ble_ll_hci_le_set_def_phy(uint8_t *cmdbuf) +{ + int rc; + + rc = ble_ll_hci_chk_phy_masks(cmdbuf, &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) == 1) /** * HCI write suggested default data length command. @@ -322,6 +391,8 @@ ble_ll_hci_le_wr_sugg_data_len(uint8_t *cmdbuf) g_ble_ll_conn_params.sugg_tx_octets = (uint8_t)tx_oct; g_ble_ll_conn_params.sugg_tx_time = tx_time; + /* XXX TODO: This has to change! They do not have to be the same + at this point. Deal with this */ if ((tx_time <= g_ble_ll_conn_params.supp_max_tx_time) && (tx_oct <= g_ble_ll_conn_params.supp_max_tx_octets)) { g_ble_ll_conn_params.conn_init_max_tx_octets = tx_oct; @@ -500,6 +571,7 @@ ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf) 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: rc = 1; break; default: @@ -768,6 +840,17 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) case BLE_HCI_OCF_LE_RD_MAX_DATA_LEN: rc = ble_ll_hci_le_rd_max_data_len(rspbuf, rsplen); break; +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + case BLE_HCI_OCF_LE_RD_PHY: + rc = ble_ll_conn_hci_le_rd_phy(cmdbuf, rspbuf, rsplen); + break; + case BLE_HCI_OCF_LE_SET_DEFAULT_PHY: + rc = ble_ll_hci_le_set_def_phy(cmdbuf); + break; + case BLE_HCI_OCF_LE_SET_PHY: + rc = ble_ll_conn_hci_le_set_phy(cmdbuf); + break; +#endif default: rc = BLE_ERR_UNKNOWN_HCI_CMD; break; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/src/ble_ll_hci_ev.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_hci_ev.c b/net/nimble/controller/src/ble_ll_hci_ev.c index 3319db3..2ee9908 100644 --- a/net/nimble/controller/src/ble_ll_hci_ev.c +++ b/net/nimble/controller/src/ble_ll_hci_ev.c @@ -292,3 +292,36 @@ ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm) } } #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) +{ + int rc; + uint8_t *evbuf; + + rc = 0; + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE)) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (evbuf) { + evbuf[0] = BLE_HCI_EVCODE_LE_META; + evbuf[1] = BLE_HCI_LE_PHY_UPD_COMP_LEN; + evbuf[2] = BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE; + evbuf[3] = status; + put_le16(evbuf + 4, connsm->conn_handle); + evbuf[6] = connsm->phy_data.cur_tx_phy; + evbuf[7] = connsm->phy_data.cur_rx_phy; + ble_ll_hci_event_send(evbuf); + } else { + rc = BLE_ERR_MEM_CAPACITY; + } + } + return rc; +} +#endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/controller/syscfg.yml ---------------------------------------------------------------------- diff --git a/net/nimble/controller/syscfg.yml b/net/nimble/controller/syscfg.yml index 0fe7a48..1b00a4e 100644 --- a/net/nimble/controller/syscfg.yml +++ b/net/nimble/controller/syscfg.yml @@ -210,6 +210,16 @@ syscfg.defs: 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_PUBLIC_DEV_ADDR: description: > Allows the target or app to override the public device address http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/4e347012/net/nimble/include/nimble/hci_common.h ---------------------------------------------------------------------- diff --git a/net/nimble/include/nimble/hci_common.h b/net/nimble/include/nimble/hci_common.h index 8ae29dc..f929d85 100644 --- a/net/nimble/include/nimble/hci_common.h +++ b/net/nimble/include/nimble/hci_common.h @@ -161,7 +161,6 @@ extern "C" { #define BLE_HCI_OCF_LE_SET_PRIVACY_MODE (0x004E) /* Command Specific Definitions */ - #define BLE_HCI_VARIABLE_LEN (0xFF) /* --- Disconnect command (OGF 0x01, OCF 0x0006) --- */ @@ -418,12 +417,19 @@ extern "C" { #define BLE_HCI_RD_MAX_DATALEN_RSPLEN (8) /* --- LE read maximum default PHY (OCF 0x0030) */ -#define BLE_HCI_LE_RD_PHY_LEN (2) +#define BLE_HCI_LE_RD_PHY_LEN (2) +#define BLE_HCI_LE_RD_PHY_RSPLEN (4) +#define BLE_HCI_LE_PHY_1M (1) +#define BLE_HCI_LE_PHY_2M (2) +#define BLE_HCI_LE_PHY_CODED (3) /* --- LE set default PHY (OCF 0x0031) */ #define BLE_HCI_LE_SET_DEFAULT_PHY_LEN (3) #define BLE_HCI_LE_PHY_NO_TX_PREF_MASK (0x01) #define BLE_HCI_LE_PHY_NO_RX_PREF_MASK (0x02) +#define BLE_HCI_LE_PHY_PREF_MASK_ALL \ + (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | \ + BLE_HCI_LE_PHY_CODED_PREF_MASK) #define BLE_HCI_LE_PHY_1M_PREF_MASK (0x01) #define BLE_HCI_LE_PHY_2M_PREF_MASK (0x02)
