blob: 2c1f951ddcbb04f5bc32bf6b66dc1ffd2712f9ef [file] [log] [blame]
/*
* 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] + BLE_HCI_CMD_HDR_LEN;
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, BLE_HCI_EVENT_HDR_LEN);
if (rc != 0) {
goto err;
}
len = data[1];
if (len > 0) {
rc = ble_hci_emspi_rx(data + BLE_HCI_EVENT_HDR_LEN, 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);
}