BLE HCI transport - emspi

This is an HCI transport that works over SPI.  It uses EM's SPI
protocol, as described in the EM9304 data sheet:
http://www.emmicroelectronic.com/sites/default/files/public/products/datasheets/9304-ds.pdf

Currently only the host side is supported.

In addition, this package only builds when the MCU is an APOLLO 2.  This
is due to a dependency on the ambiq SDK.

X-Original-Commit: e2889f9acf4f4faf7816e1906842f9a31035f8bf
This commit is contained in:
Christopher Collins
2017-11-09 21:53:56 -08:00
parent 9726391cde
commit bfffc40b32
4 changed files with 1096 additions and 0 deletions
@@ -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
+36
View File
@@ -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: net/nimble/transport/emspi
pkg.description: "HCI transport using EM's HCI SPI protocol."
pkg.author: "Apache Mynewt <[email protected]>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
pkg.deps:
- net/nimble
- kernel/os
pkg.apis:
- ble_transport
pkg.init:
ble_hci_emspi_init: 100
+935
View File
@@ -0,0 +1,935 @@
/*
* 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
/**
* A packet to be sent over the UART. 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 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 void *ble_hci_emspi_evt_hi_buf;
static struct os_mempool ble_hci_emspi_evt_lo_pool;
static void *ble_hci_emspi_evt_lo_buf;
static struct os_mempool ble_hci_emspi_cmd_pool;
static void *ble_hci_emspi_cmd_buf;
static struct os_mempool ble_hci_emspi_pkt_pool;
static void *ble_hci_emspi_pkt_buf;
static struct os_mbuf_pool ble_hci_emspi_acl_mbuf_pool;
static struct os_mempool ble_hci_emspi_acl_pool;
static void *ble_hci_emspi_acl_buf;
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;
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) {
*out_bytes_txed = 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)
{
struct os_mbuf *m;
/*
* XXX: note that for host only there would be no need to allocate
* a user header. Address this later.
*/
m = os_mbuf_get_pkthdr(&ble_hci_emspi_acl_mbuf_pool,
sizeof(struct ble_mbuf_hdr));
return m;
}
/**
* 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;
}
}
/**
* Sends an HCI event from the controller to the host.
*
* @param cmd The HCI event 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_ll_evt_tx(uint8_t *cmd)
{
return BLE_ERR_UNSUPPORTED;
}
/**
* Sends ACL data from controller to host.
*
* @param om The ACL data packet to send.
*
* @return 0 on success;
* A BLE_ERR_[...] error code on failure.
*/
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)
{
/* XXX: Unimplemented. */
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);
}
}
}
/**
* 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 acl_block_size;
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
/*
* 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.
*/
acl_block_size = MYNEWT_VAL(BLE_ACL_BUF_SIZE) +
BLE_MBUF_MEMBLOCK_OVERHEAD +
BLE_HCI_DATA_HDR_SZ;
acl_block_size = OS_ALIGN(acl_block_size, OS_ALIGNMENT);
rc = mem_malloc_mempool(&ble_hci_emspi_acl_pool,
MYNEWT_VAL(BLE_ACL_BUF_COUNT),
acl_block_size,
"ble_hci_emspi_acl_pool",
&ble_hci_emspi_acl_buf);
SYSINIT_PANIC_ASSERT(rc == 0);
rc = os_mbuf_pool_init(&ble_hci_emspi_acl_mbuf_pool,
&ble_hci_emspi_acl_pool, acl_block_size,
MYNEWT_VAL(BLE_ACL_BUF_COUNT));
SYSINIT_PANIC_ASSERT(rc == 0);
rc = mem_malloc_mempool(&ble_hci_emspi_cmd_pool,
1,
BLE_HCI_TRANS_CMD_SZ,
"ble_hci_emspi_cmd_pool",
&ble_hci_emspi_cmd_buf);
SYSINIT_PANIC_ASSERT(rc == 0);
rc = mem_malloc_mempool(&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_pool",
&ble_hci_emspi_evt_hi_buf);
SYSINIT_PANIC_ASSERT(rc == 0);
rc = mem_malloc_mempool(&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_pool",
&ble_hci_emspi_evt_lo_buf);
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 = mem_malloc_mempool(&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_pool",
&ble_hci_emspi_pkt_buf);
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);
}
+92
View File
@@ -0,0 +1,92 @@
# 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.
#
# Package: net/nimble/transport/ram
syscfg.defs:
BLE_HCI_EMSPI:
description: 'Indicates that the emspi HCI transport is present.'
value: 1
restrictions:
# XXX: This package only builds with the apollo2 MCU.
# MCU-dependencies need to be removed.
- MCU_APOLLO2
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.
value: 5
BLE_HCI_EMSPI_STACK_SIZE:
description: 'The size of the emspi task (units: 4-byte words).'
value: 256
syscfg.vals.BLE_EXT_ADV:
BLE_HCI_EVT_BUF_SIZE: 274