diff --git a/.travis.yml b/.travis.yml
index 285b81e5d0..185ca35507 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,9 +26,10 @@ _addons: &addon_conf
     packages:
       - gcc-multilib
       - gcc-7-multilib
+      - g++-multilib
 
 go:
-  - "1.12"
+  - "1.16"
 
 git:
   depth: false
diff --git a/NOTICE b/NOTICE
index 84ecb14d9f..90952ecc53 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Mynewt NimBLE
-Copyright 2015-2020 The Apache Software Foundation
+Copyright 2015-2021 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
index 37103be08e..96c636634c 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ Features highlight:
   - Support for up to 32 simultaneous connections.
   - Legacy and SC (secure connections) SMP support (pairing and bonding).
   - Advertising Extensions.
+  - Periodic Advertising.
   - Coded (aka Long Range) and 2M PHYs.
   - Bluetooth Mesh.
 
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 3bdd31ad05..99810a718d 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,6 @@
 # RELEASE NOTES
 
-18 March 2020 - Apache NimBLE v1.3.0
+24 March 2021 - Apache NimBLE v1.4.0
 
 For full release notes, please visit the
 [Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes).
@@ -10,23 +10,12 @@ replaces the proprietary SoftDevice on Nordic chipsets.
 
 New features in this version of NimBLE include:
 
-* Support for Bluetooth Core Specification 5.1
-* New blestress test application
-* Dialog DA1469x CMAC driver
-* Support for LE Secure Connections out-of-band (OOB) association model
-* Support for automated generation of syscfg for ports
-* Qualification related bugfixes
-* BLE Mesh improvements - fixes and resync with latest Zephyr code
-* RIOT OS port fixes and improvements
-* btshell sample application improvements
-* improvements for bttester application
-* Controller duplicates filtering improvements
-* Multi PHY support improvements
-* Memory and CPU usage optimizations
-* Use of packed structs for HCI (code size reduction)
-* Linux sample improvements
-* PTS test instructions updates
-* Clock managements improvements in controller
+* Support for PHY on Dialog Configurable MAC (CMAC)
+* Support for PHY on Nordic nRF5340
+* Support for Apache NuttX port of NimBLE
+* Controller-to-host flow control support
+* Support for USB transport
+* Various bugfixes
 
 If working on next-generation RTOS and Bluetooth protocol stack
 sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt
diff --git a/apps/blecsc/pkg.yml b/apps/blecsc/pkg.yml
index 828ff301ee..a2119c1b35 100644
--- a/apps/blecsc/pkg.yml
+++ b/apps/blecsc/pkg.yml
@@ -32,7 +32,6 @@ pkg.deps:
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/sysinit"
     - "@apache-mynewt-core/sys/id"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
diff --git a/apps/blecsc/src/main.c b/apps/blecsc/src/main.c
index 60f0b3d8f7..5cca6b77ec 100644
--- a/apps/blecsc/src/main.c
+++ b/apps/blecsc/src/main.c
@@ -58,7 +58,7 @@ static struct os_callout blecsc_measure_timer;
 /* Variable holds current CSC measurement state */
 static struct ble_csc_measurement_state csc_measurement_state;
 
-/* Variable holds simulted speed (kilometers per hour) */
+/* Variable holds simulated speed (kilometers per hour) */
 static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
 
 /* Variable holds simulated cadence (RPM) */
diff --git a/apps/blecsc/syscfg.yml b/apps/blecsc/syscfg.yml
index ecf4b25f15..abf899691d 100644
--- a/apps/blecsc/syscfg.yml
+++ b/apps/blecsc/syscfg.yml
@@ -32,7 +32,7 @@ syscfg.vals:
     CONFIG_FCB: 1
 
     # Set public device address.
-    BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
+    BLE_LL_PUBLIC_DEV_ADDR: 0x1122aabb33cc
 
     # Set device appearance to Cycling Speed and Cadence Sensor
     BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR
diff --git a/apps/blehcibridge/pkg.yml b/apps/blehcibridge/pkg.yml
new file mode 100644
index 0000000000..9d170a89f3
--- /dev/null
+++ b/apps/blehcibridge/pkg.yml
@@ -0,0 +1,40 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: apps/blehcibridge
+pkg.type: app
+pkg.description: BLE controller application exposing HCI over external interface
+pkg.author: "Jerzy Kasenberg <jerzy.kasenberg@codecoup.pl>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/log/stub"
+    - "@apache-mynewt-core/sys/stats/full"
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/sys/shell"
+    - nimble/transport
+
+pkg.req_apis:
+    - ble_transport
+
+pkg.deps.'CONSOLE_MODE=="full"':
+    - "@apache-mynewt-core/sys/console/full"
+pkg.deps.'CONSOLE_MODE=="minimal":
+    - "@apache-mynewt-core/sys/console/minimal"
+pkg.deps.'CONSOLE_MODE=="stub":
+    - "@apache-mynewt-core/sys/console/stub"
diff --git a/apps/blehcibridge/src/main.c b/apps/blehcibridge/src/main.c
new file mode 100644
index 0000000000..620cc37121
--- /dev/null
+++ b/apps/blehcibridge/src/main.c
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <os/mynewt.h>
+#include <nimble/ble_hci_trans.h>
+
+static int
+forward_cmd_to_controller(uint8_t *cmdbuf, void *arg)
+{
+    (void)arg;
+
+    return ble_hci_trans_hs_cmd_tx(cmdbuf);
+}
+
+int
+forward_acl_to_controller(struct os_mbuf *om, void *arg)
+{
+    (void)arg;
+
+    return ble_hci_trans_hs_acl_tx(om);
+}
+
+static int
+forward_evt_to_host(uint8_t *hci_ev, void *arg)
+{
+    (void)arg;
+
+    return ble_hci_trans_ll_evt_tx(hci_ev);
+}
+
+int
+forward_acl_to_host(struct os_mbuf *om, void *arg)
+{
+    (void)arg;
+
+    return ble_hci_trans_ll_acl_tx(om);
+}
+
+int
+main(void)
+{
+    /* Initialize OS */
+    sysinit();
+
+    ble_hci_trans_cfg_hs(forward_evt_to_host, NULL, forward_acl_to_host, NULL);
+    ble_hci_trans_cfg_ll(forward_cmd_to_controller, NULL, forward_acl_to_controller, NULL);
+
+    while (1) {
+        os_eventq_run(os_eventq_dflt_get());
+    }
+    return 0;
+}
diff --git a/apps/blehcibridge/syscfg.yml b/apps/blehcibridge/syscfg.yml
new file mode 100644
index 0000000000..146ab52642
--- /dev/null
+++ b/apps/blehcibridge/syscfg.yml
@@ -0,0 +1,36 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    CONSOLE_MODE:
+        description: Which console to use
+        value: stub
+        choices:
+            - full
+            - minimal
+            - stub
+
+syscfg.vals:
+    # Default task settings
+    OS_MAIN_STACK_SIZE: 64
+    # Use USB transport by default
+    BLE_HCI_TRANSPORT: usb
+
+    SHELL_TASK: 1
+
+    BLE_HCI_BRIDGE: 1
diff --git a/apps/blehr/pkg.yml b/apps/blehr/pkg.yml
index b91ba3775b..8212f8f6a2 100644
--- a/apps/blehr/pkg.yml
+++ b/apps/blehr/pkg.yml
@@ -32,9 +32,8 @@ pkg.deps:
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/sysinit"
     - "@apache-mynewt-core/sys/id"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/blehr/syscfg.yml b/apps/blehr/syscfg.yml
index 5f047041e1..98cf255428 100644
--- a/apps/blehr/syscfg.yml
+++ b/apps/blehr/syscfg.yml
@@ -32,7 +32,7 @@ syscfg.vals:
     CONFIG_FCB: 1
 
     # Set public device address.
-    BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
+    BLE_LL_PUBLIC_DEV_ADDR: 0x1122aabb33cc
 
     # Whether to save data to sys/config, or just keep it in RAM.
     BLE_STORE_CONFIG_PERSIST: 0
diff --git a/apps/blemesh/pkg.yml b/apps/blemesh/pkg.yml
index c70565423c..afe419b57b 100644
--- a/apps/blemesh/pkg.yml
+++ b/apps/blemesh/pkg.yml
@@ -29,9 +29,8 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/modlog"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/shell"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/blemesh/src/main.c b/apps/blemesh/src/main.c
index 65270554b3..56d0476f86 100644
--- a/apps/blemesh/src/main.c
+++ b/apps/blemesh/src/main.c
@@ -144,11 +144,12 @@ static struct bt_mesh_model_pub gen_onoff_pub;
 static uint8_t gen_on_off_state;
 static int16_t gen_level_state;
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
                              struct bt_mesh_msg_ctx *ctx)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(3);
     uint8_t *status;
+    int rc;
 
     console_printf("#mesh-onoff STATUS\n");
 
@@ -156,23 +157,25 @@ static void gen_onoff_status(struct bt_mesh_model *model,
     status = net_buf_simple_add(msg, 1);
     *status = gen_on_off_state;
 
-    if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+    if (rc) {
         console_printf("#mesh-onoff STATUS: send status failed\n");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
     console_printf("#mesh-onoff GET\n");
 
-    gen_onoff_status(model, ctx);
+    return gen_onoff_status(model, ctx);
 }
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
@@ -181,10 +184,10 @@ static void gen_onoff_set(struct bt_mesh_model *model,
     gen_on_off_state = buf->om_data[0];
     hal_gpio_write(LED_2, !gen_on_off_state);
 
-    gen_onoff_status(model, ctx);
+    return gen_onoff_status(model, ctx);
 }
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
@@ -192,6 +195,7 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 
     gen_on_off_state = buf->om_data[0];
     hal_gpio_write(LED_2, !gen_on_off_state);
+    return 0;
 }
 
 static const struct bt_mesh_model_op gen_onoff_op[] = {
@@ -201,48 +205,56 @@ static const struct bt_mesh_model_op gen_onoff_op[] = {
     BT_MESH_MODEL_OP_END,
 };
 
-static void gen_level_status(struct bt_mesh_model *model,
+static int gen_level_status(struct bt_mesh_model *model,
                              struct bt_mesh_msg_ctx *ctx)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(4);
+    int rc;
 
     console_printf("#mesh-level STATUS\n");
 
     bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
     net_buf_simple_add_le16(msg, gen_level_state);
 
-    if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+    if (rc) {
         console_printf("#mesh-level STATUS: send status failed\n");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void gen_level_get(struct bt_mesh_model *model,
+static int gen_level_get(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
     console_printf("#mesh-level GET\n");
 
-    gen_level_status(model, ctx);
+    return gen_level_status(model, ctx);
 }
 
-static void gen_level_set(struct bt_mesh_model *model,
+static int gen_level_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
     int16_t level;
+    int rc;
 
     level = (int16_t) net_buf_simple_pull_le16(buf);
     console_printf("#mesh-level SET: level=%d\n", level);
 
-    gen_level_status(model, ctx);
+    rc = gen_level_status(model, ctx);
+    if (rc) {
+        return rc;
+    }
 
     gen_level_state = level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_level_set_unack(struct bt_mesh_model *model,
+static int gen_level_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
@@ -253,9 +265,10 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 
     gen_level_state = level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_delta_set(struct bt_mesh_model *model,
+static int gen_delta_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
@@ -268,9 +281,10 @@ static void gen_delta_set(struct bt_mesh_model *model,
 
     gen_level_state += delta_level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_delta_set_unack(struct bt_mesh_model *model,
+static int gen_delta_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
@@ -281,18 +295,21 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 
     gen_level_state += delta_level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_move_set(struct bt_mesh_model *model,
+static int gen_move_set(struct bt_mesh_model *model,
              struct bt_mesh_msg_ctx *ctx,
              struct os_mbuf *buf)
 {
+    return 0;
 }
 
-static void gen_move_set_unack(struct bt_mesh_model *model,
+static int gen_move_set_unack(struct bt_mesh_model *model,
                    struct bt_mesh_msg_ctx *ctx,
                    struct os_mbuf *buf)
 {
+    return 0;
 }
 
 static const struct bt_mesh_model_op gen_level_op[] = {
@@ -317,11 +334,12 @@ static struct bt_mesh_model root_models[] = {
 
 static struct bt_mesh_model_pub vnd_model_pub;
 
-static void vnd_model_recv(struct bt_mesh_model *model,
+static int vnd_model_recv(struct bt_mesh_model *model,
                            struct bt_mesh_msg_ctx *ctx,
                            struct os_mbuf *buf)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(3);
+    int rc;
 
     console_printf("#vendor-model-recv\n");
 
@@ -331,11 +349,13 @@ static void vnd_model_recv(struct bt_mesh_model *model,
     bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR));
     os_mbuf_append(msg, buf->om_data, buf->om_len);
 
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
     if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
         console_printf("#vendor-model-recv: send rsp failed\n");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
 static const struct bt_mesh_model_op vnd_model_op[] = {
diff --git a/apps/blemesh_light/pkg.yml b/apps/blemesh_light/pkg.yml
index 75a60df7bb..46b6d6c3d1 100644
--- a/apps/blemesh_light/pkg.yml
+++ b/apps/blemesh_light/pkg.yml
@@ -29,9 +29,8 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/modlog"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/shell"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/blemesh_models_example_1/pkg.yml b/apps/blemesh_models_example_1/pkg.yml
index 451f37a11c..b8d77c4e47 100644
--- a/apps/blemesh_models_example_1/pkg.yml
+++ b/apps/blemesh_models_example_1/pkg.yml
@@ -27,8 +27,7 @@ pkg.deps:
     - "@apache-mynewt-core/sys/console/full"
     - "@apache-mynewt-core/sys/log/full"
     - "@apache-mynewt-core/sys/stats/full"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/blemesh_models_example_1/src/main.c b/apps/blemesh_models_example_1/src/main.c
index 736d4d32bf..80a69c7052 100644
--- a/apps/blemesh_models_example_1/src/main.c
+++ b/apps/blemesh_models_example_1/src/main.c
@@ -68,19 +68,19 @@
 #define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK	BT_MESH_MODEL_OP_2(0x82, 0x03)
 #define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS	BT_MESH_MODEL_OP_2(0x82, 0x04)
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
                           struct bt_mesh_msg_ctx *ctx,
                           struct os_mbuf *buf);
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
                                 struct bt_mesh_msg_ctx *ctx,
                                 struct os_mbuf *buf);
 
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
                           struct bt_mesh_msg_ctx *ctx,
                           struct os_mbuf *buf);
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
                              struct bt_mesh_msg_ctx *ctx,
                              struct os_mbuf *buf);
 
@@ -318,26 +318,29 @@ static uint16_t primary_net_idx;
  *
  */
 
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
                           struct bt_mesh_msg_ctx *ctx,
                           struct os_mbuf *buf)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
     struct onoff_state *state = model->user_data;
+    int rc;
 
     BT_INFO("addr 0x%04x onoff 0x%02x",
                 bt_mesh_model_elem(model)->addr, state->current);
     bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
     net_buf_simple_add_u8(msg, state->current);
 
-    if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+    if (rc) {
         BT_ERR("Unable to send On Off Status response");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
                                 struct bt_mesh_msg_ctx *ctx,
                                 struct os_mbuf *buf)
 {
@@ -372,23 +375,34 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
                                BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
         net_buf_simple_add_u8(msg, state->current);
         err = bt_mesh_model_publish(model);
-        if (err) {
+        if (err != 0) {
             BT_ERR("bt_mesh_model_publish err %d", err);
+            return err;
         }
     }
+    return 0;
 }
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
                           struct bt_mesh_msg_ctx *ctx,
                           struct os_mbuf *buf)
 {
     BT_INFO("");
+    int rc;
+
+    rc = gen_onoff_set_unack(model, ctx, buf);
+    if (rc != 0) {
+        return rc;
+    }
 
-    gen_onoff_set_unack(model, ctx, buf);
-    gen_onoff_get(model, ctx, buf);
+    rc = gen_onoff_get(model, ctx, buf);
+    if (rc != 0) {
+        return rc;
+    }
+    return 0;
 }
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
                              struct bt_mesh_msg_ctx *ctx,
                              struct os_mbuf *buf)
 {
@@ -398,6 +412,7 @@ static void gen_onoff_status(struct bt_mesh_model *model,
 
     BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
                 bt_mesh_model_elem(model)->addr, ctx->addr, state);
+    return 0;
 }
 
 static int output_number(bt_mesh_output_action_t action, uint32_t number)
diff --git a/apps/blemesh_models_example_2/pkg.yml b/apps/blemesh_models_example_2/pkg.yml
index ee2be6da2a..020a9616da 100644
--- a/apps/blemesh_models_example_2/pkg.yml
+++ b/apps/blemesh_models_example_2/pkg.yml
@@ -29,11 +29,10 @@ pkg.deps:
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/encoding/base64"
     - "@apache-mynewt-core/sys/config"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
-    - nimble/transport/ram
+    - nimble/transport
 
 pkg.lflags:
     - -DFLOAT_SUPPORT
diff --git a/apps/blemesh_models_example_2/src/device_composition.c b/apps/blemesh_models_example_2/src/device_composition.c
index 5dfeaf8ed5..40ede50126 100644
--- a/apps/blemesh_models_example_2/src/device_composition.c
+++ b/apps/blemesh_models_example_2/src/device_composition.c
@@ -147,7 +147,7 @@ static struct bt_mesh_elem elements[];
 /* message handlers (Start) */
 
 /* Generic OnOff Server message handlers */
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -168,16 +168,17 @@ static void gen_onoff_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void gen_onoff_publish(struct bt_mesh_model *model)
+int gen_onoff_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct generic_onoff_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
@@ -193,21 +194,23 @@ void gen_onoff_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
 	uint8_t tid, onoff, tt, delay;
 	int64_t now;
+	int err;
 	struct generic_onoff_state *state = model->user_data;
 
 	onoff = net_buf_simple_pull_u8(buf);
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (onoff > STATE_ON) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -215,7 +218,7 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -226,13 +229,13 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -247,8 +250,7 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 	if (state->target_onoff != state->onoff) {
 		onoff_tt_values(state, tt, delay);
 	} else {
-		gen_onoff_publish(model);
-		return;
+        return gen_onoff_publish(model);
 	}
 
 	/* For Instantaneous Transition */
@@ -257,23 +259,28 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 	}
 
 	state->transition->just_started = true;
-	gen_onoff_publish(model);
+	err = gen_onoff_publish(model);
 	onoff_handler(state);
+	if (err) {
+        return err;
+	}
+    return 0;
 }
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	uint8_t tid, onoff, tt, delay;
 	int64_t now;
+	int rc;
 	struct generic_onoff_state *state = model->user_data;
 
 	onoff = net_buf_simple_pull_u8(buf);
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (onoff > STATE_ON) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -281,8 +288,8 @@ static void gen_onoff_set(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		gen_onoff_get(model, ctx, buf);
-		return;
+		rc = gen_onoff_get(model, ctx, buf);
+		return rc;
 	}
 
 	switch (buf->om_len) {
@@ -293,13 +300,13 @@ static void gen_onoff_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -315,8 +322,8 @@ static void gen_onoff_set(struct bt_mesh_model *model,
 		onoff_tt_values(state, tt, delay);
 	} else {
 		gen_onoff_get(model, ctx, buf);
-		gen_onoff_publish(model);
-		return;
+		rc = gen_onoff_publish(model);
+		return rc;
 	}
 
 	/* For Instantaneous Transition */
@@ -326,12 +333,13 @@ static void gen_onoff_set(struct bt_mesh_model *model,
 
 	state->transition->just_started = true;
 	gen_onoff_get(model, ctx, buf);
-	gen_onoff_publish(model);
+	rc = gen_onoff_publish(model);
 	onoff_handler(state);
+    return rc;
 }
 
 /* Generic OnOff Client message handlers */
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -342,10 +350,11 @@ static void gen_onoff_status(struct bt_mesh_model *model,
 		printk("Target OnOff = %02x\n", net_buf_simple_pull_u8(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
 /* Generic Level Server message handlers */
-static void gen_level_get(struct bt_mesh_model *model,
+static int gen_level_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -366,16 +375,17 @@ static void gen_level_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void gen_level_publish(struct bt_mesh_model *model)
+int gen_level_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct generic_level_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
@@ -391,9 +401,10 @@ void gen_level_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void gen_level_set_unack(struct bt_mesh_model *model,
+static int gen_level_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -410,7 +421,7 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -421,13 +432,13 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -443,7 +454,7 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 		level_tt_values(state, tt, delay);
 	} else {
 		gen_level_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -463,9 +474,10 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT;
 		level_temp_handler(state);
 	}
+    return 0;
 }
 
-static void gen_level_set(struct bt_mesh_model *model,
+static int gen_level_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -483,7 +495,7 @@ static void gen_level_set(struct bt_mesh_model *model,
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
 		gen_level_get(model, ctx, buf);
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -494,13 +506,13 @@ static void gen_level_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -517,7 +529,7 @@ static void gen_level_set(struct bt_mesh_model *model,
 	} else {
 		gen_level_get(model, ctx, buf);
 		gen_level_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -538,9 +550,10 @@ static void gen_level_set(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT;
 		level_temp_handler(state);
 	}
+    return 0;
 }
 
-static void gen_delta_set_unack(struct bt_mesh_model *model,
+static int gen_delta_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -559,7 +572,7 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
 
 		if (state->last_delta == delta) {
-			return;
+			return 0;
 		}
 		tmp32 = state->last_level + delta;
 
@@ -576,13 +589,13 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -605,8 +618,7 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 	if (state->target_level != state->level) {
 		level_tt_values(state, tt, delay);
 	} else {
-		gen_level_publish(model);
-		return;
+        return gen_level_publish(model);
 	}
 
 	/* For Instantaneous Transition */
@@ -627,9 +639,10 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT_DELTA;
 		level_temp_handler(state);
 	}
+    return 0;
 }
 
-static void gen_delta_set(struct bt_mesh_model *model,
+static int gen_delta_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -649,7 +662,7 @@ static void gen_delta_set(struct bt_mesh_model *model,
 
 		if (state->last_delta == delta) {
 			gen_level_get(model, ctx, buf);
-			return;
+			return 0;
 		}
 		tmp32 = state->last_level + delta;
 
@@ -666,13 +679,13 @@ static void gen_delta_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -697,7 +710,7 @@ static void gen_delta_set(struct bt_mesh_model *model,
 	} else {
 		gen_level_get(model, ctx, buf);
 		gen_level_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -718,14 +731,16 @@ static void gen_delta_set(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT_DELTA;
 		level_temp_handler(state);
 	}
+    return 0;
 }
 
-static void gen_level_move_get(struct bt_mesh_model *model,
+static int gen_level_move_get(struct bt_mesh_model *model,
 			       struct bt_mesh_msg_ctx *ctx,
 			       struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
 	struct generic_level_state *state = model->user_data;
+	int rc;
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
 	net_buf_simple_add_le16(msg, state->level);
@@ -740,21 +755,23 @@ static void gen_level_move_get(struct bt_mesh_model *model,
 		net_buf_simple_add_u8(msg, UNKNOWN_VALUE);
 	}
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (rc) {
 		printk("Unable to send GEN_LEVEL_SRV Status response\n");
 	}
 
 	os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void gen_level_move_publish(struct bt_mesh_model *model)
+static int gen_level_move_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct generic_level_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
@@ -774,9 +791,10 @@ static void gen_level_move_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void gen_move_set_unack(struct bt_mesh_model *model,
+static int gen_move_set_unack(struct bt_mesh_model *model,
 			       struct bt_mesh_msg_ctx *ctx,
 			       struct os_mbuf *buf)
 {
@@ -785,6 +803,7 @@ static void gen_move_set_unack(struct bt_mesh_model *model,
 	int32_t tmp32;
 	int64_t now;
 	struct generic_level_state *state = model->user_data;
+	int rc;
 
 	delta = (int16_t) net_buf_simple_pull_le16(buf);
 	tid = net_buf_simple_pull_u8(buf);
@@ -794,7 +813,7 @@ static void gen_move_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -805,13 +824,13 @@ static void gen_move_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -835,16 +854,16 @@ static void gen_move_set_unack(struct bt_mesh_model *model,
 	if (state->target_level != state->level) {
 		level_tt_values(state, tt, delay);
 	} else {
-		gen_level_move_publish(model);
-		return;
+		rc = gen_level_move_publish(model);
+		return rc;
 	}
 
 	if (state->transition->counter == 0) {
-		return;
+		return 0;
 	}
 
 	state->transition->just_started = true;
-	gen_level_move_publish(model);
+	rc = gen_level_move_publish(model);
 
 	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
 		/* Root element */
@@ -855,9 +874,10 @@ static void gen_move_set_unack(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT_MOVE;
 		level_temp_handler(state);
 	}
+    return rc;
 }
 
-static void gen_move_set(struct bt_mesh_model *model,
+static int gen_move_set(struct bt_mesh_model *model,
 			 struct bt_mesh_msg_ctx *ctx,
 			 struct os_mbuf *buf)
 {
@@ -866,6 +886,7 @@ static void gen_move_set(struct bt_mesh_model *model,
 	int32_t tmp32;
 	int64_t now;
 	struct generic_level_state *state = model->user_data;
+	int rc;
 
 	delta = (int16_t) net_buf_simple_pull_le16(buf);
 	tid = net_buf_simple_pull_u8(buf);
@@ -875,8 +896,8 @@ static void gen_move_set(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		gen_level_move_get(model, ctx, buf);
-		return;
+		rc = gen_level_move_get(model, ctx, buf);
+		return rc;
 	}
 
 	switch (buf->om_len) {
@@ -887,13 +908,13 @@ static void gen_move_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -918,17 +939,17 @@ static void gen_move_set(struct bt_mesh_model *model,
 		level_tt_values(state, tt, delay);
 	} else {
 		gen_level_move_get(model, ctx, buf);
-		gen_level_move_publish(model);
-		return;
+		rc = gen_level_move_publish(model);
+		return rc;
 	}
 
 	if (state->transition->counter == 0) {
-		return;
+		return 0;
 	}
 
 	state->transition->just_started = true;
 	gen_level_move_get(model, ctx, buf);
-	gen_level_move_publish(model);
+	rc = gen_level_move_publish(model);
 
 	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
 		/* Root element */
@@ -939,10 +960,11 @@ static void gen_move_set(struct bt_mesh_model *model,
 		transition_type = LEVEL_TEMP_TT_MOVE;
 		level_temp_handler(state);
 	}
+    return rc;
 }
 
 /* Generic Level Client message handlers */
-static void gen_level_status(struct bt_mesh_model *model,
+static int gen_level_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -953,10 +975,11 @@ static void gen_level_status(struct bt_mesh_model *model,
 		printk("Target Level = %04x\n", net_buf_simple_pull_le16(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
 /* Generic Default Transition Time Server message handlers */
-static void gen_def_trans_time_get(struct bt_mesh_model *model,
+static int gen_def_trans_time_get(struct bt_mesh_model *model,
 				   struct bt_mesh_msg_ctx *ctx,
 				   struct os_mbuf *buf)
 {
@@ -971,16 +994,17 @@ static void gen_def_trans_time_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-static void gen_def_trans_time_publish(struct bt_mesh_model *model)
+static int gen_def_trans_time_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct gen_def_trans_time_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS);
@@ -990,6 +1014,7 @@ static void gen_def_trans_time_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
 static bool gen_def_trans_time_setunack(struct bt_mesh_model *model,
@@ -1017,71 +1042,78 @@ static bool gen_def_trans_time_setunack(struct bt_mesh_model *model,
 	return true;
 }
 
-static void gen_def_trans_time_set_unack(struct bt_mesh_model *model,
+static int gen_def_trans_time_set_unack(struct bt_mesh_model *model,
 					 struct bt_mesh_msg_ctx *ctx,
 					 struct os_mbuf *buf)
 {
 	if (gen_def_trans_time_setunack(model, ctx, buf) == true) {
-		gen_def_trans_time_publish(model);
+        return gen_def_trans_time_publish(model);
 	}
+    return 0;
 }
 
-static void gen_def_trans_time_set(struct bt_mesh_model *model,
+static int gen_def_trans_time_set(struct bt_mesh_model *model,
 				   struct bt_mesh_msg_ctx *ctx,
 				   struct os_mbuf *buf)
 {
 	if (gen_def_trans_time_setunack(model, ctx, buf) == true) {
 		gen_def_trans_time_get(model, ctx, buf);
-		gen_def_trans_time_publish(model);
+        return gen_def_trans_time_publish(model);
 	}
+    return 0;
 }
 
 /* Generic Default Transition Time Client message handlers */
-static void gen_def_trans_time_status(struct bt_mesh_model *model,
+static int gen_def_trans_time_status(struct bt_mesh_model *model,
 				      struct bt_mesh_msg_ctx *ctx,
 				      struct os_mbuf *buf)
 {
 	printk("Acknownledgement from GEN_DEF_TT_SRV\n");
 	printk("Transition Time = %02x\n", net_buf_simple_pull_u8(buf));
+    return 0;
 }
 
 /* Generic Power OnOff Server message handlers */
-static void gen_onpowerup_get(struct bt_mesh_model *model,
+static int gen_onpowerup_get(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
 	struct generic_onpowerup_state *state = model->user_data;
+	int rc;
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS);
 	net_buf_simple_add_u8(msg, state->onpowerup);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (rc) {
 		printk("Unable to send GEN_POWER_ONOFF_SRV Status response\n");
 	}
 
 	os_mbuf_free_chain(msg);
+    return rc;
 }
 
 /* Generic Power OnOff Client message handlers */
-static void gen_onpowerup_status(struct bt_mesh_model *model,
+static int gen_onpowerup_status(struct bt_mesh_model *model,
 				 struct bt_mesh_msg_ctx *ctx,
 				 struct os_mbuf *buf)
 {
 	printk("Acknownledgement from GEN_POWER_ONOFF_SRV\n");
 	printk("OnPowerUp = %02x\n", net_buf_simple_pull_u8(buf));
+    return 0;
 }
 
 /* Generic Power OnOff Setup Server message handlers */
 
-static void gen_onpowerup_publish(struct bt_mesh_model *model)
+static int gen_onpowerup_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct generic_onpowerup_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS);
@@ -1091,6 +1123,7 @@ static void gen_onpowerup_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
 static bool gen_onpowerup_setunack(struct bt_mesh_model *model,
@@ -1117,32 +1150,35 @@ static bool gen_onpowerup_setunack(struct bt_mesh_model *model,
 	return true;
 }
 
-static void gen_onpowerup_set_unack(struct bt_mesh_model *model,
+static int gen_onpowerup_set_unack(struct bt_mesh_model *model,
 				    struct bt_mesh_msg_ctx *ctx,
 				    struct os_mbuf *buf)
 {
 	if (gen_onpowerup_setunack(model, ctx, buf) == true) {
-		gen_onpowerup_publish(model);
+        return gen_onpowerup_publish(model);
 	}
+	return 0;
 }
 
-static void gen_onpowerup_set(struct bt_mesh_model *model,
+static int gen_onpowerup_set(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
 	if (gen_onpowerup_setunack(model, ctx, buf) == true) {
 		gen_onpowerup_get(model, ctx, buf);
-		gen_onpowerup_publish(model);
+        return gen_onpowerup_publish(model);
 	}
+    return 0;
 }
 
 /* Vendor Model message handlers*/
-static void vnd_get(struct bt_mesh_model *model,
+static int vnd_get(struct bt_mesh_model *model,
 		    struct bt_mesh_msg_ctx *ctx,
 		    struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 6 + 4);
 	struct vendor_state *state = model->user_data;
+    int err;
 
 	/* This is dummy response for demo purpose */
 	state->response = 0xA578FEB3;
@@ -1151,14 +1187,16 @@ static void vnd_get(struct bt_mesh_model *model,
 	net_buf_simple_add_le16(msg, state->current);
 	net_buf_simple_add_le32(msg, state->response);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (err) {
 		printk("Unable to send VENDOR Status response\n");
 	}
 
 	os_mbuf_free_chain(msg);
+    return err;
 }
 
-static void vnd_set_unack(struct bt_mesh_model *model,
+static int vnd_set_unack(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -1175,7 +1213,7 @@ static void vnd_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	state->last_tid = tid;
@@ -1193,27 +1231,37 @@ static void vnd_set_unack(struct bt_mesh_model *model,
 		/* LED2 Off */
 		hal_gpio_write(led_device[1], 1);
 	}
+    return 0;
 }
 
-static void vnd_set(struct bt_mesh_model *model,
+static int vnd_set(struct bt_mesh_model *model,
 		    struct bt_mesh_msg_ctx *ctx,
 		    struct os_mbuf *buf)
 {
-	vnd_set_unack(model, ctx, buf);
-	vnd_get(model, ctx, buf);
+    int rc;
+	rc = vnd_set_unack(model, ctx, buf);
+	if (rc) {
+        return rc;
+	}
+	rc = vnd_get(model, ctx, buf);
+	if (rc) {
+	    return rc;
+	}
+    return 0;
 }
 
-static void vnd_status(struct bt_mesh_model *model,
+static int vnd_status(struct bt_mesh_model *model,
 		       struct bt_mesh_msg_ctx *ctx,
 		       struct os_mbuf *buf)
 {
 	printk("Acknownledgement from Vendor\n");
 	printk("cmd = %04x\n", net_buf_simple_pull_le16(buf));
 	printk("response = %08lx\n", net_buf_simple_pull_le32(buf));
+    return 0;
 }
 
 /* Light Lightness Server message handlers */
-static void light_lightness_get(struct bt_mesh_model *model,
+static int light_lightness_get(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -1234,16 +1282,17 @@ static void light_lightness_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void light_lightness_publish(struct bt_mesh_model *model)
+int light_lightness_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_lightness_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS);
@@ -1259,9 +1308,10 @@ void light_lightness_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void light_lightness_set_unack(struct bt_mesh_model *model,
+static int light_lightness_set_unack(struct bt_mesh_model *model,
 				      struct bt_mesh_msg_ctx *ctx,
 				      struct os_mbuf *buf)
 {
@@ -1278,7 +1328,7 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -1289,13 +1339,13 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -1318,7 +1368,7 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
 		light_lightness_actual_tt_values(state, tt, delay);
 	} else {
 		light_lightness_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -1329,9 +1379,10 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
 	state->transition->just_started = true;
 	light_lightness_publish(model);
 	light_lightness_actual_handler(state);
+    return 0;
 }
 
-static void light_lightness_set(struct bt_mesh_model *model,
+static int light_lightness_set(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -1339,6 +1390,7 @@ static void light_lightness_set(struct bt_mesh_model *model,
 	uint16_t actual;
 	int64_t now;
 	struct light_lightness_state *state = model->user_data;
+	int rc;
 
 	actual = net_buf_simple_pull_le16(buf);
 	tid = net_buf_simple_pull_u8(buf);
@@ -1348,8 +1400,7 @@ static void light_lightness_set(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		light_lightness_get(model, ctx, buf);
-		return;
+	    return light_lightness_get(model, ctx, buf);
 	}
 
 	switch (buf->om_len) {
@@ -1360,13 +1411,13 @@ static void light_lightness_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -1388,9 +1439,9 @@ static void light_lightness_set(struct bt_mesh_model *model,
 	if (state->target_actual != state->actual) {
 		light_lightness_actual_tt_values(state, tt, delay);
 	} else {
-		light_lightness_get(model, ctx, buf);
+		rc = light_lightness_get(model, ctx, buf);
 		light_lightness_publish(model);
-		return;
+		return rc;
 	}
 
 	/* For Instantaneous Transition */
@@ -1402,9 +1453,10 @@ static void light_lightness_set(struct bt_mesh_model *model,
 	light_lightness_get(model, ctx, buf);
 	light_lightness_publish(model);
 	light_lightness_actual_handler(state);
+    return 0;
 }
 
-static void light_lightness_linear_get(struct bt_mesh_model *model,
+static int light_lightness_linear_get(struct bt_mesh_model *model,
 				       struct bt_mesh_msg_ctx *ctx,
 				       struct os_mbuf *buf)
 {
@@ -1426,16 +1478,17 @@ static void light_lightness_linear_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void light_lightness_linear_publish(struct bt_mesh_model *model)
+int light_lightness_linear_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_lightness_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg,
@@ -1452,9 +1505,10 @@ void light_lightness_linear_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
+static int light_lightness_linear_set_unack(struct bt_mesh_model *model,
 					     struct bt_mesh_msg_ctx *ctx,
 					     struct os_mbuf *buf)
 {
@@ -1471,7 +1525,7 @@ static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -1482,13 +1536,13 @@ static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -1504,7 +1558,7 @@ static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
 		light_lightness_linear_tt_values(state, tt, delay);
 	} else {
 		light_lightness_linear_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -1515,9 +1569,10 @@ static void light_lightness_linear_set_unack(struct bt_mesh_model *model,
 	state->transition->just_started = true;
 	light_lightness_linear_publish(model);
 	light_lightness_linear_handler(state);
+    return 0;
 }
 
-static void light_lightness_linear_set(struct bt_mesh_model *model,
+static int light_lightness_linear_set(struct bt_mesh_model *model,
 				       struct bt_mesh_msg_ctx *ctx,
 				       struct os_mbuf *buf)
 {
@@ -1525,6 +1580,7 @@ static void light_lightness_linear_set(struct bt_mesh_model *model,
 	uint16_t linear;
 	int64_t now;
 	struct light_lightness_state *state = model->user_data;
+	int rc;
 
 	linear = net_buf_simple_pull_le16(buf);
 	tid = net_buf_simple_pull_u8(buf);
@@ -1534,8 +1590,7 @@ static void light_lightness_linear_set(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		light_lightness_linear_get(model, ctx, buf);
-		return;
+	    return light_lightness_linear_get(model, ctx, buf);
 	}
 
 	switch (buf->om_len) {
@@ -1546,13 +1601,13 @@ static void light_lightness_linear_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -1567,9 +1622,9 @@ static void light_lightness_linear_set(struct bt_mesh_model *model,
 	if (state->target_linear != state->linear) {
 		light_lightness_linear_tt_values(state, tt, delay);
 	} else {
-		light_lightness_linear_get(model, ctx, buf);
+		rc = light_lightness_linear_get(model, ctx, buf);
 		light_lightness_linear_publish(model);
-		return;
+		return rc;
 	}
 
 	/* For Instantaneous Transition */
@@ -1581,9 +1636,10 @@ static void light_lightness_linear_set(struct bt_mesh_model *model,
 	light_lightness_linear_get(model, ctx, buf);
 	light_lightness_linear_publish(model);
 	light_lightness_linear_handler(state);
+    return 0;
 }
 
-static void light_lightness_last_get(struct bt_mesh_model *model,
+static int light_lightness_last_get(struct bt_mesh_model *model,
 				     struct bt_mesh_msg_ctx *ctx,
 				     struct os_mbuf *buf)
 {
@@ -1598,9 +1654,10 @@ static void light_lightness_last_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-static void light_lightness_default_get(struct bt_mesh_model *model,
+static int light_lightness_default_get(struct bt_mesh_model *model,
 					struct bt_mesh_msg_ctx *ctx,
 					struct os_mbuf *buf)
 {
@@ -1616,9 +1673,10 @@ static void light_lightness_default_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-static void light_lightness_range_get(struct bt_mesh_model *model,
+static int light_lightness_range_get(struct bt_mesh_model *model,
 				      struct bt_mesh_msg_ctx *ctx,
 				      struct os_mbuf *buf)
 {
@@ -1637,18 +1695,19 @@ static void light_lightness_range_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
 /* Light Lightness Setup Server message handlers */
 
-static void light_lightness_default_publish(struct bt_mesh_model *model)
+static int light_lightness_default_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_lightness_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg,
@@ -1659,9 +1718,10 @@ static void light_lightness_default_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void light_lightness_default_set_unack(struct bt_mesh_model *model,
+static int light_lightness_default_set_unack(struct bt_mesh_model *model,
 					      struct bt_mesh_msg_ctx *ctx,
 					      struct os_mbuf *buf)
 {
@@ -1679,26 +1739,37 @@ static void light_lightness_default_set_unack(struct bt_mesh_model *model,
 		save_on_flash(LIGHTNESS_TEMP_DEF_STATE);
 	}
 
-	light_lightness_default_publish(model);
+    return light_lightness_default_publish(model);
 }
 
-static void light_lightness_default_set(struct bt_mesh_model *model,
+static int light_lightness_default_set(struct bt_mesh_model *model,
 					struct bt_mesh_msg_ctx *ctx,
 					struct os_mbuf *buf)
 {
-	light_lightness_default_set_unack(model, ctx, buf);
-	light_lightness_default_get(model, ctx, buf);
-	light_lightness_default_publish(model);
+    int rc;
+	rc = light_lightness_default_set_unack(model, ctx, buf);
+	if (rc) {
+	    return rc;
+	}
+	rc = light_lightness_default_get(model, ctx, buf);
+	if (rc) {
+	    return rc;
+	}
+	rc = light_lightness_default_publish(model);
+	if (rc) {
+	    return rc;
+	}
+    return 0;
 }
 
-static void light_lightness_range_publish(struct bt_mesh_model *model)
+static int light_lightness_range_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_lightness_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS);
@@ -1710,6 +1781,7 @@ static void light_lightness_range_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
 static bool light_lightness_range_setunack(struct bt_mesh_model *model,
@@ -1748,27 +1820,36 @@ static bool light_lightness_range_setunack(struct bt_mesh_model *model,
 	return true;
 }
 
-static void light_lightness_range_set_unack(struct bt_mesh_model *model,
+static int light_lightness_range_set_unack(struct bt_mesh_model *model,
 					    struct bt_mesh_msg_ctx *ctx,
 					    struct os_mbuf *buf)
 {
 	if (light_lightness_range_setunack(model, ctx, buf) == true) {
-		light_lightness_range_publish(model);
+        return light_lightness_range_publish(model);
 	}
+    return 0;
 }
 
-static void light_lightness_range_set(struct bt_mesh_model *model,
+static int light_lightness_range_set(struct bt_mesh_model *model,
 				      struct bt_mesh_msg_ctx *ctx,
 				      struct os_mbuf *buf)
 {
+    int rc;
 	if (light_lightness_range_setunack(model, ctx, buf) == true) {
-		light_lightness_range_get(model, ctx, buf);
-		light_lightness_range_publish(model);
+		rc = light_lightness_range_get(model, ctx, buf);
+		if (rc) {
+		    return rc;
+		}
+		rc = light_lightness_range_publish(model);
+		if (rc) {
+		    return rc;
+		}
 	}
+    return 0;
 }
 
 /* Light Lightness Client message handlers */
-static void light_lightness_status(struct bt_mesh_model *model,
+static int light_lightness_status(struct bt_mesh_model *model,
 				   struct bt_mesh_msg_ctx *ctx,
 				   struct os_mbuf *buf)
 {
@@ -1780,9 +1861,10 @@ static void light_lightness_status(struct bt_mesh_model *model,
 		       net_buf_simple_pull_le16(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
-static void light_lightness_linear_status(struct bt_mesh_model *model,
+static int light_lightness_linear_status(struct bt_mesh_model *model,
 					  struct bt_mesh_msg_ctx *ctx,
 					  struct os_mbuf *buf)
 {
@@ -1794,25 +1876,28 @@ static void light_lightness_linear_status(struct bt_mesh_model *model,
 		       net_buf_simple_pull_le16(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
-static void light_lightness_last_status(struct bt_mesh_model *model,
+static int light_lightness_last_status(struct bt_mesh_model *model,
 					struct bt_mesh_msg_ctx *ctx,
 					struct os_mbuf *buf)
 {
 	printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Last)\n");
 	printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+    return 0;
 }
 
-static void light_lightness_default_status(struct bt_mesh_model *model,
+static int light_lightness_default_status(struct bt_mesh_model *model,
 					   struct bt_mesh_msg_ctx *ctx,
 					   struct os_mbuf *buf)
 {
 	printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Default)\n");
 	printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
+    return 0;
 }
 
-static void light_lightness_range_status(struct bt_mesh_model *model,
+static int light_lightness_range_status(struct bt_mesh_model *model,
 					 struct bt_mesh_msg_ctx *ctx,
 					 struct os_mbuf *buf)
 {
@@ -1820,10 +1905,11 @@ static void light_lightness_range_status(struct bt_mesh_model *model,
 	printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf));
 	printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf));
 	printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf));
+    return 0;
 }
 
 /* Light CTL Server message handlers */
-static void light_ctl_get(struct bt_mesh_model *model,
+static int light_ctl_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -1846,16 +1932,17 @@ static void light_ctl_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void light_ctl_publish(struct bt_mesh_model *model)
+int light_ctl_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_ctl_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS);
@@ -1877,9 +1964,10 @@ void light_ctl_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void light_ctl_set_unack(struct bt_mesh_model *model,
+static int light_ctl_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -1895,7 +1983,7 @@ static void light_ctl_set_unack(struct bt_mesh_model *model,
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (temp < TEMP_MIN || temp > TEMP_MAX) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -1903,7 +1991,7 @@ static void light_ctl_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -1914,13 +2002,13 @@ static void light_ctl_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -1947,7 +2035,7 @@ static void light_ctl_set_unack(struct bt_mesh_model *model,
 		light_ctl_tt_values(state, tt, delay);
 	} else {
 		light_ctl_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -1960,9 +2048,10 @@ static void light_ctl_set_unack(struct bt_mesh_model *model,
 	state->transition->just_started = true;
 	light_ctl_publish(model);
 	light_ctl_handler(state);
+    return 0;
 }
 
-static void light_ctl_set(struct bt_mesh_model *model,
+static int light_ctl_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -1978,7 +2067,7 @@ static void light_ctl_set(struct bt_mesh_model *model,
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (temp < TEMP_MIN || temp > TEMP_MAX) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -1987,7 +2076,7 @@ static void light_ctl_set(struct bt_mesh_model *model,
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
 		light_ctl_get(model, ctx, buf);
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -1998,13 +2087,13 @@ static void light_ctl_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -2032,7 +2121,7 @@ static void light_ctl_set(struct bt_mesh_model *model,
 	} else {
 		light_ctl_get(model, ctx, buf);
 		light_ctl_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -2046,14 +2135,16 @@ static void light_ctl_set(struct bt_mesh_model *model,
 	light_ctl_get(model, ctx, buf);
 	light_ctl_publish(model);
 	light_ctl_handler(state);
+    return 0;
 }
 
-static void light_ctl_temp_range_get(struct bt_mesh_model *model,
+static int light_ctl_temp_range_get(struct bt_mesh_model *model,
 				     struct bt_mesh_msg_ctx *ctx,
 				     struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4);
 	struct light_ctl_state *state = model->user_data;
+    int rc;
 
 	state->status_code = RANGE_SUCCESSFULLY_UPDATED;
 
@@ -2062,42 +2153,47 @@ static void light_ctl_temp_range_get(struct bt_mesh_model *model,
 	net_buf_simple_add_le16(msg, state->temp_range_min);
 	net_buf_simple_add_le16(msg, state->temp_range_max);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (rc) {
 		printk("Unable to send LightCTL Temp Range Status response\n");
 	}
 
 	os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void light_ctl_default_get(struct bt_mesh_model *model,
+static int light_ctl_default_get(struct bt_mesh_model *model,
 				  struct bt_mesh_msg_ctx *ctx,
 				  struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4);
 	struct light_ctl_state *state = model->user_data;
+    int rc;
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS);
 	net_buf_simple_add_le16(msg, state->lightness_def);
 	net_buf_simple_add_le16(msg, state->temp_def);
 	net_buf_simple_add_le16(msg, state->delta_uv_def);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (rc) {
 		printk("Unable to send LightCTL Default Status response\n");
 	}
 
 	os_mbuf_free_chain(msg);
+    return rc;
 }
 
 /* Light CTL Setup Server message handlers */
 
-static void light_ctl_default_publish(struct bt_mesh_model *model)
+static int light_ctl_default_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_ctl_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS);
@@ -2109,6 +2205,7 @@ static void light_ctl_default_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
 static bool light_ctl_default_setunack(struct bt_mesh_model *model,
@@ -2147,33 +2244,35 @@ static bool light_ctl_default_setunack(struct bt_mesh_model *model,
 	return true;
 }
 
-static void light_ctl_default_set_unack(struct bt_mesh_model *model,
+static int light_ctl_default_set_unack(struct bt_mesh_model *model,
 					struct bt_mesh_msg_ctx *ctx,
 					struct os_mbuf *buf)
 {
 	if (light_ctl_default_setunack(model, ctx, buf) == true) {
-		light_ctl_default_publish(model);
+        return light_ctl_default_publish(model);
 	}
+    return 0;
 }
 
-static void light_ctl_default_set(struct bt_mesh_model *model,
+static int light_ctl_default_set(struct bt_mesh_model *model,
 				  struct bt_mesh_msg_ctx *ctx,
 				  struct os_mbuf *buf)
 {
 	if (light_ctl_default_setunack(model, ctx, buf) == true) {
 		light_ctl_default_get(model, ctx, buf);
-		light_ctl_default_publish(model);
+        return light_ctl_default_publish(model);
 	}
+    return 0;
 }
 
-static void light_ctl_temp_range_publish(struct bt_mesh_model *model)
+static int light_ctl_temp_range_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_ctl_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS);
@@ -2185,6 +2284,7 @@ static void light_ctl_temp_range_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
 static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model,
@@ -2225,27 +2325,29 @@ static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model,
 	return true;
 }
 
-static void light_ctl_temp_range_set_unack(struct bt_mesh_model *model,
+static int light_ctl_temp_range_set_unack(struct bt_mesh_model *model,
 					   struct bt_mesh_msg_ctx *ctx,
 					   struct os_mbuf *buf)
 {
 	if (light_ctl_temp_range_setunack(model, ctx, buf) == true) {
-		light_ctl_temp_range_publish(model);
+        return light_ctl_temp_range_publish(model);
 	}
+    return 0;
 }
 
-static void light_ctl_temp_range_set(struct bt_mesh_model *model,
+static int light_ctl_temp_range_set(struct bt_mesh_model *model,
 				     struct bt_mesh_msg_ctx *ctx,
 				     struct os_mbuf *buf)
 {
 	if (light_ctl_temp_range_setunack(model, ctx, buf) == true) {
 		light_ctl_temp_range_get(model, ctx, buf);
-		light_ctl_temp_range_publish(model);
+        return light_ctl_temp_range_publish(model);
 	}
+    return 0;
 }
 
 /* Light CTL Client message handlers */
-static void light_ctl_status(struct bt_mesh_model *model,
+static int light_ctl_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -2261,9 +2363,10 @@ static void light_ctl_status(struct bt_mesh_model *model,
 		       net_buf_simple_pull_le16(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
-static void light_ctl_temp_range_status(struct bt_mesh_model *model,
+static int light_ctl_temp_range_status(struct bt_mesh_model *model,
 					struct bt_mesh_msg_ctx *ctx,
 					struct os_mbuf *buf)
 {
@@ -2271,9 +2374,10 @@ static void light_ctl_temp_range_status(struct bt_mesh_model *model,
 	printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf));
 	printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf));
 	printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf));
+    return 0;
 }
 
-static void light_ctl_temp_status(struct bt_mesh_model *model,
+static int light_ctl_temp_status(struct bt_mesh_model *model,
 				  struct bt_mesh_msg_ctx *ctx,
 				  struct os_mbuf *buf)
 {
@@ -2290,9 +2394,10 @@ static void light_ctl_temp_status(struct bt_mesh_model *model,
 		       net_buf_simple_pull_le16(buf));
 		printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf));
 	}
+    return 0;
 }
 
-static void light_ctl_default_status(struct bt_mesh_model *model,
+static int light_ctl_default_status(struct bt_mesh_model *model,
 				     struct bt_mesh_msg_ctx *ctx,
 				     struct os_mbuf *buf)
 {
@@ -2300,10 +2405,11 @@ static void light_ctl_default_status(struct bt_mesh_model *model,
 	printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf));
 	printk("Temperature = %04x\n", net_buf_simple_pull_le16(buf));
 	printk("Delta UV = %04x\n", net_buf_simple_pull_le16(buf));
+    return 0;
 }
 
 /* Light CTL Temp. Server message handlers */
-static void light_ctl_temp_get(struct bt_mesh_model *model,
+static int light_ctl_temp_get(struct bt_mesh_model *model,
 			       struct bt_mesh_msg_ctx *ctx,
 			       struct os_mbuf *buf)
 {
@@ -2326,16 +2432,17 @@ static void light_ctl_temp_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+    return 0;
 }
 
-void light_ctl_temp_publish(struct bt_mesh_model *model)
+int light_ctl_temp_publish(struct bt_mesh_model *model)
 {
 	int err;
 	struct os_mbuf *msg = model->pub->msg;
 	struct light_ctl_state *state = model->user_data;
 
 	if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
+		return 0;
 	}
 
 	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS);
@@ -2353,9 +2460,10 @@ void light_ctl_temp_publish(struct bt_mesh_model *model)
 	if (err) {
 		printk("bt_mesh_model_publish err %d\n", err);
 	}
+    return err;
 }
 
-static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
+static int light_ctl_temp_set_unack(struct bt_mesh_model *model,
 				     struct bt_mesh_msg_ctx *ctx,
 				     struct os_mbuf *buf)
 {
@@ -2370,7 +2478,7 @@ static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (temp < TEMP_MIN || temp > TEMP_MAX) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -2378,7 +2486,7 @@ static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		return;
+		return 0;
 	}
 
 	switch (buf->om_len) {
@@ -2389,13 +2497,13 @@ static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -2420,7 +2528,7 @@ static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
 		light_ctl_temp_tt_values(state, tt, delay);
 	} else {
 		light_ctl_temp_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -2432,9 +2540,10 @@ static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
 	state->transition->just_started = true;
 	light_ctl_temp_publish(model);
 	light_ctl_temp_handler(state);
+    return 0;
 }
 
-static void light_ctl_temp_set(struct bt_mesh_model *model,
+static int light_ctl_temp_set(struct bt_mesh_model *model,
 			       struct bt_mesh_msg_ctx *ctx,
 			       struct os_mbuf *buf)
 {
@@ -2449,7 +2558,7 @@ static void light_ctl_temp_set(struct bt_mesh_model *model,
 	tid = net_buf_simple_pull_u8(buf);
 
 	if (temp < TEMP_MIN || temp > TEMP_MAX) {
-		return;
+		return 0;
 	}
 
 	now = k_uptime_get();
@@ -2457,8 +2566,7 @@ static void light_ctl_temp_set(struct bt_mesh_model *model,
 	    state->last_src_addr == ctx->addr &&
 	    state->last_dst_addr == ctx->recv_dst &&
 	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
-		light_ctl_temp_get(model, ctx, buf);
-		return;
+	    return light_ctl_temp_get(model, ctx, buf);
 	}
 
 	switch (buf->om_len) {
@@ -2469,13 +2577,13 @@ static void light_ctl_temp_set(struct bt_mesh_model *model,
 	case 0x02:	/* Optional fields are available */
 		tt = net_buf_simple_pull_u8(buf);
 		if ((tt & 0x3F) == 0x3F) {
-			return;
+			return 0;
 		}
 
 		delay = net_buf_simple_pull_u8(buf);
 		break;
 	default:
-		return;
+		return 0;
 	}
 
 	*ptr_counter = 0;
@@ -2501,7 +2609,7 @@ static void light_ctl_temp_set(struct bt_mesh_model *model,
 	} else {
 		light_ctl_temp_get(model, ctx, buf);
 		light_ctl_temp_publish(model);
-		return;
+		return 0;
 	}
 
 	/* For Instantaneous Transition */
@@ -2514,6 +2622,7 @@ static void light_ctl_temp_set(struct bt_mesh_model *model,
 	light_ctl_temp_get(model, ctx, buf);
 	light_ctl_temp_publish(model);
 	light_ctl_temp_handler(state);
+    return 0;
 }
 
 /* message handlers (End) */
diff --git a/apps/blemesh_models_example_2/src/device_composition.h b/apps/blemesh_models_example_2/src/device_composition.h
index d0f054ee2b..1b5bf5f72a 100644
--- a/apps/blemesh_models_example_2/src/device_composition.h
+++ b/apps/blemesh_models_example_2/src/device_composition.h
@@ -167,11 +167,11 @@ extern struct bt_mesh_model s0_models[];
 
 extern const struct bt_mesh_comp comp;
 
-void gen_onoff_publish(struct bt_mesh_model *model);
-void gen_level_publish(struct bt_mesh_model *model);
-void light_lightness_publish(struct bt_mesh_model *model);
-void light_lightness_linear_publish(struct bt_mesh_model *model);
-void light_ctl_publish(struct bt_mesh_model *model);
-void light_ctl_temp_publish(struct bt_mesh_model *model);
+int gen_onoff_publish(struct bt_mesh_model *model);
+int gen_level_publish(struct bt_mesh_model *model);
+int light_lightness_publish(struct bt_mesh_model *model);
+int light_lightness_linear_publish(struct bt_mesh_model *model);
+int light_ctl_publish(struct bt_mesh_model *model);
+int light_ctl_temp_publish(struct bt_mesh_model *model);
 
 #endif
diff --git a/apps/blemesh_shell/pkg.yml b/apps/blemesh_shell/pkg.yml
index e4852f6fa5..f472fdf2aa 100644
--- a/apps/blemesh_shell/pkg.yml
+++ b/apps/blemesh_shell/pkg.yml
@@ -29,9 +29,8 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/modlog"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/shell"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/blestress/pkg.yml b/apps/blestress/pkg.yml
index b23358c564..53f99ed944 100644
--- a/apps/blestress/pkg.yml
+++ b/apps/blestress/pkg.yml
@@ -30,10 +30,9 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/full"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/id"
-    - "@apache-mynewt-nimble/nimble/controller"
     - "@apache-mynewt-nimble/nimble/host"
     - "@apache-mynewt-nimble/nimble/host/util"
     - "@apache-mynewt-nimble/nimble/host/services/gap"
     - "@apache-mynewt-nimble/nimble/host/services/gatt"
     - "@apache-mynewt-nimble/nimble/host/store/config"
-    - "@apache-mynewt-nimble/nimble/transport/ram"
+    - "@apache-mynewt-nimble/nimble/transport"
diff --git a/apps/blestress/src/rx_stress.c b/apps/blestress/src/rx_stress.c
index 440966adcc..a9d9cda09a 100644
--- a/apps/blestress/src/rx_stress.c
+++ b/apps/blestress/src/rx_stress.c
@@ -155,7 +155,8 @@ rx_stress_simple_adv(struct rx_stress_adv_set *adv_set)
     assert (rc == 0);
 
     if (own_addr_type == 0) {
-        memcpy(addr.val, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), 6);
+        rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL);
+        assert (rc == 0);
     } else {
         rc = ble_hs_id_gen_rnd(1, &addr);
         assert (rc == 0);
@@ -1010,12 +1011,24 @@ rx_stress_12_gap_event(struct ble_gap_event *event, void *arg)
     return 0;
 }
 
-static int
-rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
+static struct ble_npl_event rx_stress_13_notify_ev;
+
+static void
+rx_stress_13_notify_ev_func(struct ble_npl_event *ev)
 {
+    struct os_mbuf *om;
     int rc;
-    struct os_mbuf *om = NULL;
 
+    om = ble_hs_mbuf_from_flat(test_6_pattern, 10);
+    rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle,
+                                 hrs_hrm_handle, om);
+    assert(rc == 0);
+
+}
+
+static int
+rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
+{
     switch (event->type) {
     case BLE_GAP_EVENT_CONNECT:
         /* A new connection was established or a connection attempt failed */
@@ -1026,6 +1039,9 @@ rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
             rx_stress_ctx->conn_handle = event->connect.conn_handle;
 
             rx_stress_ctx->begin_us = os_get_uptime_usec();
+
+            ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
+                               &rx_stress_13_notify_ev);
             break;
         } else {
             /* Connection failed; resume advertising */
@@ -1045,7 +1061,7 @@ rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
         rx_stress_ctx->s13_notif_time = rx_stress_ctx->time_sum /
                                         rx_stress_ctx->send_num;
 
-        MODLOG_DFLT(INFO, "Average time: %lld us\n",
+        MODLOG_DFLT(INFO, "Average time: %d us\n",
                     rx_stress_ctx->s13_notif_time);
         rx_stress_on_test_finish(13);
         return 0;
@@ -1061,6 +1077,9 @@ rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
                               BLE_ERR_REM_USER_CONN_TERM);
             return 0;
         }
+
+        ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
+                           &rx_stress_13_notify_ev);
         break;
 
     default:
@@ -1068,10 +1087,6 @@ rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
         return 0;
     }
 
-    om = ble_hs_mbuf_from_flat(test_6_pattern, 10);
-    rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle,
-                                 hrs_hrm_handle, om);
-    assert(rc == 0);
     return 0;
 }
 
@@ -1377,6 +1392,8 @@ rx_stress_start(int test_num)
         break;
     case 13:
         console_printf("Stress GATT notification\033[0m\n");
+        ble_npl_event_init(&rx_stress_13_notify_ev,
+                           rx_stress_13_notify_ev_func, NULL);
         rx_stress_simple_adv(&rx_stress_adv_sets[13]);
         break;
     case 14:
diff --git a/apps/blestress/src/tx_stress.c b/apps/blestress/src/tx_stress.c
index 4416c568cb..1711f9fbf8 100644
--- a/apps/blestress/src/tx_stress.c
+++ b/apps/blestress/src/tx_stress.c
@@ -1271,7 +1271,6 @@ tx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
     case BLE_GAP_EVENT_NOTIFY_RX:
         MODLOG_DFLT(INFO, "Notify RX event\n");
         console_printf("\033[0;32m>\033[0m");
-        os_mbuf_free_chain(event->notify_rx.om);
         ++tx_stress_ctx->rcv_num;
         return 0;
 
@@ -1390,7 +1389,7 @@ tx_stress_14_gap_event(struct ble_gap_event *event, void *arg)
         if (++tx_stress_ctx->rcv_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
             rc = ble_gap_terminate(event->notify_rx.conn_handle,
                                    BLE_ERR_REM_USER_CONN_TERM);
-            MODLOG_DFLT(INFO, "rc=%d\n");
+            MODLOG_DFLT(INFO, "rc=%d\n", rc);
             assert(rc == 0);
             return 0;
         }
diff --git a/apps/btshell/src/cmd.c b/apps/btshell/src/cmd.c
index a0452d22eb..6cb2713518 100644
--- a/apps/btshell/src/cmd.c
+++ b/apps/btshell/src/cmd.c
@@ -98,16 +98,19 @@ parse_dev_addr(const char *prefix, const struct kv_pair *addr_types,
 {
     char name[32];
     int rc;
+    int written = 0;
 
     if (!prefix) {
         name[0] = '\0';
     } else {
-        if (strlcpy(name, prefix, sizeof(name)) >= sizeof(name)) {
+        written = snprintf(name, sizeof(name) - 1, "%s", prefix);
+        if (written >= sizeof(name) || written < 0) {
             return EINVAL;
         }
     }
 
-    if (strlcat(name, "addr", sizeof(name)) >= sizeof(name)) {
+    written = snprintf(name + written, sizeof(name) - written - 1, "%s", "addr");
+    if (written >= sizeof(name) || written < 0) {
         return EINVAL;
     }
     rc = parse_arg_addr(name, addr);
@@ -116,7 +119,8 @@ parse_dev_addr(const char *prefix, const struct kv_pair *addr_types,
         return rc;
     } else if (rc == EAGAIN) {
         /* address found, but no type provided */
-        if (strlcat(name, "_type", sizeof(name)) >= sizeof(name)) {
+        written = snprintf(name + written, sizeof(name) - written - 1, "%s", "_type");
+        if (written >= sizeof(name) || written < 0) {
             return EINVAL;
         }
         addr->type = parse_arg_kv(name, addr_types, &rc);
@@ -130,7 +134,8 @@ parse_dev_addr(const char *prefix, const struct kv_pair *addr_types,
         return rc;
     } else {
         /* full address found, but let's just make sure there is no type arg */
-        if (strlcat(name, "_type", sizeof(name)) >= sizeof(name)) {
+        written = snprintf(name + written, sizeof(name) - written, "%s", "_type");
+        if (written >= sizeof(name) || written < 0) {
             return EINVAL;
         }
         if (parse_arg_extract(name)) {
diff --git a/apps/btshell/src/parse.c b/apps/btshell/src/parse.c
index a2e2ae5217..e4dbade420 100644
--- a/apps/btshell/src/parse.c
+++ b/apps/btshell/src/parse.c
@@ -301,7 +301,7 @@ parse_time_us(const char *str, int *out_status)
     uint32_t val_mult = 1;
     uint32_t val_us;
 
-    while (isdigit(*str)) {
+    while (isdigit((unsigned char)*str)) {
         val *= 10;
         val += *str - '0';
         str++;
@@ -309,7 +309,7 @@ parse_time_us(const char *str, int *out_status)
 
     if (*str == '.') {
         str++;
-        while (isdigit(*str)) {
+        while (isdigit((unsigned char)*str)) {
             val *= 10;
             val += *str - '0';
             val_div *= 10;
diff --git a/apps/bttester/pkg.yml b/apps/bttester/pkg.yml
index ba2b7fb1be..270dc7e882 100644
--- a/apps/bttester/pkg.yml
+++ b/apps/bttester/pkg.yml
@@ -31,14 +31,13 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/modlog"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/shell"
-    - "@apache-mynewt-nimble/nimble/controller"
     - "@apache-mynewt-nimble/nimble/host"
     - "@apache-mynewt-nimble/nimble/host/util"
     - "@apache-mynewt-nimble/nimble/host/services/gap"
     - "@apache-mynewt-nimble/nimble/host/services/gatt"
     - "@apache-mynewt-nimble/nimble/host/services/dis"
     - "@apache-mynewt-nimble/nimble/host/store/config"
-    - "@apache-mynewt-nimble/nimble/transport/ram"
+    - "@apache-mynewt-nimble/nimble/transport"
     - "@apache-mynewt-core/hw/drivers/uart"
     - "@apache-mynewt-core/hw/drivers/rtt"
 
diff --git a/apps/bttester/src/bttester.h b/apps/bttester/src/bttester.h
index 6761681e15..4a8fc47142 100644
--- a/apps/bttester/src/bttester.h
+++ b/apps/bttester/src/bttester.h
@@ -997,6 +997,27 @@ struct mesh_invalid_bearer_ev {
 
 #define MESH_EV_INCOMP_TIMER_EXP	0x88
 
+#define MESH_EV_LPN_ESTABLISHED		0x8b
+struct mesh_lpn_established_ev {
+    uint16_t net_idx;
+    uint16_t friend_addr;
+    uint8_t queue_size;
+    uint8_t recv_win;
+} __packed;
+
+#define MESH_EV_LPN_TERMINATED		0x8c
+struct mesh_lpn_terminated_ev {
+    uint16_t net_idx;
+    uint16_t friend_addr;
+} __packed;
+
+#define MESH_EV_LPN_POLLED			0x8d
+struct mesh_lpn_polled_ev {
+    uint16_t net_idx;
+    uint16_t friend_addr;
+    uint8_t retry;
+} __packed;
+
 void tester_init(void);
 void tester_rsp(uint8_t service, uint8_t opcode, uint8_t index, uint8_t status);
 void tester_send(uint8_t service, uint8_t opcode, uint8_t index, uint8_t *data,
diff --git a/apps/bttester/src/gap.c b/apps/bttester/src/gap.c
index acac9989f0..75adeb9fc0 100644
--- a/apps/bttester/src/gap.c
+++ b/apps/bttester/src/gap.c
@@ -63,6 +63,10 @@ static struct os_callout connected_ev_co;
 static struct gap_device_connected_ev connected_ev;
 #define CONNECTED_EV_DELAY_MS(itvl) 8 * BLE_HCI_CONN_ITVL * itvl / 1000
 static int connection_attempts;
+#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA)
+static int64_t advertising_start;
+static struct os_callout bttester_nrpa_rotate_timer;
+#endif
 
 static const struct ble_gap_conn_params dflt_conn_params = {
 	.scan_itvl = 0x0010,
@@ -162,18 +166,6 @@ static void controller_index_list(uint8_t *data,  uint16_t len)
 		    BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf));
 }
 
-static int check_pub_addr_unassigned(void)
-{
-#ifdef ARCH_sim
-	return 0;
-#else
-	uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 };
-
-	return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
-		      zero_addr, BLE_DEV_ADDR_LEN) == 0;
-#endif
-}
-
 static void controller_info(uint8_t *data, uint16_t len)
 {
 	struct gap_read_controller_info_rp rp;
@@ -208,15 +200,14 @@ static void controller_info(uint8_t *data, uint16_t len)
 		supported_settings |= BIT(GAP_SETTINGS_PRIVACY);
 		memcpy(rp.address, addr.val, sizeof(rp.address));
 	} else {
-		if (check_pub_addr_unassigned()) {
+		rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, rp.address, NULL);
+		if (rc) {
 			own_addr_type = BLE_OWN_ADDR_RANDOM;
 			memcpy(rp.address, addr.val, sizeof(rp.address));
 			supported_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS);
 			current_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS);
 		} else {
 			own_addr_type = BLE_OWN_ADDR_PUBLIC;
-			memcpy(rp.address, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
-			       sizeof(rp.address));
 		}
 	}
 
@@ -248,6 +239,38 @@ static struct ble_gap_adv_params adv_params = {
 	.disc_mode = BLE_GAP_DISC_MODE_NON,
 };
 
+#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA)
+static void rotate_nrpa_cb(struct os_event *ev)
+{
+    int rc;
+    ble_addr_t addr;
+    int32_t duration_ms = BLE_HS_FOREVER;
+    int32_t remaining_time;
+    os_time_t remaining_ticks;
+
+    if (adv_params.disc_mode == BLE_GAP_DISC_MODE_LTD) {
+        duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT);
+    }
+
+    ble_gap_adv_stop();
+    rc = ble_hs_id_gen_rnd(1, &addr);
+    assert(rc == 0);
+    rc = ble_hs_id_set_rnd(addr.val);
+    assert(rc == 0);
+
+    ble_gap_adv_start(own_addr_type, NULL, duration_ms,
+                      &adv_params, gap_event_cb, NULL);
+
+    remaining_time = os_get_uptime_usec() - advertising_start;
+    if (remaining_time > 0) {
+        advertising_start = os_get_uptime_usec();
+        os_time_ms_to_ticks(remaining_time, &remaining_ticks);
+        os_callout_reset(&bttester_nrpa_rotate_timer,
+                         remaining_ticks);
+    }
+}
+#endif
+
 static void set_connectable(uint8_t *data, uint16_t len)
 {
 	const struct gap_set_connectable_cmd *cmd = (void *) data;
@@ -418,6 +441,13 @@ static void start_advertising(const uint8_t *data, uint16_t len)
 		duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT);
 	}
 
+#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA)
+	if (MYNEWT_VAL(BTTESTER_NRPA_TIMEOUT) < duration_ms / 1000) {
+	    advertising_start = os_get_uptime_usec();
+	    os_callout_reset(&bttester_nrpa_rotate_timer,
+                         OS_TICKS_PER_SEC * MYNEWT_VAL(BTTESTER_NRPA_TIMEOUT));
+	}
+#endif
 	err = ble_gap_adv_start(own_addr_type, NULL, duration_ms,
 				&adv_params, gap_event_cb, NULL);
 	if (err) {
@@ -1675,7 +1705,10 @@ uint8_t tester_init_gap(void)
 		return BTP_STATUS_FAILED;
 	}
 #endif
-
+#if MYNEWT_VAL(BTTESTER_PRIVACY_MODE) && MYNEWT_VAL(BTTESTER_USE_NRPA)
+	os_callout_init(&bttester_nrpa_rotate_timer, os_eventq_dflt_get(),
+                    rotate_nrpa_cb, NULL);
+#endif
 	adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN);
 
 	tester_init_gap_cb(BTP_STATUS_SUCCESS);
diff --git a/apps/bttester/src/l2cap.c b/apps/bttester/src/l2cap.c
index 5c4fc71297..27cc6a9a6e 100644
--- a/apps/bttester/src/l2cap.c
+++ b/apps/bttester/src/l2cap.c
@@ -249,6 +249,7 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 {
 	struct ble_l2cap_chan_info chan_info;
 	int accept_response;
+	struct ble_gap_conn_desc conn;
 
 	switch (event->type) {
 		case BLE_L2CAP_EVENT_COC_CONNECTED:
@@ -258,8 +259,6 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 
 		if (event->connect.status) {
 			console_printf("LE COC error: %d\n", event->connect.status);
-			disconnected_cb(event->connect.conn_handle,
-					event->connect.chan, &chan_info, arg);
 			return 0;
 		}
 
@@ -288,6 +287,28 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 				event->disconnect.chan, &chan_info, arg);
 		return 0;
 	case BLE_L2CAP_EVENT_COC_ACCEPT:
+		ble_l2cap_get_chan_info(event->accept.chan, &chan_info);
+		if (chan_info.psm == 0x00F2) {
+			/* TSPX_psm_authentication_required */
+			ble_gap_conn_find(event->accept.conn_handle, &conn);
+			if (!conn.sec_state.authenticated) {
+				return BLE_HS_EAUTHEN;
+			}
+		} else if (chan_info.psm == 0x00F3) {
+			/* TSPX_psm_authorization_required */
+			ble_gap_conn_find(event->accept.conn_handle, &conn);
+			if (!conn.sec_state.encrypted) {
+				return BLE_HS_EAUTHOR;
+			}
+			return BLE_HS_EAUTHOR;
+		} else if (chan_info.psm == 0x00F4) {
+			/* TSPX_psm_encryption_key_size_required */
+			ble_gap_conn_find(event->accept.conn_handle, &conn);
+			if (conn.sec_state.key_size < 16) {
+				return BLE_HS_EENCRYPT_KEY_SZ;
+			}
+		}
+
 		accept_response = POINTER_TO_INT(arg);
 		if (accept_response) {
 			return accept_response;
@@ -375,7 +396,7 @@ static void connect(uint8_t *data, uint16_t len)
 	ble_addr_t *addr = (void *) data;
 	uint16_t mtu = htole16(cmd->mtu);
 	int rc;
-	int i;
+	int i, j;
 
 	SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6));
 
@@ -397,6 +418,8 @@ static void connect(uint8_t *data, uint16_t len)
 			SYS_LOG_ERR("No free channels");
 			goto fail;
 		}
+		/* temporarily mark channel as used to select next one */
+        chan->state = 1;
 
 		rp->chan_ids[i] = chan->chan_id;
 
@@ -407,6 +430,15 @@ static void connect(uint8_t *data, uint16_t len)
 		}
 	}
 
+	/* mark selected channels as unused again */
+	for (i = 0; i < cmd->num; i++) {
+	    for (j = 0; j < CHANNELS; j++) {
+	        if (rp->chan_ids[i] == channels[j].chan_id) {
+	            channels[j].state = 0;
+	        }
+	    }
+	}
+
 	if (cmd->num == 1) {
 		rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
 				       mtu, sdu_rx[0],
diff --git a/apps/bttester/src/mesh.c b/apps/bttester/src/mesh.c
index 8226fe99e1..46bf7699c3 100644
--- a/apps/bttester/src/mesh.c
+++ b/apps/bttester/src/mesh.c
@@ -41,7 +41,7 @@
 extern uint8_t own_addr_type;
 
 #define CONTROLLER_INDEX 0
-#define CID_LOCAL 0xffff
+#define CID_LOCAL 0x0002
 
 /* Health server data */
 #define CUR_FAULTS_MAX 4
@@ -599,7 +599,7 @@ static void net_send(uint8_t *data, uint16_t len)
 	SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl,
 		    ctx.addr, cmd->payload_len);
 
-	if (!bt_mesh_app_key_get(vnd_app_key_idx)) {
+	if (!bt_mesh_app_key_exists(vnd_app_key_idx)) {
 		(void)bt_mesh_app_key_add(vnd_app_key_idx, net.net_idx,
 					  vnd_app_key);
 		vnd_models[0].keys[0] = vnd_app_key_idx;
@@ -937,11 +937,48 @@ static struct bt_test_cb bt_test_cb = {
 	.mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
 };
 
+static void lpn_established(uint16_t friend_addr)
+{
+
+	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+	struct mesh_lpn_established_ev ev = { lpn->sub->net_idx, friend_addr, lpn->queue_size,
+					      lpn->recv_win };
+
+	SYS_LOG_DBG("Friendship (as LPN) established with "
+		"Friend 0x%04x Queue Size %d Receive Window %d",
+		friend_addr, lpn->queue_size, lpn->recv_win);
+
+	tester_send(BTP_SERVICE_ID_MESH, MESH_EV_LPN_ESTABLISHED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void lpn_terminated(uint16_t friend_addr)
+{
+	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+	struct mesh_lpn_terminated_ev ev = { lpn->sub->net_idx, friend_addr };
+
+	SYS_LOG_DBG("Friendship (as LPN) lost with Friend "
+		"0x%04x", friend_addr);
+
+	tester_send(BTP_SERVICE_ID_MESH, MESH_EV_LPN_TERMINATED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+void lpn_cb(uint16_t friend_addr, bool established)
+{
+	if (established) {
+		lpn_established(friend_addr);
+	} else {
+		lpn_terminated(friend_addr);
+	}
+}
+
 uint8_t tester_init_mesh(void)
 {
 	health_pub_init();
 
 	if (IS_ENABLED(CONFIG_BT_TESTING)) {
+		bt_mesh_lpn_set_cb(lpn_cb);
 		bt_test_cb_register(&bt_test_cb);
 	}
 
diff --git a/apps/bttester/syscfg.yml b/apps/bttester/syscfg.yml
index db869c341b..f00afeb10f 100644
--- a/apps/bttester/syscfg.yml
+++ b/apps/bttester/syscfg.yml
@@ -75,12 +75,16 @@ syscfg.defs:
         description: Maximum MTU size the application can handle
         value: 230
 
+    BTTESTER_NRPA_TIMEOUT:
+        description: NRPA rotation timeout in seconds
+        value: 5
+
 syscfg.vals:
     OS_MAIN_STACK_SIZE: 512
     SHELL_TASK: 0
     SHELL_NEWTMGR: 0
     LOG_LEVEL: 12
-    MSYS_1_BLOCK_COUNT: 80
+    MSYS_1_BLOCK_COUNT: 100
 
     BLE_MONITOR_RTT: 1
     CONSOLE_RTT: 0
@@ -127,7 +131,8 @@ syscfg.vals:
     BLE_MESH_CFG_CLI: 1
     BLE_MESH_RX_SDU_MAX: 110
     BLE_MESH_HEALTH_CLI: 1
-    BLE_MESH_FRIEND_QUEUE_SIZE: 32
+    BLE_MESH_FRIEND_QUEUE_SIZE: 16
+    BLE_MESH_SEG_RETRANSMIT_ATTEMPTS: 6
     BLE_MESH_RX_SEG_MAX: 13
     BLE_MESH_TX_SEG_MSG_COUNT: 2
     BLE_MAX_CONNECTIONS: 8
diff --git a/apps/ext_advertiser/pkg.yml b/apps/ext_advertiser/pkg.yml
index 097764b206..5a60b2270b 100644
--- a/apps/ext_advertiser/pkg.yml
+++ b/apps/ext_advertiser/pkg.yml
@@ -25,13 +25,12 @@ pkg.homepage: "http://mynewt.apache.org/"
 pkg.keywords:
 
 pkg.deps:
-    - nimble/controller
     - nimble/host
     - nimble/host/util
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
     - "@apache-mynewt-core/kernel/os"
     - "@apache-mynewt-core/sys/console/full"
     - "@apache-mynewt-core/sys/log/full"
diff --git a/apps/ext_advertiser/src/main.c b/apps/ext_advertiser/src/main.c
index 6bbc23d598..9cb6c6fe57 100644
--- a/apps/ext_advertiser/src/main.c
+++ b/apps/ext_advertiser/src/main.c
@@ -86,7 +86,7 @@ start_ext_max_events(uint8_t pattern, bool configure)
         params.primary_phy = BLE_HCI_LE_PHY_1M;
         params.secondary_phy = BLE_HCI_LE_PHY_1M;
         params.tx_power = 127;
-        params.sid = pattern % 16;
+        params.sid = 4;
 
         /* allow larger interval, 400 * 0.625ms with 100 events will give up to
          * ~2.5 seconds for instance
@@ -94,7 +94,7 @@ start_ext_max_events(uint8_t pattern, bool configure)
         params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
         params.itvl_max = 400;
 
-        /* configure instance 0 */
+        /* configure instance 4 */
         rc = ble_gap_ext_adv_configure(instance, &params, NULL,
                                         start_ext_max_events_gap_event, NULL);
         assert (rc == 0);
@@ -184,9 +184,9 @@ start_legacy_duration(uint8_t pattern, bool configure)
         params.primary_phy = BLE_HCI_LE_PHY_1M;
         params.secondary_phy = BLE_HCI_LE_PHY_1M;
         params.tx_power = 127;
-        params.sid = pattern % 16;
+        params.sid = 3;
 
-        /* configure instance 0 */
+        /* configure instance 3 */
         rc = ble_gap_ext_adv_configure(instance, &params, NULL,
                                         start_legacy_duration_gap_event, NULL);
         assert (rc == 0);
@@ -251,7 +251,7 @@ start_scannable_legacy_ext(void)
     params.tx_power = 127;
     params.sid = 2;
 
-    /* configure instance 0 */
+    /* configure instance 2 */
     rc = ble_gap_ext_adv_configure(instance, &params, NULL, NULL, NULL);
     assert (rc == 0);
 
@@ -335,7 +335,7 @@ start_scannable_ext(void)
     params.tx_power = 127;
     params.sid = 1;
 
-    /* configure instance 0 */
+    /* configure instance 1 */
     rc = ble_gap_ext_adv_configure(instance, &params, NULL,
                                    scannable_ext_gap_event, NULL);
     assert (rc == 0);
@@ -387,7 +387,7 @@ start_non_connectable_ext(void)
     params.tx_power = 127;
     params.sid = 0;
 
-    /* configure instance */
+    /* configure instance 0 */
     rc = ble_gap_ext_adv_configure(instance, &params, NULL, NULL, NULL);
     assert (rc == 0);
 
@@ -411,6 +411,84 @@ start_non_connectable_ext(void)
     console_printf("instance %u started (non-con non-scan)\n", instance);
 }
 
+static void start_periodic(void)
+{
+    struct ble_gap_periodic_adv_params pparams;
+    struct ble_gap_ext_adv_params params;
+    struct ble_hs_adv_fields adv_fields;
+    struct os_mbuf *data;
+    uint8_t instance = 5;
+    ble_addr_t addr;
+    int rc;
+
+    /* For periodic we use nstance with non-connectable advertising */
+    memset (&params, 0, sizeof(params));
+
+    /* advertise using random addr */
+    params.own_addr_type = BLE_OWN_ADDR_RANDOM;
+
+    params.primary_phy = BLE_HCI_LE_PHY_1M;
+    params.secondary_phy = BLE_HCI_LE_PHY_1M;
+    params.tx_power = 127;
+    params.sid = 5;
+
+    /* configure instance 5 */
+    rc = ble_gap_ext_adv_configure(instance, &params, NULL, NULL, NULL);
+    assert (rc == 0);
+
+    /* set random (NRPA) address for instance */
+    rc = ble_hs_id_gen_rnd(1, &addr);
+    assert (rc == 0);
+
+    rc = ble_gap_ext_adv_set_addr(instance, &addr );
+    assert (rc == 0);
+
+    memset(&adv_fields, 0, sizeof(adv_fields));
+    adv_fields.name = (const uint8_t *)"nimble with periodic";
+    adv_fields.name_len = strlen((char *)adv_fields.name);
+
+    /* Default to legacy PDUs size, mbuf chain will be increased if needed
+     */
+    data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
+    assert(data);
+
+    rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
+    assert(rc == 0);
+
+    rc = ble_gap_ext_adv_set_data(instance, data);
+    assert(rc == 0);
+
+    /* configure periodic advertising */
+    memset(&pparams, 0, sizeof(pparams));
+    pparams.include_tx_power = 1;
+    pparams.itvl_min = 160;
+    pparams.itvl_max = 240;
+
+    rc = ble_gap_periodic_adv_configure(instance, &pparams);
+    assert(rc == 0);
+
+    /* get mbuf for periodic data */
+    data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
+    assert(data);
+
+    /* fill mbuf with periodic data */
+    rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
+    assert(rc == 0);
+
+    rc = ble_gap_periodic_adv_set_data(instance, data);
+    assert (rc == 0);
+
+    /* start periodic advertising */
+    rc = ble_gap_periodic_adv_start(instance);
+    assert (rc == 0);
+
+    /* start advertising */
+     rc = ble_gap_ext_adv_start(instance, 0, 0);
+     assert (rc == 0);
+
+     console_printf("instance %u started (periodic)\n", instance);
+}
+
 static void
 on_sync(void)
 {
@@ -435,6 +513,8 @@ on_sync(void)
     start_legacy_duration(0, true);
 
     start_ext_max_events(0, true);
+
+    start_periodic();
 }
 
 /*
diff --git a/apps/ext_advertiser/syscfg.yml b/apps/ext_advertiser/syscfg.yml
index 5f5fb54b95..f157ab82b0 100644
--- a/apps/ext_advertiser/syscfg.yml
+++ b/apps/ext_advertiser/syscfg.yml
@@ -30,6 +30,9 @@ syscfg.vals:
     # Enable Extended Advertising
     BLE_EXT_ADV: 1
 
+    # Enable Periodic Advertising
+    BLE_PERIODIC_ADV: 1
+
     # Max advertising data size
     BLE_EXT_ADV_MAX_SIZE: 1650
 
@@ -37,12 +40,12 @@ syscfg.vals:
     # to historical reasonds total number of advertising
     # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance
     # 0 is always available
-    BLE_MULTI_ADV_INSTANCES: 4
+    BLE_MULTI_ADV_INSTANCES: 5
 
     # Controller uses msys pool for storing advertising data and scan responses.
-    # Since we advertise a lot of data (~4k in total) at the same time we need
+    # Since we advertise a lot of data (~6k in total) at the same time we need
     # to increase block count.
-    MSYS_1_BLOCK_COUNT: 24
+    MSYS_1_BLOCK_COUNT: 32
 
     # Whether to save data to sys/config, or just keep it in RAM.
     BLE_STORE_CONFIG_PERSIST: 0
diff --git a/apps/mesh_badge/pkg.yml b/apps/mesh_badge/pkg.yml
index 0718236f6a..96a42ba6a7 100644
--- a/apps/mesh_badge/pkg.yml
+++ b/apps/mesh_badge/pkg.yml
@@ -31,9 +31,8 @@ pkg.deps:
     - "@apache-mynewt-core/sys/log/modlog"
     - "@apache-mynewt-core/sys/stats/full"
     - "@apache-mynewt-core/sys/shell"
-    - nimble/controller
     - nimble/host
     - nimble/host/services/gap
     - nimble/host/services/gatt
     - nimble/host/store/config
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/apps/mesh_badge/src/reel_board.c b/apps/mesh_badge/src/reel_board.c
index bc8229377f..5e5f6b4088 100644
--- a/apps/mesh_badge/src/reel_board.c
+++ b/apps/mesh_badge/src/reel_board.c
@@ -49,8 +49,8 @@ struct font_info {
 static struct os_dev *epd_dev;
 static bool pressed;
 static bool stats_view;
-static struct k_delayed_work epd_work;
-static struct k_delayed_work long_press_work;
+static struct k_work_delayable epd_work;
+static struct k_work_delayable long_press_work;
 
 static struct {
     int pin;
@@ -61,7 +61,7 @@ static struct {
 	{ .pin = RGB_LED_BLU, },
 };
 
-struct k_delayed_work led_timer;
+struct k_work_delayable led_timer;
 
 static size_t print_line(enum font_size font_size, int row, const char *text,
 			 size_t len, bool center)
@@ -121,7 +121,7 @@ static size_t get_len(enum font_size font, const char *text)
 
 void board_blink_leds(void)
 {
-	k_delayed_work_submit(&led_timer, K_MSEC(100));
+	k_work_reschedule(&led_timer, K_MSEC(100));
 }
 
 void board_show_text(const char *text, bool center, int32_t duration)
@@ -151,7 +151,7 @@ void board_show_text(const char *text, bool center, int32_t duration)
 	cfb_framebuffer_finalize(epd_dev);
 
 	if (duration != K_FOREVER) {
-		k_delayed_work_submit(&epd_work, duration);
+		k_work_reschedule(&epd_work, duration);
 	}
 }
 
@@ -381,11 +381,11 @@ static void button_interrupt(struct os_event *ev)
 	printk("Button %s\n", pressed ? "pressed" : "released");
 
 	if (pressed) {
-		k_delayed_work_submit(&long_press_work, LONG_PRESS_TIMEOUT);
+		k_work_reschedule(&long_press_work, LONG_PRESS_TIMEOUT);
 		return;
 	}
 
-	k_delayed_work_cancel(&long_press_work);
+	k_work_cancel_delayable(&long_press_work);
 
 	if (!mesh_is_initialized()) {
 		return;
@@ -441,7 +441,7 @@ static void led_timeout(struct ble_npl_event *work)
 	i = led_cntr++ % ARRAY_SIZE(leds);
 	hal_gpio_write(leds[i].pin, 0);
 
-	k_delayed_work_submit(&led_timer, K_MSEC(100));
+	k_work_reschedule(&led_timer, K_MSEC(100));
 }
 
 static int configure_leds(void)
@@ -452,7 +452,7 @@ static int configure_leds(void)
 		hal_gpio_init_out(leds[i].pin, 1);
 	}
 
-	k_delayed_work_init(&led_timer, led_timeout);
+	k_work_init_delayable(&led_timer, led_timeout);
 	return 0;
 }
 
@@ -466,7 +466,7 @@ static int erase_storage(void)
 
 void board_refresh_display(void)
 {
-	k_delayed_work_submit(&epd_work, K_NO_WAIT);
+	k_work_reschedule(&epd_work, K_NO_WAIT);
 }
 
 int board_init(void)
@@ -494,8 +494,8 @@ int board_init(void)
 		return -EIO;
 	}
 
-	k_delayed_work_init(&epd_work, epd_update);
-	k_delayed_work_init(&long_press_work, long_press);
+	k_work_init_delayable(&epd_work, epd_update);
+	k_work_init_delayable(&long_press_work, long_press);
 
 	pressed = button_is_pressed();
 	if (pressed) {
diff --git a/docs/btshell/btshell_GAP.rst b/docs/btshell/btshell_GAP.rst
index ce6475554d..738d146c5b 100644
--- a/docs/btshell/btshell_GAP.rst
+++ b/docs/btshell/btshell_GAP.rst
@@ -411,7 +411,7 @@ Advertising with Extended Advertising enabled
 +------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+
 |                              | scan\_req\_notif         | [``0``-1]                  | Enable SCAN\_REQ notifications                                                      |
 +------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+
-| **advertise-set-addr**       |                          |                            | Configure *random* adress for instance                                              |
+| **advertise-set-addr**       |                          |                            | Configure *random* address for instance                                              |
 +------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+
 |                              | instance                 | [``0``-UINT8\_MAX]         | Advertising instance                                                                |
 +------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+
diff --git a/docs/index.rst b/docs/index.rst
index b41b1b283e..b07f1f88d0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -46,7 +46,7 @@ ideal wireless technology for the Internet of Things (IoT).
 -  LE Secure Connections featuring FIPS-compliant algorithms.
 -  LE Data Length Extension for higher throughput
 -  **Coming Soon**: Assigning an Internet Protocol (IP) address
-   (complaint with the IPv6 or 6LoWPAN standard) to a Bluetooth device
+   (compliant with the IPv6 or 6LoWPAN standard) to a Bluetooth device
    through Internet Protocol Support Profile (IPSP)
 
 The Bluetooth 5 is backward compatible with previous Bluetooth version
diff --git a/nimble/controller/include/controller/ble_ll.h b/nimble/controller/include/controller/ble_ll.h
index 24d7db3003..898907a19d 100644
--- a/nimble/controller/include/controller/ble_ll.h
+++ b/nimble/controller/include/controller/ble_ll.h
@@ -48,7 +48,7 @@ extern "C" {
         if (hal_debugger_connected()) { \
             assert(0);\
         } else {\
-            ble_ll_hci_ev_send_vendor_err(__FILE__, __LINE__); \
+            ble_ll_hci_ev_send_vs_assert(__FILE__, __LINE__); \
             while(1) {}\
         }\
     }
@@ -108,11 +108,13 @@ struct ble_ll_obj
     /* Current Link Layer state */
     uint8_t ll_state;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /* Number of ACL data packets supported */
     uint8_t ll_num_acl_pkts;
 
     /* ACL data packet size */
     uint16_t ll_acl_pkt_size;
+#endif
 
     /* Preferred PHY's */
     uint8_t ll_pref_tx_phys;
@@ -129,14 +131,18 @@ struct ble_ll_obj
     struct ble_ll_pkt_q ll_rx_pkt_q;
 
     /* Packet transmit queue */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     struct ble_npl_event ll_tx_pkt_ev;
+#endif
     struct ble_ll_pkt_q ll_tx_pkt_q;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /* Data buffer overflow event */
     struct ble_npl_event ll_dbuf_overflow_ev;
 
     /* Number of completed packets event */
     struct ble_npl_event ll_comp_pkt_ev;
+#endif
 
     /* HW error callout */
     struct ble_npl_callout ll_hw_err_timer;
@@ -214,12 +220,24 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats;
 
 /* States */
 #define BLE_LL_STATE_STANDBY        (0)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_LL_STATE_ADV            (1)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #define BLE_LL_STATE_SCANNING       (2)
-#define BLE_LL_STATE_INITIATING     (3)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_LL_STATE_CONNECTION     (4)
+#endif
+#if MYNEWT_VAL(BLE_LL_DTM)
 #define BLE_LL_STATE_DTM            (5)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
 #define BLE_LL_STATE_SYNC           (6)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#define BLE_LL_STATE_SCAN_AUX       (7)
+#endif
 
 /* LL Features */
 #define BLE_LL_FEAT_LE_ENCRYPTION    (0x0000000001)
@@ -354,6 +372,7 @@ struct ble_dev_addr
 #define BLE_LL_EXT_ADV_FLAGS_SIZE       (1)
 #define BLE_LL_EXT_ADV_ADVA_SIZE        (6)
 #define BLE_LL_EXT_ADV_TARGETA_SIZE     (6)
+#define BLE_LL_EXT_ADV_CTE_INFO_SIZE    (1)
 #define BLE_LL_EXT_ADV_DATA_INFO_SIZE   (2)
 #define BLE_LL_EXT_ADV_AUX_PTR_SIZE     (3)
 #define BLE_LL_EXT_ADV_SYNC_INFO_SIZE   (18)
diff --git a/nimble/controller/include/controller/ble_ll_conn.h b/nimble/controller/include/controller/ble_ll_conn.h
index d7db6878a1..c3073c6e7d 100644
--- a/nimble/controller/include/controller/ble_ll_conn.h
+++ b/nimble/controller/include/controller/ble_ll_conn.h
@@ -34,8 +34,13 @@ extern "C" {
 
 /* Roles */
 #define BLE_LL_CONN_ROLE_NONE           (0)
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_LL_CONN_ROLE_MASTER         (1)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 #define BLE_LL_CONN_ROLE_SLAVE          (2)
+#endif
 
 /* Connection states */
 #define BLE_LL_CONN_STATE_IDLE          (0)
@@ -45,16 +50,6 @@ extern "C" {
 /* Channel map size */
 #define BLE_LL_CONN_CHMAP_LEN           (5)
 
-/* Definitions for source clock accuracy */
-#define BLE_MASTER_SCA_251_500_PPM      (0)
-#define BLE_MASTER_SCA_151_250_PPM      (1)
-#define BLE_MASTER_SCA_101_150_PPM      (2)
-#define BLE_MASTER_SCA_76_100_PPM       (3)
-#define BLE_MASTER_SCA_51_75_PPM        (4)
-#define BLE_MASTER_SCA_31_50_PPM        (5)
-#define BLE_MASTER_SCA_21_30_PPM        (6)
-#define BLE_MASTER_SCA_0_20_PPM         (7)
-
 /* Definition for RSSI when the RSSI is unknown */
 #define BLE_LL_CONN_UNKNOWN_RSSI        (127)
 
@@ -188,28 +183,6 @@ struct hci_conn_update
     uint16_t max_ce_len;
 };
 
-struct hci_ext_conn_params
-{
-    uint16_t scan_itvl;
-    uint16_t scan_window;
-    uint16_t conn_itvl_min;
-    uint16_t conn_itvl_max;
-    uint16_t conn_latency;
-    uint16_t supervision_timeout;
-    uint16_t min_ce_len;
-    uint16_t max_ce_len;
-};
-
-struct hci_ext_create_conn
-{
-    uint8_t filter_policy;
-    uint8_t own_addr_type;
-    uint8_t peer_addr_type;
-    uint8_t peer_addr[BLE_DEV_ADDR_LEN];
-    uint8_t init_phy_mask;
-    struct hci_ext_conn_params params[3];
-};
-
 /* Connection state machine */
 struct ble_ll_conn_sm
 {
@@ -224,9 +197,6 @@ struct ble_ll_conn_sm
     /* 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;
@@ -240,7 +210,6 @@ struct ble_ll_conn_sm
     uint16_t rem_max_rx_time;
     uint16_t eff_max_tx_time;
     uint16_t eff_max_rx_time;
-    uint8_t max_tx_octets_phy_mode[BLE_PHY_NUM_MODE];
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
     uint16_t host_req_max_tx_time;
 #endif
@@ -327,6 +296,9 @@ struct ble_ll_conn_sm
     uint8_t own_addr_type;
     uint8_t peer_addr_type;
     uint8_t peer_addr[BLE_DEV_ADDR_LEN];
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    uint8_t peer_addr_resolved;
+#endif
 
     /*
      * XXX: TODO. Could save memory. Have single event at LL and put these
@@ -378,11 +350,6 @@ struct ble_ll_conn_sm
     /* XXX: for now, just store them all */
     struct ble_ll_conn_params conn_cp;
 
-    struct ble_ll_scan_sm *scansm;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct hci_ext_create_conn initial_params;
-#endif
-
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
     uint8_t  sync_transfer_mode;
     uint16_t sync_transfer_skip;
@@ -408,8 +375,17 @@ struct ble_ll_conn_sm
 #define CONN_F_AUX_CONN_REQ(csm)  ((csm)->csmflags.cfbit.aux_conn_req)
 
 /* Role */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define CONN_IS_MASTER(csm)         (csm->conn_role == BLE_LL_CONN_ROLE_MASTER)
+#else
+#define CONN_IS_MASTER(csm)         (false)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 #define CONN_IS_SLAVE(csm)          (csm->conn_role == BLE_LL_CONN_ROLE_SLAVE)
+#else
+#define CONN_IS_SLAVE(csm)          (false)
+#endif
 
 /*
  * Given a handle, returns an active connection state machine (or NULL if the
@@ -425,6 +401,34 @@ uint8_t ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn, uint16_t latency);
 void ble_ll_conn_get_anchor(struct ble_ll_conn_sm *connsm, uint16_t conn_event,
                             uint32_t *anchor, uint8_t *anchor_usecs);
 
+struct ble_ll_scan_addr_data;
+struct ble_ll_scan_pdu_data;
+
+uint8_t ble_ll_conn_tx_connect_ind_pducb(uint8_t *dptr, void *pducb_arg,
+                                         uint8_t *hdr_byte);
+void ble_ll_conn_prepare_connect_ind(struct ble_ll_conn_sm *connsm,
+                                    struct ble_ll_scan_pdu_data *pdu_data,
+                                    uint8_t adva_type, uint8_t *adva,
+                                    uint8_t inita_type, uint8_t *inita,
+                                    int rpa_index, uint8_t channel);
+
+/* Send CONNECT_IND/AUX_CONNECT_REQ */
+int ble_ll_conn_send_connect_req(struct os_mbuf *rxpdu,
+                                 struct ble_ll_scan_addr_data *addrd,
+                                 uint8_t ext);
+/* Cancel connection after AUX_CONNECT_REQ was sent */
+void ble_ll_conn_send_connect_req_cancel(void);
+/* Signal connection created via CONNECT_IND */
+void ble_ll_conn_created_on_legacy(struct os_mbuf *rxpdu,
+                                   struct ble_ll_scan_addr_data *addrd,
+                                   uint8_t *targeta);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+/* Signal connection created via AUX_CONNECT_REQ */
+void ble_ll_conn_created_on_aux(struct os_mbuf *rxpdu,
+                                struct ble_ll_scan_addr_data *addrd,
+                                uint8_t *targeta);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/controller/include/controller/ble_ll_ctrl.h b/nimble/controller/include/controller/ble_ll_ctrl.h
index 15a45b2a08..62e1a53569 100644
--- a/nimble/controller/include/controller/ble_ll_ctrl.h
+++ b/nimble/controller/include/controller/ble_ll_ctrl.h
@@ -315,7 +315,9 @@ int ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status);
 void ble_ll_calc_session_key(struct ble_ll_conn_sm *connsm);
 void ble_ll_ctrl_phy_update_proc_complete(struct ble_ll_conn_sm *connsm);
 void ble_ll_ctrl_initiate_dle(struct ble_ll_conn_sm *connsm);
-void ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line);
+void ble_ll_hci_ev_send_vs_assert(const char *file, uint32_t line);
+void ble_ll_hci_ev_send_vs_llcp_trace(uint8_t type, uint16_t handle, uint16_t count,
+                                      void *pdu, size_t length);
 
 uint8_t ble_ll_ctrl_phy_tx_transition_get(uint8_t phy_mask);
 uint8_t ble_ll_ctrl_phy_from_phy_mask(uint8_t phy_mask);
diff --git a/nimble/controller/include/controller/ble_ll_hci.h b/nimble/controller/include/controller/ble_ll_hci.h
index 6a9e48e537..a9ea3bb055 100644
--- a/nimble/controller/include/controller/ble_ll_hci.h
+++ b/nimble/controller/include/controller/ble_ll_hci.h
@@ -43,6 +43,20 @@ extern const uint8_t g_ble_ll_supp_cmds[BLE_LL_SUPP_CMD_LEN];
 
 typedef void (*ble_ll_hci_post_cmd_complete_cb)(void);
 
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
+typedef int (* ble_ll_hci_vs_cb_t)(uint16_t ocf,
+                                   const uint8_t *cmdbuf, uint8_t cmdlen,
+                                   uint8_t *rspbuf, uint8_t *rsplen);
+
+#define BLE_LL_HCI_VS_CMD(_ocf, _cb)    { .ocf = (_ocf), .cb = (_cb) }
+
+struct ble_ll_hci_vs_cmd {
+    uint16_t ocf;
+    ble_ll_hci_vs_cb_t cb;
+    SLIST_ENTRY(ble_ll_hci_vs_cmd) link;
+};
+#endif
+
 /* Initialize LL HCI */
 void ble_ll_hci_init(void);
 
@@ -68,6 +82,10 @@ bool ble_ll_hci_adv_mode_ext(void);
 /* Get TX power compensation rounded to integer dB */
 int8_t ble_ll_get_tx_pwr_compensation(void);
 
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
+void ble_ll_hci_vs_register(struct ble_ll_hci_vs_cmd *cmds, uint32_t num_cmds);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/controller/include/controller/ble_ll_plna.h b/nimble/controller/include/controller/ble_ll_plna.h
new file mode 100644
index 0000000000..c4fb65ea98
--- /dev/null
+++ b/nimble/controller/include/controller/ble_ll_plna.h
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_LL_PLNA_
+#define H_BLE_LL_PLNA_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_LL_PA)
+void ble_ll_plna_pa_init(void);
+void ble_ll_plna_pa_enable(void);
+void ble_ll_plna_pa_disable(void);
+#endif
+
+#if MYNEWT_VAL(BLE_LL_LNA)
+void ble_ll_plna_lna_init(void);
+void ble_ll_plna_lna_enable(void);
+void ble_ll_plna_lna_disable(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_BLE_LL_PLNA_ */
diff --git a/nimble/controller/include/controller/ble_ll_resolv.h b/nimble/controller/include/controller/ble_ll_resolv.h
index 228e0a3703..b9ca7fd387 100644
--- a/nimble/controller/include/controller/ble_ll_resolv.h
+++ b/nimble/controller/include/controller/ble_ll_resolv.h
@@ -72,6 +72,12 @@ int ble_ll_resolv_local_addr_rd(const uint8_t *cmdbuf, uint8_t len,
 struct ble_ll_resolv_entry *
 ble_ll_resolv_list_find(const uint8_t *addr, uint8_t addr_type);
 
+static inline int8_t
+ble_ll_resolv_get_idx(struct ble_ll_resolv_entry *rl)
+{
+    return rl - g_ble_ll_resolv_list;
+}
+
 /* Returns true if address resolution is enabled */
 uint8_t ble_ll_resolv_enabled(void);
 
diff --git a/nimble/controller/include/controller/ble_ll_rfmgmt.h b/nimble/controller/include/controller/ble_ll_rfmgmt.h
index 37b81a88be..5e2d636ff8 100644
--- a/nimble/controller/include/controller/ble_ll_rfmgmt.h
+++ b/nimble/controller/include/controller/ble_ll_rfmgmt.h
@@ -51,7 +51,7 @@ static inline void ble_ll_rfmgmt_reset(void) { }
 static inline void ble_ll_rfmgmt_scan_changed(bool e, uint32_t n) { }
 static inline void ble_ll_rfmgmt_sched_changed(struct ble_ll_sched_item *f) { }
 static inline void ble_ll_rfmgmt_release(void) { }
-static inline uint32_t ble_ll_rfmgmt_enable_now(void) { return 0; }
+static inline uint32_t ble_ll_rfmgmt_enable_now(void) { return os_cputime_get32(); }
 static inline bool ble_ll_rfmgmt_is_enabled(void) { return true; }
 
 #endif
diff --git a/nimble/controller/include/controller/ble_ll_scan.h b/nimble/controller/include/controller/ble_ll_scan.h
index 139ad5e1f7..a44e431379 100644
--- a/nimble/controller/include/controller/ble_ll_scan.h
+++ b/nimble/controller/include/controller/ble_ll_scan.h
@@ -80,67 +80,15 @@ struct ble_ll_scan_timing {
     uint32_t start_time;
 };
 
-struct ble_ll_scan_params
+struct ble_ll_scan_phy
 {
     uint8_t phy;
-    uint8_t own_addr_type;
-    uint8_t scan_filt_policy;
     uint8_t configured;
     uint8_t scan_type;
     uint8_t scan_chan;
     struct ble_ll_scan_timing timing;
 };
 
-#define BLE_LL_AUX_HAS_ADVA                     0x01
-#define BLE_LL_AUX_HAS_TARGETA                  0x02
-#define BLE_LL_AUX_HAS_ADI                      0x04
-#define BLE_LL_AUX_IS_MATCHED                   0x08
-#define BLE_LL_AUX_IS_TARGETA_RESOLVED          0x10
-
-#define BLE_LL_AUX_FLAG_HCI_SENT_ANY            0x02
-#define BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED      0x04
-#define BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED      0x08
-#define BLE_LL_AUX_FLAG_SCAN_COMPLETE           0x10
-#define BLE_LL_AUX_FLAG_SCAN_ERROR              0x20
-#define BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED        0x40
-#define BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED      0x80
-
-struct ble_ll_aux_data {
-    uint8_t flags;
-
-    /*
-     * Since aux_data can be accessed from ISR and LL, we have separate copies
-     * of flags to make sure that ISR does not modify flags while LL uses them.
-     * ISR updates 'flags_isr' and LL adds these to 'flags_ll' which it then
-     * uses for further processing allowing to update 'flags_isr' if another
-     * scan for given 'aux_data' is scheduled. Note that flags must not be unset
-     * while aux_data is valid.
-     */
-    uint8_t flags_isr;
-    uint8_t flags_ll;
-
-    uint8_t ref_cnt;
-    uint8_t chan;
-    uint8_t aux_phy;
-    uint8_t aux_primary_phy;
-    uint8_t mode;
-    uint8_t scanning;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    int8_t rpa_index;
-#endif
-    uint16_t adi;
-    uint32_t offset;
-    uint8_t offset_units;
-    uint8_t adva[6];
-    uint8_t adva_type;
-    uint8_t targeta[6];
-    uint8_t targeta_type;
-    uint16_t evt_type;
-    struct ble_ll_sched_item sch;
-    struct ble_hci_ev *evt;
-    struct ble_npl_event ev;
-};
-
 struct ble_ll_scan_pdu_data {
     uint8_t hdr_byte;
     /* ScanA for SCAN_REQ and InitA for CONNECT_IND */
@@ -151,10 +99,26 @@ struct ble_ll_scan_pdu_data {
     uint8_t adva[BLE_DEV_ADDR_LEN];
 };
 
+struct ble_ll_scan_addr_data {
+    uint8_t *adva;
+    uint8_t *targeta;
+    uint8_t *adv_addr;
+    uint8_t adva_type : 1;
+    uint8_t targeta_type : 1;
+    uint8_t adv_addr_type : 1;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    uint8_t adva_resolved : 1;
+    uint8_t targeta_resolved : 1;
+    int8_t rpa_index;
+#endif
+};
+
 struct ble_ll_scan_sm
 {
     uint8_t scan_enabled;
+
     uint8_t own_addr_type;
+    uint8_t scan_filt_policy;
     uint8_t scan_filt_dups;
     uint8_t scan_rsp_pending;
     uint8_t scan_rsp_cons_fails;
@@ -175,36 +139,42 @@ struct ble_ll_scan_sm
     struct ble_npl_event scan_interrupted_ev;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct hal_timer duration_timer;
-    struct hal_timer period_timer;
-    uint32_t duration_ticks;
-    uint32_t period_ticks;
+    struct ble_npl_callout duration_timer;
+    struct ble_npl_callout period_timer;
+    ble_npl_time_t duration_ticks;
+    ble_npl_time_t period_ticks;
     uint8_t ext_scanning;
 #endif
 
     uint8_t restart_timer_needed;
-    struct ble_ll_aux_data *cur_aux_data;
 
-    struct ble_ll_scan_params *scanp;
-    struct ble_ll_scan_params *scanp_next;
-    struct ble_ll_scan_params scanp_phys[BLE_LL_SCAN_PHY_NUMBER];
+    struct ble_ll_scan_phy *scanp;
+    struct ble_ll_scan_phy *scanp_next;
+    struct ble_ll_scan_phy scan_phys[BLE_LL_SCAN_PHY_NUMBER];
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    /* Connection sm for initiator scan */
+    struct ble_ll_conn_sm *connsm;
+#endif
 };
 
 /* Scan types */
 #define BLE_SCAN_TYPE_PASSIVE   (BLE_HCI_SCAN_TYPE_PASSIVE)
 #define BLE_SCAN_TYPE_ACTIVE    (BLE_HCI_SCAN_TYPE_ACTIVE)
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SCAN_TYPE_INITIATE  (2)
+#endif
 
 /*---- HCI ----*/
 /* Set scanning parameters */
-int ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_scan_hci_set_params(const uint8_t *cmdbuf, uint8_t len);
 
 /* Turn scanning on/off */
-int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len);
-int ble_ll_hci_ext_scan_set_enable(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_scan_hci_set_enable(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_scan_hci_set_ext_enable(const uint8_t *cmdbuf, uint8_t len);
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-int ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_scan_hci_set_ext_params(const uint8_t *cmdbuf, uint8_t len);
 #endif
 
 /*--- Controller Internal API ---*/
@@ -230,13 +200,12 @@ int ble_ll_scan_can_chg_whitelist(void);
 /* Boolean function returning true if scanning enabled */
 int ble_ll_scan_enabled(void);
 
-/* Boolean function returns true if whitelist is enabled for scanning */
-int ble_ll_scan_whitelist_enabled(void);
-
 /* Initialize the scanner when we start initiating */
-struct hci_create_conn;
-int ble_ll_scan_initiator_start(struct hci_create_conn *hcc,
-                                struct ble_ll_scan_sm **sm);
+struct ble_ll_conn_create_scan;
+struct ble_ll_conn_create_params;
+int
+ble_ll_scan_initiator_start(struct ble_ll_conn_sm *connsm, uint8_t ext,
+                            struct ble_ll_conn_create_scan *cc_scan);
 
 /* Returns storage for PDU data (for SCAN_REQ or CONNECT_IND) */
 struct ble_ll_scan_pdu_data *ble_ll_scan_get_pdu_data(void);
@@ -254,7 +223,11 @@ uint8_t *ble_ll_scan_get_local_rpa(void);
 void ble_ll_scan_sm_stop(int chk_disable);
 
 /* Resume scanning */
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 void ble_ll_scan_chk_resume(void);
+#else
+static inline void ble_ll_scan_chk_resume(void) { };
+#endif
 
 /* Called when wait for response timer expires in scanning mode */
 void ble_ll_scan_wfr_timer_exp(void);
@@ -262,30 +235,35 @@ void ble_ll_scan_wfr_timer_exp(void);
 /* Called when scan could be interrupted  */
 void ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm);
 
-int ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf,
-                                struct ble_mbuf_hdr *ble_hdr,
-                                uint8_t **addr, uint8_t *addr_type,
-                                uint8_t **inita, uint8_t *init_addr_type,
-                                int *ext_mode);
-
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-int ble_ll_scan_update_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf,
-                                bool *adva_present);
-
-/* Initialize the extended scanner when we start initiating */
-struct hci_ext_create_conn;
-int ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc,
-                                    struct ble_ll_scan_sm **sm);
-
 /* Called to parse extended advertising*/
-struct ble_ll_aux_data *ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_scan);
-void ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_scan);
 void ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data);
 #endif
 
 /* Called to halt currently running scan */
 void ble_ll_scan_halt(void);
 
+uint8_t *ble_ll_get_scan_nrpa(void);
+uint8_t ble_ll_scan_get_own_addr_type(void);
+uint8_t ble_ll_scan_get_filt_policy(void);
+uint8_t ble_ll_scan_get_filt_dups(void);
+uint8_t ble_ll_scan_backoff_kick(void);
+void ble_ll_scan_backoff_update(int success);
+
+int ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr, bool has_aux,
+                              uint16_t adi);
+int ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr, bool has_aux,
+                               uint16_t adi);
+int ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd, uint8_t ext_adv,
+                                  uint16_t adi);
+void ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd, uint8_t ext_adv,
+                                  uint16_t adi);
+
+int
+ble_ll_scan_rx_filter(uint8_t own_addr_type, uint8_t scan_filt_policy,
+                      struct ble_ll_scan_addr_data *addrd, uint8_t *scan_ok);
+int ble_ll_scan_rx_check_init(struct ble_ll_scan_addr_data *addrd);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/controller/include/controller/ble_ll_scan_aux.h b/nimble/controller/include/controller/ble_ll_scan_aux.h
new file mode 100644
index 0000000000..c7d63b8449
--- /dev/null
+++ b/nimble/controller/include/controller/ble_ll_scan_aux.h
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_LL_SCAN_AUX_
+#define H_BLE_LL_SCAN_AUX_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+
+struct ble_ll_scan_aux_data;
+
+void ble_ll_scan_aux_init(void);
+int ble_ll_scan_aux_sched(struct ble_ll_scan_aux_data *aux, uint32_t pdu_time,
+                          uint8_t pdu_time_rem, uint32_t aux_ptr);
+int ble_ll_scan_aux_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr);
+int ble_ll_scan_aux_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok);
+void ble_ll_scan_aux_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr);
+
+void ble_ll_scan_aux_break(struct ble_ll_scan_aux_data *aux);
+void ble_ll_scan_aux_wfr_timer_exp(void);
+void ble_ll_scan_aux_halt(void);
+void ble_ll_scan_aux_sched_remove(struct ble_ll_sched_item *sch);
+
+int ble_ll_scan_aux_rx_isr_end_on_ext(struct ble_ll_scan_sm *scansm,
+                                      struct os_mbuf *rxpdu);
+void ble_ll_scan_aux_pkt_in_on_ext(struct os_mbuf *rxpdu,
+                                   struct ble_mbuf_hdr *rxhdr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_BLE_LL_SCAN_AUX_ */
diff --git a/nimble/controller/include/controller/ble_ll_sched.h b/nimble/controller/include/controller/ble_ll_sched.h
index a614cf0905..45e148cbd6 100644
--- a/nimble/controller/include/controller/ble_ll_sched.h
+++ b/nimble/controller/include/controller/ble_ll_sched.h
@@ -25,8 +25,8 @@ extern "C" {
 #endif
 
 /* Time per BLE scheduler slot */
-#define BLE_LL_SCHED_USECS_PER_SLOT         (1250)
-#define BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT   (41)    /* 1 tick = 30.517 usecs */
+#define BLE_LL_SCHED_USECS_PER_SLOT   (1250)
+#define BLE_LL_SCHED_TICKS_PER_SLOT   (41)    /* 1 tick = 30.517 usecs */
 
 /*
  * Worst case time needed for scheduled advertising item. This is the longest
@@ -67,10 +67,10 @@ extern uint8_t g_ble_ll_sched_offset_ticks;
 #define BLE_LL_SCHED_TYPE_ADV       (1)
 #define BLE_LL_SCHED_TYPE_SCAN      (2)
 #define BLE_LL_SCHED_TYPE_CONN      (3)
-#define BLE_LL_SCHED_TYPE_AUX_SCAN  (4)
 #define BLE_LL_SCHED_TYPE_DTM       (5)
 #define BLE_LL_SCHED_TYPE_PERIODIC  (6)
 #define BLE_LL_SCHED_TYPE_SYNC      (7)
+#define BLE_LL_SCHED_TYPE_SCAN_AUX  (8)
 
 /* Return values for schedule callback. */
 #define BLE_LL_SCHED_STATE_RUNNING  (0)
@@ -159,8 +159,7 @@ int ble_ll_sched_adv_new(struct ble_ll_sched_item *sch,
                          ble_ll_sched_adv_new_cb cb, void *arg);
 
 /* Schedule periodic advertising event */
-int ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start,
-                              bool after_overlap);
+int ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, bool first_event);
 
 int ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
                                  uint32_t anchor_point,
@@ -171,7 +170,7 @@ int ble_ll_sched_sync(struct ble_ll_sched_item *sch,
                       int8_t phy_mode);
 
 /* Reschedule an advertising event */
-int ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start,
+int ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch,
                                 uint32_t max_delay_ticks);
 
 /* Reschedule and advertising pdu */
@@ -199,7 +198,8 @@ int ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr,
                           struct ble_ll_scan_sm *scansm,
                           struct ble_ll_aux_data *aux_scan);
 
-int ble_ll_sched_scan_req_over_aux_ptr(uint32_t chan, uint8_t phy_mode);
+int ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch, uint32_t pdu_time,
+                          uint8_t pdu_time_rem, uint32_t offset_us);
 #endif
 
 /* Stop the scheduler */
diff --git a/nimble/controller/include/controller/ble_ll_sync.h b/nimble/controller/include/controller/ble_ll_sync.h
index 712af6dffc..8002d2a317 100644
--- a/nimble/controller/include/controller/ble_ll_sync.h
+++ b/nimble/controller/include/controller/ble_ll_sync.h
@@ -30,6 +30,7 @@
 extern "C" {
 #endif
 
+struct ble_ll_scan_addr_data;
 struct ble_ll_sync_sm;
 
 int ble_ll_sync_create(const uint8_t *cmdbuf, uint8_t len);
@@ -48,8 +49,7 @@ void ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm,
                               uint16_t max_skip, uint32_t sync_timeout);
 void ble_ll_sync_transfer_disconnected(struct ble_ll_conn_sm *connsm);
 
-void ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type,
-                            int rpa_index, uint8_t sid,
+void ble_ll_sync_info_event(struct ble_ll_scan_addr_data *addrd, uint8_t sid,
                             struct ble_mbuf_hdr *rxhdr,
                             const uint8_t *syncinfo);
 
diff --git a/nimble/controller/include/controller/ble_phy.h b/nimble/controller/include/controller/ble_phy.h
index cd8350d657..47f3afd4ec 100644
--- a/nimble/controller/include/controller/ble_phy.h
+++ b/nimble/controller/include/controller/ble_phy.h
@@ -202,6 +202,11 @@ void ble_phy_resolv_list_disable(void);
 #define BLE_PHY_MASK_2M             (BLE_HCI_LE_PHY_2M_PREF_MASK)
 #define BLE_PHY_MASK_CODED          (BLE_HCI_LE_PHY_CODED_PREF_MASK)
 
+/* PHY indices (for a zero-based array) */
+#define BLE_PHY_IDX_1M              (0)
+#define BLE_PHY_IDX_2M              (1)
+#define BLE_PHY_IDX_CODED           (2)
+
 #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) || MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY))
 uint32_t ble_phy_mode_pdu_start_off(int phy);
 void ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode);
diff --git a/nimble/controller/pkg.yml b/nimble/controller/pkg.yml
index 96c636798b..9661bb31e8 100644
--- a/nimble/controller/pkg.yml
+++ b/nimble/controller/pkg.yml
@@ -29,6 +29,10 @@ pkg.req_apis:
     - ble_driver
     - ble_transport
     - stats
+pkg.req_apis.BLE_LL_PA:
+    - ble_ll_pa
+pkg.req_apis.BLE_LL_LNA:
+    - ble_ll_lna
 
 pkg.deps:
     - "@apache-mynewt-core/kernel/os"
diff --git a/nimble/controller/src/ble_ll.c b/nimble/controller/src/ble_ll.c
index cf83b79c38..1e378bc61b 100644
--- a/nimble/controller/src/ble_ll.c
+++ b/nimble/controller/src/ble_ll.c
@@ -37,13 +37,17 @@
 #include "controller/ble_ll_adv.h"
 #include "controller/ble_ll_sched.h"
 #include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_scan_aux.h"
 #include "controller/ble_ll_hci.h"
 #include "controller/ble_ll_whitelist.h"
 #include "controller/ble_ll_resolv.h"
 #include "controller/ble_ll_rfmgmt.h"
 #include "controller/ble_ll_trace.h"
 #include "controller/ble_ll_sync.h"
+#include "controller/ble_ll_plna.h"
 #include "ble_ll_conn_priv.h"
+#include "ble_ll_hci_priv.h"
+#include "ble_ll_priv.h"
 
 #if MYNEWT_VAL(BLE_LL_DTM)
 #include "ble_ll_dtm_priv.h"
@@ -61,49 +65,174 @@
  * right thing to do.
  */
 
+int8_t g_ble_ll_tx_power = MYNEWT_VAL(BLE_LL_TX_PWR_DBM);
+
 /* Supported states */
-#define BLE_LL_S_NCA                    (0x00000000001)
-#define BLE_LL_S_SA                     (0x00000000002)
-#define BLE_LL_S_CA                     (0x00000000004)
-#define BLE_LL_S_HDCA                   (0x00000000008)
-#define BLE_LL_S_PS                     (0x00000000010)
-#define BLE_LL_S_AS                     (0x00000000020)
-#define BLE_LL_S_INIT                   (0x00000000040)
-#define BLE_LL_S_SLAVE                  (0x00000000080)
-#define BLE_LL_S_NCA_PS                 (0x00000000100)
-#define BLE_LL_S_SA_PS                  (0x00000000200)
-#define BLE_LL_S_CA_PS                  (0x00000000400)
-#define BLE_LL_S_HDCA_PS                (0x00000000800)
-#define BLE_LL_S_NCA_AS                 (0x00000001000)
-#define BLE_LL_S_SA_AS                  (0x00000002000)
-#define BLE_LL_S_CA_AS                  (0x00000004000)
-#define BLE_LL_S_HDCA_AS                (0x00000008000)
-#define BLE_LL_S_NCA_INIT               (0x00000010000)
-#define BLE_LL_S_SA_INIT                (0x00000020000)
-#define BLE_LL_S_NCA_MASTER             (0x00000040000)
-#define BLE_LL_S_SA_MASTER              (0x00000080000)
-#define BLE_LL_S_NCA_SLAVE              (0x00000100000)
-#define BLE_LL_S_SA_SLAVE               (0x00000200000)
-#define BLE_LL_S_PS_INIT                (0x00000400000)
-#define BLE_LL_S_AS_INIT                (0x00000800000)
-#define BLE_LL_S_PS_MASTER              (0x00001000000)
-#define BLE_LL_S_AS_MASTER              (0x00002000000)
-#define BLE_LL_S_PS_SLAVE               (0x00004000000)
-#define BLE_LL_S_AS_SLAVE               (0x00008000000)
-#define BLE_LL_S_INIT_MASTER            (0x00010000000)
-#define BLE_LL_S_LDCA                   (0x00020000000)
-#define BLE_LL_S_LDCA_PS                (0x00040000000)
-#define BLE_LL_S_LDCA_AS                (0x00080000000)
-#define BLE_LL_S_CA_INIT                (0x00100000000)
-#define BLE_LL_S_HDCA_INIT              (0x00200000000)
-#define BLE_LL_S_LDCA_INIT              (0x00400000000)
-#define BLE_LL_S_CA_MASTER              (0x00800000000)
-#define BLE_LL_S_HDCA_MASTER            (0x01000000000)
-#define BLE_LL_S_LDCA_MASTER            (0x02000000000)
-#define BLE_LL_S_CA_SLAVE               (0x04000000000)
-#define BLE_LL_S_HDCA_SLAVE             (0x08000000000)
-#define BLE_LL_S_LDCA_SLAVE             (0x10000000000)
-#define BLE_LL_S_INIT_SLAVE             (0x20000000000)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+#define BLE_LL_S_NCA                    ((uint64_t)1 << 0)
+#define BLE_LL_S_SA                     ((uint64_t)1 << 1)
+#else
+#define BLE_LL_S_NCA                    ((uint64_t)0 << 0)
+#define BLE_LL_S_SA                     ((uint64_t)0 << 1)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_CA                     ((uint64_t)1 << 2)
+#define BLE_LL_S_HDCA                   ((uint64_t)1 << 3)
+#else
+#define BLE_LL_S_CA                     ((uint64_t)0 << 2)
+#define BLE_LL_S_HDCA                   ((uint64_t)0 << 3)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_PS                     ((uint64_t)1 << 4)
+#define BLE_LL_S_AS                     ((uint64_t)1 << 5)
+#else
+#define BLE_LL_S_PS                     ((uint64_t)0 << 4)
+#define BLE_LL_S_AS                     ((uint64_t)0 << 5)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_INIT                   ((uint64_t)1 << 6)
+#else
+#define BLE_LL_S_INIT                   ((uint64_t)0 << 6)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_SLAVE                  ((uint64_t)1 << 7)
+#else
+#define BLE_LL_S_SLAVE                  ((uint64_t)0 << 7)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_NCA_PS                 ((uint64_t)1 << 8)
+#define BLE_LL_S_SA_PS                  ((uint64_t)1 << 9)
+#else
+#define BLE_LL_S_NCA_PS                 ((uint64_t)0 << 8)
+#define BLE_LL_S_SA_PS                  ((uint64_t)0 << 9)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_CA_PS                  ((uint64_t)1 << 10)
+#define BLE_LL_S_HDCA_PS                ((uint64_t)1 << 11)
+#else
+#define BLE_LL_S_CA_PS                  ((uint64_t)0 << 10)
+#define BLE_LL_S_HDCA_PS                ((uint64_t)0 << 11)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_NCA_AS                 ((uint64_t)1 << 12)
+#define BLE_LL_S_SA_AS                  ((uint64_t)1 << 13)
+#else
+#define BLE_LL_S_NCA_AS                 ((uint64_t)0 << 12)
+#define BLE_LL_S_SA_AS                  ((uint64_t)0 << 13)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_CA_AS                  ((uint64_t)1 << 14)
+#define BLE_LL_S_HDCA_AS                ((uint64_t)1 << 15)
+#else
+#define BLE_LL_S_CA_AS                  ((uint64_t)0 << 14)
+#define BLE_LL_S_HDCA_AS                ((uint64_t)0 << 15)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_NCA_INIT               ((uint64_t)1 << 16)
+#define BLE_LL_S_SA_INIT                ((uint64_t)1 << 17)
+#define BLE_LL_S_NCA_MASTER             ((uint64_t)1 << 18)
+#define BLE_LL_S_SA_MASTER              ((uint64_t)1 << 19)
+#else
+#define BLE_LL_S_NCA_INIT               ((uint64_t)0 << 16)
+#define BLE_LL_S_SA_INIT                ((uint64_t)0 << 17)
+#define BLE_LL_S_NCA_MASTER             ((uint64_t)0 << 18)
+#define BLE_LL_S_SA_MASTER              ((uint64_t)0 << 19)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_NCA_SLAVE              ((uint64_t)1 << 20)
+#define BLE_LL_S_SA_SLAVE               ((uint64_t)1 << 21)
+#else
+#define BLE_LL_S_NCA_SLAVE              ((uint64_t)0 << 20)
+#define BLE_LL_S_SA_SLAVE               ((uint64_t)0 << 21)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+/* We do not support passive scanning while initiating yet */
+#define BLE_LL_S_PS_INIT                ((uint64_t)0 << 22)
+/* We do not support active scanning while initiating yet */
+#define BLE_LL_S_AS_INIT                ((uint64_t)0 << 23)
+#define BLE_LL_S_PS_MASTER              ((uint64_t)1 << 24)
+#define BLE_LL_S_AS_MASTER              ((uint64_t)1 << 25)
+#else
+#define BLE_LL_S_PS_INIT                ((uint64_t)0 << 22)
+#define BLE_LL_S_AS_INIT                ((uint64_t)0 << 23)
+#define BLE_LL_S_PS_MASTER              ((uint64_t)0 << 24)
+#define BLE_LL_S_AS_MASTER              ((uint64_t)0 << 25)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_PS_SLAVE               ((uint64_t)1 << 26)
+#define BLE_LL_S_AS_SLAVE               ((uint64_t)1 << 27)
+#else
+#define BLE_LL_S_PS_SLAVE               ((uint64_t)0 << 26)
+#define BLE_LL_S_AS_SLAVE               ((uint64_t)0 << 27)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_INIT_MASTER            ((uint64_t)1 << 28)
+#else
+#define BLE_LL_S_INIT_MASTER            ((uint64_t)0 << 28)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_LDCA                   ((uint64_t)1 << 29)
+#else
+#define BLE_LL_S_LDCA                   ((uint64_t)0 << 29)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+#define BLE_LL_S_LDCA_PS                ((uint64_t)1 << 30)
+#define BLE_LL_S_LDCA_AS                ((uint64_t)1 << 31)
+#else
+#define BLE_LL_S_LDCA_PS                ((uint64_t)0 << 30)
+#define BLE_LL_S_LDCA_AS                ((uint64_t)0 << 31)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_CA_INIT                ((uint64_t)1 << 32)
+#define BLE_LL_S_HDCA_INIT              ((uint64_t)1 << 33)
+#define BLE_LL_S_LDCA_INIT              ((uint64_t)1 << 34)
+#else
+#define BLE_LL_S_CA_INIT                ((uint64_t)0 << 32)
+#define BLE_LL_S_HDCA_INIT              ((uint64_t)0 << 33)
+#define BLE_LL_S_LDCA_INIT              ((uint64_t)0 << 34)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_CA_MASTER              ((uint64_t)1 << 35)
+#define BLE_LL_S_HDCA_MASTER            ((uint64_t)1 << 36)
+#define BLE_LL_S_LDCA_MASTER            ((uint64_t)1 << 37)
+#else
+#define BLE_LL_S_CA_MASTER              ((uint64_t)0 << 35)
+#define BLE_LL_S_HDCA_MASTER            ((uint64_t)0 << 36)
+#define BLE_LL_S_LDCA_MASTER            ((uint64_t)0 << 37)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+#define BLE_LL_S_CA_SLAVE               ((uint64_t)1 << 38)
+#define BLE_LL_S_HDCA_SLAVE             ((uint64_t)1 << 39)
+#define BLE_LL_S_LDCA_SLAVE             ((uint64_t)1 << 40)
+#else
+#define BLE_LL_S_CA_SLAVE               ((uint64_t)0 << 38)
+#define BLE_LL_S_HDCA_SLAVE             ((uint64_t)0 << 39)
+#define BLE_LL_S_LDCA_SLAVE             ((uint64_t)0 << 40)
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+#define BLE_LL_S_INIT_SLAVE             ((uint64_t)1 << 41)
+#else
+#define BLE_LL_S_INIT_SLAVE             ((uint64_t)0 << 41)
+#endif
 
 #define BLE_LL_SUPPORTED_STATES             \
 (                                           \
@@ -223,8 +352,10 @@ STATS_NAME_START(ble_ll_stats)
 STATS_NAME_END(ble_ll_stats)
 
 static void ble_ll_event_rx_pkt(struct ble_npl_event *ev);
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static void ble_ll_event_tx_pkt(struct ble_npl_event *ev);
 static void ble_ll_event_dbuf_overflow(struct ble_npl_event *ev);
+#endif
 
 #if MYNEWT
 
@@ -576,10 +707,23 @@ ble_ll_set_random_addr(const uint8_t *cmdbuf, uint8_t len, bool hci_adv_ext)
      * Test specification extends this also to initiating.
      */
 
-    if (g_ble_ll_conn_create_sm || ble_ll_scan_enabled() ||
-                                (!hci_adv_ext && ble_ll_adv_enabled())) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (g_ble_ll_conn_create_sm.connsm) {
         return BLE_ERR_CMD_DISALLOWED;
     }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    if (ble_ll_scan_enabled()){
+        return BLE_ERR_CMD_DISALLOWED;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    if (!hci_adv_ext && ble_ll_adv_enabled()) {
+        return BLE_ERR_CMD_DISALLOWED;
+    }
+#endif
 
     if (!ble_ll_is_valid_random_addr(cmd->addr)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
@@ -588,12 +732,14 @@ ble_ll_set_random_addr(const uint8_t *cmdbuf, uint8_t len, bool hci_adv_ext)
     memcpy(g_random_addr, cmd->addr, BLE_DEV_ADDR_LEN);
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     /* For instance 0 we need same address if legacy advertising might be
      * used. If extended advertising is in use than this command doesn't
      * affect instance 0.
      */
     if (!hci_adv_ext)
         ble_ll_adv_set_random_addr(cmd->addr, 0);
+#endif
 #endif
 
     return BLE_ERR_SUCCESS;
@@ -667,27 +813,35 @@ ble_ll_wfr_timer_exp(void *arg)
     /* If we have started a reception, there is nothing to do here */
     if (!rx_start) {
         switch (lls) {
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
         case BLE_LL_STATE_ADV:
             ble_ll_adv_wfr_timer_exp();
             break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         case BLE_LL_STATE_CONNECTION:
             ble_ll_conn_wfr_timer_exp();
             break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
         case BLE_LL_STATE_SCANNING:
             ble_ll_scan_wfr_timer_exp();
             break;
-        case BLE_LL_STATE_INITIATING:
-            ble_ll_conn_init_wfr_timer_exp();
-            break;
-#if MYNEWT_VAL(BLE_LL_DTM)
-        case BLE_LL_STATE_DTM:
-            ble_ll_dtm_wfr_timer_exp();
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+        case BLE_LL_STATE_SCAN_AUX:
+            ble_ll_scan_aux_wfr_timer_exp();
             break;
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
         case BLE_LL_STATE_SYNC:
             ble_ll_sync_wfr_timer_exp();
             break;
+#endif
+#endif
+#if MYNEWT_VAL(BLE_LL_DTM)
+        case BLE_LL_STATE_DTM:
+            ble_ll_dtm_wfr_timer_exp();
+            break;
 #endif
         default:
             break;
@@ -703,6 +857,7 @@ ble_ll_wfr_timer_exp(void *arg)
  * Context: Link layer task
  *
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static void
 ble_ll_tx_pkt_in(void)
 {
@@ -742,6 +897,7 @@ ble_ll_tx_pkt_in(void)
         ble_ll_conn_tx_pkt_in(om, handle, length);
     }
 }
+#endif
 
 /**
  * Count Link Layer statistics for received PDUs
@@ -758,7 +914,12 @@ ble_ll_count_rx_stats(struct ble_mbuf_hdr *hdr, uint16_t len, uint8_t pdu_type)
     bool connection_data;
 
     crcok = BLE_MBUF_HDR_CRC_OK(hdr);
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     connection_data = (BLE_MBUF_HDR_RX_STATE(hdr) == BLE_LL_STATE_CONNECTION);
+#else
+    connection_data = false;
+#endif
 
 #if MYNEWT_VAL(BLE_LL_DTM)
     /* Reuse connection stats for DTM */
@@ -825,29 +986,37 @@ ble_ll_rx_pkt_in(void)
         /* Process the data or advertising pdu */
         /* Process the PDU */
         switch (BLE_MBUF_HDR_RX_STATE(ble_hdr)) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         case BLE_LL_STATE_CONNECTION:
             ble_ll_conn_rx_data_pdu(m, ble_hdr);
             /* m is going to be free by function above */
             m = NULL;
             break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
         case BLE_LL_STATE_ADV:
             ble_ll_adv_rx_pkt_in(pdu_type, rxbuf, ble_hdr);
             break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
         case BLE_LL_STATE_SCANNING:
             ble_ll_scan_rx_pkt_in(pdu_type, m, ble_hdr);
             break;
-        case BLE_LL_STATE_INITIATING:
-            ble_ll_init_rx_pkt_in(pdu_type, rxbuf, ble_hdr);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+        case BLE_LL_STATE_SYNC:
+            ble_ll_sync_rx_pkt_in(m, ble_hdr);
+            break;
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+        case BLE_LL_STATE_SCAN_AUX:
+            ble_ll_scan_aux_rx_pkt_in(m, ble_hdr);
             break;
+#endif
+#endif
 #if MYNEWT_VAL(BLE_LL_DTM)
         case BLE_LL_STATE_DTM:
             ble_ll_dtm_rx_pkt_in(m, ble_hdr);
             break;
-#endif
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-        case BLE_LL_STATE_SYNC:
-            ble_ll_sync_rx_pkt_in(m, ble_hdr);
-            break;
 #endif
         default:
             /* Any other state should never occur */
@@ -876,6 +1045,7 @@ ble_ll_rx_pdu_in(struct os_mbuf *rxpdu)
     ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_rx_pkt_ev);
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 /**
  * Called to put a packet on the Link Layer transmit packet queue.
  *
@@ -906,6 +1076,7 @@ ble_ll_data_buffer_overflow(void)
 {
     ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_dbuf_overflow_ev);
 }
+#endif
 
 /**
  * Called when a HW error occurs.
@@ -963,27 +1134,35 @@ ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *rxhdr)
                        pdu_type);
 
     switch (g_ble_ll_data.ll_state) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_LL_STATE_CONNECTION:
         rc = ble_ll_conn_rx_isr_start(rxhdr, ble_phy_access_addr_get());
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     case BLE_LL_STATE_ADV:
         rc = ble_ll_adv_rx_isr_start(pdu_type);
         break;
-    case BLE_LL_STATE_INITIATING:
-        rc = ble_ll_init_rx_isr_start(pdu_type, rxhdr);
-        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     case BLE_LL_STATE_SCANNING:
         rc = ble_ll_scan_rx_isr_start(pdu_type, &rxhdr->rxinfo.flags);
         break;
-#if MYNEWT_VAL(BLE_LL_DTM)
-    case BLE_LL_STATE_DTM:
-        rc = ble_ll_dtm_rx_isr_start(rxhdr, ble_phy_access_addr_get());
-        break;
-#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
     case BLE_LL_STATE_SYNC:
         rc = ble_ll_sync_rx_isr_start(pdu_type, rxhdr);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    case BLE_LL_STATE_SCAN_AUX:
+        rc = ble_ll_scan_aux_rx_isr_start(pdu_type, rxhdr);
+        break;
+#endif
+#endif
+#if MYNEWT_VAL(BLE_LL_DTM)
+    case BLE_LL_STATE_DTM:
+        rc = ble_ll_dtm_rx_isr_start(rxhdr, ble_phy_access_addr_get());
+        break;
 #endif
     default:
         /* Should not be in this state! */
@@ -1035,12 +1214,14 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
     }
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_CONNECTION) {
         rc = ble_ll_conn_rx_isr_end(rxbuf, rxhdr);
         return rc;
     }
+#endif
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_SYNC) {
         rc = ble_ll_sync_rx_isr_end(rxbuf, rxhdr);
         return rc;
@@ -1088,6 +1269,7 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
     /* Hand packet to the appropriate state machine (if crc ok) */
     rxpdu = NULL;
     switch (BLE_MBUF_HDR_RX_STATE(rxhdr)) {
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     case BLE_LL_STATE_ADV:
         if (!badpkt) {
             rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
@@ -1097,6 +1279,8 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
         }
         rc = ble_ll_adv_rx_isr_end(pdu_type, rxpdu, crcok);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     case BLE_LL_STATE_SCANNING:
         if (!badpkt) {
             rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
@@ -1106,9 +1290,18 @@ ble_ll_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
         }
         rc = ble_ll_scan_rx_isr_end(rxpdu, crcok);
         break;
-    case BLE_LL_STATE_INITIATING:
-        rc = ble_ll_init_rx_isr_end(rxbuf, crcok, rxhdr);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    case BLE_LL_STATE_SCAN_AUX:
+        if (!badpkt) {
+            rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
+            if (rxpdu) {
+                ble_phy_rxpdu_copy(rxbuf, rxpdu);
+            }
+        }
+        rc = ble_ll_scan_aux_rx_isr_end(rxpdu, crcok);
         break;
+#endif
+#endif
     default:
         rc = -1;
         STATS_INC(ble_ll_stats, bad_ll_state);
@@ -1164,6 +1357,7 @@ ble_ll_event_rx_pkt(struct ble_npl_event *ev)
     ble_ll_rx_pkt_in();
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static void
 ble_ll_event_tx_pkt(struct ble_npl_event *ev)
 {
@@ -1181,6 +1375,7 @@ ble_ll_event_comp_pkts(struct ble_npl_event *ev)
 {
     ble_ll_conn_num_comp_pkts_event_send(NULL);
 }
+#endif
 
 /**
  * Link Layer task.
@@ -1198,7 +1393,7 @@ ble_ll_task(void *arg)
     ble_phy_init();
 
     /* Set output power to 1mW (0 dBm) */
-    ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
+    ble_phy_txpwr_set(g_ble_ll_tx_power);
 
     /* Register callback for transport */
     ble_hci_trans_cfg_ll(ble_ll_hci_cmd_rx, NULL, ble_ll_hci_acl_rx, NULL);
@@ -1226,6 +1421,10 @@ void
 ble_ll_state_set(uint8_t ll_state)
 {
     g_ble_ll_data.ll_state = ll_state;
+
+    if (ll_state == BLE_LL_STATE_STANDBY) {
+        BLE_LL_DEBUG_GPIO(SCHED_ITEM, 0);
+    }
 }
 
 /**
@@ -1293,9 +1492,11 @@ ble_ll_set_host_feat(const uint8_t *cmdbuf, uint8_t len)
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (!SLIST_EMPTY(&g_ble_ll_conn_active_list)) {
         return BLE_ERR_CMD_DISALLOWED;
     }
+#endif
 
     if ((cmd->bit_num > 0x3F) || (cmd->val > 1)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
@@ -1402,19 +1603,23 @@ ble_ll_reset(void)
     OS_ENTER_CRITICAL(sr);
     ble_phy_disable();
     ble_ll_sched_stop();
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     ble_ll_scan_reset();
+#endif
     ble_ll_rfmgmt_reset();
     OS_EXIT_CRITICAL(sr);
 
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     /* Stop any advertising */
     ble_ll_adv_reset();
+#endif
 
 #if MYNEWT_VAL(BLE_LL_DTM)
     ble_ll_dtm_reset();
 #endif
 
     /* Stop sync */
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     ble_ll_sync_reset();
 #endif
 
@@ -1429,8 +1634,10 @@ ble_ll_reset(void)
     g_ble_ll_data.ll_pref_tx_phys = 0;
     g_ble_ll_data.ll_pref_rx_phys = 0;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     /* Reset connection module */
     ble_ll_conn_module_reset();
+#endif
 
     /* All this does is re-initialize the event masks so call the hci init */
     ble_ll_hci_init();
@@ -1452,6 +1659,14 @@ ble_ll_reset(void)
     ble_ll_resolv_list_reset();
 #endif
 
+
+#if MYNEWT_VAL(BLE_LL_PA)
+    ble_ll_plna_pa_init();
+#endif
+#if MYNEWT_VAL(BLE_LL_LNA)
+    ble_ll_plna_lna_init();
+#endif
+
     /* Re-initialize the PHY */
     rc = ble_phy_init();
 
@@ -1553,6 +1768,10 @@ ble_ll_init(void)
 {
     int rc;
     uint64_t features;
+#if MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR)
+    uint64_t pub_dev_addr;
+    int i;
+#endif
     ble_addr_t addr;
     struct ble_ll_obj *lldata;
 
@@ -1564,10 +1783,17 @@ ble_ll_init(void)
 
     /* Set public device address if not already set */
     if (ble_ll_is_addr_empty(g_dev_addr)) {
-        /* Use sycfg address if configured, otherwise try to read from HW */
-        if (!ble_ll_is_addr_empty(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR))) {
-            memcpy(g_dev_addr, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), BLE_DEV_ADDR_LEN);
-        } else {
+#if MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR)
+        pub_dev_addr = MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR);
+
+        for (i = 0; i < BLE_DEV_ADDR_LEN; i++) {
+            g_dev_addr[i] = pub_dev_addr & 0xff;
+            pub_dev_addr >>= 8;
+        }
+#else
+        memcpy(g_dev_addr, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), BLE_DEV_ADDR_LEN);
+#endif
+        if (ble_ll_is_addr_empty(g_dev_addr)) {
             rc = ble_hw_get_public_addr(&addr);
             if (!rc) {
                 memcpy(g_dev_addr, &addr.val[0], BLE_DEV_ADDR_LEN);
@@ -1580,9 +1806,11 @@ ble_ll_init(void)
     /* Get pointer to global data object */
     lldata = &g_ble_ll_data;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /* Set acl pkt size and number */
     lldata->ll_num_acl_pkts = MYNEWT_VAL(BLE_ACL_BUF_COUNT);
     lldata->ll_acl_pkt_size = MYNEWT_VAL(BLE_ACL_BUF_SIZE);
+#endif
 
     /* Initialize eventq */
     ble_npl_eventq_init(&lldata->ll_evq);
@@ -1593,11 +1821,15 @@ ble_ll_init(void)
 
     /* Initialize transmit (from host) and receive packet (from phy) event */
     ble_npl_event_init(&lldata->ll_rx_pkt_ev, ble_ll_event_rx_pkt, NULL);
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     ble_npl_event_init(&lldata->ll_tx_pkt_ev, ble_ll_event_tx_pkt, NULL);
+#endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /* Initialize data buffer overflow event and completed packets */
     ble_npl_event_init(&lldata->ll_dbuf_overflow_ev, ble_ll_event_dbuf_overflow, NULL);
     ble_npl_event_init(&lldata->ll_comp_pkt_ev, ble_ll_event_comp_pkts, NULL);
+#endif
 
     /* Initialize the HW error timer */
     ble_npl_callout_init(&g_ble_ll_data.ll_hw_err_timer,
@@ -1611,14 +1843,20 @@ ble_ll_init(void)
     /* Init the scheduler */
     ble_ll_sched_init();
 
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     /* Initialize advertiser */
     ble_ll_adv_init();
+#endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     /* Initialize a scanner */
     ble_ll_scan_init();
+#endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /* Initialize the connection module */
     ble_ll_conn_module_init();
+#endif
 
     /* Set the supported features. NOTE: we always support extended reject. */
     features = BLE_LL_FEAT_EXTENDED_REJ;
@@ -1664,8 +1902,10 @@ ble_ll_init(void)
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
     features |= BLE_LL_FEAT_PERIODIC_ADV;
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     ble_ll_sync_init();
 #endif
+#endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
     features |= BLE_LL_FEAT_SYNC_TRANS_RECV;
@@ -1700,6 +1940,10 @@ ble_ll_init(void)
     ble_ll_dtm_init();
 #endif
 
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
+    ble_ll_hci_vs_init();
+#endif
+
 #if MYNEWT
     /* Initialize the LL task */
     os_task_init(&g_ble_ll_task, "ble_ll", ble_ll_task, NULL,
diff --git a/nimble/controller/src/ble_ll_adv.c b/nimble/controller/src/ble_ll_adv.c
index 72c4e7d67d..db17dc4c22 100644
--- a/nimble/controller/src/ble_ll_adv.c
+++ b/nimble/controller/src/ble_ll_adv.c
@@ -41,6 +41,9 @@
 #include "controller/ble_ll_utils.h"
 #include "controller/ble_ll_rfmgmt.h"
 #include "ble_ll_conn_priv.h"
+#include "ble_ll_priv.h"
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 
 /* XXX: TODO
  * 1) Need to look at advertising and scan request PDUs. Do I allocate these
@@ -110,7 +113,6 @@ struct ble_ll_adv_sm
     uint32_t adv_event_start_time;
     uint32_t adv_pdu_start_time;
     uint32_t adv_end_time;
-    uint32_t adv_rpa_timer;
     uint8_t adva[BLE_DEV_ADDR_LEN];
     uint8_t adv_rpa[BLE_DEV_ADDR_LEN];
     uint8_t peer_addr[BLE_DEV_ADDR_LEN];
@@ -119,7 +121,9 @@ struct ble_ll_adv_sm
     struct os_mbuf *new_adv_data;
     struct os_mbuf *scan_rsp_data;
     struct os_mbuf *new_scan_rsp_data;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     uint8_t *conn_comp_ev;
+#endif
     struct ble_npl_event adv_txdone_ev;
     struct ble_ll_sched_item adv_sch;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
@@ -207,6 +211,8 @@ struct ble_ll_adv_sm
 struct ble_ll_adv_sm g_ble_ll_adv_sm[BLE_ADV_INSTANCES];
 struct ble_ll_adv_sm *g_ble_ll_cur_adv_sm;
 
+static void ble_ll_adv_drop_event(struct ble_ll_adv_sm *advsm);
+
 static struct ble_ll_adv_sm *
 ble_ll_adv_sm_find_configured(uint8_t instance)
 {
@@ -551,11 +557,6 @@ ble_ll_adv_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
     /* only ADV_EXT_IND goes on primary advertising channels */
     pdu_type = BLE_ADV_PDU_TYPE_ADV_EXT_IND;
 
-    /* Set TxAdd to random if needed. */
-    if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
-        pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
-    }
-
     *hdr_byte = pdu_type;
 
     adv_mode = 0;
@@ -958,6 +959,7 @@ struct aux_conn_rsp_data {
  *
  * @param advsm
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static uint8_t
 ble_ll_adv_aux_conn_rsp_pdu_make(uint8_t *dptr, void *pducb_arg,
                                  uint8_t *hdr_byte)
@@ -1006,6 +1008,7 @@ ble_ll_adv_aux_conn_rsp_pdu_make(uint8_t *dptr, void *pducb_arg,
     return pdulen;
 }
 #endif
+#endif
 
 /**
  * Called to indicate the advertising event is over.
@@ -1021,7 +1024,7 @@ ble_ll_adv_tx_done(void *arg)
     struct ble_ll_adv_sm *advsm;
 
     /* reset power to max after advertising */
-    ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
+    ble_phy_txpwr_set(g_ble_ll_tx_power);
 
     advsm = (struct ble_ll_adv_sm *)arg;
 
@@ -1055,12 +1058,7 @@ ble_ll_adv_tx_done(void *arg)
 void
 ble_ll_adv_event_rmvd_from_sched(struct ble_ll_adv_sm *advsm)
 {
-    /*
-     * Need to set advertising channel to final chan so new event gets
-     * scheduled.
-     */
-    advsm->adv_chan = ble_ll_adv_final_chan(advsm);
-    ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
+    ble_ll_adv_drop_event(advsm);
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
@@ -1536,7 +1534,8 @@ ble_ll_adv_aux_schedule_next(struct ble_ll_adv_sm *advsm)
      * scheduled aux will fit inside duration. If not, remove it from scheduler
      * so advertising will stop after current aux.
      */
-    if (advsm->duration && (aux_next->sch.end_time > advsm->adv_end_time)) {
+    if (advsm->duration &&
+        CPUTIME_GT(aux_next->sch.end_time, advsm->adv_end_time)) {
         ble_ll_sched_rmv_elem(&aux_next->sch);
     }
 }
@@ -1666,7 +1665,7 @@ ble_ll_adv_aux_schedule(struct ble_ll_adv_sm *advsm)
      * not start extended advertising event which we cannot finish in time.
      */
     if (advsm->duration &&
-            (AUX_CURRENT(advsm)->sch.end_time > advsm->adv_end_time)) {
+        CPUTIME_GT(AUX_CURRENT(advsm)->sch.end_time, advsm->adv_end_time)) {
         ble_ll_adv_sm_stop_timeout(advsm);
     }
 }
@@ -1689,7 +1688,7 @@ ble_ll_adv_halt(void)
 
         ble_ll_trace_u32(BLE_LL_TRACE_ID_ADV_HALT, advsm->adv_instance);
 
-        ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
+        ble_phy_txpwr_set(g_ble_ll_tx_power);
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
         if (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_SYNC_SENDING) {
@@ -1758,6 +1757,7 @@ ble_ll_adv_set_adv_params(const uint8_t *cmdbuf, uint8_t len)
     adv_filter_policy = cmd->filter_policy;
 
     switch (cmd->type) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD:
         adv_filter_policy = BLE_HCI_ADV_FILT_NONE;
         memcpy(advsm->peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN);
@@ -1777,6 +1777,7 @@ ble_ll_adv_set_adv_params(const uint8_t *cmdbuf, uint8_t len)
     case BLE_HCI_ADV_TYPE_ADV_IND:
         props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND;
         break;
+#endif
     case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND:
         props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN;
         break;
@@ -1943,11 +1944,13 @@ ble_ll_adv_sm_stop(struct ble_ll_adv_sm *advsm)
         ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
         /* If there is an event buf we need to free it */
         if (advsm->conn_comp_ev) {
             ble_hci_trans_buf_free(advsm->conn_comp_ev);
             advsm->conn_comp_ev = NULL;
         }
+#endif
 
         ble_ll_adv_active_chanset_clear(advsm);
 
@@ -1970,6 +1973,7 @@ ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm)
     }
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     /*
      * For high duty directed advertising we need to send connection
      * complete event with proper status
@@ -1979,6 +1983,7 @@ ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm)
                                     advsm->conn_comp_ev, advsm);
         advsm->conn_comp_ev = NULL;
     }
+#endif
 
     /* Disable advertising */
     ble_ll_adv_sm_stop(advsm);
@@ -2000,11 +2005,13 @@ ble_ll_adv_sm_stop_limit_reached(struct ble_ll_adv_sm *advsm)
      * be used if HD directed advertising was terminated before timeout due to
      * events count limit. For now just use same code as with duration timeout.
      */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
         ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO,
                                     advsm->conn_comp_ev, advsm);
         advsm->conn_comp_ev = NULL;
     }
+#endif
 
     /* Disable advertising */
     ble_ll_adv_sm_stop(advsm);
@@ -2106,8 +2113,8 @@ ble_ll_adv_sync_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
 static void
 ble_ll_adv_sync_tx_done(struct ble_ll_adv_sm *advsm)
 {
-    /* reset power to max after advertising */
-    ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
+    /* reset power to default after advertising */
+    ble_phy_txpwr_set(g_ble_ll_tx_power);
 
     /* for sync we trace a no pri nor sec set */
     ble_ll_trace_u32x2(BLE_LL_TRACE_ID_ADV_TXDONE, advsm->adv_instance, 0);
@@ -2289,7 +2296,6 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
 {
     struct ble_ll_adv_sync *sync;
     struct ble_ll_sched_item *sch;
-    uint32_t sch_start;
     uint32_t max_usecs;
     uint8_t chan;
     int rc;
@@ -2332,7 +2338,7 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
     sch->end_time = sch->start_time + ble_ll_usecs_to_ticks_round_up(max_usecs);
     sch->start_time -= g_ble_ll_sched_offset_ticks;
 
-    rc = ble_ll_sched_periodic_adv(sch, &sch_start, first_pdu);
+    rc = ble_ll_sched_periodic_adv(sch, first_pdu);
     if (rc) {
         STATS_INC(ble_ll_stats, periodic_adv_drop_event);
         ble_npl_eventq_put(&g_ble_ll_data.ll_evq,
@@ -2340,7 +2346,7 @@ ble_ll_adv_periodic_schedule_first(struct ble_ll_adv_sm *advsm,
         return;
     }
 
-    sync->start_time = sch_start + g_ble_ll_sched_offset_ticks;
+    sync->start_time = sch->start_time + g_ble_ll_sched_offset_ticks;
 
     assert(first_pdu ||
            (sync->start_time == advsm->periodic_adv_event_start_time));
@@ -2424,8 +2430,8 @@ ble_ll_adv_periodic_schedule_next(struct ble_ll_adv_sm *advsm)
                          sync_next);
 
     /* if we are pass advertising interval, drop chain */
-    if (sch->end_time > advsm->periodic_adv_event_start_time +
-            advsm->periodic_adv_itvl_ticks) {
+    if (CPUTIME_GT(sch->end_time, advsm->periodic_adv_event_start_time +
+                                  advsm->periodic_adv_itvl_ticks)) {
         STATS_INC(ble_ll_stats, periodic_chain_drop_event);
         ble_ll_sched_rmv_elem(&sync->sch);
         ble_npl_eventq_put(&g_ble_ll_data.ll_evq,
@@ -2638,7 +2644,6 @@ ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm)
 {
     uint8_t adv_chan;
     uint8_t *addr;
-    uint8_t *evbuf;
     uint32_t start_delay_us;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
     uint32_t access_addr;
@@ -2666,16 +2671,17 @@ ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm)
      * Get an event with which to send the connection complete event if
      * this is connectable
      */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
         /* We expect this to be NULL but if not we wont allocate one... */
         if (advsm->conn_comp_ev == NULL) {
-            evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
-            if (!evbuf) {
+            advsm->conn_comp_ev = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
+            if (!advsm->conn_comp_ev) {
                 return BLE_ERR_MEM_CAPACITY;
             }
-            advsm->conn_comp_ev = evbuf;
         }
     }
+#endif
 
     /* Set advertising address */
     if ((advsm->own_addr_type & 1) == 0) {
@@ -3291,9 +3297,11 @@ ble_ll_adv_ext_set_param(const uint8_t *cmdbuf, uint8_t len,
 
         /* if legacy bit is set possible values are limited */
         switch (props) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
         case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND:
         case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR:
         case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR:
+#endif
         case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN:
         case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN:
             break;
@@ -3302,6 +3310,12 @@ ble_ll_adv_ext_set_param(const uint8_t *cmdbuf, uint8_t len,
             goto done;
         }
     } else {
+#if !MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
+            rc = BLE_ERR_INV_HCI_CMD_PARMS;
+            goto done;
+        }
+#endif
         /* HD directed advertising allowed only on legacy PDUs */
         if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
             rc = BLE_ERR_INV_HCI_CMD_PARMS;
@@ -4105,6 +4119,7 @@ ble_ll_adv_periodic_set_info_transfer(const uint8_t *cmdbuf, uint8_t len,
  * @param   [in]    addr_type   Public address (0) or random address (1).
  * @return  Return 1 if already connected, 0 otherwise.
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static int
 ble_ll_adv_already_connected(const uint8_t* addr, uint8_t addr_type)
 {
@@ -4127,6 +4142,7 @@ ble_ll_adv_already_connected(const uint8_t* addr, uint8_t addr_type)
 
     return 0;
 }
+#endif
 
 /**
  * Called when the LL receives a scan request or connection request
@@ -4152,9 +4168,11 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
     uint8_t *peer;
     struct ble_mbuf_hdr *ble_hdr;
     struct ble_ll_adv_sm *advsm;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     struct aux_conn_rsp_data rsp_data;
 #endif
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
     struct ble_ll_resolv_entry *rl;
 #endif
@@ -4261,6 +4279,7 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
             STATS_INC(ble_ll_stats, scan_rsp_txg);
         }
     } else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_CONNECT_REQ) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
         /* See if the device is already connected */
         if (ble_ll_adv_already_connected(peer, peer_addr_type)) {
             return -1;
@@ -4293,6 +4312,7 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
             ble_ll_adv_flags_set(advsm, BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD);
             STATS_INC(ble_ll_stats, aux_conn_rsp_tx);
         }
+#endif
 #endif
     }
 
@@ -4309,6 +4329,7 @@ ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
  *
  * @return 0: no connection started. 1: connection started
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static int
 ble_ll_adv_conn_req_rxd(uint8_t *rxbuf, struct ble_mbuf_hdr *hdr,
                         struct ble_ll_adv_sm *advsm)
@@ -4399,6 +4420,7 @@ ble_ll_adv_conn_req_rxd(uint8_t *rxbuf, struct ble_mbuf_hdr *hdr,
 
     return valid;
 }
+#endif
 
 /**
  * Called on phy rx pdu end when in advertising state.
@@ -4489,7 +4511,7 @@ ble_ll_adv_rx_pkt_in(uint8_t ptype, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr)
 #endif
 
     /*
-     * It is possible that advertising was stopped and a packet plcaed on the
+     * It is possible that advertising was stopped and a packet placed on the
      * LL receive packet queue. In this case, just ignore the received packet
      * as the advertising state machine is no longer "valid"
      */
@@ -4507,9 +4529,11 @@ ble_ll_adv_rx_pkt_in(uint8_t ptype, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr)
     adv_event_over = 1;
     if (BLE_MBUF_HDR_CRC_OK(hdr)) {
         if (ptype == BLE_ADV_PDU_TYPE_CONNECT_IND) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
             if (ble_ll_adv_conn_req_rxd(rxbuf, hdr, advsm)) {
                 adv_event_over = 0;
             }
+#endif
         } else {
             if ((ptype == BLE_ADV_PDU_TYPE_SCAN_REQ) &&
                 (hdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_TXD)) {
@@ -4594,13 +4618,15 @@ ble_ll_adv_drop_event(struct ble_ll_adv_sm *advsm)
 static void
 ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm)
 {
-    int rc;
-    uint32_t start_time;
+    struct ble_ll_sched_item *sch;
     uint32_t max_delay_ticks;
+    int rc;
 
     assert(advsm->adv_enabled);
 
-    if (!advsm->adv_sch.enqueued) {
+    sch = &advsm->adv_sch;
+
+    if (!sch->enqueued) {
         if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
             max_delay_ticks = 0;
         } else {
@@ -4608,16 +4634,15 @@ ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm)
                     os_cputime_usecs_to_ticks(BLE_LL_ADV_DELAY_MS_MAX * 1000);
         }
 
-        rc = ble_ll_sched_adv_reschedule(&advsm->adv_sch, &start_time,
-                                         max_delay_ticks);
+        rc = ble_ll_sched_adv_reschedule(sch, max_delay_ticks);
         if (rc) {
             ble_ll_adv_drop_event(advsm);
             return;
         }
 
-        start_time += g_ble_ll_sched_offset_ticks;
-        advsm->adv_event_start_time = start_time;
-        advsm->adv_pdu_start_time = start_time;
+        advsm->adv_event_start_time = sch->start_time +
+                                      g_ble_ll_sched_offset_ticks;
+        advsm->adv_pdu_start_time = advsm->adv_event_start_time;
     }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
@@ -4739,7 +4764,8 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm)
         /* If we're past aux (unlikely, but can happen), just drop an event */
         if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) &&
                 advsm->aux_active &&
-                advsm->adv_pdu_start_time > AUX_CURRENT(advsm)->start_time) {
+                CPUTIME_GT(advsm->adv_pdu_start_time,
+                           AUX_CURRENT(advsm)->start_time)) {
             ble_ll_adv_drop_event(advsm);
             return;
         }
@@ -4751,7 +4777,7 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm)
     /* check if advertising timed out */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     if (advsm->duration &&
-        advsm->adv_pdu_start_time >= advsm->adv_end_time) {
+        CPUTIME_GEQ(advsm->adv_pdu_start_time, advsm->adv_end_time)) {
         /* Legacy PDUs need to be stop here.
          * For ext adv it will be stopped when AUX is done (unless it was
          * dropped so check if AUX is active here as well).
@@ -4765,7 +4791,7 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm)
     }
 #else
     if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) &&
-            (advsm->adv_pdu_start_time >= advsm->adv_end_time)) {
+        CPUTIME_GEQ(advsm->adv_pdu_start_time, advsm->adv_end_time)) {
         ble_ll_adv_sm_stop_timeout(advsm);
         return;
     }
@@ -4869,7 +4895,8 @@ ble_ll_adv_sec_done(struct ble_ll_adv_sm *advsm)
     ble_ll_scan_chk_resume();
 
     /* Check if advertising timed out */
-    if (advsm->duration && (advsm->adv_pdu_start_time >= advsm->adv_end_time)) {
+    if (advsm->duration &&
+        CPUTIME_GEQ(advsm->adv_pdu_start_time, advsm->adv_end_time)) {
         ble_ll_adv_sm_stop_timeout(advsm);
         return;
     }
@@ -4943,11 +4970,11 @@ ble_ll_adv_can_chg_whitelist(void)
  *
  * @return uint8_t* Pointer to event buffer
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 void
 ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm,
                              struct ble_mbuf_hdr *rxhdr)
 {
-    uint8_t *evbuf;
     struct ble_ll_adv_sm *advsm;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
@@ -4956,12 +4983,11 @@ ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm,
     advsm = &g_ble_ll_adv_sm[0];
 #endif
 
-    evbuf = advsm->conn_comp_ev;
-    assert(evbuf != NULL);
+    assert(advsm->conn_comp_ev != NULL);
+    ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, advsm->conn_comp_ev,
+                                advsm);
     advsm->conn_comp_ev = NULL;
 
-    ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, evbuf, advsm);
-
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
     ble_ll_hci_ev_le_csa(connsm);
 #endif
@@ -4973,6 +4999,7 @@ ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm,
     }
 #endif
 }
+#endif
 
 /**
  * Returns the local resolvable private address currently being using by
@@ -5134,3 +5161,5 @@ ble_ll_adv_init(void)
         ble_ll_adv_sm_init(&g_ble_ll_adv_sm[i]);
     }
 }
+
+#endif
diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c
index b8352f4aa2..2fb65869ef 100644
--- a/nimble/controller/src/ble_ll_conn.c
+++ b/nimble/controller/src/ble_ll_conn.c
@@ -25,10 +25,8 @@
 #include "os/os.h"
 #include "os/os_cputime.h"
 #include "nimble/ble.h"
-#include "nimble/nimble_opt.h"
 #include "nimble/hci_common.h"
 #include "nimble/ble_hci_trans.h"
-#include "ble/xcvr.h"
 #include "controller/ble_ll.h"
 #include "controller/ble_ll_hci.h"
 #include "controller/ble_ll_scan.h"
@@ -40,9 +38,9 @@
 #include "controller/ble_ll_trace.h"
 #include "controller/ble_ll_rfmgmt.h"
 #include "controller/ble_phy.h"
-#include "controller/ble_hw.h"
 #include "controller/ble_ll_utils.h"
 #include "ble_ll_conn_priv.h"
+#include "ble_ll_ctrl_priv.h"
 
 #if (BLETEST_THROUGHPUT_TEST == 1)
 extern void bletest_completed_pkt(uint16_t handle);
@@ -119,6 +117,11 @@ extern void bletest_completed_pkt(uint16_t handle);
  *  1) The current connection event has not ended but a schedule item starts
  */
 
+/* Global LL connection parameters */
+struct ble_ll_conn_global_params g_ble_ll_conn_params;
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+
 /* This is a dummy structure we use for the empty PDU */
 struct ble_ll_empty_pdu
 {
@@ -132,19 +135,19 @@ struct ble_ll_empty_pdu
     #error "Maximum # of connections is 254"
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 /* Global connection complete event. Used when initiating */
 uint8_t *g_ble_ll_conn_comp_ev;
-
-/* Global LL connection parameters */
-struct ble_ll_conn_global_params g_ble_ll_conn_params;
+#endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
 /* Global default sync transfer params */
 struct ble_ll_conn_sync_transfer_params g_ble_ll_conn_sync_transfer_params;
 #endif
 
-/* Pointer to connection state machine we are trying to create */
-struct ble_ll_conn_sm *g_ble_ll_conn_create_sm;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+struct ble_ll_conn_create_sm g_ble_ll_conn_create_sm;
+#endif
 
 /* Pointer to current connection */
 struct ble_ll_conn_sm *g_ble_ll_conn_cur_sm;
@@ -421,25 +424,23 @@ ble_ll_conn_chk_phy_upd_start(struct ble_ll_conn_sm *csm)
 }
 #endif
 
-static void
-ble_ll_conn_calc_itvl_ticks(struct ble_ll_conn_sm *connsm)
+void
+ble_ll_conn_itvl_to_ticks(uint32_t itvl, uint32_t *itvl_ticks,
+                          uint8_t *itvl_usecs)
 {
     uint32_t ticks;
     uint32_t usecs;
 
-    /*
-     * Precalculate the number of ticks and remaining microseconds for
-     * the connection interval
-     */
-    usecs = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS;
+    usecs = itvl * BLE_LL_CONN_ITVL_USECS;
     ticks = os_cputime_usecs_to_ticks(usecs);
-    connsm->conn_itvl_usecs = (uint8_t)(usecs -
-                                        os_cputime_ticks_to_usecs(ticks));
-    if (connsm->conn_itvl_usecs == 31) {
-        connsm->conn_itvl_usecs = 0;
+    usecs = usecs - os_cputime_ticks_to_usecs(ticks);
+    if (usecs == 31) {
+        usecs = 0;
         ++ticks;
     }
-    connsm->conn_itvl_ticks = ticks;
+
+    *itvl_ticks = ticks;
+    *itvl_usecs = usecs;
 }
 
 /**
@@ -448,6 +449,7 @@ ble_ll_conn_calc_itvl_ticks(struct ble_ll_conn_sm *connsm)
  *
  * @return uint8_t*
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 static uint8_t *
 ble_ll_init_get_conn_comp_ev(void)
 {
@@ -459,6 +461,7 @@ ble_ll_init_get_conn_comp_ev(void)
 
     return evbuf;
 }
+#endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
 /**
@@ -674,64 +677,6 @@ ble_ll_conn_wfr_timer_exp(void)
     STATS_INC(ble_ll_conn_stats, wfr_expirations);
 }
 
-void
-ble_ll_conn_reset_pending_aux_conn_rsp(void)
-{
-#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    return;
-#endif
-    struct ble_ll_conn_sm *connsm;
-
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-        return;
-    }
-
-    if (CONN_F_AUX_CONN_REQ(connsm)) {
-        STATS_INC(ble_ll_stats, aux_conn_rsp_err);
-        CONN_F_CONN_REQ_TXD(connsm) = 0;
-        CONN_F_AUX_CONN_REQ(connsm) = 0;
-        ble_ll_sched_rmv_elem(&connsm->conn_sch);
-        return;
-    }
-
-    return;
-}
-
-bool
-ble_ll_conn_init_pending_aux_conn_rsp(void)
-{
-#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    return false;
-#endif
-    struct ble_ll_conn_sm *connsm;
-
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-        return false;
-    }
-
-    return CONN_F_AUX_CONN_REQ(connsm);
-}
-
-void
-ble_ll_conn_init_wfr_timer_exp(void)
-{
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_conn_sm *connsm;
-
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-        return;
-    }
-
-    ble_ll_conn_reset_pending_aux_conn_rsp();
-    connsm->inita_identity_used = 0;
-
-    ble_ll_scan_interrupted(connsm->scansm);
-
-#endif
-}
 /**
  * Callback for slave when it transmits a data pdu and the connection event
  * ends after the transmission.
@@ -764,6 +709,7 @@ ble_ll_conn_start_rx_encrypt(void *arg)
                            !CONN_IS_MASTER(connsm));
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static void
 ble_ll_conn_start_rx_unencrypt(void *arg)
 {
@@ -773,6 +719,7 @@ ble_ll_conn_start_rx_unencrypt(void *arg)
     CONN_F_ENCRYPTED(connsm) = 0;
     ble_phy_encrypt_disable();
 }
+#endif
 
 static void
 ble_ll_conn_txend_encrypt(void *arg)
@@ -784,6 +731,7 @@ ble_ll_conn_txend_encrypt(void *arg)
     ble_ll_conn_current_sm_over(connsm);
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static void
 ble_ll_conn_rxend_unencrypt(void *arg)
 {
@@ -793,6 +741,7 @@ ble_ll_conn_rxend_unencrypt(void *arg)
     CONN_F_ENCRYPTED(connsm) = 0;
     ble_ll_conn_current_sm_over(connsm);
 }
+#endif
 
 static void
 ble_ll_conn_continue_rx_encrypt(void *arg)
@@ -917,7 +866,7 @@ ble_ll_conn_chk_csm_flags(struct ble_ll_conn_sm *connsm)
 static uint16_t
 ble_ll_conn_adjust_pyld_len(struct ble_ll_conn_sm *connsm, uint16_t pyld_len)
 {
-    uint16_t phy_max_tx_octets;
+    uint16_t max_pyld_len;
     uint16_t ret;
 
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
@@ -930,22 +879,28 @@ ble_ll_conn_adjust_pyld_len(struct ble_ll_conn_sm *connsm, uint16_t pyld_len)
         phy_mode = connsm->phy_data.tx_phy_mode;
     }
 
-    phy_max_tx_octets = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time,
-                                                     phy_mode);
+    max_pyld_len = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time,
+                                                phy_mode);
 
 #else
-    phy_max_tx_octets = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time,
-                                                     BLE_PHY_MODE_1M);
+    max_pyld_len = ble_ll_pdu_max_tx_octets_get(connsm->eff_max_tx_time,
+                                                BLE_PHY_MODE_1M);
 #endif
 
     ret = pyld_len;
 
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
+    if (CONN_F_ENCRYPTED(connsm)) {
+        max_pyld_len -= BLE_LL_DATA_MIC_LEN;
+    }
+#endif
+
     if (ret > connsm->eff_max_tx_octets) {
         ret = connsm->eff_max_tx_octets;
     }
 
-    if (ret > phy_max_tx_octets) {
-        ret = phy_max_tx_octets;
+    if (ret > max_pyld_len) {
+        ret = max_pyld_len;
     }
 
     return ret;
@@ -959,8 +914,8 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
     uint8_t hdr_byte;
     uint8_t end_transition;
     uint8_t cur_txlen;
-    uint8_t next_txlen;
-    uint8_t cur_offset;
+    uint16_t next_txlen;
+    uint16_t cur_offset;
     uint16_t pktlen;
     uint32_t next_event_time;
     uint32_t ticks;
@@ -1035,7 +990,7 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
              * packets can be let go.
              */
             if (nextpkthdr && !ble_ll_ctrl_enc_allowed_pdu_tx(nextpkthdr)
-                && ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) ||
+                && (CONN_IS_MASTER(connsm) ||
                     !ble_ll_ctrl_is_start_enc_rsp(m))) {
                 nextpkthdr = NULL;
             }
@@ -1082,7 +1037,7 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
                 /* We will allow a next packet if it itself is allowed */
                 pkthdr = OS_MBUF_PKTHDR(connsm->cur_tx_pdu);
                 if (nextpkthdr && !ble_ll_ctrl_enc_allowed_pdu_tx(nextpkthdr)
-                    && ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) ||
+                    && (CONN_IS_MASTER(connsm) ||
                         !ble_ll_ctrl_is_start_enc_rsp(connsm->cur_tx_pdu))) {
                     nextpkthdr = NULL;
                 }
@@ -1129,11 +1084,10 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
         if ((cur_offset + cur_txlen) < pktlen) {
             next_txlen = pktlen - (cur_offset + cur_txlen);
         } else {
-            if (nextpkthdr->omp_len > connsm->eff_max_tx_octets) {
-                next_txlen = connsm->eff_max_tx_octets;
-            } else {
-                next_txlen = nextpkthdr->omp_len;
-            }
+            next_txlen = connsm->eff_max_tx_octets;
+        }
+        if (next_txlen > connsm->eff_max_tx_octets) {
+            next_txlen = connsm->eff_max_tx_octets;
         }
 
         /*
@@ -1152,9 +1106,11 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
             ble_ll_pdu_tx_time_get(next_txlen, tx_phy_mode) +
             ble_ll_pdu_tx_time_get(cur_txlen, tx_phy_mode);
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
             ticks += (BLE_LL_IFS + connsm->eff_max_rx_time);
         }
+#endif
 
         ticks = os_cputime_usecs_to_ticks(ticks);
         if (CPUTIME_LT(os_cputime_get32() + ticks, next_event_time)) {
@@ -1210,7 +1166,7 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
      *  overrun next scheduled item.
      */
     if ((connsm->csmflags.cfbit.terminate_ind_rxd) ||
-        ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) && (md == 0) &&
+        (CONN_IS_SLAVE(connsm) && (md == 0) &&
          (connsm->cons_rxd_bad_crc == 0) &&
          ((connsm->last_rxd_hdr_byte & BLE_LL_DATA_HDR_MD_MASK) == 0) &&
          !ble_ll_ctrl_is_terminate_ind(hdr_byte, m->om_data[0]))) {
@@ -1263,7 +1219,17 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
          * The slave sends the PAUSE_ENC_RSP encrypted. The master sends
          * it unencrypted (note that link was already set unencrypted).
          */
-        if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+        switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_LL_CONN_ROLE_MASTER:
+            CONN_F_ENCRYPTED(connsm) = 0;
+            connsm->enc_data.enc_state = CONN_ENC_S_PAUSED;
+            connsm->enc_data.tx_encrypted = 0;
+            ble_phy_encrypt_disable();
+            break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        case BLE_LL_CONN_ROLE_SLAVE:
             CONN_F_ENCRYPTED(connsm) = 1;
             connsm->enc_data.tx_encrypted = 1;
             ble_phy_encrypt_enable(connsm->enc_data.tx_pkt_cntr,
@@ -1275,11 +1241,11 @@ ble_ll_conn_tx_pdu(struct ble_ll_conn_sm *connsm)
             } else {
                 txend_func = ble_ll_conn_rxend_unencrypt;
             }
-        } else {
-            CONN_F_ENCRYPTED(connsm) = 0;
-            connsm->enc_data.enc_state = CONN_ENC_S_PAUSED;
-            connsm->enc_data.tx_encrypted = 0;
-            ble_phy_encrypt_disable();
+            break;
+#endif
+        default:
+            BLE_LL_ASSERT(0);
+            break;
         }
     } else {
         /* If encrypted set packet counter */
@@ -1336,7 +1302,9 @@ static int
 ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch)
 {
     int rc;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     uint32_t usecs;
+#endif
     uint32_t start;
     struct ble_ll_conn_sm *connsm;
 
@@ -1377,7 +1345,9 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch)
     ble_phy_mode_set(connsm->phy_data.tx_phy_mode, connsm->phy_data.rx_phy_mode);
 #endif
 
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         /* Set start time of transmission */
         start = sch->start_time + g_ble_ll_sched_offset_ticks;
         rc = ble_phy_tx_set_start_time(start, sch->remainder);
@@ -1403,7 +1373,10 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch)
             STATS_INC(ble_ll_conn_stats, conn_ev_late);
             rc = BLE_LL_SCHED_STATE_DONE;
         }
-    } else {
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
         if (CONN_F_ENCRYPTED(connsm)) {
             ble_phy_encrypt_enable(connsm->enc_data.rx_pkt_cntr,
@@ -1459,6 +1432,11 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch)
             /* Set next wakeup time to connection event end time */
             rc = BLE_LL_SCHED_STATE_RUNNING;
         }
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
     if (rc == BLE_LL_SCHED_STATE_DONE) {
@@ -1490,8 +1468,9 @@ static int
 ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime,
                               uint32_t add_usecs)
 {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     int rc;
-    uint8_t rem_bytes;
+    uint16_t rem_bytes;
     uint32_t ticks;
     uint32_t usecs;
     uint32_t next_sched_time;
@@ -1545,6 +1524,9 @@ ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime,
     }
 
     return rc;
+#else
+    return 1;
+#endif
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING)
@@ -1569,22 +1551,6 @@ ble_ll_conn_auth_pyld_timer_cb(struct ble_npl_event *ev)
     ble_ll_conn_auth_pyld_timer_start(connsm);
 }
 
-void
-ble_ll_conn_rd_features_timer_cb(struct ble_npl_event *ev)
-{
-    struct ble_ll_conn_sm *connsm;
-
-    connsm = (struct ble_ll_conn_sm *)ble_npl_event_get_arg(ev);
-
-    if (!connsm->csmflags.cfbit.pending_hci_rd_features ||
-                                        !connsm->csmflags.cfbit.rxd_features) {
-        return;
-    }
-
-    ble_ll_hci_ev_rd_rem_used_feat(connsm, BLE_ERR_SUCCESS);
-    connsm->csmflags.cfbit.pending_hci_rd_features = 0;
-}
-
 /**
  * Start (or restart) the authenticated payload timer
  *
@@ -1601,6 +1567,7 @@ ble_ll_conn_auth_pyld_timer_start(struct ble_ll_conn_sm *connsm)
 }
 #endif
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 static void
 ble_ll_conn_master_common_init(struct ble_ll_conn_sm *connsm)
 {
@@ -1644,55 +1611,25 @@ ble_ll_conn_master_common_init(struct ble_ll_conn_sm *connsm)
  */
 void
 ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm,
-                        struct hci_create_conn *hcc)
+                        struct ble_ll_conn_create_scan *cc_scan,
+                        struct ble_ll_conn_create_params *cc_params)
 {
 
     ble_ll_conn_master_common_init(connsm);
 
-    /* Set slave latency and supervision timeout */
-    connsm->slave_latency = hcc->conn_latency;
-    connsm->supervision_tmo = hcc->supervision_timeout;
-
-    /* Set own address type and peer address if needed */
-    connsm->own_addr_type = hcc->own_addr_type;
-    if (hcc->filter_policy == 0) {
-        memcpy(&connsm->peer_addr, &hcc->peer_addr, BLE_DEV_ADDR_LEN);
-        connsm->peer_addr_type = hcc->peer_addr_type;
-    }
-
-    /* XXX: for now, just make connection interval equal to max */
-    connsm->conn_itvl = hcc->conn_itvl_max;
+    connsm->own_addr_type = cc_scan->own_addr_type;
+    memcpy(&connsm->peer_addr, &cc_scan->peer_addr, BLE_DEV_ADDR_LEN);
+    connsm->peer_addr_type = cc_scan->peer_addr_type;
 
-    /* Check the min/max CE lengths are less than connection interval */
-    if (hcc->min_ce_len > (connsm->conn_itvl * 2)) {
-        connsm->min_ce_len = connsm->conn_itvl * 2;
-    } else {
-        connsm->min_ce_len = hcc->min_ce_len;
-    }
-
-    if (hcc->max_ce_len > (connsm->conn_itvl * 2)) {
-        connsm->max_ce_len = connsm->conn_itvl * 2;
-    } else {
-        connsm->max_ce_len = hcc->max_ce_len;
-    }
-}
-
-static void
-ble_ll_update_max_tx_octets_phy_mode(struct ble_ll_conn_sm *connsm)
-{
-    uint32_t usecs;
-
-    usecs = connsm->eff_max_tx_time;
-
-    connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_1M] =
-            ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_1M);
-    connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_2M] =
-            ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_2M);
-    connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_CODED_125KBPS] =
-            ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_CODED_125KBPS);
-    connsm->max_tx_octets_phy_mode[BLE_PHY_MODE_CODED_500KBPS] =
-            ble_ll_pdu_max_tx_octets_get(usecs, BLE_PHY_MODE_CODED_500KBPS);
+    connsm->conn_itvl = cc_params->conn_itvl;
+    connsm->conn_itvl_ticks = cc_params->conn_itvl_ticks;
+    connsm->conn_itvl_usecs = cc_params->conn_itvl_usecs;
+    connsm->slave_latency = cc_params->conn_latency;
+    connsm->supervision_tmo = cc_params->supervision_timeout;
+    connsm->min_ce_len = cc_params->min_ce_len;
+    connsm->max_ce_len = cc_params->max_ce_len;
 }
+#endif
 
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
 
@@ -1743,64 +1680,27 @@ ble_ll_conn_init_phy(struct ble_ll_conn_sm *connsm, int phy)
     connsm->rem_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN;
     connsm->eff_max_tx_octets = BLE_LL_CONN_SUPP_BYTES_MIN;
     connsm->eff_max_rx_octets = BLE_LL_CONN_SUPP_BYTES_MIN;
-
-    ble_ll_update_max_tx_octets_phy_mode(connsm);
 }
 
 #endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-
-void
-ble_ll_conn_ext_master_init(struct ble_ll_conn_sm *connsm,
-                        struct hci_ext_create_conn *hcc)
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+static void
+ble_ll_conn_create_set_params(struct ble_ll_conn_sm *connsm, uint8_t phy)
 {
+    struct ble_ll_conn_create_params *cc_params;
 
-    ble_ll_conn_master_common_init(connsm);
+    cc_params = &g_ble_ll_conn_create_sm.params[phy - 1];
 
-    /* Set own address type and peer address if needed */
-    connsm->own_addr_type = hcc->own_addr_type;
-    if (hcc->filter_policy == 0) {
-        memcpy(&connsm->peer_addr, &hcc->peer_addr, BLE_DEV_ADDR_LEN);
-        connsm->peer_addr_type = hcc->peer_addr_type;
-    }
+    connsm->slave_latency = cc_params->conn_latency;
+    connsm->supervision_tmo = cc_params->supervision_timeout;
 
-    connsm->initial_params = *hcc;
+    connsm->conn_itvl = cc_params->conn_itvl;
+    connsm->conn_itvl_ticks = cc_params->conn_itvl_ticks;
+    connsm->conn_itvl_usecs = cc_params->conn_itvl_usecs;
 }
-
-void
-ble_ll_conn_ext_set_params(struct ble_ll_conn_sm *connsm,
-                           struct hci_ext_conn_params *hcc_params, int phy)
-{
-    /* Set slave latency and supervision timeout */
-    connsm->slave_latency = hcc_params->conn_latency;
-    connsm->supervision_tmo = hcc_params->supervision_timeout;
-
-    /* XXX: for now, just make connection interval equal to max */
-    connsm->conn_itvl = hcc_params->conn_itvl_max;
-
-
-    /* Check the min/max CE lengths are less than connection interval */
-    if (hcc_params->min_ce_len > (connsm->conn_itvl * 2)) {
-        connsm->min_ce_len = connsm->conn_itvl * 2;
-    } else {
-        connsm->min_ce_len = hcc_params->min_ce_len;
-    }
-
-    if (hcc_params->max_ce_len > (connsm->conn_itvl * 2)) {
-        connsm->max_ce_len = connsm->conn_itvl * 2;
-    } else {
-        connsm->max_ce_len = hcc_params->max_ce_len;
-    }
-
-    ble_ll_conn_calc_itvl_ticks(connsm);
-
-#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
-    ble_ll_conn_init_phy(connsm, phy);
 #endif
-}
-
-
 #endif
 
 static void
@@ -1851,7 +1751,6 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm)
     connsm->sub_vers_nr = 0;
     connsm->reject_reason = BLE_ERR_SUCCESS;
     connsm->conn_rssi = BLE_LL_CONN_UNKNOWN_RSSI;
-    connsm->rpa_index = -1;
     connsm->inita_identity_used = 0;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
@@ -1915,8 +1814,6 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm)
     connsm->host_req_max_tx_time = 0;
 #endif
 
-    ble_ll_update_max_tx_octets_phy_mode(connsm);
-
     /* Reset encryption data */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
     memset(&connsm->enc_data, 0, sizeof(struct ble_ll_conn_enc_data));
@@ -1932,8 +1829,6 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm)
                     connsm);
 #endif
 
-    ble_ll_conn_calc_itvl_ticks(connsm);
-
     /* Add to list of active connections */
     SLIST_INSERT_HEAD(&g_ble_ll_conn_active_list, connsm, act_sle);
 }
@@ -1968,8 +1863,6 @@ ble_ll_conn_update_eff_data_len(struct ble_ll_conn_sm *connsm)
     if (eff_time != connsm->eff_max_tx_time) {
         connsm->eff_max_tx_time = eff_time;
         send_event = 1;
-
-        ble_ll_update_max_tx_octets_phy_mode(connsm);
     }
     eff_bytes = min(connsm->rem_max_tx_octets, connsm->max_rx_octets);
     if (eff_bytes != connsm->eff_max_rx_octets) {
@@ -2149,8 +2042,10 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
 {
     uint16_t latency;
     uint32_t itvl;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     uint32_t cur_ww;
     uint32_t max_ww;
+#endif
     struct ble_ll_conn_upd_req *upd;
     uint32_t ticks;
     uint32_t usecs;
@@ -2163,7 +2058,7 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
         ble_ll_ctrl_terminate_start(connsm);
     }
 
-    if (CONN_F_TERMINATE_STARTED(connsm) && (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE)) {
+    if (CONN_F_TERMINATE_STARTED(connsm) && CONN_IS_SLAVE(connsm)) {
         /* Some of the devices waits whole connection interval to ACK our
          * TERMINATE_IND sent as a Slave. Since we are here it means we are still waiting for ACK.
          * Make sure we catch it in next connection event.
@@ -2203,7 +2098,6 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
         connsm->anchor_point += connsm->conn_itvl_ticks;
         connsm->anchor_point_usecs += connsm->conn_itvl_usecs;
     } else {
-        uint32_t ticks;
         ticks = os_cputime_usecs_to_ticks(itvl);
         connsm->anchor_point += ticks;
         connsm->anchor_point_usecs += (itvl - os_cputime_ticks_to_usecs(ticks));
@@ -2224,8 +2118,8 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
 
         /* Set flag so we send connection update event */
         upd = &connsm->conn_update_req;
-        if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER)  ||
-            ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) &&
+        if (CONN_IS_MASTER(connsm) ||
+            (CONN_IS_SLAVE(connsm) &&
              IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) ||
             (connsm->conn_itvl != upd->interval)            ||
             (connsm->slave_latency != upd->latency)         ||
@@ -2240,7 +2134,10 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
             connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS;
         connsm->tx_win_off = upd->winoffset;
         connsm->conn_itvl = upd->interval;
-        ble_ll_conn_calc_itvl_ticks(connsm);
+
+        ble_ll_conn_itvl_to_ticks(connsm->conn_itvl, &connsm->conn_itvl_ticks,
+                                  &connsm->conn_itvl_usecs);
+
         if (upd->winoffset != 0) {
             usecs = upd->winoffset * BLE_LL_CONN_ITVL_USECS;
             ticks = os_cputime_usecs_to_ticks(usecs);
@@ -2355,8 +2252,9 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
 #if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
     itvl = g_ble_ll_sched_data.sch_ticks_per_period;
 #else
-    itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
+    itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_TICKS_PER_SLOT;
 #endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
 
         cur_ww = ble_ll_utils_calc_window_widening(connsm->anchor_point,
@@ -2370,6 +2268,7 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
         connsm->slave_cur_window_widening = cur_ww;
         itvl += os_cputime_usecs_to_ticks(cur_ww + connsm->slave_cur_tx_win_usecs);
     }
+#endif
     itvl -= g_ble_ll_sched_offset_ticks;
     connsm->ce_end_time = connsm->anchor_point + itvl;
 
@@ -2395,9 +2294,13 @@ static int
 ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
 {
     int rc;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     uint8_t *evbuf;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     uint32_t endtime;
     uint32_t usecs;
+#endif
 
     /* XXX: TODO this assumes we received in 1M phy */
 
@@ -2422,6 +2325,7 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
      * the transmit window offset from the end of the connection request.
      */
     rc = 1;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         /*
          * With a 32.768 kHz crystal we dont care about the remaining usecs
@@ -2469,7 +2373,7 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
 
 #else
         connsm->ce_end_time = connsm->anchor_point +
-            (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT)
+            (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_TICKS_PER_SLOT)
             + os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1;
 #endif
         connsm->slave_cur_window_widening = BLE_LL_JITTER_USECS;
@@ -2483,6 +2387,7 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
             }
         }
     }
+#endif
 
     /* Send connection complete event to inform host of connection */
     if (rc) {
@@ -2499,9 +2404,9 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
             CONN_F_CTRLR_PHY_UPDATE(connsm) = 1;
         }
 #endif
-        if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
-            ble_ll_adv_send_conn_comp_ev(connsm, rxhdr);
-        } else {
+        switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_LL_CONN_ROLE_MASTER:
             evbuf = ble_ll_init_get_conn_comp_ev();
             ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, evbuf, NULL);
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
@@ -2517,6 +2422,16 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
              * if it has some additional features to use.
              */
             ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG);
+            break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        case BLE_LL_CONN_ROLE_SLAVE:
+            ble_ll_adv_send_conn_comp_ev(connsm, rxhdr);
+            break;
+#endif
+        default:
+            BLE_LL_ASSERT(0);
+            break;
         }
     }
 
@@ -2685,8 +2600,8 @@ ble_ll_conn_event_end(struct ble_npl_event *ev)
 
  * @param txoffset      The tx window offset for this connection
  */
-static void
-ble_ll_conn_connect_ind_prepare(struct ble_ll_conn_sm *connsm,
+void
+ble_ll_conn_prepare_connect_ind(struct ble_ll_conn_sm *connsm,
                                 struct ble_ll_scan_pdu_data *pdu_data,
                                 uint8_t adva_type, uint8_t *adva,
                                 uint8_t inita_type, uint8_t *inita,
@@ -2721,7 +2636,7 @@ ble_ll_conn_connect_ind_prepare(struct ble_ll_conn_sm *connsm,
         }
     } else {
         /* Get pointer to our device address */
-        connsm = g_ble_ll_conn_create_sm;
+        connsm = g_ble_ll_conn_create_sm.connsm;
         if ((connsm->own_addr_type & 1) == 0) {
             addr = g_dev_addr;
         } else {
@@ -2771,110 +2686,8 @@ ble_ll_conn_connect_ind_prepare(struct ble_ll_conn_sm *connsm,
     pdu_data->hdr_byte = hdr;
 }
 
-/* Returns true if the address matches the connection peer address having in
- * mind privacy mode
- */
-static int
-ble_ll_conn_is_peer_adv(uint8_t addr_type, uint8_t *adva, int index)
-{
-    int rc;
-    uint8_t *peer_addr = NULL;
-    struct ble_ll_conn_sm *connsm;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    struct ble_ll_resolv_entry *rl;
-#endif
-
-    /* XXX: Deal with different types of random addresses here! */
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-        return 0;
-    }
-
-    switch (connsm->peer_addr_type) {
-    /* Fall-through intentional */
-    case BLE_HCI_CONN_PEER_ADDR_PUBLIC:
-    case BLE_HCI_CONN_PEER_ADDR_RANDOM:
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-        if (ble_ll_addr_is_id(adva, addr_type)) {
-                /* Peer uses its identity address. Let's verify privacy mode.
-                 *
-                 * Note: Core Spec 5.0 Vol 6, Part B
-                 * If the Host has added the peer device to the resolving list
-                 * with an all-zero peer IRK, the Controller shall only accept
-                 * the peer's identity address.
-                 */
-            if (ble_ll_resolv_enabled()) {
-                rl = ble_ll_resolv_list_find(adva, addr_type);
-                if (rl && (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) &&
-                    rl->rl_has_peer) {
-                    return 0;
-                }
-            }
-        }
-
-        /* Check if peer uses RPA. If so and it match, use it as controller
-         * supports privacy mode
-         */
-        if ((index >= 0) &&
-                (g_ble_ll_resolv_list[index].rl_addr_type == connsm->peer_addr_type)) {
-            peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr;
-        }
-#endif
-        /*
-         * If we are here it means we don't know the device, lets
-         * check if type is what we are looking for and later
-         * if address matches
-         */
-        if ((connsm->peer_addr_type == addr_type) && !peer_addr) {
-            peer_addr = adva;
-        }
-
-        break;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    case BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT:
-        if ((index < 0) ||
-            (g_ble_ll_resolv_list[index].rl_addr_type != 0)) {
-            return 0;
-        }
-        peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr;
-        break;
-    case BLE_HCI_CONN_PEER_ADDR_RANDOM_IDENT:
-        if ((index < 0) ||
-            (g_ble_ll_resolv_list[index].rl_addr_type != 1)) {
-            return 0;
-        }
-        peer_addr = g_ble_ll_resolv_list[index].rl_identity_addr;
-        break;
-#endif
-    default:
-        peer_addr = NULL;
-        break;
-    }
-
-    rc = 0;
-    if (peer_addr) {
-        if (!memcmp(peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN)) {
-            rc = 1;
-        }
-    }
-
-    return rc;
-}
-
-static void
-ble_ll_conn_connect_ind_txend_to_standby(void *arg)
-{
-    ble_ll_state_set(BLE_LL_STATE_STANDBY);
-}
-
-static void
-ble_ll_conn_connect_ind_txend_to_init(void *arg)
-{
-    ble_ll_state_set(BLE_LL_STATE_INITIATING);
-}
-
-static uint8_t
-ble_ll_conn_connect_ind_tx_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
+uint8_t
+ble_ll_conn_tx_connect_ind_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
 {
     struct ble_ll_conn_sm *connsm;
     struct ble_ll_scan_pdu_data *pdu_data;
@@ -2908,30 +2721,6 @@ ble_ll_conn_connect_ind_tx_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_by
     return 34;
 }
 
-/**
- * Send a connection requestion to an advertiser
- *
- * Context: Interrupt
- *
- * @param addr_type Address type of advertiser
- * @param adva Address of advertiser
- */
-int
-ble_ll_conn_connect_ind_send(struct ble_ll_conn_sm *connsm, uint8_t end_trans)
-{
-    int rc;
-
-    if (end_trans == BLE_PHY_TRANSITION_NONE) {
-        ble_phy_set_txend_cb(ble_ll_conn_connect_ind_txend_to_standby, NULL);
-    } else {
-        ble_phy_set_txend_cb(ble_ll_conn_connect_ind_txend_to_init, NULL);
-    }
-
-    rc = ble_phy_tx(ble_ll_conn_connect_ind_tx_pducb, connsm, end_trans);
-
-    return rc;
-}
-
 /**
  * Called when a schedule item overlaps the currently running connection
  * event. This generally should not happen, but if it does we stop the
@@ -2951,581 +2740,149 @@ ble_ll_conn_event_halt(void)
     }
 }
 
-/**
- * Process a received PDU while in the initiating state.
- *
- * Context: Link Layer task.
- *
- * @param pdu_type
- * @param rxbuf
- * @param ble_hdr
- */
-void
-ble_ll_init_rx_pkt_in(uint8_t pdu_type, uint8_t *rxbuf,
-                      struct ble_mbuf_hdr *ble_hdr)
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+int
+ble_ll_conn_send_connect_req(struct os_mbuf *rxpdu,
+                             struct ble_ll_scan_addr_data *addrd,
+                             uint8_t ext)
 {
-    uint8_t addr_type;
-    uint8_t *addr;
-    uint8_t *adv_addr;
-    uint8_t *inita;
-    uint8_t inita_type;
     struct ble_ll_conn_sm *connsm;
-    int ext_adv_mode = -1;
+    struct ble_mbuf_hdr *rxhdr;
+    int8_t rpa_index;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_aux_data *aux_data = NULL;
-
-     if (ble_hdr->rxinfo.user_data) {
-         /* aux_data just a local helper, no need to ref
-          * as ble_hdr->rxinfo.user_data is unref in the end of this function
-          */
-         aux_data = ble_hdr->rxinfo.user_data;
-     }
-#endif
-
-    /* Get the connection state machine we are trying to create */
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        if (aux_data) {
-            ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data);
-            ble_hdr->rxinfo.user_data = NULL;
-        }
+    uint8_t phy;
 #endif
-        return;
-    }
+    int rc;
 
-    if (!BLE_MBUF_HDR_CRC_OK(ble_hdr)) {
-        goto scan_continue;
-    }
+    connsm = g_ble_ll_conn_create_sm.connsm;
+    rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (BLE_MBUF_HDR_AUX_INVALID(ble_hdr)) {
-        goto scan_continue;
-    }
-
-    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
-        if (BLE_MBUF_HDR_WAIT_AUX(ble_hdr)) {
-            /* Just continue scanning. We are waiting for AUX */
-            if (!ble_ll_sched_aux_scan(ble_hdr, connsm->scansm, aux_data)) {
-                /* ref for aux ptr in the scheduler */
-                ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data);
-                ble_hdr->rxinfo.user_data = NULL;
-                ble_ll_scan_chk_resume();
-                return;
-            }
-            goto scan_continue;
-        }
-    }
-
-    if (CONN_F_AUX_CONN_REQ(connsm)) {
-        if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
-            /* Wait for connection response, in this point of time aux is NULL */
-            BLE_LL_ASSERT(ble_hdr->rxinfo.user_data == NULL);
-            return;
-        }
+    if (ext) {
+#if BLE_LL_BT5_PHY_SUPPORTED
+        phy = rxhdr->rxinfo.phy;
+#else
+        phy = BLE_PHY_1M;
+#endif
+        ble_ll_conn_create_set_params(connsm, phy);
     }
 #endif
 
-    /* If we have sent a connect request, we need to enter CONNECTION state */
-    if (connsm && CONN_F_CONN_REQ_TXD(connsm)) {
-        /* Set address of advertiser to which we are connecting. */
-
-        if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr,
-                                        &adv_addr, &addr_type,
-                                        &inita, &inita_type, &ext_adv_mode)) {
-            /* Something got wrong, keep trying to connect */
-            goto scan_continue;
-        }
+    if (ble_ll_sched_master_new(connsm, rxhdr, 0)) {
+        return -1;
+    }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-        /*
-         * Did we resolve this address? If so, set correct peer address
-         * and peer address type.
-         */
-        if (connsm->rpa_index >= 0) {
-            addr_type = g_ble_ll_resolv_list[connsm->rpa_index].rl_addr_type + 2;
-            addr = g_ble_ll_resolv_list[connsm->rpa_index].rl_identity_addr;
-        } else {
-            addr = adv_addr;
-        }
+    rpa_index = addrd->rpa_index;
 #else
-        addr = adv_addr;
-#endif
-
-        if (connsm->rpa_index >= 0) {
-            connsm->peer_addr_type = addr_type;
-            memcpy(connsm->peer_addr, addr, BLE_DEV_ADDR_LEN);
-
-            ble_ll_scan_set_peer_rpa(adv_addr);
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-            /* Update resolving list with current peer RPA */
-            ble_ll_resolv_set_peer_rpa(connsm->rpa_index, rxbuf + BLE_LL_PDU_HDR_LEN);
-            if (ble_ll_is_rpa(inita, inita_type)) {
-                ble_ll_resolv_set_local_rpa(connsm->rpa_index, inita);
-            }
-
+    rpa_index = -1;
 #endif
-        } else if (ble_ll_scan_whitelist_enabled()) {
-            /* if WL is used we need to store peer addr also if it was not
-             * resolved
-             */
-            connsm->peer_addr_type = addr_type;
-            memcpy(connsm->peer_addr, addr, BLE_DEV_ADDR_LEN);
-        }
+    ble_ll_conn_prepare_connect_ind(connsm, ble_ll_scan_get_pdu_data(),
+                                    addrd->adva_type, addrd->adva,
+                                    addrd->targeta_type, addrd->targeta,
+                                    rpa_index, rxhdr->rxinfo.channel);
 
-        /* Connection has been created. Stop scanning */
-        g_ble_ll_conn_create_sm = NULL;
-        ble_ll_scan_sm_stop(0);
-
-        /* For AUX Connect CSA2 is mandatory. Otherwise we need to check bit
-         * mask
-         */
-        if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) {
-            ble_ll_conn_set_csa(connsm, 1);
-        } else {
-            ble_ll_conn_set_csa(connsm, rxbuf[0] & BLE_ADV_PDU_HDR_CHSEL_MASK);
-        }
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
-        /* Lets take last used phy */
-        ble_ll_conn_init_phy(connsm, ble_hdr->rxinfo.phy);
-#endif
-        if (aux_data) {
-            ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data);
-            ble_hdr->rxinfo.user_data = NULL;
-        }
-#endif
-        ble_ll_conn_created(connsm, NULL);
-        return;
+    ble_phy_set_txend_cb(NULL, NULL);
+    rc = ble_phy_tx(ble_ll_conn_tx_connect_ind_pducb, connsm,
+                    ext ? BLE_PHY_TRANSITION_TX_RX : BLE_PHY_TRANSITION_NONE);
+    if (rc) {
+        ble_ll_conn_send_connect_req_cancel();
+        return -1;
     }
 
-scan_continue:
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    /* Drop last reference and keep continue to connect */
-    if (aux_data) {
-        ble_ll_scan_aux_data_unref(ble_hdr->rxinfo.user_data);
-        ble_hdr->rxinfo.user_data = NULL;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && BLE_LL_BT5_PHY_SUPPORTED
+    if (ext) {
+        ble_ll_conn_init_phy(connsm, phy);
     }
 #endif
-    ble_ll_scan_chk_resume();
+
+    return 0;
 }
 
-/**
- * Called when a receive PDU has started and we are in the initiating state.
- *
- * Context: Interrupt
- *
- * @param pdu_type
- * @param ble_hdr
- *
- * @return int
- *  0: we will not attempt to reply to this frame
- *  1: we may send a response to this frame.
- */
-int
-ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr)
+void
+ble_ll_conn_send_connect_req_cancel(void)
 {
     struct ble_ll_conn_sm *connsm;
 
-    connsm = g_ble_ll_conn_create_sm;
-    if (!connsm) {
-        return 0;
-    }
-
-    if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) ||
-        (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND ||
-         pdu_type == BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP)) {
-        return 1;
-    }
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND &&
-                                                connsm->scansm->ext_scanning) {
-        if (connsm->scansm->cur_aux_data) {
-            STATS_INC(ble_ll_stats, aux_received);
-        }
-
-        ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_EXT_ADV;
-        return 1;
-    }
-#endif
+    connsm = g_ble_ll_conn_create_sm.connsm;
 
-    return 0;
+    ble_ll_sched_rmv_elem(&connsm->conn_sch);
 }
 
-/**
- * Called when a receive PDU has ended and we are in the initiating state.
- *
- * Context: Interrupt
- *
- * @param rxpdu
- * @param crcok
- * @param ble_hdr
- *
- * @return int
- *       < 0: Disable the phy after reception.
- *      == 0: Success. Do not disable the PHY.
- *       > 0: Do not disable PHY as that has already been done.
- */
-int
-ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok,
-                       struct ble_mbuf_hdr *ble_hdr)
+static void
+ble_ll_conn_master_start(uint8_t phy, uint8_t csa,
+                         struct ble_ll_scan_addr_data *addrd, uint8_t *targeta)
 {
-    int rc;
-    int resolved;
-    int chk_wl;
-    int index;
-    uint8_t pdu_type;
-    uint8_t adv_addr_type;
-    uint8_t peer_addr_type;
-    uint8_t *adv_addr = NULL;
-    uint8_t *peer;
-    uint8_t *init_addr = NULL;
-    uint8_t init_addr_type;
-    uint8_t pyld_len;
-    uint8_t inita_is_rpa;
-    uint8_t conn_req_end_trans;
-    struct os_mbuf *rxpdu;
     struct ble_ll_conn_sm *connsm;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    struct ble_ll_resolv_entry *rl;
-#endif
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_scan_sm *scansm;
-    uint8_t phy;
-#endif
-    int ext_adv_mode = -1;
 
-    /* Get connection state machine to use if connection to be established */
-    connsm = g_ble_ll_conn_create_sm;
-    /* This could happen if connection init was cancelled while isr end was
-     * already pending
-     */
-    if (!connsm) {
-        ble_ll_state_set(BLE_LL_STATE_STANDBY);
-        return -1;
-    }
+    connsm = g_ble_ll_conn_create_sm.connsm;
+    g_ble_ll_conn_create_sm.connsm = NULL;
 
-    rc = -1;
-    pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
-    pyld_len = rxbuf[1];
+    connsm->peer_addr_type = addrd->adv_addr_type;
+    memcpy(connsm->peer_addr, addrd->adv_addr, 6);
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    scansm = connsm->scansm;
-    if (scansm->cur_aux_data) {
-        ble_hdr->rxinfo.user_data = scansm->cur_aux_data;
-        scansm->cur_aux_data = NULL;
-    }
-#endif
-
-    if (!crcok) {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        /* Invalid packet - make sure we do not wait for AUX_CONNECT_RSP */
-        ble_ll_conn_reset_pending_aux_conn_rsp();
-#endif
-
-        /* Ignore this packet */
-        goto init_rx_isr_exit;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    if (addrd->adva_resolved) {
+        BLE_LL_ASSERT(addrd->rpa_index >= 0);
+        connsm->peer_addr_resolved = 1;
+        ble_ll_resolv_set_peer_rpa(addrd->rpa_index, addrd->adva);
+        ble_ll_scan_set_peer_rpa(addrd->adva);
+    } else {
+        connsm->peer_addr_resolved = 0;
     }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    /* If we sent AUX_CONNECT_REQ, we only expect AUX_CONNECT_RSP here */
-    if (CONN_F_AUX_CONN_REQ(connsm)) {
-        if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
-            STATS_INC(ble_ll_stats, aux_conn_rsp_err);
-            CONN_F_CONN_REQ_TXD(connsm) = 0;
-            CONN_F_AUX_CONN_REQ(connsm) = 0;
-            ble_ll_sched_rmv_elem(&connsm->conn_sch);
-        }
-        goto init_rx_isr_exit;
+    if (addrd->targeta_resolved) {
+        BLE_LL_ASSERT(addrd->rpa_index >= 0);
+        BLE_LL_ASSERT(targeta);
+        ble_ll_resolv_set_local_rpa(addrd->rpa_index, targeta);
     }
 #endif
 
-    inita_is_rpa = 0;
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
-        if (!scansm->ext_scanning) {
-            goto init_rx_isr_exit;
-        }
-
-        rc = ble_ll_scan_update_aux_data(ble_hdr, rxbuf, NULL);
-        if (rc < 0) {
-            /* No memory or broken packet */
-            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;
-            goto init_rx_isr_exit;
-        }
-    }
-#endif
-
-    /* Lets get addresses from advertising report*/
-    if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr,
-                                    &adv_addr, &adv_addr_type,
-                                    &init_addr, &init_addr_type,
-                                    &ext_adv_mode)) {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;
-#endif
-        goto init_rx_isr_exit;
-    }
-
-    switch (pdu_type) {
-    case BLE_ADV_PDU_TYPE_ADV_IND:
-        break;
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
-        rc = -1;
-
-        /* If this is not connectable adv mode, lets skip it */
-        if (!(ext_adv_mode & BLE_LL_EXT_ADV_MODE_CONN)) {
-            goto init_rx_isr_exit;
-        }
-
-        if (!adv_addr) {
-            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT;
-            goto init_rx_isr_exit;
-        }
-
-        if (!init_addr) {
-            break;
-        }
-        /* if there is direct address lets fall down and check it.*/
-        // no break
-#endif
-    case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
-        inita_is_rpa = (uint8_t)ble_ll_is_rpa(init_addr, init_addr_type);
-        if (!inita_is_rpa) {
-
-            /* Resolving will be done later. Check if identity InitA matches */
-            if (!ble_ll_is_our_devaddr(init_addr, init_addr_type)) {
-                goto init_rx_isr_exit;
-            }
-        }
-#if !MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-        else {
-            /* If privacy is off - reject RPA InitA*/
-            goto init_rx_isr_exit;
-        }
+    ble_ll_conn_set_csa(connsm, csa);
+#if BLE_LL_BT5_PHY_SUPPORTED
+    ble_ll_conn_init_phy(connsm, phy);
 #endif
+    ble_ll_conn_created(connsm, NULL);
+}
 
-        break;
-    default:
-        goto init_rx_isr_exit;
-    }
-
-    /* Should we send a connect request? */
-    index = -1;
-    peer = adv_addr;
-    peer_addr_type = adv_addr_type;
-
-    resolved = 0;
-    chk_wl = ble_ll_scan_whitelist_enabled();
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    if (ble_ll_is_rpa(adv_addr, adv_addr_type) && ble_ll_resolv_enabled()) {
-        index = ble_hw_resolv_list_match();
-        if (index >= 0) {
-            rl = &g_ble_ll_resolv_list[index];
-
-            ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED;
-            connsm->rpa_index = index;
-            peer = rl->rl_identity_addr;
-            peer_addr_type = rl->rl_addr_type;
-            resolved = 1;
-
-            /* Assure privacy */
-            if ((rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && init_addr &&
-                !inita_is_rpa && rl->rl_has_local) {
-                goto init_rx_isr_exit;
-            }
-
-            /*
-             * If the InitA is a RPA, we must see if it resolves based on the
-             * identity address of the resolved ADVA.
-             */
-            if (init_addr && inita_is_rpa) {
-                if (!ble_ll_resolv_rpa(init_addr,
-                                       g_ble_ll_resolv_list[index].rl_local_irk)) {
-                    goto init_rx_isr_exit;
-                }
-
-                /* Core Specification Vol 6, Part B, Section 6.4:
-                 * "The Link Layer should not set the InitA field to the same
-                 * value as the TargetA field in the received advertising PDU."
-                 *
-                 * We update the received PDU directly here, so ble_ll_init_rx_pkt_in
-                 * can process it as is.
-                 */
-                memcpy(init_addr, rl->rl_local_rpa, BLE_DEV_ADDR_LEN);
-            }
-
-        } else {
-            if (chk_wl) {
-                goto init_rx_isr_exit;
-            }
-
-            /* Could not resolved InitA */
-            if (init_addr && inita_is_rpa) {
-                goto init_rx_isr_exit;
-            }
-        }
-    } else if (init_addr) {
-
-        /* If resolving is off and InitA is RPA we reject advertising */
-        if (inita_is_rpa && !ble_ll_resolv_enabled()) {
-            goto init_rx_isr_exit;
-        }
-
-        /* Let's see if we have IRK with that peer.*/
-        rl = ble_ll_resolv_list_find(adv_addr, adv_addr_type);
-
-        /* Lets make sure privacy mode is correct together with InitA in case it
-         * is identity address
-         */
-        if (rl && !inita_is_rpa &&
-           (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) &&
-           rl->rl_has_local) {
-            goto init_rx_isr_exit;
-        }
+void
+ble_ll_conn_created_on_legacy(struct os_mbuf *rxpdu,
+                              struct ble_ll_scan_addr_data *addrd,
+                              uint8_t *targeta)
+{
+    uint8_t *rxbuf;
+    uint8_t csa;
 
-        /*
-         * If the InitA is a RPA, we must see if it resolves based on the
-         * identity address of the resolved ADVA.
-         */
-        if (inita_is_rpa) {
-            if (!rl || !ble_ll_resolv_rpa(init_addr, rl->rl_local_irk)) {
-                goto init_rx_isr_exit;
-            }
+    rxbuf = rxpdu->om_data;
+    csa = rxbuf[0] & BLE_ADV_PDU_HDR_CHSEL_MASK;
 
-            /* Core Specification Vol 6, Part B, Section 6.4:
-             * "The Link Layer should not set the InitA field to the same
-             * value as the TargetA field in the received advertising PDU."
-             *
-             * We update the received PDU directly here, so ble_ll_init_rx_pkt_in
-             * can process it as is.
-             */
-            memcpy(init_addr, rl->rl_local_rpa, BLE_DEV_ADDR_LEN);
-        }
-    } else if (!ble_ll_is_rpa(adv_addr, adv_addr_type)) {
-        /* undirected with ID address, assure privacy if on RL */
-        rl = ble_ll_resolv_list_find(adv_addr, adv_addr_type);
-        if (rl && (rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) &&
-            rl->rl_has_peer) {
-            goto init_rx_isr_exit;
-        }
-    }
+    ble_ll_conn_master_start(BLE_PHY_1M, csa, addrd, targeta);
+}
 #endif
 
-    /* Check filter policy */
-    if (chk_wl) {
-        if (!ble_ll_whitelist_match(peer, peer_addr_type, resolved)) {
-            goto init_rx_isr_exit;
-        }
-    } else {
-        /* Must match the connection address */
-        if (!ble_ll_conn_is_peer_adv(adv_addr_type, adv_addr, index)) {
-            goto init_rx_isr_exit;
-        }
-    }
-    ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;
-
-    /* For CONNECT_IND we don't go into RX state */
-    conn_req_end_trans = BLE_PHY_TRANSITION_NONE;
-
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    /* Check if we should send AUX_CONNECT_REQ and wait for AUX_CONNECT_RSP */
-    if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) {
-        conn_req_end_trans = BLE_PHY_TRANSITION_TX_RX;
-    }
-
-    if (connsm->scansm->ext_scanning) {
-            phy = ble_hdr->rxinfo.phy;
-
-            /* Update connection state machine with appropriate parameters for
-             * certain PHY
-             */
-            ble_ll_conn_ext_set_params(connsm,
-                                       &connsm->initial_params.params[phy - 1],
-                                       phy);
-
-    }
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+void
+ble_ll_conn_created_on_aux(struct os_mbuf *rxpdu,
+                           struct ble_ll_scan_addr_data *addrd,
+                           uint8_t *targeta)
+{
+#if BLE_LL_BT5_PHY_SUPPORTED
+    struct ble_mbuf_hdr *rxhdr;
 #endif
+    uint8_t phy;
 
-    /* Schedule new connection */
-    if (ble_ll_sched_master_new(connsm, ble_hdr, pyld_len)) {
-        STATS_INC(ble_ll_conn_stats, cant_set_sched);
-        goto init_rx_isr_exit;
-    }
-
-    /* Prepare data for connect request */
-    ble_ll_conn_connect_ind_prepare(connsm,
-                                    ble_ll_scan_get_pdu_data(),
-                                    adv_addr_type, adv_addr,
-                                    init_addr_type, init_addr,
-                                    index, ble_hdr->rxinfo.channel);
-
-    /* Setup to transmit the connect request */
-    rc = ble_ll_conn_connect_ind_send(connsm, conn_req_end_trans);
-    if (rc) {
-        ble_ll_sched_rmv_elem(&connsm->conn_sch);
-        goto init_rx_isr_exit;
-    }
-
-    if (init_addr && !inita_is_rpa) {
-        connsm->inita_identity_used = 1;
-    }
-
-    CONN_F_CONN_REQ_TXD(connsm) = 1;
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) {
-        /* Lets wait for AUX_CONNECT_RSP */
-        CONN_F_AUX_CONN_REQ(connsm) = 1;
-        /* Keep aux data until we get scan response */
-        scansm->cur_aux_data = ble_hdr->rxinfo.user_data;
-        ble_hdr->rxinfo.user_data = NULL;
-        STATS_INC(ble_ll_stats, aux_conn_req_tx);
-    }
+#if BLE_LL_BT5_PHY_SUPPORTED
+    rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
+    phy = rxhdr->rxinfo.phy;
+#else
+    phy = BLE_PHY_1M;
 #endif
 
-    STATS_INC(ble_ll_conn_stats, conn_req_txd);
-
-init_rx_isr_exit:
-
-    /*
-     * We have to restart receive if we cant hand up pdu. We return 0 so that
-     * the phy does not get disabled.
-     */
-    rxpdu = ble_ll_rxpdu_alloc(pyld_len + BLE_LL_PDU_HDR_LEN);
-    if (rxpdu == NULL) {
-        /*
-         * XXX: possible allocate the PDU when we start initiating?
-         * I cannot say I like this solution, but if we cannot allocate a PDU
-         * to hand up to the LL, we need to remove the connection we just
-         * scheduled since the connection state machine will not get processed
-         * by link layer properly. For now, just remove it from the scheduler
-         */
-        if (CONN_F_CONN_REQ_TXD(connsm) == 1) {
-            CONN_F_CONN_REQ_TXD(connsm) = 0;
-            CONN_F_AUX_CONN_REQ(connsm) = 0;
-            ble_ll_sched_rmv_elem(&connsm->conn_sch);
-        }
-        ble_phy_restart_rx();
-        rc = 0;
-    } else {
-        ble_phy_rxpdu_copy(rxbuf, rxpdu);
-        ble_ll_rx_pdu_in(rxpdu);
-    }
-
-    if (rc) {
-        ble_ll_state_set(BLE_LL_STATE_STANDBY);
-    }
-
-    return rc;
+    ble_ll_conn_master_start(phy, 1, addrd, targeta);
 }
+#endif
+#endif /* BLE_LL_CFG_FEAT_LL_EXT_ADV */
 
 /**
  * Function called when a timeout has occurred for a connection. There are
@@ -3695,11 +3052,13 @@ ble_ll_conn_rx_data_pdu(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr)
      * If we are a slave, we can only start to use slave latency
      * once we have received a NESN of 1 from the master
      */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         if (hdr_byte & BLE_LL_DATA_HDR_NESN_MASK) {
             connsm->csmflags.cfbit.allow_slave_latency = 1;
         }
     }
+#endif
 
     /*
      * Discard the received PDU if the sequence number is the same
@@ -3791,7 +3150,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
     uint8_t conn_sn;
     uint8_t conn_nesn;
     uint8_t reply;
-    uint8_t rem_bytes;
+    uint16_t rem_bytes;
     uint8_t opcode = 0;
     uint8_t rx_pyld_len;
     uint32_t begtime;
@@ -3872,7 +3231,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
     /*
      * Check the packet CRC. A connection event can continue even if the
      * received PDU does not pass the CRC check. If we receive two consecutive
-     * CRC errors we end the conection event.
+     * CRC errors we end the connection event.
      */
     if (!BLE_MBUF_HDR_CRC_OK(rxhdr)) {
         /*
@@ -3883,11 +3242,21 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
         if (connsm->cons_rxd_bad_crc >= 2) {
             reply = 0;
         } else {
-            if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+            switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+            case BLE_LL_CONN_ROLE_MASTER:
                 reply = CONN_F_LAST_TXD_MD(connsm);
-            } else {
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            case BLE_LL_CONN_ROLE_SLAVE:
                 /* A slave always responds with a packet */
                 reply = 1;
+                break;
+#endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
             }
         }
     } else {
@@ -4001,7 +3370,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
 
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
                         if (BLE_LL_LLID_IS_CTRL(hdr_byte) &&
-                            (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) &&
+                            CONN_IS_SLAVE(connsm) &&
                             (opcode == BLE_LL_CTRL_PHY_UPDATE_IND)) {
                             connsm->phy_tx_transition =
                                     ble_ll_ctrl_phy_tx_transition_get(rxbuf[3]);
@@ -4026,11 +3395,21 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
             connsm->rxd_disconnect_reason = rxbuf[3];
         }
 
-        if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+        switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_LL_CONN_ROLE_MASTER:
             reply = CONN_F_LAST_TXD_MD(connsm) || (hdr_byte & BLE_LL_DATA_HDR_MD_MASK);
-        } else {
+            break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        case BLE_LL_CONN_ROLE_SLAVE:
             /* A slave always replies */
             reply = 1;
+            break;
+#endif
+        default:
+            BLE_LL_ASSERT(0);
+            break;
         }
     }
 
@@ -4074,7 +3453,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
  */
 void
 ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om,
-                        uint8_t hdr_byte, uint8_t length)
+                        uint8_t hdr_byte, uint16_t length)
 {
     os_sr_t sr;
     struct os_mbuf_pkthdr *pkthdr;
@@ -4118,9 +3497,11 @@ ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om,
                 lifo = 1;
                 break;
             case BLE_LL_CTRL_PAUSE_ENC_RSP:
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
                 if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
                     lifo = 1;
                 }
+#endif
                 break;
             case BLE_LL_CTRL_ENC_REQ:
             case BLE_LL_CTRL_ENC_RSP:
@@ -4191,7 +3572,7 @@ ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t length)
         os_mbuf_free_chain(om);
     }
 }
-
+#endif
 /**
  * Called to set the global channel mask that we use for all connections.
  *
@@ -4201,7 +3582,9 @@ ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t length)
 void
 ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, const uint8_t *chanmap)
 {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     struct ble_ll_conn_sm *connsm;
+#endif
     struct ble_ll_conn_global_params *conn_params;
 
     /* Do nothing if same channel map */
@@ -4214,14 +3597,17 @@ ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, const uint8_t *chanmap)
     conn_params->num_used_chans = num_used_chans;
     memcpy(conn_params->master_chan_map, chanmap, BLE_LL_CONN_CHMAP_LEN);
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     /* Perform channel map update */
     SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) {
         if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
             ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_CHAN_MAP_UPD);
         }
     }
+#endif
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 /**
  * Called when a device has received a connect request while advertising and
  * the connect request has passed the advertising filter policy and is for
@@ -4235,6 +3621,7 @@ ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, const uint8_t *chanmap)
  *
  * @return 0: connection not started; 1 connecton started
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 int
 ble_ll_conn_slave_start(uint8_t *rxbuf, uint8_t pat, struct ble_mbuf_hdr *rxhdr,
                         bool force_csa2)
@@ -4324,6 +3711,9 @@ ble_ll_conn_slave_start(uint8_t *rxbuf, uint8_t pat, struct ble_mbuf_hdr *rxhdr,
         goto err_slave_start;
     }
 
+    ble_ll_conn_itvl_to_ticks(connsm->conn_itvl, &connsm->conn_itvl_ticks,
+                              &connsm->conn_itvl_usecs);
+
     /* Start the connection state machine */
     connsm->conn_role = BLE_LL_CONN_ROLE_SLAVE;
     ble_ll_conn_sm_new(connsm);
@@ -4350,6 +3740,7 @@ ble_ll_conn_slave_start(uint8_t *rxbuf, uint8_t pat, struct ble_mbuf_hdr *rxhdr,
     STATS_INC(ble_ll_conn_stats, slave_rxd_bad_conn_req_params);
     return 0;
 }
+#endif
 
 #define MAX_TIME_UNCODED(_maxbytes) \
         ble_ll_pdu_tx_time_get(_maxbytes + BLE_LL_DATA_MIC_LEN, \
@@ -4380,6 +3771,7 @@ ble_ll_conn_module_reset(void)
         ble_ll_conn_end(connsm, BLE_ERR_SUCCESS);
     }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     /* Free the global connection complete event if there is one */
     if (g_ble_ll_conn_comp_ev) {
         ble_hci_trans_buf_free(g_ble_ll_conn_comp_ev);
@@ -4387,7 +3779,8 @@ ble_ll_conn_module_reset(void)
     }
 
     /* Reset connection we are attempting to create */
-    g_ble_ll_conn_create_sm = NULL;
+    g_ble_ll_conn_create_sm.connsm = NULL;
+#endif
 
     /* Now go through and end all the connections */
     while (1) {
@@ -4459,7 +3852,7 @@ ble_ll_conn_module_init(void)
     uint16_t i;
     struct ble_ll_conn_sm *connsm;
 
-    /* Initialize list of active conections */
+    /* Initialize list of active connections */
     SLIST_INIT(&g_ble_ll_conn_active_list);
     STAILQ_INIT(&g_ble_ll_conn_free_list);
 
@@ -4478,6 +3871,9 @@ ble_ll_conn_module_init(void)
         /* Initialize fixed schedule elements */
         connsm->conn_sch.sched_type = BLE_LL_SCHED_TYPE_CONN;
         connsm->conn_sch.cb_arg = connsm;
+
+        ble_ll_ctrl_init_conn_sm(connsm);
+
         ++connsm;
     }
 
@@ -4496,3 +3892,4 @@ ble_ll_conn_module_init(void)
     /* Call reset to finish reset of initialization */
     ble_ll_conn_module_reset();
 }
+#endif
diff --git a/nimble/controller/src/ble_ll_conn_hci.c b/nimble/controller/src/ble_ll_conn_hci.c
index 9936b9d34e..2e50713e84 100644
--- a/nimble/controller/src/ble_ll_conn_hci.c
+++ b/nimble/controller/src/ble_ll_conn_hci.c
@@ -23,7 +23,6 @@
 #include "syscfg/syscfg.h"
 #include "os/os.h"
 #include "nimble/ble.h"
-#include "nimble/nimble_opt.h"
 #include "nimble/hci_common.h"
 #include "nimble/ble_hci_trans.h"
 #include "controller/ble_ll.h"
@@ -35,6 +34,8 @@
 #include "controller/ble_ll_adv.h"
 #include "ble_ll_conn_priv.h"
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+
 /*
  * Used to limit the rate at which we send the number of completed packets
  * event to the host. This is the os time at which we can send an event.
@@ -43,19 +44,22 @@ static ble_npl_time_t g_ble_ll_last_num_comp_pkt_evt;
 extern uint8_t *g_ble_ll_conn_comp_ev;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static const uint8_t ble_ll_valid_conn_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK
+static const uint8_t ble_ll_conn_create_valid_phy_mask = (
+        BLE_HCI_LE_PHY_1M_PREF_MASK |
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
-                                | BLE_HCI_LE_PHY_2M_PREF_MASK
+        BLE_HCI_LE_PHY_2M_PREF_MASK |
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-                                | BLE_HCI_LE_PHY_CODED_PREF_MASK
+        BLE_HCI_LE_PHY_CODED_PREF_MASK |
 #endif
-                              );
-static const uint8_t ble_ll_conn_required_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK
+        0);
+
+static const uint8_t ble_ll_conn_create_required_phy_mask = (
+        BLE_HCI_LE_PHY_1M_PREF_MASK |
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-                            | BLE_HCI_LE_PHY_CODED_PREF_MASK
+        BLE_HCI_LE_PHY_CODED_PREF_MASK |
 #endif
-                            );
+        0);
 #endif
 
 /**
@@ -156,7 +160,9 @@ ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status,
             enh_ev->peer_addr_type = connsm->peer_addr_type;
             memcpy(enh_ev->peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN);
 
-            if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+            switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+            case BLE_LL_CONN_ROLE_MASTER:
                 if (connsm->inita_identity_used) {
                     /* We used identity address in CONNECT_IND which can be just
                      * fine if
@@ -171,35 +177,57 @@ ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status,
                 } else {
                     rpa = NULL;
                 }
-            } else {
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            case BLE_LL_CONN_ROLE_SLAVE:
                 rpa = ble_ll_adv_get_local_rpa(advsm);
+                break;
+#endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
             }
 
             if (rpa) {
                 memcpy(enh_ev->local_rpa, rpa, BLE_DEV_ADDR_LEN);
             }
 
-            /* We need to adjust peer type if device connected using RPA
-             * and was resolved since RPA needs to be added to HCI event.
-             */
-             if (connsm->peer_addr_type < BLE_HCI_CONN_PEER_ADDR_PUBLIC_IDENT
-                     && (connsm->rpa_index > -1)) {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+            /* Adjust address type if peer address was resolved */
+             if (connsm->peer_addr_resolved) {
                  enh_ev->peer_addr_type += 2;
              }
+#endif
 
              if (enh_ev->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) {
-                 if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+                 switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+                 case BLE_LL_CONN_ROLE_MASTER:
                      rpa = ble_ll_scan_get_peer_rpa();
-                 } else {
+                     break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+                 case BLE_LL_CONN_ROLE_SLAVE:
                      rpa = ble_ll_adv_get_peer_rpa(advsm);
+                 break;
+#endif
+                 default:
+                     BLE_LL_ASSERT(0);
+                     break;
                  }
+
                  memcpy(enh_ev->peer_rpa, rpa, BLE_DEV_ADDR_LEN);
              }
 
             enh_ev->conn_itvl = htole16(connsm->conn_itvl);
             enh_ev->conn_latency = htole16(connsm->slave_latency);
             enh_ev->supervision_timeout = htole16(connsm->supervision_tmo);
-            enh_ev->mca = connsm->master_sca;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+                enh_ev->mca = connsm->master_sca;
+            }
+#endif
         }
 
         ble_ll_hci_event_send(hci_ev);
@@ -220,15 +248,15 @@ ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status,
             ev->conn_handle = htole16(connsm->conn_handle);
             ev->role = connsm->conn_role - 1;
             ev->peer_addr_type = connsm->peer_addr_type;
-
-            if (ev->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) {
-                ev->peer_addr_type -= 2;
-            }
             memcpy(ev->peer_addr, connsm->peer_addr, BLE_DEV_ADDR_LEN);
             ev->conn_itvl = htole16(connsm->conn_itvl);
             ev->conn_latency = htole16(connsm->slave_latency);
             ev->supervision_timeout = htole16(connsm->supervision_tmo);
-            ev->mca = connsm->master_sca;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+                ev->mca = connsm->master_sca;
+            }
+#endif
         }
 
         ble_ll_hci_event_send(hci_ev);
@@ -406,18 +434,64 @@ ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t reason)
     }
 }
 
+int
+ble_ll_conn_hci_create_check_scan(struct ble_ll_conn_create_scan *p)
+{
+    if (p->filter_policy > BLE_HCI_INITIATOR_FILT_POLICY_MAX) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if ((p->filter_policy == 0) &&
+        (p->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (p->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    if (p->init_phy_mask & ~ble_ll_conn_create_valid_phy_mask) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (!(p->init_phy_mask & ble_ll_conn_create_required_phy_mask)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+#endif
+
+    return 0;
+}
+
 static int
-ble_ll_conn_hci_chk_scan_params(uint16_t itvl, uint16_t window)
+ble_ll_conn_hci_create_check_params(struct ble_ll_conn_create_params *cc_params)
 {
-    /* Check interval and window */
-    if ((itvl < BLE_HCI_SCAN_ITVL_MIN) ||
-        (itvl > BLE_HCI_SCAN_ITVL_MAX) ||
-        (window < BLE_HCI_SCAN_WINDOW_MIN) ||
-        (window > BLE_HCI_SCAN_WINDOW_MAX) ||
-        (itvl < window)) {
+    int rc;
+
+    rc = ble_ll_conn_hci_chk_conn_params(cc_params->conn_itvl,
+                                         cc_params->conn_itvl,
+                                         cc_params->conn_latency,
+                                         cc_params->supervision_timeout);
+    if (rc) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (cc_params->min_ce_len > cc_params->max_ce_len) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
+    /* Adjust min/max ce length to be less than interval */
+    if (cc_params->min_ce_len > cc_params->conn_itvl) {
+        cc_params->min_ce_len = cc_params->conn_itvl;
+    }
+    if (cc_params->max_ce_len > cc_params->conn_itvl) {
+        cc_params->max_ce_len = cc_params->conn_itvl;
+    }
+
+    /* Precalculate conn interval */
+    ble_ll_conn_itvl_to_ticks(cc_params->conn_itvl, &cc_params->conn_itvl_ticks,
+                              &cc_params->conn_itvl_usecs);
+
     return 0;
 }
 
@@ -431,11 +505,14 @@ ble_ll_conn_hci_chk_scan_params(uint16_t itvl, uint16_t window)
  * @return int
  */
 int
-ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len)
+ble_ll_conn_hci_create(const uint8_t *cmdbuf, uint8_t len)
 {
     const struct ble_hci_le_create_conn_cp *cmd = (const void *) cmdbuf;
+    struct ble_ll_conn_create_scan cc_scan;
+    struct ble_ll_conn_create_params cc_params;
     struct ble_ll_conn_sm *connsm;
-    struct hci_create_conn hcc = { 0 };
+    uint16_t conn_itvl_min;
+    uint16_t conn_itvl_max;
     int rc;
 
     if (len < sizeof(*cmd)) {
@@ -443,7 +520,7 @@ ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len)
     }
 
     /* If we are already creating a connection we should leave */
-    if (g_ble_ll_conn_create_sm) {
+    if (g_ble_ll_conn_create_sm.connsm) {
         return BLE_ERR_CMD_DISALLOWED;
     }
 
@@ -452,57 +529,48 @@ ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len)
         return BLE_ERR_CMD_DISALLOWED;
     }
 
-    /* Retrieve command data */
-    hcc.scan_itvl = le16toh(cmd->scan_itvl);
-    hcc.scan_window = le16toh(cmd->scan_window);
-
-    rc = ble_ll_conn_hci_chk_scan_params(hcc.scan_itvl, hcc.scan_window);
-    if (rc) {
-        return BLE_ERR_INV_HCI_CMD_PARMS;
-    }
-
-    /* Check filter policy */
-    hcc.filter_policy = cmd->filter_policy;
-    if (hcc.filter_policy > BLE_HCI_INITIATOR_FILT_POLICY_MAX) {
-        return BLE_ERR_INV_HCI_CMD_PARMS;
+    cc_scan.own_addr_type = cmd->own_addr_type;
+    cc_scan.filter_policy = cmd->filter_policy;
+    if (cc_scan.filter_policy == 0) {
+        cc_scan.peer_addr_type = cmd->peer_addr_type;
+        memcpy(&cc_scan.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN);
+    } else {
+        cc_scan.peer_addr_type = 0;
+        memset(&cc_scan.peer_addr, 0, BLE_DEV_ADDR_LEN);
     }
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    cc_scan.init_phy_mask = BLE_HCI_LE_PHY_1M_PREF_MASK;
+#endif
 
-    /* Get peer address type and address only if no whitelist used */
-    if (hcc.filter_policy == 0) {
-        hcc.peer_addr_type = cmd->peer_addr_type;
-        if (hcc.peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
+    cc_scan.scan_params[PHY_UNCODED].itvl = le16toh(cmd->scan_itvl);
+    cc_scan.scan_params[PHY_UNCODED].window = le16toh(cmd->scan_window);
 
-        memcpy(&hcc.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN);
+    rc = ble_ll_conn_hci_create_check_scan(&cc_scan);
+    if (rc) {
+        return rc;
     }
 
-    /* Get own address type (used in connection request) */
-    hcc.own_addr_type = cmd->own_addr_type;
-    if (hcc.own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) {
+    conn_itvl_min = le16toh(cmd->min_conn_itvl);
+    conn_itvl_max = le16toh(cmd->max_conn_itvl);
+
+    /* Check min/max interval here since generic check does not have min/max
+     * parameters to check.
+     */
+    if (conn_itvl_min > conn_itvl_max) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
-    /* Check connection interval, latency and supervision timeoout */
-    hcc.conn_itvl_min = le16toh(cmd->min_conn_itvl);
-    hcc.conn_itvl_max = le16toh(cmd->max_conn_itvl);
-    hcc.conn_latency = le16toh(cmd->conn_latency);
-    hcc.supervision_timeout = le16toh(cmd->tmo);
-    rc = ble_ll_conn_hci_chk_conn_params(hcc.conn_itvl_min,
-                                         hcc.conn_itvl_max,
-                                         hcc.conn_latency,
-                                         hcc.supervision_timeout);
+    cc_params.conn_itvl = conn_itvl_max;
+    cc_params.conn_latency = le16toh(cmd->conn_latency);
+    cc_params.supervision_timeout = le16toh(cmd->tmo);
+    cc_params.min_ce_len = le16toh(cmd->min_ce);
+    cc_params.max_ce_len = le16toh(cmd->max_ce);
+
+    rc = ble_ll_conn_hci_create_check_params(&cc_params);
     if (rc) {
         return rc;
     }
 
-    /* Min/max connection event lengths */
-    hcc.min_ce_len = le16toh(cmd->min_ce);
-    hcc.max_ce_len = le16toh(cmd->max_ce);
-    if (hcc.min_ce_len > hcc.max_ce_len) {
-        return BLE_ERR_INV_HCI_CMD_PARMS;
-    }
-
     /* Make sure we can allocate an event to send the connection complete */
     if (ble_ll_init_alloc_conn_comp_ev()) {
         return BLE_ERR_MEM_CAPACITY;
@@ -515,18 +583,14 @@ ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len)
     }
 
     /* Initialize state machine in master role and start state machine */
-    ble_ll_conn_master_init(connsm, &hcc);
+    ble_ll_conn_master_init(connsm, &cc_scan, &cc_params);
     ble_ll_conn_sm_new(connsm);
-    /* CSA will be selected when advertising is received */
 
     /* Start scanning */
-    rc = ble_ll_scan_initiator_start(&hcc, &connsm->scansm);
+    rc = ble_ll_scan_initiator_start(connsm, 0, &cc_scan);
     if (rc) {
         SLIST_REMOVE(&g_ble_ll_conn_active_list,connsm,ble_ll_conn_sm,act_sle);
         STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe);
-    } else {
-        /* Set the connection state machine we are trying to create. */
-        g_ble_ll_conn_create_sm = connsm;
     }
 
     return rc;
@@ -534,198 +598,166 @@ ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len)
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
 static void
-ble_ll_conn_hcc_params_set_fallback(struct hci_ext_create_conn *hcc,
-                                    const struct hci_ext_conn_params *fallback)
+ble_ll_conn_hci_ext_create_set_fb_params(uint8_t init_phy_mask,
+                                         struct ble_ll_conn_create_params *cc_params_fb)
 {
-    BLE_LL_ASSERT(fallback);
-
-    if (!(hcc->init_phy_mask & BLE_PHY_MASK_1M)) {
-        hcc->params[0] = *fallback;
+    if ((init_phy_mask & BLE_PHY_MASK_1M) == 0) {
+        g_ble_ll_conn_create_sm.params[BLE_PHY_IDX_1M] = *cc_params_fb;
     }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
-    if (!(hcc->init_phy_mask & BLE_PHY_MASK_2M)) {
-        hcc->params[1] = *fallback;
+    if ((init_phy_mask & BLE_PHY_MASK_2M) == 0) {
+        g_ble_ll_conn_create_sm.params[BLE_PHY_IDX_2M] = *cc_params_fb;
     }
 #endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-    if (!(hcc->init_phy_mask & BLE_PHY_MASK_CODED)) {
-        hcc->params[2] = *fallback;
+    if ((init_phy_mask & BLE_PHY_MASK_CODED) == 0) {
+        g_ble_ll_conn_create_sm.params[BLE_PHY_IDX_CODED] = *cc_params_fb;
     }
 #endif
 }
 
-int
-ble_ll_ext_conn_create(const uint8_t *cmdbuf, uint8_t len)
+static int
+ble_ll_conn_hci_ext_create_parse_params(const struct conn_params *params,
+                                        uint8_t phy,
+                                        struct ble_ll_conn_create_scan *cc_scan,
+                                        struct ble_ll_conn_create_params *cc_params)
 {
-    const struct ble_hci_le_ext_create_conn_cp *cmd = (const void *) cmdbuf;
-    const struct conn_params *params = cmd->conn_params;
-    const struct hci_ext_conn_params *fallback_params = NULL;
-    struct hci_ext_create_conn hcc = { 0 };
-    struct ble_ll_conn_sm *connsm;
+    uint16_t conn_itvl_min;
+    uint16_t conn_itvl_max;
+    uint16_t scan_itvl;
+    uint16_t scan_window;
     int rc;
 
-    /* validate length */
-    if (len < sizeof(*cmd)) {
+    conn_itvl_min = le16toh(params->conn_min_itvl);
+    conn_itvl_max = le16toh(params->conn_max_itvl);
+
+    /* Check min/max interval here since generic check does not have min/max
+     * parameters to check.
+     */
+    if (conn_itvl_min > conn_itvl_max) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
-    len -= sizeof(*cmd);
+    cc_params->conn_itvl = conn_itvl_max;
+    cc_params->conn_latency = le16toh(params->conn_latency);
+    cc_params->supervision_timeout = le16toh(params->supervision_timeout);
+    cc_params->min_ce_len = le16toh(params->min_ce);
+    cc_params->max_ce_len = le16toh(params->max_ce);
 
-    /* If we are already creating a connection we should leave */
-    if (g_ble_ll_conn_create_sm) {
-        return BLE_ERR_CMD_DISALLOWED;
+    rc = ble_ll_conn_hci_create_check_params(cc_params);
+    if (rc) {
+        return rc;
     }
 
-    /* If already enabled, we return an error */
-    if (ble_ll_scan_enabled()) {
-        return BLE_ERR_CMD_DISALLOWED;
-    }
+    if (phy != BLE_PHY_2M) {
+        scan_itvl = le16toh(params->scan_itvl);
+        scan_window = le16toh(params->scan_window);
 
-    hcc.filter_policy = cmd->filter_policy;
-    if (hcc.filter_policy > BLE_HCI_INITIATOR_FILT_POLICY_MAX) {
-        return BLE_ERR_INV_HCI_CMD_PARMS;
-    }
+        if ((scan_itvl < BLE_HCI_SCAN_ITVL_MIN) ||
+            (scan_itvl > BLE_HCI_SCAN_ITVL_MAX) ||
+            (scan_window < BLE_HCI_SCAN_WINDOW_MIN) ||
+            (scan_window > BLE_HCI_SCAN_WINDOW_MAX) ||
+            (scan_itvl < scan_window)) {
+                return BLE_ERR_INV_HCI_CMD_PARMS;
+        }
 
-    hcc.own_addr_type = cmd->own_addr_type;
-    if (hcc.own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) {
-        return BLE_ERR_INV_HCI_CMD_PARMS;
+        if (phy == BLE_PHY_1M) {
+            cc_scan->scan_params[PHY_UNCODED].itvl = scan_itvl;
+            cc_scan->scan_params[PHY_UNCODED].window = scan_window;
+        } else {
+            cc_scan->scan_params[PHY_CODED].itvl = scan_itvl;
+            cc_scan->scan_params[PHY_CODED].window = scan_window;
+        }
     }
 
-    /* Validate peer address type only if no whitelist used */
-    if (hcc.filter_policy == 0) {
-        hcc.peer_addr_type = cmd->peer_addr_type;
+    return 0;
+}
 
-        if (hcc.peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
+int
+ble_ll_conn_hci_ext_create(const uint8_t *cmdbuf, uint8_t len)
+{
+    static const struct init_phy {
+        uint8_t idx;
+        uint8_t mask;
+        uint8_t phy;
+    } init_phys[] = {
+            {BLE_PHY_IDX_1M, BLE_PHY_MASK_1M, BLE_PHY_1M},
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
+            {BLE_PHY_IDX_2M, BLE_PHY_MASK_2M, BLE_PHY_2M},
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+            {BLE_PHY_IDX_CODED, BLE_PHY_MASK_CODED, BLE_PHY_CODED},
+#endif
+    };
 
-        memcpy(hcc.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN);
-    }
+    const struct ble_hci_le_ext_create_conn_cp *cmd = (const void *)cmdbuf;
+    const struct conn_params *params = cmd->conn_params;
+    struct ble_ll_conn_create_scan cc_scan;
+    struct ble_ll_conn_create_params *cc_params;
+    struct ble_ll_conn_create_params *cc_params_fb;
+    struct ble_ll_conn_sm *connsm;
+    const struct init_phy *init_phy;
+    int rc;
 
-    hcc.init_phy_mask = cmd->init_phy_mask;
-    if (hcc.init_phy_mask & ~ble_ll_valid_conn_phy_mask) {
+    /* validate length */
+    if (len < sizeof(*cmd) +
+              __builtin_popcount(cmd->init_phy_mask) * sizeof(*params)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
-    if (!(hcc.init_phy_mask & ble_ll_conn_required_phy_mask)) {
-        /* At least one of those need to be set */
-        return BLE_ERR_INV_HCI_CMD_PARMS;
+    /* If we are already creating a connection we should leave */
+    if (g_ble_ll_conn_create_sm.connsm) {
+        return BLE_ERR_CMD_DISALLOWED;
     }
 
-    if (hcc.init_phy_mask & BLE_PHY_MASK_1M) {
-        if (len < sizeof(*params)) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
-        len -= sizeof(*params);
-
-        hcc.params[0].scan_itvl = le16toh(params->scan_itvl);
-        hcc.params[0].scan_window = le16toh(params->scan_window);
-
-        rc = ble_ll_conn_hci_chk_scan_params(hcc.params[0].scan_itvl,
-                                             hcc.params[0].scan_window);
-        if (rc) {
-            return rc;
-        }
-
-        hcc.params[0].conn_itvl_min = le16toh(params->conn_min_itvl);
-        hcc.params[0].conn_itvl_max = le16toh(params->conn_min_itvl);
-        hcc.params[0].conn_latency = le16toh(params->conn_latency);
-        hcc.params[0].supervision_timeout = le16toh(params->supervision_timeout);
-
-        rc = ble_ll_conn_hci_chk_conn_params(hcc.params[0].conn_itvl_min,
-                                             hcc.params[0].conn_itvl_max,
-                                             hcc.params[0].conn_latency,
-                                             hcc.params[0].supervision_timeout);
-        if (rc) {
-            return rc;
-        }
-
-        /* Min/max connection event lengths */
-        hcc.params[0].min_ce_len = le16toh(params->min_ce);
-        hcc.params[0].max_ce_len = le16toh(params->max_ce);
-        if (hcc.params[0].min_ce_len > hcc.params[0].max_ce_len) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
-
-        fallback_params = &hcc.params[0];
-        params++;
+    /* If already enabled, we return an error */
+    if (ble_ll_scan_enabled()) {
+        return BLE_ERR_CMD_DISALLOWED;
     }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
-    if (hcc.init_phy_mask & BLE_PHY_MASK_2M) {
-        if (len < sizeof(*params)) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
-        len -= sizeof(*params);
-
-        hcc.params[1].conn_itvl_min = le16toh(params->conn_min_itvl);
-        hcc.params[1].conn_itvl_max = le16toh(params->conn_min_itvl);
-        hcc.params[1].conn_latency = le16toh(params->conn_latency);
-        hcc.params[1].supervision_timeout = le16toh(params->supervision_timeout);
-
-        rc = ble_ll_conn_hci_chk_conn_params(hcc.params[1].conn_itvl_min,
-                                             hcc.params[1].conn_itvl_max,
-                                             hcc.params[1].conn_latency,
-                                             hcc.params[1].supervision_timeout);
-        if (rc) {
-            return rc;
-        }
-
-        /* Min/max connection event lengths */
-        hcc.params[1].min_ce_len = le16toh(params->min_ce);
-        hcc.params[1].max_ce_len = le16toh(params->max_ce);
-        if (hcc.params[1].min_ce_len > hcc.params[1].max_ce_len) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
+    cc_scan.own_addr_type = cmd->own_addr_type;
+    cc_scan.filter_policy = cmd->filter_policy;
+    if (cc_scan.filter_policy == 0) {
+        cc_scan.peer_addr_type = cmd->peer_addr_type;
+        memcpy(cc_scan.peer_addr, cmd->peer_addr, BLE_DEV_ADDR_LEN);
+    } else {
+        cc_scan.peer_addr_type = 0;
+        memset(cc_scan.peer_addr, 0, BLE_DEV_ADDR_LEN);
+    }
+    cc_scan.init_phy_mask = cmd->init_phy_mask;
 
-        params++;
+    rc = ble_ll_conn_hci_create_check_scan(&cc_scan);
+    if (rc) {
+        return rc;
     }
-#endif
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-    if (hcc.init_phy_mask & BLE_PHY_MASK_CODED) {
-        if (len < sizeof(*params)) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
-        len -= sizeof(*params);
+    cc_params_fb = NULL;
 
-        hcc.params[2].scan_itvl = le16toh(params->scan_itvl);
-        hcc.params[2].scan_window = le16toh(params->scan_window);
+    for (int i = 0; i < ARRAY_SIZE(init_phys); i++) {
+        init_phy = &init_phys[i];
 
-        rc = ble_ll_conn_hci_chk_scan_params(hcc.params[2].scan_itvl,
-                                             hcc.params[2].scan_window);
-        if (rc) {
-            return rc;
+        if ((cc_scan.init_phy_mask & init_phy->mask) == 0) {
+            continue;
         }
 
-        hcc.params[2].conn_itvl_min = le16toh(params->conn_min_itvl);
-        hcc.params[2].conn_itvl_max = le16toh(params->conn_min_itvl);
-        hcc.params[2].conn_latency = le16toh(params->conn_latency);
-        hcc.params[2].supervision_timeout = le16toh(params->supervision_timeout);
+        cc_params = &g_ble_ll_conn_create_sm.params[init_phy->idx];
 
-        rc = ble_ll_conn_hci_chk_conn_params(hcc.params[2].conn_itvl_min,
-                                             hcc.params[2].conn_itvl_max,
-                                             hcc.params[2].conn_latency,
-                                             hcc.params[2].supervision_timeout);
+        rc = ble_ll_conn_hci_ext_create_parse_params(params, init_phy->phy,
+                                                     &cc_scan, cc_params);
         if (rc) {
             return rc;
         }
 
-        /* Min/max connection event lengths */
-        hcc.params[2].min_ce_len = le16toh(params->min_ce);
-        hcc.params[2].max_ce_len = le16toh(params->max_ce);
-        if (hcc.params[2].min_ce_len > hcc.params[2].max_ce_len) {
-            return BLE_ERR_INV_HCI_CMD_PARMS;
-        }
-
-        if (!fallback_params) {
-            fallback_params = &hcc.params[2];
+        if (!cc_params_fb) {
+            cc_params_fb = cc_params;
         }
         params++;
     }
-#endif
+
+    ble_ll_conn_hci_ext_create_set_fb_params(cc_scan.init_phy_mask,
+                                             cc_params_fb);
 
     /* Make sure we can allocate an event to send the connection complete */
     if (ble_ll_init_alloc_conn_comp_ev()) {
@@ -738,22 +770,16 @@ ble_ll_ext_conn_create(const uint8_t *cmdbuf, uint8_t len)
         return BLE_ERR_CONN_LIMIT;
     }
 
-    ble_ll_conn_hcc_params_set_fallback(&hcc, fallback_params);
-
     /* Initialize state machine in master role and start state machine */
-    ble_ll_conn_ext_master_init(connsm, &hcc);
+    ble_ll_conn_master_init(connsm, &cc_scan,
+                            &g_ble_ll_conn_create_sm.params[0]);
     ble_ll_conn_sm_new(connsm);
 
-    /* CSA will be selected when advertising is received */
-
     /* Start scanning */
-    rc = ble_ll_scan_ext_initiator_start(&hcc, &connsm->scansm);
+    rc = ble_ll_scan_initiator_start(connsm, 1, &cc_scan);
     if (rc) {
         SLIST_REMOVE(&g_ble_ll_conn_active_list,connsm,ble_ll_conn_sm,act_sle);
         STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe);
-    } else {
-        /* Set the connection state machine we are trying to create. */
-        g_ble_ll_conn_create_sm = connsm;
     }
 
     return rc;
@@ -827,10 +853,12 @@ ble_ll_conn_hci_read_rem_features(const uint8_t *cmdbuf, uint8_t len)
      */
     if (!connsm->csmflags.cfbit.rxd_features &&
                 !IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG)) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
         if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) &&
             !(ble_ll_read_supp_features() & BLE_LL_FEAT_SLAVE_INIT)) {
                 return BLE_ERR_CMD_DISALLOWED;
         }
+#endif
 
         ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG);
     }
@@ -881,9 +909,11 @@ ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len)
 
     /* See if this feature is supported on both sides */
     if ((connsm->conn_features & BLE_LL_FEAT_CONN_PARM_REQ) == 0) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
         if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
             return BLE_ERR_UNSUPP_REM_FEATURE;
         }
+#endif
         ctrl_proc = BLE_LL_CTRL_PROC_CONN_UPDATE;
     } else {
         ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ;
@@ -896,14 +926,24 @@ ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len)
      * slave.
      */
     if (connsm->csmflags.cfbit.awaiting_host_reply) {
-        if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
-            return BLE_ERR_LMP_COLLISION;
-        } else {
+        switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_LL_CONN_ROLE_MASTER:
             connsm->csmflags.cfbit.awaiting_host_reply = 0;
 
             /* XXX: If this fails no reject ind will be sent! */
             ble_ll_ctrl_reject_ind_send(connsm, connsm->host_reply_opcode,
                                         BLE_ERR_LMP_COLLISION);
+            break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        case BLE_LL_CONN_ROLE_SLAVE:
+            return BLE_ERR_LMP_COLLISION;
+        break;
+#endif
+        default:
+            BLE_LL_ASSERT(0);
+            break;
         }
     }
 
@@ -911,11 +951,13 @@ ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len)
      * If we are a slave and the master has initiated the channel map
      * update procedure we should deny the slave request for now.
      */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (connsm->csmflags.cfbit.chanmap_update_scheduled) {
         if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
             return BLE_ERR_DIFF_TRANS_COLL;
         }
     }
+#endif
 
     /* Retrieve command data */
     hcu = &connsm->conn_param_req;
@@ -1099,10 +1141,10 @@ ble_ll_conn_create_cancel(ble_ll_hci_post_cmd_complete_cb *post_cmd_cb)
      * return disallowed as well
      */
     OS_ENTER_CRITICAL(sr);
-    connsm = g_ble_ll_conn_create_sm;
+    connsm = g_ble_ll_conn_create_sm.connsm;
     if (connsm && (connsm->conn_state == BLE_LL_CONN_STATE_IDLE)) {
         /* stop scanning and end the connection event */
-        g_ble_ll_conn_create_sm = NULL;
+        g_ble_ll_conn_create_sm.connsm = NULL;
         ble_ll_scan_sm_stop(1);
         ble_ll_conn_end(connsm, BLE_ERR_UNK_CONN_ID);
 
@@ -1302,6 +1344,7 @@ ble_ll_conn_hci_rd_chan_map(const uint8_t *cmdbuf, uint8_t len,
     *rsplen = sizeof(*rsp);
     return rc;
 }
+#endif
 
 /**
  * Called when the host issues the LE command "set host channel classification"
@@ -1335,6 +1378,8 @@ ble_ll_conn_hci_set_chan_class(const uint8_t *cmdbuf, uint8_t len)
     return BLE_ERR_SUCCESS;
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
 int
 ble_ll_conn_hci_set_data_len(const uint8_t *cmdbuf, uint8_t len,
@@ -1422,8 +1467,10 @@ ble_ll_conn_hci_le_start_encrypt(const uint8_t *cmdbuf, uint8_t len)
     connsm = ble_ll_conn_find_active_conn(le16toh(cmd->conn_handle));
     if (!connsm) {
         rc = BLE_ERR_UNK_CONN_ID;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     } else if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         rc = BLE_ERR_UNSPECIFIED;
+#endif
     } else if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_ENCRYPT) {
         /*
          * The specification does not say what to do here but the host should
@@ -1476,11 +1523,13 @@ ble_ll_conn_hci_le_ltk_reply(const uint8_t *cmdbuf, uint8_t len,
         goto ltk_key_cmd_complete;
     }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     /* Should never get this if we are a master! */
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         rc = BLE_ERR_UNSPECIFIED;
         goto ltk_key_cmd_complete;
     }
+#endif
 
     /* The connection should be awaiting a reply. If not, just discard */
     if (connsm->enc_data.enc_state != CONN_ENC_S_LTK_REQ_WAIT) {
@@ -1533,11 +1582,13 @@ ble_ll_conn_hci_le_ltk_neg_reply(const uint8_t *cmdbuf, uint8_t len,
         goto ltk_key_cmd_complete;
     }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     /* Should never get this if we are a master! */
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         rc = BLE_ERR_UNSPECIFIED;
         goto ltk_key_cmd_complete;
     }
+#endif
 
     /* The connection should be awaiting a reply. If not, just discard */
     if (connsm->enc_data.enc_state != CONN_ENC_S_LTK_REQ_WAIT) {
@@ -1917,3 +1968,4 @@ ble_ll_set_default_sync_transfer_params(const uint8_t *cmdbuf, uint8_t len)
     return BLE_ERR_SUCCESS;
 }
 #endif
+#endif
diff --git a/nimble/controller/src/ble_ll_conn_priv.h b/nimble/controller/src/ble_ll_conn_priv.h
index 53358c4a0d..73e4106074 100644
--- a/nimble/controller/src/ble_ll_conn_priv.h
+++ b/nimble/controller/src/ble_ll_conn_priv.h
@@ -64,6 +64,7 @@ struct ble_ll_conn_global_params
 {
     uint8_t master_chan_map[BLE_LL_CONN_CHMAP_LEN];
     uint8_t num_used_chans;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     uint8_t supp_max_tx_octets;
     uint8_t supp_max_rx_octets;
     uint8_t conn_init_max_tx_octets;
@@ -74,6 +75,7 @@ struct ble_ll_conn_global_params
     uint16_t conn_init_max_tx_time_coded;
     uint16_t supp_max_tx_time;
     uint16_t supp_max_rx_time;
+#endif
 };
 extern struct ble_ll_conn_global_params g_ble_ll_conn_params;
 
@@ -93,8 +95,38 @@ STAILQ_HEAD(ble_ll_conn_free_list, ble_ll_conn_sm);
 extern struct ble_ll_conn_active_list g_ble_ll_conn_active_list;
 extern struct ble_ll_conn_free_list g_ble_ll_conn_free_list;
 
-/* Pointer to connection state machine we are trying to create */
-extern struct ble_ll_conn_sm *g_ble_ll_conn_create_sm;
+struct ble_ll_conn_create_scan {
+    uint8_t filter_policy;
+    uint8_t own_addr_type;
+    uint8_t peer_addr_type;
+    uint8_t peer_addr[BLE_DEV_ADDR_LEN];
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    uint8_t init_phy_mask;
+#endif
+    struct {
+        uint16_t itvl;
+        uint16_t window;
+    } scan_params[2];
+};
+
+struct ble_ll_conn_create_params {
+    uint32_t conn_itvl;
+    uint32_t conn_itvl_ticks;
+    uint8_t conn_itvl_usecs;
+    uint16_t conn_latency;
+    uint16_t supervision_timeout;
+    uint16_t min_ce_len;
+    uint16_t max_ce_len;
+};
+
+struct ble_ll_conn_create_sm {
+    struct ble_ll_conn_sm *connsm;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    struct ble_ll_conn_create_params params[3];
+#endif
+};
+
+extern struct ble_ll_conn_create_sm g_ble_ll_conn_create_sm;
 
 /* Generic interface */
 struct ble_ll_len_req;
@@ -120,18 +152,11 @@ struct hci_create_conn
 void ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm);
 void ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err);
 void ble_ll_conn_enqueue_pkt(struct ble_ll_conn_sm *connsm, struct os_mbuf *om,
-                             uint8_t hdr_byte, uint8_t length);
+                             uint8_t hdr_byte, uint16_t length);
 struct ble_ll_conn_sm *ble_ll_conn_sm_get(void);
 void ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm,
-                             struct hci_create_conn *hcc);
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-void ble_ll_conn_ext_master_init(struct ble_ll_conn_sm *connsm,
-                                 struct hci_ext_create_conn *hcc);
-
-void ble_ll_conn_ext_set_params(struct ble_ll_conn_sm *connsm,
-                                struct hci_ext_conn_params *hcc_params,
-                                int phy);
-#endif
+                             struct ble_ll_conn_create_scan *cc_scan,
+                             struct ble_ll_conn_create_params *cc_params);
 
 struct ble_ll_conn_sm *ble_ll_conn_find_active_conn(uint16_t handle);
 void ble_ll_conn_update_eff_data_len(struct ble_ll_conn_sm *connsm);
@@ -148,25 +173,17 @@ void ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t len);
 int ble_ll_conn_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa);
 int ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr);
 void ble_ll_conn_rx_data_pdu(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *hdr);
-void ble_ll_init_rx_pkt_in(uint8_t pdu_type, uint8_t *rxbuf,
-                           struct ble_mbuf_hdr *ble_hdr);
-int ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr);
-int ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok,
-                           struct ble_mbuf_hdr *ble_hdr);
 void ble_ll_conn_wfr_timer_exp(void);
-void ble_ll_conn_init_wfr_timer_exp(void);
 int ble_ll_conn_is_lru(struct ble_ll_conn_sm *s1, struct ble_ll_conn_sm *s2);
 uint32_t ble_ll_conn_get_ce_end_time(void);
 void ble_ll_conn_event_halt(void);
-void ble_ll_conn_reset_pending_aux_conn_rsp(void);
-bool ble_ll_conn_init_pending_aux_conn_rsp(void);
 /* HCI */
 void ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm,
                                     uint8_t reason);
 void ble_ll_auth_pyld_tmo_event_send(struct ble_ll_conn_sm *connsm);
 int ble_ll_conn_hci_disconnect_cmd(const struct ble_hci_lc_disconnect_cp *cmd);
 int ble_ll_conn_hci_rd_rem_ver_cmd(const uint8_t *cmdbuf, uint8_t len);
-int ble_ll_conn_create(const uint8_t *cmdbuf, uint8_t len);
+int ble_ll_conn_hci_create(const uint8_t *cmdbuf, uint8_t len);
 int ble_ll_conn_hci_update(const uint8_t *cmdbuf, uint8_t len);
 int ble_ll_conn_hci_set_chan_class(const uint8_t *cmdbuf, uint8_t len);
 int ble_ll_conn_hci_param_rr(const uint8_t *cmdbuf, uint8_t len,
@@ -213,6 +230,9 @@ bool ble_ll_conn_cth_flow_enable(bool enabled);
 void ble_ll_conn_cth_flow_process_cmd(const uint8_t *cmdbuf);
 #endif
 
+void ble_ll_conn_itvl_to_ticks(uint32_t itvl,
+                               uint32_t *itvl_ticks, uint8_t *itvl_usecs);
+
 int ble_ll_hci_cmd_rx(uint8_t *cmd, void *arg);
 int ble_ll_hci_acl_rx(struct os_mbuf *om, void *arg);
 
@@ -221,7 +241,7 @@ int ble_ll_conn_hci_le_rd_phy(const uint8_t *cmdbuf, uint8_t len,
 int ble_ll_conn_hci_le_set_phy(const uint8_t *cmdbuf, uint8_t len);
 int ble_ll_conn_chk_phy_upd_start(struct ble_ll_conn_sm *connsm);
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-int ble_ll_ext_conn_create(const uint8_t *cmdbuf, uint8_t cmdlen);
+int ble_ll_conn_hci_ext_create(const uint8_t *cmdbuf, uint8_t len);
 #endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
diff --git a/nimble/controller/src/ble_ll_ctrl.c b/nimble/controller/src/ble_ll_ctrl.c
index c4ac6504c9..432045e675 100644
--- a/nimble/controller/src/ble_ll_ctrl.c
+++ b/nimble/controller/src/ble_ll_ctrl.c
@@ -31,6 +31,8 @@
 #include "controller/ble_ll_sync.h"
 #include "ble_ll_conn_priv.h"
 
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+
 /* To use spec sample data for testing */
 #undef BLE_LL_ENCRYPT_USE_TEST_DATA
 
@@ -314,8 +316,7 @@ ble_ll_ctrl_conn_param_pdu_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
      * update the connection parameters. This means that the previous
      * check is all we need for a master (when receiving a request).
      */
-    if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) ||
-        (opcode == BLE_LL_CTRL_CONN_PARM_RSP)) {
+    if (CONN_IS_SLAVE(connsm) || (opcode == BLE_LL_CTRL_CONN_PARM_RSP)) {
         /*
          * Not sure what to do about the slave. It is possible that the
          * current connection parameters are not the same ones as the local host
@@ -488,11 +489,13 @@ ble_ll_ctrl_proc_unk_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr, uint8_t *
         break;
     case BLE_LL_CTRL_CONN_PARM_REQ:
         BLE_LL_CONN_CLEAR_FEATURE(connsm, BLE_LL_FEAT_CONN_PARM_REQ);
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
             ble_ll_ctrl_conn_upd_make(connsm, rspdata, NULL);
             connsm->reject_reason = BLE_ERR_SUCCESS;
             return BLE_LL_CTRL_CONN_UPDATE_IND;
         }
+#endif
         /* note: fall-through intentional */
     case BLE_LL_CTRL_CONN_PARM_RSP:
         ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ;
@@ -559,11 +562,6 @@ ble_ll_ctrl_proc_rsp_timer_cb(struct ble_npl_event *ev)
 static void
 ble_ll_ctrl_start_rsp_timer(struct ble_ll_conn_sm *connsm)
 {
-    ble_npl_callout_init(&connsm->ctrl_proc_rsp_timer,
-                    &g_ble_ll_data.ll_evq,
-                    ble_ll_ctrl_proc_rsp_timer_cb,
-                    connsm);
-
     /* Re-start timer. Control procedure timeout is 40 seconds */
     ble_npl_callout_reset(&connsm->ctrl_proc_rsp_timer,
                      ble_npl_time_ms_to_ticks32(BLE_LL_CTRL_PROC_TIMEOUT_MS));
@@ -679,6 +677,7 @@ ble_ll_ctrl_phy_update_proc_complete(struct ble_ll_conn_sm *connsm)
     }
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 /**
  *
  *  There is probably a better way for the controller to choose which PHY use.
@@ -718,6 +717,7 @@ ble_ll_ctrl_find_new_phy(uint8_t phy_mask_prefs)
  * @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)
@@ -734,10 +734,12 @@ ble_ll_ctrl_phy_update_ind_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
     rx_phys = dptr[1];
 
     /* If we are master, check if slave requested symmetric PHY */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         is_slave_sym = tx_phys == rx_phys;
         is_slave_sym &= __builtin_popcount(tx_phys) == 1;
     }
+#endif
 
     /* Get m_to_s and s_to_m masks */
     if (slave_req) {
@@ -823,6 +825,7 @@ ble_ll_ctrl_phy_update_ind_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
     ctrdata[1] = s_to_m;
     put_le16(ctrdata + 2, instant);
 }
+#endif
 
 /**
  * Create a LL_PHY_REQ or LL_PHY_RSP pdu
@@ -877,7 +880,9 @@ ble_ll_ctrl_rx_phy_req(struct ble_ll_conn_sm *connsm, uint8_t *req,
     err = ble_ll_ctrl_proc_with_instant_initiated(connsm,
                                                   BLE_LL_CTRL_PROC_PHY_UPDATE);
 
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case 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;
@@ -890,7 +895,10 @@ ble_ll_ctrl_rx_phy_req(struct ble_ll_conn_sm *connsm, uint8_t *req,
             ble_ll_ctrl_phy_update_ind_make(connsm, req, rsp, 1);
             rsp_opcode = BLE_LL_CTRL_PHY_UPDATE_IND;
         }
-    } else {
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
         /* XXX: deal with other control procedures that we need to stop */
         if (err) {
             if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) {
@@ -918,7 +926,13 @@ ble_ll_ctrl_rx_phy_req(struct ble_ll_conn_sm *connsm, uint8_t *req,
         /* Start response timer */
         connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_PHY_UPDATE;
         ble_ll_ctrl_start_rsp_timer(connsm);
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
+
     return rsp_opcode;
 }
 
@@ -938,7 +952,10 @@ ble_ll_ctrl_rx_phy_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
     uint8_t rsp_opcode;
 
     rsp_opcode = BLE_ERR_MAX;
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_PHY_UPDATE) {
             ble_ll_ctrl_phy_update_ind_make(connsm, dptr, rsp, 0);
             ble_npl_callout_stop(&connsm->ctrl_proc_rsp_timer);
@@ -951,8 +968,16 @@ ble_ll_ctrl_rx_phy_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
          *
          * XXX: TODO count some stat?
          */
-    } else {
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
         rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP;
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
     /* NOTE: slave should never receive one of these */
@@ -981,9 +1006,11 @@ ble_ll_ctrl_rx_phy_update_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr)
     uint16_t instant;
     uint16_t delta;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         return BLE_LL_CTRL_UNKNOWN_RSP;
     }
+#endif
 
     /*
      * Reception stops the procedure response timer but does not
@@ -1439,9 +1466,11 @@ static uint8_t
 ble_ll_ctrl_rx_enc_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
                        uint8_t *rspdata)
 {
-    if (connsm->conn_role != BLE_LL_CONN_ROLE_SLAVE) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         return BLE_LL_CTRL_UNKNOWN_RSP;
     }
+#endif
 
     connsm->enc_data.enc_state = CONN_ENC_S_ENC_RSP_TO_BE_SENT;
 
@@ -1484,15 +1513,27 @@ ble_ll_ctrl_rx_start_enc_req(struct ble_ll_conn_sm *connsm)
 
     /* Only master should receive start enc request */
     rc = BLE_ERR_MAX;
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         /* We only want to send a START_ENC_RSP if we havent yet */
         if (connsm->enc_data.enc_state == CONN_ENC_S_START_ENC_REQ_WAIT) {
             connsm->enc_data.enc_state = CONN_ENC_S_START_ENC_RSP_WAIT;
             rc = BLE_LL_CTRL_START_ENC_RSP;
         }
-    } else {
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
         rc = BLE_LL_CTRL_UNKNOWN_RSP;
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
+
     return rc;
 }
 
@@ -1507,7 +1548,7 @@ ble_ll_ctrl_rx_pause_enc_req(struct ble_ll_conn_sm *connsm)
      * ignore it...
      */
     rc = BLE_ERR_MAX;
-    if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) &&
+    if (CONN_IS_SLAVE(connsm) &&
         (connsm->enc_data.enc_state == CONN_ENC_S_ENCRYPTED)) {
         rc = BLE_LL_CTRL_PAUSE_ENC_RSP;
     } else {
@@ -1530,16 +1571,28 @@ ble_ll_ctrl_rx_pause_enc_rsp(struct ble_ll_conn_sm *connsm)
 {
     int rc;
 
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         rc = BLE_LL_CTRL_PAUSE_ENC_RSP;
-    } else if (connsm->enc_data.enc_state == CONN_ENC_S_PAUSE_ENC_RSP_WAIT) {
-        /* Master sends back unencrypted LL_PAUSE_ENC_RSP.
-         * From this moment encryption is paused.
-         */
-        rc = BLE_ERR_MAX;
-        connsm->enc_data.enc_state = CONN_ENC_S_PAUSED;
-    } else {
-        rc = BLE_LL_CTRL_UNKNOWN_RSP;
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
+        if (connsm->enc_data.enc_state == CONN_ENC_S_PAUSE_ENC_RSP_WAIT) {
+                /* Master sends back unencrypted LL_PAUSE_ENC_RSP.
+                 * From this moment encryption is paused.
+                 */
+                rc = BLE_ERR_MAX;
+                connsm->enc_data.enc_state = CONN_ENC_S_PAUSED;
+            } else {
+                rc = BLE_LL_CTRL_UNKNOWN_RSP;
+            }
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
     return rc;
@@ -1564,9 +1617,9 @@ ble_ll_ctrl_rx_start_enc_rsp(struct ble_ll_conn_sm *connsm)
         return BLE_ERR_MAX;
     }
 
-    /* If master, we are done. Stop control procedure and sent event to host */
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
-
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_ENCRYPT);
 
         /* We are encrypted */
@@ -1575,7 +1628,10 @@ ble_ll_ctrl_rx_start_enc_rsp(struct ble_ll_conn_sm *connsm)
         ble_ll_conn_auth_pyld_timer_start(connsm);
 #endif
         rc = BLE_ERR_MAX;
-    } else {
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
         /* Procedure has completed but slave needs to send START_ENC_RSP */
         rc = BLE_LL_CTRL_START_ENC_RSP;
 
@@ -1583,6 +1639,11 @@ ble_ll_ctrl_rx_start_enc_rsp(struct ble_ll_conn_sm *connsm)
         if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_ENCRYPT) {
             ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_ENCRYPT);
         }
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
     /*
@@ -1697,14 +1758,24 @@ ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, uint8_t *rsp,
 {
     uint8_t rsp_opcode;
 
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
-        /* Create a connection parameter response */
-        ble_ll_ctrl_conn_param_pdu_make(connsm, rsp + 1, req);
-        rsp_opcode = BLE_LL_CTRL_CONN_PARM_RSP;
-    } else {
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
         /* Create a connection update pdu */
         ble_ll_ctrl_conn_upd_make(connsm, rsp + 1, req);
         rsp_opcode = BLE_LL_CTRL_CONN_UPDATE_IND;
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
+        /* Create a connection parameter response */
+        ble_ll_ctrl_conn_param_pdu_make(connsm, rsp + 1, req);
+        rsp_opcode = BLE_LL_CTRL_CONN_PARM_RSP;
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
     return rsp_opcode;
@@ -1737,14 +1808,24 @@ ble_ll_ctrl_rx_reject_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
     switch (connsm->cur_ctrl_proc) {
     case BLE_LL_CTRL_PROC_CONN_PARAM_REQ:
         if (opcode == BLE_LL_CTRL_REJECT_IND_EXT) {
-            /* As a master we should send connection update indication in this point */
-            if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+            switch (connsm->conn_role) {
+        #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+            case BLE_LL_CONN_ROLE_MASTER:
+                /* As a master we should send connection update indication in this point */
                 rsp_opcode = BLE_LL_CTRL_CONN_UPDATE_IND;
                 ble_ll_ctrl_conn_upd_make(connsm, rspdata, NULL);
                 connsm->reject_reason = BLE_ERR_SUCCESS;
-            } else {
+                break;
+        #endif
+        #if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            case BLE_LL_CONN_ROLE_SLAVE:
                 ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ);
                 ble_ll_hci_ev_conn_update(connsm, ble_error);
+                break;
+        #endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
             }
         }
         break;
@@ -1797,9 +1878,11 @@ ble_ll_ctrl_rx_conn_update(struct ble_ll_conn_sm *connsm, uint8_t *dptr)
     struct ble_ll_conn_upd_req *reqdata;
 
     /* Only a slave should receive this */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         return BLE_LL_CTRL_UNKNOWN_RSP;
     }
+#endif
 
     /* Retrieve parameters */
     reqdata = &connsm->conn_update_req;
@@ -1918,12 +2001,12 @@ ble_ll_ctrl_rx_feature_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
      * requests if we are a slave.
      */
     if (opcode ==  BLE_LL_CTRL_SLAVE_FEATURE_REQ) {
-        if (connsm->conn_role != BLE_LL_CONN_ROLE_MASTER) {
+        if (!CONN_IS_MASTER(connsm)) {
             return BLE_LL_CTRL_UNKNOWN_RSP;
         }
     } else {
         /* XXX: not sure this is correct but do it anyway */
-        if (connsm->conn_role != BLE_LL_CONN_ROLE_SLAVE) {
+        if (!CONN_IS_SLAVE(connsm)) {
             return BLE_LL_CTRL_UNKNOWN_RSP;
         }
     }
@@ -2023,15 +2106,24 @@ ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
      * transaction collision error code.
      */
     if (IS_PENDING_CTRL_PROC(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) {
-        if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
-            ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ);
-            ble_ll_hci_ev_conn_update(connsm, BLE_ERR_LMP_COLLISION);
-        } else {
+        switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_LL_CONN_ROLE_MASTER:
             /* The master sends reject ind ext w/error code 0x23 */
             rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT;
             rspbuf[1] = BLE_LL_CTRL_CONN_PARM_REQ;
             rspbuf[2] = BLE_ERR_LMP_COLLISION;
             return rsp_opcode;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+        case BLE_LL_CONN_ROLE_SLAVE:
+            ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ);
+            ble_ll_hci_ev_conn_update(connsm, BLE_ERR_LMP_COLLISION);
+            break;
+#endif
+        default:
+            BLE_LL_ASSERT(0);
+            break;
         }
     }
 
@@ -2039,6 +2131,7 @@ ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
      * If we are a master and we currently performing a channel map
      * update procedure we need to return an error
      */
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) &&
         (connsm->csmflags.cfbit.chanmap_update_scheduled)) {
         rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT;
@@ -2046,6 +2139,7 @@ ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
         rspbuf[2] = BLE_ERR_DIFF_TRANS_COLL;
         return rsp_opcode;
     }
+#endif
 
     /* Process the received connection parameter request */
     rsp_opcode = ble_ll_ctrl_conn_param_pdu_proc(connsm, dptr, rspbuf,
@@ -2060,9 +2154,11 @@ ble_ll_ctrl_rx_conn_param_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr,
     uint8_t rsp_opcode;
 
     /* A slave should never receive this response */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
         return BLE_LL_CTRL_UNKNOWN_RSP;
     }
+#endif
 
     /*
      * This case should never happen! It means that the slave initiated a
@@ -2136,9 +2232,11 @@ ble_ll_ctrl_rx_chanmap_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr)
     uint16_t instant;
     uint16_t conn_events;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
         return BLE_LL_CTRL_UNKNOWN_RSP;
     }
+#endif
 
     /* If instant is in the past, we have to end the connection */
     instant = get_le16(dptr + BLE_LL_CONN_CHMAP_LEN);
@@ -2193,10 +2291,20 @@ ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc)
             ble_ll_ctrl_chanmap_req_make(connsm, ctrdata);
             break;
         case BLE_LL_CTRL_PROC_FEATURE_XCHG:
-            if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) {
+            switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+            case BLE_LL_CONN_ROLE_MASTER:
                 opcode = BLE_LL_CTRL_FEATURE_REQ;
-            } else {
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            case BLE_LL_CONN_ROLE_SLAVE:
                 opcode = BLE_LL_CTRL_SLAVE_FEATURE_REQ;
+                break;
+#endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
             }
             put_le64(ctrdata, ble_ll_read_supp_features());
             break;
@@ -2473,6 +2581,12 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om)
     len = dptr[1];
     opcode = dptr[2];
 
+#if MYNEWT_VAL(BLE_LL_HCI_LLCP_TRACE)
+    ble_ll_hci_ev_send_vs_llcp_trace(0x03, connsm->conn_handle,
+                                     connsm->event_cntr,
+                                     &dptr[2], len);
+#endif
+
     /*
      * rspbuf points to first byte of response. The response buffer does not
      * contain the Data Channel PDU. Thus, the first byte of rspbuf is the
@@ -2802,6 +2916,12 @@ ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm)
     int rc;
     uint8_t opcode;
 
+#if MYNEWT_VAL(BLE_LL_HCI_LLCP_TRACE)
+    ble_ll_hci_ev_send_vs_llcp_trace(0x04, connsm->conn_handle,
+                                     connsm->event_cntr,
+                                     txpdu->om_data, txpdu->om_len);
+#endif
+
     rc = 0;
     opcode = txpdu->om_data[0];
     switch (opcode) {
@@ -2842,6 +2962,7 @@ ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm)
         connsm->enc_data.enc_state = CONN_ENC_S_LTK_REQ_WAIT;
         connsm->csmflags.cfbit.send_ltk_req = 1;
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_LL_CTRL_START_ENC_RSP:
         if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
             connsm->enc_data.enc_state = CONN_ENC_S_ENCRYPTED;
@@ -2856,11 +2977,17 @@ ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm)
         }
         break;
 #endif
+#endif
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_LL_CTRL_PHY_REQ:
-        connsm->phy_tx_transition =
-                    ble_ll_ctrl_phy_tx_transition_get(connsm->phy_data.req_pref_tx_phys_mask);
+        if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+            connsm->phy_tx_transition =
+                    ble_ll_ctrl_phy_tx_transition_get(
+                            connsm->phy_data.req_pref_tx_phys_mask);
+        }
         break;
+#endif
     case BLE_LL_CTRL_PHY_UPDATE_IND:
         connsm->phy_tx_transition =
                     ble_ll_ctrl_phy_tx_transition_get(txpdu->om_data[2]);
@@ -2873,3 +3000,11 @@ ble_ll_ctrl_tx_done(struct os_mbuf *txpdu, struct ble_ll_conn_sm *connsm)
     os_mbuf_free_chain(txpdu);
     return rc;
 }
+
+void
+ble_ll_ctrl_init_conn_sm(struct ble_ll_conn_sm *connsm)
+{
+    ble_npl_callout_init(&connsm->ctrl_proc_rsp_timer, &g_ble_ll_data.ll_evq,
+                         ble_ll_ctrl_proc_rsp_timer_cb, connsm);
+}
+#endif
diff --git a/porting/targets/dummy_bsp/include/bsp/bsp.h b/nimble/controller/src/ble_ll_ctrl_priv.h
similarity index 82%
rename from porting/targets/dummy_bsp/include/bsp/bsp.h
rename to nimble/controller/src/ble_ll_ctrl_priv.h
index 69e8b1cfbb..ca1c83d390 100644
--- a/porting/targets/dummy_bsp/include/bsp/bsp.h
+++ b/nimble/controller/src/ble_ll_ctrl_priv.h
@@ -17,15 +17,19 @@
  * under the License.
  */
 
-#ifndef H_BSP_H
-#define H_BSP_H
+#ifndef H_BLE_LL_CTRL_PRIV_
+#define H_BLE_LL_CTRL_PRIV_
+
+#include "controller/ble_ll_conn.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+void ble_ll_ctrl_init_conn_sm(struct ble_ll_conn_sm *connsm);
+
 #ifdef __cplusplus
 }
 #endif
 
-#endif  /* H_BSP_H */
+#endif /* H_BLE_LL_CTRL_PRIV_ */
diff --git a/nimble/controller/src/ble_ll_dtm.c b/nimble/controller/src/ble_ll_dtm.c
index de3b168b22..8c9167799a 100644
--- a/nimble/controller/src/ble_ll_dtm.c
+++ b/nimble/controller/src/ble_ll_dtm.c
@@ -261,7 +261,7 @@ ble_ll_dtm_tx_sched_cb(struct ble_ll_sched_item *sch)
 
     ble_ll_state_set(BLE_LL_STATE_DTM);
 
-    return BLE_LL_SCHED_STATE_DONE;
+    return BLE_LL_SCHED_STATE_RUNNING;
 
 resched:
     /* Reschedule from LL task if late for this PDU */
@@ -427,9 +427,10 @@ ble_ll_dtm_rx_sched_cb(struct ble_ll_sched_item *sch)
     if (ble_ll_dtm_rx_start() != 0) {
         ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_dtm_ctx.evt);
         STATS_INC(ble_ll_dtm_stats, rx_failed);
+        return BLE_LL_SCHED_STATE_DONE;
     }
 
-    return BLE_LL_SCHED_STATE_DONE;
+    return BLE_LL_SCHED_STATE_RUNNING;
 }
 
 static int
diff --git a/nimble/controller/src/ble_ll_hci.c b/nimble/controller/src/ble_ll_hci.c
index 7cd3621c6d..d368672914 100644
--- a/nimble/controller/src/ble_ll_hci.c
+++ b/nimble/controller/src/ble_ll_hci.c
@@ -36,6 +36,7 @@
 #include "controller/ble_ll_iso.h"
 #include "ble_ll_priv.h"
 #include "ble_ll_conn_priv.h"
+#include "ble_ll_hci_priv.h"
 
 #if MYNEWT_VAL(BLE_LL_DTM)
 #include "ble_ll_dtm_priv.h"
@@ -321,8 +322,14 @@ ble_ll_hci_le_read_bufsize(uint8_t *rspbuf, uint8_t *rsplen)
 {
     struct ble_hci_le_rd_buf_size_rp *rp = (void *) rspbuf;
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     rp->data_len = htole16(g_ble_ll_data.ll_acl_pkt_size);
     rp->data_packets = g_ble_ll_data.ll_num_acl_pkts;
+#else
+    /* TODO check if can just not support this command */
+    rp->data_len = 0;
+    rp->data_packets = 0;
+#endif
 
     *rsplen = sizeof(*rp);
     return BLE_ERR_SUCCESS;
@@ -409,6 +416,7 @@ ble_ll_hci_chk_phy_masks(uint8_t all_phys, uint8_t tx_phys, uint8_t rx_phys,
  *
  * @return int
  */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 static int
 ble_ll_hci_le_set_def_phy(const uint8_t *cmdbuf, uint8_t len)
 {
@@ -425,6 +433,7 @@ ble_ll_hci_le_set_def_phy(const uint8_t *cmdbuf, uint8_t len)
     return rc;
 }
 #endif
+#endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
 /**
@@ -657,6 +666,7 @@ ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf)
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 /** HCI LE read maximum advertising data length command. Returns the controllers
 * max supported advertising data length;
 *
@@ -694,6 +704,7 @@ ble_ll_adv_rd_sup_adv_sets(uint8_t *rspbuf, uint8_t *rsplen)
     *rsplen = sizeof(*rsp);
     return BLE_ERR_SUCCESS;
 }
+#endif
 
 static bool
 ble_ll_is_valid_adv_mode(uint8_t ocf)
@@ -785,7 +796,7 @@ ble_ll_read_rf_path_compensation(uint8_t *rspbuf, uint8_t *rsplen)
     rsp->rx_path_compensation = htole16(rx_path_pwr_compensation);
     rsp->tx_path_compensation = htole16(tx_path_pwr_compensation);
 
-    *rsplen = sizeof(*rsp);;
+    *rsplen = sizeof(*rsp);
     return BLE_ERR_SUCCESS;
 }
 
@@ -886,6 +897,7 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         rc = ble_ll_set_random_addr(cmdbuf, len, false);
 #endif
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     case BLE_HCI_OCF_LE_SET_ADV_PARAMS:
         rc = ble_ll_adv_set_adv_params(cmdbuf, len);
         break;
@@ -903,20 +915,25 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
     case BLE_HCI_OCF_LE_SET_ADV_ENABLE:
         rc = ble_ll_hci_adv_set_enable(cmdbuf, len);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     case BLE_HCI_OCF_LE_SET_SCAN_PARAMS:
-        rc = ble_ll_scan_set_scan_params(cmdbuf, len);
+        rc = ble_ll_scan_hci_set_params(cmdbuf, len);
         break;
     case BLE_HCI_OCF_LE_SET_SCAN_ENABLE:
-        rc = ble_ll_hci_scan_set_enable(cmdbuf, len);
+        rc = ble_ll_scan_hci_set_enable(cmdbuf, len);
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_CREATE_CONN:
-        rc = ble_ll_conn_create(cmdbuf, len);
+        rc = ble_ll_conn_hci_create(cmdbuf, len);
         break;
     case BLE_HCI_OCF_LE_CREATE_CONN_CANCEL:
         if (len == 0) {
             rc = ble_ll_conn_create_cancel(cb);
         }
         break;
+#endif
+#endif
     case BLE_HCI_OCF_LE_RD_WHITE_LIST_SIZE:
         if (len == 0) {
             rc = ble_ll_whitelist_read_size(rspbuf, rsplen);
@@ -933,18 +950,22 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
     case BLE_HCI_OCF_LE_RMV_WHITE_LIST:
         rc = ble_ll_whitelist_rmv(cmdbuf, len);
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_CONN_UPDATE:
         rc = ble_ll_conn_hci_update(cmdbuf, len);
         break;
+#endif
     case BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS:
         rc = ble_ll_conn_hci_set_chan_class(cmdbuf, len);
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_RD_CHAN_MAP:
         rc = ble_ll_conn_hci_rd_chan_map(cmdbuf, len, rspbuf, rsplen);
         break;
     case BLE_HCI_OCF_LE_RD_REM_FEAT:
         rc = ble_ll_conn_hci_read_rem_features(cmdbuf, len);
         break;
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
     case BLE_HCI_OCF_LE_ENCRYPT:
         rc = ble_ll_hci_le_encrypt(cmdbuf, len, rspbuf, rsplen);
@@ -956,15 +977,19 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         }
         break;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_START_ENCRYPT:
         rc = ble_ll_conn_hci_le_start_encrypt(cmdbuf, len);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY:
         rc = ble_ll_conn_hci_le_ltk_reply(cmdbuf, len, rspbuf, rsplen);
         break;
     case BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY:
         rc = ble_ll_conn_hci_le_ltk_neg_reply(cmdbuf, len, rspbuf, rsplen);
         break;
+#endif
 #endif
     case BLE_HCI_OCF_LE_RD_SUPP_STATES :
         if (len == 0) {
@@ -984,12 +1009,14 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         }
         break;
 #endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_REM_CONN_PARAM_RR:
         rc = ble_ll_conn_hci_param_rr(cmdbuf, len, rspbuf, rsplen);
         break;
     case BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR:
         rc = ble_ll_conn_hci_param_nrr(cmdbuf, len, rspbuf, rsplen);
         break;
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
     case BLE_HCI_OCF_LE_SET_DATA_LEN:
         rc = ble_ll_conn_hci_set_data_len(cmdbuf, len, rspbuf, rsplen);
@@ -1041,6 +1068,7 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         break;
 #endif
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_RD_PHY:
         rc = ble_ll_conn_hci_le_rd_phy(cmdbuf, len, rspbuf, rsplen);
         break;
@@ -1051,6 +1079,7 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         rc = ble_ll_conn_hci_le_set_phy(cmdbuf, len);
         break;
 #endif
+#endif
 #if MYNEWT_VAL(BLE_LL_DTM)
     case BLE_HCI_OCF_LE_RX_TEST_V2:
         rc = ble_ll_hci_dtm_rx_test_v2(cmdbuf, len);
@@ -1059,6 +1088,7 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         rc = ble_ll_hci_dtm_tx_test_v2(cmdbuf, len);
         break;
 #endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     case BLE_HCI_OCF_LE_SET_ADV_SET_RND_ADDR:
         rc = ble_ll_adv_hci_set_random_addr(cmdbuf, len);
@@ -1105,17 +1135,21 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         break;
 #endif
 #endif
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     case BLE_HCI_OCF_LE_SET_EXT_SCAN_PARAM:
-        rc = ble_ll_set_ext_scan_params(cmdbuf, len);
+        rc = ble_ll_scan_hci_set_ext_params(cmdbuf, len);
         break;
     case BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE:
-        rc = ble_ll_hci_ext_scan_set_enable(cmdbuf, len);
+        rc = ble_ll_scan_hci_set_ext_enable(cmdbuf, len);
         break;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_LE_EXT_CREATE_CONN:
-        rc = ble_ll_ext_conn_create(cmdbuf, len);
+        rc = ble_ll_conn_hci_ext_create(cmdbuf, len);
         break;
 #endif
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
     case BLE_HCI_OCF_LE_PERIODIC_ADV_CREATE_SYNC:
         rc = ble_ll_sync_create(cmdbuf, len);
@@ -1149,12 +1183,17 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         rc = ble_ll_sync_receive_enable(cmdbuf, len);
         break;
 #endif
+#endif
 #endif
     case BLE_HCI_OCF_LE_RD_TRANSMIT_POWER:
-        rc = ble_ll_read_tx_power(rspbuf, rsplen);
+        if (len == 0) {
+            rc = ble_ll_read_tx_power(rspbuf, rsplen);
+        }
         break;
     case BLE_HCI_OCF_LE_RD_RF_PATH_COMPENSATION:
-        rc = ble_ll_read_rf_path_compensation(rspbuf, rsplen);
+        if (len == 0) {
+            rc = ble_ll_read_rf_path_compensation(rspbuf, rsplen);
+        }
         break;
     case BLE_HCI_OCF_LE_WR_RF_PATH_COMPENSATION:
         rc = ble_ll_write_rf_path_compensation(cmdbuf, len);
@@ -1165,12 +1204,16 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         break;
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER:
         rc = ble_ll_sync_transfer(cmdbuf, len, rspbuf, rsplen);
         break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     case BLE_HCI_OCF_LE_PERIODIC_ADV_SET_INFO_TRANSFER:
         rc = ble_ll_adv_periodic_set_info_transfer(cmdbuf, len, rspbuf, rsplen);
         break;
+#endif
     case BLE_HCI_OCF_LE_PERIODIC_ADV_SYNC_TRANSFER_PARAMS:
         rc = ble_ll_set_sync_transfer_params(cmdbuf, len, rspbuf, rsplen);
         break;
@@ -1216,7 +1259,9 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
         rc = ble_ll_iso_remove_iso_data_path(cmdbuf, len);
         break;
     case BLE_HCI_OCF_LE_RD_BUF_SIZE_V2:
-        rc = ble_ll_hci_le_read_bufsize_v2(rspbuf, rsplen);
+        if (len == 0) {
+            rc = ble_ll_hci_le_read_bufsize_v2(rspbuf, rsplen);
+        }
         break;
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ISO_TEST)
@@ -1259,17 +1304,15 @@ ble_ll_hci_le_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
      * This code is here because we add 256 to the return code to denote
      * that the reply to this command should be command status (as opposed to
      * command complete).
-     *
-     * For unknown HCI command let us return always command status as per
-     * specification Bluetooth 5, Vol. 2, Chapter 4.4
      */
-    if (ble_ll_hci_le_cmd_send_cmd_status(ocf) || rc == BLE_ERR_UNKNOWN_HCI_CMD) {
+    if (ble_ll_hci_le_cmd_send_cmd_status(ocf)) {
         rc += (BLE_ERR_MAX + 1);
     }
 
     return rc;
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 static int
 ble_ll_hci_disconnect(const uint8_t *cmdbuf, uint8_t len)
 {
@@ -1289,6 +1332,7 @@ ble_ll_hci_disconnect(const uint8_t *cmdbuf, uint8_t len)
 
     return ble_ll_conn_hci_disconnect_cmd(cmd);
 }
+#endif
 
 /**
  * Process a link control command sent from the host to the controller. The HCI
@@ -1309,18 +1353,18 @@ ble_ll_hci_link_ctrl_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf)
     int rc;
 
     switch (ocf) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_HCI_OCF_DISCONNECT_CMD:
         rc = ble_ll_hci_disconnect(cmdbuf, len);
         /* Send command status instead of command complete */
         rc += (BLE_ERR_MAX + 1);
         break;
-
     case BLE_HCI_OCF_RD_REM_VER_INFO:
         rc = ble_ll_conn_hci_rd_rem_ver_cmd(cmdbuf, len);
         /* Send command status instead of command complete */
         rc += (BLE_ERR_MAX + 1);
         break;
-
+#endif
     default:
         rc = BLE_ERR_UNKNOWN_HCI_CMD;
         break;
@@ -1514,9 +1558,11 @@ ble_ll_hci_status_params_cmd_proc(const uint8_t *cmdbuf, uint8_t len,
     int rc;
 
     switch (ocf) {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_HCI_OCF_RD_RSSI:
         rc = ble_ll_conn_hci_rd_rssi(cmdbuf, len, rspbuf, rsplen);
         break;
+#endif
     default:
         rc = BLE_ERR_UNKNOWN_HCI_CMD;
         break;
@@ -1525,47 +1571,6 @@ ble_ll_hci_status_params_cmd_proc(const uint8_t *cmdbuf, uint8_t len,
     return rc;
 }
 
-#if MYNEWT_VAL(BLE_HCI_VS)
-static int
-ble_ll_hci_vs_rd_static_addr(uint8_t *rspbuf, uint8_t *rsplen)
-{
-    struct ble_hci_vs_rd_static_addr_rp *rsp = (void *) rspbuf;
-    ble_addr_t addr;
-
-    if (ble_hw_get_static_addr(&addr) < 0) {
-        return BLE_ERR_UNSPECIFIED;
-    }
-
-    memcpy(rsp->addr, addr.val, sizeof(rsp->addr));
-
-    *rsplen = sizeof(*rsp);
-    return BLE_ERR_SUCCESS;
-}
-
-static int
-ble_ll_hci_vs_cmd_proc(const uint8_t *cmdbuf, uint8_t len, uint16_t ocf,
-                       uint8_t *rspbuf, uint8_t *rsplen)
-{
-    int rc;
-
-    /* Assume error; if all pass rc gets set to 0 */
-    rc = BLE_ERR_INV_HCI_CMD_PARMS;
-
-    switch (ocf) {
-    case BLE_HCI_OCF_VS_RD_STATIC_ADDR:
-        if (len == 0) {
-            rc = ble_ll_hci_vs_rd_static_addr(rspbuf, rsplen);
-        }
-        break;
-    default:
-        rc = BLE_ERR_UNKNOWN_HCI_CMD;
-        break;
-    }
-
-    return rc;
-}
-#endif
-
 /**
  * Called to process an HCI command from the host.
  *
@@ -1625,7 +1630,7 @@ ble_ll_hci_cmd_proc(struct ble_npl_event *ev)
     case BLE_HCI_OGF_LE:
         rc = ble_ll_hci_le_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen, &post_cb);
         break;
-#if MYNEWT_VAL(BLE_HCI_VS)
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
     case BLE_HCI_OGF_VENDOR:
         rc = ble_ll_hci_vs_cmd_proc(cmd->data, cmd->length, ocf, rspbuf, &rsplen);
         break;
@@ -1636,6 +1641,13 @@ ble_ll_hci_cmd_proc(struct ble_npl_event *ev)
         break;
     }
 
+    /* We always send command status for unknown command
+     * ref: Core 5.3, Vol 4, Part E, 4.5
+     */
+    if (rc == BLE_ERR_UNKNOWN_HCI_CMD) {
+        rc += (BLE_ERR_MAX + 1);
+    }
+
     /* If no response is generated, we free the buffers */
     BLE_LL_ASSERT(rc >= 0);
     if (rc <= BLE_ERR_MAX) {
@@ -1735,7 +1747,12 @@ ble_ll_hci_cmd_rx(uint8_t *cmdbuf, void *arg)
 int
 ble_ll_hci_acl_rx(struct os_mbuf *om, void *arg)
 {
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     ble_ll_acl_data_in(om);
+#else
+    /* host should never send ACL in that case but if it does just ignore it */
+    os_mbuf_free_chain(om);
+#endif
     return 0;
 }
 
diff --git a/nimble/controller/src/ble_ll_hci_ev.c b/nimble/controller/src/ble_ll_hci_ev.c
index 0d6da9a01e..9d6c39ecc0 100644
--- a/nimble/controller/src/ble_ll_hci_ev.c
+++ b/nimble/controller/src/ble_ll_hci_ev.c
@@ -493,9 +493,9 @@ ble_ll_hci_ev_sca_update(struct ble_ll_conn_sm *connsm, uint8_t status,
 #endif
 
 void
-ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line)
+ble_ll_hci_ev_send_vs_assert(const char *file, uint32_t line)
 {
-    struct ble_hci_ev_vendor_debug *ev;
+    struct ble_hci_ev_vs_debug *ev;
     struct ble_hci_ev *hci_ev;
     unsigned int str_len;
     bool skip = true;
@@ -510,7 +510,7 @@ ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line)
 
     hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
     if (hci_ev) {
-        hci_ev->opcode = BLE_HCI_EVCODE_VENDOR_DEBUG;
+        hci_ev->opcode = BLE_HCI_EVCODE_VS_DEBUG;
         hci_ev->length = sizeof(*ev);
         ev = (void *) hci_ev->data;
 
@@ -551,3 +551,31 @@ ble_ll_hci_ev_send_vendor_err(const char *file, uint32_t line)
         ble_ll_hci_event_send(hci_ev);
     }
 }
+
+#if MYNEWT_VAL(BLE_LL_HCI_LLCP_TRACE)
+void
+ble_ll_hci_ev_send_vs_llcp_trace(uint8_t type, uint16_t handle, uint16_t count,
+                                 void *pdu, size_t length)
+{
+    struct ble_hci_ev_vs_debug *ev;
+    struct ble_hci_ev *hci_ev;
+
+    hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+    if (hci_ev) {
+        hci_ev->opcode = BLE_HCI_EVCODE_VS_DEBUG;
+        hci_ev->length = sizeof(*ev) + 8 + length;
+        ev = (void *) hci_ev->data;
+
+        ev->id = 0x17;
+        ev->data[0] = type;
+        put_le16(&ev->data[1], handle);
+        put_le16(&ev->data[3], count);
+        ev->data[5] = 0;
+        ev->data[6] = 0;
+        ev->data[7] = 0;
+        memcpy(&ev->data[8], pdu, length);
+
+        ble_ll_hci_event_send(hci_ev);
+    }
+}
+#endif
diff --git a/nimble/controller/src/ble_ll_hci_priv.h b/nimble/controller/src/ble_ll_hci_priv.h
new file mode 100644
index 0000000000..af67e0a1a3
--- /dev/null
+++ b/nimble/controller/src/ble_ll_hci_priv.h
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_LL_HCI_PRIV_
+#define H_BLE_LL_HCI_PRIV_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
+void ble_ll_hci_vs_init(void);
+int ble_ll_hci_vs_cmd_proc(const uint8_t *cmdbuf, uint8_t cmdlen, uint16_t ocf,
+                           uint8_t *rspbuf, uint8_t *rsplen);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_BLE_LL_HCI_ */
diff --git a/nimble/controller/src/ble_ll_hci_vs.c b/nimble/controller/src/ble_ll_hci_vs.c
new file mode 100644
index 0000000000..f2057ea428
--- /dev/null
+++ b/nimble/controller/src/ble_ll_hci_vs.c
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdint.h>
+#include "syscfg/syscfg.h"
+#include "controller/ble_ll.h"
+#include "controller/ble_ll_hci.h"
+#include "controller/ble_ll_sync.h"
+#include "controller/ble_ll_adv.h"
+#include "controller/ble_ll_scan.h"
+#include "controller/ble_hw.h"
+#include "ble_ll_conn_priv.h"
+#include "ble_ll_priv.h"
+
+#if MYNEWT_VAL(BLE_LL_HCI_VS)
+
+SLIST_HEAD(ble_ll_hci_vs_list, ble_ll_hci_vs_cmd);
+static struct ble_ll_hci_vs_list g_ble_ll_hci_vs_list;
+
+static int
+ble_ll_hci_vs_rd_static_addr(uint16_t ocf,
+                             const uint8_t *cmdbuf, uint8_t cmdlen,
+                             uint8_t *rspbuf, uint8_t *rsplen)
+{
+    struct ble_hci_vs_rd_static_addr_rp *rsp = (void *) rspbuf;
+    ble_addr_t addr;
+
+    if (cmdlen != 0) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (ble_hw_get_static_addr(&addr) < 0) {
+        return BLE_ERR_UNSPECIFIED;
+    }
+
+    memcpy(rsp->addr, addr.val, sizeof(rsp->addr));
+
+    *rsplen = sizeof(*rsp);
+
+    return BLE_ERR_SUCCESS;
+}
+
+/* disallow changing TX power if there is any radio activity
+ * note: we could allow to change it if there is no TX activity (eg only
+ * passive scan or sync) but lets just keep this simple for now
+ */
+static int
+ble_ll_hci_vs_is_controller_busy(void)
+{
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    struct ble_ll_conn_sm *cur;
+    int i = 0;
+#endif
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    if (ble_ll_sync_enabled()) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    if (ble_ll_adv_enabled()) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    if (ble_ll_scan_enabled()) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (g_ble_ll_conn_create_sm.connsm) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    STAILQ_FOREACH(cur, &g_ble_ll_conn_free_list, free_stqe) {
+        i++;
+    }
+
+    /* check if all connection objects are free */
+    if (i < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) {
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+
+static int
+ble_ll_hci_vs_set_tx_power(uint16_t ocf, const uint8_t *cmdbuf, uint8_t cmdlen,
+                           uint8_t *rspbuf, uint8_t *rsplen)
+{
+    const struct ble_hci_vs_set_tx_pwr_cp *cmd = (const void *) cmdbuf;
+    struct ble_hci_vs_set_tx_pwr_rp *rsp = (void *) rspbuf;
+
+    if (cmdlen != sizeof(*cmd)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    if (ble_ll_hci_vs_is_controller_busy()) {
+        return BLE_ERR_CMD_DISALLOWED;
+    }
+
+    if (cmd->tx_power == 127) {
+        /* restore reset default */
+        g_ble_ll_tx_power = MYNEWT_VAL(BLE_LL_TX_PWR_DBM);
+    } else {
+        g_ble_ll_tx_power = ble_phy_txpower_round(cmd->tx_power);
+    }
+
+    rsp->tx_power = g_ble_ll_tx_power;
+    *rsplen = sizeof(*rsp);
+
+    return BLE_ERR_SUCCESS;
+}
+
+static struct ble_ll_hci_vs_cmd g_ble_ll_hci_vs_cmds[] = {
+    BLE_LL_HCI_VS_CMD(BLE_HCI_OCF_VS_RD_STATIC_ADDR,
+                      ble_ll_hci_vs_rd_static_addr),
+    BLE_LL_HCI_VS_CMD(BLE_HCI_OCF_VS_SET_TX_PWR,
+            ble_ll_hci_vs_set_tx_power),
+};
+
+static struct ble_ll_hci_vs_cmd *
+ble_ll_hci_vs_find_by_ocf(uint16_t ocf)
+{
+    struct ble_ll_hci_vs_cmd *entry;
+
+    entry = SLIST_FIRST(&g_ble_ll_hci_vs_list);
+    while (entry) {
+        if (entry->ocf == ocf) {
+            return entry;
+        }
+
+        entry = SLIST_NEXT(entry, link);
+    }
+
+    return NULL;
+}
+
+int
+ble_ll_hci_vs_cmd_proc(const uint8_t *cmdbuf, uint8_t cmdlen, uint16_t ocf,
+                       uint8_t *rspbuf, uint8_t *rsplen)
+{
+    struct ble_ll_hci_vs_cmd *cmd;
+    int rc;
+
+    cmd = ble_ll_hci_vs_find_by_ocf(ocf);
+    if (!cmd) {
+        rc = BLE_ERR_UNKNOWN_HCI_CMD;
+    } else {
+        rc = cmd->cb(ocf, cmdbuf, cmdlen, rspbuf, rsplen);
+    }
+
+    return rc;
+}
+
+void
+ble_ll_hci_vs_register(struct ble_ll_hci_vs_cmd *cmds, uint32_t num_cmds)
+{
+    uint32_t i;
+
+    /* Assume all cmds are registered early on init, so just assert in case of
+     * invalid request since it means something is wrong with the code itself.
+     */
+
+    for (i = 0; i < num_cmds; i++, cmds++) {
+        BLE_LL_ASSERT(cmds->cb != NULL);
+        BLE_LL_ASSERT(ble_ll_hci_vs_find_by_ocf(cmds->ocf) == NULL);
+
+        SLIST_INSERT_HEAD(&g_ble_ll_hci_vs_list, cmds, link);
+    }
+}
+
+void
+ble_ll_hci_vs_init(void)
+{
+    SLIST_INIT(&g_ble_ll_hci_vs_list);
+
+    ble_ll_hci_vs_register(g_ble_ll_hci_vs_cmds,
+                           ARRAY_SIZE(g_ble_ll_hci_vs_cmds));
+}
+
+#endif
diff --git a/nimble/controller/src/ble_ll_priv.h b/nimble/controller/src/ble_ll_priv.h
index 900950ef64..ca8e082958 100644
--- a/nimble/controller/src/ble_ll_priv.h
+++ b/nimble/controller/src/ble_ll_priv.h
@@ -24,6 +24,8 @@
 extern "C" {
 #endif
 
+extern int8_t g_ble_ll_tx_power;
+
 #ifdef MYNEWT
 
 #include "syscfg/syscfg.h"
diff --git a/nimble/controller/src/ble_ll_resolv.c b/nimble/controller/src/ble_ll_resolv.c
index 02af93040a..86f8ea7830 100644
--- a/nimble/controller/src/ble_ll_resolv.c
+++ b/nimble/controller/src/ble_ll_resolv.c
@@ -50,14 +50,31 @@ struct ble_ll_resolv_entry g_ble_ll_resolv_list[MYNEWT_VAL(BLE_LL_RESOLV_LIST_SI
 static int
 ble_ll_is_controller_busy(void)
 {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
     if (ble_ll_sync_enabled()) {
         return 1;
     }
 #endif
 
-    return ble_ll_adv_enabled() || ble_ll_scan_enabled() ||
-           g_ble_ll_conn_create_sm;
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    if (ble_ll_adv_enabled()) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    if (ble_ll_scan_enabled()) {
+        return 1;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (g_ble_ll_conn_create_sm.connsm) {
+        return 1;
+    }
+#endif
+
+    return 0;
 }
 /**
  * Called to determine if a change is allowed to the resolving list at this
@@ -155,7 +172,9 @@ ble_ll_resolv_rpa_timer_cb(struct ble_npl_event *ev)
     ble_npl_callout_reset(&g_ble_ll_resolv_data.rpa_timer,
                           g_ble_ll_resolv_data.rpa_tmo);
 
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     ble_ll_adv_rpa_timeout();
+#endif
 }
 
 /**
diff --git a/nimble/controller/src/ble_ll_rfmgmt.c b/nimble/controller/src/ble_ll_rfmgmt.c
index 3bf5d5fae6..986645f4a1 100644
--- a/nimble/controller/src/ble_ll_rfmgmt.c
+++ b/nimble/controller/src/ble_ll_rfmgmt.c
@@ -27,6 +27,7 @@
 #include "controller/ble_ll.h"
 #include "controller/ble_ll_sched.h"
 #include "controller/ble_ll_rfmgmt.h"
+#include "ble_ll_priv.h"
 
 #if MYNEWT_VAL(BLE_LL_RFMGMT_ENABLE_TIME) > 0
 
@@ -65,6 +66,7 @@ ble_ll_rfmgmt_enable(void)
         g_ble_ll_rfmgmt_data.state = RFMGMT_STATE_ENABLING;
         g_ble_ll_rfmgmt_data.enabled_at = os_cputime_get32();
         ble_phy_rfclk_enable();
+        BLE_LL_DEBUG_GPIO(RFMGMT, 1);
     }
 }
 
@@ -74,6 +76,7 @@ ble_ll_rfmgmt_disable(void)
     OS_ASSERT_CRITICAL();
 
     if (g_ble_ll_rfmgmt_data.state != RFMGMT_STATE_OFF) {
+        BLE_LL_DEBUG_GPIO(RFMGMT, 0);
         ble_phy_rfclk_disable();
         g_ble_ll_rfmgmt_data.state = RFMGMT_STATE_OFF;
     }
@@ -212,6 +215,8 @@ ble_ll_rfmgmt_init(void)
 {
     struct ble_ll_rfmgmt_data *rfmgmt = &g_ble_ll_rfmgmt_data;
 
+    BLE_LL_DEBUG_GPIO_INIT(RFMGMT);
+
     rfmgmt->state = RFMGMT_STATE_OFF;
 
     rfmgmt->ticks_to_enabled =
diff --git a/nimble/controller/src/ble_ll_scan.c b/nimble/controller/src/ble_ll_scan.c
index 197ec91072..987ab463ff 100644
--- a/nimble/controller/src/ble_ll_scan.c
+++ b/nimble/controller/src/ble_ll_scan.c
@@ -25,15 +25,16 @@
 #include "os/os.h"
 #include "os/os_cputime.h"
 #include "nimble/ble.h"
-#include "nimble/nimble_opt.h"
 #include "nimble/hci_common.h"
 #include "nimble/ble_hci_trans.h"
 #include "controller/ble_phy.h"
 #include "controller/ble_hw.h"
 #include "controller/ble_ll.h"
 #include "controller/ble_ll_sched.h"
-#include "controller/ble_ll_adv.h"
 #include "controller/ble_ll_scan.h"
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#include "controller/ble_ll_scan_aux.h"
+#endif
 #include "controller/ble_ll_hci.h"
 #include "controller/ble_ll_whitelist.h"
 #include "controller/ble_ll_resolv.h"
@@ -42,6 +43,8 @@
 #include "controller/ble_ll_sync.h"
 #include "ble_ll_conn_priv.h"
 
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+
 /*
  * XXX:
  * 1) I think I can guarantee that we dont process things out of order if
@@ -64,30 +67,17 @@
 #define SCAN_VALID_PHY_MASK     (BLE_HCI_LE_PHY_1M_PREF_MASK)
 #endif
 
-/* The scanning parameters set by host */
-static struct ble_ll_scan_params g_ble_ll_scan_params[BLE_LL_SCAN_PHY_NUMBER];
+struct ble_ll_scan_params {
+    uint8_t own_addr_type;
+    uint8_t scan_filt_policy;
+    struct ble_ll_scan_phy scan_phys[BLE_LL_SCAN_PHY_NUMBER];
+};
+
+static struct ble_ll_scan_params g_ble_ll_scan_params;
 
 /* The scanning state machine global object */
 static struct ble_ll_scan_sm g_ble_ll_scan_sm;
 
-struct ble_ll_ext_adv_hdr
-{
-    uint8_t mode;
-    uint8_t hdr_len;
-    uint8_t hdr[0];
-};
-
-struct ble_ll_scan_addr_data {
-    bool adva_present;
-    uint8_t adva_type;
-    uint8_t *adva;
-    uint8_t targeta_type;
-    uint8_t *targeta;
-    uint8_t adv_addr_type;
-    uint8_t *adv_addr;
-    struct ble_ll_resolv_entry *rl;
-};
-
 /*
  * Structure used to store advertisers. This is used to limit sending scan
  * requests to the same advertiser and also to filter duplicate events sent
@@ -140,122 +130,9 @@ static struct os_mempool g_scan_dup_pool;
 static TAILQ_HEAD(ble_ll_scan_dup_list, ble_ll_scan_dup_entry) g_scan_dup_list;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-#if MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT) != 0
-static os_membuf_t ext_scan_aux_mem[ OS_MEMPOOL_SIZE(
-                    MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
-                    sizeof (struct ble_ll_aux_data))
-];
-#else
-#define ext_scan_aux_mem NULL
-#endif
-
-static struct os_mempool ext_scan_aux_pool;
-
-static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm,
-                             struct ble_ll_sched_item *sch);
-
-static void
-ble_ll_aux_scan_drop_event_cb(struct ble_npl_event *ev)
-{
-    struct ble_ll_aux_data *aux_data = ble_npl_event_get_arg(ev);
-
-    ble_ll_scan_end_adv_evt(aux_data);
-    ble_ll_scan_aux_data_unref(aux_data);
-}
-
-static void
-ble_ll_aux_scan_drop(struct ble_ll_aux_data *aux_data)
-{
-    BLE_LL_ASSERT(aux_data);
-
-    STATS_INC(ble_ll_stats, aux_scan_drop);
-
-    ble_npl_event_init(&aux_data->ev, ble_ll_aux_scan_drop_event_cb, aux_data);
-    ble_ll_event_send(&aux_data->ev);
-}
-
-static int
-ble_ll_aux_scan_cb(struct ble_ll_sched_item *sch)
-{
-    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-    uint8_t lls = ble_ll_state_get();
-    uint32_t wfr_usec;
-
-    STATS_INC(ble_ll_stats, aux_sched_cb);
-
-    /* Drop the scheduled item if scan was disable or there is aux or scan
-     * response pending
-     */
-    if (!scansm->scan_enabled || scansm->cur_aux_data ||
-            scansm->scan_rsp_pending) {
-        ble_ll_aux_scan_drop(sch->cb_arg);
-        sch->cb_arg = NULL;
-        goto done;
-    }
-
-    /* Check if there is no aux connect sent. If so drop the sched item */
-    if (lls == BLE_LL_STATE_INITIATING && ble_ll_conn_init_pending_aux_conn_rsp()) {
-        ble_ll_aux_scan_drop(sch->cb_arg);
-        sch->cb_arg = NULL;
-        goto done;
-    }
-
-    /* This function is called only when scanner is running. This can happen
-     *  in 3 states:
-     * BLE_LL_STATE_SCANNING
-     * BLE_LL_STATE_INITIATING
-     * BLE_LL_STATE_STANDBY
-     */
-    if (lls != BLE_LL_STATE_STANDBY) {
-        ble_phy_disable();
-        ble_ll_state_set(BLE_LL_STATE_STANDBY);
-    }
-
-    /* When doing RX for AUX pkt, cur_aux_data keeps valid aux data */
-    scansm->cur_aux_data = sch->cb_arg;
-    sch->cb_arg = NULL;
-    BLE_LL_ASSERT(scansm->cur_aux_data != NULL);
-    scansm->cur_aux_data->scanning = 1;
-
-    if (ble_ll_scan_start(scansm, sch)) {
-        ble_ll_scan_interrupted(scansm);
-        goto done;
-    }
-
-    STATS_INC(ble_ll_stats, aux_fired_for_read);
-
-    wfr_usec = scansm->cur_aux_data->offset_units ? 300 : 30;
-    ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usec);
-
-done:
-
-    return BLE_LL_SCHED_STATE_DONE;
-}
-
 static int
-ble_ll_scan_ext_adv_init(struct ble_ll_aux_data **aux_data)
-{
-    struct ble_ll_aux_data *e;
-
-    e = os_memblock_get(&ext_scan_aux_pool);
-    if (!e) {
-        return -1;
-    }
-
-    memset(e, 0, sizeof(*e));
-    e->sch.sched_cb = ble_ll_aux_scan_cb;
-    e->sch.sched_type = BLE_LL_SCHED_TYPE_AUX_SCAN;
-    e->ref_cnt = 1;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    e->rpa_index = -1;
-#endif
-    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t)e, e->ref_cnt);
-
-    *aux_data = e;
-    STATS_INC(ble_ll_stats, aux_allocated);
+ble_ll_scan_start(struct ble_ll_scan_sm *scansm);
 
-    return 0;
-}
 #endif
 
 static inline uint32_t
@@ -314,17 +191,66 @@ ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm)
         scansm->scan_nrpa_timer = now + ble_ll_resolv_get_rpa_tmo();
     }
 }
+
+uint8_t *
+ble_ll_get_scan_nrpa(void)
+{
+    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
+
+    ble_ll_scan_refresh_nrpa(scansm);
+
+    return scansm->scan_nrpa;
+}
 #endif
 
+uint8_t
+ble_ll_scan_get_own_addr_type(void)
+{
+    return g_ble_ll_scan_sm.own_addr_type;
+}
+
+uint8_t
+ble_ll_scan_get_filt_policy(void)
+{
+    return g_ble_ll_scan_sm.scan_filt_policy;
+}
+
+uint8_t
+ble_ll_scan_get_filt_dups(void)
+{
+    return g_ble_ll_scan_sm.scan_filt_dups;
+}
+
+uint8_t
+ble_ll_scan_backoff_kick(void)
+{
+    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
+
+    if (scansm->backoff_count > 0) {
+        scansm->backoff_count--;
+    }
+
+    return scansm->backoff_count;
+}
+
+void
+ble_ll_scan_backoff_update(int success)
+{
+    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
+
+    ble_ll_scan_req_backoff(scansm, success);
+}
+
 static void
 ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm,
                             const uint8_t *adv_addr, uint8_t adv_addr_type,
-                            struct ble_ll_resolv_entry *rl)
+                            int8_t rpa_index)
 {
     uint8_t hdr_byte;
     struct ble_ll_scan_pdu_data *pdu_data;
     uint8_t *scana;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    struct ble_ll_resolv_entry *rl;
     uint8_t rpa[BLE_DEV_ADDR_LEN];
 #endif
 
@@ -346,6 +272,12 @@ ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm,
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
     if (scansm->own_addr_type & 0x02) {
+        if (rpa_index >= 0) {
+            rl = &g_ble_ll_resolv_list[rpa_index];
+        } else {
+            rl = NULL;
+        }
+
         /*
          * If device is on RL and we have local IRK, we use RPA generated using
          * that IRK as ScanA. Otherwise we use NRPA as ScanA to prevent our
@@ -431,99 +363,17 @@ ble_ll_scan_get_ext_adv_report(struct ext_adv_report *copy_from)
     return hci_ev;
 }
 
-static void
-ble_ll_scan_send_truncated(struct ble_ll_aux_data *aux_data)
-{
-    struct ble_hci_ev_le_subev_ext_adv_rpt *ev;
-    struct ext_adv_report *report;
-    struct ble_hci_ev *hci_ev;
-
-    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
-        return;
-    }
-
-    BLE_LL_ASSERT(aux_data);
-
-    /* No need to send if we did not send any report or sent truncated already */
-    if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) ||
-        (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)) {
-        return;
-    }
-
-    BLE_LL_ASSERT(aux_data->evt);
-    hci_ev = aux_data->evt;
-    aux_data->evt = NULL;
-
-    hci_ev->length = sizeof(*ev) + sizeof(*report);
-
-    ev = (void *) hci_ev->data;
-    report = ev->reports;
-
-    report->data_len = 0;
-
-    report->evt_type = aux_data->evt_type;
-    report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
-
-    if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) {
-        memcpy(report->addr, aux_data->adva, 6);
-        report->addr_type = aux_data->adva_type;
-    }
-
-    if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) {
-        memcpy(report->dir_addr, aux_data->targeta, 6);
-        report->dir_addr_type = aux_data->targeta_type;
-    }
-
-    report->sid = aux_data->adi >> 12;
-    ble_ll_hci_event_send(hci_ev);
-
-    aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-    aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY;
-    aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED;
-}
-
-static int
-ble_ll_scan_get_adi(struct ble_ll_aux_data *aux_data, uint16_t *adi)
-{
-    if (!aux_data || !(aux_data->flags & BLE_LL_AUX_HAS_ADI)) {
-        return -1;
-    }
-
-    *adi = aux_data->adi;
-
-    return 0;
-}
-
 void
 ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data)
 {
-    /* Make sure we send report with 'truncated' data state if needed */
-    ble_ll_scan_send_truncated(aux_data);
 }
 #endif
 
-static void
-ble_ll_scan_clean_cur_aux_data(void)
-{
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-
-    /* If scanner was reading aux ptr, we need to clean it up */
-    if (scansm->cur_aux_data) {
-        ble_ll_scan_end_adv_evt(scansm->cur_aux_data);
-        ble_ll_scan_aux_data_unref(scansm->cur_aux_data);
-        scansm->cur_aux_data = NULL;
-    }
-#endif
-}
-
 void
 ble_ll_scan_halt(void)
 {
     struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
 
-    ble_ll_scan_clean_cur_aux_data();
-
     /* Update backoff if we failed to receive scan response */
     if (scansm->scan_rsp_pending) {
         scansm->scan_rsp_pending = 0;
@@ -539,7 +389,7 @@ ble_ll_scan_halt(void)
  *
  * @return int 0: have not received a scan response; 1 otherwise.
  */
-static int
+int
 ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd,
                               uint8_t ext_adv, uint16_t adi)
 {
@@ -583,7 +433,7 @@ ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd,
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static void
+void
 ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd,
                              uint8_t ext_adv, uint16_t adi)
 {
@@ -862,8 +712,8 @@ ble_ll_scan_send_adv_report(uint8_t pdu_type,
                                                    hdr->rxinfo.rssi,
                                                    adv_data_len, om,
                                                    inita, inita_type);
-        goto done;
-    }
+goto done;
+}
 #endif
 
     if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) {
@@ -880,27 +730,6 @@ ble_ll_scan_send_adv_report(uint8_t pdu_type,
     }
 }
 
-static void
-ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan,
-                        int *phy)
-{
-    struct ble_ll_scan_params *scanp = scansm->scanp;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_aux_data *aux_data = scansm->cur_aux_data;
-
-    if (!scansm->ext_scanning || !aux_data || !aux_data->scanning) {
-        *chan = scanp->scan_chan;
-        *phy = scanp->phy;
-        return;
-    }
-
-    *chan = aux_data->chan;
-    *phy = aux_data->aux_phy;
-#else
-    *chan = scanp->scan_chan;
-    *phy = scanp->phy;
-#endif
-}
 /**
  * Called to enable the receiver for scanning.
  *
@@ -911,29 +740,21 @@ ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan,
  * @return int
  */
 static int
-ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch)
+ble_ll_scan_start(struct ble_ll_scan_sm *scansm)
 {
     int rc;
-    struct ble_ll_scan_params *scanp = scansm->scanp;
-    uint8_t scan_chan;
+    struct ble_ll_scan_phy *scanp = scansm->scanp;
+    uint8_t chan;
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
     uint8_t phy_mode;
-#endif
     int phy;
+#endif
 
     BLE_LL_ASSERT(scansm->scan_rsp_pending == 0);
 
-    ble_ll_get_chan_to_scan(scansm, &scan_chan, &phy);
-
-    /* XXX: right now scheduled item is only present if we schedule for aux
-     *      scan just make sanity check that we have proper combination of
-     *      sch and resulting scan_chan
-     */
-    BLE_LL_ASSERT(!sch || scan_chan < BLE_PHY_ADV_CHAN_START);
-    BLE_LL_ASSERT(sch || scan_chan >= BLE_PHY_ADV_CHAN_START);
-
     /* Set channel */
-    rc = ble_phy_setchan(scan_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
+    chan = scanp->scan_chan;
+    rc = ble_phy_setchan(chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
     BLE_LL_ASSERT(rc == 0);
 
     /*
@@ -955,20 +776,13 @@ ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch)
 #endif
 
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+    phy = scanp->phy;
     phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY);
     ble_phy_mode_set(phy_mode, phy_mode);
 #endif
 
-    /* XXX: probably need to make sure hfxo is running too */
-    /* XXX: can make this better; want to just start asap. */
-    if (sch) {
-        rc = ble_phy_rx_set_start_time(sch->start_time +
-                                       g_ble_ll_sched_offset_ticks,
-                                       sch->remainder);
-    } else {
-        rc = ble_phy_rx_set_start_time(os_cputime_get32() +
-                                       g_ble_ll_sched_offset_ticks, 0);
-    }
+    rc = ble_phy_rx_set_start_time(os_cputime_get32() +
+                                   g_ble_ll_sched_offset_ticks, 0);
     if (!rc || rc == BLE_PHY_ERR_RX_LATE) {
         /* If we are late here, it is still OK because we keep scanning.
          * Clear error
@@ -976,18 +790,13 @@ ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch)
         rc = 0;
 
         /* Enable/disable whitelisting */
-        if (scanp->scan_filt_policy & 1) {
+        if (scansm->scan_filt_policy & 1) {
             ble_ll_whitelist_enable();
         } else {
             ble_ll_whitelist_disable();
         }
 
-        /* Set link layer state to scanning */
-        if (scanp->scan_type == BLE_SCAN_TYPE_INITIATE) {
-            ble_ll_state_set(BLE_LL_STATE_INITIATING);
-        } else {
-            ble_ll_state_set(BLE_LL_STATE_SCANNING);
-        }
+        ble_ll_state_set(BLE_LL_STATE_SCANNING);
     }
 
     return rc;
@@ -1005,7 +814,7 @@ ble_ll_scan_get_next_adv_prim_chan(uint8_t chan)
 }
 
 static uint32_t
-ble_ll_scan_move_window_to(struct ble_ll_scan_params *scanp, uint32_t time)
+ble_ll_scan_move_window_to(struct ble_ll_scan_phy *scanp, uint32_t time)
 {
     uint32_t end_time;
 
@@ -1025,7 +834,7 @@ ble_ll_scan_move_window_to(struct ble_ll_scan_params *scanp, uint32_t time)
 }
 
 static bool
-ble_ll_scan_is_inside_window(struct ble_ll_scan_params *scanp, uint32_t time)
+ble_ll_scan_is_inside_window(struct ble_ll_scan_phy *scanp, uint32_t time)
 {
     uint32_t start_time;
 
@@ -1041,76 +850,6 @@ ble_ll_scan_is_inside_window(struct ble_ll_scan_params *scanp, uint32_t time)
            CPUTIME_LT(time, start_time + scanp->timing.window);
 }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static void
-ble_ll_scan_aux_data_free(struct ble_ll_aux_data *aux_data)
-{
-    if (aux_data) {
-        if (aux_data->evt) {
-            ble_hci_trans_buf_free((uint8_t *)aux_data->evt);
-            aux_data->evt = NULL;
-        }
-        os_memblock_put(&ext_scan_aux_pool, aux_data);
-        STATS_INC(ble_ll_stats, aux_freed);
-    }
-}
-
-struct ble_ll_aux_data *
-ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_data)
-{
-    os_sr_t sr;
-
-    BLE_LL_ASSERT(aux_data);
-
-    OS_ENTER_CRITICAL(sr);
-    aux_data->ref_cnt++;
-    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t) aux_data, aux_data->ref_cnt);
-
-    OS_EXIT_CRITICAL(sr);
-
-    return aux_data;
-}
-
-void
-ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_data)
-{
-    os_sr_t sr;
-
-    BLE_LL_ASSERT(aux_data);
-
-    OS_ENTER_CRITICAL(sr);
-    aux_data->ref_cnt--;
-    ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_UNREF, (uint32_t) aux_data, aux_data->ref_cnt);
-
-    if (aux_data->ref_cnt == 0) {
-        /*
-         * Some validation to make sure that we completed scan properly:
-         * - we either did not send any report or sent completed/truncated
-         * - we only sent one of completed/truncated
-         * - in case of error, we wither did not send anything or sent truncated
-         */
-        BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) ||
-                      ((aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) &&
-                       (aux_data->flags_ll & (BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED | BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED))));
-        BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED) || !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED));
-        BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) ||
-                      !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) ||
-                      (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED));
-
-        ble_ll_scan_aux_data_free(aux_data);
-    }
-
-    OS_EXIT_CRITICAL(sr);
-}
-
-static void
-ble_ll_scan_sched_remove(struct ble_ll_sched_item *sch)
-{
-    ble_ll_scan_end_adv_evt(sch->cb_arg);
-    ble_ll_scan_aux_data_unref(sch->cb_arg);
-    sch->cb_arg = NULL;
-}
-#endif
 /**
  * Stop the scanning state machine
  */
@@ -1130,8 +869,7 @@ ble_ll_scan_sm_stop(int chk_disable)
         OS_ENTER_CRITICAL(sr);
         lls = ble_ll_state_get();
 
-        if ((lls == BLE_LL_STATE_SCANNING) ||
-                        (lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) {
+        if (lls == BLE_LL_STATE_SCANNING) {
             /* Disable phy */
             ble_phy_disable();
 
@@ -1143,14 +881,17 @@ ble_ll_scan_sm_stop(int chk_disable)
 
     OS_ENTER_CRITICAL(sr);
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    scansm->connsm = NULL;
+#endif
+
     /* Disable scanning state machine */
     scansm->scan_enabled = 0;
     scansm->restart_timer_needed = 0;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     if (scansm->ext_scanning) {
-        ble_ll_scan_clean_cur_aux_data();
-        ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_AUX_SCAN, ble_ll_scan_sched_remove);
+        ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_SCAN_AUX, ble_ll_scan_aux_sched_remove);
         scansm->ext_scanning = 0;
     }
 #endif
@@ -1175,8 +916,8 @@ ble_ll_scan_sm_stop(int chk_disable)
 static int
 ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm)
 {
-    struct ble_ll_scan_params *scanp;
-    struct ble_ll_scan_params *scanp_next;
+    struct ble_ll_scan_phy *scanp;
+    struct ble_ll_scan_phy *scanp_next;
 
     if (!ble_ll_is_valid_own_addr_type(scansm->own_addr_type, g_random_addr)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
@@ -1229,45 +970,15 @@ ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm)
     return BLE_ERR_SUCCESS;
 }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static void
-ble_ll_aux_scan_rsp_failed(struct ble_ll_scan_sm *scansm)
-{
-    if (!scansm->cur_aux_data) {
-        return;
-    }
-
-    STATS_INC(ble_ll_stats, aux_scan_rsp_err);
-    ble_ll_scan_interrupted(scansm);
-}
-#endif
-
 static void
 ble_ll_scan_interrupted_event_cb(struct ble_npl_event *ev)
 {
     struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_aux_data *aux_data;
-#endif
 
     if (!scansm->scan_enabled) {
         return;
     }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    aux_data = ble_npl_event_get_arg(ev);
-
-    if (aux_data) {
-        if (scansm->scan_rsp_pending) {
-            STATS_INC(ble_ll_stats, aux_scan_rsp_err);
-        }
-        ble_ll_scan_end_adv_evt(aux_data);
-        ble_ll_scan_aux_data_unref(aux_data);
-        ble_npl_event_set_arg(ev, NULL);
-        STATS_INC(ble_ll_stats, aux_missed_adv);
-    }
-#endif
-
     /*
     * If we timed out waiting for a response, the scan response pending
     * flag should be set. Deal with scan backoff. Put device back into rx.
@@ -1295,10 +1006,10 @@ ble_ll_scan_event_proc(struct ble_npl_event *ev)
     os_sr_t sr;
     bool start_scan;
     bool inside_window;
-    struct ble_ll_scan_params *scanp;
+    struct ble_ll_scan_phy *scanp;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     bool inside_window_next;
-    struct ble_ll_scan_params *scanp_next;
+    struct ble_ll_scan_phy *scanp_next;
 #endif
     uint32_t next_proc_time;
     uint32_t now;
@@ -1321,7 +1032,7 @@ ble_ll_scan_event_proc(struct ble_npl_event *ev)
         return;
     }
 
-    if (scansm->cur_aux_data || scansm->scan_rsp_pending) {
+    if (scansm->scan_rsp_pending) {
         /* Aux scan in progress. Wait */
         STATS_INC(ble_ll_stats, scan_timer_stopped);
         scansm->restart_timer_needed = 1;
@@ -1380,20 +1091,20 @@ ble_ll_scan_event_proc(struct ble_npl_event *ev)
      */
     start_scan = inside_window;
     switch (ble_ll_state_get()) {
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
     case BLE_LL_STATE_ADV:
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     case BLE_LL_STATE_CONNECTION:
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
     case BLE_LL_STATE_SYNC:
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    case BLE_LL_STATE_SCAN_AUX:
          start_scan = false;
          break;
-    case BLE_LL_STATE_INITIATING:
-        /* Must disable PHY since we will move to a new channel */
-        ble_phy_disable();
-        if (!inside_window) {
-            ble_ll_state_set(BLE_LL_STATE_STANDBY);
-        }
-        /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */
-        ble_ll_conn_reset_pending_aux_conn_rsp();
-        break;
+#endif
     case BLE_LL_STATE_SCANNING:
         /* Must disable PHY since we will move to a new channel */
         ble_phy_disable();
@@ -1409,7 +1120,7 @@ ble_ll_scan_event_proc(struct ble_npl_event *ev)
     }
 
     if (start_scan) {
-        ble_ll_scan_start(scansm, NULL);
+        ble_ll_scan_start(scansm);
     } else {
         ble_ll_rfmgmt_release();
     }
@@ -1438,7 +1149,7 @@ ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
 {
     int rc;
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
+    struct ble_ll_scan_phy *scanp;
 
     rc = 0;
     scansm = &g_ble_ll_scan_sm;
@@ -1459,10 +1170,6 @@ ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
         }
 #endif
 
-        if (scansm->cur_aux_data && !scansm->scan_rsp_pending ) {
-            STATS_INC(ble_ll_stats, aux_received);
-        }
-
         /*
          * If this is the first PDU after we sent the scan response (as
          * denoted by the scan rsp pending flag), we set a bit in the ble
@@ -1479,9 +1186,6 @@ ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
                 *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD;
             } else {
                 ble_ll_scan_req_backoff(scansm, 0);
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-                ble_ll_aux_scan_rsp_failed(scansm);
-#endif
             }
         }
         break;
@@ -1492,469 +1196,26 @@ ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
         }
         break;
 #endif
-    default:
-        break;
-    }
-
-    return rc;
-}
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_SCAN_TYPE_INITIATE:
+        if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) ||
+            (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND)) {
+            rc = 1;
+        }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static uint8_t
-ble_ll_ext_adv_phy_mode_to_local_phy(uint8_t adv_phy_mode)
-{
-    switch (adv_phy_mode) {
-    case 0x00:
-        return BLE_PHY_1M;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
-     case 0x01:
-        return BLE_PHY_2M;
-#endif
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
-    case 0x02:
-        return BLE_PHY_CODED;
-#endif
-    }
-
-    return 0;
-}
-
-static int
-ble_ll_ext_scan_parse_aux_ptr(struct ble_ll_aux_data *aux_data, uint8_t *buf)
-{
-    uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF;
-
-    aux_data->chan = (aux_ptr_field) & 0x3F;
-    if (aux_data->chan >= BLE_PHY_NUM_DATA_CHANS) {
-        return -1;
-    }
-
-    /* TODO use CA aux_ptr_field >> 6 */
-
-    aux_data->offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF);
-
-    if ((aux_ptr_field >> 7) & 0x01) {
-            aux_data->offset *= 10;
-            aux_data->offset_units = 1;
-    }
-
-    if (aux_data->offset < BLE_LL_MAFS) {
-        return -1;
-    }
-
-    aux_data->aux_phy =
-            ble_ll_ext_adv_phy_mode_to_local_phy((aux_ptr_field >> 21) & 0x07);
-    if (aux_data->aux_phy == 0) {
-        return -1;
-    }
-
-    return 0;
-}
-
-static void
-ble_ll_ext_scan_parse_adv_info(struct ext_adv_report *report, const uint8_t *buf)
-{
-    uint16_t adv_info = get_le16(buf);
-
-    /* TODO Use DID */
-
-    report->sid = (adv_info >> 12);
-}
-
-/**
- * ble_ll_scan_update_aux_data
- *
- * Update aux_data stored in ble_hdr.rxinfo.user_data. If no aux_data is present
- * (i.e. processing ADV_EXT_IND) this will try to allocate new aux_data.
- *
- * Context: Interrupt
- *
- * @param ble_hdr
- * @param rxbuf
- *
- * @return int
- *  1: do not scan for next AUX (no AuxPtr or malformed data)
- *  0: scan for next AUX (valid AuxPtr found)
- * -1: error
- */
-int
-ble_ll_scan_update_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf,
-                            bool *adva_present)
-{
-    uint8_t pdu_hdr;
-    uint8_t pdu_len;
-    uint8_t adv_mode;
-    uint8_t eh_len;
-    uint8_t eh_flags;
-    uint8_t *eh;
-    struct ble_ll_aux_data *aux_data;
-    bool is_aux;
-
-    aux_data = ble_hdr->rxinfo.user_data;
-    /* aux_data is initially not set only for ADV_EXT_IND */
-    is_aux = aux_data;
-
-    pdu_hdr = rxbuf[0];
-    pdu_len = rxbuf[1];
-
-    /* PDU without at least Extended Header Length is invalid */
-    if (pdu_len == 0) {
-        return -1;
-    }
-
-    adv_mode = rxbuf[2] >> 6;
-    eh_len = rxbuf[2] & 0x3f;
-    eh_flags = rxbuf[3];
-    eh = &rxbuf[4];
-
-    /*
-     * PDU without Extended Header is valid in case of last AUX_CHAIN_IND in
-     * chain so aux_data has to be set and advertising mode has to be 00b,
-     * otherwise it's an invalid PDU.
-     */
-    if (eh_len == 0) {
-        if (!aux_data || adv_mode) {
-            return -1;
-        }
-        aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE;
-        return 1;
-    }
-
-    /*
-     * If aux_data is not set, this is ADV_EXT_IND which starts new extended
-     * advertising event.
-     */
-    if (!aux_data) {
-        if (ble_ll_scan_ext_adv_init(&aux_data)) {
-            return -1;
-        }
-
-        aux_data->aux_primary_phy = ble_hdr->rxinfo.phy;
-    } else {
-        if (aux_data->flags_isr & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED) {
-            aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED;
-        } else {
-            aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED;
-        }
-    }
-
-    /* Now parse extended header... */
-
-    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
-        aux_data->flags |= BLE_LL_AUX_HAS_ADVA;
-        memcpy(aux_data->adva, eh, 6);
-        aux_data->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK);
-        eh += BLE_LL_EXT_ADV_ADVA_SIZE;
-
-        if (adva_present) {
-            *adva_present = true;
-        }
-    } else if (adva_present) {
-        *adva_present = false;
-    }
-
-    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
-        aux_data->flags |= BLE_LL_AUX_HAS_TARGETA;
-        memcpy(aux_data->targeta, eh, 6);
-        aux_data->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK);
-        eh += BLE_LL_EXT_ADV_TARGETA_SIZE;
-    }
-
-
-    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
-        eh += 1;
-    }
-
-    if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
-        aux_data->flags |= BLE_LL_AUX_HAS_ADI;
-        if (is_aux) {
-            if (get_le16(eh) != aux_data->adi) {
-                aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-                STATS_INC(ble_ll_stats, aux_chain_err);
-            }
-        } else {
-            aux_data->adi = get_le16(eh);
-        }
-        eh += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
-    }
-
-    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
-        if (ble_ll_ext_scan_parse_aux_ptr(aux_data, eh)) {
-            aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-        }
-    } else if (!(adv_mode & BLE_LL_EXT_ADV_MODE_SCAN)) {
-        /* No AuxPtr for scannable PDU is ignored since we can still scan it */
-        aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE;
-    }
-
-    ble_hdr->rxinfo.user_data = aux_data;
-
-    /* Do not scan for next AUX if either no AuxPtr or malformed data found */
-    return !(eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) ||
-           (aux_data->flags_isr & BLE_LL_AUX_FLAG_SCAN_ERROR);
-}
-
-/**
- * Called when a receive ADV_EXT PDU has ended.
- *
- * Context: Interrupt
- *
- * @return int
- *       < 0  Error
- *      >= 0: Success (number of bytes left in PDU)
- *
- */
-static int
-ble_ll_scan_parse_ext_hdr(struct os_mbuf *om,
-                          const uint8_t *adva, uint8_t adva_type,
-                          const uint8_t *inita, uint8_t inita_type,
-                          struct ble_mbuf_hdr *ble_hdr,
-                          struct ext_adv_report *report)
-{
-    uint8_t pdu_len;
-    uint8_t ext_hdr_len;
-    uint8_t ext_hdr_flags;
-    uint8_t *ext_hdr;
-    uint8_t *rxbuf = om->om_data;
-    int i = 1;
-    struct ble_ll_scan_sm *scansm;
-    struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;
-
-    BLE_LL_ASSERT(report);
-
-    scansm = &g_ble_ll_scan_sm;
-
-    if (!scansm->ext_scanning) {
-       /* Ignore ext adv if host does not want it*/
-       return -1;
-    }
-
-    pdu_len = rxbuf[1];
-    if (pdu_len == 0) {
-        return -1;
-    }
-
-    report->evt_type = rxbuf[2] >> 6;
-    if ( report->evt_type > BLE_LL_EXT_ADV_MODE_SCAN) {
-        return -1;
-    }
-
-    if (BLE_MBUF_HDR_SCAN_RSP_RXD(ble_hdr)) {
-        report->evt_type |= BLE_HCI_ADV_SCAN_RSP_MASK;
-    }
-
-    ext_hdr_len = rxbuf[2] & 0x3F;
-    os_mbuf_adj(om, 3);
-
-    ext_hdr_flags = rxbuf[3];
-    ext_hdr = &rxbuf[4];
-
-    if (ext_hdr_len) {
-        i = 0;
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
-            i += BLE_LL_EXT_ADV_ADVA_SIZE;
-        }
-
-        if (adva) {
-            memcpy(report->addr, adva, 6);
-            report->addr_type = adva_type;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
-            i += BLE_LL_EXT_ADV_TARGETA_SIZE;
-        }
-
-        if (inita) {
-           memcpy(report->dir_addr, inita, 6);
-           report->dir_addr_type = inita_type;
-           report->evt_type |= BLE_HCI_ADV_DIRECT_MASK;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
-            /* Just skip it for now*/
-            i += 1;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
-            ble_ll_ext_scan_parse_adv_info(report, (ext_hdr + i));
-            i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
-        } else if (report->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) {
-            report->sid = (aux_data->adi >> 12);
-        }
-
-        /* In this point of time we don't want to care about aux ptr */
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
-            i += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
-            report->periodic_itvl = get_le16(ext_hdr + i + 2);
-            i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
-            report->tx_power = *(ext_hdr + i);
-            i += BLE_LL_EXT_ADV_TX_POWER_SIZE;
-        }
-
-        /* TODO Handle ACAD if needed */
-    }
-
-    /* In the event we need information on primary and secondary PHY used during
-     * advertising.
-     */
-    if (!aux_data) {
-        report->pri_phy = ble_hdr->rxinfo.phy;
-        goto done;
-    }
-
-    report->sec_phy = aux_data->aux_phy;
-    report->pri_phy = aux_data->aux_primary_phy;
-
-    if (ext_hdr_len) {
-        /* Adjust mbuf to contain advertising data only */
-        os_mbuf_adj(om, ext_hdr_len);
-    }
-
-    /* Let us first keep update event type in aux data.
-     * Note that in aux chain and aux scan response packets
-     * we do miss original event type, which we need for advertising report.
-     */
-    aux_data->evt_type |= report->evt_type;
-    report->evt_type = aux_data->evt_type;
-
-done:
-    return pdu_len - ext_hdr_len - 1;
-}
-
-static int
-ble_ll_scan_get_addr_from_ext_adv(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr,
-                                  uint8_t **addr, uint8_t *addr_type,
-                                  uint8_t **inita, uint8_t *inita_type,
-                                  int *ext_mode)
-{
-    uint8_t pdu_len;
-    uint8_t ext_hdr_len;
-    uint8_t ext_hdr_flags;
-    uint8_t *ext_hdr;
-    bool has_adva = false;
-    bool has_inita = false;
-    int i;
-    struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;
-
-    *addr = NULL;
-    *inita = NULL;
-
-    pdu_len = rxbuf[1];
-    if (pdu_len == 0) {
-        return -1;
-    }
-
-    *ext_mode = rxbuf[2] >> 6;
-    if (*ext_mode > BLE_LL_EXT_ADV_MODE_SCAN) {
-        return -1;
-    }
-
-    ext_hdr_len = rxbuf[2] & 0x3F;
-    if (ext_hdr_len == 0) {
-        goto done;
-    }
-
-    ext_hdr_flags = rxbuf[3];
-    ext_hdr = &rxbuf[4];
-
-    i = 0;
-    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
-        if (ext_hdr_len < BLE_LL_EXT_ADV_ADVA_SIZE) {
-            return -1;
-        }
-
-        *addr = ext_hdr + i;
-        *addr_type =
-                ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
-        i += BLE_LL_EXT_ADV_ADVA_SIZE;
-
-        has_adva = true;
-    }
-
-    if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
-        *inita = ext_hdr + i;
-        *inita_type =
-                ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
-        i += BLE_LL_EXT_ADV_TARGETA_SIZE;
-
-        has_inita = true;
-    }
-
-done:
-    /* Check if we had address already. If yes, replace it with new one */
-
-    if (aux_data) {
-        /* If address has been provided, we do have it already in aux_data.*/
-        if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) {
-            if (!has_adva) {
-                *addr = aux_data->adva;
-                *addr_type = aux_data->adva_type;
-            } else {
-                memcpy(aux_data->adva, *addr, 6);
-                aux_data->adva_type = *addr_type;
-            }
-        }
-
-        if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) {
-            if (!has_inita) {
-                *inita = aux_data->targeta;
-                *inita_type = aux_data->targeta_type;
-            } else {
-                memcpy(aux_data->targeta, *inita, 6);
-                aux_data->targeta_type = *inita_type;
-            }
+        if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning)) {
+            *rxflags |= BLE_MBUF_HDR_F_EXT_ADV;
+            rc = 1;
         }
-    }
-
-    return 0;
-}
 #endif
-
-int
-ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf,
-                            struct ble_mbuf_hdr *ble_hdr,
-                            uint8_t **addr, uint8_t *addr_type,
-                            uint8_t **inita, uint8_t *inita_type,
-                            int *ext_mode)
-{
-    /*
-     * XXX this should be only used for legacy advertising, but need to refactor
-     *     code in ble_ll_init first so it does not call this for ext
-     */
-
-    if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND &&
-        pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
-        /* Legacy advertising */
-        *addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
-        *addr = rxbuf + BLE_LL_PDU_HDR_LEN;
-
-        if (pdu_type != BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
-            *inita = NULL;
-            *inita_type = 0;
-            return 0;
-        }
-
-        *inita = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN;
-        *inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
-
-        return 0;
-    }
-
-    /* Extended advertising */
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    return ble_ll_scan_get_addr_from_ext_adv(rxbuf, ble_hdr, addr, addr_type,
-                                          inita, inita_type, ext_mode);
-#else
-    return -1;
+        break;
 #endif
+    default:
+        break;
+    }
 
-    return 0;
+    return rc;
 }
 
 static void
@@ -1963,8 +1224,6 @@ ble_ll_scan_get_addr_data_from_legacy(uint8_t pdu_type, uint8_t *rxbuf,
 {
     BLE_LL_ASSERT(pdu_type < BLE_ADV_PDU_TYPE_ADV_EXT_IND);
 
-    addrd->adva_present = true;
-
     addrd->adva = rxbuf + BLE_LL_PDU_HDR_LEN;
     addrd->adva_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
 
@@ -1977,80 +1236,45 @@ ble_ll_scan_get_addr_data_from_legacy(uint8_t pdu_type, uint8_t *rxbuf,
     }
 }
 
-/*
- * Matches incoming PDU using scan filter policy and whitelist, if applicable.
- * This will also resolve addresses and update flags/fields in header and
- * addr_data as needed.
- *
- * @return  0 = no match
- *          1 = match
- *          2 = match, but do not scan
- */
-static int
-ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd)
+int
+ble_ll_scan_rx_filter(uint8_t own_addr_type, uint8_t scan_filt_policy,
+                      struct ble_ll_scan_addr_data *addrd, uint8_t *scan_ok)
 {
-    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-    struct ble_ll_scan_params *scanp = scansm->scanp;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data;
-#endif
-    struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
     struct ble_ll_resolv_entry *rl = NULL;
 #endif
     bool scan_req_allowed = true;
-    int resolved = 0;
+    bool resolved;
+
+    /* Note: caller is expected to fill adva, targeta and rpa_index in addrd */
 
-    /* Use AdvA as initial advertiser address, we may try to resolve it later */
+    /* Use AdvA as initial advertiser address, we may change it if resolved */
     addrd->adv_addr = addrd->adva;
     addrd->adv_addr_type = addrd->adva_type;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    /* By default, assume AdvA is not resolved */
-    rxinfo->rpa_index = -1;
+    addrd->adva_resolved = 0;
+    addrd->targeta_resolved = 0;
+
+    BLE_LL_ASSERT((addrd->rpa_index < 0) ||
+                  (ble_ll_addr_subtype(addrd->adva, addrd->adva_type) ==
+                   BLE_LL_ADDR_SUBTYPE_RPA));
 
     switch (ble_ll_addr_subtype(addrd->adva, addrd->adva_type)) {
     case BLE_LL_ADDR_SUBTYPE_RPA:
-        /*
-         * Only resolve if packet actually contained AdvA.
-         * In extended advertising PDUs we may use RL index from a PDU that
-         * already had AdvA (e.g. ADV_EXT_IND in case of AUX_ADV_IND without
-         * AdvA). In legacy advertising PDUs we always need to resolve AdvA.
-         */
-        if (addrd->adva_present) {
-            rxinfo->rpa_index = ble_hw_resolv_list_match();
-        } else {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-            BLE_LL_ASSERT(aux_data);
-            rxinfo->rpa_index = aux_data->rpa_index;
-#else
-            BLE_LL_ASSERT(false);
-            rxinfo->rpa_index = -1;
-#endif
-        }
-
-        if (rxinfo->rpa_index < 0) {
+        if (addrd->rpa_index < 0) {
             break;
         }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        if (aux_data) {
-            aux_data->rpa_index = rxinfo->rpa_index;
-        }
-#endif
+        addrd->adva_resolved = 1;
 
         /* Use resolved identity address as advertiser address */
-        rl = &g_ble_ll_resolv_list[rxinfo->rpa_index];
+        rl = &g_ble_ll_resolv_list[addrd->rpa_index];
         addrd->adv_addr = rl->rl_identity_addr;
         addrd->adv_addr_type = rl->rl_addr_type;
-        addrd->rl = rl;
-
-        rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
-        resolved = 1;
         break;
     case BLE_LL_ADDR_SUBTYPE_IDENTITY:
-        /*
-         * If AdvA is an identity address, we need to check if that device was
+        /* If AdvA is an identity address, we need to check if that device was
          * added to RL in order to use proper privacy mode.
          */
         rl = ble_ll_resolv_list_find(addrd->adva, addrd->adva_type);
@@ -2058,12 +1282,12 @@ ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *ad
             break;
         }
 
-        addrd->rl = rl;
-
         /* Ignore device if using network privacy mode and it has IRK */
         if ((rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && rl->rl_has_peer) {
-            return 0;
+            return -1;
         }
+
+        addrd->rpa_index = ble_ll_resolv_get_idx(rl);
         break;
     default:
         /* NRPA goes through filtering policy directly */
@@ -2075,30 +1299,29 @@ ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *ad
         case BLE_LL_ADDR_SUBTYPE_RPA:
             /* Check if TargetA can be resolved using the same RL entry as AdvA */
             if (rl && ble_ll_resolv_rpa(addrd->targeta, rl->rl_local_irk)) {
-                rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED;
+                addrd->targeta_resolved = 1;
                 break;
             }
 
             /* Check if scan filter policy allows unresolved RPAs to be processed */
-            if (!(scanp->scan_filt_policy & 0x02)) {
-                return 0;
+            if (!(scan_filt_policy & 0x02)) {
+                return -2;
             }
 
-            /*
-             * We will notify host as requited by scan policy, but make sure we
-             * do not send scan request since we do not know if this is directed
-             * to us.
+            /* Do not send scan request even if scan policy allows unresolved
+             * RPAs - we do not know if this one if directed to us.
              */
             scan_req_allowed = false;
             break;
         case BLE_LL_ADDR_SUBTYPE_IDENTITY:
             /* We shall ignore identity in TargetA if we are using RPA */
-            if ((scanp->own_addr_type & 0x02) && rl && rl->rl_has_local) {
-                return 0;
+            if ((own_addr_type & 0x02) && rl && rl->rl_has_local) {
+                return -1;
             }
+
             /* Ignore if not directed to us */
             if (!ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) {
-                return 0;
+                return -1;
             }
             break;
         default:
@@ -2106,182 +1329,174 @@ ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *ad
             break;
         }
     }
+
+    resolved = addrd->adva_resolved;
 #else
     /* Ignore if not directed to us */
     if (addrd->targeta &&
         !ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) {
-        return 0;
+        return -1;
     }
+
+    resolved = false;
 #endif
 
-    /* Check on WL if required by scan filter policy */
-    if (scanp->scan_filt_policy & 0x01) {
-        if (!ble_ll_whitelist_match(addrd->adv_addr, addrd->adv_addr_type, resolved)) {
-            return 0;
+    if (scan_filt_policy & 0x01) {
+        /* Check on WL if required by scan filter policy */
+        if (!ble_ll_whitelist_match(addrd->adv_addr, addrd->adv_addr_type,
+                                    resolved)) {
+            return -2;
         }
     }
 
-    return scan_req_allowed ? 1 : 2;
+    if (scan_ok) {
+        *scan_ok = scan_req_allowed;
+    }
+
+    return 0;
+}
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+int
+ble_ll_scan_rx_check_init(struct ble_ll_scan_addr_data *addrd)
+{
+    struct ble_ll_scan_sm *scansm;
+    struct ble_ll_conn_sm *connsm;
+
+    scansm = &g_ble_ll_scan_sm;
+    connsm = scansm->connsm;
+    BLE_LL_ASSERT(connsm);
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    if ((connsm->peer_addr_type > BLE_ADDR_RANDOM) && !addrd->adva_resolved) {
+        return -1;
+    }
+#endif
+    if ((addrd->adv_addr_type != (connsm->peer_addr_type & 0x01)) ||
+        memcmp(addrd->adv_addr, connsm->peer_addr, 6) != 0) {
+        return -1;
+    }
+
+    return 0;
 }
+#endif
 
 static int
-ble_ll_scan_rx_isr_on_legacy(uint8_t pdu_type, uint8_t *rxbuf,
-                             struct ble_mbuf_hdr *hdr,
-                             struct ble_ll_scan_addr_data *addrd)
+ble_ll_scan_rx_isr_end_on_adv(uint8_t pdu_type, uint8_t *rxbuf,
+                              struct ble_mbuf_hdr *hdr,
+                              struct ble_ll_scan_addr_data *addrd)
 {
     struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-    struct ble_ll_scan_params *scanp = scansm->scanp;
+    struct ble_ll_scan_phy *scanp = scansm->scanp;
     struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
-    uint8_t sreq_adva_type;
-    uint8_t *sreq_adva;
+    uint8_t scan_ok;
     int rc;
 
     ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd);
 
-    if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) {
-        if (!BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) {
-            /*
-             * We were not expecting scan response so just ignore and do not
-             * update backoff.
-             */
-            return -1;
-        }
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    addrd->rpa_index = ble_hw_resolv_list_match();
+#endif
 
-        sreq_adva_type = !!(scansm->pdu_data.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK);
-        sreq_adva = scansm->pdu_data.adva;
+    rc = ble_ll_scan_rx_filter(scansm->own_addr_type,
+                               scansm->scan_filt_policy, addrd, &scan_ok);
+    if (rc < 0) {
+        return 0;
+    }
 
-        /*
-         * Ignore scan response if AdvA does not match AdvA in request and also
-         * update backoff as if there was no scan response.
-         */
-        if ((addrd->adva_type != sreq_adva_type) ||
-            memcmp(addrd->adva, sreq_adva, BLE_DEV_ADDR_LEN)) {
-            ble_ll_scan_req_backoff(scansm, 0);
-            return -1;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if ((scanp->scan_type == BLE_SCAN_TYPE_INITIATE) &&
+        !(scansm->scan_filt_policy & 0x01)) {
+        rc = ble_ll_scan_rx_check_init(addrd);
+        if (rc < 0) {
+            return 0;
         }
+    }
+#endif
 
+    rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-        /*
-         * We are not pushing this one through filters so need to update
-         * rpa_index here as otherwise pkt_in won't be able to determine
-         * advertiser address properly.
-         */
-        rxinfo->rpa_index = ble_hw_resolv_list_match();
-        if (rxinfo->rpa_index >= 0) {
-            rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
-        }
+    rxinfo->rpa_index = addrd->rpa_index;
+    if (addrd->adva_resolved) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
+    }
+    if (addrd->targeta_resolved) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED;
+    }
 #endif
 
-        rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
-
+    if (!scan_ok) {
+        /* Scan request forbidden by filter policy */
         return 0;
     }
 
-    rc = ble_ll_scan_rx_filter(hdr, addrd);
-    if (!rc) {
-        return 0;
+    /* Allow responding to all PDUs when initiating since unwanted PDUs were
+     * already filtered out in isr_start.
+     */
+    if ((scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) &&
+            ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) ||
+             (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND))) {
+        return 1;
     }
 
-    rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
-
-    if (rc == 2) {
-        /* Scan request forbidden by filter policy */
-        return 0;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (scanp->scan_type == BLE_SCAN_TYPE_INITIATE) {
+        return 1;
     }
+#endif
 
-    return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) &&
-           ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) ||
-            (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND));
+    return 0;
 }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
 static int
-ble_ll_scan_rx_isr_on_aux(uint8_t pdu_type, uint8_t *rxbuf,
-                          struct ble_mbuf_hdr *hdr,
-                          struct ble_ll_scan_addr_data *addrd)
+ble_ll_scan_rx_isr_end_on_scan_rsp(uint8_t pdu_type, uint8_t *rxbuf,
+                                   struct ble_mbuf_hdr *hdr,
+                                   struct ble_ll_scan_addr_data *addrd)
 {
     struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-    struct ble_ll_scan_params *scanp = scansm->scanp;
     struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
-    struct ble_ll_aux_data *aux_data;
-    int rc;
+    uint8_t sreq_adva_type;
+    uint8_t *sreq_adva;
 
-    if (!scansm->ext_scanning) {
-        return -1;
-    }
+    ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd);
 
-    rc = ble_ll_scan_update_aux_data(hdr, rxbuf, &addrd->adva_present);
-    if (rc < 0) {
-        rxinfo->flags |= BLE_MBUF_HDR_F_AUX_INVALID;
+    if (!BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) {
+        /*
+         * We were not expecting scan response so just ignore and do not
+         * update backoff.
+         */
         return -1;
-    } else if (rc == 0) {
-        rxinfo->flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT;
     }
 
-    /* Now we can update aux_data from header since it may have just been created */
-    aux_data = rxinfo->user_data;
+    sreq_adva_type = !!(scansm->pdu_data.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK);
+    sreq_adva = scansm->pdu_data.adva;
 
     /*
-     * Restore proper header flags if filtering was already done successfully on
-     * some previous PDU in an event.
+     * Ignore scan response if AdvA does not match AdvA in request and also
+     * update backoff as if there was no scan response.
      */
-    if (aux_data->flags & BLE_LL_AUX_IS_MATCHED) {
-        rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-        rxinfo->rpa_index = aux_data->rpa_index;
-        if (rxinfo->rpa_index >= 0) {
-            rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
-        }
-        if (aux_data->flags & BLE_LL_AUX_IS_TARGETA_RESOLVED) {
-            rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED;
-        }
-#endif
-        goto done;
-    }
-
-    if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) {
-        addrd->adva = aux_data->adva;
-        addrd->adva_type = aux_data->adva_type;
-    } else {
-        /* Accept this PDU and wait for AdvA in aux */
-        rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
-        return 0;
-    }
-    if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) {
-        addrd->targeta = aux_data->targeta;
-        addrd->targeta_type = aux_data->targeta_type;
-    } else {
-        addrd->targeta = NULL;
-    }
-
-    rc = ble_ll_scan_rx_filter(hdr, addrd);
-    if (!rc) {
-        return 0;
+    if ((addrd->adva_type != sreq_adva_type) ||
+        memcmp(addrd->adva, sreq_adva, BLE_DEV_ADDR_LEN)) {
+        ble_ll_scan_req_backoff(scansm, 0);
+        return -1;
     }
 
-    rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
-
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
     /*
-     * Once we matched device, there's no need to go through filtering on every
-     * other PDU in an event so just store info required to restore state for
-     * subsequent PDUs in aux_data.
+     * We are not pushing this one through filters so need to update
+     * rpa_index here as otherwise pkt_in won't be able to determine
+     * advertiser address properly.
      */
-    aux_data->flags |= BLE_LL_AUX_IS_MATCHED;
-    if (rxinfo->flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) {
-        aux_data->flags |= BLE_LL_AUX_IS_TARGETA_RESOLVED;
-        /* AdvA state is already stored in rpa_index */
+    rxinfo->rpa_index = ble_hw_resolv_list_match();
+    if (rxinfo->rpa_index >= 0) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
     }
+#endif
 
-    if (rc == 2) {
-        /* Scan request forbidden by filter policy */
-        return 0;
-    }
+    rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
 
-done:
-    return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) &&
-           ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_SCAN);
+    return 0;
 }
-#endif
 
 static bool
 ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf,
@@ -2290,23 +1505,11 @@ ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf,
 {
     struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
     struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_ll_aux_data *aux_data = rxinfo->user_data;
-    uint8_t phy_mode;
-#endif
     bool is_ext_adv = false;
+    int8_t rpa_index;
     uint16_t adi = 0;
     int rc;
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
-        if (ble_ll_scan_get_adi(aux_data, &adi) < 0) {
-            return false;
-        }
-        is_ext_adv = true;
-    }
-#endif
-
     /* Check if we already scanned this device successfully */
     if (ble_ll_scan_have_rxd_scan_rsp(addrd->adv_addr, addrd->adv_addr_type,
                                       is_ext_adv, adi)) {
@@ -2323,15 +1526,15 @@ ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf,
         }
     }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    phy_mode = ble_ll_phy_to_phy_mode(rxinfo->phy, BLE_HCI_LE_PHY_CODED_ANY);
-    if (ble_ll_sched_scan_req_over_aux_ptr(rxinfo->channel, phy_mode)) {
-        return false;
-    }
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    rpa_index = addrd->rpa_index;
+#else
+    rpa_index = -1;
 #endif
 
     /* Use original AdvA in scan request (Core 5.1, Vol 6, Part B, section 6.3) */
-    ble_ll_scan_req_pdu_prepare(scansm, addrd->adva, addrd->adva_type, addrd->rl);
+    ble_ll_scan_req_pdu_prepare(scansm, addrd->adva, addrd->adva_type,
+                                rpa_index);
 
     rc = ble_phy_tx(ble_ll_scan_req_tx_pdu_cb, scansm, BLE_PHY_TRANSITION_TX_RX);
     if (rc) {
@@ -2341,14 +1544,6 @@ ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf,
     scansm->scan_rsp_pending = 1;
     rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_REQ_TXD;
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (rxinfo->channel <  BLE_PHY_NUM_DATA_CHANS) {
-        /* Keep aux_data for expected scan response */
-        scansm->cur_aux_data = ble_ll_scan_aux_data_ref(aux_data);
-        STATS_INC(ble_ll_stats, aux_scan_req_tx);
-    }
-#endif
-
     return true;
 }
 
@@ -2384,48 +1579,34 @@ ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok)
         return 0;
     }
 
-    rxbuf = rxpdu->om_data;
-    pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    /*
-     * In case aux was expected, copy aux_data for LL to use. Make sure this was
-     * indeed an aux as otherwise there's no need to process it and just pass to
-     * LL immediately.
-     */
-    if (scansm->cur_aux_data) {
-        rxinfo->user_data = scansm->cur_aux_data;
-        scansm->cur_aux_data = NULL;
-        if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
-            ble_ll_state_set(BLE_LL_STATE_STANDBY);
-            return -1;
-        }
-    }
-#endif
-
     if (!crcok) {
         goto scan_rx_isr_ignore;
     }
 
-    /*
-     * Addresses will be always set in handlers, no need to initialize them. We
-     * only need to initialize rl which may not be always set, depending on how
-     * filtering goes.
-     */
-    addrd.rl = NULL;
+    rxbuf = rxpdu->om_data;
+    pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
 
     switch (pdu_type) {
     case BLE_ADV_PDU_TYPE_ADV_IND:
     case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
     case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND:
-    case BLE_ADV_PDU_TYPE_SCAN_RSP:
     case BLE_ADV_PDU_TYPE_ADV_SCAN_IND:
-        rc = ble_ll_scan_rx_isr_on_legacy(pdu_type, rxbuf, hdr, &addrd);
+        rc = ble_ll_scan_rx_isr_end_on_adv(pdu_type, rxbuf, hdr, &addrd);
+        break;
+    case BLE_ADV_PDU_TYPE_SCAN_RSP:
+        rc = ble_ll_scan_rx_isr_end_on_scan_rsp(pdu_type, rxbuf, hdr, &addrd);
         break;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
     case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
-        rc = ble_ll_scan_rx_isr_on_aux(pdu_type, rxbuf, hdr, &addrd);
-        break;
+        rc = ble_ll_scan_aux_rx_isr_end_on_ext(&g_ble_ll_scan_sm, rxpdu);
+        if (rc < 0) {
+            rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+        }
+        ble_ll_state_set(BLE_LL_STATE_STANDBY);
+        /* Return here, we do not want any further processing since it's all
+         * handled in scan_aux.
+         */
+        return -1;
 #endif
     default:
         /* This is not something we would like to process here */
@@ -2435,10 +1616,24 @@ ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok)
 
     if (rc == -1) {
         goto scan_rx_isr_ignore;
-    } else if (rc == 1) {
-        if (ble_ll_scan_send_scan_req(pdu_type, rxbuf, hdr, &addrd)) {
-            /* Keep PHY active and LL in scanning state */
-            return 0;
+    }
+
+    if (rc == 1) {
+        switch (scansm->scanp->scan_type) {
+        case BLE_SCAN_TYPE_ACTIVE:
+            if (ble_ll_scan_send_scan_req(pdu_type, rxbuf, hdr, &addrd)) {
+                /* Keep PHY active and LL in scanning state */
+                return 0;
+            }
+            break;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        case BLE_SCAN_TYPE_INITIATE:
+            if (ble_ll_conn_send_connect_req(rxpdu, &addrd, 0) == 0) {
+                hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CONNECT_IND_TXD;
+                return 0;
+            }
+            break;
+#endif
         }
     }
 
@@ -2474,372 +1669,64 @@ ble_ll_scan_chk_resume(void)
         OS_ENTER_CRITICAL(sr);
         if (scansm->restart_timer_needed) {
             scansm->restart_timer_needed = 0;
-            ble_ll_event_send(&scansm->scan_sched_ev);
-            STATS_INC(ble_ll_stats, scan_timer_restarted);
-            OS_EXIT_CRITICAL(sr);
-            return;
-        }
-
-        now = os_cputime_get32();
-        if (ble_ll_state_get() == BLE_LL_STATE_STANDBY &&
-            ble_ll_scan_is_inside_window(scansm->scanp, now)) {
-            /* Turn on the receiver and set state */
-            ble_ll_scan_start(scansm, NULL);
-        }
-        OS_EXIT_CRITICAL(sr);
-    }
-}
-
-/**
- * Scan timer callback; means that the scan window timeout has been reached
- * and we should perform the appropriate actions.
- *
- * Context: Interrupt (cputimer)
- *
- * @param arg Pointer to scan state machine.
- */
-void
-ble_ll_scan_timer_cb(void *arg)
-{
-    struct ble_ll_scan_sm *scansm;
-
-    scansm = (struct ble_ll_scan_sm *)arg;
-    ble_ll_event_send(&scansm->scan_sched_ev);
-}
-
-void
-ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm)
-{
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    ble_npl_event_set_arg(&scansm->scan_interrupted_ev, scansm->cur_aux_data);
-    scansm->cur_aux_data = NULL;
-#endif
-
-    ble_ll_event_send(&scansm->scan_interrupted_ev);
-}
-
-/**
- * Called when the wait for response timer expires while in the scanning
- * state.
- *
- * Context: Interrupt.
- */
-void
-ble_ll_scan_wfr_timer_exp(void)
-{
-    struct ble_ll_scan_sm *scansm;
-    uint8_t chan;
-    int phy;
-    int rc;
-#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
-    uint8_t phy_mode;
-#endif
-    uint32_t now;
-
-    scansm = &g_ble_ll_scan_sm;
-
-    /* Update backoff if we failed to receive scan response */
-    if (scansm->scan_rsp_pending) {
-        scansm->scan_rsp_pending = 0;
-        ble_ll_scan_req_backoff(scansm, 0);
-    }
-
-    if (scansm->cur_aux_data) {
-        /* We actually care about interrupted scan only for EXT ADV because only
-         * then we might consider to send truncated event to the host.
-         */
-        ble_ll_scan_interrupted(scansm);
-
-        /* Need to disable phy since we are going to move to BLE_LL_STATE_STANDBY
-         * or we will need to change channel to primary one
-         */
-        ble_phy_disable();
-
-        now = os_cputime_get32();
-        if (!ble_ll_scan_is_inside_window(scansm->scanp, now)) {
-            /* Outside the window scan */
-            ble_ll_state_set(BLE_LL_STATE_STANDBY);
-            return;
-        }
-
-        ble_ll_get_chan_to_scan(scansm, &chan, &phy);
-#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
-        phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY);
-        ble_phy_mode_set(phy_mode, phy_mode);
-#endif
-        rc = ble_phy_setchan(chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
-        BLE_LL_ASSERT(rc == 0);
-    }
-
-
-    ble_phy_restart_rx();
-}
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-/*
- * Send extended advertising report
- *
- * @return -1 on error (data truncated or other error)
- *          0 on success (data status is "completed")
- *          1 on success (data status is not "completed")
- */
-static int
-ble_ll_hci_send_ext_adv_report(uint8_t ptype, uint8_t *adva, uint8_t adva_type,
-                               uint8_t *inita, uint8_t inita_type,
-                               struct os_mbuf *om,
-                               struct ble_mbuf_hdr *hdr)
-{
-    struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data;
-    struct ble_hci_ev_le_subev_ext_adv_rpt *ev;
-    struct ext_adv_report *report;
-    struct ble_hci_ev *hci_ev;
-    struct ble_hci_ev *hci_ev_next;
-    int offset;
-    int datalen;
-    int rc;
-    bool need_event;
-    bool is_scannable_aux;
-    uint8_t max_data_len;
-
-    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
-        rc = -1;
-        goto done;
-    }
-
-    /*
-     * We keep one allocated event in aux_data to be able to truncate chain
-     * properly in case of error. If there is no event in aux_data it means this
-     * is the first event for this chain.
-     */
-    if (aux_data && aux_data->evt) {
-        hci_ev = aux_data->evt;
-        aux_data->evt = NULL;
-
-        hci_ev->length = sizeof(*ev) + sizeof(*report);
-    } else {
-        hci_ev = ble_ll_scan_get_ext_adv_report(NULL);
-        if (!hci_ev) {
-            rc = -1;
-            goto done;
-        }
-    }
-
-    ev = (void *) hci_ev->data;
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    /* If RPA has been used, make sure we use correct address types
-     * in the advertising report.
-     */
-    if (BLE_MBUF_HDR_RESOLVED(hdr)) {
-        adva_type += 2;
-    }
-    if (BLE_MBUF_HDR_TARGETA_RESOLVED(hdr)) {
-        inita_type += 2;
-    }
-#endif
-
-    datalen = ble_ll_scan_parse_ext_hdr(om, adva, adva_type, inita, inita_type,
-                                        hdr, ev->reports);
-    if (datalen < 0) {
-        rc = -1;
-
-        /* Need to send truncated event if we already sent some reports */
-        if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) {
-            BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED));
-            BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED));
-
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED;
-
-            report = ev->reports;
-            report->data_len = 0;
-            report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
-
-            ble_ll_hci_event_send(hci_ev);
-            goto done;
-        }
-
-        ble_hci_trans_buf_free((uint8_t *)hci_ev);
-        goto done;
-    }
-
-    is_scannable_aux = aux_data &&
-                       (aux_data->evt_type & BLE_HCI_ADV_SCAN_MASK) &&
-                       !(aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK);
-
-    max_data_len = BLE_LL_MAX_EVT_LEN - sizeof(*hci_ev) - sizeof(*ev) - sizeof(*report);
-    offset = 0;
-
-    do {
-        hci_ev_next = NULL;
-
-        ev = (void *) hci_ev->data;
-        report = ev->reports;
-
-        report->data_len = min(max_data_len, datalen - offset);
-
-        /* adjust event length */
-        hci_ev->length += report->data_len;
-        report->rssi = hdr->rxinfo.rssi;
-
-        os_mbuf_copydata(om, offset, report->data_len, report->data);
-        offset += report->data_len;
-
-        /*
-         * We need another event if either there are still some data left to
-         * send in current PDU or scan is not completed. There are two exceptions
-         * though:
-         * - we sent all data from this PDU and there is scan error set already;
-         *   it may be set before entering current function due to failed aux
-         *   scan scheduling
-         * - this is a scannable event which is not a scan response
-         */
-        need_event = ((offset < datalen) || (aux_data && !(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_COMPLETE))) &&
-                     !((offset == datalen) && (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR)) &&
-                     !is_scannable_aux;
-
-        if (need_event) {
-            /*
-             * We will need another event so let's try to allocate one now. If
-             * we cannot do this, need to mark event as truncated.
-             */
-            hci_ev_next = ble_ll_scan_get_ext_adv_report(report);
-
-            if (hci_ev_next) {
-                report->evt_type |= BLE_HCI_ADV_DATA_STATUS_INCOMPLETE;
-                rc = 1;
-            } else {
-                report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
-                rc = -1;
-            }
-        } else if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR)) {
-            report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
-            rc = -1;
-        } else {
-            rc = 0;
+            ble_ll_event_send(&scansm->scan_sched_ev);
+            STATS_INC(ble_ll_stats, scan_timer_restarted);
+            OS_EXIT_CRITICAL(sr);
+            return;
         }
 
-        if ((rc == -1) && aux_data) {
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-
-            if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) {
-                ble_hci_trans_buf_free((uint8_t *)hci_ev);
-                goto  done;
-            }
-
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED;
-        } else if (!is_scannable_aux) {
-            /*
-             * We do not set 'sent' flags for scannable AUX since we only care
-             * about scan response that will come next.
-             */
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY;
-            if (rc == 0) {
-                aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED;
-            }
+        now = os_cputime_get32();
+        if (ble_ll_state_get() == BLE_LL_STATE_STANDBY &&
+            ble_ll_scan_is_inside_window(scansm->scanp, now)) {
+            /* Turn on the receiver and set state */
+            ble_ll_scan_start(scansm);
         }
-
-        ble_ll_hci_event_send(hci_ev);
-
-        hci_ev = hci_ev_next;
-    } while ((offset < datalen) && hci_ev);
-
-    BLE_LL_ASSERT(offset <= datalen);
-
-    if (aux_data) {
-        /* Store any event left for later use */
-        aux_data->evt = hci_ev;
-    } else {
-        /* If it is empty beacon, evt shall be NULL */
-        BLE_LL_ASSERT(!hci_ev);
+        OS_EXIT_CRITICAL(sr);
     }
+}
 
-done:
-    if (!aux_data) {
-        return rc;
-    }
+/**
+ * Scan timer callback; means that the scan window timeout has been reached
+ * and we should perform the appropriate actions.
+ *
+ * Context: Interrupt (cputimer)
+ *
+ * @param arg Pointer to scan state machine.
+ */
+void
+ble_ll_scan_timer_cb(void *arg)
+{
+    struct ble_ll_scan_sm *scansm;
 
-    if (rc == 0) {
-        if (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) {
-            /* Complete scan response can be added to duplicates list */
-            ble_ll_scan_add_scan_rsp_adv(aux_data->adva, aux_data->adva_type,
-                                         1, aux_data->adi);
-        } else if (is_scannable_aux) {
-            /*
-             * Scannable AUX is marked as incomplete because we do not want to
-             * add this to duplicates list now, this should happen only after
-             * we receive complete scan response. The drawback here is that we
-             * will keep receiving reports for scannable PDUs until complete
-             * scan response is received.
-             *
-             * XXX ^^ extend duplicates list to fix
-             */
-            rc = 1;
-        }
-    } else if (rc < 0) {
-        aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-    }
+    scansm = (struct ble_ll_scan_sm *)arg;
+    ble_ll_event_send(&scansm->scan_sched_ev);
+}
 
-    return rc;
+void
+ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm)
+{
+    ble_ll_event_send(&scansm->scan_interrupted_ev);
 }
-#endif
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-static void
-ble_ll_scan_check_periodic_sync(const struct os_mbuf *om, struct ble_mbuf_hdr *rxhdr,
-                          uint8_t *adva, uint8_t adva_type, int rpa_index)
+/**
+ * Called when the wait for response timer expires while in the scanning
+ * state.
+ *
+ * Context: Interrupt.
+ */
+void
+ble_ll_scan_wfr_timer_exp(void)
 {
-    uint8_t pdu_len;
-    uint8_t ext_hdr_len;
-    uint8_t ext_hdr_flags;
-    uint8_t *ext_hdr;
-    uint8_t *rxbuf = om->om_data;
-    uint8_t sid;
-    int i;
+    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
 
-    pdu_len = rxbuf[1];
-    if (pdu_len == 0) {
-        return;
+    /* Update backoff if we failed to receive scan response */
+    if (scansm->scan_rsp_pending) {
+        scansm->scan_rsp_pending = 0;
+        ble_ll_scan_req_backoff(scansm, 0);
     }
 
-    ext_hdr_len = rxbuf[2] & 0x3F;
-
-    if (ext_hdr_len) {
-        ext_hdr_flags = rxbuf[3];
-        ext_hdr = &rxbuf[4];
-        i = 0;
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
-            i += BLE_LL_EXT_ADV_ADVA_SIZE;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
-            i += BLE_LL_EXT_ADV_TARGETA_SIZE;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
-            i += 1;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
-            sid = (get_le16(ext_hdr + i) >> 12);
-            i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
-        } else {
-            /* ADI is mandatory */
-            return;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
-            i += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
-        }
-
-        if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
-            ble_ll_sync_info_event(adva, adva_type, rpa_index, sid, rxhdr,
-                                   ext_hdr + i);
-        }
-    }
+    ble_phy_restart_rx();
 }
-#endif
 
 static inline void
 ble_ll_scan_dup_move_to_head(struct ble_ll_scan_dup_entry *e)
@@ -2906,20 +1793,17 @@ ble_ll_scan_dup_check_legacy(uint8_t addr_type, uint8_t *addr, uint8_t pdu_type)
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static int
-ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr,
-                          struct ble_ll_aux_data *aux_data)
+int
+ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr, bool has_aux,
+                          uint16_t adi)
 {
     struct ble_ll_scan_dup_entry *e;
-    bool has_aux;
     bool is_anon;
-    uint16_t adi;
     uint8_t type;
     int rc;
 
-    has_aux = aux_data != NULL;
     is_anon = addr == NULL;
-    adi = has_aux ? aux_data->adi : 0;
+    adi = has_aux ? adi : 0;
 
     type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi);
 
@@ -2958,19 +1842,16 @@ ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr,
     return rc;
 }
 
-static int
-ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr,
-                           struct ble_ll_aux_data *aux_data)
+int
+ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr, bool has_aux,
+                           uint16_t adi)
 {
     struct ble_ll_scan_dup_entry *e;
-    bool has_aux;
     bool is_anon;
-    uint16_t adi;
     uint8_t type;
 
-    has_aux = aux_data != NULL;
     is_anon = addr == NULL;
-    adi = has_aux ? aux_data->adi : 0;
+    adi = has_aux ? adi : 0;
 
     type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi);
 
@@ -3002,15 +1883,24 @@ ble_ll_scan_rx_pkt_in_restore_addr_data(struct ble_mbuf_hdr *hdr,
     addrd->adv_addr_type = addrd->adva_type;
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    if (rxinfo->rpa_index >= 0) {
+    addrd->rpa_index = rxinfo->rpa_index;
+
+    if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_RESOLVED) {
+        BLE_LL_ASSERT(rxinfo->rpa_index >= 0);
         rl = &g_ble_ll_resolv_list[rxinfo->rpa_index];
         addrd->adv_addr = rl->rl_identity_addr;
         addrd->adv_addr_type = rl->rl_addr_type;
-        addrd->rl = rl;
+        addrd->adva_resolved = 1;
+    } else {
+        addrd->adva_resolved = 0;
     }
+
     if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) {
         addrd->targeta = ble_ll_get_our_devaddr(scansm->own_addr_type & 1);
         addrd->targeta_type = scansm->own_addr_type & 1;
+        addrd->targeta_resolved = 1;
+    } else {
+        addrd->targeta_resolved = 0;
     }
 #endif
 }
@@ -3052,143 +1942,6 @@ ble_ll_scan_rx_pkt_in_on_legacy(uint8_t pdu_type, struct os_mbuf *om,
     }
 }
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-static void
-ble_ll_scan_rx_pkt_in_on_aux(uint8_t pdu_type, struct os_mbuf *om,
-                             struct ble_mbuf_hdr *hdr,
-                             struct ble_ll_scan_addr_data *addrd)
-{
-    struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-    uint8_t *rxbuf = om->om_data;
-#endif
-    struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
-    struct ble_ll_aux_data *aux_data = rxinfo->user_data;
-    bool send_hci_report;
-    int rc;
-
-    if (aux_data) {
-        aux_data->flags_ll |= aux_data->flags_isr;
-    }
-
-    /*
-     * For every new extended advertising event scanned, rx_isr_end will either
-     * allocate new aux_data or set 'invalid' flag. This means if no 'invalid'
-     * flag is set, aux_data is always valid.
-     */
-
-    /* Drop on scan error or if we received not what we expected to receive */
-    if (!BLE_MBUF_HDR_CRC_OK(hdr) ||
-        BLE_MBUF_HDR_IGNORED(hdr) ||
-        BLE_MBUF_HDR_AUX_INVALID(hdr) ||
-        (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) ||
-        (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) ||
-        !scansm->scan_enabled) {
-        if (aux_data) {
-            ble_ll_scan_end_adv_evt(aux_data);
-            ble_ll_scan_aux_data_unref(aux_data);
-            rxinfo->user_data = NULL;
-        }
-        return;
-    }
-
-    BLE_LL_ASSERT(aux_data);
-
-    if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) {
-        addrd->adva = aux_data->adva;
-        addrd->adva_type = aux_data->adva_type;
-    } else {
-        addrd->adva = NULL;
-        addrd->adva_type = 0;
-    }
-    if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) {
-        addrd->targeta = aux_data->targeta;
-        addrd->targeta_type = aux_data->targeta_type;
-    } else {
-        addrd->targeta = NULL;
-        addrd->targeta_type = 0;
-    }
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-    /*
-     * Periodic scan uses own filter list so we need to let it do own filtering
-     * regardless of scanner filtering. Just make sure we already have AdvA.
-     */
-    if (ble_ll_sync_enabled() &&
-        ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_NON_CONN) && addrd->adva &&
-        !(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED)) {
-        ble_ll_scan_check_periodic_sync(om, hdr, addrd->adva, addrd->adva_type,
-                                        rxinfo->rpa_index);
-    }
-#endif
-
-    /* Ignore if device was not matched by either whitelist or scan policy */
-    if (!BLE_MBUF_HDR_DEVMATCH(hdr)) {
-        goto scan_continue;
-    }
-
-    ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, addrd);
-
-    /*
-     * If there is AuxPtr in this PDU, we should first try to schedule scan for
-     * subsequent aux.
-     */
-    if (BLE_MBUF_HDR_WAIT_AUX(hdr)) {
-        if (ble_ll_sched_aux_scan(hdr, scansm, aux_data)) {
-            rxinfo->flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-
-            /* Silently ignore if no HCI event was sent to host */
-            if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) {
-                goto scan_continue;
-            }
-        }
-
-        /* Ignore if this was just ADV_EXT_IND with AuxPtr, will process aux */
-        if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED)) {
-            goto scan_continue;
-        }
-
-        STATS_INC(ble_ll_stats, aux_chain_cnt);
-    }
-
-    send_hci_report = !scansm->scan_filt_dups ||
-                      !ble_ll_scan_dup_check_ext(addrd->adv_addr_type,
-                                                 addrd->adv_addr, aux_data);
-    if (send_hci_report) {
-        rc = ble_ll_hci_send_ext_adv_report(pdu_type,
-                                            addrd->adv_addr, addrd->adv_addr_type,
-                                            addrd->targeta, addrd->targeta_type,
-                                            om, hdr);
-        if ((rc < 0) && BLE_MBUF_HDR_WAIT_AUX(hdr)) {
-            /* Data were truncated so stop scanning for subsequent auxes */
-            aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR;
-
-            if (ble_ll_sched_rmv_elem(&aux_data->sch) == 0) {
-                ble_ll_scan_aux_data_unref(aux_data->sch.cb_arg);
-                aux_data->sch.cb_arg = NULL;
-            }
-        } else if ((rc == 0) && scansm->scan_filt_dups) {
-            /* Complete data were send so we can update scan_dup list */
-            ble_ll_scan_dup_update_ext(addrd->adv_addr_type, addrd->adv_addr,
-                                       aux_data);
-        }
-    }
-
-    if (BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) {
-        /*
-         * For now assume success if we just received direct scan response,
-         * don't care about complete aux chain.
-         */
-        ble_ll_scan_req_backoff(scansm, 1);
-    }
-
-scan_continue:
-    ble_ll_scan_aux_data_unref(rxinfo->user_data);
-    rxinfo->user_data = NULL;
-}
-#endif
-
 /**
  * Process a received PDU while in the scanning state.
  *
@@ -3200,32 +1953,71 @@ ble_ll_scan_rx_pkt_in_on_aux(uint8_t pdu_type, struct os_mbuf *om,
 void
 ble_ll_scan_rx_pkt_in(uint8_t ptype, struct os_mbuf *om, struct ble_mbuf_hdr *hdr)
 {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo;
-    struct ble_ll_aux_data *aux_data = rxinfo->user_data;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    struct ble_mbuf_hdr_rxinfo *rxinfo;
+    uint8_t *targeta;
 #endif
+    struct ble_ll_scan_sm *scansm;
     struct ble_ll_scan_addr_data addrd;
+    uint8_t max_pdu_type;
+
+    scansm = &g_ble_ll_scan_sm;
+
+    /* Ignore PDUs we do not expect here */
+    max_pdu_type = BLE_ADV_PDU_TYPE_ADV_SCAN_IND;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    if (scansm->ext_scanning) {
+        /* Note: We do not expect AUX_CONNECT_RSP here */
+        max_pdu_type = BLE_ADV_PDU_TYPE_ADV_EXT_IND;
+    }
+#endif
+    if (ptype > max_pdu_type) {
+        ble_ll_scan_chk_resume();
+        return;
+    }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    if (aux_data || (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND)) {
-        ble_ll_scan_rx_pkt_in_on_aux(ptype, om, hdr, &addrd);
+    if (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
+        ble_ll_scan_aux_pkt_in_on_ext(om, hdr);
         ble_ll_scan_chk_resume();
         return;
     }
 #endif
 
-    ble_ll_scan_rx_pkt_in_on_legacy(ptype, om, hdr, &addrd);
+    switch (scansm->scanp->scan_type) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_SCAN_TYPE_INITIATE:
+        rxinfo = &hdr->rxinfo;
+        if (rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_IND_TXD) {
+            /* We need to keep original TargetA in case it was resolved, so rl
+             * can be updated properly.
+             */
+            ble_ll_scan_get_addr_data_from_legacy(ptype, om->om_data, &addrd);
+            targeta = addrd.targeta;
+            ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, &addrd);
+
+            ble_ll_scan_sm_stop(0);
+            ble_ll_conn_created_on_legacy(om, &addrd, targeta);
+            return;
+        }
+        break;
+#endif
+    default:
+        ble_ll_scan_rx_pkt_in_on_legacy(ptype, om, hdr, &addrd);
+        break;
+    }
+
     ble_ll_scan_chk_resume();
 }
 
 int
-ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len)
+ble_ll_scan_hci_set_params(const uint8_t *cmdbuf, uint8_t len)
 {
     const struct ble_hci_le_set_scan_params_cp *cmd = (const void *)cmdbuf;
     uint16_t scan_itvl;
     uint16_t scan_window;
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
+    struct ble_ll_scan_phy *scanp;
 
     if (len != sizeof(*cmd)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
@@ -3267,16 +2059,18 @@ ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len)
     }
 
     /* Store scan parameters */
-    scanp = &g_ble_ll_scan_params[PHY_UNCODED];
+    g_ble_ll_scan_params.own_addr_type = cmd->own_addr_type;
+    g_ble_ll_scan_params.scan_filt_policy = cmd->filter_policy;
+
+    scanp = &g_ble_ll_scan_params.scan_phys[PHY_UNCODED];
     scanp->configured = 1;
     scanp->scan_type = cmd->scan_type;
     scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(scan_itvl);
     scanp->timing.window = ble_ll_scan_time_hci_to_ticks(scan_window);
-    scanp->scan_filt_policy = cmd->filter_policy;
-    scanp->own_addr_type = cmd->own_addr_type;
 
 #if (BLE_LL_SCAN_PHY_NUMBER == 2)
-    g_ble_ll_scan_params[PHY_CODED].configured = 0;
+    scanp = &g_ble_ll_scan_params.scan_phys[PHY_CODED];
+    scanp->configured = 0;
 #endif
 
     return 0;
@@ -3284,7 +2078,7 @@ ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len)
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
 static int
-ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window)
+ble_ll_scan_check_phy_params(uint8_t type, uint16_t itvl, uint16_t window)
 {
     /* Check scan type */
     if ((type != BLE_HCI_SCAN_TYPE_PASSIVE) &&
@@ -3294,9 +2088,9 @@ ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window)
 
     /* Check interval and window */
     if ((itvl < BLE_HCI_SCAN_ITVL_MIN) ||
-        (itvl > BLE_HCI_SCAN_ITVL_MAX) ||
+        (itvl > BLE_HCI_SCAN_ITVL_MAX_EXT) ||
         (window < BLE_HCI_SCAN_WINDOW_MIN) ||
-        (window > BLE_HCI_SCAN_WINDOW_MAX) ||
+        (window > BLE_HCI_SCAN_WINDOW_MAX_EXT) ||
         (itvl < window)) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
@@ -3305,14 +2099,16 @@ ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window)
 }
 
 int
-ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
+ble_ll_scan_hci_set_ext_params(const uint8_t *cmdbuf, uint8_t len)
 {
     const struct ble_hci_le_set_ext_scan_params_cp *cmd = (const void *) cmdbuf;
     const struct scan_params *params = cmd->scans;
 
-    struct ble_ll_scan_params new_params[BLE_LL_SCAN_PHY_NUMBER] = { };
-    struct ble_ll_scan_params *uncoded = &new_params[PHY_UNCODED];
-    struct ble_ll_scan_params *coded = &new_params[PHY_CODED];
+    struct ble_ll_scan_phy new_params[BLE_LL_SCAN_PHY_NUMBER] = { };
+    struct ble_ll_scan_phy *uncoded = &new_params[PHY_UNCODED];
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+    struct ble_ll_scan_phy *coded = &new_params[PHY_CODED];
+#endif
     uint16_t interval;
     uint16_t window;
     int rc;
@@ -3333,17 +2129,11 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
-    coded->own_addr_type = cmd->own_addr_type;
-    uncoded->own_addr_type = cmd->own_addr_type;
-
     /* Check scanner filter policy */
     if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) {
         return BLE_ERR_INV_HCI_CMD_PARMS;
     }
 
-    coded->scan_filt_policy = cmd->filter_policy;
-    uncoded->scan_filt_policy = cmd->filter_policy;
-
     /* Check if no reserved bits in PHYS are set and that at least one valid PHY
      * is set.
      */
@@ -3360,7 +2150,7 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
         interval = le16toh(params->itvl);
         window = le16toh(params->window);
 
-        rc = ble_ll_check_scan_params(params->type, interval, window);
+        rc = ble_ll_scan_check_phy_params(params->type, interval, window);
         if (rc) {
             return rc;
         }
@@ -3384,7 +2174,7 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
         interval = le16toh(params->itvl);
         window = le16toh(params->window);
 
-        rc = ble_ll_check_scan_params(params->type, interval, window);
+        rc = ble_ll_scan_check_phy_params(params->type, interval, window);
         if (rc) {
             return rc;
         }
@@ -3396,7 +2186,6 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
         /* That means user wants to use this PHY for scanning */
         coded->configured = 1;
     }
-#endif
 
     /* if any of PHYs is configured for continuous scan we alter interval to
      * fit other PHY
@@ -3407,11 +2196,15 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
         }
 
         if (uncoded->timing.interval == uncoded->timing.window) {
-            uncoded->timing.window += coded->timing.window;
+            uncoded->timing.interval += coded->timing.window;
         }
     }
+#endif
 
-    memcpy(g_ble_ll_scan_params, new_params, sizeof(new_params));
+    g_ble_ll_scan_params.own_addr_type = cmd->own_addr_type;
+    g_ble_ll_scan_params.scan_filt_policy = cmd->filter_policy;
+
+    memcpy(g_ble_ll_scan_params.scan_phys, new_params, sizeof(new_params));
 
     return 0;
 }
@@ -3422,30 +2215,24 @@ ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len)
 static void
 ble_ll_scan_duration_period_timers_restart(struct ble_ll_scan_sm *scansm)
 {
-    uint32_t now;
-
-    now = os_cputime_get32();
-
-    os_cputime_timer_stop(&scansm->duration_timer);
-    os_cputime_timer_stop(&scansm->period_timer);
+    ble_npl_callout_stop(&scansm->duration_timer);
+    ble_npl_callout_stop(&scansm->period_timer);
 
     if (scansm->duration_ticks) {
-        os_cputime_timer_start(&scansm->duration_timer,
-                                                now + scansm->duration_ticks);
+        ble_npl_callout_reset(&scansm->duration_timer,
+                              scansm->duration_ticks);
 
         if (scansm->period_ticks) {
-            os_cputime_timer_start(&scansm->period_timer,
-                                                    now + scansm->period_ticks);
+            ble_npl_callout_reset(&scansm->period_timer,
+                                  scansm->period_ticks);
         }
     }
 }
 
 static void
-ble_ll_scan_duration_timer_cb(void *arg)
+ble_ll_scan_duration_timer_cb(struct ble_npl_event *ev)
 {
-    struct ble_ll_scan_sm *scansm;
-
-    scansm = (struct ble_ll_scan_sm *)arg;
+    struct ble_ll_scan_sm *scansm = ble_npl_event_get_arg(ev);
 
     ble_ll_scan_sm_stop(2);
 
@@ -3456,9 +2243,9 @@ ble_ll_scan_duration_timer_cb(void *arg)
 }
 
 static void
-ble_ll_scan_period_timer_cb(void *arg)
+ble_ll_scan_period_timer_cb(struct ble_npl_event *ev)
 {
-    struct ble_ll_scan_sm *scansm = arg;
+    struct ble_ll_scan_sm *scansm = ble_npl_event_get_arg(ev);
 
     ble_ll_scan_sm_start(scansm);
 
@@ -3484,12 +2271,12 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
 {
     int rc;
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
-    struct ble_ll_scan_params *scanp_phy;
+    struct ble_ll_scan_phy *scanp;
+    struct ble_ll_scan_phy *scanp_phy;
     int i;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    uint32_t period_ticks = 0;
-    uint32_t dur_ticks = 0;
+    ble_npl_time_t period_ticks = 0;
+    ble_npl_time_t dur_ticks = 0;
 #endif
 
     /* Check for valid parameters */
@@ -3509,16 +2296,13 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
             period = 0;
         }
 
-        /* period is in 1.28 sec units
-         * TODO support full range, would require os_cputime milliseconds API
-         */
-        if (period > 3355) {
+        /* period is in 1.28 sec units */
+        if (ble_npl_time_ms_to_ticks(period * 1280, &period_ticks)) {
             return BLE_ERR_INV_HCI_CMD_PARMS;
         }
-        period_ticks = os_cputime_usecs_to_ticks(period * 1280000);
 
         /* duration is in 10ms units */
-        dur_ticks = os_cputime_usecs_to_ticks(dur * 10000);
+        dur_ticks = ble_npl_time_ms_to_ticks32(dur * 10);
 
         if (dur_ticks && period_ticks && (dur_ticks >= period_ticks)) {
             return BLE_ERR_INV_HCI_CMD_PARMS;
@@ -3532,8 +2316,8 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
             ble_ll_scan_sm_stop(1);
         }
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        os_cputime_timer_stop(&scansm->duration_timer);
-        os_cputime_timer_stop(&scansm->period_timer);
+        ble_npl_callout_stop(&scansm->duration_timer);
+        ble_npl_callout_stop(&scansm->period_timer);
 #endif
 
         return BLE_ERR_SUCCESS;
@@ -3542,13 +2326,15 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
     /* if already enable we just need to update parameters */
     if (scansm->scan_enabled) {
         /* Controller does not allow initiating and scanning.*/
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
-            scanp_phy = &scansm->scanp_phys[i];
+            scanp_phy = &scansm->scan_phys[i];
             if (scanp_phy->configured &&
                                 scanp_phy->scan_type == BLE_SCAN_TYPE_INITIATE) {
                 return BLE_ERR_CMD_DISALLOWED;
             }
         }
+#endif
 
 #if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS)
         /* update filter policy */
@@ -3575,9 +2361,12 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
     scansm->scanp = NULL;
     scansm->scanp_next = NULL;
 
+    scansm->own_addr_type = g_ble_ll_scan_params.own_addr_type;
+    scansm->scan_filt_policy = g_ble_ll_scan_params.scan_filt_policy;
+
     for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
-        scanp_phy = &scansm->scanp_phys[i];
-        scanp = &g_ble_ll_scan_params[i];
+        scanp_phy = &scansm->scan_phys[i];
+        scanp = &g_ble_ll_scan_params.scan_phys[i];
 
         if (!scanp->configured) {
             continue;
@@ -3586,15 +2375,9 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
         scanp_phy->configured = scanp->configured;
         scanp_phy->scan_type = scanp->scan_type;
         scanp_phy->timing = scanp->timing;
-        scanp_phy->scan_filt_policy = scanp->scan_filt_policy;
-        scanp_phy->own_addr_type = scanp->own_addr_type;
 
         if (!scansm->scanp) {
             scansm->scanp = scanp_phy;
-            /* Take own_addr_type from the first configured PHY.
-             * Note: All configured PHYs shall have the same own_addr_type
-             */
-            scansm->own_addr_type = scanp_phy->own_addr_type;
         } else {
             scansm->scanp_next = scanp_phy;
         }
@@ -3606,8 +2389,9 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
      * Parameters defaults.
      */
     if (!scansm->scanp) {
-        scansm->scanp = &scansm->scanp_phys[PHY_UNCODED];
+        scansm->scanp = &scansm->scan_phys[PHY_UNCODED];
         scansm->own_addr_type = BLE_ADDR_PUBLIC;
+        scansm->scan_filt_policy = BLE_HCI_SCAN_FILT_NO_WL;
 
         scanp_phy = scansm->scanp;
         scanp_phy->configured = 1;
@@ -3616,8 +2400,6 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
                         ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF);
         scanp_phy->timing.window =
                         ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF);
-        scanp_phy->scan_filt_policy = BLE_HCI_SCAN_FILT_NO_WL;
-        scanp_phy->own_addr_type = BLE_ADDR_PUBLIC;
     }
 
     rc = ble_ll_scan_sm_start(scansm);
@@ -3633,7 +2415,7 @@ ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period,
     return rc;
 }
 
-int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len)
+int ble_ll_scan_hci_set_enable(const uint8_t *cmdbuf, uint8_t len)
 {
     const struct ble_hci_le_set_scan_enable_cp *cmd = (const void *) cmdbuf;
 
@@ -3646,7 +2428,7 @@ int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len)
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-int ble_ll_hci_ext_scan_set_enable(const uint8_t *cmdbuf, uint8_t len)
+int ble_ll_scan_hci_set_ext_enable(const uint8_t *cmdbuf, uint8_t len)
 {
     const struct ble_hci_le_set_ext_scan_enable_cp *cmd = (const void *) cmdbuf;
 
@@ -3671,11 +2453,9 @@ ble_ll_scan_can_chg_whitelist(void)
 {
     int rc;
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
 
     scansm = &g_ble_ll_scan_sm;
-    scanp = scansm->scanp;
-    if (scansm->scan_enabled && (scanp->scan_filt_policy & 1)) {
+    if (scansm->scan_enabled && (scansm->scan_filt_policy & 1)) {
         rc = 0;
     } else {
         rc = 1;
@@ -3684,83 +2464,61 @@ ble_ll_scan_can_chg_whitelist(void)
     return rc;
 }
 
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 int
-ble_ll_scan_initiator_start(struct hci_create_conn *hcc,
-                            struct ble_ll_scan_sm **sm)
+ble_ll_scan_initiator_start(struct ble_ll_conn_sm *connsm, uint8_t ext,
+                            struct ble_ll_conn_create_scan *cc_scan)
 {
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
-    int rc;
-
-    scansm = &g_ble_ll_scan_sm;
-    scansm->own_addr_type = hcc->own_addr_type;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    scansm->ext_scanning = 0;
+    struct ble_ll_scan_phy *scanp_uncoded;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+    struct ble_ll_scan_phy *scanp_coded;
 #endif
-    scansm->scanp = &scansm->scanp_phys[PHY_UNCODED];
-    scansm->scanp_next = NULL;
-
-    scanp = scansm->scanp;
-    scanp->scan_filt_policy = hcc->filter_policy;
-    scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(hcc->scan_itvl);
-    scanp->timing.window = ble_ll_scan_time_hci_to_ticks(hcc->scan_window);
-    scanp->scan_type = BLE_SCAN_TYPE_INITIATE;
-
-    rc = ble_ll_scan_sm_start(scansm);
-    if (sm == NULL) {
-        return rc;
-    }
-
-    if (rc == BLE_ERR_SUCCESS) {
-        *sm = scansm;
-    } else {
-        *sm = NULL;
-    }
-
-    return rc;
-}
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-int
-ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc,
-                                struct ble_ll_scan_sm **sm)
-{
-    struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp_uncoded;
-    struct ble_ll_scan_params *scanp_coded;
-    struct hci_ext_conn_params *params;
+    uint8_t init_phy_mask;
     int rc;
 
     scansm = &g_ble_ll_scan_sm;
-    scansm->own_addr_type = hcc->own_addr_type;
+    scansm->own_addr_type = cc_scan->own_addr_type;
+    scansm->scan_filt_policy = cc_scan->filter_policy;
     scansm->scanp = NULL;
     scansm->scanp_next = NULL;
-    scansm->ext_scanning = 1;
-
-    if (hcc->init_phy_mask & BLE_PHY_MASK_1M) {
-        params = &hcc->params[0];
-        scanp_uncoded = &scansm->scanp_phys[PHY_UNCODED];
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    scansm->ext_scanning = ext;
+    init_phy_mask = cc_scan->init_phy_mask;
+#else
+    init_phy_mask = BLE_PHY_MASK_1M;
+#endif
+    scansm->connsm = connsm;
 
-        scanp_uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl);
-        scanp_uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window);
+    scanp_uncoded = &scansm->scan_phys[PHY_UNCODED];
+    if (init_phy_mask & BLE_PHY_MASK_1M) {
+        scanp_uncoded->configured = 1;
+        scanp_uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(
+                                cc_scan->scan_params[PHY_UNCODED].itvl);
+        scanp_uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(
+                                cc_scan->scan_params[PHY_UNCODED].window);
         scanp_uncoded->scan_type = BLE_SCAN_TYPE_INITIATE;
-        scanp_uncoded->scan_filt_policy = hcc->filter_policy;
         scansm->scanp = scanp_uncoded;
+    } else {
+        scanp_uncoded->configured = 0;
     }
 
-    if (hcc->init_phy_mask & BLE_PHY_MASK_CODED) {
-        params = &hcc->params[2];
-        scanp_coded = &scansm->scanp_phys[PHY_CODED];
-
-        scanp_coded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl);
-        scanp_coded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+    scanp_coded = &scansm->scan_phys[PHY_CODED];
+    if (init_phy_mask & BLE_PHY_MASK_CODED) {
+        scanp_coded->configured = 1;
+        scanp_coded->timing.interval = ble_ll_scan_time_hci_to_ticks(
+                                cc_scan->scan_params[PHY_CODED].itvl);
+        scanp_coded->timing.window = ble_ll_scan_time_hci_to_ticks(
+                                cc_scan->scan_params[PHY_CODED].window);
         scanp_coded->scan_type = BLE_SCAN_TYPE_INITIATE;
-        scanp_coded->scan_filt_policy = hcc->filter_policy;
         if (scansm->scanp) {
             scansm->scanp_next = scanp_coded;
         } else {
             scansm->scanp = scanp_coded;
         }
+    } else {
+        scanp_coded->configured = 0;
     }
 
     /* if any of PHYs is configured for continuous scan we alter interval to
@@ -3776,16 +2534,11 @@ ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc,
             scanp_uncoded->timing.interval += scanp_coded->timing.window;
         }
     }
+#endif
 
     rc = ble_ll_scan_sm_start(scansm);
-    if (sm == NULL) {
-        return rc;
-    }
-
-    if (rc == BLE_ERR_SUCCESS) {
-        *sm = scansm;
-    } else {
-        *sm = NULL;
+    if (rc == 0) {
+        g_ble_ll_conn_create_sm.connsm = connsm;
     }
 
     return rc;
@@ -3853,18 +2606,11 @@ ble_ll_scan_get_pdu_data(void)
     return &g_ble_ll_scan_sm.pdu_data;
 }
 
-/* Returns true if whitelist is enabled for scanning */
-int
-ble_ll_scan_whitelist_enabled(void)
-{
-    return g_ble_ll_scan_sm.scanp->scan_filt_policy & 1;
-}
-
 static void
 ble_ll_scan_common_init(void)
 {
     struct ble_ll_scan_sm *scansm;
-    struct ble_ll_scan_params *scanp;
+    struct ble_ll_scan_phy *scanp;
     int i;
 
     /* Clear state machine in case re-initialized */
@@ -3872,23 +2618,23 @@ ble_ll_scan_common_init(void)
     memset(scansm, 0, sizeof(struct ble_ll_scan_sm));
 
     /* Clear scan parameters in case re-initialized */
-    memset(g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params));
+    memset(&g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params));
 
     /* Initialize scanning window end event */
     ble_npl_event_init(&scansm->scan_sched_ev, ble_ll_scan_event_proc, scansm);
 
     for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
         /* Set all non-zero default parameters */
-        scanp = &g_ble_ll_scan_params[i];
+        scanp = &g_ble_ll_scan_params.scan_phys[i];
         scanp->timing.interval =
                         ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF);
         scanp->timing.window =
                         ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF);
     }
 
-    scansm->scanp_phys[PHY_UNCODED].phy = BLE_PHY_1M;
+    scansm->scan_phys[PHY_UNCODED].phy = BLE_PHY_1M;
 #if (BLE_LL_SCAN_PHY_NUMBER == 2)
-    scansm->scanp_phys[PHY_CODED].phy = BLE_PHY_CODED;
+    scansm->scan_phys[PHY_CODED].phy = BLE_PHY_CODED;
 #endif
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
@@ -3901,10 +2647,10 @@ ble_ll_scan_common_init(void)
 
     /* Initialize extended scan timers */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    os_cputime_timer_init(&scansm->duration_timer,
+    ble_npl_callout_init(&scansm->duration_timer, &g_ble_ll_data.ll_evq,
                                         ble_ll_scan_duration_timer_cb, scansm);
-    os_cputime_timer_init(&scansm->period_timer, ble_ll_scan_period_timer_cb,
-                                                                        scansm);
+    ble_npl_callout_init(&scansm->period_timer, &g_ble_ll_data.ll_evq,
+                                        ble_ll_scan_period_timer_cb, scansm);
 #endif
 
     ble_npl_event_init(&scansm->scan_interrupted_ev, ble_ll_scan_interrupted_event_cb, NULL);
@@ -3930,8 +2676,8 @@ ble_ll_scan_reset(void)
 
     /* stop extended scan timers */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    os_cputime_timer_stop(&scansm->duration_timer);
-    os_cputime_timer_stop(&scansm->period_timer);
+    ble_npl_callout_stop(&scansm->duration_timer);
+    ble_npl_callout_stop(&scansm->period_timer);
 #endif
 
     /* Reset duplicate advertisers and those from which we rxd a response */
@@ -3941,13 +2687,11 @@ ble_ll_scan_reset(void)
     os_mempool_clear(&g_scan_dup_pool);
     TAILQ_INIT(&g_scan_dup_list);
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    /* clear memory pool for AUX scan results */
-    os_mempool_clear(&ext_scan_aux_pool);
-#endif
-
     /* Call the common init function again */
     ble_ll_scan_common_init();
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    ble_ll_scan_aux_init();
+#endif
 }
 
 /**
@@ -3961,15 +2705,6 @@ ble_ll_scan_init(void)
 {
     os_error_t err;
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-    err = os_mempool_init(&ext_scan_aux_pool,
-                          MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
-                          sizeof (struct ble_ll_aux_data),
-                          ext_scan_aux_mem,
-                          "ble_ll_aux_scan_pool");
-    BLE_LL_ASSERT(err == 0);
-#endif
-
     err = os_mempool_init(&g_scan_dup_pool,
                           MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS),
                           sizeof(struct ble_ll_scan_dup_entry),
@@ -3980,4 +2715,9 @@ ble_ll_scan_init(void)
     TAILQ_INIT(&g_scan_dup_list);
 
     ble_ll_scan_common_init();
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    ble_ll_scan_aux_init();
+#endif
 }
+
+#endif
diff --git a/nimble/controller/src/ble_ll_scan_aux.c b/nimble/controller/src/ble_ll_scan_aux.c
new file mode 100644
index 0000000000..69ab10cde9
--- /dev/null
+++ b/nimble/controller/src/ble_ll_scan_aux.c
@@ -0,0 +1,1761 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <syscfg/syscfg.h>
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "os/os.h"
+#include "nimble/ble.h"
+#include "nimble/hci_common.h"
+#include "nimble/ble_hci_trans.h"
+#include "controller/ble_phy.h"
+#include "controller/ble_hw.h"
+#include "controller/ble_ll.h"
+#include "controller/ble_ll_sched.h"
+#include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_scan_aux.h"
+#include "controller/ble_ll_hci.h"
+#include "controller/ble_ll_whitelist.h"
+#include "controller/ble_ll_resolv.h"
+#include "controller/ble_ll_sync.h"
+
+#define BLE_LL_SCAN_AUX_F_AUX_ADV           0x0001
+#define BLE_LL_SCAN_AUX_F_AUX_CHAIN         0x0002
+#define BLE_LL_SCAN_AUX_F_MATCHED           0x0004
+#define BLE_LL_SCAN_AUX_F_W4_SCAN_RSP       0x0008
+#define BLE_LL_SCAN_AUX_F_SCANNED           0x0010
+#define BLE_LL_SCAN_AUX_F_HAS_ADVA          0x0020
+#define BLE_LL_SCAN_AUX_F_HAS_TARGETA       0x0040
+#define BLE_LL_SCAN_AUX_F_HAS_ADI           0x0080
+#define BLE_LL_SCAN_AUX_F_RESOLVED_ADVA     0x0100
+#define BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA  0x0200
+#define BLE_LL_SCAN_AUX_F_CONNECTABLE       0x0400
+#define BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP    0x0800
+
+#define BLE_LL_SCAN_AUX_H_SENT_ANY          0x01
+#define BLE_LL_SCAN_AUX_H_DONE              0x02
+#define BLE_LL_SCAN_AUX_H_TRUNCATED         0x04
+
+struct ble_ll_scan_aux_data {
+    uint16_t flags;
+    uint8_t hci_state;
+
+    uint8_t scan_type;
+
+    uint8_t pri_phy;
+    uint8_t sec_phy;
+    uint8_t chan;
+    uint8_t offset_unit : 1;
+    uint32_t aux_ptr;
+    struct ble_ll_sched_item sch;
+    struct ble_npl_event break_ev;
+    struct ble_hci_ev *hci_ev;
+
+    uint16_t adi;
+
+    uint8_t adva[6];
+    uint8_t targeta[6];
+    uint8_t adva_type : 1;
+    uint8_t targeta_type : 1;
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    int8_t rpa_index;
+#endif
+};
+
+#define AUX_MEMPOOL_SIZE    (OS_MEMPOOL_SIZE( \
+                                MYNEWT_VAL(BLE_LL_SCAN_AUX_SEGMENT_CNT), \
+                                sizeof(struct ble_ll_scan_aux_data)))
+
+static os_membuf_t aux_data_mem[AUX_MEMPOOL_SIZE];
+static struct os_mempool aux_data_pool;
+
+static struct ble_ll_scan_aux_data *aux_data_current;
+
+static void ble_ll_hci_ev_send_ext_adv_truncated_report(struct ble_ll_scan_aux_data *aux);
+
+static inline uint8_t *
+ble_ll_scan_aux_get_own_addr(void)
+{
+    uint8_t own_addr_type;
+
+    own_addr_type = ble_ll_scan_get_own_addr_type() & 1;
+
+    return ble_ll_get_our_devaddr(own_addr_type);
+}
+
+static int
+ble_ll_scan_aux_sched_cb(struct ble_ll_sched_item *sch)
+{
+    struct ble_ll_scan_aux_data *aux = sch->cb_arg;
+    uint32_t wfr_us;
+#if BLE_LL_BT5_PHY_SUPPORTED
+    uint8_t phy_mode;
+#endif
+    uint8_t lls;
+    int rc;
+
+    BLE_LL_ASSERT(aux);
+
+    lls = ble_ll_state_get();
+    BLE_LL_ASSERT(lls == BLE_LL_STATE_STANDBY);
+
+    rc = ble_phy_setchan(aux->chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
+    BLE_LL_ASSERT(rc == 0);
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
+    ble_phy_encrypt_disable();
+#endif
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    if (ble_ll_resolv_enabled()) {
+        ble_phy_resolv_list_enable();
+    } else {
+        ble_phy_resolv_list_disable();
+    }
+#endif
+
+#if BLE_LL_BT5_PHY_SUPPORTED
+    phy_mode = ble_ll_phy_to_phy_mode(aux->sec_phy, BLE_HCI_LE_PHY_CODED_ANY);
+    ble_phy_mode_set(phy_mode, phy_mode);
+#endif
+
+    rc = ble_phy_rx_set_start_time(sch->start_time + g_ble_ll_sched_offset_ticks,
+                                   sch->remainder);
+    if (rc != 0 && rc != BLE_PHY_ERR_RX_LATE) {
+        ble_ll_scan_aux_break(aux);
+        return BLE_LL_SCHED_STATE_DONE;
+    }
+
+    /* Keep listening even if we are late, we may still receive something */
+
+    if (ble_ll_scan_get_filt_policy() & 1) {
+        ble_ll_whitelist_enable();
+    } else {
+        ble_ll_whitelist_disable();
+    }
+
+    wfr_us = aux->offset_unit ? 300 : 30;
+    ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_us);
+
+    aux_data_current = aux;
+
+    ble_ll_state_set(BLE_LL_STATE_SCAN_AUX);
+
+    return BLE_LL_SCHED_STATE_RUNNING;
+}
+
+static struct ble_ll_scan_aux_data *
+ble_ll_scan_aux_alloc(void)
+{
+    struct ble_ll_scan_aux_data *aux;
+
+    aux = os_memblock_get(&aux_data_pool);
+    if (!aux) {
+        return NULL;
+    }
+
+    memset(aux, 0, sizeof(*aux));
+
+    aux->sch.sched_cb = ble_ll_scan_aux_sched_cb;
+    aux->sch.sched_type = BLE_LL_SCHED_TYPE_SCAN_AUX;
+    aux->sch.cb_arg = aux;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    aux->rpa_index = -1;
+#endif
+
+    return aux;
+}
+
+static void
+ble_ll_scan_aux_free(struct ble_ll_scan_aux_data *aux)
+{
+    BLE_LL_ASSERT(!aux->sch.enqueued);
+    BLE_LL_ASSERT(aux->hci_ev == NULL);
+    BLE_LL_ASSERT((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) ||
+                  !(aux->hci_state & BLE_LL_SCAN_AUX_H_SENT_ANY));
+
+    os_memblock_put(&aux_data_pool, aux);
+}
+
+static void
+ble_ll_scan_aux_update_scan_backoff(struct ble_ll_scan_aux_data *aux)
+{
+    if (!(aux->flags & BLE_LL_SCAN_AUX_F_W4_SCAN_RSP) &&
+        !(aux->flags & BLE_LL_SCAN_AUX_F_SCANNED)) {
+        return;
+    }
+
+    if ((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) &&
+        !(aux->hci_state & BLE_LL_SCAN_AUX_H_TRUNCATED)) {
+        ble_ll_scan_backoff_update(1);
+    } else {
+        ble_ll_scan_backoff_update(0);
+    }
+}
+
+static inline bool
+ble_ll_scan_aux_need_truncation(struct ble_ll_scan_aux_data *aux)
+{
+    return (aux->hci_state & BLE_LL_SCAN_AUX_H_SENT_ANY) &&
+           !(aux->hci_state & BLE_LL_SCAN_AUX_H_DONE);
+}
+
+static struct ble_hci_ev *
+ble_ll_hci_ev_alloc_ext_adv_report_for_aux(struct ble_ll_scan_addr_data *addrd,
+                                           struct ble_ll_scan_aux_data *aux)
+{
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ext_adv_report *report;
+    struct ble_hci_ev *hci_ev;
+
+    hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+    if (!hci_ev) {
+        return NULL;
+    }
+
+    hci_ev->opcode = BLE_HCI_EVCODE_LE_META;
+    hci_ev->length = sizeof(*hci_subev) + sizeof(*report);
+
+    hci_subev = (void *)hci_ev->data;
+    hci_subev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT;
+    hci_subev->num_reports = 1;
+
+    report = hci_subev->reports;
+
+    memset(report, 0, sizeof(*report));
+
+    report->evt_type = 0;
+    if (addrd->adva) {
+        report->addr_type = addrd->adv_addr_type;
+        memcpy(report->addr, addrd->adv_addr, 6);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+        if (addrd->adva_resolved) {
+            report->addr_type += 2;
+        }
+#endif
+    } else {
+        report->addr_type = 0xff;
+    }
+    report->pri_phy = aux->pri_phy;
+    report->sec_phy = aux->sec_phy;
+    report->sid = aux->adi >> 12;
+    report->tx_power = 0x7f;
+    report->rssi = 0x7f;
+    report->periodic_itvl = 0;
+    if (addrd->targeta) {
+        report->evt_type |= BLE_HCI_ADV_DIRECT_MASK;
+        report->dir_addr_type = addrd->targeta_type;
+        memcpy(report->dir_addr, addrd->targeta, 6);
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+        if (addrd->targeta_resolved) {
+            report->dir_addr_type += 2;
+        }
+#endif
+    }
+    report->data_len = 0;
+
+    return hci_ev;
+}
+
+static struct ble_hci_ev *
+ble_ll_hci_ev_dup_ext_adv_report(struct ble_hci_ev *hci_ev_src)
+{
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ext_adv_report *report;
+    struct ble_hci_ev *hci_ev;
+
+    hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+    if (!hci_ev) {
+        return NULL;
+    }
+
+    memcpy(hci_ev, hci_ev_src, sizeof(*hci_ev) + sizeof(*hci_subev) +
+                               sizeof(*report));
+    hci_ev->length = sizeof(*hci_subev) + sizeof(*report);
+
+    hci_subev = (void *)hci_ev->data;
+
+    report = hci_subev->reports;
+    report->data_len = 0;
+
+    return hci_ev;
+}
+
+static void
+ble_ll_hci_ev_update_ext_adv_report_from_aux(struct ble_hci_ev *hci_ev,
+                                             struct os_mbuf *rxpdu,
+                                             struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ext_adv_report *report;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+    uint8_t *rxbuf;
+
+    hci_subev = (void *)hci_ev->data;
+    report = hci_subev->reports;
+    rxbuf = rxpdu->om_data;
+
+    adv_mode = rxbuf[2] >> 6;
+    eh_len = rxbuf[2] & 0x3f;
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    report->evt_type |= adv_mode;
+    if (rxhdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_RXD) {
+        report->evt_type |= BLE_HCI_ADV_SCAN_MASK | BLE_HCI_ADV_SCAN_RSP_MASK;
+    }
+    report->sec_phy = rxhdr->rxinfo.phy;
+
+    /* Strip PDU header and ext header, leave only AD */
+    os_mbuf_adj(rxpdu, 3 + eh_len);
+
+    /*
+     * We only care about SyncInfo and TxPower so don't bother parsing if they
+     * are not present, just set to 'unknown' values.
+     */
+    if ((eh_len == 0) || !(eh_flags & ((1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT) |
+                                       (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)))) {
+        report->periodic_itvl = 0;
+        report->tx_power = 0x7f;
+        return;
+    }
+    /* Now parse extended header... */
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
+        report->periodic_itvl = get_le16(eh_data + 2);
+        eh_data += BLE_LL_EXT_ADV_SYNC_INFO_SIZE;
+    } else {
+        report->periodic_itvl = 0;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
+        report->tx_power = *eh_data;
+    } else {
+        report->tx_power = 0x7f;
+    }
+}
+
+static void
+ble_ll_hci_ev_update_ext_adv_report_from_ext(struct ble_hci_ev *hci_ev,
+                                             struct os_mbuf *rxpdu,
+                                             struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo;
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ext_adv_report *report;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    struct ble_ll_resolv_entry *rl;
+#endif
+    uint8_t pdu_hdr;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+    uint8_t *rxbuf;
+
+    rxbuf = rxpdu->om_data;
+
+    pdu_hdr = rxbuf[0];
+    adv_mode = rxbuf[2] >> 6;
+    eh_len = rxbuf[2] & 0x3f;
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    hci_ev->opcode = BLE_HCI_EVCODE_LE_META;
+    hci_ev->length = sizeof(*hci_subev) + sizeof(*report);
+
+    hci_subev = (void *)hci_ev->data;
+    hci_subev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT;
+    hci_subev->num_reports = 1;
+
+    report = hci_subev->reports;
+
+    memset(report, 0, sizeof(*report));
+
+    report->evt_type = adv_mode;
+
+    report->pri_phy = rxinfo->phy;
+    report->sec_phy = 0;
+    report->sid = 0xff;
+    report->rssi = rxhdr->rxinfo.rssi;
+    report->periodic_itvl = 0;
+    report->data_len = 0;
+
+    /* Now parse extended header... */
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+        if (rxinfo->rpa_index >= 0) {
+            rl = &g_ble_ll_resolv_list[rxinfo->rpa_index];
+            report->addr_type = rl->rl_addr_type + 2;
+            memcpy(report->addr, rl->rl_identity_addr, 6);
+        } else {
+            report->addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK);
+            memcpy(report->addr, eh_data, 6);
+        }
+#else
+        report->addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK);
+        memcpy(report->addr, eh_data, 6);
+#endif
+        eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
+        report->evt_type |= BLE_HCI_ADV_DIRECT_MASK;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+        if (rxinfo->flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) {
+            report->dir_addr_type = (ble_ll_scan_get_own_addr_type() & 1) + 2;
+            memcpy(report->dir_addr, ble_ll_scan_aux_get_own_addr(), 6);
+        } else {
+            report->dir_addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK);
+            memcpy(report->dir_addr, eh_data, 6);
+        }
+#else
+        report->dir_addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK);
+        memcpy(report->dir_addr, eh_data, 6);
+#endif
+        eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_SYNC_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
+        report->tx_power = *eh_data;
+    } else {
+        report->tx_power = 0x7f;
+    }
+
+    /* Strip PDU header and ext header, leave only AD */
+    os_mbuf_adj(rxpdu, 3 + eh_len);
+
+}
+
+static void
+ble_ll_hci_ev_send_ext_adv_truncated_report(struct ble_ll_scan_aux_data *aux)
+{
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ext_adv_report *report;
+    struct ble_hci_ev *hci_ev;
+
+    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
+        return;
+    }
+
+    hci_ev = aux->hci_ev;
+    aux->hci_ev = NULL;
+
+    BLE_LL_ASSERT(hci_ev);
+
+    hci_subev = (void *)hci_ev->data;
+    report = hci_subev->reports;
+    report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
+
+    ble_ll_hci_event_send(hci_ev);
+
+    aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE | BLE_LL_SCAN_AUX_H_TRUNCATED;
+}
+
+static int
+ble_ll_hci_ev_send_ext_adv_report(struct os_mbuf *rxpdu,
+                                  struct ble_mbuf_hdr *rxhdr,
+                                  struct ble_hci_ev **hci_ev)
+{
+    struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo;
+    struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev;
+    struct ble_hci_ev *hci_ev_next;
+    struct ext_adv_report *report;
+    bool truncated = false;
+    int max_data_len;
+    int data_len;
+    int offset;
+
+    data_len = OS_MBUF_PKTLEN(rxpdu);
+    max_data_len = BLE_LL_MAX_EVT_LEN -
+                   sizeof(**hci_ev) - sizeof(*hci_subev) - sizeof(*report);
+    offset = 0;
+    hci_ev_next = NULL;
+
+    do {
+        hci_subev = (void *)(*hci_ev)->data;
+        report = hci_subev->reports;
+
+        report->rssi = rxinfo->rssi;
+
+        report->data_len = min(max_data_len, data_len - offset);
+        os_mbuf_copydata(rxpdu, offset, report->data_len, report->data);
+        (*hci_ev)->length += report->data_len;
+
+        offset += report->data_len;
+
+        /*
+         * We need another event if either there are still some data left in
+         * this PDU or scan for next aux is scheduled.
+         */
+        if ((offset < data_len) ||
+            (rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT)) {
+            hci_ev_next = ble_ll_hci_ev_dup_ext_adv_report(*hci_ev);
+            if (hci_ev_next) {
+                report->evt_type |= BLE_HCI_ADV_DATA_STATUS_INCOMPLETE;
+            } else {
+                report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED;
+            }
+        }
+
+        switch (report->evt_type & BLE_HCI_ADV_DATA_STATUS_MASK) {
+        case BLE_HCI_ADV_DATA_STATUS_TRUNCATED:
+            truncated = true;
+            /* no break */
+        case BLE_HCI_ADV_DATA_STATUS_COMPLETE:
+            BLE_LL_ASSERT(!hci_ev_next);
+            break;
+        case BLE_HCI_ADV_DATA_STATUS_INCOMPLETE:
+            BLE_LL_ASSERT(hci_ev_next);
+            break;
+        default:
+            BLE_LL_ASSERT(0);
+        }
+
+        ble_ll_hci_event_send(*hci_ev);
+
+        *hci_ev = hci_ev_next;
+    } while ((offset < data_len) && *hci_ev);
+
+    return truncated ? -1 : 0;
+}
+
+
+static int
+ble_ll_hci_ev_send_ext_adv_report_for_aux(struct os_mbuf *rxpdu,
+                                          struct ble_mbuf_hdr *rxhdr,
+                                          struct ble_ll_scan_aux_data *aux,
+                                          struct ble_ll_scan_addr_data *addrd)
+{
+    struct ble_hci_ev *hci_ev;
+    int rc;
+
+    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
+        return -1;
+    }
+
+    /*
+     * We need to always keep one event allocated in aux to be able to truncate
+     * data properly in case of an error. If there is no event in aux it means
+     * this is first event and we can silently ignore in case of an error.
+     */
+    if (aux->hci_ev) {
+        hci_ev = aux->hci_ev;
+        aux->hci_ev = NULL;
+    } else {
+        hci_ev = ble_ll_hci_ev_alloc_ext_adv_report_for_aux(addrd, aux);
+        if (!hci_ev) {
+            return -1;
+        }
+    }
+
+    ble_ll_hci_ev_update_ext_adv_report_from_aux(hci_ev, rxpdu, rxhdr);
+
+    rc = ble_ll_hci_ev_send_ext_adv_report(rxpdu, rxhdr, &hci_ev);
+    if (rc < 0) {
+        BLE_LL_ASSERT(!hci_ev);
+        aux->hci_state = BLE_LL_SCAN_AUX_H_DONE | BLE_LL_SCAN_AUX_H_TRUNCATED;
+    } else if (hci_ev) {
+        aux->hci_state = BLE_LL_SCAN_AUX_H_SENT_ANY;
+    } else {
+        aux->hci_state = BLE_LL_SCAN_AUX_H_DONE;
+    }
+
+    aux->hci_ev = hci_ev;
+
+    return rc;
+}
+
+static void
+ble_ll_hci_ev_send_ext_adv_report_for_ext(struct os_mbuf *rxpdu,
+                                          struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_hci_ev *hci_ev;
+
+    if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
+        return;
+    }
+
+    hci_ev = (void *)ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+    if (!hci_ev) {
+        return;
+    }
+
+    ble_ll_hci_ev_update_ext_adv_report_from_ext(hci_ev, rxpdu, rxhdr);
+
+    ble_ll_hci_ev_send_ext_adv_report(rxpdu, rxhdr, &hci_ev);
+
+    BLE_LL_ASSERT(!hci_ev);
+}
+
+static void
+ble_ll_scan_aux_break_ev(struct ble_npl_event *ev)
+{
+    struct ble_ll_scan_aux_data *aux = ble_npl_event_get_arg(ev);
+
+    BLE_LL_ASSERT(aux);
+
+    if (ble_ll_scan_aux_need_truncation(aux)) {
+        ble_ll_hci_ev_send_ext_adv_truncated_report(aux);
+    }
+
+    ble_ll_scan_aux_update_scan_backoff(aux);
+
+    ble_ll_scan_aux_free(aux);
+    ble_ll_scan_chk_resume();
+}
+
+void
+ble_ll_scan_aux_break(struct ble_ll_scan_aux_data *aux)
+{
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) {
+        ble_ll_conn_send_connect_req_cancel();
+    }
+#endif
+
+    ble_npl_event_init(&aux->break_ev, ble_ll_scan_aux_break_ev, aux);
+    ble_ll_event_send(&aux->break_ev);
+}
+
+static int
+ble_ll_scan_aux_parse_aux_ptr(struct ble_ll_scan_aux_data *aux,
+                              uint32_t aux_ptr, uint32_t *offset_us)
+{
+    uint8_t offset_unit;
+    uint32_t offset;
+    uint8_t chan;
+    uint8_t phy;
+
+    phy = (aux_ptr >> 21) & 0x07;
+    switch (phy) {
+    case 0:
+        phy = BLE_PHY_1M;
+        break;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
+    case 1:
+        phy = BLE_PHY_2M;
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
+    case 2:
+        phy = BLE_PHY_CODED;
+        break;
+#endif
+    default:
+        return -1;
+    }
+
+    chan = aux_ptr & 0x3f;
+    if (chan >= BLE_PHY_NUM_DATA_CHANS) {
+        return -1;
+    }
+
+    offset = 30 * ((aux_ptr >> 8) & 0x1fff);
+    if ((aux_ptr >> 7) & 0x01) {
+        offset *= 10;
+        offset_unit = 1;
+    } else {
+        offset_unit = 0;
+    }
+
+    if (offset < BLE_LL_MAFS) {
+        return -1;
+    }
+
+    aux->sec_phy = phy;
+    aux->chan = chan;
+    aux->offset_unit = offset_unit;
+
+    *offset_us = offset;
+
+    return 0;
+}
+
+int
+ble_ll_scan_aux_sched(struct ble_ll_scan_aux_data *aux, uint32_t pdu_time,
+                      uint8_t pdu_time_rem, uint32_t aux_ptr)
+{
+    uint32_t offset_us;
+    int rc;
+
+    rc = ble_ll_scan_aux_parse_aux_ptr(aux, aux_ptr, &offset_us);
+    if (rc < 0) {
+        return -1;
+    }
+
+    rc = ble_ll_sched_scan_aux(&aux->sch, pdu_time, pdu_time_rem, offset_us);
+    if (rc < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+ble_ll_scan_aux_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_ll_scan_aux_data *aux;
+
+    BLE_LL_ASSERT(aux_data_current);
+    aux = aux_data_current;
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) {
+        if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
+            aux_data_current = NULL;
+            ble_ll_scan_aux_break(aux);
+            ble_ll_state_set(BLE_LL_STATE_STANDBY);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
+        aux_data_current = NULL;
+        ble_ll_scan_aux_break(aux);
+        ble_ll_state_set(BLE_LL_STATE_STANDBY);
+        return -1;
+    }
+
+    /*
+     * Prepare TX transition when doing active scanning and receiving 1st PDU
+     * since we may want to send AUX_SCAN_REQ.
+     */
+    if ((aux->scan_type != BLE_SCAN_TYPE_PASSIVE) &&
+        !(aux->flags & BLE_LL_SCAN_AUX_F_AUX_ADV)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+ble_ll_scan_aux_parse_to_aux_data(struct ble_ll_scan_aux_data *aux,
+                                  uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
+{
+    uint8_t pdu_hdr;
+    uint8_t pdu_len;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+
+    pdu_hdr = rxbuf[0];
+    pdu_len = rxbuf[1];
+
+    /* PDU without at least Extended Header Length is invalid */
+    if (pdu_len == 0) {
+        return -1;
+    }
+
+    /* Mark as AUX received on 1st PDU, then as CHAIN received on subsequent */
+    if (aux->flags & BLE_LL_SCAN_AUX_F_AUX_ADV) {
+        aux->flags |= BLE_LL_SCAN_AUX_F_AUX_CHAIN;
+    } else {
+        aux->flags |= BLE_LL_SCAN_AUX_F_AUX_ADV;
+    }
+
+    adv_mode = rxbuf[2] >> 6;
+    eh_len = rxbuf[2] & 0x3f;
+
+    /* Only AUX_CHAIN_IND is valid without Extended Header */
+    if (eh_len == 0) {
+        if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN) || adv_mode) {
+            return -1;
+        }
+        return 0;
+    }
+
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    /* Now parse extended header... */
+
+    /* AdvA is only valid in 1st PDU, ignore in AUX_CHAIN_IND */
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
+        if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN)) {
+            memcpy(aux->adva, eh_data, 6);
+            aux->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK);
+            aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADVA;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+            aux->rpa_index = ble_hw_resolv_list_match();
+#endif
+        }
+        eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+    }
+
+    /* TargetA is only valid in 1st PDU, ignore in AUX_CHAIN_IND */
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
+        if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN)) {
+            memcpy(aux->targeta, eh_data, 6);
+            aux->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK);
+            aux->flags |= BLE_LL_SCAN_AUX_F_HAS_TARGETA;
+        }
+        eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE;
+    }
+
+    /*
+     * ADI handling is a bit convoluted....
+     * ADI is mandatory in ADV_EXT_IND with AuxPtr and is also mandatory in PDU
+     * if included in superior PDU. This implies that each AUX_CHAIN shall have
+     * ADI. However... AUX_SCAN_RSP does not need to have ADI, so if there's no
+     * ADI in AUX_SCAN_RSP we allow it and clear corresponding flag to skip ADI
+     * checks on subsequent PDUs.
+     */
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
+        if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADI) {
+            if (get_le16(eh_data) != aux->adi) {
+                return -1;
+            }
+        }
+        eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
+    } else if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADI) {
+        if (rxhdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_RXD) {
+            aux->flags &= ~BLE_LL_SCAN_AUX_F_HAS_ADI;
+        } else {
+            return -1;
+        }
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
+        aux->aux_ptr = get_le24(eh_data);
+        rxhdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT;
+    }
+
+    return 0;
+}
+
+static int
+ble_ll_scan_aux_ext_parse(uint8_t *rxbuf, struct ble_ll_scan_addr_data *addrd,
+                          uint16_t *adi, uint32_t *aux_ptr)
+{
+    uint8_t pdu_hdr;
+    uint8_t pdu_len;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+
+    pdu_hdr = rxbuf[0];
+    pdu_len = rxbuf[1];
+
+    if (pdu_len == 0) {
+        return -1;
+    }
+
+    adv_mode = rxbuf[2] >> 6;
+    eh_len = rxbuf[2] & 0x3f;
+
+    if ((adv_mode == 3) || (eh_len == 0)) {
+        return -1;
+    }
+
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    /* ADV_EXT_IND with AuxPtr but without ADI is invalid */
+    if ((eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) &&
+        !(eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT))) {
+        return -1;
+    }
+
+    /* ADV_EXT_IND without either AdvA or AuxPtr is not valid */
+    if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_ADVA_BIT) |
+                      (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)))) {
+        return -1;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
+        addrd->adva = eh_data;
+        addrd->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_RAND);
+        eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+    } else {
+        addrd->adva = NULL;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
+        addrd->targeta = eh_data;
+        addrd->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_RAND);
+        eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE;
+    } else {
+        addrd->targeta = NULL;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
+        *adi = get_le16(eh_data);
+        eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
+    } else {
+        *adi = 0;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
+        *aux_ptr = get_le24(eh_data);
+    } else {
+        *aux_ptr = 0;
+    }
+
+    return 0;
+}
+
+static void
+ble_ll_scan_aux_update_rxinfo_from_addrd(struct ble_ll_scan_addr_data *addrd,
+                                         struct ble_mbuf_hdr_rxinfo *rxinfo)
+{
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    rxinfo->rpa_index = addrd->rpa_index;
+    if (addrd->adva_resolved) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED;
+    }
+    if (addrd->targeta_resolved) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED;
+    }
+#endif
+}
+
+static void
+ble_ll_scan_aux_update_aux_data_from_addrd(struct ble_ll_scan_addr_data *addrd,
+                                           struct ble_ll_scan_aux_data *aux)
+{
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    aux->rpa_index = addrd->rpa_index;
+    if (addrd->adva_resolved) {
+        aux->flags |= BLE_LL_SCAN_AUX_F_RESOLVED_ADVA;
+    }
+    if (addrd->targeta_resolved) {
+        aux->flags |= BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA;
+    }
+#endif
+}
+
+int
+ble_ll_scan_aux_rx_isr_end_on_ext(struct ble_ll_scan_sm *scansm,
+                                  struct os_mbuf *rxpdu)
+{
+    struct ble_mbuf_hdr *rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
+    struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo;
+    struct ble_ll_scan_addr_data addrd;
+    struct ble_ll_scan_aux_data *aux;
+    uint8_t *rxbuf;
+    uint32_t aux_ptr;
+    uint16_t adi;
+    bool do_match;
+    int rc;
+
+    rxbuf = rxpdu->om_data;
+
+    rc = ble_ll_scan_aux_ext_parse(rxbuf, &addrd, &adi, &aux_ptr);
+    if (rc < 0) {
+        return -1;
+    }
+
+    /* We can filter based on ext PDU alone if both AdvA and TargetA are present
+     * or there's no AuxPtr. Otherwise, we need to wait for aux PDU to filter.
+     */
+    do_match = !aux_ptr || (addrd.adva && addrd.targeta);
+    if (do_match) {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+        addrd.rpa_index = ble_hw_resolv_list_match();
+#endif
+        rc = ble_ll_scan_rx_filter(ble_ll_scan_get_own_addr_type(),
+                                   ble_ll_scan_get_filt_policy(),
+                                   &addrd, NULL);
+        if (rc < 0) {
+            return -1;
+        }
+
+        /* Don't care about initiator here, there are no AdvA and TargetA
+         * in connectable ADV_EXT_IND so no filtering to do.
+         */
+
+        if (!aux_ptr) {
+            /* We do not allocate aux_data for ADV_EXT_IND without AuxPtr so
+             * need to pass match data in rxinfo.
+             */
+            ble_ll_scan_aux_update_rxinfo_from_addrd(&addrd, rxinfo);
+        }
+    }
+
+    if (aux_ptr) {
+        aux = ble_ll_scan_aux_alloc();
+        if (!aux) {
+            return -1;
+        }
+
+        aux->scan_type = scansm->scanp->scan_type;
+
+        aux->pri_phy = rxinfo->phy;
+        aux->aux_ptr = aux_ptr;
+
+        if (addrd.adva) {
+            memcpy(aux->adva, addrd.adva, 6);
+            aux->adva_type = addrd.adva_type;
+            aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADVA;
+        }
+
+        if (addrd.targeta) {
+            memcpy(aux->targeta, addrd.targeta, 6);
+            aux->targeta_type = addrd.targeta_type;
+            aux->flags |= BLE_LL_SCAN_AUX_F_HAS_TARGETA;
+        }
+
+        aux->adi = adi;
+        aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADI;
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+        if (aux->scan_type == BLE_SCAN_TYPE_INITIATE) {
+            aux->flags |= BLE_LL_SCAN_AUX_F_CONNECTABLE;
+        }
+#endif
+
+        if (do_match) {
+            aux->flags |= BLE_LL_SCAN_AUX_F_MATCHED;
+            ble_ll_scan_aux_update_aux_data_from_addrd(&addrd, aux);
+        } else if (addrd.adva) {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+            /* If ext PDU has AdvA, we need to store rpa_index to be able to
+             * reuse it for filtering when done on aux PDU.
+             */
+            aux->rpa_index = ble_hw_resolv_list_match();
+#endif
+        }
+
+        rxinfo->user_data = aux;
+    }
+
+    return 0;
+}
+
+void
+ble_ll_scan_aux_pkt_in_on_ext(struct os_mbuf *rxpdu,
+                              struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo;
+    struct ble_ll_scan_aux_data *aux = rxinfo->user_data;
+    int rc;
+
+    if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) {
+        BLE_LL_ASSERT(!aux);
+        return;
+    }
+
+    if (!aux) {
+        ble_ll_hci_ev_send_ext_adv_report_for_ext(rxpdu, rxhdr);
+        return;
+    }
+
+    BLE_LL_ASSERT(aux->aux_ptr);
+
+    rc = ble_ll_scan_aux_sched(aux, rxhdr->beg_cputime, rxhdr->rem_usecs,
+                               aux->aux_ptr);
+    if (rc < 0) {
+        ble_ll_scan_aux_free(aux);
+    }
+}
+
+static uint8_t
+ble_ll_scan_aux_scan_req_tx_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte)
+{
+    struct ble_ll_scan_aux_data *aux = arg;
+    uint8_t *scana;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    struct ble_ll_resolv_entry *rl;
+    uint8_t rpa[BLE_DEV_ADDR_LEN];
+#endif
+    uint8_t hb;
+
+    hb = BLE_ADV_PDU_TYPE_SCAN_REQ;
+
+    /* ScanA */
+    if (ble_ll_scan_get_own_addr_type() & 0x01) {
+        hb |= BLE_ADV_PDU_HDR_TXADD_RAND;
+        scana = g_random_addr;
+    } else {
+        scana = g_dev_addr;
+    }
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    if (ble_ll_scan_get_own_addr_type() & 0x02) {
+        if (aux->rpa_index >=0) {
+            rl = &g_ble_ll_resolv_list[aux->rpa_index];
+        } else {
+            rl = NULL;
+        }
+
+        /*
+         * If device is on RL and we have local IRK, we use RPA generated using
+         * that IRK as ScanA. Otherwise we use NRPA as ScanA to prevent our
+         * device from being tracked when doing an active scan
+         * ref: Core 5.2, Vol 6, Part B, section 6.3)
+         */
+        if (rl && rl->rl_has_local) {
+            ble_ll_resolv_get_priv_addr(rl, 1, rpa);
+            scana = rpa;
+        } else {
+            scana = ble_ll_get_scan_nrpa();
+        }
+
+        hb |= BLE_ADV_PDU_HDR_TXADD_RAND;
+    }
+#endif
+    memcpy(dptr, scana, BLE_DEV_ADDR_LEN);
+
+    /* AdvA */
+    if (aux->adva_type) {
+        hb |= BLE_ADV_PDU_HDR_RXADD_RAND;
+    }
+    memcpy(dptr + BLE_DEV_ADDR_LEN, aux->adva, BLE_DEV_ADDR_LEN);
+
+    *hdr_byte = hb;
+
+    return BLE_DEV_ADDR_LEN * 2;
+}
+
+static bool
+ble_ll_scan_aux_send_scan_req(struct ble_ll_scan_aux_data *aux,
+                              struct ble_ll_scan_addr_data *addrd)
+{
+    int rc;
+
+    /* Check if we already scanned this device successfully */
+    if (ble_ll_scan_have_rxd_scan_rsp(addrd->adv_addr, addrd->adv_addr_type,
+                                      true, aux->adi)) {
+        return false;
+    }
+
+    /* We want to send a request, see if backoff allows us */
+    if (ble_ll_scan_backoff_kick() != 0) {
+        return false;
+    }
+
+    /* TODO perhaps we should check if scan req+rsp won't overlap with scheduler
+     *      item (old code did it), but for now let's just scan and we will be
+     *      interrupted if scheduler kicks in.
+     */
+
+    rc = ble_phy_tx(ble_ll_scan_aux_scan_req_tx_pdu_cb, aux,
+                    BLE_PHY_TRANSITION_TX_RX);
+    if (rc) {
+        return false;
+    }
+
+    return true;
+}
+
+int
+ble_ll_scan_aux_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok)
+{
+    struct ble_ll_scan_addr_data addrd;
+    struct ble_mbuf_hdr_rxinfo *rxinfo;
+    struct ble_ll_scan_aux_data *aux;
+    struct ble_mbuf_hdr *rxhdr;
+    uint8_t scan_filt_policy;
+    uint8_t scan_ok;
+    uint8_t adv_mode;
+    uint8_t *rxbuf;
+    int rc;
+
+    BLE_LL_ASSERT(aux_data_current);
+    aux = aux_data_current;
+    aux_data_current = NULL;
+
+    if (rxpdu == NULL) {
+        ble_ll_scan_aux_break(aux);
+        goto done;
+    }
+
+    rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
+    rxinfo = &rxhdr->rxinfo;
+    rxinfo->user_data = aux;
+
+    if (!crcok) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+        goto done;
+    }
+
+    rxbuf = rxpdu->om_data;
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) {
+        aux->flags &= ~BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP;
+        rxinfo->flags |= BLE_MBUF_HDR_F_CONNECT_RSP_RXD;
+        goto done;
+    }
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_W4_SCAN_RSP) {
+        aux->flags &= ~BLE_LL_SCAN_AUX_F_W4_SCAN_RSP;
+        aux->flags |= BLE_LL_SCAN_AUX_F_SCANNED;
+        rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD;
+    }
+
+    rc = ble_ll_scan_aux_parse_to_aux_data(aux, rxbuf, rxhdr);
+    if (rc < 0) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+        goto done;
+    }
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_MATCHED) {
+        goto done;
+    }
+
+    /* We do filtering on either ADV_EXT_IND or AUX_ADV_IND so we should not be
+     * here when processing AUX_CHAIN_IND.
+     */
+    BLE_LL_ASSERT(!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN));
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADVA) {
+        addrd.adva = aux->adva;
+        addrd.adva_type = aux->adva_type;
+    } else {
+        addrd.adva = NULL;
+    }
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_TARGETA) {
+        addrd.targeta = aux->targeta;
+        addrd.targeta_type = aux->targeta_type;
+    } else {
+        addrd.targeta = NULL;
+    }
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    addrd.rpa_index = aux->rpa_index;
+#endif
+
+    scan_filt_policy = ble_ll_scan_get_filt_policy();
+
+    rc = ble_ll_scan_rx_filter(ble_ll_scan_get_own_addr_type(),
+                               scan_filt_policy, &addrd, &scan_ok);
+    if (rc < 0) {
+        rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+        /*
+         * XXX hack warning
+         * Even if PDU was not allowed by current scan filter policy, we should
+         * still allow it to sync if SyncInfo is present. Since we do not use
+         * F_DEVMATCH in aux code for its intended purpose, let's use it here to
+         * indicate no match due to scan filter policy.
+         */
+        if (rc == -2) {
+            rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH;
+        }
+        goto done;
+    }
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if ((aux->scan_type == BLE_SCAN_TYPE_INITIATE) &&
+        !(scan_filt_policy & 0x01)) {
+        rc = ble_ll_scan_rx_check_init(&addrd);
+        if (rc < 0) {
+            rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+            goto done;
+        }
+    }
+#endif
+
+    aux->flags |= BLE_LL_SCAN_AUX_F_MATCHED;
+
+    ble_ll_scan_aux_update_aux_data_from_addrd(&addrd, aux);
+
+    adv_mode = rxbuf[2] >> 6;
+
+    switch (aux->scan_type) {
+    case BLE_SCAN_TYPE_ACTIVE:
+        if ((adv_mode == BLE_LL_EXT_ADV_MODE_SCAN) && scan_ok &&
+            ble_ll_scan_aux_send_scan_req(aux, &addrd)) {
+            /* AUX_SCAN_REQ sent, keep PHY enabled to continue */
+            aux->flags |= BLE_LL_SCAN_AUX_F_W4_SCAN_RSP;
+            rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_REQ_TXD;
+            aux_data_current = aux;
+            return 0;
+        }
+        break;
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_SCAN_TYPE_INITIATE:
+        if (ble_ll_conn_send_connect_req(rxpdu, &addrd, 1) == 0) {
+            /* AUX_CONNECT_REQ sent, keep PHY enabled to continue */
+            aux->flags |= BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP;
+            rxinfo->flags |= BLE_MBUF_HDR_F_CONNECT_REQ_TXD;
+            aux_data_current = aux;
+            return 0;
+        } else {
+            rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED;
+        }
+        break;
+#endif
+    default:
+        break;
+    }
+
+done:
+    /* We are done with this PDU so go to standby and let LL resume if needed */
+    ble_ll_state_set(BLE_LL_STATE_STANDBY);
+    return -1;
+}
+
+static void
+ble_ll_scan_aux_init_addrd_from_aux_data(struct ble_ll_scan_aux_data *aux,
+                                         struct ble_ll_scan_addr_data *addrd)
+{
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    struct ble_ll_resolv_entry *rl;
+#endif
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADVA) {
+        addrd->adva = aux->adva;
+        addrd->adva_type = aux->adva_type;
+    } else {
+        addrd->adva = NULL;
+        addrd->adva_type = 0;
+    }
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_TARGETA) {
+        addrd->targeta = aux->targeta;
+        addrd->targeta_type = aux->targeta_type;
+    } else {
+        addrd->targeta = NULL;
+        addrd->targeta_type = 0;
+    }
+
+    addrd->adv_addr = addrd->adva;
+    addrd->adv_addr_type = addrd->adva_type;
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    addrd->rpa_index = aux->rpa_index;
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_RESOLVED_ADVA) {
+        BLE_LL_ASSERT(aux->rpa_index >= 0);
+        rl = &g_ble_ll_resolv_list[aux->rpa_index];
+        addrd->adv_addr = rl->rl_identity_addr;
+        addrd->adv_addr_type = rl->rl_addr_type;
+        addrd->adva_resolved = 1;
+    } else {
+        addrd->adva_resolved = 0;
+    }
+
+    if (aux->flags & BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA) {
+        addrd->targeta = ble_ll_scan_aux_get_own_addr();
+        addrd->targeta_type = ble_ll_scan_get_own_addr_type() & 1;
+        addrd->targeta_resolved = 1;
+    } else {
+        addrd->targeta_resolved = 0;
+    }
+#endif
+}
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+static void
+ble_ll_scan_aux_sync_check(struct os_mbuf *rxpdu,
+                           struct ble_ll_scan_addr_data *addrd)
+{
+    struct ble_mbuf_hdr *rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
+    uint8_t *rxbuf = rxpdu->om_data;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+    uint8_t sid;
+
+    adv_mode = rxbuf[2] >> 6;
+
+    if (adv_mode != BLE_LL_EXT_ADV_MODE_NON_CONN) {
+        return;
+    }
+
+    eh_len = rxbuf[2] & 0x3f;
+
+    if (eh_len == 0) {
+        return;
+    }
+
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    /* Need ADI and SyncInfo */
+    if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT) |
+                      (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)))) {
+        return;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE;
+    }
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
+        eh_data += 1;
+    }
+
+    sid = get_le16(eh_data) >> 12;
+    eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
+
+    if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
+        eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
+    }
+
+    ble_ll_sync_info_event(addrd, sid, rxhdr, eh_data);
+}
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+static int
+ble_ll_scan_aux_check_connect_rsp(uint8_t *rxbuf,
+                                  struct ble_ll_scan_pdu_data *pdu_data,
+                                  struct ble_ll_scan_addr_data *addrd)
+{
+    uint8_t pdu_hdr;
+    uint8_t pdu_len;
+    uint8_t adv_mode;
+    uint8_t eh_len;
+    uint8_t eh_flags;
+    uint8_t *eh_data;
+    uint8_t adva_type;
+    uint8_t *adva;
+    uint8_t targeta_type;
+    uint8_t *targeta;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    struct ble_ll_resolv_entry *rl = NULL;
+#endif
+
+    pdu_hdr = rxbuf[0];
+    pdu_len = rxbuf[1];
+
+    if (pdu_len == 0) {
+        return -1;
+    }
+
+    adv_mode = rxbuf[2] >> 6;
+    eh_len = rxbuf[2] & 0x3f;
+
+    if ((adv_mode != 0) || (eh_len == 0)) {
+        return -1;
+    }
+
+    eh_flags = rxbuf[3];
+    eh_data = &rxbuf[4];
+
+    /* AUX_CONNECT_RSP without AdvA or TargetA is not valid */
+    if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_ADVA_BIT) |
+                      (1 << BLE_LL_EXT_ADV_TARGETA_BIT)))) {
+        return -1;
+    }
+
+    adva = eh_data;
+    adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_RAND);
+    eh_data += BLE_LL_EXT_ADV_ADVA_SIZE;
+
+    targeta = eh_data;
+    targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_RAND);
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    /* If AdvA was initially resolved, we need to check if current AdvA also
+     * resolved with the same IRK since it may have changes due to RPA timeout.
+     * Otherwise it shall be the same as in AUX_CONNECT_REQ.
+     */
+    if (addrd->adva_resolved) {
+        if (!adva_type) {
+            return -1;
+        }
+
+        BLE_LL_ASSERT(addrd->rpa_index >= 0);
+        rl = &g_ble_ll_resolv_list[addrd->rpa_index];
+
+        if (!ble_ll_resolv_rpa(adva, rl->rl_peer_irk)) {
+            return -1;
+        }
+
+        addrd->adva = adva;
+    } else if ((adva_type !=
+                !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK)) ||
+               (memcmp(adva, pdu_data->adva, 6) != 0)) {
+            return -1;
+    }
+#else
+    if ((adva_type != !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK)) ||
+        (memcmp(adva, pdu_data->adva, 6) != 0)) {
+            return -1;
+    }
+#endif /* BLE_LL_CFG_FEAT_LL_PRIVACY */
+
+    if ((targeta_type != !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK)) ||
+        (memcmp(targeta, pdu_data->inita, 6) != 0)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static void
+ble_ll_scan_aux_rx_pkt_in_for_initiator(struct os_mbuf *rxpdu,
+                                        struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_ll_scan_addr_data addrd;
+    struct ble_mbuf_hdr_rxinfo *rxinfo;
+    struct ble_ll_scan_aux_data *aux;
+
+    rxinfo = &rxhdr->rxinfo;
+    aux = rxinfo->user_data;
+
+    if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) {
+        ble_ll_scan_chk_resume();
+        goto done;
+    }
+
+    if (!(rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_RSP_RXD)) {
+        BLE_LL_ASSERT(rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_REQ_TXD);
+        /* Waiting for AUX_CONNECT_RSP, do nothing */
+        return;
+    }
+
+    ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd);
+
+    if (ble_ll_scan_aux_check_connect_rsp(rxpdu->om_data,
+                                          ble_ll_scan_get_pdu_data(),
+                                          &addrd) < 0) {
+        ble_ll_scan_chk_resume();
+        goto done;
+    }
+
+    aux->flags &= ~BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP;
+
+    ble_ll_scan_sm_stop(0);
+    ble_ll_conn_created_on_aux(rxpdu, &addrd, aux->targeta);
+
+done:
+    if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) {
+        ble_ll_conn_send_connect_req_cancel();
+    }
+    ble_ll_scan_aux_free(aux);
+}
+#endif
+
+void
+ble_ll_scan_aux_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr)
+{
+    struct ble_ll_scan_addr_data addrd;
+    struct ble_mbuf_hdr_rxinfo *rxinfo;
+    struct ble_ll_scan_aux_data *aux;
+    bool scan_duplicate = false;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+    bool sync_check = false;
+#endif
+    int rc;
+
+    rxinfo = &rxhdr->rxinfo;
+    aux = rxinfo->user_data;
+
+    BLE_LL_ASSERT(aux);
+
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    if (aux->scan_type == BLE_SCAN_TYPE_INITIATE) {
+        ble_ll_scan_aux_rx_pkt_in_for_initiator(rxpdu, rxhdr);
+        return;
+    }
+#endif
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+    sync_check = ble_ll_sync_enabled() &&
+                 !(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN);
+
+    /* PDU was not allowed due to scan filter policy, but we can still try to
+     * sync since separate filter policy is used for this purpose.
+     */
+    if ((rxinfo->flags & BLE_MBUF_HDR_F_DEVMATCH) && sync_check) {
+        ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd);
+        ble_ll_scan_aux_sync_check(rxpdu, &addrd);
+    }
+#endif
+
+    if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) {
+        if (ble_ll_scan_aux_need_truncation(aux)) {
+            ble_ll_hci_ev_send_ext_adv_truncated_report(aux);
+        } else {
+            aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE;
+        }
+
+        ble_ll_scan_aux_update_scan_backoff(aux);
+    }
+
+    if (aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) {
+        ble_ll_scan_aux_free(aux);
+        ble_ll_scan_chk_resume();
+        return;
+    }
+
+    /* Try to schedule scan for subsequent aux asap, if needed */
+    if (rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT) {
+        rc = ble_ll_scan_aux_sched(aux, rxhdr->beg_cputime, rxhdr->rem_usecs,
+                                   aux->aux_ptr);
+        if (rc < 0) {
+            rxinfo->flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
+        }
+    }
+
+    ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd);
+
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+    if (sync_check) {
+        ble_ll_scan_aux_sync_check(rxpdu, &addrd);
+    }
+#endif
+
+    scan_duplicate = ble_ll_scan_get_filt_dups() &&
+                     ble_ll_scan_dup_check_ext(addrd.adv_addr_type,
+                                               addrd.adv_addr, true, aux->adi);
+    if (!scan_duplicate) {
+        rc = ble_ll_hci_ev_send_ext_adv_report_for_aux(rxpdu, rxhdr, aux,
+                                                       &addrd);
+
+        /*
+         * Update duplicates list if report was sent successfully and we are
+         * done with this chain. On error, status is already set to 'done' so
+         * we will cancel aux scan (if any) and stop further processing.
+         * However, if we send AUX_SCAN_REQ for this PDU then need to remove
+         * 'done' as we should continue with scanning after AUX_SCAN_RSP.
+         */
+
+        if (rc == 0) {
+            if (rxinfo->flags & BLE_MBUF_HDR_F_SCAN_REQ_TXD) {
+                aux->hci_state &= ~BLE_LL_SCAN_AUX_H_DONE;
+            } else if (aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) {
+                BLE_LL_ASSERT(!(rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT));
+                if (ble_ll_scan_get_filt_dups()) {
+                    ble_ll_scan_dup_update_ext(addrd.adv_addr_type,
+                                               addrd.adv_addr, true, aux->adi);
+                }
+                if (aux->flags & BLE_LL_SCAN_AUX_F_SCANNED) {
+                    ble_ll_scan_add_scan_rsp_adv(addrd.adv_addr,
+                                                 addrd.adv_addr_type,
+                                                 1, aux->adi);
+                }
+            }
+        }
+    } else {
+        /* This is a duplicate, we don't want to scan it anymore */
+        aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE;
+    }
+
+    /*
+     * If we are done processing this chain and aux scan was not scheduled or
+     * we removed it from scheduler, we can remove aux_data now. Otherwise we
+     * will remove on next pkt_in.
+     */
+    if ((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) &&
+        (!(rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT) ||
+         (ble_ll_sched_rmv_elem(&aux->sch) == 0))) {
+        ble_ll_scan_aux_update_scan_backoff(aux);
+        ble_ll_scan_aux_free(aux);
+    }
+
+    ble_ll_scan_chk_resume();
+}
+
+void
+ble_ll_scan_aux_wfr_timer_exp(void)
+{
+    ble_phy_disable();
+    ble_ll_scan_aux_halt();
+}
+
+void
+ble_ll_scan_aux_halt(void)
+{
+    struct ble_ll_scan_aux_data *aux = aux_data_current;
+
+    BLE_LL_ASSERT(aux);
+
+    aux_data_current = NULL;
+
+    ble_ll_state_set(BLE_LL_STATE_STANDBY);
+
+    ble_ll_scan_aux_break(aux);
+}
+
+void
+ble_ll_scan_aux_sched_remove(struct ble_ll_sched_item *sch)
+{
+    ble_ll_scan_aux_break(sch->cb_arg);
+}
+
+void
+ble_ll_scan_aux_init(void)
+{
+    os_error_t err;
+
+    err = os_mempool_init(&aux_data_pool,
+                          MYNEWT_VAL(BLE_LL_SCAN_AUX_SEGMENT_CNT),
+                          sizeof(struct ble_ll_scan_aux_data),
+                          aux_data_mem, "ble_ll_scan_aux_data_pool");
+    BLE_LL_ASSERT(err == 0);
+}
+
+#endif /* BLE_LL_CFG_FEAT_LL_EXT_ADV */
diff --git a/nimble/controller/src/ble_ll_sched.c b/nimble/controller/src/ble_ll_sched.c
index d01f10edab..cd60014d67 100644
--- a/nimble/controller/src/ble_ll_sched.c
+++ b/nimble/controller/src/ble_ll_sched.c
@@ -20,7 +20,6 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <string.h>
-#include "os/os.h"
 #include "os/os_cputime.h"
 #include "ble/xcvr.h"
 #include "controller/ble_phy.h"
@@ -28,26 +27,27 @@
 #include "controller/ble_ll_sched.h"
 #include "controller/ble_ll_adv.h"
 #include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_scan_aux.h"
 #include "controller/ble_ll_rfmgmt.h"
 #include "controller/ble_ll_trace.h"
 #include "controller/ble_ll_sync.h"
 #include "ble_ll_priv.h"
 #include "ble_ll_conn_priv.h"
 
-/* XXX: this is temporary. Not sure what I want to do here */
-struct hal_timer g_ble_ll_sched_timer;
+#define BLE_LL_SCHED_MAX_DELAY_ANY      (0x7fffffff)
 
-uint8_t g_ble_ll_sched_offset_ticks;
+static struct hal_timer g_ble_ll_sched_timer;
 
-#define BLE_LL_SCHED_ADV_WORST_CASE_USECS       \
-    (BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + BLE_LL_SCHED_ADV_MAX_USECS \
-     + XCVR_TX_SCHED_DELAY_USECS)
+uint8_t g_ble_ll_sched_offset_ticks;
 
 #if (BLE_LL_SCHED_DEBUG == 1)
 int32_t g_ble_ll_sched_max_late;
 int32_t g_ble_ll_sched_max_early;
 #endif
 
+typedef int (* ble_ll_sched_preempt_cb_t)(struct ble_ll_sched_item *sch,
+                                          struct ble_ll_sched_item *item);
+
 /* XXX: TODO:
  *  1) Add some accounting to the schedule code to see how late we are
  *  (min/max?)
@@ -61,12 +61,230 @@ int32_t g_ble_ll_sched_max_early;
  */
 
 /* Queue for timers */
-TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q;
+static TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q;
+static uint8_t g_ble_ll_sched_q_head_changed;
 
 #if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 struct ble_ll_sched_obj g_ble_ll_sched_data;
 #endif
 
+static int
+preempt_any(struct ble_ll_sched_item *sch,
+            struct ble_ll_sched_item *item)
+{
+    return 1;
+}
+
+static int
+preempt_none(struct ble_ll_sched_item *sch,
+             struct ble_ll_sched_item *item)
+{
+    return 0;
+}
+
+static int
+preempt_any_except_conn(struct ble_ll_sched_item *sch,
+                        struct ble_ll_sched_item *item)
+{
+    BLE_LL_ASSERT(sch->sched_type == BLE_LL_SCHED_TYPE_CONN);
+
+    if (item->sched_type != BLE_LL_SCHED_TYPE_CONN) {
+        return 1;
+    }
+
+    return ble_ll_conn_is_lru(sch->cb_arg, item->cb_arg);
+}
+
+static inline int
+ble_ll_sched_check_overlap(struct ble_ll_sched_item *sch1,
+                           struct ble_ll_sched_item *sch2)
+{
+    /* Note: item ranges are defined as [start, end) so items do not overlap
+     *       if one item starts at the same time as another ends.
+     */
+    return CPUTIME_GT(sch1->end_time, sch2->start_time) &&
+           CPUTIME_GT(sch2->end_time, sch1->start_time);
+}
+
+static void
+ble_ll_sched_preempt(struct ble_ll_sched_item *sch,
+                     struct ble_ll_sched_item *first)
+{
+    struct ble_ll_sched_item *entry;
+    struct ble_ll_sched_item *next;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    struct ble_ll_conn_sm *connsm;
+#endif
+
+    entry = first;
+
+    do {
+        next = TAILQ_NEXT(entry, link);
+
+        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
+        entry->enqueued = 0;
+
+        switch (entry->sched_type) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+            case BLE_LL_SCHED_TYPE_CONN:
+                connsm = (struct ble_ll_conn_sm *)entry->cb_arg;
+                ble_ll_event_send(&connsm->conn_ev_end);
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+            case BLE_LL_SCHED_TYPE_ADV:
+                ble_ll_adv_event_rmvd_from_sched(entry->cb_arg);
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+            case BLE_LL_SCHED_TYPE_SCAN_AUX:
+                ble_ll_scan_aux_break(entry->cb_arg);
+                break;
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+            case BLE_LL_SCHED_TYPE_PERIODIC:
+                ble_ll_adv_periodic_rmvd_from_sched(entry->cb_arg);
+                break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+            case BLE_LL_SCHED_TYPE_SYNC:
+                ble_ll_sync_rmvd_from_sched(entry->cb_arg);
+                break;
+#endif
+#endif
+#endif
+            default:
+                BLE_LL_ASSERT(0);
+                break;
+        }
+
+        entry = next;
+    } while (entry != sch);
+}
+
+static inline void
+ble_ll_sched_q_head_changed(void)
+{
+    if (g_ble_ll_sched_q_head_changed) {
+        return;
+    }
+
+    g_ble_ll_sched_q_head_changed = 1;
+
+    os_cputime_timer_stop(&g_ble_ll_sched_timer);
+}
+
+static inline void
+ble_ll_sched_restart(void)
+{
+    struct ble_ll_sched_item *first;
+
+    if (!g_ble_ll_sched_q_head_changed) {
+        return;
+    }
+
+    g_ble_ll_sched_q_head_changed = 0;
+
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+
+    ble_ll_rfmgmt_sched_changed(first);
+
+    if (first) {
+        os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
+    }
+}
+
+static int
+ble_ll_sched_insert(struct ble_ll_sched_item *sch, uint32_t max_delay,
+                    ble_ll_sched_preempt_cb_t preempt_cb)
+{
+    struct ble_ll_sched_item *preempt_first;
+    struct ble_ll_sched_item *first;
+    struct ble_ll_sched_item *entry;
+    uint32_t max_start_time;
+    uint32_t duration;
+
+    OS_ASSERT_CRITICAL();
+
+    preempt_first = NULL;
+
+    max_start_time = sch->start_time + max_delay;
+    duration = sch->end_time - sch->start_time;
+
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (!first) {
+        TAILQ_INSERT_HEAD(&g_ble_ll_sched_q, sch, link);
+        sch->enqueued = 1;
+        goto done;
+    }
+
+    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
+        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
+            TAILQ_INSERT_BEFORE(entry, sch, link);
+            sch->enqueued = 1;
+            goto done;
+        }
+
+        /* If current item overlaps our item check if we can preempt. If we
+         * cannot preempt, move our item past current item and see if it's
+         * still within allowed range.
+         */
+
+        if (ble_ll_sched_check_overlap(sch, entry)) {
+            if (preempt_cb(sch, entry)) {
+                if (!preempt_first) {
+                    preempt_first = entry;
+                }
+            } else {
+                preempt_first = NULL;
+                /*
+                 * For the 32768 Hz crystal in nrf chip, 1 tick is 30.517us.
+                 * The connection state machine use anchor point to store the
+                 * cpu ticks and anchor_point_usec to store the remainder.
+                 * Therefore, to compensate the inaccuracy of the crystal, the
+                 * ticks of anchor_point will be add with 1 once the value of
+                 * anchor_point_usec exceed 31. If two connections have same
+                 * connection interval, the time difference between the two
+                 * start of schedule item will decreased 1, which lead to
+                 * an overlap. To prevent this from happenning, we set the
+                 * start_time of sch to 1 cpu tick after the end_time of entry.
+                 */
+                sch->start_time = entry->end_time + 1;
+
+                if ((max_delay == 0) || CPUTIME_GEQ(sch->start_time,
+                                                    max_start_time)) {
+                    sch->enqueued = 0;
+                    goto done;
+                }
+
+                sch->end_time = sch->start_time + duration;
+            }
+        }
+    }
+
+    if (!entry) {
+        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
+        sch->enqueued = 1;
+    }
+
+done:
+    if (preempt_first) {
+        BLE_LL_ASSERT(sch->enqueued);
+        ble_ll_sched_preempt(sch, preempt_first);
+    }
+
+    /* Pause scheduler if inserted as 1st item, we do not want to miss this
+     * one. Caller should restart outside critical section.
+     */
+    if (TAILQ_FIRST(&g_ble_ll_sched_q) == sch) {
+        BLE_LL_ASSERT(sch->enqueued);
+        ble_ll_sched_q_head_changed();
+    }
+
+    return sch->enqueued ? 0 : -1;
+}
+
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 /**
  * Checks if two events in the schedule will overlap in time. NOTE: consecutive
  * schedule items can end and start at the same time.
@@ -97,6 +315,7 @@ ble_ll_sched_is_overlap(struct ble_ll_sched_item *s1,
 
     return rc;
 }
+#endif
 
 /*
  * Determines if the schedule item overlaps the currently running schedule
@@ -105,39 +324,21 @@ ble_ll_sched_is_overlap(struct ble_ll_sched_item *s1,
 static int
 ble_ll_sched_overlaps_current(struct ble_ll_sched_item *sch)
 {
-    int rc;
+    int rc = 0;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
     uint32_t ce_end_time;
 
-    rc = 0;
     if (ble_ll_state_get() == BLE_LL_STATE_CONNECTION) {
         ce_end_time = ble_ll_conn_get_ce_end_time();
         if (CPUTIME_GT(ce_end_time, sch->start_time)) {
             rc = 1;
         }
     }
+#endif
     return rc;
 }
 
-static int
-ble_ll_sched_conn_overlap(struct ble_ll_sched_item *entry)
-{
-    int rc;
-    struct ble_ll_conn_sm *connsm;
-
-    /* Should only be advertising or a connection here */
-    if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN) {
-        connsm = (struct ble_ll_conn_sm *)entry->cb_arg;
-        entry->enqueued = 0;
-        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-        ble_ll_event_send(&connsm->conn_ev_end);
-        rc = 0;
-    } else {
-        rc = -1;
-    }
-
-    return rc;
-}
-
+#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
 static struct ble_ll_sched_item *
 ble_ll_sched_insert_if_empty(struct ble_ll_sched_item *sch)
 {
@@ -150,31 +351,41 @@ ble_ll_sched_insert_if_empty(struct ble_ll_sched_item *sch)
     }
     return entry;
 }
+#endif
 
 int
 ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
 {
-    int rc;
-    os_sr_t sr;
-    uint32_t usecs;
     struct ble_ll_sched_item *sch;
-    struct ble_ll_sched_item *start_overlap;
-    struct ble_ll_sched_item *end_overlap;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_conn_sm *tmp;
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    uint32_t usecs;
+#endif
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
     sch = &connsm->conn_sch;
 
     /* Set schedule start and end times */
     sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks;
-    if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
+    switch (connsm->conn_role) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
+    case BLE_LL_CONN_ROLE_MASTER:
+        sch->remainder = connsm->anchor_point_usecs;
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_CONN_ROLE_SLAVE:
         usecs = connsm->slave_cur_window_widening;
         sch->start_time -= (os_cputime_usecs_to_ticks(usecs) + 1);
         sch->remainder = 0;
-    } else {
-        sch->remainder = connsm->anchor_point_usecs;
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
+
     sch->end_time = connsm->ce_end_time;
 
     /* Better be past current time or we just leave */
@@ -182,7 +393,6 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         return -1;
     }
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
     if (ble_ll_sched_overlaps_current(sch)) {
@@ -190,98 +400,11 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
         return -1;
     }
 
-    /* Stop timer since we will add an element */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-
-    start_overlap = NULL;
-    end_overlap = NULL;
-    rc = 0;
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-           if (entry->sched_type == BLE_LL_SCHED_TYPE_CONN &&
-                            !ble_ll_conn_is_lru((struct ble_ll_conn_sm *)sch->cb_arg,
-                                    (struct ble_ll_conn_sm *)entry->cb_arg)) {
-                /* Only insert if this element is older than all that we
-                 * overlap
-                 */
-                start_overlap = NULL;
-                rc = -1;
-                break;
-            }
-
-            if (start_overlap == NULL) {
-                start_overlap = entry;
-                end_overlap = entry;
-            } else {
-                end_overlap = entry;
-            }
-        } else {
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                rc = 0;
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-        }
-    }
-
-    if (!rc) {
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        sch->enqueued = 1;
-    }
-
-    /* Remove first to last scheduled elements */
-    entry = start_overlap;
-    while (entry) {
-        start_overlap = TAILQ_NEXT(entry,link);
-        switch (entry->sched_type) {
-        case BLE_LL_SCHED_TYPE_CONN:
-            tmp = (struct ble_ll_conn_sm *)entry->cb_arg;
-            ble_ll_event_send(&tmp->conn_ev_end);
-            break;
-        case BLE_LL_SCHED_TYPE_ADV:
-            ble_ll_adv_event_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg);
-            break;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-        case BLE_LL_SCHED_TYPE_AUX_SCAN:
-            ble_ll_scan_end_adv_evt((struct ble_ll_aux_data *)entry->cb_arg);
-            break;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-        case BLE_LL_SCHED_TYPE_PERIODIC:
-            ble_ll_adv_periodic_rmvd_from_sched((struct ble_ll_adv_sm *)entry->cb_arg);
-            break;
-        case BLE_LL_SCHED_TYPE_SYNC:
-            ble_ll_sync_rmvd_from_sched((struct ble_ll_sync_sm *)entry->cb_arg);
-            break;
-#endif
-#endif
-        default:
-            BLE_LL_ASSERT(0);
-            break;
-        }
-
-        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-        entry->enqueued = 0;
-
-        if (entry == end_overlap) {
-            break;
-        }
-        entry = start_overlap;
-    }
-
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_any_except_conn);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -401,7 +524,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
         }
     }
     earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
-                      BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
+                      BLE_LL_SCHED_TICKS_PER_SLOT;
     itvl_t = connsm->conn_itvl_ticks;
 
     /* We have to find a place for this schedule */
@@ -536,27 +659,17 @@ int
 ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
                         struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len)
 {
-    int rc;
-    os_sr_t sr;
-    uint8_t req_slots;
-    uint32_t initial_start;
+    struct ble_ll_sched_item *sch;
+    uint32_t orig_start_time;
     uint32_t earliest_start;
-    uint32_t earliest_end;
-    uint32_t dur;
-    uint32_t itvl_t;
+    uint32_t min_win_offset;
+    uint32_t max_delay;
     uint32_t adv_rxend;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *sch;
-
-    /*
-     * XXX: TODO this code assumes the advertisement and connect request were
-     * sent at 1Mbps.
-     */
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
-    rc = -1;
     sch = &connsm->conn_sch;
-    req_slots = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS);
 
     /* XXX:
      * The calculations for the 32kHz crystal bear alot of explanation. The
@@ -599,7 +712,6 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
      * wont be listening. We could do better calculation if we wanted to use
      * a transmit window of 1 as opposed to 2, but for now we dont care.
      */
-    dur = req_slots * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
     adv_rxend = os_cputime_get32();
     if (ble_hdr->rxinfo.channel >= BLE_PHY_NUM_DATA_CHANS) {
         /*
@@ -631,86 +743,38 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
             BLE_LL_ASSERT(0);
         }
     }
-    earliest_start += MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
-                      BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
-    earliest_end = earliest_start + dur;
-    itvl_t = connsm->conn_itvl_ticks;
 
-    /* We have to find a place for this schedule */
-    OS_ENTER_CRITICAL(sr);
+    sch->start_time = earliest_start - g_ble_ll_sched_offset_ticks;
+    sch->end_time = earliest_start + MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) *
+                                     BLE_LL_SCHED_TICKS_PER_SLOT;
 
-    /* The schedule item must occur after current running item (if any) */
-    sch->start_time = earliest_start;
-    initial_start = earliest_start;
+    orig_start_time = sch->start_time;
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible */
-        rc = 0;
-        connsm->tx_win_off = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET);
-    } else {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* Set these because overlap function needs them to be set */
-            sch->start_time = earliest_start;
-            sch->end_time = earliest_end;
+    min_win_offset = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET) *
+                     BLE_LL_SCHED_TICKS_PER_SLOT;
+    sch->start_time += min_win_offset;
+    sch->end_time += min_win_offset;
 
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                if ((earliest_start - initial_start) <= itvl_t) {
-                    rc = 0;
-                    TAILQ_INSERT_BEFORE(entry, sch, link);
-                }
-                break;
-            }
+    max_delay = connsm->conn_itvl_ticks - min_win_offset;
 
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* Earliest start is end of this event since we overlap */
-                earliest_start = entry->end_time;
-                earliest_end = earliest_start + dur;
-            }
-        }
+    OS_ENTER_CRITICAL(sr);
 
-        /* Must be able to schedule within one connection interval */
-        if (!entry) {
-            if ((earliest_start - initial_start) <= itvl_t) {
-                rc = 0;
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-            }
-        }
+    rc = ble_ll_sched_insert(sch, max_delay, preempt_none);
 
-        if (!rc) {
-            /* calculate number of window offsets. Each offset is 1.25 ms */
-            sch->enqueued = 1;
-            /*
-             * NOTE: we dont add sched offset ticks as we want to under-estimate
-             * the transmit window slightly since the window size is currently
-             * 2 when using a 32768 crystal.
-             */
-            dur = os_cputime_ticks_to_usecs(earliest_start - initial_start);
-            connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS;
-        }
-    }
+    if (rc == 0) {
+        connsm->tx_win_off = os_cputime_ticks_to_usecs(sch->start_time -
+                                                       orig_start_time) /
+                             BLE_LL_CONN_TX_OFF_USECS;
 
-    if (!rc) {
-        sch->start_time = earliest_start;
-        sch->end_time = earliest_end;
-        /*
-         * Since we have the transmit window to transmit in, we dont need
-         * to set the anchor point usecs; just transmit to the nearest tick.
-         */
-        connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks;
+        connsm->anchor_point = sch->start_time + g_ble_ll_sched_offset_ticks;
         connsm->anchor_point_usecs = 0;
-        connsm->ce_end_time = earliest_end;
-    }
+        connsm->ce_end_time = sch->end_time;
 
-    /* Get head of list to restart timer */
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    ble_ll_rfmgmt_sched_changed(sch);
+    }
 
     OS_EXIT_CRITICAL(sr);
 
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -728,15 +792,11 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
 int
 ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
 {
-    int rc;
-    os_sr_t sr;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *next_sch;
     struct ble_ll_sched_item *sch;
-    int first = 0;
+    os_sr_t sr;
+    int rc;
 
     /* Get schedule element from connection */
-    rc = -1;
     sch = &connsm->conn_sch;
 
     /* Set schedule start and end times */
@@ -751,73 +811,34 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm)
     sch->end_time = connsm->ce_end_time;
     sch->remainder = 0;
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
-    /* The schedule item must occur after current running item (if any) */
-    if (ble_ll_sched_overlaps_current(sch)) {
-        OS_EXIT_CRITICAL(sr);
-        return rc;
-    }
-
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        /* Nothing in schedule. Schedule as soon as possible */
-        rc = 0;
-        first = 1;
-    } else {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        while (1) {
-            next_sch = entry->link.tqe_next;
-            /* Insert if event ends before next starts */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                rc = 0;
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* If we overlap with a connection, we re-schedule */
-                if (ble_ll_sched_conn_overlap(entry)) {
-                    break;
-                }
-            }
+    rc = ble_ll_sched_insert(sch, 0, preempt_any);
 
-            /* Move to next entry */
-            entry = next_sch;
+    OS_EXIT_CRITICAL(sr);
 
-            /* Insert at tail if none left to check */
-            if (!entry) {
-                rc = 0;
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-                break;
-            }
-        }
+    ble_ll_sched_restart();
 
-        if (!rc) {
-            sch->enqueued = 1;
-        }
+    return rc;
+}
 
-        next_sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (next_sch == sch) {
-            first = 1;
-        } else {
-            sch = next_sch;
-        }
-    }
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+static inline uint32_t
+usecs_to_ticks_fast(uint32_t usecs)
+{
+    uint32_t ticks;
 
-    if (first) {
-        ble_ll_rfmgmt_sched_changed(sch);
+    if (usecs <= 31249) {
+        ticks = (usecs * 137439) / 4194304;
+    } else {
+        ticks = os_cputime_usecs_to_ticks(usecs);
     }
 
-    OS_EXIT_CRITICAL(sr);
-
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-
-    return rc;
+    return ticks;
 }
+#endif
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 /*
  * Determines if the schedule item overlaps the currently running schedule
  * item. This function cares about connection and sync.
@@ -830,9 +851,11 @@ ble_ll_sched_sync_overlaps_current(struct ble_ll_sched_item *sch)
 
     state = ble_ll_state_get();
     switch (state) {
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
     case BLE_LL_STATE_CONNECTION:
         end_time = ble_ll_conn_get_ce_end_time();
         break;
+#endif
     case BLE_LL_STATE_SYNC:
         end_time = ble_ll_sync_get_event_end_time();
         break;
@@ -849,7 +872,6 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
                              uint32_t window_widening,
                              int8_t phy_mode)
 {
-    struct ble_ll_sched_item *entry;
     uint8_t start_time_rem_usecs;
     uint8_t window_rem_usecs;
     uint32_t window_ticks;
@@ -889,7 +911,6 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
         return -1;
     }
 
-    /* We have to find a place for this schedule */
     OS_ENTER_CRITICAL(sr);
 
     if (ble_ll_sched_sync_overlaps_current(sch)) {
@@ -897,43 +918,11 @@ ble_ll_sched_sync_reschedule(struct ble_ll_sched_item *sch,
         return -1;
     }
 
-    /* Try to find slot for sync scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            rc = -1;
-            break;
-        }
-    }
-
-    if (!entry) {
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -943,7 +932,6 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
                   uint32_t beg_cputime, uint32_t rem_usecs,
                   uint32_t offset, int8_t phy_mode)
 {
-    struct ble_ll_sched_item *entry;
     uint32_t start_time_rem_usecs;
     uint32_t off_rem_usecs;
     uint32_t start_time;
@@ -953,7 +941,7 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
     os_sr_t sr;
     int rc = 0;
 
-    off_ticks = os_cputime_usecs_to_ticks(offset);
+    off_ticks = usecs_to_ticks_fast(offset);
     off_rem_usecs = offset - os_cputime_ticks_to_usecs(off_ticks);
 
     start_time = beg_cputime + off_ticks;
@@ -965,7 +953,7 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
 
     dur = ble_ll_pdu_tx_time_get(MYNEWT_VAL(BLE_LL_SCHED_SCAN_SYNC_PDU_LEN),
                                   phy_mode);
-    end_time = start_time + os_cputime_usecs_to_ticks(dur);
+    end_time = start_time + usecs_to_ticks_fast(dur);
 
     start_time -= g_ble_ll_sched_offset_ticks;
 
@@ -975,51 +963,14 @@ ble_ll_sched_sync(struct ble_ll_sched_item *sch,
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        goto done;
-    }
-
-    /* Try to find slot for scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            rc = -1;
-            break;
-        }
-    }
-
-    if (!entry) {
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     STATS_INC(ble_ll_stats, sync_scheduled);
+
     return rc;
 }
 #endif
@@ -1029,257 +980,87 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch, ble_ll_sched_adv_new_cb cb,
                      void *arg)
 {
     os_sr_t sr;
-    uint32_t adv_start;
-    uint32_t duration;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *orig;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
-    orig = sch;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        adv_start = sch->start_time;
-    } else {
-        /* XXX: no need to stop timer if not first on list. Modify code? */
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                /* Earliest start is end of this event since we overlap */
-                sch->start_time = entry->end_time;
-                sch->end_time = sch->start_time + duration;
-            }
-        }
 
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        adv_start = sch->start_time;
-
-        sch->enqueued = 1;
-
-        /* Restart with head of list */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    }
-
-    if (cb) {
-        cb((struct ble_ll_adv_sm *)orig->cb_arg, adv_start, arg);
-    }
+    rc = ble_ll_sched_insert(sch, BLE_LL_SCHED_MAX_DELAY_ANY,
+                             preempt_none);
+    BLE_LL_ASSERT(rc == 0);
 
-    if (orig == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    }
+    cb(sch->cb_arg, sch->start_time, arg);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
-    return 0;
+    return rc;
 }
 
 int
-ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, uint32_t *start,
-                          bool after_overlap)
+ble_ll_sched_periodic_adv(struct ble_ll_sched_item *sch, bool first_event)
 {
-    int rc = 0;
     os_sr_t sr;
-    uint32_t adv_start;
-    uint32_t duration;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *orig = sch;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (!entry) {
-        adv_start = sch->start_time;
-    } else {
-        /* XXX: no need to stop timer if not first on list. Modify code? */
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-            /* We can insert if before entry in list */
-            if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                TAILQ_INSERT_BEFORE(entry, sch, link);
-                break;
-            }
-
-            /* Check for overlapping events */
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                if (after_overlap) {
-                    /* Earliest start is end of this event since we overlap */
-                    sch->start_time = entry->end_time;
-                    sch->end_time = sch->start_time + duration;
-                } else {
-                    rc = -1;
-                    break;
-                }
-            }
-        }
-
-        if (!entry) {
-            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        }
-        adv_start = sch->start_time;
-
-        if (!rc) {
-            sch->enqueued = 1;
-        }
-
-        /* Restart with head of list */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    }
-
-    if (!rc) {
-        *start = adv_start;
-    }
 
-    if (orig == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
+    if (first_event) {
+        rc = ble_ll_sched_insert(sch, BLE_LL_SCHED_MAX_DELAY_ANY,
+                                 preempt_none);
+    } else {
+        rc = ble_ll_sched_insert(sch, 0, preempt_any);
     }
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
 
 int
-ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch, uint32_t *start,
+ble_ll_sched_adv_reschedule(struct ble_ll_sched_item *sch,
                             uint32_t max_delay_ticks)
 {
-    int rc;
-    os_sr_t sr;
-    uint32_t orig_start;
-    uint32_t duration;
+    struct ble_ll_sched_item *next;
+    uint32_t max_end_time;
     uint32_t rand_ticks;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *next_sch;
-    struct ble_ll_sched_item *before;
-    struct ble_ll_sched_item *start_overlap;
-    struct ble_ll_sched_item *end_overlap;
-
-    /* Get length of schedule item */
-    duration = sch->end_time - sch->start_time;
+    os_sr_t sr;
+    int rc;
 
-    /* Add maximum randomization delay to end */
-    rand_ticks = max_delay_ticks;
-    sch->end_time += max_delay_ticks;
+    max_end_time = sch->end_time + max_delay_ticks;
 
-    start_overlap = NULL;
-    end_overlap = NULL;
-    before = NULL;
-    rc = 0;
     OS_ENTER_CRITICAL(sr);
 
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (entry) {
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        while (1) {
-            next_sch = entry->link.tqe_next;
-            if (ble_ll_sched_is_overlap(sch, entry)) {
-                if (start_overlap == NULL) {
-                    start_overlap = entry;
-                    end_overlap = entry;
-                } else {
-                    end_overlap = entry;
-                }
-            } else {
-                if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                    before = entry;
-                    break;
-                }
-            }
-
-            entry = next_sch;
-            if (entry == NULL) {
-                break;
-            }
-        }
+    /* Try to schedule as early as possible but no later than max allowed delay.
+     * If succeeded, randomize start time to be within max allowed delay from
+     * the original start time but make sure it ends before next scheduled item.
+     */
 
-        /*
-         * If there is no overlap, we either insert before the 'before' entry
-         * or we insert at the end if there is no before entry.
-         */
-        if (start_overlap == NULL) {
-            if (before) {
-                TAILQ_INSERT_BEFORE(before, sch, link);
-            } else {
-                TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
+    rc = ble_ll_sched_insert(sch, max_delay_ticks, preempt_none);
+    if (rc == 0) {
+        next = TAILQ_NEXT(sch, link);
+        if (next) {
+            if (CPUTIME_LT(next->start_time, max_end_time)) {
+                max_end_time = next->start_time;
             }
+            rand_ticks = max_end_time - sch->end_time;
         } else {
-            /*
-             * This item will overlap with others. See if we can fit it in
-             * with original duration.
-             */
-            before = NULL;
-            orig_start = sch->start_time;
-            entry = start_overlap;
-            sch->end_time = sch->start_time + duration;
-            while (1) {
-                next_sch = entry->link.tqe_next;
-                if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-                    rand_ticks = entry->start_time - sch->end_time;
-                    before = entry;
-                    TAILQ_INSERT_BEFORE(before, sch, link);
-                    break;
-                } else {
-                    sch->start_time = entry->end_time;
-                    sch->end_time = sch->start_time + duration;
-                }
-
-                if (entry == end_overlap) {
-                    rand_ticks = (orig_start + max_delay_ticks) - sch->start_time;
-                    if (rand_ticks > max_delay_ticks) {
-                        /* No place for advertisement. */
-                        rc = -1;
-                    } else {
-                        if (next_sch == NULL) {
-                            TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-                        } else {
-                            TAILQ_INSERT_BEFORE(next_sch, sch, link);
-                        }
-                    }
-                    break;
-                }
-                entry = next_sch;
-                BLE_LL_ASSERT(entry != NULL);
-            }
+            rand_ticks = max_delay_ticks;
         }
-    }
 
-    if (!rc) {
-        sch->enqueued = 1;
         if (rand_ticks) {
-            sch->start_time += ble_ll_rand() % rand_ticks;
+            rand_ticks = ble_ll_rand() % rand_ticks;
         }
-        sch->end_time = sch->start_time + duration;
-        *start = sch->start_time;
 
-        if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) {
-            ble_ll_rfmgmt_sched_changed(sch);
-        }
+        sch->start_time += rand_ticks;
+        sch->end_time += rand_ticks;
     }
 
     OS_EXIT_CRITICAL(sr);
 
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -1289,36 +1070,38 @@ ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch)
 {
     uint8_t lls;
     os_sr_t sr;
-    struct ble_ll_sched_item *entry;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
 
     lls = ble_ll_state_get();
-    if ((lls == BLE_LL_STATE_ADV) || (lls == BLE_LL_STATE_CONNECTION) ||
-            (lls == BLE_LL_STATE_SYNC)) {
-        goto adv_resched_pdu_fail;
-    }
-
-    entry = ble_ll_sched_insert_if_empty(sch);
-    if (entry) {
-        /* If we overlap with the first item, simply re-schedule */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            goto adv_resched_pdu_fail;
-        }
-        os_cputime_timer_stop(&g_ble_ll_sched_timer);
-        TAILQ_INSERT_BEFORE(entry, sch, link);
-        sch->enqueued = 1;
+    switch(lls) {
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    case BLE_LL_STATE_ADV:
+        OS_EXIT_CRITICAL(sr);
+        return -1;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_STATE_CONNECTION:
+        OS_EXIT_CRITICAL(sr);
+        return -1;
+#endif
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    case BLE_LL_STATE_SYNC:
+        OS_EXIT_CRITICAL(sr);
+        return -1;
+#endif
+    default:
+        break;
     }
 
-    ble_ll_rfmgmt_sched_changed(TAILQ_FIRST(&g_ble_ll_sched_q));
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-    return 0;
 
-adv_resched_pdu_fail:
-    OS_EXIT_CRITICAL(sr);
-    return -1;
+    ble_ll_sched_restart();
+
+    return rc;
 }
 
 /**
@@ -1331,75 +1114,71 @@ ble_ll_sched_adv_resched_pdu(struct ble_ll_sched_item *sch)
 int
 ble_ll_sched_rmv_elem(struct ble_ll_sched_item *sch)
 {
+    uint8_t first_removed;
     os_sr_t sr;
-    struct ble_ll_sched_item *first;
-    int rc = 1;
+    int rc;
 
-    if (!sch) {
-        return rc;
-    }
+    BLE_LL_ASSERT(sch);
 
     OS_ENTER_CRITICAL(sr);
+
+    first_removed = 0;
+
     if (sch->enqueued) {
-        first = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (first == sch) {
-            os_cputime_timer_stop(&g_ble_ll_sched_timer);
+        if (sch == TAILQ_FIRST(&g_ble_ll_sched_q)) {
+            first_removed = 1;
         }
 
         TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
         sch->enqueued = 0;
+
         rc = 0;
+    } else {
+        rc = 1;
+    }
 
-        if (first == sch) {
-            first = TAILQ_FIRST(&g_ble_ll_sched_q);
-            if (first) {
-                os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
-            }
-            ble_ll_rfmgmt_sched_changed(first);
-        }
+    if (first_removed) {
+        ble_ll_sched_q_head_changed();
     }
+
     OS_EXIT_CRITICAL(sr);
 
+    ble_ll_sched_restart();
+
     return rc;
 }
 
 void
 ble_ll_sched_rmv_elem_type(uint8_t type, sched_remove_cb_func remove_cb)
 {
-    os_sr_t sr;
-    struct ble_ll_sched_item *entry;
     struct ble_ll_sched_item *first;
+    struct ble_ll_sched_item *entry;
+    uint8_t first_removed;
+    os_sr_t sr;
 
     OS_ENTER_CRITICAL(sr);
-    first = TAILQ_FIRST(&g_ble_ll_sched_q);
 
-    if (!first) {
-        OS_EXIT_CRITICAL(sr);
-        return;
+    first = TAILQ_FIRST(&g_ble_ll_sched_q);
+    if (first->sched_type == type) {
+        first_removed = 1;
     }
 
     TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        if (entry->sched_type == type) {
-            if (first == entry) {
-                os_cputime_timer_stop(&g_ble_ll_sched_timer);
-                first = NULL;
-            }
-
-            TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
-            remove_cb(entry);
-            entry->enqueued = 0;
+        if (entry->sched_type != type) {
+            continue;
         }
+        TAILQ_REMOVE(&g_ble_ll_sched_q, entry, link);
+        remove_cb(entry);
+        entry->enqueued = 0;
     }
 
-    if (!first) {
-        first = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (first) {
-            os_cputime_timer_start(&g_ble_ll_sched_timer, first->start_time);
-        }
-        ble_ll_rfmgmt_sched_changed(first);
+    if (first_removed) {
+        ble_ll_sched_q_head_changed();
     }
 
     OS_EXIT_CRITICAL(sr);
+
+    ble_ll_sched_restart();
 }
 
 /**
@@ -1426,20 +1205,6 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch)
         goto sched;
     }
 
-    /* If aux scan scheduled and LL is in state when scanner is running
-     * in 3 states:
-     * BLE_LL_STATE_SCANNING
-     * BLE_LL_STATE_INITIATING
-     * BLE_LL_STATE_STANDBY
-     *
-     * Let scanner to decide to disable phy or not.
-     */
-    if (sch->sched_type == BLE_LL_SCHED_TYPE_AUX_SCAN) {
-        if (lls == BLE_LL_STATE_INITIATING || lls == BLE_LL_STATE_SCANNING) {
-            goto sched;
-        }
-    }
-
     /*
      * This is either an advertising event or connection event start. If
      * we are scanning or initiating just stop it.
@@ -1448,32 +1213,51 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch)
     /* We have to disable the PHY no matter what */
     ble_phy_disable();
 
-    if (lls == BLE_LL_STATE_SCANNING) {
+    switch (lls) {
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    case BLE_LL_STATE_SCANNING:
         ble_ll_state_set(BLE_LL_STATE_STANDBY);
         ble_ll_scan_halt();
-    } else if (lls == BLE_LL_STATE_INITIATING) {
-        ble_ll_state_set(BLE_LL_STATE_STANDBY);
-        ble_ll_scan_halt();
-        /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */
-        ble_ll_conn_reset_pending_aux_conn_rsp();
-    } else if (lls == BLE_LL_STATE_ADV) {
-        STATS_INC(ble_ll_stats, sched_state_adv_errs);
-        ble_ll_adv_halt();
+        break;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
-    } else if (lls == BLE_LL_STATE_SYNC) {
+    case BLE_LL_STATE_SYNC:
         STATS_INC(ble_ll_stats, sched_state_sync_errs);
         ble_ll_sync_halt();
+        break;
 #endif
-    } else {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+    case BLE_LL_STATE_SCAN_AUX:
+        ble_ll_state_set(BLE_LL_STATE_STANDBY);
+        ble_ll_scan_aux_halt();
+        break;
+#endif
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    case BLE_LL_STATE_ADV:
+        STATS_INC(ble_ll_stats, sched_state_adv_errs);
+        ble_ll_adv_halt();
+        break;
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
+    case BLE_LL_STATE_CONNECTION:
         STATS_INC(ble_ll_stats, sched_state_conn_errs);
         ble_ll_conn_event_halt();
+        break;
+#endif
+    default:
+        BLE_LL_ASSERT(0);
+        break;
     }
 
 sched:
-    BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 1);
     BLE_LL_ASSERT(sch->sched_cb);
+
+    BLE_LL_DEBUG_GPIO(SCHED_ITEM, 1);
     rc = sch->sched_cb(sch);
-    BLE_LL_DEBUG_GPIO(SCHED_ITEM_CB, 0);
+    if (rc != BLE_LL_SCHED_STATE_RUNNING) {
+        BLE_LL_DEBUG_GPIO(SCHED_ITEM, 0);
+    }
+
     return rc;
 }
 
@@ -1510,14 +1294,11 @@ ble_ll_sched_run(void *arg)
         /* Remove schedule item and execute the callback */
         TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
         sch->enqueued = 0;
+        g_ble_ll_sched_q_head_changed = 1;
+
         ble_ll_sched_execute_item(sch);
 
-        /* Restart if there is an item on the schedule */
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-        if (sch) {
-            os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
-        }
-        ble_ll_rfmgmt_sched_changed(sch);
+        ble_ll_sched_restart();
     }
 
     BLE_LL_DEBUG_GPIO(SCHED_RUN, 0);
@@ -1553,164 +1334,29 @@ ble_ll_sched_next_time(uint32_t *next_event_time)
 }
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
-/**
- * Called to check if there is place for a planned scan req.
- *
- * @param chan
- * @param phy_mode
- *
- * @return int 0: Clear for scan req 1: there is an upcoming event
- */
 int
-ble_ll_sched_scan_req_over_aux_ptr(uint32_t chan, uint8_t phy_mode)
+ble_ll_sched_scan_aux(struct ble_ll_sched_item *sch, uint32_t pdu_time,
+                      uint8_t pdu_time_rem, uint32_t offset_us)
 {
-    struct ble_ll_sched_item *sch;
-    uint32_t usec_dur;
-    uint32_t now = os_cputime_get32();
-
-    /* Lets calculate roughly how much time we need for scan req and scan rsp */
-    usec_dur = ble_ll_pdu_tx_time_get(BLE_SCAN_REQ_LEN, phy_mode);
-    if (chan >=  BLE_PHY_NUM_DATA_CHANS) {
-        usec_dur += ble_ll_pdu_tx_time_get(BLE_SCAN_RSP_MAX_LEN, phy_mode);
-    } else {
-        usec_dur += ble_ll_pdu_tx_time_get(BLE_SCAN_RSP_MAX_EXT_LEN, phy_mode);
-    }
-
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    while (sch) {
-        /* Let's check if there is no scheduled item which want to start within
-         * given usecs.*/
-        if (CPUTIME_GT(sch->start_time, now + os_cputime_usecs_to_ticks(usec_dur))) {
-            /* We are fine. Have time for scan req */
-            return 0;
-        }
-
-        /* There is something in the scheduler. If it is not aux ptr we assume
-         * it is more important that scan req
-         */
-        if (sch->sched_type != BLE_LL_SCHED_TYPE_AUX_SCAN) {
-            return 1;
-        }
-
-        ble_ll_scan_end_adv_evt((struct ble_ll_aux_data *)sch->cb_arg);
-        TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 0;
-        sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-    }
-    return 0;
-}
-
-/**
- * Called to schedule a aux scan.
- *
- * Context: Interrupt
- *
- * @param ble_hdr
- * @param scansm
- * @param aux_scan
- *
- * @return 0 on success, 1 otherwise
- */
-int
-ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr,
-                      struct ble_ll_scan_sm *scansm,
-                      struct ble_ll_aux_data *aux_scan)
-{
-    int rc = 1;
+    uint32_t offset_ticks;
     os_sr_t sr;
-    uint32_t off_ticks;
-    uint32_t off_rem_usecs;
-    uint32_t start_time;
-    uint32_t start_time_rem_usecs;
-    uint32_t end_time;
-    uint32_t dur;
-    struct ble_ll_sched_item *entry;
-    struct ble_ll_sched_item *sch;
-    int phy_mode;
-
-    sch = &aux_scan->sch;
-    BLE_LL_ASSERT(sch->cb_arg == NULL);
-
-    off_ticks = os_cputime_usecs_to_ticks(aux_scan->offset);
-    off_rem_usecs = aux_scan->offset - os_cputime_ticks_to_usecs(off_ticks);
-
-    start_time = ble_hdr->beg_cputime + off_ticks;
-    start_time_rem_usecs = ble_hdr->rem_usecs + off_rem_usecs;
-    if (start_time_rem_usecs >= 31) {
-        start_time++;
-        start_time_rem_usecs -= 31;
-    }
-    start_time -= g_ble_ll_sched_offset_ticks;
+    int rc;
 
-    /* Let's calculate time we reserve for aux packet. For now we assume to wait
-     * for fixed number of bytes and handle possible interrupting it in
-     * ble_ll_sched_execute_item(). This is because aux packet can be up to
-     * 256bytes and we don't want to block sched that long
-     */
-    phy_mode = ble_ll_phy_to_phy_mode(aux_scan->aux_phy,
-                                      BLE_HCI_LE_PHY_CODED_ANY);
-    dur = ble_ll_pdu_tx_time_get(MYNEWT_VAL(BLE_LL_SCHED_SCAN_AUX_PDU_LEN),
-                                 phy_mode);
-    end_time = start_time + os_cputime_usecs_to_ticks(dur);
+    offset_us += pdu_time_rem;
+    offset_ticks = usecs_to_ticks_fast(offset_us);
 
-    sch->start_time = start_time;
-    sch->remainder = start_time_rem_usecs;
-    sch->end_time = end_time;
+    sch->start_time = pdu_time + offset_ticks - g_ble_ll_sched_offset_ticks;
+    sch->remainder = offset_us - os_cputime_ticks_to_usecs(offset_ticks);
+    /* TODO: make some sane slot reservation */
+    sch->end_time = sch->start_time + usecs_to_ticks_fast(5000);
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        rc = 0;
-        goto done;
-    }
-
-    /* Try to find slot for aux scan. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (CPUTIME_LEQ(sch->end_time, entry->start_time)) {
-            rc = 0;
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            break;
-        }
-    }
-
-    if (!entry) {
-        rc = 0;
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
-
-    if (rc == 0) {
-        sch->cb_arg = ble_ll_scan_aux_data_ref(aux_scan);
-        STATS_INC(ble_ll_stats, aux_scheduled);
-    }
-
-    /* Get head of list to restart timer */
-    entry = TAILQ_FIRST(&g_ble_ll_sched_q);
-    if (entry == sch) {
-        ble_ll_rfmgmt_sched_changed(sch);
-    } else {
-        sch = entry;
-    }
+    rc = ble_ll_sched_insert(sch, 0, preempt_none);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    ble_ll_sched_restart();
 
     return rc;
 }
@@ -1719,57 +1365,18 @@ ble_ll_sched_aux_scan(struct ble_mbuf_hdr *ble_hdr,
 #if MYNEWT_VAL(BLE_LL_DTM)
 int ble_ll_sched_dtm(struct ble_ll_sched_item *sch)
 {
-    int rc;
     os_sr_t sr;
-    struct ble_ll_sched_item *entry;
+    int rc;
 
     OS_ENTER_CRITICAL(sr);
 
-    if (!ble_ll_sched_insert_if_empty(sch)) {
-        /* Nothing in schedule. Schedule as soon as possible
-         * If we are here it means sch has been added to the scheduler */
-        rc = 0;
-        goto done;
-    }
-
-    /* Try to find slot for test. */
-    os_cputime_timer_stop(&g_ble_ll_sched_timer);
-    TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
-        /* We can insert if before entry in list */
-        if (sch->end_time <= entry->start_time) {
-            rc = 0;
-            TAILQ_INSERT_BEFORE(entry, sch, link);
-            sch->enqueued = 1;
-            break;
-        }
-
-        /* Check for overlapping events. For now drop if it overlaps with
-         * anything. We can make it smarter later on
-         */
-        if (ble_ll_sched_is_overlap(sch, entry)) {
-            OS_EXIT_CRITICAL(sr);
-            return -1;
-        }
-    }
-
-    if (!entry) {
-        rc = 0;
-        TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
-        sch->enqueued = 1;
-    }
-
-done:
-
-    /* Get head of list to restart timer */
-    sch = TAILQ_FIRST(&g_ble_ll_sched_q);
-
-    ble_ll_rfmgmt_sched_changed(sch);
+    rc = ble_ll_sched_insert(sch, 0, preempt_any);
 
     OS_EXIT_CRITICAL(sr);
 
-    /* Restart timer */
-    BLE_LL_ASSERT(sch != NULL);
-    os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
+    if (rc == 0) {
+        ble_ll_sched_restart();
+    }
 
     return rc;
 }
@@ -1794,7 +1401,7 @@ ble_ll_sched_stop(void)
 int
 ble_ll_sched_init(void)
 {
-    BLE_LL_DEBUG_GPIO_INIT(SCHED_ITEM_CB);
+    BLE_LL_DEBUG_GPIO_INIT(SCHED_ITEM);
     BLE_LL_DEBUG_GPIO_INIT(SCHED_RUN);
 
     /*
@@ -1824,5 +1431,7 @@ ble_ll_sched_init(void)
         g_ble_ll_sched_data.sch_ticks_per_period;
 #endif
 
+    g_ble_ll_sched_q_head_changed = 0;
+
     return 0;
 }
diff --git a/nimble/controller/src/ble_ll_supp_cmd.c b/nimble/controller/src/ble_ll_supp_cmd.c
index cae9eb7dd1..2fc0d7d3af 100644
--- a/nimble/controller/src/ble_ll_supp_cmd.c
+++ b/nimble/controller/src/ble_ll_supp_cmd.c
@@ -27,7 +27,11 @@
 #include "controller/ble_ll_hci.h"
 
 /* Octet 0 */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_DISCONNECT             (1 << 5)
+#else
+#define BLE_SUPP_CMD_DISCONNECT             (0 << 5)
+#endif
 #define BLE_LL_SUPP_CMD_OCTET_0             (BLE_SUPP_CMD_DISCONNECT)
 
 /* Octet 5 */
@@ -64,7 +68,12 @@
 
 /* Octet 15 */
 #define BLE_SUPP_CMD_RD_BD_ADDR             (1 << 1)
+
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_RD_RSSI                (1 << 5)
+#else
+#define BLE_SUPP_CMD_RD_RSSI                (0 << 5)
+#endif
 
 #define BLE_LL_SUPP_CMD_OCTET_15            \
 (                                           \
@@ -77,9 +86,15 @@
 #define BLE_SUPP_CMD_LE_RD_BUF_SIZE         (1 << 1)
 #define BLE_SUPP_CMD_LE_RD_LOC_FEAT         (1 << 2)
 #define BLE_SUPP_CMD_LE_SET_RAND_ADDR       (1 << 4)
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_SUPP_CMD_LE_SET_ADV_PARAMS      (1 << 5)
 #define BLE_SUPP_CMD_LE_SET_ADV_TX_PWR      (1 << 6)
 #define BLE_SUPP_CMD_LE_SET_ADV_DATA        (1 << 7)
+#else
+#define BLE_SUPP_CMD_LE_SET_ADV_PARAMS      (0 << 5)
+#define BLE_SUPP_CMD_LE_SET_ADV_TX_PWR      (0 << 6)
+#define BLE_SUPP_CMD_LE_SET_ADV_DATA        (0 << 7)
+#endif
 
 #define BLE_LL_SUPP_CMD_OCTET_25            \
 (                                           \
@@ -93,12 +108,28 @@
 )
 
 /* Octet 26 */
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_SUPP_CMD_LE_SET_SCAN_RSP_DATA   (1 << 0)
 #define BLE_SUPP_CMD_LE_SET_ADV_ENABLE      (1 << 1)
+#else
+#define BLE_SUPP_CMD_LE_SET_SCAN_RSP_DATA   (0 << 0)
+#define BLE_SUPP_CMD_LE_SET_ADV_ENABLE      (0 << 1)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #define BLE_SUPP_CMD_LE_SET_SCAN_PARAMS     (1 << 2)
 #define BLE_SUPP_CMD_LE_SET_SCAN_ENABLE     (1 << 3)
+#else
+#define BLE_SUPP_CMD_LE_SET_SCAN_PARAMS     (0 << 2)
+#define BLE_SUPP_CMD_LE_SET_SCAN_ENABLE     (0 << 3)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_CREATE_CONN         (1 << 4)
 #define BLE_SUPP_CMD_LE_CREATE_CONN_CANCEL  (1 << 5)
+#else
+#define BLE_SUPP_CMD_LE_CREATE_CONN         (0 << 4)
+#define BLE_SUPP_CMD_LE_CREATE_CONN_CANCEL  (0 << 5)
+
+#endif
 #define BLE_SUPP_CMD_LE_RD_WHITELIST_SIZE   (1 << 6)
 #define BLE_SUPP_CMD_LE_CLR_WHITELIST       (1 << 7)
 
@@ -117,10 +148,19 @@
 /* Octet 27 */
 #define BLE_SUPP_CMD_LE_ADD_DEV_WHITELIST   (1 << 0)
 #define BLE_SUPP_CMD_LE_RMV_DEV_WHITELIST   (1 << 1)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_CONN_UPDATE         (1 << 2)
+#else
+#define BLE_SUPP_CMD_LE_CONN_UPDATE         (0 << 2)
+#endif
 #define BLE_SUPP_CMD_LE_SET_HOST_CHAN_CLASS (1 << 3)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_RD_CHAN_MAP         (1 << 4)
 #define BLE_SUPP_CMD_LE_RD_REM_USED_FEAT    (1 << 5)
+#else
+#define BLE_SUPP_CMD_LE_RD_CHAN_MAP         (0 << 4)
+#define BLE_SUPP_CMD_LE_RD_REM_USED_FEAT    (0 << 5)
+#endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
 #define BLE_SUPP_CMD_LE_ENCRYPT             (1 << 6)
 #else
@@ -142,10 +182,19 @@
 
 /* Octet 28 */
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_START_ENCRYPT       (1 << 0)
+#else
+#define BLE_SUPP_CMD_LE_START_ENCRYPT       (0 << 0)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
 #define BLE_SUPP_CMD_LE_LTK_REQ_REPLY       (1 << 1)
 #define BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY   (1 << 2)
 #else
+#define BLE_SUPP_CMD_LE_LTK_REQ_REPLY       (0 << 1)
+#define BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY   (0 << 2)
+#endif
+#else
 #define BLE_SUPP_CMD_LE_START_ENCRYPT       (0 << 0)
 #define BLE_SUPP_CMD_LE_LTK_REQ_REPLY       (0 << 1)
 #define BLE_SUPP_CMD_LE_LTK_REQ_NEG_REPLY   (0 << 2)
@@ -175,6 +224,7 @@
 )
 
 /* Octet 33 */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_REM_CONN_PRR        (1 << 4)
 #define BLE_SUPP_CMD_LE_REM_CONN_PRNR       (1 << 5)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
@@ -184,6 +234,13 @@
 #define BLE_SUPP_CMD_LE_SET_DATALEN         (0 << 6)
 #define BLE_SUPP_CMD_LE_RD_SUGG_DATALEN     (0 << 7)
 #endif
+#else
+#define BLE_SUPP_CMD_LE_REM_CONN_PRR        (0 << 4)
+#define BLE_SUPP_CMD_LE_REM_CONN_PRNR       (0 << 5)
+#define BLE_SUPP_CMD_LE_SET_DATALEN         (0 << 6)
+#define BLE_SUPP_CMD_LE_RD_SUGG_DATALEN     (0 << 7)
+#endif
+
 
 #define BLE_LL_SUPP_CMD_OCTET_33            \
 (                                           \
@@ -194,11 +251,15 @@
 )
 
 /* Octet 34 */
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
 #define BLE_SUPP_CMD_LE_WR_SUGG_DATALEN     (1 << 0)
 #else
 #define BLE_SUPP_CMD_LE_WR_SUGG_DATALEN     (0 << 0)
 #endif
+#else
+#define BLE_SUPP_CMD_LE_WR_SUGG_DATALEN     (0 << 0)
+#endif
 #define BLE_SUPP_CMD_LE_READ_LOCAL_P256_PK  (0 << 1)
 #define BLE_SUPP_CMD_LE_GENERATE_DH_KEY     (0 << 2)
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
@@ -239,6 +300,7 @@
 #endif
 #define BLE_SUPP_CMD_LE_RD_MAX_DATALEN      (1 << 3)
 #if (BLE_LL_BT5_PHY_SUPPORTED == 1)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_READ_PHY            (1 << 4)
 #define BLE_SUPP_CMD_LE_SET_DEFAULT_PHY     (1 << 5)
 #define BLE_SUPP_CMD_LE_SET_PHY             (1 << 6)
@@ -247,6 +309,11 @@
 #define BLE_SUPP_CMD_LE_SET_DEFAULT_PHY     (0 << 5)
 #define BLE_SUPP_CMD_LE_SET_PHY             (0 << 6)
 #endif
+#else
+#define BLE_SUPP_CMD_LE_READ_PHY            (0 << 4)
+#define BLE_SUPP_CMD_LE_SET_DEFAULT_PHY     (0 << 5)
+#define BLE_SUPP_CMD_LE_SET_PHY             (0 << 6)
+#endif
 
 #if MYNEWT_VAL(BLE_LL_DTM)
 #define BLE_SUPP_CMD_LE_ENHANCED_RX_TEST    (1 << 7)
@@ -273,7 +340,7 @@
 #define BLE_SUPP_CMD_LE_ENHANCED_TX_TEST    (0 << 0)
 #endif
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_SUPP_CMD_LE_SET_ADVS_RAND_ADDR  (1 << 1)
 #define BLE_SUPP_CMD_LE_SET_EXT_ADV_PARAM   (1 << 2)
 #define BLE_SUPP_CMD_LE_SET_EXT_ADV_DATA    (1 << 3)
@@ -304,14 +371,14 @@
 )
 
 /* Octet 37 */
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_SUPP_CMD_LE_REMOVE_ADVS         (1 << 0)
 #define BLE_SUPP_CMD_LE_CLEAR_ADVS          (1 << 1)
 #else
 #define BLE_SUPP_CMD_LE_REMOVE_ADVS         (0 << 0)
 #define BLE_SUPP_CMD_LE_CLEAR_ADVS          (0 << 1)
 #endif
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
 #define BLE_SUPP_CMD_LE_SET_PADV_PARAM      (1 << 2)
 #define BLE_SUPP_CMD_LE_SET_PADV_DATA       (1 << 3)
 #define BLE_SUPP_CMD_LE_SET_PADV_ENABLE     (1 << 4)
@@ -321,10 +388,19 @@
 #define BLE_SUPP_CMD_LE_SET_PADV_ENABLE     (0 << 4)
 #endif
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #define BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM  (1 << 5)
 #define BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE (1 << 6)
+#else
+#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM  (0 << 5)
+#define BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE (0 << 6)
+#endif
+#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
 #define BLE_SUPP_CMD_LE_EXT_CREATE_CONN     (1 << 7)
 #else
+#define BLE_SUPP_CMD_LE_EXT_CREATE_CONN     (0 << 7)
+#endif
+#else
 #define BLE_SUPP_CMD_LE_SET_EXT_SCAN_PARAM  (0 << 5)
 #define BLE_SUPP_CMD_LE_SET_EXT_SCAN_ENABLE (0 << 6)
 #define BLE_SUPP_CMD_LE_EXT_CREATE_CONN     (0 << 7)
@@ -343,7 +419,7 @@
 )
 
 /* Octet 38 */
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC    (1 << 0)
 #define BLE_SUPP_CMD_LE_PADV_CREATE_SYNC_C  (1 << 1)
 #define BLE_SUPP_CMD_LE_PADV_TERMINATE_SYNC (1 << 2)
@@ -391,7 +467,8 @@
 )
 
 /* Octet 40 */
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_VERSION) >= 51
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_VERSION) >= 51 && \
+    MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 #define BLE_SUPP_CMD_LE_PADV_RECV_ENABLE (1 << 5)
 #else
 #define BLE_SUPP_CMD_LE_PADV_RECV_ENABLE (0 << 5)
@@ -483,9 +560,9 @@
 
 /* Octet 44 */
 #if MYNEWT_VAL(BLE_VERSION) >= 52
-#define BLE_SUPP_CMD_LE_SET_HOST_FEATURE (1 << 0)
+#define BLE_SUPP_CMD_LE_SET_HOST_FEATURE (1 << 1)
 #else
-#define BLE_SUPP_CMD_LE_SET_HOST_FEATURE (0 << 0)
+#define BLE_SUPP_CMD_LE_SET_HOST_FEATURE (0 << 1)
 #endif
 #define BLE_LL_SUPP_CMD_OCTET_44                        \
 (                                                       \
diff --git a/nimble/controller/src/ble_ll_sync.c b/nimble/controller/src/ble_ll_sync.c
index 7c46d10a29..7b6208ce11 100644
--- a/nimble/controller/src/ble_ll_sync.c
+++ b/nimble/controller/src/ble_ll_sync.c
@@ -41,7 +41,7 @@
 
 #include "stats/stats.h"
 
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
 
 /* defines number of events that can be lost during sync establishment
  * before failed to be established error is reported
@@ -61,6 +61,7 @@
 #define BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED   0x0080
 #define BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED   0x0100
 #define BLE_LL_SYNC_SM_FLAG_NEW_CHANMAP     0x0200
+#define BLE_LL_SYNC_SM_FLAG_CHAIN           0x0400
 
 #define BLE_LL_SYNC_CHMAP_LEN               5
 #define BLE_LL_SYNC_ITVL_USECS              1250
@@ -137,6 +138,8 @@ static uint8_t *g_ble_ll_sync_create_comp_ev;
 
 static struct ble_ll_sync_sm *g_ble_ll_sync_sm_current;
 
+static int ble_ll_sync_event_start_cb(struct ble_ll_sched_item *sch);
+
 static int
 ble_ll_sync_on_list(const uint8_t *addr, uint8_t addr_type, uint8_t sid)
 {
@@ -211,7 +214,12 @@ ble_ll_sync_sm_clear(struct ble_ll_sync_sm *sm)
 
     BLE_LL_ASSERT(sm->sync_ev_end.ev.ev_queued == 0);
     BLE_LL_ASSERT(sm->sch.enqueued == 0);
+
     memset(sm, 0, sizeof(*sm));
+
+    sm->sch.sched_cb = ble_ll_sync_event_start_cb;
+    sm->sch.cb_arg = sm;
+    sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC;
 }
 
 static uint8_t
@@ -436,6 +444,7 @@ ble_ll_sync_event_start_cb(struct ble_ll_sched_item *sch)
     struct ble_ll_sync_sm *sm;
     uint32_t wfr_usecs;
     uint32_t start;
+    uint8_t chan;
     int rc;
 
     /* Set current connection state machine */
@@ -451,7 +460,9 @@ ble_ll_sync_event_start_cb(struct ble_ll_sched_item *sch)
     ble_ll_state_set(BLE_LL_STATE_SYNC);
 
     /* Set channel */
-    ble_phy_setchan(sm->chan_index, sm->access_addr, sm->crcinit);
+    chan = sm->flags & BLE_LL_SYNC_SM_FLAG_CHAIN ? sm->chan_chain :
+                                                   sm->chan_index;
+    ble_phy_setchan(chan, sm->access_addr, sm->crcinit);
 
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
     ble_phy_resolv_list_disable();
@@ -469,31 +480,35 @@ ble_ll_sync_event_start_cb(struct ble_ll_sched_item *sch)
     rc = ble_phy_rx_set_start_time(start, sch->remainder);
     if (rc && rc != BLE_PHY_ERR_RX_LATE) {
         STATS_INC(ble_ll_stats, sync_event_failed);
-        rc = BLE_LL_SCHED_STATE_DONE;
         ble_ll_event_send(&sm->sync_ev_end);
         ble_ll_sync_current_sm_over();
+        rc = BLE_LL_SCHED_STATE_DONE;
     } else {
-        /*
-         * Set flag that tells to set last anchor point if a packet
-         * has been received.
-         */
-        sm->flags |= BLE_LL_SYNC_SM_FLAG_SET_ANCHOR;
-
-        /* Set WFR timer.
-         * If establishing we always adjust with offset unit.
-         * If this is first packet of sync (one that was pointed by from
-         * SyncInfo we don't adjust WFT with window widening.
-         */
-        if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) {
+        if (sm->flags & BLE_LL_SYNC_SM_FLAG_CHAIN) {
             wfr_usecs = (sm->flags & BLE_LL_SYNC_SM_FLAG_OFFSET_300) ? 300 : 30;
-            if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_SYNC_INFO)) {
-                wfr_usecs += 2 * sm->window_widening;
-            }
         } else {
-            wfr_usecs = 2 * sm->window_widening;
+            /* Set flag that tells to set last anchor point if a packet
+             * has been received.
+             */
+            sm->flags |= BLE_LL_SYNC_SM_FLAG_SET_ANCHOR;
+
+            /* Set WFR timer.
+             * If establishing we always adjust with offset unit.
+             * If this is first packet of sync (one that was pointed by from
+             * SyncInfo we don't adjust WFR with window widening.
+             */
+            if (sm->flags & BLE_LL_SYNC_SM_FLAG_ESTABLISHING) {
+                wfr_usecs = (sm->flags & BLE_LL_SYNC_SM_FLAG_OFFSET_300) ? 300
+                                                                         : 30;
+                if (!(sm->flags & BLE_LL_SYNC_SM_FLAG_SYNC_INFO)) {
+                    wfr_usecs += 2 * sm->window_widening;
+                }
+            } else {
+                wfr_usecs = 2 * sm->window_widening;
+            }
         }
-        ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usecs);
 
+        ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usecs);
         rc = BLE_LL_SCHED_STATE_RUNNING;
     }
 
@@ -566,7 +581,7 @@ ble_ll_sync_parse_ext_hdr(struct os_mbuf *om, uint8_t **aux, int8_t *tx_power,
 
         /* Ignore CTE for now */
         if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) {
-            i += 1;
+            i += BLE_LL_EXT_ADV_CTE_INFO_SIZE;
         }
 
         /* there should be no ADI in Sync or chain, skip it */
@@ -848,7 +863,7 @@ static void
 ble_ll_sync_parse_aux_ptr(const uint8_t *buf, uint8_t *chan, uint32_t *offset,
                           uint8_t *offset_units, uint8_t *phy)
 {
-    uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF;
+    uint32_t aux_ptr_field = get_le24(buf);
 
     *chan = aux_ptr_field & 0x3F;
 
@@ -865,63 +880,6 @@ ble_ll_sync_parse_aux_ptr(const uint8_t *buf, uint8_t *chan, uint32_t *offset,
     *phy = (aux_ptr_field >> 21) & 0x07;
 }
 
-static int
-ble_ll_sync_chain_start_cb(struct ble_ll_sched_item *sch)
-{
-    struct ble_ll_sync_sm *sm;
-    uint32_t wfr_usecs;
-    uint32_t start;
-    int rc;
-
-    /* Set current connection state machine */
-    sm = sch->cb_arg;
-    g_ble_ll_sync_sm_current = sm;
-    BLE_LL_ASSERT(sm);
-
-    /* Disable whitelisting */
-    ble_ll_whitelist_disable();
-
-    /* Set LL state */
-    ble_ll_state_set(BLE_LL_STATE_SYNC);
-
-    /* Set channel */
-    ble_phy_setchan(sm->chan_chain, sm->access_addr, sm->crcinit);
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
-    ble_phy_resolv_list_disable();
-#endif
-
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
-    ble_phy_encrypt_disable();
-#endif
-
-#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
-    ble_phy_mode_set(sm->phy_mode, sm->phy_mode);
-#endif
-
-    start = sch->start_time + g_ble_ll_sched_offset_ticks;
-    rc = ble_phy_rx_set_start_time(start, sch->remainder);
-    if (rc && rc != BLE_PHY_ERR_RX_LATE) {
-        STATS_INC(ble_ll_stats, sync_chain_failed);
-        rc = BLE_LL_SCHED_STATE_DONE;
-        ble_ll_event_send(&sm->sync_ev_end);
-        ble_ll_sync_current_sm_over();
-    } else {
-        /*
-         * Clear flag that tells to set last anchor point if a packet
-         * has been received, this is chain and we don't need it.
-         */
-        sm->flags &= ~BLE_LL_SYNC_SM_FLAG_SET_ANCHOR;
-
-        wfr_usecs = (sm->flags & BLE_LL_SYNC_SM_FLAG_OFFSET_300) ? 300 : 30;
-
-        ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usecs);
-        rc = BLE_LL_SCHED_STATE_RUNNING;
-    }
-
-    return rc;
-}
-
 static int
 ble_ll_sync_schedule_chain(struct ble_ll_sync_sm *sm, struct ble_mbuf_hdr *hdr,
                            const uint8_t *aux)
@@ -954,9 +912,7 @@ ble_ll_sync_schedule_chain(struct ble_ll_sync_sm *sm, struct ble_mbuf_hdr *hdr,
 
     sm->chan_chain = chan;
 
-    sm->sch.sched_cb = ble_ll_sync_chain_start_cb;
-    sm->sch.cb_arg = sm;
-    sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC;
+    sm->flags |= BLE_LL_SYNC_SM_FLAG_CHAIN;
 
     return ble_ll_sched_sync(&sm->sch, hdr->beg_cputime, hdr->rem_usecs,
                              offset, sm->phy_mode);
@@ -1308,10 +1264,7 @@ ble_ll_sync_event_end(struct ble_npl_event *ev)
 
     /* Event ended so we are no longer chaining */
     sm->flags &= ~BLE_LL_SYNC_SM_FLAG_HCI_TRUNCATED;
-
-    sm->sch.sched_cb = ble_ll_sync_event_start_cb;
-    sm->sch.cb_arg = sm;
-    sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC;
+    sm->flags &= ~BLE_LL_SYNC_SM_FLAG_CHAIN;
 
     do {
         if (ble_ll_sync_next_event(sm, 0) < 0) {
@@ -1333,14 +1286,11 @@ ble_ll_sync_event_end(struct ble_npl_event *ev)
 }
 
 void
-ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, int rpa_index,
+ble_ll_sync_info_event(struct ble_ll_scan_addr_data *addrd,
                        uint8_t sid, struct ble_mbuf_hdr *rxhdr,
                        const uint8_t *syncinfo)
 {
     struct ble_ll_sync_sm *sm = NULL;
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
-    const uint8_t *rpa = NULL;
-#endif
     uint16_t max_skip;
     uint32_t offset;
     uint32_t usecs;
@@ -1365,28 +1315,21 @@ ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, int rpa_index,
         return;
     }
 
-    /* check if resolved */
-    if (rpa_index >= 0) {
-#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
-        rpa = addr;
-#endif
-        addr = g_ble_ll_resolv_list[rpa_index].rl_identity_addr;
-        addr_type = g_ble_ll_resolv_list[rpa_index].rl_addr_type;
-    }
-
     /* check peer */
     if (g_ble_ll_sync_create_params.options & BLE_HCI_LE_PERIODIC_ADV_CREATE_SYNC_OPT_FILTER) {
-        if (ble_ll_sync_on_list(addr, addr_type, sid) < 0) {
+        if (ble_ll_sync_on_list(addrd->adv_addr,
+                                addrd->adv_addr_type, sid) < 0) {
             return;
         }
 
         /* set addr and sid in sm */
         sm->adv_sid = sid;
-        sm->adv_addr_type = addr_type;
-        memcpy(sm->adv_addr, addr, BLE_DEV_ADDR_LEN);
+        sm->adv_addr_type = addrd->adv_addr_type;
+        memcpy(sm->adv_addr, addrd->adv_addr, BLE_DEV_ADDR_LEN);
     } else {
-        if ((sm->adv_sid != sid) || (sm->adv_addr_type != addr_type) ||
-                memcmp(sm->adv_addr, addr, BLE_DEV_ADDR_LEN)) {
+        if ((sm->adv_sid != sid) ||
+                (sm->adv_addr_type != addrd->adv_addr_type) ||
+                memcmp(sm->adv_addr, addrd->adv_addr, BLE_DEV_ADDR_LEN)) {
             return;
         }
     }
@@ -1406,12 +1349,14 @@ ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, int rpa_index,
         return;
     }
 
-    if (rpa_index >= 0) {
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
+    if (addrd->adva_resolved) {
         sm->flags |= BLE_LL_SYNC_SM_FLAG_ADDR_RESOLVED;
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
-        memcpy(sm->adv_addr_rpa, rpa, BLE_DEV_ADDR_LEN);
+        memcpy(sm->adv_addr_rpa, addrd->adva, BLE_DEV_ADDR_LEN);
 #endif
     }
+#endif
 
     /* set params from HCI LE Create Periodic Sync */
     sm->timeout = g_ble_ll_sync_create_params.timeout;
@@ -1481,10 +1426,6 @@ ble_ll_sync_info_event(const uint8_t *addr, uint8_t addr_type, int rpa_index,
     sm->chan_index = ble_ll_utils_calc_dci_csa2(sm->event_cntr, sm->channel_id,
                                                 sm->num_used_chans, sm->chanmap);
 
-    sm->sch.sched_cb = ble_ll_sync_event_start_cb;
-    sm->sch.cb_arg = sm;
-    sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC;
-
     if (ble_ll_sched_sync(&sm->sch, rxhdr->beg_cputime, rxhdr->rem_usecs,
                           offset, sm->phy_mode)) {
         return;
@@ -1930,6 +1871,7 @@ ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm,
 
     rpa_index = -1;
 
+#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
     /* check if need to resolve */
     if (ble_ll_is_rpa(addr, addr_type)) {
         rpa_index = ble_ll_resolv_peer_rpa_any(addr);
@@ -1939,6 +1881,7 @@ ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm,
             addr_type = g_ble_ll_resolv_list[rpa_index].rl_addr_type;
         }
     }
+#endif
 
     OS_ENTER_CRITICAL(sr);
     /* check if already synchronized with this peer */
@@ -2037,10 +1980,6 @@ ble_ll_sync_periodic_ind(struct ble_ll_conn_sm *connsm,
     sm->chan_index = ble_ll_utils_calc_dci_csa2(sm->event_cntr, sm->channel_id,
                                                 sm->num_used_chans, sm->chanmap);
 
-    sm->sch.sched_cb = ble_ll_sync_event_start_cb;
-    sm->sch.cb_arg = sm;
-    sm->sch.sched_type = BLE_LL_SCHED_TYPE_SYNC;
-
     /* get anchor for specified conn event */
     conn_event_count = get_le16(sync_ind + 20);
     ble_ll_conn_get_anchor(connsm, conn_event_count, &sm->anchor_point,
diff --git a/nimble/controller/src/ble_ll_whitelist.c b/nimble/controller/src/ble_ll_whitelist.c
index 04ec6428b5..12d841a7f8 100644
--- a/nimble/controller/src/ble_ll_whitelist.c
+++ b/nimble/controller/src/ble_ll_whitelist.c
@@ -48,8 +48,6 @@ struct ble_ll_whitelist_entry g_ble_ll_whitelist[BLE_LL_WHITELIST_SIZE];
 static int
 ble_ll_whitelist_chg_allowed(void)
 {
-    int rc;
-
     /*
      * This command is not allowed if:
      *  -> advertising uses the whitelist and we are currently advertising.
@@ -57,11 +55,19 @@ ble_ll_whitelist_chg_allowed(void)
      *  -> initiating uses whitelist and a LE create connection command is in
      *     progress
      */
-    rc = 1;
-    if (!ble_ll_adv_can_chg_whitelist() || !ble_ll_scan_can_chg_whitelist()) {
-        rc = 0;
+#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
+    if (ble_ll_adv_can_chg_whitelist()) {
+        return 1;
     }
-    return rc;
+#endif
+
+#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
+    if (ble_ll_scan_can_chg_whitelist()) {
+        return 1;
+    }
+#endif
+
+    return 0;
 }
 
 /**
diff --git a/nimble/controller/syscfg.yml b/nimble/controller/syscfg.yml
index b900ba9106..afb3859708 100644
--- a/nimble/controller/syscfg.yml
+++ b/nimble/controller/syscfg.yml
@@ -23,6 +23,30 @@ syscfg.defs:
             this setting shall not be overriden.
         value: 1
 
+    BLE_LL_ROLE_CENTRAL:
+        description: 'Enables controller support for the Central role.'
+        value: MYNEWT_VAL(BLE_ROLE_CENTRAL)
+        restrictions:
+            - 'BLE_LL_ROLE_OBSERVER if 1'
+
+    BLE_LL_ROLE_PERIPHERAL:
+        description: 'Enables controller support for the Peripheral role.'
+        value: MYNEWT_VAL(BLE_ROLE_PERIPHERAL)
+        restrictions:
+            - 'BLE_LL_ROLE_BROADCASTER if 1'
+
+    BLE_LL_ROLE_BROADCASTER:
+        description: 'Enables controller support for the Broadcaster role.'
+        value: MYNEWT_VAL(BLE_ROLE_BROADCASTER)
+        restrictions:
+            - 'BLE_LL_ROLE_OBSERVER if 0'
+
+    BLE_LL_ROLE_OBSERVER:
+        description: 'Enables controller support for the Observer role.'
+        value: MYNEWT_VAL(BLE_ROLE_OBSERVER)
+        restrictions:
+            - 'BLE_LL_ROLE_BROADCASTER if 0'
+
     BLE_HW_WHITELIST_ENABLE:
         description: >
             Used to enable hardware white list
@@ -44,7 +68,9 @@ syscfg.defs:
         range: 0..500
 
     BLE_LL_TX_PWR_DBM:
-        description: 'Transmit power level.'
+        description: >
+            Default Transmit power level (in dBm). Actual transmit power
+            may be rounded up or down depending on used radio.
         value: '0'
 
     BLE_LL_NUM_COMP_PKT_ITVL_MS:
@@ -163,6 +189,11 @@ syscfg.defs:
             PHY enabled all the time.
         value: MYNEWT_VAL(BLE_XTAL_SETTLE_TIME)
 
+    BLE_LL_HCI_LLCP_TRACE:
+        description: >
+            Enables LLCP tracing using HCI vendor-specific events.
+        value: '0'
+
     # Configuration for LL supported features.
     #
     # There are a total 8 features that the LL can support. These can be found
@@ -179,21 +210,21 @@ syscfg.defs:
         description: >
             This option enables/disables encryption support in the controller.
             This option saves both both code and RAM.
-        value: '1'
+        value: 'MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL'
 
     BLE_LL_CFG_FEAT_CONN_PARAM_REQ:
         description: >
             This option enables/disables the connection parameter request
             procedure.  This is implemented in the controller but is disabled
             by default.
-        value: '1'
+        value: 'MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL'
 
     BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG:
         description: >
             This option allows a slave to initiate the feature exchange
             procedure.  This feature is implemented but currently has no impact
             on code or ram size
-        value: '1'
+        value: 'MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL'
 
     BLE_LL_CFG_FEAT_LE_PING:
         description: >
@@ -208,7 +239,7 @@ syscfg.defs:
             the controller. If enabled, the controller is allowed to change the
             size of tx/rx pdu's used in a connection. This option has only
             minor impact on code size and non on RAM.
-        value: '1'
+        value: 'MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL'
 
     BLE_LL_CFG_FEAT_LL_PRIVACY:
         description: >
@@ -243,8 +274,6 @@ syscfg.defs:
             This option is used to enable/disable support for Periodic
             Advertising Feature.
         value: MYNEWT_VAL(BLE_PERIODIC_ADV)
-        restrictions:
-            - 'BLE_LL_CFG_FEAT_LL_EXT_ADV if 1'
 
     BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_CNT:
         description: >
@@ -268,6 +297,8 @@ syscfg.defs:
             limit number of ACL packets sent at once from controller to avoid
             congestion on HCI transport if feature is also supported by host.
         value: 0
+        restrictions:
+            - '(BLE_ROLE_CENTRAL || BLE_ROLE_PERIPHERAL) if 1'
 
     BLE_LL_CFG_FEAT_LL_SCA_UPDATE:
         description: >
@@ -291,20 +322,25 @@ syscfg.defs:
         restrictions:
             - 'BLE_LL_CFG_FEAT_LL_ISO if 1'
 
+    BLE_LL_SCAN_AUX_SEGMENT_CNT:
+         description: >
+            Number of auxiliary advertising segments that can be scanned
+            concurrently (Core 5.2, Vol 6, Part B, 4.4.2.2.2).
+         value: 8
+
     BLE_LL_EXT_ADV_AUX_PTR_CNT:
          description: >
             This option configure a max number of scheduled outstanding auxiliary
             packets for receive on secondary advertising channel.
          value: 0
 
-    BLE_PUBLIC_DEV_ADDR:
+    BLE_LL_PUBLIC_DEV_ADDR:
         description: >
-            Allows the target or app to override the public device address
-            used by the controller. If all zero, the controller will
-            attempt to retrieve the public device address from its
-            chip specific location. If non-zero, this address will
-            be used.
-        value: "(uint8_t[6]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
+            Set public device address. Address is specified as 48-bit number.
+            If non-zero, this setting has priority over BLE_PUBLIC_DEV_ADDR.
+            Note: this setting should only be used for testing purposes, it is
+                  not intended for production builds.
+        value: 0x000000000000
 
     BLE_LL_DTM:
         description: >
@@ -329,13 +365,35 @@ syscfg.defs:
              depending on specified HCI command length.
         value: 0
 
-    BLE_LL_VND_EVENT_ON_ASSERT:
+    BLE_LL_HCI_VS:
+        description: >
+            Enables support for vendor-specific HCI commands.
+        value: MYNEWT_VAL(BLE_HCI_VS)
+
+    BLE_LL_HCI_VS_EVENT_ON_ASSERT:
         description: >
             This options enables controller to send a vendor-specific event on
             an assertion in controller code. The event contains file name and
             line number where assertion occured.
         value: 0
 
+    BLE_LL_PA:
+        description: Enable PA support
+        value: 0
+    BLE_LL_PA_GPIO:
+        description: >
+            GPIO pin number to control PA. Pin is set to high state when PA
+            should be enabled.
+        value: -1
+    BLE_LL_LNA:
+        description: Enable LNA support
+        value: 0
+    BLE_LL_LNA_GPIO:
+        description: >
+            GPIO pin number to control LNA. Pin is set to high state when LNA
+            should be enabled.
+        value: -1
+
     BLE_LL_SYSINIT_STAGE:
         description: >
             Sysinit stage for the NimBLE controller.
@@ -356,10 +414,15 @@ syscfg.defs:
             GPIO pin number to debug scheduler running (on timer). Pin is set
             to high state while scheduler is running.
         value: -1
-    BLE_LL_DEBUG_GPIO_SCHED_ITEM_CB:
+    BLE_LL_DEBUG_GPIO_SCHED_ITEM:
         description: >
             GPIO pin number to debug scheduler item execution times. Pin is set
-            to high state while item is executed.
+            to high state while item is active.
+        value: -1
+    BLE_LL_DEBUG_GPIO_RFMGMT:
+        description: >
+            GPIO pin number to debug rfmgmt activity. Pin is set to high state
+            while rfmgmt is active.
         value: -1
 
 # Below settings allow to change scheduler timings. These should be left at
@@ -414,6 +477,14 @@ syscfg.defs:
         description: use BLE_LL_SCA instead
         value: 60
         deprecated: 1
+    BLE_LL_VND_EVENT_ON_ASSERT:
+        description: use BLE_LL_HCI_VS_EVENT_ON_ASSERT
+        value: 0
+        deprecated: 1
+    BLE_PUBLIC_DEV_ADDR:
+        description: use BLE_LL_PUBLIC_DEV_ADDR
+        value: "(uint8_t[6]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}"
+        deprecated: 1
 
 # defunct settings (to be removed eventually)
     BLE_DEVICE:
@@ -442,7 +513,10 @@ syscfg.vals.BLE_LL_CFG_FEAT_LL_EXT_ADV:
 # Enable vendor event on assert in standalone build to make failed assertions in
 # controller code visible when connected to external host
 syscfg.vals.!BLE_HOST:
-    BLE_LL_VND_EVENT_ON_ASSERT: 1
+    BLE_LL_HCI_VS_EVENT_ON_ASSERT: 1
 
 syscfg.restrictions:
     - OS_CPUTIME_FREQ == 32768
+    - BLE_LL_PUBLIC_DEV_ADDR <= 0xffffffffffff
+    - BLE_LL_PA == 0 || BLE_LL_PA_GPIO >= 0
+    - BLE_LL_LNA == 0 || BLE_LL_LNA_GPIO >= 0
diff --git a/nimble/controller/test/pkg.yml b/nimble/controller/test/pkg.yml
index ea72881149..1b06a5b576 100644
--- a/nimble/controller/test/pkg.yml
+++ b/nimble/controller/test/pkg.yml
@@ -31,4 +31,4 @@ pkg.deps.SELFTEST:
     - "@apache-mynewt-core/sys/log/full"
     - "@apache-mynewt-core/sys/stats/stub"
     - nimble/drivers/native
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/nimble/controller/test/syscfg.yml b/nimble/controller/test/syscfg.yml
index 6edad438bb..4448a0e66f 100644
--- a/nimble/controller/test/syscfg.yml
+++ b/nimble/controller/test/syscfg.yml
@@ -23,3 +23,4 @@ syscfg.vals:
     MCU_TIMER_POLLER_PRIO: 1
     MCU_UART_POLLER_PRIO: 2
     NATIVE_SOCKETS_PRIO: 3
+    BLE_HCI_TRANSPORT: ram
diff --git a/nimble/drivers/dialog_cmac/src/ble_phy.c b/nimble/drivers/dialog_cmac/src/ble_phy.c
index ab0f88505f..212d19b671 100644
--- a/nimble/drivers/dialog_cmac/src/ble_phy.c
+++ b/nimble/drivers/dialog_cmac/src/ble_phy.c
@@ -935,6 +935,12 @@ ble_phy_irq_frame_rx_exc_phy_to_idle_4this(void)
     rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel];
     ble_rf_setup_tx(rf_chan, g_ble_phy_data.phy_mode_tx);
     g_ble_phy_data.phy_state = BLE_PHY_STATE_TX;
+
+    /* We do not want FIELD/FRAME interrupts until ble_phy_tx() has pushed all
+     * fields.
+     */
+    NVIC_DisableIRQ(FRAME_IRQn);
+    NVIC_DisableIRQ(FIELD_IRQn);
 }
 
 static void
@@ -1305,6 +1311,9 @@ ble_phy_disable(void)
 
     __enable_irq();
 
+    NVIC_EnableIRQ(FRAME_IRQn);
+    NVIC_EnableIRQ(FIELD_IRQn);
+
     g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE;
 }
 
@@ -1369,6 +1378,7 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
 {
     uint32_t ll_val32;
     int32_t time_till_start;
+    int rc = 0;
 
     MCU_DIAG_SER('r');
 
@@ -1404,11 +1414,12 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
             /* We missed start. Start now */
             CMAC->CM_EV_LINKUP_REG =
                 CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_ASAP_Msk;
+            rc = BLE_PHY_ERR_RX_LATE;
         }
     }
     __enable_irq();
 
-    return 0;
+    return rc;
 }
 
 int
@@ -1550,8 +1561,6 @@ ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
 tx_late:
     STATS_INC(ble_phy_stats, tx_late);
     ble_phy_disable();
-    NVIC_EnableIRQ(FRAME_IRQn);
-    NVIC_EnableIRQ(FIELD_IRQn);
     rc = BLE_PHY_ERR_TX_LATE;
 
 done:
diff --git a/nimble/drivers/nrf51/src/ble_hw.c b/nimble/drivers/nrf51/src/ble_hw.c
index 3c7296b845..437efd4f7c 100644
--- a/nimble/drivers/nrf51/src/ble_hw.c
+++ b/nimble/drivers/nrf51/src/ble_hw.c
@@ -474,13 +474,8 @@ ble_hw_resolv_list_size(void)
 int
 ble_hw_resolv_list_match(void)
 {
-    uint32_t index;
-
-    if (NRF_AAR->EVENTS_END) {
-        if (NRF_AAR->EVENTS_RESOLVED) {
-            index = NRF_AAR->STATUS;
-            return (int)index;
-        }
+    if (NRF_AAR->ENABLE && NRF_AAR->EVENTS_END && NRF_AAR->EVENTS_RESOLVED) {
+        return (int)NRF_AAR->STATUS;
     }
 
     return -1;
diff --git a/nimble/drivers/nrf51/src/ble_phy.c b/nimble/drivers/nrf51/src/ble_phy.c
index b7e6329721..d3bc3c2ecc 100644
--- a/nimble/drivers/nrf51/src/ble_phy.c
+++ b/nimble/drivers/nrf51/src/ble_phy.c
@@ -408,7 +408,7 @@ ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs)
 /**
  * Function is used to set PPI so that we can time out waiting for a reception
  * to occur. This happens for two reasons: we have sent a packet and we are
- * waiting for a respons (txrx should be set to ENABLE_TXRX) or we are
+ * waiting for a response (txrx should be set to ENABLE_TXRX) or we are
  * starting a connection event and we are a slave and we are waiting for the
  * master to send us a packet (txrx should be set to ENABLE_RX).
  *
@@ -1357,7 +1357,7 @@ ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit)
         NRF_RADIO->RXADDRESSES = (1 << 1);
         NRF_RADIO->CRCINIT = crcinit;
     } else {
-        /* Logical adddress 0 preconfigured */
+        /* Logical address 0 preconfigured */
         NRF_RADIO->TXADDRESS = 0;
         NRF_RADIO->RXADDRESSES = (1 << 0);
         NRF_RADIO->CRCINIT = BLE_LL_CRCINIT_ADV;
diff --git a/nimble/drivers/nrf52/src/ble_hw.c b/nimble/drivers/nrf52/src/ble_hw.c
index 82e6d17ed7..79a7772852 100644
--- a/nimble/drivers/nrf52/src/ble_hw.c
+++ b/nimble/drivers/nrf52/src/ble_hw.c
@@ -69,6 +69,22 @@ ble_hw_get_public_addr(ble_addr_t *addr)
     uint32_t addr_high;
     uint32_t addr_low;
 
+#if MYNEWT_VAL(BLE_PHY_UBLOX_BMD345_PUBLIC_ADDR)
+    /*
+    * The BMD-345 modules are preprogrammed from the factory with a unique public
+    * The  Bluetooth device address stored in the CUSTOMER[0] and CUSTOMER[1]
+    * registers of the User Information Configuration Registers (UICR).
+    * The Bluetooth device address consists of the IEEE Organizationally Unique
+    * Identifier (OUI) combined with the hexadecimal digits that are printed on
+    * a 2D barcode and in human-readable text on the module label.The Bluetooth
+    * device address is stored in little endian format. The most significant
+    * bytes of the CUSTOMER[1] register are 0xFF to complete the 32-bit register.
+    */
+
+    /* Copy into device address. We can do this because we know platform */
+    addr_low = NRF_UICR->CUSTOMER[0];
+    addr_high = NRF_UICR->CUSTOMER[1];
+#else
     /* Does FICR have a public address */
     if ((NRF_FICR->DEVICEADDRTYPE & 1) != 0) {
         return -1;
@@ -77,6 +93,8 @@ ble_hw_get_public_addr(ble_addr_t *addr)
     /* Copy into device address. We can do this because we know platform */
     addr_low = NRF_FICR->DEVICEADDR[0];
     addr_high = NRF_FICR->DEVICEADDR[1];
+#endif
+
     memcpy(addr->val, &addr_low, 4);
     memcpy(&addr->val[4], &addr_high, 2);
     addr->type = BLE_ADDR_PUBLIC;
@@ -474,13 +492,8 @@ ble_hw_resolv_list_size(void)
 int
 ble_hw_resolv_list_match(void)
 {
-    uint32_t index;
-
-    if (NRF_AAR->EVENTS_END) {
-        if (NRF_AAR->EVENTS_RESOLVED) {
-            index = NRF_AAR->STATUS;
-            return (int)index;
-        }
+    if (NRF_AAR->ENABLE && NRF_AAR->EVENTS_END && NRF_AAR->EVENTS_RESOLVED) {
+        return (int)NRF_AAR->STATUS;
     }
 
     return -1;
diff --git a/nimble/drivers/nrf52/src/ble_phy.c b/nimble/drivers/nrf52/src/ble_phy.c
index 2f6ce08e72..3c7b62690b 100644
--- a/nimble/drivers/nrf52/src/ble_phy.c
+++ b/nimble/drivers/nrf52/src/ble_phy.c
@@ -29,6 +29,7 @@
 #include "controller/ble_phy.h"
 #include "controller/ble_phy_trace.h"
 #include "controller/ble_ll.h"
+#include "controller/ble_ll_plna.h"
 #include "nrfx.h"
 #if MYNEWT
 #include "mcu/nrf52_clock.h"
@@ -49,9 +50,11 @@
  *       using PPI somewhere else.
  *
  * Pre-programmed channels: CH20, CH21, CH23, CH25, CH31
- * Regular channels: CH4, CH5 and optionally CH17, CH18, CH19
+ * Regular channels: CH4, CH5 and optionally CH6, CH7, CH17, CH18, CH19
  *  - CH4 = cancel wfr timer on address match
  *  - CH5 = disable radio on wfr timer expiry
+ *  - CH6 = PA/LNA control (enable)
+ *  - CH7 = PA/LNA control (disable)
  *  - CH17 = (optional) gpio debug for radio ramp-up
  *  - CH18 = (optional) gpio debug for wfr timer RX enabled
  *  - CH19 = (optional) gpio debug for wfr timer radio disabled
@@ -277,6 +280,27 @@ struct nrf_ccm_data
 struct nrf_ccm_data g_nrf_ccm_data;
 #endif
 
+static int g_ble_phy_gpiote_idx;
+
+#if MYNEWT_VAL(BLE_LL_PA) || MYNEWT_VAL(BLE_LL_LNA)
+
+#define PLNA_SINGLE_GPIO \
+        (!MYNEWT_VAL(BLE_LL_PA) || !MYNEWT_VAL(BLE_LL_LNA) || \
+         (MYNEWT_VAL(BLE_LL_PA_GPIO) == MYNEWT_VAL(BLE_LL_LNA_GPIO)))
+
+#if PLNA_SINGLE_GPIO
+static uint8_t plna_idx;
+#else
+#if MYNEWT_VAL(BLE_LL_PA)
+static uint8_t plna_pa_idx;
+#endif
+#if MYNEWT_VAL(BLE_LL_LNA)
+static uint8_t plna_lna_idx;
+#endif
+#endif
+
+#endif
+
 static void
 ble_phy_apply_errata_102_106_107(void)
 {
@@ -400,6 +424,36 @@ ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode)
 }
 #endif
 
+static void
+ble_phy_plna_enable_pa(void)
+{
+#if MYNEWT_VAL(BLE_LL_PA)
+    ble_ll_plna_pa_enable();
+
+#if !PLNA_SINGLE_GPIO
+    NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_pa_idx]);
+    NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_pa_idx]);
+#endif
+
+    NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
+#endif
+}
+
+static void
+ble_phy_plna_enable_lna(void)
+{
+#if MYNEWT_VAL(BLE_LL_LNA)
+    ble_ll_plna_lna_enable();
+
+#if !PLNA_SINGLE_GPIO
+    NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_lna_idx]);
+    NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_lna_idx]);
+#endif
+
+    NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
+#endif
+}
+
 int
 ble_phy_get_cur_phy(void)
 {
@@ -669,7 +723,7 @@ ble_phy_set_start_now(void)
 /**
  * Function is used to set PPI so that we can time out waiting for a reception
  * to occur. This happens for two reasons: we have sent a packet and we are
- * waiting for a respons (txrx should be set to ENABLE_TXRX) or we are
+ * waiting for a response (txrx should be set to ENABLE_TXRX) or we are
  * starting a connection event and we are a slave and we are waiting for the
  * master to send us a packet (txrx should be set to ENABLE_RX).
  *
@@ -703,17 +757,6 @@ ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs)
          * by waiting 1 usec more.
          */
         end_time += 1;
-#if MYNEWT_VAL(BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN) > 0
-        if ((phy == BLE_PHY_MODE_CODED_125KBPS) ||
-                                    (phy == BLE_PHY_MODE_CODED_500KBPS)) {
-            /*
-             * Some controllers exceed T_IFS when transmitting on coded phy
-             * so let's wait a bit longer to be able to talk to them if this
-             * workaround is enabled.
-             */
-            end_time += MYNEWT_VAL(BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN);
-        }
-#endif
     } else {
         /*
          * RX shall start no later than wfr_usecs after RX enabled.
@@ -741,9 +784,6 @@ ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs)
     /* Enable wait for response PPI */
     NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
 
-    /* Enable the disabled interrupt so we time out on events compare */
-    NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
-
     /*
      * It may happen that if CPU is halted for a brief moment (e.g. during flash
      * erase or write), TIMER0 already counted past CC[3] and thus wfr will not
@@ -871,7 +911,8 @@ ble_phy_rx_xcvr_setup(void)
                         RADIO_SHORTS_ADDRESS_RSSISTART_Msk |
                         RADIO_SHORTS_DISABLED_RSSISTOP_Msk;
 
-    NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk;
+    NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk |
+                          RADIO_INTENSET_DISABLED_Msk;
 }
 
 /**
@@ -885,7 +926,6 @@ ble_phy_tx_end_isr(void)
     uint8_t was_encrypted;
     uint8_t transition;
     uint32_t rx_time;
-    uint32_t wfr_time;
 
     /* Store PHY on which we've just transmitted smth */
     tx_phy_mode = g_ble_phy_data.phy_cur_phy_mode;
@@ -897,13 +937,6 @@ ble_phy_tx_end_isr(void)
     /* Better be in TX state! */
     assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX);
 
-    /* Clear events and clear interrupt on disabled event */
-    NRF_RADIO->EVENTS_DISABLED = 0;
-    NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk;
-    NRF_RADIO->EVENTS_END = 0;
-    wfr_time = NRF_RADIO->SHORTS;
-    (void)wfr_time;
-
 #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
     /*
      * XXX: not sure what to do. We had a HW error during transmission.
@@ -946,6 +979,8 @@ ble_phy_tx_end_isr(void)
         NRF_TIMER0->CC[0] = rx_time;
         NRF_TIMER0->EVENTS_COMPARE[0] = 0;
         NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk;
+
+        ble_phy_plna_enable_lna();
     } else {
         /*
          * XXX: not sure we need to stop the timer here all the time. Or that
@@ -993,10 +1028,6 @@ ble_phy_rx_end_isr(void)
     uint32_t tx_time;
     struct ble_mbuf_hdr *ble_hdr;
 
-    /* Clear events and clear interrupt */
-    NRF_RADIO->EVENTS_END = 0;
-    NRF_RADIO->INTENCLR = RADIO_INTENCLR_END_Msk;
-
     /* Disable automatic RXEN */
     NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk;
 
@@ -1078,6 +1109,8 @@ ble_phy_rx_end_isr(void)
     NRF_TIMER0->EVENTS_COMPARE[0] = 0;
     NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk;
 
+    ble_phy_plna_enable_pa();
+
     /*
      * XXX: Hack warning!
      *
@@ -1127,9 +1160,9 @@ ble_phy_rx_start_isr(void)
 
     /* Clear events and clear interrupt */
     NRF_RADIO->EVENTS_ADDRESS = 0;
+    NRF_RADIO->INTENCLR = RADIO_INTENCLR_ADDRESS_Msk;
 
-    /* Clear wfr timer channels and DISABLED interrupt */
-    NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk | RADIO_INTENCLR_ADDRESS_Msk;
+    /* Clear wfr timer channels */
     NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk;
 
     /* Initialize the ble mbuf header */
@@ -1221,7 +1254,6 @@ ble_phy_rx_start_isr(void)
     if (rc >= 0) {
         /* Set rx started flag and enable rx end ISR */
         g_ble_phy_data.phy_rx_started = 1;
-        NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk;
     } else {
         /* Disable PHY */
         ble_phy_disable();
@@ -1267,28 +1299,44 @@ ble_phy_isr(void)
         }
     }
 
-    /* Check for disabled event. This only happens for transmits now */
+    /* Handle disabled event. This is enabled for both TX and RX. On RX, we
+     * need to check phy_rx_started flag to make sure we actually were receiving
+     * a PDU, otherwise this is due to wfr.
+     */
     if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) {
-        if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) {
-            NRF_RADIO->EVENTS_DISABLED = 0;
-            ble_ll_wfr_timer_exp(NULL);
-        } else if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) {
-            assert(0);
-        } else {
+        BLE_LL_ASSERT(NRF_RADIO->EVENTS_END ||
+                      ((g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) &&
+                       !g_ble_phy_data.phy_rx_started));
+        NRF_RADIO->EVENTS_END = 0;
+        NRF_RADIO->EVENTS_DISABLED = 0;
+        NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk;
+
+        switch (g_ble_phy_data.phy_state) {
+        case BLE_PHY_STATE_RX:
+#if MYNEWT_VAL(BLE_LL_LNA)
+            NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
+            ble_ll_plna_lna_disable();
+#endif
+            if (g_ble_phy_data.phy_rx_started) {
+                ble_phy_rx_end_isr();
+            } else {
+                ble_ll_wfr_timer_exp(NULL);
+            }
+            break;
+        case BLE_PHY_STATE_TX:
+#if MYNEWT_VAL(BLE_LL_PA)
+            NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
+            ble_ll_plna_pa_disable();
+#endif
             ble_phy_tx_end_isr();
+            break;
+        default:
+            BLE_LL_ASSERT(0);
         }
     }
 
-    /* Receive packet end (we dont enable this for transmit) */
-    if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) {
-        ble_phy_rx_end_isr();
-    }
-
     g_ble_phy_data.phy_transition_late = 0;
 
-    /* Ensures IRQ is cleared */
-    irq_en = NRF_RADIO->SHORTS;
-
     /* Count # of interrupts */
     STATS_INC(ble_phy_stats, phy_isrs);
 
@@ -1296,13 +1344,17 @@ ble_phy_isr(void)
 }
 
 #if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 || \
-        MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \
-        MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
-static inline void
-ble_phy_dbg_time_setup_gpiote(int index, int pin)
+    MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \
+    MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0 || \
+    MYNEWT_VAL(BLE_LL_PA) || \
+    MYNEWT_VAL(BLE_LL_LNA)
+static int
+ble_phy_gpiote_configure(int pin)
 {
     NRF_GPIO_Type *port;
 
+    g_ble_phy_gpiote_idx--;
+
 #if NRF52840_XXAA
     port = pin > 31 ? NRF_P1 : NRF_P0;
     pin &= 0x1f;
@@ -1314,7 +1366,7 @@ ble_phy_dbg_time_setup_gpiote(int index, int pin)
     port->DIRSET = (1 << pin);
     port->OUTCLR = (1 << pin);
 
-    NRF_GPIOTE->CONFIG[index] =
+    NRF_GPIOTE->CONFIG[g_ble_phy_gpiote_idx] =
                         (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
                         ((pin & 0x1F) << GPIOTE_CONFIG_PSEL_Pos) |
 #if NRF52840_XXAA
@@ -1322,13 +1374,17 @@ ble_phy_dbg_time_setup_gpiote(int index, int pin)
 #else
                         0;
 #endif
+
+    BLE_LL_ASSERT(g_ble_phy_gpiote_idx >= 0);
+
+    return g_ble_phy_gpiote_idx;
 }
 #endif
 
 static void
 ble_phy_dbg_time_setup(void)
 {
-    int gpiote_idx __attribute__((unused)) = 8;
+    int idx __attribute__((unused));
 
     /*
      * We setup GPIOTE starting from last configuration index to minimize risk
@@ -1337,44 +1393,41 @@ ble_phy_dbg_time_setup(void)
      */
 
 #if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0
-    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
-                              MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN));
+    idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN));
 
     NRF_PPI->CH[17].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
-    NRF_PPI->CH[17].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]);
+    NRF_PPI->CH[17].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]);
     NRF_PPI->CHENSET = PPI_CHEN_CH17_Msk;
 
     /* CH[20] and PPI CH[21] are on to trigger TASKS_TXEN or TASKS_RXEN */
-    NRF_PPI->FORK[20].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]);
-    NRF_PPI->FORK[21].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]);
+    NRF_PPI->FORK[20].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]);
+    NRF_PPI->FORK[21].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]);
 #endif
 
 #if MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0
-    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
-                              MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN));
+    idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN));
 
     /* CH[26] and CH[27] are always on for EVENT_ADDRESS and EVENT_END */
-    NRF_PPI->FORK[26].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]);
-    NRF_PPI->FORK[27].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]);
+    NRF_PPI->FORK[26].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]);
+    NRF_PPI->FORK[27].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]);
 #endif
 
 #if MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
-    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
-                              MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN));
+    idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN));
 
 #if NRF52840_XXAA
     NRF_PPI->CH[18].EEP = (uint32_t)&(NRF_RADIO->EVENTS_RXREADY);
 #else
     NRF_PPI->CH[18].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
 #endif
-    NRF_PPI->CH[18].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[gpiote_idx]);
+    NRF_PPI->CH[18].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_SET[idx]);
     NRF_PPI->CH[19].EEP = (uint32_t)&(NRF_RADIO->EVENTS_DISABLED);
-    NRF_PPI->CH[19].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]);
+    NRF_PPI->CH[19].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]);
     NRF_PPI->CHENSET = PPI_CHEN_CH18_Msk | PPI_CHEN_CH19_Msk;
 
     /* CH[4] and CH[5] are always on for wfr */
-    NRF_PPI->FORK[4].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]);
-    NRF_PPI->FORK[5].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[gpiote_idx]);
+    NRF_PPI->FORK[4].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]);
+    NRF_PPI->FORK[5].TEP = (uint32_t)&(NRF_GPIOTE->TASKS_CLR[idx]);
 #endif
 }
 
@@ -1390,6 +1443,8 @@ ble_phy_init(void)
 {
     int rc;
 
+    g_ble_phy_gpiote_idx = 8;
+
     /* Default phy to use is 1M */
     g_ble_phy_data.phy_cur_phy_mode = BLE_PHY_MODE_1M;
     g_ble_phy_data.phy_tx_phy_mode = BLE_PHY_MODE_1M;
@@ -1473,6 +1528,27 @@ ble_phy_init(void)
     NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[3]);
     NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE);
 
+#if MYNEWT_VAL(BLE_LL_PA) || MYNEWT_VAL(BLE_LL_LNA)
+#if PLNA_SINGLE_GPIO
+    plna_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_PA_GPIO));
+    NRF_PPI->CH[6].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_SET[plna_idx]);
+    NRF_PPI->CH[7].TEP = (uint32_t) &(NRF_GPIOTE->TASKS_CLR[plna_idx]);
+#else
+#if MYNEWT_VAL(BLE_LL_PA)
+    plna_pa_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_PA_GPIO));
+    NRF_GPIOTE->TASKS_CLR[plna_pa_idx] = 1;
+#endif
+#if MYNEWT_VAL(BLE_LL_LNA)
+    plna_lna_idx = ble_phy_gpiote_configure(MYNEWT_VAL(BLE_LL_LNA_GPIO));
+    NRF_GPIOTE->TASKS_CLR[plna_lna_idx] = 1;
+#endif
+#endif
+
+    NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
+    NRF_PPI->CH[7].EEP = (uint32_t)&(NRF_RADIO->EVENTS_DISABLED);
+    NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
+#endif
+
     /* Set isr in vector table and enable interrupt */
 #ifndef RIOT_VERSION
     NVIC_SetPriority(RADIO_IRQn, 0);
@@ -1635,7 +1711,10 @@ ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
         /* Enable PPI to automatically start TXEN */
         NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk;
         rc = 0;
+
+        ble_phy_plna_enable_pa();
     }
+
     return rc;
 }
 
@@ -1680,6 +1759,8 @@ ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs)
     /* Enable PPI to automatically start RXEN */
     NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk;
 
+    ble_phy_plna_enable_lna();
+
     /* Start rx */
     rc = ble_phy_rx();
 
@@ -1977,6 +2058,7 @@ ble_phy_disable_irq_and_ppi(void)
     NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH20_Msk |
           PPI_CHEN_CH21_Msk | PPI_CHEN_CH23_Msk |
           PPI_CHEN_CH25_Msk | PPI_CHEN_CH31_Msk;
+    NRF_PPI->CHENCLR = PPI_CHEN_CH6_Msk | PPI_CHEN_CH7_Msk;
     NVIC_ClearPendingIRQ(RADIO_IRQn);
     g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE;
 }
diff --git a/nimble/drivers/nrf52/syscfg.yml b/nimble/drivers/nrf52/syscfg.yml
index ce5123721a..3bd49708ca 100644
--- a/nimble/drivers/nrf52/syscfg.yml
+++ b/nimble/drivers/nrf52/syscfg.yml
@@ -22,16 +22,6 @@ syscfg.defs:
             Enable SystemView tracing module for radio driver.
         value: 0
 
-    BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN:
-        description: >
-            This defines additional margin for T_IFS tolerance while in
-            RX on coded phy to allow maintaining connections with some
-            controllers that exceed proper T_IFS (150 usecs) by more
-            than allowed 2 usecs.
-            This value shall be only used for debugging purposes. It is
-            strongly recommended to keep this settings at default value
-            to ensure compliance with specification.
-        value: 0
     BLE_PHY_DBG_TIME_TXRXEN_READY_PIN:
         description: >
             When set to proper GPIO pin number, this pin will be set
@@ -73,3 +63,10 @@ syscfg.defs:
             - nRF52840 Engineering C
             - nRF52840 Rev 1 (final silicon)
         value: 1
+
+    BLE_PHY_UBLOX_BMD345_PUBLIC_ADDR:
+        description: >
+            Ublox BMD-345 modules come with public address preprogrammed
+            in UICR register. If enabled public address will be read from
+            custom UICR instead of FICR register.
+        value: 0
diff --git a/nimble/drivers/nrf5340/src/ble_hw.c b/nimble/drivers/nrf5340/src/ble_hw.c
index bbe6169779..92bf024979 100644
--- a/nimble/drivers/nrf5340/src/ble_hw.c
+++ b/nimble/drivers/nrf5340/src/ble_hw.c
@@ -461,13 +461,9 @@ ble_hw_resolv_list_size(void)
 int
 ble_hw_resolv_list_match(void)
 {
-    uint32_t index;
-
-    if (NRF_AAR_NS->EVENTS_END) {
-        if (NRF_AAR_NS->EVENTS_RESOLVED) {
-            index = NRF_AAR_NS->STATUS;
-            return (int)index;
-        }
+    if (NRF_AAR_NS->ENABLE && NRF_AAR_NS->EVENTS_END &&
+        NRF_AAR_NS->EVENTS_RESOLVED) {
+        return (int)NRF_AAR_NS->STATUS;
     }
 
     return -1;
diff --git a/nimble/drivers/nrf5340/src/ble_phy.c b/nimble/drivers/nrf5340/src/ble_phy.c
index e07bbaa11f..12be202db5 100644
--- a/nimble/drivers/nrf5340/src/ble_phy.c
+++ b/nimble/drivers/nrf5340/src/ble_phy.c
@@ -22,6 +22,7 @@
 #include <assert.h>
 #include <syscfg/syscfg.h>
 #include <os/os.h>
+#include <bsp/bsp.h>
 #include <nimble/ble.h>
 #include <nimble/nimble_opt.h>
 #include <nimble/nimble_npl.h>
@@ -38,6 +39,8 @@
  * DPPI somewhere else.
  * TODO maybe we could reduce number of used channels if we reuse same channel
  * for mutually exclusive events but for now make it simpler to debug.
+ *
+ * Optionally channels 6,7,8 are used for GPIO DBG.
  */
 
 #define DPPI_CH_TIMER0_EVENTS_COMPARE_0         0
@@ -82,6 +85,39 @@
 #define DPPI_SUBSCRIBE_CCM_TASKS_CRYPT(_enable)        ((DPPI_CH_RADIO_EVENTS_ADDRESS << CCM_SUBSCRIBE_CRYPT_CHIDX_Pos) | \
                                                         ((_enable) << CCM_SUBSCRIBE_CRYPT_EN_Pos))
 
+/* used for GPIO DBG */
+#define DPPI_CH_RADIO_EVENTS_READY              6
+#define DPPI_CH_RADIO_EVENTS_RXREADY            7
+#define DPPI_CH_RADIO_EVENTS_DISABLED           8
+
+#define DPPI_CH_ENABLE_RADIO_EVENTS_READY       DPPIC_CHEN_CH6_Msk
+#define DPPI_CH_ENABLE_RADIO_EVENTS_RXREADY     DPPIC_CHEN_CH7_Msk
+#define DPPI_CH_ENABLE_RADIO_EVENTS_DISABLED    DPPIC_CHEN_CH8_Msk
+
+#define DPPI_PUBLISH_RADIO_EVENTS_READY                 ((DPPI_CH_RADIO_EVENTS_READY << RADIO_PUBLISH_READY_CHIDX_Pos) | \
+                                                         (RADIO_PUBLISH_READY_EN_Enabled << RADIO_PUBLISH_READY_EN_Pos))
+#define DPPI_PUBLISH_RADIO_EVENTS_RXREADY               ((DPPI_CH_RADIO_EVENTS_RXREADY << RADIO_PUBLISH_RXREADY_CHIDX_Pos) | \
+                                                         (RADIO_PUBLISH_RXREADY_EN_Enabled << RADIO_PUBLISH_RXREADY_EN_Pos))
+#define DPPI_PUBLISH_RADIO_EVENTS_DISABLED              ((DPPI_CH_RADIO_EVENTS_DISABLED << RADIO_PUBLISH_DISABLED_CHIDX_Pos) | \
+                                                         (RADIO_PUBLISH_DISABLED_EN_Enabled << RADIO_PUBLISH_DISABLED_EN_Pos))
+
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_TXRXEN     ((DPPI_CH_TIMER0_EVENTS_COMPARE_0 << GPIOTE_SUBSCRIBE_SET_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_SET_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_ADDRESS    ((DPPI_CH_RADIO_EVENTS_ADDRESS << GPIOTE_SUBSCRIBE_SET_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_SET_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_END        ((DPPI_CH_RADIO_EVENTS_END << GPIOTE_SUBSCRIBE_CLR_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_CLR_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_READY      ((DPPI_CH_RADIO_EVENTS_READY << GPIOTE_SUBSCRIBE_CLR_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_CLR_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_RXREADY    ((DPPI_CH_RADIO_EVENTS_RXREADY << GPIOTE_SUBSCRIBE_SET_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_SET_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_DISABLED   ((DPPI_CH_RADIO_EVENTS_DISABLED << GPIOTE_SUBSCRIBE_CLR_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_CLR_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_CAPTURE3   ((DPPI_CH_TIMER0_EVENTS_COMPARE_3 << GPIOTE_SUBSCRIBE_CLR_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_CLR_EN_Pos))
+#define DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_ADDRESS    ((DPPI_CH_RADIO_EVENTS_ADDRESS << GPIOTE_SUBSCRIBE_CLR_CHIDX_Pos) | \
+                                                    (1 << GPIOTE_SUBSCRIBE_CLR_EN_Pos))
+
 extern uint8_t g_nrf_num_irks;
 extern uint32_t g_nrf_irk_list[];
 
@@ -1179,6 +1215,84 @@ ble_phy_isr(void)
     os_trace_isr_exit();
 }
 
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 || \
+        MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \
+        MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
+static inline void
+ble_phy_dbg_time_setup_gpiote(int index, int pin)
+{
+    NRF_GPIO_Type *port;
+
+    port = pin > 31 ? NRF_P1_NS : NRF_P0_NS;
+    pin &= 0x1f;
+
+    /* Configure GPIO directly to avoid dependency to hal_gpio (for porting) */
+    port->DIRSET = (1 << pin);
+    port->OUTCLR = (1 << pin);
+
+    NRF_GPIOTE_NS->CONFIG[index] =
+                        (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
+                        ((pin & 0x1F) << GPIOTE_CONFIG_PSEL_Pos) |
+                        ((port == NRF_P1_NS) << GPIOTE_CONFIG_PORT_Pos);
+}
+#endif
+
+static void
+ble_phy_dbg_time_setup(void)
+{
+    int gpiote_idx __attribute__((unused)) = 8;
+
+    /*
+     * We setup GPIOTE starting from last configuration index to minimize risk
+     * of conflict with GPIO setup via hal. It's not great solution, but since
+     * this is just debugging code we can live with this.
+     */
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0
+    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
+                                  MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN));
+
+    NRF_GPIOTE_NS->SUBSCRIBE_SET[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_TXRXEN;
+    NRF_GPIOTE_NS->SUBSCRIBE_CLR[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_READY;
+
+    /* Publish RADIO->EVENTS_READY */
+    NRF_RADIO_NS->PUBLISH_READY = DPPI_PUBLISH_RADIO_EVENTS_READY;
+    NRF_DPPIC_NS->CHENSET = DPPI_CH_ENABLE_RADIO_EVENTS_READY;
+
+#endif
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0
+    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
+                                  MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN));
+
+    NRF_GPIOTE_NS->SUBSCRIBE_SET[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_ADDRESS;
+    NRF_GPIOTE_NS->SUBSCRIBE_CLR[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_END;
+#endif
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
+    ble_phy_dbg_time_setup_gpiote(--gpiote_idx,
+                                  MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN));
+
+    NRF_GPIOTE_NS->SUBSCRIBE_SET[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_SET_RXREADY;
+
+    /* TODO figure out how (if?) to subscribe task to multiple DPPI channels
+     * Currently only last one is working. Also using multiple GPIOTE for same
+     * PIN doesn't work...
+     */
+    NRF_GPIOTE_NS->SUBSCRIBE_CLR[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_DISABLED;
+    NRF_GPIOTE_NS->SUBSCRIBE_CLR[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_ADDRESS;
+    NRF_GPIOTE_NS->SUBSCRIBE_CLR[gpiote_idx] = DPPI_SUBSCRIBE_GPIOTE_TASKS_CLR_CAPTURE3;
+
+    /* Publish RADIO->EVENTS_RXREADY */
+    NRF_RADIO_NS->PUBLISH_RXREADY = DPPI_PUBLISH_RADIO_EVENTS_RXREADY;
+    NRF_DPPIC_NS->CHENSET = DPPI_CH_ENABLE_RADIO_EVENTS_RXREADY;
+
+    /* Publish RADIO->EVENTS_DISABLED */
+    NRF_RADIO_NS->PUBLISH_DISABLED = DPPI_CH_ENABLE_RADIO_EVENTS_DISABLED;
+    NRF_DPPIC_NS->CHENSET = DPPI_CH_ENABLE_RADIO_EVENTS_DISABLED;
+#endif
+}
+
 int
 ble_phy_init(void)
 {
@@ -1296,6 +1410,8 @@ ble_phy_init(void)
         g_ble_phy_data.phy_stats_initialized = 1;
     }
 
+    ble_phy_dbg_time_setup();
+
     return 0;
 }
 
@@ -1725,6 +1841,41 @@ ble_phy_restart_rx(void)
     ble_phy_rx();
 }
 
+static void
+ble_phy_dbg_clear_pins(void)
+{
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0 || \
+        MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0 || \
+        MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
+    NRF_GPIO_Type *port;
+    int pin;
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN) >= 0
+    pin = MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN);
+    port = pin > 31 ? NRF_P1_NS : NRF_P0_NS;
+    pin &= 0x1f;
+
+    port->OUTCLR = (1 << pin);
+#endif
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN) >= 0
+    pin = MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN);
+    port = pin > 31 ? NRF_P1_NS : NRF_P0_NS;
+    pin &= 0x1f;
+
+    port->OUTCLR = (1 << pin);
+#endif
+
+#if MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN) >= 0
+    pin = MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN);
+    port = pin > 31 ? NRF_P1_NS : NRF_P0_NS;
+    pin &= 0x1f;
+
+    port->OUTCLR = (1 << pin);
+#endif
+#endif
+}
+
 void
 ble_phy_disable(void)
 {
@@ -1732,6 +1883,8 @@ ble_phy_disable(void)
 
     ble_phy_stop_usec_timer();
     ble_phy_disable_irq_and_ppi();
+
+    ble_phy_dbg_clear_pins();
 }
 
 uint32_t
diff --git a/nimble/drivers/nrf5340/syscfg.yml b/nimble/drivers/nrf5340/syscfg.yml
index dd8b93047c..ed82f6d604 100644
--- a/nimble/drivers/nrf5340/syscfg.yml
+++ b/nimble/drivers/nrf5340/syscfg.yml
@@ -21,3 +21,42 @@ syscfg.defs:
         description: >
             Enable SystemView tracing module for radio driver.
         value: 0
+
+    BLE_PHY_DBG_TIME_TXRXEN_READY_PIN:
+        description: >
+            When set to proper GPIO pin number, this pin will be set
+            to high state when radio is enabled (TASKS_TXEN or TASKS_RXEN)
+            and back to low state on radio EVENTS_READY.
+            This can be used to measure radio ram-up time.
+
+            Note:
+            GPIO control for selected pin needs to be assigned to Network
+            Core, configure IPC_NRF5340_NET_GPIO in ipc_nrf5340 driver on
+            Application Core.
+        value: -1
+
+    BLE_PHY_DBG_TIME_ADDRESS_END_PIN:
+        description: >
+            When set to proper GPIO pin number, this pin will be set
+            to high state on radio EVENTS_ADDRESS and back to low state
+            on radio EVENTS_END.
+            This can be used to measure radio pipeline delays.
+
+            Note:
+            GPIO control for selected pin needs to be assigned to Network
+            Core, configure IPC_NRF5340_NET_GPIO in ipc_nrf5340 driver on
+            Application Core.
+        value: -1
+
+    BLE_PHY_DBG_TIME_WFR_PIN:
+        description: >
+            When set to proper GPIO pin number, this pin will be set
+            to high state on radio EVENTS_RXREADY and back to low
+            state when wfr timer expires.
+            This can be used to check if wfr is calculated properly.
+
+            Note:
+            GPIO control for selected pin needs to be assigned to Network
+            Core, configure IPC_NRF5340_NET_GPIO in ipc_nrf5340 driver on
+            Application Core.
+        value: -1
diff --git a/nimble/drivers/plna/sky66112/pkg.yml b/nimble/drivers/plna/sky66112/pkg.yml
new file mode 100644
index 0000000000..c3cb0fd322
--- /dev/null
+++ b/nimble/drivers/plna/sky66112/pkg.yml
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: nimble/drivers/plna/sky66112
+pkg.description: Driver for SKY66112 front-end module
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "https://mynewt.apache.org/"
+pkg.apis:
+    - ble_ll_pa
+    - ble_ll_lna
+pkg.deps:
+    - nimble/controller
+
+pkg.init:
+    sky66112_init: 999
diff --git a/nimble/drivers/plna/sky66112/src/sky66112.c b/nimble/drivers/plna/sky66112/src/sky66112.c
new file mode 100644
index 0000000000..09afe5f96e
--- /dev/null
+++ b/nimble/drivers/plna/sky66112/src/sky66112.c
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include "syscfg/syscfg.h"
+#include "hal/hal_gpio.h"
+#include "controller/ble_ll_plna.h"
+
+#define NO_BYPASS \
+        ((MYNEWT_VAL(SKY66112_TX_BYPASS) >= 0) && \
+         (MYNEWT_VAL(SKY66112_RX_BYPASS) >= 0))
+
+static void
+sky66112_bypass(uint8_t enabled)
+{
+    if (NO_BYPASS) {
+        return;
+    }
+
+    hal_gpio_write(MYNEWT_VAL(SKY66112_PIN_CPS), enabled);
+}
+
+void
+ble_ll_plna_pa_init(void)
+{
+    /* Nothing to do here */
+}
+
+void
+ble_ll_plna_pa_enable(void)
+{
+    if (!MYNEWT_VAL(SKY66112_TX_BYPASS)) {
+        sky66112_bypass(0);
+    }
+}
+
+void
+ble_ll_plna_pa_disable(void)
+{
+    if (!MYNEWT_VAL(SKY66112_TX_BYPASS)) {
+        sky66112_bypass(1);
+    }
+}
+
+void
+ble_ll_plna_lna_init(void)
+{
+    /* Nothing to do here */
+}
+
+void
+ble_ll_plna_lna_enable(void)
+{
+    if (!MYNEWT_VAL(SKY66112_RX_BYPASS)) {
+        sky66112_bypass(0);
+    }
+}
+
+void
+ble_ll_plna_lna_disable(void)
+{
+    if (!MYNEWT_VAL(SKY66112_RX_BYPASS)) {
+        sky66112_bypass(1);
+    }
+}
+
+void
+sky66112_init(void)
+{
+    int pin;
+
+    /* Use CRX and CTX to enable sleep mode */
+    pin = MYNEWT_VAL(SKY66112_PIN_CSD);
+    if (pin >= 0) {
+        hal_gpio_init_out(pin, 1);
+    }
+
+    pin = MYNEWT_VAL(SKY66112_PIN_CPS);
+    if (NO_BYPASS) {
+        /* Disable bypass */
+        if (pin >= 0) {
+            hal_gpio_init_out(pin, 0);
+        }
+    } else {
+        /* Enable bypass, we'll disable it when needed */
+        assert(pin >= 0);
+        hal_gpio_init_out(pin, 1);
+    }
+
+    pin = MYNEWT_VAL(SKY66112_PIN_CHL);
+    if (pin >= 0) {
+        hal_gpio_init_out(pin, MYNEWT_VAL(SKY66112_TX_HP_MODE));
+    }
+
+    /* Select ANT1 */
+    pin = MYNEWT_VAL(SKY66112_PIN_SEL);
+    if (pin >= 0) {
+        hal_gpio_init_out(pin, 0);
+    }
+}
diff --git a/nimble/drivers/plna/sky66112/syscfg.yml b/nimble/drivers/plna/sky66112/syscfg.yml
new file mode 100644
index 0000000000..50434553a0
--- /dev/null
+++ b/nimble/drivers/plna/sky66112/syscfg.yml
@@ -0,0 +1,66 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    SKY66112_PIN_CSD:
+        description: >
+            GPIO pin number to control CSD signal.
+            When set to '-1', pin state will not be changed and it should be
+            driven externally.
+        value: -1
+    SKY66112_PIN_CPS:
+        description: >
+            GPIO pin number to control CPS signal.
+            When set to '-1', pin state will not be changed and it should be
+            driven externally.
+        value: -1
+    SKY66112_PIN_CHL:
+        description: >
+            GPIO pin number to control CHL signal.
+            When set to '-1', pin state will not be changed and it should be
+            driven externally.
+        value: -1
+    SKY66112_PIN_SEL:
+        description: >
+            GPIO pin number to control SEL signal.
+            When set to '-1', pin state will not be changed and it should be
+            driven externally.
+        value: -1
+    SKY66112_TX_HP_MODE:
+        description: >
+            Enables high-power mode for TX.
+            Only valid if CHL signal is controller by driver.
+        value: 0
+    SKY66112_TX_BYPASS:
+        description: >
+            Enables bypass for TX which effectively disables operation as PA.
+            Only valid if CPS signal is controller by driver.
+        value: 0
+    SKY66112_RX_BYPASS:
+        description: >
+            Enables bypass for RX which effectively disables operation as PA.
+            Only valid if CPS signal is controller by driver.
+        value: 0
+
+syscfg.vals.!BLE_LL_PA:
+    # Enable TX bypass by default if PA is disabled
+    SKY66112_TX_BYPASS: 1
+
+syscfg.vals.!BLE_LL_LNA:
+    # Enable RX bypass by default if LNA is disabled
+    SKY66112_RX_BYPASS: 1
diff --git a/nimble/host/include/host/ble_att.h b/nimble/host/include/host/ble_att.h
index 391a992aeb..1427b3b33d 100644
--- a/nimble/host/include/host/ble_att.h
+++ b/nimble/host/include/host/ble_att.h
@@ -38,6 +38,7 @@ struct os_mbuf;
 #define BLE_ATT_UUID_SECONDARY_SERVICE      0x2801
 #define BLE_ATT_UUID_INCLUDE                0x2802
 #define BLE_ATT_UUID_CHARACTERISTIC         0x2803
+#define BLE_ATT_UUID_DESCRIPTOR             0x2902
 
 #define BLE_ATT_ERR_INVALID_HANDLE          0x01
 #define BLE_ATT_ERR_READ_NOT_PERMITTED      0x02
diff --git a/nimble/host/include/host/ble_gap.h b/nimble/host/include/host/ble_gap.h
index ec795f0e21..63ae900da5 100644
--- a/nimble/host/include/host/ble_gap.h
+++ b/nimble/host/include/host/ble_gap.h
@@ -1160,7 +1160,19 @@ struct ble_gap_ext_adv_params {
     /** If perform high-duty directed advertising */
     unsigned int high_duty_directed:1;
 
-    /** If use legacy PDUs for advertising */
+    /** If use legacy PDUs for advertising.
+     *
+     *  Valid combinations of the connectable, scannable, directed,
+     *  high_duty_directed options with the legcy_pdu flag are:
+     *  - IND       -> legacy_pdu + connectable + scannable
+     *  - LD_DIR    -> legacy_pdu + connectable + directed
+     *  - HD_DIR    -> legacy_pdu + connectable + directed + high_duty_directed
+     *  - SCAN      -> legacy_pdu + scannable
+     *  - NONCONN   -> legacy_pdu
+     *
+     * Any other combination of these options combined with the legacy_pdu flag
+     * are invalid.
+     */
     unsigned int legacy_pdu:1;
 
     /** If perform anonymous advertising */
@@ -1314,6 +1326,18 @@ int ble_gap_ext_adv_remove(uint8_t instance);
  *                      other error code on failure.
  */
 int ble_gap_ext_adv_clear(void);
+
+/**
+ * Indicates whether an advertisement procedure is currently in progress on
+ * the specified Instance
+ *
+ * @param instance            Instance Id
+ *
+ * @return 0 if there is no active advertising procedure for the instance,
+ *         1 otherwise
+ *
+ */
+int ble_gap_ext_adv_active(uint8_t instance);
 #endif
 
 /* Periodic Advertising */
@@ -1592,6 +1616,16 @@ int ble_gap_disc(uint8_t own_addr_type, int32_t duration_ms,
  *                              its last Scan Duration until it begins the
  *                              subsequent Scan Duration. Specify 0 to scan
  *                              continuously. Units are 1.28 second.
+ * @param filter_duplicates     Set to enable packet filtering in the
+ *                              controller
+ * @param filter_policy         Set the used filter policy. Valid values are:
+ *                                      - BLE_HCI_SCAN_FILT_NO_WL
+ *                                      - BLE_HCI_SCAN_FILT_USE_WL
+ *                                      - BLE_HCI_SCAN_FILT_NO_WL_INITA
+ *                                      - BLE_HCI_SCAN_FILT_USE_WL_INITA
+ *                                      - BLE_HCI_SCAN_FILT_MAX
+ *                              This parameter is ignored unless
+ *                              @p filter_duplicates is set.
  * @param limited               If limited discovery procedure should be used.
  * @param uncoded_params        Additional arguments specifying the particulars
  *                              of the discovery procedure for uncoded PHY.
diff --git a/nimble/host/include/host/ble_gatt.h b/nimble/host/include/host/ble_gatt.h
index b06344fc2e..77028ce46d 100644
--- a/nimble/host/include/host/ble_gatt.h
+++ b/nimble/host/include/host/ble_gatt.h
@@ -894,3 +894,5 @@ int ble_gatts_start(void);
  */
 
 #endif
+
+int ble_compute_db_hash(uint8_t db_hash[16]);
\ No newline at end of file
diff --git a/nimble/host/include/host/ble_store.h b/nimble/host/include/host/ble_store.h
index 30a5666cfe..1f8a35cf9c 100644
--- a/nimble/host/include/host/ble_store.h
+++ b/nimble/host/include/host/ble_store.h
@@ -30,6 +30,7 @@ extern "C" {
 #define BLE_STORE_OBJ_TYPE_OUR_SEC      1
 #define BLE_STORE_OBJ_TYPE_PEER_SEC     2
 #define BLE_STORE_OBJ_TYPE_CCCD         3
+#define BLE_STORE_OBJ_TYPE_HASH         4
 
 /** Failed to persist record; insufficient storage capacity. */
 #define BLE_STORE_EVENT_OVERFLOW        1
@@ -120,6 +121,30 @@ struct ble_store_value_cccd {
     unsigned value_changed:1;
 };
 
+/**
+ * Used as a key for lookups of stored database hashes. 
+ * This struct corresponds to the BLE_STORE_OBJ_TYPE_HASH store object type.
+ */
+struct ble_store_key_hash {
+    /**
+     * Key by peer identity address;
+     * peer_addr=BLE_ADDR_NONE means don't key off peer.
+     */
+    ble_addr_t peer_addr;
+
+    /** Number of results to skip; 0 means retrieve the first match. */
+    uint8_t idx;
+};
+
+/**
+ * Represents the last stored server database hash for a paired client device.
+ * This struct corresponds to the BLE_STORE_OBJ_TYPE_HASH store object type.
+ */
+struct ble_store_value_hash{
+    ble_addr_t peer_addr;
+    uint8_t db_hash[16];
+};
+
 /**
  * Used as a key for store lookups.  This union must be accompanied by an
  * object type code to indicate which field is valid.
@@ -127,6 +152,7 @@ struct ble_store_value_cccd {
 union ble_store_key {
     struct ble_store_key_sec sec;
     struct ble_store_key_cccd cccd;
+    struct ble_store_key_hash hash;
 };
 
 /**
@@ -136,6 +162,7 @@ union ble_store_key {
 union ble_store_value {
     struct ble_store_value_sec sec;
     struct ble_store_value_cccd cccd;
+    struct ble_store_value_hash hash;
 };
 
 struct ble_store_status_event {
@@ -266,10 +293,18 @@ int ble_store_read_cccd(const struct ble_store_key_cccd *key,
 int ble_store_write_cccd(const struct ble_store_value_cccd *value);
 int ble_store_delete_cccd(const struct ble_store_key_cccd *key);
 
+int ble_store_read_hash(uint16_t conn_handle,
+                        const struct ble_store_key_hash *key,
+                        struct ble_store_value_hash *out_value);
+int ble_store_write_hash(const struct ble_store_value_hash *value);
+int ble_store_delete_hash(const struct ble_store_key_hash *key);
+
 void ble_store_key_from_value_sec(struct ble_store_key_sec *out_key,
                                   const struct ble_store_value_sec *value);
 void ble_store_key_from_value_cccd(struct ble_store_key_cccd *out_key,
                                    const struct ble_store_value_cccd *value);
+void ble_store_key_from_value_hash(struct ble_store_key_hash *out_key,
+                                   const struct ble_store_value_hash *value);
 
 void ble_store_key_from_value(int obj_type,
                               union ble_store_key *out_key,
diff --git a/nimble/host/mesh/include/mesh/access.h b/nimble/host/mesh/include/mesh/access.h
index 9f923cb9e4..f552b584c2 100644
--- a/nimble/host/mesh/include/mesh/access.h
+++ b/nimble/host/mesh/include/mesh/access.h
@@ -10,6 +10,9 @@
 #ifndef __BT_MESH_ACCESS_H
 #define __BT_MESH_ACCESS_H
 
+#include "msg.h"
+#include <sys/types.h>
+
 /**
  * @brief Bluetooth Mesh Access Layer
  * @defgroup bt_mesh_access Bluetooth Mesh Access Layer
@@ -138,50 +141,33 @@ struct bt_mesh_elem {
 #define BT_MESH_MODEL_ID_LIGHT_LC_SETUPSRV         0x1310
 #define BT_MESH_MODEL_ID_LIGHT_LC_CLI              0x1311
 
-/** Message sending context. */
-struct bt_mesh_msg_ctx {
-	/** NetKey Index of the subnet to send the message on. */
-	uint16_t net_idx;
-
-	/** AppKey Index to encrypt the message with. */
-	uint16_t app_idx;
-
-	/** Remote address. */
-	uint16_t addr;
-
-	/** Destination address of a received message. Not used for sending. */
-	uint16_t recv_dst;
-
-	/** RSSI of received packet. Not used for sending. */
-	int8_t  recv_rssi;
-
-	/** Received TTL value. Not used for sending. */
-	uint8_t  recv_ttl;
-
-	/** Force sending reliably by using segment acknowledgement */
-	bool  send_rel;
-
-	/** TTL, or BT_MESH_TTL_DEFAULT for default TTL. */
-	uint8_t  send_ttl;
-};
-
 struct bt_mesh_model_op {
 	/* OpCode encoded using the BT_MESH_MODEL_OP_* macros */
 	const uint32_t  opcode;
 
-	/* Minimum required message length */
-	const size_t min_len;
+	/** Message length. If the message has variable length then this value
+	 *  indicates minimum message length and should be positive. Handler
+	 *  function should verify precise length based on the contents of the
+	 *  message. If the message has fixed length then this value should
+	 *  be negative. Use BT_MESH_LEN_* macros when defining this value.
+	 */
+	const ssize_t len;
 
 	/* Message handler for the opcode */
-	void (*const func)(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf);
+	int (*const func)(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf);
 };
 
 #define BT_MESH_MODEL_OP_1(b0) (b0)
 #define BT_MESH_MODEL_OP_2(b0, b1) (((b0) << 8) | (b1))
 #define BT_MESH_MODEL_OP_3(b0, cid) ((((b0) << 16) | 0xc00000) | (cid))
 
+/** Macro for encoding exact message length for fixed-length messages.  */
+#define BT_MESH_LEN_EXACT(len) (-len)
+/** Macro for encoding minimum message length for variable-length messages.  */
+#define BT_MESH_LEN_MIN(len) (len)
+
 #define BT_MESH_MODEL_OP_END { 0, 0, NULL }
 #define BT_MESH_MODEL_NO_OPS ((struct bt_mesh_model_op []) \
 			      { BT_MESH_MODEL_OP_END })
@@ -189,55 +175,6 @@ struct bt_mesh_model_op {
 /** Helper to define an empty model array */
 #define BT_MESH_MODEL_NONE ((struct bt_mesh_model []){})
 
-/** Length of a short Mesh MIC. */
-#define BT_MESH_MIC_SHORT 4
-/** Length of a long Mesh MIC. */
-#define BT_MESH_MIC_LONG 8
-
-/** @def BT_MESH_MODEL_OP_LEN
- *
- * @brief Helper to determine the length of an opcode.
- *
- * @param _op Opcode.
- */
-#define BT_MESH_MODEL_OP_LEN(_op) ((_op) <= 0xff ? 1 : (_op) <= 0xffff ? 2 : 3)
-
-/** @def BT_MESH_MODEL_BUF_LEN
- *
- * @brief Helper for model message buffer length.
- *
- * Returns the length of a Mesh model message buffer, including the opcode
- * length and a short MIC.
- *
- * @param _op Opcode of the message.
- * @param _payload_len Length of the model payload.
- */
-#define BT_MESH_MODEL_BUF_LEN(_op, _payload_len)                               \
-	(BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_SHORT)
-
-/** @def BT_MESH_MODEL_BUF_LEN_LONG_MIC
- *
- * @brief Helper for model message buffer length.
- *
- * Returns the length of a Mesh model message buffer, including the opcode
- * length and a long MIC.
- *
- * @param _op Opcode of the message.
- * @param _payload_len Length of the model payload.
- */
-#define BT_MESH_MODEL_BUF_LEN_LONG_MIC(_op, _payload_len)                      \
-	(BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_LONG)
-
-/** @def BT_MESH_MODEL_BUF_DEFINE
- *
- * @brief Define a Mesh model message buffer using @ref NET_BUF_SIMPLE.
- *
- * @param _op Opcode of the message.
- * @param _payload_len Length of the model message payload.
- */
-#define BT_MESH_MODEL_BUF(_op, _payload_len)                      \
-	NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN(_op, (_payload_len)))
-
 /** @def BT_MESH_MODEL_CB
  *
  * @brief Composition data SIG model entry with callback functions.
@@ -384,17 +321,17 @@ struct bt_mesh_model_pub {
 	/** The model the context belongs to. Initialized by the stack. */
 	struct bt_mesh_model *mod;
 
-	uint16_t addr;         /**< Publish Address. */
-	uint16_t key;          /**< Publish AppKey Index. */
+	uint16_t addr;          /**< Publish Address. */
+	uint16_t key:12,        /**< Publish AppKey Index. */
+		 cred:1,        /**< Friendship Credentials Flag. */
+		 send_rel:1,    /**< Force reliable sending (segment acks) */
+		 fast_period:1; /**< Use FastPeriodDivisor */
 
 	uint8_t  ttl;          /**< Publish Time to Live. */
 	uint8_t  retransmit;   /**< Retransmit Count & Interval Steps. */
 	uint8_t  period;       /**< Publish Period. */
 	uint8_t  period_div:4, /**< Divisor for the Period. */
-	      cred:1,       /**< Friendship Credentials Flag. */
-		  send_rel:1,
-	      fast_period:1,/**< Use FastPeriodDivisor */
-	      count:3;      /**< Retransmissions left. */
+		 count:4;
 
 	uint32_t period_start; /**< Start of the current period. */
 
@@ -436,7 +373,7 @@ struct bt_mesh_model_pub {
 	int (*update)(struct bt_mesh_model *mod);
 
 	/** Publish Period Timer. Only for stack-internal use. */
-	struct k_delayed_work timer;
+	struct k_work_delayable timer;
 };
 
 /** Model callback functions. */
@@ -495,14 +432,19 @@ struct bt_mesh_model_cb {
 	void (*const reset)(struct bt_mesh_model *model);
 };
 
+/** Vendor model ID */
+struct bt_mesh_mod_id_vnd {
+	/** Vendor's company ID */
+	uint16_t company;
+	/** Model ID */
+	uint16_t id;
+};
+
 /** Abstraction that describes a Mesh Model instance */
 struct bt_mesh_model {
 	union {
 		const uint16_t id;
-		struct {
-			uint16_t company;
-			uint16_t id;
-		} vnd;
+		const struct bt_mesh_mod_id_vnd vnd;
 	};
 
 	/* Internal information, mainly for persistent storage */
@@ -525,11 +467,10 @@ struct bt_mesh_model {
 	const struct bt_mesh_model_cb * const cb;
 
 #if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
-	/* Pointer to the next model in a model extension tree. */
+	/* Pointer to the next model in a model extension list. */
 	struct bt_mesh_model *next;
-	/* Pointer to the first model this model extends. */
-	struct bt_mesh_model *extends;
 #endif
+
 	/* Model-specific user data */
 	void *user_data;
 };
@@ -539,8 +480,6 @@ struct bt_mesh_send_cb {
 	void (*end)(int err, void *cb_data);
 };
 
-void bt_mesh_model_msg_init(struct os_mbuf *msg, uint32_t opcode);
-
 /** Special TTL value to request using configured default TTL */
 #define BT_MESH_TTL_DEFAULT 0xff
 
@@ -625,41 +564,49 @@ static inline bool bt_mesh_model_in_primary(const struct bt_mesh_model *mod)
 
 /** @brief Immediately store the model's user data in persistent storage.
  *
- * @param mod Mesh model.
- * @param vnd This is a vendor model.
- * @param name     Name/key of the settings item.
- * @param data Model data to store, or NULL to delete any model data.
+ * @param mod      Mesh model.
+ * @param vnd      This is a vendor model.
+ * @param name     Name/key of the settings item. Only
+ *                 @ref SETTINGS_MAX_DIR_DEPTH bytes will be used at most.
+ * @param data     Model data to store, or NULL to delete any model data.
  * @param data_len Length of the model data.
  *
  * @return 0 on success, or (negative) error code on failure.
  */
 int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
-				 const char *name, const void *data, size_t data_len);
+			     const char *name, const void *data,
+			     size_t data_len);
 
 /** @brief Let a model extend another.
  *
  * Mesh models may be extended to reuse their functionality, forming a more
  * complex model. A Mesh model may extend any number of models, in any element.
  * The extensions may also be nested, ie a model that extends another may itself
- * be extended. Extensions may not be cyclical, and a model can only be extended
- * by one other model.
+ * be extended.
  *
- * A set of models that extend each other form a model extension tree.
+ * A set of models that extend each other form a model extension list.
  *
- * All models in an extension tree share one subscription list per element. The
+ * All models in an extension list share one subscription list per element. The
  * access layer will utilize the combined subscription list of all models in an
- * extension tree and element, giving the models extended subscription list
+ * extension list and element, giving the models extended subscription list
  * capacity.
  *
- * @param[in] mod Mesh model.
- * @param[in] base_mod The model being extended.
+ *  @param extending_mod      Mesh model that is extending the base model.
+ *  @param base_mod           The model being extended.
  *
  * @retval 0 Successfully extended the base_mod model.
- * @retval -EALREADY The base_mod model is already extended.
  */
-int bt_mesh_model_extend(struct bt_mesh_model *mod,
+int bt_mesh_model_extend(struct bt_mesh_model *extending_mod,
 			 struct bt_mesh_model *base_mod);
 
+/** @brief Check if model is extended by another model.
+ *
+ *  @param model The model to check.
+ *
+ *  @retval true If model is extended by another model, otherwise false
+ */
+bool bt_mesh_model_is_extended(struct bt_mesh_model *model);
+
 /** Node Composition */
 struct bt_mesh_comp {
 	uint16_t cid;
diff --git a/nimble/host/mesh/include/mesh/cdb.h b/nimble/host/mesh/include/mesh/cdb.h
index 8f9a6bc9b9..fa691eb642 100644
--- a/nimble/host/mesh/include/mesh/cdb.h
+++ b/nimble/host/mesh/include/mesh/cdb.h
@@ -22,7 +22,6 @@
 
 enum {
 	BT_MESH_CDB_NODE_CONFIGURED,
-	BT_MESH_CDB_NODE_BLACKLISTED,
 
 	BT_MESH_CDB_NODE_FLAG_COUNT
 };
@@ -40,7 +39,6 @@ struct bt_mesh_cdb_node {
 struct bt_mesh_cdb_subnet {
 	uint16_t net_idx;
 
-	bool kr_flag;
 	uint8_t kr_phase;
 
 	struct {
@@ -264,4 +262,4 @@ struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(uint16_t app_idx);
  */
 void bt_mesh_cdb_app_key_store(const struct bt_mesh_cdb_app_key *key);
 
-#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_CDB_H_ */
\ No newline at end of file
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_CDB_H_ */
diff --git a/nimble/host/mesh/include/mesh/cfg.h b/nimble/host/mesh/include/mesh/cfg.h
index 378f0a0aa7..422ef3e661 100644
--- a/nimble/host/mesh/include/mesh/cfg.h
+++ b/nimble/host/mesh/include/mesh/cfg.h
@@ -35,6 +35,12 @@ enum bt_mesh_feat_state {
 	BT_MESH_FEATURE_NOT_SUPPORTED,
 };
 
+/* Key Refresh Phase */
+#define BT_MESH_KR_NORMAL                   0x00
+#define BT_MESH_KR_PHASE_1                  0x01
+#define BT_MESH_KR_PHASE_2                  0x02
+#define BT_MESH_KR_PHASE_3                  0x03
+
 /* Legacy feature defines */
 #define BT_MESH_RELAY_DISABLED              BT_MESH_FEATURE_DISABLED
 #define BT_MESH_RELAY_ENABLED               BT_MESH_FEATURE_ENABLED
@@ -482,4 +488,4 @@ ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
  * @}
  */
 
-#endif /* _BT_MESH_CFG_H_ */
\ No newline at end of file
+#endif /* _BT_MESH_CFG_H_ */
diff --git a/nimble/host/mesh/include/mesh/cfg_cli.h b/nimble/host/mesh/include/mesh/cfg_cli.h
index bd2f9fe5bb..86716f3162 100644
--- a/nimble/host/mesh/include/mesh/cfg_cli.h
+++ b/nimble/host/mesh/include/mesh/cfg_cli.h
@@ -24,10 +24,7 @@ extern "C" {
 /** Mesh Configuration Client Model Context */
 struct bt_mesh_cfg_cli {
 	struct bt_mesh_model *model;
-
-	struct k_sem          op_sync;
-	uint32_t                 op_pending;
-	void                 *op_param;
+	struct bt_mesh_msg_ack_ctx ack_ctx;
 };
 
 extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[];
@@ -40,10 +37,16 @@ extern const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb;
 int bt_mesh_cfg_node_reset(uint16_t net_idx, uint16_t addr, bool *status);
 
 int bt_mesh_cfg_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page,
-			      uint8_t *status, struct os_mbuf *comp);
+			      uint8_t *rsp, struct os_mbuf *comp);
 
 int bt_mesh_cfg_beacon_get(uint16_t net_idx, uint16_t addr, uint8_t *status);
 
+int bt_mesh_cfg_krp_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx,
+			uint8_t *status, uint8_t *phase);
+
+int bt_mesh_cfg_krp_set(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx,
+			uint8_t transition, uint8_t *status, uint8_t *phase);
+
 int bt_mesh_cfg_beacon_set(uint16_t net_idx, uint16_t addr, uint8_t val, uint8_t *status);
 
 int bt_mesh_cfg_ttl_get(uint16_t net_idx, uint16_t addr, uint8_t *ttl);
@@ -156,7 +159,8 @@ int bt_mesh_cfg_mod_app_get_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 #define BT_MESH_PUB_PERIOD_10MIN(steps) (((steps) & BIT_MASK(6)) | (3 << 6))
 
 struct bt_mesh_cfg_mod_pub {
-	uint16_t  addr;
+	uint16_t addr;
+	const uint8_t *uuid;
 	uint16_t  app_idx;
 	bool   cred_flag;
 	uint8_t   ttl;
@@ -265,9 +269,72 @@ int bt_mesh_cfg_hb_pub_set(uint16_t net_idx, uint16_t addr,
 int bt_mesh_cfg_hb_pub_get(uint16_t net_idx, uint16_t addr,
 			   struct bt_mesh_cfg_hb_pub *pub, uint8_t *status);
 
+int bt_mesh_cfg_mod_sub_del_all(uint16_t net_idx, uint16_t addr,
+				uint16_t elem_addr, uint16_t mod_id,
+				uint8_t *status);
+
+int bt_mesh_cfg_mod_sub_del_all_vnd(uint16_t net_idx, uint16_t addr,
+				    uint16_t elem_addr, uint16_t mod_id,
+				    uint16_t cid, uint8_t *status);
+
+int bt_mesh_cfg_net_key_update(uint16_t net_idx, uint16_t addr,
+			       uint16_t key_net_idx, const uint8_t net_key[16],
+			       uint8_t *status);
+
+int bt_mesh_cfg_app_key_update(uint16_t net_idx, uint16_t addr,
+			       uint16_t key_net_idx, uint16_t key_app_idx,
+			       const uint8_t app_key[16], uint8_t *status);
+
+int bt_mesh_cfg_node_identity_set(uint16_t net_idx, uint16_t addr,
+				  uint16_t key_net_idx, uint8_t new_identity,
+				  uint8_t *status, uint8_t *identity);
+
+int bt_mesh_cfg_node_identity_get(uint16_t net_idx, uint16_t addr,
+				  uint16_t key_net_idx, uint8_t *status,
+				  uint8_t *identity);
+
+int bt_mesh_cfg_lpn_timeout_get(uint16_t net_idx, uint16_t addr,
+				uint16_t unicast_addr, int32_t *polltimeout);
+
 int32_t bt_mesh_cfg_cli_timeout_get(void);
 void bt_mesh_cfg_cli_timeout_set(int32_t timeout);
 
+struct bt_mesh_comp_p0 {
+	/** Company ID */
+	uint16_t cid;
+	/** Product ID */
+	uint16_t pid;
+	/** Version ID */
+	uint16_t vid;
+	/** Replay protection list size */
+	uint16_t crpl;
+	/** Supported features, see @ref BT_MESH_FEAT_SUPPORTED. */
+	uint16_t feat;
+
+	struct os_mbuf *_buf;
+};
+
+struct bt_mesh_comp_p0_elem {
+	/** Element location */
+	uint16_t loc;
+	/** The number of SIG models in this element */
+	size_t nsig;
+	/** The number of vendor models in this element */
+	size_t nvnd;
+
+	uint8_t *_buf;
+};
+
+int bt_mesh_comp_p0_get(struct bt_mesh_comp_p0 *comp,
+	struct os_mbuf *buf);
+
+struct bt_mesh_comp_p0_elem *bt_mesh_comp_p0_elem_pull(const struct bt_mesh_comp_p0 *comp,
+	struct bt_mesh_comp_p0_elem *elem);
+
+uint16_t bt_mesh_comp_p0_elem_mod(struct bt_mesh_comp_p0_elem *elem, int idx);
+
+struct bt_mesh_mod_id_vnd bt_mesh_comp_p0_elem_mod_vnd(struct bt_mesh_comp_p0_elem *elem, int idx);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h
index 8c9166c259..58e3e2d8de 100644
--- a/nimble/host/mesh/include/mesh/glue.h
+++ b/nimble/host/mesh/include/mesh/glue.h
@@ -51,6 +51,19 @@
 extern "C" {
 #endif
 
+#define SETTINGS_MAX_DIR_DEPTH	8	/* max depth of settings tree */
+
+/** Key size used in Bluetooth's ECC domain. */
+#define BT_ECC_KEY_SIZE            32
+	/** Length of a Bluetooth ECC public key coordinate. */
+#define BT_PUB_KEY_COORD_LEN       (BT_ECC_KEY_SIZE)
+	/** Length of a Bluetooth ECC public key. */
+#define BT_PUB_KEY_LEN             (2 * (BT_PUB_KEY_COORD_LEN))
+	/** Length of a Bluetooth ECC private key. */
+#define BT_PRIV_KEY_LEN            (BT_ECC_KEY_SIZE)
+	/** Length of a Bluetooth Diffie-Hellman key. */
+#define BT_DH_KEY_LEN              (BT_ECC_KEY_SIZE)
+
 /** @brief Helper to declare elements of bt_data arrays
  *
  *  This macro is mainly for creating an array of struct bt_data
@@ -144,6 +157,10 @@ extern "C" {
 #define ASSERT_NOT_CHAIN(om) (void)(om)
 #endif
 
+#define CHECKIF(expr) \
+	__ASSERT_NO_MSG(!(expr));   \
+	if (0)
+
 #define __packed    __attribute__((__packed__))
 
 #define MSEC_PER_SEC   (1000)
@@ -241,11 +258,18 @@ static inline void net_buf_simple_reset(struct os_mbuf *om)
     net_buf_simple_init(om, 0);
 }
 
+struct bt_le_ext_adv_start_param {
+	uint16_t timeout;
+
+	uint8_t  num_events;
+};
+
 void net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *buf);
 void * net_buf_ref(struct os_mbuf *om);
 void net_buf_unref(struct os_mbuf *om);
 uint16_t net_buf_simple_pull_le16(struct os_mbuf *om);
 uint16_t net_buf_simple_pull_be16(struct os_mbuf *om);
+uint32_t net_buf_simple_pull_le24(struct os_mbuf *om);
 uint32_t net_buf_simple_pull_be32(struct os_mbuf *om);
 uint32_t net_buf_simple_pull_le32(struct os_mbuf *om);
 uint8_t net_buf_simple_pull_u8(struct os_mbuf *om);
@@ -305,15 +329,84 @@ struct bt_pub_key_cb {
      *
      *  @param key The local public key, or NULL in case of no key.
      */
-    void (*func)(const uint8_t key[64]);
+    void (*func)(const uint8_t key[BT_PUB_KEY_LEN]);
 
     struct bt_pub_key_cb *_next;
 };
 
-typedef void (*bt_dh_key_cb_t)(const uint8_t key[32]);
-int bt_dh_key_gen(const uint8_t remote_pk[64], bt_dh_key_cb_t cb);
+/** LE Advertising Parameters. */
+struct bt_le_adv_param {
+	/**
+	 * @brief Local identity.
+	 *
+	 * @note When extended advertising @kconfig{CONFIG_BT_EXT_ADV} is not
+	 *       enabled or not supported by the controller it is not possible
+	 *       to scan and advertise simultaneously using two different
+	 *       random addresses.
+	 */
+	uint8_t  id;
+
+	/**
+	 * @brief Advertising Set Identifier, valid range 0x00 - 0x0f.
+	 *
+	 * @note Requires @ref BT_LE_ADV_OPT_EXT_ADV
+	 **/
+	uint8_t  sid;
+
+	/**
+	 * @brief Secondary channel maximum skip count.
+	 *
+	 * Maximum advertising events the advertiser can skip before it must
+	 * send advertising data on the secondary advertising channel.
+	 *
+	 * @note Requires @ref BT_LE_ADV_OPT_EXT_ADV
+	 */
+	uint8_t  secondary_max_skip;
+
+	/** Bit-field of advertising options */
+	uint32_t options;
+
+	/** Minimum Advertising Interval (N * 0.625 milliseconds)
+	 * Minimum Advertising Interval shall be less than or equal to the
+	 * Maximum Advertising Interval. The Minimum Advertising Interval and
+	 * Maximum Advertising Interval should not be the same value (as stated
+	 * in Bluetooth Core Spec 5.2, section 7.8.5)
+	 * Range: 0x0020 to 0x4000
+	 */
+	uint32_t interval_min;
+
+	/** Maximum Advertising Interval (N * 0.625 milliseconds)
+	 * Minimum Advertising Interval shall be less than or equal to the
+	 * Maximum Advertising Interval. The Minimum Advertising Interval and
+	 * Maximum Advertising Interval should not be the same value (as stated
+	 * in Bluetooth Core Spec 5.2, section 7.8.5)
+	 * Range: 0x0020 to 0x4000
+	 */
+	uint32_t interval_max;
+
+	/**
+	 * @brief Directed advertising to peer
+	 *
+	 * When this parameter is set the advertiser will send directed
+	 * advertising to the remote device.
+	 *
+	 * The advertising type will either be high duty cycle, or low duty
+	 * cycle if the BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY option is enabled.
+	 * When using @ref BT_LE_ADV_OPT_EXT_ADV then only low duty cycle is
+	 * allowed.
+	 *
+	 * In case of connectable high duty cycle if the connection could not
+	 * be established within the timeout the connected() callback will be
+	 * called with the status set to @ref BT_HCI_ERR_ADV_TIMEOUT.
+	 */
+	const bt_addr_le_t *peer;
+};
+
+typedef void (*bt_dh_key_cb_t)(const uint8_t key[BT_DH_KEY_LEN]);
+int bt_dh_key_gen(const uint8_t remote_pk[BT_PUB_KEY_LEN], bt_dh_key_cb_t cb);
 int bt_pub_key_gen(struct bt_pub_key_cb *new_cb);
 uint8_t *bt_pub_key_get(void);
+void bt_conn_get_info(struct ble_hs_conn *conn, struct ble_gap_conn_desc *desc);
 int bt_rand(void *buf, size_t len);
 const char * bt_hex(const void *buf, size_t len);
 int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data);
@@ -327,24 +420,28 @@ void bt_mesh_register_gatt(void);
 int bt_le_adv_start(const struct ble_gap_adv_params *param,
                     const struct bt_data *ad, size_t ad_len,
                     const struct bt_data *sd, size_t sd_len);
-int bt_le_adv_stop(bool proxy);
 
-struct k_delayed_work {
+int bt_le_adv_stop();
+
+struct k_work_delayable {
     struct ble_npl_callout work;
 };
 
 void k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler);
-void k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f);
-void k_delayed_work_cancel(struct k_delayed_work *w);
-bool k_delayed_work_pending(struct k_delayed_work *w);
-void k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms);
+void k_work_init_delayable(struct k_work_delayable *w, ble_npl_event_fn *f);
+void k_work_cancel_delayable(struct k_work_delayable *w);
+bool k_work_delayable_is_pending(struct k_work_delayable *w);
+void k_work_reschedule(struct k_work_delayable *w, uint32_t ms);
 int64_t k_uptime_get(void);
 uint32_t k_uptime_get_32(void);
+int64_t k_uptime_delta(int64_t *reftime);
 void k_sleep(int32_t duration);
 void k_work_submit(struct ble_npl_callout *w);
 void k_work_add_arg(struct ble_npl_callout *w, void *arg);
-void k_delayed_work_add_arg(struct k_delayed_work *w, void *arg);
-uint32_t k_delayed_work_remaining_get(struct k_delayed_work *w);
+void k_work_add_arg_delayable(struct k_work_delayable *w, void *arg);
+ble_npl_time_t k_work_delayable_remaining_get(struct k_work_delayable *w);
+void k_work_schedule(struct k_work_delayable *w, uint32_t ms);
+uint32_t k_ticks_to_ms_floor32(ble_npl_time_t ticks);
 
 static inline void net_buf_simple_save(struct os_mbuf *buf,
                        struct net_buf_simple_state *state)
@@ -353,6 +450,8 @@ static inline void net_buf_simple_save(struct os_mbuf *buf,
     state->len = buf->om_len;
 }
 
+void net_buf_simple_clone(const struct os_mbuf *original, struct os_mbuf *clone);
+
 static inline void net_buf_simple_restore(struct os_mbuf *buf,
                                           struct net_buf_simple_state *state)
 {
@@ -405,13 +504,15 @@ static inline unsigned int find_msb_set(uint32_t op)
 #define CONFIG_BT_MESH_PROVISIONER            BLE_MESH_PROVISIONER
 #define CONFIG_BT_MESH_PROV_DEVICE            BLE_MESH_PROV_DEVICE
 #define CONFIG_BT_MESH_CDB                    BLE_MESH_CDB
+#define CONFIG_BT_MESH_DEBUG_CFG              BLE_MESH_DEBUG_CFG
+#define CONFIG_BT_MESH_DEBUG_ADV              BLE_MESH_DEBUG_ADV
 
 /* Above flags are used with IS_ENABLED macro */
 #define IS_ENABLED(config) MYNEWT_VAL(config)
 
 #define CONFIG_BT_MESH_LPN_GROUPS                MYNEWT_VAL(BLE_MESH_LPN_GROUPS)
 #define CONFIG_BT_MESH_ADV_BUF_COUNT             MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT)
-#define CONFIG_BT_MESH_SEG_BUFS                  MYNEWT_VAL(BLE_MESH_SEG_BUFS )
+#define CONFIG_BT_MESH_SEG_BUFS                  MYNEWT_VAL(BLE_MESH_SEG_BUFS)
 #define CONFIG_BT_MESH_FRIEND_QUEUE_SIZE         MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE)
 #define CONFIG_BT_MESH_FRIEND_RECV_WIN           MYNEWT_VAL(BLE_MESH_FRIEND_RECV_WIN)
 #define CONFIG_BT_MESH_LPN_POLL_TIMEOUT          MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT)
@@ -424,6 +525,7 @@ static inline unsigned int find_msb_set(uint32_t op)
 #define CONFIG_BT_MESH_APP_KEY_COUNT             MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT)
 #define CONFIG_BT_MESH_SUBNET_COUNT              MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)
 #define CONFIG_BT_MESH_STORE_TIMEOUT             MYNEWT_VAL(BLE_MESH_STORE_TIMEOUT)
+#define CONFIG_BT_MESH_IV_UPDATE_SEQ_LIMIT       MYNEWT_VAL(BLE_MESH_IV_UPDATE_SEQ_LIMIT)
 #define CONFIG_BT_MESH_IVU_DIVIDER               MYNEWT_VAL(BLE_MESH_IVU_DIVIDER)
 #define CONFIG_BT_DEVICE_NAME                    MYNEWT_VAL(BLE_MESH_DEVICE_NAME)
 #define CONFIG_BT_RX_SEG_MAX                     MYNEWT_VAL(BLE_MESH_RX_SEG_MAX)
@@ -431,6 +533,7 @@ static inline unsigned int find_msb_set(uint32_t op)
 #define CONFIG_BT_MESH_RX_SEG_MAX                MYNEWT_VAL(BLE_MESH_RX_SEG_MAX)
 #define CONFIG_BT_MESH_RX_SEG_MSG_COUNT          MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT)
 #define CONFIG_BT_MESH_LABEL_COUNT               MYNEWT_VAL(BLE_MESH_LABEL_COUNT)
+#define CONFIG_BT_MESH_MODEL_VND_MSG_CID_FORCE   MYNEWT_VAL(BLE_MESH_MODEL_VND_MSG_CID_FORCE)
 #define CONFIG_BT_MESH_NODE_COUNT                MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT)
 #define CONFIG_BT_GATT_PROXY_ENABLED             MYNEWT_VAL(BLE_MESH_GATT_PROXY_ENABLED)
 #define CONFIG_BT_MESH_DEFAULT_TTL               MYNEWT_VAL(BLE_MESH_DEFAULT_TTL)
@@ -443,6 +546,7 @@ static inline unsigned int find_msb_set(uint32_t op)
 #define CONFIG_BT_MESH_RELAY                     MYNEWT_VAL(BLE_MESH_RELAY)
 #define CONFIG_BT_MESH_RELAY_RETRANSMIT_COUNT    MYNEWT_VAL(BLE_MESH_RELAY_RETRANSMIT_COUNT)
 #define CONFIG_BT_MESH_GATT_PROXY_ENABLED        MYNEWT_VAL(BLE_MESH_GATT_PROXY_ENABLED)
+#define CONFIG_BT_MESH_PROXY_MSG_LEN             MYNEWT_VAL(BLE_MESH_PROXY_MSG_LEN)
 
 #define printk console_printf
 
@@ -471,6 +575,10 @@ static inline void k_sem_give(struct k_sem *sem)
 	ble_npl_sem_release(sem);
 }
 
+static inline void k_sem_reset(struct k_sem *sem)
+{
+	ble_npl_sem_init(sem, 0);
+}
 /* Helpers to access the storage array, since we don't have access to its
  * type at this point anymore.
  */
@@ -525,7 +633,7 @@ settings_load(void)
 
 #endif /* MYNEWT_VAL(MYNEWT_VAL_BLE_MESH_SETTINGS) */
 
-#define BUILD_ASSERT(cond) _Static_assert(cond, "")
+#define BUILD_ASSERT(cond, msg) _Static_assert(cond, msg)
 
 
 /* Memory slabs/blocks */
diff --git a/nimble/host/mesh/include/mesh/health_cli.h b/nimble/host/mesh/include/mesh/health_cli.h
index e9efe4b1f9..cb14ee6bcd 100644
--- a/nimble/host/mesh/include/mesh/health_cli.h
+++ b/nimble/host/mesh/include/mesh/health_cli.h
@@ -29,9 +29,7 @@ struct bt_mesh_health_cli {
 			       uint8_t test_id, uint16_t cid, uint8_t *faults,
 			       size_t fault_count);
 
-	struct k_sem          op_sync;
-	uint32_t                 op_pending;
-	void                 *op_param;
+	struct bt_mesh_msg_ack_ctx ack_ctx;
 };
 
 extern const struct bt_mesh_model_op bt_mesh_health_cli_op[];
diff --git a/nimble/host/mesh/include/mesh/health_srv.h b/nimble/host/mesh/include/mesh/health_srv.h
index ad79e368da..980e757486 100644
--- a/nimble/host/mesh/include/mesh/health_srv.h
+++ b/nimble/host/mesh/include/mesh/health_srv.h
@@ -65,7 +65,7 @@ struct bt_mesh_health_srv {
 	const struct bt_mesh_health_srv_cb *cb;
 
 	/* Attention Timer state */
-	struct k_delayed_work attn_timer;
+	struct k_work_delayable attn_timer;
 };
 
 int bt_mesh_fault_update(struct bt_mesh_elem *elem);
diff --git a/nimble/host/mesh/include/mesh/heartbeat.h b/nimble/host/mesh/include/mesh/heartbeat.h
index b9990f6fdc..2e4986094d 100644
--- a/nimble/host/mesh/include/mesh/heartbeat.h
+++ b/nimble/host/mesh/include/mesh/heartbeat.h
@@ -120,4 +120,4 @@ extern struct bt_mesh_hb_cb hb_cb;
  * @}
  */
 
-#endif /* _BLUETOOTH_MESH_HEARTBEAT_H_ */
\ No newline at end of file
+#endif /* _BLUETOOTH_MESH_HEARTBEAT_H_ */
diff --git a/nimble/host/mesh/include/mesh/main.h b/nimble/host/mesh/include/mesh/main.h
index 2bcb05c830..6e9d2e629d 100644
--- a/nimble/host/mesh/include/mesh/main.h
+++ b/nimble/host/mesh/include/mesh/main.h
@@ -101,6 +101,23 @@ struct bt_mesh_prov {
 	/** Out of Band information field. */
 	bt_mesh_prov_oob_info_t oob_info;
 
+	/** Pointer to Public Key in big-endian for OOB public key type support.
+	 *
+	 * Remember to enable @option{CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY}
+	 * when initializing this parameter.
+	 *
+	 * Must be used together with @ref bt_mesh_prov::private_key_be.
+	 */
+	const uint8_t *public_key_be;
+	/** Pointer to Private Key in big-endian for OOB public key type support.
+	 *
+	 * Remember to enable @option{CONFIG_BT_MESH_PROV_OOB_PUBLIC_KEY}
+	 * when initializing this parameter.
+	 *
+	 * Must be used together with @ref bt_mesh_prov::public_key_be.
+	 */
+	const uint8_t *private_key_be;
+
 	/** Static OOB value */
 	const uint8_t *static_val;
 	/** Static OOB value length */
@@ -270,11 +287,11 @@ int bt_mesh_input_number(uint32_t num);
 
 /** @brief Provide Device public key.
  *
- *  @param public_key Device public key.
+ *  @param public_key Device public key in big-endian.
  *
  *  @return Zero on success or (negative) error code otherwise.
  */
-int bt_mesh_prov_remote_pub_key_set(const uint8_t public_key[64]);
+int bt_mesh_prov_remote_pub_key_set(const uint8_t public_key[BT_PUB_KEY_LEN]);
 
 /** @brief Use Input OOB authentication.
  *
@@ -548,6 +565,19 @@ void bt_mesh_lpn_set_cb(void (*cb)(uint16_t friend_addr, bool established));
  */
 int bt_mesh_friend_terminate(uint16_t lpn_addr);
 
+/** @brief Store pending RPL entry(ies) in the persistent storage.
+ *
+ * This API allows the user to store pending RPL entry(ies) in the persistent
+ * storage without waiting for the timeout.
+ *
+ * @note When flash is used as the persistent storage, calling this API too
+ *       frequently may wear it out.
+ *
+ * @param addr Address of the node which RPL entry needs to be stored or
+ * @ref BT_MESH_ADDR_ALL_NODES to store all pending RPL entries.
+ */
+void bt_mesh_rpl_pending_store(uint16_t addr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/nimble/host/mesh/include/mesh/mesh.h b/nimble/host/mesh/include/mesh/mesh.h
index b9bc24fb18..c644d0bb0b 100644
--- a/nimble/host/mesh/include/mesh/mesh.h
+++ b/nimble/host/mesh/include/mesh/mesh.h
@@ -15,6 +15,7 @@
 #include "os/os_mbuf.h"
 
 #include "glue.h"
+#include "msg.h"
 #include "access.h"
 #include "main.h"
 #include "cfg.h"
@@ -27,5 +28,6 @@
 #include "cfg.h"
 #include "heartbeat.h"
 #include "../src/app_keys.h"
+#include "../src/net.h"
 
 #endif /* __BT_MESH_H */
diff --git a/nimble/host/mesh/include/mesh/msg.h b/nimble/host/mesh/include/mesh/msg.h
new file mode 100644
index 0000000000..e327545b12
--- /dev/null
+++ b/nimble/host/mesh/include/mesh/msg.h
@@ -0,0 +1,225 @@
+/** @file
+ *  @brief Bluetooth Mesh Message APIs.
+ */
+
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_MSG_H_
+#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_MSG_H_
+
+/**
+ * @brief Bluetooth Mesh Message API
+ * @defgroup bt_mesh_msg Bluetooth Mesh Message API
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Length of a short Mesh MIC. */
+#define BT_MESH_MIC_SHORT 4
+/** Length of a long Mesh MIC. */
+#define BT_MESH_MIC_LONG 8
+
+/** @def BT_MESH_MODEL_OP_LEN
+ *
+ *  @brief Helper to determine the length of an opcode.
+ *
+ *  @param _op Opcode.
+ */
+#define BT_MESH_MODEL_OP_LEN(_op) ((_op) <= 0xff ? 1 : (_op) <= 0xffff ? 2 : 3)
+
+
+/** @def BT_MESH_MODEL_BUF_LEN
+ *
+ *  @brief Helper for model message buffer length.
+ *
+ *  Returns the length of a Mesh model message buffer, including the opcode
+ *  length and a short MIC.
+ *
+ *  @param _op          Opcode of the message.
+ *  @param _payload_len Length of the model payload.
+ */
+#define BT_MESH_MODEL_BUF_LEN(_op, _payload_len)                               \
+	(BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_SHORT)
+
+
+/** @def BT_MESH_MODEL_BUF_LEN_LONG_MIC
+ *
+ *  @brief Helper for model message buffer length.
+ *
+ *  Returns the length of a Mesh model message buffer, including the opcode
+ *  length and a long MIC.
+ *
+ *  @param _op          Opcode of the message.
+ *  @param _payload_len Length of the model payload.
+ */
+#define BT_MESH_MODEL_BUF_LEN_LONG_MIC(_op, _payload_len)                      \
+	(BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_LONG)
+
+
+/** @def BT_MESH_MODEL_BUF_DEFINE
+ *
+ *  @brief Define a Mesh model message buffer using @ref NET_BUF_SIMPLE_DEFINE.
+ *
+ *  @param _buf         Buffer name.
+ *  @param _op          Opcode of the message.
+ *  @param _payload_len Length of the model message payload.
+ */
+#define BT_MESH_MODEL_BUF(_op, _payload_len)                      \
+	NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN(_op, (_payload_len)))
+
+
+/** Message sending context. */
+struct bt_mesh_msg_ctx {
+	/** NetKey Index of the subnet to send the message on. */
+	uint16_t net_idx;
+
+	/** AppKey Index to encrypt the message with. */
+	uint16_t app_idx;
+
+	/** Remote address. */
+	uint16_t addr;
+
+	/** Destination address of a received message. Not used for sending. */
+	uint16_t recv_dst;
+
+	/** RSSI of received packet. Not used for sending. */
+	int8_t  recv_rssi;
+
+	/** Received TTL value. Not used for sending. */
+	uint8_t  recv_ttl;
+
+	/** Force sending reliably by using segment acknowledgment */
+	bool  send_rel;
+
+	/** TTL, or BT_MESH_TTL_DEFAULT for default TTL. */
+	uint8_t  send_ttl;
+};
+
+/** @brief Initialize a model message.
+ *
+ *  Clears the message buffer contents, and encodes the given opcode.
+ *  The message buffer will be ready for filling in payload data.
+ *
+ *  @param msg    Message buffer.
+ *  @param opcode Opcode to encode.
+ */
+void bt_mesh_model_msg_init(struct os_mbuf *msg, uint32_t opcode);
+
+/**
+ * Acknowledged message context for tracking the status of model messages pending a response.
+ */
+struct bt_mesh_msg_ack_ctx {
+	struct k_sem          sem;       /**< Sync semaphore. */
+	uint32_t              op;        /**< Opcode we're waiting for. */
+	uint16_t              dst;       /**< Address of the node that should respond. */
+	void                 *user_data; /**< User specific parameter. */
+};
+
+/** @brief Initialize an acknowledged message context.
+ *
+ *  Initializes semaphore used for synchronization between @ref bt_mesh_msg_ack_ctx_wait and
+ *  @ref bt_mesh_msg_ack_ctx_rx calls. Call this function before using @ref bt_mesh_msg_ack_ctx.
+ *
+ *  @param ack Acknowledged message context to initialize.
+ */
+static inline void bt_mesh_msg_ack_ctx_init(struct bt_mesh_msg_ack_ctx *ack)
+{
+	k_sem_init(&ack->sem, 0, 1);
+}
+
+/** @brief Reset the synchronization semaphore in an acknowledged message context.
+ *
+ *  This function aborts call to @ref bt_mesh_msg_ack_ctx_wait.
+ *
+ *  @param ack Acknowledged message context to be reset.
+ */
+static inline void bt_mesh_msg_ack_ctx_reset(struct bt_mesh_msg_ack_ctx *ack)
+{
+	k_sem_reset(&ack->sem);
+}
+
+/** @brief Clear parameters of an acknowledged message context.
+ *
+ *  This function clears the opcode, remote address and user data set
+ *  by @ref bt_mesh_msg_ack_ctx_prepare.
+ *
+ *  @param ack Acknowledged message context to be cleared.
+ */
+void bt_mesh_msg_ack_ctx_clear(struct bt_mesh_msg_ack_ctx *ack);
+
+/** @brief Prepare an acknowledged message context for the incoming message to wait.
+ *
+ *  This function sets the opcode, remote address of the incoming message and stores the user data.
+ *  Use this function before calling @ref bt_mesh_msg_ack_ctx_wait.
+ *
+ *  @param ack       Acknowledged message context to prepare.
+ *  @param op        The message OpCode.
+ *  @param dst       Destination address of the message.
+ *  @param user_data User data for the acknowledged message context.
+ *
+ *  @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_msg_ack_ctx_prepare(struct bt_mesh_msg_ack_ctx *ack,
+				uint32_t op, uint16_t dst, void *user_data);
+/** @brief Check if the acknowledged message context is initialized with an opcode.
+ *
+ *  @param ack Acknowledged message context.
+ *
+ *  @return true if the acknowledged message context is initialized with an opcode,
+ *  false otherwise.
+ */
+static inline bool bt_mesh_msg_ack_ctx_busy(struct bt_mesh_msg_ack_ctx *ack)
+{
+	return (ack->op != 0);
+}
+
+/** @brief Wait for a message acknowledge.
+ *
+ *  This function blocks execution until @ref bt_mesh_msg_ack_ctx_rx is called or by timeout.
+ *
+ *  @param ack     Acknowledged message context of the message to wait for.
+ *  @param timeout Wait timeout.
+ *
+ *  @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_msg_ack_ctx_wait(struct bt_mesh_msg_ack_ctx *ack, int32_t timeout);
+
+/** @brief Mark a message as acknowledged.
+ *
+ *  This function unblocks call to @ref bt_mesh_msg_ack_ctx_wait.
+ *
+ *  @param ack Context of a message to be acknowledged.
+ */
+static inline void bt_mesh_msg_ack_ctx_rx(struct bt_mesh_msg_ack_ctx *ack)
+{
+	k_sem_give(&ack->sem);
+}
+
+/** @brief Check if an opcode and address of a message matches the expected one.
+ *
+ *  @param ack       Acknowledged message context to be checked.
+ *  @param op        OpCode of the incoming message.
+ *  @param addr      Source address of the incoming message.
+ *  @param user_data If not NULL, returns a user data stored in the acknowledged message context
+ *  by @ref bt_mesh_msg_ack_ctx_prepare.
+ *
+ *  @return true if the incoming message matches the expected one, false otherwise.
+ */
+bool bt_mesh_msg_ack_ctx_match(const struct bt_mesh_msg_ack_ctx *ack,
+	uint32_t op, uint16_t addr, void **user_data);
+
+#ifdef __cplusplus
+}
+#endif
+/**
+ * @}
+ */
+#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_MSG_H_ */
diff --git a/nimble/host/mesh/src/access.c b/nimble/host/mesh/src/access.c
index da077182ab..2c07b09478 100644
--- a/nimble/host/mesh/src/access.c
+++ b/nimble/host/mesh/src/access.c
@@ -10,6 +10,7 @@
 #define MESH_LOG_MODULE BLE_MESH_ACCESS_LOG
 
 #include <errno.h>
+#include <stdlib.h>
 #include <os/os_mbuf.h>
 
 #include "mesh/mesh.h"
@@ -21,12 +22,33 @@
 #include "transport.h"
 #include "access.h"
 #include "foundation.h"
+#include "settings.h"
 #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
 #include "mesh/model_cli.h"
 #endif
 
+/* bt_mesh_model.flags */
+enum {
+	BT_MESH_MOD_BIND_PENDING = BIT(0),
+	BT_MESH_MOD_SUB_PENDING = BIT(1),
+	BT_MESH_MOD_PUB_PENDING = BIT(2),
+	BT_MESH_MOD_EXTENDED = BIT(3),
+};
+
+/* Model publication information for persistent storage. */
+struct mod_pub_val {
+	uint16_t addr;
+	uint16_t key;
+	uint8_t  ttl;
+	uint8_t  retransmit;
+	uint8_t  period;
+	uint8_t  period_div:4,
+		 cred:1;
+};
+
 static const struct bt_mesh_comp *dev_comp;
 static uint16_t dev_primary_addr;
+static void (*msg_cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf);
 
 void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod,
 					struct bt_mesh_elem *elem,
@@ -127,7 +149,7 @@ static void publish_sent(int err, void *user_data)
 
 	if (delay) {
 		BT_DBG("Publishing next time in %dms", (int) delay);
-		k_delayed_work_submit(&mod->pub->timer, delay);
+		k_work_schedule(&mod->pub->timer, delay);
 	}
 }
 
@@ -138,6 +160,7 @@ static void publish_start(uint16_t duration, int err, void *user_data)
 
 	if (err) {
 		BT_ERR("Failed to publish: err %d", err);
+		publish_sent(err, user_data);
 		return;
 	}
 
@@ -152,7 +175,7 @@ static const struct bt_mesh_send_cb pub_sent_cb = {
 	.end = publish_sent,
 };
 
-static int publish_retransmit(struct bt_mesh_model *mod)
+static int publish_transmit(struct bt_mesh_model *mod)
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_model_pub *pub = mod->pub;
@@ -171,67 +194,68 @@ static int publish_retransmit(struct bt_mesh_model *mod)
 	net_buf_simple_init(sdu, 0);
 	net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
 
-	pub->count--;
-
 	err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod);
 
 	os_mbuf_free_chain(sdu);
 	return err;
 }
 
-static void publish_retransmit_end(int err, struct bt_mesh_model_pub *pub)
+static int pub_period_start(struct bt_mesh_model_pub *pub)
 {
-	/* Cancel all retransmits for this publish attempt */
-	pub->count = 0U;
-	/* Make sure the publish timer gets reset */
-	publish_sent(err, pub->mod);
+	int err;
+
+	pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit);
+
+	if (!pub->update) {
+		return 0;
+	}
+
+	err = pub->update(pub->mod);
+	if (err) {
+		/* Skip this publish attempt. */
+		BT_DBG("Update failed, skipping publish (err: %d)", err);
+		pub->count = 0;
+		pub->period_start = k_uptime_get_32();
+		publish_sent(err, pub->mod);
+		return err;
+	}
+
+	return 0;
 }
 
 static void mod_publish(struct ble_npl_event *work)
 {
 	struct bt_mesh_model_pub *pub = ble_npl_event_get_arg(work);
-	int32_t period_ms;
 	int err;
 
-	BT_DBG("");
+	if (pub->addr == BT_MESH_ADDR_UNASSIGNED ||
+	atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
+		/* Publication is no longer active, but the cancellation of the
+		 * delayed work failed. Abandon recurring timer.
+		 */
+		return;
+	}
 
-	period_ms = bt_mesh_model_pub_period_get(pub->mod);
-	BT_DBG("period %u ms", (unsigned) period_ms);
+	BT_DBG("");
 
 	if (pub->count) {
-		err = publish_retransmit(pub->mod);
+		pub->count--;
+	} else {
+		/* First publication in this period */
+		err = pub_period_start(pub);
 		if (err) {
-			BT_ERR("Failed to retransmit (err %d)", err);
-
-			pub->count = 0;
-
-			/* Continue with normal publication */
-			if (period_ms) {
-				k_delayed_work_submit(&pub->timer, period_ms);
-			}
+			return;
 		}
-
-		return;
 	}
 
-	if (!period_ms) {
-		return;
-	}
-
-	__ASSERT_NO_MSG(pub->update != NULL);
-
-	err = pub->update(pub->mod);
+	err = publish_transmit(pub->mod);
 	if (err) {
-		/* Cancel this publish attempt. */
-		BT_DBG("Update failed, skipping publish (err: %d)", err);
-		pub->period_start = k_uptime_get_32();
-		publish_retransmit_end(err, pub);
-		return;
-	}
+		BT_ERR("Failed to publish (err %d)", err);
+		if (pub->count == BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit)) {
+			pub->period_start = k_uptime_get_32();
+		}
 
-	err = bt_mesh_model_publish(pub->mod);
-	if (err) {
-		BT_ERR("Publishing failed (err %d)", err);
+		publish_sent(err, pub->mod);
 	}
 }
 
@@ -268,6 +292,30 @@ struct bt_mesh_model *bt_mesh_model_get(bool vnd, uint8_t elem_idx, uint8_t mod_
 	}
 }
 
+#if defined(CONFIG_BT_MESH_MODEL_VND_MSG_CID_FORCE)
+static int bt_mesh_vnd_mod_msg_cid_check(struct bt_mesh_model *mod)
+{
+	uint16_t cid;
+	const struct bt_mesh_model_op *op;
+
+	for (op = mod->op; op->func; op++) {
+		cid = (uint16_t)(op->opcode & 0xffff);
+
+		if (cid == mod->vnd.company) {
+			continue;
+		}
+
+		BT_ERR("Invalid vendor model(company:0x%04x"
+		       " id:0x%04x) message opcode 0x%08x",
+		       mod->vnd.company, mod->vnd.id, op->opcode);
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif
+
 static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 		     bool vnd, bool primary, void *user_data)
 {
@@ -280,8 +328,8 @@ static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 
 	if (mod->pub) {
 		mod->pub->mod = mod;
-		k_delayed_work_init(&mod->pub->timer, mod_publish);
-		k_delayed_work_add_arg(&mod->pub->timer, mod->pub);
+		k_work_init_delayable(&mod->pub->timer, mod_publish);
+		k_work_add_arg_delayable(&mod->pub->timer, mod->pub);
 	}
 
 	for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
@@ -291,6 +339,13 @@ static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 	mod->elem_idx = elem - dev_comp->elem;
 	if (vnd) {
 		mod->mod_idx = mod - elem->vnd_models;
+
+		if (CONFIG_BT_MESH_MODEL_VND_MSG_CID_FORCE) {
+			*err = bt_mesh_vnd_mod_msg_cid_check(mod);
+			if (*err) {
+				return;
+			}
+		}
 	} else {
 		mod->mod_idx = mod - elem->models;
 	}
@@ -305,7 +360,7 @@ int bt_mesh_comp_register(const struct bt_mesh_comp *comp)
 	int err;
 
 	/* There must be at least one element */
-	if (!comp->elem_count) {
+	if (!comp || !comp->elem_count) {
 		return -EINVAL;
 	}
 
@@ -366,8 +421,7 @@ struct find_group_visitor_ctx {
 	uint16_t addr;
 };
 
-static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod,
-						uint32_t depth, void *user_data)
+static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod, void *user_data)
 {
 	struct find_group_visitor_ctx *ctx = user_data;
 
@@ -392,8 +446,7 @@ uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr)
 		.addr = addr,
 	};
 
-	bt_mesh_model_tree_walk(bt_mesh_model_root(*mod),
-				find_group_mod_visitor, &ctx);
+	bt_mesh_model_extensions_walk(*mod, find_group_mod_visitor, &ctx);
 
 	*mod = ctx.mod;
 	return ctx.entry;
@@ -431,24 +484,67 @@ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr)
 {
 	uint16_t index;
 
+	if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
+		return NULL;
+	}
+
+	index = addr - dev_comp->elem[0].addr;
+	if (index >= dev_comp->elem_count) {
+		return NULL;
+	}
+
+	return &dev_comp->elem[index];
+}
+
+bool bt_mesh_has_addr(uint16_t addr)
+{
+	uint16_t index;
+
 	if (BT_MESH_ADDR_IS_UNICAST(addr)) {
-		index = (addr - dev_comp->elem[0].addr);
-		if (index < dev_comp->elem_count) {
-			return &dev_comp->elem[index];
-		} else {
-			return NULL;
-		}
+		return bt_mesh_elem_find(addr) != NULL;
+	}
+
+	if (MYNEWT_VAL(BLE_MESH_ACCESS_LAYER_MSG) && msg_cb) {
+		return true;
 	}
 
 	for (index = 0; index < dev_comp->elem_count; index++) {
 		struct bt_mesh_elem *elem = &dev_comp->elem[index];
 
 		if (bt_mesh_elem_find_group(elem, addr)) {
-			return elem;
+			return true;
 		}
 	}
 
-	return NULL;
+	return false;
+}
+
+#if MYNEWT_VAL(BLE_MESH_ACCESS_LAYER_MSG)
+void bt_mesh_msg_cb_set(void (*cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf *buf))
+{
+	msg_cb = cb;
+}
+#endif
+
+int bt_mesh_msg_send(struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf, uint16_t src_addr,
+		     const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+	struct bt_mesh_net_tx tx = {
+		.ctx = ctx,
+		.src = src_addr,
+	};
+
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx.ctx->net_idx,
+	       tx.ctx->app_idx, tx.ctx->addr);
+	BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+	if (!bt_mesh_is_provisioned()) {
+		BT_ERR("Local node is not yet provisioned");
+		return -EAGAIN;
+	}
+
+	return bt_mesh_trans_send(&tx, buf, cb, cb_data);
 }
 
 uint8_t bt_mesh_elem_count(void)
@@ -456,7 +552,7 @@ uint8_t bt_mesh_elem_count(void)
 	return dev_comp->elem_count;
 }
 
-static bool model_has_key(struct bt_mesh_model *mod, uint16_t key)
+bool bt_mesh_model_has_key(struct bt_mesh_model *mod, uint16_t key)
 {
 	int i;
 
@@ -486,15 +582,37 @@ static bool model_has_dst(struct bt_mesh_model *mod, uint16_t dst)
 	return mod->elem_idx == 0;
 }
 
-static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models,
-					      uint8_t model_count, uint32_t opcode,
-					      struct bt_mesh_model **model)
+static const struct bt_mesh_model_op *find_op(struct bt_mesh_elem *elem,
+					      uint32_t opcode, struct bt_mesh_model **model)
 {
 	uint8_t i;
+	uint8_t count;
+	/* This value shall not be used in shipping end products. */
+	uint32_t cid = UINT32_MAX;
+	struct bt_mesh_model *models;
 
-	for (i = 0; i < model_count; i++) {
+	/* SIG models cannot contain 3-byte (vendor) OpCodes, and
+	 * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so
+	 * we only need to do the lookup in one of the model lists.
+	 */
+	if (BT_MESH_MODEL_OP_LEN(opcode) < 3) {
+		models = elem->models;
+		count = elem->model_count;
+	} else {
+		models = elem->vnd_models;
+		count = elem->vnd_model_count;
+
+		cid = (uint16_t)(opcode & 0xffff);
+	}
+
+	for (i = 0U; i < count; i++) {
 		const struct bt_mesh_model_op *op;
 
+		if (CONFIG_BT_MESH_MODEL_VND_MSG_CID_FORCE &&
+		cid != UINT32_MAX &&
+		cid != models[i].vnd.company) {
+			continue;
+		}
 		*model = &models[i];
 
 		for (op = (*model)->op; op->func; op++) {
@@ -548,10 +666,9 @@ static int get_opcode(struct os_mbuf *buf, uint32_t *opcode)
 
 void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 {
-	struct bt_mesh_model *models, *model;
+	struct bt_mesh_model *model;
 	const struct bt_mesh_model_op *op;
 	uint32_t opcode;
-	uint8_t count;
 	int i;
 
 	BT_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", rx->ctx.app_idx,
@@ -566,28 +683,16 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 	BT_DBG("OpCode 0x%08x", (unsigned) opcode);
 
 	for (i = 0; i < dev_comp->elem_count; i++) {
-		struct bt_mesh_elem *elem = &dev_comp->elem[i];
 		struct net_buf_simple_state state;
 
-		/* SIG models cannot contain 3-byte (vendor) OpCodes, and
-		 * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so
-		 * we only need to do the lookup in one of the model lists.
-		 */
-		if (BT_MESH_MODEL_OP_LEN(opcode) < 3) {
-			models = elem->models;
-			count = elem->model_count;
-		} else {
-			models = elem->vnd_models;
-			count = elem->vnd_model_count;
-		}
+		op = find_op(&dev_comp->elem[i], opcode, &model);
 
-		op = find_op(models, count, opcode, &model);
 		if (!op) {
 			BT_DBG("No OpCode 0x%08x for elem %d", opcode, i);
 			continue;
 		}
 
-		if (!model_has_key(model, rx->ctx.app_idx)) {
+		if (!bt_mesh_model_has_key(model, rx->ctx.app_idx)) {
 				continue;
 			}
 
@@ -595,9 +700,13 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 			continue;
 		}
 
-		if (buf->om_len < op->min_len) {
+		if ((op->len >= 0) && (buf->om_len < (size_t)op->len)) {
 			BT_ERR("Too short message for OpCode 0x%08x", opcode);
 			continue;
+		} else if ((op->len < 0) && (buf->om_len != (size_t)(-op->len))) {
+			BT_ERR("Invalid message size for OpCode 0x%08x",
+			       opcode);
+			continue;
 		}
 
 		/* The callback will likely parse the buffer, so
@@ -605,138 +714,64 @@ void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 		 * receive the message.
 		 */
 		net_buf_simple_save(buf, &state);
-		op->func(model, &rx->ctx, buf);
+		(void)op->func(model, &rx->ctx, buf);
 		net_buf_simple_restore(buf, &state);
 	}
-}
 
-void bt_mesh_model_msg_init(struct os_mbuf *msg, uint32_t opcode)
-{
-	net_buf_simple_init(msg, 0);
-
-	switch (BT_MESH_MODEL_OP_LEN(opcode)) {
-	case 1:
-		net_buf_simple_add_u8(msg, opcode);
-		break;
-	case 2:
-		net_buf_simple_add_be16(msg, opcode);
-		break;
-	case 3:
-		net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff));
-		/* Using LE for the CID since the model layer is defined as
-		 * little-endian in the mesh spec and using BT_MESH_MODEL_OP_3
-		 * will declare the opcode in this way.
-		 */
-		net_buf_simple_add_le16(msg, opcode & 0xffff);
-		break;
-	default:
-		BT_WARN("Unknown opcode format");
-		break;
+	if (MYNEWT_VAL(BLE_MESH_ACCESS_LAYER_MSG) && msg_cb) {
+		msg_cb(opcode, &rx->ctx, buf);
 	}
 }
 
-static int model_send(struct bt_mesh_model *model,
-		      struct bt_mesh_net_tx *tx, bool implicit_bind,
-		      struct os_mbuf *msg,
-		      const struct bt_mesh_send_cb *cb, void *cb_data)
-{
-	BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->ctx->net_idx,
-	       tx->ctx->app_idx, tx->ctx->addr);
-	BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
-
-	if (!bt_mesh_is_provisioned()) {
-		BT_ERR("Local node is not yet provisioned");
-		return -EAGAIN;
-	}
-
-	if (net_buf_simple_tailroom(msg) < 4) {
-		BT_ERR("Not enough tailroom for TransMIC");
-		return -EINVAL;
-	}
-
-	if (msg->om_len > BT_MESH_TX_SDU_MAX - 4) {
-		BT_ERR("Too big message");
-		return -EMSGSIZE;
-	}
-
-	if (!implicit_bind && !model_has_key(model, tx->ctx->app_idx)) {
-		BT_ERR("Model not bound to AppKey 0x%04x", tx->ctx->app_idx);
-		return -EINVAL;
-	}
-
-	return bt_mesh_trans_send(tx, msg, cb, cb_data);
-}
-
-int bt_mesh_model_send(struct bt_mesh_model *model,
-		       struct bt_mesh_msg_ctx *ctx,
+int bt_mesh_model_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
 		       struct os_mbuf *msg,
 		       const struct bt_mesh_send_cb *cb, void *cb_data)
 {
-	struct bt_mesh_net_tx tx = {
-		.ctx = ctx,
-		.src = bt_mesh_model_elem(model)->addr,
-	};
+	if (!bt_mesh_model_has_key(model, ctx->app_idx)) {
+		BT_ERR("Model not bound to AppKey 0x%04x", ctx->app_idx);
+		return -EINVAL;
+	}
 
-	return model_send(model, &tx, false, msg, cb, cb_data);
+	return bt_mesh_msg_send(ctx, msg, bt_mesh_model_elem(model)->addr, cb, cb_data);
 }
 
 int bt_mesh_model_publish(struct bt_mesh_model *model)
 {
-	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_model_pub *pub = model->pub;
-	struct bt_mesh_msg_ctx ctx = {
-		.addr = pub->addr,
-		.send_ttl = pub->ttl,
-		.send_rel = pub->send_rel,
-		.app_idx = pub->key,
-	};
-	struct bt_mesh_net_tx tx = {
-		.ctx = &ctx,
-		.src = bt_mesh_model_elem(model)->addr,
-	};
-	int err;
-
-	BT_DBG("");
 
 	if (!pub) {
-		err = -ENOTSUP;
-		goto done;
+		return -ENOTSUP;
 	}
 
+	BT_DBG("");
+
 	if (pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		err = -EADDRNOTAVAIL;
-		goto done;
+		return -EADDRNOTAVAIL;
 	}
 
-	if (pub->msg->om_len + 4 > BT_MESH_TX_SDU_MAX) {
+	if (!pub->msg || !pub->msg->om_len) {
+		BT_ERR("No publication message");
+		return -EINVAL;
+	}
+
+	if (pub->msg->om_len + BT_MESH_MIC_SHORT > BT_MESH_TX_SDU_MAX) {
 		BT_ERR("Message does not fit maximum SDU size");
-		err = -EMSGSIZE;
-		goto done;
+		return -EMSGSIZE;
 	}
 
 	if (pub->count) {
 		BT_WARN("Clearing publish retransmit timer");
-		k_delayed_work_cancel(&pub->timer);
 	}
 
-	net_buf_simple_init(sdu, 0);
-	net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
-
-	tx.friend_cred = pub->cred;
-
-	pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit);
+	/* Account for initial transmission */
+	pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit) + 1;
 
 	BT_DBG("Publish Retransmit Count %u Interval %ums", pub->count,
 	       BT_MESH_PUB_TRANSMIT_INT(pub->retransmit));
 
-	err = model_send(model, &tx, true, sdu, &pub_sent_cb, model);
-	if (err) {
-		publish_retransmit_end(err, pub);
-	}
+	k_work_reschedule(&pub->timer, K_NO_WAIT);
 
-done:
-	os_mbuf_free_chain(sdu);
-	return err;
+	return 0;
 }
 
 struct bt_mesh_model *bt_mesh_model_find_vnd(const struct bt_mesh_elem *elem,
@@ -773,76 +808,506 @@ const struct bt_mesh_comp *bt_mesh_comp_get(void)
 	return dev_comp;
 }
 
-struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod)
+void bt_mesh_model_extensions_walk(struct bt_mesh_model *model,
+				   enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
+				   			   void *user_data),
+				   void *user_data)
 {
-#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS
-	while (mod->next) {
-		mod = mod->next;
+#ifndef CONFIG_BT_MESH_MODEL_EXTENSIONS
+	(void)cb(model, user_data);
+	return;
+#else
+	struct bt_mesh_model *it;
+
+	if (cb(model, user_data) == BT_MESH_WALK_STOP || !model->next) {
+		return;
+	}
+	/* List is circular. Step through all models until we reach the start: */
+	for (it = model->next; it != model; it = it->next) {
+		if (cb(it, user_data) == BT_MESH_WALK_STOP) {
+			return;
+		}
 	}
 #endif
-	return mod;
 }
 
-void bt_mesh_model_tree_walk(struct bt_mesh_model *root,
-			     enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
-						     uint32_t depth,
-						     void *user_data),
-			     void *user_data)
+#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
+int bt_mesh_model_extend(struct bt_mesh_model *extending_mod, struct bt_mesh_model *base_mod)
 {
-	struct bt_mesh_model *m = root;
-	int depth = 0;
-	/* 'skip' is set to true when we ascend from child to parent node.
-	 * In that case, we want to skip calling the callback on the parent
-	 * node and we don't want to descend onto a child node as those
-	 * nodes have already been visited.
-	 */
-	bool skip = false;
+	struct bt_mesh_model *a = extending_mod;
+	struct bt_mesh_model *b = base_mod;
+	struct bt_mesh_model *a_next = a->next;
+	struct bt_mesh_model *b_next = b->next;
+	struct bt_mesh_model *it;
+
+	base_mod->flags |= BT_MESH_MOD_EXTENDED;
+
+	if (a == b) {
+		return 0;
+	}
 
-	do {
-		if (!skip &&
-		    cb(m, (uint32_t)depth, user_data) == BT_MESH_WALK_STOP) {
+	/* Check if a's list contains b */
+	for (it = a; (it != NULL) && (it->next != a); it = it->next) {
+		if (it == b) {
+			return 0;
+		}
+	}
+
+	/* Merge lists */
+	if (a_next) {
+		b->next = a_next;
+	} else {
+		b->next = a;
+	}
+
+	if (b_next) {
+		a->next = b_next;
+	} else {
+		a->next = b;
+	}
+
+	return 0;
+}
+#endif
+
+bool bt_mesh_model_is_extended(struct bt_mesh_model *model)
+{
+	return model->flags & BT_MESH_MOD_EXTENDED;
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int mod_set_bind(struct bt_mesh_model *mod, char *val)
+{
+	int len, err, i;
+
+	/* Start with empty array regardless of cleared or set value */
+	for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
+		mod->keys[i] = BT_MESH_KEY_UNUSED;
+	}
+
+	if (!val) {
+		BT_DBG("Cleared bindings for model");
+		return 0;
+	}
+
+	len = sizeof(mod->keys);
+	err = settings_bytes_from_str(val, mod->keys, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return -EINVAL;
+	}
+
+	BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0]));
+	return 0;
+}
+
+static int mod_set_sub(struct bt_mesh_model *mod, char *val)
+{
+	int len, err;
+
+	/* Start with empty array regardless of cleared or set value */
+	memset(mod->groups, 0, sizeof(mod->groups));
+
+	if (!val) {
+		BT_DBG("Cleared subscriptions for model");
+		return 0;
+	}
+
+	len = sizeof(mod->groups);
+	err = settings_bytes_from_str(val, mod->groups, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return -EINVAL;
+	}
+
+	BT_DBG("Decoded %u subscribed group addresses for model",
+	       len / sizeof(mod->groups[0]));
+	return 0;
+}
+
+static int mod_set_pub(struct bt_mesh_model *mod, char *val)
+{
+	struct mod_pub_val pub;
+	int len, err;
+
+	if (!mod->pub) {
+		BT_WARN("Model has no publication context!");
+		return -EINVAL;
+	}
+
+	if (!val) {
+		mod->pub->addr = BT_MESH_ADDR_UNASSIGNED;
+		mod->pub->key = 0;
+		mod->pub->cred = 0;
+		mod->pub->ttl = 0;
+		mod->pub->period = 0;
+		mod->pub->retransmit = 0;
+		mod->pub->period_div = pub.period_div;
+		mod->pub->count = 0;
+
+		BT_DBG("Cleared publication for model");
+		return 0;
+	}
+
+	len = sizeof(pub);
+	err = settings_bytes_from_str(val, &pub, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return -EINVAL;
+	}
+
+	if (len != sizeof(pub)) {
+		BT_ERR("Invalid length for model publication");
+		return -EINVAL;
+	}
+
+	mod->pub->addr = pub.addr;
+	mod->pub->key = pub.key;
+	mod->pub->cred = pub.cred;
+	mod->pub->ttl = pub.ttl;
+	mod->pub->period = pub.period;
+	mod->pub->retransmit = pub.retransmit;
+	mod->pub->period_div = pub.period_div;
+	mod->pub->count = 0;
+
+	BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x",
+			       pub.addr, pub.key);
+
+	return 0;
+}
+
+static int mod_data_set(struct bt_mesh_model *mod,
+			char *name, char *len_rd)
+{
+	char *next;
+
+	settings_name_next(name, &next);
+
+	if (mod->cb && mod->cb->settings_set) {
+		return mod->cb->settings_set(mod, next, len_rd);
+	}
+
+	return 0;
+}
+
+static int mod_set(bool vnd, int argc, char **argv, char *val)
+{
+	struct bt_mesh_model *mod;
+	uint8_t elem_idx, mod_idx;
+	uint16_t mod_key;
+
+	if (argc < 2) {
+			BT_ERR("Too small argc (%d)", argc);
+			return -ENOENT;
+		}
+
+		mod_key = strtol(argv[0], NULL, 16);
+	elem_idx = mod_key >> 8;
+	mod_idx = mod_key;
+
+	BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u",
+			       mod_key, elem_idx, mod_idx);
+
+	mod = bt_mesh_model_get(vnd, elem_idx, mod_idx);
+	if (!mod) {
+		BT_ERR("Failed to get model for elem_idx %u mod_idx %u",
+					       elem_idx, mod_idx);
+		return -ENOENT;
+	}
+
+	if (!strcmp(argv[1], "bind")) {
+		return mod_set_bind(mod, val);
+	}
+
+	if (!strcmp(argv[1], "sub")) {
+		return mod_set_sub(mod, val);
+	}
+
+	if (!strcmp(argv[1], "pub")) {
+		return mod_set_pub(mod, val);
+	}
+
+	if (!strcmp(argv[1], "data")) {
+			return mod_data_set(mod, argv[1], val);
+	}
+
+	BT_WARN("Unknown module key %s", argv[1]);
+	return -ENOENT;
+}
+
+static int sig_mod_set(int argc, char **argv, char *val)
+{
+	return mod_set(false, argc, argv, val);
+}
+
+static int vnd_mod_set(int argc, char **argv, char *val)
+{
+	return mod_set(true, argc, argv, val);
+}
+
+static void encode_mod_path(struct bt_mesh_model *mod, bool vnd,
+			    const char *key, char *path, size_t path_len)
+{
+	uint16_t mod_key = (((uint16_t)mod->elem_idx << 8) | mod->mod_idx);
+
+	if (vnd) {
+		snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key);
+	} else {
+		snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key);
+	}
+}
+
+static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd)
+{
+	uint16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];
+	char buf[BT_SETTINGS_SIZE(sizeof(keys))];
+	char path[20];
+	int i, count, err;
+	char *val;
+
+	for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) {
+		if (mod->keys[i] != BT_MESH_KEY_UNUSED) {
+			keys[count++] = mod->keys[i];
+			BT_DBG("model key 0x%04x", mod->keys[i]);
+		}
+	}
+
+	if (count) {
+		val = settings_str_from_bytes(keys, count * sizeof(keys[0]),
+					      buf, sizeof(buf));
+		if (!val) {
+			BT_ERR("Unable to encode model bindings as value");
 			return;
 		}
-#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
-		if (!skip && m->extends) {
-			m = m->extends;
-			depth++;
-		} else if (m->flags & BT_MESH_MOD_NEXT_IS_PARENT) {
-			m = m->next;
-			depth--;
-			skip = true;
-		} else {
-			m = m->next;
-			skip = false;
+	} else {
+		val = NULL;
+	}
+
+	encode_mod_path(mod, vnd, "bind", path, sizeof(path));
+
+	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+	err = settings_save_one(path, val);
+	if (err) {
+		BT_ERR("Failed to store bind");
+	} else {
+		BT_DBG("Stored bind");
+	}
+}
+
+static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd)
+{
+	uint16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT];
+	char buf[BT_SETTINGS_SIZE(sizeof(groups))];
+	char path[20];
+	int i, count, err;
+	char *val;
+
+	for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) {
+		if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+				groups[count++] = mod->groups[i];
+			}
+	}
+
+	if (count) {
+		val = settings_str_from_bytes(groups, count * sizeof(groups[0]),
+				      buf, sizeof(buf));
+		if (!val) {
+			BT_ERR("Unable to encode model subscription as value");
+			return;
 		}
-#endif
-	} while (m && depth > 0);
+	} else {
+		val = NULL;
+	}
+
+	encode_mod_path(mod, vnd, "sub", path, sizeof(path));
+
+	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+	err = settings_save_one(path, val);
+	if (err) {
+		BT_ERR("Failed to store sub");
+	} else {
+		BT_DBG("Stored sub");
+	}
 }
 
-#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
-int bt_mesh_model_extend(struct bt_mesh_model *mod,
-			 struct bt_mesh_model *base_mod)
-{
-	/* Form a cyclical LCRS tree:
-	 * The extends-pointer points to the first child, and the next-pointer
-	 * points to the next sibling. The last sibling is marked by the
-	 * BT_MESH_MOD_NEXT_IS_PARENT flag, and its next-pointer points back to
-	 * the parent. This way, the whole tree is accessible from any node.
-	 *
-	 * We add children (extend them) by inserting them as the first child.
-	 */
-	if (base_mod->next) {
-		return -EALREADY;
+static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
+	struct mod_pub_val pub;
+	char path[20];
+	char *val;
+	int err;
+
+	if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+		val = NULL;
+	} else {
+		pub.addr = mod->pub->addr;
+		pub.key = mod->pub->key;
+		pub.ttl = mod->pub->ttl;
+		pub.retransmit = mod->pub->retransmit;
+		pub.period = mod->pub->period;
+		pub.period_div = mod->pub->period_div;
+		pub.cred = mod->pub->cred;
+
+		val = settings_str_from_bytes(&pub, sizeof(pub), buf, sizeof(buf));
+		if (!val) {
+			BT_ERR("Unable to encode model publication as value");
+			return;
+		}
 	}
 
-	if (mod->extends) {
-		base_mod->next = mod->extends;
+	encode_mod_path(mod, vnd, "pub", path, sizeof(path));
+
+	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+	err = settings_save_one(path, val);
+	if (err) {
+		BT_ERR("Failed to store pub");
 	} else {
-		base_mod->next = mod;
-		base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT;
+		BT_DBG("Stored pub");
 	}
+}
 
-	mod->extends = base_mod;
-	return 0;
+static void store_pending_mod(struct bt_mesh_model *mod,
+			      struct bt_mesh_elem *elem, bool vnd,
+			      bool primary, void *user_data)
+{
+	if (!mod->flags) {
+		return;
+	}
+
+	if (mod->flags & BT_MESH_MOD_BIND_PENDING) {
+		mod->flags &= ~BT_MESH_MOD_BIND_PENDING;
+		store_pending_mod_bind(mod, vnd);
+	}
+
+	if (mod->flags & BT_MESH_MOD_SUB_PENDING) {
+		mod->flags &= ~BT_MESH_MOD_SUB_PENDING;
+		store_pending_mod_sub(mod, vnd);
+	}
+
+	if (mod->flags & BT_MESH_MOD_PUB_PENDING) {
+		mod->flags &= ~BT_MESH_MOD_PUB_PENDING;
+		store_pending_mod_pub(mod, vnd);
+	}
+}
+
+void bt_mesh_model_pending_store(void)
+{
+	bt_mesh_model_foreach(store_pending_mod, NULL);
+}
+
+void bt_mesh_model_bind_store(struct bt_mesh_model *mod)
+{
+	mod->flags |= BT_MESH_MOD_BIND_PENDING;
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING);
+}
+
+void bt_mesh_model_sub_store(struct bt_mesh_model *mod)
+{
+	mod->flags |= BT_MESH_MOD_SUB_PENDING;
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING);
+}
+
+void bt_mesh_model_pub_store(struct bt_mesh_model *mod)
+{
+	mod->flags |= BT_MESH_MOD_PUB_PENDING;
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_MOD_PENDING);
+}
+
+int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
+			     const char *name, const void *data,
+			     size_t data_len)
+{
+	char path[30];
+	char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
+	char *val;
+	int err;
+
+	encode_mod_path(mod, vnd, "data", path, sizeof(path));
+	if (name) {
+		strcat(path, "/");
+		strncat(path, name, SETTINGS_MAX_DIR_DEPTH);
+	}
+
+	if (data_len) {
+		val = settings_str_from_bytes(data, data_len, buf, sizeof(buf));
+		if (!val) {
+			BT_ERR("Unable to encode model publication as value");
+			return -EINVAL;
+		}
+		err = settings_save_one(path, val);
+	} else {
+		err = settings_save_one(path, NULL);
+	}
+
+	if (err) {
+		BT_ERR("Failed to store %s value", path);
+	} else {
+		BT_DBG("Stored %s value", path);
+	}
+	return err;
 }
 #endif
+
+static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+		       bool vnd, bool primary, void *user_data)
+{
+	if (mod->pub && mod->pub->update &&
+	    	mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int32_t ms = bt_mesh_model_pub_period_get(mod);
+
+		if (ms > 0) {
+			BT_DBG("Starting publish timer (period %u ms)", ms);
+			k_work_schedule(&mod->pub->timer, K_MSEC(ms));
+		}
+	}
+
+	if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+		return;
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(mod->groups); i++) {
+		if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+			bt_mesh_lpn_group_add(mod->groups[i]);
+		}
+	}
+}
+
+void bt_mesh_model_settings_commit(void)
+{
+	bt_mesh_model_foreach(commit_mod, NULL);
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct conf_handler bt_mesh_sig_mod_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = sig_mod_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+
+static struct conf_handler bt_mesh_vnd_mod_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = vnd_mod_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_access_init(void)
+{
+    #if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_sig_mod_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_access conf");
+	rc = conf_register(&bt_mesh_vnd_mod_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_access conf");
+    #endif
+}
diff --git a/nimble/host/mesh/src/access.h b/nimble/host/mesh/src/access.h
index affba538f2..217f4abe0f 100644
--- a/nimble/host/mesh/src/access.h
+++ b/nimble/host/mesh/src/access.h
@@ -11,14 +11,6 @@
 
 #include "mesh/mesh.h"
 
-/* bt_mesh_model.flags */
-enum {
-	BT_MESH_MOD_BIND_PENDING = BIT(0),
-	BT_MESH_MOD_SUB_PENDING = BIT(1),
-	BT_MESH_MOD_PUB_PENDING = BIT(2),
-	BT_MESH_MOD_NEXT_IS_PARENT = BIT(3),
-};
-
 /* Tree walk return codes */
 enum bt_mesh_walk {
 	BT_MESH_WALK_STOP,
@@ -29,15 +21,16 @@ void bt_mesh_elem_register(struct bt_mesh_elem *elem, uint8_t count);
 
 uint8_t bt_mesh_elem_count(void);
 
-/* Find local element based on unicast or group address */
+/* Find local element based on unicast address */
 struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr);
 
-struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod);
-void bt_mesh_model_tree_walk(struct bt_mesh_model *root,
-			     enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
-						     uint32_t depth,
-						     void *user_data),
-			     void *user_data);
+bool bt_mesh_has_addr(uint16_t addr);
+bool bt_mesh_model_has_key(struct bt_mesh_model *mod, uint16_t key);
+
+void bt_mesh_model_extensions_walk(struct bt_mesh_model *root,
+				   enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
+							   void *user_data),
+				   void *user_data);
 
 uint16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, uint16_t addr);
 
@@ -61,4 +54,35 @@ struct bt_mesh_model *bt_mesh_model_get(bool vnd, uint8_t elem_idx, uint8_t mod_
 void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
 
 int bt_mesh_comp_register(const struct bt_mesh_comp *comp);
+
+void bt_mesh_model_pending_store(void);
+void bt_mesh_model_bind_store(struct bt_mesh_model *mod);
+void bt_mesh_model_sub_store(struct bt_mesh_model *mod);
+void bt_mesh_model_pub_store(struct bt_mesh_model *mod);
+void bt_mesh_model_settings_commit(void);
+
+/** @brief Register a callback function hook for mesh model messages.
+ *
+ * Register a callback function to act as a hook for recieving mesh model layer messages
+ * directly to the application without having instantiated the relevant models.
+ *
+ * @param cb A pointer to the callback function.
+ */
+void bt_mesh_msg_cb_set(void (*cb)(uint32_t opcode, struct bt_mesh_msg_ctx *ctx,
+	struct os_mbuf *buf));
+
+/** @brief Send a mesh model message.
+ *
+ * Send a mesh model layer message out into the mesh network without having instantiated
+ * the relevant mesh models.
+ *
+ * @param ctx The Bluetooth mesh message context.
+ * @param buf The message payload.
+ *
+ * @return 0 on success or negative error code on failure.
+ */
+int bt_mesh_msg_send(struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf, uint16_t src_addr,
+	const struct bt_mesh_send_cb *cb, void *cb_data);
+
+void bt_mesh_access_init(void);
 #endif
diff --git a/nimble/host/mesh/src/adv.c b/nimble/host/mesh/src/adv.c
index 7e0e1927fa..d7a8c123c9 100644
--- a/nimble/host/mesh/src/adv.c
+++ b/nimble/host/mesh/src/adv.c
@@ -13,7 +13,6 @@
 #include "mesh/mesh.h"
 #include "host/ble_hs_adv.h"
 #include "host/ble_gap.h"
-#include "nimble/hci_common.h"
 #include "mesh/porting.h"
 
 #include "adv.h"
@@ -23,41 +22,26 @@
 #include "prov.h"
 #include "proxy.h"
 
-/* Convert from ms to 0.625ms units */
-#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
-
 /* Window and Interval are equal for continuous scanning */
-#define MESH_SCAN_INTERVAL_MS 30
-#define MESH_SCAN_WINDOW_MS   30
-#define MESH_SCAN_INTERVAL    ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS)
-#define MESH_SCAN_WINDOW      ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS)
-
-/* Pre-5.0 controllers enforce a minimum interval of 100ms
- * whereas 5.0+ controllers can go down to 20ms.
- */
-#define ADV_INT_DEFAULT_MS 100
-#define ADV_INT_FAST_MS    20
+#define MESH_SCAN_INTERVAL    BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_INTERVAL_MS)
+#define MESH_SCAN_WINDOW      BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_WINDOW_MS)
 
-static int32_t adv_int_min =  ADV_INT_DEFAULT_MS;
+const uint8_t bt_mesh_adv_type[BT_MESH_ADV_TYPES] = {
+	[BT_MESH_ADV_PROV]   = BT_DATA_MESH_PROV,
+	[BT_MESH_ADV_DATA]   = BT_DATA_MESH_MESSAGE,
+	[BT_MESH_ADV_BEACON] = BT_DATA_MESH_BEACON,
+	[BT_MESH_ADV_URI]    = BT_DATA_URI,
+};
 
-/* TinyCrypt PRNG consumes a lot of stack space, so we need to have
- * an increased call stack whenever it's used.
- */
-#if MYNEWT
-OS_TASK_STACK_DEFINE(g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE));
-struct os_task adv_task;
-#endif
-
-static struct ble_npl_eventq adv_queue;
 extern uint8_t g_mesh_addr_type;
-static int adv_initialized = false;
-
-static os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE(
-		MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
-		BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)];
 
 struct os_mbuf_pool adv_os_mbuf_pool;
-static struct os_mempool adv_buf_mempool;
+struct ble_npl_eventq bt_mesh_adv_queue;
+
+os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE(
+        MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
+        BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)];
+struct os_mempool adv_buf_mempool;
 
 static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT];
 
@@ -66,143 +50,6 @@ static struct bt_mesh_adv *adv_alloc(int id)
 	return &adv_pool[id];
 }
 
-static inline void adv_send_start(uint16_t duration, int err,
-				  const struct bt_mesh_send_cb *cb,
-				  void *cb_data)
-{
-	if (cb && cb->start) {
-		cb->start(duration, err, cb_data);
-	}
-}
-
-static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb,
-				void *cb_data)
-{
-	if (cb && cb->end) {
-		cb->end(err, cb_data);
-	}
-}
-
-static inline void adv_send(struct os_mbuf *buf)
-{
-	static const uint8_t adv_type[] = {
-		[BT_MESH_ADV_PROV]   = BLE_HS_ADV_TYPE_MESH_PROV,
-		[BT_MESH_ADV_DATA]   = BLE_HS_ADV_TYPE_MESH_MESSAGE,
-		[BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON,
-		[BT_MESH_ADV_URI]    = BLE_HS_ADV_TYPE_URI,
-}	;
-
-	const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb;
-	void *cb_data = BT_MESH_ADV(buf)->cb_data;
-	struct ble_gap_adv_params param = { 0 };
-	uint16_t duration, adv_int;
-	struct bt_data ad;
-	int err;
-
-	adv_int = max(adv_int_min,
-		      BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
-#if MYNEWT_VAL(BLE_CONTROLLER)
-	duration = ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
-				(adv_int + 10));
-#else
-	duration = (MESH_SCAN_WINDOW_MS +
-		    ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
-		     (adv_int + 10)));
-#endif
-
-	BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type,
-	       buf->om_len, bt_hex(buf->om_data, buf->om_len));
-	BT_DBG("count %u interval %ums duration %ums",
-	       BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int,
-	       duration);
-
-	ad.type = adv_type[BT_MESH_ADV(buf)->type];
-	ad.data_len = buf->om_len;
-	ad.data = buf->om_data;
-
-	param.itvl_min = ADV_SCAN_UNIT(adv_int);
-	param.itvl_max = param.itvl_min;
-	param.conn_mode = BLE_GAP_CONN_MODE_NON;
-
-	err = bt_le_adv_start(&param, &ad, 1, NULL, 0);
-	net_buf_unref(buf);
-	adv_send_start(duration, err, cb, cb_data);
-	if (err) {
-		BT_ERR("Advertising failed: err %d", err);
-		return;
-	}
-
-	BT_DBG("Advertising started. Sleeping %u ms", duration);
-
-	k_sleep(K_MSEC(duration));
-
-	err = bt_le_adv_stop(false);
-	adv_send_end(err, cb, cb_data);
-	if (err) {
-		BT_ERR("Stopping advertising failed: err %d", err);
-		return;
-	}
-
-	BT_DBG("Advertising stopped");
-}
-
-void
-mesh_adv_thread(void *args)
-{
-	static struct ble_npl_event *ev;
-	struct os_mbuf *buf;
-#if (MYNEWT_VAL(BLE_MESH_PROXY))
-	int32_t timeout;
-#endif
-
-	BT_DBG("started");
-
-	while (1) {
-#if (MYNEWT_VAL(BLE_MESH_PROXY))
-		ev = ble_npl_eventq_get(&adv_queue, 0);
-		while (!ev) {
-			timeout = bt_mesh_proxy_adv_start();
-			BT_DBG("Proxy Advertising up to %d ms", (int) timeout);
-
-			// FIXME: should we redefine K_SECONDS macro instead in glue?
-			if (timeout != K_FOREVER) {
-				timeout = ble_npl_time_ms_to_ticks32(timeout);
-			}
-
-			ev = ble_npl_eventq_get(&adv_queue, timeout);
-			bt_mesh_proxy_adv_stop();
-		}
-#else
-		ev = ble_npl_eventq_get(&adv_queue, BLE_NPL_TIME_FOREVER);
-#endif
-
-		if (!ev || !ble_npl_event_get_arg(ev)) {
-			continue;
-		}
-
-		buf = ble_npl_event_get_arg(ev);
-
-		/* busy == 0 means this was canceled */
-		if (BT_MESH_ADV(buf)->busy) {
-			BT_MESH_ADV(buf)->busy = 0;
-			adv_send(buf);
-		} else {
-			net_buf_unref(buf);
-		}
-
-		/* os_sched(NULL); */
-	}
-}
-
-void bt_mesh_adv_update(void)
-{
-	static struct ble_npl_event ev = { };
-
-	BT_DBG("");
-
-	ble_npl_eventq_put(&adv_queue, &ev);
-}
-
 struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool,
 					     bt_mesh_adv_alloc_t get_id,
 					     enum bt_mesh_adv_type type,
@@ -252,7 +99,8 @@ void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb,
 	BT_MESH_ADV(buf)->cb_data = cb_data;
 	BT_MESH_ADV(buf)->busy = 1;
 
-	net_buf_put(&adv_queue, net_buf_ref(buf));
+	net_buf_put(&bt_mesh_adv_queue, net_buf_ref(buf));
+	bt_mesh_adv_buf_ready();
 }
 
 static void bt_mesh_scan_cb(const bt_addr_le_t *addr, int8_t rssi,
@@ -306,42 +154,6 @@ static void bt_mesh_scan_cb(const bt_addr_le_t *addr, int8_t rssi,
 	}
 }
 
-void bt_mesh_adv_init(void)
-{
-	int rc;
-
-	/* Advertising should only be initialized once. Calling
-	 * os_task init the second time will result in an assert. */
-	if (adv_initialized) {
-		return;
-	}
-
-	rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
-			     BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
-			     adv_buf_mem, "adv_buf_pool");
-	assert(rc == 0);
-
-	rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool,
-			       BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
-			       MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT));
-	assert(rc == 0);
-
-	ble_npl_eventq_init(&adv_queue);
-
-#if MYNEWT
-	os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL,
-	             MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER,
-	             g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE));
-#endif
-
-	/* For BT5 controllers we can have fast advertising interval */
-	if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) {
-	    adv_int_min = ADV_INT_FAST_MS;
-	}
-
-	adv_initialized = true;
-}
-
 int
 ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg)
 {
diff --git a/nimble/host/mesh/src/adv.h b/nimble/host/mesh/src/adv.h
index 779ee58ed5..aa83a47b86 100644
--- a/nimble/host/mesh/src/adv.h
+++ b/nimble/host/mesh/src/adv.h
@@ -14,32 +14,59 @@
 
 #define BT_MESH_ADV(om) (*(struct bt_mesh_adv **) OS_MBUF_USRHDR(om))
 
+#define BT_MESH_ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
+#define BT_MESH_SCAN_INTERVAL_MS 30
+#define BT_MESH_SCAN_WINDOW_MS   30
+
 #define BT_MESH_ADV_DATA_SIZE 31
 
 /* The user data is a pointer (4 bytes) to struct bt_mesh_adv */
-#define BT_MESH_ADV_USER_DATA_SIZE (sizeof(struct bt_mesh_adv *))
+#define BT_MESH_ADV_USER_DATA_SIZE 4
 
 #define BT_MESH_MBUF_HEADER_SIZE (sizeof(struct os_mbuf_pkthdr) + \
                                     BT_MESH_ADV_USER_DATA_SIZE +\
 				    sizeof(struct os_mbuf))
 
+/* We declare it as extern here to share it between 'adv' and 'adv_legacy' */
+extern struct os_mbuf_pool adv_os_mbuf_pool;
+extern struct ble_npl_eventq bt_mesh_adv_queue;
+extern struct os_mempool adv_buf_mempool;
+extern os_membuf_t adv_buf_mem[];
+
 enum bt_mesh_adv_type
 {
 	BT_MESH_ADV_PROV,
 	BT_MESH_ADV_DATA,
 	BT_MESH_ADV_BEACON,
 	BT_MESH_ADV_URI,
+
+	BT_MESH_ADV_TYPES,
 };
 
 typedef void (*bt_mesh_adv_func_t)(struct os_mbuf *buf, uint16_t duration,
 				   int err, void *user_data);
 
+
+static inline void adv_send_start(uint16_t duration, int err,
+				  const struct bt_mesh_send_cb *cb,
+				  	void *cb_data)
+{
+	if (cb && cb->start) {
+		cb->start(duration, err, cb_data);
+	}
+}
+
 struct bt_mesh_adv {
+	/** Fragments associated with this buffer. */
+	struct os_mbuf *frags;
+
 	const struct bt_mesh_send_cb *cb;
 	void *cb_data;
 
 	uint8_t      type:2,
-		  busy:1;
+		     started:1,
+		     busy:1;
+
 	uint8_t      xmit;
 
 	uint8_t flags;
@@ -69,6 +96,36 @@ void bt_mesh_adv_init(void);
 int bt_mesh_scan_enable(void);
 
 int bt_mesh_scan_disable(void);
+int bt_mesh_adv_enable(void);
 
+void bt_mesh_adv_buf_ready(void);
+
+int bt_mesh_adv_start(const struct ble_gap_adv_params *param, int32_t duration,
+		      const struct bt_data *ad, size_t ad_len,
+		      const struct bt_data *sd, size_t sd_len);
+
+static inline void bt_mesh_adv_send_start(uint16_t duration, int err,
+					  struct bt_mesh_adv *adv)
+{
+	if (!adv->started) {
+		adv->started = 1;
+
+		if (adv->cb && adv->cb->start) {
+			adv->cb->start(duration, err, adv->cb_data);
+		}
+
+		if (err) {
+			adv->cb = NULL;
+		}
+	}
+}
+
+static inline void bt_mesh_adv_send_end(
+	int err, struct bt_mesh_adv const *adv)
+{
+	if (adv->started && adv->cb && adv->cb->end) {
+		adv->cb->end(err, adv->cb_data);
+	}
+}
 int ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg);
 #endif
diff --git a/nimble/host/mesh/src/adv_ext.c b/nimble/host/mesh/src/adv_ext.c
new file mode 100644
index 0000000000..07c693dea1
--- /dev/null
+++ b/nimble/host/mesh/src/adv_ext.c
@@ -0,0 +1,345 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2018 Nordic Semiconductor ASA
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_ADV_LOG
+
+
+#include "adv.h"
+#include "net.h"
+#include "proxy.h"
+#include "pb_gatt_srv.h"
+#include "syscfg/syscfg.h"
+#include "host/ble_gap.h"
+
+#if MYNEWT_VAL(BLE_MESH_ADV_EXT)
+/* Convert from ms to 0.625ms units */
+#define ADV_INT_FAST_MS    20
+#define BT_ID_DEFAULT      0
+
+static struct ble_gap_ext_adv_params adv_param = {
+	.itvl_min = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
+	.itvl_max = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
+};
+
+bool ext_adv_configured = false;
+
+enum {
+	/** Controller is currently advertising */
+	ADV_FLAG_ACTIVE,
+	/** Currently performing proxy advertising */
+	ADV_FLAG_PROXY,
+	/** The send-call has been scheduled. */
+	ADV_FLAG_SCHEDULED,
+	/** Custom adv params have been set, we need to update the parameters on
+	 *  the next send.
+	 */
+	ADV_FLAG_UPDATE_PARAMS,
+
+	/* Number of adv flags. */
+	ADV_FLAGS_NUM
+};
+
+static struct {
+	ATOMIC_DEFINE(flags, ADV_FLAGS_NUM);
+	struct bt_le_ext_adv *instance;
+	struct os_mbuf *buf;
+	int64_t timestamp;
+	struct k_work_delayable work;
+} adv;
+
+
+static void schedule_send(void)
+{
+	int64_t timestamp = adv.timestamp;
+	int64_t delta;
+
+	if (atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
+		ble_gap_ext_adv_stop(BT_ID_DEFAULT);
+		atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+	}
+
+	if (atomic_test_bit(adv.flags, ADV_FLAG_ACTIVE) ||
+	atomic_test_and_set_bit(adv.flags, ADV_FLAG_SCHEDULED)) {
+		return;
+	}
+
+	/* The controller will send the next advertisement immediately.
+	 * Introduce a delay here to avoid sending the next mesh packet closer
+	 * to the previous packet than what's permitted by the specification.
+	 */
+	delta = k_uptime_delta(&timestamp);
+	k_work_reschedule(&adv.work, K_MSEC(ADV_INT_FAST_MS - delta));
+}
+
+static int
+ble_mesh_ext_adv_event_handler(struct ble_gap_event *event, void *arg)
+{
+	int64_t duration;
+
+	switch (event->type) {
+	case BLE_GAP_EVENT_CONNECT:
+		if (atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
+			atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+			schedule_send();
+		}
+		break;
+	case BLE_GAP_EVENT_ADV_COMPLETE:
+		/* Calling k_uptime_delta on a timestamp moves it to the current time.
+		 * This is essential here, as schedule_send() uses the end of the event
+		 * as a reference to avoid sending the next advertisement too soon.
+		 */
+		duration = k_uptime_delta(&adv.timestamp);
+
+		BT_DBG("Advertising stopped after %u ms", (uint32_t)duration);
+
+		atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+
+		if (!atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
+			net_buf_unref(adv.buf);
+		}
+
+		schedule_send();
+		break;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+static int adv_start(const struct ble_gap_ext_adv_params *param,
+		     uint32_t timeout,
+		     const struct bt_data *ad, size_t ad_len,
+		     const struct bt_data *sd, size_t sd_len)
+{
+	int err;
+	struct os_mbuf *ad_data;
+	struct os_mbuf *sd_data;
+
+	ad_data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
+	assert(ad_data);
+	sd_data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
+	assert(sd_data);
+	if (!adv.instance) {
+		BT_ERR("Mesh advertiser not enabled");
+		err = -ENODEV;
+		goto error;
+	}
+
+	if (atomic_test_and_set_bit(adv.flags, ADV_FLAG_ACTIVE)) {
+		BT_ERR("Advertiser is busy");
+		err = -EBUSY;
+		goto error;
+	}
+
+	if (atomic_test_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS)) {
+		err = ble_gap_ext_adv_configure(BT_ID_DEFAULT, param, NULL,
+			       ble_mesh_ext_adv_event_handler, NULL);
+		if (err) {
+			BT_ERR("Failed updating adv params: %d", err);
+			atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+			goto error;
+		}
+
+		atomic_set_bit_to(adv.flags, ADV_FLAG_UPDATE_PARAMS,
+				  param != &adv_param);
+	}
+
+	assert(ad_data);
+	err = os_mbuf_append(ad_data, ad, ad_len);
+	if (err) {
+		goto error;
+	}
+
+	err = ble_gap_ext_adv_set_data(BT_ID_DEFAULT, ad_data);
+	if (err) {
+		BT_ERR("Failed setting adv data: %d", err);
+		atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+		goto error;
+	}
+
+	err = os_mbuf_append(sd_data, sd, sd_len);
+	if (err) {
+		goto error;
+	}
+	err = ble_gap_ext_adv_rsp_set_data(BT_ID_DEFAULT, sd_data);
+	if (err) {
+		BT_ERR("Failed setting scan response data: %d", err);
+		atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+		goto error;
+	}
+
+	adv.timestamp = k_uptime_get();
+
+	err = ble_gap_ext_adv_start(BT_ID_DEFAULT, timeout, 0);
+	if (err) {
+		BT_ERR("Advertising failed: err %d", err);
+		atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
+	}
+
+error:
+	if (ad_data) {
+		os_mbuf_free_chain(ad_data);
+	}
+
+	if (sd_data) {
+		os_mbuf_free_chain(sd_data);
+	}
+	return err;
+}
+
+static int buf_send(struct os_mbuf *buf)
+{
+	static const uint8_t bt_mesh_adv_type[] = {
+		[BT_MESH_ADV_PROV]   = BLE_HS_ADV_TYPE_MESH_PROV,
+		[BT_MESH_ADV_DATA]   = BLE_HS_ADV_TYPE_MESH_MESSAGE,
+		[BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON,
+		[BT_MESH_ADV_URI]    = BLE_HS_ADV_TYPE_URI,
+	};
+
+	struct bt_le_ext_adv_start_param start = {
+		.num_events =
+			BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1,
+	};
+	uint16_t duration, adv_int;
+	struct bt_data ad;
+	int err;
+
+	adv_int = MAX(ADV_INT_FAST_MS,
+		      BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
+	/* Upper boundary estimate: */
+	duration = start.num_events * (adv_int + 10);
+
+	BT_DBG("type %u len %u: %s", BT_MESH_ADV(buf)->type,
+	       buf->om_len, bt_hex(buf->om_data, buf->om_len));
+	BT_DBG("count %u interval %ums duration %ums",
+	       BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int,
+	       duration);
+
+	ad.type = bt_mesh_adv_type[BT_MESH_ADV(buf)->type];
+	ad.data_len = buf->om_len;
+	ad.data = buf->om_data;
+
+	/* Only update advertising parameters if they're different */
+	if (adv_param.itvl_min != BT_MESH_ADV_SCAN_UNIT(adv_int)) {
+		adv_param.itvl_min = BT_MESH_ADV_SCAN_UNIT(adv_int);
+		adv_param.itvl_max = adv_param.itvl_min;
+		atomic_set_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS);
+	}
+
+	err = adv_start(&adv_param, duration, &ad, 1, NULL, 0);
+	if (!err) {
+		adv.buf = net_buf_ref(buf);
+	}
+
+	bt_mesh_adv_send_start(duration, err, BT_MESH_ADV(buf));
+
+	return err;
+}
+
+static void send_pending_adv(struct ble_npl_event *work)
+{
+	struct os_mbuf *buf;
+	int err;
+
+	atomic_clear_bit(adv.flags, ADV_FLAG_SCHEDULED);
+
+	while ((buf = net_buf_get(&bt_mesh_adv_queue, K_NO_WAIT))) {
+		/* busy == 0 means this was canceled */
+		if (!BT_MESH_ADV(buf)->busy) {
+			net_buf_unref(buf);
+			continue;
+		}
+
+		BT_MESH_ADV(buf)->busy = 0U;
+		err = buf_send(buf);
+
+		net_buf_unref(buf);
+
+		if (!err) {
+			return; /* Wait for advertising to finish */
+		}
+	}
+
+	if (!MYNEWT_VAL(BLE_MESH_GATT_SERVER)) {
+		return;
+	}
+
+	/* No more pending buffers */
+	if (bt_mesh_is_provisioned()) {
+	    if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+	        err = bt_mesh_proxy_adv_start();
+	        BT_DBG("Proxy Advertising");
+	    }
+	} else if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
+	    err = bt_mesh_pb_gatt_adv_start();
+	    BT_DBG("PB-GATT Advertising");
+	}
+
+	if (!err) {
+		atomic_set_bit(adv.flags, ADV_FLAG_PROXY);
+	}
+}
+
+void bt_mesh_adv_update(void)
+{
+	BT_DBG("");
+
+	schedule_send();
+}
+
+void bt_mesh_adv_buf_ready(void)
+{
+	schedule_send();
+}
+
+void bt_mesh_adv_init(void)
+{
+    int rc;
+
+    rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
+                         BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+                         adv_buf_mem, "adv_buf_pool");
+    assert(rc == 0);
+
+    rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool,
+                           BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+                           MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT));
+    assert(rc == 0);
+
+    ble_npl_eventq_init(&bt_mesh_adv_queue);
+
+	k_work_init_delayable(&adv.work, send_pending_adv);
+}
+
+int bt_mesh_adv_enable(void)
+{
+    /* No need to initialize extended advertiser instance here */
+	return 0;
+}
+
+int bt_mesh_adv_start(const struct ble_gap_adv_params *param, int32_t duration,
+		      const struct bt_data *ad, size_t ad_len,
+		      const struct bt_data *sd, size_t sd_len)
+{
+	static uint32_t adv_timeout;
+	struct ble_gap_ext_adv_params params = {
+		.itvl_min = param->itvl_min,
+		.itvl_max = param->itvl_max
+	};
+
+	/* In NimBLE duration is in ms, not 10ms units */
+	adv_timeout = (duration == BLE_HS_FOREVER) ? 0 : duration;
+
+	BT_DBG("Start advertising %d ms", duration);
+
+	atomic_set_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS);
+
+	return adv_start(&params, adv_timeout, ad, ad_len, sd, sd_len);
+}
+#endif
diff --git a/nimble/host/mesh/src/adv_legacy.c b/nimble/host/mesh/src/adv_legacy.c
new file mode 100644
index 0000000000..e495dd614c
--- /dev/null
+++ b/nimble/host/mesh/src/adv_legacy.c
@@ -0,0 +1,244 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2018 Nordic Semiconductor ASA
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_ADV_LOG
+
+#include "adv.h"
+#include "net.h"
+#include "foundation.h"
+#include "beacon.h"
+#include "prov.h"
+#include "proxy.h"
+#include "mesh/glue.h"
+#include "pb_gatt_srv.h"
+
+#if MYNEWT_VAL(BLE_MESH_ADV_LEGACY)
+/* Convert from ms to 0.625ms units */
+#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
+
+#if (MYNEWT_VAL(BSP_NRF51) && !MYNEWT_VAL(BLE_CONTROLLER))
+#define CONFIG_BT_CTLR_LOW_LAT 1
+#else
+#define CONFIG_BT_CTLR_LOW_LAT 0
+#endif
+
+/* Pre-5.0 controllers enforce a minimum interval of 100ms
+ * whereas 5.0+ controllers can go down to 20ms.
+ */
+#define ADV_INT_DEFAULT_MS 100
+#define ADV_INT_FAST_MS    20
+
+static int32_t adv_int_min =  ADV_INT_DEFAULT_MS;
+
+static int adv_initialized = false;
+/* TinyCrypt PRNG consumes a lot of stack space, so we need to have
+ * an increased call stack whenever it's used.
+ */
+#if MYNEWT
+OS_TASK_STACK_DEFINE(g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE));
+struct os_task adv_task;
+#endif
+
+static int32_t adv_timeout;
+
+static inline void adv_send(struct os_mbuf *buf)
+{
+	static const uint8_t adv_type[] = {
+			[BT_MESH_ADV_PROV]   = BLE_HS_ADV_TYPE_MESH_PROV,
+			[BT_MESH_ADV_DATA]   = BLE_HS_ADV_TYPE_MESH_MESSAGE,
+			[BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON,
+			[BT_MESH_ADV_URI]    = BLE_HS_ADV_TYPE_URI,
+	};
+
+	struct ble_gap_adv_params param = { 0 };
+	uint16_t duration, adv_int;
+	struct bt_data ad;
+	int err;
+
+	adv_int = max(adv_int_min,
+		      BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
+#if MYNEWT_VAL(BLE_CONTROLLER)
+	duration = ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
+				(adv_int + 10));
+#else
+	/* Zephyr Bluetooth Low Energy Controller for mesh stack uses
+	 * pre-emptible continuous scanning, allowing advertising events to be
+	 * transmitted without delay when advertising is enabled. No need to
+	 * compensate with scan window duration.
+	 * An advertising event could be delayed by upto one interval when
+	 * advertising is stopped and started in quick succession, hence add
+	 * advertising interval to the total advertising duration.
+	 */
+	duration = (adv_int +
+		    ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
+		     (adv_int + 10)));
+
+	/* Zephyr Bluetooth Low Energy Controller built for nRF51x SoCs use
+	 * CONFIG_BT_CTLR_LOW_LAT=y, and continuous scanning cannot be
+	 * pre-empted, hence, scanning will block advertising events from
+	 * being transmitted. Increase the advertising duration by the
+	 * amount of scan window duration to compensate for the blocked
+	 * advertising events.
+	 */
+	if (CONFIG_BT_CTLR_LOW_LAT) {
+		duration += BT_MESH_SCAN_WINDOW_MS;
+	}
+#endif
+
+	BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type,
+	       buf->om_len, bt_hex(buf->om_data, buf->om_len));
+	BT_DBG("count %u interval %ums duration %ums",
+	       BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int,
+	       duration);
+
+	ad.type = adv_type[BT_MESH_ADV(buf)->type];
+	ad.data_len = buf->om_len;
+	ad.data = buf->om_data;
+
+	param.itvl_min = ADV_SCAN_UNIT(adv_int);
+	param.itvl_max = param.itvl_min;
+	param.conn_mode = BLE_GAP_CONN_MODE_NON;
+
+	int64_t time = k_uptime_get();
+
+	err = bt_le_adv_start(&param, &ad, 1, NULL, 0);
+
+	bt_mesh_adv_send_start(duration, err, BT_MESH_ADV(buf));
+	if (err) {
+		BT_ERR("Advertising failed: err %d", err);
+		return;
+	}
+
+	BT_DBG("Advertising started. Sleeping %u ms", duration);
+
+	k_sleep(K_MSEC(duration));
+
+	err = bt_le_adv_stop();
+	if (err) {
+		BT_ERR("Stopping advertising failed: err %d", err);
+		return;
+	}
+
+	BT_DBG("Advertising stopped (%u ms)", (uint32_t) k_uptime_delta(&time));
+}
+
+void
+mesh_adv_thread(void *args)
+{
+	static struct ble_npl_event *ev;
+	struct os_mbuf *buf;
+
+	BT_DBG("started");
+
+	while (1) {
+		if (MYNEWT_VAL(BLE_MESH_GATT_SERVER)) {
+			ev = ble_npl_eventq_get(&bt_mesh_adv_queue, 0);
+			while (!ev) {
+				/* Adv timeout may be set by a call from proxy
+				 * to bt_mesh_adv_start:
+				 */
+				adv_timeout = K_FOREVER;
+				if (bt_mesh_is_provisioned()) {
+					if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+						bt_mesh_proxy_adv_start();
+						BT_DBG("Proxy Advertising up to %d ms", (int) adv_timeout);
+					}
+				} else if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
+					bt_mesh_pb_gatt_adv_start();
+					BT_DBG("PB-GATT Advertising up to %d ms", (int) adv_timeout);
+				}
+
+				ev = ble_npl_eventq_get(&bt_mesh_adv_queue, ble_npl_time_ms_to_ticks32(adv_timeout));
+				bt_le_adv_stop();
+			}
+		} else {
+			ev = ble_npl_eventq_get(&bt_mesh_adv_queue, BLE_NPL_TIME_FOREVER);
+		}
+		if (!ev || !ble_npl_event_get_arg(ev)) {
+			continue;
+		}
+
+		buf = ble_npl_event_get_arg(ev);
+
+		/* busy == 0 means this was canceled */
+		if (BT_MESH_ADV(buf)->busy) {
+			BT_MESH_ADV(buf)->busy = 0;
+			adv_send(buf);
+		}
+
+		net_buf_unref(buf);
+
+		/* os_sched(NULL); */
+	}
+}
+
+void bt_mesh_adv_update(void)
+{
+	static struct ble_npl_event ev = { };
+
+	BT_DBG("");
+
+	ble_npl_eventq_put(&bt_mesh_adv_queue, &ev);
+}
+
+void bt_mesh_adv_buf_ready(void)
+{
+	/* Will be handled automatically */
+}
+
+void bt_mesh_adv_init(void)
+{
+	int rc;
+
+	/* Advertising should only be initialized once. Calling
+	 * os_task init the second time will result in an assert. */
+	if (adv_initialized) {
+		return;
+	}
+
+	rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
+			     BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+			     adv_buf_mem, "adv_buf_pool");
+	assert(rc == 0);
+
+	rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool,
+			       BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+			       MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT));
+	assert(rc == 0);
+
+	ble_npl_eventq_init(&bt_mesh_adv_queue);
+
+#if MYNEWT
+	os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL,
+	             MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER,
+	             g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE));
+#endif
+
+	/* For BT5 controllers we can have fast advertising interval */
+	if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) {
+	    adv_int_min = ADV_INT_FAST_MS;
+	}
+
+	adv_initialized = true;
+}
+
+int bt_mesh_adv_enable(void)
+{
+	/* Dummy function - in legacy adv thread is started on init*/
+	return 0;
+}
+
+int bt_mesh_adv_start(const struct ble_gap_adv_params *param, int32_t duration,
+		      const struct bt_data *ad, size_t ad_len,
+		      const struct bt_data *sd, size_t sd_len)
+{
+	adv_timeout = duration;
+	return bt_le_adv_start(param, ad, ad_len, sd, sd_len);
+}
+#endif
diff --git a/nimble/host/mesh/src/app_keys.c b/nimble/host/mesh/src/app_keys.c
index 9582bbd045..cfe82576ab 100644
--- a/nimble/host/mesh/src/app_keys.c
+++ b/nimble/host/mesh/src/app_keys.c
@@ -22,41 +22,184 @@
 #include "subnet.h"
 
 #define MESH_LOG_MODULE BLE_MESH_LOG
+
 #include "log/log.h"
 
-static struct bt_mesh_app_key apps[CONFIG_BT_MESH_APP_KEY_COUNT] = {
+/* Tracking of what storage changes are pending for App Keys. We track this in
+ * a separate array here instead of within the respective bt_mesh_app_key
+ * struct itselve, since once a key gets deleted its struct becomes invalid
+ * and may be reused for other keys.
+ */
+struct app_key_update {
+	uint16_t key_idx:12,    /* AppKey Index */
+		 valid:1,       /* 1 if this entry is valid, 0 if not */
+		 clear:1;       /* 1 if key needs clearing, 0 if storing */
+};
+
+/* AppKey information for persistent storage. */
+struct app_key_val {
+	uint16_t net_idx;
+	bool     updated;
+	uint8_t  val[2][16];
+} __packed;
+
+/** Mesh Application Key. */
+struct app_key {
+	uint16_t net_idx;
+	uint16_t app_idx;
+	bool     updated;
+	struct bt_mesh_app_cred {
+		uint8_t id;
+		uint8_t val[16];
+	} keys[2];
+};
+
+static struct app_key_update app_key_updates[CONFIG_BT_MESH_APP_KEY_COUNT];
+
+static struct app_key apps[CONFIG_BT_MESH_APP_KEY_COUNT] = {
 	[0 ... (CONFIG_BT_MESH_APP_KEY_COUNT - 1)] = {
 		.app_idx = BT_MESH_KEY_UNUSED,
 		.net_idx = BT_MESH_KEY_UNUSED,
 	}
 };
 
-static void app_key_evt(struct bt_mesh_app_key *app, enum bt_mesh_key_evt evt)
+static struct app_key *app_get(uint16_t app_idx)
 {
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		if (apps[i].app_idx == app_idx) {
+			return &apps[i];
+		}
+	}
+
+	return NULL;
+}
+
+static void clear_app_key(uint16_t app_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[20];
+	int err;
+
+	BT_DBG("AppKeyIndex 0x%03x", app_idx);
+
+	snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
+	} else {
+		BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
+	}
+#endif
+}
+
+static void store_app_key(uint16_t app_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	const struct app_key *app;
+	struct app_key_val key;
+	char path[20];
+	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
+	int err;
+	char *str;
+
+	snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx);
+
+	app = app_get(app_idx);
+	if (!app) {
+		BT_WARN("ApKeyIndex 0x%03x not found", app_idx);
+		return;
+	}
+
+	key.net_idx = app->net_idx,
+	key.updated = app->updated,
+
+	memcpy(key.val[0], app->keys[0].val, 16);
+	memcpy(key.val[1], app->keys[1].val, 16);
+	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store AppKey");
+	} else {
+		BT_DBG("Stored AppKey %s value");
+	}
+#endif
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct app_key_update *app_key_update_find(uint16_t key_idx,
+	struct app_key_update **free_slot)
+		{
+	struct app_key_update *match;
 	int i;
 
-	for (i = 0; i < (sizeof(bt_mesh_app_key_cb_list)/sizeof(void *)); i++) {
-		if (bt_mesh_app_key_cb_list[i]) {
-			BT_DBG("app_key_evt %d", i);
-			bt_mesh_app_key_cb_list[i] (app->app_idx, app->net_idx, evt);
+	match = NULL;
+	*free_slot = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
+		struct app_key_update *update = &app_key_updates[i];
+
+		if (!update->valid) {
+			*free_slot = update;
+			continue;
+		}
+
+		if (update->key_idx == key_idx) {
+			match = update;
 		}
 	}
+
+	return match;
 }
+#endif
 
-struct bt_mesh_app_key *app_get(uint16_t app_idx)
+static void update_app_key_settings(uint16_t app_idx, bool store)
 {
-	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
-		if (apps[i].app_idx == app_idx) {
-			return &apps[i];
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	struct app_key_update *update, *free_slot;
+	uint8_t clear = store ? 0U : 1U;
+
+	BT_DBG("AppKeyIndex 0x%03x", app_idx);
+
+	update = app_key_update_find(app_idx, &free_slot);
+	if (update) {
+		update->clear = clear;
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_APP_KEYS_PENDING);
+		return;
+	}
+
+	if (!free_slot) {
+		if (store) {
+			store_app_key(app_idx);
+		} else {
+			clear_app_key(app_idx);
 		}
+		return;
 	}
 
-	return NULL;
+	free_slot->valid = 1U;
+	free_slot->key_idx = app_idx;
+	free_slot->clear = clear;
+
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_APP_KEYS_PENDING);
+#endif
 }
 
-static struct bt_mesh_app_key *app_key_alloc(uint16_t app_idx)
+static void app_key_evt(struct app_key *app, enum bt_mesh_key_evt evt)
 {
-	struct bt_mesh_app_key *app = NULL;
+	int i;
+
+	for (i = 0; i < (sizeof(bt_mesh_app_key_cb_list)/sizeof(void *)); i++) {
+		if (bt_mesh_app_key_cb_list[i]) {
+			BT_DBG("app_key_evt %d", i);
+			bt_mesh_app_key_cb_list[i] (app->app_idx, app->net_idx, evt);
+		}
+	}
+}
+
+static struct app_key *app_key_alloc(uint16_t app_idx)
+{
+	struct app_key *app = NULL;
 
 	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
 		/* Check for already existing app_key */
@@ -72,12 +215,12 @@ static struct bt_mesh_app_key *app_key_alloc(uint16_t app_idx)
 	return app;
 }
 
-static void app_key_del(struct bt_mesh_app_key *app)
+static void app_key_del(struct app_key *app)
 {
 	BT_DBG("AppIdx 0x%03x", app->app_idx);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_clear_app_key(app->app_idx);
+		update_app_key_settings(app->app_idx, false);
 	}
 
 	app_key_evt(app, BT_MESH_KEY_DELETED);
@@ -87,7 +230,7 @@ static void app_key_del(struct bt_mesh_app_key *app)
 	(void)memset(app->keys, 0, sizeof(app->keys));
 }
 
-static void app_key_revoke(struct bt_mesh_app_key *app)
+static void app_key_revoke(struct app_key *app)
 {
 	if (!app->updated) {
 		return;
@@ -98,7 +241,7 @@ static void app_key_revoke(struct bt_mesh_app_key *app)
 	app->updated = false;
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_app_key(app->app_idx);
+		update_app_key_settings(app->app_idx, true);
 	}
 
 	app_key_evt(app, BT_MESH_KEY_REVOKED);
@@ -112,7 +255,7 @@ static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
 	}
 
 	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
-		struct bt_mesh_app_key *app = &apps[i];
+		struct app_key *app = &apps[i];
 
 		if (app->app_idx == BT_MESH_KEY_UNUSED) {
 			continue;
@@ -140,7 +283,7 @@ uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
 		bt_mesh_subnet_cb_list[0] = subnet_evt;
 	}
 
-	struct bt_mesh_app_key *app;
+	struct app_key *app;
 
 	BT_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx,
 	       bt_hex(key, 16));
@@ -156,7 +299,7 @@ uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
 
 	if (app->app_idx == app_idx) {
 		if (app->net_idx != net_idx) {
-			return STATUS_INVALID_BINDING;
+			return STATUS_INVALID_NETKEY;
 		}
 
 		if (memcmp(key, app->keys[0].val, 16)) {
@@ -179,7 +322,7 @@ uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		BT_DBG("Storing AppKey persistently");
-		bt_mesh_store_app_key(app->app_idx);
+		update_app_key_settings(app->app_idx, true);
 	}
 
 	app_key_evt(app, BT_MESH_KEY_ADDED);
@@ -187,22 +330,10 @@ uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
 	return STATUS_SUCCESS;
 }
 
-struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx)
-{
-	struct bt_mesh_app_key *app;
-
-	app = app_get(app_idx);
-	if (app) {
-		return app;
-	}
-
-	return NULL;
-}
-
 uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
 			       const uint8_t key[16])
 {
-	struct bt_mesh_app_key *app;
+	struct app_key *app;
 	struct bt_mesh_subnet *sub;
 
 	BT_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx,
@@ -250,7 +381,7 @@ uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		BT_DBG("Storing AppKey persistently");
-		bt_mesh_store_app_key(app->app_idx);
+		update_app_key_settings(app->app_idx, true);
 	}
 
 	app_key_evt(app, BT_MESH_KEY_UPDATED);
@@ -260,7 +391,7 @@ uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
 
 uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx)
 {
-	struct bt_mesh_app_key *app;
+	struct app_key *app;
 
 	BT_DBG("AppIdx 0x%03x", app_idx);
 
@@ -288,7 +419,7 @@ uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx)
 int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
 		    const uint8_t old_key[16], const uint8_t new_key[16])
 {
-	struct bt_mesh_app_key *app;
+	struct app_key *app;
 
 	app = app_key_alloc(app_idx);
 	if (!app) {
@@ -337,7 +468,7 @@ ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
 	size_t count = 0;
 
 	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
-		struct bt_mesh_app_key *app = &apps[i];
+		struct app_key *app = &apps[i];
 
 		if (app->app_idx == BT_MESH_KEY_UNUSED) {
 			continue;
@@ -364,9 +495,9 @@ ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
 
 int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
 			 struct bt_mesh_subnet **sub,
-			 const uint8_t *app_key[16], uint8_t *aid)
+			 const uint8_t **app_key, uint8_t *aid)
 {
-	struct bt_mesh_app_key *app = NULL;
+	struct app_key *app = NULL;
 
 	if (BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
 		/* With device keys, the application has to decide which subnet
@@ -379,7 +510,7 @@ int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
 		}
 
 		if (ctx->app_idx == BT_MESH_KEY_DEV_REMOTE &&
-		    !bt_mesh_elem_find(ctx->addr)) {
+		    !bt_mesh_has_addr(ctx->addr)) {
 			struct bt_mesh_cdb_node *node;
 
 			if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
@@ -462,7 +593,7 @@ uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
 	}
 
 	for (i = 0; i < ARRAY_SIZE(apps); i++) {
-		const struct bt_mesh_app_key *app = &apps[i];
+		const struct app_key *app = &apps[i];
 		const struct bt_mesh_app_cred *cred;
 
 		if (app->app_idx == BT_MESH_KEY_UNUSED) {
@@ -498,10 +629,89 @@ uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
 void bt_mesh_app_keys_reset(void)
 {
 	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
-		struct bt_mesh_app_key *app = &apps[i];
+		struct app_key *app = &apps[i];
 
 		if (app->app_idx != BT_MESH_KEY_UNUSED) {
 			app_key_del(app);
 		}
 	}
 }
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int app_key_set(int argc, char **argv, char *val)
+{
+	struct app_key_val key;
+	uint16_t app_idx;
+	int len_rd, err;
+
+	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+	app_idx = strtol(argv[0], NULL, 16);
+	len_rd = strtol(argv[1], NULL, 16);
+
+
+	if (!len_rd) {
+		return 0;
+	}
+
+	err = settings_bytes_from_str(val, &key, &len_rd);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0],
+				  key.updated ? key.val[1] : NULL);
+	if (err) {
+		BT_ERR("Failed to set \'app-key\'");
+		return err;
+	}
+
+	BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
+
+	return 0;
+}
+#endif
+
+void bt_mesh_app_key_pending_store(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
+		struct app_key_update *update = &app_key_updates[i];
+
+		if (!update->valid) {
+			continue;
+		}
+
+		if (update->clear) {
+			clear_app_key(update->key_idx);
+		} else {
+			store_app_key(update->key_idx);
+		}
+
+		update->valid = 0U;
+	}
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct conf_handler bt_mesh_app_key_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = app_key_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_app_key_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_app_key_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_app_key conf");
+#endif
+}
diff --git a/nimble/host/mesh/src/app_keys.h b/nimble/host/mesh/src/app_keys.h
index d007b785f6..40df664506 100644
--- a/nimble/host/mesh/src/app_keys.h
+++ b/nimble/host/mesh/src/app_keys.h
@@ -10,28 +10,9 @@
 #include "mesh/mesh.h"
 #include "subnet.h"
 
-/** Mesh Application. */
-struct bt_mesh_app_key {
-	uint16_t net_idx;
-	uint16_t app_idx;
-	bool updated;
-	struct bt_mesh_app_cred {
-		uint8_t id;
-		uint8_t val[16];
-	} keys[2];
-};
-
 /** @brief Reset the app keys module. */
 void bt_mesh_app_keys_reset(void);
 
-/** @brief Get the application key with the given AppIdx.
- *
- *  @param app_idx App index.
- *
- *  @return The matching application, or NULL if the application isn't known.
- */
-struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx);
-
 /** @brief Initialize a new application key with the given parameters.
  *
  *  @param app_idx AppIndex.
@@ -60,7 +41,7 @@ int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
  */
 int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
 			 struct bt_mesh_subnet **sub,
-			 const uint8_t *app_key[16], uint8_t *aid);
+			 const uint8_t **app_key, uint8_t *aid);
 
 /** @brief Iterate through all matching application keys and call @c cb on each.
  *
@@ -78,9 +59,10 @@ uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
 					const uint8_t key[16], void *cb_data),
 			      void *cb_data);
 
-struct bt_mesh_app_key *app_get(uint16_t app_idx);
-
 extern void (*bt_mesh_app_key_cb_list[1]) (uint16_t app_idx, uint16_t net_idx,
 			enum bt_mesh_key_evt evt);
 
-#endif /* _BT_MESH_APP_KEYS_H_ */
\ No newline at end of file
+/** @brief Store pending application keys in persistent storage. */
+void bt_mesh_app_key_pending_store(void);
+void bt_mesh_app_key_init(void);
+#endif /* _BT_MESH_APP_KEYS_H_ */
diff --git a/nimble/host/mesh/src/beacon.c b/nimble/host/mesh/src/beacon.c
index 6a5fc5a1cd..2b678f9008 100644
--- a/nimble/host/mesh/src/beacon.c
+++ b/nimble/host/mesh/src/beacon.c
@@ -34,7 +34,7 @@
 /* 1 transmission, 20ms interval */
 #define PROV_XMIT                  BT_MESH_TRANSMIT(0, 20)
 
-static struct k_delayed_work beacon_timer;
+static struct k_work_delayable beacon_timer;
 
 static int cache_check(struct bt_mesh_subnet *sub, void *beacon_data)
 {
@@ -228,33 +228,30 @@ static void update_beacon_observation(void)
 
 static void beacon_send(struct ble_npl_event *work)
 {
-	/* Don't send anything if we have an active provisioning link */
-	if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) && bt_mesh_prov_active()) {
-		k_delayed_work_submit(&beacon_timer,
-							  K_SECONDS(MYNEWT_VAL(BLE_MESH_UNPROV_BEACON_INT)));
-		return;
-	}
 
 	BT_DBG("");
 
 	if (bt_mesh_is_provisioned()) {
+		if (!bt_mesh_beacon_enabled() &&
+		!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
+			return;
+		}
+
 		update_beacon_observation();
 		(void)bt_mesh_subnet_find(secure_beacon_send, NULL);
 
-		/* Only resubmit if beaconing is still enabled */
-		if (bt_mesh_beacon_enabled() ||
-		    atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
-			k_delayed_work_submit(&beacon_timer,
-					      PROVISIONED_INTERVAL);
-		}
+		k_work_schedule(&beacon_timer, PROVISIONED_INTERVAL);
 
 		return;
 	}
 
 	if (IS_ENABLED(BLE_MESH_PB_ADV)) {
-		unprovisioned_beacon_send();
-		k_delayed_work_submit(&beacon_timer,
-							  K_SECONDS(MYNEWT_VAL(BLE_MESH_UNPROV_BEACON_INT)));
+		/* Don't send anything if we have an active provisioning link */
+		if (!bt_mesh_prov_active()) {
+			unprovisioned_beacon_send();
+		}
+
+		k_work_schedule(&beacon_timer, K_SECONDS(MYNEWT_VAL(BLE_MESH_UNPROV_BEACON_INT)));
 	}
 }
 
@@ -426,22 +423,27 @@ static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
 
 void bt_mesh_beacon_init(void)
 {
-		if (!bt_mesh_subnet_cb_list[1]) {
+	if (!bt_mesh_subnet_cb_list[1]) {
 		bt_mesh_subnet_cb_list[1] = subnet_evt;
 	}
 
-	k_delayed_work_init(&beacon_timer, beacon_send);
+	k_work_init_delayable(&beacon_timer, beacon_send);
 }
 
 void bt_mesh_beacon_ivu_initiator(bool enable)
 {
 	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_INITIATOR, enable);
 
-	if (enable) {
-		k_delayed_work_submit(&beacon_timer, K_NO_WAIT);
-	} else if (!bt_mesh_beacon_enabled()) {
-		k_delayed_work_cancel(&beacon_timer);
-	}
+	/* Fire the beacon handler straight away if it's not already pending -
+	 * in which case we'll fire according to the ongoing periodic sending.
+	 * If beacons are disabled, the handler will exit early.
+	 *
+	 * An alternative solution would be to check whether beacons are enabled
+	 * here, and cancel if not. As the cancel operation may fail, we would
+	 * still have to implement an early exit mechanism, so we might as well
+	 * just use this every time.
+	 */
+	k_work_schedule(&beacon_timer, K_NO_WAIT);
 }
 
 static void subnet_beacon_enable(struct bt_mesh_subnet *sub)
@@ -454,19 +456,17 @@ static void subnet_beacon_enable(struct bt_mesh_subnet *sub)
 
 void bt_mesh_beacon_enable(void)
 {
-	if (!bt_mesh_is_provisioned()) {
-		k_delayed_work_submit(&beacon_timer, K_NO_WAIT);
-		return;
+	if (bt_mesh_is_provisioned()) {
+		bt_mesh_subnet_foreach(subnet_beacon_enable);
 	}
 
-	bt_mesh_subnet_foreach(subnet_beacon_enable);
-
-	k_delayed_work_submit(&beacon_timer, K_NO_WAIT);
+	k_work_reschedule(&beacon_timer, K_NO_WAIT);
 }
 
 void bt_mesh_beacon_disable(void)
 {
 	if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
-		k_delayed_work_cancel(&beacon_timer);
+		/* If this fails, we'll do an early exit in the work handler. */
+		(void)k_work_cancel_delayable(&beacon_timer);
 	}
 }
diff --git a/nimble/host/mesh/src/cdb.c b/nimble/host/mesh/src/cdb.c
index 459730ee76..98084ee3d0 100644
--- a/nimble/host/mesh/src/cdb.c
+++ b/nimble/host/mesh/src/cdb.c
@@ -4,11 +4,11 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-#define BT_DBG_ENABLED MYNEWT_VAL(BLE_MESH_DEBUG_CDB)
-#define LOG_MODULE_NAME bt_mesh_cdb
+#define MESH_LOG_MODULE BLE_MESH_LOG
 #include "log/log.h"
+#include <stdlib.h>
 
-#include "mesh/mesh.h"
+#include "cdb_priv.h"
 #include "net.h"
 #include "rpl.h"
 #include "settings.h"
@@ -16,6 +16,58 @@
 #include "mesh/glue.h"
 
 #if MYNEWT_VAL(BLE_MESH_CDB)
+/* Tracking of what storage changes are pending for App and Net Keys. We
+ * track this in a separate array here instead of within the respective
+ * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key
+ * gets deleted its struct becomes invalid and may be reused for other keys.
+ */
+struct key_update {
+	uint16_t key_idx:12,    /* AppKey or NetKey Index */
+		 valid:1,       /* 1 if this entry is valid, 0 if not */
+		 app_key:1,     /* 1 if this is an AppKey, 0 if a NetKey */
+		 clear:1;       /* 1 if key needs clearing, 0 if storing */
+};
+
+/* Tracking of what storage changes are pending for node settings. */
+struct node_update {
+	uint16_t addr;
+	bool clear;
+};
+
+/* Node information for persistent storage. */
+struct node_val {
+	uint16_t net_idx;
+	uint8_t  num_elem;
+	uint8_t  flags;
+#define F_NODE_CONFIGURED 0x01
+	uint8_t  uuid[16];
+	uint8_t  dev_key[16];
+} __packed;
+
+/* NetKey storage information */
+struct net_key_val {
+	uint8_t  kr_flag:1,
+	kr_phase:7;
+	uint8_t  val[2][16];
+} __packed;
+
+/* AppKey information for persistent storage. */
+struct app_key_val {
+	uint16_t net_idx;
+	bool     updated;
+	uint8_t  val[2][16];
+} __packed;
+
+/* IV Index & IV Update information for persistent storage. */
+struct net_val {
+	uint32_t iv_index;
+	bool  iv_update;
+} __packed;
+
+static struct node_update cdb_node_updates[MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT)];
+static struct key_update cdb_key_updates[MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) +
+					 MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT)];
+
 struct bt_mesh_cdb bt_mesh_cdb = {
 	.nodes = {
 		[0 ... (CONFIG_BT_MESH_NODE_COUNT - 1)] = {
@@ -107,6 +159,544 @@ static uint16_t find_lowest_free_addr(uint8_t num_elem)
 	return addr;
 }
 
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int cdb_net_set(int argc, char *val)
+{
+	struct net_val net;
+	int len, err;
+
+	len = sizeof(net);
+	err = settings_bytes_from_str(val, &net, &len);
+	if (err) {
+		BT_ERR("Failed to set \'cdb_net\'");
+		return err;
+	}
+
+	bt_mesh_cdb.iv_index = net.iv_index;
+
+	if (net.iv_update) {
+		atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS);
+	}
+
+	atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID);
+
+	return 0;
+}
+
+static int cdb_node_set(int argc, char *str)
+{
+	struct bt_mesh_cdb_node *node;
+	struct node_val val;
+	uint16_t addr;
+	int len, err;
+
+	if (argc < 1) {
+		BT_ERR("Insufficient number of arguments");
+		return -ENOENT;
+	}
+
+	addr = strtol(str, NULL, 16);
+	len = sizeof(str);
+
+	if (argc < 1) {
+		BT_DBG("val (null)");
+		BT_DBG("Deleting node 0x%04x", addr);
+
+		node = bt_mesh_cdb_node_get(addr);
+		if (node) {
+			bt_mesh_cdb_node_del(node, false);
+		}
+
+		return 0;
+	}
+
+	err = settings_bytes_from_str(str, &val, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return -EINVAL;
+	}
+
+	if (len != sizeof(struct node_val)) {
+		BT_ERR("Invalid length for node_val");
+		return -EINVAL;
+	}
+
+	node = bt_mesh_cdb_node_get(addr);
+	if (!node) {
+		node = bt_mesh_cdb_node_alloc(val.uuid, addr, val.num_elem,
+					      			  val.net_idx);
+	}
+
+	if (!node) {
+		BT_ERR("No space for a new node");
+		return -ENOMEM;
+	}
+
+	if (val.flags & F_NODE_CONFIGURED) {
+		atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);
+	}
+
+	memcpy(node->uuid, val.uuid, 16);
+	memcpy(node->dev_key, val.dev_key, 16);
+
+	BT_DBG("Node 0x%04x recovered from storage", addr);
+
+	return 0;
+}
+
+static int cdb_subnet_set(int argc, char *name)
+{
+	struct bt_mesh_cdb_subnet *sub;
+	struct net_key_val key;
+	uint16_t net_idx;
+	int len, len_rd, err;
+
+	if (!name) {
+		BT_ERR("Insufficient number of arguments");
+		return -ENOENT;
+	}
+
+	len_rd = sizeof(sub);
+	net_idx = strtol(name, NULL, 16);
+	sub = bt_mesh_cdb_subnet_get(net_idx);
+
+	if (len_rd == 0) {
+		BT_DBG("val (null)");
+		if (!sub) {
+			BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
+			return -ENOENT;
+		}
+
+		BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
+		bt_mesh_cdb_subnet_del(sub, false);
+		return 0;
+	}
+
+	len = sizeof(key);
+	err = settings_bytes_from_str(name, &key, &len);
+	if (err) {
+		BT_ERR("Failed to set \'net-key\'");
+		return err;
+	}
+
+	if (sub) {
+		BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx);
+
+		sub->kr_phase = key.kr_phase;
+		memcpy(sub->keys[0].net_key, &key.val[0], 16);
+		memcpy(sub->keys[1].net_key, &key.val[1], 16);
+
+		return 0;
+	}
+
+	sub = bt_mesh_cdb_subnet_alloc(net_idx);
+	if (!sub) {
+		BT_ERR("No space to allocate a new subnet");
+		return -ENOMEM;
+	}
+
+	sub->kr_phase = key.kr_phase;
+	memcpy(sub->keys[0].net_key, &key.val[0], 16);
+	memcpy(sub->keys[1].net_key, &key.val[1], 16);
+
+	BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
+
+	return 0;
+}
+
+static int cdb_app_key_set(int argc, char *name)
+{
+	struct bt_mesh_cdb_app_key *app;
+	struct app_key_val key;
+	uint16_t app_idx;
+	int len_rd, err;
+
+	app_idx = strtol(name, NULL, 16);
+	len_rd = sizeof(key);
+
+	if (len_rd == 0) {
+		BT_DBG("val (null)");
+		BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
+
+		app = bt_mesh_cdb_app_key_get(app_idx);
+		if (app) {
+			bt_mesh_cdb_app_key_del(app, false);
+		}
+
+		return 0;
+	}
+
+	err = settings_bytes_from_str(name, &key, &len_rd);
+	if (err) {
+		BT_ERR("Failed to set \'app-key\'");
+		return err;
+	}
+
+	app = bt_mesh_cdb_app_key_get(app_idx);
+	if (!app) {
+		app = bt_mesh_cdb_app_key_alloc(key.net_idx, app_idx);
+	}
+
+	if (!app) {
+		BT_ERR("No space for a new app key");
+		return -ENOMEM;
+	}
+
+	memcpy(app->keys[0].app_key, key.val[0], 16);
+	memcpy(app->keys[1].app_key, key.val[1], 16);
+
+	BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
+
+	return 0;
+}
+
+static int cdb_set(int argc, char **argv, char *name)
+{
+	int len;
+	char *next;
+
+	if (argc < 1) {
+		BT_ERR("Insufficient number of arguments");
+		return -ENOENT;
+	}
+
+	if (!strcmp(name, "Net")) {
+		return cdb_net_set(1, name);
+	}
+
+	len = settings_name_next(name, &next);
+
+	if (!next) {
+		BT_ERR("Insufficient number of arguments");
+		return -ENOENT;
+	}
+
+	if (!strncmp(name, "Node", len)) {
+		return cdb_node_set(1, next);
+	}
+
+	if (!strncmp(name, "Subnet", len)) {
+		return cdb_subnet_set(1, next);
+	}
+
+	if (!strncmp(name, "AppKey", len)) {
+		return cdb_app_key_set(1, next);
+	}
+
+	BT_WARN("Unknown module key %s", name);
+	return -ENOENT;
+}
+#endif
+
+static void store_cdb_node(const struct bt_mesh_cdb_node *node)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))];
+	struct node_val val;
+	char path[30];
+	char *str;
+	int err;
+
+	val.net_idx = node->net_idx;
+	val.num_elem = node->num_elem;
+	val.flags = 0;
+
+	if (atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) {
+		val.flags |= F_NODE_CONFIGURED;
+	}
+
+	memcpy(val.uuid, node->uuid, 16);
+	memcpy(val.dev_key, node->dev_key, 16);
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/Node/%x", node->addr);
+
+	str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode Node as value");
+		return;
+	}
+
+
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store Node %s value", path);
+	} else {
+		BT_DBG("Stored Node %s value", path);
+	}
+#endif
+}
+
+static void clear_cdb_node(uint16_t addr)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[30];
+	int err;
+
+	BT_DBG("Node 0x%04x", addr);
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/Node/%x", addr);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear Node 0x%04x", addr);
+	} else {
+		BT_DBG("Cleared Node 0x%04x", addr);
+	}
+#endif
+}
+
+static void store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
+	struct net_key_val key;
+	char path[30];
+	int err;
+	char *str;
+
+	BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx,
+	       bt_hex(sub->keys[0].net_key, 16));
+
+	memcpy(&key.val[0], sub->keys[0].net_key, 16);
+	memcpy(&key.val[1], sub->keys[1].net_key, 16);
+	key.kr_flag = 0U; /* Deprecated */
+	key.kr_phase = sub->kr_phase;
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/Subnet/%x", sub->net_idx);
+
+
+	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode Subnet as value");
+		return;
+	}
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store Subnet value");
+	} else {
+		BT_DBG("Stored Subnet value");
+	}
+#endif
+}
+
+static void clear_cdb_subnet(uint16_t net_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[30];
+	int err;
+
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/Subnet/%x", net_idx);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx);
+	} else {
+		BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx);
+	}
+#endif
+}
+
+static void store_cdb_app_key(const struct bt_mesh_cdb_app_key *app)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
+	struct app_key_val key;
+	char path[30];
+	int err;
+	char *str;
+
+	key.net_idx = app->net_idx;
+	key.updated = false;
+	memcpy(key.val[0], app->keys[0].app_key, 16);
+	memcpy(key.val[1], app->keys[1].app_key, 16);
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/AppKey/%x", app->app_idx);
+
+	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store AppKey");
+	} else {
+		BT_DBG("Stored AppKey");
+	}
+#endif
+}
+
+static void clear_cdb_app_key(uint16_t app_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[30];
+	int err;
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/AppKey/%x", app_idx);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
+	} else {
+		BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
+	}
+#endif
+}
+
+static void schedule_cdb_store(int flag)
+{
+	atomic_set_bit(bt_mesh_cdb.flags, flag);
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CDB_PENDING);
+}
+
+static void update_cdb_net_settings(void)
+{
+	schedule_cdb_store(BT_MESH_CDB_SUBNET_PENDING);
+}
+
+static struct node_update *cdb_node_update_find(uint16_t addr,
+						struct node_update **free_slot)
+{
+	struct node_update *match;
+	int i;
+
+	match = NULL;
+	*free_slot = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(cdb_node_updates); i++) {
+		struct node_update *update = &cdb_node_updates[i];
+
+		if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
+			*free_slot = update;
+			continue;
+		}
+
+		if (update->addr == addr) {
+			match = update;
+		}
+	}
+
+	return match;
+}
+
+static void update_cdb_node_settings(const struct bt_mesh_cdb_node *node,
+				     bool store)
+{
+	struct node_update *update, *free_slot;
+
+	BT_DBG("Node 0x%04x", node->addr);
+
+	update = cdb_node_update_find(node->addr, &free_slot);
+	if (update) {
+		update->clear = !store;
+		schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
+		return;
+	}
+
+	if (!free_slot) {
+		if (store) {
+			store_cdb_node(node);
+		} else {
+			clear_cdb_node(node->addr);
+		}
+		return;
+	}
+
+	free_slot->addr = node->addr;
+	free_slot->clear = !store;
+
+	schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
+}
+
+static struct key_update *cdb_key_update_find(bool app_key, uint16_t key_idx,
+					      struct key_update **free_slot)
+{
+	struct key_update *match;
+	int i;
+
+	match = NULL;
+	*free_slot = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
+		struct key_update *update = &cdb_key_updates[i];
+
+		if (!update->valid) {
+			*free_slot = update;
+			continue;
+		}
+
+		if (update->app_key != app_key) {
+			continue;
+		}
+
+		if (update->key_idx == key_idx) {
+			match = update;
+		}
+	}
+
+	return match;
+}
+
+static void update_cdb_subnet_settings(const struct bt_mesh_cdb_subnet *sub,
+				       bool store)
+{
+	struct key_update *update, *free_slot;
+	uint8_t clear = store ? 0U : 1U;
+
+	BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
+
+	update = cdb_key_update_find(false, sub->net_idx, &free_slot);
+	if (update) {
+		update->clear = clear;
+		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
+		return;
+	}
+
+	if (!free_slot) {
+		if (store) {
+			store_cdb_subnet(sub);
+		} else {
+			clear_cdb_subnet(sub->net_idx);
+		}
+		return;
+	}
+
+	free_slot->valid = 1U;
+	free_slot->key_idx = sub->net_idx;
+	free_slot->app_key = 0U;
+	free_slot->clear = clear;
+
+	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
+}
+
+static void update_cdb_app_key_settings(const struct bt_mesh_cdb_app_key *key,
+					bool store)
+{
+	struct key_update *update, *free_slot;
+	uint8_t clear = store ? 0U : 1U;
+
+	BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
+
+	update = cdb_key_update_find(true, key->app_idx, &free_slot);
+	if (update) {
+		update->clear = clear;
+		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
+		return;
+	}
+
+	if (!free_slot) {
+		if (store) {
+			store_cdb_app_key(key);
+		} else {
+			clear_cdb_app_key(key->app_idx);
+		}
+
+		return;
+	}
+
+	free_slot->valid = 1U;
+	free_slot->key_idx = key->app_idx;
+	free_slot->app_key = 1U;
+	free_slot->clear = clear;
+
+	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
+}
+
 int bt_mesh_cdb_create(const uint8_t key[16])
 {
 	struct bt_mesh_cdb_subnet *sub;
@@ -125,8 +715,8 @@ int bt_mesh_cdb_create(const uint8_t key[16])
 	bt_mesh_cdb.iv_index = 0;
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_cdb();
-		bt_mesh_store_cdb_subnet(sub);
+		update_cdb_net_settings();
+		update_cdb_subnet_settings(sub, true);
 	}
 
 	return 0;
@@ -157,7 +747,7 @@ void bt_mesh_cdb_clear(void)
 	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_cdb();
+		update_cdb_net_settings();
 	}
 }
 
@@ -171,7 +761,7 @@ void bt_mesh_cdb_iv_update(uint32_t iv_index, bool iv_update)
 			  iv_update);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_cdb();
+		update_cdb_net_settings();
 	}
 }
 
@@ -204,7 +794,7 @@ void bt_mesh_cdb_subnet_del(struct bt_mesh_cdb_subnet *sub, bool store)
 	BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_clear_cdb_subnet(sub);
+		update_cdb_subnet_settings(sub, false);
 	}
 
 	sub->net_idx = BT_MESH_KEY_UNUSED;
@@ -226,8 +816,8 @@ struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_get(uint16_t net_idx)
 
 void bt_mesh_cdb_subnet_store(const struct bt_mesh_cdb_subnet *sub)
 {
-	if (MYNEWT_VAL(BLE_MESH_SETTINGS)) {
-		bt_mesh_store_cdb_subnet(sub);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		update_cdb_subnet_settings(sub, true);
 	}
 }
 
@@ -235,7 +825,7 @@ uint8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub)
 {
 	uint8_t flags = 0x00;
 
-	if (sub && sub->kr_flag) {
+	if (sub && SUBNET_KEY_TX_IDX(sub)) {
 		flags |= BT_MESH_NET_FLAG_KR;
 	}
 
@@ -283,7 +873,7 @@ void bt_mesh_cdb_node_del(struct bt_mesh_cdb_node *node, bool store)
 	BT_DBG("Node addr 0x%04x store %u", node->addr, store);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_clear_cdb_node(node);
+		update_cdb_node_settings(node, false);
 	}
 
 	node->addr = BT_MESH_ADDR_UNASSIGNED;
@@ -308,8 +898,8 @@ struct bt_mesh_cdb_node *bt_mesh_cdb_node_get(uint16_t addr)
 
 void bt_mesh_cdb_node_store(const struct bt_mesh_cdb_node *node)
 {
-	if (MYNEWT_VAL(BLE_MESH_SETTINGS)) {
-		bt_mesh_store_cdb_node(node);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		update_cdb_node_settings(node, true);
 	}
 }
 
@@ -356,10 +946,10 @@ void bt_mesh_cdb_app_key_del(struct bt_mesh_cdb_app_key *key, bool store)
 	BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_clear_cdb_app_key(key);
+		update_cdb_app_key_settings(key, false);
 	}
 
-	key->net_idx = BT_MESH_ADDR_UNASSIGNED;
+	key->net_idx = BT_MESH_KEY_UNUSED;
 	memset(key->keys, 0, sizeof(key->keys));
 }
 
@@ -381,8 +971,172 @@ struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(uint16_t app_idx)
 
 void bt_mesh_cdb_app_key_store(const struct bt_mesh_cdb_app_key *key)
 {
-	if (MYNEWT_VAL(BLE_MESH_SETTINGS)) {
-		bt_mesh_store_cdb_app_key(key);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		update_cdb_app_key_settings(key, true);
+	}
+}
+
+static void clear_cdb_net(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[30];
+	int err;
+
+	snprintk(path, sizeof(path), "bt_mesh/cdb/Net");
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear Net");
+	} else {
+		BT_DBG("Cleared NetKeyIndex 0x%03x");
 	}
+#endif
+}
+
+static void store_cdb_pending_net(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	struct net_val net;
+	int err;
+	char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))];
+	char *str;
+
+	BT_DBG("");
+
+	net.iv_index = bt_mesh_cdb.iv_index;
+	net.iv_update = atomic_test_bit(bt_mesh_cdb.flags,
+					BT_MESH_CDB_IVU_IN_PROGRESS);
+
+	str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode Network as value");
+		return;
+	}
+	err = settings_save_one("bt_mesh/cdb/Net", str);
+	if (err) {
+		BT_ERR("Failed to store Network value");
+	} else {
+		BT_DBG("Stored Network value");
+	}
+#endif
+}
+
+static void store_cdb_pending_nodes(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cdb_node_updates); ++i) {
+		struct node_update *update = &cdb_node_updates[i];
+
+		if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
+			continue;
+		}
+
+		BT_DBG("addr: 0x%04x, clear: %d", update->addr, update->clear);
+
+		if (update->clear) {
+			clear_cdb_node(update->addr);
+		} else {
+			struct bt_mesh_cdb_node *node;
+
+			node = bt_mesh_cdb_node_get(update->addr);
+			if (node) {
+				store_cdb_node(node);
+			} else {
+				BT_WARN("Node 0x%04x not found", update->addr);
+			}
+		}
+
+		update->addr = BT_MESH_ADDR_UNASSIGNED;
+	}
+}
+
+static void store_cdb_pending_keys(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
+		struct key_update *update = &cdb_key_updates[i];
+
+		if (!update->valid) {
+			continue;
+		}
+
+		if (update->clear) {
+			if (update->app_key) {
+				clear_cdb_app_key(update->key_idx);
+			} else {
+				clear_cdb_subnet(update->key_idx);
+			}
+		} else {
+			if (update->app_key) {
+				struct bt_mesh_cdb_app_key *key;
+
+				key = bt_mesh_cdb_app_key_get(update->key_idx);
+				if (key) {
+					store_cdb_app_key(key);
+				} else {
+					BT_WARN("AppKeyIndex 0x%03x not found",
+						update->key_idx);
+				}
+			} else {
+				struct bt_mesh_cdb_subnet *sub;
+
+				sub = bt_mesh_cdb_subnet_get(update->key_idx);
+				if (sub) {
+					store_cdb_subnet(sub);
+				} else {
+					BT_WARN("NetKeyIndex 0x%03x not found",
+						update->key_idx);
+				}
+			}
+		}
+
+		update->valid = 0U;
+	}
+}
+
+void bt_mesh_cdb_pending_store(void)
+{
+	if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
+				      BT_MESH_CDB_SUBNET_PENDING)) {
+		if (atomic_test_bit(bt_mesh_cdb.flags,
+				    BT_MESH_CDB_VALID)) {
+			store_cdb_pending_net();
+		} else {
+			clear_cdb_net();
+		}
+	}
+
+	if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
+				      BT_MESH_CDB_NODES_PENDING)) {
+		store_cdb_pending_nodes();
+	}
+
+	if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
+				      BT_MESH_CDB_KEYS_PENDING)) {
+		store_cdb_pending_keys();
+	}
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct conf_handler bt_mesh_cdb_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = cdb_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_cdb_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_cdb_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_net conf");
+#endif
 }
 #endif
diff --git a/nimble/host/mesh/src/cdb_priv.h b/nimble/host/mesh/src/cdb_priv.h
new file mode 100644
index 0000000000..ff865a56fd
--- /dev/null
+++ b/nimble/host/mesh/src/cdb_priv.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* ommitted `bt_mesh_cdb_node_store` declaration - every header
+ * includes mesh/mesh.h, which already has it
+ * void bt_mesh_cdb_node_store(const struct bt_mesh_cdb_node *node);
+ */
+void bt_mesh_cdb_pending_store(void);
+void bt_mesh_cdb_init(void);
diff --git a/nimble/host/mesh/src/cfg.c b/nimble/host/mesh/src/cfg.c
index 4e14925506..282aefaf43 100644
--- a/nimble/host/mesh/src/cfg.c
+++ b/nimble/host/mesh/src/cfg.c
@@ -15,6 +15,20 @@
 #include "cfg.h"
 #include "mesh/glue.h"
 
+#define MESH_LOG_MODULE BLE_MESH_LOG
+#include "log/log.h"
+
+/* Miscellaneous configuration server model states */
+struct cfg_val {
+	uint8_t net_transmit;
+	uint8_t relay;
+	uint8_t relay_retransmit;
+	uint8_t beacon;
+	uint8_t gatt_proxy;
+	uint8_t frnd;
+	uint8_t default_ttl;
+};
+
 void bt_mesh_beacon_set(bool beacon)
 {
 	if (atomic_test_bit(bt_mesh.flags, BT_MESH_BEACON) == beacon) {
@@ -31,7 +45,7 @@ void bt_mesh_beacon_set(bool beacon)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 }
 
@@ -82,7 +96,7 @@ int bt_mesh_gatt_proxy_set(enum bt_mesh_feat_state gatt_proxy)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 
 	return 0;
@@ -111,7 +125,7 @@ int bt_mesh_default_ttl_set(uint8_t default_ttl)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 
 	return 0;
@@ -139,7 +153,7 @@ int bt_mesh_friend_set(enum bt_mesh_feat_state friendship)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 
 	if (friendship == BT_MESH_FEATURE_DISABLED) {
@@ -168,7 +182,7 @@ void bt_mesh_net_transmit_set(uint8_t xmit)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 }
 
@@ -199,7 +213,7 @@ int bt_mesh_relay_set(enum bt_mesh_feat_state relay, uint8_t xmit)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 	    atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-		bt_mesh_store_cfg();
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
 	}
 
 	return 0;
@@ -236,8 +250,115 @@ bool bt_mesh_fixed_group_match(uint16_t addr)
 	}
 }
 
-void bt_mesh_cfg_init(void)
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int cfg_set(int argc, char **argv, char *val)
+{
+	struct cfg_val cfg;
+	int len, err;
+
+	BT_DBG("val %s", val ? val : "(null)");
+
+	if (!val) {
+		BT_DBG("Cleared configuration state");
+		return 0;
+	}
+
+	len = sizeof(cfg);
+	err = settings_bytes_from_str(val, &cfg, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(cfg)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len,
+					       sizeof(cfg));
+		return -EINVAL;
+	}
+
+	bt_mesh_net_transmit_set(cfg.net_transmit);
+	bt_mesh_relay_set(cfg.relay, cfg.relay_retransmit);
+	bt_mesh_beacon_set(cfg.beacon);
+	bt_mesh_gatt_proxy_set(cfg.gatt_proxy);
+	bt_mesh_friend_set(cfg.frnd);
+	bt_mesh_default_ttl_set(cfg.default_ttl);
+
+	BT_DBG("Restored configuration state");
+
+	return 0;
+}
+
+static void clear_cfg(void)
+{
+	int err;
+
+	err = settings_save_one("bt_mesh/Cfg", NULL);
+	if (err) {
+		BT_ERR("Failed to clear configuration");
+	} else {
+		BT_DBG("Cleared configuration");
+	}
+}
+
+static void store_pending_cfg(void)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))];
+	struct cfg_val val;
+	char *str;
+	int err;
+
+	val.net_transmit = bt_mesh_net_transmit_get();
+	val.relay = bt_mesh_relay_get();
+	val.relay_retransmit = bt_mesh_relay_retransmit_get();
+	val.beacon = bt_mesh_beacon_enabled();
+	val.gatt_proxy = bt_mesh_gatt_proxy_get();
+	val.frnd = bt_mesh_friend_get();
+	val.default_ttl = bt_mesh_default_ttl_get();
+
+	str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode configuration as value");
+		return;
+	}
+
+	BT_DBG("Saving configuration as value %s", str);
+	err = settings_save_one("bt_mesh/Cfg", str);
+	if (err) {
+		BT_ERR("Failed to store configuration");
+	} else {
+		BT_DBG("Stored configuration");
+	}
+}
+
+void bt_mesh_cfg_pending_store(void)
 {
+	if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+		store_pending_cfg();
+	} else {
+		clear_cfg();
+	}
+}
+
+static struct conf_handler bt_mesh_cfg_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = cfg_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_cfg_default_set(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_cfg_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_settings conf");
+#endif
+
 	bt_mesh.default_ttl = CONFIG_BT_MESH_DEFAULT_TTL;
 	bt_mesh.net_xmit =
 		BT_MESH_TRANSMIT(CONFIG_BT_MESH_NETWORK_TRANSMIT_COUNT,
@@ -264,4 +385,4 @@ void bt_mesh_cfg_init(void)
 	if (CONFIG_BT_MESH_FRIEND_ENABLED) {
 		atomic_set_bit(bt_mesh.flags, BT_MESH_FRIEND);
 	}
-}
\ No newline at end of file
+}
diff --git a/nimble/host/mesh/src/cfg.h b/nimble/host/mesh/src/cfg.h
index 6f58acf8b0..4901655f57 100644
--- a/nimble/host/mesh/src/cfg.h
+++ b/nimble/host/mesh/src/cfg.h
@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-void bt_mesh_cfg_init(void);
+void bt_mesh_cfg_default_set(void);
+void bt_mesh_cfg_pending_store(void);
 
-bool bt_mesh_fixed_group_match(uint16_t addr);
\ No newline at end of file
+bool bt_mesh_fixed_group_match(uint16_t addr);
diff --git a/nimble/host/mesh/src/cfg_cli.c b/nimble/host/mesh/src/cfg_cli.c
index a87cd7bb9f..773bafa709 100644
--- a/nimble/host/mesh/src/cfg_cli.c
+++ b/nimble/host/mesh/src/cfg_cli.c
@@ -25,7 +25,7 @@
 #define DUMMY_2_BYTE_OP	BT_MESH_MODEL_OP_2(0xff, 0xff)
 
 struct comp_data {
-	uint8_t *status;
+	uint8_t *page;
 	struct os_mbuf *comp;
 };
 
@@ -33,9 +33,9 @@ static int32_t msg_timeout = K_SECONDS(5);
 
 static struct bt_mesh_cfg_cli *cli;
 
-static void comp_data_status(struct bt_mesh_model *model,
-			     struct bt_mesh_msg_ctx *ctx,
-			     struct os_mbuf *buf)
+static int comp_data_status(struct bt_mesh_model *model,
+			    struct bt_mesh_msg_ctx *ctx,
+			    struct os_mbuf *buf)
 {
 	struct comp_data *param;
 	size_t to_copy;
@@ -44,24 +44,26 @@ static void comp_data_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_DEV_COMP_DATA_STATUS) {
-		BT_WARN("Unexpected Composition Data Status");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_DEV_COMP_DATA_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
-
-	*(param->status) = net_buf_simple_pull_u8(buf);
+	if (param->page) {
+		*(param->page) = net_buf_simple_pull_u8(buf);
+	}
 	to_copy  = min(net_buf_simple_tailroom(param->comp), buf->om_len);
 	net_buf_simple_add_mem(param->comp, buf->om_data, to_copy);
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
-static void state_status_u8(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf*buf,
-			    uint32_t expect_status)
+static int state_status_u8(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf*buf,
+			   uint32_t expect_status)
 {
 	uint8_t *status;
 
@@ -69,44 +71,92 @@ static void state_status_u8(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != expect_status) {
-		BT_WARN("Unexpected Status (0x%08x != 0x%08x)",
-			(unsigned) cli->op_pending, (unsigned) expect_status);
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, expect_status, ctx->addr,
+				       (void **)&status)) {
+		return -ENOENT;
 	}
 
-	status = cli->op_param;
 	*status = net_buf_simple_pull_u8(buf);
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
-static void beacon_status(struct bt_mesh_model *model,
-			  struct bt_mesh_msg_ctx *ctx,
-			  struct os_mbuf *buf)
+static int beacon_status(struct bt_mesh_model *model,
+			 struct bt_mesh_msg_ctx *ctx,
+			 struct os_mbuf *buf)
 {
-	state_status_u8(model, ctx, buf, OP_BEACON_STATUS);
+	return state_status_u8(model, ctx, buf, OP_BEACON_STATUS);
 }
 
-static void ttl_status(struct bt_mesh_model *model,
-			  struct bt_mesh_msg_ctx *ctx,
-			  struct os_mbuf*buf)
+static int ttl_status(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf*buf)
 {
-	state_status_u8(model, ctx, buf, OP_DEFAULT_TTL_STATUS);
+	return state_status_u8(model, ctx, buf, OP_DEFAULT_TTL_STATUS);
 }
 
-static void friend_status(struct bt_mesh_model *model,
-			  struct bt_mesh_msg_ctx *ctx,
-			  struct os_mbuf*buf)
+static int friend_status(struct bt_mesh_model *model,
+			 struct bt_mesh_msg_ctx *ctx,
+			 struct os_mbuf*buf)
 {
-	state_status_u8(model, ctx, buf, OP_FRIEND_STATUS);
+	return state_status_u8(model, ctx, buf, OP_FRIEND_STATUS);
 }
 
-static void gatt_proxy_status(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf*buf)
+static int gatt_proxy_status(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf*buf)
+{
+	return state_status_u8(model, ctx, buf, OP_GATT_PROXY_STATUS);
+}
+
+static int transmit_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
+{
+	return state_status_u8(model, ctx, buf, OP_NET_TRANSMIT_STATUS);
+}
+
+		struct krp_param {
+	uint8_t *status;
+	uint16_t net_idx;
+	uint8_t *phase;
+};
+
+static int krp_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+		      struct os_mbuf *buf)
 {
-	state_status_u8(model, ctx, buf, OP_GATT_PROXY_STATUS);
+	struct krp_param *param;
+	uint16_t net_idx;
+	uint8_t status, phase;
+
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+	       bt_hex(buf->om_data, buf->om_len));
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_KRP_STATUS, ctx->addr, (void **)&param)) {
+		return -ENOENT;
+	}
+
+	status = net_buf_simple_pull_u8(buf);
+	net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
+	phase = net_buf_simple_pull_u8(buf);
+
+	if (param->net_idx != net_idx) {
+		return -ENOENT;
+	}
+
+	if (param->status) {
+		*param->status = status;
+	}
+
+	if (param->phase) {
+		*param->phase = phase;
+	}
+
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct relay_param {
@@ -114,9 +164,9 @@ struct relay_param {
 	uint8_t *transmit;
 };
 
-static void relay_status(struct bt_mesh_model *model,
-			 struct bt_mesh_msg_ctx *ctx,
-			 struct os_mbuf*buf)
+static int relay_status(struct bt_mesh_model *model,
+			struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf*buf)
 {
 	struct relay_param *param;
 
@@ -124,16 +174,24 @@ static void relay_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_RELAY_STATUS) {
-		BT_WARN("Unexpected Relay Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_RELAY_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
 	*param->status = net_buf_simple_pull_u8(buf);
 	*param->transmit = net_buf_simple_pull_u8(buf);
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
+}
+
+static int net_transmit_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       struct os_mbuf *buf)
+{
+	return state_status_u8(model, ctx, buf, OP_NET_TRANSMIT_STATUS);
 }
 
 struct net_key_param {
@@ -141,9 +199,9 @@ struct net_key_param {
 	uint16_t net_idx;
 };
 
-static void net_key_status(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int net_key_status(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	struct net_key_param *param;
 	uint16_t net_idx;
@@ -153,25 +211,26 @@ static void net_key_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_NET_KEY_STATUS) {
-		BT_WARN("Unexpected Net Key Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_NET_KEY_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
 	net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
 
-	param = cli->op_param;
 	if (param->net_idx != net_idx) {
 		BT_WARN("Net Key Status key index does not match");
-		return;
+		return -ENOENT;
 	}
 
 	if (param->status) {
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct net_key_list_param {
@@ -179,9 +238,9 @@ struct net_key_list_param {
 	size_t *key_cnt;
 };
 
-static void net_key_list(struct bt_mesh_model *model,
-			 struct bt_mesh_msg_ctx *ctx,
-			 struct os_mbuf *buf)
+static int net_key_list(struct bt_mesh_model *model,
+			struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf *buf)
 {
 	struct net_key_list_param *param;
 	int i;
@@ -190,13 +249,11 @@ static void net_key_list(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_NET_KEY_LIST) {
-		BT_WARN("Unexpected Net Key List message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_NET_KEY_LIST, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
-
 	for (i = 0; i < *param->key_cnt && buf->om_len >= 3; i += 2) {
 		key_idx_unpack(buf, &param->keys[i], &param->keys[i + 1]);
 	}
@@ -205,29 +262,37 @@ static void net_key_list(struct bt_mesh_model *model,
 		param->keys[i++] = net_buf_simple_pull_le16(buf) & 0xfff;
 	}
 
+	if (buf->om_len > 0) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	*param->key_cnt = i;
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
-static void node_reset_status(struct bt_mesh_model *model,
-		struct bt_mesh_msg_ctx *ctx, struct os_mbuf *buf)
+static int node_reset_status(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	bool *param = NULL;
-	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x",
-		ctx->net_idx, ctx->app_idx, ctx->addr);
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x", ctx->net_idx,
+	       ctx->app_idx, ctx->addr);
 
-	if (cli->op_pending != OP_NODE_RESET_STATUS) {
-		BT_WARN("Unexpected Node Reset Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_NODE_RESET_STATUS,
+				       ctx->addr, (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
-
 	if (param) {
 		*param = true;
 	}
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct app_key_param {
@@ -236,9 +301,9 @@ struct app_key_param {
 	uint16_t app_idx;
 };
 
-static void app_key_status(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf*buf)
+static int app_key_status(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf*buf)
 {
 	struct app_key_param *param;
 	uint16_t net_idx, app_idx;
@@ -248,25 +313,26 @@ static void app_key_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_APP_KEY_STATUS) {
-		BT_WARN("Unexpected App Key Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_APP_KEY_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
 	key_idx_unpack(buf, &net_idx, &app_idx);
 
-	param = cli->op_param;
 	if (param->net_idx != net_idx || param->app_idx != app_idx) {
 		BT_WARN("App Key Status key indices did not match");
-		return;
+		return -ENOENT;
 	}
 
 	if (param->status) {
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct app_key_list_param {
@@ -276,9 +342,9 @@ struct app_key_list_param {
 	size_t *key_cnt;
 };
 
-static void app_key_list(struct bt_mesh_model *model,
-			 struct bt_mesh_msg_ctx *ctx,
-			 struct os_mbuf *buf)
+static int app_key_list(struct bt_mesh_model *model,
+			struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf *buf)
 {
 	struct app_key_list_param *param;
 	uint16_t net_idx;
@@ -289,18 +355,17 @@ static void app_key_list(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_APP_KEY_LIST) {
-		BT_WARN("Unexpected App Key List message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_APP_KEY_LIST, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
 	net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
 
-	param = cli->op_param;
 	if (param->net_idx != net_idx) {
 		BT_WARN("App Key List Net Key index did not match");
-		return;
+		return -ENOENT;
 	}
 
 	for (i = 0; i < *param->key_cnt && buf->om_len >= 3; i += 2) {
@@ -311,12 +376,19 @@ static void app_key_list(struct bt_mesh_model *model,
 		param->keys[i++] = net_buf_simple_pull_le16(buf) & 0xfff;
 	}
 
+	if (buf->om_len > 0U) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EINVAL;
+	}
+
 	*param->key_cnt = i;
 	if (param->status) {
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct mod_app_param {
@@ -327,9 +399,9 @@ struct mod_app_param {
 	uint16_t cid;
 };
 
-static void mod_app_status(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf*buf)
+static int mod_app_status(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf*buf)
 {
 	uint16_t elem_addr, mod_app_idx, mod_id, cid;
 	struct mod_app_param *param;
@@ -339,9 +411,14 @@ static void mod_app_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_MOD_APP_STATUS) {
-		BT_WARN("Unexpected Model App Status message");
-		return;
+	if ((buf->om_len != 7U) && (buf->om_len != 9U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_MOD_APP_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
@@ -356,19 +433,20 @@ static void mod_app_status(struct bt_mesh_model *model,
 
 	mod_id = net_buf_simple_pull_le16(buf);
 
-	param = cli->op_param;
 	if (param->elem_addr != elem_addr ||
 	    param->mod_app_idx != mod_app_idx || param->mod_id != mod_id ||
 	    param->cid != cid) {
 		BT_WARN("Model App Status parameters did not match");
-		return;
+		return -ENOENT;
 	}
 
 	if (param->status) {
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct mod_member_list_param {
@@ -380,14 +458,19 @@ struct mod_member_list_param {
 	size_t *member_cnt;
 };
 
-static void mod_member_list_handle(struct bt_mesh_msg_ctx *ctx,
-				   struct os_mbuf *buf, bool vnd)
+static int mod_member_list_handle(struct bt_mesh_msg_ctx *ctx,
+				  struct os_mbuf *buf, bool vnd,
+				  struct mod_member_list_param *param)
 {
-	struct mod_member_list_param *param;
 	uint16_t elem_addr, mod_id, cid;
 	uint8_t status;
 	int i;
 
+	if ((vnd && buf->om_len < 7U) || (buf->om_len < 5U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	status = net_buf_simple_pull_u8(buf);
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (vnd) {
@@ -396,16 +479,15 @@ static void mod_member_list_handle(struct bt_mesh_msg_ctx *ctx,
 
 	mod_id = net_buf_simple_pull_le16(buf);
 
-	param = cli->op_param;
 	if (param->elem_addr != elem_addr || param->mod_id != mod_id ||
 	    (vnd && param->cid != cid)) {
 		BT_WARN("Model Member List parameters did not match");
-		return;
+		return -ENOENT;
 	}
 
-	if (buf->om_len % 2) {
-		BT_WARN("Model Member List invalid length");
-		return;
+	if (buf->om_len % 2U) {
+		BT_ERR("Model Member List invalid length");
+		return -EMSGSIZE;
 	}
 
 	for (i = 0; i < *param->member_cnt && buf->om_len; i++) {
@@ -417,39 +499,45 @@ static void mod_member_list_handle(struct bt_mesh_msg_ctx *ctx,
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
-static void mod_app_list(struct bt_mesh_model *model,
+static int mod_app_list(struct bt_mesh_model *model,
 			 struct bt_mesh_msg_ctx *ctx,
 			 struct os_mbuf *buf)
 {
+	struct mod_member_list_param *param;
+
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_SIG_MOD_APP_LIST) {
-		BT_WARN("Unexpected Model App List message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_SIG_MOD_APP_LIST, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	mod_member_list_handle(ctx, buf, false);
+	return mod_member_list_handle(ctx, buf, false, param);
 }
 
-static void mod_app_list_vnd(struct bt_mesh_model *model,
+static int mod_app_list_vnd(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
+	struct mod_member_list_param *param;
+
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_VND_MOD_APP_LIST) {
-		BT_WARN("Unexpected Model App List Vendor message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_VND_MOD_APP_LIST, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	mod_member_list_handle(ctx, buf, true);
+	return mod_member_list_handle(ctx, buf, true, param);
 }
 
 struct mod_pub_param {
@@ -460,7 +548,7 @@ struct mod_pub_param {
 	struct bt_mesh_cfg_mod_pub *pub;
 };
 
-static void mod_pub_status(struct bt_mesh_model *model,
+static int mod_pub_status(struct bt_mesh_model *model,
 			   struct bt_mesh_msg_ctx *ctx,
 			   struct os_mbuf*buf)
 {
@@ -472,16 +560,21 @@ static void mod_pub_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_MOD_PUB_STATUS) {
+	if ((buf->om_len != 12U) && (buf->om_len != 14U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EINVAL;
+	}
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_MOD_PUB_STATUS, ctx->addr,
+				       (void **)&param)) {
 		BT_WARN("Unexpected Model Pub Status message");
-		return;
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
 	if (param->cid != CID_NVAL) {
 		if (buf->om_len < 14) {
 			BT_WARN("Unexpected Mod Pub Status with SIG Model");
-			return;
+			return -ENOENT;
 		}
 
 		cid = sys_get_le16(&buf->om_data[10]);
@@ -489,7 +582,7 @@ static void mod_pub_status(struct bt_mesh_model *model,
 	} else {
 		if (buf->om_len > 12) {
 			BT_WARN("Unexpected Mod Pub Status with Vendor Model");
-			return;
+			return -ENOENT;
 		}
 
 		cid = CID_NVAL;
@@ -498,7 +591,7 @@ static void mod_pub_status(struct bt_mesh_model *model,
 
 	if (mod_id != param->mod_id || cid != param->cid) {
 		BT_WARN("Mod Pub Model ID or Company ID mismatch");
-		return;
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
@@ -507,7 +600,7 @@ static void mod_pub_status(struct bt_mesh_model *model,
 	if (elem_addr != param->elem_addr) {
 		BT_WARN("Model Pub Status for unexpected element (0x%04x)",
 			elem_addr);
-		return;
+		return -ENOENT;
 	}
 
 	if (param->status) {
@@ -524,7 +617,9 @@ static void mod_pub_status(struct bt_mesh_model *model,
 		param->pub->transmit = net_buf_simple_pull_u8(buf);
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct mod_sub_param {
@@ -536,9 +631,9 @@ struct mod_sub_param {
 	uint16_t cid;
 };
 
-static void mod_sub_status(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf*buf)
+static int mod_sub_status(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf*buf)
 {
 	uint16_t elem_addr, sub_addr, mod_id, cid;
 	struct mod_sub_param *param;
@@ -548,9 +643,14 @@ static void mod_sub_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_MOD_SUB_STATUS) {
-		BT_WARN("Unexpected Model Subscription Status message");
-		return;
+	if ((buf->om_len != 7U) && (buf->om_len != 9U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EINVAL;
+	}
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_MOD_SUB_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
 	status = net_buf_simple_pull_u8(buf);
@@ -565,12 +665,11 @@ static void mod_sub_status(struct bt_mesh_model *model,
 
 	mod_id = net_buf_simple_pull_le16(buf);
 
-	param = cli->op_param;
 	if (param->elem_addr != elem_addr || param->mod_id != mod_id ||
 	    (param->expect_sub && *param->expect_sub != sub_addr) ||
 	    param->cid != cid) {
 		BT_WARN("Model Subscription Status parameters did not match");
-		return;
+		return -ENOENT;
 	}
 
 	if (param->sub_addr) {
@@ -581,39 +680,45 @@ static void mod_sub_status(struct bt_mesh_model *model,
 		*param->status = status;
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
-static void mod_sub_list(struct bt_mesh_model *model,
-			 struct bt_mesh_msg_ctx *ctx,
-			 struct os_mbuf *buf)
+static int mod_sub_list(struct bt_mesh_model *model,
+			struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf *buf)
 {
+	struct mod_member_list_param *param;
+
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_MOD_SUB_LIST) {
-		BT_WARN("Unexpected Model Subscription List message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_MOD_SUB_LIST, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	mod_member_list_handle(ctx, buf, false);
+	return mod_member_list_handle(ctx, buf, false, param);
 }
 
-static void mod_sub_list_vnd(struct bt_mesh_model *model,
+static int mod_sub_list_vnd(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
+	struct mod_member_list_param *param;
+
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_MOD_SUB_LIST_VND) {
-		BT_WARN("Unexpected Model Subscription List Vendor message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_MOD_SUB_LIST_VND, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	mod_member_list_handle(ctx, buf, true);
+	return mod_member_list_handle(ctx, buf, true,param);
 }
 
 struct hb_sub_param {
@@ -621,9 +726,9 @@ struct hb_sub_param {
 	struct bt_mesh_cfg_hb_sub *sub;
 };
 
-static void hb_sub_status(struct bt_mesh_model *model,
-			  struct bt_mesh_msg_ctx *ctx,
-			  struct os_mbuf*buf)
+static int hb_sub_status(struct bt_mesh_model *model,
+			 struct bt_mesh_msg_ctx *ctx,
+			 struct os_mbuf*buf)
 {
 	struct hb_sub_param *param;
 
@@ -631,13 +736,11 @@ static void hb_sub_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_HEARTBEAT_SUB_STATUS) {
-		BT_WARN("Unexpected Heartbeat Subscription Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_HEARTBEAT_SUB_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
-
 	*param->status = net_buf_simple_pull_u8(buf);
 
 	param->sub->src = net_buf_simple_pull_le16(buf);
@@ -647,7 +750,9 @@ static void hb_sub_status(struct bt_mesh_model *model,
 	param->sub->min = net_buf_simple_pull_u8(buf);
 	param->sub->max = net_buf_simple_pull_u8(buf);
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 struct hb_pub_param {
@@ -655,7 +760,7 @@ struct hb_pub_param {
 	struct bt_mesh_cfg_hb_pub *pub;
 };
 
-static void hb_pub_status(struct bt_mesh_model *model,
+static int hb_pub_status(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
@@ -665,13 +770,11 @@ static void hb_pub_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (cli->op_pending != OP_HEARTBEAT_PUB_STATUS) {
-		BT_WARN("Unexpected Heartbeat Publication Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_HEARTBEAT_PUB_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = cli->op_param;
-
 	*param->status = net_buf_simple_pull_u8(buf);
 
 	if (param->pub) {
@@ -683,30 +786,118 @@ static void hb_pub_status(struct bt_mesh_model *model,
 		param->pub->net_idx = net_buf_simple_pull_u8(buf);
 	}
 
-	k_sem_give(&cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
+}
+
+struct node_idt_param {
+	uint8_t *status;
+	uint16_t net_idx;
+	uint8_t *identity;
+};
+
+static int node_identity_status(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	struct node_idt_param *param;
+	uint16_t net_idx, identity;
+	uint8_t status;
+
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+	       bt_hex(buf->om_data, buf->om_len));
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_NODE_IDENTITY_STATUS,
+				       ctx->addr, (void **)&param)) {
+		return -ENOENT;
+	}
+
+	status = net_buf_simple_pull_u8(buf);
+	net_idx = net_buf_simple_pull_le16(buf) & 0xfff;
+	identity = net_buf_simple_pull_u8(buf);
+
+	if (param && param->status) {
+		*param->status = status;
+	}
+
+	if (param->net_idx != net_idx) {
+		return -ENOENT;
+	}
+
+	if (param && param->identity) {
+		*param->identity = identity;
+	}
+
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
+}
+
+struct lpn_timeout_param {
+	uint16_t unicast_addr;
+	int32_t *polltimeout;
+};
+
+static int lpn_timeout_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+			      struct os_mbuf *buf)
+{
+	struct lpn_timeout_param *param;
+	uint16_t unicast_addr;
+	int32_t polltimeout;
+
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+	       bt_hex(buf->om_data, buf->om_len));
+
+	if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_LPN_TIMEOUT_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
+	}
+
+	unicast_addr = net_buf_simple_pull_le16(buf);
+	polltimeout = net_buf_simple_pull_le24(buf);
+
+	if (param->unicast_addr != unicast_addr) {
+		return -ENOENT;
+	}
+
+	if (param->polltimeout) {
+		*param->polltimeout = polltimeout;
+	}
+
+	bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
+
+	return 0;
 }
 
 const struct bt_mesh_model_op bt_mesh_cfg_cli_op[] = {
-	{ OP_DEV_COMP_DATA_STATUS,   15,  comp_data_status },
-	{ OP_BEACON_STATUS,          1,   beacon_status },
-	{ OP_DEFAULT_TTL_STATUS,     1,   ttl_status },
-	{ OP_FRIEND_STATUS,          1,   friend_status },
-	{ OP_GATT_PROXY_STATUS,      1,   gatt_proxy_status },
-	{ OP_RELAY_STATUS,           2,   relay_status },
-	{ OP_NET_KEY_STATUS,         3,   net_key_status },
-	{ OP_NET_KEY_LIST,           0,   net_key_list },
-	{ OP_APP_KEY_STATUS,         4,   app_key_status },
-	{ OP_APP_KEY_LIST,           3,   app_key_list },
-	{ OP_MOD_APP_STATUS,         7,   mod_app_status },
-	{ OP_SIG_MOD_APP_LIST,       5,   mod_app_list},
-	{ OP_VND_MOD_APP_LIST,       7,   mod_app_list_vnd},
-	{ OP_MOD_PUB_STATUS,         12,  mod_pub_status },
-	{ OP_MOD_SUB_STATUS,         7,   mod_sub_status },
-	{ OP_MOD_SUB_LIST,           5,   mod_sub_list},
-	{ OP_MOD_SUB_LIST_VND,       7,   mod_sub_list_vnd},
-	{ OP_HEARTBEAT_SUB_STATUS,   9,   hb_sub_status },
-	{ OP_HEARTBEAT_PUB_STATUS,   10,  hb_pub_status },
-	{ OP_NODE_RESET_STATUS,      0,   node_reset_status},
+	{ OP_DEV_COMP_DATA_STATUS,   BT_MESH_LEN_MIN(15),     comp_data_status },
+	{ OP_BEACON_STATUS,          BT_MESH_LEN_EXACT(1),    beacon_status },
+	{ OP_DEFAULT_TTL_STATUS,     BT_MESH_LEN_EXACT(1),    ttl_status },
+	{ OP_FRIEND_STATUS,          BT_MESH_LEN_EXACT(1),    friend_status },
+	{ OP_GATT_PROXY_STATUS,      BT_MESH_LEN_EXACT(1),    gatt_proxy_status },
+	{ OP_RELAY_STATUS,           BT_MESH_LEN_EXACT(2),    relay_status },
+	{ OP_NET_TRANSMIT_STATUS,    BT_MESH_LEN_EXACT(1),    net_transmit_status },
+	{ OP_NET_KEY_STATUS,         BT_MESH_LEN_EXACT(3),    net_key_status },
+	{ OP_NET_KEY_LIST,           BT_MESH_LEN_MIN(0),      net_key_list },
+	{ OP_APP_KEY_STATUS,         BT_MESH_LEN_EXACT(4),    app_key_status },
+	{ OP_APP_KEY_LIST,           BT_MESH_LEN_MIN(3),      app_key_list },
+	{ OP_MOD_APP_STATUS,         BT_MESH_LEN_MIN(7),      mod_app_status },
+	{ OP_SIG_MOD_APP_LIST,       BT_MESH_LEN_MIN(5),      mod_app_list },
+	{ OP_VND_MOD_APP_LIST,       BT_MESH_LEN_MIN(7),      mod_app_list_vnd },
+	{ OP_MOD_PUB_STATUS,         BT_MESH_LEN_MIN(12),     mod_pub_status },
+	{ OP_MOD_SUB_STATUS,         BT_MESH_LEN_MIN(7),      mod_sub_status },
+	{ OP_MOD_SUB_LIST,           BT_MESH_LEN_MIN(5),      mod_sub_list },
+	{ OP_MOD_SUB_LIST_VND,       BT_MESH_LEN_MIN(7),      mod_sub_list_vnd },
+	{ OP_HEARTBEAT_SUB_STATUS,   BT_MESH_LEN_EXACT(9),    hb_sub_status },
+	{ OP_HEARTBEAT_PUB_STATUS,   BT_MESH_LEN_EXACT(10),   hb_pub_status },
+	{ OP_NODE_RESET_STATUS,      BT_MESH_LEN_EXACT(0),    node_reset_status },
+	{ OP_NODE_IDENTITY_STATUS,   BT_MESH_LEN_EXACT(4),    node_identity_status},
+	{ OP_LPN_TIMEOUT_STATUS,     BT_MESH_LEN_EXACT(5),    lpn_timeout_status },
+	{ OP_NET_TRANSMIT_STATUS,    BT_MESH_LEN_EXACT(1),    transmit_status},
+	{ OP_KRP_STATUS,             BT_MESH_LEN_EXACT(4),    krp_status},
 	BT_MESH_MODEL_OP_END,
 };
 
@@ -733,7 +924,7 @@ static int cfg_cli_init(struct bt_mesh_model *model)
 	 */
 	model->keys[0] = BT_MESH_KEY_DEV_ANY;
 
-	k_sem_init(&cli->op_sync, 0, 1);
+	bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
 
 	return 0;
 }
@@ -742,43 +933,18 @@ const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb = {
 	.init = cfg_cli_init,
 };
 
-static int cli_prepare(void *param, uint32_t op)
+static int cli_prepare(void *param, uint32_t op, uint16_t addr)
 {
 	if (!cli) {
 		BT_ERR("No available Configuration Client context!");
 		return -EINVAL;
 	}
 
-	if (cli->op_pending) {
-		BT_WARN("Another synchronous operation pending");
-		return -EBUSY;
-	}
-
-	cli->op_param = param;
-	cli->op_pending = op;
-
-	return 0;
-}
-
-static void cli_reset(void)
-{
-	cli->op_pending = 0;
-	cli->op_param = NULL;
-}
-
-static int cli_wait(void)
-{
-	int err;
-
-	err = k_sem_take(&cli->op_sync, msg_timeout);
-
-	cli_reset();
-
-	return err;
+	return bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, op, addr, param);
 }
 
 int bt_mesh_cfg_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page,
-			      uint8_t *status, struct os_mbuf *comp)
+			      uint8_t *rsp, struct os_mbuf *comp)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEV_COMP_DATA_GET, 1);
 	struct bt_mesh_msg_ctx ctx = {
@@ -788,12 +954,12 @@ int bt_mesh_cfg_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page,
 		.send_ttl = BT_MESH_TTL_DEFAULT,
 	};
 	struct comp_data param = {
-		.status = status,
+		.page = rsp,
 		.comp = comp,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_DEV_COMP_DATA_STATUS);
+	err = cli_prepare(&param, OP_DEV_COMP_DATA_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -804,11 +970,11 @@ int bt_mesh_cfg_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -826,7 +992,7 @@ static int get_state_u8(uint16_t net_idx, uint16_t addr, uint32_t op, uint32_t r
 	};
 	int err;
 
-	err = cli_prepare(val, rsp);
+	err = cli_prepare(val, rsp, addr);
 	if (err) {
 		goto done;
 	}
@@ -836,11 +1002,11 @@ static int get_state_u8(uint16_t net_idx, uint16_t addr, uint32_t op, uint32_t r
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -858,7 +1024,7 @@ static int set_state_u8(uint16_t net_idx, uint16_t addr, uint32_t op, uint32_t r
 	};
 	int err;
 
-	err = cli_prepare(val, rsp);
+	err = cli_prepare(val, rsp, addr);
 	if (err) {
 		goto done;
 	}
@@ -869,11 +1035,11 @@ static int set_state_u8(uint16_t net_idx, uint16_t addr, uint32_t op, uint32_t r
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -885,6 +1051,75 @@ int bt_mesh_cfg_beacon_get(uint16_t net_idx, uint16_t addr, uint8_t *status)
 			    status);
 }
 
+int bt_mesh_cfg_krp_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx,
+			uint8_t *status, uint8_t *phase)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_KRP_GET, 2);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct krp_param param = {
+		.status = status,
+		.phase = phase,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_KRP_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_KRP_GET);
+	net_buf_simple_add_le16(msg, key_net_idx);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
+int bt_mesh_cfg_krp_set(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx,
+			uint8_t transition, uint8_t *status, uint8_t *phase)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_KRP_SET, 3);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct krp_param param = {
+		.status = status,
+		.phase = phase,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_KRP_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_KRP_SET);
+	net_buf_simple_add_le16(msg, key_net_idx);
+	net_buf_simple_add_u8(msg, transition);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
 int bt_mesh_cfg_beacon_set(uint16_t net_idx, uint16_t addr, uint8_t val, uint8_t *status)
 {
 	return set_state_u8(net_idx, addr, OP_BEACON_SET, OP_BEACON_STATUS,
@@ -958,7 +1193,7 @@ int bt_mesh_cfg_relay_get(uint16_t net_idx, uint16_t addr, uint8_t *status,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_RELAY_STATUS);
+	err = cli_prepare(&param, OP_RELAY_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -968,11 +1203,11 @@ int bt_mesh_cfg_relay_get(uint16_t net_idx, uint16_t addr, uint8_t *status,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -994,7 +1229,7 @@ int bt_mesh_cfg_relay_set(uint16_t net_idx, uint16_t addr, uint8_t new_relay,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_RELAY_STATUS);
+	err = cli_prepare(&param, OP_RELAY_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1006,11 +1241,11 @@ int bt_mesh_cfg_relay_set(uint16_t net_idx, uint16_t addr, uint8_t new_relay,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1032,7 +1267,7 @@ int bt_mesh_cfg_net_key_add(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_NET_KEY_STATUS);
+	err = cli_prepare(&param, OP_NET_KEY_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1044,21 +1279,62 @@ int bt_mesh_cfg_net_key_add(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
 }
 
+int bt_mesh_cfg_net_key_update(uint16_t net_idx, uint16_t addr,
+			       uint16_t key_net_idx, const uint8_t net_key[16],
+			       uint8_t *status)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_UPDATE, 18);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct net_key_param param = {
+		.status = status,
+		.net_idx = key_net_idx,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_NET_KEY_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_NET_KEY_UPDATE);
+	net_buf_simple_add_le16(msg, key_net_idx);
+	net_buf_simple_add_mem(msg, net_key, 16);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	if (!status) {
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return 0;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
 int bt_mesh_cfg_net_key_get(uint16_t net_idx, uint16_t addr, uint16_t *keys,
 			     size_t *key_cnt)
 {
@@ -1075,7 +1351,7 @@ int bt_mesh_cfg_net_key_get(uint16_t net_idx, uint16_t addr, uint16_t *keys,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_NET_KEY_LIST);
+	err = cli_prepare(&param, OP_NET_KEY_LIST, addr);
 	if (err) {
 		return err;
 	}
@@ -1085,12 +1361,12 @@ int bt_mesh_cfg_net_key_get(uint16_t net_idx, uint16_t addr, uint16_t *keys,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		return err;
 	}
 
 	os_mbuf_free_chain(msg);
-	return cli_wait();
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 }
 
 int bt_mesh_cfg_net_key_del(uint16_t net_idx, uint16_t addr,
@@ -1109,7 +1385,7 @@ int bt_mesh_cfg_net_key_del(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_NET_KEY_STATUS);
+	err = cli_prepare(&param, OP_NET_KEY_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1120,16 +1396,16 @@ int bt_mesh_cfg_net_key_del(uint16_t net_idx, uint16_t addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1153,7 +1429,7 @@ int bt_mesh_cfg_app_key_add(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_APP_KEY_STATUS);
+	err = cli_prepare(&param, OP_APP_KEY_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1165,21 +1441,63 @@ int bt_mesh_cfg_app_key_add(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
 }
 
+int bt_mesh_cfg_app_key_update(uint16_t net_idx, uint16_t addr,
+			       uint16_t key_net_idx, uint16_t key_app_idx,
+			       const uint8_t app_key[16], uint8_t *status)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_UPDATE, 19);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct app_key_param param = {
+		.status = status,
+		.net_idx = key_net_idx,
+		.app_idx = key_app_idx,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_APP_KEY_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_APP_KEY_UPDATE);
+	key_idx_pack(msg, key_net_idx, key_app_idx);
+	net_buf_simple_add_mem(msg, app_key, 16);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	if (!status) {
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return 0;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
 int bt_mesh_cfg_node_reset(uint16_t net_idx, uint16_t addr, bool *status)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_RESET, 0);
@@ -1196,7 +1514,7 @@ int bt_mesh_cfg_node_reset(uint16_t net_idx, uint16_t addr, bool *status)
 		*status = false;
 	}
 
-	err = cli_prepare(status, OP_NODE_RESET_STATUS);
+	err = cli_prepare(status, OP_NODE_RESET_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1206,16 +1524,16 @@ int bt_mesh_cfg_node_reset(uint16_t net_idx, uint16_t addr, bool *status)
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	if (err) {
@@ -1243,7 +1561,7 @@ int bt_mesh_cfg_app_key_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_APP_KEY_LIST);
+	err = cli_prepare(&param, OP_APP_KEY_LIST, addr);
 	if (err) {
 		os_mbuf_free_chain(msg);
 		return err;
@@ -1256,12 +1574,12 @@ int bt_mesh_cfg_app_key_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_id
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
 		os_mbuf_free_chain(msg);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		return err;
 	}
 
 	os_mbuf_free_chain(msg);
-	return cli_wait();
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 }
 
 int bt_mesh_cfg_app_key_del(uint16_t net_idx, uint16_t addr,
@@ -1281,7 +1599,7 @@ int bt_mesh_cfg_app_key_del(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_APP_KEY_STATUS);
+	err = cli_prepare(&param, OP_APP_KEY_STATUS, addr);
 	if (err) {
 		os_mbuf_free_chain(msg);
 		return err;
@@ -1294,18 +1612,18 @@ int bt_mesh_cfg_app_key_del(uint16_t net_idx, uint16_t addr,
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
 		os_mbuf_free_chain(msg);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		return err;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		os_mbuf_free_chain(msg);
 		return 0;
 	}
 
 	os_mbuf_free_chain(msg);
-	return cli_wait();
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 }
 
 static int mod_app_bind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
@@ -1328,7 +1646,7 @@ static int mod_app_bind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_APP_STATUS);
+	err = cli_prepare(&param, OP_MOD_APP_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1346,16 +1664,16 @@ static int mod_app_bind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1400,7 +1718,7 @@ static int mod_app_unbind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_APP_STATUS);
+	err = cli_prepare(&param, OP_MOD_APP_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1418,12 +1736,12 @@ static int mod_app_unbind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		err = 0;
 		goto done;
 	}
@@ -1433,7 +1751,7 @@ static int mod_app_unbind(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	if (err) {
 		return err;
 	} else {
-		return cli_wait();
+		return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 	}
 }
 
@@ -1479,7 +1797,7 @@ static int mod_member_list_get(uint32_t op, uint32_t expect_op, uint16_t net_idx
 	};
 	int err;
 
-	err = cli_prepare(&param, expect_op);
+	err = cli_prepare(&param, expect_op, addr);
 	if (err) {
 		goto done;
 	}
@@ -1500,11 +1818,11 @@ static int mod_member_list_get(uint32_t op, uint32_t expect_op, uint16_t net_idx
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1528,7 +1846,7 @@ int bt_mesh_cfg_mod_app_get_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 	}
 
 	return mod_member_list_get(OP_VND_MOD_APP_GET, OP_VND_MOD_APP_LIST,
-				   net_idx, addr, elem_addr, mod_id, CID_NVAL,
+				   net_idx, addr, elem_addr, mod_id, cid,
 				   status, apps, app_cnt);
 }
 
@@ -1551,14 +1869,17 @@ static int mod_sub(uint32_t op, uint16_t net_idx, uint16_t addr, uint16_t elem_a
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_SUB_STATUS);
+	err = cli_prepare(&param, OP_MOD_SUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
 
 	bt_mesh_model_msg_init(msg, op);
 	net_buf_simple_add_le16(msg, elem_addr);
-	net_buf_simple_add_le16(msg, sub_addr);
+
+	if (sub_addr != BT_MESH_ADDR_UNASSIGNED) {
+		net_buf_simple_add_le16(msg, sub_addr);
+	}
 
 	if (cid != CID_NVAL) {
 		net_buf_simple_add_le16(msg, cid);
@@ -1569,17 +1890,17 @@ static int mod_sub(uint32_t op, uint16_t net_idx, uint16_t addr, uint16_t elem_a
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		err = 0;
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1588,6 +1909,10 @@ static int mod_sub(uint32_t op, uint16_t net_idx, uint16_t addr, uint16_t elem_a
 int bt_mesh_cfg_mod_sub_add(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 			    uint16_t sub_addr, uint16_t mod_id, uint8_t *status)
 {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+		return -EINVAL;
+	}
+
 	return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr,
 		       mod_id, CID_NVAL, status);
 }
@@ -1596,7 +1921,7 @@ int bt_mesh_cfg_mod_sub_add_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 				 uint16_t sub_addr, uint16_t mod_id, uint16_t cid,
 				 uint8_t *status)
 {
-	if (cid == CID_NVAL) {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr) || cid == CID_NVAL) {
 		return -EINVAL;
 	}
 
@@ -1607,15 +1932,27 @@ int bt_mesh_cfg_mod_sub_add_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 int bt_mesh_cfg_mod_sub_del(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 			    uint16_t sub_addr, uint16_t mod_id, uint8_t *status)
 {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+		return -EINVAL;
+	}
+
 	return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr,
 		       mod_id, CID_NVAL, status);
 }
 
+int bt_mesh_cfg_mod_sub_del_all(uint16_t net_idx, uint16_t addr,
+				uint16_t elem_addr, uint16_t mod_id,
+				uint8_t *status)
+{
+	return mod_sub(OP_MOD_SUB_DEL_ALL, net_idx, addr, elem_addr,
+		       BT_MESH_ADDR_UNASSIGNED, mod_id, CID_NVAL, status);
+}
+
 int bt_mesh_cfg_mod_sub_del_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 				 uint16_t sub_addr, uint16_t mod_id, uint16_t cid,
 				 uint8_t *status)
 {
-	if (cid == CID_NVAL) {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr) || cid == CID_NVAL) {
 		return -EINVAL;
 	}
 
@@ -1623,9 +1960,25 @@ int bt_mesh_cfg_mod_sub_del_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 		       mod_id, cid, status);
 }
 
+int bt_mesh_cfg_mod_sub_del_all_vnd(uint16_t net_idx, uint16_t addr,
+				    uint16_t elem_addr, uint16_t mod_id,
+				    uint16_t cid, uint8_t *status)
+{
+	if (cid == CID_NVAL) {
+		return -EINVAL;
+	}
+
+	return mod_sub(OP_MOD_SUB_DEL_ALL, net_idx, addr, elem_addr,
+		       BT_MESH_ADDR_UNASSIGNED, mod_id, cid, status);
+}
+
 int bt_mesh_cfg_mod_sub_overwrite(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 				  uint16_t sub_addr, uint16_t mod_id, uint8_t *status)
 {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+		return -EINVAL;
+	}
+
 	return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr,
 		       sub_addr, mod_id, CID_NVAL, status);
 }
@@ -1634,7 +1987,7 @@ int bt_mesh_cfg_mod_sub_overwrite_vnd(uint16_t net_idx, uint16_t addr,
 				      uint16_t elem_addr, uint16_t sub_addr,
 				      uint16_t mod_id, uint16_t cid, uint8_t *status)
 {
-	if (cid == CID_NVAL) {
+	if (!BT_MESH_ADDR_IS_GROUP(sub_addr) || cid == CID_NVAL) {
 		return -EINVAL;
 	}
 
@@ -1662,13 +2015,13 @@ static int mod_sub_va(uint32_t op, uint16_t net_idx, uint16_t addr, uint16_t ele
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_SUB_STATUS);
+	err = cli_prepare(&param, OP_MOD_SUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
 
 	BT_DBG("net_idx 0x%04x addr 0x%04x elem_addr 0x%04x label %s",
-	       net_idx, addr, elem_addr, label);
+	       net_idx, addr, elem_addr, bt_hex(label, 16));
 	BT_DBG("mod_id 0x%04x cid 0x%04x", mod_id, cid);
 
 	bt_mesh_model_msg_init(msg, op);
@@ -1684,16 +2037,16 @@ static int mod_sub_va(uint32_t op, uint16_t net_idx, uint16_t addr, uint16_t ele
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1779,7 +2132,7 @@ int bt_mesh_cfg_mod_sub_get_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_a
 	}
 
 	return mod_member_list_get(OP_MOD_SUB_GET_VND, OP_MOD_SUB_LIST_VND,
-				   net_idx, addr, elem_addr, mod_id, CID_NVAL,
+				   net_idx, addr, elem_addr, mod_id, cid,
 				   status, subs, sub_cnt);
 }
 
@@ -1803,7 +2156,7 @@ static int mod_pub_get(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_PUB_STATUS);
+	err = cli_prepare(&param, OP_MOD_PUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1821,16 +2174,16 @@ static int mod_pub_get(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1875,7 +2228,7 @@ static int mod_pub_set(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_MOD_PUB_STATUS);
+	err = cli_prepare(&param, OP_MOD_PUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1898,38 +2251,101 @@ static int mod_pub_set(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
 }
 
+static int mod_pub_va_set(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
+			  uint16_t mod_id, uint16_t cid,
+			  struct bt_mesh_cfg_mod_pub *pub, uint8_t *status)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_VA_SET, 27);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct mod_pub_param param = {
+		.mod_id = mod_id,
+		.cid = cid,
+		.elem_addr = elem_addr,
+		.status = status,
+		.pub = pub,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_MOD_PUB_STATUS, addr);
+	if (err) {
+		return err;
+	}
+	BT_DBG("app_idx 0x%04x", pub->app_idx);
+	bt_mesh_model_msg_init(msg, OP_MOD_PUB_VA_SET);
+
+	net_buf_simple_add_le16(msg, elem_addr);
+	net_buf_simple_add_mem(msg, pub->uuid, 16);
+	net_buf_simple_add_le16(msg, (pub->app_idx | (pub->cred_flag << 12)));
+	net_buf_simple_add_u8(msg, pub->ttl);
+	net_buf_simple_add_u8(msg, pub->period);
+	net_buf_simple_add_u8(msg, pub->transmit);
+
+	if (cid != CID_NVAL) {
+		net_buf_simple_add_le16(msg, cid);
+	}
+
+	net_buf_simple_add_le16(msg, mod_id);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	if (!status) {
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return 0;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
+
 int bt_mesh_cfg_mod_pub_set(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
 			    uint16_t mod_id, struct bt_mesh_cfg_mod_pub *pub,
 			    uint8_t *status)
 {
-	return mod_pub_set(net_idx, addr, elem_addr, mod_id, CID_NVAL,
-			   pub, status);
+	if (pub->uuid) {
+		return mod_pub_va_set(net_idx, addr, elem_addr, mod_id, CID_NVAL, pub, status);
+	} else {
+		return mod_pub_set(net_idx, addr, elem_addr, mod_id, CID_NVAL, pub, status);
+	}
 }
 
 int bt_mesh_cfg_mod_pub_set_vnd(uint16_t net_idx, uint16_t addr, uint16_t elem_addr,
-				uint16_t mod_id, uint16_t cid,
-				struct bt_mesh_cfg_mod_pub *pub, uint8_t *status)
+				uint16_t mod_id, uint16_t cid, struct bt_mesh_cfg_mod_pub *pub,
+				uint8_t *status)
 {
 	if (cid == CID_NVAL) {
 		return -EINVAL;
 	}
 
-	return mod_pub_set(net_idx, addr, elem_addr, mod_id, cid, pub, status);
+	if (pub->uuid) {
+		return mod_pub_va_set(net_idx, addr, elem_addr, mod_id, cid, pub, status);
+	} else {
+		return mod_pub_set(net_idx, addr, elem_addr, mod_id, cid, pub, status);
+	}
 }
 
 int bt_mesh_cfg_hb_sub_set(uint16_t net_idx, uint16_t addr,
@@ -1948,7 +2364,7 @@ int bt_mesh_cfg_hb_sub_set(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS);
+	err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -1961,16 +2377,16 @@ int bt_mesh_cfg_hb_sub_set(uint16_t net_idx, uint16_t addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -1992,7 +2408,7 @@ int bt_mesh_cfg_hb_sub_get(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS);
+	err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -2002,16 +2418,16 @@ int bt_mesh_cfg_hb_sub_get(uint16_t net_idx, uint16_t addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -2032,7 +2448,7 @@ int bt_mesh_cfg_hb_pub_set(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS);
+	err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -2048,16 +2464,16 @@ int bt_mesh_cfg_hb_pub_set(uint16_t net_idx, uint16_t addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -2079,7 +2495,7 @@ int bt_mesh_cfg_hb_pub_get(uint16_t net_idx, uint16_t addr,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS);
+	err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -2089,21 +2505,138 @@ int bt_mesh_cfg_hb_pub_get(uint16_t net_idx, uint16_t addr,
 	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
 	if (!status) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
 }
 
+int bt_mesh_cfg_node_identity_set(uint16_t net_idx, uint16_t addr,
+				  uint16_t key_net_idx, uint8_t new_identity,
+				  uint8_t *status, uint8_t *identity)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_SET, 4);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct node_idt_param param = {
+		.status = status,
+		.net_idx = key_net_idx,
+		.identity = identity,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_NODE_IDENTITY_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_SET);
+	net_buf_simple_add_le16(msg, key_net_idx);
+	net_buf_simple_add_u8(msg, new_identity);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	if (!status) {
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return 0;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
+int bt_mesh_cfg_node_identity_get(uint16_t net_idx, uint16_t addr,
+			    uint16_t key_net_idx, uint8_t *status,
+			    uint8_t *identity)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_GET, 2);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct node_idt_param param = {
+		.status = status,
+		.net_idx = key_net_idx,
+		.identity = identity,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_NODE_IDENTITY_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_GET);
+	net_buf_simple_add_le16(msg, key_net_idx);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	if (!status) {
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return 0;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
+int bt_mesh_cfg_lpn_timeout_get(uint16_t net_idx, uint16_t addr,
+			    uint16_t unicast_addr, int32_t *polltimeout)
+{
+	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_LPN_TIMEOUT_GET, 2);
+	struct bt_mesh_msg_ctx ctx = {
+		.net_idx = net_idx,
+		.app_idx = BT_MESH_KEY_DEV_REMOTE,
+		.addr = addr,
+		.send_ttl = BT_MESH_TTL_DEFAULT,
+		};
+	struct lpn_timeout_param param = {
+		.unicast_addr = unicast_addr,
+		.polltimeout = polltimeout,
+		};
+	int err;
+
+	err = cli_prepare(&param, OP_LPN_TIMEOUT_STATUS, addr);
+	if (err) {
+		return err;
+	}
+
+	bt_mesh_model_msg_init(msg, OP_LPN_TIMEOUT_GET);
+	net_buf_simple_add_le16(msg, unicast_addr);
+
+	err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+	if (err) {
+		BT_ERR("model_send() failed (err %d)", err);
+		bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx);
+		return err;
+	}
+
+	return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(msg_timeout));
+}
+
 int32_t bt_mesh_cfg_cli_timeout_get(void)
 {
 	return msg_timeout;
@@ -2114,4 +2647,68 @@ void bt_mesh_cfg_cli_timeout_set(int32_t timeout)
 	msg_timeout = timeout;
 }
 
+int bt_mesh_comp_p0_get(struct bt_mesh_comp_p0 *page,
+			struct os_mbuf *buf)
+{
+	if (buf->om_len < 10) {
+		return -EINVAL;
+	}
+
+	page->cid = net_buf_simple_pull_le16(buf);
+	page->pid = net_buf_simple_pull_le16(buf);
+	page->vid = net_buf_simple_pull_le16(buf);
+	page->crpl = net_buf_simple_pull_le16(buf);
+	page->feat = net_buf_simple_pull_le16(buf);
+	page->_buf = buf;
+
+	return 0;
+}
+
+struct bt_mesh_comp_p0_elem *bt_mesh_comp_p0_elem_pull(const struct bt_mesh_comp_p0 *page,
+						       struct bt_mesh_comp_p0_elem *elem)
+{
+	size_t modlist_size;
+
+	if (page->_buf->om_len < 4) {
+		return NULL;
+	}
+
+	elem->loc = net_buf_simple_pull_le16(page->_buf);
+	elem->nsig = net_buf_simple_pull_u8(page->_buf);
+	elem->nvnd = net_buf_simple_pull_u8(page->_buf);
+
+	modlist_size = elem->nsig * 2 + elem->nvnd * 4;
+
+	if (page->_buf->om_len < modlist_size) {
+		return NULL;
+	}
+
+	elem->_buf = net_buf_simple_pull_mem(page->_buf, modlist_size);
+
+	return elem;
+}
+
+uint16_t bt_mesh_comp_p0_elem_mod(struct bt_mesh_comp_p0_elem *elem, int idx)
+	{
+	CHECKIF(idx >= elem->nsig) {
+		return 0xffff;
+	}
+
+	return sys_get_le16(&elem->_buf[idx * 2]);
+}
+
+struct bt_mesh_mod_id_vnd bt_mesh_comp_p0_elem_mod_vnd(struct bt_mesh_comp_p0_elem *elem, int idx)
+	{
+	CHECKIF(idx >= elem->nvnd) {
+		return (struct bt_mesh_mod_id_vnd){ 0xffff, 0xffff };
+	}
+
+	size_t offset = elem->nsig * 2 + idx * 4;
+	struct bt_mesh_mod_id_vnd mod = {
+		.company = sys_get_le16(&elem->_buf[offset]),
+		.id = sys_get_le16(&elem->_buf[offset + 2]),
+		};
+
+	return mod;
+}
 #endif
diff --git a/nimble/host/mesh/src/cfg_srv.c b/nimble/host/mesh/src/cfg_srv.c
index 8a085bd710..2af24370e3 100644
--- a/nimble/host/mesh/src/cfg_srv.c
+++ b/nimble/host/mesh/src/cfg_srv.c
@@ -109,12 +109,13 @@ static int comp_get_page_0(struct os_mbuf *buf)
 	return 0;
 }
 
-static void dev_comp_data_get(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int dev_comp_data_get(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	uint8_t page;
+	int err;
 
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
@@ -129,7 +130,8 @@ static void dev_comp_data_get(struct bt_mesh_model *model,
 	bt_mesh_model_msg_init(sdu, OP_DEV_COMP_DATA_STATUS);
 
 	net_buf_simple_add_u8(sdu, page);
-	if (comp_get_page_0(sdu) < 0) {
+	err = comp_get_page_0(sdu);
+	if (err) {
 		BT_ERR("Unable to get composition page 0");
 		goto done;
 	}
@@ -140,6 +142,7 @@ static void dev_comp_data_get(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(sdu);
+	return err;
 }
 
 static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem,
@@ -200,17 +203,20 @@ static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr,
 		model->pub->count = 0;
 
 		if (model->pub->update) {
-			k_delayed_work_cancel(&model->pub->timer);
+			/* If this fails, the timer will check pub->addr and
+			 * exit without transmitting.
+			 */
+			(void)k_work_cancel_delayable(&model->pub->timer);
 		}
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-			bt_mesh_store_mod_pub(model);
+			bt_mesh_model_pub_store(model);
 		}
 
 		return STATUS_SUCCESS;
 	}
 
-	if (!bt_mesh_app_key_exists(app_idx)) {
+	if (!bt_mesh_app_key_exists(app_idx) || !bt_mesh_model_has_key(model, app_idx)) {
 		return STATUS_INVALID_APPKEY;
 	}
 
@@ -238,14 +244,17 @@ static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr,
 		BT_DBG("period %u ms", (unsigned) period_ms);
 
 		if (period_ms > 0) {
-			k_delayed_work_submit(&model->pub->timer, period_ms);
+			k_work_reschedule(&model->pub->timer, period_ms);
 		} else {
-			k_delayed_work_cancel(&model->pub->timer);
+			/* If this fails, publication will stop after the
+			 * ongoing set of retransmits.
+			 */
+			k_work_cancel_delayable(&model->pub->timer);
 		}
 	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_store_mod_pub(model);
+		bt_mesh_model_pub_store(model);
 	}
 
 	return STATUS_SUCCESS;
@@ -273,7 +282,7 @@ uint8_t mod_bind(struct bt_mesh_model *model, uint16_t key_idx)
 			model->keys[i] = key_idx;
 
 			if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-				bt_mesh_store_mod_bind(model);
+				bt_mesh_model_bind_store(model);
 			}
 
 			return STATUS_SUCCESS;
@@ -301,7 +310,7 @@ uint8_t mod_unbind(struct bt_mesh_model *model, uint16_t key_idx, bool store)
 		model->keys[i] = BT_MESH_KEY_UNUSED;
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-			bt_mesh_store_mod_bind(model);
+			bt_mesh_model_bind_store(model);
 		}
 
 		if (model->pub && model->pub->key == key_idx) {
@@ -313,10 +322,10 @@ uint8_t mod_unbind(struct bt_mesh_model *model, uint16_t key_idx, bool store)
 	return STATUS_SUCCESS;
 }
 
-static void send_app_key_status(struct bt_mesh_model *model,
-				struct bt_mesh_msg_ctx *ctx,
-				uint8_t status,
-				uint16_t app_idx, uint16_t net_idx)
+static int send_app_key_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       uint8_t status,
+			       uint16_t app_idx, uint16_t net_idx)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(
 		BT_MESH_MODEL_BUF_LEN(OP_APP_KEY_STATUS, 4));
@@ -330,11 +339,12 @@ static void send_app_key_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+	return 0;
 }
 
-static void app_key_add(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int app_key_add(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t key_net_idx, key_app_idx;
 	uint8_t status;
@@ -345,10 +355,10 @@ static void app_key_add(struct bt_mesh_model *model,
 
 	status = bt_mesh_app_key_add(key_app_idx, key_net_idx, buf->om_data);
 
-	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
+	return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
-static void app_key_update(struct bt_mesh_model *model,
+static int app_key_update(struct bt_mesh_model *model,
 			   struct bt_mesh_msg_ctx *ctx,
 			   struct os_mbuf *buf)
 {
@@ -361,7 +371,7 @@ static void app_key_update(struct bt_mesh_model *model,
 
 	status = bt_mesh_app_key_update(key_app_idx, key_net_idx, buf->om_data);
 	BT_DBG("status 0x%02x", status);
-	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
+	return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
 static void mod_app_key_del(struct bt_mesh_model *mod,
@@ -381,9 +391,9 @@ static void app_key_evt(uint16_t app_idx, uint16_t net_idx,
 	}
 }
 
-static void app_key_del(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int app_key_del(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t key_net_idx, key_app_idx;
 	uint8_t status;
@@ -394,15 +404,15 @@ static void app_key_del(struct bt_mesh_model *model,
 
 	status = bt_mesh_app_key_del(key_app_idx, key_net_idx);
 
-	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
+	return send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
 /* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */
 #define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2)
 
-static void app_key_get(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int app_key_get(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	struct os_mbuf *msg =
 		BT_MESH_MODEL_BUF(OP_APP_KEY_LIST,
@@ -412,10 +422,12 @@ static void app_key_get(struct bt_mesh_model *model,
 	uint8_t status;
 	ssize_t count;
 	int i;
+	int err = 0;
 
 	get_idx = net_buf_simple_pull_le16(buf);
 	if (get_idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", get_idx);
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -455,10 +467,11 @@ static void app_key_get(struct bt_mesh_model *model,
 	}
 
 done:
-    os_mbuf_free_chain(msg);
+	os_mbuf_free_chain(msg);
+	return err;
 }
 
-static void beacon_get(struct bt_mesh_model *model,
+static int beacon_get(struct bt_mesh_model *model,
 		       struct bt_mesh_msg_ctx *ctx,
 		       struct os_mbuf *buf)
 {
@@ -474,13 +487,16 @@ static void beacon_get(struct bt_mesh_model *model,
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
 		BT_ERR("Unable to send Config Beacon Status response");
 	}
+
+	return 0;
 	os_mbuf_free_chain(msg);
 }
 
-static void beacon_set(struct bt_mesh_model *model,
-		       struct bt_mesh_msg_ctx *ctx,
-		       struct os_mbuf *buf)
+static int beacon_set(struct bt_mesh_model *model,
+		      struct bt_mesh_msg_ctx *ctx,
+		      struct os_mbuf *buf)
 {
+	int err = 0;
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_BEACON_STATUS, 1);
 
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
@@ -489,6 +505,7 @@ static void beacon_set(struct bt_mesh_model *model,
 
 	if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
 		BT_WARN("Invalid Config Beacon value 0x%02x", buf->om_data[0]);
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -503,12 +520,12 @@ static void beacon_set(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(msg);
-
+	return err;
 }
 
-static void default_ttl_get(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf *buf)
+static int default_ttl_get(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1);
 
@@ -524,12 +541,12 @@ static void default_ttl_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
-
+	return 0;
 }
 
-static void default_ttl_set(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf *buf)
+static int default_ttl_set(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1);
 	int err;
@@ -553,9 +570,10 @@ static void default_ttl_set(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(msg);
+	return err;
 }
 
-static void send_gatt_proxy_status(struct bt_mesh_model *model,
+static int send_gatt_proxy_status(struct bt_mesh_model *model,
 				   struct bt_mesh_msg_ctx *ctx)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_GATT_PROXY_STATUS, 1);
@@ -569,20 +587,21 @@ static void send_gatt_proxy_status(struct bt_mesh_model *model,
 
 	os_mbuf_free_chain(msg);
 
+	return 0;
 }
 
-static void gatt_proxy_get(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int gatt_proxy_get(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	send_gatt_proxy_status(model, ctx);
+	return send_gatt_proxy_status(model, ctx);
 }
 
-static void gatt_proxy_set(struct bt_mesh_model *model,
+static int gatt_proxy_set(struct bt_mesh_model *model,
 			   struct bt_mesh_msg_ctx *ctx,
 			   struct os_mbuf *buf)
 {
@@ -592,15 +611,15 @@ static void gatt_proxy_set(struct bt_mesh_model *model,
 
 	if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
 		BT_WARN("Invalid GATT Proxy value 0x%02x", buf->om_data[0]);
-		return;
+		return -EINVAL;
 	}
 
 	(void)bt_mesh_gatt_proxy_set(buf->om_data[0]);
 
-	send_gatt_proxy_status(model, ctx);
+	return send_gatt_proxy_status(model, ctx);
 }
 
-static void net_transmit_get(struct bt_mesh_model *model,
+static int net_transmit_get(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -619,11 +638,12 @@ static void net_transmit_get(struct bt_mesh_model *model,
 
 	os_mbuf_free_chain(msg);
 
+	return 0;
 }
 
-static void net_transmit_set(struct bt_mesh_model *model,
-			     struct bt_mesh_msg_ctx *ctx,
-			     struct os_mbuf *buf)
+static int net_transmit_set(struct bt_mesh_model *model,
+			    struct bt_mesh_msg_ctx *ctx,
+			    struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_TRANSMIT_STATUS, 1);
 
@@ -645,9 +665,11 @@ static void net_transmit_set(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void relay_get(struct bt_mesh_model *model,
+static int relay_get(struct bt_mesh_model *model,
 		      struct bt_mesh_msg_ctx *ctx,
 		      struct os_mbuf *buf)
 {
@@ -667,13 +689,15 @@ static void relay_get(struct bt_mesh_model *model,
 
 	os_mbuf_free_chain(msg);
 
+	return 0;
 }
 
-static void relay_set(struct bt_mesh_model *model,
+static int relay_set(struct bt_mesh_model *model,
 		      struct bt_mesh_msg_ctx *ctx,
 		      struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_STATUS, 2);
+	int err = 0;
 
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
@@ -681,6 +705,7 @@ static void relay_set(struct bt_mesh_model *model,
 
 	if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
 		BT_WARN("Invalid Relay value 0x%02x", buf->om_data[0]);
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -696,14 +721,14 @@ static void relay_set(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(msg);
-
+	return err;
 }
 
-static void send_mod_pub_status(struct bt_mesh_model *cfg_mod,
-				struct bt_mesh_msg_ctx *ctx,
-				uint16_t elem_addr, uint16_t pub_addr,
-				bool vnd, struct bt_mesh_model *mod,
-				uint8_t status, uint8_t *mod_id)
+static int send_mod_pub_status(struct bt_mesh_model *cfg_mod,
+			       struct bt_mesh_msg_ctx *ctx,
+			       uint16_t elem_addr, uint16_t pub_addr,
+			       bool vnd, struct bt_mesh_model *mod,
+			       uint8_t status, uint8_t *mod_id)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_STATUS, 14);
 
@@ -737,11 +762,12 @@ static void send_mod_pub_status(struct bt_mesh_model *cfg_mod,
 	}
 
 	os_mbuf_free_chain(msg);
+	return 0;
 }
 
-static void mod_pub_get(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int mod_pub_get(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t elem_addr, pub_addr = 0;
 	struct bt_mesh_model *mod;
@@ -749,10 +775,15 @@ static void mod_pub_get(struct bt_mesh_model *model,
 	uint8_t *mod_id, status;
 	bool vnd;
 
+	if ((buf->om_len != 4U) && (buf->om_len != 6U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	mod_id = buf->om_data;
@@ -782,11 +813,11 @@ static void mod_pub_get(struct bt_mesh_model *model,
 	status = STATUS_SUCCESS;
 
 send_status:
-	send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
-			    status, mod_id);
+	return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+				   status, mod_id);
 }
 
-static void mod_pub_set(struct bt_mesh_model *model,
+static int mod_pub_set(struct bt_mesh_model *model,
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
@@ -797,10 +828,15 @@ static void mod_pub_set(struct bt_mesh_model *model,
 	uint8_t *mod_id;
 	bool vnd;
 
+	if ((buf->om_len != 11U) && (buf->om_len != 13U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	pub_addr = net_buf_simple_pull_le16(buf);
@@ -811,7 +847,7 @@ static void mod_pub_set(struct bt_mesh_model *model,
 	pub_ttl = net_buf_simple_pull_u8(buf);
 	if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
 		BT_ERR("Invalid TTL value 0x%02x", pub_ttl);
-		return;
+		return -EINVAL;
 	}
 
 	pub_period = net_buf_simple_pull_u8(buf);
@@ -844,8 +880,8 @@ static void mod_pub_set(struct bt_mesh_model *model,
 			      pub_period, retransmit, true);
 
 send_status:
-	send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
-			    status, mod_id);
+	return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+				   status, mod_id);
 }
 
 static size_t mod_sub_list_clear(struct bt_mesh_model *mod)
@@ -880,9 +916,9 @@ static size_t mod_sub_list_clear(struct bt_mesh_model *mod)
 	return clear_count;
 }
 
-static void mod_pub_va_set(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int mod_pub_va_set(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	uint8_t retransmit, status, pub_ttl, pub_period, cred_flag;
 	uint16_t elem_addr, pub_addr, pub_app_idx;
@@ -892,10 +928,15 @@ static void mod_pub_va_set(struct bt_mesh_model *model,
 	uint8_t *mod_id;
 	bool vnd;
 
+	if ((buf->om_len != 25U) && (buf->om_len != 27U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	label_uuid = net_buf_simple_pull_mem(buf, 16);
@@ -905,7 +946,7 @@ static void mod_pub_va_set(struct bt_mesh_model *model,
 	pub_ttl = net_buf_simple_pull_u8(buf);
 	if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
 		BT_ERR("Invalid TTL value 0x%02x", pub_ttl);
-		return;
+		return -EINVAL;
 	}
 
 	pub_period = net_buf_simple_pull_u8(buf);
@@ -947,14 +988,14 @@ static void mod_pub_va_set(struct bt_mesh_model *model,
 	}
 
 send_status:
-	send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
-			    status, mod_id);
+	return send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+				   status, mod_id);
 }
 
-static void send_mod_sub_status(struct bt_mesh_model *model,
-				struct bt_mesh_msg_ctx *ctx, uint8_t status,
-				uint16_t elem_addr, uint16_t sub_addr, uint8_t *mod_id,
-				bool vnd)
+static int send_mod_sub_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx, uint8_t status,
+			       uint16_t elem_addr, uint16_t sub_addr, uint8_t *mod_id,
+			       bool vnd)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_SUB_STATUS, 9);
 
@@ -978,11 +1019,13 @@ static void send_mod_sub_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void mod_sub_add(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int mod_sub_add(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr;
 	struct bt_mesh_model *mod;
@@ -992,10 +1035,15 @@ static void mod_sub_add(struct bt_mesh_model *model,
 	uint16_t *entry;
 	bool vnd;
 
+	if ((buf->om_len != 6U) && (buf->om_len != 8U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	sub_addr = net_buf_simple_pull_le16(buf);
@@ -1040,7 +1088,7 @@ static void mod_sub_add(struct bt_mesh_model *model,
 	status = STATUS_SUCCESS;
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_mod_sub(mod);
+		bt_mesh_model_sub_store(mod);
 	}
 
 	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
@@ -1049,13 +1097,13 @@ static void mod_sub_add(struct bt_mesh_model *model,
 
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
-			    mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+				   mod_id, vnd);
 }
 
-static void mod_sub_del(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int mod_sub_del(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr;
 	struct bt_mesh_model *mod;
@@ -1065,10 +1113,15 @@ static void mod_sub_del(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 6U) && (buf->om_len != 8U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	sub_addr = net_buf_simple_pull_le16(buf);
@@ -1110,17 +1163,16 @@ static void mod_sub_del(struct bt_mesh_model *model,
 		*match = BT_MESH_ADDR_UNASSIGNED;
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			bt_mesh_store_mod_sub(mod);
+			bt_mesh_model_sub_store(mod);
 		}
 	}
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
-			    mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+				   mod_id, vnd);
 }
 
-static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod,
-					       uint32_t depth, void *user_data)
+static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, void *user_data)
 {
 	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
 		bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups));
@@ -1131,9 +1183,9 @@ static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod,
 	return BT_MESH_WALK_CONTINUE;
 }
 
-static void mod_sub_overwrite(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int mod_sub_overwrite(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr;
 	struct bt_mesh_model *mod;
@@ -1142,10 +1194,15 @@ static void mod_sub_overwrite(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 6U) && (buf->om_len != 8U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	sub_addr = net_buf_simple_pull_le16(buf);
@@ -1174,14 +1231,13 @@ static void mod_sub_overwrite(struct bt_mesh_model *model,
 	}
 
 	if (ARRAY_SIZE(mod->groups) > 0) {
-		bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
-					mod_sub_clear_visitor, NULL);
+		bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
 
 		mod->groups[0] = sub_addr;
 		status = STATUS_SUCCESS;
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			bt_mesh_store_mod_sub(mod);
+			bt_mesh_model_sub_store(mod);
 		}
 
 		if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
@@ -1193,13 +1249,13 @@ static void mod_sub_overwrite(struct bt_mesh_model *model,
 
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
 			    mod_id, vnd);
 }
 
-static void mod_sub_del_all(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf *buf)
+static int mod_sub_del_all(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
 {
 	struct bt_mesh_model *mod;
 	struct bt_mesh_elem *elem;
@@ -1208,10 +1264,15 @@ static void mod_sub_del_all(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 4U) && (buf->om_len != 6U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	BT_DBG("elem_addr 0x%04x", elem_addr);
@@ -1232,18 +1293,17 @@ static void mod_sub_del_all(struct bt_mesh_model *model,
 		goto send_status;
 	}
 
-	bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor,
-				NULL);
+	bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_mod_sub(mod);
+		bt_mesh_model_sub_store(mod);
 	}
 
 	status = STATUS_SUCCESS;
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr,
-			    BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr,
+				   BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
 }
 
 struct mod_sub_list_ctx {
@@ -1251,8 +1311,7 @@ struct mod_sub_list_ctx {
 	struct os_mbuf *msg;
 };
 
-static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod,
-					      uint32_t depth, void *ctx)
+static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, void *ctx)
 {
 	struct mod_sub_list_ctx *visit = ctx;
 	int count = 0;
@@ -1283,19 +1342,21 @@ static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod,
 	return BT_MESH_WALK_CONTINUE;
 }
 
-static void mod_sub_get(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int mod_sub_get(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct mod_sub_list_ctx visit_ctx;
 	struct bt_mesh_model *mod;
 	struct bt_mesh_elem *elem;
 	uint16_t addr, id;
+	int err = 0;
 
 	addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
 		BT_WARN("Prohibited element address");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -1328,8 +1389,7 @@ static void mod_sub_get(struct bt_mesh_model *model,
 
 	visit_ctx.msg = msg;
 	visit_ctx.elem_idx = mod->elem_idx;
-	bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
-				&visit_ctx);
+	bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
 
 send_list:
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
@@ -1339,21 +1399,24 @@ static void mod_sub_get(struct bt_mesh_model *model,
 done:
 	os_mbuf_free_chain(msg);
 
+	return err;
 }
 
-static void mod_sub_get_vnd(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf *buf)
+static int mod_sub_get_vnd(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct mod_sub_list_ctx visit_ctx;
 	struct bt_mesh_model *mod;
 	struct bt_mesh_elem *elem;
 	uint16_t company, addr, id;
+	int err = 0;
 
 	addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
 		BT_WARN("Prohibited element address");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -1390,8 +1453,7 @@ static void mod_sub_get_vnd(struct bt_mesh_model *model,
 
 	visit_ctx.msg = msg;
 	visit_ctx.elem_idx = mod->elem_idx;
-	bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
-				&visit_ctx);
+	bt_mesh_model_extensions_walk(mod, mod_sub_list_visitor, &visit_ctx);
 
 send_list:
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
@@ -1401,11 +1463,12 @@ static void mod_sub_get_vnd(struct bt_mesh_model *model,
 done:
 	os_mbuf_free_chain(msg);
 
+	return err;
 }
 
-static void mod_sub_va_add(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int mod_sub_va_add(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr;
 	struct bt_mesh_model *mod;
@@ -1416,10 +1479,15 @@ static void mod_sub_va_add(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 20U) && (buf->om_len != 22U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	label_uuid = net_buf_simple_pull_mem(buf, 16);
@@ -1470,19 +1538,19 @@ static void mod_sub_va_add(struct bt_mesh_model *model,
 	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_mod_sub(mod);
+		bt_mesh_model_sub_store(mod);
 	}
 
 	status = STATUS_SUCCESS;
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
-			    mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+				   mod_id, vnd);
 }
 
-static void mod_sub_va_del(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int mod_sub_va_del(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr;
 	struct bt_mesh_model *mod;
@@ -1493,10 +1561,15 @@ static void mod_sub_va_del(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 20U) && (buf->om_len != 22U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	label_uuid = net_buf_simple_pull_mem(buf, 16);
@@ -1535,7 +1608,7 @@ static void mod_sub_va_del(struct bt_mesh_model *model,
 		*match = BT_MESH_ADDR_UNASSIGNED;
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			bt_mesh_store_mod_sub(mod);
+			bt_mesh_model_sub_store(mod);
 		}
 
 		status = STATUS_SUCCESS;
@@ -1544,13 +1617,13 @@ static void mod_sub_va_del(struct bt_mesh_model *model,
 	}
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
-			    mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+				   mod_id, vnd);
 }
 
-static void mod_sub_va_overwrite(struct bt_mesh_model *model,
-				 struct bt_mesh_msg_ctx *ctx,
-				 struct os_mbuf *buf)
+static int mod_sub_va_overwrite(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
 {
 	uint16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED;
 	struct bt_mesh_model *mod;
@@ -1560,10 +1633,15 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model,
 	uint8_t status;
 	bool vnd;
 
+	if ((buf->om_len != 20U) && (buf->om_len != 22U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
+
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
-		return;
+		return -EINVAL;
 	}
 
 	label_uuid = net_buf_simple_pull_mem(buf, 16);
@@ -1589,12 +1667,11 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model,
 	if (ARRAY_SIZE(mod->groups) > 0) {
 		status = bt_mesh_va_add(label_uuid, &sub_addr);
 		if (status == STATUS_SUCCESS) {
-			bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
-						mod_sub_clear_visitor, NULL);
+			bt_mesh_model_extensions_walk(mod, mod_sub_clear_visitor, NULL);
 			mod->groups[0] = sub_addr;
 
 			if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-				bt_mesh_store_mod_sub(mod);
+				bt_mesh_model_sub_store(mod);
 			}
 
 			if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
@@ -1606,13 +1683,13 @@ static void mod_sub_va_overwrite(struct bt_mesh_model *model,
 	}
 
 send_status:
-	send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
-			    mod_id, vnd);
+	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+				   mod_id, vnd);
 }
 
-static void send_net_key_status(struct bt_mesh_model *model,
-				struct bt_mesh_msg_ctx *ctx,
-				uint16_t idx, uint8_t status)
+static int send_net_key_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       uint16_t idx, uint8_t status)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_STATUS, 3);
 
@@ -1626,9 +1703,11 @@ static void send_net_key_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void net_key_add(struct bt_mesh_model *model,
+static int net_key_add(struct bt_mesh_model *model,
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
@@ -1638,18 +1717,18 @@ static void net_key_add(struct bt_mesh_model *model,
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	BT_DBG("idx 0x%04x", idx);
 
 	status = bt_mesh_subnet_add(idx, buf->om_data);
-	send_net_key_status(model, ctx, idx, status);
+	return send_net_key_status(model, ctx, idx, status);
 }
 
-static void net_key_update(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int net_key_update(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	uint8_t status;
 	uint16_t idx;
@@ -1657,24 +1736,24 @@ static void net_key_update(struct bt_mesh_model *model,
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	status = bt_mesh_subnet_update(idx, buf->om_data);
 
-	send_net_key_status(model, ctx, idx, status);
+	return send_net_key_status(model, ctx, idx, status);
 }
 
-static void net_key_del(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int net_key_del(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	uint16_t del_idx;
 
 	del_idx = net_buf_simple_pull_le16(buf);
 	if (del_idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", del_idx);
-		return;
+		return -EINVAL;
 	}
 
 	BT_DBG("idx 0x%04x", del_idx);
@@ -1683,19 +1762,18 @@ static void net_key_del(struct bt_mesh_model *model,
 	 * The NetKey List must contain a minimum of one NetKey.
 	 */
 	if (ctx->net_idx == del_idx) {
-		send_net_key_status(model, ctx, del_idx,
+		return send_net_key_status(model, ctx, del_idx,
 				    STATUS_CANNOT_REMOVE);
-		return;
 	}
 
-	bt_mesh_subnet_del(del_idx);
+	(void)bt_mesh_subnet_del(del_idx);
 
-	send_net_key_status(model, ctx, del_idx, STATUS_SUCCESS);
+	return send_net_key_status(model, ctx, del_idx, STATUS_SUCCESS);
 }
 
-static void net_key_get(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int net_key_get(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	struct os_mbuf *msg =
 		BT_MESH_MODEL_BUF(OP_NET_KEY_LIST,
@@ -1724,12 +1802,13 @@ static void net_key_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+	return 0;
 }
 
-static void send_node_id_status(struct bt_mesh_model *model,
-				struct bt_mesh_msg_ctx *ctx,
-				uint8_t status,
-				uint16_t net_idx, uint8_t node_id)
+static int send_node_id_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       uint8_t status,
+			       uint16_t net_idx, uint8_t node_id)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4);
 
@@ -1743,11 +1822,13 @@ static void send_node_id_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void node_identity_get(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int node_identity_get(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	enum bt_mesh_feat_state node_id;
 	uint8_t status;
@@ -1760,17 +1841,17 @@ static void node_identity_get(struct bt_mesh_model *model,
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	status = bt_mesh_subnet_node_id_get(idx, &node_id);
 
-	send_node_id_status(model, ctx, status, idx, node_id);
+	return send_node_id_status(model, ctx, status, idx, node_id);
 }
 
-static void node_identity_set(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int node_identity_set(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	uint8_t node_id, status;
 	uint16_t idx;
@@ -1782,30 +1863,28 @@ static void node_identity_set(struct bt_mesh_model *model,
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_WARN("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	node_id = net_buf_simple_pull_u8(buf);
 	if (node_id != 0x00 && node_id != 0x01) {
 		BT_WARN("Invalid Node ID value 0x%02x", node_id);
-		return;
+		return -EINVAL;
 	}
 
 	status = bt_mesh_subnet_node_id_set(idx, node_id);
 	if (status == STATUS_INVALID_NETKEY) {
-		send_node_id_status(model, ctx, status, idx,
-				    BT_MESH_NODE_IDENTITY_STOPPED);
-		return;
+		return send_node_id_status(model, ctx, status, idx,
+					   BT_MESH_NODE_IDENTITY_STOPPED);
 	}
 
 	if (status == STATUS_FEAT_NOT_SUPP) {
 		/* Should return success, even if feature isn't supported: */
-		send_node_id_status(model, ctx, STATUS_SUCCESS, idx,
-				    BT_MESH_NODE_IDENTITY_NOT_SUPPORTED);
-		return;
+		return send_node_id_status(model, ctx, STATUS_SUCCESS, idx,
+					   BT_MESH_NODE_IDENTITY_NOT_SUPPORTED);
 	}
 
-	send_node_id_status(model, ctx, status, idx, node_id);
+	return send_node_id_status(model, ctx, status, idx, node_id);
 }
 
 static void create_mod_app_status(struct os_mbuf *msg,
@@ -1826,9 +1905,9 @@ static void create_mod_app_status(struct os_mbuf *msg,
 	}
 }
 
-static void mod_app_bind(struct bt_mesh_model *model,
-			 struct bt_mesh_msg_ctx *ctx,
-			 struct os_mbuf *buf)
+static int mod_app_bind(struct bt_mesh_model *model,
+			struct bt_mesh_msg_ctx *ctx,
+			struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9);
 	uint16_t elem_addr, key_app_idx;
@@ -1836,10 +1915,17 @@ static void mod_app_bind(struct bt_mesh_model *model,
 	struct bt_mesh_elem *elem;
 	uint8_t *mod_id, status;
 	bool vnd;
+	int err = 0;
+
+	if ((buf->om_len != 6U) && (buf->om_len != 8U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
 
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -1883,13 +1969,14 @@ static void mod_app_bind(struct bt_mesh_model *model,
 	}
 
 done:
-    os_mbuf_free_chain(msg);
+	os_mbuf_free_chain(msg);
 
+	return err;
 }
 
-static void mod_app_unbind(struct bt_mesh_model *model,
-			   struct bt_mesh_msg_ctx *ctx,
-			   struct os_mbuf *buf)
+static int mod_app_unbind(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9);
 	uint16_t elem_addr, key_app_idx;
@@ -1897,10 +1984,17 @@ static void mod_app_unbind(struct bt_mesh_model *model,
 	struct bt_mesh_elem *elem;
 	uint8_t *mod_id, status;
 	bool vnd;
+	int err = 0;
+
+	if ((buf->om_len != 6U) && (buf->om_len != 8U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
 
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -1937,14 +2031,16 @@ static void mod_app_unbind(struct bt_mesh_model *model,
 	}
 
 done:
-    os_mbuf_free_chain(msg);
+	os_mbuf_free_chain(msg);
+
+	return err;
 }
 
 #define KEY_LIST_LEN (MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) * 2)
 
-static void mod_app_get(struct bt_mesh_model *model,
-			struct bt_mesh_msg_ctx *ctx,
-			struct os_mbuf *buf)
+static int mod_app_get(struct bt_mesh_model *model,
+		       struct bt_mesh_msg_ctx *ctx,
+		       struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = NET_BUF_SIMPLE(max(BT_MESH_MODEL_BUF_LEN(OP_VND_MOD_APP_LIST,
 								       9 + KEY_LIST_LEN),
@@ -1956,10 +2052,17 @@ static void mod_app_get(struct bt_mesh_model *model,
 	uint8_t *mod_id, status;
 	uint16_t elem_addr;
 	bool vnd;
+	int err = 0;
+
+	if ((buf->om_len != 4U) && (buf->om_len != 6U)) {
+		BT_ERR("The message size for the application opcode is incorrect.");
+		return -EMSGSIZE;
+	}
 
 	elem_addr = net_buf_simple_pull_le16(buf);
 	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
 		BT_WARN("Prohibited element address");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -2015,13 +2118,31 @@ static void mod_app_get(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(msg);
+
+	return err;
 }
 
-static void node_reset(struct bt_mesh_model *model,
-		       struct bt_mesh_msg_ctx *ctx,
-		       struct os_mbuf *buf)
+static void reset_send_start(uint16_t duration, int err, void *cb_data)
+{
+	if (err) {
+		BT_ERR("Sending Node Reset Status failed (err %d)", err);
+		bt_mesh_reset();
+	}
+}
+
+static void reset_send_end(int err, void *cb_data)
+{
+	bt_mesh_reset();
+}
+
+static int node_reset(struct bt_mesh_model *model,
+		      struct bt_mesh_msg_ctx *ctx,
+		      struct os_mbuf *buf)
 {
-	static struct bt_mesh_proxy_idle_cb proxy_idle = {.cb = bt_mesh_reset};
+	static const struct bt_mesh_send_cb reset_cb = {
+		.start = reset_send_start,
+		.end = reset_send_end,
+	};
 
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_RESET_STATUS, 0);
 
@@ -2029,30 +2150,19 @@ static void node_reset(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-
 	bt_mesh_model_msg_init(msg, OP_NODE_RESET_STATUS);
 
-	/* Send the response first since we wont have any keys left to
-	 * send it later.
-	 */
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	if (bt_mesh_model_send(model, ctx, msg, &reset_cb, NULL)) {
 		BT_ERR("Unable to send Node Reset Status");
 	}
 
-	if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
-		bt_mesh_reset();
-		return;
-	}
+	os_mbuf_free_chain(msg);
 
-	/* If the response goes to a proxy node, we'll wait for the sending to
-	 * complete before moving on.
-	 */
-	bt_mesh_proxy_on_idle(&proxy_idle);
-    os_mbuf_free_chain(msg);
+	return 0;
 }
 
-static void send_friend_status(struct bt_mesh_model *model,
-			       struct bt_mesh_msg_ctx *ctx)
+static int send_friend_status(struct bt_mesh_model *model,
+			      struct bt_mesh_msg_ctx *ctx)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_FRIEND_STATUS, 1);
 
@@ -2062,23 +2172,26 @@ static void send_friend_status(struct bt_mesh_model *model,
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
 		BT_ERR("Unable to send Friend Status");
 	}
-    os_mbuf_free_chain(msg);
+
+	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void friend_get(struct bt_mesh_model *model,
-		       struct bt_mesh_msg_ctx *ctx,
-		       struct os_mbuf *buf)
+static int friend_get(struct bt_mesh_model *model,
+		      struct bt_mesh_msg_ctx *ctx,
+		      struct os_mbuf *buf)
 {
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	send_friend_status(model, ctx);
+	return send_friend_status(model, ctx);
 }
 
-static void friend_set(struct bt_mesh_model *model,
-		       struct bt_mesh_msg_ctx *ctx,
-		       struct os_mbuf *buf)
+static int friend_set(struct bt_mesh_model *model,
+		      struct bt_mesh_msg_ctx *ctx,
+		      struct os_mbuf *buf)
 {
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
@@ -2086,22 +2199,23 @@ static void friend_set(struct bt_mesh_model *model,
 
 	if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
 		BT_WARN("Invalid Friend value 0x%02x", buf->om_data[0]);
-		return;
+		return -EINVAL;
 	}
 
 	(void)bt_mesh_friend_set(buf->om_data[0]);
 
-	send_friend_status(model, ctx);
+	return send_friend_status(model, ctx);
 }
 
-static void lpn_timeout_get(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    struct os_mbuf *buf)
+static int lpn_timeout_get(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   struct os_mbuf *buf)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_LPN_TIMEOUT_STATUS, 5);
 	struct bt_mesh_friend *frnd;
+	int32_t timeout_steps;
 	uint16_t lpn_addr;
-	int32_t timeout;
+	int err = 0;
 
 	lpn_addr = net_buf_simple_pull_le16(buf);
 
@@ -2111,6 +2225,7 @@ static void lpn_timeout_get(struct bt_mesh_model *model,
 	/* check if it's the address of the Low Power Node? */
 	if (!BT_MESH_ADDR_IS_UNICAST(lpn_addr)) {
 		BT_WARN("Invalid LPNAddress; ignoring msg");
+		err = -EINVAL;
 		goto done;
 	}
 
@@ -2118,20 +2233,21 @@ static void lpn_timeout_get(struct bt_mesh_model *model,
 	net_buf_simple_add_le16(msg, lpn_addr);
 
 	if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
-		timeout = 0;
+		timeout_steps = 0;
 		goto send_rsp;
 	}
 
 	frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true);
 	if (!frnd) {
-		timeout = 0;
+		timeout_steps = 0;
 		goto send_rsp;
 	}
 
-	timeout = k_delayed_work_remaining_get(&frnd->timer) / 100;
+	/* PollTimeout should be reported in steps of 100ms. */
+	timeout_steps = frnd->poll_to / 100;
 
 send_rsp:
-	net_buf_simple_add_le24(msg, timeout);
+	net_buf_simple_add_le24(msg, timeout_steps);
 
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
 		BT_ERR("Unable to send LPN PollTimeout Status");
@@ -2139,11 +2255,13 @@ static void lpn_timeout_get(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(msg);
+
+	return err;
 }
 
-static void send_krp_status(struct bt_mesh_model *model,
-			    struct bt_mesh_msg_ctx *ctx,
-			    uint16_t idx, uint8_t phase, uint8_t status)
+static int send_krp_status(struct bt_mesh_model *model,
+			   struct bt_mesh_msg_ctx *ctx,
+			   uint16_t idx, uint8_t phase, uint8_t status)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_KRP_STATUS, 4);
 
@@ -2158,10 +2276,12 @@ static void send_krp_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
-		    struct os_mbuf *buf)
+static int krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+		   struct os_mbuf *buf)
 {
 	uint8_t kr_phase, status;
 	uint16_t idx;
@@ -2169,18 +2289,18 @@ static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	BT_DBG("idx 0x%04x", idx);
 
 	status = bt_mesh_subnet_kr_phase_get(idx, &kr_phase);
 
-	send_krp_status(model, ctx, idx, kr_phase, status);
+	return send_krp_status(model, ctx, idx, kr_phase, status);
 }
 
-static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
-		    struct os_mbuf *buf)
+static int krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+		   struct os_mbuf *buf)
 {
 	uint8_t phase, status;
 	uint16_t idx;
@@ -2190,16 +2310,16 @@ static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
 
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		return;
+		return -EINVAL;
 	}
 
 	status = bt_mesh_subnet_kr_phase_set(idx, &phase);
 	if (status == STATUS_CANNOT_UPDATE) {
 		BT_ERR("Invalid kr phase transition 0x%02x", phase);
-		return;
+		return -EINVAL;
 	}
 
-	send_krp_status(model, ctx, idx, phase, status);
+	return send_krp_status(model, ctx, idx, phase, status);
 }
 
 static uint8_t hb_pub_count_log(uint16_t val)
@@ -2224,9 +2344,9 @@ struct hb_pub_param {
 	uint16_t net_idx;
 } __packed;
 
-static void hb_pub_send_status(struct bt_mesh_model *model,
-			       struct bt_mesh_msg_ctx *ctx, uint8_t status,
-			        const struct bt_mesh_hb_pub *pub)
+static int hb_pub_send_status(struct bt_mesh_model *model,
+			      struct bt_mesh_msg_ctx *ctx, uint8_t status,
+			      const struct bt_mesh_hb_pub *pub)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_STATUS, 10);
 
@@ -2248,9 +2368,11 @@ static void hb_pub_send_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void heartbeat_pub_get(struct bt_mesh_model *model,
+static int heartbeat_pub_get(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
@@ -2260,12 +2382,12 @@ static void heartbeat_pub_get(struct bt_mesh_model *model,
 
 	bt_mesh_hb_pub_get(&pub);
 
-	hb_pub_send_status(model, ctx, STATUS_SUCCESS, &pub);
+	return hb_pub_send_status(model, ctx, STATUS_SUCCESS, &pub);
 }
 
-static void heartbeat_pub_set(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int heartbeat_pub_set(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	struct hb_pub_param *param = (void *)buf->om_data;
 	struct bt_mesh_hb_pub pub;
@@ -2298,22 +2420,22 @@ static void heartbeat_pub_set(struct bt_mesh_model *model,
 
 	if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) {
 		BT_ERR("Invalid TTL value 0x%02x", param->ttl);
-		return;
+		return -EINVAL;
 	}
 
 	if (pub.net_idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", pub.net_idx);
-		return;
+		return -EINVAL;
 	}
 
 	status = bt_mesh_hb_pub_set(&pub);
 rsp:
-	hb_pub_send_status(model, ctx, status, &pub);
+	return hb_pub_send_status(model, ctx, status, &pub);
 }
 
-static void hb_sub_send_status(struct bt_mesh_model *model,
-			       struct bt_mesh_msg_ctx *ctx,
-			       const struct bt_mesh_hb_sub *sub)
+static int hb_sub_send_status(struct bt_mesh_model *model,
+			      struct bt_mesh_msg_ctx *ctx,
+			      const struct bt_mesh_hb_sub *sub)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_STATUS, 9);
 	BT_DBG("src 0x%04x ", ctx->addr);
@@ -2334,11 +2456,13 @@ static void hb_sub_send_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void heartbeat_sub_get(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int heartbeat_sub_get(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	struct bt_mesh_hb_sub sub;
 
@@ -2346,17 +2470,18 @@ static void heartbeat_sub_get(struct bt_mesh_model *model,
 
 	bt_mesh_hb_sub_get(&sub);
 
-	hb_sub_send_status(model, ctx, &sub);
+	return hb_sub_send_status(model, ctx, &sub);
 }
 
-static void heartbeat_sub_set(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int heartbeat_sub_set(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	uint8_t period_log, status;
 	struct bt_mesh_hb_sub sub;
 	uint16_t sub_src, sub_dst;
 	uint32_t period;
+	int err;
 
 	BT_DBG("src 0x%04x", ctx->addr);
 
@@ -2369,7 +2494,7 @@ static void heartbeat_sub_set(struct bt_mesh_model *model,
 
 	if (period_log > 0x11) {
 		BT_WARN("Prohibited subscription period 0x%02x", period_log);
-		return;
+		return -EINVAL;
 	}
 
 	period = bt_mesh_hb_pwr2(period_log);
@@ -2379,7 +2504,7 @@ static void heartbeat_sub_set(struct bt_mesh_model *model,
 		/* All errors are caused by invalid packets, which should be
 		 * ignored.
 		 */
-		return;
+		return -EINVAL;
 	}
 
 	bt_mesh_hb_sub_get(&sub);
@@ -2387,61 +2512,74 @@ static void heartbeat_sub_set(struct bt_mesh_model *model,
 	/* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after
 	 * disabling subscription, but 0x00 for subsequent Get requests.
 	 */
-	if (!period_log) {
+	if (sub.src == BT_MESH_ADDR_UNASSIGNED || !period_log) {
 		sub.min_hops = BT_MESH_TTL_MAX;
 	}
 
-	hb_sub_send_status(model, ctx, &sub);
+	err = hb_sub_send_status(model, ctx, &sub);
+	if (err) {
+		return err;
+	}
+
+	/* MESH/NODE/CFG/HBS/BV-02-C expects us to return previous
+	 * count value and then reset it to 0.
+	 */
+	if (sub.src != BT_MESH_ADDR_UNASSIGNED &&
+	    sub.dst != BT_MESH_ADDR_UNASSIGNED && !period) {
+		bt_mesh_hb_sub_reset_count();
+	}
+
+	return 0;
 }
 
 const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = {
-	{ OP_DEV_COMP_DATA_GET,        1,   dev_comp_data_get },
-	{ OP_APP_KEY_ADD,              19,  app_key_add },
-	{ OP_APP_KEY_UPDATE,           19,  app_key_update },
-	{ OP_APP_KEY_DEL,              3,   app_key_del },
-	{ OP_APP_KEY_GET,              2,   app_key_get },
-	{ OP_BEACON_GET,               0,   beacon_get },
-	{ OP_BEACON_SET,               1,   beacon_set },
-	{ OP_DEFAULT_TTL_GET,          0,   default_ttl_get },
-	{ OP_DEFAULT_TTL_SET,          1,   default_ttl_set },
-	{ OP_GATT_PROXY_GET,           0,   gatt_proxy_get },
-	{ OP_GATT_PROXY_SET,           1,   gatt_proxy_set },
-	{ OP_NET_TRANSMIT_GET,         0,   net_transmit_get },
-	{ OP_NET_TRANSMIT_SET,         1,   net_transmit_set },
-	{ OP_RELAY_GET,                0,   relay_get },
-	{ OP_RELAY_SET,                2,   relay_set },
-	{ OP_MOD_PUB_GET,              4,   mod_pub_get },
-	{ OP_MOD_PUB_SET,              11,  mod_pub_set },
-	{ OP_MOD_PUB_VA_SET,           24,  mod_pub_va_set },
-	{ OP_MOD_SUB_ADD,              6,   mod_sub_add },
-	{ OP_MOD_SUB_VA_ADD,           20,  mod_sub_va_add },
-	{ OP_MOD_SUB_DEL,              6,   mod_sub_del },
-	{ OP_MOD_SUB_VA_DEL,           20,  mod_sub_va_del },
-	{ OP_MOD_SUB_OVERWRITE,        6,   mod_sub_overwrite },
-	{ OP_MOD_SUB_VA_OVERWRITE,     20,  mod_sub_va_overwrite },
-	{ OP_MOD_SUB_DEL_ALL,          4,   mod_sub_del_all },
-	{ OP_MOD_SUB_GET,              4,   mod_sub_get },
-	{ OP_MOD_SUB_GET_VND,          6,   mod_sub_get_vnd },
-	{ OP_NET_KEY_ADD,              18,  net_key_add },
-	{ OP_NET_KEY_UPDATE,           18,  net_key_update },
-	{ OP_NET_KEY_DEL,              2,   net_key_del },
-	{ OP_NET_KEY_GET,              0,   net_key_get },
-	{ OP_NODE_IDENTITY_GET,        2,   node_identity_get },
-	{ OP_NODE_IDENTITY_SET,        3,   node_identity_set },
-	{ OP_MOD_APP_BIND,             6,   mod_app_bind },
-	{ OP_MOD_APP_UNBIND,           6,   mod_app_unbind },
-	{ OP_SIG_MOD_APP_GET,          4,   mod_app_get },
-	{ OP_VND_MOD_APP_GET,          6,   mod_app_get },
-	{ OP_NODE_RESET,               0,   node_reset },
-	{ OP_FRIEND_GET,               0,   friend_get },
-	{ OP_FRIEND_SET,               1,   friend_set },
-	{ OP_LPN_TIMEOUT_GET,          2,   lpn_timeout_get },
-	{ OP_KRP_GET,                  2,   krp_get },
-	{ OP_KRP_SET,                  3,   krp_set },
-	{ OP_HEARTBEAT_PUB_GET,        0,   heartbeat_pub_get },
-	{ OP_HEARTBEAT_PUB_SET,        9,   heartbeat_pub_set },
-	{ OP_HEARTBEAT_SUB_GET,        0,   heartbeat_sub_get },
-	{ OP_HEARTBEAT_SUB_SET,        5,   heartbeat_sub_set },
+	{ OP_DEV_COMP_DATA_GET,        BT_MESH_LEN_EXACT(1),   dev_comp_data_get },
+	{ OP_APP_KEY_ADD,              BT_MESH_LEN_EXACT(19),  app_key_add },
+	{ OP_APP_KEY_UPDATE,           BT_MESH_LEN_EXACT(19),  app_key_update },
+	{ OP_APP_KEY_DEL,              BT_MESH_LEN_EXACT(3),   app_key_del },
+	{ OP_APP_KEY_GET,              BT_MESH_LEN_EXACT(2),   app_key_get },
+	{ OP_BEACON_GET,               BT_MESH_LEN_EXACT(0),   beacon_get },
+	{ OP_BEACON_SET,               BT_MESH_LEN_EXACT(1),   beacon_set },
+	{ OP_DEFAULT_TTL_GET,          BT_MESH_LEN_EXACT(0),   default_ttl_get },
+	{ OP_DEFAULT_TTL_SET,          BT_MESH_LEN_EXACT(1),   default_ttl_set },
+	{ OP_GATT_PROXY_GET,           BT_MESH_LEN_EXACT(0),   gatt_proxy_get },
+	{ OP_GATT_PROXY_SET,           BT_MESH_LEN_EXACT(1),   gatt_proxy_set },
+	{ OP_NET_TRANSMIT_GET,         BT_MESH_LEN_EXACT(0),   net_transmit_get },
+	{ OP_NET_TRANSMIT_SET,         BT_MESH_LEN_EXACT(1),   net_transmit_set },
+	{ OP_RELAY_GET,                BT_MESH_LEN_EXACT(0),   relay_get },
+	{ OP_RELAY_SET,                BT_MESH_LEN_EXACT(2),   relay_set },
+	{ OP_MOD_PUB_GET,              BT_MESH_LEN_MIN(4),     mod_pub_get },
+	{ OP_MOD_PUB_SET,              BT_MESH_LEN_MIN(11),    mod_pub_set },
+	{ OP_MOD_PUB_VA_SET,           BT_MESH_LEN_MIN(25),    mod_pub_va_set },
+	{ OP_MOD_SUB_ADD,              BT_MESH_LEN_MIN(6),     mod_sub_add },
+	{ OP_MOD_SUB_VA_ADD,           BT_MESH_LEN_MIN(20),    mod_sub_va_add },
+	{ OP_MOD_SUB_DEL,              BT_MESH_LEN_MIN(6),     mod_sub_del },
+	{ OP_MOD_SUB_VA_DEL,           BT_MESH_LEN_MIN(20),    mod_sub_va_del },
+	{ OP_MOD_SUB_OVERWRITE,        BT_MESH_LEN_MIN(6),     mod_sub_overwrite },
+	{ OP_MOD_SUB_VA_OVERWRITE,     BT_MESH_LEN_MIN(20),    mod_sub_va_overwrite },
+	{ OP_MOD_SUB_DEL_ALL,          BT_MESH_LEN_MIN(4),     mod_sub_del_all },
+	{ OP_MOD_SUB_GET,              BT_MESH_LEN_EXACT(4),   mod_sub_get },
+	{ OP_MOD_SUB_GET_VND,          BT_MESH_LEN_EXACT(6),   mod_sub_get_vnd },
+	{ OP_NET_KEY_ADD,              BT_MESH_LEN_EXACT(18),  net_key_add },
+	{ OP_NET_KEY_UPDATE,           BT_MESH_LEN_EXACT(18),  net_key_update },
+	{ OP_NET_KEY_DEL,              BT_MESH_LEN_EXACT(2),   net_key_del },
+	{ OP_NET_KEY_GET,              BT_MESH_LEN_EXACT(0),   net_key_get },
+	{ OP_NODE_IDENTITY_GET,        BT_MESH_LEN_EXACT(2),   node_identity_get },
+	{ OP_NODE_IDENTITY_SET,        BT_MESH_LEN_EXACT(3),   node_identity_set },
+	{ OP_MOD_APP_BIND,             BT_MESH_LEN_MIN(6),     mod_app_bind },
+	{ OP_MOD_APP_UNBIND,           BT_MESH_LEN_MIN(6),     mod_app_unbind },
+	{ OP_SIG_MOD_APP_GET,          BT_MESH_LEN_MIN(4),     mod_app_get },
+	{ OP_VND_MOD_APP_GET,          BT_MESH_LEN_MIN(6),     mod_app_get },
+	{ OP_NODE_RESET,               BT_MESH_LEN_EXACT(0),   node_reset },
+	{ OP_FRIEND_GET,               BT_MESH_LEN_EXACT(0),   friend_get },
+	{ OP_FRIEND_SET,               BT_MESH_LEN_EXACT(1),   friend_set },
+	{ OP_LPN_TIMEOUT_GET,          BT_MESH_LEN_EXACT(2),   lpn_timeout_get },
+	{ OP_KRP_GET,                  BT_MESH_LEN_EXACT(2),   krp_get },
+	{ OP_KRP_SET,                  BT_MESH_LEN_EXACT(3),   krp_set },
+	{ OP_HEARTBEAT_PUB_GET,        BT_MESH_LEN_EXACT(0),   heartbeat_pub_get },
+	{ OP_HEARTBEAT_PUB_SET,        BT_MESH_LEN_EXACT(9),   heartbeat_pub_set },
+	{ OP_HEARTBEAT_SUB_GET,        BT_MESH_LEN_EXACT(0),   heartbeat_sub_get },
+	{ OP_HEARTBEAT_SUB_SET,        BT_MESH_LEN_EXACT(5),   heartbeat_sub_set },
 	BT_MESH_MODEL_OP_END,
 };
 
@@ -2484,7 +2622,7 @@ static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		if (clear_count) {
-			bt_mesh_store_mod_sub(mod);
+			bt_mesh_model_sub_store(mod);
 		}
 	}
 
@@ -2493,7 +2631,7 @@ static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 	}
 }
 
-void bt_mesh_cfg_reset(void)
+void bt_mesh_model_reset(void)
 {
 	bt_mesh_model_foreach(mod_reset, NULL);
 }
diff --git a/nimble/host/mesh/src/crypto.h b/nimble/host/mesh/src/crypto.h
index 637d13e724..dbdedf1fd6 100644
--- a/nimble/host/mesh/src/crypto.h
+++ b/nimble/host/mesh/src/crypto.h
@@ -70,7 +70,7 @@ static inline int bt_mesh_beacon_key(const uint8_t net_key[16],
 }
 
 int bt_mesh_beacon_auth(const uint8_t beacon_key[16], uint8_t flags,
-			const uint8_t net_id[16], uint32_t iv_index,
+			const uint8_t net_id[8], uint32_t iv_index,
 			uint8_t auth[8]);
 
 static inline int bt_mesh_app_id(const uint8_t app_key[16], uint8_t app_id[1])
diff --git a/nimble/host/mesh/src/foundation.h b/nimble/host/mesh/src/foundation.h
index 012afbbb07..21734047ec 100644
--- a/nimble/host/mesh/src/foundation.h
+++ b/nimble/host/mesh/src/foundation.h
@@ -115,7 +115,7 @@
 #define STATUS_UNSPECIFIED                 0x10
 #define STATUS_INVALID_BINDING             0x11
 
-void bt_mesh_cfg_reset(void);
+void bt_mesh_model_reset(void);
 
 void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time);
 
diff --git a/nimble/host/mesh/src/friend.c b/nimble/host/mesh/src/friend.c
index 9a83c87fc5..4ce0bbdece 100644
--- a/nimble/host/mesh/src/friend.c
+++ b/nimble/host/mesh/src/friend.c
@@ -42,8 +42,6 @@ static struct os_mempool friend_buf_mempool;
 
 #define NET_BUF_FRAGS        BIT(0)
 
-#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), struct friend_adv, adv)
-
 /* PDUs from Friend to the LPN should only be transmitted once with the
  * smallest possible interval (20ms).
  */
@@ -66,12 +64,19 @@ static struct friend_adv {
 	uint16_t app_idx;
 } adv_pool[FRIEND_BUF_COUNT];
 
+#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), struct friend_adv, adv)
+
 static struct bt_mesh_adv *adv_alloc(int id)
 {
 	adv_pool[id].app_idx = BT_MESH_KEY_UNUSED;
 	return &adv_pool[id].adv;
 }
 
+static bool friend_is_allocated(const struct bt_mesh_friend *frnd)
+{
+	return frnd->subnet != NULL;
+}
+
 static bool is_lpn_unicast(struct bt_mesh_friend *frnd, uint16_t addr)
 {
 	if (frnd->lpn == BT_MESH_ADDR_UNASSIGNED) {
@@ -91,7 +96,7 @@ struct bt_mesh_friend *bt_mesh_friend_find(uint16_t net_idx, uint16_t lpn_addr,
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (valid && !frnd->subnet) {
+		if (valid && !friend_is_allocated(frnd)) {
 			continue;
 		}
 
@@ -125,6 +130,7 @@ static void purge_buffers(struct net_buf_slist_t *list)
 	while (!net_buf_slist_is_empty(list)) {
 		struct os_mbuf *buf;
 		buf = (void *)net_buf_slist_get(list);
+		BT_MESH_ADV(buf)->frags = NULL;
 		BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS;
 		net_buf_unref(buf);
 	}
@@ -150,16 +156,11 @@ static void friend_clear(struct bt_mesh_friend *frnd)
 
 	BT_DBG("LPN 0x%04x", frnd->lpn);
 
-	k_delayed_work_cancel(&frnd->timer);
+	(void)k_work_cancel_delayable(&frnd->timer);
 
 	memset(frnd->cred, 0, sizeof(frnd->cred));
 
 	if (frnd->last) {
-		/* Cancel the sending if necessary */
-		if (frnd->pending_buf) {
-			BT_MESH_ADV(frnd->last)->busy = 0;
-		}
-
 		net_buf_unref(frnd->last);
 		frnd->last = NULL;
 	}
@@ -192,7 +193,7 @@ void bt_mesh_friends_clear(void)
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (!frnd->subnet) {
+		if (!friend_is_allocated(frnd)) {
 			continue;
 		}
 
@@ -211,7 +212,7 @@ void bt_mesh_friend_sec_update(uint16_t net_idx)
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (!frnd->subnet) {
+		if (!friend_is_allocated(frnd)) {
 			continue;
 		}
 
@@ -345,7 +346,12 @@ static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
 				struct unseg_app_sdu_meta *meta)
 {
 	uint16_t app_idx = FRIEND_ADV(buf)->app_idx;
-	struct bt_mesh_net_rx net;
+	struct bt_mesh_net_rx net = {
+		.ctx = {
+			.app_idx = app_idx,
+			.net_idx = frnd->subnet->net_idx,
+		},
+	};
 	int err;
 
 	meta->subnet = frnd->subnet;
@@ -379,15 +385,21 @@ static int unseg_app_sdu_decrypt(struct bt_mesh_friend *frnd,
 				 const struct unseg_app_sdu_meta *meta)
 {
 	struct net_buf_simple_state state;
+	struct os_mbuf *in = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
+	struct os_mbuf *out = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
 	int err;
 
-	BT_DBG("");
-
 	net_buf_simple_save(buf, &state);
-	net_buf_simple_pull_mem(buf, 10);
-	buf->om_len -= 4;
+	/* Direct the input buffer at the Upper Transport Access PDU, accounting for
+	 * the network header and the 1 byte lower transport header
+	 */
+	net_buf_simple_clone(buf, in);
+	net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+	net_buf_simple_pull(buf, 1);
+	in->om_len -= BT_MESH_MIC_SHORT;;
 
-	err = bt_mesh_app_decrypt(meta->key, &meta->crypto, buf, buf);
+	net_buf_simple_clone(in, out);
+	err = bt_mesh_app_decrypt(meta->key, &meta->crypto, in, out);
 
 	net_buf_simple_restore(buf, &state);
 	net_buf_unref(buf);
@@ -404,8 +416,9 @@ static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd,
 	BT_DBG("");
 
 	net_buf_simple_save(buf, &state);
-	net_buf_simple_pull_mem(buf, 10);
-	buf->om_len -= 4;
+	net_buf_simple_pull_mem(buf, BT_MESH_NET_HDR_LEN);
+	net_buf_simple_pull_mem(buf, 1);
+	buf->om_len -= BT_MESH_MIC_SHORT;
 
 	err = bt_mesh_app_encrypt(meta->key, &meta->crypto, buf);
 
@@ -437,12 +450,19 @@ static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
 		return 0;
 	}
 
+	BT_DBG("Re-encrypting friend pdu (SeqNum %06x -> %06x)",
+	       meta.crypto.seq_num, bt_mesh.seq);
+
+	meta.crypto.seq_num = bt_mesh.seq;
+
 	err = unseg_app_sdu_decrypt(frnd, buf, &meta);
 	if (err) {
 		BT_WARN("Decryption failed! %d", err);
 		return err;
 	}
 
+	meta.crypto.seq_num = bt_mesh.seq;
+
 	err = unseg_app_sdu_encrypt(frnd, buf, &meta);
 	if (err) {
 		BT_WARN("Re-encryption failed! %d", err);
@@ -452,14 +472,14 @@ static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
 }
 
 static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct os_mbuf *buf,
-			      bool master_cred)
+			      bool flooding_cred)
 {
 	const struct bt_mesh_net_cred *cred;
 	uint32_t iv_index;
 	uint16_t src;
 	int err;
 
-	if (master_cred) {
+	if (flooding_cred) {
 		cred = &frnd->subnet->keys[SUBNET_KEY_TX_IDX(frnd->subnet)]
 				.msg;
 	} else {
@@ -468,7 +488,7 @@ static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct os_mbuf *buf,
 
 	src = sys_get_be16(&buf->om_data[5]);
 
-	if (bt_mesh_elem_find(src)) {
+	if (bt_mesh_has_addr(src)) {
 		uint32_t seq;
 
 		if (FRIEND_ADV(buf)->app_idx != BT_MESH_KEY_UNUSED) {
@@ -516,8 +536,8 @@ static struct os_mbuf *encode_friend_ctl(struct bt_mesh_friend *frnd,
 	info.src = bt_mesh_primary_addr();
 	info.dst = frnd->lpn;
 
-	info.ctl = 1;
-	info.ttl = 0;
+	info.ctl = 1U;
+	info.ttl = 0U;
 
 	memset(info.seq, 0, sizeof(info.seq));
 
@@ -532,7 +552,7 @@ static struct os_mbuf *encode_update(struct bt_mesh_friend *frnd, uint8_t md)
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd));
 	struct os_mbuf *buf;
 
-	__ASSERT_NO_MSG(frnd->subnet);
+	__ASSERT_NO_MSG(friend_is_allocated(frnd));
 
 	BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md);
 
@@ -589,7 +609,7 @@ static void friend_recv_delay(struct bt_mesh_friend *frnd)
 	int32_t delay = recv_delay(frnd);
 
 	frnd->pending_req = 1;
-	k_delayed_work_submit(&frnd->timer, K_MSEC(delay));
+	k_work_reschedule(&frnd->timer, K_MSEC(delay));
 	BT_DBG("Waiting RecvDelay of %d ms", delay);
 }
 
@@ -756,7 +776,7 @@ static void friend_clear_sent(int err, void *user_data)
 {
 	struct bt_mesh_friend *frnd = user_data;
 
-	k_delayed_work_submit(&frnd->clear.timer,
+	k_work_reschedule(&frnd->clear.timer,
 			      K_SECONDS(frnd->clear.repeat_sec));
 	frnd->clear.repeat_sec *= 2;
 }
@@ -795,6 +815,11 @@ static void clear_timeout(struct ble_npl_event *work)
 	struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work);
 	uint32_t duration;
 
+	if (frnd->clear.frnd == BT_MESH_ADDR_UNASSIGNED) {
+		/* Failed cancelling timer, return early. */
+		return;
+	}
+
 	BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd);
 
 	duration = k_uptime_get_32() - frnd->clear.start;
@@ -851,7 +876,8 @@ int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx,
 		return 0;
 	}
 
-	k_delayed_work_cancel(&frnd->clear.timer);
+	/* If this fails, the unassigned check will make the handler return early. */
+	(void)k_work_cancel_delayable(&frnd->clear.timer);
 	frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED;
 
 	return 0;
@@ -1016,12 +1042,12 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 	       (unsigned) frnd->poll_to);
 
 	if (BT_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) &&
-	    !bt_mesh_elem_find(frnd->clear.frnd)) {
+	    !bt_mesh_has_addr(frnd->clear.frnd)) {
 		clear_procedure_start(frnd);
 	}
 
 	delay = offer_delay(frnd, rx->ctx.recv_rssi, msg->criteria);
-	k_delayed_work_submit(&frnd->timer, K_MSEC(delay));
+	k_work_reschedule(&frnd->timer, K_MSEC(delay));
 
 	enqueue_offer(frnd, rx->ctx.recv_rssi);
 
@@ -1138,12 +1164,13 @@ static void buf_send_end(int err, void *user_data)
 	}
 
 	if (frnd->established) {
-		k_delayed_work_submit(&frnd->timer, frnd->poll_to);
+		/* Always restart poll timeout timer after sending */
+		k_work_reschedule(&frnd->timer, K_MSEC(frnd->poll_to));
 		BT_DBG("Waiting %u ms for next poll",
 		       (unsigned) frnd->poll_to);
 	} else {
 		/* Friend offer timeout is 1 second */
-		k_delayed_work_submit(&frnd->timer, K_SECONDS(1));
+		k_work_reschedule(&frnd->timer, K_SECONDS(1));
 		BT_DBG("Waiting for first poll");
 	}
 }
@@ -1187,9 +1214,13 @@ static void friend_timeout(struct ble_npl_event *work)
 		.start = buf_send_start,
 		.end = buf_send_end,
 	};
-
+	struct os_mbuf *buf;
 	uint8_t md;
 
+	if (!friend_is_allocated(frnd)) {
+		return;
+	}
+
 	__ASSERT_NO_MSG(frnd->pending_buf == 0);
 
 	BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn,
@@ -1225,16 +1256,24 @@ static void friend_timeout(struct ble_npl_event *work)
 
 	/* Clear the flag we use for segment tracking */
 	BT_MESH_ADV(frnd->last)->flags &= ~NET_BUF_FRAGS;
-	BT_MESH_ADV(frnd->last)->flags = 0;
+	BT_MESH_ADV(frnd->last)->frags = NULL;
 
 	BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x",
 	       frnd->last, frnd->lpn);
 	frnd->queue_size--;
 
 send_last:
+	buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, FRIEND_XMIT, K_NO_WAIT);
+	if (!buf) {
+		BT_ERR("Unable to allocate friend adv buffer");
+		return;
+	}
+
+	net_buf_add_mem(buf, frnd->last->om_data, frnd->last->om_len);
 	frnd->pending_req = 0;
 	frnd->pending_buf = 1;
-	bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd);
+	bt_mesh_adv_send(buf, &buf_sent_cb, frnd);
+	net_buf_unref(buf);
 }
 
 static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
@@ -1306,10 +1345,10 @@ int bt_mesh_friend_init(void)
 
 		net_buf_slist_init(&frnd->queue);
 
-		k_delayed_work_init(&frnd->timer, friend_timeout);
-		k_delayed_work_add_arg(&frnd->timer, frnd);
-		k_delayed_work_init(&frnd->clear.timer, clear_timeout);
-		k_delayed_work_add_arg(&frnd->clear.timer, frnd);
+		k_work_init_delayable(&frnd->timer, friend_timeout);
+		k_work_add_arg_delayable(&frnd->timer, frnd);
+		k_work_init_delayable(&frnd->clear.timer, clear_timeout);
+		k_work_add_arg_delayable(&frnd->clear.timer, frnd);
 
 		for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) {
 			net_buf_slist_init(&frnd->seg[j].queue);
@@ -1371,7 +1410,8 @@ static void friend_purge_old_ack(struct bt_mesh_friend *frnd, uint64_t *seq_auth
 
 			net_buf_slist_remove(&frnd->queue, prev, cur);
 			frnd->queue_size--;
-
+			/* Make sure old slist entry state doesn't remain */
+			BT_MESH_ADV(buf)->frags = NULL;
 			net_buf_unref(buf);
 			break;
 		}
@@ -1391,7 +1431,7 @@ static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd,
 	 * this rx function. These packets have already been added to the
 	 * queue, and should be ignored.
 	 */
-	if (bt_mesh_elem_find(rx->ctx.addr)) {
+	if (bt_mesh_has_addr(rx->ctx.addr)) {
 		return;
 	}
 
@@ -1612,6 +1652,7 @@ static bool friend_queue_prepare_space(struct bt_mesh_friend *frnd, uint16_t add
 		BT_DBG("PENDING SEGMENTS %d", pending_segments);
 
 		/* Make sure old slist entry state doesn't remain */
+		BT_MESH_ADV(buf)->frags = NULL;
 		BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS;
 
 		net_buf_unref(buf);
@@ -1650,6 +1691,11 @@ void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx,
 			continue;	
 		}
 
+		if (friend_lpn_matches(frnd, rx->sub->net_idx,
+                               rx->ctx.addr)) {
+		    continue;
+		}
+
 		if (!friend_queue_prepare_space(frnd, rx->ctx.addr, seq_auth,
 						seg_count)) {
 			continue;
diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c
index aab7f37414..c00af9477e 100644
--- a/nimble/host/mesh/src/glue.c
+++ b/nimble/host/mesh/src/glue.c
@@ -22,6 +22,7 @@
 
 #include "mesh/glue.h"
 #include "adv.h"
+#include "../src/ble_hs_conn_priv.h"
 #ifndef MYNEWT
 #include "nimble/nimble_port.h"
 #endif
@@ -104,6 +105,10 @@ net_buf_unref(struct os_mbuf *om)
     }
 
     adv = BT_MESH_ADV(om);
+    if (adv->started && adv->cb && adv->cb->end) {
+    	adv->cb->end(0, adv->cb_data);
+    }
+
     if (--adv->ref_cnt > 0) {
         return;
     }
@@ -112,6 +117,14 @@ net_buf_unref(struct os_mbuf *om)
     os_mbuf_free_chain(om);
 }
 
+
+void net_buf_simple_clone(const struct os_mbuf *original,
+	struct os_mbuf *clone)
+{
+	memcpy(clone, original, sizeof(struct os_mbuf));
+}
+
+
 int
 bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data)
 {
@@ -142,6 +155,20 @@ net_buf_simple_pull_le16(struct os_mbuf *om)
     return val;
 }
 
+uint32_t
+net_buf_simple_pull_le24(struct os_mbuf *om)
+{
+	uint32_t val;
+	struct os_mbuf *old = om;
+
+	om = os_mbuf_pullup(om, 3);
+	assert(om == old);
+	val = get_le24(om->om_data);
+	os_mbuf_adj(om, 3);
+
+	return val;
+}
+
 uint16_t
 net_buf_simple_pull_be16(struct os_mbuf *om)
 {
@@ -399,7 +426,7 @@ k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler)
 }
 
 void
-k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f)
+k_work_init_delayable(struct k_work_delayable *w, ble_npl_event_fn *f)
 {
 #ifndef MYNEWT
     ble_npl_callout_init(&w->work, nimble_port_get_dflt_eventq(), f, NULL);
@@ -409,26 +436,37 @@ k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f)
 }
 
 bool
-k_delayed_work_pending(struct k_delayed_work *w)
+k_work_delayable_is_pending(struct k_work_delayable *w)
 {
     return ble_npl_callout_is_active(&w->work);
 }
 
 void
-k_delayed_work_cancel(struct k_delayed_work *w)
+k_work_cancel_delayable(struct k_work_delayable *w)
 {
     ble_npl_callout_stop(&w->work);
 }
 
 void
-k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms)
+k_work_schedule(struct k_work_delayable *w, uint32_t ms)
 {
-    uint32_t ticks;
+	uint32_t ticks;
 
-    if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) {
-        assert(0);
-    }
-    ble_npl_callout_reset(&w->work, ticks);
+	if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) {
+		assert(0);
+	}
+	ble_npl_callout_reset(&w->work, ticks);
+}
+
+void
+k_work_reschedule(struct k_work_delayable *w, uint32_t ms)
+{
+	uint32_t ticks;
+
+	if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) {
+		assert(0);
+	}
+	ble_npl_callout_reset(&w->work, ticks);
 }
 
 void
@@ -444,13 +482,13 @@ k_work_add_arg(struct ble_npl_callout *w, void *arg)
 }
 
 void
-k_delayed_work_add_arg(struct k_delayed_work *w, void *arg)
+k_work_add_arg_delayable(struct k_work_delayable *w, void *arg)
 {
     k_work_add_arg(&w->work, arg);
 }
 
-uint32_t
-k_delayed_work_remaining_get (struct k_delayed_work *w)
+ble_npl_time_t
+k_work_delayable_remaining_get (struct k_work_delayable *w)
 {
     int sr;
     ble_npl_time_t t;
@@ -461,7 +499,13 @@ k_delayed_work_remaining_get (struct k_delayed_work *w)
 
     OS_EXIT_CRITICAL(sr);
 
-    return ble_npl_time_ticks_to_ms32(t);
+    return t;
+}
+
+uint32_t
+k_ticks_to_ms_floor32(ble_npl_time_t ticks)
+{
+    return ble_npl_time_ticks_to_ms32(ticks);
 }
 
 int64_t k_uptime_get(void)
@@ -475,6 +519,17 @@ uint32_t k_uptime_get_32(void)
     return k_uptime_get();
 }
 
+int64_t k_uptime_delta(int64_t *reftime)
+{
+	int64_t uptime, delta;
+
+	uptime = k_uptime_get();
+	delta = uptime - *reftime;
+	*reftime = uptime;
+
+	return delta;
+}
+
 void k_sleep(int32_t duration)
 {
     uint32_t ticks;
@@ -502,6 +557,33 @@ bt_dh_key_gen(const uint8_t remote_pk[64], bt_dh_key_cb_t cb)
     return 0;
 }
 
+void
+bt_conn_get_info(struct ble_hs_conn *conn,
+		 struct ble_gap_conn_desc *desc)
+{
+	struct ble_hs_conn_addrs addrs;
+
+	ble_hs_conn_addrs(conn, &addrs);
+
+	desc->our_id_addr = addrs.our_id_addr;
+	desc->peer_id_addr = addrs.peer_id_addr;
+	desc->our_ota_addr = addrs.our_ota_addr;
+	desc->peer_ota_addr = addrs.peer_ota_addr;
+
+	desc->conn_handle = conn->bhc_handle;
+	desc->conn_itvl = conn->bhc_itvl;
+	desc->conn_latency = conn->bhc_latency;
+	desc->supervision_timeout = conn->bhc_supervision_timeout;
+	desc->master_clock_accuracy = conn->bhc_master_clock_accuracy;
+	desc->sec_state = conn->bhc_sec_state;
+
+	if (conn->bhc_flags & BLE_HS_CONN_F_MASTER) {
+		desc->role = BLE_GAP_ROLE_MASTER;
+	} else {
+		desc->role = BLE_GAP_ROLE_SLAVE;
+	}
+}
+
 int
 bt_rand(void *buf, size_t len)
 {
@@ -705,24 +787,6 @@ bt_le_adv_start(const struct ble_gap_adv_params *param,
 
     return err;
 }
-
-int bt_le_adv_stop(bool proxy)
-{
-#if MYNEWT_VAL(BLE_MESH_PROXY)
-    int rc;
-
-    if (proxy) {
-        rc = ble_gap_ext_adv_stop(BT_MESH_ADV_GATT_INST);
-    } else {
-        rc = ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
-    }
-
-    return rc;
-#else
-    return ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
-#endif
-}
-
 #else
 
 int
@@ -769,14 +833,13 @@ bt_le_adv_start(const struct ble_gap_adv_params *param,
 
     return 0;
 }
+#endif
 
-int bt_le_adv_stop(bool proxy)
+int bt_le_adv_stop()
 {
-	return ble_gap_adv_stop();
+    return ble_gap_adv_stop();
 }
 
-#endif
-
 #if MYNEWT_VAL(BLE_MESH_PROXY)
 int bt_mesh_proxy_svcs_register(void);
 #endif
@@ -906,6 +969,7 @@ int create_free_list(struct k_mem_slab *slab)
 	uint32_t j;
 	char *p;
 
+    /* blocks must be word aligned */
     if(((slab->block_size | (uintptr_t)slab->buffer) &
 				(sizeof(void *) - 1)) != 0) {
 		return -EINVAL;
diff --git a/nimble/host/mesh/src/health_cli.c b/nimble/host/mesh/src/health_cli.c
index 76d639c5f3..caa70215b7 100644
--- a/nimble/host/mesh/src/health_cli.c
+++ b/nimble/host/mesh/src/health_cli.c
@@ -33,9 +33,9 @@ struct health_fault_param {
 	size_t *fault_count;
 };
 
-static void health_fault_status(struct bt_mesh_model *model,
-				struct bt_mesh_msg_ctx *ctx,
-				struct os_mbuf *buf)
+static int health_fault_status(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       struct os_mbuf *buf)
 {
 	struct health_fault_param *param;
 	uint8_t test_id;
@@ -45,23 +45,21 @@ static void health_fault_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (health_cli->op_pending != OP_HEALTH_FAULT_STATUS) {
-		BT_WARN("Unexpected Health Fault Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_FAULT_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = health_cli->op_param;
-
 	test_id = net_buf_simple_pull_u8(buf);
 	if (param->expect_test_id && test_id != *param->expect_test_id) {
 		BT_WARN("Health fault with unexpected Test ID");
-		return;
+		return -ENOENT;
 	}
 
 	cid = net_buf_simple_pull_le16(buf);
 	if (cid != param->cid) {
 		BT_WARN("Health fault with unexpected Company ID");
-		return;
+		return -ENOENT;
 	}
 
 	if (param->test_id) {
@@ -76,12 +74,14 @@ static void health_fault_status(struct bt_mesh_model *model,
 
 	memcpy(param->faults, buf->om_data, *param->fault_count);
 
-	k_sem_give(&health_cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
+
+	return 0;
 }
 
-static void health_current_status(struct bt_mesh_model *model,
-				  struct bt_mesh_msg_ctx *ctx,
-				  struct os_mbuf *buf)
+static int health_current_status(struct bt_mesh_model *model,
+				 struct bt_mesh_msg_ctx *ctx,
+				 struct os_mbuf *buf)
 {
 	struct bt_mesh_health_cli *cli = model->user_data;
 	uint8_t test_id;
@@ -99,19 +99,21 @@ static void health_current_status(struct bt_mesh_model *model,
 
 	if (!cli->current_status) {
 		BT_WARN("No Current Status callback available");
-		return;
+		return 0;
 	}
 
 	cli->current_status(cli, ctx->addr, test_id, cid, buf->om_data, buf->om_len);
+
+	return 0;
 }
 
 struct health_period_param {
 	uint8_t *divisor;
 };
 
-static void health_period_status(struct bt_mesh_model *model,
-				 struct bt_mesh_msg_ctx *ctx,
-				 struct os_mbuf *buf)
+static int health_period_status(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
 {
 	struct health_period_param *param;
 
@@ -119,23 +121,23 @@ static void health_period_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (health_cli->op_pending != OP_HEALTH_PERIOD_STATUS) {
-		BT_WARN("Unexpected Health Period Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_PERIOD_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = health_cli->op_param;
-
 	*param->divisor = net_buf_simple_pull_u8(buf);
 
-	k_sem_give(&health_cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
+
+	return 0;
 }
 
 struct health_attention_param {
 	uint8_t *attention;
 };
 
-static void health_attention_status(struct bt_mesh_model *model,
+static int health_attention_status(struct bt_mesh_model *model,
 				    struct bt_mesh_msg_ctx *ctx,
 				    struct os_mbuf *buf)
 {
@@ -145,61 +147,36 @@ static void health_attention_status(struct bt_mesh_model *model,
 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	if (health_cli->op_pending != OP_ATTENTION_STATUS) {
-		BT_WARN("Unexpected Health Attention Status message");
-		return;
+	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_ATTENTION_STATUS, ctx->addr,
+				       (void **)&param)) {
+		return -ENOENT;
 	}
 
-	param = health_cli->op_param;
-
 	if (param->attention) {
 		*param->attention = net_buf_simple_pull_u8(buf);
 	}
 
-	k_sem_give(&health_cli->op_sync);
+	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
+
+	return 0;
 }
 
 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
-	{ OP_HEALTH_FAULT_STATUS,    3,   health_fault_status },
-	{ OP_HEALTH_CURRENT_STATUS,  3,   health_current_status },
-	{ OP_HEALTH_PERIOD_STATUS,   1,   health_period_status },
-	{ OP_ATTENTION_STATUS,       1,   health_attention_status },
+	{ OP_HEALTH_FAULT_STATUS,     BT_MESH_LEN_MIN(3),    health_fault_status },
+	{ OP_HEALTH_CURRENT_STATUS,   BT_MESH_LEN_MIN(3),    health_current_status },
+	{ OP_HEALTH_PERIOD_STATUS,    BT_MESH_LEN_EXACT(1),  health_period_status },
+	{ OP_ATTENTION_STATUS,        BT_MESH_LEN_EXACT(1),  health_attention_status },
 	BT_MESH_MODEL_OP_END,
 };
 
-static int cli_prepare(void *param, uint32_t op)
+static int cli_prepare(void *param, uint32_t op, uint16_t addr)
 {
 	if (!health_cli) {
 		BT_ERR("No available Health Client context!");
 		return -EINVAL;
 	}
 
-	if (health_cli->op_pending) {
-		BT_WARN("Another synchronous operation pending");
-		return -EBUSY;
-	}
-
-	health_cli->op_param = param;
-	health_cli->op_pending = op;
-
-	return 0;
-}
-
-static void cli_reset(void)
-{
-	health_cli->op_pending = 0;
-	health_cli->op_param = NULL;
-}
-
-static int cli_wait(void)
-{
-	int err;
-
-	err = k_sem_take(&health_cli->op_sync, msg_timeout);
-
-	cli_reset();
-
-	return err;
+	return bt_mesh_msg_ack_ctx_prepare(&health_cli->ack_ctx, op, addr, param);
 }
 
 int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *attention)
@@ -215,7 +192,7 @@ int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *atten
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_ATTENTION_STATUS);
+	err = cli_prepare(&param, OP_ATTENTION_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -225,11 +202,11 @@ int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *atten
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -249,7 +226,7 @@ int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attent
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_ATTENTION_STATUS);
+	err = cli_prepare(&param, OP_ATTENTION_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -265,16 +242,16 @@ int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attent
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
 	if (!updated_attention) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -293,7 +270,7 @@ int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor)
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS);
+	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -303,11 +280,11 @@ int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor)
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -327,7 +304,7 @@ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS);
+	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -343,16 +320,16 @@ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor,
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
 	if (!updated_divisor) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -376,7 +353,7 @@ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -393,16 +370,16 @@ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
 	if (!faults) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -426,7 +403,7 @@ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -442,16 +419,16 @@ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
 	if (!test_id) {
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -475,7 +452,7 @@ int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	};
 	int err;
 
-	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
 	if (err) {
 		goto done;
 	}
@@ -486,11 +463,11 @@ int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid,
 	err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
 	if (err) {
 		BT_ERR("model_send() failed (err %d)", err);
-		cli_reset();
+		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
 		goto done;
 	}
 
-	err = cli_wait();
+	err = bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
 done:
 	os_mbuf_free_chain(msg);
 	return err;
@@ -532,14 +509,14 @@ static int health_cli_init(struct bt_mesh_model *model)
 
 	cli = model->user_data;
 	cli->model = model;
-
-	k_sem_init(&cli->op_sync, 0, 1);
+	msg_timeout = 2 * MSEC_PER_SEC;
 
 	/* Set the default health client pointer */
 	if (!health_cli) {
 		health_cli = cli;
 	}
 
+	bt_mesh_msg_ack_ctx_init(&health_cli->ack_ctx);
 	return 0;
 }
 
diff --git a/nimble/host/mesh/src/health_srv.c b/nimble/host/mesh/src/health_srv.c
index dd90533be8..4065d24f83 100644
--- a/nimble/host/mesh/src/health_srv.c
+++ b/nimble/host/mesh/src/health_srv.c
@@ -67,7 +67,6 @@ static size_t health_get_current(struct bt_mesh_model *mod,
 	uint8_t *test_id, *company_ptr;
 	uint16_t company_id;
 	uint8_t fault_count;
-	int err;
 
 	bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
 
@@ -77,6 +76,8 @@ static size_t health_get_current(struct bt_mesh_model *mod,
 
 	if (srv->cb && srv->cb->fault_get_cur) {
 		fault_count = net_buf_simple_tailroom(msg);
+		int err;
+
 		err = srv->cb->fault_get_cur(mod, test_id, &company_id,
 					     net_buf_simple_tail(msg),
 					     &fault_count);
@@ -99,9 +100,9 @@ static size_t health_get_current(struct bt_mesh_model *mod,
 	return fault_count;
 }
 
-static void health_fault_get(struct bt_mesh_model *model,
-			     struct bt_mesh_msg_ctx *ctx,
-			     struct os_mbuf *buf)
+static int health_fault_get(struct bt_mesh_model *model,
+			    struct bt_mesh_msg_ctx *ctx,
+			    struct os_mbuf *buf)
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	uint16_t company_id;
@@ -117,11 +118,13 @@ static void health_fault_get(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(sdu);
+
+	return 0;
 }
 
-static void health_fault_clear_unrel(struct bt_mesh_model *model,
-				     struct bt_mesh_msg_ctx *ctx,
-				     struct os_mbuf *buf)
+static int health_fault_clear_unrel(struct bt_mesh_model *model,
+				    struct bt_mesh_msg_ctx *ctx,
+				    struct os_mbuf *buf)
 {
 	struct bt_mesh_health_srv *srv = model->user_data;
 	uint16_t company_id;
@@ -131,13 +134,15 @@ static void health_fault_clear_unrel(struct bt_mesh_model *model,
 	BT_DBG("company_id 0x%04x", company_id);
 
 	if (srv->cb && srv->cb->fault_clear) {
-		srv->cb->fault_clear(model, company_id);
+		return srv->cb->fault_clear(model, company_id);
 	}
+
+	return 0;
 }
 
-static void health_fault_clear(struct bt_mesh_model *model,
-			       struct bt_mesh_msg_ctx *ctx,
-			       struct os_mbuf *buf)
+static int health_fault_clear(struct bt_mesh_model *model,
+			      struct bt_mesh_msg_ctx *ctx,
+			      struct os_mbuf *buf)
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_health_srv *srv = model->user_data;
@@ -148,7 +153,12 @@ static void health_fault_clear(struct bt_mesh_model *model,
 	BT_DBG("company_id 0x%04x", company_id);
 
 	if (srv->cb && srv->cb->fault_clear) {
-		srv->cb->fault_clear(model, company_id);
+		int err;
+
+		err = srv->cb->fault_clear(model, company_id);
+		if (err) {
+			return err;
+		}
 	}
 
 	health_get_registered(model, company_id, sdu);
@@ -158,9 +168,11 @@ static void health_fault_clear(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(sdu);
+
+	return 0;
 }
 
-static void health_fault_test_unrel(struct bt_mesh_model *model,
+static int health_fault_test_unrel(struct bt_mesh_model *model,
 				    struct bt_mesh_msg_ctx *ctx,
 				    struct os_mbuf *buf)
 {
@@ -174,18 +186,21 @@ static void health_fault_test_unrel(struct bt_mesh_model *model,
 	BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
 
 	if (srv->cb && srv->cb->fault_test) {
-		srv->cb->fault_test(model, test_id, company_id);
+		return srv->cb->fault_test(model, test_id, company_id);
 	}
+
+	return 0;
 }
 
-static void health_fault_test(struct bt_mesh_model *model,
-			      struct bt_mesh_msg_ctx *ctx,
-			      struct os_mbuf *buf)
+static int health_fault_test(struct bt_mesh_model *model,
+			     struct bt_mesh_msg_ctx *ctx,
+			     struct os_mbuf *buf)
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_health_srv *srv = model->user_data;
 	uint16_t company_id;
 	uint8_t test_id;
+	int err = 0;
 
 	BT_DBG("");
 
@@ -195,11 +210,10 @@ static void health_fault_test(struct bt_mesh_model *model,
 	BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
 
 	if (srv->cb && srv->cb->fault_test) {
-		int err;
-
 		err = srv->cb->fault_test(model, test_id, company_id);
 		if (err) {
 			BT_WARN("Running fault test failed with err %d", err);
+
 			goto done;
 		}
 	}
@@ -212,16 +226,19 @@ static void health_fault_test(struct bt_mesh_model *model,
 
 done:
 	os_mbuf_free_chain(sdu);
+
+	return err;
 }
 
-static void send_attention_status(struct bt_mesh_model *model,
-				  struct bt_mesh_msg_ctx *ctx)
+static int send_attention_status(struct bt_mesh_model *model,
+				 struct bt_mesh_msg_ctx *ctx)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_STATUS, 1);
 	struct bt_mesh_health_srv *srv = model->user_data;
 	uint8_t time;
 
-	time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000;
+	time = k_ticks_to_ms_floor32(
+		k_work_delayable_remaining_get(&srv->attn_timer)) / 1000;
 	BT_DBG("%u second%s", time, (time == 1) ? "" : "s");
 
 	bt_mesh_model_msg_init(msg, OP_ATTENTION_STATUS);
@@ -233,18 +250,20 @@ static void send_attention_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void attention_get(struct bt_mesh_model *model,
+static int attention_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	BT_DBG("");
 
-	send_attention_status(model, ctx);
+	return send_attention_status(model, ctx);
 }
 
-static void attention_set_unrel(struct bt_mesh_model *model,
+static int attention_set_unrel(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -255,20 +274,27 @@ static void attention_set_unrel(struct bt_mesh_model *model,
 	BT_DBG("%u second%s", time, (time == 1) ? "" : "s");
 
 	bt_mesh_attention(model, time);
+
+	return 0;
 }
 
-static void attention_set(struct bt_mesh_model *model,
-			  struct bt_mesh_msg_ctx *ctx,
-			  struct os_mbuf *buf)
+static int attention_set(struct bt_mesh_model *model,
+			 struct bt_mesh_msg_ctx *ctx,
+			 struct os_mbuf *buf)
 {
+	int err;
+
 	BT_DBG("");
 
-	attention_set_unrel(model, ctx, buf);
+	err = attention_set_unrel(model, ctx, buf);
+	if (err) {
+		return err;
+	}
 
-	send_attention_status(model, ctx);
+	return send_attention_status(model, ctx);
 }
 
-static void send_health_period_status(struct bt_mesh_model *model,
+static int send_health_period_status(struct bt_mesh_model *model,
 				      struct bt_mesh_msg_ctx *ctx)
 {
 	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_STATUS, 1);
@@ -282,18 +308,20 @@ static void send_health_period_status(struct bt_mesh_model *model,
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return 0;
 }
 
-static void health_period_get(struct bt_mesh_model *model,
+static int health_period_get(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
 	BT_DBG("");
 
-	send_health_period_status(model, ctx);
+	return send_health_period_status(model, ctx);
 }
 
-static void health_period_set_unrel(struct bt_mesh_model *model,
+static int health_period_set_unrel(struct bt_mesh_model *model,
 				    struct bt_mesh_msg_ctx *ctx,
 				    struct os_mbuf *buf)
 {
@@ -302,37 +330,44 @@ static void health_period_set_unrel(struct bt_mesh_model *model,
 	period = net_buf_simple_pull_u8(buf);
 	if (period > 15) {
 		BT_WARN("Prohibited period value %u", period);
-		return;
+		return -EINVAL;
 	}
 
 	BT_DBG("period %u", period);
 
 	model->pub->period_div = period;
+
+	return 0;
 }
 
-static void health_period_set(struct bt_mesh_model *model,
+static int health_period_set(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
+	int err;
+
 	BT_DBG("");
 
-	health_period_set_unrel(model, ctx, buf);
+	err = health_period_set_unrel(model, ctx, buf);
+	if (err) {
+		return err;
+	}
 
-	send_health_period_status(model, ctx);
+	return send_health_period_status(model, ctx);
 }
 
 const struct bt_mesh_model_op bt_mesh_health_srv_op[] = {
-	{ OP_HEALTH_FAULT_GET,         2,   health_fault_get },
-	{ OP_HEALTH_FAULT_CLEAR,       2,   health_fault_clear },
-	{ OP_HEALTH_FAULT_CLEAR_UNREL, 2,   health_fault_clear_unrel },
-	{ OP_HEALTH_FAULT_TEST,        3,   health_fault_test },
-	{ OP_HEALTH_FAULT_TEST_UNREL,  3,   health_fault_test_unrel },
-	{ OP_HEALTH_PERIOD_GET,        0,   health_period_get },
-	{ OP_HEALTH_PERIOD_SET,        1,   health_period_set },
-	{ OP_HEALTH_PERIOD_SET_UNREL,  1,   health_period_set_unrel },
-	{ OP_ATTENTION_GET,            0,   attention_get },
-	{ OP_ATTENTION_SET,            1,   attention_set },
-	{ OP_ATTENTION_SET_UNREL,      1,   attention_set_unrel },
+	{ OP_HEALTH_FAULT_GET,         BT_MESH_LEN_EXACT(2),   health_fault_get },
+	{ OP_HEALTH_FAULT_CLEAR,       BT_MESH_LEN_EXACT(2),   health_fault_clear },
+	{ OP_HEALTH_FAULT_CLEAR_UNREL, BT_MESH_LEN_EXACT(2),   health_fault_clear_unrel },
+	{ OP_HEALTH_FAULT_TEST,        BT_MESH_LEN_EXACT(3),   health_fault_test },
+	{ OP_HEALTH_FAULT_TEST_UNREL,  BT_MESH_LEN_EXACT(3),   health_fault_test_unrel },
+	{ OP_HEALTH_PERIOD_GET,        BT_MESH_LEN_EXACT(0),   health_period_get },
+	{ OP_HEALTH_PERIOD_SET,        BT_MESH_LEN_EXACT(1),   health_period_set },
+	{ OP_HEALTH_PERIOD_SET_UNREL,  BT_MESH_LEN_EXACT(1),   health_period_set_unrel },
+	{ OP_ATTENTION_GET,            BT_MESH_LEN_EXACT(0),   attention_get },
+	{ OP_ATTENTION_SET,            BT_MESH_LEN_EXACT(1),   attention_set },
+	{ OP_ATTENTION_SET_UNREL,      BT_MESH_LEN_EXACT(1),   attention_set_unrel },
 	BT_MESH_MODEL_OP_END,
 };
 
@@ -400,8 +435,8 @@ static int health_srv_init(struct bt_mesh_model *model)
 
 	model->pub->update = health_pub_update;
 
-	k_delayed_work_init(&srv->attn_timer, attention_off);
-	k_delayed_work_add_arg(&srv->attn_timer, srv);
+	k_work_init_delayable(&srv->attn_timer, attention_off);
+	k_work_add_arg_delayable(&srv->attn_timer, srv);
 
 	srv->model = model;
 
@@ -433,17 +468,8 @@ void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time)
 		srv = model->user_data;
 	}
 
-	if (time) {
-		if (srv->cb && srv->cb->attn_on) {
-			srv->cb->attn_on(model);
-		}
-
-		k_delayed_work_submit(&srv->attn_timer, time * 1000);
-	} else {
-		k_delayed_work_cancel(&srv->attn_timer);
-
-		if (srv->cb && srv->cb->attn_off) {
-			srv->cb->attn_off(model);
-		}
+	if ((time > 0) && srv->cb && srv->cb->attn_on) {
+		srv->cb->attn_on(model);
 	}
+	k_work_reschedule(&srv->attn_timer, K_SECONDS(time));
 }
diff --git a/nimble/host/mesh/src/heartbeat.c b/nimble/host/mesh/src/heartbeat.c
index 538a1cb662..faf2f76611 100644
--- a/nimble/host/mesh/src/heartbeat.c
+++ b/nimble/host/mesh/src/heartbeat.c
@@ -17,12 +17,22 @@
 #include "foundation.h"
 #include "mesh/glue.h"
 
+/* Heartbeat Publication information for persistent storage. */
+struct hb_pub_val {
+	uint16_t dst;
+	uint8_t  period;
+	uint8_t  ttl;
+	uint16_t feat;
+	uint16_t net_idx:12,
+		 indefinite:1;
+};
+
 struct bt_mesh_hb_cb hb_cb;
 
 static struct bt_mesh_hb_pub pub;
 static struct bt_mesh_hb_sub sub;
-static struct k_delayed_work sub_timer;
-static struct k_delayed_work pub_timer;
+static struct k_work_delayable sub_timer;
+static struct k_work_delayable pub_timer;
 
 static int64_t sub_remaining(void)
 {
@@ -30,13 +40,15 @@ static int64_t sub_remaining(void)
 		return 0U;
 	}
 
-	return k_delayed_work_remaining_get(&sub_timer) / MSEC_PER_SEC;
+	uint32_t rem_ms = k_ticks_to_ms_floor32(
+		k_work_delayable_remaining_get(&sub_timer));
+	return rem_ms / MSEC_PER_SEC;
 }
 
 static void hb_publish_end_cb(int err, void *cb_data)
 {
 	if (pub.period && pub.count > 1) {
-		k_delayed_work_submit(&pub_timer, K_SECONDS(pub.period));
+		k_work_reschedule(&pub_timer, K_SECONDS(pub.period));
 	}
 
 	if (pub.count != 0xffff) {
@@ -87,9 +99,11 @@ static int heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data)
 		.xmit = bt_mesh_net_transmit_get(),
 	};
 
-	/* Do nothing if heartbeat publication is not enabled */
-	if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
-		return 0U;
+	/* Do nothing if heartbeat publication is not enabled or the subnet is
+	 * removed.
+	 */
+	if (!tx.sub || pub.dst == BT_MESH_ADDR_UNASSIGNED) {
+		return 0;
 	}
 
 	hb.init_ttl = pub.ttl;
@@ -136,6 +150,11 @@ static void hb_publish(struct ble_npl_event *work)
 
 	BT_DBG("hb_pub.count: %u", pub.count);
 
+	/* Fast exit if disabled or expired */
+	if (pub.period == 0U || pub.count == 0U) {
+		return;
+	}
+
 	sub = bt_mesh_subnet_get(pub.net_idx);
 	if (!sub) {
 		BT_ERR("No matching subnet for idx 0x%02x", pub.net_idx);
@@ -143,10 +162,6 @@ static void hb_publish(struct ble_npl_event *work)
 		return;
 	}
 
-	if (pub.count == 0U) {
-		return;
-	}
-
 	err = heartbeat_send(&publish_cb, NULL);
 	if (err) {
 		hb_publish_end_cb(err, NULL);
@@ -173,8 +188,8 @@ int bt_mesh_hb_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
 		return 0;
 	}
 
-	if (!k_delayed_work_pending(&sub_timer)) {
-		BT_DBG("Heartbeat subscription period expired");
+	if (!k_work_delayable_is_pending(&sub_timer)) {
+		BT_DBG("Heartbeat subscription inactive");
 		return 0;
 	}
 
@@ -203,7 +218,7 @@ static void pub_disable(void)
 	pub.ttl = 0U;
 	pub.period = 0U;
 
-	k_delayed_work_cancel(&pub_timer);
+	k_work_cancel_delayable(&pub_timer);
 }
 
 uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *new_pub)
@@ -213,7 +228,8 @@ uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *new_pub)
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
 		    bt_mesh_is_provisioned()) {
-			bt_mesh_store_hb_pub();
+			bt_mesh_settings_store_schedule(
+				BT_MESH_SETTINGS_HB_PUB_PENDING);
 		}
 
 		return STATUS_SUCCESS;
@@ -234,15 +250,15 @@ uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *new_pub)
 	/* The first Heartbeat message shall be published as soon as possible
 	 * after the Heartbeat Publication Period state has been configured for
 	 * periodic publishing.
+	 *
+	 * If the new configuration disables publishing this flushes
+	 * the work item.
 	 */
-	if (pub.period && pub.count) {
-		k_delayed_work_submit(&pub_timer, K_NO_WAIT);
-	} else {
-		k_delayed_work_cancel(&pub_timer);
-	}
+	k_work_reschedule(&pub_timer, K_NO_WAIT);
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_hb_pub();
+		bt_mesh_settings_store_schedule(
+			BT_MESH_SETTINGS_HB_PUB_PENDING);
 	}
 
 	return STATUS_SUCCESS;
@@ -280,9 +296,7 @@ uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period)
 		sub.min_hops = 0U;
 		sub.max_hops = 0U;
 		sub.count = 0U;
-		sub.period = sub.period - sub_remaining();
-		k_delayed_work_cancel(&sub_timer);
-		notify_sub_end();
+		sub.period = 0U;
 	} else if (period) {
 		sub.src = src;
 		sub.dst = dst;
@@ -290,19 +304,26 @@ uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period)
 		sub.max_hops = 0U;
 		sub.count = 0U;
 		sub.period = period;
-		k_delayed_work_submit(&sub_timer, K_SECONDS(period));
 	} else {
 		/* Clearing the period should stop heartbeat subscription
 		 * without clearing the parameters, so we can still read them.
 		 */
-		sub.period = sub.period - sub_remaining();
-		k_delayed_work_cancel(&sub_timer);
-		notify_sub_end();
+		sub.period = 0U;
 	}
 
+	/* Start the timer, which notifies immediately if the new
+	 * configuration disables the subscription.
+	 */
+	k_work_reschedule(&sub_timer, K_SECONDS(sub.period));
+
 	return STATUS_SUCCESS;
 }
 
+void bt_mesh_hb_sub_reset_count(void)
+{
+	sub.count = 0;
+}
+
 void bt_mesh_hb_sub_get(struct bt_mesh_hb_sub *get)
 {
 	*get = sub;
@@ -325,27 +346,119 @@ void bt_mesh_hb_feature_changed(uint16_t features)
 void bt_mesh_hb_init(void)
 {
 	pub.net_idx = BT_MESH_KEY_UNUSED;
-	k_delayed_work_init(&pub_timer, hb_publish);
-	k_delayed_work_init(&sub_timer, sub_end);
+	k_work_init_delayable(&pub_timer, hb_publish);
+	k_work_init_delayable(&sub_timer, sub_end);
 }
 
 void bt_mesh_hb_start(void)
 {
 	if (pub.count && pub.period) {
 		BT_DBG("Starting heartbeat publication");
-		k_delayed_work_submit(&pub_timer, K_NO_WAIT);
+		k_work_reschedule(&pub_timer, K_NO_WAIT);
 	}
 }
 
 void bt_mesh_hb_suspend(void)
 {
-	k_delayed_work_cancel(&pub_timer);
+	(void)k_work_cancel_delayable(&pub_timer);
 }
 
 void bt_mesh_hb_resume(void)
 {
 	if (pub.period && pub.count) {
 		BT_DBG("Starting heartbeat publication");
-		k_delayed_work_submit(&pub_timer, K_NO_WAIT);
+		k_work_reschedule(&pub_timer, K_NO_WAIT);
+	}
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int hb_pub_set(int argc, char **argv, char *val)
+{
+	struct bt_mesh_hb_pub pub;
+	struct hb_pub_val hb_val;
+	int len, err;
+	BT_DBG("val %s", val ? val : "(null)");
+	len = sizeof(hb_val);
+	err = settings_bytes_from_str(val, &hb_val, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+	if (len != sizeof(hb_val)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len,
+		       sizeof(hb_val));
+		return -EINVAL;
+	}
+	pub.dst = hb_val.dst;
+	pub.period = bt_mesh_hb_pwr2(hb_val.period);
+	pub.ttl = hb_val.ttl;
+	pub.feat = hb_val.feat;
+	pub.net_idx = hb_val.net_idx;
+	if (hb_val.indefinite) {
+		pub.count = 0xffff;
+	} else {
+		pub.count = 0;
+	}
+	(void) bt_mesh_hb_pub_set(&pub);
+
+	BT_DBG("Restored heartbeat publication");
+
+	return 0;
+}
+
+void bt_mesh_hb_pub_pending_store(void)
+{
+	struct bt_mesh_hb_pub pub;
+	char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))];
+	struct hb_pub_val val;
+	int err;
+	char *str;
+
+	bt_mesh_hb_pub_get(&pub);
+	if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
+		err = settings_save_one("bt_mesh/HBPub", NULL);
+	} else {
+		val.indefinite = (pub.count == 0xffff);
+		val.dst = pub.dst;
+		val.period = bt_mesh_hb_log(pub.period);
+		val.ttl = pub.ttl;
+		val.feat = pub.feat;
+		val.net_idx = pub.net_idx;
+
+		str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
+		if (!str) {
+			BT_ERR("Unable to encode configuration as value");
+			return;
+		}
+
+		BT_DBG("Saving configuration as value %s", str);
+		err = settings_save_one("bt_mesh/HBPub", str);
+	}
+
+	if (err) {
+		BT_ERR("Failed to store Heartbeat Publication");
+	} else {
+		BT_DBG("Stored Heartbeat Publication");
 	}
 }
+
+static struct conf_handler bt_mesh_hb_pub_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = hb_pub_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_hb_pub_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_hb_pub_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_hb_pub conf");
+#endif
+}
diff --git a/nimble/host/mesh/src/heartbeat.h b/nimble/host/mesh/src/heartbeat.h
index c43683fef0..a036366637 100644
--- a/nimble/host/mesh/src/heartbeat.h
+++ b/nimble/host/mesh/src/heartbeat.h
@@ -38,3 +38,6 @@ void bt_mesh_hb_feature_changed(uint16_t features);
 
 uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *hb_pub);
 uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period);
+void bt_mesh_hb_sub_reset_count(void);
+void bt_mesh_hb_pub_pending_store(void);
+void bt_mesh_hb_pub_init(void);
diff --git a/nimble/host/mesh/src/light_model.c b/nimble/host/mesh/src/light_model.c
index bc4792c637..e7199519bb 100644
--- a/nimble/host/mesh/src/light_model.c
+++ b/nimble/host/mesh/src/light_model.c
@@ -55,4 +55,3 @@ int light_model_light_lightness_set(struct bt_mesh_model *model, int16_t lightne
 {
 	return light_model_gen_level_set(model, lightness);
 }
-
diff --git a/nimble/host/mesh/src/lpn.c b/nimble/host/mesh/src/lpn.c
index bd48c0883e..1d2f229e6b 100644
--- a/nimble/host/mesh/src/lpn.c
+++ b/nimble/host/mesh/src/lpn.c
@@ -148,6 +148,24 @@ static inline void group_clear(atomic_t *target, atomic_t *source)
 
 static void clear_friendship(bool force, bool disable);
 
+static int32_t poll_timeout(struct bt_mesh_lpn *lpn)
+{
+	/* If we're waiting for segment acks keep polling at high freq */
+	if (bt_mesh_tx_in_progress()) {
+		return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1));
+	}
+
+	if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) {
+		lpn->poll_timeout *= 2;
+		lpn->poll_timeout = min(lpn->poll_timeout,
+					POLL_TIMEOUT_MAX(lpn));
+	}
+
+	BT_DBG("Poll Timeout is %ums", (unsigned) lpn->poll_timeout);
+
+	return lpn->poll_timeout;
+}
+
 static void friend_clear_sent(int err, void *user_data)
 {
 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
@@ -164,7 +182,7 @@ static void friend_clear_sent(int err, void *user_data)
 	}
 
 	lpn_set_state(BT_MESH_LPN_CLEAR);
-	k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT);
+	k_work_reschedule(&lpn->timer, FRIEND_REQ_TIMEOUT);
 }
 
 static const struct bt_mesh_send_cb clear_sent_cb = {
@@ -211,7 +229,8 @@ static void clear_friendship(bool force, bool disable)
 
 	bt_mesh_rx_reset();
 
-	k_delayed_work_cancel(&lpn->timer);
+	lpn_set_state(BT_MESH_LPN_DISABLED);
+	k_work_cancel_delayable(&lpn->timer);
 
 	if (lpn->clear_success) {
 		lpn->old_friend = BT_MESH_ADDR_UNASSIGNED;
@@ -229,7 +248,7 @@ static void clear_friendship(bool force, bool disable)
 	lpn->recv_win = 0;
 	lpn->queue_size = 0;
 	lpn->disable = 0;
-	lpn->sent_req = 0;
+	lpn->sent_req = 0U;
 	lpn->established = 0;
 	lpn->clear_success = 0;
 	lpn->sub = NULL;
@@ -246,13 +265,12 @@ static void clear_friendship(bool force, bool disable)
 
 	bt_mesh_hb_feature_changed(BT_MESH_FEAT_LOW_POWER);
 
-	if (disable) {
+	if (!disable) {
 		lpn_set_state(BT_MESH_LPN_DISABLED);
+		k_work_reschedule(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
 		return;
 	}
 
-	lpn_set_state(BT_MESH_LPN_ENABLED);
-	k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
 }
 
 static void friend_req_sent(uint16_t duration, int err, void *user_data)
@@ -267,10 +285,10 @@ static void friend_req_sent(uint16_t duration, int err, void *user_data)
 	lpn->adv_duration = duration;
 
 	if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
-		k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT);
+		k_work_reschedule(&lpn->timer, FRIEND_REQ_WAIT);
 		lpn_set_state(BT_MESH_LPN_REQ_WAIT);
 	} else {
-		k_delayed_work_submit(&lpn->timer,
+		k_work_reschedule(&lpn->timer,
 				      duration + FRIEND_REQ_TIMEOUT);
 		lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
 	}
@@ -331,7 +349,7 @@ static void req_sent(uint16_t duration, int err, void *user_data)
 
 	if (err) {
 		BT_ERR("Sending request failed (err %d)", err);
-		lpn->sent_req = 0;
+		lpn->sent_req = 0U;
 		group_zero(lpn->pending);
 		return;
 	}
@@ -344,11 +362,11 @@ static void req_sent(uint16_t duration, int err, void *user_data)
 		/* We start scanning a bit early to elimitate risk of missing
 		 * response data due to HCI and other latencies.
 		 */
-		k_delayed_work_submit(&lpn->timer,
+		k_work_reschedule(&lpn->timer,
 				      LPN_RECV_DELAY - SCAN_LATENCY);
 	} else {
 		lpn_set_state(BT_MESH_LPN_WAIT_UPDATE);
-		k_delayed_work_submit(&lpn->timer,
+		k_work_reschedule(&lpn->timer,
 				      LPN_RECV_DELAY + duration +
 				      lpn->recv_win);
 	}
@@ -441,7 +459,7 @@ int bt_mesh_lpn_set(bool enable)
 	} else {
 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO) &&
 		    lpn->state == BT_MESH_LPN_TIMER) {
-			k_delayed_work_cancel(&lpn->timer);
+			k_work_cancel_delayable(&lpn->timer);
 			lpn_set_state(BT_MESH_LPN_DISABLED);
 		} else {
 			bt_mesh_lpn_disable(false);
@@ -459,11 +477,14 @@ static void friend_response_received(struct bt_mesh_lpn *lpn)
 		lpn->fsn++;
 	}
 
-	k_delayed_work_cancel(&lpn->timer);
 	bt_mesh_scan_disable();
 	lpn_set_state(BT_MESH_LPN_ESTABLISHED);
 	lpn->req_attempts = 0;
-	lpn->sent_req = 0;
+	lpn->sent_req = 0U;
+
+	int32_t timeout = poll_timeout(lpn);
+
+	k_work_reschedule(&lpn->timer, K_MSEC(timeout));
 }
 
 void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
@@ -472,7 +493,19 @@ void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
 
 	if (lpn->state == BT_MESH_LPN_TIMER) {
 		BT_DBG("Restarting establishment timer");
-		k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
+		k_work_reschedule(&lpn->timer, LPN_AUTO_TIMEOUT);
+		return;
+	}
+
+	if (lpn->state != BT_MESH_LPN_WAIT_UPDATE) {
+		return;
+	}
+
+	/* If the message was a Friend control message, it's possible that a
+	 * Poll was already queued for sending. In this case, we're already in
+	 * a different state.
+	 */
+	if (lpn->state != BT_MESH_LPN_WAIT_UPDATE) {
 		return;
 	}
 
@@ -545,7 +578,6 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
 		}
 	}
 	/* TODO: Add offer acceptance criteria check */
-	k_delayed_work_cancel(&lpn->timer);
 
 	lpn->recv_win = msg->recv_win;
 	lpn->queue_size = msg->queue_size;
@@ -730,7 +762,7 @@ static void update_timeout(struct bt_mesh_lpn *lpn)
 		BT_WARN("No response from Friend during ReceiveWindow");
 		bt_mesh_scan_disable();
 		lpn_set_state(BT_MESH_LPN_ESTABLISHED);
-		k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT);
+		k_work_reschedule(&lpn->timer, POLL_RETRY_TIMEOUT);
 	} else {
 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
 			bt_mesh_scan_disable();
@@ -738,7 +770,7 @@ static void update_timeout(struct bt_mesh_lpn *lpn)
 
 		if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) {
 			BT_WARN("Retrying first Friend Poll");
-			lpn->sent_req = 0;
+			lpn->sent_req = 0U;
 			if (send_friend_poll() == 0) {
 				return;
 			}
@@ -775,7 +807,7 @@ static void lpn_timeout(struct ble_npl_event *work)
 		break;
 	case BT_MESH_LPN_REQ_WAIT:
 		bt_mesh_scan_enable();
-		k_delayed_work_submit(&lpn->timer,
+		k_work_reschedule(&lpn->timer,
 				      lpn->adv_duration + FRIEND_REQ_SCAN);
 		lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
 		break;
@@ -787,13 +819,13 @@ static void lpn_timeout(struct ble_npl_event *work)
 		lpn->lpn_counter++;
 		lpn_set_state(BT_MESH_LPN_ENABLED);
 		lpn->sent_req = 0U;
-		k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
+		k_work_reschedule(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
 		break;
 	case BT_MESH_LPN_ESTABLISHED:
 		if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) {
 			uint8_t req = lpn->sent_req;
 
-			lpn->sent_req = 0;
+			lpn->sent_req = 0U;
 
 			if (!req || req == TRANS_CTL_OP_FRIEND_POLL) {
 				send_friend_poll();
@@ -810,7 +842,7 @@ static void lpn_timeout(struct ble_npl_event *work)
 		clear_friendship(false, false);
 		break;
 	case BT_MESH_LPN_RECV_DELAY:
-		k_delayed_work_submit(&lpn->timer,
+		k_work_reschedule(&lpn->timer,
 				      lpn->adv_duration + SCAN_LATENCY +
 				      lpn->recv_win);
 		bt_mesh_scan_enable();
@@ -856,24 +888,6 @@ void bt_mesh_lpn_group_del(uint16_t *groups, size_t group_count)
 	sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
 }
 
-static int32_t poll_timeout(struct bt_mesh_lpn *lpn)
-{
-	/* If we're waiting for segment acks keep polling at high freq */
-	if (bt_mesh_tx_in_progress()) {
-		return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1));
-	}
-
-	if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) {
-		lpn->poll_timeout *= 2;
-		lpn->poll_timeout = min(lpn->poll_timeout,
-					POLL_TIMEOUT_MAX(lpn));
-	}
-
-	BT_DBG("Poll Timeout is %ums", (unsigned) lpn->poll_timeout);
-
-	return lpn->poll_timeout;
-}
-
 int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
 			       struct os_mbuf *buf)
 {
@@ -932,12 +946,6 @@ int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
 		send_friend_poll();
 	}
 
-	if (!lpn->sent_req) {
-		int32_t timeout = poll_timeout(lpn);
-
-		k_delayed_work_submit(&lpn->timer, K_MSEC(timeout));
-	}
-
 	return 0;
 }
 
@@ -972,7 +980,7 @@ int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
 
 	if (!lpn->established) {
 		/* This is normally checked on the transport layer, however
-		 * in this state we're also still accepting master
+		 * in this state we're also still accepting flooding
 		 * credentials so we need to ensure the right ones (Friend
 		 * Credentials) were used for this message.
 		 */
@@ -1020,12 +1028,6 @@ int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
 		send_friend_poll();
 	}
 
-	if (!lpn->sent_req) {
-		int32_t timeout = poll_timeout(lpn);
-
-		k_delayed_work_submit(&lpn->timer, K_MSEC(timeout));
-	}
-
 	return 0;
 }
 
@@ -1073,9 +1075,7 @@ int bt_mesh_lpn_init(void)
 
 	BT_DBG("");
 
-	lpn->groups_changed = 0;
-
-	k_delayed_work_init(&lpn->timer, lpn_timeout);
+	k_work_init_delayable(&lpn->timer, lpn_timeout);
 
 	if (lpn->state == BT_MESH_LPN_ENABLED) {
 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
@@ -1091,7 +1091,7 @@ int bt_mesh_lpn_init(void)
 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO)) {
 			BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT);
 			lpn_set_state(BT_MESH_LPN_TIMER);
-			k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
+			k_work_reschedule(&lpn->timer, LPN_AUTO_TIMEOUT);
 		}
 	}
 
diff --git a/nimble/host/mesh/src/lpn.h b/nimble/host/mesh/src/lpn.h
index 90de2ca086..cb2e1f2fef 100644
--- a/nimble/host/mesh/src/lpn.h
+++ b/nimble/host/mesh/src/lpn.h
@@ -47,15 +47,6 @@ static inline bool bt_mesh_lpn_waiting_update(void)
 #endif
 }
 
-static inline bool bt_mesh_lpn_timer(void)
-{
-#if MYNEWT_VAL(BLE_MESH_LOW_POWER) && MYNEWT_VAL(BLE_MESH_LPN_AUTO)
-	return (bt_mesh.lpn.state == BT_MESH_LPN_TIMER);
-#else
-	return false;
-#endif
-}
-
 void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx);
 
 void bt_mesh_lpn_group_add(uint16_t group);
diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c
index bbb4ee3d22..6ab71e8b11 100644
--- a/nimble/host/mesh/src/mesh.c
+++ b/nimble/host/mesh/src/mesh.c
@@ -35,6 +35,7 @@
 #include "shell.h"
 #include "mesh_priv.h"
 #include "settings.h"
+#include "pb_gatt_srv.h"
 
 
 uint8_t g_mesh_addr_type;
@@ -44,7 +45,6 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
 		      uint8_t flags, uint32_t iv_index, uint16_t addr,
 		      const uint8_t dev_key[16])
 {
-	bool pb_gatt_enabled;
 	int err;
 
 	BT_INFO("Primary Element: 0x%04x", addr);
@@ -55,32 +55,16 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
 		return -EALREADY;
 	}
 
-	if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) {
-		if (bt_mesh_proxy_prov_disable(false) == 0) {
-			pb_gatt_enabled = true;
-		} else {
-			pb_gatt_enabled = false;
-		}
-	} else {
-		pb_gatt_enabled = false;
-	}
-
 	/*
 	 * FIXME:
 	 * Should net_key and iv_index be over-ridden?
 	 */
-	if (IS_ENABLED(BLE_MESH_CDB)) {
+	if (IS_ENABLED(CONFIG_BT_MESH_CDB) &&
+	    atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID)) {
 		const struct bt_mesh_comp *comp;
 		const struct bt_mesh_prov *prov;
 		struct bt_mesh_cdb_node *node;
 
-		if (!atomic_test_bit(bt_mesh_cdb.flags,
-				     BT_MESH_CDB_VALID)) {
-			BT_ERR("No valid network");
-			atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
-			return -EINVAL;
-		}
-
 		comp = bt_mesh_comp_get();
 		if (comp == NULL) {
 			BT_ERR("Failed to get node composition");
@@ -108,7 +92,7 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
 		memcpy(node->dev_key, dev_key, 16);
 
 		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			bt_mesh_store_cdb_node(node);
+			bt_mesh_cdb_node_store(node);
 		}
 	}
 
@@ -116,10 +100,6 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
 	if (err) {
 		atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
 
-		if (MYNEWT_VAL(BLE_MESH_PB_GATT)  && pb_gatt_enabled) {
-			bt_mesh_proxy_prov_enable();
-		}
-
 		return err;
 	}
 
@@ -134,6 +114,10 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
 			bt_mesh_lpn_group_add(BT_MESH_ADDR_ALL_NODES);
 	}
 
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_net_pending_net_store();
+	}
+
 	bt_mesh_start();
 
 	return 0;
@@ -166,14 +150,15 @@ void bt_mesh_reset(void)
 	}
 
 	bt_mesh.iv_index = 0U;
+	bt_mesh.ivu_duration = 0;
 	bt_mesh.seq = 0U;
 
 	memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags));
 
-	k_delayed_work_cancel(&bt_mesh.ivu_timer);
-
-	bt_mesh_cfg_reset();
+	k_work_cancel_delayable(&bt_mesh.ivu_timer);
 
+	bt_mesh_model_reset();
+	bt_mesh_cfg_default_set();
 	bt_mesh_trans_reset();
 	bt_mesh_app_keys_reset();
 	bt_mesh_net_keys_reset();
@@ -195,11 +180,11 @@ void bt_mesh_reset(void)
 	}
 
 	if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
-		bt_mesh_proxy_gatt_disable();
+		(void)bt_mesh_proxy_gatt_disable();
 	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_clear_net();
+		bt_mesh_net_clear();
 	}
 
 	memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
@@ -236,7 +221,10 @@ static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 {
 	if (mod->pub && mod->pub->update) {
 		mod->pub->count = 0;
-		k_delayed_work_cancel(&mod->pub->timer);
+		/* If this fails, the work handler will check the suspend call
+		 * and exit without transmitting.
+		 */
+		(void)k_work_cancel_delayable(&mod->pub->timer);
 	}
 }
 
@@ -277,7 +265,8 @@ static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 		int32_t period_ms = bt_mesh_model_pub_period_get(mod);
 
 		if (period_ms) {
-			k_delayed_work_submit(&mod->pub->timer, period_ms);
+			k_work_reschedule(&mod->pub->timer,
+					  K_MSEC(period_ms));
 		}
 	}
 }
@@ -338,7 +327,18 @@ int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov,
 	}
 #endif
 
-	bt_mesh_cfg_init();
+	bt_mesh_app_key_init();
+	bt_mesh_access_init();
+	bt_mesh_hb_pub_init();
+	bt_mesh_rpl_init();
+	bt_mesh_net_key_init();
+#if CONFIG_BT_MESH_LABEL_COUNT > 0
+	bt_mesh_va_init();
+#endif
+#if CONFIG_BT_MESH_CDB
+	bt_mesh_cdb_init();
+#endif
+	bt_mesh_cfg_default_set();
 	bt_mesh_net_init();
 	bt_mesh_trans_init();
 	bt_mesh_hb_init();
@@ -365,16 +365,30 @@ static void model_start(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 
 int bt_mesh_start(void)
 {
+	int err;
+
+	err = bt_mesh_adv_enable();
+	if (err) {
+		BT_ERR("Failed enabling advertiser");
+		return err;
+	}
+
 	if (bt_mesh_beacon_enabled()) {
 		bt_mesh_beacon_enable();
 	} else {
 		bt_mesh_beacon_disable();
 	}
 
-	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
-	    bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED) {
-		bt_mesh_proxy_gatt_enable();
-		bt_mesh_adv_update();
+	if (!IS_ENABLED(CONFIG_BT_MESH_PROV) || !bt_mesh_prov_active() ||
+	bt_mesh_prov_link.bearer->type == BT_MESH_PROV_ADV) {
+		if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
+			(void)bt_mesh_pb_gatt_disable();
+		}
+
+		if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+			(void)bt_mesh_proxy_gatt_enable();
+			bt_mesh_adv_update();
+		}
 	}
 
 	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
diff --git a/nimble/host/mesh/src/model_cli.c b/nimble/host/mesh/src/model_cli.c
index 9755ec1fd9..22f9b99ee8 100644
--- a/nimble/host/mesh/src/model_cli.c
+++ b/nimble/host/mesh/src/model_cli.c
@@ -26,7 +26,7 @@ struct gen_level_param {
     int16_t *level;
 };
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -41,7 +41,7 @@ static void gen_onoff_status(struct bt_mesh_model *model,
 
 	if (cli->op_pending != OP_GEN_ONOFF_STATUS) {
 		BT_WARN("Unexpected Generic OnOff Status message");
-		return;
+		return -ENOENT;
 	}
 
 	param = cli->op_param;
@@ -54,9 +54,11 @@ static void gen_onoff_status(struct bt_mesh_model *model,
 	BT_DBG("state: %d", state);
 
 	k_sem_give(&cli->op_sync);
+
+	return 0;
 }
 
-static void gen_level_status(struct bt_mesh_model *model,
+static int gen_level_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx,
 			     struct os_mbuf *buf)
 {
@@ -71,7 +73,7 @@ static void gen_level_status(struct bt_mesh_model *model,
 
 	if (cli->op_pending != OP_GEN_LEVEL_STATUS) {
 		BT_WARN("Unexpected Generic LEVEL Status message");
-		return;
+		return -EINVAL;
 	}
 
 	param = cli->op_param;
@@ -84,6 +86,8 @@ static void gen_level_status(struct bt_mesh_model *model,
 	BT_DBG("level: %d", level);
 
 	k_sem_give(&cli->op_sync);
+
+	return 0;
 }
 
 const struct bt_mesh_model_op gen_onoff_cli_op[] = {
@@ -298,4 +302,3 @@ int bt_mesh_gen_level_set(uint16_t net_idx, uint16_t addr, uint16_t app_idx,
 	os_mbuf_free_chain(msg);
 	return err;
 }
-
diff --git a/nimble/host/mesh/src/model_srv.c b/nimble/host/mesh/src/model_srv.c
index b6f34360aa..96981cbab6 100644
--- a/nimble/host/mesh/src/model_srv.c
+++ b/nimble/host/mesh/src/model_srv.c
@@ -15,9 +15,10 @@ static struct bt_mesh_gen_onoff_srv *gen_onoff_srv;
 static struct bt_mesh_gen_level_srv *gen_level_srv;
 static struct bt_mesh_light_lightness_srv *light_lightness_srv;
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx)
 {
+	int err;
 	struct bt_mesh_gen_onoff_srv *cb = model->user_data;
 	struct os_mbuf *msg = NET_BUF_SIMPLE(3);
 	uint8_t *state;
@@ -30,23 +31,26 @@ static void gen_onoff_status(struct bt_mesh_model *model,
 
 	BT_DBG("state: %d", *state);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (err) {
 		BT_ERR("Send status failed");
 	}
 
 	os_mbuf_free_chain(msg);
+
+	return err;
 }
 
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	BT_DBG("");
 
-	gen_onoff_status(model, ctx);
+	return gen_onoff_status(model, ctx);
 }
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf)
 {
@@ -58,23 +62,26 @@ static void gen_onoff_set_unack(struct bt_mesh_model *model,
 	BT_DBG("state: %d", state);
 
 	if (cb && cb->set) {
-		cb->set(model, state);
+		return cb->set(model, state);
 	}
+
+	return 0;
 }
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	BT_DBG("");
 
 	gen_onoff_set_unack(model, ctx, buf);
-	gen_onoff_status(model, ctx);
+	return gen_onoff_status(model, ctx);
 }
 
-static void gen_level_status(struct bt_mesh_model *model,
+static int gen_level_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx)
 {
+	int err;
 	struct bt_mesh_gen_level_srv *cb = model->user_data;
 	struct os_mbuf *msg = NET_BUF_SIMPLE(4);
 	int16_t *level;
@@ -87,23 +94,25 @@ static void gen_level_status(struct bt_mesh_model *model,
 
 	BT_DBG("level: %d", *level);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (err) {
 		BT_ERR("Send status failed");
 	}
 
 	os_mbuf_free_chain(msg);
+	return err;
 }
 
-static void gen_level_get(struct bt_mesh_model *model,
+static int gen_level_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	BT_DBG("");
 
-	gen_level_status(model, ctx);
+	return gen_level_status(model, ctx);
 }
 
-static void gen_level_set_unack(struct bt_mesh_model *model,
+static int gen_level_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf) {
 	struct bt_mesh_gen_level_srv *cb = model->user_data;
@@ -113,21 +122,24 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 	BT_DBG("level: %d", level);
 
 	if (cb && cb->set) {
-		cb->set(model, level);
+		return cb->set(model, level);
 	}
+
+	return 0;
 }
 
-static void gen_level_set(struct bt_mesh_model *model,
+static int gen_level_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	gen_level_set_unack(model, ctx, buf);
-	gen_level_status(model, ctx);
+	return gen_level_status(model, ctx);
 }
 
-static void light_lightness_status(struct bt_mesh_model *model,
+static int light_lightness_status(struct bt_mesh_model *model,
 			     struct bt_mesh_msg_ctx *ctx)
 {
+	int err;
 	struct bt_mesh_light_lightness_srv *cb = model->user_data;
 	struct os_mbuf *msg = NET_BUF_SIMPLE(4);
 	int16_t *lightness;
@@ -140,23 +152,25 @@ static void light_lightness_status(struct bt_mesh_model *model,
 
 	BT_DBG("lightness: %d", *lightness);
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+	err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+	if (err) {
 		BT_ERR("Send status failed");
 	}
 
 	os_mbuf_free_chain(msg);
+	return err;
 }
 
-static void light_lightness_get(struct bt_mesh_model *model,
+static int light_lightness_get(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	BT_DBG("");
 
-	light_lightness_status(model, ctx);
+	return light_lightness_status(model, ctx);
 }
 
-static void light_lightness_set_unack(struct bt_mesh_model *model,
+static int light_lightness_set_unack(struct bt_mesh_model *model,
 				struct bt_mesh_msg_ctx *ctx,
 				struct os_mbuf *buf) {
 	struct bt_mesh_light_lightness_srv *cb = model->user_data;
@@ -166,16 +180,18 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
 	BT_DBG("lightness: %d", lightness);
 
 	if (cb && cb->set) {
-		cb->set(model, lightness);
+		return cb->set(model, lightness);
 	}
+
+	return 0;
 }
 
-static void light_lightness_set(struct bt_mesh_model *model,
+static int light_lightness_set(struct bt_mesh_model *model,
 			  struct bt_mesh_msg_ctx *ctx,
 			  struct os_mbuf *buf)
 {
 	light_lightness_set_unack(model, ctx, buf);
-	light_lightness_status(model, ctx);
+	return light_lightness_status(model, ctx);
 }
 
 const struct bt_mesh_model_op gen_onoff_srv_op[] = {
diff --git a/nimble/host/mesh/src/msg.c b/nimble/host/mesh/src/msg.c
new file mode 100644
index 0000000000..b788ffe1e9
--- /dev/null
+++ b/nimble/host/mesh/src/msg.c
@@ -0,0 +1,84 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "mesh/mesh.h"
+
+void bt_mesh_model_msg_init(struct os_mbuf *msg, uint32_t opcode)
+{
+	net_buf_simple_init(msg, 0);
+
+	switch (BT_MESH_MODEL_OP_LEN(opcode)) {
+	case 1:
+		net_buf_simple_add_u8(msg, opcode);
+		break;
+	case 2:
+		net_buf_simple_add_be16(msg, opcode);
+		break;
+	case 3:
+		net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff));
+		/* Using LE for the CID since the model layer is defined as
+		 * little-endian in the mesh spec and using BT_MESH_MODEL_OP_3
+		 * will declare the opcode in this way.
+		 */
+		net_buf_simple_add_le16(msg, opcode & 0xffff);
+		break;
+	default:
+		BT_WARN("Unknown opcode format");
+		break;
+	}
+}
+
+void bt_mesh_msg_ack_ctx_clear(struct bt_mesh_msg_ack_ctx *ack)
+{
+	ack->op = 0U;
+	ack->user_data = NULL;
+	ack->dst = BT_MESH_ADDR_UNASSIGNED;
+}
+
+int bt_mesh_msg_ack_ctx_prepare(struct bt_mesh_msg_ack_ctx *ack,
+	uint32_t op, uint16_t dst, void *user_data)
+{
+	if (ack->op) {
+		BT_WARN("Another synchronous operation pending");
+		return -EBUSY;
+	}
+
+	ack->op = op;
+	ack->user_data = user_data;
+	ack->dst = dst;
+
+	return 0;
+}
+
+int bt_mesh_msg_ack_ctx_wait(struct bt_mesh_msg_ack_ctx *ack, int32_t timeout)
+{
+	int err;
+
+	err = k_sem_take(&ack->sem, timeout);
+	bt_mesh_msg_ack_ctx_clear(ack);
+
+	if (err == -EAGAIN) {
+		return -ETIMEDOUT;
+	}
+
+	return err;
+}
+
+bool bt_mesh_msg_ack_ctx_match(const struct bt_mesh_msg_ack_ctx *ack,
+	uint32_t op, uint16_t addr, void **user_data)
+{
+	if (ack->op != op || (BT_MESH_ADDR_IS_UNICAST(ack->dst) && ack->dst != addr)) {
+		return false;
+	}
+
+	if (user_data != NULL) {
+		*user_data = ack->user_data;
+	}
+
+	return true;
+}
diff --git a/nimble/host/mesh/src/net.c b/nimble/host/mesh/src/net.c
index 8b86935d31..229e40546e 100644
--- a/nimble/host/mesh/src/net.c
+++ b/nimble/host/mesh/src/net.c
@@ -34,20 +34,12 @@
 #include "mesh/glue.h"
 #include "mesh/slist.h"
 
-/* Minimum valid Mesh Network PDU length. The Network headers
- * themselves take up 9 bytes. After that there is a minumum of 1 byte
- * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1
- * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least
- * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes.
- */
-#define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8)
-
 #define LOOPBACK_MAX_PDU_LEN (BT_MESH_NET_HDR_LEN + 16)
 #define LOOPBACK_USER_DATA_SIZE sizeof(struct bt_mesh_subnet *)
 #define LOOPBACK_BUF_SUB(buf) (*(struct bt_mesh_subnet **)net_buf_user_data(buf))
 
 /* Seq limit after IV Update is triggered */
-#define IV_UPDATE_SEQ_LIMIT 8000000
+#define IV_UPDATE_SEQ_LIMIT CONFIG_BT_MESH_IV_UPDATE_SEQ_LIMIT
 
 #define IVI(pdu)           ((pdu)[0] >> 7)
 #define NID(pdu)           ((pdu)[0] & 0x7f)
@@ -57,6 +49,31 @@
 #define SRC(pdu)           (sys_get_be16(&(pdu)[5]))
 #define DST(pdu)           (sys_get_be16(&(pdu)[7]))
 
+/** Define CONFIG_BT_MESH_SEQ_STORE_RATE even if settings are disabled to
+ * compile the code.
+ */
+#ifndef CONFIG_BT_SETTINGS
+#define CONFIG_BT_MESH_SEQ_STORE_RATE 1
+#endif
+
+/* Mesh network information for persistent storage. */
+struct net_val {
+	uint16_t primary_addr;
+	uint8_t  dev_key[16];
+} __packed;
+
+/* Sequence number information for persistent storage. */
+struct seq_val {
+	uint8_t val[3];
+} __packed;
+
+/* IV Index & IV Update information for persistent storage. */
+struct iv_val {
+	uint32_t iv_index;
+	uint8_t  iv_update:1,
+	iv_duration:7;
+} __packed;
+
 static struct {
 	uint32_t src : 15, /* MSb of source is always 0 */
 	      seq : 17;
@@ -68,6 +85,15 @@ struct bt_mesh_net bt_mesh = {
 	.local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue),
 };
 
+/* Mesh Profile Specification 3.10.6
+ * The node shall not execute more than one IV Index Recovery within a period of
+ * 192 hours.
+ *
+ * Mark that the IV Index Recovery has been done to prevent two recoveries to be
+ * done before a normal IV Index update has been completed within 96h+96h.
+ */
+static bool ivi_was_recovered;
+
 static struct os_mbuf_pool loopback_os_mbuf_pool;
 static struct os_mempool loopback_buf_mempool;
 os_membuf_t loopback_mbuf_membuf[
@@ -120,6 +146,30 @@ static void msg_cache_add(struct bt_mesh_net_rx *rx)
 	msg_cache_next %= ARRAY_SIZE(msg_cache);
 }
 
+static void store_iv(bool only_duration)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_IV_PENDING);
+
+	if (!only_duration) {
+		/* Always update Seq whenever IV changes */
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
+	}
+#endif
+}
+
+static void store_seq(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	if (CONFIG_BT_MESH_SEQ_STORE_RATE > 1 &&
+	(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) {
+		return;
+	}
+
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
+#endif
+}
+
 int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
 		       uint32_t iv_index)
 {
@@ -147,17 +197,18 @@ int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
 	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS,
 			  BT_MESH_IV_UPDATE(flags));
 
-	/* Set minimum required hours, since the 96-hour minimum requirement
-	 * doesn't apply straight after provisioning (since we can't know how
-	 * long has actually passed since the network changed its state).
+	/* If IV Update is already in progress, set minimum required hours,
+	 * since the 96-hour minimum requirement doesn't apply in this case straight
+	 * after provisioning.
 	 */
-	bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
+	if (BT_MESH_IV_UPDATE(flags)) {
+		bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
+	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		BT_DBG("Storing network information persistently");
-		bt_mesh_store_net();
-		bt_mesh_store_subnet(idx);
-		bt_mesh_store_iv(false);
+		bt_mesh_subnet_store(idx);
+		store_iv(false);
 	}
 
 	return 0;
@@ -221,24 +272,24 @@ bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update)
 			return false;
 		}
 
-		if (iv_index > bt_mesh.iv_index + 1) {
+		if ((iv_index > bt_mesh.iv_index + 1) ||
+		(iv_index == bt_mesh.iv_index + 1 && !iv_update)) {
+			if (ivi_was_recovered) {
+				BT_ERR("IV Index Recovery before minimum delay");
+				return false;
+			}
+			/* The Mesh profile specification allows to initiate an
+			 * IV Index Recovery procedure if previous IV update has
+			 * been missed. This allows the node to remain
+			 * functional.
+			 */
 			BT_WARN("Performing IV Index Recovery");
+			ivi_was_recovered = true;
 			bt_mesh_rpl_clear();
 			bt_mesh.iv_index = iv_index;
 			bt_mesh.seq = 0;
 			goto do_update;
 		}
-
-		if (iv_index == bt_mesh.iv_index + 1 && !iv_update) {
-			BT_WARN("Ignoring new index in normal mode");
-			return false;
-		}
-
-		if (!iv_update) {
-			/* Nothing to do */
-			BT_DBG("Already in Normal state");
-			return false;
-		}
 	}
 
 	if (!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) &&
@@ -256,41 +307,42 @@ bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update)
 		return false;
 	}
 
-do_update:
-	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update);
-	bt_mesh.ivu_duration = 0U;
-
 	if (iv_update) {
 		bt_mesh.iv_index = iv_index;
 		BT_DBG("IV Update state entered. New index 0x%08x",
 		       (unsigned) bt_mesh.iv_index);
 
 		bt_mesh_rpl_reset();
+		ivi_was_recovered = false;
 	} else {
 		BT_DBG("Normal mode entered");
 		bt_mesh.seq = 0;
 	}
 
-	k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+do_update:
+	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update);
+	bt_mesh.ivu_duration = 0U;
+
+	k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
 
 	/* Notify other modules */
 	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
 		bt_mesh_friend_sec_update(BT_MESH_KEY_ANY);
 	}
 
+	bt_mesh_subnet_foreach(bt_mesh_beacon_update);
+
 	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
 	    bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
 		bt_mesh_proxy_beacon_send(NULL);
 	}
 
-	bt_mesh_subnet_foreach(bt_mesh_beacon_update);
-
 	if (MYNEWT_VAL(BLE_MESH_CDB)) {
 		bt_mesh_cdb_iv_update(iv_index, iv_update);
 	}
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_iv(false);
+		store_iv(false);
 	}
 
 	return true;
@@ -301,7 +353,7 @@ uint32_t bt_mesh_next_seq(void)
 	uint32_t seq = bt_mesh.seq++;
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_seq();
+		store_seq();
 	}
 
 	if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) &&
@@ -346,14 +398,14 @@ static void bt_mesh_net_local(struct ble_npl_event *work)
 		       rx.ctx.addr, rx.seq, sub);
 
 		(void) bt_mesh_trans_recv(buf, &rx);
-		net_buf_unref(buf);
+		os_mbuf_free_chain(buf);
 	}
 }
 
 static const struct bt_mesh_net_cred *net_tx_cred_get(struct bt_mesh_net_tx *tx)
 {
-#if defined(BLE_MESH_LOW_POWER)
-	if (tx->friend_cred && bt_mesh_lpn_established()) {
+#if IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)
+	if (tx->friend_cred && bt_mesh.lpn.frnd) {
 		return &bt_mesh.lpn.cred[SUBNET_KEY_TX_IDX(tx->sub)];
 	}
 #endif
@@ -428,7 +480,7 @@ static int loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data,
 {
 	struct os_mbuf *buf;
 
-	buf = os_mbuf_get_pkthdr(&loopback_os_mbuf_pool, 0);
+	buf = os_mbuf_get_pkthdr(&loopback_os_mbuf_pool, BT_MESH_NET_HDR_LEN);
 	if (!buf) {
 		BT_WARN("Unable to allocate loopback");
 		return -ENOMEM;
@@ -470,7 +522,7 @@ int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 
 	/* Deliver to local network interface if necessary */
 	if (bt_mesh_fixed_group_match(tx->ctx->addr) ||
-	    bt_mesh_elem_find(tx->ctx->addr)) {
+	    bt_mesh_has_addr(tx->ctx->addr)) {
 		err = loopback(tx, buf->om_data, buf->om_len);
 
 		/* Local unicast messages should not go out to network */
@@ -498,12 +550,13 @@ int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 		goto done;
 	}
 
+	BT_MESH_ADV(buf)->cb = cb;
+	BT_MESH_ADV(buf)->cb_data = cb_data;
+
 	/* Deliver to GATT Proxy Clients if necessary. */
 	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
 	    bt_mesh_proxy_relay(buf, tx->ctx->addr) &&
 	    BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
-		/* Notify completion if this only went through the Mesh Proxy */
-		send_cb_finalize(cb, cb_data);
 
 		err = 0;
 		goto done;
@@ -567,7 +620,7 @@ static bool net_decrypt(struct bt_mesh_net_rx *rx, struct os_mbuf *in,
 		return false;
 	}
 
-	if (bt_mesh_elem_find(rx->ctx.addr)) {
+	if (bt_mesh_has_addr(rx->ctx.addr)) {
 		BT_DBG("Dropping locally originated packet");
 		return false;
 	}
@@ -665,7 +718,7 @@ static void bt_mesh_net_relay(struct os_mbuf *sbuf,
 	       bt_hex(buf->om_data, buf->om_len));
 
 	/* When the Friend node relays message for lpn, the message will be
-	 * retransmitted using the managed master security credentials and
+	 * retransmitted using the managed flooding security credentials and
 	 * the Network PDU shall be retransmitted to all network interfaces.
 	 */
 	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
@@ -702,6 +755,11 @@ int bt_mesh_net_decode(struct os_mbuf *in, enum bt_mesh_net_if net_if,
 		return -EINVAL;
 	}
 
+	if (in->om_len > BT_MESH_NET_MAX_PDU_LEN) {
+		BT_WARN("Dropping too long mesh packet (len %u)", in->om_len);
+		return -EINVAL;
+	}
+
 	if (net_if == BT_MESH_NET_IF_ADV && check_dup(in)) {
 		BT_DBG("duplicate packet; dropping %u bytes: %s", in->om_len,
 			   bt_hex(in->om_data, in->om_len));
@@ -754,7 +812,7 @@ int bt_mesh_net_decode(struct os_mbuf *in, enum bt_mesh_net_if net_if,
 void bt_mesh_net_recv(struct os_mbuf *data, int8_t rssi,
 		      enum bt_mesh_net_if net_if)
 {
-	struct os_mbuf *buf = NET_BUF_SIMPLE(29);
+	struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_NET_MAX_PDU_LEN);
 	struct bt_mesh_net_rx rx = { .ctx.recv_rssi = rssi };
 	struct net_buf_simple_state state;
 
@@ -773,7 +831,7 @@ void bt_mesh_net_recv(struct os_mbuf *data, int8_t rssi,
 	net_buf_simple_save(buf, &state);
 
 	rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) ||
-			  bt_mesh_elem_find(rx.ctx.recv_dst));
+			  bt_mesh_has_addr(rx.ctx.recv_dst));
 
 	if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY)) &&
 	    net_if == BT_MESH_NET_IF_PROXY) {
@@ -815,6 +873,10 @@ void bt_mesh_net_recv(struct os_mbuf *data, int8_t rssi,
 
 static void ivu_refresh(struct ble_npl_event *work)
 {
+	if (!bt_mesh_is_provisioned()) {
+		return;
+	}
+
 	bt_mesh.ivu_duration = MIN(UINT8_MAX,
 	       bt_mesh.ivu_duration + BT_MESH_IVU_HOURS);
 
@@ -825,10 +887,10 @@ static void ivu_refresh(struct ble_npl_event *work)
 
 	if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
 		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			bt_mesh_store_iv(true);
+			store_iv(true);
 		}
 
-		k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+		k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
 		return;
 	}
 
@@ -836,15 +898,169 @@ static void ivu_refresh(struct ble_npl_event *work)
 		bt_mesh_beacon_ivu_initiator(true);
 		bt_mesh_net_iv_update(bt_mesh.iv_index, false);
 	} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_iv(true);
+		store_iv(true);
 	}
 }
 
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int net_set(int argc, char **argv, char *val)
+{
+	struct net_val net;
+	int len, err;
+
+	BT_DBG("val %s", val ? val : "(null)");
+
+	if (!val) {
+		bt_mesh_comp_unprovision();
+		memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
+		return 0;
+	}
+
+	len = sizeof(net);
+	err = settings_bytes_from_str(val, &net, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(net)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net));
+		return -EINVAL;
+	}
+
+	memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key));
+	bt_mesh_comp_provision(net.primary_addr);
+
+	BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr);
+	BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16));
+
+	return 0;
+}
+
+static int iv_set(int argc, char **argv, char *val)
+{
+	struct iv_val iv;
+	int len, err;
+
+	BT_DBG("val %s", val ? val : "(null)");
+
+	if (!val) {
+		bt_mesh.iv_index = 0U;
+		atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
+		return 0;
+	}
+
+	len = sizeof(iv);
+	err = settings_bytes_from_str(val, &iv, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(iv)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv));
+		return -EINVAL;
+	}
+
+	bt_mesh.iv_index = iv.iv_index;
+	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update);
+	bt_mesh.ivu_duration = iv.iv_duration;
+
+	BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours",
+			       (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration);
+
+	return 0;
+}
+
+static int seq_set(int argc, char **argv, char *val)
+{
+	struct seq_val seq;
+	int len, err;
+
+	BT_DBG("val %s", val ? val : "(null)");
+
+	if (!val) {
+		bt_mesh.seq = 0;
+		return 0;
+	}
+
+	len = sizeof(seq);
+	err = settings_bytes_from_str(val, &seq, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(seq)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq));
+		return -EINVAL;
+	}
+
+	bt_mesh.seq = sys_get_le24(seq.val);
+
+	if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) {
+	/* Make sure we have a large enough sequence number. We
+	 * subtract 1 so that the first transmission causes a write
+	 * to the settings storage.
+	 */
+		bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE -
+					(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE));
+		bt_mesh.seq--;
+	}
+
+	BT_DBG("Sequence Number 0x%06x", bt_mesh.seq);
+
+	return 0;
+}
+
+static struct conf_handler bt_mesh_net_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = net_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+
+static struct conf_handler bt_mesh_iv_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = iv_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+
+static struct conf_handler bt_mesh_seq_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = seq_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
 void bt_mesh_net_init(void)
 {
 	int rc;
 
-	k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh);
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	rc = conf_register(&bt_mesh_net_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_net conf");
+
+
+	rc = conf_register(&bt_mesh_iv_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+			 "Failed to register bt_mesh_iv conf");
+
+	rc = conf_register(&bt_mesh_seq_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_seq conf");
+#endif
+
+	k_work_init_delayable(&bt_mesh.ivu_timer, ivu_refresh);
 
 	k_work_init(&bt_mesh.local_work, bt_mesh_net_local);
 	net_buf_slist_init(&bt_mesh.local_queue);
@@ -855,7 +1071,153 @@ void bt_mesh_net_init(void)
 	assert(rc == 0);
 
 	rc = os_mbuf_pool_init(&loopback_os_mbuf_pool, &loopback_buf_mempool,
-						   LOOPBACK_MAX_PDU_LEN + BT_MESH_MBUF_HEADER_SIZE,
-						   MYNEWT_VAL(BLE_MESH_LOOPBACK_BUFS));
+			       LOOPBACK_MAX_PDU_LEN + BT_MESH_MBUF_HEADER_SIZE,
+			       MYNEWT_VAL(BLE_MESH_LOOPBACK_BUFS));
 	assert(rc == 0);
 }
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static void clear_iv(void)
+{
+	int err;
+
+	err = settings_save_one("bt_mesh/IV", NULL);
+	if (err) {
+		BT_ERR("Failed to clear IV");
+	} else {
+		BT_DBG("Cleared IV");
+	}
+}
+
+static void store_pending_iv(void)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))];
+	struct iv_val iv;
+	char *str;
+	int err;
+
+	iv.iv_index = bt_mesh.iv_index;
+	iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
+	iv.iv_duration = bt_mesh.ivu_duration;
+
+	str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode IV as value");
+		return;
+	}
+
+	BT_DBG("Saving IV as value %s", str);
+	err = settings_save_one("bt_mesh/IV", str);
+	if (err) {
+		BT_ERR("Failed to store IV");
+	} else {
+		BT_DBG("Stored IV");
+	}
+}
+
+void bt_mesh_net_pending_iv_store(void)
+{
+	if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+		store_pending_iv();
+	} else {
+		clear_iv();
+	}
+}
+
+static void clear_net(void)
+{
+	int err;
+
+	err = settings_save_one("bt_mesh/Net", NULL);
+	if (err) {
+		BT_ERR("Failed to clear Network");
+	} else {
+		BT_DBG("Cleared Network");
+	}
+}
+
+static void store_pending_net(void)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))];
+	struct net_val net;
+	char *str;
+	int err;
+
+	BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(),
+	       bt_hex(bt_mesh.dev_key, 16));
+
+	net.primary_addr = bt_mesh_primary_addr();
+	memcpy(net.dev_key, bt_mesh.dev_key, 16);
+
+	str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode Network as value");
+		return;
+	}
+
+	BT_DBG("Saving Network as value %s", str);
+	err = settings_save_one("bt_mesh/Net", str);
+	if (err) {
+		BT_ERR("Failed to store Network");
+	} else {
+		BT_DBG("Stored Network");
+	}
+}
+
+void bt_mesh_net_pending_net_store(void)
+{
+	if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+		store_pending_net();
+	} else {
+		clear_net();
+	}
+}
+
+void bt_mesh_net_pending_seq_store(void)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))];
+	char *str;
+	struct seq_val seq;
+	int err;
+
+	if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+		sys_put_le24(bt_mesh.seq, seq.val);
+
+		str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf));
+		if (!str) {
+			BT_ERR("Unable to encode Network as value");
+			return;
+		}
+
+		BT_DBG("Saving Network as value %s", str);
+		err = settings_save_one("bt_mesh/Seq", str);
+		if (err) {
+			BT_ERR("Failed to stor Seq value");
+		} else {
+			BT_DBG("Stored Seq value");
+		}
+	} else {
+		err = settings_save_one("bt_mesh/Seq", NULL);
+		if (err) {
+			BT_ERR("Failed to clear Seq value");
+		} else {
+			BT_DBG("Cleared Seq value");
+		}
+	}
+}
+
+void bt_mesh_net_clear(void)
+{
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_NET_PENDING);
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_IV_PENDING);
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING);
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_SEQ_PENDING);
+}
+#endif
+
+void bt_mesh_net_settings_commit(void)
+{
+	if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
+		k_work_reschedule(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+	}
+}
diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h
index f4b1192b2c..b8147b4d86 100644
--- a/nimble/host/mesh/src/net.h
+++ b/nimble/host/mesh/src/net.h
@@ -25,6 +25,19 @@
 				    CONFIG_BT_MESH_IVU_DIVIDER)
 #define BT_MESH_IVU_TIMEOUT        K_HOURS(BT_MESH_IVU_HOURS)
 
+/* Minimum valid Mesh Network PDU length. The Network headers
+ * themselves take up 9 bytes. After that there is a minimum of 1 byte
+ * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1
+ * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least
+ * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes.
+ */
+#define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8)
+/* Maximum valid Mesh Network PDU length. The longest packet can either be a
+ * transport control message (CTL=1) of 12 bytes + 8 bytes of NetMIC, or an
+ * access message (CTL=0) of 16 bytes + 4 bytes of NetMIC.
+ */
+#define BT_MESH_NET_MAX_PDU_LEN (BT_MESH_NET_HDR_LEN + 16 + 4)
+
 struct bt_mesh_net_cred;
 
 struct bt_mesh_node {
@@ -61,7 +74,7 @@ struct bt_mesh_friend {
 
 	uint16_t sub_list[FRIEND_SUB_LIST_SIZE];
 
-	struct k_delayed_work timer;
+	struct k_work_delayable timer;
 
 	struct bt_mesh_friend_seg {
 		struct net_buf_slist_t queue;
@@ -83,7 +96,7 @@ struct bt_mesh_friend {
 		uint32_t start;                  /* Clear Procedure start */
 		uint16_t frnd;                   /* Previous Friend's address */
 		uint16_t repeat_sec;             /* Repeat timeout in seconds */
-		struct k_delayed_work timer;  /* Repeat timer */
+		struct k_work_delayable timer;  /* Repeat timer */
 	} clear;
 };
 
@@ -147,7 +160,7 @@ struct bt_mesh_lpn {
 	uint16_t adv_duration;
 
 	/* Next LPN related action timer */
-	struct k_delayed_work timer;
+	struct k_work_delayable timer;
 
 	/* Subscribed groups */
 	uint16_t groups[LPN_GROUPS];
@@ -171,17 +184,6 @@ enum {
 	BT_MESH_IVU_TEST,        /* IV Update test mode */
 	BT_MESH_IVU_PENDING,     /* Update blocked by SDU in progress */
 
-	/* pending storage actions, must reside within first 32 flags */
-	BT_MESH_RPL_PENDING,
-	BT_MESH_KEYS_PENDING,
-	BT_MESH_NET_PENDING,
-	BT_MESH_IV_PENDING,
-	BT_MESH_SEQ_PENDING,
-	BT_MESH_HB_PUB_PENDING,
-	BT_MESH_CFG_PENDING,
-	BT_MESH_MOD_PENDING,
-	BT_MESH_VA_PENDING,
-
 	/* Feature flags */
 	BT_MESH_RELAY,
 	BT_MESH_BEACON,
@@ -219,7 +221,7 @@ struct bt_mesh_net {
 	uint8_t default_ttl;
 
 	/* Timer to track duration in current IV Update state */
-	struct k_delayed_work ivu_timer;
+	struct k_work_delayable ivu_timer;
 
 	uint8_t dev_key[16];
 };
@@ -269,7 +271,12 @@ extern struct bt_mesh_net bt_mesh;
 
 static inline void *net_buf_user_data(const struct os_mbuf *buf)
 {
-	return (void *)buf->om_data;
+    /* In Zephyr at the end of net_buf (which is ported as os_mbuf) is place
+     * for user_data, which is array of octets, just like os_mbuf's om_data. Let's just
+     * use last octets (starting at start of om_data + total size of data mbuf can hold -
+     * intended user_data size) of om_data as Zephyr's user_data.
+     */
+    return (void *)(buf->om_data + buf->om_omp->omp_databuf_len - MYNEWT_VAL(BLE_MESH_NET_BUF_USER_DATA_SIZE));
 }
 
 int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
@@ -296,7 +303,11 @@ uint32_t bt_mesh_next_seq(void);
 void bt_mesh_net_init(void);
 void bt_mesh_net_header_parse(struct os_mbuf *buf,
 			      struct bt_mesh_net_rx *rx);
-
+void bt_mesh_net_pending_net_store(void);
+void bt_mesh_net_pending_iv_store(void);
+void bt_mesh_net_pending_seq_store(void);
+void bt_mesh_net_clear(void);
+void bt_mesh_net_settings_commit(void);
 
 static inline void send_cb_finalize(const struct bt_mesh_send_cb *cb,
 				    void *cb_data)
diff --git a/nimble/host/mesh/src/pb_adv.c b/nimble/host/mesh/src/pb_adv.c
index 28391b1883..1fa2c35b24 100644
--- a/nimble/host/mesh/src/pb_adv.c
+++ b/nimble/host/mesh/src/pb_adv.c
@@ -55,10 +55,10 @@
 /* Acked messages, will do retransmissions manually, taking acks into account:
  */
 #define RETRANSMITS_RELIABLE   0
-/* Unacked messages: */
-#define RETRANSMITS_UNRELIABLE 2
 /* PDU acks: */
 #define RETRANSMITS_ACK        2
+/* Link close retransmits: */
+#define RETRANSMITS_LINK_CLOSE 2
 
 enum {
 	ADV_LINK_ACTIVE,    	/* Link has been opened */
@@ -105,11 +105,11 @@ struct pb_adv {
 		void *cb_data;
 
 		/* Retransmit timer */
-		struct k_delayed_work retransmit;
+		struct k_work_delayable retransmit;
 	} tx;
 
 	/* Protocol timeout */
-	struct k_delayed_work prot_timer;
+	struct k_work_delayable prot_timer;
 };
 
 struct prov_rx {
@@ -125,20 +125,28 @@ static void gen_prov_ack_send(uint8_t xact_id);
 static void link_open(struct prov_rx *rx, struct os_mbuf *buf);
 static void link_ack(struct prov_rx *rx, struct os_mbuf *buf);
 static void link_close(struct prov_rx *rx, struct os_mbuf *buf);
+static void prov_link_close(enum prov_bearer_link_status status);
+static void close_link(enum prov_bearer_link_status status);
 
 static void buf_sent(int err, void *user_data)
 {
 	BT_DBG("buf_send");
 
-	if (!link.tx.buf[0]) {
+	if (atomic_test_and_clear_bit(link.flags, ADV_LINK_CLOSING)) {
+		close_link(PROV_BEARER_LINK_STATUS_SUCCESS);
 		return;
 	}
+}
 
-	BT_DBG("submit retransmit");
-	k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT);
+static void buf_start(uint16_t duration, int err, void *user_data)
+{
+	if (err) {
+		buf_sent(err, user_data);
+	}
 }
 
 static struct bt_mesh_send_cb buf_sent_cb = {
+	.start = buf_start,
 	.end = buf_sent,
 };
 
@@ -180,7 +188,12 @@ static void prov_clear_tx(void)
 {
 	BT_DBG("");
 
-	k_delayed_work_cancel(&link.tx.retransmit);
+	/* If this fails, the work handler will not find any buffers to send,
+	 * and return without rescheduling. The work handler also checks the
+	 * LINK_ACTIVE flag, so if this call is part of reset_adv_link, it'll
+	 * exit early.
+	 */
+	(void)k_work_cancel_delayable(&link.tx.retransmit);
 
 	free_segments();
 }
@@ -190,7 +203,10 @@ static void reset_adv_link(void)
 	BT_DBG("");
 	prov_clear_tx();
 
-	k_delayed_work_cancel(&link.prot_timer);
+	/* If this fails, the work handler will exit early on the LINK_ACTIVE
+	 * check.
+	 */
+	(void)k_work_cancel_delayable(&link.prot_timer);
 
 	if (atomic_test_bit(link.flags, ADV_PROVISIONER)) {
 		/* Clear everything except the retransmit and protocol timer
@@ -257,7 +273,7 @@ static void prov_failed(uint8_t err)
 
 static void prov_msg_recv(void)
 {
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
 
 	if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) {
 		BT_ERR("Incorrect FCS");
@@ -278,10 +294,14 @@ static void prov_msg_recv(void)
 
 static void protocol_timeout(struct ble_npl_event *work)
 {
+	if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) {
+		return;
+	}
+
 	BT_DBG("");
 
 	link.rx.seg = 0U;
-	close_link(PROV_BEARER_LINK_STATUS_TIMEOUT);
+	prov_link_close(PROV_BEARER_LINK_STATUS_TIMEOUT);
 }
 /*******************************************************************************
  * Generic provisioning
@@ -543,8 +563,6 @@ static void send_reliable(void)
 {
 	int i;
 
-	link.tx.start = k_uptime_get();
-
 	for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) {
 		struct os_mbuf *buf = link.tx.buf[i];
 
@@ -552,19 +570,20 @@ static void send_reliable(void)
 			break;
 		}
 
-		if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) {
-			bt_mesh_adv_send(buf, NULL, NULL);
-		} else {
-			bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
+		if (BT_MESH_ADV(buf)->busy) {
+			continue;
 		}
+
+		BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+		bt_mesh_adv_send(buf, NULL, NULL);
 	}
+
+	k_work_reschedule(&link.tx.retransmit, RETRANSMIT_TIMEOUT);
 }
 
 static void prov_retransmit(struct ble_npl_event *work)
 {
-	int32_t timeout_ms;
-	int i;
-
 	BT_DBG("");
 
 	if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) {
@@ -572,63 +591,25 @@ static void prov_retransmit(struct ble_npl_event *work)
 		return;
 	}
 
-	/*
-	 * According to mesh profile spec (5.3.1.4.3), the close message should
-	 * be restransmitted at least three times. Retransmit the link_close
-	 * message until CLOSING_TIMEOUT has elapsed.
-	 */
-	if (atomic_test_bit(link.flags, ADV_LINK_CLOSING)) {
-		timeout_ms = CLOSING_TIMEOUT;
-	} else {
-		timeout_ms = TRANSACTION_TIMEOUT;
-	}
-
-	if (k_uptime_get() - link.tx.start > timeout_ms) {
-		if (atomic_test_bit(link.flags, ADV_LINK_CLOSING)) {
-			close_link(PROV_BEARER_LINK_STATUS_SUCCESS);
-		} else {
-			BT_WARN("Giving up transaction");
-			close_link(PROV_BEARER_LINK_STATUS_TIMEOUT);
-		}
-
+	if (k_uptime_get() - link.tx.start > TRANSACTION_TIMEOUT) {
+		BT_WARN("Giving up transaction");
+		prov_link_close(PROV_BEARER_LINK_STATUS_FAIL);
 		return;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) {
-		struct os_mbuf *buf = link.tx.buf[i];
-
-		if (!buf) {
-			break;
-		}
-
-		if (BT_MESH_ADV(buf)->busy) {
-			continue;
-		}
-
-		BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
-
-		if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) {
-			bt_mesh_adv_send(buf, NULL, NULL);
-		} else {
-			bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
-		}
-	}
+	send_reliable();
 }
 
-static int bearer_ctl_send(uint8_t op, const void *data, uint8_t data_len,
-			   bool reliable)
+static struct os_mbuf *ctl_buf_create(uint8_t op, const void *data, uint8_t data_len,
+				      uint8_t retransmits)
 {
 	struct os_mbuf *buf;
 
 	BT_DBG("op 0x%02x data_len %u", op, data_len);
 
-	prov_clear_tx();
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
-
-	buf = adv_buf_create(reliable ? RETRANSMITS_RELIABLE :
-					RETRANSMITS_UNRELIABLE);
+	buf = adv_buf_create(retransmits);
 	if (!buf) {
-		return -ENOBUFS;
+		return NULL;
 	}
 
 	net_buf_add_be32(buf, link.id);
@@ -636,16 +617,36 @@ static int bearer_ctl_send(uint8_t op, const void *data, uint8_t data_len,
 	net_buf_add_u8(buf, 0x00);
 	net_buf_add_u8(buf, GPC_CTL(op));
 	net_buf_add_mem(buf, data, data_len);
+	return buf;
+}
 
-	if (reliable) {
-		link.tx.buf[0] = buf;
-		send_reliable();
-	} else {
-		bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
-		net_buf_unref(buf);
+static int bearer_ctl_send(struct os_mbuf *buf)
+{
+	if (!buf) {
+		return -ENOMEM;
 	}
+	prov_clear_tx();
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
+
+	link.tx.start = k_uptime_get();
+	link.tx.buf[0] = buf;
+	send_reliable();
 
 	return 0;
+	}
+
+static int bearer_ctl_send_unacked(struct os_mbuf *buf)
+{
+	if (!buf) {
+		return -ENOMEM;
+	}
+
+	prov_clear_tx();
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
+
+	bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
+	net_buf_unref(buf);
+	return 0;
 }
 
 static int prov_send_adv(struct os_mbuf *msg,
@@ -655,7 +656,7 @@ static int prov_send_adv(struct os_mbuf *msg,
 	uint8_t seg_len, seg_id;
 
 	prov_clear_tx();
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
 
 	start = adv_buf_create(RETRANSMITS_RELIABLE);
 	if (!start) {
@@ -673,6 +674,7 @@ static int prov_send_adv(struct os_mbuf *msg,
 	link.tx.buf[0] = start;
 	link.tx.cb = cb;
 	link.tx.cb_data = cb_data;
+	link.tx.start = k_uptime_get();
 
 	BT_DBG("xact_id: 0x%x len: %u", link.tx.id, msg->om_len);
 
@@ -720,6 +722,8 @@ static int prov_send_adv(struct os_mbuf *msg,
 
 static void link_open(struct prov_rx *rx, struct os_mbuf *buf)
 {
+	int err;
+
 	BT_DBG("len %u", buf->om_len);
 
 	if (buf->om_len < 16) {
@@ -729,13 +733,14 @@ static void link_open(struct prov_rx *rx, struct os_mbuf *buf)
 
 	if (atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) {
 		/* Send another link ack if the provisioner missed the last */
-		if (link.id == rx->link_id) {
-			BT_DBG("Resending link ack");
-			bearer_ctl_send(LINK_ACK, NULL, 0, false);
-		} else {
+		if (link.id != rx->link_id) {
 			BT_DBG("Ignoring bearer open: link already active");
+			return;
 		}
 
+		BT_DBG("Resending link ack");
+		/* Ignore errors, message will be attempted again if we keep receiving link open: */
+		(void)bearer_ctl_send_unacked(ctl_buf_create(LINK_ACK, NULL, 0, RETRANSMITS_ACK));
 		return;
 	}
 
@@ -748,7 +753,11 @@ static void link_open(struct prov_rx *rx, struct os_mbuf *buf)
 	atomic_set_bit(link.flags, ADV_LINK_ACTIVE);
 	net_buf_simple_reset(link.rx.buf);
 
-	bearer_ctl_send(LINK_ACK, NULL, 0, false);
+	err = bearer_ctl_send_unacked(ctl_buf_create(LINK_ACK, NULL, 0, RETRANSMITS_ACK));
+	if (err) {
+		reset_adv_link();
+		return;
+	}
 
 	link.cb->link_opened(&pb_adv, link.cb_data);
 }
@@ -812,8 +821,16 @@ void bt_mesh_pb_adv_recv(struct os_mbuf *buf)
 static int prov_link_open(const uint8_t uuid[16], int32_t timeout,
 			  const struct prov_bearer_cb *cb, void *cb_data)
 {
+	int err;
+
 	BT_DBG("uuid %s", bt_hex(uuid, 16));
 
+	err = bt_mesh_adv_enable();
+	if (err) {
+		BT_ERR("Failed enabling advertiser");
+		return err;
+	}
+
 	if (atomic_test_and_set_bit(link.flags, ADV_LINK_ACTIVE)) {
 		return -EBUSY;
 	}
@@ -828,13 +845,19 @@ static int prov_link_open(const uint8_t uuid[16], int32_t timeout,
 
 	net_buf_simple_reset(link.rx.buf);
 
-	bearer_ctl_send(LINK_OPEN, uuid, 16, true);
-
-	return 0;
+	return bearer_ctl_send(ctl_buf_create(LINK_OPEN, uuid, 16, RETRANSMITS_RELIABLE));
 }
 
 static int prov_link_accept(const struct prov_bearer_cb *cb, void *cb_data)
 {
+	int err;
+
+	err = bt_mesh_adv_enable();
+	if (err) {
+		BT_ERR("Failed enabling advertiser");
+		return err;
+	}
+
 	if (atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) {
 		return -EBUSY;
 	}
@@ -844,7 +867,7 @@ static int prov_link_accept(const struct prov_bearer_cb *cb, void *cb_data)
 	link.cb = cb;
 	link.cb_data = cb_data;
 
-	/* Make sure we're scanning for provisioning inviations */
+	/* Make sure we're scanning for provisioning invitations */
 	bt_mesh_scan_enable();
 	/* Enable unprovisioned beacon sending */
 	bt_mesh_beacon_enable();
@@ -858,13 +881,14 @@ static void prov_link_close(enum prov_bearer_link_status status)
 		return;
 	}
 
-	bearer_ctl_send(LINK_CLOSE, &status, 1, true);
+	/* Ignore errors, the link will time out eventually if this doesn't get sent */
+	bearer_ctl_send_unacked(ctl_buf_create(LINK_CLOSE, &status, 1, RETRANSMITS_LINK_CLOSE));
 }
 
 void pb_adv_init(void)
 {
-	k_delayed_work_init(&link.prot_timer, protocol_timeout);
-	k_delayed_work_init(&link.tx.retransmit, prov_retransmit);
+	k_work_init_delayable(&link.prot_timer, protocol_timeout);
+	k_work_init_delayable(&link.tx.retransmit, prov_retransmit);
 
     if (!rx_buf) {
         rx_buf = NET_BUF_SIMPLE(65);
diff --git a/nimble/host/mesh/src/pb_gatt.c b/nimble/host/mesh/src/pb_gatt.c
index a8f73787dd..9a0d7091e1 100644
--- a/nimble/host/mesh/src/pb_gatt.c
+++ b/nimble/host/mesh/src/pb_gatt.c
@@ -6,6 +6,7 @@
  *
  * SPDX-License-Identifier: Apache-2.0
  */
+
 #define MESH_LOG_MODULE BLE_MESH_PROV_LOG
 
 #include "mesh/mesh.h"
@@ -14,20 +15,28 @@
 #include "proxy.h"
 #include "adv.h"
 #include "prov.h"
+#include "syscfg/syscfg.h"
+#include "pb_gatt_srv.h"
+
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+struct prov_bearer_send_cb {
+	prov_bearer_send_complete_t cb;
+	void *cb_data;
+};
 
 struct prov_link {
 	uint16_t conn_handle;
 	const struct prov_bearer_cb *cb;
 	void *cb_data;
+	struct prov_bearer_send_cb comp;
 	struct {
 		uint8_t  id;        /* Transaction ID */
 		uint8_t  prev_id;   /* Previous Transaction ID */
 		uint8_t  seg;       /* Bit-field of unreceived segments */
 		uint8_t  last_seg;  /* Last segment (to check length) */
 		uint8_t  fcs;       /* Expected FCS value */
-		struct os_mbuf *buf;
 	} rx;
-	struct k_delayed_work prot_timer;
+	struct k_work_delayable prot_timer;
 };
 
 static struct prov_link link;
@@ -36,9 +45,8 @@ static void reset_state(void)
 {
 	link.conn_handle = BLE_HS_CONN_HANDLE_NONE;
 
-	k_delayed_work_cancel(&link.prot_timer);
-
-	link.rx.buf = bt_mesh_proxy_get_buf();
+	/* If this fails, the protocol timeout handler will exit early. */
+	(void)k_work_cancel_delayable(&link.prot_timer);
 }
 
 static void link_closed(enum prov_bearer_link_status status)
@@ -54,6 +62,11 @@ static void link_closed(enum prov_bearer_link_status status)
 
 static void protocol_timeout(struct ble_npl_event *work)
 {
+	if (!link.conn_handle) {
+		/* Already disconnected */
+		return;
+	}
+
 	BT_DBG("Protocol timeout");
 
 	link_closed(PROV_BEARER_LINK_STATUS_TIMEOUT);
@@ -73,7 +86,7 @@ int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf)
 		return -EINVAL;
 	}
 
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
 
 	link.cb->recv(&pb_gatt, link.cb_data, buf);
 
@@ -89,7 +102,7 @@ int bt_mesh_pb_gatt_open(uint16_t conn_handle)
 	}
 
 	link.conn_handle = conn_handle;
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
 
 	link.cb->link_opened(&pb_gatt, link.cb_data);
 
@@ -115,7 +128,7 @@ int bt_mesh_pb_gatt_close(uint16_t conn_handle)
 
 static int link_accept(const struct prov_bearer_cb *cb, void *cb_data)
 {
-	bt_mesh_proxy_prov_enable();
+	(void)bt_mesh_pb_gatt_enable();
 	bt_mesh_adv_update();
 
 	link.cb = cb;
@@ -131,9 +144,12 @@ static int buf_send(struct os_mbuf *buf, prov_bearer_send_complete_t cb,
 		return -ENOTCONN;
 	}
 
-	k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+	link.comp.cb = cb;
+	link.comp.cb_data = cb_data;
 
-	return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, buf);
+	k_work_reschedule(&link.prot_timer, PROTOCOL_TIMEOUT);
+
+	return bt_mesh_pb_gatt_send(link.conn_handle, buf);
 }
 
 static void clear_tx(void)
@@ -143,7 +159,7 @@ static void clear_tx(void)
 
 void pb_gatt_init(void)
 {
-	k_delayed_work_init(&link.prot_timer, protocol_timeout);
+	k_work_init_delayable(&link.prot_timer, protocol_timeout);
 }
 
 void pb_gatt_reset(void)
@@ -155,4 +171,5 @@ const struct prov_bearer pb_gatt = {
 	.link_accept = link_accept,
 	.send = buf_send,
 	.clear_tx = clear_tx,
-};
\ No newline at end of file
+};
+#endif
diff --git a/nimble/host/mesh/src/pb_gatt_srv.c b/nimble/host/mesh/src/pb_gatt_srv.c
new file mode 100644
index 0000000000..ee37819d5c
--- /dev/null
+++ b/nimble/host/mesh/src/pb_gatt_srv.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2021 Lingao Meng
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_PROV_LOG
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "rpl.h"
+#include "transport.h"
+#include "prov.h"
+#include "beacon.h"
+#include "foundation.h"
+#include "access.h"
+#include "proxy.h"
+#include "proxy_msg.h"
+#include "pb_gatt_srv.h"
+#include "syscfg/syscfg.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "../../host/src/ble_hs_priv.h"
+
+#if defined(CONFIG_BT_MESH_PB_GATT_USE_DEVICE_NAME)
+#define ADV_OPT_USE_NAME BT_LE_ADV_OPT_USE_NAME
+#else
+#define ADV_OPT_USE_NAME 0
+#endif
+
+#define ADV_OPT_PROV                                                           \
+.conn_mode = (BLE_GAP_CONN_MODE_UND),                                  \
+.disc_mode = (BLE_GAP_DISC_MODE_GEN),
+
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+/** @def BT_UUID_MESH_PROV
+ *  @brief Mesh Provisioning Service
+ */
+ble_uuid16_t BT_UUID_MESH_PROV                 = BLE_UUID16_INIT(0x1827);
+#define BT_UUID_MESH_PROV_VAL             0x1827
+/** @def BT_UUID_MESH_PROXY
+ *  @brief Mesh Proxy Service
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY                = BLE_UUID16_INIT(0x1828);
+#define BT_UUID_MESH_PROXY_VAL            0x1828
+/** @def BT_UUID_GATT_CCC
+ *  @brief GATT Client Characteristic Configuration
+ */
+ble_uuid16_t BT_UUID_GATT_CCC                  = BLE_UUID16_INIT(0x2902);
+#define BT_UUID_GATT_CCC_VAL              0x2902
+/** @def BT_UUID_MESH_PROV_DATA_IN
+ *  @brief Mesh Provisioning Data In
+ */
+ble_uuid16_t BT_UUID_MESH_PROV_DATA_IN         = BLE_UUID16_INIT(0x2adb);
+#define BT_UUID_MESH_PROV_DATA_IN_VAL     0x2adb
+/** @def BT_UUID_MESH_PROV_DATA_OUT
+ *  @brief Mesh Provisioning Data Out
+ */
+ble_uuid16_t BT_UUID_MESH_PROV_DATA_OUT        = BLE_UUID16_INIT(0x2adc);
+#define BT_UUID_MESH_PROV_DATA_OUT_VAL    0x2adc
+/** @def BT_UUID_MESH_PROXY_DATA_IN
+ *  @brief Mesh Proxy Data In
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY_DATA_IN        = BLE_UUID16_INIT(0x2add);
+#define BT_UUID_MESH_PROXY_DATA_IN_VAL    0x2add
+/** @def BT_UUID_MESH_PROXY_DATA_OUT
+ *  @brief Mesh Proxy Data Out
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY_DATA_OUT       = BLE_UUID16_INIT(0x2ade);
+#define BT_UUID_MESH_PROXY_DATA_OUT_VAL   0x2ade
+#define BT_UUID_16_ENCODE(w16)  \
+	(((w16) >>  0) & 0xFF), \
+	(((w16) >>  8) & 0xFF)
+
+static bool prov_fast_adv;
+
+struct svc_handles svc_handles;
+static atomic_t pending_notifications;
+
+static int gatt_send(uint16_t conn_handle,
+		     const void *data, uint16_t len);
+
+static struct bt_mesh_proxy_role *cli;
+
+static void proxy_msg_recv(struct bt_mesh_proxy_role *role)
+{
+	switch (role->msg_type) {
+	case BT_MESH_PROXY_PROV:
+		BT_DBG("Mesh Provisioning PDU");
+		bt_mesh_pb_gatt_recv(role->conn_handle, role->buf);
+		break;
+	default:
+		BT_WARN("Unhandled Message Type 0x%02x", role->msg_type);
+		break;
+	}
+}
+
+static bool service_registered;
+
+static int gatt_recv_proxy(uint16_t conn_handle, uint16_t attr_handle,
+			 struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+	const uint8_t *data = ctxt->om->om_data;
+	uint16_t len = ctxt->om->om_len;
+	struct bt_mesh_proxy_client *client = find_client(conn_handle);
+
+	if (len < 1) {
+		BT_WARN("Too small Proxy PDU");
+		return -EINVAL;
+	}
+
+	if (PDU_TYPE(data) == BT_MESH_PROXY_PROV) {
+		BT_WARN("Proxy PDU type doesn't match GATT service");
+		return -EINVAL;
+	}
+
+	return bt_mesh_proxy_msg_recv(client->cli, data, len);
+}
+
+static int gatt_recv_prov(uint16_t conn_handle, uint16_t attr_handle,
+		     struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+	const uint8_t *data = ctxt->om->om_data;
+	uint16_t len = ctxt->om->om_len;
+
+	if (conn_handle != cli->conn_handle) {
+		BT_WARN("conn_handle != cli->conn_handle");
+		return -ENOTCONN;
+	}
+
+	if (len < 1) {
+		BT_WARN("Too small Proxy PDU");
+		return -EINVAL;
+	}
+
+	if (PDU_TYPE(data) != BT_MESH_PROXY_PROV) {
+		BT_WARN("Proxy PDU type doesn't match GATT service");
+		return -EINVAL;
+	}
+
+	return bt_mesh_proxy_msg_recv(cli, data, len);
+}
+
+void gatt_connected_pb_gatt(uint16_t conn_handle, uint8_t err)
+{
+	struct ble_gap_conn_desc info;
+	struct ble_hs_conn *conn;
+
+	conn = ble_hs_conn_find(conn_handle);
+	bt_conn_get_info(conn, &info);
+	if (info.role != BLE_GAP_ROLE_SLAVE ||
+	    !service_registered || bt_mesh_is_provisioned()) {
+		return;
+	}
+
+	cli = bt_mesh_proxy_role_setup(conn_handle, gatt_send, proxy_msg_recv);
+
+	BT_DBG("conn %p err 0x%02x", (void *)conn, err);
+}
+
+void gatt_disconnected_pb_gatt(uint16_t conn_handle, uint8_t reason)
+{
+	struct ble_gap_conn_desc info;
+
+	struct ble_hs_conn *conn;
+
+	conn = ble_hs_conn_find(conn_handle);
+	bt_conn_get_info(conn, &info);
+	if (info.role != BLE_GAP_ROLE_SLAVE ||
+	    !service_registered) {
+		return;
+	}
+
+	if (cli) {
+		bt_mesh_proxy_role_cleanup(cli);
+		cli = NULL;
+	}
+
+	BT_DBG("conn %p reason 0x%02x", (void *)conn, reason);
+
+	bt_mesh_pb_gatt_close(conn_handle);
+
+	if (bt_mesh_is_provisioned()) {
+		(void)bt_mesh_pb_gatt_disable();
+	}
+}
+
+int prov_ccc_write(uint16_t conn_handle, uint8_t type)
+{
+	if (cli->conn_handle != conn_handle) {
+		BT_ERR("No PB-GATT Client found");
+		return -ENOTCONN;
+	}
+
+	if (type != BLE_GAP_EVENT_SUBSCRIBE) {
+		BT_WARN("Client wrote instead enabling notify");
+		return BT_GATT_ERR(EINVAL);
+	}
+
+	bt_mesh_pb_gatt_open(conn_handle);
+
+	return 0;
+}
+
+/* Mesh Provisioning Service Declaration */
+
+static int
+dummy_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+		struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+	/*
+	 * We should never never enter this callback - it's attached to notify-only
+	 * characteristic which are notified directly from mbuf. And we can't pass
+	 * NULL as access_cb because gatts will assert on init...
+	 */
+	BLE_HS_DBG_ASSERT(0);
+	return 0;
+}
+
+static const struct ble_gatt_svc_def svc_defs [] = {
+	{
+		.type = BLE_GATT_SVC_TYPE_PRIMARY,
+		.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+		.characteristics = (struct ble_gatt_chr_def[]) { {
+				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_IN_VAL),
+				.access_cb = gatt_recv_proxy,
+				.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+			}, {
+				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
+				.access_cb = dummy_access_cb,
+				.flags = BLE_GATT_CHR_F_NOTIFY,
+			}, {
+			0, /* No more characteristics in this service. */
+		} },
+	}, {
+		.type = BLE_GATT_SVC_TYPE_PRIMARY,
+		.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+		.characteristics = (struct ble_gatt_chr_def[]) { {
+				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
+				.access_cb = gatt_recv_prov,
+				.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+			}, {
+				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
+				.access_cb = dummy_access_cb,
+				.flags = BLE_GATT_CHR_F_NOTIFY,
+			}, {
+				0, /* No more characteristics in this service. */
+		} },
+	}, {
+		0, /* No more services. */
+	},
+};
+
+void resolve_svc_handles(void)
+{
+	int rc;
+
+	/* Either all handles are already resolved, or none of them */
+	if (svc_handles.prov_data_out_h) {
+		return;
+	}
+
+	/*
+	 * We assert if attribute is not found since at this stage all attributes
+	 * shall be already registered and thus shall be found.
+	 */
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+				&svc_handles.proxy_h);
+	assert(rc == 0);
+
+	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+				BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
+				NULL, &svc_handles.proxy_data_out_h);
+	assert(rc == 0);
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+				&svc_handles.prov_h);
+	assert(rc == 0);
+
+	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+				BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
+				NULL, &svc_handles.prov_data_in_h);
+	assert(rc == 0);
+
+	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+				BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
+				NULL, &svc_handles.prov_data_out_h);
+	assert(rc == 0);
+}
+
+
+int bt_mesh_proxy_svcs_register(void)
+{
+	int rc;
+
+	rc = ble_gatts_count_cfg(svc_defs);
+	assert(rc == 0);
+
+	rc = ble_gatts_add_svcs(svc_defs);
+	assert(rc == 0);
+
+	return 0;
+}
+
+int bt_mesh_pb_gatt_enable(void)
+{
+	int rc;
+	uint16_t handle;
+	BT_DBG("");
+
+	if (bt_mesh_is_provisioned()) {
+		return -ENOTSUP;
+	}
+
+	if (service_registered) {
+		return -EBUSY;
+	}
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
+	assert(rc == 0);
+	ble_gatts_svc_set_visibility(handle, 1);
+	/* FIXME: figure out end handle */
+	ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
+
+	service_registered = true;
+	prov_fast_adv = true;
+
+	return 0;
+}
+
+int bt_mesh_pb_gatt_disable(void)
+{
+	uint16_t handle;
+	int rc;
+
+	BT_DBG("");
+
+	if (!service_registered) {
+		return -EALREADY;
+	}
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
+	assert(rc == 0);
+	ble_gatts_svc_set_visibility(handle, 0);
+	/* FIXME: figure out end handle */
+	ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
+	service_registered = false;
+
+	bt_mesh_adv_update();
+
+	return 0;
+}
+
+static uint8_t prov_svc_data[20] = {
+	BT_UUID_16_ENCODE(BT_UUID_MESH_PROV_VAL),
+};
+
+static const struct bt_data prov_ad[] = {
+	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
+		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROV_VAL)),
+		      BT_DATA(BT_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)),
+};
+
+int bt_mesh_pb_gatt_send(uint16_t conn_handle, struct os_mbuf *buf)
+{
+	if (!cli || cli->conn_handle != conn_handle) {
+		BT_ERR("No PB-GATT Client found");
+		return -ENOTCONN;
+	}
+
+	return bt_mesh_proxy_msg_send(cli, BT_MESH_PROXY_PROV, buf);
+}
+
+static size_t gatt_prov_adv_create(struct bt_data prov_sd[1])
+{
+	const struct bt_mesh_prov *prov = bt_mesh_prov_get();
+	size_t uri_len;
+
+	memcpy(prov_svc_data + 2, prov->uuid, 16);
+	sys_put_be16(prov->oob_info, prov_svc_data + 18);
+
+	if (!prov->uri) {
+		return 0;
+	}
+
+	uri_len = strlen(prov->uri);
+	if (uri_len > 29) {
+		/* There's no way to shorten an URI */
+		BT_WARN("Too long URI to fit advertising packet");
+		return 0;
+	}
+
+	prov_sd[0].type = BT_DATA_URI;
+	prov_sd[0].data_len = uri_len;
+	prov_sd[0].data = (const uint8_t *)prov->uri;
+
+	return 1;
+}
+
+static int gatt_send(uint16_t conn_handle,
+	const void *data, uint16_t len)
+{
+	struct os_mbuf *om;
+	int err = 0;
+
+	BT_DBG("%u bytes: %s", len, bt_hex(data, len));
+	om = ble_hs_mbuf_from_flat(data, len);
+	assert(om);
+	err = ble_gattc_notify_custom(conn_handle, svc_handles.prov_data_out_h, om);
+	notify_complete();
+
+	if (!err) {
+		atomic_inc(&pending_notifications);
+	}
+
+	return err;
+}
+
+int bt_mesh_pb_gatt_adv_start(void)
+{
+	BT_DBG("");
+
+	if (!service_registered || bt_mesh_is_provisioned()) {
+		return -ENOTSUP;
+	}
+
+	struct ble_gap_adv_params fast_adv_param = {
+		ADV_OPT_PROV
+		ADV_FAST_INT
+	};
+#if ADV_OPT_USE_NAME
+	const char *name = CONFIG_BT_DEVICE_NAME;
+	size_t name_len = strlen(name);
+	struct bt_data prov_sd = {
+		.type = BT_DATA_NAME_COMPLETE,
+		.data_len = name_len,
+		.data = (void *)name
+	};
+#else
+	struct bt_data *prov_sd = NULL;
+#endif
+
+	size_t prov_sd_len;
+	int err;
+
+	prov_sd_len = gatt_prov_adv_create(prov_sd);
+
+	if (!prov_fast_adv) {
+		struct ble_gap_adv_params slow_adv_param = {
+			ADV_OPT_PROV
+			ADV_SLOW_INT
+		};
+
+		return bt_mesh_adv_start(&slow_adv_param, K_FOREVER, prov_ad,
+					 ARRAY_SIZE(prov_ad), prov_sd, prov_sd_len);
+	}
+
+	/* Advertise 60 seconds using fast interval */
+	err = bt_mesh_adv_start(&fast_adv_param, (60 * MSEC_PER_SEC),
+				prov_ad, ARRAY_SIZE(prov_ad),
+				prov_sd, prov_sd_len);
+	if (!err) {
+		prov_fast_adv = false;
+	}
+
+	return err;
+}
+#endif
diff --git a/nimble/host/mesh/src/pb_gatt_srv.h b/nimble/host/mesh/src/pb_gatt_srv.h
new file mode 100644
index 0000000000..a5ebce5283
--- /dev/null
+++ b/nimble/host/mesh/src/pb_gatt_srv.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2021 Lingao Meng
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __PB_GATT_SRV_H__
+#define __PB_GATT_SRV_H__
+
+int bt_mesh_pb_gatt_send(uint16_t conn_handle, struct os_mbuf *buf);
+
+int bt_mesh_pb_gatt_enable(void);
+int bt_mesh_pb_gatt_disable(void);
+
+int prov_ccc_write(uint16_t conn_handle, uint8_t type);
+void gatt_disconnected_pb_gatt(uint16_t conn_handle, uint8_t err);
+void gatt_connected_pb_gatt(uint16_t conn_handle, uint8_t err);
+void resolve_svc_handles(void);
+
+int bt_mesh_pb_gatt_adv_start(void);
+
+extern struct svc_handles {
+	uint16_t proxy_h;
+	uint16_t proxy_data_out_h;
+	uint16_t prov_h;
+	uint16_t prov_data_in_h;
+	uint16_t prov_data_out_h;
+} svc_handles;
+
+#endif /* __PB_GATT_SRV_H__ */
diff --git a/nimble/host/mesh/src/prov.c b/nimble/host/mesh/src/prov.c
index ba514dde97..cdadbb45ba 100644
--- a/nimble/host/mesh/src/prov.c
+++ b/nimble/host/mesh/src/prov.c
@@ -25,6 +25,10 @@
 struct bt_mesh_prov_link bt_mesh_prov_link;
 const struct bt_mesh_prov *bt_mesh_prov;
 
+/* Verify specification defined length: */
+BUILD_ASSERT(sizeof(bt_mesh_prov_link.conf_inputs) == 145,
+	     "Confirmation inputs shall be 145 bytes");
+
 static void pub_key_ready(const uint8_t *pkey)
 {
 	if (!pkey) {
@@ -34,18 +38,18 @@ static void pub_key_ready(const uint8_t *pkey)
 	BT_DBG("Local public key ready");
 }
 
-int bt_mesh_prov_reset_state(void (*func)(const uint8_t key[64]))
+int bt_mesh_prov_reset_state(void (*func)(const uint8_t key[BT_PUB_KEY_LEN]))
 {
 	BT_DBG("bt_mesh_prov_reset_state");
 
 	int err;
 	static struct bt_pub_key_cb pub_key_cb;
-	const size_t offset = offsetof(struct bt_mesh_prov_link, dhkey);
+	const size_t offset = offsetof(struct bt_mesh_prov_link, auth);
 
 	pub_key_cb.func = func ? func : pub_key_ready;
 
 	/* Disable Attention Timer if it was set */
-	if (bt_mesh_prov_link.conf_inputs[0]) {
+	if (bt_mesh_prov_link.conf_inputs.invite[0]) {
 		bt_mesh_attention(NULL, 0);
 	}
 
@@ -95,10 +99,98 @@ static bt_mesh_input_action_t input_action(uint8_t action)
 	}
 }
 
-int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size)
+static int check_output_auth(bt_mesh_output_action_t output, uint8_t size)
+{
+	if (!output) {
+		return -EINVAL;
+	}
+
+	if (!(bt_mesh_prov->output_actions & output)) {
+		return -EINVAL;
+	}
+
+	if (size > bt_mesh_prov->output_size) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int check_input_auth(bt_mesh_input_action_t input, uint8_t size)
+{
+	if (!input) {
+		return -EINVAL;
+	}
+
+	if (!(bt_mesh_prov->input_actions & input)) {
+		return -EINVAL;
+	}
+
+	if (size > bt_mesh_prov->input_size) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void get_auth_string(char *str, uint8_t size)
+{
+	uint64_t value;
+
+	bt_rand(&value, sizeof(value));
+
+	static const char characters[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+	for (int i = 0; i < size; i++) {
+		/* pull base-36 digits: */
+		int idx = value % 36;
+
+		value = value / 36;
+		str[i] = characters[idx];
+	}
+
+	str[size] = '\0';
+
+	memcpy(bt_mesh_prov_link.auth, str, size);
+	memset(bt_mesh_prov_link.auth + size, 0,
+	       sizeof(bt_mesh_prov_link.auth) - size);
+}
+
+static uint32_t get_auth_number(bt_mesh_output_action_t output,
+				bt_mesh_input_action_t input, uint8_t size)
+				{
+	const uint32_t divider[PROV_IO_OOB_SIZE_MAX] = { 10, 100, 1000, 10000,
+							 100000, 1000000, 10000000, 100000000 };
+	uint32_t num = 0;
+
+	bt_rand(&num, sizeof(num));
+
+	if (output == BT_MESH_BLINK ||
+	output == BT_MESH_BEEP ||
+	output == BT_MESH_VIBRATE ||
+	input == BT_MESH_PUSH ||
+	input == BT_MESH_TWIST) {
+		/* According to the Bluetooth Mesh Profile
+		 * Specification Section 5.4.2.4, blink, beep
+		 * vibrate, push and twist should be a random integer
+		 * between 0 and 10^size, *exclusive*:
+		 */
+		num = (num % (divider[size - 1] - 1)) + 1;
+	} else {
+		num %= divider[size - 1];
+	}
+
+	sys_put_be32(num, &bt_mesh_prov_link.auth[12]);
+	memset(bt_mesh_prov_link.auth, 0, 12);
+
+	return num;
+				}
+
+int bt_mesh_prov_auth(bool is_provisioner, uint8_t method, uint8_t action, uint8_t size)
 {
 	bt_mesh_output_action_t output;
 	bt_mesh_input_action_t input;
+	int err;
 
 	switch (method) {
 	case AUTH_METHOD_NO_OOB:
@@ -119,77 +211,63 @@ int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size)
 
 	case AUTH_METHOD_OUTPUT:
 		output = output_action(action);
-		if (!output) {
-			return -EINVAL;
-		}
+		if (is_provisioner) {
+			if (output == BT_MESH_DISPLAY_STRING) {
+				input = BT_MESH_ENTER_STRING;
+				atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
+			} else {
+				input = BT_MESH_ENTER_NUMBER;
+				atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
+			}
 
-		if (!(bt_mesh_prov->output_actions & output)) {
-			return -EINVAL;
+			return bt_mesh_prov->input(input, size);
 		}
 
-		if (size > bt_mesh_prov->output_size) {
-			return -EINVAL;
+		err = check_output_auth(output, size);
+		if (err) {
+			return err;
 		}
 
-		atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
-
 		if (output == BT_MESH_DISPLAY_STRING) {
-			unsigned char str[9];
-			uint8_t i;
-
-			bt_rand(str, size);
-
-			/* Normalize to '0' .. '9' & 'A' .. 'Z' */
-			for (i = 0; i < size; i++) {
-				str[i] %= 36;
-				if (str[i] < 10) {
-					str[i] += '0';
-				} else {
-					str[i] += 'A' - 10;
-				}
-			}
-			str[size] = '\0';
+			char str[9];
 
-			memcpy(bt_mesh_prov_link.auth, str, size);
-			memset(bt_mesh_prov_link.auth + size, 0,
-				     sizeof(bt_mesh_prov_link.auth) - size);
-
-			return bt_mesh_prov->output_string((char *)str);
-		} else {
-			uint32_t div[8] = { 10, 100, 1000, 10000, 100000,
-					    1000000, 10000000, 100000000 };
-			uint32_t num;
-
-			bt_rand(&num, sizeof(num));
-			num %= div[size - 1];
-
-			sys_put_be32(num, &bt_mesh_prov_link.auth[12]);
-			memset(bt_mesh_prov_link.auth, 0, 12);
-
-			return bt_mesh_prov->output_number(output, num);
+			atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
+			get_auth_string(str, size);
+			return bt_mesh_prov->output_string(str);
 		}
 
+		atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
+		return bt_mesh_prov->output_number(output,
+						   get_auth_number(output, BT_MESH_NO_INPUT, size));
 	case AUTH_METHOD_INPUT:
 		input = input_action(action);
-		if (!input) {
-			return -EINVAL;
-		}
+		if (!is_provisioner) {
+			err = check_input_auth(input, size);
+			if (err) {
+				return err;
+			}
 
-		if (!(bt_mesh_prov->input_actions & input)) {
-			return -EINVAL;
-		}
+			if (input == BT_MESH_ENTER_STRING) {
+				atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
+			} else {
+				atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
+			}
 
-		if (size > bt_mesh_prov->input_size) {
-			return -EINVAL;
+			return bt_mesh_prov->input(input, size);
 		}
 
 		if (input == BT_MESH_ENTER_STRING) {
-			atomic_set_bit(bt_mesh_prov_link.flags, WAIT_STRING);
-		} else {
-			atomic_set_bit(bt_mesh_prov_link.flags, WAIT_NUMBER);
+			char str[9];
+
+			atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
+			get_auth_string(str, size);
+			return bt_mesh_prov->output_string(str);
 		}
 
-		return bt_mesh_prov->input(input, size);
+		atomic_set_bit(bt_mesh_prov_link.flags, NOTIFY_INPUT_COMPLETE);
+		output = BT_MESH_DISPLAY_NUMBER;
+		return bt_mesh_prov->output_number(output,
+						   get_auth_number(BT_MESH_NO_OUTPUT, input, size));
 
 	default:
 		return -EINVAL;
@@ -215,11 +293,16 @@ int bt_mesh_input_string(const char *str)
 {
 	BT_DBG("%s", str);
 
+	if (strlen(str) > PROV_IO_OOB_SIZE_MAX ||
+	    strlen(str) > bt_mesh_prov_link.oob_size) {
+		return -ENOTSUP;
+	}
+
 	if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
 		return -EINVAL;
 	}
 
-	strncpy((char *)bt_mesh_prov_link.auth, str, bt_mesh_prov->input_size);
+	strcpy((char *)bt_mesh_prov_link.auth, str);
 
 	bt_mesh_prov_link.role->input_complete();
 
@@ -240,16 +323,16 @@ static void prov_recv(const struct prov_bearer *bearer, void *cb_data,
 		      		  struct os_mbuf *buf)
 {
 	static const uint8_t op_len[10] = {
-		[PROV_INVITE] = 1,
-		[PROV_CAPABILITIES] = 11,
-		[PROV_START] = 5,
-		[PROV_PUB_KEY] = 64,
-		[PROV_INPUT_COMPLETE] = 0,
-		[PROV_CONFIRM] = 16,
-		[PROV_RANDOM] = 16,
-		[PROV_DATA] = 33,
-		[PROV_COMPLETE] = 0,
-		[PROV_FAILED] = 1,
+		[PROV_INVITE]         = PDU_LEN_INVITE,
+		[PROV_CAPABILITIES]   = PDU_LEN_CAPABILITIES,
+		[PROV_START]          = PDU_LEN_START,
+		[PROV_PUB_KEY]        = PDU_LEN_PUB_KEY,
+		[PROV_INPUT_COMPLETE] = PDU_LEN_INPUT_COMPLETE,
+		[PROV_CONFIRM]        = PDU_LEN_CONFIRM,
+		[PROV_RANDOM]         = PDU_LEN_RANDOM,
+		[PROV_DATA]           = PDU_LEN_DATA,
+		[PROV_COMPLETE]       = PDU_LEN_COMPLETE,
+		[PROV_FAILED]         = PDU_LEN_FAILED,
 	};
 
 	uint8_t type = buf->om_data[0];
@@ -257,7 +340,7 @@ static void prov_recv(const struct prov_bearer *bearer, void *cb_data,
 
 	if (type >= ARRAY_SIZE(bt_mesh_prov_link.role->op)) {
 		BT_ERR("Unknown provisioning PDU type 0x%02x", type);
-		bt_mesh_prov_link.role->error(PROV_ERR_NVAL_FMT);
+		bt_mesh_prov_link.role->error(PROV_ERR_NVAL_PDU);
 		return;
 	}
 
diff --git a/nimble/host/mesh/src/prov.h b/nimble/host/mesh/src/prov.h
index 89f027256a..d18ee0ebb7 100644
--- a/nimble/host/mesh/src/prov.h
+++ b/nimble/host/mesh/src/prov.h
@@ -56,10 +56,25 @@
 
 #define PROV_NO_PDU            0xff
 
+#define PDU_LEN_INVITE         1
+#define PDU_LEN_CAPABILITIES   11
+#define PDU_LEN_START          5
+#define PDU_LEN_PUB_KEY        64
+#define PDU_LEN_INPUT_COMPLETE 0
+#define PDU_LEN_CONFIRM        16
+#define PDU_LEN_RANDOM         16
+#define PDU_LEN_DATA           33
+#define PDU_LEN_COMPLETE       0
+#define PDU_LEN_FAILED         1
+
+#define PDU_OP_LEN             1
+
 #define PROV_ALG_P256          0x00
 
+#define PROV_IO_OOB_SIZE_MAX   8 /* in bytes */
+
 #define PROV_BUF(len) \
-	NET_BUF_SIMPLE(PROV_BEARER_BUF_HEADROOM + len)
+	NET_BUF_SIMPLE(PROV_BEARER_BUF_HEADROOM + PDU_OP_LEN + len)
 
 enum {
 	WAIT_PUB_KEY,           /* Waiting for local PubKey to be generated */
@@ -75,6 +90,7 @@ enum {
 	WAIT_CONFIRM,           /* Wait for send confirm */
 	WAIT_AUTH,              /* Wait for auth response */
 	OOB_STATIC_KEY,         /* OOB Static Authentication */
+	WAIT_DH_KEY, /* Wait for DH Key */
 
 	NUM_FLAGS,
 };
@@ -103,14 +119,21 @@ struct bt_mesh_prov_link {
 	uint8_t oob_size;               /* Authen size */
 	uint8_t auth[16];               /* Authen value */
 
-	uint8_t dhkey[32];              /* Calculated DHKey */
+	uint8_t dhkey[BT_DH_KEY_LEN];   /* Calculated DHKey */
 	uint8_t expect;                 /* Next expected PDU */
-	uint8_t conf[16];               /* Remote Confirmation */
+	uint8_t conf[16];               /* Local/Remote Confirmation */
 	uint8_t rand[16];               /* Local Random */
 
 	uint8_t conf_salt[16];          /* ConfirmationSalt */
 	uint8_t conf_key[16];           /* ConfirmationKey */
-	uint8_t conf_inputs[145];       /* ConfirmationInputs */
+	/* ConfirmationInput fields: */
+	struct {
+		uint8_t invite[PDU_LEN_INVITE];
+		uint8_t capabilities[PDU_LEN_CAPABILITIES];
+		uint8_t start[PDU_LEN_START];
+		uint8_t pub_key_provisioner[PDU_LEN_PUB_KEY]; /* big-endian */
+		uint8_t pub_key_device[PDU_LEN_PUB_KEY]; /* big-endian */
+	} conf_inputs;
 	uint8_t prov_salt[16];          /* Provisioning Salt */
 };
 
@@ -129,11 +152,11 @@ static inline void bt_mesh_prov_buf_init(struct os_mbuf *buf, uint8_t type)
 	net_buf_simple_add_u8(buf, type);
 }
 
-int bt_mesh_prov_reset_state(void (*func)(const uint8_t key[64]));
+int bt_mesh_prov_reset_state(void (*func)(const uint8_t key[BT_PUB_KEY_LEN]));
 
 bool bt_mesh_prov_active(void);
 
-int bt_mesh_prov_auth(uint8_t method, uint8_t action, uint8_t size);
+int bt_mesh_prov_auth(bool is_provisioner, uint8_t method, uint8_t action, uint8_t size);
 
 int bt_mesh_pb_gatt_open(uint16_t conn_handle);
 int bt_mesh_pb_gatt_close(uint16_t conn_handle);
diff --git a/nimble/host/mesh/src/prov_device.c b/nimble/host/mesh/src/prov_device.c
index 38a96e7a6b..e9e2d2b122 100644
--- a/nimble/host/mesh/src/prov_device.c
+++ b/nimble/host/mesh/src/prov_device.c
@@ -21,6 +21,7 @@
 #include "proxy.h"
 #include "prov.h"
 #include "settings.h"
+#include "pb_gatt_srv.h"
 
 static void send_pub_key(void);
 static void pub_key_ready(const uint8_t *pkey);
@@ -32,7 +33,7 @@ static int reset_state(void)
 
 static void prov_send_fail_msg(uint8_t err)
 {
-	struct os_mbuf *buf = PROV_BUF(2);
+	struct os_mbuf *buf = PROV_BUF(PDU_LEN_FAILED);
 
 	BT_DBG("%u", err);
 
@@ -58,7 +59,7 @@ static void prov_fail(uint8_t reason)
 
 static void prov_invite(const uint8_t *data)
 {
-	struct os_mbuf *buf = PROV_BUF(12);
+	struct os_mbuf *buf = PROV_BUF(PDU_LEN_CAPABILITIES);
 
 	BT_DBG("Attention Duration: %u seconds", data[0]);
 
@@ -66,7 +67,7 @@ static void prov_invite(const uint8_t *data)
 		bt_mesh_attention(NULL, data[0]);
 	}
 
-	bt_mesh_prov_link.conf_inputs[0] = data[0];
+	memcpy(bt_mesh_prov_link.conf_inputs.invite, data, PDU_LEN_INVITE);
 
 	bt_mesh_prov_buf_init(buf, PROV_CAPABILITIES);
 
@@ -76,8 +77,9 @@ static void prov_invite(const uint8_t *data)
 	/* Supported algorithms - FIPS P-256 Eliptic Curve */
 	net_buf_simple_add_be16(buf, BIT(PROV_ALG_P256));
 
-	/* Public Key Type, Only "No OOB" Public Key is supported */
-	net_buf_simple_add_u8(buf, PUB_KEY_NO_OOB);
+	/* Public Key Type */
+	net_buf_simple_add_u8(buf,
+			      bt_mesh_prov->public_key_be == NULL ? PUB_KEY_NO_OOB : PUB_KEY_OOB);
 
 	/* Static OOB Type */
 	net_buf_simple_add_u8(buf, bt_mesh_prov->static_val ? BIT(0) : 0x00);
@@ -94,7 +96,7 @@ static void prov_invite(const uint8_t *data)
 	/* Input OOB Action */
 	net_buf_simple_add_be16(buf, bt_mesh_prov->input_actions);
 
-	memcpy(&bt_mesh_prov_link.conf_inputs[1], &buf->om_data[1], 11);
+	memcpy(bt_mesh_prov_link.conf_inputs.capabilities, &buf->om_data[1], PDU_LEN_CAPABILITIES);
 
 	if (bt_mesh_prov_send(buf, NULL)) {
 		BT_ERR("Failed to send capabilities");
@@ -118,17 +120,24 @@ static void prov_start(const uint8_t *data)
 		return;
 	}
 
-	if (data[1] != PUB_KEY_NO_OOB) {
+	if (data[1] > PUB_KEY_OOB ||
+	    (data[1] == PUB_KEY_OOB &&
+	    (!MYNEWT_VAL(BLE_MESH_PROV_OOB_PUBLIC_KEY) || !bt_mesh_prov->public_key_be))) {
 		BT_ERR("Invalid public key type: 0x%02x", data[1]);
 		prov_fail(PROV_ERR_NVAL_FMT);
 		return;
 	}
 
-	memcpy(&bt_mesh_prov_link.conf_inputs[12], data, 5);
+	atomic_set_bit_to(bt_mesh_prov_link.flags, OOB_PUB_KEY, data[1] == PUB_KEY_OOB);
+
+	memcpy(bt_mesh_prov_link.conf_inputs.start, data, PDU_LEN_START);
 
 	bt_mesh_prov_link.expect = PROV_PUB_KEY;
+	bt_mesh_prov_link.oob_method = data[2];
+	bt_mesh_prov_link.oob_action = data[3];
+	bt_mesh_prov_link.oob_size = data[4];
 
-	if (bt_mesh_prov_auth(data[2], data[3], data[4]) < 0) {
+	if (bt_mesh_prov_auth(false, data[2], data[3], data[4]) < 0) {
 		BT_ERR("Invalid authentication method: 0x%02x; "
 		       "action: 0x%02x; size: 0x%02x", data[2], data[3],
 		       data[4]);
@@ -145,14 +154,15 @@ static void prov_start(const uint8_t *data)
 
 static void send_confirm(void)
 {
-	struct os_mbuf *cfm = PROV_BUF(17);
+	struct os_mbuf *cfm = PROV_BUF(PDU_LEN_CONFIRM);
+
+	uint8_t *inputs = (uint8_t *)&bt_mesh_prov_link.conf_inputs;
 
-	BT_DBG("ConfInputs[0]   %s", bt_hex(bt_mesh_prov_link.conf_inputs, 64));
-	BT_DBG("ConfInputs[64]  %s", bt_hex(&bt_mesh_prov_link.conf_inputs[64], 64));
-	BT_DBG("ConfInputs[128] %s", bt_hex(&bt_mesh_prov_link.conf_inputs[128], 17));
+	BT_DBG("ConfInputs[0]   %s", bt_hex(inputs, 64));
+	BT_DBG("ConfInputs[64]  %s", bt_hex(&inputs[64], 64));
+	BT_DBG("ConfInputs[128] %s", bt_hex(&inputs[128], 17));
 
-	if (bt_mesh_prov_conf_salt(bt_mesh_prov_link.conf_inputs,
-				   bt_mesh_prov_link.conf_salt)) {
+	if (bt_mesh_prov_conf_salt(inputs, bt_mesh_prov_link.conf_salt)) {
 		BT_ERR("Unable to generate confirmation salt");
 		prov_fail(PROV_ERR_UNEXP_ERR);
 		return;
@@ -197,7 +207,7 @@ static void send_confirm(void)
 
 static void send_input_complete(void)
 {
-	struct os_mbuf *buf = PROV_BUF(1);
+	struct os_mbuf *buf = PROV_BUF(PDU_LEN_INPUT_COMPLETE);
 
 	bt_mesh_prov_buf_init(buf, PROV_INPUT_COMPLETE);
 	if (bt_mesh_prov_send(buf, NULL)) {
@@ -216,9 +226,19 @@ static void public_key_sent(int err, void *cb_data)
 	}
 }
 
+static void start_auth(void)
+{
+	if (atomic_test_bit(bt_mesh_prov_link.flags, WAIT_NUMBER) ||
+	atomic_test_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
+		bt_mesh_prov_link.expect = PROV_NO_PDU; /* Wait for input */
+	} else {
+		bt_mesh_prov_link.expect = PROV_CONFIRM;
+	}
+}
+
 static void send_pub_key(void)
 {
-	struct os_mbuf *buf = PROV_BUF(65);
+	struct os_mbuf *buf = PROV_BUF(PDU_LEN_PUB_KEY);
 	const uint8_t *key;
 
 	key = bt_pub_key_get();
@@ -228,31 +248,38 @@ static void send_pub_key(void)
 		return;
 	}
 
-	BT_DBG("Local Public Key: %s", bt_hex(key, 64));
-
 	bt_mesh_prov_buf_init(buf, PROV_PUB_KEY);
 
 	/* Swap X and Y halves independently to big-endian */
-	sys_memcpy_swap(net_buf_simple_add(buf, 32), key, 32);
-	sys_memcpy_swap(net_buf_simple_add(buf, 32), &key[32], 32);
+	sys_memcpy_swap(net_buf_simple_add(buf, BT_PUB_KEY_COORD_LEN), key, BT_PUB_KEY_COORD_LEN);
+	sys_memcpy_swap(net_buf_simple_add(buf, BT_PUB_KEY_COORD_LEN), &key[BT_PUB_KEY_COORD_LEN], 32);
+
+	BT_DBG("Local Public Key: %s", bt_hex(buf->om_data + 1, BT_PUB_KEY_LEN));
 
-	/* PublicKeyRemote */
-	memcpy(&bt_mesh_prov_link.conf_inputs[81], &buf->om_data[1], 64);
+	/* PublicKeyDevice */
+	memcpy(bt_mesh_prov_link.conf_inputs.pub_key_device, &buf->om_data[1], PDU_LEN_PUB_KEY);
 
 	if (bt_mesh_prov_send(buf, public_key_sent)) {
 		BT_ERR("Failed to send Public Key");
 		return;
 	}
 
-	if (atomic_test_bit(bt_mesh_prov_link.flags, WAIT_NUMBER) ||
-	    atomic_test_bit(bt_mesh_prov_link.flags, WAIT_STRING)) {
-		bt_mesh_prov_link.expect = PROV_NO_PDU; /* Wait for input */
-	} else {
-		bt_mesh_prov_link.expect = PROV_CONFIRM;
+	start_auth();
+}
+
+static void dh_key_gen_complete(void)
+{
+	BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, BT_DH_KEY_LEN));
+
+	if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY) &&
+	atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
+		send_confirm();
+	} else if (!atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
+		send_pub_key();
 	}
 }
 
-static void prov_dh_key_cb(const uint8_t dhkey[32])
+static void prov_dh_key_cb(const uint8_t dhkey[BT_DH_KEY_LEN])
 {
 	BT_DBG("%p", dhkey);
 
@@ -262,25 +289,41 @@ static void prov_dh_key_cb(const uint8_t dhkey[32])
 		return;
 	}
 
-	sys_memcpy_swap(bt_mesh_prov_link.dhkey, dhkey, 32);
-
-	BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, 32));
+	sys_memcpy_swap(bt_mesh_prov_link.dhkey, dhkey, BT_DH_KEY_LEN);
 
-	send_pub_key();
+	dh_key_gen_complete();
 }
 
 static void prov_dh_key_gen(void)
 {
-	uint8_t remote_pk_le[64], *remote_pk;
+	const uint8_t *remote_pk;
+	uint8_t remote_pk_le[BT_PUB_KEY_LEN];
+
+	remote_pk = bt_mesh_prov_link.conf_inputs.pub_key_provisioner;
+	if (MYNEWT_VAL(BLE_MESH_PROV_OOB_PUBLIC_KEY) &&
+	    atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
+		if (uECC_valid_public_key(remote_pk, &curve_secp256r1)) {
+			BT_ERR("Public key is not valid");
+		} else if (uECC_shared_secret(remote_pk, bt_mesh_prov->private_key_be,
+					      bt_mesh_prov_link.dhkey,
+					      &curve_secp256r1) != TC_CRYPTO_SUCCESS) {
+			BT_ERR("DHKey generation failed");
+		} else {
+			dh_key_gen_complete();
+			return;
+		}
 
-	remote_pk = &bt_mesh_prov_link.conf_inputs[17];
+		prov_fail(PROV_ERR_UNEXP_ERR);
+		return;
+	}
 
 	/* Copy remote key in little-endian for bt_dh_key_gen().
 	 * X and Y halves are swapped independently. The bt_dh_key_gen()
 	 * will also take care of validating the remote public key.
 	 */
-	sys_memcpy_swap(remote_pk_le, remote_pk, 32);
-	sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32);
+	sys_memcpy_swap(remote_pk_le, remote_pk, BT_PUB_KEY_COORD_LEN);
+	sys_memcpy_swap(&remote_pk_le[BT_PUB_KEY_COORD_LEN], &remote_pk[BT_PUB_KEY_COORD_LEN],
+			BT_PUB_KEY_COORD_LEN);
 
 	if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) {
 		BT_ERR("Failed to generate DHKey");
@@ -290,12 +333,27 @@ static void prov_dh_key_gen(void)
 
 static void prov_pub_key(const uint8_t *data)
 {
-	BT_DBG("Remote Public Key: %s", bt_hex(data, 64));
+	BT_DBG("Remote Public Key: %s", bt_hex(data, BT_PUB_KEY_LEN));
 
 	/* PublicKeyProvisioner */
-	memcpy(&bt_mesh_prov_link.conf_inputs[17], data, 64);
+	memcpy(bt_mesh_prov_link.conf_inputs.pub_key_provisioner, data, PDU_LEN_PUB_KEY);
+
+	if (MYNEWT_VAL(BLE_MESH_PROV_OOB_PUBLIC_KEY) &&
+	    atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
+		if (!bt_mesh_prov->public_key_be || !bt_mesh_prov->private_key_be) {
+			BT_ERR("Public or private key is not ready");
+			prov_fail(PROV_ERR_UNEXP_ERR);
+			return;
+		}
+
+		/* No swap needed since user provides public key in big-endian */
+		memcpy(bt_mesh_prov_link.conf_inputs.pub_key_device, bt_mesh_prov->public_key_be,
+		       PDU_LEN_PUB_KEY);
+
+		atomic_set_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY);
 
-	if (!bt_pub_key_get()) {
+		start_auth();
+	} else if (!bt_pub_key_get()) {
 		/* Clear retransmit timer */
 		bt_mesh_prov_link.bearer->clear_tx();
 		atomic_set_bit(bt_mesh_prov_link.flags, WAIT_PUB_KEY);
@@ -331,7 +389,7 @@ static void notify_input_complete(void)
 
 static void send_random(void)
 {
-	struct os_mbuf *rnd = PROV_BUF(17);
+	struct os_mbuf *rnd = PROV_BUF(PDU_LEN_RANDOM);
 
 	bt_mesh_prov_buf_init(rnd, PROV_RANDOM);
 	net_buf_simple_add_mem(rnd, bt_mesh_prov_link.rand, 16);
@@ -390,7 +448,9 @@ static void prov_confirm(const uint8_t *data)
 
 	notify_input_complete();
 
-	send_confirm();
+	if (!atomic_test_and_clear_bit(bt_mesh_prov_link.flags, WAIT_DH_KEY)) {
+		send_confirm();
+	}
 }
 
 static inline bool is_pb_gatt(void)
@@ -401,7 +461,7 @@ static inline bool is_pb_gatt(void)
 
 static void prov_data(const uint8_t *data)
 {
-	struct os_mbuf *msg = PROV_BUF(1);
+	struct os_mbuf *msg = PROV_BUF(PDU_LEN_COMPLETE);
 	uint8_t session_key[16];
 	uint8_t nonce[13];
 	uint8_t dev_key[16];
@@ -492,7 +552,8 @@ static void prov_data(const uint8_t *data)
 
 static void local_input_complete(void)
 {
-	if (atomic_test_bit(bt_mesh_prov_link.flags, PUB_KEY_SENT)) {
+	if (atomic_test_bit(bt_mesh_prov_link.flags, PUB_KEY_SENT) ||
+	    atomic_test_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY)) {
 		send_input_complete();
 	} else {
 		atomic_set_bit(bt_mesh_prov_link.flags, INPUT_COMPLETE);
@@ -554,6 +615,10 @@ int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers)
 		return -EALREADY;
 	}
 
+	if (bt_mesh_prov_active()) {
+		return -EBUSY;
+	}
+
 	if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
 	    (bearers & BT_MESH_PROV_ADV)) {
 		bt_mesh_beacon_disable();
@@ -562,7 +627,7 @@ int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers)
 
 	if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
 	    (bearers & BT_MESH_PROV_GATT)) {
-		bt_mesh_proxy_prov_disable(true);
+		(void)bt_mesh_pb_gatt_disable();
 	}
 
 	return 0;
diff --git a/nimble/host/mesh/src/provisioner.c b/nimble/host/mesh/src/provisioner.c
index d69aa6650a..f4278e110b 100644
--- a/nimble/host/mesh/src/provisioner.c
+++ b/nimble/host/mesh/src/provisioner.c
@@ -3,6 +3,7 @@
 /*
  * Copyright (c) 2017 Intel Corporation
  * Copyright (c) 2020 Lingao Meng
+ * Copyright (c) 2021 Manulytica Limited
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -65,14 +66,15 @@ static void prov_fail(uint8_t reason)
 
 static void send_invite(void)
 {
-	struct os_mbuf *inv = PROV_BUF(2);
+	struct os_mbuf *inv = PROV_BUF(PDU_LEN_INVITE);
 
 	BT_DBG("");
 
 	bt_mesh_prov_buf_init(inv, PROV_INVITE);
 	net_buf_simple_add_u8(inv, prov_device.attention_duration);
 
-	bt_mesh_prov_link.conf_inputs[0] = prov_device.attention_duration;
+	memcpy(bt_mesh_prov_link.conf_inputs.invite, &prov_device.attention_duration,
+	       PDU_LEN_INVITE);
 
 	if (bt_mesh_prov_send(inv, NULL)) {
 		BT_ERR("Failed to send invite");
@@ -95,54 +97,35 @@ static void start_sent(int err, void *cb_data)
 static void send_start(void)
 {
 	BT_DBG("");
-	uint8_t method, action;
-	struct os_mbuf *start = PROV_BUF(6);
+	struct os_mbuf *start = PROV_BUF(PDU_LEN_START);
 
-	const uint8_t *data = &bt_mesh_prov_link.conf_inputs[1 + 3];
+	bool oob_pub_key = bt_mesh_prov_link.conf_inputs.capabilities[3] == PUB_KEY_OOB;
 
 	bt_mesh_prov_buf_init(start, PROV_START);
 	net_buf_simple_add_u8(start, PROV_ALG_P256);
 
-	if (atomic_test_bit(bt_mesh_prov_link.flags, REMOTE_PUB_KEY) &&
-	    *data == PUB_KEY_OOB) {
+	memcpy(bt_mesh_prov_link.conf_inputs.start, &start->om_data[1], PDU_LEN_START);
+
+	if (atomic_test_bit(bt_mesh_prov_link.flags, REMOTE_PUB_KEY) && oob_pub_key) {
 		net_buf_simple_add_u8(start, PUB_KEY_OOB);
 		atomic_set_bit(bt_mesh_prov_link.flags, OOB_PUB_KEY);
 	} else {
 		net_buf_simple_add_u8(start, PUB_KEY_NO_OOB);
 	}
 
-	if (bt_mesh_prov_link.oob_method == AUTH_METHOD_INPUT) {
-		method = AUTH_METHOD_OUTPUT;
-		if (bt_mesh_prov_link.oob_action == INPUT_OOB_STRING) {
-			action = OUTPUT_OOB_STRING;
-		} else {
-			action = OUTPUT_OOB_NUMBER;
-		}
-
-	} else if (bt_mesh_prov_link.oob_method == AUTH_METHOD_OUTPUT) {
-		method = AUTH_METHOD_INPUT;
-		if (bt_mesh_prov_link.oob_action == OUTPUT_OOB_STRING) {
-			action = INPUT_OOB_STRING;
-		} else {
-			action = INPUT_OOB_NUMBER;
-		}
-	} else {
-		method = bt_mesh_prov_link.oob_method;
-		action = 0x00;
-	}
-
 	net_buf_simple_add_u8(start, bt_mesh_prov_link.oob_method);
 
 	net_buf_simple_add_u8(start, bt_mesh_prov_link.oob_action);
 
 	net_buf_simple_add_u8(start, bt_mesh_prov_link.oob_size);
 
-	memcpy(&bt_mesh_prov_link.conf_inputs[12], &start->om_data[1], 5);
+	memcpy(bt_mesh_prov_link.conf_inputs.invite, &start->om_data[1], PDU_LEN_INVITE);
 
-	if (bt_mesh_prov_auth(method, action, bt_mesh_prov_link.oob_size) < 0) {
+	if (bt_mesh_prov_auth(true, bt_mesh_prov_link.oob_method,
+			      bt_mesh_prov_link.oob_action, bt_mesh_prov_link.oob_size) < 0) {
 		BT_ERR("Invalid authentication method: 0x%02x; "
-		       "action: 0x%02x; size: 0x%02x", method,
-		       action, bt_mesh_prov_link.oob_size);
+		       "action: 0x%02x; size: 0x%02x", bt_mesh_prov_link.oob_method,
+		       bt_mesh_prov_link.oob_action, bt_mesh_prov_link.oob_size);
 		return;
 	}
 
@@ -168,9 +151,9 @@ static bool prov_check_method(struct bt_mesh_dev_capabilities *caps)
 		}
 
 		if (!(BIT(bt_mesh_prov_link.oob_action) & caps->input_actions)) {
-			BT_WARN("The required input action (0x%02x) "
+			BT_WARN("The required input action (0x%04x) "
 				"not supported by the device (0x%02x)",
-				bt_mesh_prov_link.oob_action, caps->input_actions);
+				(uint16_t)BIT(bt_mesh_prov_link.oob_action), caps->input_actions);
 			return false;
 		}
 
@@ -194,9 +177,9 @@ static bool prov_check_method(struct bt_mesh_dev_capabilities *caps)
 		}
 
 		if (!(BIT(bt_mesh_prov_link.oob_action) & caps->output_actions)) {
-			BT_WARN("The required output action (0x%02x) "
+			BT_WARN("The required output action (0x%04x) "
 				"not supported by the device (0x%02x)",
-				bt_mesh_prov_link.oob_action, caps->output_actions);
+				(uint16_t)BIT(bt_mesh_prov_link.oob_action), caps->output_actions);
 			return false;
 		}
 
@@ -226,9 +209,11 @@ static void prov_capabilities(const uint8_t *data)
 	BT_DBG("Static OOB Type:   0x%02x", caps.static_oob);
 	BT_DBG("Output OOB Size:   %u", caps.output_size);
 
-	caps.output_actions = (bt_mesh_output_action_t)data[6];
+	caps.output_actions = (bt_mesh_output_action_t)
+		(sys_get_be16(&data[6]));
 	caps.input_size = data[8];
-	caps.input_actions = (bt_mesh_input_action_t)data[9];
+	caps.input_actions = (bt_mesh_input_action_t)
+		(sys_get_be16(&data[9]));
 	BT_DBG("Output OOB Action: 0x%04x", caps.output_actions);
 	BT_DBG("Input OOB Size:    %u", caps.input_size);
 	BT_DBG("Input OOB Action:  0x%04x", caps.input_actions);
@@ -249,7 +234,7 @@ static void prov_capabilities(const uint8_t *data)
 		return;
 	}
 #endif
-	memcpy(&bt_mesh_prov_link.conf_inputs[1], data, 11);
+	memcpy(bt_mesh_prov_link.conf_inputs.capabilities, data, PDU_LEN_CAPABILITIES);
 
 	if (bt_mesh_prov->capabilities) {
 		bt_mesh_prov->capabilities(&caps);
@@ -265,14 +250,14 @@ static void prov_capabilities(const uint8_t *data)
 
 static void send_confirm(void)
 {
-	struct os_mbuf *cfm = PROV_BUF(17);
+	struct os_mbuf *cfm = PROV_BUF(PDU_LEN_CONFIRM);
+	uint8_t *inputs = (uint8_t *)&bt_mesh_prov_link.conf_inputs;
 
-	BT_DBG("ConfInputs[0]   %s", bt_hex(bt_mesh_prov_link.conf_inputs, 64));
-	BT_DBG("ConfInputs[64]  %s", bt_hex(&bt_mesh_prov_link.conf_inputs[64], 64));
-	BT_DBG("ConfInputs[128] %s", bt_hex(&bt_mesh_prov_link.conf_inputs[128], 17));
+	BT_DBG("ConfInputs[0]   %s", bt_hex(inputs, 64));
+	BT_DBG("ConfInputs[64]  %s", bt_hex(&inputs[64], 64));
+	BT_DBG("ConfInputs[128] %s", bt_hex(&inputs[128], 17));
 
-	if (bt_mesh_prov_conf_salt(bt_mesh_prov_link.conf_inputs,
-				   bt_mesh_prov_link.conf_salt)) {
+	if (bt_mesh_prov_conf_salt(inputs, bt_mesh_prov_link.conf_salt)) {
 		BT_ERR("Unable to generate confirmation salt");
 		prov_fail(PROV_ERR_UNEXP_ERR);
 		return;
@@ -301,12 +286,14 @@ static void send_confirm(void)
 
 	if (bt_mesh_prov_conf(bt_mesh_prov_link.conf_key,
 			      bt_mesh_prov_link.rand, bt_mesh_prov_link.auth,
-			      net_buf_simple_add(cfm, 16))) {
+			      bt_mesh_prov_link.conf)) {
 		BT_ERR("Unable to generate confirmation value");
 		prov_fail(PROV_ERR_UNEXP_ERR);
 		return;
 	}
 
+	net_buf_simple_add_mem(cfm, bt_mesh_prov_link.conf, 16);
+
 	if (bt_mesh_prov_send(cfm, NULL)) {
 		BT_ERR("Failed to send Provisioning Confirm");
 		return;
@@ -324,13 +311,11 @@ static void public_key_sent(int err, void *cb_data)
 		prov_dh_key_gen();
 		return;
 	}
-
-	bt_mesh_prov_link.expect = PROV_PUB_KEY;
 }
 
 static void send_pub_key(void)
 {
-	struct os_mbuf *buf = PROV_BUF(65);
+	struct os_mbuf *buf = PROV_BUF(PDU_LEN_PUB_KEY);
 	const uint8_t *key;
 
 	key = bt_pub_key_get();
@@ -340,24 +325,27 @@ static void send_pub_key(void)
 		return;
 	}
 
-	BT_DBG("Local Public Key: %s", bt_hex(key, 64));
+	BT_DBG("Local Public Key: %s", bt_hex(key, BT_PUB_KEY_LEN));
 
 	bt_mesh_prov_buf_init(buf, PROV_PUB_KEY);
 
 	/* Swap X and Y halves independently to big-endian */
-	sys_memcpy_swap(net_buf_simple_add(buf, 32), key, 32);
-	sys_memcpy_swap(net_buf_simple_add(buf, 32), &key[32], 32);
+	sys_memcpy_swap(net_buf_simple_add(buf, BT_PUB_KEY_COORD_LEN), key, BT_PUB_KEY_COORD_LEN);
+	sys_memcpy_swap(net_buf_simple_add(buf, BT_PUB_KEY_COORD_LEN), &key[BT_PUB_KEY_COORD_LEN],
+			BT_PUB_KEY_COORD_LEN);
 
 	/* PublicKeyProvisioner */
-	memcpy(&bt_mesh_prov_link.conf_inputs[17], &buf->om_databuf[1], 64);
+	memcpy(bt_mesh_prov_link.conf_inputs.pub_key_provisioner, &buf->om_data[1], PDU_LEN_PUB_KEY);
 
 	if (bt_mesh_prov_send(buf, public_key_sent)) {
 		BT_ERR("Failed to send Public Key");
 		return;
 	}
+
+	bt_mesh_prov_link.expect = PROV_PUB_KEY;
 }
 
-static void prov_dh_key_cb(const uint8_t dhkey[32])
+static void prov_dh_key_cb(const uint8_t dhkey[BT_DH_KEY_LEN])
 {
 	BT_DBG("%p", dhkey);
 
@@ -367,9 +355,9 @@ static void prov_dh_key_cb(const uint8_t dhkey[32])
 		return;
 	}
 
-	sys_memcpy_swap(bt_mesh_prov_link.dhkey, dhkey, 32);
+	sys_memcpy_swap(bt_mesh_prov_link.dhkey, dhkey, BT_DH_KEY_LEN);
 
-	BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, 32));
+	BT_DBG("DHkey: %s", bt_hex(bt_mesh_prov_link.dhkey, BT_DH_KEY_LEN));
 
 	if (atomic_test_bit(bt_mesh_prov_link.flags, WAIT_STRING) ||
 	    atomic_test_bit(bt_mesh_prov_link.flags, WAIT_NUMBER) ||
@@ -383,16 +371,26 @@ static void prov_dh_key_cb(const uint8_t dhkey[32])
 
 static void prov_dh_key_gen(void)
 {
-	uint8_t remote_pk_le[64], *remote_pk;
+	uint8_t remote_pk_le[BT_PUB_KEY_LEN];
+	const uint8_t *remote_pk;
+	const uint8_t *local_pk;
 
-	remote_pk = &bt_mesh_prov_link.conf_inputs[81];
+	local_pk = bt_mesh_prov_link.conf_inputs.pub_key_provisioner;
+	remote_pk = bt_mesh_prov_link.conf_inputs.pub_key_device;
 
 	/* Copy remote key in little-endian for bt_dh_key_gen().
 	 * X and Y halves are swapped independently. The bt_dh_key_gen()
 	 * will also take care of validating the remote public key.
 	 */
-	sys_memcpy_swap(remote_pk_le, remote_pk, 32);
-	sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32);
+	sys_memcpy_swap(remote_pk_le, remote_pk, BT_PUB_KEY_COORD_LEN);
+	sys_memcpy_swap(&remote_pk_le[BT_PUB_KEY_COORD_LEN], &remote_pk[BT_PUB_KEY_COORD_LEN],
+			BT_PUB_KEY_COORD_LEN);
+
+	if (!memcmp(local_pk, remote_pk, BT_PUB_KEY_LEN)) {
+		BT_ERR("Public keys are identical");
+		prov_fail(PROV_ERR_NVAL_FMT);
+		return;
+	}
 
 	if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) {
 		BT_ERR("Failed to generate DHKey");
@@ -406,12 +404,12 @@ static void prov_dh_key_gen(void)
 
 static void prov_pub_key(const uint8_t *data)
 {
-	BT_DBG("Remote Public Key: %s", bt_hex(data, 64));
+	BT_DBG("Remote Public Key: %s", bt_hex(data, BT_PUB_KEY_LEN));
 
 	atomic_set_bit(bt_mesh_prov_link.flags, REMOTE_PUB_KEY);
 
 	/* PublicKeyDevice */
-	memcpy(&bt_mesh_prov_link.conf_inputs[81], data, 64);
+	memcpy(bt_mesh_prov_link.conf_inputs.pub_key_device, data, BT_PUB_KEY_LEN);
 	bt_mesh_prov_link.bearer->clear_tx();
 
 	prov_dh_key_gen();
@@ -453,7 +451,7 @@ static void prov_input_complete(const uint8_t *data)
 
 static void send_prov_data(void)
 {
-	struct os_mbuf *pdu = PROV_BUF(34);
+	struct os_mbuf *pdu = PROV_BUF(PDU_LEN_DATA);
 #if MYNEWT_VAL(BLE_MESH_CDB)
 	struct bt_mesh_cdb_subnet *sub;
 #endif
@@ -501,7 +499,7 @@ static void send_prov_data(void)
 #endif
 	bt_mesh_prov_buf_init(pdu, PROV_DATA);
 #if MYNEWT_VAL(BLE_MESH_CDB)
-	net_buf_simple_add_mem(pdu, sub->keys[sub->kr_flag].net_key, 16);
+	net_buf_simple_add_mem(pdu, sub->keys[SUBNET_KEY_TX_IDX(sub)].net_key, 16);
 	net_buf_simple_add_be16(pdu, prov_device.node->net_idx);
 	net_buf_simple_add_u8(pdu, bt_mesh_cdb_subnet_flags(sub));
 	net_buf_simple_add_be32(pdu, bt_mesh_cdb.iv_index);
@@ -539,7 +537,7 @@ static void prov_complete(const uint8_t *data)
 
 #if MYNEWT_VAL(BLE_MESH_CDB)
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_cdb_node(node);
+		bt_mesh_cdb_node_store(node);
 	}
 #endif
 
@@ -554,7 +552,7 @@ static void prov_complete(const uint8_t *data)
 
 static void send_random(void)
 {
-	struct os_mbuf *rnd = PROV_BUF(17);
+	struct os_mbuf *rnd = PROV_BUF(PDU_LEN_RANDOM);
 
 	bt_mesh_prov_buf_init(rnd, PROV_RANDOM);
 	net_buf_simple_add_mem(rnd, bt_mesh_prov_link.rand, 16);
@@ -609,6 +607,12 @@ static void prov_confirm(const uint8_t *data)
 {
 	BT_DBG("Remote Confirm: %s", bt_hex(data, 16));
 
+	if (!memcmp(data, bt_mesh_prov_link.conf, 16)) {
+		BT_ERR("Confirm value is identical to ours, rejecting.");
+		prov_fail(PROV_ERR_CFM_FAILED);
+		return;
+	}
+
 	memcpy(bt_mesh_prov_link.conf, data, 16);
 
 	send_random();
@@ -662,7 +666,7 @@ static void prov_set_method(uint8_t method, uint8_t action, uint8_t size)
 
 int bt_mesh_auth_method_set_input(bt_mesh_input_action_t action, uint8_t size)
 {
-	if (!action || !size || size > 8) {
+	if (!action || !size || size > PROV_IO_OOB_SIZE_MAX) {
 		return -EINVAL;
 	}
 
@@ -672,7 +676,7 @@ int bt_mesh_auth_method_set_input(bt_mesh_input_action_t action, uint8_t size)
 
 int bt_mesh_auth_method_set_output(bt_mesh_output_action_t action, uint8_t size)
 {
-	if (!action || !size || size > 8) {
+	if (!action || !size || size > PROV_IO_OOB_SIZE_MAX) {
 		return -EINVAL;
 	}
 
@@ -702,7 +706,7 @@ int bt_mesh_auth_method_set_none(void)
 	return 0;
 }
 
-int bt_mesh_prov_remote_pub_key_set(const uint8_t public_key[64])
+int bt_mesh_prov_remote_pub_key_set(const uint8_t public_key[BT_PUB_KEY_LEN])
 {
 	if (public_key == NULL) {
 		return -EINVAL;
@@ -713,8 +717,7 @@ int bt_mesh_prov_remote_pub_key_set(const uint8_t public_key[64])
 	}
 
 	/* Swap X and Y halves independently to big-endian */
-	memcpy(&bt_mesh_prov_link.conf_inputs[81], public_key, 32);
-	memcpy(&bt_mesh_prov_link.conf_inputs[81 + 32], &public_key[32], 32);
+	memcpy(bt_mesh_prov_link.conf_inputs.pub_key_device, public_key, PDU_LEN_PUB_KEY);
 
 	return 0;
 }
diff --git a/nimble/host/mesh/src/provisioner.h b/nimble/host/mesh/src/provisioner.h
index ccda47efed..315bde299f 100644
--- a/nimble/host/mesh/src/provisioner.h
+++ b/nimble/host/mesh/src/provisioner.h
@@ -7,4 +7,4 @@
  */
 
 int bt_mesh_pb_adv_open(const uint8_t uuid[16], uint16_t net_idx, uint16_t addr,
-			uint8_t attention_duration);
\ No newline at end of file
+			uint8_t attention_duration);
diff --git a/nimble/host/mesh/src/proxy.c b/nimble/host/mesh/src/proxy.c
deleted file mode 100644
index 6d631b7211..0000000000
--- a/nimble/host/mesh/src/proxy.c
+++ /dev/null
@@ -1,1568 +0,0 @@
-/*  Bluetooth Mesh */
-
-/*
- * Copyright (c) 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include "syscfg/syscfg.h"
-#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG
-
-#if MYNEWT_VAL(BLE_MESH_PROXY)
-
-#include "mesh/mesh.h"
-#include "host/ble_att.h"
-#include "services/gatt/ble_svc_gatt.h"
-#include "../../host/src/ble_hs_priv.h"
-
-#include "mesh_priv.h"
-#include "adv.h"
-#include "net.h"
-#include "rpl.h"
-#include "prov.h"
-#include "beacon.h"
-#include "foundation.h"
-#include "access.h"
-#include "proxy.h"
-
-#define PDU_TYPE(data)     (data[0] & BIT_MASK(6))
-#define PDU_SAR(data)      (data[0] >> 6)
-
-#define BT_UUID_16_ENCODE(w16)  \
-	(((w16) >>  0) & 0xFF), \
-	(((w16) >>  8) & 0xFF)
-/* Mesh Profile 1.0 Section 6.6:
- * "The timeout for the SAR transfer is 20 seconds. When the timeout
- *  expires, the Proxy Server shall disconnect."
- */
-#define PROXY_SAR_TIMEOUT  K_SECONDS(20)
-
-#define SAR_COMPLETE       0x00
-#define SAR_FIRST          0x01
-#define SAR_CONT           0x02
-#define SAR_LAST           0x03
-
-#define CFG_FILTER_SET     0x00
-#define CFG_FILTER_ADD     0x01
-#define CFG_FILTER_REMOVE  0x02
-#define CFG_FILTER_STATUS  0x03
-
-/** @def BT_UUID_MESH_PROV
- *  @brief Mesh Provisioning Service
- */
-ble_uuid16_t BT_UUID_MESH_PROV                 = BLE_UUID16_INIT(0x1827);
-#define BT_UUID_MESH_PROV_VAL             0x1827
-/** @def BT_UUID_MESH_PROXY
- *  @brief Mesh Proxy Service
- */
-ble_uuid16_t BT_UUID_MESH_PROXY                = BLE_UUID16_INIT(0x1828);
-#define BT_UUID_MESH_PROXY_VAL            0x1828
-/** @def BT_UUID_GATT_CCC
- *  @brief GATT Client Characteristic Configuration
- */
-ble_uuid16_t BT_UUID_GATT_CCC                  = BLE_UUID16_INIT(0x2902);
-#define BT_UUID_GATT_CCC_VAL              0x2902
-/** @def BT_UUID_MESH_PROV_DATA_IN
- *  @brief Mesh Provisioning Data In
- */
-ble_uuid16_t BT_UUID_MESH_PROV_DATA_IN         = BLE_UUID16_INIT(0x2adb);
-#define BT_UUID_MESH_PROV_DATA_IN_VAL     0x2adb
-/** @def BT_UUID_MESH_PROV_DATA_OUT
- *  @brief Mesh Provisioning Data Out
- */
-ble_uuid16_t BT_UUID_MESH_PROV_DATA_OUT        = BLE_UUID16_INIT(0x2adc);
-#define BT_UUID_MESH_PROV_DATA_OUT_VAL    0x2adc
-/** @def BT_UUID_MESH_PROXY_DATA_IN
- *  @brief Mesh Proxy Data In
- */
-ble_uuid16_t BT_UUID_MESH_PROXY_DATA_IN        = BLE_UUID16_INIT(0x2add);
-#define BT_UUID_MESH_PROXY_DATA_IN_VAL    0x2add
-/** @def BT_UUID_MESH_PROXY_DATA_OUT
- *  @brief Mesh Proxy Data Out
- */
-ble_uuid16_t BT_UUID_MESH_PROXY_DATA_OUT       = BLE_UUID16_INIT(0x2ade);
-#define BT_UUID_MESH_PROXY_DATA_OUT_VAL   0x2ade
-
-#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
-
-#define CLIENT_BUF_SIZE 68
-
-static const struct ble_gap_adv_params slow_adv_param = {
-	.conn_mode = (BLE_GAP_CONN_MODE_UND),
-	.disc_mode = (BLE_GAP_DISC_MODE_GEN),
-	.itvl_min = BT_GAP_ADV_SLOW_INT_MIN,
-	.itvl_max = BT_GAP_ADV_SLOW_INT_MAX,
-};
-
-static const struct ble_gap_adv_params fast_adv_param = {
-	.conn_mode = (BLE_GAP_CONN_MODE_UND),
-	.disc_mode = (BLE_GAP_DISC_MODE_GEN),
-	.itvl_min = BT_GAP_ADV_FAST_INT_MIN_2,
-	.itvl_max = BT_GAP_ADV_FAST_INT_MAX_2,
-};
-
-static bool proxy_adv_enabled;
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-static void proxy_send_beacons(struct ble_npl_event *work);
-#endif
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-static bool prov_fast_adv;
-#endif
-
-static struct bt_mesh_proxy_client {
-	uint16_t conn_handle;
-	uint16_t filter[MYNEWT_VAL(BLE_MESH_PROXY_FILTER_SIZE)];
-	enum __packed {
-		NONE,
-		WHITELIST,
-		BLACKLIST,
-		PROV,
-	} filter_type;
-	uint8_t msg_type;
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-	struct ble_npl_callout send_beacons;
-#endif
-	struct k_delayed_work sar_timer;
-	struct os_mbuf    *buf;
-} clients[MYNEWT_VAL(BLE_MAX_CONNECTIONS)] = {
-	[0 ... (MYNEWT_VAL(BLE_MAX_CONNECTIONS) - 1)] = { 0 },
-};
-
-static sys_slist_t idle_waiters;
-static atomic_t pending_notifications;
-
-/* Track which service is enabled */
-static enum {
-	MESH_GATT_NONE,
-	MESH_GATT_PROV,
-	MESH_GATT_PROXY,
-} gatt_svc = MESH_GATT_NONE;
-
-static struct {
-	uint16_t proxy_h;
-	uint16_t proxy_data_out_h;
-	uint16_t prov_h;
-	uint16_t prov_data_in_h;
-	uint16_t prov_data_out_h;
-} svc_handles;
-
-static void resolve_svc_handles(void)
-{
-	int rc;
-
-	/* Either all handles are already resolved, or none of them */
-	if (svc_handles.prov_data_out_h) {
-		return;
-	}
-
-	/*
-	 * We assert if attribute is not found since at this stage all attributes
-	 * shall be already registered and thus shall be found.
-	 */
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
-				&svc_handles.proxy_h);
-	assert(rc == 0);
-
-	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
-				BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
-				NULL, &svc_handles.proxy_data_out_h);
-	assert(rc == 0);
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
-				&svc_handles.prov_h);
-	assert(rc == 0);
-
-	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
-				BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
-				NULL, &svc_handles.prov_data_in_h);
-	assert(rc == 0);
-
-	rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
-				BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
-				NULL, &svc_handles.prov_data_out_h);
-	assert(rc == 0);
-}
-
-static struct bt_mesh_proxy_client *find_client(uint16_t conn_handle)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		if (clients[i].conn_handle == conn_handle) {
-			return &clients[i];
-		}
-	}
-
-	return NULL;
-}
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-/* Next subnet in queue to be advertised */
-static struct bt_mesh_subnet *beacon_sub;
-
-static int proxy_segment_and_send(uint16_t conn_handle, uint8_t type,
-				  struct os_mbuf *msg);
-
-static int filter_set(struct bt_mesh_proxy_client *client,
-		      struct os_mbuf *buf)
-{
-	uint8_t type;
-
-	if (buf->om_len < 1) {
-		BT_WARN("Too short Filter Set message");
-		return -EINVAL;
-	}
-
-	type = net_buf_simple_pull_u8(buf);
-	BT_DBG("type 0x%02x", type);
-
-	switch (type) {
-	case 0x00:
-		memset(client->filter, 0, sizeof(client->filter));
-		client->filter_type = WHITELIST;
-		break;
-	case 0x01:
-		memset(client->filter, 0, sizeof(client->filter));
-		client->filter_type = BLACKLIST;
-		break;
-	default:
-		BT_WARN("Prohibited Filter Type 0x%02x", type);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static void filter_add(struct bt_mesh_proxy_client *client, uint16_t addr)
-{
-	int i;
-
-	BT_DBG("addr 0x%04x", addr);
-
-	if (addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
-		if (client->filter[i] == addr) {
-			return;
-		}
-	}
-
-	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
-		if (client->filter[i] == BT_MESH_ADDR_UNASSIGNED) {
-			client->filter[i] = addr;
-			return;
-		}
-	}
-}
-
-static void filter_remove(struct bt_mesh_proxy_client *client, uint16_t addr)
-{
-	int i;
-
-	BT_DBG("addr 0x%04x", addr);
-
-	if (addr == BT_MESH_ADDR_UNASSIGNED) {
-		return;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
-		if (client->filter[i] == addr) {
-			client->filter[i] = BT_MESH_ADDR_UNASSIGNED;
-			return;
-		}
-	}
-}
-
-static void send_filter_status(struct bt_mesh_proxy_client *client,
-			       struct bt_mesh_net_rx *rx,
-			       struct os_mbuf *buf)
-{
-	struct bt_mesh_net_tx tx = {
-		.sub = rx->sub,
-		.ctx = &rx->ctx,
-		.src = bt_mesh_primary_addr(),
-	};
-	uint16_t filter_size;
-	int i, err;
-
-	/* Configuration messages always have dst unassigned */
-	tx.ctx->addr = BT_MESH_ADDR_UNASSIGNED;
-
-	net_buf_simple_init(buf, 10);
-
-	net_buf_simple_add_u8(buf, CFG_FILTER_STATUS);
-
-	if (client->filter_type == WHITELIST) {
-		net_buf_simple_add_u8(buf, 0x00);
-	} else {
-		net_buf_simple_add_u8(buf, 0x01);
-	}
-
-	for (filter_size = 0, i = 0; i < ARRAY_SIZE(client->filter); i++) {
-		if (client->filter[i] != BT_MESH_ADDR_UNASSIGNED) {
-			filter_size++;
-		}
-	}
-
-	net_buf_simple_add_be16(buf, filter_size);
-
-	BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
-
-	err = bt_mesh_net_encode(&tx, buf, true);
-	if (err) {
-		BT_ERR("Encoding Proxy cfg message failed (err %d)", err);
-		return;
-	}
-
-	err = proxy_segment_and_send(client->conn_handle, BT_MESH_PROXY_CONFIG, buf);
-	if (err) {
-		BT_ERR("Failed to send proxy cfg message (err %d)", err);
-	}
-}
-
-static void proxy_cfg(struct bt_mesh_proxy_client *client)
-{
-	struct os_mbuf *buf = NET_BUF_SIMPLE(29);
-	struct bt_mesh_net_rx rx;
-	uint8_t opcode;
-	int err;
-
-	err = bt_mesh_net_decode(client->buf, BT_MESH_NET_IF_PROXY_CFG,
-				 &rx, buf);
-	if (err) {
-		BT_ERR("Failed to decode Proxy Configuration (err %d)", err);
-		goto done;
-	}
-
-	rx.local_match = 1U;
-
-		if (bt_mesh_rpl_check(&rx, NULL)) {	
-			BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",	
-					rx.ctx.addr, rx.ctx.recv_dst, rx.seq);	
-		goto done;
-	}
-
-	/* Remove network headers */
-	net_buf_simple_pull_mem(buf, BT_MESH_NET_HDR_LEN);
-
-	BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
-
-	if (buf->om_len < 1) {
-		BT_WARN("Too short proxy configuration PDU");
-		goto done;
-	}
-
-	opcode = net_buf_simple_pull_u8(buf);
-	switch (opcode) {
-	case CFG_FILTER_SET:
-		filter_set(client, buf);
-		send_filter_status(client, &rx, buf);
-		break;
-	case CFG_FILTER_ADD:
-		while (buf->om_len >= 2) {
-			uint16_t addr;
-
-			addr = net_buf_simple_pull_be16(buf);
-			filter_add(client, addr);
-		}
-		send_filter_status(client, &rx, buf);
-		break;
-	case CFG_FILTER_REMOVE:
-		while (buf->om_len >= 2) {
-			uint16_t addr;
-
-			addr = net_buf_simple_pull_be16(buf);
-			filter_remove(client, addr);
-		}
-		send_filter_status(client, &rx, buf);
-		break;
-	default:
-		BT_WARN("Unhandled configuration OpCode 0x%02x", opcode);
-		break;
-	}
-
-done:
-	os_mbuf_free_chain(buf);
-}
-
-static int beacon_send(uint16_t conn_handle, struct bt_mesh_subnet *sub)
-{
-	struct os_mbuf *buf = NET_BUF_SIMPLE(23);
-	int rc;
-
-	net_buf_simple_init(buf, 1);
-	bt_mesh_beacon_create(sub, buf);
-
-	rc = proxy_segment_and_send(conn_handle, BT_MESH_PROXY_BEACON, buf);
-	os_mbuf_free_chain(buf);
-	return rc;
-}
-
-static int send_beacon_cb(struct bt_mesh_subnet *sub, void *cb_data)
-{
-	struct bt_mesh_proxy_client *client = cb_data;
-
-	return beacon_send(client->conn_handle, sub);
-}
-
-static void proxy_send_beacons(struct ble_npl_event *work)
-{
-	struct bt_mesh_proxy_client *client;
-
-	client = ble_npl_event_get_arg(work);
-
-	(void)bt_mesh_subnet_find(send_beacon_cb, client);
-}
-
-static void proxy_sar_timeout(struct ble_npl_event *work)
-{
-	struct bt_mesh_proxy_client *client;
-	int rc;
-
-	BT_WARN("Proxy SAR timeout");
-
-	client = ble_npl_event_get_arg(work);
-	assert(client != NULL);
-
-	if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE)) {
-		rc = ble_gap_terminate(client->conn_handle,
-				       BLE_ERR_REM_USER_CONN_TERM);
-		assert(rc == 0);
-	}
-}
-
-void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
-{
-	int i;
-
-	if (!sub) {
-		/* NULL means we send on all subnets */
-		bt_mesh_subnet_foreach(bt_mesh_proxy_beacon_send);
-		return;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
-			beacon_send(clients[i].conn_handle, sub);
-		}
-	}
-}
-
-static void node_id_start(struct bt_mesh_subnet *sub)
-{
-	sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
-	sub->node_id_start = k_uptime_get_32();
-}
-
-void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
-{
-	node_id_start(sub);
-	/* Prioritize the recently enabled subnet */
-	beacon_sub = sub;
-}
-
-void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
-{
-	sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
-	sub->node_id_start = 0;
-}
-
-int bt_mesh_proxy_identity_enable(void)
-{
-	BT_DBG("");
-
-	if (!bt_mesh_is_provisioned()) {
-		return -EAGAIN;
-	}
-
-	if (bt_mesh_subnet_foreach(node_id_start)) {
-		bt_mesh_adv_update();
-	}
-
-	return 0;
-}
-
-#endif /* GATT_PROXY */
-
-static void proxy_complete_pdu(struct bt_mesh_proxy_client *client)
-{
-	switch (client->msg_type) {
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-	case BT_MESH_PROXY_NET_PDU:
-		BT_INFO("Mesh Network PDU");
-		bt_mesh_net_recv(client->buf, 0, BT_MESH_NET_IF_PROXY);
-		break;
-	case BT_MESH_PROXY_BEACON:
-		BT_INFO("Mesh Beacon PDU");
-		bt_mesh_beacon_recv(client->buf);
-		break;
-	case BT_MESH_PROXY_CONFIG:
-		BT_INFO("Mesh Configuration PDU");
-		proxy_cfg(client);
-		break;
-#endif
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-	case BT_MESH_PROXY_PROV:
-		BT_INFO("Mesh Provisioning PDU");
-		bt_mesh_pb_gatt_recv(client->conn_handle, client->buf);
-		break;
-#endif
-	default:
-		BT_WARN("Unhandled Message Type 0x%02x", client->msg_type);
-		break;
-	}
-
-	net_buf_simple_init(client->buf, 0);
-}
-
-static int proxy_recv(uint16_t conn_handle, uint16_t attr_handle,
-		      struct ble_gatt_access_ctxt *ctxt, void *arg)
-{
-	struct bt_mesh_proxy_client *client = find_client(conn_handle);
-	const uint8_t *data = ctxt->om->om_data;
-	uint16_t len = ctxt->om->om_len;
-
-	client = find_client(conn_handle);
-
-	if (!client) {
-		return -ENOTCONN;
-	}
-
-	if (len < 1) {
-		BT_WARN("Too small Proxy PDU");
-		return -EINVAL;
-	}
-
-	if ((attr_handle == svc_handles.prov_data_in_h) !=
-	    (PDU_TYPE(data) == BT_MESH_PROXY_PROV)) {
-		BT_WARN("Proxy PDU type doesn't match GATT service");
-		return -EINVAL;
-	}
-
-	if (len - 1 > net_buf_simple_tailroom(client->buf)) {
-		BT_WARN("Too big proxy PDU");
-		return -EINVAL;
-	}
-
-	switch (PDU_SAR(data)) {
-	case SAR_COMPLETE:
-		if (client->buf->om_len) {
-			BT_WARN("Complete PDU while a pending incomplete one");
-			return -EINVAL;
-		}
-
-		client->msg_type = PDU_TYPE(data);
-		net_buf_simple_add_mem(client->buf, data + 1, len - 1);
-		proxy_complete_pdu(client);
-		break;
-
-	case SAR_FIRST:
-		if (client->buf->om_len) {
-			BT_WARN("First PDU while a pending incomplete one");
-			return -EINVAL;
-		}
-
-		k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT);
-		client->msg_type = PDU_TYPE(data);
-		net_buf_simple_add_mem(client->buf, data + 1, len - 1);
-		break;
-
-	case SAR_CONT:
-		if (!client->buf->om_len) {
-			BT_WARN("Continuation with no prior data");
-			return -EINVAL;
-		}
-
-		if (client->msg_type != PDU_TYPE(data)) {
-			BT_WARN("Unexpected message type in continuation");
-			return -EINVAL;
-		}
-
-		k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT);
-		net_buf_simple_add_mem(client->buf, data + 1, len - 1);
-		break;
-
-	case SAR_LAST:
-		if (!client->buf->om_len) {
-			BT_WARN("Last SAR PDU with no prior data");
-			return -EINVAL;
-		}
-
-		if (client->msg_type != PDU_TYPE(data)) {
-			BT_WARN("Unexpected message type in last SAR PDU");
-			return -EINVAL;
-		}
-
-		k_delayed_work_cancel(&client->sar_timer);
-		net_buf_simple_add_mem(client->buf, data + 1, len - 1);
-		proxy_complete_pdu(client);
-		break;
-	}
-
-	return len;
-}
-
-static int conn_count;
-
-static void proxy_connected(uint16_t conn_handle)
-{
-	struct bt_mesh_proxy_client *client;
-	int i;
-
-	BT_INFO("conn_handle %d", conn_handle);
-
-	conn_count++;
-
-	/* Since we use ADV_OPT_ONE_TIME */
-	proxy_adv_enabled = false;
-
-	/* Try to re-enable advertising in case it's possible */
-	if (conn_count < CONFIG_BT_MAX_CONN) {
-		bt_mesh_adv_update();
-	}
-
-	for (client = NULL, i = 0; i < ARRAY_SIZE(clients); i++) {
-		if (clients[i].conn_handle == BLE_HS_CONN_HANDLE_NONE) {
-			client = &clients[i];
-			break;
-		}
-	}
-
-	if (!client) {
-		BT_ERR("No free Proxy Client objects");
-		return;
-	}
-
-	client->conn_handle = conn_handle;
-	client->filter_type = NONE;
-	memset(client->filter, 0, sizeof(client->filter));
-	net_buf_simple_init(client->buf, 0);
-}
-
-static void proxy_disconnected(uint16_t conn_handle, int reason)
-{
-	int i;
-
-	BT_DBG("conn handle %u reason 0x%02x", conn_handle, reason);	
-	conn_count--;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		struct bt_mesh_proxy_client *client = &clients[i];
-
-		if (client->conn_handle == conn_handle) {
-			if ((MYNEWT_VAL(BLE_MESH_PB_GATT)) &&
-			    client->filter_type == PROV) {
-				bt_mesh_pb_gatt_close(conn_handle);
-			}
-
-			k_delayed_work_cancel(&client->sar_timer);
-			client->conn_handle = BLE_HS_CONN_HANDLE_NONE;
-			break;
-		}
-	}
-
-	bt_mesh_adv_update();
-}
-
-struct os_mbuf *bt_mesh_proxy_get_buf(void)
-{
-	struct os_mbuf *buf = clients[0].buf;
-
-	if (buf != NULL) {
-		net_buf_simple_init(buf, 0);
-	}
-
-	return buf;
-}
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-static void prov_ccc_write(uint16_t conn_handle)
-{
-	struct bt_mesh_proxy_client *client;
-
-	BT_DBG("conn_handle %d", conn_handle);
-
-	/* If a connection exists there must be a client */
-	client = find_client(conn_handle);
-	__ASSERT(client, "No client for connection");
-
-	if (client->filter_type == NONE) {
-		client->filter_type = PROV;
-		bt_mesh_pb_gatt_open(conn_handle);
-	}
-}
-
-int bt_mesh_proxy_prov_enable(void)
-{
-	uint16_t handle;
-	int rc;
-	int i;
-
-	BT_DBG("");
-
-	if (gatt_svc == MESH_GATT_PROV) {
-		return -EALREADY;
-	}
-
-	if (gatt_svc != MESH_GATT_NONE) {
-		return -EBUSY;
-	}
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
-	assert(rc == 0);
-	ble_gatts_svc_set_visibility(handle, 1);
-	/* FIXME: figure out end handle */
-	ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
-
-	gatt_svc = MESH_GATT_PROV;
-	prov_fast_adv = true;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
-			clients[i].filter_type = PROV;
-		}
-	}
-
-
-	return 0;
-}
-
-int bt_mesh_proxy_prov_disable(bool disconnect)
-{
-	uint16_t handle;
-	int rc;
-	int i;
-
-	BT_DBG("");
-
-	if (gatt_svc == MESH_GATT_NONE) {
-		return -EALREADY;
-	}
-
-	if (gatt_svc != MESH_GATT_PROV) {
-		return -EBUSY;
-	}
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
-	assert(rc == 0);
-	ble_gatts_svc_set_visibility(handle, 0);
-	/* FIXME: figure out end handle */
-	ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
-
-	gatt_svc = MESH_GATT_NONE;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		struct bt_mesh_proxy_client *client = &clients[i];
-
-		if ((client->conn_handle == BLE_HS_CONN_HANDLE_NONE)
-		    || (client->filter_type != PROV)) {
-			continue;
-		}
-
-		if (disconnect) {
-			rc = ble_gap_terminate(client->conn_handle,
-					       BLE_ERR_REM_USER_CONN_TERM);
-			assert(rc == 0);
-		} else {
-			bt_mesh_pb_gatt_close(client->conn_handle);
-			client->filter_type = NONE;
-		}
-	}
-
-	bt_mesh_adv_update();
-
-	return 0;
-}
-#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-static void proxy_ccc_write(uint16_t conn_handle)
-{
-	struct bt_mesh_proxy_client *client;
-
-	BT_DBG("conn_handle %d", conn_handle);
-
-	client = find_client(conn_handle);
-	__ASSERT(client, "No client for connection");
-
-	if (client->filter_type == NONE) {
-		client->filter_type = WHITELIST;
-		k_work_add_arg(&client->send_beacons, client);
-		k_work_submit(&client->send_beacons);
-	}
-}
-
-int bt_mesh_proxy_gatt_enable(void)
-{
-	uint16_t handle;
-	int rc;
-	int i;
-
-	BT_DBG("");
-
-	if (gatt_svc == MESH_GATT_PROXY) {
-		return -EALREADY;
-	}
-
-	if (gatt_svc != MESH_GATT_NONE) {
-		return -EBUSY;
-	}
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
-	assert(rc == 0);
-	ble_gatts_svc_set_visibility(handle, 1);
-	/* FIXME: figure out end handle */
-	ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
-
-	gatt_svc = MESH_GATT_PROXY;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
-			clients[i].filter_type = WHITELIST;
-		}
-	}
-
-	return 0;
-}
-
-void bt_mesh_proxy_gatt_disconnect(void)
-{
-	int rc;
-	int i;
-
-	BT_DBG("");
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		struct bt_mesh_proxy_client *client = &clients[i];
-
-		if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE) &&
-		    (client->filter_type == WHITELIST ||
-		     client->filter_type == BLACKLIST)) {
-			client->filter_type = NONE;
-			rc = ble_gap_terminate(client->conn_handle,
-			                       BLE_ERR_REM_USER_CONN_TERM);
-			assert(rc == 0);
-		}
-	}
-}
-
-int bt_mesh_proxy_gatt_disable(void)
-{
-	uint16_t handle;
-	int rc;
-
-	BT_DBG("");
-
-	if (gatt_svc == MESH_GATT_NONE) {
-		return -EALREADY;
-	}
-
-	if (gatt_svc != MESH_GATT_PROXY) {
-		return -EBUSY;
-	}
-
-	bt_mesh_proxy_gatt_disconnect();
-
-	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
-	assert(rc == 0);
-	ble_gatts_svc_set_visibility(handle, 0);
-	/* FIXME: figure out end handle */
-	ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
-
-	gatt_svc = MESH_GATT_NONE;
-
-	return 0;
-}
-
-void bt_mesh_proxy_addr_add(struct os_mbuf *buf, uint16_t addr)
-{
-	struct bt_mesh_proxy_client *client = NULL;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		client = &clients[i];
-		if (client->buf == buf) {
-			break;
-		}
-	}
-
-	assert(client);
-
-	BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
-
-	if (client->filter_type == WHITELIST) {
-		filter_add(client, addr);
-	} else if (client->filter_type == BLACKLIST) {
-		filter_remove(client, addr);
-	}
-}
-
-static bool client_filter_match(struct bt_mesh_proxy_client *client,
-				uint16_t addr)
-{
-	int i;
-
-	BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
-
-	if (client->filter_type == BLACKLIST) {
-		for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
-			if (client->filter[i] == addr) {
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	if (addr == BT_MESH_ADDR_ALL_NODES) {
-		return true;
-	}
-
-	if (client->filter_type == WHITELIST) {
-		for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
-			if (client->filter[i] == addr) {
-				return true;
-			}
-		}
-	}
-
-	return false;
-}
-
-bool bt_mesh_proxy_relay(struct os_mbuf *buf, uint16_t dst)
-{
-	bool relayed = false;
-	int i;
-
-	BT_DBG("%u bytes to dst 0x%04x", buf->om_len, dst);
-
-	for (i = 0; i < ARRAY_SIZE(clients); i++) {
-		struct bt_mesh_proxy_client *client = &clients[i];
-		struct os_mbuf *msg;
-
-		if (client->conn_handle == BLE_HS_CONN_HANDLE_NONE) {
-			continue;
-		}
-
-		if (!client_filter_match(client, dst)) {
-			continue;
-		}
-
-		/* Proxy PDU sending modifies the original buffer,
-		 * so we need to make a copy.
-		 */
-		msg = NET_BUF_SIMPLE(32);
-		net_buf_simple_init(msg, 1);
-		net_buf_simple_add_mem(msg, buf->om_data, buf->om_len);
-
-		bt_mesh_proxy_send(client->conn_handle, BT_MESH_PROXY_NET_PDU, msg);
-		os_mbuf_free_chain(msg);
-		relayed = true;
-	}
-
-	return relayed;
-}
-
-#endif /* MYNEWT_VAL(BLE_MESH_GATT_PROXY) */
-
-static void notify_complete(void)
-{
-	sys_snode_t *n;
-
-	if (atomic_dec(&pending_notifications) > 1) {
-		return;
-	}
-
-	BT_DBG("");
-
-	while ((n = sys_slist_get(&idle_waiters))) {
-		CONTAINER_OF(n, struct bt_mesh_proxy_idle_cb, n)->cb();
-	}
-}
-
-static int proxy_send(uint16_t conn_handle, const void *data, uint16_t len)
-{
-	struct os_mbuf *om;
-	int err = 0;
-
-	BT_DBG("%u bytes: %s", len, bt_hex(data, len));
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-	if (gatt_svc == MESH_GATT_PROXY) {
-		om = ble_hs_mbuf_from_flat(data, len);
-		assert(om);
-		err = ble_gattc_notify_custom(conn_handle, svc_handles.proxy_data_out_h, om);
-		notify_complete();
-	}
-#endif
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-	if (gatt_svc == MESH_GATT_PROV) {
-		om = ble_hs_mbuf_from_flat(data, len);
-		assert(om);
-		err = ble_gattc_notify_custom(conn_handle, svc_handles.prov_data_out_h, om);
-		notify_complete();
-	}
-#endif
-
-	if (!err) {
-			atomic_inc(&pending_notifications);
-	}
-
-	return err;
-}
-
-static int proxy_segment_and_send(uint16_t conn_handle, uint8_t type,
-				  struct os_mbuf *msg)
-{
-	uint16_t mtu;
-
-	BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len,
-	       bt_hex(msg->om_data, msg->om_len));
-
-	/* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
-	mtu = ble_att_mtu(conn_handle) - 3;
-	if (mtu > msg->om_len) {
-		net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
-		return proxy_send(conn_handle, msg->om_data, msg->om_len);
-	}
-
-	net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
-	proxy_send(conn_handle, msg->om_data, mtu);
-	net_buf_simple_pull_mem(msg, mtu);
-
-	while (msg->om_len) {
-		if (msg->om_len + 1 < mtu) {
-			net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
-			proxy_send(conn_handle, msg->om_data, msg->om_len);
-			break;
-		}
-
-		net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
-		proxy_send(conn_handle, msg->om_data, mtu);
-		net_buf_simple_pull_mem(msg, mtu);
-	}
-
-	return 0;
-}
-
-int bt_mesh_proxy_send(uint16_t conn_handle, uint8_t type,
-		       struct os_mbuf *msg)
-{
-	struct bt_mesh_proxy_client *client = find_client(conn_handle);
-
-	if (!client) {
-		BT_ERR("No Proxy Client found");
-		return -ENOTCONN;
-	}
-
-	if ((client->filter_type == PROV) != (type == BT_MESH_PROXY_PROV)) {
-		BT_ERR("Invalid PDU type for Proxy Client");
-		return -EINVAL;
-	}
-
-	return proxy_segment_and_send(conn_handle, type, msg);
-}
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-static uint8_t prov_svc_data[20] = {
-	BT_UUID_16_ENCODE(BT_UUID_MESH_PROV_VAL),
-};
-
-static const struct bt_data prov_ad[] = {
-	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
-	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
-		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROV_VAL)),
-	BT_DATA(BT_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)),
-};
-#endif /* PB_GATT */
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-
-#define ID_TYPE_NET  0x00
-#define ID_TYPE_NODE 0x01
-
-#define NODE_ID_LEN  19
-#define NET_ID_LEN   11
-
-#define NODE_ID_TIMEOUT K_SECONDS(CONFIG_BT_MESH_NODE_ID_TIMEOUT)
-
-static uint8_t proxy_svc_data[NODE_ID_LEN] = {
-	BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL),
-};
-
-static const struct bt_data node_id_ad[] = {
-	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
-	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
-		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
-	BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN),
-};
-
-static const struct bt_data net_id_ad[] = {
-	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
-	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
-		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
-	BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN),
-};
-
-static int node_id_adv(struct bt_mesh_subnet *sub)
-{
-	uint8_t tmp[16];
-	int err;
-
-	BT_DBG("");
-
-	proxy_svc_data[2] = ID_TYPE_NODE;
-
-	err = bt_rand(proxy_svc_data + 11, 8);
-	if (err) {
-		return err;
-	}
-
-	memset(tmp, 0, 6);
-	memcpy(tmp + 6, proxy_svc_data + 11, 8);
-	sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
-
-	err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
-			    tmp);
-	if (err) {
-		return err;
-	}
-
-	memcpy(proxy_svc_data + 3, tmp + 8, 8);
-
-	err = bt_le_adv_start(&fast_adv_param, node_id_ad,
-			      ARRAY_SIZE(node_id_ad), NULL, 0);
-	if (err) {
-		BT_WARN("Failed to advertise using Node ID (err %d)", err);
-		return err;
-	}
-
-	proxy_adv_enabled = true;
-
-	return 0;
-}
-
-static int net_id_adv(struct bt_mesh_subnet *sub)
-{
-	int err;
-
-	BT_DBG("");
-
-	proxy_svc_data[2] = ID_TYPE_NET;
-
-	BT_DBG("Advertising with NetId %s",
-	       bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
-
-	memcpy(proxy_svc_data + 3, sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
-
-	err = bt_le_adv_start(&slow_adv_param, net_id_ad,
-			      ARRAY_SIZE(net_id_ad), NULL, 0);
-	if (err) {
-		BT_WARN("Failed to advertise using Network ID (err %d)", err);
-		return err;
-	}
-
-	proxy_adv_enabled = true;
-
-	return 0;
-}
-
-static bool advertise_subnet(struct bt_mesh_subnet *sub)
-{
-	if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-		return false;
-	}
-
-	return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
-		bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
-}
-
-static struct bt_mesh_subnet *next_sub(void)
-{
-	struct bt_mesh_subnet *sub = NULL;
-
-	if (!beacon_sub) {
-		beacon_sub = bt_mesh_subnet_next(NULL);
-		if (!beacon_sub) {
-			/* No valid subnets */
-			return NULL;
-		}
-	}
-
-	sub = beacon_sub;
-	do {
-		if (advertise_subnet(sub)) {
-			beacon_sub = sub;
-			return sub;
-		}
-
-	sub = bt_mesh_subnet_next(sub);
-	} while (sub != beacon_sub);
-
-	/* No subnets to advertise on */
-
-	return NULL;
-}
-
-static int sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
-{
-	int *count = cb_data;
-
-	if (advertise_subnet(sub)) {
-		(*count)++;
-	}
-
-	return 0;
-}
-
-static int sub_count(void)
-{
-	int count = 0;
-
-	(void)bt_mesh_subnet_find(sub_count_cb, &count);
-	return count;
-}
-
-static int32_t gatt_proxy_advertise(struct bt_mesh_subnet *sub)
-{
-	int32_t remaining = K_FOREVER;
-	int subnet_count;
-
-	BT_DBG("");
-
-	if (conn_count == CONFIG_BT_MAX_CONN) {
-		BT_DBG("Connectable advertising deferred (max connections %d)", conn_count);
-		return -ENOMEM;
-	}
-
-	sub = beacon_sub ? beacon_sub : bt_mesh_subnet_next(beacon_sub);
-	if (!sub) {
-		BT_WARN("No subnets to advertise on");
-		return -ENOENT;
-	}
-
-	if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
-		uint32_t active = k_uptime_get_32() - sub->node_id_start;
-
-		if (active < NODE_ID_TIMEOUT) {
-			remaining = NODE_ID_TIMEOUT - active;
-			BT_DBG("Node ID active for %u ms, %d ms remaining",
-			       (unsigned) active, (int) remaining);
-			node_id_adv(sub);
-		} else {
-			bt_mesh_proxy_identity_stop(sub);
-			BT_DBG("Node ID stopped");
-		}
-	}
-
-	if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
-		net_id_adv(sub);
-	}
-
-	subnet_count = sub_count();
-	BT_DBG("sub_count %u", subnet_count);
-	if (subnet_count > 1) {
-		int32_t max_timeout;
-
-		/* We use NODE_ID_TIMEOUT as a starting point since it may
-		 * be less than 60 seconds. Divide this period into at least
-		 * 6 slices, but make sure that a slice is at least one
-		 * second long (to avoid excessive rotation).
-		 */
-		max_timeout = NODE_ID_TIMEOUT / max(subnet_count, 6);
-		max_timeout = max(max_timeout, K_SECONDS(1));
-
-		if (remaining > max_timeout || remaining < 0) {
-			remaining = max_timeout;
-		}
-	}
-
-	BT_DBG("Advertising %d ms for net_idx 0x%04x",
-	       (int) remaining, sub->net_idx);
-
-	beacon_sub = bt_mesh_subnet_next(beacon_sub);
-
-	return remaining;
-}
-#endif /* GATT_PROXY */
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-static size_t gatt_prov_adv_create(struct bt_data prov_sd[2])
-{
-	const struct bt_mesh_prov *prov = bt_mesh_prov_get();
-	const char *name = CONFIG_BT_DEVICE_NAME;
-	size_t name_len = strlen(name);
-	size_t prov_sd_len = 0;
-	size_t sd_space = 31;
-
-	memcpy(prov_svc_data + 2, prov->uuid, 16);
-	sys_put_be16(prov->oob_info, prov_svc_data + 18);
-
-	if (prov->uri) {
-		size_t uri_len = strlen(prov->uri);
-
-		if (uri_len > 29) {
-			/* There's no way to shorten an URI */
-			BT_WARN("Too long URI to fit advertising packet");
-		} else {
-			prov_sd[0].type = BT_DATA_URI;
-			prov_sd[0].data_len = uri_len;
-			prov_sd[0].data = (void *)prov->uri;
-			sd_space -= 2 + uri_len;
-			prov_sd_len++;
-		}
-	}
-
-	if (sd_space > 2 && name_len > 0) {
-		sd_space -= 2;
-
-		if (sd_space < name_len) {
-			prov_sd[prov_sd_len].type = BT_DATA_NAME_SHORTENED;
-			prov_sd[prov_sd_len].data_len = sd_space;
-		} else {
-			prov_sd[prov_sd_len].type = BT_DATA_NAME_COMPLETE;
-			prov_sd[prov_sd_len].data_len = name_len;
-		}
-
-		prov_sd[prov_sd_len].data = (void *)name;
-		prov_sd_len++;
-	}
-
-	return prov_sd_len;
-}
-#endif /* PB_GATT */
-
-int32_t bt_mesh_proxy_adv_start(void)
-{
-	BT_DBG("");
-
-	if (gatt_svc == MESH_GATT_NONE) {
-		return K_FOREVER;
-	}
-
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-	if (!bt_mesh_is_provisioned()) {
-		const struct ble_gap_adv_params *param;
-		struct bt_data prov_sd[2];
-		size_t prov_sd_len;
-
-		if (prov_fast_adv) {
-			param = &fast_adv_param;
-		} else {
-			param = &slow_adv_param;
-		}
-
-		prov_sd_len = gatt_prov_adv_create(prov_sd);
-
-		if (bt_le_adv_start(param, prov_ad, ARRAY_SIZE(prov_ad),
-				    prov_sd, prov_sd_len) == 0) {
-			proxy_adv_enabled = true;
-
-			/* Advertise 60 seconds using fast interval */
-			if (prov_fast_adv) {
-				prov_fast_adv = false;
-				return K_SECONDS(60);
-			}
-		}
-	}
-#endif /* PB_GATT */
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-	if (bt_mesh_is_provisioned()) {
-		return gatt_proxy_advertise(next_sub());
-	}
-#endif /* GATT_PROXY */
-
-	return K_FOREVER;
-}
-
-void bt_mesh_proxy_adv_stop(void)
-{
-	int err;
-
-	BT_DBG("adv_enabled %u", proxy_adv_enabled);
-
-	if (!proxy_adv_enabled) {
-		return;
-	}
-
-	err = bt_le_adv_stop(true);
-	if (err) {
-		BT_ERR("Failed to stop advertising (err %d)", err);
-	} else {
-		proxy_adv_enabled = false;
-	}
-}
-
-#if defined(CONFIG_BT_MESH_GATT_PROXY)
-static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
-{
-	if (evt == BT_MESH_KEY_DELETED) {
-		if (sub == beacon_sub) {
-			beacon_sub = NULL;
-		}
-	} else {
-		bt_mesh_proxy_beacon_send(sub);
-	}
-}
-#endif
-
-static void ble_mesh_handle_connect(struct ble_gap_event *event, void *arg)
-{
-#if MYNEWT_VAL(BLE_EXT_ADV)
-	/* When EXT ADV is enabled then mesh proxy is connected
-	 * when proxy advertising instance is completed.
-	 * Therefore no need to handle BLE_GAP_EVENT_CONNECT
-	 */
-	if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
-		/* Reason 0 means advertising has been completed because
-		 * connection has been established
-		 */
-		if (event->adv_complete.reason != 0) {
-			return;
-		}
-
-		if (event->adv_complete.instance != BT_MESH_ADV_GATT_INST) {
-			return;
-		}
-
-		proxy_connected(event->adv_complete.conn_handle);
-	}
-#else
-	if (event->type == BLE_GAP_EVENT_CONNECT) {
-		proxy_connected(event->connect.conn_handle);
-	}
-#endif
-}
-
-int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg)
-{
-	if ((event->type == BLE_GAP_EVENT_CONNECT) ||
-		(event->type == BLE_GAP_EVENT_ADV_COMPLETE)) {
-		ble_mesh_handle_connect(event, arg);
-	} else if (event->type == BLE_GAP_EVENT_DISCONNECT) {
-		proxy_disconnected(event->disconnect.conn.conn_handle,
-				   event->disconnect.reason);
-	} else if (event->type == BLE_GAP_EVENT_SUBSCRIBE) {
-		if (event->subscribe.attr_handle == svc_handles.proxy_data_out_h) {
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-			proxy_ccc_write(event->subscribe.conn_handle);
-#endif
-		} else if (event->subscribe.attr_handle ==
-			   svc_handles.prov_data_out_h) {
-#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
-			prov_ccc_write(event->subscribe.conn_handle);
-#endif
-		}
-	}
-
-	return 0;
-}
-
-static int
-dummy_access_cb(uint16_t conn_handle, uint16_t attr_handle,
-		struct ble_gatt_access_ctxt *ctxt, void *arg)
-{
-	/*
-	 * We should never never enter this callback - it's attached to notify-only
-	 * characteristic which are notified directly from mbuf. And we can't pass
-	 * NULL as access_cb because gatts will assert on init...
-	 */
-	BLE_HS_DBG_ASSERT(0);
-	return 0;
-}
-
-static const struct ble_gatt_svc_def svc_defs [] = {
-	{
-		.type = BLE_GATT_SVC_TYPE_PRIMARY,
-		.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
-		.characteristics = (struct ble_gatt_chr_def[]) { {
-				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_IN_VAL),
-				.access_cb = proxy_recv,
-				.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
-			}, {
-				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
-				.access_cb = dummy_access_cb,
-				.flags = BLE_GATT_CHR_F_NOTIFY,
-			}, {
-				0, /* No more characteristics in this service. */
-			} },
-	}, {
-		.type = BLE_GATT_SVC_TYPE_PRIMARY,
-		.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
-		.characteristics = (struct ble_gatt_chr_def[]) { {
-				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
-				.access_cb = proxy_recv,
-				.flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
-			}, {
-				.uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
-				.access_cb = dummy_access_cb,
-				.flags = BLE_GATT_CHR_F_NOTIFY,
-			}, {
-				0, /* No more characteristics in this service. */
-			} },
-	}, {
-		0, /* No more services. */
-	},
-};
-
-int bt_mesh_proxy_svcs_register(void)
-{
-	int rc;
-
-	rc = ble_gatts_count_cfg(svc_defs);
-	assert(rc == 0);
-
-	rc = ble_gatts_add_svcs(svc_defs);
-	assert(rc == 0);
-
-	return 0;
-}
-
-int bt_mesh_proxy_init(void)
-{
-	int i;
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-		if (!bt_mesh_subnet_cb_list[4]) {
-		bt_mesh_subnet_cb_list[4] = subnet_evt;
-	}
-#endif
-
-	for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) {
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-		k_work_init(&clients[i].send_beacons, proxy_send_beacons);
-#endif
-		clients[i].buf = NET_BUF_SIMPLE(CLIENT_BUF_SIZE);
-		clients[i].conn_handle = BLE_HS_CONN_HANDLE_NONE;
-
-		k_delayed_work_init(&clients[i].sar_timer, proxy_sar_timeout);
-		k_delayed_work_add_arg(&clients[i].sar_timer, &clients[i]);
-	}
-
-	resolve_svc_handles();
-
-	ble_gatts_svc_set_visibility(svc_handles.proxy_h, 0);
-	ble_gatts_svc_set_visibility(svc_handles.prov_h, 0);
-
-	return 0;
-}
-
-void bt_mesh_proxy_on_idle(struct bt_mesh_proxy_idle_cb *cb)
-{
-	if (!atomic_get(&pending_notifications)) {
-		cb->cb();
-		return;
-	}
-
-	sys_slist_append(&idle_waiters, &cb->n);
-}
-
-#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */
diff --git a/nimble/host/mesh/src/proxy.h b/nimble/host/mesh/src/proxy.h
index ebade45ab6..42530293e9 100644
--- a/nimble/host/mesh/src/proxy.h
+++ b/nimble/host/mesh/src/proxy.h
@@ -9,34 +9,35 @@
 #ifndef __PROXY_H__
 #define __PROXY_H__
 
-#define BT_MESH_PROXY_NET_PDU   0x00
-#define BT_MESH_PROXY_BEACON    0x01
-#define BT_MESH_PROXY_CONFIG    0x02
-#define BT_MESH_PROXY_PROV      0x03
-
-#include "mesh/mesh.h"
 #include "mesh/slist.h"
 
+#if CONFIG_BT_MESH_DEBUG_USE_ID_ADDR
+#define ADV_OPT_USE_IDENTITY BT_LE_ADV_OPT_USE_IDENTITY
+#else
+#define ADV_OPT_USE_IDENTITY 0
+#endif
+
+#define ADV_SLOW_INT                                                           \
+.itvl_min = BT_GAP_ADV_SLOW_INT_MIN,                             \
+.itvl_max = BT_GAP_ADV_SLOW_INT_MAX,
+
+#define ADV_FAST_INT                                                           \
+.itvl_min = BT_GAP_ADV_FAST_INT_MIN_2,                             \
+.itvl_max = BT_GAP_ADV_FAST_INT_MAX_2,
+
 struct bt_mesh_proxy_idle_cb {
 	sys_snode_t n;
 	void (*cb)(void);
 };
 
-int bt_mesh_proxy_send(uint16_t conn_handle, uint8_t type, struct os_mbuf *msg);
-
-int bt_mesh_proxy_prov_enable(void);
-int bt_mesh_proxy_prov_disable(bool disconnect);
-
+void notify_complete(void);
 int bt_mesh_proxy_gatt_enable(void);
 int bt_mesh_proxy_gatt_disable(void);
 void bt_mesh_proxy_gatt_disconnect(void);
 
 void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub);
 
-struct os_mbuf *bt_mesh_proxy_get_buf(void);
-
-int32_t bt_mesh_proxy_adv_start(void);
-void bt_mesh_proxy_adv_stop(void);
+int bt_mesh_proxy_adv_start(void);
 
 void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub);
 void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub);
@@ -44,9 +45,7 @@ void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub);
 bool bt_mesh_proxy_relay(struct os_mbuf *buf, uint16_t dst);
 void bt_mesh_proxy_addr_add(struct os_mbuf *buf, uint16_t addr);
 
-int bt_mesh_proxy_init(void);
-void bt_mesh_proxy_on_idle(struct bt_mesh_proxy_idle_cb *cb);
-
 int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg);
+int bt_mesh_proxy_init(void);
 
-#endif
+#endif /* __PROXY_H__ */
diff --git a/nimble/host/mesh/src/proxy_msg.c b/nimble/host/mesh/src/proxy_msg.c
new file mode 100644
index 0000000000..44613af16d
--- /dev/null
+++ b/nimble/host/mesh/src/proxy_msg.c
@@ -0,0 +1,235 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2021 Lingao Meng
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG
+
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+
+#include "mesh/mesh.h"
+#include "host/ble_att.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "../../host/src/ble_hs_priv.h"
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "rpl.h"
+#include "prov.h"
+#include "beacon.h"
+#include "foundation.h"
+#include "access.h"
+#include "proxy.h"
+#include "proxy_msg.h"
+
+#define PDU_SAR(data)      (data[0] >> 6)
+
+#define BT_UUID_16_ENCODE(w16)  \
+	(((w16) >>  0) & 0xFF), \
+	(((w16) >>  8) & 0xFF)
+/* Mesh Profile 1.0 Section 6.6:
+ * "The timeout for the SAR transfer is 20 seconds. When the timeout
+ *  expires, the Proxy Server shall disconnect."
+ */
+#define PROXY_SAR_TIMEOUT  K_SECONDS(20)
+
+#define SAR_COMPLETE       0x00
+#define SAR_FIRST          0x01
+#define SAR_CONT           0x02
+#define SAR_LAST           0x03
+
+#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
+
+static uint8_t bufs[CONFIG_BT_MAX_CONN * CONFIG_BT_MESH_PROXY_MSG_LEN];
+
+static struct bt_mesh_proxy_role roles[CONFIG_BT_MAX_CONN];
+
+static void proxy_sar_timeout(struct ble_npl_event *work)
+{
+	struct bt_mesh_proxy_role *role;
+	int rc;
+	role = ble_npl_event_get_arg(work);
+
+
+	BT_WARN("Proxy SAR timeout");
+
+	if (role->conn_handle) {
+		rc = ble_gap_terminate(role->conn_handle,
+				       BLE_ERR_REM_USER_CONN_TERM);
+		assert(rc == 0);
+	}
+}
+
+int bt_mesh_proxy_msg_recv(struct bt_mesh_proxy_role *role,
+			       const void *buf, uint16_t len)
+{
+	const uint8_t *data = buf;
+
+	switch (PDU_SAR(data)) {
+	case SAR_COMPLETE:
+		if (role->buf->om_len) {
+			BT_WARN("Complete PDU while a pending incomplete one");
+			return -EINVAL;
+		}
+
+		role->msg_type = PDU_TYPE(data);
+		net_buf_simple_add_mem(role->buf, data + 1, len - 1);
+		role->cb.recv(role);
+		net_buf_simple_reset(role->buf);
+		break;
+
+	case SAR_FIRST:
+		if (role->buf->om_len) {
+			BT_WARN("First PDU while a pending incomplete one");
+			return -EINVAL;
+		}
+
+		k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT);
+		role->msg_type = PDU_TYPE(data);
+		net_buf_simple_add_mem(role->buf, data + 1, len - 1);
+		break;
+
+	case SAR_CONT:
+		if (!role->buf->om_len) {
+			BT_WARN("Continuation with no prior data");
+			return -EINVAL;
+		}
+
+		if (role->msg_type != PDU_TYPE(data)) {
+			BT_WARN("Unexpected message type in continuation");
+			return -EINVAL;
+		}
+
+		k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT);
+		net_buf_simple_add_mem(role->buf, data + 1, len - 1);
+		break;
+
+	case SAR_LAST:
+		if (!role->buf->om_len) {
+			BT_WARN("Last SAR PDU with no prior data");
+			return -EINVAL;
+		}
+
+		if (role->msg_type != PDU_TYPE(data)) {
+			BT_WARN("Unexpected message type in last SAR PDU");
+			return -EINVAL;
+		}
+
+		/* If this fails, the work handler exits early, as there's no
+		 * active SAR buffer.
+		 */
+		(void)k_work_cancel_delayable(&role->sar_timer);
+		net_buf_simple_add_mem(role->buf, data + 1, len - 1);
+		role->cb.recv(role);
+		net_buf_simple_reset(role->buf);
+		break;
+	}
+
+	return len;
+}
+
+int bt_mesh_proxy_msg_send(struct bt_mesh_proxy_role *role, uint8_t type,
+			   struct os_mbuf *msg)
+{
+	int err;
+	uint16_t mtu;
+	uint16_t conn_handle = role->conn_handle;
+
+	BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len,
+	       bt_hex(msg->om_data, msg->om_len));
+
+	/* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
+	mtu = ble_att_mtu(conn_handle) - 3;
+	if (mtu > msg->om_len) {
+		net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
+		return role->cb.send(conn_handle, msg->om_data, msg->om_len);
+	}
+
+	net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
+	err = role->cb.send(conn_handle, msg->om_data, mtu);
+	if (err) {
+		return err;
+	}
+	net_buf_simple_pull_mem(msg, mtu);
+
+	while (msg->om_len) {
+		if (msg->om_len + 1 < mtu) {
+			net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
+			err = role->cb.send(conn_handle, msg->om_data, msg->om_len);
+			if (err) {
+				return err;
+			}
+			break;
+		}
+
+		net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
+		err = role->cb.send(conn_handle, msg->om_data, mtu);
+		if (err) {
+			return err;
+		}
+		net_buf_simple_pull_mem(msg, mtu);
+	}
+
+	return 0;
+}
+
+static void proxy_msg_init(struct bt_mesh_proxy_role *role)
+{
+
+	/* Check if buf has been allocated, in this way, we no longer need
+	 * to repeat the operation.
+	 */
+	if (role->buf != NULL) {
+		net_buf_simple_reset(role->buf);
+		return;
+	}
+
+	role->buf = NET_BUF_SIMPLE(CONFIG_BT_MESH_PROXY_MSG_LEN);
+	net_buf_simple_init_with_data(role->buf,
+				      &bufs[role->conn_handle *
+				      CONFIG_BT_MESH_PROXY_MSG_LEN],
+				      CONFIG_BT_MESH_PROXY_MSG_LEN);
+
+	net_buf_simple_reset(role->buf);
+
+	k_work_init_delayable(&role->sar_timer, proxy_sar_timeout);
+	k_work_add_arg_delayable(&role->sar_timer, role);
+}
+
+struct bt_mesh_proxy_role *bt_mesh_proxy_role_setup(uint16_t conn_handle,
+	proxy_send_cb_t send,
+	proxy_recv_cb_t recv)
+{
+	struct bt_mesh_proxy_role *role;
+
+	role = &roles[conn_handle];
+
+	role->conn_handle = conn_handle;
+	proxy_msg_init(role);
+
+	role->cb.recv = recv;
+	role->cb.send = send;
+
+	return role;
+}
+
+void bt_mesh_proxy_role_cleanup(struct bt_mesh_proxy_role *role)
+{
+
+	/* If this fails, the work handler exits early, as
+	 * there's no active connection.
+	 */
+	(void)k_work_cancel_delayable(&role->sar_timer);
+
+	role->conn_handle = BLE_HS_CONN_HANDLE_NONE;
+
+	bt_mesh_adv_update();
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */
diff --git a/nimble/host/mesh/src/proxy_msg.h b/nimble/host/mesh/src/proxy_msg.h
new file mode 100644
index 0000000000..349ebcdf4a
--- /dev/null
+++ b/nimble/host/mesh/src/proxy_msg.h
@@ -0,0 +1,66 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2021 Lingao Meng
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_SUBSYS_BLUETOOTH_MESH_PROXY_MSG_H_
+#define ZEPHYR_SUBSYS_BLUETOOTH_MESH_PROXY_MSG_H_
+
+#define PDU_TYPE(data)     (data[0] & BIT_MASK(6))
+#define CFG_FILTER_SET     0x00
+#define CFG_FILTER_ADD     0x01
+#define CFG_FILTER_REMOVE  0x02
+#define CFG_FILTER_STATUS  0x03
+
+#define BT_MESH_PROXY_NET_PDU   0x00
+#define BT_MESH_PROXY_BEACON    0x01
+#define BT_MESH_PROXY_CONFIG    0x02
+#define BT_MESH_PROXY_PROV      0x03
+
+#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
+
+struct bt_mesh_proxy_role;
+
+typedef int (*proxy_send_cb_t)(uint16_t conn_handle,
+	const void *data, uint16_t len);
+
+typedef void (*proxy_recv_cb_t)(struct bt_mesh_proxy_role *role);
+
+struct bt_mesh_proxy_role {
+	uint16_t conn_handle;
+	uint8_t msg_type;
+
+	struct {
+		proxy_send_cb_t send;
+		proxy_recv_cb_t recv;
+	} cb;
+
+	struct k_work_delayable sar_timer;
+	struct os_mbuf *buf;
+};
+
+struct bt_mesh_proxy_client {
+	struct bt_mesh_proxy_role *cli;
+	uint16_t filter[MYNEWT_VAL(BLE_MESH_PROXY_FILTER_SIZE)];
+	enum __packed {
+		NONE,
+		ACCEPT,
+		REJECT,
+		} filter_type;
+	struct ble_npl_callout send_beacons;
+};
+
+int bt_mesh_proxy_msg_recv(struct bt_mesh_proxy_role *role,
+	const void *buf, uint16_t len);
+int bt_mesh_proxy_msg_send(struct bt_mesh_proxy_role *role, uint8_t type, struct os_mbuf *msg);
+void bt_mesh_proxy_msg_init(struct bt_mesh_proxy_role *role);
+void bt_mesh_proxy_role_cleanup(struct bt_mesh_proxy_role *role);
+struct bt_mesh_proxy_role *bt_mesh_proxy_role_setup(uint16_t conn_handle,
+						    proxy_send_cb_t send,
+						    proxy_recv_cb_t recv);
+struct bt_mesh_proxy_client *find_client(uint16_t conn_handle);
+#endif /* ZEPHYR_SUBSYS_BLUETOOTH_MESH_PROXY_MSG_H_ */
diff --git a/nimble/host/mesh/src/proxy_srv.c b/nimble/host/mesh/src/proxy_srv.c
new file mode 100644
index 0000000000..dcbabfa296
--- /dev/null
+++ b/nimble/host/mesh/src/proxy_srv.c
@@ -0,0 +1,985 @@
+/*  Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2021 Lingao Meng
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG
+
+#include "mesh/slist.h"
+#include "mesh/mesh.h"
+#include "../../host/src/ble_hs_priv.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "rpl.h"
+#include "transport.h"
+#include "prov.h"
+#include "beacon.h"
+#include "foundation.h"
+#include "access.h"
+#include "proxy.h"
+#include "proxy_msg.h"
+#include "pb_gatt_srv.h"
+
+#if defined(CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME)
+#define ADV_OPT_USE_NAME BT_LE_ADV_OPT_USE_NAME
+#else
+#define ADV_OPT_USE_NAME 0
+#endif
+
+#define ADV_OPT_PROXY                                                           \
+.conn_mode = (BLE_GAP_CONN_MODE_UND),                                  \
+.disc_mode = (BLE_GAP_DISC_MODE_GEN),
+
+
+#define BT_UUID_MESH_PROXY_VAL            0x1828
+#define CLIENT_BUF_SIZE 66
+
+#define BT_UUID_16_ENCODE(w16)  \
+	(((w16) >>  0) & 0xFF), \
+	(((w16) >>  8) & 0xFF)
+
+static sys_slist_t idle_waiters;
+static atomic_t pending_notifications;
+
+static void proxy_send_beacons(struct ble_npl_event *work);
+
+static int proxy_send(uint16_t conn_handle,
+	const void *data, uint16_t len);
+
+
+static struct bt_mesh_proxy_client clients[CONFIG_BT_MAX_CONN];
+
+static bool service_registered;
+
+static int conn_count;
+
+struct bt_mesh_proxy_client *find_client(uint16_t conn_handle)
+{
+	return &clients[conn_handle];
+}
+
+/* Next subnet in queue to be advertised */
+static struct bt_mesh_subnet *beacon_sub;
+
+static int filter_set(struct bt_mesh_proxy_client *client,
+		      struct os_mbuf *buf)
+{
+	uint8_t type;
+
+	if (buf->om_len < 1) {
+		BT_WARN("Too short Filter Set message");
+		return -EINVAL;
+	}
+
+	type = net_buf_simple_pull_u8(buf);
+	BT_DBG("type 0x%02x", type);
+
+	switch (type) {
+		case 0x00:
+			(void)memset(client->filter, 0, sizeof(client->filter));
+			client->filter_type = ACCEPT;
+			break;
+		case 0x01:
+			(void)memset(client->filter, 0, sizeof(client->filter));
+			client->filter_type = REJECT;
+			break;
+		default:
+			BT_WARN("Prohibited Filter Type 0x%02x", type);
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void filter_add(struct bt_mesh_proxy_client *client, uint16_t addr)
+{
+	int i;
+
+	BT_DBG("addr 0x%04x", addr);
+
+	if (addr == BT_MESH_ADDR_UNASSIGNED) {
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+		if (client->filter[i] == addr) {
+			return;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+		if (client->filter[i] == BT_MESH_ADDR_UNASSIGNED) {
+			client->filter[i] = addr;
+			return;
+		}
+	}
+}
+
+static void filter_remove(struct bt_mesh_proxy_client *client, uint16_t addr)
+{
+	int i;
+
+	BT_DBG("addr 0x%04x", addr);
+
+	if (addr == BT_MESH_ADDR_UNASSIGNED) {
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+		if (client->filter[i] == addr) {
+			client->filter[i] = BT_MESH_ADDR_UNASSIGNED;
+			return;
+		}
+	}
+}
+
+static void send_filter_status(struct bt_mesh_proxy_client *client,
+			       struct bt_mesh_net_rx *rx,
+			       struct os_mbuf *buf)
+{
+	struct bt_mesh_net_tx tx = {
+		.sub = rx->sub,
+		.ctx = &rx->ctx,
+		.src = bt_mesh_primary_addr(),
+	};
+	uint16_t filter_size;
+	int i, err;
+
+	/* Configuration messages always have dst unassigned */
+	tx.ctx->addr = BT_MESH_ADDR_UNASSIGNED;
+
+	net_buf_simple_init(buf, 10);
+
+	net_buf_simple_add_u8(buf, CFG_FILTER_STATUS);
+
+	if (client->filter_type == ACCEPT) {
+		net_buf_simple_add_u8(buf, 0x00);
+	} else {
+		net_buf_simple_add_u8(buf, 0x01);
+	}
+
+	for (filter_size = 0U, i = 0; i < ARRAY_SIZE(client->filter); i++) {
+		if (client->filter[i] != BT_MESH_ADDR_UNASSIGNED) {
+			filter_size++;
+		}
+	}
+
+	net_buf_simple_add_be16(buf, filter_size);
+
+	BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+	err = bt_mesh_net_encode(&tx, buf, true);
+	if (err) {
+		BT_ERR("Encoding Proxy cfg message failed (err %d)", err);
+		return;
+	}
+
+	err = bt_mesh_proxy_msg_send(client->cli, BT_MESH_PROXY_CONFIG, buf);
+	if (err) {
+		BT_ERR("Failed to send proxy cfg message (err %d)", err);
+	}
+}
+
+static void proxy_filter_recv(uint16_t conn_handle,
+			      struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+	struct bt_mesh_proxy_client *client;
+	uint8_t opcode;
+
+	client = find_client(conn_handle);
+
+	opcode = net_buf_simple_pull_u8(buf);
+	switch (opcode) {
+	case CFG_FILTER_SET:
+		filter_set(client, buf);
+		send_filter_status(client, rx, buf);
+		break;
+		case CFG_FILTER_ADD:
+			while (buf->om_len >= 2) {
+				uint16_t addr;
+
+				addr = net_buf_simple_pull_be16(buf);
+				filter_add(client, addr);
+			}
+			send_filter_status(client, rx, buf);
+			break;
+		case CFG_FILTER_REMOVE:
+			while (buf->om_len >= 2) {
+				uint16_t addr;
+
+				addr = net_buf_simple_pull_be16(buf);
+				filter_remove(client, addr);
+			}
+			send_filter_status(client, rx, buf);
+			break;
+		default:
+			BT_WARN("Unhandled configuration OpCode 0x%02x", opcode);
+			break;
+	}
+}
+
+static void proxy_cfg(struct bt_mesh_proxy_role *role)
+{
+	struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_NET_MAX_PDU_LEN);
+	struct bt_mesh_net_rx rx;
+	int err;
+
+	err = bt_mesh_net_decode(role->buf, BT_MESH_NET_IF_PROXY_CFG,
+				 &rx, buf);
+	if (err) {
+		BT_ERR("Failed to decode Proxy Configuration (err %d)", err);
+		return;
+	}
+
+	rx.local_match = 1U;
+
+	if (bt_mesh_rpl_check(&rx, NULL)) {
+		BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
+			rx.ctx.addr, rx.ctx.recv_dst, rx.seq);
+		return;
+	}
+
+	/* Remove network headers */
+	net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+
+	BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+	if (buf->om_len < 1) {
+		BT_WARN("Too short proxy configuration PDU");
+		return;
+	}
+
+	proxy_filter_recv(role->conn_handle, &rx, buf);
+}
+
+static void proxy_msg_recv(struct bt_mesh_proxy_role *role)
+{
+	switch (role->msg_type) {
+	case BT_MESH_PROXY_NET_PDU:
+		BT_DBG("Mesh Network PDU");
+		bt_mesh_net_recv(role->buf, 0, BT_MESH_NET_IF_PROXY);
+		break;
+	case BT_MESH_PROXY_BEACON:
+		BT_DBG("Mesh Beacon PDU");
+		bt_mesh_beacon_recv(role->buf);
+		break;
+	case BT_MESH_PROXY_CONFIG:
+		BT_DBG("Mesh Configuration PDU");
+		proxy_cfg(role);
+		break;
+	default:
+		BT_WARN("Unhandled Message Type 0x%02x", role->msg_type);
+		break;
+	}
+}
+
+static int beacon_send(struct bt_mesh_proxy_client *client, struct bt_mesh_subnet *sub)
+{
+	struct os_mbuf *buf = NET_BUF_SIMPLE(23);
+	int rc;
+
+	net_buf_simple_init(buf, 1);
+	bt_mesh_beacon_create(sub, buf);
+
+	rc = bt_mesh_proxy_msg_send(client->cli, BT_MESH_PROXY_BEACON, buf);
+	os_mbuf_free_chain(buf);
+	return rc;
+}
+
+static int send_beacon_cb(struct bt_mesh_subnet *sub, void *cb_data)
+{
+	struct bt_mesh_proxy_client *client = cb_data;
+
+	return beacon_send(client, sub);
+}
+
+static void proxy_send_beacons(struct ble_npl_event *work)
+{
+	struct bt_mesh_proxy_client *client;
+
+	client = ble_npl_event_get_arg(work);
+
+	(void)bt_mesh_subnet_find(send_beacon_cb, client);
+}
+
+void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
+{
+	int i;
+
+	if (!sub) {
+		/* NULL means we send on all subnets */
+		bt_mesh_subnet_foreach(bt_mesh_proxy_beacon_send);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(clients); i++) {
+		if (clients[i].cli) {
+			beacon_send(&clients[i], sub);
+		}
+	}
+}
+
+static void node_id_start(struct bt_mesh_subnet *sub)
+{
+	sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
+	sub->node_id_start = k_uptime_get_32();
+}
+
+void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
+{
+	node_id_start(sub);
+
+	/* Prioritize the recently enabled subnet */
+	beacon_sub = sub;
+}
+
+void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
+	{
+	sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+	sub->node_id_start = 0U;
+}
+
+int bt_mesh_proxy_identity_enable(void)
+{
+	BT_DBG("");
+
+	if (!bt_mesh_is_provisioned()) {
+		return -EAGAIN;
+	}
+
+	if (bt_mesh_subnet_foreach(node_id_start)) {
+		bt_mesh_adv_update();
+	}
+
+	return 0;
+}
+
+#define ID_TYPE_NET  0x00
+#define ID_TYPE_NODE 0x01
+
+#define NODE_ID_LEN  19
+#define NET_ID_LEN   11
+
+#define NODE_ID_TIMEOUT (CONFIG_BT_MESH_NODE_ID_TIMEOUT * MSEC_PER_SEC)
+
+static uint8_t proxy_svc_data[NODE_ID_LEN] = {
+	BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL),
+};
+
+static const struct bt_data node_id_ad[] = {
+	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
+		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
+		      BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN),
+};
+
+static const struct bt_data net_id_ad[] = {
+	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
+		      BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)),
+		      BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN),
+};
+
+static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
+{
+	struct ble_gap_adv_params fast_adv_param = {
+		ADV_OPT_PROXY
+		ADV_FAST_INT
+	};
+#if ADV_OPT_USE_NAME
+	const char *name = CONFIG_BT_DEVICE_NAME;
+	size_t name_len = strlen(name);
+	struct bt_data sd = {
+		.type = BT_DATA_NAME_COMPLETE,
+		.data_len = name_len,
+		.data = (void *)name
+	};
+#else
+	struct bt_data *sd = NULL;
+#endif
+	uint8_t tmp[16];
+	int err;
+
+	BT_DBG("");
+
+	proxy_svc_data[2] = ID_TYPE_NODE;
+
+	err = bt_rand(proxy_svc_data + 11, 8);
+	if (err) {
+		return err;
+	}
+
+	(void)memset(tmp, 0, 6);
+	memcpy(tmp + 6, proxy_svc_data + 11, 8);
+	sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
+
+	err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
+			    tmp);
+	if (err) {
+		return err;
+	}
+
+	memcpy(proxy_svc_data + 3, tmp + 8, 8);
+
+	err = bt_mesh_adv_start(&fast_adv_param, duration, node_id_ad,
+			      ARRAY_SIZE(node_id_ad), sd, 0);
+	if (err) {
+		BT_WARN("Failed to advertise using Node ID (err %d)", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration)
+{
+	struct ble_gap_adv_params slow_adv_param = {
+		ADV_OPT_PROXY
+		ADV_SLOW_INT
+	};
+#if ADV_OPT_USE_NAME
+	const char *name = CONFIG_BT_DEVICE_NAME;
+	size_t name_len = strlen(name);
+	struct bt_data sd = {
+		.type = BT_DATA_NAME_COMPLETE,
+		.data_len = name_len,
+		.data = (void *)name
+	};
+#else
+	struct bt_data *sd = NULL;
+#endif
+	int err;
+
+	BT_DBG("");
+
+	proxy_svc_data[2] = ID_TYPE_NET;
+
+	BT_DBG("Advertising with NetId %s",
+	       bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
+
+	memcpy(proxy_svc_data + 3, sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
+
+	err = bt_mesh_adv_start(&slow_adv_param, duration, net_id_ad,
+			      ARRAY_SIZE(net_id_ad), sd, 0);
+	if (err) {
+		BT_WARN("Failed to advertise using Network ID (err %d)", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static bool advertise_subnet(struct bt_mesh_subnet *sub)
+{
+	if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+		return false;
+	}
+
+	return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
+	bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
+}
+
+static struct bt_mesh_subnet *next_sub(void)
+{
+	struct bt_mesh_subnet *sub = NULL;
+
+	if (!beacon_sub) {
+		beacon_sub = bt_mesh_subnet_next(NULL);
+		if (!beacon_sub) {
+			/* No valid subnets */
+			return NULL;
+		}
+	}
+
+	sub = beacon_sub;
+	do {
+		if (advertise_subnet(sub)) {
+			beacon_sub = sub;
+			return sub;
+		}
+
+		sub = bt_mesh_subnet_next(sub);
+	} while (sub != beacon_sub);
+
+	/* No subnets to advertise on */
+	return NULL;
+}
+
+static int sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
+{
+	int *count = cb_data;
+
+	if (advertise_subnet(sub)) {
+		(*count)++;
+	}
+
+	return 0;
+}
+
+static int sub_count(void)
+{
+	int count = 0;
+
+	(void)bt_mesh_subnet_find(sub_count_cb, &count);
+
+	return count;
+}
+
+static int gatt_proxy_advertise(struct bt_mesh_subnet *sub)
+{
+	int32_t remaining = K_FOREVER;
+	int subnet_count;
+	int err = -EBUSY;
+
+	BT_DBG("");
+
+	if (conn_count == CONFIG_BT_MAX_CONN) {
+		BT_DBG("Connectable advertising deferred (max connections %d)", conn_count);
+		return -ENOMEM;
+	}
+
+	sub = beacon_sub ? beacon_sub : bt_mesh_subnet_next(beacon_sub);
+	if (!sub) {
+		BT_WARN("No subnets to advertise on");
+		return -ENOENT;
+	}
+
+	subnet_count = sub_count();
+	BT_DBG("sub_count %u", subnet_count);
+	if (subnet_count > 1) {
+		int32_t max_timeout;
+
+		/* We use NODE_ID_TIMEOUT as a starting point since it may
+		 * be less than 60 seconds. Divide this period into at least
+		 * 6 slices, but make sure that a slice is at least one
+		 * second long (to avoid excessive rotation).
+		 */
+		max_timeout = NODE_ID_TIMEOUT / max(subnet_count, 6);
+		max_timeout = max(max_timeout, K_SECONDS(1));
+
+		if (remaining > max_timeout || remaining < 0) {
+			remaining = max_timeout;
+		}
+	}
+
+	if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
+		uint32_t active = k_uptime_get_32() - sub->node_id_start;
+
+		if (active < NODE_ID_TIMEOUT) {
+			remaining = NODE_ID_TIMEOUT - active;
+			BT_DBG("Node ID active for %u ms, %d ms remaining",
+			       active, remaining);
+			err = node_id_adv(sub, remaining);
+		} else {
+			bt_mesh_proxy_identity_stop(sub);
+			BT_DBG("Node ID stopped");
+		}
+	}
+
+	if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
+		err = net_id_adv(sub, remaining);
+	}
+
+	BT_DBG("Advertising %d ms for net_idx 0x%04x",
+	       (int) remaining, sub->net_idx);
+
+	beacon_sub = bt_mesh_subnet_next(beacon_sub);
+
+	return err;
+}
+
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	if (evt == BT_MESH_KEY_DELETED) {
+		if (sub == beacon_sub) {
+			beacon_sub = NULL;
+		}
+	} else {
+		bt_mesh_proxy_beacon_send(sub);
+	}
+}
+
+static void proxy_ccc_write(uint16_t conn_handle)
+{
+	struct bt_mesh_proxy_client *client;
+
+	BT_DBG("conn_handle %d", conn_handle);
+
+	client = find_client(conn_handle);
+
+	if (client->filter_type == NONE) {
+		client->filter_type = ACCEPT;
+		k_work_add_arg(&client->send_beacons, client);
+		k_work_submit(&client->send_beacons);
+	}
+}
+
+int bt_mesh_proxy_gatt_enable(void)
+{
+	uint16_t handle;
+	int rc;
+	int i;
+
+	BT_DBG("");
+
+	if (!bt_mesh_is_provisioned()) {
+		return -ENOTSUP;
+	}
+
+	if (service_registered) {
+		return -EBUSY;
+	}
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
+	assert(rc == 0);
+	ble_gatts_svc_set_visibility(handle, 1);
+	/* FIXME: figure out end handle */
+	ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
+
+	service_registered = true;
+
+	for (i = 0; i < ARRAY_SIZE(clients); i++) {
+		if (clients[i].cli) {
+			clients[i].filter_type = ACCEPT;
+		}
+	}
+	return 0;
+}
+
+void bt_mesh_proxy_gatt_disconnect(void)
+{
+	int rc;
+	int i;
+
+	BT_DBG("");
+
+	for (i = 0; i < ARRAY_SIZE(clients); i++) {
+		struct bt_mesh_proxy_client *client = &clients[i];
+
+		if ((client->cli) &&
+		    (client->filter_type == ACCEPT ||
+		    client->filter_type == REJECT)) {
+			client->filter_type = NONE;
+			rc = ble_gap_terminate(client->cli->conn_handle,
+			                       BLE_ERR_REM_USER_CONN_TERM);
+			assert(rc == 0);
+		}
+	}
+}
+
+int bt_mesh_proxy_gatt_disable(void)
+{
+	uint16_t handle;
+	int rc;
+	BT_DBG("");
+
+	if (!service_registered) {
+		return -EALREADY;
+	}
+
+	bt_mesh_proxy_gatt_disconnect();
+
+	rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
+	assert(rc == 0);
+	ble_gatts_svc_set_visibility(handle, 0);
+	/* FIXME: figure out end handle */
+	ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
+	service_registered = false;
+
+	return 0;
+}
+
+void bt_mesh_proxy_addr_add(struct os_mbuf *buf, uint16_t addr)
+{
+	struct bt_mesh_proxy_client *client;
+	struct bt_mesh_proxy_role *cli =
+		CONTAINER_OF(buf, struct bt_mesh_proxy_role, buf);
+
+	client = find_client(cli->conn_handle);
+
+	BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
+
+	if (client->filter_type == ACCEPT) {
+		filter_add(client, addr);
+	} else if (client->filter_type == REJECT) {
+		filter_remove(client, addr);
+	}
+}
+
+static bool client_filter_match(struct bt_mesh_proxy_client *client,
+				uint16_t addr)
+{
+	int i;
+
+	BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
+
+	if (client->filter_type == REJECT) {
+		for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+			if (client->filter[i] == addr) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	if (addr == BT_MESH_ADDR_ALL_NODES) {
+		return true;
+	}
+
+	if (client->filter_type == ACCEPT) {
+		for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+			if (client->filter[i] == addr) {
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+bool bt_mesh_proxy_relay(struct os_mbuf *buf, uint16_t dst)
+{
+	const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb;
+	void *cb_data = BT_MESH_ADV(buf)->cb_data;
+	bool relayed = false;
+	int i, err;
+
+	BT_DBG("%u bytes to dst 0x%04x", buf->om_len, dst);
+
+	for (i = 0; i < ARRAY_SIZE(clients); i++) {
+		struct bt_mesh_proxy_client *client = &clients[i];
+		struct os_mbuf *msg;
+
+		if (!client->cli) {
+			continue;
+		}
+
+		if (!client_filter_match(client, dst)) {
+			continue;
+		}
+
+		/* Proxy PDU sending modifies the original buffer,
+		 * so we need to make a copy.
+		 */
+		msg = NET_BUF_SIMPLE(32);
+		net_buf_simple_init(msg, 1);
+		net_buf_simple_add_mem(msg, buf->om_data, buf->om_len);
+
+		err = bt_mesh_proxy_msg_send(client->cli, BT_MESH_PROXY_NET_PDU,
+					     msg);
+
+		adv_send_start(0, err, cb, cb_data);
+		if (err) {
+			BT_ERR("Failed to send proxy message (err %d)", err);
+
+			/* If segment_and_send() fails the buf_send_end() callback will
+			 * not be called, so we need to clear the user data (net_buf,
+			 * which is just opaque data to segment_and send) reference given
+			 * to segment_and_send() here.
+			 */
+			net_buf_unref(buf);
+			continue;
+		}
+		os_mbuf_free_chain(msg);
+		relayed = true;
+	}
+
+	return relayed;
+}
+
+static void gatt_connected(uint16_t conn_handle)
+{
+	struct bt_mesh_proxy_client *client;
+	struct ble_gap_conn_desc info;
+	struct ble_hs_conn *conn;
+
+	conn = ble_hs_conn_find(conn_handle);
+	bt_conn_get_info(conn, &info);
+	if (info.role != BLE_GAP_ROLE_SLAVE ||
+	    !service_registered) {
+		return;
+	}
+	BT_DBG("conn %d", conn_handle);
+
+	conn_count++;
+
+	client = find_client(conn_handle);
+
+	client->filter_type = NONE;
+	(void)memset(client->filter, 0, sizeof(client->filter));
+
+	client->cli = bt_mesh_proxy_role_setup(conn_handle, proxy_send,
+					       proxy_msg_recv);
+
+	/* Try to re-enable advertising in case it's possible */
+	if (conn_count < CONFIG_BT_MAX_CONN) {
+		bt_mesh_adv_update();
+	}
+}
+
+static void gatt_disconnected(uint16_t conn_handle, uint8_t reason)
+{
+	struct ble_gap_conn_desc info;
+	struct bt_mesh_proxy_client *client;
+	struct ble_hs_conn *conn;
+
+	conn = ble_hs_conn_find(conn_handle);
+	bt_conn_get_info(conn, &info);
+	if (info.role != BLE_GAP_ROLE_SLAVE) {
+		return;
+	}
+
+	if (!service_registered && bt_mesh_is_provisioned()) {
+		(void)bt_mesh_proxy_gatt_enable();
+		return;
+	}
+
+	conn_count--;
+	client = find_client(conn_handle);
+	if (client->cli) {
+		bt_mesh_proxy_role_cleanup(client->cli);
+		client->cli = NULL;
+	}
+}
+
+void notify_complete(void)
+{
+	sys_snode_t *n;
+
+	if (atomic_dec(&pending_notifications) > 1) {
+		return;
+	}
+
+	BT_DBG("");
+
+	while ((n = sys_slist_get(&idle_waiters))) {
+		CONTAINER_OF(n, struct bt_mesh_proxy_idle_cb, n)->cb();
+	}
+}
+
+static int proxy_send(uint16_t conn_handle,
+		      const void *data, uint16_t len)
+{
+	struct os_mbuf *om;
+	int err = 0;
+
+	BT_DBG("%u bytes: %s", len, bt_hex(data, len));
+
+	om = ble_hs_mbuf_from_flat(data, len);
+	assert(om);
+	err = ble_gattc_notify_custom(conn_handle, svc_handles.proxy_data_out_h, om);
+	notify_complete();
+
+	if (!err) {
+		atomic_inc(&pending_notifications);
+	}
+
+	return err;
+}
+
+int bt_mesh_proxy_adv_start(void)
+{
+	BT_DBG("");
+
+	if (!service_registered || !bt_mesh_is_provisioned()) {
+		return -ENOTSUP;
+	}
+
+	return gatt_proxy_advertise(next_sub());
+}
+
+
+static void ble_mesh_handle_connect(struct ble_gap_event *event, void *arg)
+{
+#if MYNEWT_VAL(BLE_EXT_ADV)
+	/* When EXT ADV is enabled then mesh proxy is connected
+	 * when proxy advertising instance is completed.
+	 * Therefore no need to handle BLE_GAP_EVENT_CONNECT
+	 */
+	if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
+		/* Reason 0 means advertising has been completed because
+		 * connection has been established
+		 */
+		if (event->adv_complete.reason != 0) {
+			return;
+		}
+
+		if (event->adv_complete.instance != BT_MESH_ADV_GATT_INST) {
+			return;
+		}
+
+		gatt_connected(event->adv_complete.conn_handle);
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+		gatt_connected_pb_gatt(event->adv_complete.conn_handle,
+				       event->adv_complete.reason);
+#endif
+	}
+#else
+	if (event->type == BLE_GAP_EVENT_CONNECT) {
+		gatt_connected(event->connect.conn_handle);
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+		gatt_connected_pb_gatt(event->connect.conn_handle, event->connect.status);
+#endif
+	}
+#endif
+}
+
+int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg)
+{
+	if ((event->type == BLE_GAP_EVENT_CONNECT) ||
+	    (event->type == BLE_GAP_EVENT_ADV_COMPLETE)) {
+		ble_mesh_handle_connect(event, arg);
+	} else if (event->type == BLE_GAP_EVENT_DISCONNECT) {
+		gatt_disconnected(event->disconnect.conn.conn_handle,
+				   event->disconnect.reason);
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+		gatt_disconnected_pb_gatt(event->disconnect.conn.conn_handle,
+				  event->disconnect.reason);
+#endif
+	} else if (event->type == BLE_GAP_EVENT_SUBSCRIBE) {
+		if (event->subscribe.attr_handle == svc_handles.proxy_data_out_h) {
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+			proxy_ccc_write(event->subscribe.conn_handle);
+#endif
+		} else if (event->subscribe.attr_handle ==
+			   svc_handles.prov_data_out_h) {
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+			prov_ccc_write(event->subscribe.conn_handle, event->type);
+#endif
+		}
+	}
+
+	return 0;
+}
+
+int bt_mesh_proxy_init(void)
+{
+	int i;
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+	if (!bt_mesh_subnet_cb_list[4]) {
+		bt_mesh_subnet_cb_list[4] = subnet_evt;
+	}
+#endif
+
+	for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) {
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+		k_work_init(&clients[i].send_beacons, proxy_send_beacons);
+#endif
+	}
+
+	resolve_svc_handles();
+
+	ble_gatts_svc_set_visibility(svc_handles.proxy_h, 0);
+	ble_gatts_svc_set_visibility(svc_handles.prov_h, 0);
+
+	return 0;
+}
diff --git a/nimble/host/mesh/src/rpl.c b/nimble/host/mesh/src/rpl.c
index 93c2e1a885..3a54414cea 100644
--- a/nimble/host/mesh/src/rpl.c
+++ b/nimble/host/mesh/src/rpl.c
@@ -10,6 +10,7 @@
 #define MESH_LOG_MODULE BLE_MESH_RPL_LOG
 
 #include "log/log.h"
+#include <stdlib.h>
 
 #include "mesh_priv.h"
 #include "adv.h"
@@ -17,17 +18,81 @@
 #include "rpl.h"
 #include "settings.h"
 
+/* Replay Protection List information for persistent storage. */
+struct rpl_val {
+	uint32_t seq:24,
+	old_iv:1;
+};
+
 static struct bt_mesh_rpl replay_list[MYNEWT_VAL(BLE_MESH_CRPL)];
+static ATOMIC_DEFINE(store, MYNEWT_VAL(BLE_MESH_CRPL));
+
+static inline int rpl_idx(const struct bt_mesh_rpl *rpl)
+{
+	return rpl - &replay_list[0];
+}
+
+static void clear_rpl(struct bt_mesh_rpl *rpl)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int err;
+	char path[18];
+
+	if (!rpl->src) {
+		return;
+	}
+
+	snprintk(path, sizeof(path), "bt_mesh/RPL/%x", rpl->src);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear RPL");
+	} else {
+		BT_DBG("Cleared RPL");
+	}
+
+	(void)memset(rpl, 0, sizeof(*rpl));
+	atomic_clear_bit(store, rpl_idx(rpl));
+#endif
+}
+
+static void schedule_rpl_store(struct bt_mesh_rpl *entry, bool force)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	atomic_set_bit(store, rpl_idx(entry));
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
+	if (force
+#ifdef CONFIG_BT_MESH_RPL_STORE_TIMEOUT
+	|| CONFIG_BT_MESH_RPL_STORE_TIMEOUT >= 0
+#endif
+	    ) {
+		bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
+	}
+#endif
+}
+
+static void schedule_rpl_clear(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
+#endif
+}
 
 void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
 		struct bt_mesh_net_rx *rx)
 {
+	/* If this is the first message on the new IV index, we should reset it
+	 * to zero to avoid invalid combinations of IV index and seg.
+	 */
+	if (rpl->old_iv && !rx->old_iv) {
+		rpl->seg = 0;
+	}
+
 	rpl->src = rx->ctx.addr;
 	rpl->seq = rx->seq;
 	rpl->old_iv = rx->old_iv;
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_rpl(rpl);
+		schedule_rpl_store(rpl, false);
 	}
 }
 
@@ -95,13 +160,14 @@ void bt_mesh_rpl_clear(void)
 	BT_DBG("");
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_clear_rpl();
+		schedule_rpl_clear();
 	} else {
 		(void)memset(replay_list, 0, sizeof(replay_list));
 	}
 }
 
-struct bt_mesh_rpl *bt_mesh_rpl_find(uint16_t src)
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct bt_mesh_rpl *bt_mesh_rpl_find(uint16_t src)
 {
 	int i;
 
@@ -114,7 +180,7 @@ struct bt_mesh_rpl *bt_mesh_rpl_find(uint16_t src)
 	return NULL;
 }
 
-struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src)
+static struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src)
 {
 	int i;
 
@@ -127,15 +193,7 @@ struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src)
 
 	return NULL;
 }
-
-void bt_mesh_rpl_foreach(bt_mesh_rpl_func_t func, void *user_data)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
-		func(&replay_list[i], user_data);
-	}
-}
+#endif
 
 void bt_mesh_rpl_reset(void)
 {
@@ -149,14 +207,174 @@ void bt_mesh_rpl_reset(void)
 
 		if (rpl->src) {
 			if (rpl->old_iv) {
-				(void)memset(rpl, 0, sizeof(*rpl));
+				if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+					clear_rpl(rpl);
+				} else {
+					(void)memset(rpl, 0, sizeof(*rpl));
+				}
 			} else {
 				rpl->old_iv = true;
+				if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+					schedule_rpl_store(rpl, true);
+				}
 			}
+		}
+	}
+}
 
-			if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-				bt_mesh_store_rpl(rpl);
-			}
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int rpl_set(int argc, char **argv, char *val)
+{
+	struct bt_mesh_rpl *entry;
+	struct rpl_val rpl;
+	int len, err;
+	uint16_t src;
+
+	if (argc < 1) {
+		BT_ERR("Invalid argc (%d)", argc);
+		return -ENOENT;
+	}
+
+	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+	src = strtol(argv[0], NULL, 16);
+	entry = bt_mesh_rpl_find(src);
+
+	if (!val) {
+		if (entry) {
+			memset(entry, 0, sizeof(*entry));
+		} else {
+			BT_WARN("Unable to find RPL entry for 0x%04x", src);
+		}
+
+		return 0;
+	}
+
+	if (!entry) {
+		entry = bt_mesh_rpl_alloc(src);
+		if (!entry) {
+			BT_ERR("Unable to allocate RPL entry for 0x%04x", src);
+			return -ENOMEM;
 		}
 	}
-}
\ No newline at end of file
+
+	len = sizeof(rpl);
+	err = settings_bytes_from_str(val, &rpl, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(rpl)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl));
+		return -EINVAL;
+	}
+
+	entry->seq = rpl.seq;
+	entry->old_iv = rpl.old_iv;
+
+	BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src,
+	       (unsigned) entry->seq, entry->old_iv);
+	return 0;
+}
+#endif
+
+static void store_rpl(struct bt_mesh_rpl *entry)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))];
+	struct rpl_val rpl;
+	char path[18];
+	char *str;
+	int err;
+
+	if (!entry->src) {
+		return;
+	}
+
+	BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src,
+	       (unsigned) entry->seq, entry->old_iv);
+
+	rpl.seq = entry->seq;
+	rpl.old_iv = entry->old_iv;
+
+	str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode RPL as value");
+		return;
+	}
+
+	snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src);
+
+	BT_DBG("Saving RPL %s as value %s", path, str);
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store RPL");
+	} else {
+		BT_DBG("Stored RPL");
+	}
+#endif
+}
+
+static void store_pending_rpl(struct bt_mesh_rpl *rpl)
+{
+	BT_DBG("");
+
+	if (atomic_test_and_clear_bit(store, rpl_idx(rpl))) {
+		store_rpl(rpl);
+	}
+}
+
+void bt_mesh_rpl_pending_store(uint16_t addr)
+{
+	int i;
+
+	if (!IS_ENABLED(CONFIG_BT_SETTINGS) ||
+	(!BT_MESH_ADDR_IS_UNICAST(addr) &&
+	addr != BT_MESH_ADDR_ALL_NODES)) {
+		return;
+	}
+
+	if (addr == BT_MESH_ADDR_ALL_NODES) {
+		bt_mesh_settings_store_cancel(BT_MESH_SETTINGS_RPL_PENDING);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
+		if (addr != BT_MESH_ADDR_ALL_NODES &&
+		addr != replay_list[i].src) {
+			continue;
+		}
+
+		if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+			store_pending_rpl(&replay_list[i]);
+		} else {
+			clear_rpl(&replay_list[i]);
+		}
+
+		if (addr != BT_MESH_ADDR_ALL_NODES) {
+			break;
+		}
+	}
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct conf_handler bt_mesh_rpl_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = rpl_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_rpl_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_rpl_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_rpl conf");
+#endif
+}
diff --git a/nimble/host/mesh/src/rpl.h b/nimble/host/mesh/src/rpl.h
index 0592712f87..9d110d9dab 100644
--- a/nimble/host/mesh/src/rpl.h
+++ b/nimble/host/mesh/src/rpl.h
@@ -8,12 +8,16 @@
  */
 
 struct bt_mesh_rpl {
-	uint16_t src;
-	bool  old_iv;
-#if defined(CONFIG_BT_SETTINGS)
-	bool  store;
-#endif
-	uint32_t seq;
+	uint64_t src:15,
+		 old_iv:1,
+		 seq:24,
+		 /** Sequence authentication value for the previous segmented
+		  *  message received from this address.
+		  *
+		  *  This value is used to manage the parallel RPL of the
+		  *  SeqAuth values in transport.
+		  */
+		 seg:24;
 };
 
 typedef void (*bt_mesh_rpl_func_t)(struct bt_mesh_rpl *rpl,
@@ -23,8 +27,6 @@ void bt_mesh_rpl_reset(void);
 bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
 			struct bt_mesh_rpl **match);
 void bt_mesh_rpl_clear(void);
-struct bt_mesh_rpl *bt_mesh_rpl_find(uint16_t src);
-struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src);
-void bt_mesh_rpl_foreach(bt_mesh_rpl_func_t func, void *user_data);
 void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
-			struct bt_mesh_net_rx *rx);
\ No newline at end of file
+			struct bt_mesh_net_rx *rx);
+void bt_mesh_rpl_init(void);
diff --git a/nimble/host/mesh/src/settings.c b/nimble/host/mesh/src/settings.c
index 30fbf5a014..cddf025f92 100644
--- a/nimble/host/mesh/src/settings.c
+++ b/nimble/host/mesh/src/settings.c
@@ -10,144 +10,26 @@
 #if MYNEWT_VAL(BLE_MESH_SETTINGS)
 
 #include "mesh_priv.h"
-#include "mesh/mesh.h"
 #include "mesh/glue.h"
 #include "subnet.h"
 #include "app_keys.h"
 #include "net.h"
+#include "cdb_priv.h"
 #include "rpl.h"
 #include "crypto.h"
 #include "transport.h"
 #include "heartbeat.h"
 #include "access.h"
-#include "foundation.h"
+#include "pb_gatt_srv.h"
 #include "proxy.h"
 #include "settings.h"
-#include "lpn.h"
 #include "cfg.h"
 
 
 #include "config/config.h"
 
-/* Tracking of what storage changes are pending for App and Net Keys. We
- * track this in a separate array here instead of within the respective
- * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key
- * gets deleted its struct becomes invalid and may be reused for other keys.
- */
-struct key_update {
-	uint16_t key_idx:12,    /* AppKey or NetKey Index */
-	      valid:1,       /* 1 if this entry is valid, 0 if not */
-	      app_key:1,     /* 1 if this is an AppKey, 0 if a NetKey */
-	      clear:1;       /* 1 if key needs clearing, 0 if storing */
-};
-
-static struct key_update key_updates[CONFIG_BT_MESH_APP_KEY_COUNT +
-				     CONFIG_BT_MESH_SUBNET_COUNT];
-
-static struct k_delayed_work pending_store;
-
-/* Mesh network storage information */
-struct net_val {
-	uint16_t primary_addr;
-	uint8_t  dev_key[16];
-} __packed;
-
-/* Sequence number storage */
-struct seq_val {
-	uint8_t val[3];
-} __packed;
-
-/* Heartbeat Publication storage */
-struct hb_pub_val {
-	uint16_t dst;
-	uint8_t  period;
-	uint8_t  ttl;
-	uint16_t feat;
-	uint16_t net_idx:12,
-	      indefinite:1;
-};
-
-/* Miscelaneous configuration server model states */
-struct cfg_val {
-	uint8_t net_transmit;
-	uint8_t relay;
-	uint8_t relay_retransmit;
-	uint8_t beacon;
-	uint8_t gatt_proxy;
-	uint8_t frnd;
-	uint8_t default_ttl;
-};
-
-/* IV Index & IV Update storage */
-struct iv_val {
-	uint32_t iv_index;
-	uint8_t  iv_update:1,
-	      iv_duration:7;
-} __packed;
-
-/* Replay Protection List storage */
-struct rpl_val {
-	uint32_t seq:24,
-	      old_iv:1;
-};
-
-/* NetKey storage information */
-struct net_key_val {
-	uint8_t kr_flag:1,
-	     kr_phase:7;
-	uint8_t val[2][16];
-} __packed;
-
-/* AppKey storage information */
-struct app_key_val {
-	uint16_t net_idx;
-	bool  updated;
-	uint8_t  val[2][16];
-} __packed;
-
-struct mod_pub_val {
-	uint16_t addr;
-	uint16_t key;
-	uint8_t  ttl;
-	uint8_t  retransmit;
-	uint8_t  period;
-	uint8_t  period_div:4,
-	      cred:1;
-};
-
-/* Virtual Address information */
-struct va_val {
-	uint16_t ref;
-	uint16_t addr;
-	uint8_t uuid[16];
-} __packed;
-
-struct cdb_net_val {
-	uint32_t iv_index;
-	bool  iv_update;
-} __packed;
-
-/* Node storage information */
-struct node_val {
-	uint16_t net_idx;
-	uint8_t  num_elem;
-	uint8_t  flags;
-#define F_NODE_CONFIGURED 0x01
-	uint8_t  uuid[16];
-	uint8_t  dev_key[16];
-} __packed;
-
-struct node_update {
-	uint16_t addr;
-	bool clear;
-};
-
-#if MYNEWT_VAL(BLE_MESH_CDB)
-static struct node_update cdb_node_updates[MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT)];
-static struct key_update cdb_key_updates[
-					MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) +
-					MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT)];
-#endif
+static struct k_work_delayable pending_store;
+static ATOMIC_DEFINE(pending_flags, BT_MESH_SETTINGS_FLAG_COUNT);
 
 int settings_name_next(char *name, char **next)
 {
@@ -181,2270 +63,140 @@ int settings_name_next(char *name, char **next)
 	return rc;
 }
 
-static int net_set(int argc, char **argv, char *val)
-{
-	struct net_val net;
-	int len, err;
-
-	BT_DBG("val %s", val ? val : "(null)");
-
-	if (!val) {
-		bt_mesh_comp_unprovision();
-		memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
-		return 0;
-	}
-
-	len = sizeof(net);
-	err = settings_bytes_from_str(val, &net, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(net)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net));
-		return -EINVAL;
-	}
-
-	memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key));
-	bt_mesh_comp_provision(net.primary_addr);
-
-	BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr);
-	BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16));
-
-	return 0;
-}
-
-static int iv_set(int argc, char **argv, char *val)
-{
-	struct iv_val iv;
-	int len, err;
-
-	BT_DBG("val %s", val ? val : "(null)");
-
-	if (!val) {
-		bt_mesh.iv_index = 0U;
-		atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
-		return 0;
-	}
-
-	len = sizeof(iv);
-	err = settings_bytes_from_str(val, &iv, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(iv)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv));
-		return -EINVAL;
-	}
-
-	bt_mesh.iv_index = iv.iv_index;
-	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update);
-	bt_mesh.ivu_duration = iv.iv_duration;
-
-	BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours",
-	       (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration);
-
-	return 0;
-}
-
-static int seq_set(int argc, char **argv, char *val)
-{
-	struct seq_val seq;
-	int len, err;
-
-	BT_DBG("val %s", val ? val : "(null)");
-
-	if (!val) {
-		bt_mesh.seq = 0;
-		return 0;
-	}
-
-	len = sizeof(seq);
-	err = settings_bytes_from_str(val, &seq, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(seq)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq));
-		return -EINVAL;
-	}
-
-	bt_mesh.seq = sys_get_le24(seq.val);
-
-	if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) {
-		/* Make sure we have a large enough sequence number. We
-		 * subtract 1 so that the first transmission causes a write
-		 * to the settings storage.
-		 */
-		bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE -
-				(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE));
-		bt_mesh.seq--;
-	}
-
-	BT_DBG("Sequence Number 0x%06x", bt_mesh.seq);
-
-	return 0;
-}
-
-static int rpl_set(int argc, char **argv, char *val)
+static int mesh_commit(void)
 {
-	struct bt_mesh_rpl *entry;
-	struct rpl_val rpl;
-	int len, err;
-	uint16_t src;
-
-	if (argc < 1) {
-		BT_ERR("Invalid argc (%d)", argc);
-		return -ENOENT;
-	}
-
-	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
-
-	src = strtol(argv[0], NULL, 16);
-	entry = bt_mesh_rpl_find(src);
-
-	if (!val) {
-		if (entry) {
-			memset(entry, 0, sizeof(*entry));
-		} else {
-			BT_WARN("Unable to find RPL entry for 0x%04x", src);
-		}
-
+	if (!bt_mesh_subnet_next(NULL)) {
+		/* Nothing to do since we're not yet provisioned */
 		return 0;
 	}
 
-	if (!entry) {
-		entry = bt_mesh_rpl_alloc(src);
-		if (!entry) {
-			BT_ERR("Unable to allocate RPL entry for 0x%04x", src);
-			return -ENOMEM;
-		}
-	}
-
-	len = sizeof(rpl);
-	err = settings_bytes_from_str(val, &rpl, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(rpl)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl));
-		return -EINVAL;
+	if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
+		(void)bt_mesh_pb_gatt_disable();
 	}
 
-	entry->seq = rpl.seq;
-	entry->old_iv = rpl.old_iv;
+	bt_mesh_net_settings_commit();
+	bt_mesh_model_settings_commit();
 
-	BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src,
-	       (unsigned) entry->seq, entry->old_iv);
+	atomic_set_bit(bt_mesh.flags, BT_MESH_VALID);
 
+	bt_mesh_start();
 	return 0;
 }
 
-static int net_key_set(int argc, char **argv, char *val)
-{
-	struct net_key_val key;
-	int len, err;
-	uint16_t net_idx;
-
-	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
-
-	net_idx = strtol(argv[0], NULL, 16);
-
-	len = sizeof(key);
-	err = settings_bytes_from_str(val, &key, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(key)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key));
-		return -EINVAL;
-	}
-
-	BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
-
-	return bt_mesh_subnet_set(
-		net_idx, key.kr_phase, key.val[0],
-		(key.kr_phase != BT_MESH_KR_NORMAL) ? key.val[1] : NULL);
-}
-
-static int app_key_set(int argc, char **argv, char *val)
-{
-	struct app_key_val key;
-	uint16_t app_idx;
-	int len_rd, err;
-
-	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
-
-	app_idx = strtol(argv[0], NULL, 16);
-	len_rd = strtol(argv[1], NULL, 16);
-
-	if (!len_rd) {
-		return 0;
-	}
-
-	err = settings_bytes_from_str(val, &key, &len_rd);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0],
-			      key.updated ? key.val[1] : NULL);
-	if (err) {
-		BT_ERR("Failed to set \'app-key\'");
-		return err;
-	}
-
-	BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
+/* Pending flags that use K_NO_WAIT as the storage timeout */
+#define NO_WAIT_PENDING_BITS (BIT(BT_MESH_SETTINGS_NET_PENDING) |           \
+			BIT(BT_MESH_SETTINGS_IV_PENDING)  |           \
+			BIT(BT_MESH_SETTINGS_SEQ_PENDING) |           \
+			BIT(BT_MESH_SETTINGS_CDB_PENDING))
 
-	return 0;
-}
+/* Pending flags that use CONFIG_BT_MESH_STORE_TIMEOUT */
+#define GENERIC_PENDING_BITS (BIT(BT_MESH_SETTINGS_NET_KEYS_PENDING) |      \
+			BIT(BT_MESH_SETTINGS_APP_KEYS_PENDING) |      \
+			BIT(BT_MESH_SETTINGS_HB_PUB_PENDING)   |      \
+			BIT(BT_MESH_SETTINGS_CFG_PENDING)      |      \
+			BIT(BT_MESH_SETTINGS_MOD_PENDING)      |      \
+			BIT(BT_MESH_SETTINGS_VA_PENDING))
 
-static int hb_pub_set(int argc, char **argv, char *val)
+void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag)
 {
-	struct bt_mesh_hb_pub pub;
-	struct hb_pub_val hb_val;
-	int len, err;
-
-	BT_DBG("val %s", val ? val : "(null)");
-
-	len = sizeof(hb_val);
-	err = settings_bytes_from_str(val, &hb_val, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(hb_val)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len,
-		       sizeof(hb_val));
-		return -EINVAL;
-	}
+	int32_t timeout_ms, remaining_ms;
 
-	pub.dst = hb_val.dst;
-	pub.period = bt_mesh_hb_pwr2(hb_val.period);
-	pub.ttl = hb_val.ttl;
-	pub.feat = hb_val.feat;
-	pub.net_idx = hb_val.net_idx;
+	atomic_set_bit(pending_flags, flag);
 
-	if (hb_val.indefinite) {
-		pub.count = 0xffff;
+	if (atomic_get(pending_flags) & NO_WAIT_PENDING_BITS) {
+		timeout_ms = 0;
+	} else if (CONFIG_BT_MESH_RPL_STORE_TIMEOUT >= 0 &&
+		   atomic_test_bit(pending_flags, BT_MESH_SETTINGS_RPL_PENDING) &&
+		   !(atomic_get(pending_flags) & GENERIC_PENDING_BITS)) {
+		timeout_ms = CONFIG_BT_MESH_RPL_STORE_TIMEOUT * MSEC_PER_SEC;
 	} else {
-		pub.count = 0;
-	}
-
-	(void)bt_mesh_hb_pub_set(&pub);
-
-	BT_DBG("Restored heartbeat publication");
-
-	return 0;
-}
-
-static int cfg_set(int argc, char **argv, char *val)
-{
-	struct cfg_val cfg;
-	int len, err;
-
-	BT_DBG("val %s", val ? val : "(null)");
-
-	if (!val) {
-		BT_DBG("Cleared configuration state");
-		return 0;
-	}
-
-	len = sizeof(cfg);
-	err = settings_bytes_from_str(val, &cfg, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return err;
-	}
-
-	if (len != sizeof(cfg)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len,
-		       sizeof(cfg));
-		return -EINVAL;
-	}
-
-	bt_mesh_net_transmit_set(cfg.net_transmit);
-	bt_mesh_relay_set(cfg.relay, cfg.relay_retransmit);
-	bt_mesh_beacon_set(cfg.beacon);
-	bt_mesh_gatt_proxy_set(cfg.gatt_proxy);
-	bt_mesh_friend_set(cfg.frnd);
-	bt_mesh_default_ttl_set(cfg.default_ttl);
-
-	BT_DBG("Restored configuration state");
-
-	return 0;
-}
-
-static int mod_set_bind(struct bt_mesh_model *mod, char *val)
-{
-	int len, err, i;
-
-	/* Start with empty array regardless of cleared or set value */
-	for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
-		mod->keys[i] = BT_MESH_KEY_UNUSED;
-	}
-
-	if (!val) {
-		BT_DBG("Cleared bindings for model");
-		return 0;
-	}
-
-	len = sizeof(mod->keys);
-	err = settings_bytes_from_str(val, mod->keys, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return -EINVAL;
-	}
-
-	BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0]));
-	return 0;
-}
-
-static int mod_set_sub(struct bt_mesh_model *mod, char *val)
-{
-	int len, err;
-
-	/* Start with empty array regardless of cleared or set value */
-	memset(mod->groups, 0, sizeof(mod->groups));
-
-	if (!val) {
-		BT_DBG("Cleared subscriptions for model");
-		return 0;
-	}
-
-	len = sizeof(mod->groups);
-	err = settings_bytes_from_str(val, mod->groups, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return -EINVAL;
+		timeout_ms = CONFIG_BT_MESH_STORE_TIMEOUT * MSEC_PER_SEC;
 	}
 
-	BT_DBG("Decoded %u subscribed group addresses for model",
-	       len / sizeof(mod->groups[0]));
-	return 0;
-}
-
-static int mod_set_pub(struct bt_mesh_model *mod, char *val)
-{
-	struct mod_pub_val pub;
-	int len, err;
-
-	if (!mod->pub) {
-		BT_WARN("Model has no publication context!");
-		return -EINVAL;
-	}
-
-	if (!val) {
-		mod->pub->addr = BT_MESH_ADDR_UNASSIGNED;
-		mod->pub->key = 0;
-		mod->pub->cred = 0;
-		mod->pub->ttl = 0;
-		mod->pub->period = 0;
-		mod->pub->retransmit = 0;
-		mod->pub->count = 0;
-
-		BT_DBG("Cleared publication for model");
-		return 0;
-	}
-
-	len = sizeof(pub);
-	err = settings_bytes_from_str(val, &pub, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return -EINVAL;
-	}
-
-	if (len != sizeof(pub)) {
-		BT_ERR("Invalid length for model publication");
-		return -EINVAL;
+	remaining_ms = k_ticks_to_ms_floor32(
+		k_work_delayable_remaining_get(&pending_store));
+	BT_DBG("Waiting %u ms vs rem %u ms", timeout_ms, remaining_ms);
+	/* If the new deadline is sooner, override any existing
+	 * deadline; otherwise schedule without changing any existing
+	 * deadline.
+	 */
+	if (timeout_ms < remaining_ms) {
+		k_work_reschedule(&pending_store, K_MSEC(timeout_ms));
+	} else {
+		k_work_schedule(&pending_store, K_MSEC(timeout_ms));
 	}
-
-	mod->pub->addr = pub.addr;
-	mod->pub->key = pub.key;
-	mod->pub->cred = pub.cred;
-	mod->pub->ttl = pub.ttl;
-	mod->pub->period = pub.period;
-	mod->pub->retransmit = pub.retransmit;
-	mod->pub->count = 0;
-
-	BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x",
-	       pub.addr, pub.key);
-
-	return 0;
 }
 
-static int mod_data_set(struct bt_mesh_model *mod,
-			char *name, char *len_rd)
+void bt_mesh_settings_store_cancel(enum bt_mesh_settings_flag flag)
 {
-	char *next;
-
-	settings_name_next(name, &next);
-
-	if (mod->cb && mod->cb->settings_set) {
-		return mod->cb->settings_set(mod, next, len_rd);
-	}
-
-	return 0;
+	atomic_clear_bit(pending_flags, flag);
 }
 
-static int mod_set(bool vnd, int argc, char **argv, char *val)
+static void store_pending(struct ble_npl_event *work)
 {
-	struct bt_mesh_model *mod;
-	uint8_t elem_idx, mod_idx;
-	uint16_t mod_key;
-
-	if (argc < 2) {
-		BT_ERR("Too small argc (%d)", argc);
-		return -ENOENT;
-	}
-
-	mod_key = strtol(argv[0], NULL, 16);
-	elem_idx = mod_key >> 8;
-	mod_idx = mod_key;
-
-	BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u",
-	       mod_key, elem_idx, mod_idx);
-
-	mod = bt_mesh_model_get(vnd, elem_idx, mod_idx);
-	if (!mod) {
-		BT_ERR("Failed to get model for elem_idx %u mod_idx %u",
-		       elem_idx, mod_idx);
-		return -ENOENT;
+	BT_DBG("");
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_RPL_PENDING)) {
+		bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
 	}
 
-	if (!strcmp(argv[1], "bind")) {
-		return mod_set_bind(mod, val);
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_NET_KEYS_PENDING)) {
+		bt_mesh_subnet_pending_store();
 	}
 
-	if (!strcmp(argv[1], "sub")) {
-		return mod_set_sub(mod, val);
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_APP_KEYS_PENDING)) {
+		bt_mesh_app_key_pending_store();
 	}
 
-	if (!strcmp(argv[1], "pub")) {
-		return mod_set_pub(mod, val);
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_NET_PENDING)) {
+		bt_mesh_net_pending_net_store();
 	}
 
-	if (!strcmp(argv[1], "data")) {
-		return mod_data_set(mod, argv[1], val);	
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_IV_PENDING)) {
+		bt_mesh_net_pending_iv_store();
 	}
 
-	BT_WARN("Unknown module key %s", argv[1]);
-	return -ENOENT;
-}
-
-static int sig_mod_set(int argc, char **argv, char *val)
-{
-	return mod_set(false, argc, argv, val);
-}
-
-static int vnd_mod_set(int argc, char **argv, char *val)
-{
-	return mod_set(true, argc, argv, val);
-}
-
-#if CONFIG_BT_MESH_LABEL_COUNT > 0
-static int va_set(int argc, char **argv, char *val)
-{
-	struct va_val va;
-	struct bt_mesh_va *lab;
-	uint16_t index;
-	int len, err;
-
-	if (argc < 1) {
-		BT_ERR("Insufficient number of arguments");
-		return -ENOENT;
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_SEQ_PENDING)) {
+		bt_mesh_net_pending_seq_store();
 	}
 
-	index = strtol(argv[0], NULL, 16);
-
-	if (val == NULL) {
-		BT_WARN("Mesh Virtual Address length = 0");
-		return 0;
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_HB_PUB_PENDING)) {
+		bt_mesh_hb_pub_pending_store();
 	}
 
-	err = settings_bytes_from_str(val, &va, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return -EINVAL;
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_CFG_PENDING)) {
+		bt_mesh_cfg_pending_store();
 	}
 
-	if (len != sizeof(struct va_val)) {
-		BT_ERR("Invalid length for virtual address");
-		return -EINVAL;
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_MOD_PENDING)) {
+		bt_mesh_model_pending_store();
 	}
 
-	if (va.ref == 0) {
-		BT_WARN("Ignore Mesh Virtual Address ref = 0");
-		return 0;
+	if (atomic_test_and_clear_bit(pending_flags,
+				      BT_MESH_SETTINGS_VA_PENDING)) {
+		bt_mesh_va_pending_store();
 	}
 
-	lab = bt_mesh_va_get(index);
-	if (lab == NULL) {
-		BT_WARN("Out of labels buffers");
-		return -ENOBUFS;
+#if IS_ENABLED(CONFIG_BT_MESH_CDB)
+	if (atomic_test_and_clear_bit(pending_flags,
+				     BT_MESH_SETTINGS_CDB_PENDING)) {
+		bt_mesh_cdb_pending_store();
 	}
-
-	memcpy(lab->uuid, va.uuid, 16);
-	lab->addr = va.addr;
-	lab->ref = va.ref;
-
-	BT_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x",
-	       lab->addr, lab->ref);
-
-	return 0;
-}
 #endif
-
-#if MYNEWT_VAL(BLE_MESH_CDB)
-static int cdb_net_set(int argc, char *val)
-{
-	struct cdb_net_val net;
-	int len, err;
-
-	len = sizeof(net);
-	err = settings_bytes_from_str(val, &net, &len);
-	if (err) {
-		BT_ERR("Failed to set \'cdb_net\'");
-		return err;
-	}
-
-	bt_mesh_cdb.iv_index = net.iv_index;
-
-	if (net.iv_update) {
-		atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS);
-	}
-
-	atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID);
-
-	return 0;
-}
-
-static int cdb_node_set(int argc, char *str)
-{
-	struct bt_mesh_cdb_node *node;
-	struct node_val val;
-	uint16_t addr;
-	int len, err;
-
-	if (argc < 1) {
-		BT_ERR("Insufficient number of arguments");
-		return -ENOENT;
-	}
-
-	addr = strtol(str, NULL, 16);
-	len = sizeof(str);
-
-	if (argc < 1) {
-		BT_DBG("val (null)");
-		BT_DBG("Deleting node 0x%04x", addr);
-
-		node = bt_mesh_cdb_node_get(addr);
-		if (node) {
-			bt_mesh_cdb_node_del(node, false);
-		}
-
-		return 0;
-	}
-
-	err = settings_bytes_from_str(str, &val, &len);
-	if (err) {
-		BT_ERR("Failed to decode value %s (err %d)", val, err);
-		return -EINVAL;
-	}
-
-	if (len != sizeof(struct node_val)) {
-		BT_ERR("Invalid length for node_val");
-		return -EINVAL;
-	}
-
-	node = bt_mesh_cdb_node_get(addr);
-	if (!node) {
-		node = bt_mesh_cdb_node_alloc(val.uuid, addr, val.num_elem,
-					      			  val.net_idx);
-	}
-
-	if (!node) {
-		BT_ERR("No space for a new node");
-		return -ENOMEM;
-	}
-
-	if (val.flags & F_NODE_CONFIGURED) {
-		atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);
-	}
-
-	memcpy(node->uuid, val.uuid, 16);
-	memcpy(node->dev_key, val.dev_key, 16);
-
-	BT_DBG("Node 0x%04x recovered from storage", addr);
-
-	return 0;
-}
-
-static int cdb_subnet_set(int argc, char *name)
-{
-	struct bt_mesh_cdb_subnet *sub;
-	struct net_key_val key;
-	uint16_t net_idx;
-	int len, len_rd, err;
-
-	if (!name) {
-		BT_ERR("Insufficient number of arguments");
-		return -ENOENT;
-	}
-
-	len_rd = sizeof(sub);
-	net_idx = strtol(name, NULL, 16);
-	sub = bt_mesh_cdb_subnet_get(net_idx);
-
-	if (len_rd == 0) {
-		BT_DBG("val (null)");
-		if (!sub) {
-			BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
-			return -ENOENT;
-		}
-
-		BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
-		bt_mesh_cdb_subnet_del(sub, false);
-		return 0;
-	}
-
-	len = sizeof(key);
-	err = settings_bytes_from_str(name, &key, &len);
-	if (err) {
-		BT_ERR("Failed to set \'net-key\'");
-		return err;
-	}
-
-	if (sub) {
-		BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx);
-
-		sub->kr_flag = key.kr_flag;
-		sub->kr_phase = key.kr_phase;
-		memcpy(sub->keys[0].net_key, &key.val[0], 16);
-		memcpy(sub->keys[1].net_key, &key.val[1], 16);
-
-		return 0;
-	}
-
-	sub = bt_mesh_cdb_subnet_alloc(net_idx);
-	if (!sub) {
-		BT_ERR("No space to allocate a new subnet");
-		return -ENOMEM;
-	}
-
-	sub->kr_flag = key.kr_flag;
-	sub->kr_phase = key.kr_phase;
-	memcpy(sub->keys[0].net_key, &key.val[0], 16);
-	memcpy(sub->keys[1].net_key, &key.val[1], 16);
-
-	BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
-
-	return 0;
-}
-
-static int cdb_app_key_set(int argc, char *name)
-{
-	struct bt_mesh_cdb_app_key *app;
-	struct app_key_val key;
-	uint16_t app_idx;
-	int len_rd, err;
-
-	app_idx = strtol(name, NULL, 16);
-	len_rd = sizeof(key);
-
-	if (len_rd == 0) {
-		BT_DBG("val (null)");
-		BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
-
-		app = bt_mesh_cdb_app_key_get(app_idx);
-		if (app) {
-			bt_mesh_cdb_app_key_del(app, false);
-		}
-
-		return 0;
-	}
-
-	err = settings_bytes_from_str(name, &key, &len_rd);
-	if (err) {
-		BT_ERR("Failed to set \'app-key\'");
-		return err;
-	}
-
-	app = bt_mesh_cdb_app_key_get(app_idx);
-	if (!app) {
-		app = bt_mesh_cdb_app_key_alloc(key.net_idx, app_idx);
-	}
-
-	if (!app) {
-		BT_ERR("No space for a new app key");
-		return -ENOMEM;
-	}
-
-	memcpy(app->keys[0].app_key, key.val[0], 16);
-	memcpy(app->keys[1].app_key, key.val[1], 16);
-
-	BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
-
-	return 0;
-}
-
-static int cdb_set(int argc, char **argv, char *name)
-{
-	int len;
-	char *next;
-
-	if (argc < 1) {
-		BT_ERR("Insufficient number of arguments");
-		return -ENOENT;
-	}
-
-	if (!strcmp(name, "Net")) {
-		return cdb_net_set(1, name);
-	}
-
-
-	len = settings_name_next(name, &next);
-
-	if (!next) {
-		BT_ERR("Insufficient number of arguments");
-		return -ENOENT;
-	}
-
-	if (!strncmp(name, "Node", len)) {
-		return cdb_node_set(1, next);
-	}
-
-	if (!strncmp(name, "Subnet", len)) {
-		return cdb_subnet_set(1, next);
-	}
-
-	if (!strncmp(name, "AppKey", len)) {
-		return cdb_app_key_set(1, next);
-	}
-
-	BT_WARN("Unknown module key %s", name);
-	return -ENOENT;
-}
-#endif
-
-const struct mesh_setting {
-	const char *name;
-	int (*func)(int argc, char **argv, char *val);
-} settings[] = {
-	{ "Net", net_set },
-	{ "IV", iv_set },
-	{ "Seq", seq_set },
-	{ "RPL", rpl_set },
-	{ "NetKey", net_key_set },
-	{ "AppKey", app_key_set },
-	{ "HBPub", hb_pub_set },
-	{ "Cfg", cfg_set },
-	{ "s", sig_mod_set },
-	{ "v", vnd_mod_set },
-#if CONFIG_BT_MESH_LABEL_COUNT > 0
-	{ "Va", va_set },
-#endif
-#if MYNEWT_VAL(BLE_MESH_CDB)
-	{ "cdb", cdb_set },
-#endif
-};
-
-static int mesh_set(int argc, char **argv, char *val)
-{
-	int i;
-
-	if (argc < 1) {
-		BT_ERR("Insufficient number of arguments");
-		return -EINVAL;
-	}
-
-	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
-
-	for (i = 0; i < ARRAY_SIZE(settings); i++) {
-		if (!strcmp(settings[i].name, argv[0])) {
-			argc--;
-			argv++;
-
-			return settings[i].func(argc, argv, val);
-		}
-	}
-
-	BT_WARN("No matching handler for key %s", argv[0]);
-
-	return -ENOENT;
-}
-
-static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
-		       bool vnd, bool primary, void *user_data)
-{
-	if (mod->pub && mod->pub->update &&
-	    mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
-		int32_t ms = bt_mesh_model_pub_period_get(mod);
-		if (ms) {
-			BT_DBG("Starting publish timer (period %u ms)",
-			       (unsigned) ms);
-			k_delayed_work_submit(&mod->pub->timer, ms);
-		}
-	}
-
-	if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
-		return;
-	}
-
-	for (int i = 0; i < ARRAY_SIZE(mod->groups); i++) {
-		if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
-			bt_mesh_lpn_group_add(mod->groups[i]);
-		}
-	}
-}
-
-static int mesh_commit(void)
-{
-	if (!bt_mesh_subnet_next(NULL)) {
-		/* Nothing to do since we're not yet provisioned */
-		return 0;
-	}
-
-	if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
-		bt_mesh_proxy_prov_disable(true);
-	}
-
-	if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
-		k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
-	}
-
-	bt_mesh_model_foreach(commit_mod, NULL);
-
-	atomic_set_bit(bt_mesh.flags, BT_MESH_VALID);
-
-	bt_mesh_start();
-
-	return 0;
-}
-
-/* Pending flags that use K_NO_WAIT as the storage timeout */
-#define NO_WAIT_PENDING_BITS (BIT(BT_MESH_NET_PENDING) |           \
-			      BIT(BT_MESH_IV_PENDING) |            \
-			      BIT(BT_MESH_SEQ_PENDING))
-
-/* Pending flags that use CONFIG_BT_MESH_STORE_TIMEOUT */
-#define GENERIC_PENDING_BITS (BIT(BT_MESH_KEYS_PENDING) |          \
-			      BIT(BT_MESH_HB_PUB_PENDING) |        \
-			      BIT(BT_MESH_CFG_PENDING) |           \
-			      BIT(BT_MESH_MOD_PENDING))
-
-static void schedule_store(int flag)
-{
-	int32_t timeout, remaining;
-
-	atomic_set_bit(bt_mesh.flags, flag);
-
-	if (atomic_get(bt_mesh.flags) & NO_WAIT_PENDING_BITS) {
-		timeout = K_NO_WAIT;
-	} else if (atomic_test_bit(bt_mesh.flags, BT_MESH_RPL_PENDING) &&
-		   (!(atomic_get(bt_mesh.flags) & GENERIC_PENDING_BITS) ||
-		    (CONFIG_BT_MESH_RPL_STORE_TIMEOUT <
-		     CONFIG_BT_MESH_STORE_TIMEOUT))) {
-		timeout = K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT);
-	} else {
-		timeout = K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT);
-	}
-
-	remaining = k_delayed_work_remaining_get(&pending_store);
-	if (remaining && remaining < timeout) {
-		BT_DBG("Not rescheduling due to existing earlier deadline");
-		return;
-	}
-
-	BT_DBG("Waiting %d seconds", (int) (timeout / MSEC_PER_SEC));
-
-	k_delayed_work_submit(&pending_store, timeout);
-}
-
-static void clear_iv(void)
-{
-	int err;
-
-	err = settings_save_one("bt_mesh/IV", NULL);
-	if (err) {
-		BT_ERR("Failed to clear IV");
-	} else {
-		BT_DBG("Cleared IV");
-	}
-}
-
-static void clear_net(void)
-{
-	int err;
-
-	err = settings_save_one("bt_mesh/Net", NULL);
-	if (err) {
-		BT_ERR("Failed to clear Network");
-	} else {
-		BT_DBG("Cleared Network");
-	}
-}
-
-static void store_pending_net(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))];
-	struct net_val net;
-	char *str;
-	int err;
-
-	BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(),
-	       bt_hex(bt_mesh.dev_key, 16));
-
-	net.primary_addr = bt_mesh_primary_addr();
-	memcpy(net.dev_key, bt_mesh.dev_key, 16);
-
-	str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode Network as value");
-		return;
-	}
-
-	BT_DBG("Saving Network as value %s", str);
-	err = settings_save_one("bt_mesh/Net", str);
-	if (err) {
-		BT_ERR("Failed to store Network");
-	} else {
-		BT_DBG("Stored Network");
-	}
-}
-
-void bt_mesh_store_net(void)
-{
-	schedule_store(BT_MESH_NET_PENDING);
-}
-
-static void store_pending_iv(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))];
-	struct iv_val iv;
-	char *str;
-	int err;
-
-	iv.iv_index = bt_mesh.iv_index;
-	iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
-	iv.iv_duration = bt_mesh.ivu_duration;
-
-	str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode IV as value");
-		return;
-	}
-
-	BT_DBG("Saving IV as value %s", str);
-	err = settings_save_one("bt_mesh/IV", str);
-	if (err) {
-		BT_ERR("Failed to store IV");
-	} else {
-		BT_DBG("Stored IV");
-	}
-}
-
-void bt_mesh_store_iv(bool only_duration)
-{
-	schedule_store(BT_MESH_IV_PENDING);
-
-	if (!only_duration) {
-		/* Always update Seq whenever IV changes */
-		schedule_store(BT_MESH_SEQ_PENDING);
-	}
-}
-
-static void store_pending_seq(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))];
-	struct seq_val seq;
-	char *str;
-	int err;
-
-	sys_put_le24(bt_mesh.seq, seq.val);
-
-	str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode Seq as value");
-		return;
-	}
-
-	BT_DBG("Saving Seq as value %s", str);
-	err = settings_save_one("bt_mesh/Seq", str);
-	if (err) {
-		BT_ERR("Failed to store Seq");
-	} else {
-		BT_DBG("Stored Seq");
-	}
-}
-
-void bt_mesh_store_seq(void)
-{
-	if (CONFIG_BT_MESH_SEQ_STORE_RATE &&
-	    (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) {
-		return;
-	}
-
-	schedule_store(BT_MESH_SEQ_PENDING);
-}
-
-static void store_rpl(struct bt_mesh_rpl *entry)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))];
-	struct rpl_val rpl;
-	char path[18];
-	char *str;
-	int err;
-
-	BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src,
-	       (unsigned) entry->seq, entry->old_iv);
-
-	rpl.seq = entry->seq;
-	rpl.old_iv = entry->old_iv;
-
-	str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode RPL as value");
-		return;
-	}
-
-	snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src);
-
-	BT_DBG("Saving RPL %s as value %s", path, str);
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store RPL");
-	} else {
-		BT_DBG("Stored RPL");
-	}
-}
-
-static void clear_rpl(struct bt_mesh_rpl *rpl, void *user_data)
-{
-	int err;
-	char path[18];
-
-	if (!rpl->src) {
-		return;
-	}
-
-	snprintk(path, sizeof(path), "bt/mesh/RPL/%x", rpl->src);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear RPL");
-	} else {
-		BT_DBG("Cleared RPL");
-	}
-
-	(void)memset(rpl, 0, sizeof(*rpl));
-}
-
-static void store_pending_rpl(struct bt_mesh_rpl *rpl, void *user_data)
-{
-	BT_DBG("");
-
-	if (rpl->store) {
-		rpl->store = false;
-		store_rpl(rpl);
-	}
-}
-
-static void store_pending_hb_pub(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))];
-	struct bt_mesh_hb_pub pub;
-	struct hb_pub_val val;
-	char *str;
-	int err;
-
-	bt_mesh_hb_pub_get(&pub);
-	if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
-		str = NULL;
-	} else {
-		val.indefinite = (pub.count == 0xffff);
-		val.dst = pub.dst;
-		val.period = bt_mesh_hb_log(pub.period);
-		val.ttl = pub.ttl;
-		val.feat = pub.feat;
-		val.net_idx = pub.net_idx;
-
-		str = settings_str_from_bytes(&val, sizeof(val),
-					      buf, sizeof(buf));
-		if (!str) {
-			BT_ERR("Unable to encode hb pub as value");
-			return;
-		}
-	}
-
-	BT_DBG("Saving Heartbeat Publication as value %s",
-	       str ? str : "(null)");
-	err = settings_save_one("bt_mesh/HBPub", str);
-	if (err) {
-		BT_ERR("Failed to store Heartbeat Publication");
-	} else {
-		BT_DBG("Stored Heartbeat Publication");
-	}
-}
-
-static void store_pending_cfg(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))];
-	struct cfg_val val;
-	char *str;
-	int err;
-
-	val.net_transmit = bt_mesh_net_transmit_get();
-	val.relay = bt_mesh_relay_get();
-	val.relay_retransmit = bt_mesh_relay_retransmit_get();
-	val.beacon = bt_mesh_beacon_enabled();
-	val.gatt_proxy = bt_mesh_gatt_proxy_get();
-	val.frnd = bt_mesh_friend_get();
-	val.default_ttl = bt_mesh_default_ttl_get();
-
-	str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode configuration as value");
-		return;
-	}
-
-	BT_DBG("Saving configuration as value %s", str);
-	err = settings_save_one("bt_mesh/Cfg", str);
-	if (err) {
-		BT_ERR("Failed to store configuration");
-	} else {
-		BT_DBG("Stored configuration");
-	}
-}
-
-static void clear_cfg(void)
-{
-	int err;
-
-	err = settings_save_one("bt_mesh/Cfg", NULL);
-	if (err) {
-		BT_ERR("Failed to clear configuration");
-	} else {
-		BT_DBG("Cleared configuration");
-	}
-}
-
-static void clear_app_key(uint16_t app_idx)
-{
-	char path[20];
-	int err;
-
-	BT_DBG("AppKeyIndex 0x%03x", app_idx);
-
-	snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
-	} else {
-		BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
-	}
-}
-
-static void clear_net_key(uint16_t net_idx)
-{
-	char path[20];
-	int err;
-
-	BT_DBG("NetKeyIndex 0x%03x", net_idx);
-
-	snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx);
-	} else {
-		BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx);
-	}
-}
-
-static void store_subnet(uint16_t net_idx)
-{
-	const struct bt_mesh_subnet *sub;
-	struct net_key_val key;
-	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
-	char path[20];
-	char *str;
-	int err;
-
-	sub = bt_mesh_subnet_get(net_idx);
-	if (!sub) {
-		BT_WARN("NetKeyIndex 0x%03x not found", net_idx);
-		return;
-	}
-
-	BT_DBG("NetKeyIndex 0x%03x", net_idx);
-
-	snprintk(path, sizeof(path), "bt/mesh/NetKey/%x", net_idx);
-
-	memcpy(&key.val[0], sub->keys[0].net, 16);
-	memcpy(&key.val[1], sub->keys[1].net, 16);
-	key.kr_flag = 0U; /* Deprecated */
-	key.kr_phase = sub->kr_phase;
-
-	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode AppKey as value");
-		return;
-	}
-
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store NetKey");
-	} else {
-		BT_DBG("Stored NetKey");
-	}
-}
-
-static void store_app(uint16_t app_idx)
-{
-	const struct bt_mesh_app_key *app;
-	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
-	struct app_key_val key;
-	char path[20];
-	char *str;
-	int err;
-
-	snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
-
-	app = bt_mesh_app_key_get(app_idx);
-	if (!app) {
-		BT_WARN("ApKeyIndex 0x%03x not found", app_idx);
-		return;
-	}
-
-	key.net_idx = app->net_idx,
-	key.updated = app->updated,
-
-	memcpy(key.val[0], app->keys[0].val, 16);
-	memcpy(key.val[1], app->keys[1].val, 16);
-
-	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode AppKey as value");
-		return;
-	}
-
-	snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app->app_idx);
-
-	BT_DBG("Saving AppKey %s as value %s", path, str);
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store AppKey");
-	} else {
-		BT_DBG("Stored AppKey");
-	}
-}
-
-static void store_pending_keys(void)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(key_updates); i++) {
-		struct key_update *update = &key_updates[i];
-
-		if (!update->valid) {
-			continue;
-		}
-
-		if (update->clear) {
-			if (update->app_key) {
-				clear_app_key(update->key_idx);
-			} else {
-				clear_net_key(update->key_idx);
-			}
-		} else {
-			store_subnet(update->key_idx);
-		}
-
-		update->valid = 0;
-	}
-}
-
-#if MYNEWT_VAL(BLE_MESH_CDB)
-static void clear_cdb(void)
-{
-	int err;
-
-	err = settings_save_one("bt/mesh/cdb/Net", NULL);
-	if (err) {
-		BT_ERR("Failed to clear Network");
-	} else {
-		BT_DBG("Cleared Network");
-	}
-}
-
-static void store_pending_cdb(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct cdb_net_val))];
-	struct cdb_net_val net;
-	int err;
-	char *str;
-
-	BT_DBG("");
-
-	net.iv_index = bt_mesh_cdb.iv_index;
-	net.iv_update = atomic_test_bit(bt_mesh_cdb.flags,
-					BT_MESH_CDB_IVU_IN_PROGRESS);
-
-	str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode Network as value");
-		return;
-	}
-	err = settings_save_one("bt/mesh/cdb/Net", str);
-	if (err) {
-		BT_ERR("Failed to store Network value");
-	} else {
-		BT_DBG("Stored Network value");
-	}
-}
-
-static void store_cdb_node(const struct bt_mesh_cdb_node *node)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))];
-	struct node_val val;
-	char path[30];
-	char *str;
-	int err;
-
-	val.net_idx = node->net_idx;
-	val.num_elem = node->num_elem;
-	val.flags = 0;
-
-	if (atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) {
-		val.flags |= F_NODE_CONFIGURED;
-	}
-
-	memcpy(val.uuid, node->uuid, 16);
-	memcpy(val.dev_key, node->dev_key, 16);
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", node->addr);
-
-	str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode Node as value");
-		return;
-	}
-
-
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store Node %s value", path);
-	} else {
-		BT_DBG("Stored Node %s value", path);
-	}
-}
-
-static void clear_cdb_node(uint16_t addr)
-{
-	char path[30];
-	int err;
-
-	BT_DBG("Node 0x%04x", addr);
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", addr);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear Node 0x%04x", addr);
-	} else {
-		BT_DBG("Cleared Node 0x%04x", addr);
-	}
-}
-
-static void store_pending_cdb_nodes(void)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(cdb_node_updates); ++i) {
-		struct node_update *update = &cdb_node_updates[i];
-
-		if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
-			continue;
-		}
-
-		BT_DBG("addr: 0x%04x, clear: %d", update->addr, update->clear);
-
-		if (update->clear) {
-			clear_cdb_node(update->addr);
-		} else {
-			struct bt_mesh_cdb_node *node;
-
-			node = bt_mesh_cdb_node_get(update->addr);
-			if (node) {
-				store_cdb_node(node);
-			} else {
-				BT_WARN("Node 0x%04x not found", update->addr);
-			}
-		}
-
-		update->addr = BT_MESH_ADDR_UNASSIGNED;
-	}
-}
-
-static void store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
-	struct net_key_val key;
-	char path[30];
-	int err;
-	char *str;
-
-	BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx,
-	       bt_hex(sub->keys[0].net_key, 16));
-
-	memcpy(&key.val[0], sub->keys[0].net_key, 16);
-	memcpy(&key.val[1], sub->keys[1].net_key, 16);
-	key.kr_flag = sub->kr_flag;
-	key.kr_phase = sub->kr_phase;
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", sub->net_idx);
-
-
-	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
-	if (!str) {
-		BT_ERR("Unable to encode Subnet as value");
-		return;
-	}
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store Subnet value");
-	} else {
-		BT_DBG("Stored Subnet value");
-	}
-}
-
-static void clear_cdb_subnet(uint16_t net_idx)
-{
-	char path[30];
-	int err;
-
-	BT_DBG("NetKeyIndex 0x%03x", net_idx);
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", net_idx);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx);
-	} else {
-		BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx);
-	}
-}
-
-static void store_cdb_app_key(const struct bt_mesh_cdb_app_key *app)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
-	struct app_key_val key;
-	char path[30];
-	int err;
-	char *str;
-
-	key.net_idx = app->net_idx;
-	key.updated = false;
-	memcpy(key.val[0], app->keys[0].app_key, 16);
-	memcpy(key.val[1], app->keys[1].app_key, 16);
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app->app_idx);
-
-	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
-	err = settings_save_one(path, str);
-	if (err) {
-		BT_ERR("Failed to store AppKey");
-	} else {
-		BT_DBG("Stored AppKey");
-	}
-}
-
-static void clear_cdb_app_key(uint16_t app_idx)
-{
-	char path[30];
-	int err;
-
-	snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app_idx);
-	err = settings_save_one(path, NULL);
-	if (err) {
-		BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
-	} else {
-		BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
-	}
-}
-
-static void store_pending_cdb_keys(void)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
-		struct key_update *update = &cdb_key_updates[i];
-
-		if (!update->valid) {
-			continue;
-		}
-
-		if (update->clear) {
-			if (update->app_key) {
-				clear_cdb_app_key(update->key_idx);
-			} else {
-				clear_cdb_subnet(update->key_idx);
-			}
-		} else {
-			if (update->app_key) {
-				struct bt_mesh_cdb_app_key *key;
-
-				key = bt_mesh_cdb_app_key_get(update->key_idx);
-				if (key) {
-					store_cdb_app_key(key);
-				} else {
-					BT_WARN("AppKeyIndex 0x%03x not found",
-						update->key_idx);
-				}
-			} else {
-				struct bt_mesh_cdb_subnet *sub;
-
-				sub = bt_mesh_cdb_subnet_get(update->key_idx);
-				if (sub) {
-					store_cdb_subnet(sub);
-				} else {
-					BT_WARN("NetKeyIndex 0x%03x not found",
-						update->key_idx);
-				}
-			}
-		}
-
-		update->valid = 0U;
-	}
-}
-
-static struct node_update *cdb_node_update_find(uint16_t addr,
-					       struct node_update **free_slot)
-{
-	struct node_update *match;
-	int i;
-
-	match = NULL;
-	*free_slot = NULL;
-
-	for (i = 0; i < ARRAY_SIZE(cdb_node_updates); i++) {
-		struct node_update *update = &cdb_node_updates[i];
-
-		if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
-			*free_slot = update;
-			continue;
-		}
-
-		if (update->addr == addr) {
-			match = update;
-		}
-	}
-
-	return match;
-}
-#endif
-
-static void encode_mod_path(struct bt_mesh_model *mod, bool vnd,
-			    const char *key, char *path, size_t path_len)
-{
-	uint16_t mod_key = (((uint16_t)mod->elem_idx << 8) | mod->mod_idx);
-
-	if (vnd) {
-		snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key);
-	} else {
-		snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key);
-	}
-}
-
-static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd)
-{
-	uint16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];
-	char buf[BT_SETTINGS_SIZE(sizeof(keys))];
-	char path[20];
-	int i, count, err;
-	char *val;
-
-	for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) {
-		if (mod->keys[i] != BT_MESH_KEY_UNUSED) {
-			keys[count++] = mod->keys[i];
-		}
-	}
-
-	if (count) {
-		val = settings_str_from_bytes(keys, count * sizeof(keys[0]),
-					      buf, sizeof(buf));
-		if (!val) {
-			BT_ERR("Unable to encode model bindings as value");
-			return;
-		}
-	} else {
-		val = NULL;
-	}
-
-	encode_mod_path(mod, vnd, "bind", path, sizeof(path));
-
-	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
-	err = settings_save_one(path, val);
-	if (err) {
-		BT_ERR("Failed to store bind");
-	} else {
-		BT_DBG("Stored bind");
-	}
-}
-
-static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd)
-{
-	uint16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT];
-	char buf[BT_SETTINGS_SIZE(sizeof(groups))];
-	char path[20];
-	int i, count, err;
-	char *val;
-
-	for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) {
-		if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
-			groups[count++] = mod->groups[i];
-		}
-	}
-
-	if (count) {
-		val = settings_str_from_bytes(groups, count * sizeof(groups[0]),
-					      buf, sizeof(buf));
-		if (!val) {
-			BT_ERR("Unable to encode model subscription as value");
-			return;
-		}
-	} else {
-		val = NULL;
-	}
-
-	encode_mod_path(mod, vnd, "sub", path, sizeof(path));
-
-	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
-	err = settings_save_one(path, val);
-	if (err) {
-		BT_ERR("Failed to store sub");
-	} else {
-		BT_DBG("Stored sub");
-	}
-}
-
-static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
-	struct mod_pub_val pub;
-	char path[20];
-	char *val;
-	int err;
-
-	if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
-		val = NULL;
-	} else {
-		pub.addr = mod->pub->addr;
-		pub.key = mod->pub->key;
-		pub.ttl = mod->pub->ttl;
-		pub.retransmit = mod->pub->retransmit;
-		pub.period = mod->pub->period;
-		pub.period_div = mod->pub->period_div;
-		pub.cred = mod->pub->cred;
-
-		val = settings_str_from_bytes(&pub, sizeof(pub),
-					      buf, sizeof(buf));
-		if (!val) {
-			BT_ERR("Unable to encode model publication as value");
-			return;
-		}
-	}
-
-	encode_mod_path(mod, vnd, "pub", path, sizeof(path));
-
-	BT_DBG("Saving %s as %s", path, val ? val : "(null)");
-	err = settings_save_one(path, val);
-	if (err) {
-		BT_ERR("Failed to store pub");
-	} else {
-		BT_DBG("Stored pub");
-	}
-}
-
-static void store_pending_mod(struct bt_mesh_model *mod,
-			      struct bt_mesh_elem *elem, bool vnd,
-			      bool primary, void *user_data)
-{
-	if (!mod->flags) {
-		return;
-	}
-
-	if (mod->flags & BT_MESH_MOD_BIND_PENDING) {
-		mod->flags &= ~BT_MESH_MOD_BIND_PENDING;
-		store_pending_mod_bind(mod, vnd);
-	}
-
-	if (mod->flags & BT_MESH_MOD_SUB_PENDING) {
-		mod->flags &= ~BT_MESH_MOD_SUB_PENDING;
-		store_pending_mod_sub(mod, vnd);
-	}
-
-	if (mod->flags & BT_MESH_MOD_PUB_PENDING) {
-		mod->flags &= ~BT_MESH_MOD_PUB_PENDING;
-		store_pending_mod_pub(mod, vnd);
-	}
-}
-
-#define IS_VA_DEL(_label)	((_label)->ref == 0)
-static void store_pending_va(void)
-{
-	char buf[BT_SETTINGS_SIZE(sizeof(struct va_val))];
-	struct bt_mesh_va *lab;
-	struct va_val va;
-	char path[18];
-	char *val;
-	uint16_t i;
-	int err = 0;
-
-	for (i = 0; (lab = bt_mesh_va_get(i)) != NULL; i++) {
-		if (!lab->changed) {
-			continue;
-		}
-
-		lab->changed = 0U;
-
-		snprintk(path, sizeof(path), "bt_mesh/Va/%x", i);
-
-		if (IS_VA_DEL(lab)) {
-			val = NULL;
-		} else {
-			va.ref = lab->ref;
-			va.addr = lab->addr;
-			memcpy(va.uuid, lab->uuid, 16);
-
-			val = settings_str_from_bytes(&va, sizeof(va),
-						      buf, sizeof(buf));
-			if (!val) {
-				BT_ERR("Unable to encode model publication as value");
-				return;
-			}
-
-			err = settings_save_one(path, val);
-		}
-
-		if (err) {
-			BT_ERR("Failed to %s %s value (err %d)",
-			       IS_VA_DEL(lab) ? "delete" : "store", path, err);
-		} else {
-			BT_DBG("%s %s value",
-			       IS_VA_DEL(lab) ? "Deleted" : "Stored", path);
-		}
-	}
-}
-
-static void store_pending(struct ble_npl_event *work)
-{
-	BT_DBG("");
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_RPL_PENDING)) {
-		if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-			bt_mesh_rpl_foreach(store_pending_rpl, NULL);
-		} else {
-			bt_mesh_rpl_foreach(clear_rpl, NULL);
-		}
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_KEYS_PENDING)) {
-		store_pending_keys();
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NET_PENDING)) {
-		if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-			store_pending_net();
-		} else {
-			clear_net();
-		}
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IV_PENDING)) {
-		if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-			store_pending_iv();
-		} else {
-			clear_iv();
-		}
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) {
-		store_pending_seq();
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_HB_PUB_PENDING)) {
-		store_pending_hb_pub();
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_CFG_PENDING)) {
-		if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
-			store_pending_cfg();
-		} else {
-			clear_cfg();
-		}
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_MOD_PENDING)) {
-		bt_mesh_model_foreach(store_pending_mod, NULL);
-	}
-
-	if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_VA_PENDING)) {
-		store_pending_va();
-	}
-
-#if MYNEWT_VAL(BLE_MESH_CDB)
-	if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
-		if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
-					      BT_MESH_CDB_SUBNET_PENDING)) {
-			if (atomic_test_bit(bt_mesh_cdb.flags,
-					    BT_MESH_CDB_VALID)) {
-				store_pending_cdb();
-			} else {
-				clear_cdb();
-			}
-		}
-
-		if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
-					      BT_MESH_CDB_NODES_PENDING)) {
-			store_pending_cdb_nodes();
-		}
-
-		if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
-					      BT_MESH_CDB_KEYS_PENDING)) {
-			store_pending_cdb_keys();
-		}
-	}
-#endif
-}
-
-void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
-{
-	entry->store = true;
-	schedule_store(BT_MESH_RPL_PENDING);
-}
-
-static struct key_update *key_update_find(bool app_key, uint16_t key_idx,
-					  struct key_update **free_slot)
-{
-	struct key_update *match;
-	int i;
-
-	match = NULL;
-	*free_slot = NULL;
-
-	for (i = 0; i < ARRAY_SIZE(key_updates); i++) {
-		struct key_update *update = &key_updates[i];
-
-		if (!update->valid) {
-			*free_slot = update;
-			continue;
-		}
-
-		if (update->app_key != app_key) {
-			continue;
-		}
-
-		if (update->key_idx == key_idx) {
-			match = update;
-		}
-	}
-
-	return match;
-}
-
-void bt_mesh_store_subnet(uint16_t net_idx)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("NetKeyIndex 0x%03x", net_idx);
-
-	update = key_update_find(false, net_idx, &free_slot);
-	if (update) {
-		update->clear = 0;
-		schedule_store(BT_MESH_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		store_subnet(net_idx);
-		return;
-	}
-
-	free_slot->valid = 1;
-	free_slot->key_idx = net_idx;
-	free_slot->app_key = 0;
-	free_slot->clear = 0;
-
-	schedule_store(BT_MESH_KEYS_PENDING);
-}
-
-void bt_mesh_store_app_key(uint16_t app_idx)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("AppKeyIndex 0x%03x", app_idx);
-
-	update = key_update_find(true, app_idx, &free_slot);
-	if (update) {
-		update->clear = 0;
-		schedule_store(BT_MESH_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		store_app(app_idx);
-		return;
-	}
-
-	free_slot->valid = 1;
-	free_slot->key_idx = app_idx;
-	free_slot->app_key = 1;
-	free_slot->clear = 0;
-
-	schedule_store(BT_MESH_KEYS_PENDING);
-}
-
-void bt_mesh_store_hb_pub(void)
-{
-	schedule_store(BT_MESH_HB_PUB_PENDING);
-}
-
-void bt_mesh_store_cfg(void)
-{
-	schedule_store(BT_MESH_CFG_PENDING);
-}
-
-void bt_mesh_clear_net(void)
-{
-	schedule_store(BT_MESH_NET_PENDING);
-	schedule_store(BT_MESH_IV_PENDING);
-	schedule_store(BT_MESH_CFG_PENDING);
-}
-
-void bt_mesh_clear_subnet(uint16_t net_idx)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("NetKeyIndex 0x%03x", net_idx);
-
-	update = key_update_find(false, net_idx, &free_slot);
-	if (update) {
-		update->clear = 1;
-		schedule_store(BT_MESH_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		clear_net_key(net_idx);
-		return;
-	}
-
-	free_slot->valid = 1;
-	free_slot->key_idx = net_idx;
-	free_slot->app_key = 0;
-	free_slot->clear = 1;
-
-	schedule_store(BT_MESH_KEYS_PENDING);
-}
-
-void bt_mesh_clear_app_key(uint16_t app_idx)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("AppKeyIndex 0x%03x", app_idx);
-
-	update = key_update_find(true, app_idx, &free_slot);
-	if (update) {
-		update->clear = 1;
-		schedule_store(BT_MESH_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		clear_app_key(app_idx);
-		return;
-	}
-
-	free_slot->valid = 1;
-	free_slot->key_idx = app_idx;
-	free_slot->app_key = 1;
-	free_slot->clear = 1;
-
-	schedule_store(BT_MESH_KEYS_PENDING);
-}
-
-void bt_mesh_clear_rpl(void)
-{
-	schedule_store(BT_MESH_RPL_PENDING);
-}
-
-void bt_mesh_store_mod_bind(struct bt_mesh_model *mod)
-{
-	mod->flags |= BT_MESH_MOD_BIND_PENDING;
-	schedule_store(BT_MESH_MOD_PENDING);
-}
-
-void bt_mesh_store_mod_sub(struct bt_mesh_model *mod)
-{
-	mod->flags |= BT_MESH_MOD_SUB_PENDING;
-	schedule_store(BT_MESH_MOD_PENDING);
-}
-
-void bt_mesh_store_mod_pub(struct bt_mesh_model *mod)
-{
-	mod->flags |= BT_MESH_MOD_PUB_PENDING;
-	schedule_store(BT_MESH_MOD_PENDING);
-}
-
-
-void bt_mesh_store_label(void)
-{
-	schedule_store(BT_MESH_VA_PENDING);
-}
-
-#if MYNEWT_VAL(BLE_MESH_CDB)
-static void schedule_cdb_store(int flag)
-{
-	atomic_set_bit(bt_mesh_cdb.flags, flag);
-	k_delayed_work_submit(&pending_store, K_NO_WAIT);
-}
-
-void bt_mesh_store_cdb(void)
-{
-	schedule_cdb_store(BT_MESH_CDB_SUBNET_PENDING);
-}
-
-void bt_mesh_store_cdb_node(const struct bt_mesh_cdb_node *node)
-{
-	struct node_update *update, *free_slot;
-
-	BT_DBG("Node 0x%04x", node->addr);
-
-	update = cdb_node_update_find(node->addr, &free_slot);
-	if (update) {
-		update->clear = false;
-		schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		store_cdb_node(node);
-		return;
-	}
-
-	free_slot->addr = node->addr;
-	free_slot->clear = false;
-
-	schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
-}
-
-void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node)
-{
-	struct node_update *update, *free_slot;
-
-	BT_DBG("Node 0x%04x", node->addr);
-
-	update = cdb_node_update_find(node->addr, &free_slot);
-	if (update) {
-		update->clear = true;
-		schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		clear_cdb_node(node->addr);
-		return;
-	}
-
-	free_slot->addr = node->addr;
-	free_slot->clear = true;
-
-	schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
-}
-
-/* TODO: Could be shared with key_update_find? */
-static struct key_update *cdb_key_update_find(bool app_key, uint16_t key_idx,
-					     struct key_update **free_slot)
-{
-	struct key_update *match;
-	int i;
-
-	match = NULL;
-	*free_slot = NULL;
-
-	for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
-		struct key_update *update = &cdb_key_updates[i];
-
-		if (!update->valid) {
-			*free_slot = update;
-			continue;
-		}
-
-		if (update->app_key != app_key) {
-			continue;
-		}
-
-		if (update->key_idx == key_idx) {
-			match = update;
-		}
-	}
-
-	return match;
-}
-
-void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
-
-	update = cdb_key_update_find(false, sub->net_idx, &free_slot);
-	if (update) {
-		update->clear = 0U;
-		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		store_cdb_subnet(sub);
-		return;
-	}
-
-	free_slot->valid = 1U;
-	free_slot->key_idx = sub->net_idx;
-	free_slot->app_key = 0U;
-	free_slot->clear = 0U;
-
-	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-}
-
-void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
-
-	update = cdb_key_update_find(false, sub->net_idx, &free_slot);
-	if (update) {
-		update->clear = 1U;
-		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		clear_cdb_subnet(sub->net_idx);
-		return;
-	}
-
-	free_slot->valid = 1U;
-	free_slot->key_idx = sub->net_idx;
-	free_slot->app_key = 0U;
-	free_slot->clear = 1U;
-
-	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-}
-
-void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *key)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
-
-	update = cdb_key_update_find(true, key->app_idx, &free_slot);
-	if (update) {
-		update->clear = 0U;
-		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		store_cdb_app_key(key);
-		return;
-	}
-
-	free_slot->valid = 1U;
-	free_slot->key_idx = key->app_idx;
-	free_slot->app_key = 1U;
-	free_slot->clear = 0U;
-
-	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-}
-
-void bt_mesh_clear_cdb_app_key(struct bt_mesh_cdb_app_key *key)
-{
-	struct key_update *update, *free_slot;
-
-	BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
-
-	update = cdb_key_update_find(true, key->app_idx, &free_slot);
-	if (update) {
-		update->clear = 1U;
-		schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-		return;
-	}
-
-	if (!free_slot) {
-		clear_cdb_app_key(key->app_idx);
-		return;
-	}
-
-	free_slot->valid = 1U;
-	free_slot->key_idx = key->app_idx;
-	free_slot->app_key = 1U;
-	free_slot->clear = 1U;
-
-	schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
-}
-#endif
-
-int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
-			     			 const char *name, const void *data,
-							 size_t data_len)
-{
-	char path[30];
-	char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
-	char *val;
-	int err;
-
-	encode_mod_path(mod, vnd, "data", path, sizeof(path));
-	if (name) {
-		strcat(path, "/");
-		strncat(path, name, 8);
-	}
-
-	if (data_len) {
-		val = settings_str_from_bytes(data, data_len,
-					      buf, sizeof(buf));
-		if (!val) {
-			BT_ERR("Unable to encode model publication as value");
-			return -EINVAL;
-		}
-		err = settings_save_one(path, val);
-	} else {
-		err = settings_save_one(path, NULL);
-	}
-
-	if (err) {
-		BT_ERR("Failed to store %s value", path);
-	} else {
-		BT_DBG("Stored %s value", path);
-	}
-	return err;
 }
 
 static struct conf_handler bt_mesh_settings_conf_handler = {
 	.ch_name = "bt_mesh",
 	.ch_get = NULL,
-	.ch_set = mesh_set,
+	.ch_set = NULL,
 	.ch_commit = mesh_commit,
 	.ch_export = NULL,
 };
@@ -2458,7 +210,7 @@ void bt_mesh_settings_init(void)
 	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
 				 "Failed to register bt_mesh_settings conf");
 
-	k_delayed_work_init(&pending_store, store_pending);
+	k_work_init_delayable(&pending_store, store_pending);
 }
 
 #endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */
diff --git a/nimble/host/mesh/src/settings.h b/nimble/host/mesh/src/settings.h
index 9060a14a72..73105fb345 100644
--- a/nimble/host/mesh/src/settings.h
+++ b/nimble/host/mesh/src/settings.h
@@ -3,30 +3,24 @@
  *
  * SPDX-License-Identifier: Apache-2.0
  */
+/* Pending storage actions. */
+enum bt_mesh_settings_flag {
+	BT_MESH_SETTINGS_RPL_PENDING,
+	BT_MESH_SETTINGS_NET_KEYS_PENDING,
+	BT_MESH_SETTINGS_APP_KEYS_PENDING,
+	BT_MESH_SETTINGS_NET_PENDING,
+	BT_MESH_SETTINGS_IV_PENDING,
+	BT_MESH_SETTINGS_SEQ_PENDING,
+	BT_MESH_SETTINGS_HB_PUB_PENDING,
+	BT_MESH_SETTINGS_CFG_PENDING,
+	BT_MESH_SETTINGS_MOD_PENDING,
+	BT_MESH_SETTINGS_VA_PENDING,
+	BT_MESH_SETTINGS_CDB_PENDING,
 
-void bt_mesh_store_net(void);
-void bt_mesh_store_iv(bool only_duration);
-void bt_mesh_store_seq(void);
-void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
-void bt_mesh_store_subnet(uint16_t net_idx);
-void bt_mesh_store_app_key(uint16_t app_idx);
-void bt_mesh_store_hb_pub(void);
-void bt_mesh_store_cfg(void);
-void bt_mesh_store_mod_bind(struct bt_mesh_model *mod);
-void bt_mesh_store_mod_sub(struct bt_mesh_model *mod);
-void bt_mesh_store_mod_pub(struct bt_mesh_model *mod);
-void bt_mesh_store_label(void);
-void bt_mesh_store_cdb(void);
-void bt_mesh_store_cdb_node(const struct bt_mesh_cdb_node *node);
-void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub);
-void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *app);
-
-void bt_mesh_clear_net(void);
-void bt_mesh_clear_subnet(uint16_t net_idx);
-void bt_mesh_clear_app_key(uint16_t app_idx);
-void bt_mesh_clear_rpl(void);
-void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node);
-void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub);
-void bt_mesh_clear_cdb_app_key(struct bt_mesh_cdb_app_key *app);
+	BT_MESH_SETTINGS_FLAG_COUNT,
+};
 
 void bt_mesh_settings_init(void);
+int settings_name_next(char *name, char **next);
+void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag);
+void bt_mesh_settings_store_cancel(enum bt_mesh_settings_flag flag);
diff --git a/nimble/host/mesh/src/shell.c b/nimble/host/mesh/src/shell.c
index 1ac4cafe68..e905231081 100644
--- a/nimble/host/mesh/src/shell.c
+++ b/nimble/host/mesh/src/shell.c
@@ -169,7 +169,7 @@ static struct bt_mesh_cfg_cli cfg_cli = {
 #endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */
 
 #if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
-void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count)
+static void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count)
 {
 	size_t i;
 
@@ -707,10 +707,9 @@ static int check_pub_addr_unassigned(void)
 #ifdef ARCH_sim
 	return 0;
 #else
-	uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 };
+	uint8_t addr[BLE_DEV_ADDR_LEN];
 
-	return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
-		      zero_addr, BLE_DEV_ADDR_LEN) == 0;
+	return ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL) != 0;
 #endif
 }
 
@@ -738,7 +737,7 @@ int cmd_mesh_init(int argc, char *argv[])
 
 	printk("Mesh initialized\n");
 
-	if (IS_ENABLED(CONFIG_SETTINGS)) {
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		settings_load();
 	}
 
@@ -997,78 +996,79 @@ struct shell_cmd_help cmd_timeout_help = {
 
 static int cmd_get_comp(int argc, char *argv[])
 {
-	struct os_mbuf *comp = NET_BUF_SIMPLE(32);
-	uint8_t status, page = 0x00;
+	struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
+	struct bt_mesh_comp_p0_elem elem;
+	struct bt_mesh_comp_p0 comp;
+	uint8_t page = 0x00;
 	int err = 0;
 
 	if (argc > 1) {
 		page = strtol(argv[1], NULL, 0);
 	}
 
-	net_buf_simple_init(comp, 0);
-	err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page,
-					&status, comp);
+	net_buf_simple_init(buf, 0);
+	err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page, &page,
+					buf);
 	if (err) {
 		printk("Getting composition failed (err %d)\n", err);
 		goto done;
 	}
 
-	if (status != 0x00) {
-		printk("Got non-success status 0x%02x\n", status);
+	if (page != 0x00) {
+		printk("Got page 0x%02x. No parser available.",
+			    page);
 		goto done;
 	}
 
-	printk("Got Composition Data for 0x%04x:\n", net.dst);
-	printk("\tCID      0x%04x\n", net_buf_simple_pull_le16(comp));
-	printk("\tPID      0x%04x\n", net_buf_simple_pull_le16(comp));
-	printk("\tVID      0x%04x\n", net_buf_simple_pull_le16(comp));
-	printk("\tCRPL     0x%04x\n", net_buf_simple_pull_le16(comp));
-	printk("\tFeatures 0x%04x\n", net_buf_simple_pull_le16(comp));
-
-	while (comp->om_len > 4) {
-		uint8_t sig, vnd;
-		uint16_t loc;
-		int i;
-
-		loc = net_buf_simple_pull_le16(comp);
-		sig = net_buf_simple_pull_u8(comp);
-		vnd = net_buf_simple_pull_u8(comp);
+	err = bt_mesh_comp_p0_get(&comp, buf);
+	if (err) {
+		printk("Getting composition failed (err %d)\n", err);
+		goto done;
+	}
 
-		printk("\n\tElement @ 0x%04x:\n", loc);
+	printk("Got Composition Data for 0x%04x:", net.dst);
+	printk("\tCID      0x%04x", comp.cid);
+	printk("\tPID      0x%04x", comp.pid);
+	printk("\tVID      0x%04x", comp.vid);
+	printk("\tCRPL     0x%04x", comp.crpl);
+	printk("\tFeatures 0x%04x", comp.feat);
 
-		if (comp->om_len < ((sig * 2) + (vnd * 4))) {
-			printk("\t\t...truncated data!\n");
-			break;
-		}
+	while (bt_mesh_comp_p0_elem_pull(&comp, &elem)) {
+		int i;
 
-		if (sig) {
+		printk("\tElement @ 0x%04x:", elem.loc);
+		if (elem.nsig) {
 			printk("\t\tSIG Models:\n");
 		} else {
 			printk("\t\tNo SIG Models\n");
 		}
 
-		for (i = 0; i < sig; i++) {
-			uint16_t mod_id = net_buf_simple_pull_le16(comp);
+		for (i = 0; i < elem.nsig; i++) {
+			uint16_t mod_id = bt_mesh_comp_p0_elem_mod(&elem, i);
 
 			printk("\t\t\t0x%04x\n", mod_id);
 		}
 
-		if (vnd) {
+		if (elem.nvnd) {
 			printk("\t\tVendor Models:\n");
 		} else {
 			printk("\t\tNo Vendor Models\n");
 		}
 
-		for (i = 0; i < vnd; i++) {
-			uint16_t cid = net_buf_simple_pull_le16(comp);
-			uint16_t mod_id = net_buf_simple_pull_le16(comp);
+		for (i = 0; i < elem.nvnd; i++) {
+			struct bt_mesh_mod_id_vnd mod =
+				bt_mesh_comp_p0_elem_mod_vnd(&elem, i);
 
-			printk("\t\t\tCompany 0x%04x: 0x%04x\n", cid, mod_id);
+			printk("\t\t\tCompany 0x%04x: 0x%04x",
+				    mod.company, mod.id);
 		}
 	}
+	if (buf->om_len) {
+		printk("\t\t...truncated data!");
+	}
 
 done:
-	os_mbuf_free_chain(comp);
+	os_mbuf_free_chain(buf);
 	return err;
 }
 
@@ -2001,6 +2001,7 @@ static int mod_pub_set(uint16_t addr, uint16_t mod_id, uint16_t cid, char *argv[
 	int err;
 
 	pub.addr = strtoul(argv[0], NULL, 0);
+	pub.uuid = NULL;
 	pub.app_idx = strtoul(argv[1], NULL, 0);
 	pub.cred_flag = str2bool(argv[2]);
 	pub.ttl = strtoul(argv[3], NULL, 0);
@@ -2345,7 +2346,7 @@ static int cmd_provision(int argc, char *argv[])
 			return 0;
 		}
 
-		net_key = sub->keys[sub->kr_flag].net_key;
+		net_key = sub->keys[SUBNET_KEY_TX_IDX(sub)].net_key;
 	}
 
 	err = bt_mesh_provision(net_key, net_idx, 0, iv_index, addr,
@@ -2905,8 +2906,8 @@ static int cmd_cdb_node_add(int argc, char *argv[])
 
 	memcpy(node->dev_key, dev_key, 16);
 
-	if (IS_ENABLED(CONFIG_SETTINGS)) {
-		bt_mesh_store_cdb_node(node);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_cdb_node_store(node);
 	}
 
 	printk("Added node 0x%04x", addr);
@@ -2959,8 +2960,8 @@ static int cmd_cdb_subnet_add(int argc,
 
 	memcpy(sub->keys[0].net_key, net_key, 16);
 
-	if (IS_ENABLED(CONFIG_SETTINGS)) {
-		bt_mesh_store_cdb_subnet(sub);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_cdb_subnet_store(sub);
 	}
 
 	printk("Added Subnet 0x%03x", net_idx);
@@ -3015,8 +3016,8 @@ static int cmd_cdb_app_key_add(int argc,
 
 	memcpy(key->keys[0].app_key, app_key, 16);
 
-	if (IS_ENABLED(CONFIG_SETTINGS)) {
-		bt_mesh_store_cdb_app_key(key);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_cdb_app_key_store(key);
 	}
 
 	printk("Added AppKey 0x%03x", app_idx);
diff --git a/nimble/host/mesh/src/subnet.c b/nimble/host/mesh/src/subnet.c
index d6fcfbeee7..9108cc471f 100644
--- a/nimble/host/mesh/src/subnet.c
+++ b/nimble/host/mesh/src/subnet.c
@@ -4,7 +4,7 @@
  *
  * SPDX-License-Identifier: Apache-2.0
  */
-
+#include <stdlib.h>
 #include "syscfg/syscfg.h"
 #define MESH_LOG_MODULE BLE_MESH_NET_KEYS_LOG
 
@@ -26,6 +26,26 @@
 #include "settings.h"
 #include "prov.h"
 
+/* Tracking of what storage changes are pending for Net Keys. We track this in
+ * a separate array here instead of within the respective bt_mesh_subnet
+ * struct itselve, since once a key gets deleted its struct becomes invalid
+ * and may be reused for other keys.
+ */
+struct net_key_update {
+	uint16_t key_idx:12,    /* NetKey Index */
+		 valid:1,       /* 1 if this entry is valid, 0 if not */
+		 clear:1;       /* 1 if key needs clearing, 0 if storing */
+};
+
+/* NetKey storage information */
+struct net_key_val {
+	uint8_t kr_flag:1,
+		kr_phase:7;
+	uint8_t val[2][16];
+} __packed;
+
+static struct net_key_update net_key_updates[CONFIG_BT_MESH_SUBNET_COUNT];
+
 #ifdef CONFIG_BT_MESH_GATT_PROXY
 void (*bt_mesh_subnet_cb_list[5]) (struct bt_mesh_subnet *sub,
 									      enum bt_mesh_key_evt evt);
@@ -51,6 +71,91 @@ static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
 	}
 }
 
+static void clear_net_key(uint16_t net_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	char path[20];
+	int err;
+
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+	snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx);
+	err = settings_save_one(path, NULL);
+	if (err) {
+		BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx);
+	} else {
+		BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx);
+	}
+#endif
+}
+
+static void store_subnet(uint16_t net_idx)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	const struct bt_mesh_subnet *sub;
+	struct net_key_val key;
+	char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
+	char path[20];
+	char *str;
+	int err;
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		BT_WARN("NetKeyIndex 0x%03x not found", net_idx);
+		return;
+	}
+
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+	snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx);
+
+	memcpy(&key.val[0], sub->keys[0].net, 16);
+	memcpy(&key.val[1], sub->keys[1].net, 16);
+	key.kr_flag = 0U; /* Deprecated */
+	key.kr_phase = sub->kr_phase;
+
+	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+	if (!str) {
+		BT_ERR("Unable to encode AppKey as value");
+		return;
+	}
+
+	err = settings_save_one(path, str);
+	if (err) {
+		BT_ERR("Failed to store NetKey");
+	} else {
+		BT_DBG("Stored NetKey");
+	}
+#endif
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct net_key_update *net_key_update_find(uint16_t key_idx,
+						  struct net_key_update **free_slot)
+{
+	struct net_key_update *match;
+	int i;
+
+	match = NULL;
+	*free_slot = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(net_key_updates); i++) {
+		struct net_key_update *update = &net_key_updates[i];
+
+		if (!update->valid) {
+			*free_slot = update;
+			continue;
+		}
+
+		if (update->key_idx == key_idx) {
+			match = update;
+		}
+	}
+
+	return match;
+}
+#endif
+
 uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
 {
 	uint8_t flags = 0x00;
@@ -66,6 +171,46 @@ uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
 	return flags;
 }
 
+static void update_subnet_settings(uint16_t net_idx, bool store)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	struct net_key_update *update, *free_slot;
+	uint8_t clear = store ? 0U : 1U;
+
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+	update = net_key_update_find(net_idx, &free_slot);
+	if (update) {
+		update->clear = clear;
+		bt_mesh_settings_store_schedule(
+			BT_MESH_SETTINGS_NET_KEYS_PENDING);
+		return;
+	}
+
+	if (!free_slot) {
+		if (store) {
+			store_subnet(net_idx);
+		} else {
+			clear_net_key(net_idx);
+		}
+		return;
+	}
+
+	free_slot->valid = 1U;
+	free_slot->key_idx = net_idx;
+	free_slot->clear = clear;
+
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_NET_KEYS_PENDING);
+#endif
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+void bt_mesh_subnet_store(uint16_t net_idx)
+{
+	update_subnet_settings(net_idx, true);
+}
+#endif
+
 static void key_refresh(struct bt_mesh_subnet *sub, uint8_t new_phase)
 {
 	BT_DBG("Phase 0x%02x -> 0x%02x", sub->kr_phase, new_phase);
@@ -97,7 +242,7 @@ static void key_refresh(struct bt_mesh_subnet *sub, uint8_t new_phase)
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		BT_DBG("Storing Updated NetKey persistently");
-		bt_mesh_store_subnet(sub->net_idx);
+		bt_mesh_subnet_store(sub->net_idx);
 	}
 }
 
@@ -139,7 +284,7 @@ static struct bt_mesh_subnet *subnet_alloc(uint16_t net_idx)
 static void subnet_del(struct bt_mesh_subnet *sub)
 {
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_clear_subnet(sub->net_idx);
+		update_subnet_settings(sub->net_idx, false);
 	}
 
 	bt_mesh_net_loopback_clear(sub->net_idx);
@@ -242,7 +387,7 @@ uint8_t bt_mesh_subnet_add(uint16_t net_idx, const uint8_t key[16])
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
 		BT_DBG("Storing NetKey persistently");
-		bt_mesh_store_subnet(sub->net_idx);
+		bt_mesh_subnet_store(sub->net_idx);
 	}
 
 	return STATUS_SUCCESS;
@@ -664,3 +809,77 @@ bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct os_mbuf *in,
 
 	return false;
 }
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static int net_key_set(int argc, char **argv, char *val)
+{
+	struct net_key_val key;
+	int len, err;
+	uint16_t net_idx;
+
+	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+	net_idx = strtol(argv[0], NULL, 16);
+
+	len = sizeof(key);
+	err = settings_bytes_from_str(val, &key, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return err;
+	}
+
+	if (len != sizeof(key)) {
+		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key));
+		return -EINVAL;
+	}
+
+	BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
+
+	return bt_mesh_subnet_set(
+		net_idx, key.kr_phase, key.val[0],
+		(key.kr_phase != BT_MESH_KR_NORMAL) ? key.val[1] : NULL);
+}
+#endif
+
+void bt_mesh_subnet_pending_store(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(net_key_updates); i++) {
+		struct net_key_update *update = &net_key_updates[i];
+
+		if (!update->valid) {
+			continue;
+		}
+
+		if (update->clear) {
+			clear_net_key(update->key_idx);
+		} else {
+			store_subnet(update->key_idx);
+		}
+
+		update->valid = 0U;
+	}
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct conf_handler bt_mesh_net_key_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = net_key_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+};
+#endif
+
+void bt_mesh_net_key_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_net_key_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_net_key conf");
+#endif
+}
diff --git a/nimble/host/mesh/src/subnet.h b/nimble/host/mesh/src/subnet.h
index 154b5d4e31..b64177d364 100644
--- a/nimble/host/mesh/src/subnet.h
+++ b/nimble/host/mesh/src/subnet.h
@@ -37,7 +37,7 @@ struct bt_mesh_subnet {
 	uint8_t  beacons_last;       /* Number of beacons during last
 				      * observation window
 				      */
-	uint8_t  beacons_cur;        /* Number of beaconds observed during
+	uint8_t  beacons_cur;        /* Number of beacons observed during
 				      * currently ongoing window.
 				      */
 
@@ -194,4 +194,13 @@ bt_mesh_subnet_has_new_key(const struct bt_mesh_subnet *sub)
 	return sub->kr_phase != BT_MESH_KR_NORMAL;
 }
 
-#endif /* _BLUETOOTH_MESH_SUBNET_H_ */
\ No newline at end of file
+/** @brief Store the Subnet information in persistent storage.
+ *
+ * @param net_idx Network index to store.
+ */
+void bt_mesh_subnet_store(uint16_t net_idx);
+
+/** @brief Store the pending Subnets in persistent storage. */
+void bt_mesh_subnet_pending_store(void);
+void bt_mesh_net_key_init(void);
+#endif /* _BLUETOOTH_MESH_SUBNET_H_ */
diff --git a/nimble/host/mesh/src/testing.c b/nimble/host/mesh/src/testing.c
index dfe8d18e3d..7ee11a1373 100644
--- a/nimble/host/mesh/src/testing.c
+++ b/nimble/host/mesh/src/testing.c
@@ -135,8 +135,6 @@ void bt_test_print_credentials(void)
 		console_printf("Subnet: %d\n", i);
 		console_printf("\tNetKeyIdx: %04x\n",
 			       sub->net_idx);
-		console_printf("\tNetKey: %s\n",
-			       bt_hex(sub->keys[sub->kr_flag].net_key, 16));
 	}
 
 	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i)
@@ -154,8 +152,6 @@ void bt_test_print_credentials(void)
 			       app_key->net_idx);
 		console_printf("\tAppKeyIdx: %04x\n",
 			       app_key->app_idx);
-		console_printf("\tAppKey: %s\n",
-			       bt_hex(app_key->keys[sub->kr_flag].app_key, 16));
 	}
 
 	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i)
diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c
index f3a7c2d601..d563c9fa05 100644
--- a/nimble/host/mesh/src/transport.c
+++ b/nimble/host/mesh/src/transport.c
@@ -11,6 +11,7 @@
 
 #include <errno.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "mesh/mesh.h"
 #include "mesh/glue.h"
@@ -69,6 +70,20 @@
 /* How long to wait for available buffers before giving up */
 #define BUF_TIMEOUT                 K_NO_WAIT
 
+struct virtual_addr {
+	uint16_t ref:15,
+		 changed:1;
+	uint16_t addr;
+	uint8_t  uuid[16];
+};
+
+/* Virtual Address information for persistent storage. */
+struct va_val {
+	uint16_t ref;
+	uint16_t addr;
+	uint8_t  uuid[16];
+} __packed;
+
 static struct seg_tx {
 	struct bt_mesh_subnet *sub;
 	void                  *seg[CONFIG_BT_MESH_TX_SEG_MAX];
@@ -92,7 +107,7 @@ static struct seg_tx {
 			      		  friend_cred:1; /* Using Friend credentials */
 	const struct bt_mesh_send_cb *cb;
 	void                  *cb_data;
-	struct k_delayed_work retransmit; /* Retransmit timer */
+	struct k_work_delayable retransmit; /* Retransmit timer */
 } seg_tx[MYNEWT_VAL(BLE_MESH_TX_SEG_MSG_COUNT)];
 
 static struct seg_rx {
@@ -110,20 +125,21 @@ static struct seg_rx {
 	uint8_t                     ttl;
 	uint32_t                    block;
 	uint32_t                    last;
-	struct k_delayed_work    ack;
+	struct k_work_delayable    ack;
 } seg_rx[CONFIG_BT_MESH_RX_SEG_MSG_COUNT];
 
-char _k_mem_slab_buffer_[(BT_MESH_APP_SEG_SDU_MAX*CONFIG_BT_MESH_SEG_BUFS)];
+
+char _k_mem_slab_buffer_[OS_ALIGN((BT_MESH_APP_SEG_SDU_MAX)*(CONFIG_BT_MESH_SEG_BUFS), OS_ALIGNMENT)];
 
 struct k_mem_slab segs = {
 	.num_blocks = CONFIG_BT_MESH_SEG_BUFS,
-	.block_size = BT_MESH_APP_SEG_SDU_MAX,
+	.block_size = OS_ALIGN(BT_MESH_APP_SEG_SDU_MAX, OS_ALIGNMENT),
 	.buffer = _k_mem_slab_buffer_,
 	.free_list = NULL,
 	.num_used = 0
 };
 
-static struct bt_mesh_va virtual_addrs[CONFIG_BT_MESH_LABEL_COUNT];
+static struct virtual_addr virtual_addrs[CONFIG_BT_MESH_LABEL_COUNT];
 
 static int send_unseg(struct bt_mesh_net_tx *tx, struct os_mbuf *sdu,
 		      const struct bt_mesh_send_cb *cb, void *cb_data,
@@ -232,7 +248,7 @@ static void seg_tx_unblock_check(struct seg_tx *tx)
 		BT_DBG("Unblocked 0x%04x",
 		       (uint16_t)(blocked->seq_auth & TRANS_SEQ_ZERO_MASK));
 		blocked->blocked = false;
-		k_delayed_work_submit(&blocked->retransmit, 0);
+		k_work_reschedule(&blocked->retransmit, 0);
 	}
 }
 
@@ -240,7 +256,8 @@ static void seg_tx_reset(struct seg_tx *tx)
 {
 	int i;
 
-	k_delayed_work_cancel(&tx->retransmit);
+	/* If this call fails, the handler will exit early, as nack_count is 0. */
+	(void)k_work_cancel_delayable(&tx->retransmit);
 
 	tx->cb = NULL;
 	tx->cb_data = NULL;
@@ -301,8 +318,9 @@ static void schedule_retransmit(struct seg_tx *tx)
 	 * called this from inside bt_mesh_net_send), we should continue the
 	 * retransmit immediately, as we just freed up a tx buffer.
 	 */
-	k_delayed_work_submit(&tx->retransmit,
-			      tx->seg_o ? 0 : K_MSEC(SEG_RETRANSMIT_TIMEOUT(tx)));
+	k_work_reschedule(&tx->retransmit,
+			  tx->seg_o ? K_NO_WAIT :
+			  K_MSEC(SEG_RETRANSMIT_TIMEOUT(tx)));
 }
 
 static void seg_send_start(uint16_t duration, int err, void *user_data)
@@ -423,8 +441,8 @@ static void seg_tx_send_unacked(struct seg_tx *tx)
 	tx->attempts--;
 end:
 	if (!tx->seg_pending) {
-		k_delayed_work_submit(&tx->retransmit,
-					  SEG_RETRANSMIT_TIMEOUT(tx));
+		k_work_reschedule(&tx->retransmit,
+				  K_MSEC(SEG_RETRANSMIT_TIMEOUT(tx)));
 	}
 
 	tx->sending = 0U;
@@ -614,12 +632,12 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg,
 		return -EINVAL;
 	}
 
-	if (msg->om_len > BT_MESH_TX_SDU_MAX) {
-		BT_ERR("Not enough segment buffers for length %u", msg->om_len);
+	if (msg->om_len > BT_MESH_TX_SDU_MAX - BT_MESH_MIC_SHORT) {
+		BT_ERR("Message too big: %u", msg->om_len);
 		return -EMSGSIZE;
 	}
 
-	if (net_buf_simple_tailroom(msg) < 4) {
+	if (net_buf_simple_tailroom(msg) < BT_MESH_MIC_SHORT) {
 		BT_ERR("Insufficient tailroom for Transport MIC");
 		return -EINVAL;
 	}
@@ -642,15 +660,15 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg,
 		return -EINVAL;
 	}
 
-	BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->sub->net_idx,
-	       tx->ctx->app_idx, tx->ctx->addr);
-	BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
-
 	err = bt_mesh_keys_resolve(tx->ctx, &tx->sub, &key, &aid);
 	if (err) {
 		return err;
 	}
 
+	BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->sub->net_idx,
+	       tx->ctx->app_idx, tx->ctx->addr);
+	BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
+
 	tx->xmit = bt_mesh_net_transmit_get();
 	tx->aid = aid;
 
@@ -844,8 +862,6 @@ static int trans_ack(struct bt_mesh_net_rx *rx, uint8_t hdr,
 		return -EINVAL;
 	}
 
-	k_delayed_work_cancel(&tx->retransmit);
-
 	while ((bit = find_lsb_set(ack))) {
 		if (tx->seg[bit - 1]) {
 			BT_DBG("seg %u/%u acked", bit - 1, tx->seg_n);
@@ -856,7 +872,11 @@ static int trans_ack(struct bt_mesh_net_rx *rx, uint8_t hdr,
 	}
 
 	if (tx->nack_count) {
-		seg_tx_send_unacked(tx);
+		/* According to the Bluetooth Mesh Profile specification,
+		 * section 3.5.3.3, we should reset the retransmit timer and
+		 * retransmit immediately when receiving a valid ack message:
+		 */
+		k_work_reschedule(&tx->retransmit, K_NO_WAIT);
 	} else {
 		BT_DBG("SDU TX complete");
 		seg_tx_complete(tx, 0);
@@ -1078,7 +1098,10 @@ static void seg_rx_reset(struct seg_rx *rx, bool full_reset)
 
 	BT_DBG("rx %p", rx);
 
-	k_delayed_work_cancel(&rx->ack);
+	/* If this fails, the handler will exit early on the next execution, as
+	 * it checks rx->in_use.
+	 */
+	(void)k_work_cancel_delayable(&rx->ack);
 
 	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->obo &&
 	    rx->block != BLOCK_COMPLETE(rx->seg_n)) {
@@ -1115,6 +1138,16 @@ static void seg_ack(struct ble_npl_event *work)
 	struct seg_rx *rx = ble_npl_event_get_arg(work);
 	int32_t timeout;
 
+	if (!rx->in_use || rx->block == BLOCK_COMPLETE(rx->seg_n)) {
+		/* Cancellation of this timer may have failed. If it fails as
+		 * part of seg_reset, in_use will be false.
+		 * If it fails as part of the processing of a fully received
+		 * SDU, the ack is already being sent from the receive handler,
+		 * and the timer based ack sending can be ignored.
+		 */
+		return;
+	}
+
 	BT_DBG("rx %p", rx);
 
 	if (k_uptime_get_32() - rx->last > K_SECONDS(60)) {
@@ -1132,7 +1165,7 @@ static void seg_ack(struct ble_npl_event *work)
 		 rx->block, rx->obo);
 
 	timeout = ack_timeout(rx);
-	k_delayed_work_submit(&rx->ack, K_MSEC(timeout));
+	k_work_schedule(&rx->ack, K_MSEC(timeout));
 }
 
 static inline bool sdu_len_is_ok(bool ctl, uint8_t seg_n)
@@ -1247,6 +1280,7 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
 	struct seg_rx *rx;
 	uint8_t *hdr = buf->om_data;
 	uint16_t seq_zero;
+	uint32_t auth_seqnum;
 	uint8_t seg_n;
 	uint8_t seg_o;
 	int err;
@@ -1298,6 +1332,7 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
 			      ((((net_rx->seq & BIT_MASK(14)) - seq_zero)) &
 			       BIT_MASK(13))));
 
+	auth_seqnum = *seq_auth & BIT_MASK(24);
 	*seg_count = seg_n + 1;
 
 	/* Look for old RX sessions */
@@ -1366,6 +1401,28 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
 		return -ENOBUFS;
 	}
 
+	/* Keep track of the received SeqAuth values received from this address
+	 * and discard segmented messages that are not newer, as described in
+	 * the Bluetooth Mesh specification section 3.5.3.4.
+	 *
+	 * The logic on the first segmented receive is a bit special, since the
+	 * initial value of rpl->seg is 0, which would normally fail the
+	 * comparison check with auth_seqnum:
+	 * - If this is the first time we receive from this source, rpl->src
+	 *   will be 0, and we can skip this check.
+	 * - If this is the first time we receive from this source on the new IV
+	 *   index, rpl->old_iv will be set, and the check is also skipped.
+	 * - If this is the first segmented message on the new IV index, but we
+	 *   have received an unsegmented message already, the unsegmented
+	 *   message will have reset rpl->seg to 0, and this message's SeqAuth
+	 *   cannot be zero.
+	 */
+	if (rpl && rpl->src && auth_seqnum <= rpl->seg &&
+	(!rpl->old_iv || net_rx->old_iv)) {
+		BT_WARN("Ignoring old SeqAuth 0x%06x", auth_seqnum);
+		return -EALREADY;
+	}
+
 	/* Look for free slot for a new RX session */
 	rx = seg_rx_alloc(net_rx, hdr, seq_auth, seg_n);
 	if (!rx) {
@@ -1413,11 +1470,10 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
 	/* Reset the Incomplete Timer */
 	rx->last = k_uptime_get_32();
 
-	if (!k_delayed_work_remaining_get(&rx->ack) &&
-	    !bt_mesh_lpn_established()) {
+	if (!bt_mesh_lpn_established()) {
 		int32_t timeout = ack_timeout(rx);
-
-		k_delayed_work_submit(&rx->ack, K_MSEC(timeout));
+		/* Should only start ack timer if it isn't running already: */
+		k_work_schedule(&rx->ack, K_MSEC(timeout));
 	}
 
 	/* Allocated segment here */
@@ -1444,11 +1500,20 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
 
 	if (rpl) {
 		bt_mesh_rpl_update(rpl, net_rx);
+		/* Update the seg, unless it has already been surpassed:
+		 * This needs to happen after rpl_update to ensure that the IV
+		 * update reset logic inside rpl_update doesn't overwrite the
+		 * change.
+		 */
+		rpl->seg = MAX(rpl->seg, auth_seqnum);
 	}
 
 	*pdu_type = BT_MESH_FRIEND_PDU_COMPLETE;
 
-	k_delayed_work_cancel(&rx->ack);
+	/* If this fails, the work handler will either exit early because the
+	 * block is fully received, or rx->in_use is false.
+	 */
+	(void)k_work_cancel_delayable(&rx->ack);
 	send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
 		 net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo);
 
@@ -1540,20 +1605,8 @@ int bt_mesh_trans_recv(struct os_mbuf *buf, struct bt_mesh_net_rx *rx)
 		err = trans_unseg(buf, rx, &seq_auth);
 	}
 
-	/* Notify LPN state machine so a Friend Poll will be sent. If the
-	 * message was a Friend Update it's possible that a Poll was already
-	 * queued for sending, however that's fine since then the
-	 * bt_mesh_lpn_waiting_update() function will return false:
-	 * we still need to go through the actual sending to the bearer and
-	 * wait for ReceiveDelay before transitioning to WAIT_UPDATE state.
-	 * Another situation where we want to notify the LPN state machine
-	 * is if it's configured to use an automatic Friendship establishment
-	 * timer, in which case we want to reset the timer at this point.
-	 *
-	 */
-	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
-	    (bt_mesh_lpn_timer() ||
-	     (bt_mesh_lpn_established() && bt_mesh_lpn_waiting_update()))) {
+	/* Notify LPN state machine so a Friend Poll will be sent. */
+	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
 		bt_mesh_lpn_msg_received(rx);
 	}
 
@@ -1583,6 +1636,13 @@ void bt_mesh_rx_reset(void)
 	}
 }
 
+static void store_va_label(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_VA_PENDING);
+#endif
+}
+
 void bt_mesh_trans_reset(void)
 {
 	int i;
@@ -1605,7 +1665,7 @@ void bt_mesh_trans_reset(void)
 	bt_mesh_rpl_clear();
 
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_label();
+		store_va_label();
 	}
 }
 
@@ -1616,43 +1676,34 @@ void bt_mesh_trans_init(void)
 	/* We need to initialize memslab free list here */
 	rc = create_free_list(&segs);
 	if (rc) {
-		BT_ERR("Failed to create free memslab list")
+		BT_ERR("Failed to create free memslab list (error: %d)", rc);
 	}
 
 	for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
-		k_delayed_work_init(&seg_tx[i].retransmit, seg_retransmit);
-		k_delayed_work_add_arg(&seg_tx[i].retransmit, &seg_tx[i]);
+		k_work_init_delayable(&seg_tx[i].retransmit, seg_retransmit);
+		k_work_add_arg_delayable(&seg_tx[i].retransmit, &seg_tx[i]);
 	}
 
 	/* XXX Probably we need mempool for that.
 	 *  For now we increase MSYS_1_BLOCK_COUNT
 	 */
 	for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
-		k_delayed_work_init(&seg_rx[i].ack, seg_ack);
-		k_delayed_work_add_arg(&seg_rx[i].ack, &seg_rx[i]);
-	}
-}
-
-struct bt_mesh_va *bt_mesh_va_get(uint16_t index)
-{
-	if (index >= ARRAY_SIZE(virtual_addrs)) {
-		return NULL;
+		k_work_init_delayable(&seg_rx[i].ack, seg_ack);
+		k_work_add_arg_delayable(&seg_rx[i].ack, &seg_rx[i]);
 	}
-
-	return &virtual_addrs[index];
 }
 
-static inline void va_store(struct bt_mesh_va *store)
+static inline void va_store(struct virtual_addr *store)
 {
 	store->changed = 1U;
 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		bt_mesh_store_label();
+		store_va_label();
 	}
 }
 
-uint8_t bt_mesh_va_add(uint8_t uuid[16], uint16_t *addr)
+uint8_t bt_mesh_va_add(const uint8_t uuid[16], uint16_t *addr)
 {
-	struct bt_mesh_va *va = NULL;
+	struct virtual_addr *va = NULL;
 	int err;
 
 	for (int i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
@@ -1692,9 +1743,9 @@ uint8_t bt_mesh_va_add(uint8_t uuid[16], uint16_t *addr)
 	return STATUS_SUCCESS;
 }
 
-uint8_t bt_mesh_va_del(uint8_t uuid[16], uint16_t *addr)
+uint8_t bt_mesh_va_del(const uint8_t uuid[16], uint16_t *addr)
 {
-	struct bt_mesh_va *va = NULL;
+	struct virtual_addr *va = NULL;
 
 	for (int i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
 		if (virtual_addrs[i].ref &&
@@ -1718,19 +1769,6 @@ uint8_t bt_mesh_va_del(uint8_t uuid[16], uint16_t *addr)
 	return STATUS_SUCCESS;
 }
 
-struct bt_mesh_va *bt_mesh_va_find(uint8_t uuid[16])
-{
-	for (int i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
-		if (virtual_addrs[i].ref &&
-		    !memcmp(uuid, virtual_addrs[i].uuid,
-			    ARRAY_SIZE(virtual_addrs[i].uuid))) {
-			return &virtual_addrs[i];
-		}
-	}
-
-	return NULL;
-}
-
 uint8_t *bt_mesh_va_label_get(uint16_t addr)
 {
 	int i;
@@ -1749,3 +1787,140 @@ uint8_t *bt_mesh_va_label_get(uint16_t addr)
 
 	return NULL;
 }
+
+#if CONFIG_BT_MESH_LABEL_COUNT > 0
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+static struct virtual_addr *bt_mesh_va_get(uint16_t index)
+{
+	if (index >= ARRAY_SIZE(virtual_addrs)) {
+		return NULL;
+	}
+
+	return &virtual_addrs[index];
+}
+
+static int va_set(int argc, char **argv, char *val)
+{
+	struct va_val va;
+	struct virtual_addr *lab;
+	uint16_t index;
+	int len, err;
+
+	if (argc < 1) {
+			BT_ERR("Insufficient number of arguments");
+			return -ENOENT;
+	}
+
+	index = strtol(argv[0], NULL, 16);
+
+	if (val == NULL) {
+		BT_WARN("Mesh Virtual Address length = 0");
+		return 0;
+	}
+
+	err = settings_bytes_from_str(val, &va, &len);
+	if (err) {
+		BT_ERR("Failed to decode value %s (err %d)", val, err);
+		return -EINVAL;
+	}
+
+	if (len != sizeof(struct va_val)) {
+		BT_ERR("Invalid length for virtual address");
+		return -EINVAL;
+	}
+
+	if (va.ref == 0) {
+		BT_WARN("Ignore Mesh Virtual Address ref = 0");
+		return 0;
+	}
+
+	lab = bt_mesh_va_get(index);
+	if (lab == NULL) {
+		BT_WARN("Out of labels buffers");
+		return -ENOBUFS;
+	}
+
+	memcpy(lab->uuid, va.uuid, 16);
+	lab->addr = va.addr;
+	lab->ref = va.ref;
+
+	BT_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x",
+	       lab->addr, lab->ref);
+
+	return 0;
+}
+
+#define IS_VA_DEL(_label)	((_label)->ref == 0)
+void bt_mesh_va_pending_store(void)
+{
+	char buf[BT_SETTINGS_SIZE(sizeof(struct va_val))];
+	struct virtual_addr *lab;
+	struct va_val va;
+	char path[18];
+	char *val;
+	uint16_t i;
+	int err = 0;
+
+	for (i = 0; (lab = bt_mesh_va_get(i)) != NULL; i++) {
+		if (!lab->changed) {
+			continue;
+		}
+
+		lab->changed = 0U;
+
+		snprintk(path, sizeof(path), "bt_mesh/Va/%x", i);
+
+		if (IS_VA_DEL(lab)) {
+			val = NULL;
+		} else {
+			va.ref = lab->ref;
+			va.addr = lab->addr;
+			memcpy(va.uuid, lab->uuid, 16);
+
+			val = settings_str_from_bytes(&va, sizeof(va),
+						      buf, sizeof(buf));
+			if (!val) {
+				BT_ERR("Unable to encode model publication as value");
+				return;
+			}
+
+			err = settings_save_one(path, val);
+		}
+
+		if (err) {
+			BT_ERR("Failed to %s %s value (err %d)",
+			       IS_VA_DEL(lab) ? "delete" : "store", path, err);
+		} else {
+			BT_DBG("%s %s value",
+			       IS_VA_DEL(lab) ? "Deleted" : "Stored", path);
+		}
+	}
+}
+
+static struct conf_handler bt_mesh_va_conf_handler = {
+	.ch_name = "bt_mesh",
+	.ch_get = NULL,
+	.ch_set = va_set,
+	.ch_commit = NULL,
+	.ch_export = NULL,
+	};
+#endif
+
+void bt_mesh_va_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+	int rc;
+
+	rc = conf_register(&bt_mesh_va_conf_handler);
+
+	SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+				 "Failed to register bt_mesh_hb_pub conf");
+#endif
+}
+
+#else
+void bt_mesh_va_pending_store(void)
+{
+	/* Do nothing. */
+}
+#endif /* CONFIG_BT_MESH_LABEL_COUNT > 0 */
diff --git a/nimble/host/mesh/src/transport.h b/nimble/host/mesh/src/transport.h
index 23be7139d6..ad7a59f236 100644
--- a/nimble/host/mesh/src/transport.h
+++ b/nimble/host/mesh/src/transport.h
@@ -79,13 +79,6 @@ struct bt_mesh_ctl_friend_sub_confirm {
 	uint8_t  xact;
 }__attribute__((__packed__));
 
-struct bt_mesh_va {
-	uint16_t ref:15,
-		 changed:1;
-	uint16_t addr;
-	uint8_t  uuid[16];
-};
-
 bool bt_mesh_tx_in_progress(void);
 
 void bt_mesh_rx_reset(void);
@@ -102,12 +95,12 @@ void bt_mesh_trans_init(void);
 
 void bt_mesh_trans_reset(void);
 
-struct bt_mesh_va *bt_mesh_va_get(uint16_t index);
+uint8_t bt_mesh_va_add(const uint8_t uuid[16], uint16_t *addr);
 
-struct bt_mesh_va *bt_mesh_va_find(uint8_t uuid[16]);
+uint8_t bt_mesh_va_del(const uint8_t uuid[16], uint16_t *addr);
 
-uint8_t bt_mesh_va_add(uint8_t uuid[16], uint16_t *addr);
+uint8_t *bt_mesh_va_label_get(uint16_t addr);
 
-uint8_t bt_mesh_va_del(uint8_t uuid[16], uint16_t *addr);
+void bt_mesh_va_pending_store(void);
 
-uint8_t *bt_mesh_va_label_get(uint16_t addr);
\ No newline at end of file
+void bt_mesh_va_init(void);
diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml
index cf0c7f7505..c75828d243 100644
--- a/nimble/host/mesh/syscfg.yml
+++ b/nimble/host/mesh/syscfg.yml
@@ -23,6 +23,11 @@ syscfg.defs:
             BLE_MESH_PB_ADV or BLE_MESH_PB_GATT is set.
         value: 1
 
+    BLE_MESH_PROV_OOB_PUBLIC_KEY:
+        description: >
+            Enable this option if public key is to be exchanged via Out of Band (OOB) technology.
+        value: 0
+
     BLE_MESH_PB_ADV:
         description: >
            Enable this option to allow the device to be provisioned over
@@ -63,7 +68,7 @@ syscfg.defs:
         description: >
             This option specifies how many nodes each network can at most
             save in the configuration database.
-        value: 1
+        value: 8
 
     BLE_MESH_CDB_SUBNET_COUNT:
         description: >
@@ -82,17 +87,44 @@ syscfg.defs:
             Use this option to enable configuration database debug logs.
         value: 1
 
+    BLE_MESH_DEBUG_CFG:
+        description:
+            Use this option to enable node configuration debug logs for the
+            Bluetooth Mesh functionality.
+        value: 1
+
     BLE_MESH_PROXY:
         description: >
            Enable proxy. This is automatically set whenever BLE_MESH_PB_GATT or
            BLE_MESH_GATT_PROXY is set.
         value: 0
 
+    BLE_MESH_GATT:
+        value: 1
+
+    BLE_MESH_PROXY_MSG_LEN:
+        description: >
+            Integer value
+        value:
+        restrictions: BLE_MESH_GATT
+
+    BLE_MESH_GATT_SERVER:
+        value: 1
+        restrictions: BLE_MESH_GATT
+
     BLE_MESH_PB_GATT:
         description: >
            Enable this option to allow the device to be provisioned over
            the GATT bearer.
         value: 1
+        restrictions:
+            - '(BLE_MESH_GATT_SERVER && BLE_MESH_PROV)'
+
+    BLE_MESH_PB_GATT_USE_DEVICE_NAME:
+        description: >
+            This option includes GAP device name in scan response when
+            the PB-GATT is enabled.
+        value: 1
 
     BLE_MESH_GATT_PROXY:
         description: >
@@ -100,6 +132,8 @@ syscfg.defs:
             i.e. the ability to act as a proxy between a Mesh GATT Client
             and a Mesh network.
         value: 1
+        restrictions:
+            - '(BLE_MESH_GATT_SERVER && BLE_MESH_PROXY)'
 
     BLE_MESH_GATT_PROXY_ENABLED:
         description: >
@@ -127,8 +161,22 @@ syscfg.defs:
         descryption: >
             This option specifies how many Proxy Filter entries the local
             node supports.
+        value: 3
+        restrictions: BLE_MESH_GATT_PROXY
+
+    BLE_MESH_ACCESS_LAYER_MSG:
+        descryption: >
+            This option allows the applicaiton to directly access
+            Bluetooth access layer messages without the need to
+            instantiate Bluetooth mesh models.
         value: 1
 
+    BLE_MESH_PROXY_USE_DEVICE_NAME:
+        description: >
+            Include Bluetooth device name in scan response
+        value: 0
+        restrictions: BLE_MESH_GATT_PROXY
+
     BLE_MESH_SUBNET_COUNT:
         description: >
             This option specifies how many subnets a Mesh network can
@@ -153,6 +201,12 @@ syscfg.defs:
             at most be subscribed to.
         value: 1
 
+    BLE_MESH_MODEL_VND_MSG_CID_FORCE:
+        description: >
+            This option forces vendor model to use messages for the
+              corresponding CID field.
+        value: 1
+
     BLE_MESH_LABEL_COUNT:
         description: >
             This option specifies how many Label UUIDs can be stored.
@@ -179,6 +233,11 @@ syscfg.defs:
             but has a different purpose.
         value: 10
 
+    BLE_MESH_NET_BUF_USER_DATA_SIZE:
+        description: >
+            Number of octets that are used as user_data at the end of os_mbufs
+        value: 4
+
     BLE_MESH_ADV_BUF_COUNT:
         description: >
             Number of advertising buffers available. This should be chosen
@@ -189,6 +248,37 @@ syscfg.defs:
             supported outgoing segment count (BT_MESH_TX_SEG_MAX).
         value: 6
 
+    BLE_MESH_ADV:
+        description: >
+            Advertiser mode
+        value: 1
+
+    BLE_MESH_ADV_LEGACY:
+        description: >
+            Use legacy advertising commands for mesh sending. Legacy
+            advertising is significantly slower than the extended advertising, but
+            is supported by all controllers.
+        value: 1
+        restrictions:
+            - "BLE_MESH_ADV if 0"
+            - "!BLE_MESH_ADV_EXT"
+
+    BLE_MESH_ADV_EXT:
+        description: >
+            Use extended advertising commands for operating the advertiser.
+            Extended advertising is faster and uses less memory than legacy
+            advertising, but isn't supported by all controllers.
+        value: 0
+        restrictions:
+            - "BLE_MESH_ADV if 0"
+            - "!BLE_MESH_ADV_LEGACY"
+            - "BLE_EXT_ADV"
+
+    BLE_MESH_DEBUG_USE_ID_ADDR:
+        description: >
+            Use ID address for mesh advertisements, use random address otherwise.
+        value: 0
+
     BLE_MESH_ADV_STACK_SIZE:
         description: >
             Mesh advertiser thread stack size.
@@ -196,6 +286,11 @@ syscfg.defs:
             absolutely necessary
         value: 768
 
+    BLE_MESH_IV_UPDATE_SEQ_LIMIT:
+        description: >
+            This option specifies the sequence number value to start iv update.
+        value: 0x800000
+
     BLE_MESH_IVU_DIVIDER:
         description: >
             When the IV Update state enters Normal operation or IV Update
@@ -311,13 +406,6 @@ syscfg.defs:
         value: 4
         retrictions: 'BLE_MESH_SEG_RETRANSMIT_ATTEMPTS > 1'
 
-    BLE_MESH_RELAY:
-        description: >
-            Controls the initial number of retransmissions of original messages,
-            in addition to the first transmission. Can be changed through runtime
-            configuration.
-        value: 2
-
     BLE_MESH_NETWORK_TRANSMIT_COUNT:
         description: >
             Controls the initial number of retransmissions of original messages,
@@ -332,7 +420,7 @@ syscfg.defs:
             configuration.
         value: 20
 
-    BT_MESH_RELAY:
+    BLE_MESH_RELAY:
         description: >
             Support for acting as a Mesh Relay Node.
         value: 1
@@ -341,14 +429,13 @@ syscfg.defs:
         description: >
             Controls whether the Mesh Relay feature is enabled by default. Can be
             changed through runtime configuration.
-        value: 1
+        value: MYNEWT_VAL(BLE_MESH_RELAY)
 
     BLE_MESH_RELAY_RETRANSMIT_COUNT:
         description: >
             Controls the initial number of retransmissions of relayed messages, in
             addition to the first transmission. Can be changed through runtime
             configuration.
-
         value: 2
 
     BLE_MESH_RELAY_RETRANSMIT_INTERVAL:
@@ -436,16 +523,6 @@ syscfg.defs:
             a value of 300 means 30 seconds.
         value: 300
 
-    BLE_MESH_LPN_POLL_TIMEOUT:
-        description: >
-            PollTimeout timer is used to measure time between two
-            consecutive requests sent by the Low Power node. If no
-            requests are received by the Friend node before the
-            PollTimeout timer expires, then the friendship is considered
-            terminated. The value is in units of 100 milliseconds, so e.g.
-            a value of 300 means 30 seconds.
-        value: 300
-
     BLE_MESH_LPN_INIT_POLL_TIMEOUT:
         description: >
             The initial value of the PollTimeout timer when Friendship
@@ -471,7 +548,7 @@ syscfg.defs:
         description: >
             Automatically subscribe all nodes address when friendship
             established.
-        value: 1
+        value: 0
 
     BLE_MESH_FRIEND:
         description: >
@@ -619,16 +696,22 @@ syscfg.defs:
 
     BLE_MESH_RPL_STORE_TIMEOUT:
         description: >
-            This value defines in seconds how soon the RPL gets written to
-            persistent storage after a change occurs. If the node receives
-            messages frequently it may make sense to have this set to a
-            large value, whereas if the RPL gets updated infrequently a
-            value as low as 0 (write immediately) may make sense. Note that
-            if the node operates a security sensitive use case, and there's
-            a risk of sudden power loss, it may be a security vulnerability
-            to set this value to anything else than 0 (a power loss before
-            writing to storage exposes the node to potential message
-            replay attacks).
+            Minimum interval after which unsaved RPL entries are updated in storage
+
+            This value defines in seconds how soon unsaved RPL entries
+            gets written to the persistent storage. Setting this value
+            to a large number may lead to security vulnerabilities if a node
+            gets powered off before the timer is fired. When flash is used
+            as the persistent storage setting this value to a low number
+            may wear out flash sooner or later. However, if the RPL gets
+            updated infrequently a value as low as 0 (write immediately)
+            may make sense. Setting this value to -1 will disable this timer.
+            In this case, a user is responsible to store pending RPL entries
+            using @ref bt_mesh_rpl_pending_store. In the mean time, when
+            IV Index is updated, the outdated RPL entries will still be
+            stored by @ref BT_MESH_STORE_TIMEOUT. Finding the right balance
+            between this timeout and calling @ref bt_mesh_rpl_pending_store
+            may reduce a risk of security vulnerability and flash wear out.
         value: 5
 
     BLE_MESH_DEVICE_NAME:
@@ -897,3 +980,8 @@ syscfg.vals.BLE_MESH_PB_GATT:
 
 syscfg.vals.BLE_MESH_PB_ADV:
     BLE_MESH_PROV: 1
+
+syscfg.vals.'BLE_MESH_PB_GATT':
+    BLE_MESH_PROXY_MSG_LEN: 66
+syscfg.vals.'BLE_MESH_GATT_PROXY':
+    BLE_MESH_PROXY_MSG_LEN: 33
diff --git a/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h b/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h
index 06c44784af..344bc8520b 100644
--- a/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h
+++ b/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h
@@ -29,8 +29,10 @@ extern "C" {
 struct ble_hs_cfg;
 
 #define BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16     0x2a05
+#define BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16     0x2B2A
 
 void ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle);
+void ble_svc_conn_gatt_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle);
 void ble_svc_gatt_init(void);
 
 #ifdef __cplusplus
diff --git a/nimble/host/services/gatt/src/ble_svc_gatt.c b/nimble/host/services/gatt/src/ble_svc_gatt.c
index 78b4a0683c..c8cae8a620 100644
--- a/nimble/host/services/gatt/src/ble_svc_gatt.c
+++ b/nimble/host/services/gatt/src/ble_svc_gatt.c
@@ -22,8 +22,10 @@
 #include "sysinit/sysinit.h"
 #include "host/ble_hs.h"
 #include "services/gatt/ble_svc_gatt.h"
+#include "host/ble_gatt.h"
 
 static uint16_t ble_svc_gatt_changed_val_handle;
+static uint16_t ble_svc_gatt_hash_val_handle;
 static uint16_t ble_svc_gatt_start_handle;
 static uint16_t ble_svc_gatt_end_handle;
 
@@ -31,6 +33,10 @@ static int
 ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,
                     struct ble_gatt_access_ctxt *ctxt, void *arg);
 
+static int 
+ble_svc_gatt_db_hash_access(uint16_t conn_handle, uint16_t attr_handle,
+                            struct ble_gatt_access_ctxt *ctxt, void *arg);
+
 static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
     {
         /*** Service: GATT */
@@ -41,7 +47,14 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
             .access_cb = ble_svc_gatt_access,
             .val_handle = &ble_svc_gatt_changed_val_handle,
             .flags = BLE_GATT_CHR_F_INDICATE,
-        }, {
+        },
+        {
+            .uuid = BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16),
+            .access_cb = ble_svc_gatt_db_hash_access,
+            .val_handle = &ble_svc_gatt_hash_val_handle,
+            .flags = BLE_GATT_CHR_F_READ,
+        },
+        {
             0, /* No more characteristics in this service. */
         } },
     },
@@ -51,6 +64,28 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
     },
 };
 
+static int 
+ble_svc_gatt_db_hash_access(uint16_t conn_handle, uint16_t attr_handle,
+                            struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+    int rc;
+    assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
+    assert(ctxt->chr == &ble_svc_gatt_defs[0].characteristics[1]);
+    uint8_t db_hash[16];
+    uint16_t len_data = sizeof(db_hash);
+    int res = ble_compute_db_hash(db_hash);
+    if(res != 0){
+        return BLE_ATT_ERR_UNLIKELY;
+    }
+
+    rc = os_mbuf_append(ctxt->om, db_hash, len_data);
+    if(rc !=0){
+        return BLE_ATT_ERR_UNLIKELY;
+    }
+
+    return 0;
+}
+
 static int
 ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,
                     struct ble_gatt_access_ctxt *ctxt, void *arg)
@@ -73,7 +108,7 @@ ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,
 
     put_le16(u8p + 0, ble_svc_gatt_start_handle);
     put_le16(u8p + 2, ble_svc_gatt_end_handle);
-
+    
     return 0;
 }
 
@@ -89,7 +124,22 @@ ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle)
 {
     ble_svc_gatt_start_handle = start_handle;
     ble_svc_gatt_end_handle = end_handle;
-    ble_gatts_chr_updated(ble_svc_gatt_changed_val_handle);
+    ble_gatts_chr_updated(ble_svc_gatt_changed_val_handle);   
+}
+
+/**
+ * Indicates a change in attribute assignment to specified peer.
+ *
+ * @param conn_handle           The connection to indicate.
+ * @param start_handle          The start of the affected handle range.
+ * @param end_handle            The end of the affected handle range.
+ */
+void
+ble_svc_conn_gatt_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle)
+{
+    ble_svc_gatt_start_handle = start_handle;
+    ble_svc_gatt_end_handle = end_handle;
+    ble_gattc_indicate(conn_handle, ble_svc_gatt_changed_val_handle);
 }
 
 void
diff --git a/nimble/host/src/ble_att_svr.c b/nimble/host/src/ble_att_svr.c
index 0cfbc7d96b..468768ac02 100644
--- a/nimble/host/src/ble_att_svr.c
+++ b/nimble/host/src/ble_att_svr.c
@@ -30,7 +30,7 @@
  * ATT server - Attribute Protocol
  *
  * Notes on buffer reuse:
- * Most request handlers reuse the request buffer for the reponse.  This is
+ * Most request handlers reuse the request buffer for the response.  This is
  * done to prevent out-of-memory conditions.  However, there are two handlers
  * which do not reuse the request buffer:
  *     1. Write request.
@@ -284,6 +284,16 @@ ble_att_svr_check_perms(uint16_t conn_handle, int is_read,
     }
 
     ble_att_svr_get_sec_state(conn_handle, &sec_state);
+    /* In SC Only mode all characteristics requiring security
+     * require it on level 4
+     */
+    if (MYNEWT_VAL(BLE_SM_SC_ONLY)) {
+        if (sec_state.key_size != 16 ||
+            !sec_state.authenticated ||
+            !sec_state.encrypted) {
+            return BLE_ATT_ERR_INSUFFICIENT_KEY_SZ;
+        }
+    }
     if ((enc || authen) && !sec_state.encrypted) {
         ble_hs_lock();
         conn = ble_hs_conn_find(conn_handle);
@@ -2661,6 +2671,8 @@ ble_att_svr_reset(void)
         ble_att_svr_entry_free(entry);
     }
 
+    ble_att_svr_id = 0;
+    
     /* Note: prep entries do not get freed here because it is assumed there are
      * no established connections.
      */
diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c
index 8e56df13fb..3d8f3038ae 100644
--- a/nimble/host/src/ble_gap.c
+++ b/nimble/host/src/ble_gap.c
@@ -1303,6 +1303,16 @@ ble_gap_adv_active_instance(uint8_t instance)
 }
 #endif
 
+#if MYNEWT_VAL(BLE_EXT_ADV)
+int ble_gap_ext_adv_active(uint8_t instance)
+{
+    if (instance >= BLE_ADV_INSTANCES) {
+        return 0;
+    }
+    return ble_gap_adv_active_instance(instance);
+}
+#endif
+
 /**
  * Clears advertisement and discovery state.  This function is necessary
  * when the controller loses its active state (e.g. on stack reset).
@@ -2169,11 +2179,6 @@ ble_gap_wl_set(const ble_addr_t *addrs, uint8_t white_list_count)
 
     ble_hs_lock();
 
-    if (white_list_count == 0) {
-        rc = BLE_HS_EINVAL;
-        goto done;
-    }
-
     for (i = 0; i < white_list_count; i++) {
         if (addrs[i].type != BLE_ADDR_PUBLIC &&
             addrs[i].type != BLE_ADDR_RANDOM) {
@@ -2687,15 +2692,28 @@ ble_gap_ext_adv_params_tx(uint8_t instance,
     if (params->high_duty_directed) {
         cmd.props |= BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED;
     }
-    if (params->legacy_pdu) {
-        cmd.props |= BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY;
-    }
     if (params->anonymous) {
         cmd.props |= BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV;
     }
     if (params->include_tx_power) {
         cmd.props |= BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR;
     }
+    if (params->legacy_pdu) {
+        cmd.props |= BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY;
+
+        /* check right away if the applied configuration is valid before handing
+         * the command to the controller to improve error reporting */
+        switch (cmd.props) {
+            case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND:
+            case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR:
+            case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR:
+            case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN:
+            case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN:
+                break;
+            default:
+                return BLE_HS_EINVAL;
+        }
+    }
 
     /* Fill optional fields if application did not specify them. */
     if (params->itvl_min == 0 && params->itvl_max == 0) {
@@ -5779,7 +5797,7 @@ ble_gap_enc_event(uint16_t conn_handle, int status,
         return;
     }
 
-    /* If encryption succeded and encryption has been restored for bonded device,
+    /* If encryption succeeded and encryption has been restored for bonded device,
      * notify gatt server so it has chance to send notification/indication if needed.
      */
     if (security_restored) {
diff --git a/nimble/host/src/ble_gattc.c b/nimble/host/src/ble_gattc.c
index 74a2837ad7..b43a0af03c 100644
--- a/nimble/host/src/ble_gattc.c
+++ b/nimble/host/src/ble_gattc.c
@@ -3765,17 +3765,20 @@ ble_gattc_write_long_rx_prep(struct ble_gattc_proc *proc,
                      proc->write_long.length) != 0) {
 
         rc = BLE_HS_EBADDATA;
-        goto err;
-    }
 
-    /* Send follow-up request. */
-    proc->write_long.attr.offset += OS_MBUF_PKTLEN(om);
-    rc = ble_gattc_write_long_resume(proc);
-    if (rc != 0) {
+        /* if data doesn't match up send cancel write */
+        ble_att_clt_tx_exec_write(proc->conn_handle, BLE_ATT_EXEC_WRITE_F_CANCEL);
         goto err;
-    }
+    } else {
+        /* Send follow-up request. */
+        proc->write_long.attr.offset += OS_MBUF_PKTLEN(om);
+        rc = ble_gattc_write_long_resume(proc);
+        if (rc != 0) {
+            goto err;
+        }
 
-    return 0;
+        return 0;
+    }
 
 err:
     /* XXX: Might need to cancel pending writes. */
diff --git a/nimble/host/src/ble_gatts.c b/nimble/host/src/ble_gatts.c
index a635f2d7f4..61a7189f8a 100644
--- a/nimble/host/src/ble_gatts.c
+++ b/nimble/host/src/ble_gatts.c
@@ -28,6 +28,17 @@
 #define BLE_GATTS_INCLUDE_SZ    6
 #define BLE_GATTS_CHR_MAX_SZ    19
 
+#if NIMBLE_BLE_SM
+#include "tinycrypt/aes.h"
+#include "tinycrypt/constants.h"
+#include "tinycrypt/utils.h"
+
+#if MYNEWT_VAL(BLE_SM_SC)
+#include "tinycrypt/cmac_mode.h"
+#include "tinycrypt/ecc_dh.h"
+#endif
+#endif
+
 static const ble_uuid_t *uuid_pri =
     BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE);
 static const ble_uuid_t *uuid_sec =
@@ -364,7 +375,7 @@ ble_gatts_val_access(uint16_t conn_handle, uint16_t attr_handle,
             gatt_ctxt->om = *om;
         } else {
             new_om = 1;
-            gatt_ctxt->om = os_msys_get_pkthdr(0, 0);
+            gatt_ctxt->om = ble_hs_mbuf_att_pkt();
             if (gatt_ctxt->om == NULL) {
                 return BLE_ATT_ERR_INSUFFICIENT_RES;
             }
@@ -1719,6 +1730,8 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
 {
     struct ble_store_value_cccd cccd_value;
     struct ble_store_key_cccd cccd_key;
+    struct ble_store_value_hash hash_value;
+    struct ble_store_key_hash hash_key;
     struct ble_gatts_clt_cfg *clt_cfg;
     struct ble_hs_conn *conn;
     uint8_t att_op;
@@ -1799,6 +1812,13 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
 
         cccd_key.idx++;
     }
+
+    hash_key.peer_addr = conn->bhc_peer_addr;
+    hash_key.peer_addr.type =
+        ble_hs_misc_peer_addr_type_to_id(conn->bhc_peer_addr.type);
+    hash_key.idx = 0;
+
+    rc = ble_store_read_hash(conn_handle, &hash_key, &hash_value); // read peer hash, read the database hash characteristic and compare, if rc != 0 => ble_svc_conn_gatt_changed(0, 0xffff)
 }
 
 static struct ble_gatts_svc_entry *
@@ -2182,3 +2202,107 @@ ble_gatts_init(void)
     return 0;
 
 }
+
+static void ble_db_hash_message_append_u16(uint16_t val, uint8_t *buf, uint8_t *len) {
+    val = htole16(val);
+    memcpy(buf + *len, &val, sizeof(uint16_t));
+    *len += sizeof(uint16_t);
+}
+
+static void ble_db_hash_message_append_u32(uint32_t val, uint8_t *buf, uint8_t *len) {
+    val = htole32(val);
+    memcpy(buf + *len, &val, sizeof(uint32_t));
+    *len += sizeof(uint32_t);
+}
+
+static void ble_db_hash_message_append_uuid(const ble_uuid_t *uuid, uint8_t *buf, uint8_t *len) {
+  switch (uuid->type) {
+    case BLE_UUID_TYPE_16: {
+        ble_db_hash_message_append_u16( BLE_UUID16(uuid)->value, buf, len);
+        break;
+     }
+    case BLE_UUID_TYPE_32: {
+        ble_db_hash_message_append_u32(BLE_UUID32(uuid)->value, buf, len);
+        break;
+   }
+    case BLE_UUID_TYPE_128: {
+        memcpy(buf + *len, BLE_UUID128(uuid)->value, 16);
+        *len += 16;
+        break;
+    }
+    default:
+        break;
+    }
+}
+
+/**
+ * Called when the database cmac hash needs to be computed using the information
+ * from registered services, characteristics and descriptors.  This
+ * function:
+ *     o Sets up the cmac generator with a key of 0s
+ *     o Parse services, characteristics and descriptors and adds information
+ *       to the message to code.
+ *     o Computes the cmac coded message.
+ */
+int ble_compute_db_hash(uint8_t db_hash[16])
+{
+    uint8_t buf[24];
+    uint8_t buf_len;
+
+    struct tc_aes_key_sched_struct sched;
+    struct tc_cmac_struct state;
+    const uint8_t key[16] = {0};
+
+    if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
+        return BLE_HS_EUNKNOWN;
+    }
+
+    for (int i = 0; i < ble_gatts_num_svc_entries; i++) {
+        struct ble_gatts_svc_entry *entry = &ble_gatts_svc_entries[i];
+        buf_len = 0;
+        ble_db_hash_message_append_u16(entry->handle, buf, &buf_len);
+        ble_db_hash_message_append_u16(BLE_ATT_UUID_PRIMARY_SERVICE, buf, &buf_len);
+        ble_db_hash_message_append_uuid(entry->svc->uuid, buf, &buf_len);
+
+       if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
+          return BLE_HS_EUNKNOWN;
+       }
+       buf_len = 0;
+        
+        if (entry->svc->characteristics != NULL) {
+            for (const struct ble_gatt_chr_def *chr = entry->svc->characteristics; chr && chr->uuid; chr++) {
+                if (chr->val_handle == NULL) {
+                    continue;
+                }
+                ble_db_hash_message_append_u16(*chr->val_handle - 1, buf, &buf_len);
+                ble_db_hash_message_append_u16(BLE_ATT_UUID_CHARACTERISTIC, buf, &buf_len);
+                ble_db_hash_message_append_u16(chr->flags, buf, &buf_len);
+                ble_db_hash_message_append_u16(*chr->val_handle, buf, &buf_len);
+                ble_db_hash_message_append_uuid(chr->uuid, buf, &buf_len);
+
+                if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
+                    return BLE_HS_EUNKNOWN;
+                }
+                buf_len = 0;
+
+                if (chr->descriptors != NULL) {
+                    for (struct ble_gatt_dsc_def *dsc = chr->descriptors; dsc && dsc->uuid; dsc++) {
+                        ble_db_hash_message_append_u16(*chr->val_handle + 1, buf, &buf_len);
+                        ble_db_hash_message_append_u16(BLE_ATT_UUID_DESCRIPTOR, buf, &buf_len);
+
+                        if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
+                            return BLE_HS_EUNKNOWN;
+                        }
+                        buf_len = 0;
+                    }
+                }
+            }
+        }
+    }
+      
+    if (tc_cmac_final(db_hash, &state) == TC_CRYPTO_FAIL) {
+        return BLE_HS_EUNKNOWN;
+    }
+
+    return 0;
+}
diff --git a/nimble/host/src/ble_hs_flow.c b/nimble/host/src/ble_hs_flow.c
index 2520c85415..1eabba9e0c 100644
--- a/nimble/host/src/ble_hs_flow.c
+++ b/nimble/host/src/ble_hs_flow.c
@@ -92,7 +92,7 @@ ble_hs_flow_tx_num_comp_pkts(void)
              * response from the controller, so don't use the normal blocking
              * HCI API when sending it.
              */
-            rc = ble_hs_hci_cmd_send_buf(
+            rc = ble_hs_hci_cmd_tx_no_rsp(
                 BLE_HCI_OP(BLE_HCI_OGF_CTLR_BASEBAND,
                            BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS),
                 buf, sizeof(buf));
diff --git a/nimble/host/src/ble_hs_hci.c b/nimble/host/src/ble_hs_hci.c
index ac472a9ea5..53d36473b6 100644
--- a/nimble/host/src/ble_hs_hci.c
+++ b/nimble/host/src/ble_hs_hci.c
@@ -41,11 +41,20 @@ static uint32_t ble_hs_hci_sup_feat;
 
 static uint8_t ble_hs_hci_version;
 
+#if MYNEWT_VAL(BLE_CONTROLLER)
 #define BLE_HS_HCI_FRAG_DATABUF_SIZE    \
     (BLE_ACL_MAX_PKT_SIZE +             \
      BLE_HCI_DATA_HDR_SZ +              \
      sizeof (struct os_mbuf_pkthdr) +   \
+     sizeof (struct ble_mbuf_hdr) +      \
      sizeof (struct os_mbuf))
+#else
+#define BLE_HS_HCI_FRAG_DATABUF_SIZE    \
+    (BLE_ACL_MAX_PKT_SIZE +             \
+     BLE_HCI_DATA_HDR_SZ +              \
+     sizeof (struct os_mbuf_pkthdr) +   \
+     sizeof (struct os_mbuf))
+#endif
 
 #define BLE_HS_HCI_FRAG_MEMBLOCK_SIZE   \
     (OS_ALIGN(BLE_HS_HCI_FRAG_DATABUF_SIZE, 4))
@@ -291,6 +300,20 @@ ble_hs_hci_wait_for_ack(void)
     return rc;
 }
 
+int
+ble_hs_hci_cmd_tx_no_rsp(uint16_t opcode, const void *cmd, uint8_t cmd_len)
+{
+    int rc;
+
+    ble_hs_hci_lock();
+
+    rc = ble_hs_hci_cmd_send_buf(opcode, cmd, cmd_len);
+
+    ble_hs_hci_unlock();
+
+    return rc;
+}
+
 int
 ble_hs_hci_cmd_tx(uint16_t opcode, const void *cmd, uint8_t cmd_len,
                   void *rsp, uint8_t rsp_len)
@@ -407,7 +430,11 @@ ble_hs_hci_frag_alloc(uint16_t frag_size, void *arg)
     struct os_mbuf *om;
 
     /* Prefer the dedicated one-element fragment pool. */
+#if MYNEWT_VAL(BLE_CONTROLLER)
+    om = os_mbuf_get_pkthdr(&ble_hs_hci_frag_mbuf_pool, sizeof(struct ble_mbuf_hdr));
+#else
     om = os_mbuf_get_pkthdr(&ble_hs_hci_frag_mbuf_pool, 0);
+#endif
     if (om != NULL) {
         om->om_data += BLE_HCI_DATA_HDR_SZ;
         return om;
diff --git a/nimble/host/src/ble_hs_hci_priv.h b/nimble/host/src/ble_hs_hci_priv.h
index b02d4ab2e8..11e544f18f 100644
--- a/nimble/host/src/ble_hs_hci_priv.h
+++ b/nimble/host/src/ble_hs_hci_priv.h
@@ -81,6 +81,8 @@ struct hci_periodic_adv_params
 
 extern uint16_t ble_hs_hci_avail_pkts;
 
+/* This function is not waiting for command status/complete HCI events */
+int ble_hs_hci_cmd_tx_no_rsp(uint16_t opcode, const void *cmd, uint8_t cmd_len);
 int ble_hs_hci_cmd_tx(uint16_t opcode, const void *cmd, uint8_t cmd_len,
                       void *rsp, uint8_t rsp_len);
 void ble_hs_hci_init(void);
diff --git a/nimble/host/src/ble_hs_mbuf.c b/nimble/host/src/ble_hs_mbuf.c
index d938d34806..6e920f94fe 100644
--- a/nimble/host/src/ble_hs_mbuf.c
+++ b/nimble/host/src/ble_hs_mbuf.c
@@ -29,7 +29,11 @@ ble_hs_mbuf_gen_pkt(uint16_t leading_space)
     struct os_mbuf *om;
     int rc;
 
+#if MYNEWT_VAL(BLE_CONTROLLER)
+    om = os_msys_get_pkthdr(0, sizeof(struct ble_mbuf_hdr));
+#else
     om = os_msys_get_pkthdr(0, 0);
+#endif
     if (om == NULL) {
         return NULL;
     }
diff --git a/nimble/host/src/ble_l2cap.c b/nimble/host/src/ble_l2cap.c
index 2bc50e0e93..bfbdadfcd6 100644
--- a/nimble/host/src/ble_l2cap.c
+++ b/nimble/host/src/ble_l2cap.c
@@ -393,8 +393,11 @@ ble_l2cap_rx(struct ble_hs_conn *conn,
         }
 
         if (l2cap_hdr.len > ble_l2cap_get_mtu(chan)) {
-            /* More data then we expected on the channel */
+            /* More data than we expected on the channel.
+             * Disconnect peer with invalid behaviour
+             */
             rc = BLE_HS_EBADDATA;
+            ble_l2cap_disconnect(chan);
             goto err;
         }
 
diff --git a/nimble/host/src/ble_l2cap_coc.c b/nimble/host/src/ble_l2cap_coc.c
index aa953d79c5..f74cea2800 100644
--- a/nimble/host/src/ble_l2cap_coc.c
+++ b/nimble/host/src/ble_l2cap_coc.c
@@ -198,6 +198,17 @@ ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan)
         }
 
         sdu_len = get_le16((*om)->om_data);
+
+        /* We should receive payload of size sdu_len + 2 bytes of sdu_len field */
+        if (om_total > sdu_len + 2) {
+            BLE_HS_LOG(ERROR, "Payload larger than expected (%d>%d)\n",
+                       om_total, sdu_len + 2);
+            /* Disconnect peer with invalid behaviour */
+            rx->sdu = NULL;
+            rx->data_offset = 0;
+            ble_l2cap_disconnect(chan);
+            return BLE_HS_EBADDATA;
+        }
         if (sdu_len > rx->mtu) {
             BLE_HS_LOG(INFO, "error: sdu_len > rx->mtu (%d>%d)\n",
                        sdu_len, rx->mtu);
@@ -227,6 +238,15 @@ ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan)
     } else {
         BLE_HS_LOG(DEBUG, "Continuation...received %d\n", (*om)->om_len);
 
+        if (OS_MBUF_PKTLEN(rx->sdu) + (*om)->om_len > rx->data_offset) {
+            /* Disconnect peer with invalid behaviour */
+            BLE_HS_LOG(ERROR, "Payload larger than expected (%d>%d)\n",
+                       OS_MBUF_PKTLEN(rx->sdu) + (*om)->om_len, rx->data_offset);
+            rx->sdu = NULL;
+            rx->data_offset = 0;
+            ble_l2cap_disconnect(chan);
+            return BLE_HS_EBADDATA;
+        }
         rc  = os_mbuf_appendfrom(rx->sdu, *om, 0, om_total);
         if (rc != 0) {
             /* FIXME: need to handle it better */
diff --git a/nimble/host/src/ble_l2cap_sig.c b/nimble/host/src/ble_l2cap_sig.c
index aaf9c642a4..3033e05dd5 100644
--- a/nimble/host/src/ble_l2cap_sig.c
+++ b/nimble/host/src/ble_l2cap_sig.c
@@ -784,10 +784,6 @@ ble_l2cap_sig_credit_base_reconfig_req_rx(uint16_t conn_handle,
 
         if (chan[i]->peer_coc_mps > req->mps) {
             reduction_mps++;
-            if (reduction_mps > 1) {
-                rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED);
-                goto failed;
-            }
         }
 
         if (chan[i]->coc_tx.mtu > req->mtu) {
@@ -796,6 +792,11 @@ ble_l2cap_sig_credit_base_reconfig_req_rx(uint16_t conn_handle,
         }
     }
 
+    if (reduction_mps > 0 && cid_cnt > 1) {
+        rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED);
+        goto failed;
+    }
+
     ble_hs_unlock();
 
     for (i = 0; i < cid_cnt; i++) {
@@ -1047,6 +1048,7 @@ ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle,
     struct ble_hs_conn *conn;
     int rc;
     int i;
+    uint16_t duplicated_cids[5] = {};
 
 #if !BLE_MONITOR
     BLE_HS_LOG(DEBUG, "L2CAP LE COC connection response received\n");
@@ -1092,6 +1094,12 @@ ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle,
             chan->dcid = 0;
             continue;
         }
+        if (ble_hs_conn_chan_find_by_dcid(conn, rsp->dcids[i])) {
+            duplicated_cids[i] = rsp->dcids[i];
+            chan->dcid = 0;
+            continue;
+        }
+
         chan->peer_coc_mps = le16toh(rsp->mps);
         chan->dcid = le16toh(rsp->dcids[i]);
         chan->coc_tx.mtu = le16toh(rsp->mtu);
@@ -1103,6 +1111,16 @@ ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle,
     ble_hs_unlock();
 
 done:
+    for (i = 0; i < 5; i++){
+        if (duplicated_cids[i] != 0){
+            ble_hs_lock();
+            conn = ble_hs_conn_find(conn_handle);
+            chan = ble_hs_conn_chan_find_by_dcid(conn, duplicated_cids[i]);
+            ble_hs_unlock();
+            rc = ble_l2cap_sig_disconnect(chan);
+        }
+    }
+
     ble_l2cap_sig_coc_connect_cb(proc, rc);
     ble_l2cap_sig_proc_free(proc);
 
@@ -1540,7 +1558,13 @@ ble_l2cap_sig_disc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr,
      * is from peer perspective. It is source CID from nimble perspective
      */
     chan = ble_hs_conn_chan_find_by_scid(conn, le16toh(req->dcid));
-    if (!chan || (le16toh(req->scid) != chan->dcid)) {
+    if (!chan) {
+        os_mbuf_free_chain(txom);
+        ble_hs_unlock();
+        ble_l2cap_sig_reject_invalid_cid_tx(conn_handle, hdr->identifier, req->dcid, req->scid);
+        return 0;
+    }
+    if (le16toh(req->scid) != chan->dcid) {
         os_mbuf_free_chain(txom);
         ble_hs_unlock();
         return 0;
diff --git a/nimble/host/src/ble_l2cap_sig_cmd.c b/nimble/host/src/ble_l2cap_sig_cmd.c
index d9e507e258..48b35249ec 100644
--- a/nimble/host/src/ble_l2cap_sig_cmd.c
+++ b/nimble/host/src/ble_l2cap_sig_cmd.c
@@ -21,6 +21,7 @@
 #include "ble_hs_priv.h"
 
 #if NIMBLE_BLE_CONNECT
+/* this function consumes tx os_mbuf */
 int
 ble_l2cap_sig_tx(uint16_t conn_handle, struct os_mbuf *txom)
 {
@@ -33,6 +34,8 @@ ble_l2cap_sig_tx(uint16_t conn_handle, struct os_mbuf *txom)
                                          &conn, &chan);
     if (rc == 0) {
         rc = ble_l2cap_tx(conn, chan, txom);
+    } else {
+        os_mbuf_free_chain(txom);
     }
     ble_hs_unlock();
 
diff --git a/nimble/host/src/ble_sm.c b/nimble/host/src/ble_sm.c
index b4e8096a57..8f2bc5f517 100644
--- a/nimble/host/src/ble_sm.c
+++ b/nimble/host/src/ble_sm.c
@@ -516,11 +516,13 @@ static void
 ble_sm_persist_keys(struct ble_sm_proc *proc)
 {
     struct ble_store_value_sec value_sec;
+    struct ble_store_value_hash value_hash;
     struct ble_hs_conn *conn;
     ble_addr_t peer_addr;
     int authenticated;
     int identity_ev = 0;
     int sc;
+    int rc;
 
     ble_hs_lock();
 
@@ -577,6 +579,14 @@ ble_sm_persist_keys(struct ble_sm_proc *proc)
     ble_sm_fill_store_value(&peer_addr, authenticated, sc, &proc->peer_keys,
                             &value_sec);
     ble_store_write_peer_sec(&value_sec);
+
+
+    // Compute current database hash and store it
+    value_hash.peer_addr = peer_addr;
+    rc = ble_compute_db_hash(value_hash.db_hash);
+    if (rc == 0) {
+        rc = ble_store_write_hash(&value_hash);
+    }    
 }
 
 static int
@@ -1726,17 +1736,23 @@ ble_sm_pair_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
 }
 
 static bool
-ble_sm_verify_auth_requirements(uint8_t authreq)
+ble_sm_verify_auth_requirements(uint8_t cmd)
 {
     /* For now we check only SC only mode. I.e.: when remote indicates
      * to not support SC pairing, let us make sure legacy pairing is supported
      * on our side. If not, we can fail right away.
      */
-    if (!(authreq & BLE_SM_PAIR_AUTHREQ_SC)) {
+    if (!(cmd & BLE_SM_PAIR_AUTHREQ_SC)) {
         if (MYNEWT_VAL(BLE_SM_LEGACY) == 0) {
             return false;
         }
     }
+    /* Fail if Secure Connections level forces MITM protection and remote does not
+     * support it
+     */
+    if (MYNEWT_VAL(BLE_SM_SC_LVL) >= 3 && !(cmd & BLE_SM_PAIR_AUTHREQ_MITM)) {
+        return false;
+    }
     return true;
 }
 
@@ -1817,12 +1833,21 @@ ble_sm_pair_req_rx(uint16_t conn_handle, struct os_mbuf **om,
         if (conn->bhc_flags & BLE_HS_CONN_F_MASTER) {
             res->sm_err = BLE_SM_ERR_CMD_NOT_SUPP;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_CMD_NOT_SUPP);
+        } else if (MYNEWT_VAL(BLE_SM_SC_LVL) == 1) {
+            res->sm_err = BLE_SM_ERR_CMD_NOT_SUPP;
+            res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_CMD_NOT_SUPP);
         } else if (req->max_enc_key_size < BLE_SM_PAIR_KEY_SZ_MIN) {
             res->sm_err = BLE_SM_ERR_ENC_KEY_SZ;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_ENC_KEY_SZ);
         } else if (req->max_enc_key_size > BLE_SM_PAIR_KEY_SZ_MAX) {
             res->sm_err = BLE_SM_ERR_INVAL;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_INVAL);
+        } else if (MYNEWT_VAL(BLE_SM_SC_ONLY) && (req->max_enc_key_size != BLE_SM_PAIR_KEY_SZ_MAX)) {
+            /* Fail if Secure Connections Only mode is on and remote does not meet
+            * key size requirements - MITM was checked in last step
+            */
+            res->sm_err = BLE_SM_ERR_ENC_KEY_SZ;
+            res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_ENC_KEY_SZ);
         } else if (!ble_sm_verify_auth_requirements(req->authreq)) {
             res->sm_err = BLE_SM_ERR_AUTHREQ;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
@@ -1886,6 +1911,12 @@ ble_sm_pair_rsp_rx(uint16_t conn_handle, struct os_mbuf **om,
         } else if (rsp->max_enc_key_size > BLE_SM_PAIR_KEY_SZ_MAX) {
             res->sm_err = BLE_SM_ERR_INVAL;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_INVAL);
+        } else if (MYNEWT_VAL(BLE_SM_SC_ONLY) && (rsp->max_enc_key_size != BLE_SM_PAIR_KEY_SZ_MAX)) {
+            /* Fail if Secure Connections Only mode is on and remote does not meet
+            * key size requirements - MITM was checked in last step
+            */
+            res->sm_err = BLE_SM_ERR_ENC_KEY_SZ;
+            res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_ENC_KEY_SZ);
         } else if (!ble_sm_verify_auth_requirements(rsp->authreq)) {
             res->sm_err = BLE_SM_ERR_AUTHREQ;
             res->app_status = BLE_HS_SM_US_ERR(BLE_SM_ERR_AUTHREQ);
diff --git a/nimble/host/src/ble_sm_cmd.c b/nimble/host/src/ble_sm_cmd.c
index 2983102cd1..b84df1ec75 100644
--- a/nimble/host/src/ble_sm_cmd.c
+++ b/nimble/host/src/ble_sm_cmd.c
@@ -63,6 +63,8 @@ ble_sm_tx(uint16_t conn_handle, struct os_mbuf *txom)
                                          &conn, &chan);
     if (rc == 0) {
         rc = ble_l2cap_tx(conn, chan, txom);
+    } else {
+        os_mbuf_free_chain(txom);
     }
 
     return rc;
diff --git a/nimble/host/src/ble_sm_lgcy.c b/nimble/host/src/ble_sm_lgcy.c
index 0259ff4682..1a500fb746 100644
--- a/nimble/host/src/ble_sm_lgcy.c
+++ b/nimble/host/src/ble_sm_lgcy.c
@@ -36,7 +36,7 @@
 #define IOACT_INPUT BLE_SM_IOACT_INPUT
 #define IOACT_DISP  BLE_SM_IOACT_DISP
 
-/* This is the initiator passkey action action dpeneding on the io
+/* This is the initiator passkey action action depending on the io
  * capabilties of both parties
  */
 static const uint8_t ble_sm_lgcy_init_ioa[5 /*resp*/ ][5 /*init*/ ] =
diff --git a/nimble/host/src/ble_sm_sc.c b/nimble/host/src/ble_sm_sc.c
index 7fae5b1c94..162a4a2ba1 100644
--- a/nimble/host/src/ble_sm_sc.c
+++ b/nimble/host/src/ble_sm_sc.c
@@ -612,6 +612,13 @@ ble_sm_sc_public_key_rx(uint16_t conn_handle, struct os_mbuf **om,
     }
 
     cmd = (struct ble_sm_public_key *)(*om)->om_data;
+    /* Check if the peer public key is same as our generated public key.
+     * Return fail if the public keys match. */
+    if (memcmp(cmd, ble_sm_sc_pub_key, 64) == 0) {
+        res->enc_cb = 1;
+        res->sm_err = BLE_SM_ERR_AUTHREQ;
+        return;
+    }
 
     ble_hs_lock();
     proc = ble_sm_proc_find(conn_handle, BLE_SM_PROC_STATE_PUBLIC_KEY, -1,
diff --git a/nimble/host/src/ble_store.c b/nimble/host/src/ble_store.c
index 22e6089471..574973904a 100644
--- a/nimble/host/src/ble_store.c
+++ b/nimble/host/src/ble_store.c
@@ -21,6 +21,8 @@
 
 #include "host/ble_store.h"
 #include "ble_hs_priv.h"
+#include "host/ble_gatt.h"
+#include "services/gatt/ble_svc_gatt.h"
 
 int
 ble_store_read(int obj_type, const union ble_store_key *key,
@@ -249,6 +251,64 @@ ble_store_write_peer_sec(const struct ble_store_value_sec *value_sec)
     return 0;
 }
 
+int
+ble_store_read_hash(uint16_t conn_handle, const struct ble_store_key_hash *key_hash,
+                         struct ble_store_value_hash *value_hash)
+{
+    union ble_store_value *store_value;
+    union ble_store_key *store_key;
+    int rc;
+    int rc_calc;
+
+    store_key = (void *)key_hash;
+    store_value = (void *)value_hash;
+    rc = ble_store_read(BLE_STORE_OBJ_TYPE_HASH, store_key, store_value);
+
+    struct ble_store_value_hash new_value_hash;
+    new_value_hash.peer_addr = key_hash->peer_addr;
+    rc_calc = ble_compute_db_hash(new_value_hash.db_hash);
+    if (rc_calc != 0) {
+        return rc_calc;
+    }
+    if (rc == 0) {
+        for(int i=0; i<16; i++){
+            if(memcmp(new_value_hash.db_hash, value_hash->db_hash, 16) != 0){
+                rc = 1;
+                break;
+            }
+        }
+    }
+
+    if (rc != 0) {
+        ble_store_write_hash(&new_value_hash);
+        ble_svc_conn_gatt_changed(conn_handle, 0x0001, 0xffff);
+    }
+
+    return rc;
+}
+
+int
+ble_store_write_hash(const struct ble_store_value_hash *value_hash)
+{
+    int rc;
+    union ble_store_value *store_value;
+    
+    store_value = (void *)value_hash;
+    rc = ble_store_write(BLE_STORE_OBJ_TYPE_HASH, store_value);
+    return rc;
+}
+
+int
+ble_store_delete_hash(const struct ble_store_key_hash *key)
+{
+    union ble_store_key *store_key;
+    int rc;
+
+    store_key = (void *)key;
+    rc = ble_store_delete(BLE_STORE_OBJ_TYPE_HASH, store_key);
+    return rc;
+}
+
 int
 ble_store_read_cccd(const struct ble_store_key_cccd *key,
                     struct ble_store_value_cccd *out_value)
@@ -306,6 +366,14 @@ ble_store_key_from_value_sec(struct ble_store_key_sec *out_key,
     out_key->idx = 0;
 }
 
+void
+ble_store_key_from_value_hash(struct ble_store_key_hash *out_key,
+                             const struct ble_store_value_hash *value)
+{
+    out_key->peer_addr = value->peer_addr;
+    out_key->idx = 0;
+}
+
 void
 ble_store_key_from_value(int obj_type,
                          union ble_store_key *out_key,
@@ -321,6 +389,10 @@ ble_store_key_from_value(int obj_type,
         ble_store_key_from_value_cccd(&out_key->cccd, &value->cccd);
         break;
 
+    case BLE_STORE_OBJ_TYPE_HASH:
+        ble_store_key_from_value_hash(&out_key->hash, &value->hash);
+        break;
+
     default:
         BLE_HS_DBG_ASSERT(0);
         break;
@@ -350,6 +422,10 @@ ble_store_iterate(int obj_type,
             key.cccd.peer_addr = *BLE_ADDR_ANY;
             pidx = &key.cccd.idx;
             break;
+        case BLE_STORE_OBJ_TYPE_HASH:
+            key.hash.peer_addr = *BLE_ADDR_ANY;
+            pidx = &key.hash.idx;
+            break;
         default:
             BLE_HS_DBG_ASSERT(0);
             return BLE_HS_EINVAL;
@@ -394,6 +470,7 @@ ble_store_clear(void)
         BLE_STORE_OBJ_TYPE_OUR_SEC,
         BLE_STORE_OBJ_TYPE_PEER_SEC,
         BLE_STORE_OBJ_TYPE_CCCD,
+        BLE_STORE_OBJ_TYPE_HASH
     };
     union ble_store_key key;
     int obj_type;
diff --git a/nimble/host/src/ble_store_util.c b/nimble/host/src/ble_store_util.c
index 7de482721b..39247f945a 100644
--- a/nimble/host/src/ble_store_util.c
+++ b/nimble/host/src/ble_store_util.c
@@ -101,7 +101,8 @@ ble_store_util_bonded_peers(ble_addr_t *out_peer_id_addrs, int *out_num_peers,
 
 /**
  * Deletes all entries from the store that are attached to the specified peer
- * address.  This function deletes security entries and CCCD records.
+ * address.  This function deletes security entries, CCCD records and stored 
+ * database hashes.
  *
  * @param peer_id_addr          Entries with this peer address get deleted.
  *
@@ -127,6 +128,11 @@ ble_store_util_delete_peer(const ble_addr_t *peer_id_addr)
         return rc;
     }
 
+    rc = ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_HASH, &key);
+    if (rc != 0) {
+        return rc;
+    }
+
     memset(&key, 0, sizeof key);
     key.cccd.peer_addr = *peer_id_addr;
 
diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml
index e72e8d529e..c814ba5695 100644
--- a/nimble/host/syscfg.yml
+++ b/nimble/host/syscfg.yml
@@ -133,6 +133,34 @@ syscfg.defs:
         description: 'Security manager secure connections (4.2).'
         value: 0
 
+    BLE_SM_SC_ONLY:
+        description: >
+            Force global Secure Connections Pairing Only mode. This means
+            that only SC pairing mode 1 level 4 shall be used, and all
+            characteristics will require it to access, except these
+            requiring mode 1 level 1.
+        value: 0
+        restrictions:
+            - 'BLE_SM_SC_LVL == 4 if 1'
+
+    BLE_SM_SC_LVL:
+        description: >
+          Force global Secure Connections mode 1 level. This level
+          describes requirements for pairing response/request received
+          to accept pairing:
+          - 1 - do not pair; only access to characteristics with no
+                authentication requirements is granted
+          - 2 - allow to pair despite MITM being on or off
+          - 3 - allow to pair only when MITM protection is on
+          - 4 - allow to pair only when 128 bit key is used and MITM is on
+          When set to 0 level is no forced and pairing is allowed for all
+          requests/responses with valid values (for example pairing will be
+          rejected with key longer than 128 bits). Successful pairing with
+          insufficient security will still cause denying access to protected
+          GATT characteristics.
+        value: 0
+        range: 0..4
+
     BLE_SM_MAX_PROCS:
         description: >
             The maximum number of concurrent security manager procedures.
diff --git a/nimble/host/test/pkg.yml b/nimble/host/test/pkg.yml
index dd1ad18bf3..cb5d97cfb3 100644
--- a/nimble/host/test/pkg.yml
+++ b/nimble/host/test/pkg.yml
@@ -31,4 +31,4 @@ pkg.deps.SELFTEST:
     - "@apache-mynewt-core/sys/console/stub"
     - "@apache-mynewt-core/sys/log/full"
     - "@apache-mynewt-core/sys/stats/stub"
-    - nimble/transport/ram
+    - nimble/transport
diff --git a/nimble/host/test/src/ble_gap_test.c b/nimble/host/test/src/ble_gap_test.c
index 7496e31681..876ef8f257 100644
--- a/nimble/host/test/src/ble_gap_test.c
+++ b/nimble/host/test/src/ble_gap_test.c
@@ -449,9 +449,10 @@ TEST_CASE_SELF(ble_gap_test_case_wl_bad_args)
 
     ble_gap_test_util_init();
 
-    /*** 0 white list entries. */
+    /*** 0 white list entries. This is acceptable now with the wl_set API
+     * change. */
     rc = ble_hs_test_util_wl_set(NULL, 0, 0, 0);
-    TEST_ASSERT(rc == BLE_HS_EINVAL);
+    TEST_ASSERT(rc == 0);
 
     /*** Invalid address type. */
     rc = ble_hs_test_util_wl_set(
diff --git a/nimble/host/test/src/ble_hs_test_util.h b/nimble/host/test/src/ble_hs_test_util.h
index 411443cfdd..7a0aa3eb19 100644
--- a/nimble/host/test/src/ble_hs_test_util.h
+++ b/nimble/host/test/src/ble_hs_test_util.h
@@ -162,6 +162,8 @@ int ble_hs_test_util_l2cap_rx_payload_flat(uint16_t conn_handle, uint16_t cid,
                                            const void *data, int len);
 uint8_t ble_hs_test_util_verify_tx_l2cap_sig(uint16_t opcode, void *cmd,
                                                  uint16_t cmd_size);
+uint8_t ble_hs_test_util_verify_tx_l2cap_discon_rej(uint16_t opcode, void *cmd,
+                                             uint16_t cmd_size);
 int ble_hs_test_util_inject_rx_l2cap_sig(uint16_t conn_handle, uint8_t opcode,
                                      uint8_t id, void *cmd, uint16_t cmd_size);
 void ble_hs_test_util_verify_tx_l2cap(struct os_mbuf *txom);
diff --git a/nimble/host/test/src/ble_l2cap_test.c b/nimble/host/test/src/ble_l2cap_test.c
index 2b17da06a7..0f55bc006b 100644
--- a/nimble/host/test/src/ble_l2cap_test.c
+++ b/nimble/host/test/src/ble_l2cap_test.c
@@ -999,6 +999,46 @@ ble_l2cap_test_coc_disc_by_peer(struct test_data *t)
                                         &req, sizeof(req)) == id);
 }
 
+static void
+ble_l2cap_test_coc_disc_by_peer_invalid_dcid(struct test_data *t)
+{
+    struct ble_l2cap_sig_disc_req req;
+    struct event *ev = &t->event[t->event_iter++];
+    uint8_t id = 10;
+    int rc;
+    struct os_mbuf *cmd;
+    uint16_t rej_err = htole16(BLE_L2CAP_SIG_ERR_INVALID_CID);
+    req.dcid = htole16(t->chan[0]->dcid + 1);
+    req.scid = htole16(t->chan[0]->dcid);
+
+    rc = ble_hs_test_util_inject_rx_l2cap_sig(2, BLE_L2CAP_SIG_OP_DISCONN_REQ,
+                                              id, &req, sizeof(req));
+    TEST_ASSERT(rc == 0);
+
+    /* Ensure callback got NOT called. */
+    TEST_ASSERT(!ev->handled);
+
+    struct {
+        uint16_t local_cid;
+        uint16_t remote_cid;
+    } data = {
+        .local_cid = req.scid,
+        .remote_cid = req.dcid,
+    };
+
+    /* Ensure an we sent back Command Reject response
+     * Recect command should contain reason  and CIDs pair
+     */
+    cmd = os_mbuf_get(&sdu_os_mbuf_pool, 0);
+    os_mbuf_append(cmd, &rej_err, sizeof(uint16_t));
+    os_mbuf_append(cmd, &data, sizeof(data));
+
+    ble_hs_test_util_verify_tx_l2cap_sig(
+        BLE_L2CAP_SIG_OP_REJECT,
+        cmd->om_data, cmd->om_len);
+    os_mbuf_free_chain(cmd);
+}
+
 static void
 ble_l2cap_test_coc_invalid_disc_by_peer(struct test_data *t)
 {
@@ -1361,6 +1401,29 @@ TEST_CASE_SELF(ble_l2cap_test_case_sig_coc_incoming_disconnect_failed)
     ble_hs_test_util_assert_mbufs_freed(NULL);
 }
 
+TEST_CASE_SELF(ble_l2cap_test_case_invalid_cid_in_disconnect_req)
+{
+    struct test_data t;
+
+    ble_l2cap_test_util_init();
+
+    ble_l2cap_test_set_chan_test_conf(BLE_L2CAP_TEST_PSM,
+    BLE_L2CAP_TEST_COC_MTU, &t);
+    t.expected_num_of_ev = 1;
+
+    t.event[0].type = BLE_L2CAP_EVENT_COC_CONNECTED;
+    t.event[0].app_status = 0;
+    t.event[0].l2cap_status = BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS;
+    t.event[1].type = BLE_L2CAP_EVENT_COC_DISCONNECTED;
+
+    ble_l2cap_test_coc_connect(&t);
+    ble_l2cap_test_coc_disc_by_peer_invalid_dcid(&t);
+
+    TEST_ASSERT(t.expected_num_of_ev == t.event_cnt);
+
+    ble_hs_test_util_assert_mbufs_freed(NULL);
+}
+
 TEST_CASE_SELF(ble_l2cap_test_case_coc_send_data_succeed)
 {
     struct test_data t;
@@ -1493,6 +1556,7 @@ TEST_SUITE(ble_l2cap_test_suite)
     ble_l2cap_test_case_sig_coc_disconnect_succeed();
     ble_l2cap_test_case_sig_coc_incoming_disconnect_succeed();
     ble_l2cap_test_case_sig_coc_incoming_disconnect_failed();
+    ble_l2cap_test_case_invalid_cid_in_disconnect_req();
     ble_l2cap_test_case_coc_send_data_succeed();
     ble_l2cap_test_case_coc_send_data_failed_too_big_sdu();
     ble_l2cap_test_case_coc_recv_data_succeed();
diff --git a/nimble/host/test/src/ble_sm_sc_test.c b/nimble/host/test/src/ble_sm_sc_test.c
index c3d195503c..cd453e023c 100644
--- a/nimble/host/test/src/ble_sm_sc_test.c
+++ b/nimble/host/test/src/ble_sm_sc_test.c
@@ -2285,6 +2285,612 @@ TEST_CASE_SELF(ble_sm_sc_us_pk_iio2_rio4_b1_iat0_rat0_ik7_rk5)
     ble_hs_test_util_assert_mbufs_freed(NULL);
 }
 
+/**
+ * Secure connections pairing
+ * Master: us
+ * Pair algorithm: passkey entry
+ * Initiator IO capabilities: 2
+ * Responder IO capabilities: 4
+ * Bonding: true
+ * Initiator address type: 0
+ * Responder address type: 0
+ * Initiator key distribution: 7
+ * Responder key distribution: 5
+ * Peer responds with same public key
+ */
+TEST_CASE_SELF(ble_sm_sc_us_pk_iio2_rio4_b1_iat0_rat0_ik7_rk5_peer_same_pk) {
+    struct ble_sm_test_params params;
+
+    params = (struct ble_sm_test_params) {
+        .init_id_addr = {
+            0x01, 0x01, 0x01, 0x07, 0x08, 0x01,
+        },
+        .resp_id_addr = {
+            0xca, 0x61, 0xa0, 0x67, 0x94, 0xe0,
+        },
+        .pair_req = {
+            .io_cap = 0x02,
+            .oob_data_flag = 0x00,
+            .authreq = 0x0d,
+            .max_enc_key_size = 0x10,
+            .init_key_dist = 0x07,
+            .resp_key_dist = 0x07,
+        },
+        .pair_rsp = {
+            .io_cap = 0x04,
+            .oob_data_flag = 0x00,
+            .authreq = 0x0d,
+            .max_enc_key_size = 0x10,
+            .init_key_dist = 0x07,
+            .resp_key_dist = 0x05,
+        },
+        .our_priv_key = {
+            0xb1, 0x6b, 0x4f, 0x81, 0xbc, 0xe3, 0x60, 0x9e,
+            0x00, 0x20, 0xf1, 0x73, 0x3e, 0xfb, 0xcc, 0x6e,
+            0x8c, 0xb6, 0xd2, 0x51, 0xd9, 0x36, 0x8a, 0x6d,
+            0xca, 0x8c, 0xd7, 0xbe, 0x96, 0x03, 0xdf, 0xd6,
+        },
+        .public_key_req = {
+            .x = {
+                0xe5, 0x0f, 0x02, 0x0a, 0x37, 0x90, 0x94, 0x5a,
+                0x06, 0x21, 0xf7, 0xbc, 0xd5, 0xbe, 0xb9, 0x24,
+                0x8a, 0x35, 0xfd, 0xf8, 0x5e, 0xe2, 0x70, 0xd5,
+                0x5a, 0xe8, 0xe7, 0xdd, 0x13, 0x90, 0xeb, 0xd4,
+            },
+            .y = {
+                0x41, 0xc8, 0x51, 0x1a, 0x25, 0x44, 0x01, 0x53,
+                0x42, 0x74, 0x07, 0x9c, 0x18, 0xe6, 0x3b, 0x8a,
+                0xce, 0x7a, 0x37, 0x1f, 0x18, 0x5c, 0x02, 0x7c,
+                0x67, 0x16, 0xf5, 0x30, 0x2b, 0x31, 0xa9, 0xc7,
+            },
+        },
+        .public_key_rsp = {
+            .x = {
+                0xe5, 0x0f, 0x02, 0x0a, 0x37, 0x90, 0x94, 0x5a,
+                0x06, 0x21, 0xf7, 0xbc, 0xd5, 0xbe, 0xb9, 0x24,
+                0x8a, 0x35, 0xfd, 0xf8, 0x5e, 0xe2, 0x70, 0xd5,
+                0x5a, 0xe8, 0xe7, 0xdd, 0x13, 0x90, 0xeb, 0xd4,
+            },
+            .y = {
+                0x41, 0xc8, 0x51, 0x1a, 0x25, 0x44, 0x01, 0x53,
+                0x42, 0x74, 0x07, 0x9c, 0x18, 0xe6, 0x3b, 0x8a,
+                0xce, 0x7a, 0x37, 0x1f, 0x18, 0x5c, 0x02, 0x7c,
+                0x67, 0x16, 0xf5, 0x30, 0x2b, 0x31, 0xa9, 0xc7,
+            },
+        },
+        .confirm_req[0] = {
+            .value = {
+                0x55, 0x2c, 0xaa, 0x41, 0x59, 0x42, 0x4d, 0xfe,
+                0x47, 0x74, 0xcd, 0x2b, 0x11, 0xab, 0x21, 0xe6,
+            },
+        },
+        .confirm_rsp[0] = {
+            .value = {
+                0x6a, 0x3c, 0x45, 0xf5, 0xb2, 0xe2, 0x04, 0x30,
+                0xde, 0xd6, 0x3c, 0x6d, 0x85, 0x00, 0x00, 0x2c,
+            },
+        },
+        .random_req[0] = {
+            .value = {
+                0x78, 0x06, 0x04, 0x60, 0x76, 0xe9, 0xc4, 0x5a,
+                0xfb, 0x34, 0x44, 0xae, 0x45, 0xa0, 0x84, 0xde,
+            },
+        },
+        .random_rsp[0] = {
+            .value = {
+                0x91, 0xc8, 0xfd, 0x1b, 0xb2, 0x85, 0x08, 0x76,
+                0xd3, 0xf1, 0xc4, 0xa0, 0xfa, 0x92, 0x8c, 0x94,
+            },
+        },
+        .confirm_req[1] = {
+            .value = {
+                0xb1, 0x2f, 0x68, 0x35, 0xa1, 0xa5, 0x84, 0xb1,
+                0x4f, 0x1a, 0xb1, 0xb5, 0xf0, 0xb2, 0xbe, 0x61,
+            },
+        },
+        .confirm_rsp[1] = {
+            .value = {
+                0x07, 0xd8, 0x43, 0x74, 0xe8, 0x42, 0xf3, 0xf1,
+                0x87, 0x3d, 0x9e, 0x92, 0xea, 0x33, 0xe8, 0x54,
+            },
+        },
+        .random_req[1] = {
+            .value = {
+                0x4c, 0xb7, 0xcc, 0x6d, 0x90, 0x9f, 0x1e, 0x2d,
+                0x9d, 0x1e, 0x52, 0xa7, 0xe0, 0x0c, 0x7b, 0xf7,
+            },
+        },
+        .random_rsp[1] = {
+            .value = {
+                0x5c, 0x32, 0x82, 0xc8, 0x76, 0x17, 0x3b, 0x18,
+                0x66, 0xda, 0xbf, 0xc3, 0x13, 0x49, 0x05, 0xfb,
+            },
+        },
+        .confirm_req[2] = {
+            .value = {
+                0x27, 0x61, 0x4d, 0x04, 0x64, 0xa9, 0x58, 0xf1,
+                0xe0, 0xf9, 0xe5, 0x78, 0x0b, 0x54, 0x89, 0x0a,
+            },
+        },
+        .confirm_rsp[2] = {
+            .value = {
+                0xe4, 0x8f, 0xdb, 0xc8, 0x35, 0xed, 0x4e, 0x7d,
+                0xbc, 0x92, 0x7f, 0x58, 0x02, 0xaa, 0xbf, 0x6b,
+            },
+        },
+        .random_req[2] = {
+            .value = {
+                0xfe, 0x85, 0x08, 0xe0, 0x35, 0x90, 0x13, 0xa9,
+                0xd3, 0xcf, 0xb6, 0x6d, 0x36, 0xaf, 0xbd, 0x59,
+            },
+        },
+        .random_rsp[2] = {
+            .value = {
+                0x47, 0x40, 0x8e, 0x97, 0xe3, 0xfe, 0x8f, 0x52,
+                0x29, 0x5e, 0x6b, 0x44, 0xdf, 0x0d, 0x60, 0xf4,
+            },
+        },
+        .confirm_req[3] = {
+            .value = {
+                0xac, 0xab, 0x13, 0x7c, 0x1a, 0x6e, 0x7a, 0xdb,
+                0xf6, 0xe8, 0x72, 0x9f, 0xc5, 0xc3, 0x99, 0x1b,
+            },
+        },
+        .confirm_rsp[3] = {
+            .value = {
+                0x79, 0xf2, 0xd1, 0x89, 0x5e, 0xa5, 0xa2, 0x90,
+                0xee, 0x25, 0x36, 0x81, 0x5a, 0x87, 0x20, 0x82,
+            },
+        },
+        .random_req[3] = {
+            .value = {
+                0xd4, 0x46, 0xa0, 0xc4, 0x3d, 0xae, 0x22, 0x06,
+                0xaf, 0x5d, 0x93, 0x96, 0xb7, 0x06, 0xc3, 0x61,
+            },
+        },
+        .random_rsp[3] = {
+            .value = {
+                0x5f, 0x81, 0x97, 0x8b, 0x52, 0x87, 0x1c, 0x67,
+                0xe0, 0x04, 0xcc, 0x50, 0xd9, 0x2b, 0x16, 0xb5,
+            },
+        },
+        .confirm_req[4] = {
+            .value = {
+                0x6c, 0x51, 0xc3, 0x61, 0x77, 0x7f, 0xf1, 0x05,
+                0x9e, 0x0f, 0xba, 0xfd, 0x32, 0x02, 0x09, 0x45,
+            },
+        },
+        .confirm_rsp[4] = {
+            .value = {
+                0x54, 0xe5, 0x24, 0x81, 0x62, 0x68, 0xe2, 0x45,
+                0x86, 0x2c, 0x11, 0x28, 0x15, 0xa8, 0x8e, 0x5b,
+            },
+        },
+        .random_req[4] = {
+            .value = {
+                0xbb, 0x29, 0x3a, 0xba, 0xe6, 0x4f, 0x06, 0xcf,
+                0xa3, 0x13, 0x27, 0xf2, 0xcb, 0xe4, 0xd2, 0xe6,
+            },
+        },
+        .random_rsp[4] = {
+            .value = {
+                0x50, 0xba, 0xd0, 0x0e, 0x26, 0xab, 0x04, 0xf8,
+                0xa2, 0x03, 0x1e, 0x63, 0x9a, 0xf7, 0x15, 0xdc,
+            },
+        },
+        .confirm_req[5] = {
+            .value = {
+                0x12, 0x3e, 0xfe, 0x5a, 0xb1, 0x09, 0x6f, 0x17,
+                0xb7, 0x77, 0x7e, 0x65, 0x88, 0xd4, 0x95, 0x56,
+            },
+        },
+        .confirm_rsp[5] = {
+            .value = {
+                0xc6, 0x9b, 0xac, 0xde, 0x7e, 0x03, 0x7a, 0xd3,
+                0xf1, 0xff, 0x3c, 0x4f, 0x4a, 0x85, 0xba, 0x73,
+            },
+        },
+        .random_req[5] = {
+            .value = {
+                0x17, 0xd5, 0x5e, 0x69, 0x30, 0x2c, 0x1f, 0x01,
+                0x87, 0x9c, 0xd6, 0xd2, 0xe4, 0x48, 0x8c, 0x84,
+            },
+        },
+        .random_rsp[5] = {
+            .value = {
+                0x9d, 0x54, 0x83, 0x4a, 0xcd, 0x93, 0x7c, 0x1e,
+                0x5b, 0xaf, 0xd2, 0x66, 0x8c, 0x2d, 0xaa, 0xc3,
+            },
+        },
+        .confirm_req[6] = {
+            .value = {
+                0xdc, 0x24, 0x69, 0xa8, 0xd3, 0xa9, 0x17, 0x11,
+                0x08, 0x37, 0x1a, 0x1e, 0x92, 0x03, 0xee, 0x36,
+            },
+        },
+        .confirm_rsp[6] = {
+            .value = {
+                0x98, 0xf8, 0x72, 0x71, 0x99, 0xa0, 0xbd, 0xcd,
+                0xb1, 0x97, 0x4c, 0x8a, 0xb8, 0xa8, 0x1a, 0x52,
+            },
+        },
+        .random_req[6] = {
+            .value = {
+                0xbf, 0xb1, 0x8e, 0xa5, 0x14, 0xe3, 0xeb, 0x9e,
+                0x29, 0x27, 0xe0, 0x19, 0xb1, 0xb2, 0x5c, 0xfe,
+            },
+        },
+        .random_rsp[6] = {
+            .value = {
+                0xae, 0x8a, 0x92, 0x78, 0x53, 0x7b, 0xdb, 0x8c,
+                0xec, 0x3a, 0x99, 0x2b, 0x94, 0xf1, 0x17, 0xfe,
+            },
+        },
+        .confirm_req[7] = {
+            .value = {
+                0xcf, 0xaf, 0x70, 0x73, 0x53, 0x65, 0x89, 0x57,
+                0x36, 0x98, 0xd2, 0x28, 0x86, 0x79, 0xfe, 0x85,
+            },
+        },
+        .confirm_rsp[7] = {
+            .value = {
+                0x0d, 0x2d, 0x77, 0x8a, 0x21, 0x11, 0xd9, 0x61,
+                0x9f, 0x80, 0x32, 0x8a, 0x32, 0x09, 0x42, 0x42,
+            },
+        },
+        .random_req[7] = {
+            .value = {
+                0x8b, 0xd2, 0x53, 0xcd, 0x96, 0xd1, 0x14, 0xb5,
+                0xea, 0x17, 0xb1, 0xa3, 0xa8, 0xfc, 0x3c, 0x2b,
+            },
+        },
+        .random_rsp[7] = {
+            .value = {
+                0xc2, 0x4f, 0x84, 0x60, 0x54, 0x79, 0x16, 0xed,
+                0x1a, 0x6e, 0x78, 0xa0, 0x99, 0x58, 0xf2, 0x94,
+            },
+        },
+        .confirm_req[8] = {
+            .value = {
+                0x9a, 0x4c, 0xbc, 0x9c, 0x55, 0x15, 0xa2, 0x4f,
+                0xa2, 0x5d, 0x3b, 0xa7, 0x43, 0xb3, 0x9c, 0x63,
+            },
+        },
+        .confirm_rsp[8] = {
+            .value = {
+                0xa3, 0xb1, 0x88, 0xa5, 0x70, 0xca, 0xa3, 0xa9,
+                0x67, 0x2a, 0xac, 0x99, 0x5e, 0x61, 0x68, 0xa0,
+            },
+        },
+        .random_req[8] = {
+            .value = {
+                0xcf, 0xcf, 0x5b, 0x94, 0xe0, 0xb2, 0x9d, 0x5a,
+                0x86, 0x71, 0x45, 0xce, 0xd9, 0xce, 0x13, 0xba,
+            },
+        },
+        .random_rsp[8] = {
+            .value = {
+                0x10, 0x96, 0x8a, 0x50, 0xa4, 0xd0, 0xaa, 0x5f,
+                0xd6, 0x32, 0xdb, 0x09, 0x7e, 0x22, 0x96, 0x42,
+            },
+        },
+        .confirm_req[9] = {
+            .value = {
+                0xf0, 0x90, 0x61, 0x25, 0x04, 0x29, 0x4f, 0xb6,
+                0x8b, 0xd5, 0x73, 0x49, 0xbd, 0xf7, 0x9b, 0xe7,
+            },
+        },
+        .confirm_rsp[9] = {
+            .value = {
+                0x5b, 0xe6, 0xb4, 0x3f, 0x1b, 0x77, 0x12, 0x75,
+                0x84, 0x94, 0xc6, 0x07, 0xfa, 0xa1, 0x41, 0x94,
+            },
+        },
+        .random_req[9] = {
+            .value = {
+                0x3d, 0x1a, 0xa3, 0x95, 0xec, 0x72, 0x84, 0xf4,
+                0xc5, 0xcd, 0xaa, 0x48, 0xe9, 0x0c, 0x0f, 0xe3,
+            },
+        },
+        .random_rsp[9] = {
+            .value = {
+                0x8a, 0x5a, 0x53, 0xfc, 0x07, 0x52, 0x01, 0xb9,
+                0xe9, 0x2d, 0xe7, 0x9d, 0x8c, 0x7c, 0xc7, 0xb3,
+            },
+        },
+        .confirm_req[10] = {
+            .value = {
+                0xe7, 0x8e, 0xc5, 0x08, 0x7f, 0x7e, 0xb8, 0xdc,
+                0x05, 0x88, 0x3a, 0x92, 0x5a, 0xf5, 0x9b, 0xa9,
+            },
+        },
+        .confirm_rsp[10] = {
+            .value = {
+                0xf7, 0xa2, 0xb6, 0xec, 0xcd, 0xef, 0xcb, 0xb7,
+                0x6f, 0xc3, 0xac, 0x17, 0xe2, 0xfd, 0xfa, 0x42,
+            },
+        },
+        .random_req[10] = {
+            .value = {
+                0x0d, 0xd1, 0xa2, 0x1d, 0xff, 0x74, 0xc5, 0x99,
+                0xe0, 0x67, 0x07, 0x99, 0x95, 0x75, 0x39, 0x76,
+            },
+        },
+        .random_rsp[10] = {
+            .value = {
+                0x2f, 0x13, 0xd1, 0x59, 0xfe, 0x20, 0x60, 0xf0,
+                0x02, 0x0c, 0xea, 0x79, 0xd7, 0x40, 0x86, 0x85,
+            },
+        },
+        .confirm_req[11] = {
+            .value = {
+                0x8b, 0x57, 0x87, 0xdd, 0xb1, 0xcc, 0x2d, 0x65,
+                0xc1, 0xba, 0xac, 0x88, 0x48, 0x23, 0xda, 0xe7,
+            },
+        },
+        .confirm_rsp[11] = {
+            .value = {
+                0xb3, 0xc4, 0x2e, 0xea, 0x33, 0xaf, 0x12, 0x9c,
+                0xb5, 0xab, 0xa1, 0x95, 0x30, 0xca, 0x46, 0x48,
+            },
+        },
+        .random_req[11] = {
+            .value = {
+                0x35, 0x57, 0xcd, 0xd5, 0xd2, 0xf8, 0xd7, 0xf2,
+                0x7b, 0xe3, 0xd7, 0xba, 0x31, 0xa5, 0xca, 0xfd,
+            },
+        },
+        .random_rsp[11] = {
+            .value = {
+                0xe2, 0x3b, 0x20, 0xbe, 0xec, 0xa5, 0x34, 0x3b,
+                0x76, 0x23, 0x53, 0x28, 0x36, 0xc4, 0x60, 0x13,
+            },
+        },
+        .confirm_req[12] = {
+            .value = {
+                0xc9, 0xfe, 0x03, 0x49, 0xe4, 0xff, 0x7e, 0xf7,
+                0x00, 0xd1, 0x2b, 0x13, 0xb1, 0x15, 0x6e, 0x92,
+            },
+        },
+        .confirm_rsp[12] = {
+            .value = {
+                0xbc, 0xa2, 0xf2, 0x03, 0x5c, 0xfd, 0x20, 0x7b,
+                0xd0, 0x1f, 0xd6, 0x50, 0xec, 0xc6, 0x7b, 0x31,
+            },
+        },
+        .random_req[12] = {
+            .value = {
+                0x04, 0x50, 0xea, 0xb8, 0xca, 0x36, 0x1a, 0x61,
+                0x92, 0xed, 0xa0, 0x67, 0x78, 0x15, 0x10, 0xb5,
+            },
+        },
+        .random_rsp[12] = {
+            .value = {
+                0x0c, 0x8e, 0x9d, 0x7b, 0x9d, 0x7e, 0xda, 0x23,
+                0xbb, 0x61, 0xd9, 0xff, 0x46, 0x77, 0x33, 0x1b,
+            },
+        },
+        .confirm_req[13] = {
+            .value = {
+                0x9a, 0xff, 0xd6, 0xe5, 0x1a, 0xc3, 0xd3, 0x37,
+                0x34, 0xeb, 0x3e, 0x3a, 0x8e, 0x0b, 0x86, 0xb4,
+            },
+        },
+        .confirm_rsp[13] = {
+            .value = {
+                0xf6, 0x32, 0x19, 0xb4, 0x08, 0x6b, 0x8a, 0x0f,
+                0xc9, 0x9c, 0x1b, 0x68, 0xb8, 0xa0, 0xd0, 0xc9,
+            },
+        },
+        .random_req[13] = {
+            .value = {
+                0x86, 0xeb, 0x5c, 0xf9, 0x33, 0x54, 0x7d, 0xe4,
+                0xa4, 0xe2, 0xe1, 0xf6, 0x6b, 0xea, 0x34, 0xed,
+            },
+        },
+        .random_rsp[13] = {
+            .value = {
+                0xad, 0x53, 0xa0, 0x6e, 0xde, 0x1d, 0xda, 0x99,
+                0x31, 0x45, 0xe5, 0x3a, 0x73, 0xa1, 0x5e, 0xe1,
+            },
+        },
+        .confirm_req[14] = {
+            .value = {
+                0x93, 0xd4, 0xe0, 0xaa, 0x0c, 0x91, 0xba, 0xde,
+                0xc9, 0x5c, 0x68, 0xb0, 0xce, 0xb6, 0x84, 0xcd,
+            },
+        },
+        .confirm_rsp[14] = {
+            .value = {
+                0x85, 0xc7, 0x05, 0x02, 0x21, 0x9d, 0x4c, 0x4c,
+                0x16, 0xf7, 0x8f, 0x7b, 0xaa, 0xb4, 0x8f, 0x37,
+            },
+        },
+        .random_req[14] = {
+            .value = {
+                0x84, 0xfd, 0xf1, 0x39, 0x1a, 0x9a, 0xa5, 0xb8,
+                0x49, 0xc0, 0x66, 0xdc, 0x33, 0x71, 0x32, 0x87,
+            },
+        },
+        .random_rsp[14] = {
+            .value = {
+                0x5d, 0xaf, 0x38, 0xcd, 0xb5, 0x83, 0xaa, 0xa0,
+                0xab, 0x30, 0x82, 0xed, 0x6f, 0xd2, 0x75, 0xe7,
+            },
+        },
+        .confirm_req[15] = {
+            .value = {
+                0x88, 0x12, 0xe8, 0x89, 0xd4, 0x52, 0x6d, 0xac,
+                0x61, 0x2a, 0x85, 0x85, 0x1e, 0x9c, 0x82, 0x21,
+            },
+        },
+        .confirm_rsp[15] = {
+            .value = {
+                0xc1, 0xe9, 0xcd, 0x21, 0x29, 0x6a, 0x78, 0xe4,
+                0x7b, 0x7d, 0x73, 0x25, 0x9e, 0x9b, 0x95, 0x8b,
+            },
+        },
+        .random_req[15] = {
+            .value = {
+                0x95, 0x87, 0x9d, 0x5a, 0x10, 0x14, 0xa0, 0xdf,
+                0x5e, 0x02, 0x22, 0x39, 0x23, 0xc9, 0xbc, 0xba,
+            },
+        },
+        .random_rsp[15] = {
+            .value = {
+                0x1b, 0x91, 0xe2, 0xdf, 0xca, 0xfe, 0x2b, 0x61,
+                0x33, 0x8c, 0x83, 0xbf, 0xcf, 0xc3, 0x72, 0xcc,
+            },
+        },
+        .confirm_req[16] = {
+            .value = {
+                0xce, 0xc9, 0x68, 0xf7, 0xea, 0x41, 0x18, 0x5c,
+                0x16, 0x6a, 0x98, 0x13, 0x0c, 0x10, 0xc2, 0xa3,
+            },
+        },
+        .confirm_rsp[16] = {
+            .value = {
+                0x97, 0x73, 0xc9, 0x72, 0x68, 0x99, 0x63, 0xed,
+                0x81, 0x3b, 0x5c, 0xee, 0x37, 0xfc, 0xca, 0xae,
+            },
+        },
+        .random_req[16] = {
+            .value = {
+                0x5b, 0x85, 0xb0, 0x1b, 0xc3, 0xde, 0x18, 0xba,
+                0xc1, 0xc7, 0x89, 0x99, 0xfe, 0xcd, 0xdb, 0x6a,
+            },
+        },
+        .random_rsp[16] = {
+            .value = {
+                0x5e, 0x1a, 0xcb, 0xbc, 0xda, 0x41, 0x06, 0x5a,
+                0x14, 0x34, 0x3a, 0xb1, 0xa1, 0x6f, 0xb2, 0xd8,
+            },
+        },
+        .confirm_req[17] = {
+            .value = {
+                0x1d, 0x59, 0x8a, 0xb0, 0x19, 0xe5, 0xff, 0x45,
+                0xb6, 0xc3, 0x33, 0x64, 0xd1, 0x6e, 0xee, 0xdd,
+            },
+        },
+        .confirm_rsp[17] = {
+            .value = {
+                0x4c, 0x9b, 0xe8, 0x68, 0x52, 0x34, 0xef, 0xe1,
+                0x84, 0xbd, 0x37, 0x85, 0x53, 0x0d, 0xd5, 0xc1,
+            },
+        },
+        .random_req[17] = {
+            .value = {
+                0xa6, 0xf7, 0x97, 0x18, 0x9a, 0x3e, 0x9d, 0xcf,
+                0x91, 0xa3, 0xa3, 0x8e, 0xda, 0x8f, 0x8f, 0x90,
+            },
+        },
+        .random_rsp[17] = {
+            .value = {
+                0x94, 0x10, 0x19, 0x17, 0x8d, 0x0a, 0x72, 0xfd,
+                0x24, 0x9d, 0xfd, 0x37, 0x4e, 0xdf, 0x4c, 0x30,
+            },
+        },
+        .confirm_req[18] = {
+            .value = {
+                0xfc, 0x64, 0x8a, 0x8b, 0x37, 0x17, 0x90, 0x6d,
+                0x25, 0x0e, 0xc6, 0x18, 0xc9, 0xc9, 0xc2, 0x2a,
+            },
+        },
+        .confirm_rsp[18] = {
+            .value = {
+                0x50, 0x98, 0x86, 0xf5, 0xc0, 0xda, 0x45, 0x2d,
+                0xea, 0xc8, 0x9d, 0x28, 0x04, 0xd8, 0x73, 0x6f,
+            },
+        },
+        .random_req[18] = {
+            .value = {
+                0x13, 0x10, 0x38, 0xe8, 0x17, 0x6d, 0x72, 0xd5,
+                0x94, 0xaf, 0xed, 0x4f, 0x23, 0xa0, 0x41, 0xfc,
+            },
+        },
+        .random_rsp[18] = {
+            .value = {
+                0xdf, 0xed, 0xf7, 0x08, 0xce, 0x64, 0xbc, 0x11,
+                0x41, 0x7a, 0xd9, 0xf7, 0x4a, 0xd9, 0x4a, 0x15,
+            },
+        },
+        .confirm_req[19] = {
+            .value = {
+                0xae, 0x24, 0x8f, 0xdf, 0xb0, 0x57, 0xc4, 0x9c,
+                0xe6, 0xae, 0x9b, 0xc2, 0x4d, 0x3d, 0x1c, 0xcb,
+            },
+        },
+        .confirm_rsp[19] = {
+            .value = {
+                0xcc, 0x5c, 0xa3, 0xbe, 0xd7, 0x83, 0xee, 0x60,
+                0x80, 0xff, 0x5f, 0x1a, 0x07, 0xbf, 0x4c, 0x33,
+            },
+        },
+        .random_req[19] = {
+            .value = {
+                0x93, 0xc3, 0x62, 0x06, 0xcb, 0xe5, 0xb0, 0x01,
+                0x02, 0x18, 0xa2, 0x50, 0x4c, 0x73, 0xa2, 0x27,
+            },
+        },
+        .random_rsp[19] = {
+            .value = {
+                0x11, 0x2a, 0xd3, 0x06, 0x28, 0x9c, 0xdf, 0x73,
+                0xa5, 0xa4, 0xe5, 0x1e, 0x07, 0xcf, 0xee, 0x71,
+            },
+        },
+        .dhkey_check_req = {
+            .value = {
+                0x73, 0xa0, 0x40, 0x58, 0x78, 0x20, 0x5f, 0x2c,
+                0xf4, 0x19, 0x23, 0xa8, 0x74, 0xbd, 0xc2, 0x3e,
+            },
+        },
+        .dhkey_check_rsp = {
+            .value = {
+                0x5a, 0x30, 0xbc, 0xce, 0xec, 0xdf, 0xf0, 0x32,
+                0x3c, 0x18, 0xa3, 0xd3, 0x3f, 0x20, 0x87, 0x10,
+            },
+        },
+        .sign_info_req = {
+            .sig_key = {
+                0x2e, 0x81, 0x09, 0xde, 0x32, 0xc5, 0x28, 0x34,
+                0xe1, 0x45, 0x4a, 0x35, 0x49, 0xef, 0xa2, 0xed,
+            },
+        },
+        .id_info_rsp = {
+            .irk = {
+                0xef, 0x8d, 0xe2, 0x16, 0x4f, 0xec, 0x43, 0x0d,
+                0xbf, 0x5b, 0xdd, 0x34, 0xc0, 0x53, 0x1e, 0xb8,
+            },
+        },
+        .id_addr_info_rsp = {
+            .addr_type = 0,
+            .bd_addr = {
+                0x01, 0x01, 0x01, 0x07, 0x08, 0x01,
+            },
+        },
+        .sign_info_rsp = {
+            .sig_key = {
+                0x90, 0x3d, 0x26, 0x65, 0xc1, 0xd1, 0x5a, 0x9d,
+                0xda, 0xab, 0x0d, 0x00, 0x05, 0x0e, 0x6c, 0x5d,
+            },
+        },
+        .ltk = {
+            0xf1, 0x41, 0x1a, 0x5b, 0x60, 0xc1, 0x43, 0xc6,
+            0x80, 0x34, 0x5e, 0x7f, 0xd8, 0x0c, 0x75, 0xdc,
+        },
+        .pair_alg = BLE_SM_PAIR_ALG_PASSKEY,
+        .authenticated = 1,
+        .passkey_info = {
+            .passkey = {
+                .action = BLE_SM_IOACT_INPUT,
+                .passkey = 516645,
+            },
+        },
+    };
+    ble_sm_test_util_us_sc_bad(&params);
+
+    ble_hs_test_util_assert_mbufs_freed(NULL);
+}
 /**
  * Secure connections pairing
  * Master: us
diff --git a/nimble/host/test/src/ble_sm_test_util.c b/nimble/host/test/src/ble_sm_test_util.c
index 6170371f51..bad473f178 100644
--- a/nimble/host/test/src/ble_sm_test_util.c
+++ b/nimble/host/test/src/ble_sm_test_util.c
@@ -844,6 +844,35 @@ ble_sm_test_util_rx_public_key(uint16_t conn_handle,
     TEST_ASSERT_FATAL(rc == 0);
 }
 
+static void
+ble_sm_test_util_rx_public_key_bad(uint16_t conn_handle,
+                                   struct ble_sm_public_key *cmd)
+{
+    struct hci_data_hdr hci_hdr;
+    struct os_mbuf *om;
+    void *v;
+    int payload_len;
+    int rc;
+
+    hci_hdr = BLE_SM_TEST_UTIL_HCI_HDR(
+        2, BLE_HCI_PB_FIRST_FLUSH,
+        BLE_L2CAP_HDR_SZ + sizeof(struct ble_sm_hdr) + sizeof(struct ble_sm_public_key));
+
+    om = ble_hs_mbuf_l2cap_pkt();
+    TEST_ASSERT_FATAL(om != NULL);
+
+    payload_len = sizeof(struct ble_sm_hdr) + sizeof(struct ble_sm_public_key);
+
+    v = os_mbuf_extend(om, payload_len);
+    TEST_ASSERT_FATAL(v != NULL);
+
+    ble_sm_public_key_write(v, payload_len, cmd);
+
+    rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SM,
+                                              &hci_hdr, om);
+    TEST_ASSERT_FATAL(rc != 0);
+}
+
 static void
 ble_sm_test_util_rx_dhkey_check(uint16_t conn_handle,
                                 struct ble_sm_dhkey_check *cmd,
@@ -2567,6 +2596,52 @@ ble_sm_test_util_us_sc_good_once_no_init(
     ble_hs_test_util_conn_disconnect(2);
 }
 
+static void
+ble_sm_test_util_us_sc_bad_once_no_init(struct ble_sm_test_params *params,
+                                        struct ble_hs_conn *conn,
+                                        struct ble_sm_test_util_entity *our_entity,
+                                        struct ble_sm_test_util_entity *peer_entity)
+{
+    int rc;
+
+    TEST_ASSERT(!conn->bhc_sec_state.encrypted);
+    TEST_ASSERT(ble_sm_num_procs() == 0);
+
+    ble_hs_test_util_hci_ack_set(
+        ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
+                                    BLE_HCI_OCF_LE_START_ENCRYPT), 0);
+    if (params->sec_req.authreq != 0) {
+        ble_sm_test_util_rx_sec_req(2, &params->sec_req, 0);
+    } else {
+        /* Initiate the pairing procedure. */
+        rc = ble_gap_security_initiate(2);
+        TEST_ASSERT_FATAL(rc == 0);
+    }
+
+    /* Ensure we sent the expected pair request. */
+    ble_sm_test_util_verify_tx_pair_req(our_entity->pair_cmd);
+    TEST_ASSERT(!conn->bhc_sec_state.encrypted);
+    TEST_ASSERT(ble_sm_num_procs() == 1);
+    ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
+
+    /* Receive a pair response from the peer. */
+    ble_sm_test_util_rx_pair_rsp(2, peer_entity->pair_cmd, 0);
+    TEST_ASSERT(!conn->bhc_sec_state.encrypted);
+    TEST_ASSERT(ble_sm_num_procs() == 1);
+    ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
+
+    /* Ensure we sent the expected public key. */
+    ble_sm_test_util_verify_tx_public_key(our_entity->public_key);
+    TEST_ASSERT(!conn->bhc_sec_state.encrypted);
+    TEST_ASSERT(ble_sm_num_procs() == 1);
+    ble_sm_test_util_io_inject_bad(2, params->passkey_info.passkey.action);
+
+    /* Receive a wrong public key from the peer. */
+    ble_sm_test_util_rx_public_key_bad(2, peer_entity->public_key);
+    TEST_ASSERT(!conn->bhc_sec_state.encrypted);
+    TEST_ASSERT(ble_sm_num_procs() == 1);
+}
+
 static void
 ble_sm_test_util_us_sc_good_once(struct ble_sm_test_params *params)
 {
@@ -2579,6 +2654,17 @@ ble_sm_test_util_us_sc_good_once(struct ble_sm_test_params *params)
         params, conn, &our_entity, &peer_entity);
 }
 
+static void
+ble_sm_test_util_us_sc_bad_once(struct ble_sm_test_params *params)
+{
+    struct ble_sm_test_util_entity peer_entity;
+    struct ble_sm_test_util_entity our_entity;
+    struct ble_hs_conn *conn;
+
+    ble_sm_test_util_init_good(params, 1, &conn, &our_entity, &peer_entity);
+    ble_sm_test_util_us_sc_bad_once_no_init(
+        params, conn, &our_entity, &peer_entity);
+}
 void
 ble_sm_test_util_us_sc_good(struct ble_sm_test_params *params)
 {
@@ -2610,12 +2696,22 @@ ble_sm_test_util_us_sc_good(struct ble_sm_test_params *params)
     TEST_ASSERT(ble_hs_test_util_num_peer_secs() == 0);
 }
 
+void
+ble_sm_test_util_us_sc_bad(struct ble_sm_test_params *params)
+{
+    /*** We are master. */
+
+    /* We initiate pairing. */
+    params->passkey_info.io_before_rx = 0;
+    params->sec_req.authreq = 0;
+    ble_sm_test_util_us_sc_bad_once(params);
+}
+
 static void
-ble_sm_test_util_peer_sc_good_once_no_init(
-    struct ble_sm_test_params *params,
-    struct ble_hs_conn *conn,
-    struct ble_sm_test_util_entity *our_entity,
-    struct ble_sm_test_util_entity *peer_entity)
+ble_sm_test_util_peer_sc_good_once_no_init(struct ble_sm_test_params *params,
+                                           struct ble_hs_conn *conn,
+                                           struct ble_sm_test_util_entity *our_entity,
+                                           struct ble_sm_test_util_entity *peer_entity)
 {
     int num_iters;
     int rc;
diff --git a/nimble/host/test/src/ble_sm_test_util.h b/nimble/host/test/src/ble_sm_test_util.h
index d8629b60c0..d9aa3805c7 100644
--- a/nimble/host/test/src/ble_sm_test_util.h
+++ b/nimble/host/test/src/ble_sm_test_util.h
@@ -119,6 +119,7 @@ void ble_sm_test_util_peer_lgcy_good(struct ble_sm_test_params *params);
 void ble_sm_test_util_peer_bonding_bad(uint16_t ediv, uint64_t rand_num);
 void ble_sm_test_util_peer_sc_good(struct ble_sm_test_params *params);
 void ble_sm_test_util_us_sc_good(struct ble_sm_test_params *params);
+void ble_sm_test_util_us_sc_bad(struct ble_sm_test_params *params);
 void ble_sm_test_util_us_fail_inval(struct ble_sm_test_params *params);
 
 #ifdef __cplusplus
diff --git a/nimble/host/test/syscfg.yml b/nimble/host/test/syscfg.yml
index 6307398e4c..45bc638b62 100644
--- a/nimble/host/test/syscfg.yml
+++ b/nimble/host/test/syscfg.yml
@@ -29,3 +29,4 @@ syscfg.vals:
     CONFIG_FCB: 1
     BLE_VERSION: 52
     BLE_L2CAP_ENHANCED_COC: 1
+    BLE_HCI_TRANSPORT: ram
diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h
index 248e8ae834..d349afebb0 100644
--- a/nimble/include/nimble/ble.h
+++ b/nimble/include/nimble/ble.h
@@ -88,6 +88,9 @@ struct ble_mbuf_hdr_rxinfo
  *       set for the same PDU (e.g. one use by scanner, other one used by
  *       connection)
  */
+#define BLE_MBUF_HDR_F_CONNECT_IND_TXD  (0x4000)
+#define BLE_MBUF_HDR_F_CONNECT_REQ_TXD  (0x4000)
+#define BLE_MBUF_HDR_F_CONNECT_RSP_RXD  (0x0008)
 #define BLE_MBUF_HDR_F_CONN_CREDIT      (0x8000)
 #define BLE_MBUF_HDR_F_IGNORED          (0x8000)
 #define BLE_MBUF_HDR_F_SCAN_REQ_TXD     (0x4000)
@@ -109,9 +112,9 @@ struct ble_mbuf_hdr_rxinfo
 struct ble_mbuf_hdr_txinfo
 {
     uint8_t flags;
-    uint8_t offset;
-    uint8_t pyld_len;
     uint8_t hdr_byte;
+    uint16_t offset;
+    uint16_t pyld_len;
 };
 
 struct ble_mbuf_hdr
diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h
index 92031c3c16..e4830833b3 100644
--- a/nimble/include/nimble/hci_common.h
+++ b/nimble/include/nimble/hci_common.h
@@ -1061,12 +1061,24 @@ struct ble_hci_le_set_host_feat_cp {
     uint8_t val;
 } __attribute__((packed));
 
-/* --- Vendor specific commands (OGF 0x00FF) */
-#define BLE_HCI_OCF_VS_RD_STATIC_ADDR                 (0x0001)
+/* --- Vendor specific commands (OGF 0x003F) */
+/* Read Random Static Address */
+#define BLE_HCI_OCF_VS_RD_STATIC_ADDR                   (0x0001)
 struct ble_hci_vs_rd_static_addr_rp {
     uint8_t addr[6];
 } __attribute__((packed));
 
+/* Set default transmit power. Actual selected TX power is returned
+ * in reply. Setting 0xff restores controller reset default.
+ */
+#define BLE_HCI_OCF_VS_SET_TX_PWR                       (0x0002)
+struct ble_hci_vs_set_tx_pwr_cp {
+    int8_t tx_power;
+} __attribute__((packed));
+struct ble_hci_vs_set_tx_pwr_rp {
+    int8_t tx_power;
+} __attribute__((packed));
+
 /* Command Specific Definitions */
 /* --- Set controller to host flow control (OGF 0x03, OCF 0x0031) --- */
 #define BLE_HCI_CTLR_TO_HOST_FC_OFF         (0)
@@ -1152,11 +1164,13 @@ struct ble_hci_vs_rd_static_addr_rp {
 
 /* Scan interval and scan window timing */
 #define BLE_HCI_SCAN_ITVL                   (625)           /* usecs */
-#define BLE_HCI_SCAN_ITVL_MIN               (4)             /* units */
-#define BLE_HCI_SCAN_ITVL_MAX               (16384)         /* units */
+#define BLE_HCI_SCAN_ITVL_MIN               (0x0004)        /* units */
+#define BLE_HCI_SCAN_ITVL_MAX               (0x4000)        /* units */
+#define BLE_HCI_SCAN_ITVL_MAX_EXT           (0xffff)        /* units */
 #define BLE_HCI_SCAN_ITVL_DEF               (16)            /* units */
-#define BLE_HCI_SCAN_WINDOW_MIN             (4)             /* units */
-#define BLE_HCI_SCAN_WINDOW_MAX             (16384)         /* units */
+#define BLE_HCI_SCAN_WINDOW_MIN             (0x0004)        /* units */
+#define BLE_HCI_SCAN_WINDOW_MAX             (0x4000)        /* units */
+#define BLE_HCI_SCAN_WINDOW_MAX_EXT         (0xffff)        /* units */
 #define BLE_HCI_SCAN_WINDOW_DEF             (16)            /* units */
 
 /*
@@ -1444,8 +1458,8 @@ struct ble_hci_ev_auth_pyld_tmo {
 
 #define BLE_HCI_EVCODE_SAM_STATUS_CHG       (0x58)
 
-#define BLE_HCI_EVCODE_VENDOR_DEBUG         (0xFF)
-struct ble_hci_ev_vendor_debug {
+#define BLE_HCI_EVCODE_VS_DEBUG             (0xFF)
+struct ble_hci_ev_vs_debug {
     uint8_t id;
     uint8_t data[0];
 } __attribute__((packed));
@@ -1826,6 +1840,7 @@ struct ble_hci_ev_le_subev_biginfo_adv_report {
 #define BLE_HCI_VER_BCS_5_0                 (9)
 #define BLE_HCI_VER_BCS_5_1                 (10)
 #define BLE_HCI_VER_BCS_5_2                 (11)
+#define BLE_HCI_VER_BCS_5_3                 (12)
 
 #define BLE_LMP_VER_BCS_1_0b                (0)
 #define BLE_LMP_VER_BCS_1_1                 (1)
@@ -1839,6 +1854,7 @@ struct ble_hci_ev_le_subev_biginfo_adv_report {
 #define BLE_LMP_VER_BCS_5_0                 (9)
 #define BLE_LMP_VER_BCS_5_1                 (10)
 #define BLE_LMP_VER_BCS_5_2                 (11)
+#define BLE_LMP_VER_BCS_5_3                 (12)
 
 /* selected HCI and LMP version */
 #if MYNEWT_VAL(BLE_VERSION) == 50
@@ -1850,7 +1866,9 @@ struct ble_hci_ev_le_subev_biginfo_adv_report {
 #elif MYNEWT_VAL(BLE_VERSION) == 52
 #define BLE_HCI_VER_BCS BLE_HCI_VER_BCS_5_2
 #define BLE_LMP_VER_BCS BLE_LMP_VER_BCS_5_2
-
+#elif MYNEWT_VAL(BLE_VERSION) == 53
+#define BLE_HCI_VER_BCS BLE_HCI_VER_BCS_5_3
+#define BLE_LMP_VER_BCS BLE_LMP_VER_BCS_5_3
 #endif
 
 #define BLE_HCI_DATA_HDR_SZ                 4
diff --git a/nimble/syscfg.yml b/nimble/syscfg.yml
index b5cfc585d6..0e87c0647d 100644
--- a/nimble/syscfg.yml
+++ b/nimble/syscfg.yml
@@ -67,6 +67,7 @@ syscfg.defs:
         value: 0
         restrictions:
             - 'BLE_PERIODIC_ADV if 1'
+            - '(BLE_ROLE_CENTRAL || BLE_ROLE_PERIPHERAL) if 1'
 
     BLE_EXT_ADV_MAX_SIZE:
         description: >
@@ -79,7 +80,7 @@ syscfg.defs:
             This allows to configure supported Bluetooth Core version. Some
             features may not be available if version is too low. Version is
             integer for easy comparison.
-        range: 50, 51, 52
+        range: 50, 51, 52, 53
         value: 50
     BLE_ISO:
         description: >
diff --git a/nimble/transport/da1469x/syscfg.yml b/nimble/transport/da1469x/syscfg.yml
index 4ea9da9b36..2ce8cd3cf2 100644
--- a/nimble/transport/da1469x/syscfg.yml
+++ b/nimble/transport/da1469x/syscfg.yml
@@ -16,28 +16,12 @@
 # under the License.
 #
 
-syscfg.defs:
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'Number of high-priority event buffers.'
-        value:  2
+syscfg.vals.'!BLE_HCI_BRIDGE && !BLE_EXT_ADV':
+    BLE_HCI_EVT_HI_BUF_COUNT: 2
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_ACL_BUF_COUNT: 4
+    BLE_ACL_BUF_SIZE: 255
 
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'Number of low-priority event buffers.'
-        value:  8
-
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'Size of each event buffer, in bytes.'
-        value:  70
-
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 4
-
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
-syscfg.vals.BLE_EXT_ADV:
+syscfg.vals.'!BLE_HCI_BRIDGE && BLE_EXT_ADV':
     BLE_HCI_EVT_BUF_SIZE: 274
diff --git a/nimble/transport/dialog_cmac/cmac_driver/diag/src/cmac_diag.c b/nimble/transport/dialog_cmac/cmac_driver/diag/src/cmac_diag.c
index 4f75470654..0a4e27e6d5 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/diag/src/cmac_diag.c
+++ b/nimble/transport/dialog_cmac/cmac_driver/diag/src/cmac_diag.c
@@ -20,7 +20,7 @@
 #include "syscfg/syscfg.h"
 #include "mcu/mcu.h"
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 void
 cmac_diag_setup_host(void)
 {
diff --git a/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_shared.h b/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_shared.h
index 90b5827dd4..4e37bb8b82 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_shared.h
+++ b/nimble/transport/dialog_cmac/cmac_driver/include/cmac_driver/cmac_shared.h
@@ -131,7 +131,7 @@ struct cmac_shared_data {
     uint8_t mbox_c2s_buf[ MYNEWT_VAL(CMAC_MBOX_SIZE_C2S) ];
 };
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 extern volatile struct cmac_shared_data *g_cmac_shared_data;
 #elif MYNEWT_VAL(BLE_CONTROLLER)
 extern volatile struct cmac_shared_data g_cmac_shared_data;
@@ -161,7 +161,7 @@ void cmac_rand_set_isr_cb(cmac_rand_isr_cb_t cb);
 void cmac_shared_init(void);
 void cmac_shared_sync(void);
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 #define CMAC_SHARED_LOCK_VAL    0x40000000
 #elif MYNEWT_VAL(BLE_CONTROLLER)
 #define CMAC_SHARED_LOCK_VAL    0xc0000000
diff --git a/nimble/transport/dialog_cmac/cmac_driver/pkg.yml b/nimble/transport/dialog_cmac/cmac_driver/pkg.yml
index 5b4a212914..00200b65a7 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/pkg.yml
+++ b/nimble/transport/dialog_cmac/cmac_driver/pkg.yml
@@ -37,3 +37,6 @@ pkg.post_link_cmds.BLE_CONTROLLER:
 
 pkg.pre_link_cmds.BLE_HOST:
     scripts/build_libcmac.sh: 100
+
+pkg.pre_link_cmds.BLE_HCI_BRIDGE:
+    scripts/build_libcmac.sh: 100
diff --git a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_mbox.c b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_mbox.c
index 9594dbe900..88764c4dfc 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_mbox.c
+++ b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_mbox.c
@@ -49,7 +49,7 @@ cmac_mbox_set_write_notif_cb(cmac_mbox_write_notif_cb *cb)
 int
 cmac_mbox_read(void)
 {
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     volatile struct cmac_mbox *mbox = &g_cmac_shared_data->mbox_c2s;
     uint8_t *mbox_buf = (uint8_t *)&g_cmac_shared_data->mbox_c2s_buf;
     const uint16_t mbox_size = MYNEWT_VAL(CMAC_MBOX_SIZE_C2S);
@@ -96,7 +96,7 @@ cmac_mbox_read(void)
 int
 cmac_mbox_write(const void *data, uint16_t len)
 {
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     volatile struct cmac_mbox *mbox = &g_cmac_shared_data->mbox_s2c;
     uint8_t *mbox_buf = (uint8_t *)&g_cmac_shared_data->mbox_s2c_buf;
     const uint16_t mbox_size = MYNEWT_VAL(CMAC_MBOX_SIZE_S2C);
diff --git a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_rand.c b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_rand.c
index 67a315f96e..10810972ca 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_rand.c
+++ b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_rand.c
@@ -26,7 +26,7 @@
 #include "os/os_arch.h"
 #include "os/os.h"
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 int
 cmac_rand_is_active(void)
 {
diff --git a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_shared.c b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_shared.c
index 24640ca475..a4f2e37f56 100644
--- a/nimble/transport/dialog_cmac/cmac_driver/src/cmac_shared.c
+++ b/nimble/transport/dialog_cmac/cmac_driver/src/cmac_shared.c
@@ -34,7 +34,7 @@
 #define min(_a, _b)     ((_a) < (_b) ? (_a) : (_b))
 #endif
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 volatile struct cmac_shared_data *g_cmac_shared_data;
 #include "mcu/da1469x_clock.h"
 #define MCU_DIAG_SER(_x)
@@ -45,7 +45,7 @@ volatile struct cmac_shared_data g_cmac_shared_data     __attribute__((section("
 void
 cmac_shared_init(void)
 {
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     g_cmac_shared_data = (void *)(MCU_MEM_SYSRAM_START_ADDRESS +
                                   MEMCTRL->CMI_SHARED_BASE_REG);
 
@@ -77,7 +77,7 @@ cmac_shared_sync(void)
      * to wait until CMAC finished initialization as otherwise host may start
      * sending HCI packets which will timeout as there is no one to read them.
      */
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     assert(g_cmac_shared_data->magic_sys == 0);
 
     while (g_cmac_shared_data->magic_cmac != CMAC_SHARED_MAGIC_CMAC);
diff --git a/nimble/transport/dialog_cmac/src/ble_hci_cmac_common.c b/nimble/transport/dialog_cmac/src/ble_hci_cmac_common.c
index 665b62166b..5a41ee6313 100644
--- a/nimble/transport/dialog_cmac/src/ble_hci_cmac_common.c
+++ b/nimble/transport/dialog_cmac/src/ble_hci_cmac_common.c
@@ -41,6 +41,7 @@
                                          BLE_MBUF_MEMBLOCK_OVERHEAD +       \
                                          BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT)
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
 static uint8_t ble_hci_pool_cmd_mempool_buf[
     OS_MEMPOOL_BYTES(HCI_CMD_COUNT, BLE_HCI_TRANS_CMD_SZ)];
 static struct os_mempool ble_hci_pool_cmd_mempool;
@@ -54,6 +55,7 @@ static uint8_t ble_hci_pool_evt_lo_mempool_buf[
     OS_MEMPOOL_BYTES(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
                      MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))];
 static struct os_mempool ble_hci_pool_evt_lo_mempool;
+#endif
 
 static uint8_t ble_hci_pool_acl_mempool_buf[
     OS_MEMPOOL_BYTES(MYNEWT_VAL(BLE_ACL_BUF_COUNT),
@@ -66,6 +68,7 @@ __attribute__((weak)) void ble_hci_trans_notify_free(void);
 static os_mempool_put_fn *g_ble_hci_pool_acl_mempool_put_cb;
 static void *g_ble_hci_pool_acl_mempool_put_arg;
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
 int
 ble_hci_trans_reset(void)
 {
@@ -117,6 +120,7 @@ ble_hci_trans_buf_free(uint8_t *buf)
 
     ble_hci_trans_notify_free();
 }
+#endif
 
 struct os_mbuf *
 ble_hci_cmac_alloc_acl_mbuf(void)
@@ -147,6 +151,7 @@ ble_hci_cmac_free_acl_cb(struct os_mempool_ext *mpe, void *data, void *arg)
 }
 
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
 int
 ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg)
 {
@@ -155,6 +160,7 @@ ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg)
 
     return 0;
 }
+#endif
 
 void
 ble_hci_cmac_init(void)
@@ -163,6 +169,7 @@ ble_hci_cmac_init(void)
 
     SYSINIT_ASSERT_ACTIVE();
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
     rc = os_mempool_init(&ble_hci_pool_cmd_mempool,
                          HCI_CMD_COUNT, BLE_HCI_TRANS_CMD_SZ,
                          ble_hci_pool_cmd_mempool_buf, "ble_hci_cmd");
@@ -179,6 +186,7 @@ ble_hci_cmac_init(void)
                          MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
                          ble_hci_pool_evt_lo_mempool_buf, "ble_hci_evt_lo");
     SYSINIT_PANIC_ASSERT(rc == 0);
+#endif
 
     rc = os_mempool_ext_init(&ble_hci_pool_acl_mempool,
                              MYNEWT_VAL(BLE_ACL_BUF_COUNT), POOL_ACL_BLOCK_SIZE,
diff --git a/nimble/transport/dialog_cmac/src/ble_hci_cmac_hs.c b/nimble/transport/dialog_cmac/src/ble_hci_cmac_hs.c
index 1164fe7186..266637f78e 100644
--- a/nimble/transport/dialog_cmac/src/ble_hci_cmac_hs.c
+++ b/nimble/transport/dialog_cmac/src/ble_hci_cmac_hs.c
@@ -21,7 +21,7 @@
 #include <string.h>
 #include "syscfg/syscfg.h"
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 
 #include "cmac_driver/cmac_shared.h"
 #include "cmac_driver/cmac_host.h"
@@ -41,6 +41,27 @@ static struct ble_hci_cmac_hs_api g_ble_hci_cmac_hs_api;
 static struct ble_hci_trans_h4_rx_state g_ble_hci_cmac_hs_rx_state;
 static bool g_ble_hci_cmac_hs_read_err;
 
+#if MYNEWT_VAL(BLE_HCI_BRIDGE)
+/*
+ * TODO: Remove/fix functions ble_ll_data_buffer_overflow() ble_ll_hw_error()
+ * Following two functions are added to allowed build of HCI bridge configurations.
+ * Those functions are only used by UART transport, in RAM transport configuration
+ * they can be called directly in bridge mode controller code is on other core
+ * and those can't be called.
+ */
+void
+ble_ll_data_buffer_overflow(void)
+{
+
+}
+
+void
+ble_ll_hw_error(uint8_t err)
+{
+    (void)err;
+}
+#endif
+
 static int
 ble_hci_cmac_hs_frame_cb(uint8_t pkt_type, void *data)
 {
diff --git a/nimble/transport/dialog_cmac/src/ble_hci_cmac_ll.c b/nimble/transport/dialog_cmac/src/ble_hci_cmac_ll.c
index 3531529c71..6b49158ad9 100644
--- a/nimble/transport/dialog_cmac/src/ble_hci_cmac_ll.c
+++ b/nimble/transport/dialog_cmac/src/ble_hci_cmac_ll.c
@@ -92,12 +92,17 @@ int
 ble_hci_trans_ll_evt_tx(uint8_t *evt)
 {
     uint8_t pkt_type = BLE_HCI_TRANS_H4_PKT_TYPE_EVT;
+    os_sr_t sr;
+
+    OS_ENTER_CRITICAL(sr);
 
     cmac_mbox_write(&pkt_type, sizeof(pkt_type));
     cmac_mbox_write(evt, evt[1] + 2);
 
     ble_hci_trans_buf_free(evt);
 
+    OS_EXIT_CRITICAL(sr);
+
     return 0;
 }
 
diff --git a/nimble/transport/dialog_cmac/src/ble_hci_trans_h4.c b/nimble/transport/dialog_cmac/src/ble_hci_trans_h4.c
index 74da391239..628ae75d96 100644
--- a/nimble/transport/dialog_cmac/src/ble_hci_trans_h4.c
+++ b/nimble/transport/dialog_cmac/src/ble_hci_trans_h4.c
@@ -64,7 +64,7 @@ ble_hci_trans_h4_rxs_start(struct ble_hci_trans_h4_rx_state *rxs, uint8_t pkt_ty
     case BLE_HCI_TRANS_H4_PKT_TYPE_ACL:
         rxs->min_len = 4;
         break;
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_TRANS_H4_PKT_TYPE_EVT:
         rxs->min_len = 2;
         break;
@@ -95,7 +95,7 @@ static int
 ble_hci_trans_h4_rx_state_w4_header(struct ble_hci_trans_h4_rx_state *rxs,
                                     struct input_buffer *ib)
 {
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     int pool;
 #endif
     int rc;
@@ -127,7 +127,7 @@ ble_hci_trans_h4_rx_state_w4_header(struct ble_hci_trans_h4_rx_state *rxs,
         os_mbuf_append(rxs->om, rxs->hdr, rxs->len);
         rxs->expected_len = get_le16(&rxs->hdr[2]) + 4;
         break;
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_TRANS_H4_PKT_TYPE_EVT:
         pool = BLE_HCI_TRANS_BUF_EVT_HI;
         if (rxs->hdr[0] == BLE_HCI_EVCODE_LE_META) {
@@ -194,7 +194,7 @@ ble_hci_trans_h4_rx_state_w4_payload(struct ble_hci_trans_h4_rx_state *rxs,
 #if MYNEWT_VAL(BLE_CONTROLLER)
     case BLE_HCI_TRANS_H4_PKT_TYPE_CMD:
 #endif
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_TRANS_H4_PKT_TYPE_EVT:
 #endif
         if (rxs->buf) {
@@ -240,7 +240,7 @@ ble_hci_trans_h4_rx_state_completed(struct ble_hci_trans_h4_rx_state *rxs,
 #if MYNEWT_VAL(BLE_CONTROLLER)
     case BLE_HCI_TRANS_H4_PKT_TYPE_CMD:
 #endif
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_TRANS_H4_PKT_TYPE_EVT:
 #endif
         if (rxs->buf) {
diff --git a/nimble/transport/dialog_cmac/syscfg.yml b/nimble/transport/dialog_cmac/syscfg.yml
index c44773efcd..54607fd814 100644
--- a/nimble/transport/dialog_cmac/syscfg.yml
+++ b/nimble/transport/dialog_cmac/syscfg.yml
@@ -16,28 +16,12 @@
 # under the License.
 #
 
-syscfg.defs:
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'Number of high-priority event buffers.'
-        value:  2
+syscfg.vals.'!BLE_HCI_BRIDGE && !(BLE_EXT_ADV || BLE_LL_CFG_FEAT_LL_EXT_ADV)':
+    BLE_HCI_EVT_HI_BUF_COUNT: 2
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_ACL_BUF_COUNT: 4
+    BLE_ACL_BUF_SIZE: 255
 
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'Number of low-priority event buffers.'
-        value:  8
-
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'Size of each event buffer, in bytes.'
-        value:  70
-
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 4
-
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
-syscfg.vals.'BLE_EXT_ADV || BLE_LL_CFG_FEAT_LL_EXT_ADV':
+syscfg.vals.'!BLE_HCI_BRIDGE && (BLE_EXT_ADV || BLE_LL_CFG_FEAT_LL_EXT_ADV)':
     BLE_HCI_EVT_BUF_SIZE: 257
diff --git a/nimble/transport/emspi/syscfg.yml b/nimble/transport/emspi/syscfg.yml
index 4751271b0d..501175467a 100644
--- a/nimble/transport/emspi/syscfg.yml
+++ b/nimble/transport/emspi/syscfg.yml
@@ -28,28 +28,6 @@ syscfg.defs:
             # This is a host-only transport.
             - BLE_HOST
 
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'Number of high-priority event buffers.'
-        value:  2
-
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'Number of low-priority event buffers.'
-        value:  8
-
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'Size of each event buffer, in bytes.'
-        value:  70
-
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 4
-
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_HCI_ACL_OUT_COUNT:
         description: >
             This count is used in creating a pool of elements used by the
@@ -95,5 +73,12 @@ syscfg.defs:
             Sysinit stage for the EMSPI BLE transport.
         value: 100
 
-syscfg.vals.BLE_EXT_ADV:
+syscfg.vals.'!BLE_HCI_BRIDGE && BLE_EXT_ADV':
     BLE_HCI_EVT_BUF_SIZE: 257
+
+syscfg.vals.'!BLE_HCI_BRIDGE && !BLE_EXT_ADV':
+    BLE_HCI_EVT_HI_BUF_COUNT: 2
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_ACL_BUF_COUNT: 4
+    BLE_ACL_BUF_SIZE: 255
diff --git a/nimble/transport/nrf5340/src/nrf5340_ble_hci.c b/nimble/transport/nrf5340/src/nrf5340_ble_hci.c
index 3f113e1175..41f164edb2 100644
--- a/nimble/transport/nrf5340/src/nrf5340_ble_hci.c
+++ b/nimble/transport/nrf5340/src/nrf5340_ble_hci.c
@@ -39,7 +39,7 @@
 #define IPC_RX_CHANNEL 1
 #endif
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 #define IPC_TX_CHANNEL 1
 #define IPC_RX_CHANNEL 0
 #endif
@@ -49,7 +49,7 @@ struct nrf5340_ble_hci_api {
     ble_hci_trans_rx_cmd_fn *cmd_cb;
     void *cmd_arg;
 #endif
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     ble_hci_trans_rx_cmd_fn *evt_cb;
     void *evt_arg;
 #endif
@@ -73,8 +73,21 @@ struct nrf5340_ble_hci_pool_cmd {
     bool allocated;
 };
 
-/* (Pseudo)pool for HCI commands */
-static struct nrf5340_ble_hci_pool_cmd nrf5340_ble_hci_pool_cmd;
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
+/*
+ * If controller-to-host flow control is enabled we need to hold an extra command
+ * buffer for HCI_Host_Number_Of_Completed_Packets which can be sent at any time.
+ */
+#if MYNEWT_VAL(BLE_HS_FLOW_CTRL) || MYNEWT_VAL(BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL)
+#define HCI_CMD_COUNT   2
+#else
+#define HCI_CMD_COUNT   1
+#endif
+
+static uint8_t nrf5340_ble_hci_pool_cmd_mempool_buf[OS_MEMPOOL_BYTES(
+                                                        HCI_CMD_COUNT,
+                                                        BLE_HCI_TRANS_CMD_SZ)];
+static struct os_mempool nrf5340_ble_hci_pool_cmd_mempool;
 
 /* Pools for HCI events (high and low priority) */
 static uint8_t nrf5340_ble_hci_pool_evt_hi_buf[OS_MEMPOOL_BYTES(
@@ -86,11 +99,13 @@ static uint8_t nrf5340_ble_hci_pool_evt_lo_buf[OS_MEMPOOL_BYTES(
                                             MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))];
 static struct os_mempool nrf5340_ble_hci_pool_evt_lo;
 
+#endif
+
 /* Pool for ACL data */
 static uint8_t nrf5340_ble_hci_pool_acl_buf[OS_MEMPOOL_BYTES(
                                             MYNEWT_VAL(BLE_ACL_BUF_COUNT),
                                             POOL_ACL_BLOCK_SIZE)];
-static struct os_mempool nrf5340_ble_hci_pool_acl;
+static struct os_mempool_ext nrf5340_ble_hci_pool_acl;
 static struct os_mbuf_pool nrf5340_ble_hci_pool_acl_mbuf;
 
 /* Interface to host/ll */
@@ -99,12 +114,35 @@ static struct nrf5340_ble_hci_api nrf5340_ble_hci_api;
 /* State of RX currently in progress (needs to reassemble frame) */
 static struct nrf5340_ble_hci_rx_data nrf5340_ble_hci_rx_data;
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
 int
 ble_hci_trans_reset(void)
 {
     /* XXX Should we do something with RF and/or BLE core? */
     return 0;
 }
+#endif
+
+#if MYNEWT_VAL(BLE_HCI_BRIDGE)
+/*
+ * TODO: Remove/fix functions ble_ll_data_buffer_overflow() ble_ll_hw_error()
+ * Following two functions are added to allowed build of HCI bridge configurations.
+ * Those functions are only used by UART transport, in RAM transport configuration
+ * they can be called directly in bridge mode controller code is on other core
+ * and those can't be called.
+ */
+void
+ble_ll_data_buffer_overflow(void)
+{
+
+}
+
+void
+ble_ll_hw_error(uint8_t err)
+{
+    (void)err;
+}
+#endif
 
 static int
 ble_hci_trans_acl_tx(struct os_mbuf *om)
@@ -165,7 +203,7 @@ ble_hci_trans_ll_acl_tx(struct os_mbuf *om)
 }
 #endif
 
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 void
 ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *evt_cb, void *evt_arg,
                      ble_hci_trans_rx_acl_fn *acl_cb, void *acl_arg)
@@ -200,6 +238,7 @@ ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
 }
 #endif
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
 uint8_t *
 ble_hci_trans_buf_alloc(int type)
 {
@@ -207,9 +246,7 @@ ble_hci_trans_buf_alloc(int type)
 
     switch (type) {
     case BLE_HCI_TRANS_BUF_CMD:
-        assert(!nrf5340_ble_hci_pool_cmd.allocated);
-        nrf5340_ble_hci_pool_cmd.allocated = 1;
-        buf = nrf5340_ble_hci_pool_cmd.cmd;
+        buf = os_memblock_get(&nrf5340_ble_hci_pool_cmd_mempool);
         break;
     case BLE_HCI_TRANS_BUF_EVT_HI:
         buf = os_memblock_get(&nrf5340_ble_hci_pool_evt_hi);
@@ -233,9 +270,9 @@ ble_hci_trans_buf_free(uint8_t *buf)
 {
     int rc;
 
-    if (buf == nrf5340_ble_hci_pool_cmd.cmd) {
-        assert(nrf5340_ble_hci_pool_cmd.allocated);
-        nrf5340_ble_hci_pool_cmd.allocated = 0;
+    if (os_memblock_from(&nrf5340_ble_hci_pool_cmd_mempool, buf)) {
+        rc = os_memblock_put(&nrf5340_ble_hci_pool_cmd_mempool, buf);
+        assert(rc == 0);
     } else if (os_memblock_from(&nrf5340_ble_hci_pool_evt_hi, buf)) {
         rc = os_memblock_put(&nrf5340_ble_hci_pool_evt_hi, buf);
         assert(rc == 0);
@@ -245,12 +282,13 @@ ble_hci_trans_buf_free(uint8_t *buf)
         assert(rc == 0);
     }
 }
+#endif
 
 static void
 nrf5340_ble_hci_trans_rx_process(int channel)
 {
     struct nrf5340_ble_hci_rx_data *rxd = &nrf5340_ble_hci_rx_data;
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     int pool = BLE_HCI_TRANS_BUF_EVT_HI;
 #endif
     int rc;
@@ -262,23 +300,37 @@ nrf5340_ble_hci_trans_rx_process(int channel)
         rxd->expected_len = 0;
 
 #if MYNEWT_VAL(BLE_CONTROLLER)
-        assert((rxd->type == HCI_PKT_ACL) || (rxd->type = HCI_PKT_CMD));
+        assert((rxd->type == HCI_PKT_ACL) || (rxd->type == HCI_PKT_CMD));
 #endif
-#if MYNEWT_VAL(BLE_HOST)
-        assert((rxd->type == HCI_PKT_ACL) || (rxd->type = HCI_PKT_EVT));
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
+        assert((rxd->type == HCI_PKT_ACL) || (rxd->type == HCI_PKT_EVT));
 #endif
         break;
 #if MYNEWT_VAL(BLE_CONTROLLER)
     case HCI_PKT_CMD:
-        /* commands are sent complete over IPC */
-        rxd->len = ipc_nrf5340_read(channel, rxd->hdr, 3);
-        assert(rxd->len == 3);
+        /* header */
+        if (rxd->len < 3) {
+            rxd->len += ipc_nrf5340_read(channel, &rxd->hdr[rxd->len],
+                                         3 - rxd->len);
+            if (rxd->len < 3) {
+                break;
+            }
+        }
 
-        rxd->buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
-        memcpy(rxd->buf, rxd->hdr, rxd->len);
+        if (rxd->expected_len == 0) {
+            rxd->buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
+            memcpy(rxd->buf, rxd->hdr, rxd->len);
 
-        rxd->len += ipc_nrf5340_read(channel, &rxd->buf[rxd->len], rxd->hdr[2]);
-        assert(rxd->len == 3 + rxd->hdr[2]);
+            rxd->expected_len = 3 + rxd->hdr[2];
+        }
+
+        if (rxd->len < rxd->expected_len) {
+            rxd->len += ipc_nrf5340_read(channel, &rxd->buf[rxd->len],
+                                         rxd->expected_len - rxd->len);
+            if (rxd->len < rxd->expected_len) {
+                break;
+            }
+        }
 
         rc = nrf5340_ble_hci_api.cmd_cb(rxd->buf, nrf5340_ble_hci_api.cmd_arg);
         if (rc != 0) {
@@ -288,16 +340,25 @@ nrf5340_ble_hci_trans_rx_process(int channel)
         rxd->type = HCI_PKT_NONE;
         break;
 #endif
-#if MYNEWT_VAL(BLE_HOST)
+#if MYNEWT_VAL(BLE_HOST) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case HCI_PKT_EVT:
-        /* events are sent complete over IPC */
-        rxd->len = ipc_nrf5340_read(channel, rxd->hdr, 2);
-        assert(rxd->len == 2);
+        /* header */
+        if (rxd->len < 2) {
+            rxd->len += ipc_nrf5340_read(channel, &rxd->hdr[rxd->len],
+                                         2 - rxd->len);
+            if (rxd->len < 2) {
+                break;
+            }
+        }
 
         if (rxd->hdr[0] == BLE_HCI_EVCODE_LE_META) {
-            /* For LE Meta event we need 3 bytes to parse header */
-            rxd->len += ipc_nrf5340_read(channel, rxd->hdr + 2, 1);
-            assert(rxd->len == 3);
+            if (rxd->len < 3) {
+                /* For LE Meta event we need 3 bytes to parse header */
+                rxd->len += ipc_nrf5340_read(channel, &rxd->hdr[rxd->len], 1);
+                if (rxd->len < 3) {
+                    break;
+                }
+            }
 
             /* Advertising reports shall be allocated from low-prio pool */
             if ((rxd->hdr[2] == BLE_HCI_LE_SUBEV_ADV_RPT) ||
@@ -306,26 +367,33 @@ nrf5340_ble_hci_trans_rx_process(int channel)
             }
         }
 
-        rxd->buf = ble_hci_trans_buf_alloc(pool);
-        if (!rxd->buf) {
-            /*
-             * Only care about valid buffer when shall be allocated from
-             * high-prio pool, otherwise NULL is fine and we'll just skip
-             * this event.
-             */
-            if (pool != BLE_HCI_TRANS_BUF_EVT_LO) {
-                rxd->buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+        if (rxd->expected_len == 0) {
+            rxd->buf = ble_hci_trans_buf_alloc(pool);
+            if (!rxd->buf) {
+                /*
+                 * Only care about valid buffer when shall be allocated from
+                 * high-prio pool, otherwise NULL is fine and we'll just skip
+                 * this event.
+                 */
+                if (pool != BLE_HCI_TRANS_BUF_EVT_LO) {
+                    rxd->buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
+                }
             }
-        }
 
-        rxd->expected_len = 2 + rxd->hdr[1];
+            rxd->expected_len = 2 + rxd->hdr[1];
 
-        if (rxd->buf) {
-            memcpy(rxd->buf, rxd->hdr, rxd->len);
+            /* copy header */
+            if (rxd->buf) {
+                memcpy(rxd->buf, rxd->hdr, rxd->len);
+            }
+        }
 
+        if (rxd->buf) {
             rxd->len += ipc_nrf5340_read(channel, &rxd->buf[rxd->len],
                                          rxd->expected_len - rxd->len);
-            assert(rxd->expected_len == rxd->len);
+            if (rxd->len < rxd->expected_len) {
+                break;
+            }
 
             rc = nrf5340_ble_hci_api.evt_cb(rxd->buf,
                                             nrf5340_ble_hci_api.evt_arg);
@@ -335,7 +403,9 @@ nrf5340_ble_hci_trans_rx_process(int channel)
         } else {
             rxd->len += ipc_nrf5340_consume(channel,
                                             rxd->expected_len - rxd->len);
-            assert(rxd->expected_len == rxd->len);
+            if (rxd->len < rxd->expected_len) {
+                break;
+            }
         }
 
         rxd->type = HCI_PKT_NONE;
@@ -343,8 +413,8 @@ nrf5340_ble_hci_trans_rx_process(int channel)
 #endif
     case HCI_PKT_ACL:
         if (rxd->len < 4) {
-            rxd->len += ipc_nrf5340_read(channel, rxd->hdr, 4 - rxd->len);
-
+            rxd->len += ipc_nrf5340_read(channel, &rxd->hdr[rxd->len],
+                                         4 - rxd->len);
             if (rxd->len < 4) {
                 break;
             }
@@ -355,7 +425,7 @@ nrf5340_ble_hci_trans_rx_process(int channel)
             rxd->om = os_mbuf_get_pkthdr(&nrf5340_ble_hci_pool_acl_mbuf,
                                          sizeof(struct ble_mbuf_hdr));
             if (!rxd->om) {
-                /* not much we can do here... */
+                /* TODO not much we can do here... */
                 assert(0);
             }
 
@@ -391,6 +461,17 @@ nrf5340_ble_hci_trans_rx(int channel, void *user_data)
     }
 }
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
+int
+ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg)
+{
+    nrf5340_ble_hci_pool_acl.mpe_put_cb = cb;
+    nrf5340_ble_hci_pool_acl.mpe_put_arg = arg;
+
+    return 0;
+}
+#endif
+
 void
 nrf5340_ble_hci_init(void)
 {
@@ -398,16 +479,18 @@ nrf5340_ble_hci_init(void)
 
     SYSINIT_ASSERT_ACTIVE();
 
-    rc = os_mempool_init(&nrf5340_ble_hci_pool_acl, MYNEWT_VAL(BLE_ACL_BUF_COUNT),
-                         POOL_ACL_BLOCK_SIZE, nrf5340_ble_hci_pool_acl_buf,
-                         "nrf5340_ble_hci_pool_acl");
+    rc = os_mempool_ext_init(&nrf5340_ble_hci_pool_acl,
+                             MYNEWT_VAL(BLE_ACL_BUF_COUNT), POOL_ACL_BLOCK_SIZE,
+                             nrf5340_ble_hci_pool_acl_buf,
+                             "nrf5340_ble_hci_pool_acl");
     SYSINIT_PANIC_ASSERT(rc == 0);
 
     rc = os_mbuf_pool_init(&nrf5340_ble_hci_pool_acl_mbuf,
-                           &nrf5340_ble_hci_pool_acl, POOL_ACL_BLOCK_SIZE,
+                           &nrf5340_ble_hci_pool_acl.mpe_mp, POOL_ACL_BLOCK_SIZE,
                            MYNEWT_VAL(BLE_ACL_BUF_COUNT));
     SYSINIT_PANIC_ASSERT(rc == 0);
 
+#if !MYNEWT_VAL(BLE_HCI_BRIDGE)
     rc = os_mempool_init(&nrf5340_ble_hci_pool_evt_hi,
                          MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
                          MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
@@ -422,5 +505,12 @@ nrf5340_ble_hci_init(void)
                          "nrf5340_ble_hci_pool_evt_lo");
     SYSINIT_PANIC_ASSERT(rc == 0);
 
+    rc = os_mempool_init(&nrf5340_ble_hci_pool_cmd_mempool,
+                         HCI_CMD_COUNT, BLE_HCI_TRANS_CMD_SZ,
+                         nrf5340_ble_hci_pool_cmd_mempool_buf,
+                         "nrf5340_ble_hci_pool_cmd_mempool");
+    SYSINIT_PANIC_ASSERT(rc == 0);
+#endif
+
     ipc_nrf5340_recv(IPC_RX_CHANNEL, nrf5340_ble_hci_trans_rx, NULL);
 }
diff --git a/nimble/transport/nrf5340/syscfg.yml b/nimble/transport/nrf5340/syscfg.yml
index 1417bbc03d..a12e0c3736 100644
--- a/nimble/transport/nrf5340/syscfg.yml
+++ b/nimble/transport/nrf5340/syscfg.yml
@@ -17,34 +17,21 @@
 #
 
 syscfg.defs:
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'Number of high-priority event buffers.'
-        value:  2
-
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'Number of low-priority event buffers.'
-        value:  8
-
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'Size of each event buffer, in bytes.'
-        value:  70
-
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 4
-
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_TRANS_NRF5340_SYSINIT_STAGE:
         description: >
             Sysinit stage for the RAM BLE transport.
         value: 100
 
-syscfg.vals.BLE_EXT_ADV:
+syscfg.vals.'!BLE_HCI_BRIDGE:
+    BLE_HCI_EVT_HI_BUF_COUNT: 2
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_ACL_BUF_COUNT: 4
+    BLE_ACL_BUF_SIZE: 255
+
+syscfg.vals.'!BLE_HCI_BRIDGE && !BLE_EXT_ADV':
+    BLE_HCI_EVT_BUF_SIZE: 70
+
+syscfg.vals.'!BLE_HCI_BRIDGE && BLE_EXT_ADV':
     BLE_HCI_EVT_BUF_SIZE: 257
     
 syscfg.restrictions:
diff --git a/nimble/transport/pkg.yml b/nimble/transport/pkg.yml
index 8174286d06..5484002ebc 100644
--- a/nimble/transport/pkg.yml
+++ b/nimble/transport/pkg.yml
@@ -52,3 +52,9 @@ pkg.deps.'BLE_HCI_TRANSPORT == "usb"':
 
 pkg.deps.'BLE_HCI_TRANSPORT == "nrf5340"':
     - nimble/transport/nrf5340
+
+pkg.deps.'BLE_HCI_BRIDGE_TRANSPORT == "nrf5340"':
+    - nimble/transport/nrf5340
+
+pkg.deps.'BLE_HCI_BRIDGE_TRANSPORT == "dialog_cmac"':
+    - nimble/transport/dialog_cmac
diff --git a/nimble/transport/ram/syscfg.yml b/nimble/transport/ram/syscfg.yml
index 3b822fcc60..6286dfe722 100644
--- a/nimble/transport/ram/syscfg.yml
+++ b/nimble/transport/ram/syscfg.yml
@@ -17,32 +17,17 @@
 #
 
 syscfg.defs:
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'Number of high-priority event buffers.'
-        value:  2
-
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'Number of low-priority event buffers.'
-        value:  8
-
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'Size of each event buffer, in bytes.'
-        value:  70
-
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 4
-
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_TRANS_RAM_SYSINIT_STAGE:
         description: >
             Sysinit stage for the RAM BLE transport.
         value: 100
 
+syscfg.vals:
+    BLE_HCI_EVT_HI_BUF_COUNT: 2
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_ACL_BUF_COUNT: 4
+    BLE_ACL_BUF_SIZE: 65535
+
 syscfg.vals.BLE_EXT_ADV:
     BLE_HCI_EVT_BUF_SIZE: 257
diff --git a/nimble/transport/socket/src/ble_hci_socket.c b/nimble/transport/socket/src/ble_hci_socket.c
index 44de8fed53..b444a24032 100644
--- a/nimble/transport/socket/src/ble_hci_socket.c
+++ b/nimble/transport/socket/src/ble_hci_socket.c
@@ -333,7 +333,6 @@ ble_hci_sock_acl_tx(struct os_mbuf *om)
 static int
 ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type)
 {
-    uint8_t btaddr[6];
     struct msghdr msg;
     struct iovec iov[8];
     int len;
diff --git a/nimble/transport/socket/syscfg.yml b/nimble/transport/socket/syscfg.yml
index 2050f64692..9c8fa583d6 100644
--- a/nimble/transport/socket/syscfg.yml
+++ b/nimble/transport/socket/syscfg.yml
@@ -17,24 +17,6 @@
 #
 
 syscfg.defs:
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'The size of the allocated event buffers'
-        value: 70
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'The number of high priority event buffers'
-        value: 8
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'The number of low priority event buffers'
-        value: 8
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 24
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_HCI_ACL_OUT_COUNT:
         description: >
             This count is used in creating a pool of elements used by the
@@ -75,5 +57,12 @@ syscfg.defs:
             Sysinit stage for the socket BLE transport.
         value: 500
 
-syscfg.vals.BLE_EXT_ADV:
+syscfg.vals.'!BLE_HCI_BRIDGE && BLE_EXT_ADV':
     BLE_HCI_EVT_BUF_SIZE: 257
+
+syscfg.vals.'!BLE_HCI_BRIDGE && !BLE_EXT_ADV':
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_HCI_EVT_HI_BUF_COUNT: 8
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_ACL_BUF_COUNT: 24
+    BLE_ACL_BUF_SIZE: 255
diff --git a/nimble/transport/syscfg.yml b/nimble/transport/syscfg.yml
index 5bec6adf4d..7eed69ba4e 100644
--- a/nimble/transport/syscfg.yml
+++ b/nimble/transport/syscfg.yml
@@ -37,6 +37,47 @@ syscfg.defs:
             - usb               # USB
             - nrf5340           # nRF5340
 
+    BLE_HCI_BRIDGE_TRANSPORT:
+        description: >
+            Selects HCI transport to be included in bridge configuration build.
+            This applies to multi core configurations where controller runs
+            on separate core and main core forwards HCI traffic to external host.
+            This has virtually the same effect as including package dependency
+            manually, but it allows to easily override HCI transport package in
+            application or target settings.
+            This is the transport
+        value:
+        choices:
+            - dialog_cmac       # Dialog CMAC via shared memory
+            - nrf5340           # nRF5340
+
+    BLE_HCI_BRIDGE:
+        description: >
+            External interface (UART/USB/Socket) bridged to second core controller.
+        value: 0
+
+    BLE_HCI_EVT_HI_BUF_COUNT:
+        description: 'Number of high-priority event buffers.'
+        value:
+
+    BLE_HCI_EVT_LO_BUF_COUNT:
+        description: 'Number of low-priority event buffers.'
+        value:
+
+    BLE_HCI_EVT_BUF_SIZE:
+        description: 'Size of each event buffer, in bytes.'
+        value:
+
+    BLE_ACL_BUF_COUNT:
+        description: 'The number of ACL data buffers'
+        value:
+
+    BLE_ACL_BUF_SIZE:
+        description: >
+            This is the maximum size of the data portion of HCI ACL data
+            packets.
+        value:
+
 # Deprecated settings
     BLE_HCI_TRANSPORT_NIMBLE_BUILTIN:
         description: Use BLE_HCI_TRANSPORT instead.
@@ -69,3 +110,10 @@ syscfg.vals.BLE_HCI_TRANSPORT_SOCKET:
     BLE_HCI_TRANSPORT: socket
 syscfg.vals.BLE_HCI_TRANSPORT_EMSPI:
     BLE_HCI_TRANSPORT: emspi
+
+syscfg.vals.'(MCU_TARGET == "DA14691" || MCU_TARGET == "DA14695" || MCU_TARGET == "DA14697" || MCU_TARGET == "DA14699")':
+    BLE_HCI_BRIDGE_TRANSPORT: dialog_cmac
+
+syscfg.vals.'(MCU_TARGET == "nRF5340_APP")':
+    BLE_HCI_BRIDGE_TRANSPORT: nrf5340
+
diff --git a/nimble/transport/uart/src/ble_hci_uart.c b/nimble/transport/uart/src/ble_hci_uart.c
index cbb6dd4259..cc796c0005 100644
--- a/nimble/transport/uart/src/ble_hci_uart.c
+++ b/nimble/transport/uart/src/ble_hci_uart.c
@@ -53,9 +53,9 @@
  */
 
 /* XXX: for now, define this here */
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 extern void ble_ll_data_buffer_overflow(void);
-extern void ble_ll_hw_error(uint8_t err);
+extern void ble_ll_hw_error(void);
 
 static const uint8_t ble_hci_uart_reset_cmd[4] = { 0x01, 0x03, 0x0C, 0x00 };
 #endif
@@ -385,7 +385,7 @@ ble_hci_uart_tx_char(void *arg)
     return rc;
 }
 
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 /**
  * HCI uart sync lost.
  *
@@ -400,7 +400,7 @@ ble_hci_uart_sync_lost(void)
     ble_hci_uart_state.rx_cmd.cur = 0;
     ble_hci_uart_state.rx_cmd.data =
         ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
-    ble_ll_hw_error(BLE_HW_ERR_HCI_SYNC_LOSS);
+    ble_ll_hw_error();
     ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SYNC_LOSS;
 }
 #endif
@@ -418,7 +418,7 @@ ble_hci_uart_rx_pkt_type(uint8_t data)
 
     switch (ble_hci_uart_state.rx_type) {
     /* Host should never receive a command! */
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_UART_H4_CMD:
         ble_hci_uart_state.rx_cmd.len = 0;
         ble_hci_uart_state.rx_cmd.cur = 0;
@@ -458,7 +458,7 @@ ble_hci_uart_rx_pkt_type(uint8_t data)
         break;
 
     default:
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
         /*
          * If we receive an unknown HCI packet type this is considered a loss
          * of sync.
@@ -477,7 +477,7 @@ ble_hci_uart_rx_pkt_type(uint8_t data)
     return 0;
 }
 
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
 /**
  * HCI uart sync loss.
  *
@@ -708,7 +708,7 @@ ble_hci_uart_rx_acl(uint8_t data)
          */
         if (pktlen > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) {
             os_mbuf_free_chain(ble_hci_uart_state.rx_acl.buf);
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
             ble_hci_uart_sync_lost();
 #else
         /*
@@ -755,7 +755,9 @@ ble_hci_uart_rx_skip_acl(uint8_t data)
     if (rxd_bytes == ble_hci_uart_state.rx_acl.len) {
 /* XXX: I dont like this but for now this denotes controller only */
 #if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
         ble_ll_data_buffer_overflow();
+#endif
 #endif
         ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
     }
@@ -767,7 +769,7 @@ ble_hci_uart_rx_char(void *arg, uint8_t data)
     switch (ble_hci_uart_state.rx_type) {
     case BLE_HCI_UART_H4_NONE:
         return ble_hci_uart_rx_pkt_type(data);
-#if MYNEWT_VAL(BLE_CONTROLLER)
+#if MYNEWT_VAL(BLE_CONTROLLER) || MYNEWT_VAL(BLE_HCI_BRIDGE)
     case BLE_HCI_UART_H4_CMD:
         ble_hci_uart_rx_cmd(data);
         return 0;
@@ -896,6 +898,7 @@ ble_hci_trans_ll_acl_tx(struct os_mbuf *om)
     return rc;
 }
 
+#if MYNEWT_VAL(BLE_HOST)
 /**
  * Sends an HCI command from the host to the controller.
  *
@@ -953,6 +956,7 @@ ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
 {
     ble_hci_uart_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg);
 }
+#endif
 
 /**
  * Configures the HCI transport to operate with a host.  The transport will
diff --git a/nimble/transport/uart/syscfg.yml b/nimble/transport/uart/syscfg.yml
index 43486a8bfe..7eff9517c1 100644
--- a/nimble/transport/uart/syscfg.yml
+++ b/nimble/transport/uart/syscfg.yml
@@ -17,24 +17,6 @@
 #
 
 syscfg.defs:
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'The size of the allocated event buffers'
-        value: 70
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'The number of high priority event buffers'
-        value: 8
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'The number of low priority event buffers'
-        value: 8
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 12
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_HCI_ACL_OUT_COUNT:
         description: >
             This count is used in creating a pool of elements used by the
@@ -70,3 +52,10 @@ syscfg.defs:
 
 syscfg.vals.BLE_EXT_ADV:
     BLE_HCI_EVT_BUF_SIZE: 257
+
+syscfg.vals:
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_HCI_EVT_HI_BUF_COUNT: 8
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_ACL_BUF_COUNT: 12
+    BLE_ACL_BUF_SIZE: 255
diff --git a/nimble/transport/usb/pkg.yml b/nimble/transport/usb/pkg.yml
index 49317c97d5..c4c314c867 100644
--- a/nimble/transport/usb/pkg.yml
+++ b/nimble/transport/usb/pkg.yml
@@ -31,6 +31,7 @@ pkg.deps:
     - "@apache-mynewt-core/kernel/os"
     - "@apache-mynewt-core/util/mem"
     - nimble
+    - "@apache-mynewt-core/hw/usb/tinyusb"
 
 pkg.apis:
     - ble_transport
diff --git a/nimble/transport/usb/src/ble_hci_usb.c b/nimble/transport/usb/src/ble_hci_usb.c
index d09421bb20..8160536b95 100644
--- a/nimble/transport/usb/src/ble_hci_usb.c
+++ b/nimble/transport/usb/src/ble_hci_usb.c
@@ -229,7 +229,7 @@ tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len)
     if (ble_hci_usb_rx_cmd_ll_cb) {
         buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
         assert(buf != NULL);
-        memcpy(buf, hci_cmd, cmd_len);
+        memcpy(buf, hci_cmd, min(cmd_len, BLE_HCI_TRANS_CMD_SZ));
 
         rc = ble_hci_usb_rx_cmd_ll_cb(buf, ble_hci_usb_rx_cmd_ll_arg);
     }
@@ -285,6 +285,11 @@ ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
 
     assert(hci_ev != NULL);
 
+    if (!tud_ready()) {
+        ble_hci_trans_buf_free(hci_ev);
+        return 0;
+    }
+
     pkt = os_memblock_get(&ble_hci_pkt_pool);
     if (pkt == NULL) {
         ble_hci_trans_buf_free(hci_ev);
diff --git a/nimble/transport/usb/syscfg.yml b/nimble/transport/usb/syscfg.yml
index ebc261a23d..38effde60c 100644
--- a/nimble/transport/usb/syscfg.yml
+++ b/nimble/transport/usb/syscfg.yml
@@ -17,24 +17,6 @@
 #
 
 syscfg.defs:
-    BLE_HCI_EVT_BUF_SIZE:
-        description: 'The size of the allocated event buffers'
-        value: 70
-    BLE_HCI_EVT_HI_BUF_COUNT:
-        description: 'The number of high priority event buffers'
-        value: 8
-    BLE_HCI_EVT_LO_BUF_COUNT:
-        description: 'The number of low priority event buffers'
-        value: 8
-    BLE_ACL_BUF_COUNT:
-        description: 'The number of ACL data buffers'
-        value: 12
-    BLE_ACL_BUF_SIZE:
-        description: >
-            This is the maximum size of the data portion of HCI ACL data
-            packets. It does not include the HCI data header (of 4 bytes).
-        value: 255
-
     BLE_HCI_ACL_OUT_COUNT:
         description: >
             This count is used in creating a pool of elements used by the
@@ -53,5 +35,12 @@ syscfg.defs:
 syscfg.vals.BLE_EXT_ADV:
     BLE_HCI_EVT_BUF_SIZE: 257
 
+syscfg.vals:
+    BLE_HCI_EVT_BUF_SIZE: 70
+    BLE_HCI_EVT_HI_BUF_COUNT: 8
+    BLE_HCI_EVT_LO_BUF_COUNT: 8
+    BLE_ACL_BUF_COUNT: 12
+    BLE_ACL_BUF_SIZE: 255
+
 syscfg.restrictions:
     - '!BLE_HOST'
diff --git a/porting/examples/dummy/Makefile b/porting/examples/dummy/Makefile
index 861934f7f0..1c38ac4a3c 100644
--- a/porting/examples/dummy/Makefile
+++ b/porting/examples/dummy/Makefile
@@ -60,6 +60,7 @@ OBJ := $(SRC:.c=.o)
 TINYCRYPT_OBJ := $(TINYCRYPT_SRC:.c=.o)
 
 CFLAGS := $(NIMBLE_CFLAGS)
+LDFLAGS := $(NIMBLE_LDFLAGS)
 
 .PHONY: all clean
 .DEFAULT: all
@@ -76,4 +77,4 @@ $(TINYCRYPT_OBJ): CFLAGS+=$(TINYCRYPT_CFLAGS)
 	$(CC) -c $(addprefix -I, $(INC)) $(CFLAGS) -o $@ $<
 
 dummy: $(OBJ) $(TINYCRYPT_OBJ)
-	$(CC) -o $@ $^
+	$(CC) -o $@ $^ $(LDFLAGS)
diff --git a/porting/examples/linux/Makefile b/porting/examples/linux/Makefile
index 11f4ebc308..c3eee16cb5 100644
--- a/porting/examples/linux/Makefile
+++ b/porting/examples/linux/Makefile
@@ -81,7 +81,7 @@ CFLAGS =                    \
     -D_GNU_SOURCE           \
     $(NULL)
 
-LIBS := -lrt -lpthread -lstdc++
+LIBS := $(NIMBLE_LDFLAGS) -lrt -lpthread -lstdc++
 
 .PHONY: all clean
 .DEFAULT: all
diff --git a/porting/examples/linux/include/logcfg/logcfg.h b/porting/examples/linux/include/logcfg/logcfg.h
index 837cdeac1f..fab4d812fc 100644
--- a/porting/examples/linux/include/logcfg/logcfg.h
+++ b/porting/examples/linux/include/logcfg/logcfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_LOGCFG_
@@ -22,11 +22,4 @@
 #define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__)
 #define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__)
 
-#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__)
-
 #endif
diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h
index 99939f2b41..fc9ef468fc 100644
--- a/porting/examples/linux/include/syscfg/syscfg.h
+++ b/porting/examples/linux/include/syscfg/syscfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSCFG_
@@ -28,11 +28,20 @@
 #define MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG (0)
 #endif
 
+/*** @apache-mynewt-core/hw/bsp/native */
+#ifndef MYNEWT_VAL_BSP_SIMULATED
+#define MYNEWT_VAL_BSP_SIMULATED (1)
+#endif
+
 /*** @apache-mynewt-core/hw/hal */
 #ifndef MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS
 #define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT
+#define MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT (0)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
 #endif
@@ -45,10 +54,47 @@
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_SBRK
+#define MYNEWT_VAL_HAL_SBRK (1)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
 #define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
 #endif
 
+/*** @apache-mynewt-core/hw/mcu/native */
+#ifndef MYNEWT_VAL_I2C_0
+#define MYNEWT_VAL_I2C_0 (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE
+#define MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC
+#define MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_ST
+#define MYNEWT_VAL_MCU_FLASH_STYLE_ST (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE
+#define MYNEWT_VAL_MCU_NATIVE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS
+#define MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_TIMER_POLLER_PRIO
+#define MYNEWT_VAL_MCU_TIMER_POLLER_PRIO (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_UART_POLLER_PRIO
+#define MYNEWT_VAL_MCU_UART_POLLER_PRIO (1)
+#endif
+
 /*** @apache-mynewt-core/kernel/os */
 #ifndef MYNEWT_VAL_FLOAT_USER
 #define MYNEWT_VAL_FLOAT_USER (0)
@@ -94,6 +140,10 @@
 #define MYNEWT_VAL_OS_COREDUMP (0)
 #endif
 
+#ifndef MYNEWT_VAL_OS_COREDUMP_CB
+#define MYNEWT_VAL_OS_COREDUMP_CB (0)
+#endif
+
 #ifndef MYNEWT_VAL_OS_CPUTIME_FREQ
 #define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000)
 #endif
@@ -102,8 +152,9 @@
 #define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_CRASH_FILE_LINE
-#define MYNEWT_VAL_OS_CRASH_FILE_LINE (0)
+#define MYNEWT_VAL_OS_CRASH_FILE_LINE (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_CRASH_LOG
@@ -142,8 +193,9 @@
 #define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MAX (600000)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN
-#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (100)
+#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_MAIN_STACK_SIZE
@@ -210,6 +262,11 @@
 #define MYNEWT_VAL_OS_TASK_RUN_TIME_CPUTIME (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/mcu/native (defined by @apache-mynewt-core/kernel/os) */
+#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
+#define MYNEWT_VAL_OS_TICKS_PER_SEC (100)
+#endif
+
 #ifndef MYNEWT_VAL_OS_TIME_DEBUG
 #define MYNEWT_VAL_OS_TIME_DEBUG (0)
 #endif
@@ -226,6 +283,33 @@
 #define MYNEWT_VAL_WATCHDOG_INTERVAL (30000)
 #endif
 
+/*** @apache-mynewt-core/net/ip/native_sockets */
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX (8)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP (2048)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS
+#define MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS (200)
+#endif
+
+#undef MYNEWT_VAL_NATIVE_SOCKETS_POLL_ITVL
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_PRIO
+#define MYNEWT_VAL_NATIVE_SOCKETS_PRIO (2)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ
+#define MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ (4096)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE
+#define MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE (200)
+#endif
+
 /*** @apache-mynewt-core/sys/console/stub */
 #ifndef MYNEWT_VAL_CONSOLE_UART_BAUD
 #define MYNEWT_VAL_CONSOLE_UART_BAUD (115200)
@@ -244,8 +328,12 @@
 #define MYNEWT_VAL_FLASH_MAP_MAX_AREAS (10)
 #endif
 
+#ifndef MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG
+#define MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG (0)
+#endif
+
 #ifndef MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE
-#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (2)
+#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (9)
 #endif
 
 /*** @apache-mynewt-core/sys/log/common */
@@ -300,23 +388,6 @@
 #define MYNEWT_VAL_LOG_LEVEL (0)
 #endif
 
-/*** @apache-mynewt-core/sys/mfg */
-#ifndef MYNEWT_VAL_MFG_LOG_LVL
-#define MYNEWT_VAL_MFG_LOG_LVL (15)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_LOG_MODULE
-#define MYNEWT_VAL_MFG_LOG_MODULE (128)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_MAX_MMRS
-#define MYNEWT_VAL_MFG_MAX_MMRS (2)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_SYSINIT_STAGE
-#define MYNEWT_VAL_MFG_SYSINIT_STAGE (100)
-#endif
-
 /*** @apache-mynewt-core/sys/sys */
 #ifndef MYNEWT_VAL_DEBUG_PANIC_ENABLED
 #define MYNEWT_VAL_DEBUG_PANIC_ENABLED (1)
@@ -344,12 +415,14 @@
 #define MYNEWT_VAL_SYSINIT_CONSTRAIN_INIT (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE
-#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_MESSAGE
-#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (1)
 #endif
 
 /*** @apache-mynewt-core/util/rwlock */
@@ -366,6 +439,10 @@
 #define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_HCI_VS
+#define MYNEWT_VAL_BLE_HCI_VS (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_ISO
 #define MYNEWT_VAL_BLE_ISO (0)
 #endif
@@ -739,6 +816,14 @@
 #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_SM_SC_LVL
+#define MYNEWT_VAL_BLE_SM_SC_LVL (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
+#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
 #define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0)
 #endif
@@ -985,19 +1070,19 @@
 #endif
 
 #ifndef MYNEWT_VAL_ARCH_NAME
-#define MYNEWT_VAL_ARCH_NAME ("dummy")
+#define MYNEWT_VAL_ARCH_NAME ("sim")
 #endif
 
-#ifndef MYNEWT_VAL_ARCH_dummy
-#define MYNEWT_VAL_ARCH_dummy (1)
+#ifndef MYNEWT_VAL_ARCH_sim
+#define MYNEWT_VAL_ARCH_sim (1)
 #endif
 
 #ifndef MYNEWT_VAL_BSP_NAME
-#define MYNEWT_VAL_BSP_NAME ("dummy_bsp")
+#define MYNEWT_VAL_BSP_NAME ("native")
 #endif
 
-#ifndef MYNEWT_VAL_BSP_dummy_bsp
-#define MYNEWT_VAL_BSP_dummy_bsp (1)
+#ifndef MYNEWT_VAL_BSP_native
+#define MYNEWT_VAL_BSP_native (1)
 #endif
 
 #ifndef MYNEWT_VAL_NEWT_FEATURE_LOGCFG
diff --git a/porting/examples/linux/include/sysflash/sysflash.h b/porting/examples/linux/include/sysflash/sysflash.h
index ab1341b25d..28391ca66a 100644
--- a/porting/examples/linux/include/sysflash/sysflash.h
+++ b/porting/examples/linux/include/sysflash/sysflash.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSFLASH_
diff --git a/porting/examples/linux/main.c b/porting/examples/linux/main.c
index 86b6fb9559..a2e255d083 100644
--- a/porting/examples/linux/main.c
+++ b/porting/examples/linux/main.c
@@ -65,8 +65,8 @@ int main(int argc, char *argv[])
         ble_hci_sock_set_device(atoi(argv[1]));
     }
 
-    ble_hci_sock_init();
     nimble_port_init();
+    ble_hci_sock_init();
 
     /* This example provides GATT Alert service */
     ble_svc_gap_init();
diff --git a/porting/examples/linux_blemesh/Makefile b/porting/examples/linux_blemesh/Makefile
index 556fc8ca6c..67783f2766 100644
--- a/porting/examples/linux_blemesh/Makefile
+++ b/porting/examples/linux_blemesh/Makefile
@@ -17,7 +17,7 @@
 
 # Toolchain commands
 CROSS_COMPILE ?=
-CC      := ccache $(CROSS_COMPILIE)gcc
+CC      := ccache $(CROSS_COMPILE)gcc
 CXX     := ccache $(CROSS_COMPILE)g++
 LD      := $(CROSS_COMPILE)gcc
 AR      := $(CROSS_COMPILE)ar
@@ -83,7 +83,7 @@ CFLAGS =                    \
     -D_GNU_SOURCE           \
     $(NULL)
 
-LIBS := -lrt -lpthread -lstdc++
+LIBS := $(NIMBLE_LDFLAGS) -lrt -lpthread -lstdc++
 
 .PHONY: all clean
 .DEFAULT: all
diff --git a/porting/examples/linux_blemesh/ble.c b/porting/examples/linux_blemesh/ble.c
index deaef5a462..7a64eac9ea 100644
--- a/porting/examples/linux_blemesh/ble.c
+++ b/porting/examples/linux_blemesh/ble.c
@@ -142,11 +142,12 @@ static struct bt_mesh_model_pub gen_onoff_pub;
 static uint8_t gen_on_off_state;
 static int16_t gen_level_state;
 
-static void gen_onoff_status(struct bt_mesh_model *model,
+static int gen_onoff_status(struct bt_mesh_model *model,
                              struct bt_mesh_msg_ctx *ctx)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(3);
     uint8_t *status;
+    int rc;
 
     console_printf("#mesh-onoff STATUS\n");
 
@@ -154,23 +155,25 @@ static void gen_onoff_status(struct bt_mesh_model *model,
     status = net_buf_simple_add(msg, 1);
     *status = gen_on_off_state;
 
-    if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+    if (rc) {
         console_printf("#mesh-onoff STATUS: send status failed\n");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
-static void gen_onoff_get(struct bt_mesh_model *model,
+static int gen_onoff_get(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
     console_printf("#mesh-onoff GET\n");
 
-    gen_onoff_status(model, ctx);
+    return gen_onoff_status(model, ctx);
 }
 
-static void gen_onoff_set(struct bt_mesh_model *model,
+static int gen_onoff_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
@@ -178,16 +181,17 @@ static void gen_onoff_set(struct bt_mesh_model *model,
 
     gen_on_off_state = buf->om_data[0];
 
-    gen_onoff_status(model, ctx);
+    return gen_onoff_status(model, ctx);
 }
 
-static void gen_onoff_set_unack(struct bt_mesh_model *model,
+static int gen_onoff_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
     console_printf("#mesh-onoff SET-UNACK\n");
 
     gen_on_off_state = buf->om_data[0];
+    return 0;
 }
 
 static const struct bt_mesh_model_op gen_onoff_op[] = {
@@ -214,16 +218,17 @@ static void gen_level_status(struct bt_mesh_model *model,
     os_mbuf_free_chain(msg);
 }
 
-static void gen_level_get(struct bt_mesh_model *model,
+static int gen_level_get(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
     console_printf("#mesh-level GET\n");
 
     gen_level_status(model, ctx);
+    return 0;
 }
 
-static void gen_level_set(struct bt_mesh_model *model,
+static int gen_level_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
@@ -236,9 +241,10 @@ static void gen_level_set(struct bt_mesh_model *model,
 
     gen_level_state = level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_level_set_unack(struct bt_mesh_model *model,
+static int gen_level_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
@@ -249,9 +255,10 @@ static void gen_level_set_unack(struct bt_mesh_model *model,
 
     gen_level_state = level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_delta_set(struct bt_mesh_model *model,
+static int gen_delta_set(struct bt_mesh_model *model,
               struct bt_mesh_msg_ctx *ctx,
               struct os_mbuf *buf)
 {
@@ -264,9 +271,10 @@ static void gen_delta_set(struct bt_mesh_model *model,
 
     gen_level_state += delta_level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_delta_set_unack(struct bt_mesh_model *model,
+static int gen_delta_set_unack(struct bt_mesh_model *model,
                 struct bt_mesh_msg_ctx *ctx,
                 struct os_mbuf *buf)
 {
@@ -277,18 +285,21 @@ static void gen_delta_set_unack(struct bt_mesh_model *model,
 
     gen_level_state += delta_level;
     console_printf("#mesh-level: level=%d\n", gen_level_state);
+    return 0;
 }
 
-static void gen_move_set(struct bt_mesh_model *model,
+static int gen_move_set(struct bt_mesh_model *model,
              struct bt_mesh_msg_ctx *ctx,
              struct os_mbuf *buf)
 {
+    return 0;
 }
 
-static void gen_move_set_unack(struct bt_mesh_model *model,
+static int gen_move_set_unack(struct bt_mesh_model *model,
                    struct bt_mesh_msg_ctx *ctx,
                    struct os_mbuf *buf)
 {
+    return 0;
 }
 
 static const struct bt_mesh_model_op gen_level_op[] = {
@@ -313,11 +324,12 @@ static struct bt_mesh_model root_models[] = {
 
 static struct bt_mesh_model_pub vnd_model_pub;
 
-static void vnd_model_recv(struct bt_mesh_model *model,
+static int vnd_model_recv(struct bt_mesh_model *model,
                            struct bt_mesh_msg_ctx *ctx,
                            struct os_mbuf *buf)
 {
     struct os_mbuf *msg = NET_BUF_SIMPLE(3);
+    int rc;
 
     console_printf("#vendor-model-recv\n");
 
@@ -327,11 +339,13 @@ static void vnd_model_recv(struct bt_mesh_model *model,
     bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR));
     os_mbuf_append(msg, buf->om_data, buf->om_len);
 
-    if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+    rc = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
+    if (rc) {
         console_printf("#vendor-model-recv: send rsp failed\n");
     }
 
     os_mbuf_free_chain(msg);
+    return rc;
 }
 
 static const struct bt_mesh_model_op vnd_model_op[] = {
diff --git a/porting/examples/linux_blemesh/include/logcfg/logcfg.h b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
index 798418dabb..c3b5cdf400 100644
--- a/porting/examples/linux_blemesh/include/logcfg/logcfg.h
+++ b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_LOGCFG_
@@ -50,6 +50,13 @@
 #define BLE_MESH_FRIEND_LOG_CRITICAL(...) MODLOG_CRITICAL(14, __VA_ARGS__)
 #define BLE_MESH_FRIEND_LOG_DISABLED(...) MODLOG_DISABLED(14, __VA_ARGS__)
 
+#define BLE_MESH_HEARTBEAT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_INFO(...) MODLOG_INFO(26, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_WARN(...) MODLOG_WARN(26, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_ERROR(...) MODLOG_ERROR(26, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_CRITICAL(...) MODLOG_CRITICAL(26, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_DISABLED(...) MODLOG_DISABLED(26, __VA_ARGS__)
+
 #define BLE_MESH_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define BLE_MESH_LOG_INFO(...) MODLOG_INFO(9, __VA_ARGS__)
 #define BLE_MESH_LOG_WARN(...) MODLOG_WARN(9, __VA_ARGS__)
@@ -71,6 +78,13 @@
 #define BLE_MESH_MODEL_LOG_CRITICAL(...) MODLOG_CRITICAL(16, __VA_ARGS__)
 #define BLE_MESH_MODEL_LOG_DISABLED(...) MODLOG_DISABLED(16, __VA_ARGS__)
 
+#define BLE_MESH_NET_KEYS_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_INFO(...) MODLOG_INFO(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_WARN(...) MODLOG_WARN(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_ERROR(...) MODLOG_ERROR(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_CRITICAL(...) MODLOG_CRITICAL(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_DISABLED(...) MODLOG_DISABLED(23, __VA_ARGS__)
+
 #define BLE_MESH_NET_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define BLE_MESH_NET_LOG_INFO(...) MODLOG_INFO(17, __VA_ARGS__)
 #define BLE_MESH_NET_LOG_WARN(...) MODLOG_WARN(17, __VA_ARGS__)
@@ -78,6 +92,20 @@
 #define BLE_MESH_NET_LOG_CRITICAL(...) MODLOG_CRITICAL(17, __VA_ARGS__)
 #define BLE_MESH_NET_LOG_DISABLED(...) MODLOG_DISABLED(17, __VA_ARGS__)
 
+#define BLE_MESH_PROVISIONER_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_PROVISIONER_LOG_INFO(...) MODLOG_INFO(25, __VA_ARGS__)
+#define BLE_MESH_PROVISIONER_LOG_WARN(...) MODLOG_WARN(25, __VA_ARGS__)
+#define BLE_MESH_PROVISIONER_LOG_ERROR(...) MODLOG_ERROR(25, __VA_ARGS__)
+#define BLE_MESH_PROVISIONER_LOG_CRITICAL(...) MODLOG_CRITICAL(25, __VA_ARGS__)
+#define BLE_MESH_PROVISIONER_LOG_DISABLED(...) MODLOG_DISABLED(25, __VA_ARGS__)
+
+#define BLE_MESH_PROV_DEVICE_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_PROV_DEVICE_LOG_INFO(...) MODLOG_INFO(24, __VA_ARGS__)
+#define BLE_MESH_PROV_DEVICE_LOG_WARN(...) MODLOG_WARN(24, __VA_ARGS__)
+#define BLE_MESH_PROV_DEVICE_LOG_ERROR(...) MODLOG_ERROR(24, __VA_ARGS__)
+#define BLE_MESH_PROV_DEVICE_LOG_CRITICAL(...) MODLOG_CRITICAL(24, __VA_ARGS__)
+#define BLE_MESH_PROV_DEVICE_LOG_DISABLED(...) MODLOG_DISABLED(24, __VA_ARGS__)
+
 #define BLE_MESH_PROV_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define BLE_MESH_PROV_LOG_INFO(...) MODLOG_INFO(18, __VA_ARGS__)
 #define BLE_MESH_PROV_LOG_WARN(...) MODLOG_WARN(18, __VA_ARGS__)
@@ -92,6 +120,13 @@
 #define BLE_MESH_PROXY_LOG_CRITICAL(...) MODLOG_CRITICAL(19, __VA_ARGS__)
 #define BLE_MESH_PROXY_LOG_DISABLED(...) MODLOG_DISABLED(19, __VA_ARGS__)
 
+#define BLE_MESH_RPL_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_RPL_LOG_INFO(...) MODLOG_INFO(22, __VA_ARGS__)
+#define BLE_MESH_RPL_LOG_WARN(...) MODLOG_WARN(22, __VA_ARGS__)
+#define BLE_MESH_RPL_LOG_ERROR(...) MODLOG_ERROR(22, __VA_ARGS__)
+#define BLE_MESH_RPL_LOG_CRITICAL(...) MODLOG_CRITICAL(22, __VA_ARGS__)
+#define BLE_MESH_RPL_LOG_DISABLED(...) MODLOG_DISABLED(22, __VA_ARGS__)
+
 #define BLE_MESH_SETTINGS_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define BLE_MESH_SETTINGS_LOG_INFO(...) MODLOG_INFO(20, __VA_ARGS__)
 #define BLE_MESH_SETTINGS_LOG_WARN(...) MODLOG_WARN(20, __VA_ARGS__)
@@ -106,34 +141,6 @@
 #define BLE_MESH_TRANS_LOG_CRITICAL(...) MODLOG_CRITICAL(21, __VA_ARGS__)
 #define BLE_MESH_TRANS_LOG_DISABLED(...) MODLOG_DISABLED(21, __VA_ARGS__)
 
-#define BLE_MESH_RPL_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define BLE_MESH_RPL_LOG_INFO(...) MODLOG_INFO(22, __VA_ARGS__)
-#define BLE_MESH_RPL_LOG_WARN(...) MODLOG_WARN(22, __VA_ARGS__)
-#define BLE_MESH_RPL_LOG_ERROR(...) MODLOG_ERROR(22, __VA_ARGS__)
-#define BLE_MESH_RPL_LOG_CRITICAL(...) MODLOG_CRITICAL(22, __VA_ARGS__)
-#define BLE_MESH_RPL_LOG_DISABLED(...) MODLOG_DISABLED(22, __VA_ARGS__)
-
-#define BLE_MESH_NET_KEYS_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define BLE_MESH_NET_KEYS_LOG_INFO(...) MODLOG_INFO(23, __VA_ARGS__)
-#define BLE_MESH_NET_KEYS_LOG_WARN(...) MODLOG_WARN(23, __VA_ARGS__)
-#define BLE_MESH_NET_KEYS_LOG_ERROR(...) MODLOG_ERROR(23, __VA_ARGS__)
-#define BLE_MESH_NET_KEYS_LOG_CRITICAL(...) MODLOG_CRITICAL(23, __VA_ARGS__)
-#define BLE_MESH_NET_KEYS_LOG_DISABLED(...) MODLOG_DISABLED(23, __VA_ARGS__)
-
-#define BLE_MESH_PROV_DEVICE_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define BLE_MESH_PROV_DEVICE_LOG_INFO(...) MODLOG_INFO(24, __VA_ARGS__)
-#define BLE_MESH_PROV_DEVICE_LOG_WARN(...) MODLOG_WARN(24, __VA_ARGS__)
-#define BLE_MESH_PROV_DEVICE_LOG_ERROR(...) MODLOG_ERROR(24, __VA_ARGS__)
-#define BLE_MESH_PROV_DEVICE_LOG_CRITICAL(...) MODLOG_CRITICAL(24, __VA_ARGS__)
-#define BLE_MESH_PROV_DEVICE_LOG_DISABLED(...) MODLOG_DISABLED(24, __VA_ARGS__)
-
-#define BLE_MESH_HEARTBEAT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define BLE_MESH_HEARTBEAT_LOG_INFO(...) MODLOG_INFO(25, __VA_ARGS__)
-#define BLE_MESH_HEARTBEAT_LOG_WARN(...) MODLOG_WARN(25, __VA_ARGS__)
-#define BLE_MESH_HEARTBEAT_LOG_ERROR(...) MODLOG_ERROR(25, __VA_ARGS__)
-#define BLE_MESH_HEARTBEAT_LOG_CRITICAL(...) MODLOG_CRITICAL(25, __VA_ARGS__)
-#define BLE_MESH_HEARTBEAT_LOG_DISABLED(...) MODLOG_DISABLED(25, __VA_ARGS__)
-
 #define DFLT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define DFLT_LOG_INFO(...) MODLOG_INFO(0, __VA_ARGS__)
 #define DFLT_LOG_WARN(...) MODLOG_WARN(0, __VA_ARGS__)
@@ -141,11 +148,4 @@
 #define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__)
 #define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__)
 
-#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__)
-
 #endif
diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h
index 9aac1068ab..3f9258d896 100644
--- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h
+++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSCFG_
@@ -28,11 +28,20 @@
 #define MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG (0)
 #endif
 
+/*** @apache-mynewt-core/hw/bsp/native */
+#ifndef MYNEWT_VAL_BSP_SIMULATED
+#define MYNEWT_VAL_BSP_SIMULATED (1)
+#endif
+
 /*** @apache-mynewt-core/hw/hal */
 #ifndef MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS
 #define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT
+#define MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT (0)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
 #endif
@@ -45,10 +54,47 @@
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_SBRK
+#define MYNEWT_VAL_HAL_SBRK (1)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
 #define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
 #endif
 
+/*** @apache-mynewt-core/hw/mcu/native */
+#ifndef MYNEWT_VAL_I2C_0
+#define MYNEWT_VAL_I2C_0 (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE
+#define MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC
+#define MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_ST
+#define MYNEWT_VAL_MCU_FLASH_STYLE_ST (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE
+#define MYNEWT_VAL_MCU_NATIVE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS
+#define MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_TIMER_POLLER_PRIO
+#define MYNEWT_VAL_MCU_TIMER_POLLER_PRIO (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_UART_POLLER_PRIO
+#define MYNEWT_VAL_MCU_UART_POLLER_PRIO (1)
+#endif
+
 /*** @apache-mynewt-core/kernel/os */
 #ifndef MYNEWT_VAL_FLOAT_USER
 #define MYNEWT_VAL_FLOAT_USER (0)
@@ -95,6 +141,10 @@
 #define MYNEWT_VAL_OS_COREDUMP (0)
 #endif
 
+#ifndef MYNEWT_VAL_OS_COREDUMP_CB
+#define MYNEWT_VAL_OS_COREDUMP_CB (0)
+#endif
+
 #ifndef MYNEWT_VAL_OS_CPUTIME_FREQ
 #define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000)
 #endif
@@ -103,8 +153,9 @@
 #define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_CRASH_FILE_LINE
-#define MYNEWT_VAL_OS_CRASH_FILE_LINE (0)
+#define MYNEWT_VAL_OS_CRASH_FILE_LINE (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_CRASH_LOG
@@ -143,8 +194,9 @@
 #define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MAX (600000)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN
-#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (100)
+#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_MAIN_STACK_SIZE
@@ -211,6 +263,11 @@
 #define MYNEWT_VAL_OS_TASK_RUN_TIME_CPUTIME (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/mcu/native (defined by @apache-mynewt-core/kernel/os) */
+#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
+#define MYNEWT_VAL_OS_TICKS_PER_SEC (100)
+#endif
+
 #ifndef MYNEWT_VAL_OS_TIME_DEBUG
 #define MYNEWT_VAL_OS_TIME_DEBUG (0)
 #endif
@@ -227,6 +284,33 @@
 #define MYNEWT_VAL_WATCHDOG_INTERVAL (30000)
 #endif
 
+/*** @apache-mynewt-core/net/ip/native_sockets */
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX (8)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP (2048)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS
+#define MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS (200)
+#endif
+
+#undef MYNEWT_VAL_NATIVE_SOCKETS_POLL_ITVL
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_PRIO
+#define MYNEWT_VAL_NATIVE_SOCKETS_PRIO (2)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ
+#define MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ (4096)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE
+#define MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE (200)
+#endif
+
 /*** @apache-mynewt-core/sys/console/stub */
 #ifndef MYNEWT_VAL_CONSOLE_UART_BAUD
 #define MYNEWT_VAL_CONSOLE_UART_BAUD (115200)
@@ -245,8 +329,12 @@
 #define MYNEWT_VAL_FLASH_MAP_MAX_AREAS (10)
 #endif
 
+#ifndef MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG
+#define MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG (0)
+#endif
+
 #ifndef MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE
-#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (2)
+#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (9)
 #endif
 
 /*** @apache-mynewt-core/sys/log/common */
@@ -301,23 +389,6 @@
 #define MYNEWT_VAL_LOG_LEVEL (0)
 #endif
 
-/*** @apache-mynewt-core/sys/mfg */
-#ifndef MYNEWT_VAL_MFG_LOG_LVL
-#define MYNEWT_VAL_MFG_LOG_LVL (15)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_LOG_MODULE
-#define MYNEWT_VAL_MFG_LOG_MODULE (128)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_MAX_MMRS
-#define MYNEWT_VAL_MFG_MAX_MMRS (2)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_SYSINIT_STAGE
-#define MYNEWT_VAL_MFG_SYSINIT_STAGE (100)
-#endif
-
 /*** @apache-mynewt-core/sys/sys */
 #ifndef MYNEWT_VAL_DEBUG_PANIC_ENABLED
 #define MYNEWT_VAL_DEBUG_PANIC_ENABLED (1)
@@ -345,12 +416,14 @@
 #define MYNEWT_VAL_SYSINIT_CONSTRAIN_INIT (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE
-#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_MESSAGE
-#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (1)
 #endif
 
 /*** @apache-mynewt-core/util/rwlock */
@@ -367,6 +440,10 @@
 #define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_HCI_VS
+#define MYNEWT_VAL_BLE_HCI_VS (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_ISO
 #define MYNEWT_VAL_BLE_ISO (0)
 #endif
@@ -742,6 +819,14 @@
 #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_SM_SC_LVL
+#define MYNEWT_VAL_BLE_SM_SC_LVL (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
+#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
 #define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0)
 #endif
@@ -755,6 +840,10 @@
 #endif
 
 /*** @apache-mynewt-nimble/nimble/host/mesh */
+#ifndef MYNEWT_VAL_BLE_MESH_ACCESS_LAYER_MSG
+#define MYNEWT_VAL_BLE_MESH_ACCESS_LAYER_MSG (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_ACCESS_LOG_LVL
 #define MYNEWT_VAL_BLE_MESH_ACCESS_LOG_LVL (1)
 #endif
@@ -763,11 +852,23 @@
 #define MYNEWT_VAL_BLE_MESH_ACCESS_LOG_MOD (10)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_ADV
+#define MYNEWT_VAL_BLE_MESH_ADV (1)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT
 #define MYNEWT_VAL_BLE_MESH_ADV_BUF_COUNT (20)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_ADV_EXT
+#define MYNEWT_VAL_BLE_MESH_ADV_EXT (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_ADV_LEGACY
+#define MYNEWT_VAL_BLE_MESH_ADV_LEGACY (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_ADV_LOG_LVL
 #define MYNEWT_VAL_BLE_MESH_ADV_LOG_LVL (1)
 #endif
@@ -776,6 +877,10 @@
 #define MYNEWT_VAL_BLE_MESH_ADV_LOG_MOD (11)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_ADV_STACK_SIZE
+#define MYNEWT_VAL_BLE_MESH_ADV_STACK_SIZE (768)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO
 #define MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO (9)
 #endif
@@ -785,6 +890,10 @@
 #define MYNEWT_VAL_BLE_MESH_APP_KEY_COUNT (4)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_BEACON_ENABLED
+#define MYNEWT_VAL_BLE_MESH_BEACON_ENABLED (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_BEACON_LOG_LVL
 #define MYNEWT_VAL_BLE_MESH_BEACON_LOG_LVL (1)
 #endif
@@ -793,6 +902,22 @@
 #define MYNEWT_VAL_BLE_MESH_BEACON_LOG_MOD (12)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_CDB
+#define MYNEWT_VAL_BLE_MESH_CDB (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT
+#define MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT
+#define MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT (8)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT
+#define MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT (1)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_CFG_CLI
 #define MYNEWT_VAL_BLE_MESH_CFG_CLI (1)
@@ -810,6 +935,22 @@
 #define MYNEWT_VAL_BLE_MESH_CRYPTO_LOG_MOD (13)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_CDB
+#define MYNEWT_VAL_BLE_MESH_DEBUG_CDB (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_CFG
+#define MYNEWT_VAL_BLE_MESH_DEBUG_CFG (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_DEBUG_USE_ID_ADDR
+#define MYNEWT_VAL_BLE_MESH_DEBUG_USE_ID_ADDR (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_DEFAULT_TTL
+#define MYNEWT_VAL_BLE_MESH_DEFAULT_TTL (7)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_DEVICE_NAME
 #define MYNEWT_VAL_BLE_MESH_DEVICE_NAME ("nimble-mesh-node")
 #endif
@@ -823,6 +964,10 @@
 #define MYNEWT_VAL_BLE_MESH_FRIEND (1)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_ENABLED
+#define MYNEWT_VAL_BLE_MESH_FRIEND_ENABLED (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_FRIEND_LOG_LVL
 #define MYNEWT_VAL_BLE_MESH_FRIEND_LOG_LVL (1)
 #endif
@@ -851,19 +996,43 @@
 #define MYNEWT_VAL_BLE_MESH_FRIEND_SUB_LIST_SIZE (3)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_GATT
+#define MYNEWT_VAL_BLE_MESH_GATT (1)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_GATT_PROXY
 #define MYNEWT_VAL_BLE_MESH_GATT_PROXY (1)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_GATT_PROXY_ENABLED
+#define MYNEWT_VAL_BLE_MESH_GATT_PROXY_ENABLED (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_GATT_SERVER
+#define MYNEWT_VAL_BLE_MESH_GATT_SERVER (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_HEALTH_CLI
 #define MYNEWT_VAL_BLE_MESH_HEALTH_CLI (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_HEARTBEAT_LOG_LVL
+#define MYNEWT_VAL_BLE_MESH_HEARTBEAT_LOG_LVL (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_HEARTBEAT_LOG_MOD
+#define MYNEWT_VAL_BLE_MESH_HEARTBEAT_LOG_MOD (26)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_IVU_DIVIDER
 #define MYNEWT_VAL_BLE_MESH_IVU_DIVIDER (4)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_IV_UPDATE_SEQ_LIMIT
+#define MYNEWT_VAL_BLE_MESH_IV_UPDATE_SEQ_LIMIT (0x800000)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST
 #define MYNEWT_VAL_BLE_MESH_IV_UPDATE_TEST (1)
@@ -882,6 +1051,10 @@
 #define MYNEWT_VAL_BLE_MESH_LOG_MOD (9)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_LOOPBACK_BUFS
+#define MYNEWT_VAL_BLE_MESH_LOOPBACK_BUFS (3)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_LOW_POWER
 #define MYNEWT_VAL_BLE_MESH_LOW_POWER (1)
@@ -912,10 +1085,6 @@
 #define MYNEWT_VAL_BLE_MESH_LPN_GROUPS (10)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_LPN_SUB_ALL_NODES_ADDR
-#define MYNEWT_VAL_BLE_MESH_LPN_SUB_ALL_NODES_ADDR (1)
-#endif
-
 #ifndef MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT
 #define MYNEWT_VAL_BLE_MESH_LPN_INIT_POLL_TIMEOUT (MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT)
 #endif
@@ -948,6 +1117,10 @@
 #define MYNEWT_VAL_BLE_MESH_LPN_SCAN_LATENCY (10)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_LPN_SUB_ALL_NODES_ADDR
+#define MYNEWT_VAL_BLE_MESH_LPN_SUB_ALL_NODES_ADDR (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_MODEL_EXTENSIONS
 #define MYNEWT_VAL_BLE_MESH_MODEL_EXTENSIONS (0)
 #endif
@@ -969,10 +1142,35 @@
 #define MYNEWT_VAL_BLE_MESH_MODEL_LOG_MOD (16)
 #endif
 
+/* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
+#ifndef MYNEWT_VAL_BLE_MESH_MODEL_VND_MSG_CID_FORCE
+#define MYNEWT_VAL_BLE_MESH_MODEL_VND_MSG_CID_FORCE (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE
 #define MYNEWT_VAL_BLE_MESH_MSG_CACHE_SIZE (10)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_COUNT
+#define MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_COUNT (2)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_INTERVAL
+#define MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_INTERVAL (20)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_NET_BUF_USER_DATA_SIZE
+#define MYNEWT_VAL_BLE_MESH_NET_BUF_USER_DATA_SIZE (4)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_NET_KEYS_LOG_LVL
+#define MYNEWT_VAL_BLE_MESH_NET_KEYS_LOG_LVL (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_NET_KEYS_LOG_MOD
+#define MYNEWT_VAL_BLE_MESH_NET_KEYS_LOG_MOD (23)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_NET_LOG_LVL
 #define MYNEWT_VAL_BLE_MESH_NET_LOG_LVL (1)
 #endif
@@ -981,10 +1179,6 @@
 #define MYNEWT_VAL_BLE_MESH_NET_LOG_MOD (17)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_NODE_COUNT
-#define MYNEWT_VAL_BLE_MESH_NODE_COUNT (1)
-#endif
-
 #ifndef MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT
 #define MYNEWT_VAL_BLE_MESH_NODE_ID_TIMEOUT (60)
 #endif
@@ -1010,34 +1204,46 @@
 #define MYNEWT_VAL_BLE_MESH_PB_ADV (1)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_PB_ADV_RETRANS_TIMEOUT
+#define MYNEWT_VAL_BLE_MESH_PB_ADV_RETRANS_TIMEOUT (500)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_PB_GATT
 #define MYNEWT_VAL_BLE_MESH_PB_GATT (1)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_PB_GATT_USE_DEVICE_NAME
+#define MYNEWT_VAL_BLE_MESH_PB_GATT_USE_DEVICE_NAME (1)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_PROV
 #define MYNEWT_VAL_BLE_MESH_PROV (1)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_PROVISIONER
-#define MYNEWT_VAL_BLE_MESH_PROVISIONER (0)
+#define MYNEWT_VAL_BLE_MESH_PROVISIONER (1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_CDB
-#define MYNEWT_VAL_BLE_MESH_CDB (0)
+#ifndef MYNEWT_VAL_BLE_MESH_PROVISIONER_LOG_LVL
+#define MYNEWT_VAL_BLE_MESH_PROVISIONER_LOG_LVL (1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT
-#define MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT (1)
+#ifndef MYNEWT_VAL_BLE_MESH_PROVISIONER_LOG_MOD
+#define MYNEWT_VAL_BLE_MESH_PROVISIONER_LOG_MOD (25)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT
-#define MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT (1)
+#ifndef MYNEWT_VAL_BLE_MESH_PROV_DEVICE
+#define MYNEWT_VAL_BLE_MESH_PROV_DEVICE (1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT
-#define MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT (1)
+#ifndef MYNEWT_VAL_BLE_MESH_PROV_DEVICE_LOG_LVL
+#define MYNEWT_VAL_BLE_MESH_PROV_DEVICE_LOG_LVL (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_PROV_DEVICE_LOG_MOD
+#define MYNEWT_VAL_BLE_MESH_PROV_DEVICE_LOG_MOD (24)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_PROV_LOG_LVL
@@ -1048,13 +1254,17 @@
 #define MYNEWT_VAL_BLE_MESH_PROV_LOG_MOD (18)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_PROV_OOB_PUBLIC_KEY
+#define MYNEWT_VAL_BLE_MESH_PROV_OOB_PUBLIC_KEY (0)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_PROXY
 #define MYNEWT_VAL_BLE_MESH_PROXY (1)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE
-#define MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE (1)
+#define MYNEWT_VAL_BLE_MESH_PROXY_FILTER_SIZE (3)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_PROXY_LOG_LVL
@@ -1065,17 +1275,43 @@
 #define MYNEWT_VAL_BLE_MESH_PROXY_LOG_MOD (19)
 #endif
 
+/* Overridden by @apache-mynewt-nimble/nimble/host/mesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
+#ifndef MYNEWT_VAL_BLE_MESH_PROXY_MSG_LEN
+#define MYNEWT_VAL_BLE_MESH_PROXY_MSG_LEN (66)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_PROXY_USE_DEVICE_NAME
+#define MYNEWT_VAL_BLE_MESH_PROXY_USE_DEVICE_NAME (0)
+#endif
+
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_RELAY
 #define MYNEWT_VAL_BLE_MESH_RELAY (1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT
-#define MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT (5)
+/* Value copied from BLE_MESH_RELAY */
+#ifndef MYNEWT_VAL_BLE_MESH_RELAY_ENABLED
+#define MYNEWT_VAL_BLE_MESH_RELAY_ENABLED (1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_SEG_BUFS
-#define MYNEWT_VAL_BLE_MESH_SEG_BUFS (72)
+#ifndef MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_COUNT
+#define MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_COUNT (2)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_INTERVAL
+#define MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_INTERVAL (20)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_RPL_LOG_LVL
+#define MYNEWT_VAL_BLE_MESH_RPL_LOG_LVL (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_RPL_LOG_MOD
+#define MYNEWT_VAL_BLE_MESH_RPL_LOG_MOD (22)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT
+#define MYNEWT_VAL_BLE_MESH_RPL_STORE_TIMEOUT (5)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MAX
@@ -1086,6 +1322,10 @@
 #define MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT (2)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_MESH_SEG_BUFS
+#define MYNEWT_VAL_BLE_MESH_SEG_BUFS (64)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_MESH_SEG_RETRANSMIT_ATTEMPTS
 #define MYNEWT_VAL_BLE_MESH_SEG_RETRANSMIT_ATTEMPTS (4)
 #endif
@@ -1146,86 +1386,29 @@
 #define MYNEWT_VAL_BLE_MESH_TRANS_LOG_MOD (21)
 #endif
 
-/* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
-#ifndef MYNEWT_VAL_BLE_MESH_RPL_LOG_LVL
-#define MYNEWT_VAL_BLE_MESH_RPL_LOG_LVL (1)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_RPL_LOG_MOD
-#define MYNEWT_VAL_BLE_MESH_RPL_LOG_MOD (22)
-#endif
-
 /* Overridden by @apache-mynewt-nimble/porting/targets/linux_blemesh (defined by @apache-mynewt-nimble/nimble/host/mesh) */
 #ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MAX
 #define MYNEWT_VAL_BLE_MESH_TX_SEG_MAX (6)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT
-#define MYNEWT_VAL_BLE_MESH_RX_SEG_MSG_COUNT (3)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_UNPROV_BEACON_INT
-#define MYNEWT_VAL_BLE_MESH_UNPROV_BEACON_INT (5)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_PB_ADV_RETRANS_TIMEOUT
-#define MYNEWT_VAL_BLE_MESH_PB_ADV_RETRANS_TIMEOUT (500)
-#endif
-
 #ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT
-#define MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT (4)
+#define MYNEWT_VAL_BLE_MESH_TX_SEG_MSG_COUNT (1)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_COUNT
 #define MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_COUNT (4)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_UNICAST
-#define MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_UNICAST (400)
-#endif
-
 #ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_GROUP
 #define MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_GROUP (50)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_LOOPBACK_BUFS
-#define MYNEWT_VAL_BLE_MESH_LOOPBACK_BUFS (3)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_DEFAULT_TTL
-#define MYNEWT_VAL_BLE_MESH_DEFAULT_TTL (7)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_COUNT
-#define MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_COUNT (2)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_INTERVAL
-#define MYNEWT_VAL_BLE_MESH_NETWORK_TRANSMIT_INTERVAL (20)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_COUNT
-#define MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_COUNT (2)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_BEACON_ENABLED
-#define MYNEWT_VAL_BLE_MESH_BEACON_ENABLED (1)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_GATT_PROXY_ENABLED
-#define MYNEWT_VAL_BLE_MESH_GATT_PROXY_ENABLED (1)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_FRIEND_ENABLED
-#define MYNEWT_VAL_BLE_MESH_FRIEND_ENABLED (1)
-#endif
-
-#ifndef MYNEWT_VAL_BLE_MESH_RELAY_ENABLED
-#define MYNEWT_VAL_BLE_MESH_RELAY_ENABLED (1)
+#ifndef MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_UNICAST
+#define MYNEWT_VAL_BLE_MESH_TX_SEG_RETRANS_TIMEOUT_UNICAST (400)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_INTERVAL
-#define MYNEWT_VAL_BLE_MESH_RELAY_RETRANSMIT_INTERVAL (20)
+#ifndef MYNEWT_VAL_BLE_MESH_UNPROV_BEACON_INT
+#define MYNEWT_VAL_BLE_MESH_UNPROV_BEACON_INT (5)
 #endif
 
 /*** @apache-mynewt-nimble/nimble/host/services/ans */
@@ -1462,19 +1645,19 @@
 #endif
 
 #ifndef MYNEWT_VAL_ARCH_NAME
-#define MYNEWT_VAL_ARCH_NAME ("dummy")
+#define MYNEWT_VAL_ARCH_NAME ("sim")
 #endif
 
-#ifndef MYNEWT_VAL_ARCH_dummy
-#define MYNEWT_VAL_ARCH_dummy (1)
+#ifndef MYNEWT_VAL_ARCH_sim
+#define MYNEWT_VAL_ARCH_sim (1)
 #endif
 
 #ifndef MYNEWT_VAL_BSP_NAME
-#define MYNEWT_VAL_BSP_NAME ("dummy_bsp")
+#define MYNEWT_VAL_BSP_NAME ("native")
 #endif
 
-#ifndef MYNEWT_VAL_BSP_dummy_bsp
-#define MYNEWT_VAL_BSP_dummy_bsp (1)
+#ifndef MYNEWT_VAL_BSP_native
+#define MYNEWT_VAL_BSP_native (1)
 #endif
 
 #ifndef MYNEWT_VAL_NEWT_FEATURE_LOGCFG
diff --git a/porting/examples/linux_blemesh/include/sysflash/sysflash.h b/porting/examples/linux_blemesh/include/sysflash/sysflash.h
index ab1341b25d..28391ca66a 100644
--- a/porting/examples/linux_blemesh/include/sysflash/sysflash.h
+++ b/porting/examples/linux_blemesh/include/sysflash/sysflash.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSFLASH_
diff --git a/porting/examples/linux_blemesh/main.c b/porting/examples/linux_blemesh/main.c
index 0913551e8c..f5c5e4f04f 100644
--- a/porting/examples/linux_blemesh/main.c
+++ b/porting/examples/linux_blemesh/main.c
@@ -77,8 +77,8 @@ int main(int argc, char *argv[])
         ble_hci_sock_set_device(atoi(argv[1]));
     }
 
-    ble_hci_sock_init();
     nimble_port_init();
+    ble_hci_sock_init();
 
     ble_svc_gap_init();
     ble_svc_gatt_init();
diff --git a/porting/examples/nuttx/include/logcfg/logcfg.h b/porting/examples/nuttx/include/logcfg/logcfg.h
index 837cdeac1f..fab4d812fc 100644
--- a/porting/examples/nuttx/include/logcfg/logcfg.h
+++ b/porting/examples/nuttx/include/logcfg/logcfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_LOGCFG_
@@ -22,11 +22,4 @@
 #define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__)
 #define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__)
 
-#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__)
-
 #endif
diff --git a/porting/examples/nuttx/include/syscfg/syscfg.h b/porting/examples/nuttx/include/syscfg/syscfg.h
index ff73312e13..1f5be13597 100644
--- a/porting/examples/nuttx/include/syscfg/syscfg.h
+++ b/porting/examples/nuttx/include/syscfg/syscfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSCFG_
@@ -15,86 +15,84 @@
 #define MYNEWT_VAL(_name)                       MYNEWT_VAL_ ## _name
 #define MYNEWT_VAL_CHOICE(_name, _val)          MYNEWT_VAL_ ## _name ## __ ## _val
 
-
-/*** Repository @apache-mynewt-core info */
-#ifndef MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_CORE
-#define MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_CORE ("4d75fc41bd7ead84638ebbfad4841d5effb296dd")
+/*** @apache-mynewt-core/crypto/tinycrypt */
+#ifndef MYNEWT_VAL_TINYCRYPT_SYSINIT_STAGE
+#define MYNEWT_VAL_TINYCRYPT_SYSINIT_STAGE (200)
 #endif
 
-#ifndef MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_CORE
-#define MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_CORE ("0.0.1")
+#ifndef MYNEWT_VAL_TINYCRYPT_UECC_RNG_TRNG_DEV_NAME
+#define MYNEWT_VAL_TINYCRYPT_UECC_RNG_TRNG_DEV_NAME ("trng")
 #endif
 
-/*** Repository @apache-mynewt-mcumgr info */
-#ifndef MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_MCUMGR
-#define MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_MCUMGR ("8d087a7e0e5485394419d10051606c92d68d2111")
+#ifndef MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG
+#define MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG (0)
 #endif
 
-#ifndef MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_MCUMGR
-#define MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_MCUMGR ("0.0.0")
+/*** @apache-mynewt-core/hw/bsp/native */
+#ifndef MYNEWT_VAL_BSP_SIMULATED
+#define MYNEWT_VAL_BSP_SIMULATED (1)
 #endif
 
-/*** Repository @apache-mynewt-nimble info */
-#ifndef MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_NIMBLE
-#define MYNEWT_VAL_REPO_HASH_APACHE_MYNEWT_NIMBLE ("37dceb35df57ff41a6c31f79290512df2fde7064")
+/*** @apache-mynewt-core/hw/hal */
+#ifndef MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS
+#define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
 #endif
 
-#ifndef MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_NIMBLE
-#define MYNEWT_VAL_REPO_VERSION_APACHE_MYNEWT_NIMBLE ("0.0.0")
+#ifndef MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT
+#define MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT (0)
 #endif
 
-/*** Repository @mcuboot info */
-#ifndef MYNEWT_VAL_REPO_HASH_MCUBOOT
-#define MYNEWT_VAL_REPO_HASH_MCUBOOT ("03d96ad1f6dd77d47ffca72ade9377acb8559115-dirty")
+#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
+#define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
 #endif
 
-#ifndef MYNEWT_VAL_REPO_VERSION_MCUBOOT
-#define MYNEWT_VAL_REPO_VERSION_MCUBOOT ("0.0.0")
+#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_ERASES
+#define MYNEWT_VAL_HAL_FLASH_VERIFY_ERASES (0)
 #endif
 
-/*** Repository @my_project info */
-#ifndef MYNEWT_VAL_REPO_HASH_MY_PROJECT
-#define MYNEWT_VAL_REPO_HASH_MY_PROJECT ("37dceb35df57ff41a6c31f79290512df2fde7064")
+#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES
+#define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
 #endif
 
-#ifndef MYNEWT_VAL_REPO_VERSION_MY_PROJECT
-#define MYNEWT_VAL_REPO_VERSION_MY_PROJECT ("0.0.0")
+#ifndef MYNEWT_VAL_HAL_SBRK
+#define MYNEWT_VAL_HAL_SBRK (1)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
+#define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
+#endif
 
-
-/*** @apache-mynewt-core/crypto/tinycrypt */
-#ifndef MYNEWT_VAL_TINYCRYPT_SYSINIT_STAGE
-#define MYNEWT_VAL_TINYCRYPT_SYSINIT_STAGE (200)
+/*** @apache-mynewt-core/hw/mcu/native */
+#ifndef MYNEWT_VAL_I2C_0
+#define MYNEWT_VAL_I2C_0 (0)
 #endif
 
-#ifndef MYNEWT_VAL_TINYCRYPT_UECC_RNG_TRNG_DEV_NAME
-#define MYNEWT_VAL_TINYCRYPT_UECC_RNG_TRNG_DEV_NAME ("trng")
+#ifndef MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE
+#define MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE (1)
 #endif
 
-#ifndef MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG
-#define MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG (0)
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC
+#define MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC (0)
 #endif
 
-/*** @apache-mynewt-core/hw/hal */
-#ifndef MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS
-#define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_ST
+#define MYNEWT_VAL_MCU_FLASH_STYLE_ST (1)
 #endif
 
-#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
-#define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
+#ifndef MYNEWT_VAL_MCU_NATIVE
+#define MYNEWT_VAL_MCU_NATIVE (1)
 #endif
 
-#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_ERASES
-#define MYNEWT_VAL_HAL_FLASH_VERIFY_ERASES (0)
+#ifndef MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS
+#define MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS (1)
 #endif
 
-#ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES
-#define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
+#ifndef MYNEWT_VAL_MCU_TIMER_POLLER_PRIO
+#define MYNEWT_VAL_MCU_TIMER_POLLER_PRIO (0)
 #endif
 
-#ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
-#define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
+#ifndef MYNEWT_VAL_MCU_UART_POLLER_PRIO
+#define MYNEWT_VAL_MCU_UART_POLLER_PRIO (1)
 #endif
 
 /*** @apache-mynewt-core/kernel/os */
@@ -142,6 +140,10 @@
 #define MYNEWT_VAL_OS_COREDUMP (0)
 #endif
 
+#ifndef MYNEWT_VAL_OS_COREDUMP_CB
+#define MYNEWT_VAL_OS_COREDUMP_CB (0)
+#endif
+
 #ifndef MYNEWT_VAL_OS_CPUTIME_FREQ
 #define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000)
 #endif
@@ -150,8 +152,9 @@
 #define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_CRASH_FILE_LINE
-#define MYNEWT_VAL_OS_CRASH_FILE_LINE (0)
+#define MYNEWT_VAL_OS_CRASH_FILE_LINE (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_CRASH_LOG
@@ -190,8 +193,9 @@
 #define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MAX (600000)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN
-#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (100)
+#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_MAIN_STACK_SIZE
@@ -258,6 +262,11 @@
 #define MYNEWT_VAL_OS_TASK_RUN_TIME_CPUTIME (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/mcu/native (defined by @apache-mynewt-core/kernel/os) */
+#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
+#define MYNEWT_VAL_OS_TICKS_PER_SEC (100)
+#endif
+
 #ifndef MYNEWT_VAL_OS_TIME_DEBUG
 #define MYNEWT_VAL_OS_TIME_DEBUG (0)
 #endif
@@ -274,6 +283,33 @@
 #define MYNEWT_VAL_WATCHDOG_INTERVAL (30000)
 #endif
 
+/*** @apache-mynewt-core/net/ip/native_sockets */
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX (8)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP (2048)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS
+#define MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS (200)
+#endif
+
+#undef MYNEWT_VAL_NATIVE_SOCKETS_POLL_ITVL
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_PRIO
+#define MYNEWT_VAL_NATIVE_SOCKETS_PRIO (2)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ
+#define MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ (4096)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE
+#define MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE (200)
+#endif
+
 /*** @apache-mynewt-core/sys/console/stub */
 #ifndef MYNEWT_VAL_CONSOLE_UART_BAUD
 #define MYNEWT_VAL_CONSOLE_UART_BAUD (115200)
@@ -292,8 +328,12 @@
 #define MYNEWT_VAL_FLASH_MAP_MAX_AREAS (10)
 #endif
 
+#ifndef MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG
+#define MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG (0)
+#endif
+
 #ifndef MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE
-#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (2)
+#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (9)
 #endif
 
 /*** @apache-mynewt-core/sys/log/common */
@@ -348,23 +388,6 @@
 #define MYNEWT_VAL_LOG_LEVEL (2)
 #endif
 
-/*** @apache-mynewt-core/sys/mfg */
-#ifndef MYNEWT_VAL_MFG_LOG_LVL
-#define MYNEWT_VAL_MFG_LOG_LVL (15)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_LOG_MODULE
-#define MYNEWT_VAL_MFG_LOG_MODULE (128)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_MAX_MMRS
-#define MYNEWT_VAL_MFG_MAX_MMRS (2)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_SYSINIT_STAGE
-#define MYNEWT_VAL_MFG_SYSINIT_STAGE (100)
-#endif
-
 /*** @apache-mynewt-core/sys/sys */
 #ifndef MYNEWT_VAL_DEBUG_PANIC_ENABLED
 #define MYNEWT_VAL_DEBUG_PANIC_ENABLED (1)
@@ -392,12 +415,14 @@
 #define MYNEWT_VAL_SYSINIT_CONSTRAIN_INIT (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE
-#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_MESSAGE
-#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (1)
 #endif
 
 /*** @apache-mynewt-core/util/rwlock */
@@ -414,6 +439,10 @@
 #define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_HCI_VS
+#define MYNEWT_VAL_BLE_HCI_VS (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_ISO
 #define MYNEWT_VAL_BLE_ISO (0)
 #endif
@@ -789,6 +818,14 @@
 #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_SM_SC_LVL
+#define MYNEWT_VAL_BLE_SM_SC_LVL (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
+#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
 #define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0)
 #endif
@@ -1015,16 +1052,10 @@
 #define MYNEWT_VAL_BLE_SOCK_TCP_PORT (14433)
 #endif
 
-/* Overridden by @apache-mynewt-nimble/porting/targets/nuttx (defined by @apache-mynewt-nimble/nimble/transport/socket) */
 #ifndef MYNEWT_VAL_BLE_SOCK_USE_LINUX_BLUE
 #define MYNEWT_VAL_BLE_SOCK_USE_LINUX_BLUE (0)
 #endif
 
-/* Overridden by @apache-mynewt-nimble/porting/targets/nuttx (defined by @apache-mynewt-nimble/nimble/transport/socket) */
-#ifndef MYNEWT_VAL_BLE_SOCK_USE_NUTTX
-#define MYNEWT_VAL_BLE_SOCK_USE_NUTTX (1)
-#endif
-
 /* Overridden by @apache-mynewt-nimble/porting/targets/nuttx (defined by @apache-mynewt-nimble/nimble/transport/socket) */
 #ifndef MYNEWT_VAL_BLE_SOCK_USE_TCP
 #define MYNEWT_VAL_BLE_SOCK_USE_TCP (0)
@@ -1040,19 +1071,19 @@
 #endif
 
 #ifndef MYNEWT_VAL_ARCH_NAME
-#define MYNEWT_VAL_ARCH_NAME ("dummy")
+#define MYNEWT_VAL_ARCH_NAME ("sim")
 #endif
 
-#ifndef MYNEWT_VAL_ARCH_dummy
-#define MYNEWT_VAL_ARCH_dummy (1)
+#ifndef MYNEWT_VAL_ARCH_sim
+#define MYNEWT_VAL_ARCH_sim (1)
 #endif
 
 #ifndef MYNEWT_VAL_BSP_NAME
-#define MYNEWT_VAL_BSP_NAME ("dummy_bsp")
+#define MYNEWT_VAL_BSP_NAME ("native")
 #endif
 
-#ifndef MYNEWT_VAL_BSP_dummy_bsp
-#define MYNEWT_VAL_BSP_dummy_bsp (1)
+#ifndef MYNEWT_VAL_BSP_native
+#define MYNEWT_VAL_BSP_native (1)
 #endif
 
 #ifndef MYNEWT_VAL_NEWT_FEATURE_LOGCFG
diff --git a/porting/examples/nuttx/include/sysflash/sysflash.h b/porting/examples/nuttx/include/sysflash/sysflash.h
index ab1341b25d..28391ca66a 100644
--- a/porting/examples/nuttx/include/sysflash/sysflash.h
+++ b/porting/examples/nuttx/include/sysflash/sysflash.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSFLASH_
diff --git a/porting/examples/nuttx/main.c b/porting/examples/nuttx/main.c
index acfc9fcd18..82582573ad 100644
--- a/porting/examples/nuttx/main.c
+++ b/porting/examples/nuttx/main.c
@@ -71,11 +71,12 @@ int main(int argc, char *argv[])
         ble_hci_sock_set_device(atoi(argv[1]));
     }
 
-    printf("hci init\n");
-    ble_hci_sock_init();
     printf("port init\n");
     nimble_port_init();
 
+    printf("hci init\n");
+    ble_hci_sock_init();
+
     /* This example provides GATT Alert service */
     printf("gap init\n");
     ble_svc_gap_init();
diff --git a/porting/nimble/Makefile.defs b/porting/nimble/Makefile.defs
index 5bab893fde..78f73fbc9b 100644
--- a/porting/nimble/Makefile.defs
+++ b/porting/nimble/Makefile.defs
@@ -19,7 +19,10 @@ ifeq (,$(NIMBLE_ROOT))
 $(error NIMBLE_ROOT shall be defined)
 endif
 
-NIMBLE_CFLAGS :=
+# For now this is required as there are places in NimBLE
+# assumingthat pointer is 4 bytes long.
+NIMBLE_CFLAGS := -m32
+NIMBLE_LDFLAGS := -m32
 
 NIMBLE_INCLUDE := \
 	$(NIMBLE_ROOT)/nimble/include \
diff --git a/porting/nimble/include/logcfg/logcfg.h b/porting/nimble/include/logcfg/logcfg.h
index 837cdeac1f..fab4d812fc 100644
--- a/porting/nimble/include/logcfg/logcfg.h
+++ b/porting/nimble/include/logcfg/logcfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_LOGCFG_
@@ -22,11 +22,4 @@
 #define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__)
 #define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__)
 
-#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__)
-
 #endif
diff --git a/porting/nimble/include/os/os_mbuf.h b/porting/nimble/include/os/os_mbuf.h
index 771ea76159..bee47d0bbf 100644
--- a/porting/nimble/include/os/os_mbuf.h
+++ b/porting/nimble/include/os/os_mbuf.h
@@ -133,11 +133,11 @@ struct os_mqueue {
 
 /** Get a packet header pointer given an mbuf pointer */
 #define OS_MBUF_PKTHDR(__om) ((struct os_mbuf_pkthdr *)     \
-    ((uint8_t *)&(__om)->om_data + sizeof(struct os_mbuf)))
+    (void *)((uint8_t *)&(__om)->om_data + sizeof(struct os_mbuf)))
 
 /** Given a mbuf packet header pointer, return a pointer to the mbuf */
 #define OS_MBUF_PKTHDR_TO_MBUF(__hdr)   \
-     (struct os_mbuf *)((uint8_t *)(__hdr) - sizeof(struct os_mbuf))
+     (struct os_mbuf *)(void *)((uint8_t *)(__hdr) - sizeof(struct os_mbuf))
 
 /**
  * Gets the length of an entire mbuf chain.  The specified mbuf must have a
diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h
index c4b70db4bc..a16c528191 100644
--- a/porting/nimble/include/syscfg/syscfg.h
+++ b/porting/nimble/include/syscfg/syscfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSCFG_
@@ -28,11 +28,20 @@
 #define MYNEWT_VAL_TINYCRYPT_UECC_RNG_USE_TRNG (0)
 #endif
 
+/*** @apache-mynewt-core/hw/bsp/native */
+#ifndef MYNEWT_VAL_BSP_SIMULATED
+#define MYNEWT_VAL_BSP_SIMULATED (1)
+#endif
+
 /*** @apache-mynewt-core/hw/hal */
 #ifndef MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS
 #define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT
+#define MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT (0)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
 #endif
@@ -45,10 +54,47 @@
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_SBRK
+#define MYNEWT_VAL_HAL_SBRK (1)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
 #define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
 #endif
 
+/*** @apache-mynewt-core/hw/mcu/native */
+#ifndef MYNEWT_VAL_I2C_0
+#define MYNEWT_VAL_I2C_0 (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE
+#define MYNEWT_VAL_MCU_FLASH_MIN_WRITE_SIZE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC
+#define MYNEWT_VAL_MCU_FLASH_STYLE_NORDIC (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_FLASH_STYLE_ST
+#define MYNEWT_VAL_MCU_FLASH_STYLE_ST (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE
+#define MYNEWT_VAL_MCU_NATIVE (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS
+#define MYNEWT_VAL_MCU_NATIVE_USE_SIGNALS (1)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_TIMER_POLLER_PRIO
+#define MYNEWT_VAL_MCU_TIMER_POLLER_PRIO (0)
+#endif
+
+#ifndef MYNEWT_VAL_MCU_UART_POLLER_PRIO
+#define MYNEWT_VAL_MCU_UART_POLLER_PRIO (1)
+#endif
+
 /*** @apache-mynewt-core/kernel/os */
 #ifndef MYNEWT_VAL_FLOAT_USER
 #define MYNEWT_VAL_FLOAT_USER (0)
@@ -94,6 +140,10 @@
 #define MYNEWT_VAL_OS_COREDUMP (0)
 #endif
 
+#ifndef MYNEWT_VAL_OS_COREDUMP_CB
+#define MYNEWT_VAL_OS_COREDUMP_CB (0)
+#endif
+
 #ifndef MYNEWT_VAL_OS_CPUTIME_FREQ
 #define MYNEWT_VAL_OS_CPUTIME_FREQ (1000000)
 #endif
@@ -102,8 +152,9 @@
 #define MYNEWT_VAL_OS_CPUTIME_TIMER_NUM (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_CRASH_FILE_LINE
-#define MYNEWT_VAL_OS_CRASH_FILE_LINE (0)
+#define MYNEWT_VAL_OS_CRASH_FILE_LINE (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_CRASH_LOG
@@ -142,8 +193,9 @@
 #define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MAX (600000)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN
-#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (100)
+#define MYNEWT_VAL_OS_IDLE_TICKLESS_MS_MIN (1)
 #endif
 
 #ifndef MYNEWT_VAL_OS_MAIN_STACK_SIZE
@@ -210,6 +262,11 @@
 #define MYNEWT_VAL_OS_TASK_RUN_TIME_CPUTIME (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/mcu/native (defined by @apache-mynewt-core/kernel/os) */
+#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
+#define MYNEWT_VAL_OS_TICKS_PER_SEC (100)
+#endif
+
 #ifndef MYNEWT_VAL_OS_TIME_DEBUG
 #define MYNEWT_VAL_OS_TIME_DEBUG (0)
 #endif
@@ -226,6 +283,33 @@
 #define MYNEWT_VAL_WATCHDOG_INTERVAL (30000)
 #endif
 
+/*** @apache-mynewt-core/net/ip/native_sockets */
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX (8)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP
+#define MYNEWT_VAL_NATIVE_SOCKETS_MAX_UDP (2048)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS
+#define MYNEWT_VAL_NATIVE_SOCKETS_POLL_INTERVAL_MS (200)
+#endif
+
+#undef MYNEWT_VAL_NATIVE_SOCKETS_POLL_ITVL
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_PRIO
+#define MYNEWT_VAL_NATIVE_SOCKETS_PRIO (2)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ
+#define MYNEWT_VAL_NATIVE_SOCKETS_STACK_SZ (4096)
+#endif
+
+#ifndef MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE
+#define MYNEWT_VAL_NATIVE_SOCKETS_SYSINIT_STAGE (200)
+#endif
+
 /*** @apache-mynewt-core/sys/console/stub */
 #ifndef MYNEWT_VAL_CONSOLE_UART_BAUD
 #define MYNEWT_VAL_CONSOLE_UART_BAUD (115200)
@@ -244,8 +328,12 @@
 #define MYNEWT_VAL_FLASH_MAP_MAX_AREAS (10)
 #endif
 
+#ifndef MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG
+#define MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG (0)
+#endif
+
 #ifndef MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE
-#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (2)
+#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (9)
 #endif
 
 /*** @apache-mynewt-core/sys/log/common */
@@ -299,23 +387,6 @@
 #define MYNEWT_VAL_LOG_LEVEL (255)
 #endif
 
-/*** @apache-mynewt-core/sys/mfg */
-#ifndef MYNEWT_VAL_MFG_LOG_LVL
-#define MYNEWT_VAL_MFG_LOG_LVL (15)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_LOG_MODULE
-#define MYNEWT_VAL_MFG_LOG_MODULE (128)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_MAX_MMRS
-#define MYNEWT_VAL_MFG_MAX_MMRS (2)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_SYSINIT_STAGE
-#define MYNEWT_VAL_MFG_SYSINIT_STAGE (100)
-#endif
-
 /*** @apache-mynewt-core/sys/sys */
 #ifndef MYNEWT_VAL_DEBUG_PANIC_ENABLED
 #define MYNEWT_VAL_DEBUG_PANIC_ENABLED (1)
@@ -343,12 +414,14 @@
 #define MYNEWT_VAL_SYSINIT_CONSTRAIN_INIT (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE
-#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_FILE_LINE (1)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/bsp/native (defined by @apache-mynewt-core/sys/sysinit) */
 #ifndef MYNEWT_VAL_SYSINIT_PANIC_MESSAGE
-#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (0)
+#define MYNEWT_VAL_SYSINIT_PANIC_MESSAGE (1)
 #endif
 
 /*** @apache-mynewt-core/util/rwlock */
@@ -365,6 +438,10 @@
 #define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_HCI_VS
+#define MYNEWT_VAL_BLE_HCI_VS (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_ISO
 #define MYNEWT_VAL_BLE_ISO (0)
 #endif
@@ -738,6 +815,14 @@
 #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_SM_SC_LVL
+#define MYNEWT_VAL_BLE_SM_SC_LVL (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
+#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
 #define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0)
 #endif
@@ -962,12 +1047,14 @@
 #define MYNEWT_VAL_BLE_SOCK_TCP_PORT (14433)
 #endif
 
+/* Overridden by @apache-mynewt-nimble/porting/targets/porting_default (defined by @apache-mynewt-nimble/nimble/transport/socket) */
 #ifndef MYNEWT_VAL_BLE_SOCK_USE_LINUX_BLUE
-#define MYNEWT_VAL_BLE_SOCK_USE_LINUX_BLUE (0)
+#define MYNEWT_VAL_BLE_SOCK_USE_LINUX_BLUE (1)
 #endif
 
+/* Overridden by @apache-mynewt-nimble/porting/targets/porting_default (defined by @apache-mynewt-nimble/nimble/transport/socket) */
 #ifndef MYNEWT_VAL_BLE_SOCK_USE_TCP
-#define MYNEWT_VAL_BLE_SOCK_USE_TCP (1)
+#define MYNEWT_VAL_BLE_SOCK_USE_TCP (0)
 #endif
 
 /*** newt */
@@ -980,19 +1067,19 @@
 #endif
 
 #ifndef MYNEWT_VAL_ARCH_NAME
-#define MYNEWT_VAL_ARCH_NAME ("dummy")
+#define MYNEWT_VAL_ARCH_NAME ("sim")
 #endif
 
-#ifndef MYNEWT_VAL_ARCH_dummy
-#define MYNEWT_VAL_ARCH_dummy (1)
+#ifndef MYNEWT_VAL_ARCH_sim
+#define MYNEWT_VAL_ARCH_sim (1)
 #endif
 
 #ifndef MYNEWT_VAL_BSP_NAME
-#define MYNEWT_VAL_BSP_NAME ("dummy_bsp")
+#define MYNEWT_VAL_BSP_NAME ("native")
 #endif
 
-#ifndef MYNEWT_VAL_BSP_dummy_bsp
-#define MYNEWT_VAL_BSP_dummy_bsp (1)
+#ifndef MYNEWT_VAL_BSP_native
+#define MYNEWT_VAL_BSP_native (1)
 #endif
 
 #ifndef MYNEWT_VAL_NEWT_FEATURE_LOGCFG
diff --git a/porting/nimble/include/sysflash/sysflash.h b/porting/nimble/include/sysflash/sysflash.h
index ab1341b25d..28391ca66a 100644
--- a/porting/nimble/include/sysflash/sysflash.h
+++ b/porting/nimble/include/sysflash/sysflash.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSFLASH_
diff --git a/porting/nimble/src/nimble_port.c b/porting/nimble/src/nimble_port.c
index 06b5f6df91..d4824d0b37 100644
--- a/porting/nimble/src/nimble_port.c
+++ b/porting/nimble/src/nimble_port.c
@@ -30,6 +30,7 @@
 static struct ble_npl_eventq g_eventq_dflt;
 
 extern void os_msys_init(void);
+extern void os_mempool_module_init(void);
 
 void
 nimble_port_init(void)
@@ -37,14 +38,17 @@ nimble_port_init(void)
     /* Initialize default event queue */
     ble_npl_eventq_init(&g_eventq_dflt);
     /* Initialize the global memory pool */
+    os_mempool_module_init();
     os_msys_init();
     /* Initialize the host */
     ble_hs_init();
 
 #if NIMBLE_CFG_CONTROLLER
     ble_hci_ram_init();
+#ifndef RIOT_VERSION
     hal_timer_init(5, NULL);
     os_cputime_init(32768);
+#endif
     ble_ll_init();
 #endif
 }
diff --git a/porting/npl/freertos/src/npl_os_freertos.c b/porting/npl/freertos/src/npl_os_freertos.c
index 87936bd8de..ecc5cd7b1e 100644
--- a/porting/npl/freertos/src/npl_os_freertos.c
+++ b/porting/npl/freertos/src/npl_os_freertos.c
@@ -279,10 +279,6 @@ npl_freertos_callout_reset(struct ble_npl_callout *co, ble_npl_time_t ticks)
 {
     BaseType_t woken1, woken2, woken3;
 
-    if (ticks < 0) {
-        return BLE_NPL_INVALID_PARAM;
-    }
-
     if (ticks == 0) {
         ticks = 1;
     }
diff --git a/porting/npl/linux/include/nimble/nimble_npl_os.h b/porting/npl/linux/include/nimble/nimble_npl_os.h
index bdd3988699..585d378525 100644
--- a/porting/npl/linux/include/nimble/nimble_npl_os.h
+++ b/porting/npl/linux/include/nimble/nimble_npl_os.h
@@ -23,6 +23,7 @@
 #include <assert.h>
 #include <stdint.h>
 #include <string.h>
+#include <limits.h>
 
 #include "os_types.h"
 
@@ -30,7 +31,7 @@
 extern "C" {
 #endif
 
-#define BLE_NPL_OS_ALIGNMENT    4
+#define BLE_NPL_OS_ALIGNMENT    (__WORDSIZE / 8)
 
 #define BLE_NPL_TIME_FOREVER    INT32_MAX
 
diff --git a/porting/npl/nuttx/src/os_task.c b/porting/npl/nuttx/src/os_task.c
index ab535a36c5..40f8c8c7ba 100644
--- a/porting/npl/nuttx/src/os_task.c
+++ b/porting/npl/nuttx/src/os_task.c
@@ -18,6 +18,8 @@
  */
 
 #include <nuttx/config.h>
+#include <errno.h>
+
 #include "os/os.h"
 #include "nimble/nimble_npl.h"
 
diff --git a/porting/npl/riot/include/logcfg/logcfg.h b/porting/npl/riot/include/logcfg/logcfg.h
index 837cdeac1f..fab4d812fc 100644
--- a/porting/npl/riot/include/logcfg/logcfg.h
+++ b/porting/npl/riot/include/logcfg/logcfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_LOGCFG_
@@ -22,11 +22,4 @@
 #define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__)
 #define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__)
 
-#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__)
-#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__)
-
 #endif
diff --git a/porting/npl/riot/include/nimble/nimble_npl_os.h b/porting/npl/riot/include/nimble/nimble_npl_os.h
index 854fd06c13..4633cbd1a7 100644
--- a/porting/npl/riot/include/nimble/nimble_npl_os.h
+++ b/porting/npl/riot/include/nimble/nimble_npl_os.h
@@ -24,8 +24,12 @@
 #include <stdbool.h>
 #include "event/callback.h"
 #include "mutex.h"
-#include "semaphore.h"
-#include "xtimer.h"
+#include "sema.h"
+#include "ztimer.h"
+
+#if defined(CPU_FAM_NRF51) || defined(CPU_FAM_NRF52)
+#include "nrf_clock.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -38,8 +42,6 @@ extern "C" {
 typedef uint32_t ble_npl_time_t;
 typedef int32_t ble_npl_stime_t;
 
-extern volatile int ble_npl_in_critical;
-
 struct ble_npl_event {
     event_callback_t e;
     void *arg;
@@ -50,8 +52,8 @@ struct ble_npl_eventq {
 };
 
 struct ble_npl_callout {
-    xtimer_t timer;
-    uint64_t target_us;
+    ztimer_t timer;
+    ble_npl_time_t ticks;
     struct ble_npl_event e;
     event_queue_t *q;
 };
@@ -61,7 +63,7 @@ struct ble_npl_mutex {
 };
 
 struct ble_npl_sem {
-    sem_t sem;
+    sema_t sem;
 };
 
 static inline bool
@@ -100,8 +102,9 @@ ble_npl_eventq_get(struct ble_npl_eventq *evq, ble_npl_time_t tmo)
     } else if (tmo == BLE_NPL_TIME_FOREVER) {
         return (struct ble_npl_event *)event_wait(&evq->q);
     } else {
-        return (struct ble_npl_event *)event_wait_timeout64(&evq->q,
-                                                            tmo * US_PER_MS);
+        return (struct ble_npl_event *)event_wait_timeout_ztimer(&evq->q,
+                                                                 ZTIMER_MSEC,
+                                                                 (uint32_t)tmo);
     }
 }
 
@@ -174,49 +177,39 @@ ble_npl_mutex_release(struct ble_npl_mutex *mu)
 static inline ble_npl_error_t
 ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens)
 {
-    int rc;
-
-    rc = sem_init(&sem->sem, 0, tokens);
-
-    return rc == 0 ? BLE_NPL_OK : BLE_NPL_ERROR;
+    sema_create(&sem->sem, (unsigned)tokens);
+    return BLE_NPL_OK;
 }
 
 static inline ble_npl_error_t
 ble_npl_sem_release(struct ble_npl_sem *sem)
 {
-    int rc;
-
-    rc = sem_post(&sem->sem);
-
-    return rc == 0 ? BLE_NPL_OK : BLE_NPL_ERROR;
+    int rc = sema_post(&sem->sem);
+    return (rc == 0) ? BLE_NPL_OK : BLE_NPL_ERROR;
 }
 
 static inline uint16_t
 ble_npl_sem_get_count(struct ble_npl_sem *sem)
 {
-    int val = 0;
-
-    sem_getvalue(&sem->sem, &val);
-
-    return (uint16_t)val;
+    return (uint16_t)sema_get_value(&sem->sem);
 }
 
 static inline void
 ble_npl_callout_stop(struct ble_npl_callout *co)
 {
-    xtimer_remove(&co->timer);
+    ztimer_remove(ZTIMER_MSEC, &co->timer);
 }
 
 static inline bool
 ble_npl_callout_is_active(struct ble_npl_callout *c)
 {
-    return (c->timer.offset || c->timer.long_offset);
+    return ztimer_is_set(ZTIMER_MSEC, &c->timer);
 }
 
 static inline ble_npl_time_t
 ble_npl_callout_get_ticks(struct ble_npl_callout *co)
 {
-    return (ble_npl_time_t)(co->target_us / US_PER_MS);
+    return co->ticks;
 }
 
 static inline void
@@ -228,7 +221,7 @@ ble_npl_callout_set_arg(struct ble_npl_callout *co, void *arg)
 static inline ble_npl_time_t
 ble_npl_time_get(void)
 {
-    return (ble_npl_time_t)(xtimer_now_usec64() / US_PER_MS);
+    return (ble_npl_time_t)ztimer_now(ZTIMER_MSEC);
 }
 
 static inline ble_npl_error_t
@@ -260,35 +253,43 @@ ble_npl_time_ticks_to_ms32(ble_npl_time_t ticks)
 static inline void
 ble_npl_time_delay(ble_npl_time_t ticks)
 {
-    xtimer_usleep64(ticks * US_PER_MS);
+    ztimer_sleep(ZTIMER_MSEC, (uint32_t)ticks);
 }
 
 static inline uint32_t
 ble_npl_hw_enter_critical(void)
 {
-    uint32_t ctx = irq_disable();
-    ++ble_npl_in_critical;
-    return ctx;
+    return (uint32_t)irq_disable();
 }
 
 static inline void
 ble_npl_hw_exit_critical(uint32_t ctx)
 {
-    --ble_npl_in_critical;
     irq_restore((unsigned)ctx);
 }
 
 static inline bool
 ble_npl_hw_is_in_critical(void)
 {
-    /*
-     * XXX Currently RIOT does not support an API for finding out if interrupts
-     *     are currently disabled, hence in a critical section in this context.
-     *     So for now, we use this global variable to keep this state for us.
-    -*/
-    return (ble_npl_in_critical > 0);
+    return (bool)!irq_is_enabled();
+}
+
+/* XXX: these functions are required to build hal_timer.c, however with the
+*       default configuration they are never used... */
+#if defined(CPU_FAM_NRF51) || defined(CPU_FAM_NRF52)
+static inline void
+nrf52_clock_hfxo_request(void)
+{
+    clock_hfxo_request();
 }
 
+static inline void
+nrf52_clock_hfxo_release(void)
+{
+    clock_hfxo_release();
+}
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/porting/npl/riot/include/npl_syscfg/npl_sycfg.h b/porting/npl/riot/include/npl_syscfg/npl_sycfg.h
new file mode 120000
index 0000000000..53c55a90b9
--- /dev/null
+++ b/porting/npl/riot/include/npl_syscfg/npl_sycfg.h
@@ -0,0 +1 @@
+../syscfg/syscfg.h
\ No newline at end of file
diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h
index 816f410ac5..538c8213b4 100644
--- a/porting/npl/riot/include/syscfg/syscfg.h
+++ b/porting/npl/riot/include/syscfg/syscfg.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSCFG_
@@ -34,6 +34,10 @@
 #define MYNEWT_VAL_HAL_ENABLE_SOFTWARE_BREAKPOINTS (1)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT
+#define MYNEWT_VAL_HAL_FLASH_MAX_DEVICE_COUNT (0)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_BUF_SZ (16)
 #endif
@@ -46,6 +50,10 @@
 #define MYNEWT_VAL_HAL_FLASH_VERIFY_WRITES (0)
 #endif
 
+#ifndef MYNEWT_VAL_HAL_SBRK
+#define MYNEWT_VAL_HAL_SBRK (1)
+#endif
+
 #ifndef MYNEWT_VAL_HAL_SYSTEM_RESET_CB
 #define MYNEWT_VAL_HAL_SYSTEM_RESET_CB (0)
 #endif
@@ -118,10 +126,24 @@
 #define MYNEWT_VAL_MCU_GPIO_USE_PORT_EVENT (0)
 #endif
 
+#ifndef MYNEWT_VAL_MCU_HFCLK_SOURCE__HFINT
+#define MYNEWT_VAL_MCU_HFCLK_SOURCE__HFINT (0)
+#endif
+#ifndef MYNEWT_VAL_MCU_HFCLK_SOURCE__HFXO
+#define MYNEWT_VAL_MCU_HFCLK_SOURCE__HFXO (1)
+#endif
+#ifndef MYNEWT_VAL_MCU_HFCLK_SOURCE
+#define MYNEWT_VAL_MCU_HFCLK_SOURCE (1)
+#endif
+
 #ifndef MYNEWT_VAL_MCU_I2C_RECOVERY_DELAY_USEC
 #define MYNEWT_VAL_MCU_I2C_RECOVERY_DELAY_USEC (100)
 #endif
 
+#ifndef MYNEWT_VAL_MCU_ICACHE_ENABLED
+#define MYNEWT_VAL_MCU_ICACHE_ENABLED (0)
+#endif
+
 /* Overridden by @apache-mynewt-core/hw/bsp/nordic_pca10056 (defined by @apache-mynewt-core/hw/mcu/nordic/nrf52xxx) */
 #ifndef MYNEWT_VAL_MCU_LFCLK_SOURCE__LFRC
 #define MYNEWT_VAL_MCU_LFCLK_SOURCE__LFRC (0)
@@ -165,10 +187,6 @@
 #define MYNEWT_VAL_NFC_PINS_AS_GPIO (1)
 #endif
 
-#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
-#define MYNEWT_VAL_OS_TICKS_PER_SEC (128)
-#endif
-
 #ifndef MYNEWT_VAL_PWM_0
 #define MYNEWT_VAL_PWM_0 (0)
 #endif
@@ -493,6 +511,10 @@
 #define MYNEWT_VAL_OS_COREDUMP (0)
 #endif
 
+#ifndef MYNEWT_VAL_OS_COREDUMP_CB
+#define MYNEWT_VAL_OS_COREDUMP_CB (0)
+#endif
+
 /* Overridden by @apache-mynewt-core/hw/bsp/nordic_pca10056 (defined by @apache-mynewt-core/kernel/os) */
 #ifndef MYNEWT_VAL_OS_CPUTIME_FREQ
 #define MYNEWT_VAL_OS_CPUTIME_FREQ (32768)
@@ -611,6 +633,11 @@
 #define MYNEWT_VAL_OS_TASK_RUN_TIME_CPUTIME (0)
 #endif
 
+/* Overridden by @apache-mynewt-core/hw/mcu/nordic/nrf52xxx (defined by @apache-mynewt-core/kernel/os) */
+#ifndef MYNEWT_VAL_OS_TICKS_PER_SEC
+#define MYNEWT_VAL_OS_TICKS_PER_SEC (128)
+#endif
+
 #ifndef MYNEWT_VAL_OS_TIME_DEBUG
 #define MYNEWT_VAL_OS_TIME_DEBUG (0)
 #endif
@@ -632,6 +659,10 @@
 #define MYNEWT_VAL_BASELIBC_ASSERT_FILE_LINE (0)
 #endif
 
+#ifndef MYNEWT_VAL_BASELIBC_EXECUTE_GLOBAL_CONSTRUCTORS
+#define MYNEWT_VAL_BASELIBC_EXECUTE_GLOBAL_CONSTRUCTORS (1)
+#endif
+
 #ifndef MYNEWT_VAL_BASELIBC_PRESENT
 #define MYNEWT_VAL_BASELIBC_PRESENT (1)
 #endif
@@ -654,8 +685,12 @@
 #define MYNEWT_VAL_FLASH_MAP_MAX_AREAS (10)
 #endif
 
+#ifndef MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG
+#define MYNEWT_VAL_FLASH_MAP_SUPPORT_MFG (0)
+#endif
+
 #ifndef MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE
-#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (2)
+#define MYNEWT_VAL_FLASH_MAP_SYSINIT_STAGE (9)
 #endif
 
 /*** @apache-mynewt-core/sys/log/common */
@@ -709,23 +744,6 @@
 #define MYNEWT_VAL_LOG_LEVEL (255)
 #endif
 
-/*** @apache-mynewt-core/sys/mfg */
-#ifndef MYNEWT_VAL_MFG_LOG_LVL
-#define MYNEWT_VAL_MFG_LOG_LVL (15)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_LOG_MODULE
-#define MYNEWT_VAL_MFG_LOG_MODULE (128)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_MAX_MMRS
-#define MYNEWT_VAL_MFG_MAX_MMRS (2)
-#endif
-
-#ifndef MYNEWT_VAL_MFG_SYSINIT_STAGE
-#define MYNEWT_VAL_MFG_SYSINIT_STAGE (100)
-#endif
-
 /*** @apache-mynewt-core/sys/sys */
 #ifndef MYNEWT_VAL_DEBUG_PANIC_ENABLED
 #define MYNEWT_VAL_DEBUG_PANIC_ENABLED (1)
@@ -775,6 +793,11 @@
 #define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (31)
 #endif
 
+/* Overridden by @apache-mynewt-nimble/porting/targets/riot (defined by @apache-mynewt-nimble/nimble) */
+#ifndef MYNEWT_VAL_BLE_HCI_VS
+#define MYNEWT_VAL_BLE_HCI_VS (1)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_ISO
 #define MYNEWT_VAL_BLE_ISO (0)
 #endif
@@ -847,7 +870,7 @@
 #endif
 
 #ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ
-#define MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ (1)
+#define MYNEWT_VAL_BLE_LL_CFG_FEAT_CONN_PARAM_REQ (MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL
@@ -926,7 +949,7 @@
 #endif
 
 #ifndef MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG
-#define MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG (1)
+#define MYNEWT_VAL_BLE_LL_CFG_FEAT_SLAVE_INIT_FEAT_XCHG (MYNEWT_VAL_BLE_LL_ROLE_CENTRAL || MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL)
 #endif
 
 /* Overridden by @apache-mynewt-nimble/porting/targets/riot (defined by @apache-mynewt-nimble/nimble/controller) */
@@ -950,8 +973,12 @@
 #define MYNEWT_VAL_BLE_LL_DEBUG_GPIO_HCI_EV (-1)
 #endif
 
-#ifndef MYNEWT_VAL_BLE_LL_DEBUG_GPIO_SCHED_ITEM_CB
-#define MYNEWT_VAL_BLE_LL_DEBUG_GPIO_SCHED_ITEM_CB (-1)
+#ifndef MYNEWT_VAL_BLE_LL_DEBUG_GPIO_RFMGMT
+#define MYNEWT_VAL_BLE_LL_DEBUG_GPIO_RFMGMT (-1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_LL_DEBUG_GPIO_SCHED_ITEM
+#define MYNEWT_VAL_BLE_LL_DEBUG_GPIO_SCHED_ITEM (-1)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_LL_DEBUG_GPIO_SCHED_RUN
@@ -975,6 +1002,19 @@
 #define MYNEWT_VAL_BLE_LL_EXT_ADV_AUX_PTR_CNT (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_LL_HCI_LLCP_TRACE
+#define MYNEWT_VAL_BLE_LL_HCI_LLCP_TRACE (0)
+#endif
+
+/* Value copied from BLE_HCI_VS */
+#ifndef MYNEWT_VAL_BLE_LL_HCI_VS
+#define MYNEWT_VAL_BLE_LL_HCI_VS (1)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_LL_HCI_VS_EVENT_ON_ASSERT
+#define MYNEWT_VAL_BLE_LL_HCI_VS_EVENT_ON_ASSERT (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_LL_MASTER_SCA
 #define MYNEWT_VAL_BLE_LL_MASTER_SCA (4)
 #endif
@@ -1007,6 +1047,10 @@
 #define MYNEWT_VAL_BLE_LL_PRIO (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_LL_PUBLIC_DEV_ADDR
+#define MYNEWT_VAL_BLE_LL_PUBLIC_DEV_ADDR (0x000000000000)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE
 #define MYNEWT_VAL_BLE_LL_RESOLV_LIST_SIZE (4)
 #endif
@@ -1020,11 +1064,35 @@
 #define MYNEWT_VAL_BLE_LL_RNG_BUFSIZE (32)
 #endif
 
+/* Value copied from BLE_ROLE_BROADCASTER */
+#ifndef MYNEWT_VAL_BLE_LL_ROLE_BROADCASTER
+#define MYNEWT_VAL_BLE_LL_ROLE_BROADCASTER (1)
+#endif
+
+/* Value copied from BLE_ROLE_CENTRAL */
+#ifndef MYNEWT_VAL_BLE_LL_ROLE_CENTRAL
+#define MYNEWT_VAL_BLE_LL_ROLE_CENTRAL (1)
+#endif
+
+/* Value copied from BLE_ROLE_OBSERVER */
+#ifndef MYNEWT_VAL_BLE_LL_ROLE_OBSERVER
+#define MYNEWT_VAL_BLE_LL_ROLE_OBSERVER (1)
+#endif
+
+/* Value copied from BLE_ROLE_PERIPHERAL */
+#ifndef MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL
+#define MYNEWT_VAL_BLE_LL_ROLE_PERIPHERAL (1)
+#endif
+
 /* Value copied from BLE_LL_OUR_SCA */
 #ifndef MYNEWT_VAL_BLE_LL_SCA
 #define MYNEWT_VAL_BLE_LL_SCA (60)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_LL_SCAN_AUX_SEGMENT_CNT
+#define MYNEWT_VAL_BLE_LL_SCAN_AUX_SEGMENT_CNT (8)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY
 #define MYNEWT_VAL_BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY (0)
 #endif
@@ -1096,10 +1164,6 @@
 #endif
 
 /*** @apache-mynewt-nimble/nimble/drivers/nrf52 */
-#ifndef MYNEWT_VAL_BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN
-#define MYNEWT_VAL_BLE_PHY_CODED_RX_IFS_EXTRA_MARGIN (0)
-#endif
-
 #ifndef MYNEWT_VAL_BLE_PHY_DBG_TIME_ADDRESS_END_PIN
 #define MYNEWT_VAL_BLE_PHY_DBG_TIME_ADDRESS_END_PIN (-1)
 #endif
@@ -1447,6 +1511,14 @@
 #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0)
 #endif
 
+#ifndef MYNEWT_VAL_BLE_SM_SC_LVL
+#define MYNEWT_VAL_BLE_SM_SC_LVL (0)
+#endif
+
+#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
+#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
+#endif
+
 #ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
 #define MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST (0)
 #endif
@@ -1516,7 +1588,7 @@
 #endif
 
 #ifndef MYNEWT_VAL_BLE_ACL_BUF_SIZE
-#define MYNEWT_VAL_BLE_ACL_BUF_SIZE (255)
+#define MYNEWT_VAL_BLE_ACL_BUF_SIZE (65535)
 #endif
 
 #ifndef MYNEWT_VAL_BLE_HCI_EVT_BUF_SIZE
diff --git a/porting/npl/riot/include/sysflash/sysflash.h b/porting/npl/riot/include/sysflash/sysflash.h
index ab1341b25d..28391ca66a 100644
--- a/porting/npl/riot/include/sysflash/sysflash.h
+++ b/porting/npl/riot/include/sysflash/sysflash.h
@@ -1,5 +1,5 @@
 /**
- * This file was generated by Apache newt version: 1.9.0-dev
+ * This file was generated by Apache newt version: 1.10.0-dev
  */
 
 #ifndef H_MYNEWT_SYSFLASH_
diff --git a/porting/npl/riot/src/npl_os_riot.c b/porting/npl/riot/src/npl_os_riot.c
index 2f4efd8ba5..71c130f198 100644
--- a/porting/npl/riot/src/npl_os_riot.c
+++ b/porting/npl/riot/src/npl_os_riot.c
@@ -35,18 +35,8 @@ _callout_fire(void *arg)
 ble_npl_error_t
 ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout)
 {
-    int rc;
-    struct timespec abs;
-    uint64_t time;
-
-    time = xtimer_now_usec64() +
-        (ble_npl_time_ticks_to_ms32(timeout) * US_PER_MS);
-    abs.tv_sec = (time_t)(time / US_PER_SEC);
-    abs.tv_nsec = (long)((time % US_PER_SEC) * NS_PER_US);
-
-    rc = sem_timedwait(&sem->sem, &abs);
-
-    return rc == 0 ? BLE_NPL_OK : BLE_NPL_ENOENT;
+    int rc = sema_wait_timed_ztimer(&sem->sem, ZTIMER_MSEC, timeout);
+    return (rc == 0) ? BLE_NPL_OK : BLE_NPL_ENOENT;
 }
 
 void
@@ -62,19 +52,18 @@ ble_npl_callout_init(struct ble_npl_callout *c, struct ble_npl_eventq *evq,
 ble_npl_error_t
 ble_npl_callout_reset(struct ble_npl_callout *c, ble_npl_time_t ticks)
 {
-    /* Use critical section to ensure matching target_us and xtimer value. */
+    /* Use critical section to ensure matching target_us and ztimer value. */
     uint32_t crit_state = ble_npl_hw_enter_critical();
-    uint64_t now = xtimer_now_usec64();
-    c->target_us = now  + ticks * US_PER_MS;
-    xtimer_set64(&c->timer, ticks * US_PER_MS);
+    c->ticks = ztimer_now(ZTIMER_MSEC) + ticks;
+    ztimer_set(ZTIMER_MSEC, &c->timer, ticks);
     ble_npl_hw_exit_critical(crit_state);
     return BLE_NPL_OK;
 }
 
-uint32_t
+ble_npl_time_t
 ble_npl_callout_remaining_ticks(struct ble_npl_callout *co,
                                 ble_npl_time_t time)
 {
-    uint64_t now = xtimer_now_usec64();
-    return (uint32_t)((co->target_us - now) / US_PER_MS);
+    ztimer_now_t now = ztimer_now(ZTIMER_MSEC);
+    return (ble_npl_time_t)(co->ticks - now);
 }
diff --git a/porting/targets/dummy_bsp/bsp.yml b/porting/targets/dummy_bsp/bsp.yml
deleted file mode 100644
index ecd1d42041..0000000000
--- a/porting/targets/dummy_bsp/bsp.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#  http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-bsp.name: "dummy"
-bsp.url:
-bsp.maker:
-bsp.arch: dummy
-bsp.compiler: "@apache-mynewt-core/compiler/sim"
-
-bsp.flash_map:
-    areas:
-        # System areas.
-        FLASH_AREA_BOOTLOADER:
-            device: 0
-            offset: 0x00000000
-            size: 32kB
-        FLASH_AREA_IMAGE_0:
-            device: 0
-            offset: 0x0000c000
-            size: 472kB
-        FLASH_AREA_IMAGE_1:
-            device: 0
-            offset: 0x00082000
-            size: 472kB
-        FLASH_AREA_IMAGE_SCRATCH:
-            device: 0
-            offset: 0x000f8000
-            size: 16kB
-
-        # User areas.
-        FLASH_AREA_REBOOT_LOG:
-            user_id: 0
-            device: 0
-            offset: 0x00008000
-            size: 16kB
-        FLASH_AREA_NFFS:
-            user_id: 1
-            device: 0
-            offset: 0x000fc000
-            size: 16kB
diff --git a/porting/targets/linux/target.yml b/porting/targets/linux/target.yml
index 9ab4152c9d..7145d70386 100644
--- a/porting/targets/linux/target.yml
+++ b/porting/targets/linux/target.yml
@@ -16,5 +16,5 @@
 # under the License.
 #
 target.app: "porting/targets/dummy_app"
-target.bsp: "porting/targets/dummy_bsp"
+target.bsp: "@apache-mynewt-core/hw/bsp/native"
 target.build_profile: "debug"
diff --git a/porting/targets/linux_blemesh/syscfg.yml b/porting/targets/linux_blemesh/syscfg.yml
index efc4150b4c..8ef7972184 100644
--- a/porting/targets/linux_blemesh/syscfg.yml
+++ b/porting/targets/linux_blemesh/syscfg.yml
@@ -34,6 +34,7 @@ syscfg.vals:
     BLE_MESH_LABEL_COUNT: 2
     BLE_MESH_SUBNET_COUNT: 2
     BLE_MESH_MODEL_GROUP_COUNT: 2
+    BLE_MESH_MODEL_VND_MSG_CID_FORCE: 1
     BLE_MESH_APP_KEY_COUNT: 4
     BLE_MESH_IV_UPDATE_TEST: 1
     BLE_MESH_TESTING: 1
diff --git a/porting/targets/linux_blemesh/target.yml b/porting/targets/linux_blemesh/target.yml
index 83eb415886..4b9a4dab8b 100644
--- a/porting/targets/linux_blemesh/target.yml
+++ b/porting/targets/linux_blemesh/target.yml
@@ -17,5 +17,5 @@
 #
 
 target.app: "porting/targets/dummy_app"
-target.bsp: "porting/targets/dummy_bsp"
+target.bsp: "@apache-mynewt-core/hw/bsp/native"
 target.build_profile: "debug"
diff --git a/porting/targets/nuttx/target.yml b/porting/targets/nuttx/target.yml
index 9ab4152c9d..7145d70386 100644
--- a/porting/targets/nuttx/target.yml
+++ b/porting/targets/nuttx/target.yml
@@ -16,5 +16,5 @@
 # under the License.
 #
 target.app: "porting/targets/dummy_app"
-target.bsp: "porting/targets/dummy_bsp"
+target.bsp: "@apache-mynewt-core/hw/bsp/native"
 target.build_profile: "debug"
diff --git a/porting/targets/porting_default/syscfg.yml b/porting/targets/porting_default/syscfg.yml
new file mode 100644
index 0000000000..d0aff807f2
--- /dev/null
+++ b/porting/targets/porting_default/syscfg.yml
@@ -0,0 +1,3 @@
+syscfg.vals:
+    BLE_SOCK_USE_TCP: 0
+    BLE_SOCK_USE_LINUX_BLUE: 1
diff --git a/porting/targets/porting_default/target.yml b/porting/targets/porting_default/target.yml
index 83eb415886..4b9a4dab8b 100644
--- a/porting/targets/porting_default/target.yml
+++ b/porting/targets/porting_default/target.yml
@@ -17,5 +17,5 @@
 #
 
 target.app: "porting/targets/dummy_app"
-target.bsp: "porting/targets/dummy_bsp"
+target.bsp: "@apache-mynewt-core/hw/bsp/native"
 target.build_profile: "debug"
diff --git a/porting/targets/riot/syscfg.yml b/porting/targets/riot/syscfg.yml
index d6ee102bf9..8359eaa7c9 100644
--- a/porting/targets/riot/syscfg.yml
+++ b/porting/targets/riot/syscfg.yml
@@ -18,6 +18,7 @@
 
 syscfg.vals:
     BLE_ACL_BUF_COUNT: 24
+    BLE_HCI_VS: 1
     BLE_HCI_EVT_HI_BUF_COUNT: 2
     BLE_HW_WHITELIST_ENABLE: 0
     BLE_LL_CFG_FEAT_DATA_LEN_EXT: 0
diff --git a/repository.yml b/repository.yml
index 4a097d39a5..9f112a825e 100644
--- a/repository.yml
+++ b/repository.yml
@@ -22,18 +22,20 @@ repo.versions:
     "0.0.0": "master"
 
     "0-dev": "0.0.0"
-    "0-latest": "1.3.0"
-    "1-latest": "1.3.0"
+    "0-latest": "1.4.0"
+    "1-latest": "1.4.0"
 
     "1.0.0": "nimble_1_0_0_tag"
     "1.1.0": "nimble_1_1_0_tag"
     "1.2.0": "nimble_1_2_0_tag"
     "1.3.0": "nimble_1_3_0_tag"
+    "1.4.0": "nimble_1_4_0_tag"
 
     "1.0-latest": "1.0.0"
     "1.1-latest": "1.1.0"
     "1.2-latest": "1.2.0"
     "1.3-latest": "1.3.0"
+    "1.4-latest": "1.4.0"
 
 repo.newt_compatibility:
     0.0.0:
@@ -46,3 +48,5 @@ repo.newt_compatibility:
         1.7.0: good
     1.3.0:
         1.8.0: good
+    1.4.0:
+        1.9.0: good
diff --git a/targets/dialog_cmac/syscfg.yml b/targets/dialog_cmac/syscfg.yml
index b94427ac22..3a8895d7db 100644
--- a/targets/dialog_cmac/syscfg.yml
+++ b/targets/dialog_cmac/syscfg.yml
@@ -32,4 +32,4 @@ syscfg.vals:
     BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY: 150
 
     # NOTE: set public address in target settings
-    # BLE_PUBLIC_DEV_ADDR: "(uint8_t[6]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}"
+    # BLE_LL_PUBLIC_DEV_ADDR: 0xffffffffffff
diff --git a/targets/nordic_pca10056-blehci-usb/pkg.yml b/targets/nordic_pca10056-blehci-usb/pkg.yml
new file mode 100644
index 0000000000..772dd75ebe
--- /dev/null
+++ b/targets/nordic_pca10056-blehci-usb/pkg.yml
@@ -0,0 +1,9 @@
+pkg.name: "targets/nordic_pca10056-blehci-usb"
+pkg.type: target
+pkg.description: 
+pkg.author: 
+pkg.homepage:
+
+pkg.deps:
+    - "@apache-mynewt-core/hw/usb/tinyusb"
+    - "@apache-mynewt-core/hw/usb/tinyusb/std_descriptors"
diff --git a/targets/nordic_pca10056-blehci-usb/syscfg.yml b/targets/nordic_pca10056-blehci-usb/syscfg.yml
new file mode 100644
index 0000000000..e664792eaa
--- /dev/null
+++ b/targets/nordic_pca10056-blehci-usb/syscfg.yml
@@ -0,0 +1,6 @@
+syscfg.vals:
+    BLE_HCI_TRANSPORT: usb
+    USBD_BTH: 1
+
+    USBD_PID: 0xC01A
+    USBD_VID: 0xC0CA
diff --git a/targets/nordic_pca10056-blehci-usb/target.yml b/targets/nordic_pca10056-blehci-usb/target.yml
new file mode 100644
index 0000000000..39f4c8c447
--- /dev/null
+++ b/targets/nordic_pca10056-blehci-usb/target.yml
@@ -0,0 +1,3 @@
+target.app: "@apache-mynewt-nimble/apps/blehci"
+target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056"
+target.build_profile: debug
diff --git a/porting/targets/dummy_bsp/pkg.yml b/targets/nordic_pca10095_net-blehci/pkg.yml
similarity index 87%
rename from porting/targets/dummy_bsp/pkg.yml
rename to targets/nordic_pca10095_net-blehci/pkg.yml
index 8b8484960a..af24376a83 100644
--- a/porting/targets/dummy_bsp/pkg.yml
+++ b/targets/nordic_pca10095_net-blehci/pkg.yml
@@ -17,11 +17,8 @@
 # under the License.
 #
 
-pkg.name: porting/targets/dummy_bsp
-pkg.type: bsp
-pkg.description: A dummy BSP definition
+pkg.name: targets/nordic_pca10095_net-blehci
+pkg.type: target
+pkg.description: Sample target for BLE controller on NRF5340
 pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
 pkg.homepage: "http://mynewt.apache.org/"
-pkg.keywords:
-pkg.cflags:
-pkg.deps:
diff --git a/targets/nordic_pca10095_net-blehci/syscfg.yml b/targets/nordic_pca10095_net-blehci/syscfg.yml
new file mode 100644
index 0000000000..4a4a1ba069
--- /dev/null
+++ b/targets/nordic_pca10095_net-blehci/syscfg.yml
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.vals:
+    BLE_HCI_TRANSPORT: nrf5340
+
+    MSYS_1_BLOCK_COUNT: 12
+    MSYS_1_BLOCK_SIZE: 292
+    BLE_LL_CFG_FEAT_DATA_LEN_EXT: 1
+    BLE_LL_CFG_FEAT_LE_2M_PHY: 1
+    BLE_LL_CFG_FEAT_LE_CODED_PHY: 1
+    BLE_LL_CFG_FEAT_LL_PRIVACY: 1
+    BLE_LL_CFG_FEAT_CTRL_TO_HOST_FLOW_CONTROL: 1
+    BLE_LL_CONN_INIT_MAX_TX_BYTES: 251
+    BLE_LL_CONN_INIT_SLOTS: 4
+    BLE_LL_DTM: 1
+    BLE_LL_DTM_EXTENSIONS: 1
+    BLE_LL_VND_EVENT_ON_ASSERT: 1
+    BLE_MAX_CONNECTIONS: 5
+    BLE_EXT_ADV: 1
+    BLE_EXT_ADV_MAX_SIZE: 1650
+    BLE_MAX_PERIODIC_SYNCS: 5
+    BLE_MULTI_ADV_INSTANCES: 5
+    BLE_PERIODIC_ADV: 1
+    BLE_PERIODIC_ADV_SYNC_TRANSFER: 1
+    BLE_VERSION: 51
diff --git a/targets/nordic_pca10095_net-blehci/target.yml b/targets/nordic_pca10095_net-blehci/target.yml
new file mode 100644
index 0000000000..4d2641b77c
--- /dev/null
+++ b/targets/nordic_pca10095_net-blehci/target.yml
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+target.app: "@apache-mynewt-nimble/apps/blehci"
+target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10095_net"
+target.build_profile: debug