You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/01/21 23:13:19 UTC

incubator-mynewt-larva git commit: GAP Connection Parameter Update procedure.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master d979b5186 -> dd338e9e0


GAP Connection Parameter Update procedure.

Timeouts are not yet implemented.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/dd338e9e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/dd338e9e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/dd338e9e

Branch: refs/heads/master
Commit: dd338e9e0de211982fc039a95c5a0cd17be6db15
Parents: d979b51
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Jan 21 14:11:56 2016 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Jan 21 14:11:56 2016 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gap.h  |  43 ++-
 net/nimble/host/include/host/host_hci.h |   2 +
 net/nimble/host/src/ble_att_priv.h      |   1 +
 net/nimble/host/src/ble_gap_conn.c      | 397 +++++++++++++++++++++++++--
 net/nimble/host/src/ble_gap_priv.h      |   2 +
 net/nimble/host/src/ble_hs_conn.h       |  10 +
 net/nimble/host/src/host_hci_cmd.c      |  33 +++
 net/nimble/host/src/test/ble_gap_test.c |  10 +-
 net/nimble/host/src/test/ble_os_test.c  |  30 +-
 net/nimble/include/nimble/hci_common.h  |  51 +++-
 project/bleshell/src/main.c             |  20 +-
 project/centtest/src/main.c             |  16 +-
 project/prphtest/src/main.c             |  14 +-
 13 files changed, 556 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/include/host/ble_gap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_gap.h b/net/nimble/host/include/host/ble_gap.h
index 3ea6644..e66adf6 100644
--- a/net/nimble/host/include/host/ble_gap.h
+++ b/net/nimble/host/include/host/ble_gap.h
@@ -20,26 +20,47 @@
 #include <inttypes.h>
 #include "host/ble_hs.h"
 struct hci_le_conn_complete;
+struct hci_conn_update;
 
 #define BLE_GAP_ADDR_TYPE_WL                0xff
 
 #define BLE_GAP_EVENT_CONN                  0
-#define BLE_GAP_EVENT_CANCEL_FAILURE        1
-#define BLE_GAP_EVENT_TERM_FAILURE          2
-#define BLE_GAP_EVENT_DISC_SUCCESS          3
-#define BLE_GAP_EVENT_DISC_FINISHED         4
-#define BLE_GAP_EVENT_ADV_FINISHED          5
-#define BLE_GAP_EVENT_ADV_FAILURE           6
-#define BLE_GAP_EVENT_ADV_STOP_FAILURE      7
+#define BLE_GAP_EVENT_CONN_UPDATED          1
+#define BLE_GAP_EVENT_CONN_UPDATE_REQ       2
+#define BLE_GAP_EVENT_CANCEL_FAILURE        3
+#define BLE_GAP_EVENT_TERM_FAILURE          4
+#define BLE_GAP_EVENT_DISC_SUCCESS          5
+#define BLE_GAP_EVENT_DISC_FINISHED         6
+#define BLE_GAP_EVENT_ADV_FINISHED          7
+#define BLE_GAP_EVENT_ADV_FAILURE           8
+#define BLE_GAP_EVENT_ADV_STOP_FAILURE      9
 
 struct ble_gap_conn_desc {
+    uint8_t peer_addr[6];
     uint16_t conn_handle;
+    uint16_t conn_itvl;
+    uint16_t conn_latency;
+    uint16_t supervision_timeout;
     uint8_t peer_addr_type;
-    uint8_t peer_addr[6];
 };
 
-typedef void ble_gap_conn_fn(int event, int status,
-                             struct ble_gap_conn_desc *desc, void *arg);
+struct ble_gap_conn_params {
+    uint16_t itvl_min;
+    uint16_t itvl_max;
+    uint16_t latency;
+    uint16_t supervision_timeout;
+    uint16_t min_ce_len;
+    uint16_t max_ce_len;
+};
+
+struct ble_gap_conn_ctxt {
+    struct ble_gap_conn_desc desc;
+    struct ble_gap_conn_params *peer_params;
+    struct ble_gap_conn_params *self_params;
+};
+
+typedef int ble_gap_conn_fn(int event, int status,
+                            struct ble_gap_conn_ctxt *ctxt, void *arg);
 
 struct ble_gap_disc_desc {
     uint8_t event_type;
@@ -83,5 +104,7 @@ int ble_gap_conn_cancel(void);
 int ble_gap_conn_wl_set(struct ble_gap_white_entry *white_list,
                         uint8_t white_list_count, ble_gap_wl_fn *cb,
                         void *cb_arg);
+int ble_gap_conn_update_params(uint16_t conn_handle,
+                               struct ble_gap_conn_params *params);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/include/host/host_hci.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/host_hci.h b/net/nimble/host/include/host/host_hci.h
index b49e12a..fbf1b02 100644
--- a/net/nimble/host/include/host/host_hci.h
+++ b/net/nimble/host/include/host/host_hci.h
@@ -48,6 +48,8 @@ int host_hci_cmd_reset(void);
 int host_hci_cmd_read_adv_pwr(void);
 int host_hci_cmd_le_create_conn_cancel(void);
 int host_hci_cmd_le_conn_update(struct hci_conn_update *hcu);
+int host_hci_cmd_le_conn_param_reply(struct hci_conn_param_reply *hcr);
+int host_hci_cmd_le_conn_param_neg_reply(struct hci_conn_param_neg_reply *hcn);
 
 int host_hci_set_buf_size(uint16_t pktlen, uint8_t max_pkts);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/ble_att_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_priv.h b/net/nimble/host/src/ble_att_priv.h
index a644acb..9c04379 100644
--- a/net/nimble/host/src/ble_att_priv.h
+++ b/net/nimble/host/src/ble_att_priv.h
@@ -90,6 +90,7 @@ SLIST_HEAD(ble_att_clt_entry_list, ble_att_clt_entry);
 struct ble_l2cap_chan *ble_att_create_chan(void);
 void ble_att_set_peer_mtu(struct ble_l2cap_chan *chan, uint16_t peer_mtu);
 struct os_mbuf *ble_att_get_pkthdr(void);
+void ble_att_init(void);
 
 
 /*** @svr */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/ble_gap_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_conn.c b/net/nimble/host/src/ble_gap_conn.c
index 731bdcc..ad6c328 100644
--- a/net/nimble/host/src/ble_gap_conn.c
+++ b/net/nimble/host/src/ble_gap_conn.c
@@ -29,6 +29,7 @@
 #include "ble_gap_priv.h"
 
 #define BLE_GAP_CONN_OP_NULL                                0
+#define BLE_GAP_CONN_STATE_NULL                             255
 
 #define BLE_GAP_CONN_OP_M_DISC                              1
 #define BLE_GAP_CONN_OP_M_CONN                              2
@@ -66,6 +67,13 @@
 #define BLE_GAP_CONN_STATE_W_CLEAR                          0
 #define BLE_GAP_CONN_STATE_W_ADD                            1
 
+/** Connection update states. */
+#define BLE_GAP_CONN_STATE_U_UPDATE                         0
+#define BLE_GAP_CONN_STATE_U_UPDATE_ACKED                   1
+#define BLE_GAP_CONN_STATE_U_REPLY                          2
+#define BLE_GAP_CONN_STATE_U_REPLY_ACKED                    3
+#define BLE_GAP_CONN_STATE_U_NEG_REPLY                      4
+
 /** 30 ms. */
 #define BLE_GAP_ADV_FAST_INTERVAL1_MIN      (30 * 1000 / BLE_HCI_ADV_ITVL)
 
@@ -99,6 +107,12 @@
 /** 10.24 seconds. */
 #define BLE_GAP_GEN_DISC_SCAN_MIN           (10.24 * 1000)
 
+/** 1 second. */
+#define BLE_GAP_CONN_PAUSE_CENTRAL          (1 * 1000)
+
+/** 5 seconds. */
+#define BLE_GAP_CONN_PAUSE_PERIPHERAL       (5 * 1000)
+
 /**
  * The maximum amount of user data that can be put into the advertising data.
  * Six bytes are reserved at the end for the flags field and the transmit power
@@ -106,6 +120,8 @@
  */
 #define BLE_GAP_CONN_ADV_DATA_LIMIT         (BLE_HCI_MAX_ADV_DATA_LEN - 6)
 
+#define BLE_GAP_CONN_MAX_UPDATES            4
+
 /**
  * The state of the in-progress master connection.  If no master connection is
  * currently in progress, then the op field is set to BLE_GAP_CONN_OP_NULL.
@@ -161,6 +177,17 @@ static struct {
     uint8_t cur;
 } ble_gap_conn_wl;
 
+struct ble_gap_conn_update_entry {
+    SLIST_ENTRY(ble_gap_conn_update_entry) next;
+    struct ble_gap_conn_params params;
+    uint16_t conn_handle;
+    uint8_t state;
+};
+static SLIST_HEAD(, ble_gap_conn_update_entry) ble_gap_conn_update_entries;
+
+static os_membuf_t *ble_gap_conn_update_mem;
+static struct os_mempool ble_gap_conn_update_pool;
+
 static int ble_gap_conn_adv_params_tx(void *arg);
 static int ble_gap_conn_adv_power_tx(void *arg);
 static int ble_gap_conn_adv_data_tx(void *arg);
@@ -190,48 +217,105 @@ static struct os_callout_func ble_gap_conn_slave_timer;
  * $misc                                                                     *
  *****************************************************************************/
 
+static struct ble_gap_conn_update_entry *
+ble_gap_conn_update_entry_alloc(void)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    entry = os_memblock_get(&ble_gap_conn_update_pool);
+    if (entry == NULL) {
+        return NULL;
+    }
+
+    memset(entry, 0, sizeof *entry);
+    SLIST_INSERT_HEAD(&ble_gap_conn_update_entries, entry, next);
+
+    return entry;
+}
+
 static void
-ble_gap_conn_call_conn_cb(struct ble_hs_conn *conn, int event, int status)
+ble_gap_conn_update_entry_free(struct ble_gap_conn_update_entry *entry)
 {
-    struct ble_gap_conn_desc desc;
+    int rc;
+
+    rc = os_memblock_put(&ble_gap_conn_update_pool, entry);
+    assert(rc == 0);
+}
+
+static struct ble_gap_conn_update_entry *
+ble_gap_conn_update_find(uint16_t conn_handle)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    SLIST_FOREACH(entry, &ble_gap_conn_update_entries, next) {
+        if (entry->conn_handle == conn_handle) {
+            return entry;
+        }
+    }
+
+    return NULL;
+}
+
+static int
+ble_gap_conn_call_conn_cb(int event, int status, struct ble_hs_conn *conn,
+                          struct ble_gap_conn_params *self_params,
+                          struct ble_gap_conn_params *peer_params)
+{
+    struct ble_gap_conn_ctxt ctxt;
     ble_gap_conn_fn *cb;
     void *cb_arg;
+    int rc;
+
+    memset(&ctxt, 0, sizeof ctxt);
 
     if (conn != NULL) {
-        desc.conn_handle = conn->bhc_handle;
-        desc.peer_addr_type = conn->bhc_addr_type;
-        memcpy(desc.peer_addr, conn->bhc_addr, sizeof desc.peer_addr);
+        ctxt.desc.conn_handle = conn->bhc_handle;
+        ctxt.desc.peer_addr_type = conn->bhc_addr_type;
+        memcpy(ctxt.desc.peer_addr, conn->bhc_addr,
+               sizeof ctxt.desc.peer_addr);
+        ctxt.desc.conn_itvl = conn->bhc_itvl;
+        ctxt.desc.conn_latency = conn->bhc_latency;
+        ctxt.desc.supervision_timeout = conn->bhc_supervision_timeout;
+
         cb = conn->bhc_cb;
         cb_arg = conn->bhc_cb_arg;
     } else {
-        desc.conn_handle = BLE_HS_CONN_HANDLE_NONE;
-        desc.peer_addr_type = ble_gap_conn_master.conn.addr_type;
-        memcpy(desc.peer_addr, ble_gap_conn_master.conn.addr,
-               sizeof desc.peer_addr);
+        ctxt.desc.conn_handle = BLE_HS_CONN_HANDLE_NONE;
+        ctxt.desc.peer_addr_type = ble_gap_conn_master.conn.addr_type;
+        memcpy(ctxt.desc.peer_addr, ble_gap_conn_master.conn.addr,
+               sizeof ctxt.desc.peer_addr);
         cb = ble_gap_conn_master.conn.cb;
         cb_arg = ble_gap_conn_master.conn.cb_arg;
     }
 
+    ctxt.self_params = self_params;
+    ctxt.peer_params = peer_params;
+
     if (cb != NULL) {
-        cb(event, status, &desc, cb_arg);
+        rc = cb(event, status, &ctxt, cb_arg);
+    } else {
+        rc = 0;
     }
+
+    return rc;
 }
 
 static void
 ble_gap_conn_call_slave_cb(int event, int status)
 {
-    struct ble_gap_conn_desc desc;
+    struct ble_gap_conn_ctxt ctxt;
 
     if (ble_gap_conn_slave.cb == NULL) {
         return;
     }
 
-    desc.conn_handle = BLE_HS_CONN_HANDLE_NONE;
-    desc.peer_addr_type = ble_gap_conn_slave.dir_addr_type;
-    memcpy(desc.peer_addr, ble_gap_conn_slave.dir_addr,
-           sizeof desc.peer_addr);
+    memset(&ctxt, 0, sizeof ctxt);
+    ctxt.desc.conn_handle = BLE_HS_CONN_HANDLE_NONE;
+    ctxt.desc.peer_addr_type = ble_gap_conn_slave.dir_addr_type;
+    memcpy(ctxt.desc.peer_addr, ble_gap_conn_slave.dir_addr,
+           sizeof ctxt.desc.peer_addr);
 
-    ble_gap_conn_slave.cb(event, status, &desc, ble_gap_conn_slave.cb_arg);
+    ble_gap_conn_slave.cb(event, status, &ctxt, ble_gap_conn_slave.cb_arg);
 }
 
 /**
@@ -244,13 +328,14 @@ ble_gap_conn_call_slave_cb(int event, int status)
 static void
 ble_gap_conn_notify_connect(int status, struct ble_hs_conn *conn)
 {
-    ble_gap_conn_call_conn_cb(conn, BLE_GAP_EVENT_CONN, status);
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN, status, conn, NULL, NULL);
 }
 
 static void
 ble_gap_conn_notify_term_failure(int status, struct ble_hs_conn *conn)
 {
-    ble_gap_conn_call_conn_cb(conn, BLE_GAP_EVENT_TERM_FAILURE, status);
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_TERM_FAILURE, status, conn,
+                              NULL, NULL);
 }
 
 static void
@@ -278,13 +363,14 @@ ble_gap_conn_notify_disc(struct ble_hs_adv *adv,
 static void
 ble_gap_conn_notify_master_conn_failure(int status)
 {
-    ble_gap_conn_call_conn_cb(NULL, BLE_GAP_EVENT_CONN, status);
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN, status, NULL, NULL, NULL);
 }
 
 static void
 ble_gap_conn_notify_master_cancel_failure(int status)
 {
-    ble_gap_conn_call_conn_cb(NULL, BLE_GAP_EVENT_CANCEL_FAILURE, status);
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CANCEL_FAILURE, status, NULL,
+                              NULL, NULL);
 }
 
 static void
@@ -344,6 +430,20 @@ ble_gap_conn_notify_wl(int status)
 }
 
 static void
+ble_gap_conn_notify_update(struct ble_gap_conn_update_entry *entry, int status)
+{
+    struct ble_hs_conn *conn;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        return;
+    }
+
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN_UPDATED, status, NULL, NULL,
+                              NULL);
+}
+
+static void
 ble_gap_conn_master_reset_state(void)
 {
     os_callout_stop(&ble_gap_conn_master_timer.cf_c);
@@ -406,6 +506,21 @@ ble_gap_conn_wl_failed(int status)
     ble_gap_conn_notify_wl(status);
 }
 
+static void
+ble_gap_conn_update_entry_remove_free(struct ble_gap_conn_update_entry *entry)
+{
+    SLIST_REMOVE(&ble_gap_conn_update_entries, entry,
+                 ble_gap_conn_update_entry, next);
+    ble_gap_conn_update_entry_free(entry);
+}
+
+static void
+ble_gap_conn_update_failed(struct ble_gap_conn_update_entry *entry, int status)
+{
+    ble_gap_conn_notify_update(entry, status);
+    ble_gap_conn_update_entry_remove_free(entry);
+}
+
 void
 ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
 {
@@ -417,7 +532,8 @@ ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
         if (conn == NULL) {
             return;
         }
-        ble_gap_conn_call_conn_cb(conn, BLE_GAP_EVENT_CONN, BLE_HS_ENOTCONN);
+        ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN, BLE_HS_ENOTCONN, conn,
+                                  NULL, NULL);
         ble_hs_conn_remove(conn);
         ble_hs_conn_free(conn);
     } else {
@@ -428,6 +544,26 @@ ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
     ble_gattc_connection_broken(evt->connection_handle);
 }
 
+void
+ble_gap_conn_rx_update_complete(struct hci_le_conn_upd_complete *evt)
+{
+    struct ble_hs_conn *conn;
+
+    conn = ble_hs_conn_find(evt->connection_handle);
+    if (conn == NULL) {
+        return;
+    }
+
+    if (evt->status == 0) {
+        conn->bhc_itvl = evt->conn_itvl;
+        conn->bhc_latency = evt->conn_latency;
+        conn->bhc_supervision_timeout = evt->supervision_timeout;
+    }
+
+    ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN_UPDATED,
+                              BLE_HS_HCI_ERR(evt->status), conn, NULL, NULL);
+}
+
 /**
  * Tells you if the BLE host is in the process of creating a master connection.
  */
@@ -698,7 +834,11 @@ ble_gap_conn_rx_conn_complete(struct hci_le_conn_complete *evt)
 
     conn->bhc_handle = evt->connection_handle;
     memcpy(conn->bhc_addr, evt->peer_addr, sizeof conn->bhc_addr);
+    conn->bhc_itvl = evt->conn_itvl;
+    conn->bhc_latency = evt->conn_latency;
+    conn->bhc_supervision_timeout = evt->supervision_timeout;
     if (evt->role == BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER) {
+        conn->bhc_flags |= BLE_HS_CONN_F_MASTER;
         conn->bhc_cb = ble_gap_conn_master.conn.cb;
         conn->bhc_cb_arg = ble_gap_conn_master.conn.cb_arg;
         ble_gap_conn_master_reset_state();
@@ -1663,6 +1803,189 @@ ble_gap_conn_cancel(void)
 }
 
 /*****************************************************************************
+ * $update connection parameters                                             *
+ *****************************************************************************/
+
+static void
+ble_gap_conn_param_neg_reply_ack(struct ble_hci_ack *ack, void *arg)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    entry = arg;
+
+    assert(entry->state == BLE_GAP_CONN_STATE_U_NEG_REPLY);
+    ble_gap_conn_update_entry_remove_free(entry);
+}
+
+static void
+ble_gap_conn_param_reply_ack(struct ble_hci_ack *ack, void *arg)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    entry = arg;
+
+    assert(entry->state == BLE_GAP_CONN_STATE_U_REPLY);
+
+    if (ack->bha_status != 0) {
+        ble_gap_conn_update_failed(entry, ack->bha_status);
+    } else {
+        entry->state = BLE_GAP_CONN_STATE_U_REPLY_ACKED;
+    }
+}
+
+void
+ble_gap_conn_rx_param_req(struct hci_le_conn_param_req *evt)
+{
+    struct ble_gap_conn_update_entry *entry;
+    struct hci_conn_param_neg_reply neg_reply;
+    struct hci_conn_param_reply pos_reply;
+    struct ble_gap_conn_params peer_params;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    entry = ble_gap_conn_update_find(evt->connection_handle);
+    if (entry != NULL) {
+        /* Parameter update already in progress; reject peer's attempt. */
+        /* XXX: Is this antisocial? */
+        rc = BLE_ERR_UNSPECIFIED;
+        goto err;
+    }
+
+    conn = ble_hs_conn_find(evt->connection_handle);
+    if (conn == NULL) {
+        return;
+    }
+
+    entry = ble_gap_conn_update_entry_alloc();
+    if (entry == NULL) {
+        /* Out of memory; reject. */
+        rc = BLE_ERR_MEM_CAPACITY;
+        goto err;
+    }
+
+    peer_params.itvl_min = evt->itvl_min;
+    peer_params.itvl_max = evt->itvl_max;
+    peer_params.latency = evt->latency;
+    peer_params.supervision_timeout = evt->timeout;
+    peer_params.min_ce_len = 0;
+    peer_params.max_ce_len = 0;
+
+    rc = ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN_UPDATE_REQ, 0, conn,
+                                   &entry->params, &peer_params);
+    if (rc != 0) {
+        goto err;
+    }
+
+    pos_reply.handle = entry->conn_handle;
+    pos_reply.conn_itvl_min = entry->params.itvl_min;
+    pos_reply.conn_itvl_max = entry->params.itvl_max;
+    pos_reply.conn_latency = entry->params.latency;
+    pos_reply.supervision_timeout = entry->params.supervision_timeout;
+    pos_reply.min_ce_len = entry->params.min_ce_len;
+    pos_reply.max_ce_len = entry->params.max_ce_len;
+
+    ble_hci_ack_set_callback(ble_gap_conn_param_reply_ack, entry);
+
+    rc = host_hci_cmd_le_conn_param_reply(&pos_reply);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return;
+
+err:
+    ble_gap_conn_update_failed(entry, rc);
+
+    neg_reply.handle = evt->connection_handle;
+    neg_reply.reason = rc;
+
+    ble_hci_ack_set_callback(ble_gap_conn_param_neg_reply_ack, entry);
+
+    host_hci_cmd_le_conn_param_neg_reply(&neg_reply);
+}
+
+static void
+ble_gap_conn_update_ack(struct ble_hci_ack *ack, void *arg)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    entry = arg;
+
+    assert(entry->state == BLE_GAP_CONN_STATE_U_UPDATE);
+
+    if (ack->bha_status != 0) {
+        ble_gap_conn_update_failed(entry, ack->bha_status);
+        return;
+    }
+
+    entry->state = BLE_GAP_CONN_STATE_U_UPDATE_ACKED;
+}
+
+static int
+ble_gap_conn_update_tx(void *arg)
+{
+    struct ble_gap_conn_update_entry *entry;
+    struct hci_conn_update cmd;
+    int rc;
+
+    entry = arg;
+
+    assert(entry->state == BLE_GAP_CONN_STATE_U_UPDATE);
+
+    cmd.handle = entry->conn_handle;
+    cmd.conn_itvl_min = entry->params.itvl_min;
+    cmd.conn_itvl_max = entry->params.itvl_max;
+    cmd.conn_latency = entry->params.latency;
+    cmd.supervision_timeout = entry->params.supervision_timeout;
+    cmd.min_ce_len = entry->params.min_ce_len;
+    cmd.max_ce_len = entry->params.max_ce_len;
+
+    ble_hci_ack_set_callback(ble_gap_conn_update_ack, entry);
+
+    rc = host_hci_cmd_le_conn_update(&cmd);
+    if (rc != 0) {
+        ble_gap_conn_update_failed(entry, rc);
+    }
+
+    return 0;
+}
+
+int
+ble_gap_conn_update_params(uint16_t conn_handle,
+                           struct ble_gap_conn_params *params)
+{
+    struct ble_gap_conn_update_entry *entry;
+    int rc;
+
+    entry = ble_gap_conn_update_find(conn_handle);
+    if (entry != NULL) {
+        return BLE_HS_EALREADY;
+    }
+
+    if (ble_hs_conn_find(conn_handle) == NULL) {
+        return BLE_HS_ENOENT;
+    }
+
+    entry = ble_gap_conn_update_entry_alloc();
+    if (entry == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    entry->conn_handle = conn_handle;
+    entry->params = *params;
+    entry->state = BLE_GAP_CONN_STATE_U_UPDATE;
+
+    rc = ble_hci_sched_enqueue(ble_gap_conn_update_tx, entry);
+    if (rc != 0) {
+        SLIST_REMOVE(&ble_gap_conn_update_entries, entry,
+                     ble_gap_conn_update_entry, next);
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
  * $init                                                                     *
  *****************************************************************************/
 
@@ -1680,9 +2003,20 @@ ble_gap_conn_init_slave_params(void)
     };
 }
 
+static void
+ble_gap_conn_free_mem(void)
+{
+    free(ble_gap_conn_update_mem);
+    ble_gap_conn_update_mem = NULL;
+}
+
 int
 ble_gap_conn_init(void)
 {
+    int rc;
+
+    ble_gap_conn_free_mem();
+
     ble_gap_conn_master.op = BLE_GAP_CONN_OP_NULL;
     ble_gap_conn_slave.op = BLE_GAP_CONN_OP_NULL;
     ble_gap_conn_wl.op = BLE_GAP_CONN_OP_NULL;
@@ -1693,5 +2027,26 @@ ble_gap_conn_init(void)
                          ble_gap_conn_master_timer_exp, NULL);
     os_callout_func_init(&ble_gap_conn_slave_timer, &ble_hs_evq,
                          ble_gap_conn_slave_timer_exp, NULL);
+
+    ble_gap_conn_update_mem = malloc(
+        OS_MEMPOOL_BYTES(BLE_GAP_CONN_MAX_UPDATES,
+                         sizeof (struct ble_gap_conn_update_entry)));
+    if (ble_gap_conn_update_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+    rc = os_mempool_init(&ble_gap_conn_update_pool, BLE_GAP_CONN_MAX_UPDATES,
+                         sizeof (struct ble_gap_conn_update_entry),
+                         ble_gap_conn_update_mem, "ble_gap_conn_update_pool");
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    SLIST_INIT(&ble_gap_conn_update_entries);
     return 0;
+
+err:
+    ble_gap_conn_free_mem();
+    return rc;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index 8c8a6f2..8de65f4 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 #include "host/ble_gap.h"
+struct hci_le_conn_upd_complete;
 struct hci_le_conn_complete;
 struct hci_disconn_complete;
 struct ble_hci_ack;
@@ -30,6 +31,7 @@ struct ble_hs_adv;
 void ble_gap_conn_rx_adv_report(struct ble_hs_adv *adv);
 int ble_gap_conn_rx_conn_complete(struct hci_le_conn_complete *evt);
 void ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt);
+void ble_gap_conn_rx_update_complete(struct hci_le_conn_upd_complete *evt);
 int ble_gap_conn_master_in_progress(void);
 int ble_gap_conn_slave_in_progress(void);
 int ble_gap_conn_wl_busy(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/ble_hs_conn.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.h b/net/nimble/host/src/ble_hs_conn.h
index d08713a..a97d160 100644
--- a/net/nimble/host/src/ble_hs_conn.h
+++ b/net/nimble/host/src/ble_hs_conn.h
@@ -26,12 +26,22 @@ struct hci_le_conn_complete;
 struct hci_create_conn;
 struct ble_l2cap_chan;
 
+typedef uint8_t ble_hs_conn_flags;
+
+#define BLE_HS_CONN_F_MASTER        0x01
+
 struct ble_hs_conn {
     SLIST_ENTRY(ble_hs_conn) bhc_next;
     uint16_t bhc_handle;
     uint8_t bhc_addr_type;
     uint8_t bhc_addr[6];
 
+    uint16_t bhc_itvl;
+    uint16_t bhc_latency;
+    uint16_t bhc_supervision_timeout;
+
+    ble_hs_conn_flags bhc_flags;
+
     struct ble_l2cap_chan_list bhc_channels;
     uint16_t bhc_outstanding_pkts;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/host_hci_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/host_hci_cmd.c b/net/nimble/host/src/host_hci_cmd.c
index 5ddc570..521639c 100644
--- a/net/nimble/host/src/host_hci_cmd.c
+++ b/net/nimble/host/src/host_hci_cmd.c
@@ -559,3 +559,36 @@ host_hci_cmd_le_conn_update(struct hci_conn_update *hcu)
     return rc;
 }
 
+int
+host_hci_cmd_le_conn_param_reply(struct hci_conn_param_reply *hcr)
+{
+    uint8_t cmd[BLE_HCI_CONN_PARAM_REPLY_LEN];
+    int rc;
+
+    htole16(cmd + 0, hcr->handle);
+    htole16(cmd + 2, hcr->conn_itvl_min);
+    htole16(cmd + 4, hcr->conn_itvl_max);
+    htole16(cmd + 6, hcr->conn_latency);
+    htole16(cmd + 8, hcr->supervision_timeout);
+    htole16(cmd + 10, hcr->min_ce_len);
+    htole16(cmd + 12, hcr->max_ce_len);
+
+    rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_REM_CONN_PARAM_RR,
+                              BLE_HCI_CONN_PARAM_REPLY_LEN, cmd);
+    return rc;
+}
+
+int
+host_hci_cmd_le_conn_param_neg_reply(struct hci_conn_param_neg_reply *hcn)
+{
+    uint8_t cmd[BLE_HCI_CONN_PARAM_NEG_REPLY_LEN];
+    int rc;
+
+    htole16(cmd + 0, hcn->handle);
+    cmd[2] = hcn->reason;
+
+    rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR,
+                              BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, cmd);
+    return rc;
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/test/ble_gap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gap_test.c b/net/nimble/host/src/test/ble_gap_test.c
index a64e257..a2cb9ce 100644
--- a/net/nimble/host/src/test/ble_gap_test.c
+++ b/net/nimble/host/src/test/ble_gap_test.c
@@ -81,14 +81,16 @@ ble_gap_test_util_disc_cb(int event, int status,
     ble_gap_test_disc_arg = arg;
 }
 
-static void
+static int
 ble_gap_test_util_connect_cb(int event, int status,
-                             struct ble_gap_conn_desc *desc, void *arg)
+                             struct ble_gap_conn_ctxt *ctxt, void *arg)
 {
     ble_gap_test_conn_event = event;
     ble_gap_test_conn_status = status;
-    ble_gap_test_conn_desc = *desc;
+    ble_gap_test_conn_desc = ctxt->desc;
     ble_gap_test_conn_arg = arg;
+
+    return 0;
 }
 
 static int
@@ -395,7 +397,7 @@ TEST_CASE(ble_gap_test_case_conn_wl_bad_args)
 
     /*** White-list-using connection in progress. */
     rc = ble_gap_conn_initiate(BLE_GAP_ADDR_TYPE_WL, NULL,
-                                     ble_gap_test_util_connect_cb, NULL);
+                               ble_gap_test_util_connect_cb, NULL);
     TEST_ASSERT(rc == 0);
     TEST_ASSERT(ble_gap_conn_wl_busy());
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/net/nimble/host/src/test/ble_os_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_os_test.c b/net/nimble/host/src/test/ble_os_test.c
index b03ce71..d6862d9 100644
--- a/net/nimble/host/src/test/ble_os_test.c
+++ b/net/nimble/host/src/test/ble_os_test.c
@@ -63,9 +63,9 @@ ble_os_test_misc_rx_le_ack(uint16_t ocf, uint8_t status)
     ble_os_test_misc_rx_ack(BLE_HCI_OGF_LE, ocf, status);
 }
 
-static void
+static int
 ble_gap_direct_connect_test_connect_cb(int event, int status,
-                                       struct ble_gap_conn_desc *desc,
+                                       struct ble_gap_conn_ctxt *ctxt,
                                        void *arg)
 {
     int *cb_called;
@@ -75,9 +75,11 @@ ble_gap_direct_connect_test_connect_cb(int event, int status,
 
     TEST_ASSERT(event == BLE_GAP_EVENT_CONN);
     TEST_ASSERT(status == 0);
-    TEST_ASSERT(desc->conn_handle == 2);
-    TEST_ASSERT(desc->peer_addr_type == BLE_ADDR_TYPE_PUBLIC);
-    TEST_ASSERT(memcmp(desc->peer_addr, ble_os_test_peer_addr, 6) == 0);
+    TEST_ASSERT(ctxt->desc.conn_handle == 2);
+    TEST_ASSERT(ctxt->desc.peer_addr_type == BLE_ADDR_TYPE_PUBLIC);
+    TEST_ASSERT(memcmp(ctxt->desc.peer_addr, ble_os_test_peer_addr, 6) == 0);
+
+    return 0;
 }
 
 static void
@@ -104,9 +106,8 @@ ble_gap_direct_connect_test_task_handler(void *arg)
     TEST_ASSERT(ble_hs_conn_first() == NULL);
 
     /* Initiate a direct connection. */
-    ble_gap_conn_initiate(0, addr,
-                                ble_gap_direct_connect_test_connect_cb,
-                                &cb_called);
+    ble_gap_conn_initiate(0, addr, ble_gap_direct_connect_test_connect_cb,
+                          &cb_called);
     TEST_ASSERT(ble_hs_conn_first() == NULL);
     TEST_ASSERT(!cb_called);
 
@@ -224,19 +225,21 @@ TEST_CASE(ble_gap_gen_disc_test_case)
     os_start();
 }
 
-static void
+static int
 ble_gap_terminate_cb(int event, int status,
-                     struct ble_gap_conn_desc *desc, void *arg)
+                     struct ble_gap_conn_ctxt *ctxt, void *arg)
 {
     int *disconn_handle;
 
     TEST_ASSERT_FATAL(event == BLE_GAP_EVENT_CONN);
     if (status == 0) {
-        return;
+        return 0;
     }
 
     disconn_handle = arg;
-    *disconn_handle = desc->conn_handle;
+    *disconn_handle = ctxt->desc.conn_handle;
+
+    return 0;
 }
 
 
@@ -267,8 +270,7 @@ ble_gap_terminate_test_task_handler(void *arg)
     TEST_ASSERT(!ble_gap_conn_master_in_progress());
 
     /* Create two direct connections. */
-    ble_gap_conn_initiate(0, addr1, ble_gap_terminate_cb,
-                                &disconn_handle);
+    ble_gap_conn_initiate(0, addr1, ble_gap_terminate_cb, &disconn_handle);
     ble_os_test_misc_rx_le_ack(BLE_HCI_OCF_LE_CREATE_CONN, 0);
     memset(&conn_evt, 0, sizeof conn_evt);
     conn_evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/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 1513cf6..528fbeb 100644
--- a/net/nimble/include/nimble/hci_common.h
+++ b/net/nimble/include/nimble/hci_common.h
@@ -260,6 +260,12 @@
 /* --- LE connection update (OCF 0x0013) */
 #define BLE_HCI_CONN_UPDATE_LEN             (14)
 
+/* --- LE remote connection parameter request reply (OCF 0x0020) */
+#define BLE_HCI_CONN_PARAM_REPLY_LEN        (14)
+
+/* --- LE remote connection parameter request negative reply (OCF 0x0021) */
+#define BLE_HCI_CONN_PARAM_NEG_REPLY_LEN    (3)
+
 /* Event Codes */
 #define BLE_HCI_EVCODE_INQUIRY_CMP          (0x01)
 #define BLE_HCI_EVCODE_INQUIRY_RESULT       (0x02)
@@ -414,7 +420,7 @@ struct hci_adv_params
     uint8_t peer_addr[BLE_DEV_ADDR_LEN];
 };
 
-/* Create connection command */
+/* LE create connection command */
 struct hci_create_conn
 {
     uint16_t scan_itvl;
@@ -431,7 +437,7 @@ struct hci_create_conn
     uint16_t max_ce_len;
 };
 
-/* Connection update command */
+/* LE connection update command */
 struct hci_conn_update
 {
     uint16_t handle;
@@ -443,6 +449,25 @@ struct hci_conn_update
     uint16_t max_ce_len;
 };
 
+/* LE Remote connection parameter request reply command */
+struct hci_conn_param_reply
+{
+    uint16_t handle;
+    uint16_t conn_itvl_min;
+    uint16_t conn_itvl_max;
+    uint16_t conn_latency;
+    uint16_t supervision_timeout;
+    uint16_t min_ce_len;
+    uint16_t max_ce_len;
+};
+
+/* LE Remote connection parameter request negative reply command */
+struct hci_conn_param_neg_reply
+{
+    uint16_t handle;
+    uint8_t reason;
+};
+
 /* Connection complete LE meta subevent */
 struct hci_le_conn_complete
 {
@@ -458,6 +483,28 @@ struct hci_le_conn_complete
     uint8_t master_clk_acc;
 };
 
+/* Connection update complete LE meta subevent */
+struct hci_le_conn_upd_complete
+{
+    uint8_t subevent_code;
+    uint8_t status;
+    uint16_t connection_handle;
+    uint16_t conn_itvl;
+    uint16_t conn_latency;
+    uint16_t supervision_timeout;
+};
+
+/* Remote connection parameter request LE meta subevent */
+struct hci_le_conn_param_req
+{
+    uint8_t subevent_code;
+    uint16_t connection_handle;
+    uint16_t itvl_min;
+    uint16_t itvl_max;
+    uint16_t latency;
+    uint16_t timeout;
+};
+
 /* Disconnection complete event (note: fields out of order). */
 struct hci_disconn_complete
 {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/project/bleshell/src/main.c
----------------------------------------------------------------------
diff --git a/project/bleshell/src/main.c b/project/bleshell/src/main.c
index 8af8e1f..46ea49b 100755
--- a/project/bleshell/src/main.c
+++ b/project/bleshell/src/main.c
@@ -613,8 +613,8 @@ bleshell_on_notify(uint16_t conn_handle, uint16_t attr_handle,
     return 0;
 }
 
-static void
-bleshell_on_connect(int event, int status, struct ble_gap_conn_desc *desc,
+static int
+bleshell_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
                     void *arg)
 {
     int conn_idx;
@@ -623,20 +623,20 @@ bleshell_on_connect(int event, int status, struct ble_gap_conn_desc *desc,
     case BLE_GAP_EVENT_CONN:
         console_printf("connection complete; handle=%d status=%d "
                        "peer_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
-                       desc->conn_handle, status,
-                       desc->peer_addr[0], desc->peer_addr[1],
-                       desc->peer_addr[2], desc->peer_addr[3],
-                       desc->peer_addr[4], desc->peer_addr[5]);
+                       ctxt->desc.conn_handle, status,
+                       ctxt->desc.peer_addr[0], ctxt->desc.peer_addr[1],
+                       ctxt->desc.peer_addr[2], ctxt->desc.peer_addr[3],
+                       ctxt->desc.peer_addr[4], ctxt->desc.peer_addr[5]);
 
         if (status == 0) {
-            bleshell_conn_add(desc);
+            bleshell_conn_add(&ctxt->desc);
         } else {
-            if (desc->conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+            if (ctxt->desc.conn_handle == BLE_HS_CONN_HANDLE_NONE) {
                 if (status == BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID)) {
                     console_printf("connection procedure cancelled.\n");
                 }
             } else {
-                conn_idx = bleshell_conn_find_idx(desc->conn_handle);
+                conn_idx = bleshell_conn_find_idx(ctxt->desc.conn_handle);
                 if (conn_idx == -1) {
                     console_printf("UNKNOWN CONNECTION\n");
                 } else {
@@ -647,6 +647,8 @@ bleshell_on_connect(int event, int status, struct ble_gap_conn_desc *desc,
 
         break;
     }
+
+    return 0;
 }
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/project/centtest/src/main.c
----------------------------------------------------------------------
diff --git a/project/centtest/src/main.c b/project/centtest/src/main.c
index fc86a3c..4134b19 100755
--- a/project/centtest/src/main.c
+++ b/project/centtest/src/main.c
@@ -179,26 +179,28 @@ centtest_on_disc_s(uint16_t conn_handle, struct ble_gatt_error *error,
     return 0;
 }
 
-static void
-centtest_on_connect(int event, int status, struct ble_gap_conn_desc *desc,
+static int
+centtest_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
                     void *arg)
 {
     switch (event) {
     case BLE_GAP_EVENT_CONN:
         console_printf("connection complete; handle=%d status=%d "
                        "peer_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
-                       desc->conn_handle, status,
-                       desc->peer_addr[0], desc->peer_addr[1],
-                       desc->peer_addr[2], desc->peer_addr[3],
-                       desc->peer_addr[4], desc->peer_addr[5]);
+                       ctxt->desc.conn_handle, status,
+                       ctxt->desc.peer_addr[0], ctxt->desc.peer_addr[1],
+                       ctxt->desc.peer_addr[2], ctxt->desc.peer_addr[3],
+                       ctxt->desc.peer_addr[4], ctxt->desc.peer_addr[5]);
 
         if (status == 0) {
-            ble_gattc_disc_all_svcs(desc->conn_handle,
+            ble_gattc_disc_all_svcs(ctxt->desc.conn_handle,
                                     centtest_on_disc_s, NULL);
         }
 
         break;
     }
+
+    return 0;
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dd338e9e/project/prphtest/src/main.c
----------------------------------------------------------------------
diff --git a/project/prphtest/src/main.c b/project/prphtest/src/main.c
index aa9035a..b66fb6b 100755
--- a/project/prphtest/src/main.c
+++ b/project/prphtest/src/main.c
@@ -195,24 +195,26 @@ prphtest_register_attrs(void)
     assert(rc == 0);
 }
 
-static void
-prphtest_on_connect(int event, int status, struct ble_gap_conn_desc *desc,
+static int
+prphtest_on_connect(int event, int status, struct ble_gap_conn_ctxt *ctxt,
                     void *arg)
 {
     switch (event) {
     case BLE_GAP_EVENT_CONN:
         console_printf("connection event; handle=%d status=%d "
                        "peer_addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
-                       desc->conn_handle, status,
-                       desc->peer_addr[0], desc->peer_addr[1],
-                       desc->peer_addr[2], desc->peer_addr[3],
-                       desc->peer_addr[4], desc->peer_addr[5]);
+                       ctxt->desc.conn_handle, status,
+                       ctxt->desc.peer_addr[0], ctxt->desc.peer_addr[1],
+                       ctxt->desc.peer_addr[2], ctxt->desc.peer_addr[3],
+                       ctxt->desc.peer_addr[4], ctxt->desc.peer_addr[5]);
         break;
 
     default:
         console_printf("unexpected connection event; type=%d\n", event);
         break;
     }
+
+    return 0;
 }
 
 /**