summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/nimble/transport/emspi
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/transport/emspi')
-rw-r--r--src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h33
-rw-r--r--src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml36
-rw-r--r--src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c965
-rw-r--r--src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml99
4 files changed, 1133 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h b/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h
new file mode 100644
index 00000000..738d9e6d
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/transport/emspi/include/transport/emspi/ble_hci_emspi.h
@@ -0,0 +1,33 @@
+/*
+ * 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_HCI_EMSPI_
+#define H_BLE_HCI_EMSPI_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ble_hci_emspi_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.yml
new file mode 100644
index 00000000..5df5a5bb
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/transport/emspi/pkg.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.
+#
+
+pkg.name: nimble/transport/emspi
+pkg.description: "HCI transport using EM's HCI SPI protocol."
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+ - ble
+ - bluetooth
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - nimble
+
+pkg.apis:
+ - ble_transport
+
+pkg.init:
+ ble_hci_emspi_init: 'MYNEWT_VAL(BLE_HCI_EMSPI_SYSINIT_STAGE)'
diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c b/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c
new file mode 100644
index 00000000..61c0c9cb
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/transport/emspi/src/ble_hci_emspi.c
@@ -0,0 +1,965 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include "sysinit/sysinit.h"
+#include "syscfg/syscfg.h"
+#include "os/os_cputime.h"
+#include "bsp/bsp.h"
+#include "os/os.h"
+#include "mem/mem.h"
+#include "hal/hal_gpio.h"
+#include "hal/hal_spi.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "nimble/hci_common.h"
+#include "nimble/ble_hci_trans.h"
+
+#include "transport/emspi/ble_hci_emspi.h"
+
+#include "am_mcu_apollo.h"
+
+/***
+ * NOTES:
+ * The emspi HCI transport doesn't use event buffer priorities. All incoming
+ * and outgoing events use buffers from the same pool.
+ *
+ */
+
+#define BLE_HCI_EMSPI_PKT_EVT_COUNT \
+ (MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT) + \
+ MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT))
+
+#define BLE_HCI_EMSPI_PKT_NONE 0x00
+#define BLE_HCI_EMSPI_PKT_CMD 0x01
+#define BLE_HCI_EMSPI_PKT_ACL 0x02
+#define BLE_HCI_EMSPI_PKT_EVT 0x04
+
+#define BLE_HCI_EMSPI_CTLR_STATUS_OK 0xc0
+#define BLE_HCI_EMSPI_OP_TX 0x42
+#define BLE_HCI_EMSPI_OP_RX 0x81
+
+static os_event_fn ble_hci_emspi_event_txrx;
+
+static struct os_event ble_hci_emspi_ev_txrx = {
+ .ev_cb = ble_hci_emspi_event_txrx,
+};
+
+static struct os_eventq ble_hci_emspi_evq;
+static struct os_task ble_hci_emspi_task;
+static os_stack_t ble_hci_emspi_stack[MYNEWT_VAL(BLE_HCI_EMSPI_STACK_SIZE)];
+
+static ble_hci_trans_rx_cmd_fn *ble_hci_emspi_rx_cmd_cb;
+static void *ble_hci_emspi_rx_cmd_arg;
+
+static ble_hci_trans_rx_acl_fn *ble_hci_emspi_rx_acl_cb;
+static void *ble_hci_emspi_rx_acl_arg;
+
+static struct os_mempool ble_hci_emspi_evt_hi_pool;
+static os_membuf_t ble_hci_emspi_evt_hi_buf[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
+ MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))
+];
+
+static struct os_mempool ble_hci_emspi_evt_lo_pool;
+static os_membuf_t ble_hci_emspi_evt_lo_buf[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
+ MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE))
+];
+
+static struct os_mempool ble_hci_emspi_cmd_pool;
+static os_membuf_t ble_hci_emspi_cmd_buf[
+ OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ)
+];
+
+static struct os_mbuf_pool ble_hci_emspi_acl_mbuf_pool;
+static struct os_mempool_ext ble_hci_emspi_acl_pool;
+
+/*
+ * The MBUF payload size must accommodate the HCI data header size plus the
+ * maximum ACL data packet length. The ACL block size is the size of the
+ * mbufs we will allocate.
+ */
+#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \
+ + BLE_MBUF_MEMBLOCK_OVERHEAD \
+ + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT)
+
+static os_membuf_t ble_hci_emspi_acl_buf[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT),
+ ACL_BLOCK_SIZE)
+];
+
+/**
+ * A packet to be sent over the EMSPI. This can be a command, an event, or ACL
+ * data.
+ */
+struct ble_hci_emspi_pkt {
+ STAILQ_ENTRY(ble_hci_emspi_pkt) next;
+ void *data;
+ uint8_t type;
+};
+STAILQ_HEAD(, ble_hci_emspi_pkt) ble_hci_emspi_tx_q;
+
+static struct os_mempool ble_hci_emspi_pkt_pool;
+static os_membuf_t ble_hci_emspi_pkt_buf[
+ OS_MEMPOOL_SIZE(BLE_HCI_EMSPI_PKT_EVT_COUNT + 1 +
+ MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT),
+ sizeof (struct ble_hci_emspi_pkt))
+];
+
+static void
+ble_hci_emspi_rdy_isr(void *arg)
+{
+ os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx);
+}
+
+static void
+ble_hci_emspi_initiate_write(void)
+{
+ hal_gpio_irq_disable(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN));
+
+ /* Assert slave select. */
+ hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 0);
+
+ /* Wait for controller to indicate ready-to-receive. */
+ while (!hal_gpio_read(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN))) { }
+}
+
+static void
+ble_hci_emspi_terminate_write(void)
+{
+ const uint64_t rdy_mask =
+ AM_HAL_GPIO_BIT(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN));
+ os_sr_t sr;
+
+ am_hal_gpio_int_clear(rdy_mask);
+
+ /* Deassert slave select. */
+ hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 1);
+
+ OS_ENTER_CRITICAL(sr);
+ hal_gpio_irq_enable(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN));
+
+ if (hal_gpio_read(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN))) {
+ am_hal_gpio_int_set(rdy_mask);
+ }
+
+ OS_EXIT_CRITICAL(sr);
+}
+
+static int
+ble_hci_emspi_write_hdr(uint8_t first_byte, uint8_t *out_buf_size)
+{
+ const uint8_t hdr[2] = { first_byte, 0x00 };
+ uint8_t rx[2];
+ int rc;
+
+ /* Send command header. */
+ rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), (void *)hdr, rx, 2);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Check for "OK" status. */
+ if (rx[0] != BLE_HCI_EMSPI_CTLR_STATUS_OK) {
+ return BLE_ERR_HW_FAIL;
+ }
+
+ *out_buf_size = rx[1];
+ return 0;
+}
+
+/**
+ * Transmits a chunk of bytes to the controller.
+ */
+static int
+ble_hci_emspi_tx_chunk(const uint8_t *data, int len, int *out_bytes_txed)
+{
+ uint8_t buf_size;
+ int rc;
+
+ /* Silence spurious "may be used uninitialized" warning. */
+ *out_bytes_txed = 0;
+
+ ble_hci_emspi_initiate_write();
+
+ rc = ble_hci_emspi_write_hdr(BLE_HCI_EMSPI_OP_TX, &buf_size);
+ if (rc != 0) {
+ goto done;
+ }
+
+ if (buf_size == 0) {
+ rc = 0;
+ goto done;
+ }
+
+ if (buf_size < len) {
+ len = buf_size;
+ }
+ rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), (void *)data, NULL,
+ len);
+ if (rc != 0) {
+ goto done;
+ }
+ *out_bytes_txed = len;
+
+done:
+ ble_hci_emspi_terminate_write();
+ return rc;
+}
+
+/**
+ * Transmits a full command or ACL data packet to the controller.
+ */
+static int
+ble_hci_emspi_tx(const uint8_t *data, int len)
+{
+ int bytes_txed;
+ int rc;
+
+ while (len > 0) {
+ rc = ble_hci_emspi_tx_chunk(data, len, &bytes_txed);
+ if (rc != 0) {
+ goto done;
+ }
+
+ data += bytes_txed;
+ len -= bytes_txed;
+ }
+
+ rc = 0;
+
+done:
+ return rc;
+}
+
+/**
+ * Reads the specified number of bytes from the controller.
+ */
+static int
+ble_hci_emspi_rx(uint8_t *data, int max_len)
+{
+ uint8_t buf_size;
+ int rc;
+
+ ble_hci_emspi_initiate_write();
+
+ rc = ble_hci_emspi_write_hdr(BLE_HCI_EMSPI_OP_RX, &buf_size);
+ if (rc != 0) {
+ goto done;
+ }
+
+ if (buf_size > max_len) {
+ buf_size = max_len;
+ }
+
+ rc = hal_spi_txrx(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), NULL, data, buf_size);
+ if (rc != 0) {
+ rc = BLE_ERR_HW_FAIL;
+ goto done;
+ }
+
+done:
+ ble_hci_emspi_terminate_write();
+ return rc;
+}
+
+/**
+ * Allocates a buffer (mbuf) for ACL operation.
+ *
+ * @return The allocated buffer on success;
+ * NULL on buffer exhaustion.
+ */
+static struct os_mbuf *
+ble_hci_trans_acl_buf_alloc(void)
+{
+ uint8_t usrhdr_len;
+
+#if MYNEWT_VAL(BLE_HS_FLOW_CTRL)
+ usrhdr_len = BLE_MBUF_HS_HDR_LEN;
+#else
+ usrhdr_len = 0;
+#endif
+
+ return os_mbuf_get_pkthdr(&ble_hci_emspi_acl_mbuf_pool, usrhdr_len);
+}
+
+/**
+ * Transmits an ACL data packet to the controller. The caller relinquishes the
+ * specified mbuf, regardless of return status.
+ */
+static int
+ble_hci_emspi_acl_tx(struct os_mbuf *om)
+{
+ struct ble_hci_emspi_pkt *pkt;
+ os_sr_t sr;
+
+ /* If this packet is zero length, just free it */
+ if (OS_MBUF_PKTLEN(om) == 0) {
+ os_mbuf_free_chain(om);
+ return 0;
+ }
+
+ pkt = os_memblock_get(&ble_hci_emspi_pkt_pool);
+ if (pkt == NULL) {
+ os_mbuf_free_chain(om);
+ return BLE_ERR_MEM_CAPACITY;
+ }
+
+ pkt->type = BLE_HCI_EMSPI_PKT_ACL;
+ pkt->data = om;
+
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_INSERT_TAIL(&ble_hci_emspi_tx_q, pkt, next);
+ OS_EXIT_CRITICAL(sr);
+
+ os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx);
+
+ return 0;
+}
+
+/**
+ * Transmits a command packet to the controller. The caller relinquishes the
+ * specified buffer, regardless of return status.
+ */
+static int
+ble_hci_emspi_cmdevt_tx(uint8_t *cmd_buf, uint8_t pkt_type)
+{
+ struct ble_hci_emspi_pkt *pkt;
+ os_sr_t sr;
+
+ pkt = os_memblock_get(&ble_hci_emspi_pkt_pool);
+ if (pkt == NULL) {
+ ble_hci_trans_buf_free(cmd_buf);
+ return BLE_ERR_MEM_CAPACITY;
+ }
+
+ pkt->type = pkt_type;
+ pkt->data = cmd_buf;
+
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_INSERT_TAIL(&ble_hci_emspi_tx_q, pkt, next);
+ OS_EXIT_CRITICAL(sr);
+
+ os_eventq_put(&ble_hci_emspi_evq, &ble_hci_emspi_ev_txrx);
+
+ return 0;
+}
+
+static int
+ble_hci_emspi_tx_flat(const uint8_t *data, int len)
+{
+ int rc;
+
+ rc = ble_hci_emspi_tx(data, len);
+ return rc;
+}
+
+static int
+ble_hci_emspi_tx_pkt_type(uint8_t pkt_type)
+{
+ return ble_hci_emspi_tx_flat(&pkt_type, 1);
+}
+
+static int
+ble_hci_emspi_tx_cmd(const uint8_t *data)
+{
+ int len;
+ int rc;
+
+ rc = ble_hci_emspi_tx_pkt_type(BLE_HCI_EMSPI_PKT_CMD);
+ if (rc != 0) {
+ return rc;
+ }
+
+ len = data[2] + sizeof(struct ble_hci_cmd);
+ rc = ble_hci_emspi_tx_flat(data, len);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+ble_hci_emspi_tx_acl(struct os_mbuf *om)
+{
+ struct os_mbuf *cur;
+ int rc;
+
+ rc = ble_hci_emspi_tx_pkt_type(BLE_HCI_EMSPI_PKT_ACL);
+ if (rc != 0) {
+ return rc;
+ }
+
+ cur = om;
+ while (cur != NULL) {
+ rc = ble_hci_emspi_tx(cur->om_data, cur->om_len);
+ if (rc != 0) {
+ break;
+ }
+
+ cur = SLIST_NEXT(cur, om_next);
+ }
+
+ return rc;
+}
+
+static struct ble_hci_emspi_pkt *
+ble_hci_emspi_pull_next_tx(void)
+{
+ struct ble_hci_emspi_pkt *pkt;
+ os_sr_t sr;
+
+ OS_ENTER_CRITICAL(sr);
+ pkt = STAILQ_FIRST(&ble_hci_emspi_tx_q);
+ if (pkt != NULL) {
+ STAILQ_REMOVE(&ble_hci_emspi_tx_q, pkt, ble_hci_emspi_pkt, next);
+ }
+ OS_EXIT_CRITICAL(sr);
+
+ return pkt;
+}
+
+static int
+ble_hci_emspi_tx_pkt(void)
+{
+ struct ble_hci_emspi_pkt *pkt;
+ int rc;
+
+ pkt = ble_hci_emspi_pull_next_tx();
+ if (pkt == NULL) {
+ return -1;
+ }
+
+ switch (pkt->type) {
+ case BLE_HCI_EMSPI_PKT_CMD:
+ rc = ble_hci_emspi_tx_cmd(pkt->data);
+ ble_hci_trans_buf_free(pkt->data);
+ break;
+
+ case BLE_HCI_EMSPI_PKT_ACL:
+ rc = ble_hci_emspi_tx_acl(pkt->data);
+ os_mbuf_free_chain(pkt->data);
+ break;
+
+ default:
+ rc = -1;
+ break;
+ }
+
+ os_memblock_put(&ble_hci_emspi_pkt_pool, pkt);
+
+ return rc;
+}
+
+static int
+ble_hci_emspi_rx_evt(void)
+{
+ uint8_t *data;
+ uint8_t len;
+ int rc;
+
+ /* XXX: we should not assert if host cannot allocate an event. Need
+ * to determine what to do here.
+ */
+ data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
+ assert(data != NULL);
+
+ rc = ble_hci_emspi_rx(data, sizeof(struct ble_hci_ev));
+ if (rc != 0) {
+ goto err;
+ }
+
+ len = data[1];
+ if (len > 0) {
+ rc = ble_hci_emspi_rx(data + sizeof(struct ble_hci_ev), len);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+ assert(ble_hci_emspi_rx_cmd_cb != NULL);
+ ble_hci_emspi_rx_cmd_cb(data, ble_hci_emspi_rx_cmd_arg);
+ if (rc != 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ ble_hci_trans_buf_free(data);
+ return rc;
+}
+
+static int
+ble_hci_emspi_rx_acl(void)
+{
+ struct os_mbuf *om;
+ uint16_t len;
+ int rc;
+
+ /* XXX: we should not assert if host cannot allocate an mbuf. Need to
+ * determine what to do here.
+ */
+ om = ble_hci_trans_acl_buf_alloc();
+ assert(om != NULL);
+
+ rc = ble_hci_emspi_rx(om->om_data, BLE_HCI_DATA_HDR_SZ);
+ if (rc != 0) {
+ goto err;
+ }
+
+ len = get_le16(om->om_data + 2);
+ if (len > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) {
+ /*
+ * Data portion cannot exceed data length of acl buffer. If it does
+ * this is considered to be a loss of sync.
+ */
+ rc = BLE_ERR_UNSPECIFIED;
+ goto err;
+ }
+
+ if (len > 0) {
+ rc = ble_hci_emspi_rx(om->om_data + BLE_HCI_DATA_HDR_SZ, len);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+
+ OS_MBUF_PKTLEN(om) = BLE_HCI_DATA_HDR_SZ + len;
+ om->om_len = BLE_HCI_DATA_HDR_SZ + len;
+
+ assert(ble_hci_emspi_rx_cmd_cb != NULL);
+ rc = ble_hci_emspi_rx_acl_cb(om, ble_hci_emspi_rx_acl_arg);
+ if (rc != 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ os_mbuf_free_chain(om);
+ return rc;
+}
+
+/**
+ * @return The type of packet to follow success;
+ * -1 if there is no valid packet to receive.
+ */
+static int
+ble_hci_emspi_rx_pkt(void)
+{
+ uint8_t pkt_type;
+ int rc;
+
+ /* XXX: This is awkward; should read the full packet in "one go". */
+ rc = ble_hci_emspi_rx(&pkt_type, 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (pkt_type) {
+ case BLE_HCI_EMSPI_PKT_EVT:
+ return ble_hci_emspi_rx_evt();
+
+ case BLE_HCI_EMSPI_PKT_ACL:
+ return ble_hci_emspi_rx_acl();
+
+ default:
+ /* XXX */
+ return -1;
+ }
+}
+
+static void
+ble_hci_emspi_set_rx_cbs(ble_hci_trans_rx_cmd_fn *cmd_cb,
+ void *cmd_arg,
+ ble_hci_trans_rx_acl_fn *acl_cb,
+ void *acl_arg)
+{
+ ble_hci_emspi_rx_cmd_cb = cmd_cb;
+ ble_hci_emspi_rx_cmd_arg = cmd_arg;
+ ble_hci_emspi_rx_acl_cb = acl_cb;
+ ble_hci_emspi_rx_acl_arg = acl_arg;
+}
+
+static void
+ble_hci_emspi_free_pkt(uint8_t type, uint8_t *cmdevt, struct os_mbuf *acl)
+{
+ switch (type) {
+ case BLE_HCI_EMSPI_PKT_NONE:
+ break;
+
+ case BLE_HCI_EMSPI_PKT_CMD:
+ case BLE_HCI_EMSPI_PKT_EVT:
+ ble_hci_trans_buf_free(cmdevt);
+ break;
+
+ case BLE_HCI_EMSPI_PKT_ACL:
+ os_mbuf_free_chain(acl);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+/**
+ * Unsupported. This is a host-only transport.
+ */
+int
+ble_hci_trans_ll_evt_tx(uint8_t *cmd)
+{
+ return BLE_ERR_UNSUPPORTED;
+}
+
+/**
+ * Unsupported. This is a host-only transport.
+ */
+int
+ble_hci_trans_ll_acl_tx(struct os_mbuf *om)
+{
+ return BLE_ERR_UNSUPPORTED;
+}
+
+/**
+ * Sends an HCI command from the host to the controller.
+ *
+ * @param cmd The HCI command to send. This buffer must be
+ * allocated via ble_hci_trans_buf_alloc().
+ *
+ * @return 0 on success;
+ * A BLE_ERR_[...] error code on failure.
+ */
+int
+ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
+{
+ int rc;
+
+ rc = ble_hci_emspi_cmdevt_tx(cmd, BLE_HCI_EMSPI_PKT_CMD);
+ return rc;
+}
+
+/**
+ * Sends ACL data from host to controller.
+ *
+ * @param om The ACL data packet to send.
+ *
+ * @return 0 on success;
+ * A BLE_ERR_[...] error code on failure.
+ */
+int
+ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
+{
+ int rc;
+
+ rc = ble_hci_emspi_acl_tx(om);
+ return rc;
+}
+
+/**
+ * Configures the HCI transport to call the specified callback upon receiving
+ * HCI packets from the controller. This function should only be called by by
+ * host.
+ *
+ * @param cmd_cb The callback to execute upon receiving an HCI
+ * event.
+ * @param cmd_arg Optional argument to pass to the command
+ * callback.
+ * @param acl_cb The callback to execute upon receiving ACL
+ * data.
+ * @param acl_arg Optional argument to pass to the ACL
+ * callback.
+ */
+void
+ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
+ void *cmd_arg,
+ ble_hci_trans_rx_acl_fn *acl_cb,
+ void *acl_arg)
+{
+ ble_hci_emspi_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg);
+}
+
+/**
+ * Configures the HCI transport to operate with a host. The transport will
+ * execute specified callbacks upon receiving HCI packets from the controller.
+ *
+ * @param cmd_cb The callback to execute upon receiving an HCI
+ * event.
+ * @param cmd_arg Optional argument to pass to the command
+ * callback.
+ * @param acl_cb The callback to execute upon receiving ACL
+ * data.
+ * @param acl_arg Optional argument to pass to the ACL
+ * callback.
+ */
+void
+ble_hci_trans_cfg_ll(ble_hci_trans_rx_cmd_fn *cmd_cb,
+ void *cmd_arg,
+ ble_hci_trans_rx_acl_fn *acl_cb,
+ void *acl_arg)
+{
+ /* Unsupported. */
+ assert(0);
+}
+
+/**
+ * Allocates a flat buffer of the specified type.
+ *
+ * @param type The type of buffer to allocate; one of the
+ * BLE_HCI_TRANS_BUF_[...] constants.
+ *
+ * @return The allocated buffer on success;
+ * NULL on buffer exhaustion.
+ */
+uint8_t *
+ble_hci_trans_buf_alloc(int type)
+{
+ uint8_t *buf;
+
+ switch (type) {
+ case BLE_HCI_TRANS_BUF_CMD:
+ buf = os_memblock_get(&ble_hci_emspi_cmd_pool);
+ break;
+ case BLE_HCI_TRANS_BUF_EVT_HI:
+ buf = os_memblock_get(&ble_hci_emspi_evt_hi_pool);
+ if (buf == NULL) {
+ /* If no high-priority event buffers remain, try to grab a
+ * low-priority one.
+ */
+ buf = os_memblock_get(&ble_hci_emspi_evt_lo_pool);
+ }
+ break;
+
+ case BLE_HCI_TRANS_BUF_EVT_LO:
+ buf = os_memblock_get(&ble_hci_emspi_evt_lo_pool);
+ break;
+
+ default:
+ assert(0);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+/**
+ * Frees the specified flat buffer. The buffer must have been allocated via
+ * ble_hci_trans_buf_alloc().
+ *
+ * @param buf The buffer to free.
+ */
+void
+ble_hci_trans_buf_free(uint8_t *buf)
+{
+ int rc;
+
+ if (buf != NULL) {
+ if (os_memblock_from(&ble_hci_emspi_evt_hi_pool, buf)) {
+ rc = os_memblock_put(&ble_hci_emspi_evt_hi_pool, buf);
+ assert(rc == 0);
+ } else if (os_memblock_from(&ble_hci_emspi_evt_lo_pool, buf)) {
+ rc = os_memblock_put(&ble_hci_emspi_evt_lo_pool, buf);
+ assert(rc == 0);
+ } else {
+ assert(os_memblock_from(&ble_hci_emspi_cmd_pool, buf));
+ rc = os_memblock_put(&ble_hci_emspi_cmd_pool, buf);
+ assert(rc == 0);
+ }
+ }
+}
+
+/**
+ * Configures a callback to get executed whenever an ACL data packet is freed.
+ * The function is called in lieu of actually freeing the packet.
+ *
+ * @param cb The callback to configure.
+ *
+ * @return 0 on success.
+ */
+int
+ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg)
+{
+ ble_hci_emspi_acl_pool.mpe_put_cb = cb;
+ ble_hci_emspi_acl_pool.mpe_put_arg = arg;
+ return 0;
+}
+
+/**
+ * Resets the HCI UART transport to a clean state. Frees all buffers and
+ * reconfigures the UART.
+ *
+ * @return 0 on success;
+ * A BLE_ERR_[...] error code on failure.
+ */
+int
+ble_hci_trans_reset(void)
+{
+ struct ble_hci_emspi_pkt *pkt;
+
+ hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 1);
+
+ while ((pkt = STAILQ_FIRST(&ble_hci_emspi_tx_q)) != NULL) {
+ STAILQ_REMOVE(&ble_hci_emspi_tx_q, pkt, ble_hci_emspi_pkt, next);
+ ble_hci_emspi_free_pkt(pkt->type, pkt->data, pkt->data);
+ os_memblock_put(&ble_hci_emspi_pkt_pool, pkt);
+ }
+
+ return 0;
+}
+
+static void
+ble_hci_emspi_event_txrx(struct os_event *ev)
+{
+ int rc;
+
+ rc = ble_hci_emspi_rx_pkt();
+
+ do {
+ rc = ble_hci_emspi_tx_pkt();
+ } while (rc == 0);
+}
+
+static void
+ble_hci_emspi_loop(void *unused)
+{
+ while (1) {
+ os_eventq_run(&ble_hci_emspi_evq);
+ }
+}
+
+static void
+ble_hci_emspi_init_hw(void)
+{
+ struct hal_spi_settings spi_cfg;
+ int rc;
+
+ rc = hal_gpio_init_out(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 0);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = hal_gpio_init_out(MYNEWT_VAL(BLE_HCI_EMSPI_SS_PIN), 1);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ spi_cfg.data_order = HAL_SPI_MSB_FIRST;
+ spi_cfg.data_mode = HAL_SPI_MODE0;
+ spi_cfg.baudrate = MYNEWT_VAL(BLE_HCI_EMSPI_BAUD);
+ spi_cfg.word_size = HAL_SPI_WORD_SIZE_8BIT;
+
+ rc = hal_spi_config(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM), &spi_cfg);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = hal_gpio_irq_init(MYNEWT_VAL(BLE_HCI_EMSPI_RDY_PIN),
+ ble_hci_emspi_rdy_isr, NULL,
+ HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = hal_spi_enable(MYNEWT_VAL(BLE_HCI_EMSPI_SPI_NUM));
+ assert(rc == 0);
+
+ hal_gpio_write(MYNEWT_VAL(BLE_HCI_EMSPI_RESET_PIN), 1);
+}
+
+/**
+ * Initializes the UART HCI transport module.
+ *
+ * @return 0 on success;
+ * A BLE_ERR_[...] error code on failure.
+ */
+void
+ble_hci_emspi_init(void)
+{
+ int rc;
+
+ /* Ensure this function only gets called by sysinit. */
+ SYSINIT_ASSERT_ACTIVE();
+
+ rc = os_mempool_ext_init(&ble_hci_emspi_acl_pool,
+ MYNEWT_VAL(BLE_ACL_BUF_COUNT),
+ ACL_BLOCK_SIZE,
+ ble_hci_emspi_acl_buf,
+ "ble_hci_emspi_acl_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = os_mbuf_pool_init(&ble_hci_emspi_acl_mbuf_pool,
+ &ble_hci_emspi_acl_pool.mpe_mp,
+ ACL_BLOCK_SIZE,
+ MYNEWT_VAL(BLE_ACL_BUF_COUNT));
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ /*
+ * Create memory pool of HCI command buffers. NOTE: we currently dont
+ * allow this to be configured. The controller will only allow one
+ * outstanding command. We decided to keep this a pool in case we allow
+ * allow the controller to handle more than one outstanding command.
+ */
+ rc = os_mempool_init(&ble_hci_emspi_cmd_pool,
+ 1,
+ BLE_HCI_TRANS_CMD_SZ,
+ ble_hci_emspi_cmd_buf,
+ "ble_hci_emspi_cmd_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = os_mempool_init(&ble_hci_emspi_evt_hi_pool,
+ MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
+ MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
+ ble_hci_emspi_evt_hi_buf,
+ "ble_hci_emspi_evt_hi_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = os_mempool_init(&ble_hci_emspi_evt_lo_pool,
+ MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
+ MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
+ ble_hci_emspi_evt_lo_buf,
+ "ble_hci_emspi_evt_lo_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ /*
+ * Create memory pool of packet list nodes. NOTE: the number of these
+ * buffers should be, at least, the total number of event buffers (hi
+ * and lo), the number of command buffers (currently 1) and the total
+ * number of buffers that the controller could possibly hand to the host.
+ */
+ rc = os_mempool_init(&ble_hci_emspi_pkt_pool,
+ BLE_HCI_EMSPI_PKT_EVT_COUNT + 1 +
+ MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT),
+ sizeof (struct ble_hci_emspi_pkt),
+ ble_hci_emspi_pkt_buf,
+ "ble_hci_emspi_pkt_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ STAILQ_INIT(&ble_hci_emspi_tx_q);
+
+ ble_hci_emspi_init_hw();
+
+ /* Initialize the LL task */
+ os_eventq_init(&ble_hci_emspi_evq);
+ rc = os_task_init(&ble_hci_emspi_task, "ble_hci_emspi", ble_hci_emspi_loop,
+ NULL, MYNEWT_VAL(BLE_HCI_EMSPI_PRIO), OS_WAIT_FOREVER,
+ ble_hci_emspi_stack,
+ MYNEWT_VAL(BLE_HCI_EMSPI_STACK_SIZE));
+ SYSINIT_PANIC_ASSERT(rc == 0);
+}
diff --git a/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml
new file mode 100644
index 00000000..4751271b
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/transport/emspi/syscfg.yml
@@ -0,0 +1,99 @@
+# 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:
+ BLE_HCI_EMSPI:
+ description: 'Indicates that the emspi host HCI transport is present.'
+ value: 1
+ restrictions:
+ # XXX: This package only builds with the apollo2 MCU.
+ # MCU-dependencies need to be removed.
+ - MCU_APOLLO2
+
+ # 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
+ code to enqueue various elements. In the case of the controller
+ only HCI, this number should be equal to the number of mbufs in
+ the msys pool. For host only, it is really dependent on the
+ number of ACL buffers that the controller tells the host it
+ has.
+ value: 12
+
+ BLE_HCI_EMSPI_SPI_NUM:
+ description: The index of the SPI device to use for HCI.
+ value: 5
+
+ BLE_HCI_EMSPI_BAUD:
+ description: The SPI baud rate.
+ value: 8000000
+
+ BLE_HCI_EMSPI_RESET_PIN:
+ description: The GPIO pin that resets the EM controller.
+ value: 42
+
+ BLE_HCI_EMSPI_SS_PIN:
+ description: The GPIO pin to use for SPI slave select.
+ value: 45
+
+ BLE_HCI_EMSPI_RDY_PIN:
+ description: >
+ The GPIO pin that the EM controller uses to indicate data ready.
+ value: 26
+
+ BLE_HCI_EMSPI_PRIO:
+ description: The priority of the emspi task.
+ type: task_priority
+ value: 5
+
+ BLE_HCI_EMSPI_STACK_SIZE:
+ description: 'The size of the emspi task (units: 4-byte words).'
+ value: 256
+
+ BLE_HCI_EMSPI_SYSINIT_STAGE:
+ description: >
+ Sysinit stage for the EMSPI BLE transport.
+ value: 100
+
+syscfg.vals.BLE_EXT_ADV:
+ BLE_HCI_EVT_BUF_SIZE: 257