You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/07/28 07:23:43 UTC

[incubator-nuttx] 02/02: stm32wb: adding BLE support

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 82ad4b0e080a4ba3e17506c6504114540c78db8e
Author: Sergey Nikitenko <s....@me.com>
AuthorDate: Wed Jul 27 16:19:20 2022 +0300

    stm32wb: adding BLE support
---
 arch/arm/src/stm32wb/Kconfig                       | 232 ++++++
 arch/arm/src/stm32wb/Make.defs                     |  16 +-
 arch/arm/src/stm32wb/hardware/stm32wb_ipcc.h       |   2 +-
 arch/arm/src/stm32wb/stm32wb_blehci.c              | 348 +++++++++
 .../stm32wb/{stm32wb_ipcc.h => stm32wb_blehci.h}   |  34 +-
 arch/arm/src/stm32wb/stm32wb_ipcc.h                | 104 +++
 arch/arm/src/stm32wb/stm32wb_mbox.c                | 813 +++++++++++++++++++++
 arch/arm/src/stm32wb/stm32wb_mbox.h                | 174 +++++
 arch/arm/src/stm32wb/stm32wb_mbox_list.h           | 144 ++++
 arch/arm/src/stm32wb/stm32wb_mbox_shci.h           | 186 +++++
 arch/arm/src/stm32wb/stm32wb_rcc.c                 |  11 +
 boards/arm/stm32wb/nucleo-wb55rg/README.txt        |   7 +
 .../stm32wb/nucleo-wb55rg/configs/ble/defconfig    |  55 ++
 .../stm32wb/nucleo-wb55rg/include/nucleo-wb55rg.h  |   6 +
 .../arm/stm32wb/nucleo-wb55rg/src/stm32_appinit.c  |  26 +
 15 files changed, 2130 insertions(+), 28 deletions(-)

diff --git a/arch/arm/src/stm32wb/Kconfig b/arch/arm/src/stm32wb/Kconfig
index 9396f76881..d13bcc0b03 100644
--- a/arch/arm/src/stm32wb/Kconfig
+++ b/arch/arm/src/stm32wb/Kconfig
@@ -941,4 +941,236 @@ config STM32WB_SPI_DMA
 
 endmenu
 
+config STM32WB_MBOX
+	bool
+	default n
+	select STM32WB_IPCC
+
+menuconfig STM32WB_BLE
+	bool "BLE"
+	default n
+	select STM32WB_MBOX
+	---help---
+		Enable BLE support.
+
+if STM32WB_BLE
+
+config STM32WB_BLE_C2HOST
+	bool "Enable CPU2 HOST stack"
+	default y
+
+config STM32WB_BLE_MAX_CONN
+	int "Maximum BLE simultaneous connections"
+	range 1 8
+	default 2
+
+config STM32WB_BLE_GATT_MAX_ATTR_NUM
+	int "GATT attributes max count"
+	range 9 255
+	default 64
+
+config STM32WB_BLE_GATT_MAX_SVC_NUM
+	int "GATT services max count"
+	range 2 64
+	default 8
+
+config STM32WB_BLE_GATT_ATTR_BUF_SIZE
+	int "GATT attributes storage buf size"
+	default 1344
+	---help---
+		Size of the storage area for attribute values. Hardcoded in CPU2 firmware.
+
+config STM32WB_BLE_DLE
+	bool "Support Data Length Extension (DLE)"
+	default y
+
+config STM32WB_BLE_MAX_ATT_MTU
+	int "Maximum supported attribute MTU"
+	range 23 512
+	default 156
+
+config STM32WB_BLE_SLAVE_SCA
+	int "Sleep clock accuracy in slave mode [PPM]"
+	default 500
+	---help---
+		Sleep clock accuracy (ppm value) in slave mode.
+
+choice 
+	prompt "Sleep clock accuracy in master mode"
+	default STM32WB_BLE_MASTER_SCA_0
+	---help---
+		Sleep clock accuracy in master mode.
+
+config STM32WB_BLE_MASTER_SCA_0
+	bool "251-500 ppm"
+
+config STM32WB_BLE_MASTER_SCA_1
+	bool "151-250 ppm"
+
+config STM32WB_BLE_MASTER_SCA_2
+	bool "101-150 ppm"
+
+config STM32WB_BLE_MASTER_SCA_3
+	bool "76-100 ppm"
+
+config STM32WB_BLE_MASTER_SCA_4
+	bool "51-75 ppm"
+
+config STM32WB_BLE_MASTER_SCA_5
+	bool "31-50 ppm"
+
+config STM32WB_BLE_MASTER_SCA_6
+	bool "21-30 ppm"
+
+config STM32WB_BLE_MASTER_SCA_7
+	bool "0-20 ppm"
+
+endchoice # Sleep clock accuracy in master mode
+
+config STM32WB_BLE_MASTER_SCA
+	int
+	default 7 if STM32WB_BLE_MASTER_SCA_7
+	default 6 if STM32WB_BLE_MASTER_SCA_6
+	default 5 if STM32WB_BLE_MASTER_SCA_5
+	default 4 if STM32WB_BLE_MASTER_SCA_4
+	default 3 if STM32WB_BLE_MASTER_SCA_3
+	default 2 if STM32WB_BLE_MASTER_SCA_2
+	default 1 if STM32WB_BLE_MASTER_SCA_1
+	default 0
+
+choice 
+	prompt "Low speed clock source"
+	default STM32WB_BLE_LS_CLK_SRC_LSE
+	---help---
+		Low speed 32 kHz clock source.
+
+config STM32WB_BLE_LS_CLK_SRC_LSE
+	bool "LSE"
+
+config STM32WB_BLE_LS_CLK_SRC_HSE
+	bool "HSE"
+
+endchoice # Low speed clock source
+
+config STM32WB_BLE_LS_CLK_SRC
+	int
+	default 1 if STM32WB_BLE_LS_CLKSRC_HSE
+	default 0
+
+config STM32WB_BLE_MAX_CONN_EVT_LENGTH
+	hex "Max connection event length"
+	default 0xffffffff
+	---help---
+		Maximum duration of a slave connection event in units of 625/256us (~2.44us).
+
+config STM32WB_BLE_HSE_STARTUP
+	hex "HSE startup time"
+	default 0x148
+	---help---
+		HSE startup time in units of 625/256us (~2.44us).
+
+config STM32WB_BLE_VITERBI
+	bool "Enable Viterbi algorithm"
+	default y
+	---help---
+		Enable Viterbi algorithm implementation
+
+config STM32WB_BLE_MAX_INITOR_COC_NUM
+	int "Max number of connection-oriented channels"
+	range 0 64
+	default 32
+	---help---
+		Maximum number of connection-oriented channels in initiator mode.
+
+config STM32WB_BLE_SVC_CHANGED_CHAR
+	bool "Enable service changed characteristic"
+	default n
+
+config STM32WB_BLE_WRITABLE_DEVICE_NAME
+	bool "Writable device name"
+	default y
+
+config STM32WB_BLE_CHAN_SEL_ALG2
+	bool "Enable channel selection algorithm 2"
+	default n
+
+choice 
+	prompt "Power class"
+	default STM32WB_BLE_POWER_CLASS_2_3
+
+config STM32WB_BLE_POWER_CLASS_2_3
+	bool "Power Class 2-3"
+
+config STM32WB_BLE_POWER_CLASS_1
+	bool "Power Class 1"
+
+endchoice # Power class
+
+config STM32WB_BLE_MIN_TX_POWER
+	int "Minimum transmit power [dBm]"
+	range -127 20
+	default 0
+
+config STM32WB_BLE_MAX_TX_POWER
+	int "Maximum transmit power [dBm]"
+	range -127 20
+	default 0
+
+choice 
+	prompt "AGC RSSI model"
+	default STM32WB_BLE_AGC_RSSI_LEGACY
+
+config STM32WB_BLE_AGC_RSSI_LEGACY
+	bool "AGC RSSI Legacy"
+
+config STM32WB_BLE_AGC_RSSI_IMPROVED
+	bool "AGC RSSI Improved"
+
+endchoice # AGC RSSI model
+
+config STM32WB_BLE_ADVERTISING
+	bool "Support advertising"
+	default y
+
+config STM32WB_BLE_SCANNING
+	bool "Support scanning"
+	default y
+
+config STM32WB_BLE_LE_2M_PHY
+	bool "Support LE 2M PHY"
+	default y
+
+config STM32WB_BLE_LE_CODED_PHY
+	bool "Support LE Coded PHY"
+	default y if STM32WB_STM32WB15 || STM32WB_STM32WB35 || STM32WB_STM32WB55
+	default n
+	depends on STM32WB_STM32WB15 || STM32WB_STM32WB35 || STM32WB_STM32WB55
+
+config STM32WB_BLE_TTY_NAME
+	string "BLE TTY device name"
+	default "/dev/ttyHCI0"
+	depends on UART_BTH4
+
+config STM32WB_BLE_FICR_STATIC_ADDR
+	bool "Configure factory generated static random address"
+	default n
+
+config STM32WB_BLE_PUB_ADDR
+	hex "Configure BT public address"
+	default 0x0000000000
+
+endif # STM32WB_BLE
+
+if STM32WB_MBOX
+
+config STM32WB_MBOX_TX_CMD_QUEUE_LEN
+	int "Mailbox TX command queue length"
+	default 2
+
+config STM32WB_MBOX_RX_EVT_QUEUE_LEN
+	int "Mailbox RX event queue length"
+	default 5
+
+endif # STM32WB_MBOX
+
 endif # ARCH_CHIP_STM32WB
diff --git a/arch/arm/src/stm32wb/Make.defs b/arch/arm/src/stm32wb/Make.defs
index a0c9611422..886776d368 100644
--- a/arch/arm/src/stm32wb/Make.defs
+++ b/arch/arm/src/stm32wb/Make.defs
@@ -81,10 +81,6 @@ ifeq ($(CONFIG_STM32WB_PWR),y)
 CHIP_CSRCS += stm32wb_exti_pwr.c
 endif
 
-ifeq ($(CONFIG_STM32WB_IPCC),y)
-CHIP_CSRCS += stm32wb_ipcc.c
-endif
-
 ifeq ($(CONFIG_STM32WB_RTC),y)
 ifeq ($(CONFIG_RTC_ALARM),y)
 CHIP_CSRCS += stm32wb_exti_alarm.c
@@ -97,6 +93,18 @@ CHIP_CSRCS += stm32wb_rtc.c stm32wb_rtc_lowerhalf.c
 endif
 endif
 
+ifeq ($(CONFIG_STM32WB_IPCC),y)
+CHIP_CSRCS += stm32wb_ipcc.c
+endif
+
+ifeq ($(CONFIG_STM32WB_MBOX),y)
+CHIP_CSRCS += stm32wb_mbox.c
+endif
+
+ifeq ($(CONFIG_STM32WB_BLE),y)
+CHIP_CSRCS += stm32wb_blehci.c
+endif
+
 ifeq ($(CONFIG_DEBUG_FEATURES),y)
 CHIP_CSRCS += stm32wb_dumpgpio.c
 endif
diff --git a/arch/arm/src/stm32wb/hardware/stm32wb_ipcc.h b/arch/arm/src/stm32wb/hardware/stm32wb_ipcc.h
index 740f408849..178ccd90a6 100644
--- a/arch/arm/src/stm32wb/hardware/stm32wb_ipcc.h
+++ b/arch/arm/src/stm32wb/hardware/stm32wb_ipcc.h
@@ -34,7 +34,7 @@
 #define STM32WB_IPCC_C2CR_OFFSET      0x0010  /* CPU2 control register */
 #define STM32WB_IPCC_C2MR_OFFSET      0x0014  /* CPU2 mask register */
 #define STM32WB_IPCC_C2SCR_OFFSET     0x0018  /* CPU2 status set/clear register */
-#define STM32WB_IPCC_C2TOC1SR_OFFSET  0x001c  /* CPU2 to CPU2 status register */
+#define STM32WB_IPCC_C2TOC1SR_OFFSET  0x001c  /* CPU2 to CPU1 status register */
 
 /* Register Addresses *******************************************************/
 
diff --git a/arch/arm/src/stm32wb/stm32wb_blehci.c b/arch/arm/src/stm32wb/stm32wb_blehci.c
new file mode 100644
index 0000000000..560a03dc1e
--- /dev/null
+++ b/arch/arm/src/stm32wb/stm32wb_blehci.c
@@ -0,0 +1,348 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_blehci.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/wireless/bluetooth/bt_hci.h>
+#include <nuttx/wireless/bluetooth/bt_driver.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+
+#if defined(CONFIG_UART_BTH4)
+#  include <nuttx/serial/uart_bth4.h>
+#endif
+
+#include "stm32wb_ipcc.h"
+#include "stm32wb_mbox.h"
+#include "stm32wb_mbox_shci.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STM32WB_BLE_PREP_WRITE_NUM \
+  STM32WB_MBOX_DEFAULT_BLE_PREP_WRITE_NUM(CONFIG_STM32WB_BLE_MAX_ATT_MTU)
+
+#define STM32WB_C2_MEM_BLOCK_NUM \
+  STM32WB_MBOX_DEFAULT_C2_MEM_BLOCK_NUM(CONFIG_STM32WB_BLE_MAX_ATT_MTU, \
+                                        CONFIG_STM32WB_BLE_MAX_CONN, \
+                                        STM32WB_BLE_PREP_WRITE_NUM)
+
+#ifdef CONFIG_STM32WB_BLE_C2HOST
+#  define STM32WB_BLE_C2HOST              STM32WB_SHCI_BLE_INIT_OPT_STACK_LL_HOST
+#else
+#  define STM32WB_BLE_C2HOST              STM32WB_SHCI_BLE_INIT_OPT_STACK_LL
+#endif
+
+#ifdef CONFIG_STM32WB_BLE_SVC_CHANGED_CHAR
+#  define STM32WB_BLE_SVC_CHANGED_CHAR    STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_ENABLED
+#else
+#  define STM32WB_BLE_SVC_CHANGED_CHAR    STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_DISABLED
+#endif
+
+#ifdef CONFIG_STM32WB_BLE_WRITABLE_DEVICE_NAME
+#  define STM32WB_BLE_DEVICE_NAME_MODE    STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RW
+#else
+#  define STM32WB_BLE_DEVICE_NAME_MODE    STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RO
+#endif
+
+#ifdef CONFIG_STM32WB_BLE_CHAN_SEL_ALG2
+#  define STM32WB_BLE_CS_ALG2             STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_ENABLED
+#else
+#  define STM32WB_BLE_CS_ALG2             STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_DISABLED
+#endif
+
+#ifdef CONFIG_STM32WB_BLE_POWER_CLASS_1
+#  define STM32WB_BLE_POWER_CLASS         STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_1
+#else
+#  define STM32WB_BLE_POWER_CLASS         STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_2_3
+#endif
+
+#define STM32WB_BLE_INIT_OPTIONS \
+  (STM32WB_BLE_C2HOST | STM32WB_BLE_SVC_CHANGED_CHAR | \
+   STM32WB_BLE_DEVICE_NAME_MODE | STM32WB_BLE_CS_ALG2 | \
+   STM32WB_BLE_POWER_CLASS)
+
+#ifdef CONFIG_STM32WB_BLE_AGC_RSSI_IMPROVED
+#  define STM32WB_BLE_RXMOD_AGC_RSSI      STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_IMPROVED
+#else
+#  define STM32WB_BLE_RXMOD_AGC_RSSI      STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_LEGACY
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32wb_blehci_driveropen(struct bt_driver_s *btdev);
+static int stm32wb_blehci_driversend(struct bt_driver_s *btdev,
+                                     enum bt_buf_type_e type,
+                                     void *data, size_t len);
+static int stm32wb_blehci_rxevt(struct stm32wb_mbox_evt_s *evt);
+static void stm32wb_blehci_bleinit(void);
+static int stm32wb_blehci_driverinitialize(void);
+static void stm32wb_blehci_drvinitworker(void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct bt_driver_s g_blehci_driver =
+{
+  .head_reserve = 0,
+  .open         = stm32wb_blehci_driveropen,
+  .send         = stm32wb_blehci_driversend
+};
+
+static sem_t  g_excl_sem = SEM_INITIALIZER(1);
+struct work_s g_drv_init_work;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_blehci_driveropen
+ ****************************************************************************/
+
+static int stm32wb_blehci_driveropen(struct bt_driver_s *btdev)
+{
+  return 0;
+}
+
+/****************************************************************************
+ * Name: stm32wb_blehci_driversend
+ ****************************************************************************/
+
+static int stm32wb_blehci_driversend(struct bt_driver_s *btdev,
+                                     enum bt_buf_type_e type,
+                                     void *data, size_t len)
+{
+  int ret = -EIO;
+
+  if (type == BT_CMD || type == BT_ACL_OUT)
+    {
+      wlinfo("passing type %s to mailbox driver\n",
+             (type == BT_CMD) ? "CMD" : "ACL");
+
+      /* Ensure non-concurrent access */
+
+      ret = nxsem_wait_uninterruptible(&g_excl_sem);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      if (type == BT_CMD)
+        {
+          ret = stm32wb_mbox_blecmd(data, len);
+        }
+      else
+        {
+          ret = stm32wb_mbox_bleacl(data, len);
+        }
+
+      nxsem_post(&g_excl_sem);
+    }
+
+  return ret < 0 ? ret : (int)len;
+}
+
+/****************************************************************************
+ * Name: stm32wb_blehci_rxevt
+ ****************************************************************************/
+
+static int stm32wb_blehci_rxevt(struct stm32wb_mbox_evt_s *evt)
+{
+  size_t len;
+
+  switch (evt->type)
+    {
+      case STM32WB_MBOX_HCIEVT:
+        len = sizeof(evt->evt_hdr) + evt->evt_hdr.len;
+        if (evt->evt_hdr.evt == BT_HCI_EVT_CMD_COMPLETE)
+          {
+            wlinfo("received CMD_COMPLETE from mailbox "
+                   "(opcode: 0x%x, status: 0x%x)\n",
+                   *(uint16_t *)((uint8_t *)&evt->evt_hdr + 3),
+                   *((uint8_t *)&evt->evt_hdr + 5));
+          }
+        else
+          {
+            wlinfo("received HCI EVT from mailbox "
+                   "(evt: %d, len: %zu)\n", evt->evt_hdr.evt, len);
+          }
+
+        bt_netdev_receive(&g_blehci_driver, BT_EVT, &evt->evt_hdr, len);
+        break;
+
+      case STM32WB_MBOX_HCIACL:
+        wlinfo("received HCI ACL from mailbox (handle: %d)\n",
+               evt->acl_hdr.handle);
+        len = sizeof(evt->acl_hdr) + evt->acl_hdr.len;
+
+        bt_netdev_receive(&g_blehci_driver, BT_ACL_IN, &evt->acl_hdr, len);
+        break;
+
+      case STM32WB_MBOX_SYSEVT:
+        wlinfo("received SYS event from mailbox (evt: %d)\n",
+               evt->evt_hdr.evt);
+        if (evt->evt_hdr.evt == STM32WB_SHCI_ASYNC_EVT &&
+            *(uint16_t *)(&evt->evt_hdr + 1) == STM32WB_SHCI_ASYNC_EVT_C2RDY)
+          {
+            stm32wb_blehci_bleinit();
+          }
+        break;
+
+      case STM32WB_MBOX_SYSACK:
+
+        /* CPU2 Ready is the only expected response */
+
+        DEBUGASSERT(evt->evt_hdr.evt == STM32WB_SHCI_ACK_EVT_C2RDY);
+
+        if (evt->evt_hdr.evt == STM32WB_SHCI_ACK_EVT_C2RDY)
+          {
+            wlinfo("system command ACK response");
+
+            /* Make driver initialisation in low priority work queue */
+
+            work_queue(LPWORK, &g_drv_init_work,
+                       stm32wb_blehci_drvinitworker, NULL, 0);
+          }
+        break;
+
+      default:
+        break;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: stm32wb_blehci_bleinit
+ ****************************************************************************/
+
+static void stm32wb_blehci_bleinit(void)
+{
+  /* Prepare BLE configuration */
+
+  struct stm32wb_shci_ble_init_cfg_s params =
+  {
+    .ble_buf =              NULL,
+    .ble_buf_size =         0,
+    .gatt_attr_num =        CONFIG_STM32WB_BLE_GATT_MAX_ATTR_NUM,
+    .gatt_srv_num =         CONFIG_STM32WB_BLE_GATT_MAX_SVC_NUM,
+    .gatt_attr_buf_size =   CONFIG_STM32WB_BLE_GATT_ATTR_BUF_SIZE,
+    .max_conn =             CONFIG_STM32WB_BLE_MAX_CONN,
+    .dle_enable =           CONFIG_STM32WB_BLE_DLE,
+    .prep_write_op_num =    STM32WB_BLE_PREP_WRITE_NUM,
+    .mem_block_num =        STM32WB_C2_MEM_BLOCK_NUM,
+    .att_max_mtu_size =     CONFIG_STM32WB_BLE_MAX_ATT_MTU,
+    .slave_sca =            CONFIG_STM32WB_BLE_SLAVE_SCA,
+    .master_sca_range =     CONFIG_STM32WB_BLE_MASTER_SCA,
+    .ls_clock_source =      CONFIG_STM32WB_BLE_LS_CLK_SRC,
+    .conn_event_length =    CONFIG_STM32WB_BLE_MAX_CONN_EVT_LENGTH,
+    .hse_startup =          CONFIG_STM32WB_BLE_HSE_STARTUP,
+    .viterbi_enable =       CONFIG_STM32WB_BLE_VITERBI,
+    .options =              STM32WB_BLE_INIT_OPTIONS,
+    .hw_version =           0,
+    .max_initor_coc_num =   CONFIG_STM32WB_BLE_MAX_INITOR_COC_NUM,
+    .tx_power_min =         CONFIG_STM32WB_BLE_MIN_TX_POWER,
+    .tx_power_max =         CONFIG_STM32WB_BLE_MAX_TX_POWER,
+    .rx_model_config =      STM32WB_BLE_RXMOD_AGC_RSSI
+  };
+
+  /* Initialise BLE */
+
+  stm32wb_mbox_bleinit(&params);
+}
+
+/****************************************************************************
+ * Name: stm32wb_blehci_driverinitialize
+ ****************************************************************************/
+
+static int stm32wb_blehci_driverinitialize(void)
+{
+  int ret = 0;
+
+#ifdef CONFIG_UART_BTH4
+  /* Register UART BT H4 device */
+
+  ret = uart_bth4_register(CONFIG_STM32WB_BLE_TTY_NAME, &g_blehci_driver);
+  if (ret < 0)
+    {
+      wlerr("bt_bth4_register error: %d\n", ret);
+      return ret;
+    }
+#elif defined(CONFIG_NET_BLUETOOTH)
+  /* Register network device */
+
+  ret = bt_netdev_register(&g_blehci_driver);
+  if (ret < 0)
+    {
+      wlerr("bt_netdev_register error: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: stm32wb_blehci_drvinitworker
+ ****************************************************************************/
+
+static void stm32wb_blehci_drvinitworker(void *arg)
+{
+  stm32wb_blehci_driverinitialize();
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_blehci_initialize
+ *
+ * Description:
+ *   Initialize and register BLE HCI driver which interfaces a BLE host
+ *   stack to a BLE controller running on CPU2 via HCI protocol.  Driver
+ *   registration occurs later when CPU2 notifies its ready status.
+ *
+ ****************************************************************************/
+
+void stm32wb_blehci_initialize(void)
+{
+  /* Initialize mbox internal data structures and set
+   * event receive handler.
+   */
+
+  stm32wb_mboxinitialize(stm32wb_blehci_rxevt);
+
+  /* Enable communication hardware and bootup CPU2 */
+
+  stm32wb_mboxenable();
+}
diff --git a/arch/arm/src/stm32wb/stm32wb_ipcc.h b/arch/arm/src/stm32wb/stm32wb_blehci.h
similarity index 74%
copy from arch/arm/src/stm32wb/stm32wb_ipcc.h
copy to arch/arm/src/stm32wb/stm32wb_blehci.h
index 5183c77d7b..84a679ab80 100644
--- a/arch/arm/src/stm32wb/stm32wb_ipcc.h
+++ b/arch/arm/src/stm32wb/stm32wb_blehci.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/arm/src/stm32wb/stm32wb_ipcc.h
+ * arch/arm/src/stm32wb/stm32wb_blehci.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,22 +18,18 @@
  *
  ****************************************************************************/
 
-#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_IPCC_H
-#define __ARCH_ARM_SRC_STM32WB_STM32WB_IPCC_H
+#ifndef __ARCH_ARM_SRC_STM32WB_STM32WB_BLEHCI_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_BLEHCI_H
 
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
-
-#include <stdbool.h>
-
-#include "chip.h"
-#include "hardware/stm32wb_ipcc.h"
+#include <sys/types.h>
 
 /****************************************************************************
- * Pre-processor Definitions
+ * Public Data
  ****************************************************************************/
 
 #ifndef __ASSEMBLY__
@@ -52,24 +48,16 @@ extern "C"
  ****************************************************************************/
 
 /****************************************************************************
- * Name: stm32wb_ipccreset
- *
- * Description:
- *   Reset the IPCC registers to default state
- *
- ****************************************************************************/
-
-void stm32wb_ipccreset(void);
-
-/****************************************************************************
- * Name: stm32wb_ipccenable
+ * Name: stm32wb_blehci_initialize
  *
  * Description:
- *   Enable the IPCC and start CPU2
+ *   Initialize and register BLE HCI driver which interfaces a BLE host
+ *   stack to a BLE controller running on CPU2 via HCI protocol.  Driver
+ *   registration occurs later when CPU2 notifies its ready status.
  *
  ****************************************************************************/
 
-void stm32wb_ipccenable(void);
+void stm32wb_blehci_initialize(void);
 
 #undef EXTERN
 #if defined(__cplusplus)
@@ -77,4 +65,4 @@ void stm32wb_ipccenable(void);
 #endif
 
 #endif /* __ASSEMBLY__ */
-#endif /* __ARCH_ARM_SRC_STM32WB_STM32WB_IPCC_H */
+#endif /* __ARCH_ARM_SRC_STM32WB_STM32WB_BLEHCI_H */
diff --git a/arch/arm/src/stm32wb/stm32wb_ipcc.h b/arch/arm/src/stm32wb/stm32wb_ipcc.h
index 5183c77d7b..8f92e3f5e9 100644
--- a/arch/arm/src/stm32wb/stm32wb_ipcc.h
+++ b/arch/arm/src/stm32wb/stm32wb_ipcc.h
@@ -30,6 +30,7 @@
 #include <stdbool.h>
 
 #include "chip.h"
+#include "arm_internal.h"
 #include "hardware/stm32wb_ipcc.h"
 
 /****************************************************************************
@@ -71,6 +72,109 @@ void stm32wb_ipccreset(void);
 
 void stm32wb_ipccenable(void);
 
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_rxactive
+ *
+ * Description:
+ *   Check channel receive active flag.
+ *
+ ****************************************************************************/
+
+static inline bool stm32wb_ipcc_rxactive(uint8_t chan)
+{
+  return (getreg32(STM32WB_IPCC_C2TOC1SR) & IPCC_C2TOC1SR_BIT(chan)) != 0;
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_txactive
+ *
+ * Description:
+ *   Check channel transmit active flag.
+ *
+ ****************************************************************************/
+
+static inline bool stm32wb_ipcc_txactive(uint8_t chan)
+{
+  return (getreg32(STM32WB_IPCC_C1TOC2SR) & IPCC_C1TOC2SR_BIT(chan)) != 0;
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_settxactive
+ *
+ * Description:
+ *   Set channel transmit active flag.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_ipcc_settxactive(uint8_t chan)
+{
+  putreg32(IPCC_C1SCR_SET_BIT(chan), STM32WB_IPCC_C1SCR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_masktxf
+ *
+ * Description:
+ *   Mask channel transmit free interrupt.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_ipcc_masktxf(uint8_t chan)
+{
+  uint32_t regval = getreg32(STM32WB_IPCC_C1MR);
+  regval |= IPCC_C1MR_FM_BIT(chan);
+  putreg32(regval, STM32WB_IPCC_C1MR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_unmasktxf
+ *
+ * Description:
+ *   Unmask channel transmit free interrupt.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_ipcc_unmasktxf(uint8_t chan)
+{
+  uint32_t regval = getreg32(STM32WB_IPCC_C1MR);
+  regval &= ~IPCC_C1MR_FM_BIT(chan);
+  putreg32(regval, STM32WB_IPCC_C1MR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_maskrxo
+ *
+ * Description:
+ *   Mask channel receive occupied interrupt.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_ipcc_maskrxo(uint8_t chan)
+{
+  uint32_t regval = getreg32(STM32WB_IPCC_C1MR);
+  regval |= IPCC_C1MR_OM_BIT(chan);
+  putreg32(regval, STM32WB_IPCC_C1MR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_maskrxo
+ *
+ * Description:
+ *   Unmask channel receive occupied interrupt.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_ipcc_unmaskrxo(uint8_t chan)
+{
+  uint32_t regval = getreg32(STM32WB_IPCC_C1MR);
+  regval &= ~IPCC_C1MR_OM_BIT(chan);
+  putreg32(regval, STM32WB_IPCC_C1MR);
+}
+
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/arch/arm/src/stm32wb/stm32wb_mbox.c b/arch/arm/src/stm32wb/stm32wb_mbox.c
new file mode 100644
index 0000000000..f3bed170a4
--- /dev/null
+++ b/arch/arm/src/stm32wb/stm32wb_mbox.c
@@ -0,0 +1,813 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mbox.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+
+#include "arm_internal.h"
+#include "stm32wb_ipcc.h"
+#include "stm32wb_mbox.h"
+#include "hardware/stm32wb_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Mailbox shared buffer base address.  Normally it is located at
+ * the beginning of SRAM2a.
+ */
+
+#define STM32WB_MBOX_SHARED_BASE      STM32WB_SRAM2A_BASE
+
+/* Mailbox shared buffer fields */
+
+#define stm32wb_mbox_shared \
+  (*(struct stm32wb_mbox_shared_buffer_s *)STM32WB_MBOX_SHARED_BASE)
+
+#define stm32wb_mbox_ref_table        (stm32wb_mbox_shared.ref_table)
+#define stm32wb_mbox_di_table         (stm32wb_mbox_shared.dev_info_table)
+#define stm32wb_mbox_sys_table        (stm32wb_mbox_shared.sys_table)
+#define stm32wb_mbox_mm_table         (stm32wb_mbox_shared.mm_table)
+#define stm32wb_mbox_ble_table        (stm32wb_mbox_shared.ble_table)
+
+/* Mailbox buffer sizes */
+
+#define STM32WB_MBOX_CS_BUF_SIZE      16
+#define STM32WB_MBOX_CMDPKT_BUF_SIZE  268
+#define STM32WB_MBOX_ACLPKT_BUF_SIZE  264
+
+#define STM32WB_MBOX_RX_BUF_SIZE \
+  (CONFIG_STM32WB_MBOX_RX_EVT_QUEUE_LEN * STM32WB_MBOX_CMDPKT_BUF_SIZE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Mailbox shared buffer structures */
+
+begin_packed_struct struct stm32wb_mbox_safe_boot_info_table_s
+{
+  uint32_t  version;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_fus_info_table_s
+{
+  uint32_t  version;
+  uint32_t  memory_size;
+  uint32_t  fus_info;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_wireless_fw_info_table_s
+{
+  uint32_t  version;
+  uint32_t  memory_size;
+  uint32_t  info_stack;
+  uint32_t  reserved;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_device_info_table_s
+{
+  struct stm32wb_mbox_safe_boot_info_table_s    safe_boot_info_table;
+  struct stm32wb_mbox_fus_info_table_s          fus_info_table;
+  struct stm32wb_mbox_wireless_fw_info_table_s  wireless_fw_info_table;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_ble_table_s
+{
+  void      *cmd_buffer;
+  void      *cs_buffer;
+  void      *evt_queue;
+  void      *acl_buffer;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_sys_table_s
+{
+  void      *cmd_buffer;
+  void      *evt_queue;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_mem_manager_table_s
+{
+  void      *ble_spare_buffer;
+  void      *sys_spare_buffer;
+  void      *evtpool_buffer;
+  uint32_t  evtpool_size;
+  void      *evtfree_buffer;
+  void      *traces_evtpool_buffer;
+  uint32_t  traces_evtpool_size;
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_ref_table_s
+{
+  struct stm32wb_mbox_device_info_table_s   *dev_info_table;
+  struct stm32wb_mbox_ble_table_s           *ble_table;
+  void                                      *thread_table;
+  struct stm32wb_mbox_sys_table_s           *sys_table;
+  struct stm32wb_mbox_mem_manager_table_s   *mm_table;
+  void                                      *traces_table;
+  void                                      *mac_802_15_4_table;
+  void                                      *zigbee_table;
+  void                                      *ble_lld_test_table;
+  void                                      *ble_lld_table;
+} end_packed_struct;
+
+/* Mailbox shared buffer memory layout structure */
+
+struct stm32wb_mbox_shared_buffer_s
+{
+  aligned_data(4) struct stm32wb_mbox_ref_table_s         ref_table;
+
+  aligned_data(4) struct stm32wb_mbox_device_info_table_s dev_info_table;
+  aligned_data(4) struct stm32wb_mbox_ble_table_s         ble_table;
+  aligned_data(4) struct stm32wb_mbox_sys_table_s         sys_table;
+  aligned_data(4) struct stm32wb_mbox_mem_manager_table_s mm_table;
+
+  aligned_data(4) stm32wb_mbox_list_t  evtfree_buffer;
+#ifdef CONFIG_STM32WB_BLE
+  aligned_data(4) stm32wb_mbox_list_t  ble_evt_queue;
+#endif
+  aligned_data(4) stm32wb_mbox_list_t  sys_evt_queue;
+
+#ifdef CONFIG_STM32WB_BLE
+  aligned_data(4) uint8_t ble_cs_buffer[STM32WB_MBOX_CS_BUF_SIZE];
+#endif
+  aligned_data(4) uint8_t evtpool_buffer[STM32WB_MBOX_RX_BUF_SIZE];
+  aligned_data(4) uint8_t sys_cmd_buffer[STM32WB_MBOX_CMDPKT_BUF_SIZE];
+  aligned_data(4) uint8_t sys_spare_buffer[STM32WB_MBOX_CMDPKT_BUF_SIZE];
+#ifdef CONFIG_STM32WB_BLE
+  aligned_data(4) uint8_t ble_spare_buffer[STM32WB_MBOX_CMDPKT_BUF_SIZE];
+  aligned_data(4) uint8_t ble_cmd_buffer[STM32WB_MBOX_CMDPKT_BUF_SIZE];
+  aligned_data(4) uint8_t ble_acl_buffer[STM32WB_MBOX_ACLPKT_BUF_SIZE];
+#endif
+};
+
+/* Mailbox channel data type */
+
+struct stm32wb_mbox_channel_s
+{
+  uint8_t                     ch_num;
+  stm32wb_mbox_list_t         msg_buf_queue;
+  struct stm32wb_mbox_cmd_s   *msg_buf;
+  bool                        ack_ready;
+};
+
+/****************************************************************************
+ * Private Function prototypes
+ ****************************************************************************/
+
+static void stm32wb_ipcc_rxoisr(int irq, uint32_t *regs, void *arg);
+static void stm32wb_ipcc_txfisr(int irq, uint32_t *regs, void *arg);
+
+static void stm32wb_mbox_rxworker(void *arg);
+static void stm32wb_mbox_txworker(void *arg);
+
+static void stm32wb_mbox_eventfree(stm32wb_mbox_list_t *evt);
+static void stm32wb_mbox_acksyscmd(void);
+
+static int stm32wb_mbox_txdata(struct stm32wb_mbox_channel_s *chan,
+                               uint8_t type, void *data, size_t len);
+static bool stm32wb_mbox_txnext(struct stm32wb_mbox_channel_s *chan);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct work_s g_rx_evt_work;
+static struct work_s g_tx_cmd_work;
+
+static stm32wb_mbox_list_t g_rx_evt_queue;
+static stm32wb_mbox_list_t g_tx_evtfree_queue;
+static uint8_t g_free_buffers[CONFIG_STM32WB_MBOX_TX_CMD_QUEUE_LEN]
+                             [STM32WB_MBOX_CMDPKT_BUF_SIZE];
+static stm32wb_mbox_list_t g_free_buffers_pool;
+
+static struct stm32wb_mbox_channel_s g_syscmd_channel;
+#ifdef CONFIG_STM32WB_BLE
+static struct stm32wb_mbox_channel_s g_blecmd_channel;
+static struct stm32wb_mbox_channel_s g_bleacl_channel;
+#endif
+
+static stm32wb_mbox_evt_handler_t receive_evt_handler;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_rxoisr
+ *
+ * Description:
+ *   RX channel occupied interrupt handler (communication data posted
+ *   by sending processor).
+ *
+ ****************************************************************************/
+
+static void stm32wb_ipcc_rxoisr(int irq, uint32_t *regs, void *arg)
+{
+  uint32_t clrmask = 0;
+
+  /* Pull events from system channel into processing queue */
+
+  if (stm32wb_ipcc_rxactive(STM32WB_MBOX_SYSEVT_CHANNEL))
+    {
+      stm32wb_mbox_list_moveall(&stm32wb_mbox_shared.sys_evt_queue,
+                                &g_rx_evt_queue);
+
+      clrmask |= IPCC_C1SCR_CLR_BIT(STM32WB_MBOX_SYSEVT_CHANNEL);
+    }
+
+#ifdef CONFIG_STM32WB_BLE
+
+  /* Pull events from BLE channel into processing queue */
+
+  if (stm32wb_ipcc_rxactive(STM32WB_MBOX_BLEEVT_CHANNEL))
+    {
+      stm32wb_mbox_list_moveall(&stm32wb_mbox_shared.ble_evt_queue,
+                                &g_rx_evt_queue);
+
+      clrmask |= IPCC_C1SCR_CLR_BIT(STM32WB_MBOX_BLEEVT_CHANNEL);
+    }
+#endif
+
+  /* Process collected events in the work queue */
+
+  if (work_available(&g_rx_evt_work))
+    {
+      work_queue(HPWORK, &g_rx_evt_work, stm32wb_mbox_rxworker, NULL, 0);
+    }
+
+  /* Clear active statuses */
+
+  putreg32(clrmask, STM32WB_IPCC_C1SCR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_ipcc_txfisr
+ *
+ * Description:
+ *   TX channel free interrupt handler (communication data retrieved
+ *   by receiving processor).
+ *
+ ****************************************************************************/
+
+static void stm32wb_ipcc_txfisr(int irq, uint32_t *regs, void *arg)
+{
+  uint32_t c1mr = getreg32(STM32WB_IPCC_C1MR);
+  uint32_t txfsrc;
+
+  /* TXF interrupt can be triggered by not masked channels and active status
+   * of the source channel is cleared by CPU2. So we ignore masked and active
+   * channels and rise other C1MR bits to highlight needed channels.
+   */
+
+  txfsrc = ~(c1mr | (getreg32(STM32WB_IPCC_C1TOC2SR) << IPCC_C1MR_FM_SHIFT))
+           & IPCC_C1MR_FM_MASK;
+
+  /* Check if the release channel triggered the interrupt */
+
+  if (txfsrc & IPCC_C1MR_FM_BIT(STM32WB_MBOX_EVT_RELEASE_CHANNEL))
+    {
+      /* Move all released events (if any) into transmission mailbox */
+
+      if (!stm32wb_mbox_list_is_empty(&g_tx_evtfree_queue))
+        {
+          stm32wb_mbox_list_moveall(&g_tx_evtfree_queue,
+                                    &stm32wb_mbox_shared.evtfree_buffer);
+
+          /* Start release channel transmission */
+
+          stm32wb_ipcc_settxactive(STM32WB_MBOX_EVT_RELEASE_CHANNEL);
+        }
+    }
+
+  /* Check other channels, except the release channel */
+
+  if (txfsrc & ~IPCC_C1MR_FM_BIT(STM32WB_MBOX_EVT_RELEASE_CHANNEL))
+    {
+      /* Check if the system channel triggered the interrupt */
+
+      if (txfsrc & IPCC_C1MR_FM_BIT(STM32WB_MBOX_SYSCMD_CHANNEL))
+        {
+          /* System channel works in 'half-duplex' mode and acks
+           * immediately on each command before TXF, so it needs
+           * to process ack response before sending next command.
+           */
+
+          g_syscmd_channel.ack_ready = true;
+        }
+
+      /* Continue command processing in a work queue */
+
+      if (work_available(&g_tx_cmd_work))
+        {
+          work_queue(HPWORK, &g_tx_cmd_work, stm32wb_mbox_txworker, NULL, 0);
+        }
+    }
+
+  /* Mask triggered channels */
+
+  putreg32(c1mr | txfsrc, STM32WB_IPCC_C1MR);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_txworker
+ ****************************************************************************/
+
+static void stm32wb_mbox_txworker(void *arg)
+{
+  bool handled;
+
+  /* Process all queued commands if a dedicated channel is free */
+
+  do
+    {
+      handled = false;
+
+      if (!stm32wb_ipcc_txactive(STM32WB_MBOX_SYSCMD_CHANNEL))
+        {
+          /* Process ack response before send new command */
+
+          if (g_syscmd_channel.ack_ready)
+            {
+              stm32wb_mbox_acksyscmd();
+              g_syscmd_channel.ack_ready = false;
+            }
+
+          handled = stm32wb_mbox_txnext(&g_syscmd_channel);
+        }
+
+#ifdef CONFIG_STM32WB_BLE
+      if (!stm32wb_ipcc_txactive(STM32WB_MBOX_BLECMD_CHANNEL))
+        {
+          handled |= stm32wb_mbox_txnext(&g_blecmd_channel);
+        }
+
+      if (!stm32wb_ipcc_txactive(STM32WB_MBOX_BLEACL_CHANNEL))
+        {
+          handled |= stm32wb_mbox_txnext(&g_bleacl_channel);
+        }
+#endif
+    }
+  while (handled);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_rxworker
+ ****************************************************************************/
+
+static void stm32wb_mbox_rxworker(void *arg)
+{
+  stm32wb_mbox_list_t *evt;
+  irqstate_t flags;
+
+  while (1)
+    {
+      flags = enter_critical_section();
+
+      /* Pull an event from the queue */
+
+      evt = stm32wb_mbox_list_remove_head(&g_rx_evt_queue);
+
+      leave_critical_section(flags);
+
+      if (evt == NULL)
+        {
+          break;
+        }
+
+      /* Pass event to a callback function without a list header */
+
+      receive_evt_handler((struct stm32wb_mbox_evt_s *)(evt + 1));
+
+      /* Free completed event.  Released event needs to return to CPU2
+       * via release channel.
+       */
+
+      stm32wb_mbox_eventfree((stm32wb_mbox_list_t *)evt);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_txdata
+ *
+ * Description:
+ *   Send data over specified mailbox channel if possible.  If the
+ *   channel is busy then add a prepared packet into awaiting queue.
+ *
+ ****************************************************************************/
+
+static int stm32wb_mbox_txdata(struct stm32wb_mbox_channel_s *chan,
+                               uint8_t type, void *data, size_t len)
+{
+  irqstate_t flags;
+  struct stm32wb_mbox_cmd_s *pkt_buf;
+
+  flags = enter_critical_section();
+
+  /* To start transmission the channel needs to be free, there should be
+   * none of other waiting commands and none of unprocessed ack responses.
+   */
+
+  if (stm32wb_mbox_list_is_empty(&chan->msg_buf_queue) &&
+      !stm32wb_ipcc_txactive(chan->ch_num) && !chan->ack_ready)
+    {
+      /* Channel is ready, copy command into transmission buffer */
+
+      pkt_buf = chan->msg_buf;
+    }
+  else
+    {
+      /* Otherwise get temp buffer for command */
+
+      pkt_buf = (struct stm32wb_mbox_cmd_s *)
+                stm32wb_mbox_list_remove_head(&g_free_buffers_pool);
+    }
+
+  leave_critical_section(flags);
+
+  if (pkt_buf == NULL)
+    {
+      return -ENOBUFS;
+    }
+
+  pkt_buf->type = type;
+  memcpy(&pkt_buf->cmd_hdr, data, len);
+
+  if (pkt_buf == chan->msg_buf)
+    {
+      /* Command is ready in mailbox buffer, start transmission now */
+
+      stm32wb_ipcc_settxactive(chan->ch_num);
+
+      if (!stm32wb_mbox_list_is_empty(&chan->msg_buf_queue) ||
+          chan->ch_num == STM32WB_MBOX_SYSCMD_CHANNEL)
+        {
+          /* There are more commands awaiting, so unmask interrupt to get
+           * notified when channel gets ready to process a next one.
+           * And the system channel needs to check ack on completion.
+           */
+
+          stm32wb_ipcc_unmasktxf(chan->ch_num);
+        }
+    }
+  else
+    {
+      /* Command is in temp buffer, push it into queue */
+
+      flags = enter_critical_section();
+      stm32wb_mbox_list_add_tail(&chan->msg_buf_queue, &pkt_buf->list_hdr);
+      leave_critical_section(flags);
+
+      /* Unmask interrupt to get notified when channel gets free */
+
+      stm32wb_ipcc_unmasktxf(chan->ch_num);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_txnext
+ *
+ * Description:
+ *   Send next command from the queue.
+ *
+ ****************************************************************************/
+
+static bool stm32wb_mbox_txnext(struct stm32wb_mbox_channel_s *chan)
+{
+  struct stm32wb_mbox_cmd_s *pkt_buf;
+
+  pkt_buf = (struct stm32wb_mbox_cmd_s *)
+            stm32wb_mbox_list_remove_head(&chan->msg_buf_queue);
+
+  if (pkt_buf != NULL)
+    {
+      chan->msg_buf->type = pkt_buf->type;
+
+      if (chan->ch_num == STM32WB_MBOX_BLEACL_CHANNEL)
+        {
+          memcpy(chan->msg_buf, &pkt_buf->acl_hdr,
+                 pkt_buf->acl_hdr.len);
+        }
+      else
+        {
+          memcpy(chan->msg_buf, &pkt_buf->cmd_hdr,
+                 pkt_buf->cmd_hdr.param_len);
+        }
+
+      /* Start transmission */
+
+      stm32wb_ipcc_settxactive(chan->ch_num);
+
+      if (!stm32wb_mbox_list_is_empty(&chan->msg_buf_queue))
+        {
+          /* Unmask TXF interrupt to get notified when completed */
+
+          stm32wb_ipcc_unmasktxf(chan->ch_num);
+        }
+
+      /* Put back to pool the freed command buffer */
+
+      stm32wb_mbox_list_add_tail(&g_free_buffers_pool, &pkt_buf->list_hdr);
+    }
+
+  return pkt_buf != NULL;
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_eventfree
+ *
+ * Description:
+ *   Free handled mailbox event.
+ *
+ ****************************************************************************/
+
+static void stm32wb_mbox_eventfree(stm32wb_mbox_list_t *evt)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  /* Collect releasing events in the global list */
+
+  stm32wb_mbox_list_add_tail(&g_tx_evtfree_queue, evt);
+
+  /* Check if release channel is ready to process now */
+
+  if (!stm32wb_ipcc_txactive(STM32WB_MBOX_EVT_RELEASE_CHANNEL))
+    {
+      /* Move all collected events into transmission queue */
+
+      stm32wb_mbox_list_moveall(&g_tx_evtfree_queue,
+                                &stm32wb_mbox_shared.evtfree_buffer);
+
+      /* Start transmission */
+
+      stm32wb_ipcc_settxactive(STM32WB_MBOX_EVT_RELEASE_CHANNEL);
+    }
+  else
+    {
+      /* Unmask interrupt to get notified when channel gets free */
+
+      stm32wb_ipcc_unmasktxf(STM32WB_MBOX_EVT_RELEASE_CHANNEL);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_acksyscmd
+ *
+ * Description:
+ *   Send ACK response event for completed system command.
+ *
+ ****************************************************************************/
+
+static void stm32wb_mbox_acksyscmd(void)
+{
+  struct stm32wb_mbox_evt_s *evt;
+
+  /* System command ACK response is placed at the same address as the
+   * processed command but without a list header.
+   */
+
+  evt = (struct stm32wb_mbox_evt_s *)(&g_syscmd_channel.msg_buf);
+  evt->type = STM32WB_MBOX_SYSACK;
+
+  receive_evt_handler(evt);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_mboxinitialize
+ *
+ * Description:
+ *   Initialize mailbox driver memory.
+ *
+ * Input Parameters:
+ *   evt_handler - the function to call on event receive
+ *
+ ****************************************************************************/
+
+void stm32wb_mboxinitialize(stm32wb_mbox_evt_handler_t evt_handler)
+{
+  int i;
+
+  /* Init mailbox shared data */
+
+  stm32wb_mbox_list_initialize(&stm32wb_mbox_shared.sys_evt_queue);
+  stm32wb_mbox_list_initialize(&stm32wb_mbox_shared.evtfree_buffer);
+#ifdef CONFIG_STM32WB_BLE
+  stm32wb_mbox_list_initialize(&stm32wb_mbox_shared.ble_evt_queue);
+#endif
+
+  stm32wb_mbox_ref_table.dev_info_table = &stm32wb_mbox_di_table;
+  stm32wb_mbox_ref_table.ble_table      = &stm32wb_mbox_ble_table;
+  stm32wb_mbox_ref_table.sys_table      = &stm32wb_mbox_sys_table;
+  stm32wb_mbox_ref_table.mm_table       = &stm32wb_mbox_mm_table;
+
+  stm32wb_mbox_sys_table.cmd_buffer = &stm32wb_mbox_shared.sys_cmd_buffer;
+  stm32wb_mbox_sys_table.evt_queue  = &stm32wb_mbox_shared.sys_evt_queue;
+
+  stm32wb_mbox_mm_table.evtpool_buffer   = &stm32wb_mbox_shared
+                                            .evtpool_buffer;
+  stm32wb_mbox_mm_table.evtpool_size     = sizeof(stm32wb_mbox_shared
+                                                  .evtpool_buffer);
+  stm32wb_mbox_mm_table.evtfree_buffer   = &stm32wb_mbox_shared
+                                            .evtfree_buffer;
+  stm32wb_mbox_mm_table.sys_spare_buffer = &stm32wb_mbox_shared
+                                            .sys_spare_buffer;
+#ifdef CONFIG_STM32WB_BLE
+  stm32wb_mbox_mm_table.ble_spare_buffer = &stm32wb_mbox_shared
+                                            .ble_spare_buffer;
+#endif
+
+#ifdef CONFIG_STM32WB_BLE
+  stm32wb_mbox_ble_table.cmd_buffer = &stm32wb_mbox_shared.ble_cmd_buffer;
+  stm32wb_mbox_ble_table.acl_buffer = &stm32wb_mbox_shared.ble_acl_buffer;
+  stm32wb_mbox_ble_table.cs_buffer  = &stm32wb_mbox_shared.ble_cs_buffer;
+  stm32wb_mbox_ble_table.evt_queue  = &stm32wb_mbox_shared.ble_evt_queue;
+#endif
+
+  /* Init system channel data */
+
+  g_syscmd_channel.ch_num =  STM32WB_MBOX_SYSCMD_CHANNEL;
+  g_syscmd_channel.msg_buf = (struct stm32wb_mbox_cmd_s *)
+                              stm32wb_mbox_shared.sys_cmd_buffer;
+  stm32wb_mbox_list_initialize(&g_syscmd_channel.msg_buf_queue);
+
+#ifdef CONFIG_STM32WB_BLE
+  /* Init BLE command channel data */
+
+  g_blecmd_channel.ch_num =  STM32WB_MBOX_BLECMD_CHANNEL;
+  g_blecmd_channel.msg_buf = (struct stm32wb_mbox_cmd_s *)
+                             stm32wb_mbox_shared.ble_cmd_buffer;
+  stm32wb_mbox_list_initialize(&g_blecmd_channel.msg_buf_queue);
+
+  /* Init BLE ACL channel data */
+
+  g_bleacl_channel.ch_num =  STM32WB_MBOX_BLEACL_CHANNEL;
+  g_bleacl_channel.msg_buf = (struct stm32wb_mbox_cmd_s *)
+                             stm32wb_mbox_shared.ble_cmd_buffer;
+  stm32wb_mbox_list_initialize(&g_bleacl_channel.msg_buf_queue);
+#endif
+
+  /* Init local (not shared) queues */
+
+  stm32wb_mbox_list_initialize(&g_rx_evt_queue);
+  stm32wb_mbox_list_initialize(&g_tx_evtfree_queue);
+
+  stm32wb_mbox_list_initialize(&g_free_buffers_pool);
+  for (i = 0; i < CONFIG_STM32WB_MBOX_TX_CMD_QUEUE_LEN; i++)
+    {
+      stm32wb_mbox_list_add_tail(&g_free_buffers_pool,
+                                 (stm32wb_mbox_list_t *)g_free_buffers[i]);
+    }
+
+  /* Set event receive function */
+
+  receive_evt_handler = evt_handler;
+}
+
+/****************************************************************************
+ * Name: stm32wb_mboxenable
+ *
+ * Description:
+ *   Enable mailbox hardware and start communication.  The CPU2 responses
+ *   with C2Ready event on success.
+ *
+ ****************************************************************************/
+
+void stm32wb_mboxenable(void)
+{
+  uint32_t regval;
+
+  /* Setup RXO and TXF interrupts */
+
+  irq_attach(STM32WB_IRQ_IPCCRX, (xcpt_t)stm32wb_ipcc_rxoisr, NULL);
+  up_enable_irq(STM32WB_IRQ_IPCCRX);
+
+  irq_attach(STM32WB_IRQ_IPCCTX, (xcpt_t)stm32wb_ipcc_txfisr, NULL);
+  up_enable_irq(STM32WB_IRQ_IPCCTX);
+
+  regval = getreg32(STM32WB_IPCC_C1CR);
+  regval |= IPCC_C1CR_RXOIE | IPCC_C1CR_TXFIE;
+  putreg32(regval, STM32WB_IPCC_C1CR);
+
+  /* Unmask system channel RXO interrupt.  Once CPU2 started we expect
+   * to receive C2READY event via system channel.
+   */
+
+  stm32wb_ipcc_unmaskrxo(STM32WB_MBOX_SYSEVT_CHANNEL);
+
+  /* Enable IPCC hardware and bootup CPU2 */
+
+  stm32wb_ipccenable();
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_syscmd
+ *
+ * Description:
+ *   Send command over mailbox system channel.  Command data must be
+ *   prepended with HCI header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_syscmd(void *data, size_t len)
+{
+  return stm32wb_mbox_txdata(&g_syscmd_channel, STM32WB_MBOX_SYSCMD,
+                             data, len);
+}
+
+#ifdef CONFIG_STM32WB_BLE
+/****************************************************************************
+ * Name: stm32wb_mbox_blecmd
+ *
+ * Description:
+ *   Send command over mailbox BLE channel.  Command data must be
+ *   prepended with HCI header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_blecmd(void *data, size_t len)
+{
+  return stm32wb_mbox_txdata(&g_blecmd_channel, STM32WB_MBOX_HCICMD,
+                             data, len);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_bleacl
+ *
+ * Description:
+ *   Send BLE ACL data over mailbox BLE ACL channel.  Data must be
+ *   prepended with HCI ACL header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_bleacl(void *data, size_t len)
+{
+  return stm32wb_mbox_txdata(&g_bleacl_channel, STM32WB_MBOX_HCIACL,
+                             data, len);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_bleinit
+ *
+ * Description:
+ *   Initialize and start BLE subsystem with provided configuration params.
+ *
+ ****************************************************************************/
+
+void stm32wb_mbox_bleinit(struct stm32wb_shci_ble_init_cfg_s *params)
+{
+  struct bt_hci_cmd_hdr_s *cmd;
+
+  /* Just borrow a temporary free buffer for command data */
+
+  cmd = (struct bt_hci_cmd_hdr_s *)stm32wb_mbox_shared.sys_spare_buffer;
+
+  /* Prepare command data */
+
+  cmd->opcode = STM32WB_SHCI_BLE_INIT;
+  cmd->param_len = sizeof(*cmd);
+  memcpy(cmd + 1, params, sizeof(*params));
+
+  /* Send BLE init command to CPU2 */
+
+  stm32wb_mbox_syscmd(cmd, sizeof(*cmd) + sizeof(*params));
+
+  /* Unmask BLE event channel RXO interrupt */
+
+  stm32wb_ipcc_unmaskrxo(STM32WB_MBOX_BLEEVT_CHANNEL);
+}
+#endif /* CONFIG_STM32WB_BLE */
diff --git a/arch/arm/src/stm32wb/stm32wb_mbox.h b/arch/arm/src/stm32wb/stm32wb_mbox.h
new file mode 100644
index 0000000000..210bf1c6b0
--- /dev/null
+++ b/arch/arm/src/stm32wb/stm32wb_mbox.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mbox.h
+ *
+ * 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 __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include <nuttx/wireless/bluetooth/bt_hci.h>
+
+#include "stm32wb_mbox_list.h"
+#include "stm32wb_mbox_shci.h"
+
+/****************************************************************************
+ * Pre-Processor Declarations
+ ****************************************************************************/
+
+/* Mailbox channels */
+
+#define STM32WB_MBOX_BLEEVT_CHANNEL       1
+#define STM32WB_MBOX_BLECMD_CHANNEL       1
+#define STM32WB_MBOX_SYSEVT_CHANNEL       2
+#define STM32WB_MBOX_SYSCMD_CHANNEL       2
+#define STM32WB_MBOX_EVT_RELEASE_CHANNEL  4
+#define STM32WB_MBOX_BLEACL_CHANNEL       6
+
+/* Mailbox packet types */
+
+#define STM32WB_MBOX_HCICMD               0x01
+#define STM32WB_MBOX_HCIACL               0x02
+#define STM32WB_MBOX_HCIEVT               0x04
+#define STM32WB_MBOX_SYSCMD               0x10
+#define STM32WB_MBOX_SYSEVT               0x12
+#define STM32WB_MBOX_SYSACK               0xe0
+
+/* Mailbox configuration helpers */
+
+#define STM32WB_MBOX_BLE_ATT_DEFAULT_MTU  23
+#define STM32WB_MBOX_C2_MEM_BLOCK_SZ      32
+
+#define DIV_UP(a, b)    (((a) + (b) - 1) / (b))
+
+#define STM32WB_MBOX_DEFAULT_BLE_PREP_WRITE_NUM(max_mtu) \
+  (DIV_UP((max_mtu), STM32WB_MBOX_BLE_ATT_DEFAULT_MTU - 5) * 2)
+
+#define STM32WB_MBOX_DEFAULT_C2_MEM_BLOCK_NUM(max_mtu, max_conn, pw) \
+  ((pw) + ((max_conn) + 1) * (DIV_UP((max_mtu) + 4, STM32WB_MBOX_C2_MEM_BLOCK_SZ) + 2))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Mailbox data transfer packets */
+
+begin_packed_struct struct stm32wb_mbox_evt_s
+{
+  uint8_t                       type;
+  union
+    {
+      struct bt_hci_evt_hdr_s   evt_hdr;
+      struct bt_hci_acl_hdr_s   acl_hdr;
+    };
+} end_packed_struct;
+
+begin_packed_struct struct stm32wb_mbox_cmd_s
+{
+  stm32wb_mbox_list_t           list_hdr;
+  uint8_t                       type;
+  union
+  {
+    struct bt_hci_cmd_hdr_s     cmd_hdr;
+    struct bt_hci_acl_hdr_s     acl_hdr;
+  };
+} end_packed_struct;
+
+/* Mailbox receive event handler type */
+
+typedef int (*stm32wb_mbox_evt_handler_t)(struct stm32wb_mbox_evt_s *);
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_mboxinitialize
+ *
+ * Description:
+ *   Initialize mailbox driver memory.
+ *
+ * Input Parameters:
+ *   evt_handler - the function to call on event receive
+ *
+ ****************************************************************************/
+
+void stm32wb_mboxinitialize(stm32wb_mbox_evt_handler_t evt_handler);
+
+/****************************************************************************
+ * Name: stm32wb_mboxenable
+ *
+ * Description:
+ *   Enable mailbox hardware and start communication.  The CPU2 responses
+ *   with C2Ready event on success.
+ *
+ ****************************************************************************/
+
+void stm32wb_mboxenable(void);
+
+/****************************************************************************
+ * Name: stm32wb_mbox_syscmd
+ *
+ * Description:
+ *   Send command over mailbox system channel.  Command data must be
+ *   prepended with HCI header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_syscmd(void *data, size_t len);
+
+/****************************************************************************
+ * Name: stm32wb_mbox_blecmd
+ *
+ * Description:
+ *   Send command over mailbox BLE channel.  Command data must be
+ *   prepended with HCI header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_blecmd(void *data, size_t len);
+
+/****************************************************************************
+ * Name: stm32wb_mbox_bleacl
+ *
+ * Description:
+ *   Send BLE ACL data over mailbox BLE ACL channel.  Data must be
+ *   prepended with HCI ACL header.
+ *
+ ****************************************************************************/
+
+int stm32wb_mbox_bleacl(void *data, size_t len);
+
+/****************************************************************************
+ * Name: stm32wb_mbox_bleinit
+ *
+ * Description:
+ *   Initialize and start BLE subsystem with provided configuration params.
+ *
+ ****************************************************************************/
+
+void stm32wb_mbox_bleinit(struct stm32wb_shci_ble_init_cfg_s *params);
+
+#endif /* __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_H */
diff --git a/arch/arm/src/stm32wb/stm32wb_mbox_list.h b/arch/arm/src/stm32wb/stm32wb_mbox_list.h
new file mode 100644
index 0000000000..6081476022
--- /dev/null
+++ b/arch/arm/src/stm32wb/stm32wb_mbox_list.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mbox_list.h
+ *
+ * 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 __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_LIST_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_LIST_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This list implementation is similar to list_node, but prev and next
+ * fields are at opposite places and the struct is packed.  Also there is
+ * a new list_moveall function.
+ */
+
+begin_packed_struct struct stm32wb_mbox_list_s
+{
+  struct stm32wb_mbox_list_s *next;
+  struct stm32wb_mbox_list_s *prev;
+} end_packed_struct;
+
+typedef struct stm32wb_mbox_list_s stm32wb_mbox_list_t;
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32wb_mbox_list_initialize
+ *
+ * Description:
+ *   Initialize internal fields.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_mbox_list_initialize(stm32wb_mbox_list_t *list)
+{
+  list->prev = list;
+  list->next = list;
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_list_add_tail
+ *
+ * Description:
+ *   Add new node at the end of the list.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_mbox_list_add_tail(stm32wb_mbox_list_t *list,
+                                              stm32wb_mbox_list_t *item)
+{
+  item->prev       = list->prev;
+  item->next       = list;
+  list->prev->next = item;
+  list->prev       = item;
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_list_remove_head
+ *
+ * Description:
+ *   Remove and return first node from the list head (if any).
+ *
+ ****************************************************************************/
+
+static inline stm32wb_mbox_list_t *
+stm32wb_mbox_list_remove_head(stm32wb_mbox_list_t *list)
+{
+  if (list->next != list)
+  {
+    stm32wb_mbox_list_t *item = list->next;
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+    item->prev       = NULL;
+    item->next       = NULL;
+    return item;
+  }
+  else
+  {
+    return NULL;
+  }
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_list_is_empty
+ *
+ * Description:
+ *   Check if the list is empty.
+ *
+ ****************************************************************************/
+
+static inline bool stm32wb_mbox_list_is_empty(stm32wb_mbox_list_t *list)
+{
+  return (list->next == list);
+}
+
+/****************************************************************************
+ * Name: stm32wb_mbox_list_moveall
+ *
+ * Description:
+ *   Remove all nodes from source list and add them to the end of the
+ *   destination list.
+ *
+ ****************************************************************************/
+
+static inline void stm32wb_mbox_list_moveall(stm32wb_mbox_list_t *src,
+                                             stm32wb_mbox_list_t *dst)
+{
+  if (src->next != src)
+    {
+      src->next->prev = dst->prev;
+      src->prev->next = dst;
+      dst->prev->next = src->next;
+      dst->prev       = src->prev;
+      src->prev       = src;
+      src->next       = src;
+    }
+}
+
+#endif /* __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_LIST_H */
diff --git a/arch/arm/src/stm32wb/stm32wb_mbox_shci.h b/arch/arm/src/stm32wb/stm32wb_mbox_shci.h
new file mode 100644
index 0000000000..7ca109fda5
--- /dev/null
+++ b/arch/arm/src/stm32wb/stm32wb_mbox_shci.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+ * arch/arm/src/stm32wb/stm32wb_mbox_shci.h
+ *
+ * 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 __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_SHCI_H
+#define __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_SHCI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/compiler.h>
+
+/****************************************************************************
+ * Pre-Processor Declarations
+ ****************************************************************************/
+
+/* SHCI event types *********************************************************/
+
+#define STM32WB_SHCI_ASYNC_EVT              0xff
+
+/* SHCI async event subtypes */
+
+#define STM32WB_SHCI_ASYNC_EVT_C2RDY        0x9200
+
+/* SHCI system command acknowledgement events */
+
+#define STM32WB_SHCI_ACK_EVT_C2RDY          0x05
+
+/* SHCI command opcodes *****************************************************/
+
+#define STM32WB_SHCI_OGF                    0x3f
+#define STM32WB_SHCI_OP(ogf, ocf)           (((ogf) << 10) | (ocf))
+
+#define STM32WB_SHCI_FUS_GET_STATE          STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x52)
+#define STM32WB_SHCI_FUS_FW_UPGRADE         STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x54)
+#define STM32WB_SHCI_FUS_FW_DELETE          STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x55)
+#define STM32WB_SHCI_FUS_UPDATE_AUTH_KEY    STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x56)
+#define STM32WB_SHCI_FUS_LOCK_AUTH_KEY      STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x57)
+#define STM32WB_SHCI_FUS_STORE_USR_KEY      STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x58)
+#define STM32WB_SHCI_FUS_LOAD_USR_KEY       STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x59)
+#define STM32WB_SHCI_FUS_START_WS           STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x5a)
+#define STM32WB_SHCI_FUS_LOCK_USR_KEY       STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x5d)
+#define STM32WB_SHCI_FUS_UNLOAD_USR_KEY     STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x5e)
+#define STM32WB_SHCI_FUS_ANTIROLLBACK       STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x5f)
+#define STM32WB_SHCI_BLE_INIT               STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x66)
+#define STM32WB_SHCI_THREAD_INIT            STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x67)
+#define STM32WB_SHCI_DEBUG_INIT             STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x68)
+#define STM32WB_SHCI_FLASH_ERASE_ACTIVITY   STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x69)
+#define STM32WB_SHCI_CONCURRENT_SET_MODE    STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6a)
+#define STM32WB_SHCI_FLASH_STORE_DATA       STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6b)
+#define STM32WB_SHCI_FLASH_ERASE_DATA       STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6c)
+#define STM32WB_SHCI_RADIO_ALLOW_LOW_POWER  STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6d)
+#define STM32WB_SHCI_MAC_802154_INIT        STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6e)
+#define STM32WB_SHCI_REINIT                 STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x6f)
+#define STM32WB_SHCI_ZIGBEE_INIT            STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x70)
+#define STM32WB_SHCI_LLD_TESTS_INIT         STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x71)
+#define STM32WB_SHCI_EXTPA_CONFIG           STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x72)
+#define STM32WB_SHCI_SET_FLASH_CONTROL      STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x73)
+#define STM32WB_SHCI_BLE_LLD_INIT           STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x74)
+#define STM32WB_SHCI_CONFIG                 STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x75)
+#define STM32WB_SHCI_GET_NEXT_BLE_EVT_TIME  STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x76)
+#define STM32WB_SHCI_ENABLE_NEXT_802154_NF  STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x77)
+#define STM32WB_SHCI_802_15_4_DEINIT        STM32WB_SHCI_OP(STM32WB_SHCI_OGF, 0x78)
+
+/* Command params bitfield definitions **************************************/
+
+/* BLE init command option flags */
+
+#define STM32WB_SHCI_BLE_INIT_OPT_STACK_MASK              (1 << 0) /* Bit 0: BLE stack select */
+#  define STM32WB_SHCI_BLE_INIT_OPT_STACK_LL_HOST         (0 << 0) /* 0x0: Link Layer and Host */
+#  define STM32WB_SHCI_BLE_INIT_OPT_STACK_LL              (1 << 0) /* 0x1: Link Layer only */
+
+#define STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_MASK         (1 << 1) /* Bit 1: Service Changed characteristic */
+#  define STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_ENABLED    (0 << 1) /* 0x0: Characteristic enabled */
+#  define STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_DISABLED   (1 << 1) /* 0x1: Characteristic disabled */
+
+#define STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_MASK   (1 << 2) /* Bit 2: Device Name mode */
+#  define STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RW   (0 << 2) /* 0x0: Read-Write mode */
+#  define STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RO   (1 << 2) /* 0x1: Read-Only mode */
+
+#define STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_MASK            (1 << 4) /* Bit 4: Channel selection algorithm 2 enabled */
+#  define STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_DISABLED      (0 << 4) /* 0x0: Algorithm 2 disabled */
+#  define STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_ENABLED       (1 << 4) /* 0x1: Algorithm 2 enabled */
+
+#define STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_MASK        (1 << 7) /* Bit 7: Power class */
+#  define STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_2_3       (0 << 7) /* 0x0: Power Class 2-3 */
+#  define STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_1         (1 << 7) /* 0x1: Power Class 1 */
+
+/* BLE init command rx_model_config flags */
+
+#define STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_MASK         (1 << 0) /* Bit 0: AGC RSSI model */
+#  define STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_LEGACY     (0 << 0) /* 0x0: AGC RSSI Legacy */
+#  define STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_IMPROVED   (1 << 0) /* 0x1: AGC RSSI Improved */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* STM32WB_SHCI_BLE_INIT command params */
+
+begin_packed_struct struct stm32wb_shci_ble_init_cfg_s
+{
+  void     *ble_buf;              /* Not used, must be NULL. */
+  uint32_t ble_buf_size;          /* Not used, must be 0. */
+
+  uint16_t gatt_attr_num;         /* Maximum number of GATT attributes. */
+  uint16_t gatt_srv_num;          /* Maximum number of GATT services. */
+  uint16_t gatt_attr_buf_size;    /* GATT attributes storage buf size. */
+
+  uint8_t  max_conn;              /* Maximum number of simultaneous
+                                   * connections, up to 8 is supported. */
+
+  uint8_t  dle_enable;            /* Data Length Extension enable. */
+
+  uint8_t  prep_write_op_num;     /* Maximum number of Prepare Write
+                                   * operations. */
+
+  uint8_t  mem_block_num;         /* Number of allocated memory blocks,
+                                   * throughput performance / memory usage
+                                   * tuning. */
+
+  uint16_t att_max_mtu_size;      /* Maximum attribute MTU size. */
+
+  uint16_t slave_sca;             /* Sleep clock accuracy (ppm value) in
+                                   * slave mode. */
+
+  uint8_t  master_sca_range;      /* Sleep clock accuracy in master mode:
+                                   * 0x00: 251 ppm to 500 ppm
+                                   * 0x01: 151 ppm to 250 ppm
+                                   * 0x02: 101 ppm to 150 ppm
+                                   * 0x03: 76 ppm to 100 ppm
+                                   * 0x04: 51 ppm to 75 ppm
+                                   * 0x05: 31 ppm to 50 ppm
+                                   * 0x06: 21 ppm to 30 ppm
+                                   * 0x07: 0 ppm to 20 ppm */
+
+  uint8_t  ls_clock_source;       /* Low speed 32 kHz clock source:
+                                   * 0x00: use LSE
+                                   * 0x01: use HSE */
+
+  uint32_t conn_event_length;     /* Maximum duration of a slave connection
+                                   * event in units of 625/256us
+                                   * (~2.44us). */
+
+  uint16_t hse_startup;           /* HSE startup time in units of 625/256us
+                                   * (~2.44us). */
+
+  uint8_t  viterbi_enable;        /* Enable Viterbi algorithm
+                                   * implementation. */
+
+  uint8_t  options;               /* BLE init option flags. */
+
+  uint8_t  hw_version;            /* Not used, must be 0. */
+
+  uint8_t  max_initor_coc_num;    /* Maximum number of connection-oriented
+                                   * channels in initiator mode. */
+
+  int8_t   tx_power_min;          /* Minimum transmit power in dBm.
+                                   * Range: -127 .. 20 */
+
+  int8_t   tx_power_max;          /* Maximum transmit power in dBm.
+                                   * Range: -127 .. 20 */
+
+  uint8_t  rx_model_config;       /* RX model config flags */
+} end_packed_struct;
+
+#endif /* __ARCH_ARM_SRC_STM32WB_STM32WB_MBOX_SHCI_H */
diff --git a/arch/arm/src/stm32wb/stm32wb_rcc.c b/arch/arm/src/stm32wb/stm32wb_rcc.c
index d268009732..44b3fe4e10 100644
--- a/arch/arm/src/stm32wb/stm32wb_rcc.c
+++ b/arch/arm/src/stm32wb/stm32wb_rcc.c
@@ -810,6 +810,17 @@ static void stm32wb_stdclockconfig(void)
       putreg32(regval, STM32WB_RCC_CR);
 #  endif
 #endif /* STM32WB_USE_LSE */
+
+      /* Select CPU2 RF wakeup clock source, no clock if not set */
+
+      regval  = getreg32(STM32WB_RCC_CSR);
+      regval &= ~RCC_CSR_RFWKPSEL_MASK;
+#if defined(STM32WB_BOARD_RFWKP_USELSE)
+      regval |= RCC_CSR_RFWKPSEL_LSE;
+#elif defined(STM32WB_BOARD_RFWKP_USEHSE)
+      regval |= RCC_CSR_RFWKPSEL_HSE;
+#endif
+      putreg32(regval, STM32WB_RCC_CSR);
     }
 }
 #endif
diff --git a/boards/arm/stm32wb/nucleo-wb55rg/README.txt b/boards/arm/stm32wb/nucleo-wb55rg/README.txt
index d0228bb114..f9fc90530e 100644
--- a/boards/arm/stm32wb/nucleo-wb55rg/README.txt
+++ b/boards/arm/stm32wb/nucleo-wb55rg/README.txt
@@ -26,6 +26,8 @@ Status
     supported - GPIO, EXTI, DMA, timers, flash, PWR, RTC, USART/LPUART, SPI,
     IPCC. SRAM2 heap allocation works. Builtin apps work and ostest passed.
 
+  July 2022: Added BLE support with mailbox IPC driver.
+
 LEDs
 ====
 
@@ -60,3 +62,8 @@ Configurations
 
     Configures the NuttShell (nsh) located at examples/nsh.  This
     configuration is focused on low level, command-line driver testing.
+
+  ble:
+
+    Besides the NuttShell this configuration also enables BLE support.
+    It includes btsak application for testing BLE applications.
diff --git a/boards/arm/stm32wb/nucleo-wb55rg/configs/ble/defconfig b/boards/arm/stm32wb/nucleo-wb55rg/configs/ble/defconfig
new file mode 100644
index 0000000000..dca33bff35
--- /dev/null
+++ b/boards/arm/stm32wb/nucleo-wb55rg/configs/ble/defconfig
@@ -0,0 +1,55 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_NET_IPv4 is not set
+CONFIG_ALLOW_BSD_COMPONENTS=y
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="nucleo-wb55rg"
+CONFIG_ARCH_BOARD_NUCLEO_WB55RG=y
+CONFIG_ARCH_CHIP="stm32wb"
+CONFIG_ARCH_CHIP_STM32WB55RG=y
+CONFIG_ARCH_CHIP_STM32WB=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARD_LOOPSPERMSEC=6500
+CONFIG_BTSAK=y
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DRIVERS_BLUETOOTH=y
+CONFIG_DRIVERS_WIRELESS=y
+CONFIG_FS_PROCFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_NET=y
+CONFIG_NET_BLUETOOTH=y
+CONFIG_NET_STATISTICS=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_DISABLE_IFUPDOWN=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=64
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_MQ_MSGS=32
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=196608
+CONFIG_RAM_START=0x20000000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_STM32WB_BLE=y
+CONFIG_STM32WB_DISABLE_IDLE_SLEEP_DURING_DEBUG=y
+CONFIG_STM32WB_DMA1=y
+CONFIG_STM32WB_PWR=y
+CONFIG_STM32WB_USART1=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TASK_NAME_SIZE=0
+CONFIG_TESTING_OSTEST=y
+CONFIG_USART1_SERIAL_CONSOLE=y
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_BLUETOOTH=y
diff --git a/boards/arm/stm32wb/nucleo-wb55rg/include/nucleo-wb55rg.h b/boards/arm/stm32wb/nucleo-wb55rg/include/nucleo-wb55rg.h
index c2fc3aacf9..79ae8bb8c4 100644
--- a/boards/arm/stm32wb/nucleo-wb55rg/include/nucleo-wb55rg.h
+++ b/boards/arm/stm32wb/nucleo-wb55rg/include/nucleo-wb55rg.h
@@ -69,6 +69,12 @@
 #  define MSI_CLOCK_CONFIG              1 /* MSI @ 4MHz autotrimmed via LSE */
 #endif
 
+#if 0
+#  define STM32WB_BOARD_RFWKP_USEHSE    1 /* CPU2 use HSE/1024 on RF wakeup */
+#elif 1
+#  define STM32WB_BOARD_RFWKP_USELSE    1 /* CPU2 use LSE on RF wakeup */
+#endif
+
 #if defined(HSI_CLOCK_CONFIG)
 
 #define STM32WB_BOARD_USEHSI            1
diff --git a/boards/arm/stm32wb/nucleo-wb55rg/src/stm32_appinit.c b/boards/arm/stm32wb/nucleo-wb55rg/src/stm32_appinit.c
index d505e8b887..497284720e 100644
--- a/boards/arm/stm32wb/nucleo-wb55rg/src/stm32_appinit.c
+++ b/boards/arm/stm32wb/nucleo-wb55rg/src/stm32_appinit.c
@@ -43,6 +43,12 @@
 #  include "stm32wb_rtc.h"
 #endif
 
+#ifdef CONFIG_STM32WB_BLE
+#  include "stm32wb_blehci.h"
+#endif
+
+#include "nucleo-wb55rg.h"
+
 /****************************************************************************
  * Private Data
  ****************************************************************************/
@@ -51,6 +57,20 @@
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: arm_netinitialize
+ *
+ * Description:
+ *   Dummy function expected to start-up logic.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET) && !defined(CONFIG_NETDEV_LATEINIT)
+void arm_netinitialize(void)
+{
+}
+#endif
+
 /****************************************************************************
  * Name: board_app_initialize
  *
@@ -135,6 +155,12 @@ int board_app_initialize(uintptr_t arg)
     }
 #endif
 
+#ifdef CONFIG_STM32WB_BLE
+  /* Initialize and register BLE HCI driver */
+
+  stm32wb_blehci_initialize();
+#endif
+
   return ret;
 }
 #endif /* CONFIG_BOARDCTL */