You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ry...@apache.org on 2017/04/27 21:45:55 UTC

[2/6] incubator-mynewt-core git commit: MYNEWT-723: Add control procedure code and HCI command/event code.

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 <wi...@runtime.io>
Authored: Sun Apr 23 15:02:14 2017 -0700
Committer: \u0141ukasz Rymanowski <lu...@codecoup.pl>
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)