mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-10 12:07:55 +00:00
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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user