blob: dceeb3477c6f9710850cd9e2fedd021ac06610cc [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_uart.h"
/* BLE */
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "nimble/hci_common.h"
#include "nimble/ble_hci_trans.h"
#include "transport/uart/ble_hci_uart.h"
#define BLE_HCI_UART_EVT_COUNT \
(MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT) + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT))
/***
* NOTES:
* The UART HCI transport doesn't use event buffer priorities. All incoming
* and outgoing events use buffers from the same pool.
*
* The "skip" definitions are here so that when buffers cannot be allocated,
* the command or acl packets are simply skipped so that the HCI interface
* does not lose synchronization and resets dont (necessarily) occur.
*/
/* XXX: for now, define this here */
#if MYNEWT_VAL(BLE_DEVICE)
extern void ble_ll_data_buffer_overflow(void);
extern void ble_ll_hw_error(uint8_t err);
static const uint8_t ble_hci_uart_reset_cmd[4] = { 0x01, 0x03, 0x0C, 0x00 };
#endif
/***
* NOTES:
* The "skip" definitions are here so that when buffers cannot be allocated,
* the command or acl packets are simply skipped so that the HCI interface
* does not lose synchronization and resets dont (necessarily) occur.
*/
#define BLE_HCI_UART_H4_NONE 0x00
#define BLE_HCI_UART_H4_CMD 0x01
#define BLE_HCI_UART_H4_ACL 0x02
#define BLE_HCI_UART_H4_SCO 0x03
#define BLE_HCI_UART_H4_EVT 0x04
#define BLE_HCI_UART_H4_SYNC_LOSS 0x80
#define BLE_HCI_UART_H4_SKIP_CMD 0x81
#define BLE_HCI_UART_H4_SKIP_ACL 0x82
#define BLE_HCI_UART_H4_LE_EVT 0x83
#define BLE_HCI_UART_H4_SKIP_EVT 0x84
static ble_hci_trans_rx_cmd_fn *ble_hci_uart_rx_cmd_cb;
static void *ble_hci_uart_rx_cmd_arg;
static ble_hci_trans_rx_acl_fn *ble_hci_uart_rx_acl_cb;
static void *ble_hci_uart_rx_acl_arg;
static struct os_mempool ble_hci_uart_evt_hi_pool;
static os_membuf_t ble_hci_uart_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_uart_evt_lo_pool;
static os_membuf_t ble_hci_uart_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_uart_cmd_pool;
static os_membuf_t ble_hci_uart_cmd_buf[
OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ)
];
static struct os_mbuf_pool ble_hci_uart_acl_mbuf_pool;
static struct os_mempool_ext ble_hci_uart_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_uart_acl_buf[
OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT),
ACL_BLOCK_SIZE)
];
/**
* A packet to be sent over the UART. This can be a command, an event, or ACL
* data.
*/
struct ble_hci_uart_pkt {
STAILQ_ENTRY(ble_hci_uart_pkt) next;
void *data;
uint8_t type;
};
static struct os_mempool ble_hci_uart_pkt_pool;
static os_membuf_t ble_hci_uart_pkt_buf[
OS_MEMPOOL_SIZE(BLE_HCI_UART_EVT_COUNT + 1 +
MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT),
sizeof (struct ble_hci_uart_pkt))
];
/**
* An incoming or outgoing command or event.
*/
struct ble_hci_uart_cmd {
uint8_t *data; /* Pointer to ble_hci_uart_cmd data */
uint16_t cur; /* Number of bytes read/written */
uint16_t len; /* Total number of bytes to read/write */
};
/**
* An incoming ACL data packet.
*/
struct ble_hci_uart_acl {
struct os_mbuf *buf; /* Buffer containing the data */
uint8_t *dptr; /* Pointer to where bytes should be placed */
uint16_t len; /* Target size when buf is considered complete */
uint16_t rxd_bytes; /* current count of bytes received for packet */
};
/**
* Structure for transmitting ACL packets over UART
*
*/
struct ble_hci_uart_h4_acl_tx
{
uint8_t *dptr;
struct os_mbuf *tx_acl;
};
static struct {
/*** State of data received over UART. */
uint8_t rx_type; /* Pending packet type. 0 means nothing pending */
union {
struct ble_hci_uart_cmd rx_cmd;
struct ble_hci_uart_acl rx_acl;
};
/*** State of data transmitted over UART. */
uint8_t tx_type; /* Pending packet type. 0 means nothing pending */
uint16_t rem_tx_len; /* Used for acl tx only currently */
union {
struct ble_hci_uart_cmd tx_cmd;
struct ble_hci_uart_h4_acl_tx tx_pkt;
};
STAILQ_HEAD(, ble_hci_uart_pkt) tx_pkts; /* Packet queue to send to UART */
} ble_hci_uart_state;
/**
* 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;
uint8_t usrhdr_len;
#if MYNEWT_VAL(BLE_DEVICE)
usrhdr_len = sizeof(struct ble_mbuf_hdr);
#elif MYNEWT_VAL(BLE_HS_FLOW_CTRL)
usrhdr_len = BLE_MBUF_HS_HDR_LEN;
#else
usrhdr_len = 0;
#endif
m = os_mbuf_get_pkthdr(&ble_hci_uart_acl_mbuf_pool, usrhdr_len);
return m;
}
static int
ble_hci_uart_acl_tx(struct os_mbuf *om)
{
struct ble_hci_uart_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_uart_pkt_pool);
if (pkt == NULL) {
os_mbuf_free_chain(om);
return BLE_ERR_MEM_CAPACITY;
}
pkt->type = BLE_HCI_UART_H4_ACL;
pkt->data = om;
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_TAIL(&ble_hci_uart_state.tx_pkts, pkt, next);
OS_EXIT_CRITICAL(sr);
hal_uart_start_tx(MYNEWT_VAL(BLE_HCI_UART_PORT));
return 0;
}
static int
ble_hci_uart_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type)
{
struct ble_hci_uart_pkt *pkt;
os_sr_t sr;
pkt = os_memblock_get(&ble_hci_uart_pkt_pool);
if (pkt == NULL) {
ble_hci_trans_buf_free(hci_ev);
return BLE_ERR_MEM_CAPACITY;
}
pkt->type = h4_type;
pkt->data = hci_ev;
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_TAIL(&ble_hci_uart_state.tx_pkts, pkt, next);
OS_EXIT_CRITICAL(sr);
hal_uart_start_tx(MYNEWT_VAL(BLE_HCI_UART_PORT));
return 0;
}
/**
* @return The packet type to transmit on success;
* -1 if there is nothing to send.
*/
static int
ble_hci_uart_tx_pkt_type(void)
{
struct ble_hci_uart_pkt *pkt;
struct os_mbuf *om;
os_sr_t sr;
int rc;
OS_ENTER_CRITICAL(sr);
pkt = STAILQ_FIRST(&ble_hci_uart_state.tx_pkts);
if (!pkt) {
OS_EXIT_CRITICAL(sr);
return -1;
}
STAILQ_REMOVE(&ble_hci_uart_state.tx_pkts, pkt, ble_hci_uart_pkt, next);
OS_EXIT_CRITICAL(sr);
rc = pkt->type;
switch (pkt->type) {
case BLE_HCI_UART_H4_CMD:
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_CMD;
ble_hci_uart_state.tx_cmd.data = pkt->data;
ble_hci_uart_state.tx_cmd.cur = 0;
ble_hci_uart_state.tx_cmd.len = ble_hci_uart_state.tx_cmd.data[2] +
BLE_HCI_CMD_HDR_LEN;
break;
case BLE_HCI_UART_H4_EVT:
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_EVT;
ble_hci_uart_state.tx_cmd.data = pkt->data;
ble_hci_uart_state.tx_cmd.cur = 0;
ble_hci_uart_state.tx_cmd.len = ble_hci_uart_state.tx_cmd.data[1] +
BLE_HCI_EVENT_HDR_LEN;
break;
case BLE_HCI_UART_H4_ACL:
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_ACL;
om = (struct os_mbuf *)pkt->data;
/* NOTE: first mbuf must have non-zero length */
os_mbuf_trim_front(om);
ble_hci_uart_state.tx_pkt.tx_acl = om;
ble_hci_uart_state.tx_pkt.dptr = om->om_data;
ble_hci_uart_state.rem_tx_len = OS_MBUF_PKTLEN(om);
break;
default:
rc = -1;
break;
}
os_memblock_put(&ble_hci_uart_pkt_pool, pkt);
return rc;
}
/**
* @return The byte to transmit on success;
* -1 if there is nothing to send.
*/
static int
ble_hci_uart_tx_char(void *arg)
{
uint8_t u8;
int rc;
struct os_mbuf *om;
switch (ble_hci_uart_state.tx_type) {
case BLE_HCI_UART_H4_NONE: /* No pending packet, pick one from the queue */
rc = ble_hci_uart_tx_pkt_type();
break;
case BLE_HCI_UART_H4_CMD:
case BLE_HCI_UART_H4_EVT:
rc = ble_hci_uart_state.tx_cmd.data[ble_hci_uart_state.tx_cmd.cur++];
if (ble_hci_uart_state.tx_cmd.cur == ble_hci_uart_state.tx_cmd.len) {
ble_hci_trans_buf_free(ble_hci_uart_state.tx_cmd.data);
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE;
}
break;
case BLE_HCI_UART_H4_ACL:
/* Copy the first unsent byte from the tx buffer and remove it from the
* source.
*/
u8 = ble_hci_uart_state.tx_pkt.dptr[0];
--ble_hci_uart_state.rem_tx_len;
if (ble_hci_uart_state.rem_tx_len == 0) {
os_mbuf_free_chain(ble_hci_uart_state.tx_pkt.tx_acl);
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE;
} else {
om = ble_hci_uart_state.tx_pkt.tx_acl;
--om->om_len;
if (om->om_len == 0) {
/* Remove and free any zero mbufs */
while ((om != NULL) && (om->om_len == 0)) {
ble_hci_uart_state.tx_pkt.tx_acl = SLIST_NEXT(om, om_next);
os_mbuf_free(om);
om = ble_hci_uart_state.tx_pkt.tx_acl;
}
/* NOTE: om should never be NULL! What to do? */
if (om == NULL) {
assert(0);
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE;
} else {
ble_hci_uart_state.tx_pkt.dptr = om->om_data;
}
} else {
ble_hci_uart_state.tx_pkt.dptr++;
}
}
rc = u8;
break;
default:
rc = -1;
break;
}
return rc;
}
#if MYNEWT_VAL(BLE_DEVICE)
/**
* HCI uart sync lost.
*
* This occurs when the controller receives an invalid packet type or a length
* field that is out of range. The controller needs to send a HW error to the
* host and wait to find a LL reset command.
*/
static void
ble_hci_uart_sync_lost(void)
{
ble_hci_uart_state.rx_cmd.len = 0;
ble_hci_uart_state.rx_cmd.cur = 0;
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
ble_ll_hw_error(BLE_HW_ERR_HCI_SYNC_LOSS);
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SYNC_LOSS;
}
#endif
/**
* @return The type of packet to follow success;
* -1 if there is no valid packet to receive.
*/
static int
ble_hci_uart_rx_pkt_type(uint8_t data)
{
struct os_mbuf *m;
ble_hci_uart_state.rx_type = data;
switch (ble_hci_uart_state.rx_type) {
/* Host should never receive a command! */
#if MYNEWT_VAL(BLE_DEVICE)
case BLE_HCI_UART_H4_CMD:
ble_hci_uart_state.rx_cmd.len = 0;
ble_hci_uart_state.rx_cmd.cur = 0;
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
if (ble_hci_uart_state.rx_cmd.data == NULL) {
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_CMD;
}
break;
#endif
/* Controller should never receive an event */
#if MYNEWT_VAL(BLE_HOST)
case BLE_HCI_UART_H4_EVT:
/*
* The event code is unknown at the moment. Depending on event priority,
* buffer *shall* be allocated from ble_hci_uart_evt_hi_pool
* or "may* be allocated from ble_hci_uart_evt_lo_pool.
* Thus do not allocate the buffer yet.
*/
ble_hci_uart_state.rx_cmd.data = NULL;
ble_hci_uart_state.rx_cmd.len = 0;
ble_hci_uart_state.rx_cmd.cur = 0;
break;
#endif
case BLE_HCI_UART_H4_ACL:
ble_hci_uart_state.rx_acl.len = 0;
ble_hci_uart_state.rx_acl.rxd_bytes = 0;
m = ble_hci_trans_acl_buf_alloc();
if (m) {
ble_hci_uart_state.rx_acl.dptr = m->om_data;
} else {
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_ACL;
}
ble_hci_uart_state.rx_acl.buf = m;
break;
default:
#if MYNEWT_VAL(BLE_DEVICE)
/*
* If we receive an unknown HCI packet type this is considered a loss
* of sync.
*/
ble_hci_uart_sync_lost();
#else
/*
* XXX: not sure what to do about host in this case. Just go back to
* none for now.
*/
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
#endif
break;
}
return 0;
}
#if MYNEWT_VAL(BLE_DEVICE)
/**
* HCI uart sync loss.
*
* Find a LL reset command in the byte stream. The LL reset command is a
* sequence of 4 bytes:
* 0x01 HCI Packet Type = HCI CMD
* 0x03 OCF for reset command
* 0x0C OGF for reset command (0x03 shifted left by two bits as the OGF
* occupies the uopper 6 bits of this byte.
* 0x00 Parameter length of reset command (no parameters).
*
* @param data Byte received over serial port
*/
void
ble_hci_uart_rx_sync_loss(uint8_t data)
{
int rc;
int index;
/*
* If we couldnt allocate a command buffer (should not occur but
* possible) try to allocate one on each received character. If we get
* a reset and buffer is not available we have to ignore reset.
*/
if (ble_hci_uart_state.rx_cmd.data == NULL) {
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
}
index = ble_hci_uart_state.rx_cmd.cur;
if (data == ble_hci_uart_reset_cmd[index]) {
if (index == 3) {
if (ble_hci_uart_state.rx_cmd.data == NULL) {
index = 0;
} else {
assert(ble_hci_uart_rx_cmd_cb != NULL);
ble_hci_uart_state.rx_cmd.data[0] = 0x03;
ble_hci_uart_state.rx_cmd.data[1] = 0x0C;
ble_hci_uart_state.rx_cmd.data[2] = 0x00;
rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data,
ble_hci_uart_rx_cmd_arg);
if (rc != 0) {
ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data);
}
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
} else {
++index;
}
} else {
index = 0;
}
ble_hci_uart_state.rx_cmd.cur = index;
}
static void
ble_hci_uart_rx_cmd(uint8_t data)
{
int rc;
ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur++] = data;
if (ble_hci_uart_state.rx_cmd.cur < BLE_HCI_CMD_HDR_LEN) {
return;
}
if (ble_hci_uart_state.rx_cmd.cur == BLE_HCI_CMD_HDR_LEN) {
ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[2] +
BLE_HCI_CMD_HDR_LEN;
}
if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) {
assert(ble_hci_uart_rx_cmd_cb != NULL);
rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data,
ble_hci_uart_rx_cmd_arg);
if (rc != 0) {
ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data);
}
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
static void
ble_hci_uart_rx_skip_cmd(uint8_t data)
{
ble_hci_uart_state.rx_cmd.cur++;
if (ble_hci_uart_state.rx_cmd.cur < BLE_HCI_CMD_HDR_LEN) {
return;
}
if (ble_hci_uart_state.rx_cmd.cur == BLE_HCI_CMD_HDR_LEN) {
ble_hci_uart_state.rx_cmd.len = data + BLE_HCI_CMD_HDR_LEN;
}
if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) {
/*
* XXX: for now we simply skip the command and do nothing. This
* should not happen but at least we retain HCI synch. The host
* can decide what to do in this case. It may be appropriate for
* the controller to attempt to send back a command complete or
* command status in this case.
*/
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
#endif
#if MYNEWT_VAL(BLE_HOST)
static inline void
ble_hci_uart_rx_evt_cb()
{
int rc;
if (ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) {
assert(ble_hci_uart_rx_cmd_cb != NULL);
rc = ble_hci_uart_rx_cmd_cb(ble_hci_uart_state.rx_cmd.data,
ble_hci_uart_rx_cmd_arg);
if (rc != 0) {
ble_hci_trans_buf_free(ble_hci_uart_state.rx_cmd.data);
}
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
static void
ble_hci_uart_rx_evt(uint8_t data)
{
/* Determine event priority to allocate buffer */
if (!ble_hci_uart_state.rx_cmd.data) {
/* In case of LE Meta Event priority might be still unknown */
if (data == BLE_HCI_EVCODE_LE_META) {
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_LE_EVT;
ble_hci_uart_state.rx_cmd.cur++;
return;
}
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
assert(ble_hci_uart_state.rx_cmd.data != NULL);
}
ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur++] = data;
if (ble_hci_uart_state.rx_cmd.cur < BLE_HCI_EVENT_HDR_LEN) {
return;
}
if (ble_hci_uart_state.rx_cmd.cur == BLE_HCI_EVENT_HDR_LEN) {
ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[1] +
BLE_HCI_EVENT_HDR_LEN;
}
ble_hci_uart_rx_evt_cb();
}
static void
ble_hci_uart_rx_le_evt(uint8_t data)
{
ble_hci_uart_state.rx_cmd.cur++;
if (ble_hci_uart_state.rx_cmd.cur == BLE_HCI_EVENT_HDR_LEN) {
/* LE Meta Event parameter length is never 0 */
assert(data != 0);
ble_hci_uart_state.rx_cmd.len = data + BLE_HCI_EVENT_HDR_LEN;
return;
}
/* Determine event priority to allocate buffer */
if (!ble_hci_uart_state.rx_cmd.data) {
/* Determine event priority to allocate buffer */
if (data == BLE_HCI_LE_SUBEV_ADV_RPT ||
data == BLE_HCI_LE_SUBEV_EXT_ADV_RPT) {
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
if (ble_hci_uart_state.rx_cmd.data == NULL) {
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_SKIP_EVT;
return;
}
} else {
ble_hci_uart_state.rx_cmd.data =
ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
assert(ble_hci_uart_state.rx_cmd.data != NULL);
}
ble_hci_uart_state.rx_cmd.data[0] = BLE_HCI_EVCODE_LE_META;
ble_hci_uart_state.rx_cmd.data[1] =
ble_hci_uart_state.rx_cmd.len - BLE_HCI_EVENT_HDR_LEN;
}
ble_hci_uart_state.rx_cmd.data[ble_hci_uart_state.rx_cmd.cur - 1] = data;
ble_hci_uart_rx_evt_cb();
}
static void
ble_hci_uart_rx_skip_evt(uint8_t data)
{
if (++ble_hci_uart_state.rx_cmd.cur == ble_hci_uart_state.rx_cmd.len) {
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
#endif
static void
ble_hci_uart_rx_acl(uint8_t data)
{
uint16_t rxd_bytes;
uint16_t pktlen;
rxd_bytes = ble_hci_uart_state.rx_acl.rxd_bytes;
ble_hci_uart_state.rx_acl.dptr[rxd_bytes] = data;
++rxd_bytes;
ble_hci_uart_state.rx_acl.rxd_bytes = rxd_bytes;
if (rxd_bytes < BLE_HCI_DATA_HDR_SZ) {
return;
}
if (rxd_bytes == BLE_HCI_DATA_HDR_SZ) {
pktlen = ble_hci_uart_state.rx_acl.dptr[3];
pktlen = (pktlen << 8) + ble_hci_uart_state.rx_acl.dptr[2];
ble_hci_uart_state.rx_acl.len = pktlen + BLE_HCI_DATA_HDR_SZ;
/*
* Data portion cannot exceed data length of acl buffer. If it does
* this is considered to be a loss of sync.
*/
if (pktlen > MYNEWT_VAL(BLE_ACL_BUF_SIZE)) {
os_mbuf_free_chain(ble_hci_uart_state.rx_acl.buf);
#if MYNEWT_VAL(BLE_DEVICE)
ble_hci_uart_sync_lost();
#else
/*
* XXX: not sure what to do about host in this case. Just go back to
* none for now.
*/
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
#endif
}
}
if (rxd_bytes == ble_hci_uart_state.rx_acl.len) {
assert(ble_hci_uart_rx_acl_cb != NULL);
/* XXX: can this callback fail? What if it does? */
OS_MBUF_PKTLEN(ble_hci_uart_state.rx_acl.buf) = rxd_bytes;
ble_hci_uart_state.rx_acl.buf->om_len = rxd_bytes;
ble_hci_uart_rx_acl_cb(ble_hci_uart_state.rx_acl.buf,
ble_hci_uart_rx_acl_arg);
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
static void
ble_hci_uart_rx_skip_acl(uint8_t data)
{
uint16_t rxd_bytes;
uint16_t pktlen;
rxd_bytes = ble_hci_uart_state.rx_acl.rxd_bytes;
++rxd_bytes;
ble_hci_uart_state.rx_acl.rxd_bytes = rxd_bytes;
if (rxd_bytes == (BLE_HCI_DATA_HDR_SZ - 1)) {
ble_hci_uart_state.rx_acl.len = data;
return;
}
if (rxd_bytes == BLE_HCI_DATA_HDR_SZ) {
pktlen = data;
pktlen = (pktlen << 8) + ble_hci_uart_state.rx_acl.len;
ble_hci_uart_state.rx_acl.len = pktlen + BLE_HCI_DATA_HDR_SZ;
}
if (rxd_bytes == ble_hci_uart_state.rx_acl.len) {
/* XXX: I dont like this but for now this denotes controller only */
#if MYNEWT_VAL(BLE_DEVICE)
ble_ll_data_buffer_overflow();
#endif
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
}
}
static int
ble_hci_uart_rx_char(void *arg, uint8_t data)
{
switch (ble_hci_uart_state.rx_type) {
case BLE_HCI_UART_H4_NONE:
return ble_hci_uart_rx_pkt_type(data);
#if MYNEWT_VAL(BLE_DEVICE)
case BLE_HCI_UART_H4_CMD:
ble_hci_uart_rx_cmd(data);
return 0;
case BLE_HCI_UART_H4_SKIP_CMD:
ble_hci_uart_rx_skip_cmd(data);
return 0;
case BLE_HCI_UART_H4_SYNC_LOSS:
ble_hci_uart_rx_sync_loss(data);
return 0;
#endif
#if MYNEWT_VAL(BLE_HOST)
case BLE_HCI_UART_H4_EVT:
ble_hci_uart_rx_evt(data);
return 0;
case BLE_HCI_UART_H4_LE_EVT:
ble_hci_uart_rx_le_evt(data);
return 0;
case BLE_HCI_UART_H4_SKIP_EVT:
ble_hci_uart_rx_skip_evt(data);
return 0;
#endif
case BLE_HCI_UART_H4_ACL:
ble_hci_uart_rx_acl(data);
return 0;
case BLE_HCI_UART_H4_SKIP_ACL:
ble_hci_uart_rx_skip_acl(data);
return 0;
default:
/* This should never happen! */
assert(0);
return 0;
}
}
static void
ble_hci_uart_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_uart_rx_cmd_cb = cmd_cb;
ble_hci_uart_rx_cmd_arg = cmd_arg;
ble_hci_uart_rx_acl_cb = acl_cb;
ble_hci_uart_rx_acl_arg = acl_arg;
}
static void
ble_hci_uart_free_pkt(uint8_t type, uint8_t *cmdevt, struct os_mbuf *acl)
{
switch (type) {
case BLE_HCI_UART_H4_NONE:
break;
case BLE_HCI_UART_H4_CMD:
case BLE_HCI_UART_H4_EVT:
ble_hci_trans_buf_free(cmdevt);
break;
case BLE_HCI_UART_H4_ACL:
os_mbuf_free_chain(acl);
break;
default:
assert(0);
break;
}
}
static int
ble_hci_uart_config(void)
{
int rc;
rc = hal_uart_init_cbs(MYNEWT_VAL(BLE_HCI_UART_PORT),
ble_hci_uart_tx_char, NULL,
ble_hci_uart_rx_char, NULL);
if (rc != 0) {
return BLE_ERR_UNSPECIFIED;
}
rc = hal_uart_config(MYNEWT_VAL(BLE_HCI_UART_PORT),
MYNEWT_VAL(BLE_HCI_UART_BAUD),
MYNEWT_VAL(BLE_HCI_UART_DATA_BITS),
MYNEWT_VAL(BLE_HCI_UART_STOP_BITS),
MYNEWT_VAL(BLE_HCI_UART_PARITY),
MYNEWT_VAL(BLE_HCI_UART_FLOW_CTRL));
if (rc != 0) {
return BLE_ERR_HW_FAIL;
}
return 0;
}
/**
* 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)
{
int rc;
rc = ble_hci_uart_cmdevt_tx(cmd, BLE_HCI_UART_H4_EVT);
return rc;
}
/**
* 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)
{
int rc;
rc = ble_hci_uart_acl_tx(om);
return rc;
}
/**
* 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_uart_cmdevt_tx(cmd, BLE_HCI_UART_H4_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_uart_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_uart_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)
{
ble_hci_uart_set_rx_cbs(cmd_cb, cmd_arg, acl_cb, acl_arg);
}
/**
* 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_uart_cmd_pool);
break;
case BLE_HCI_TRANS_BUF_EVT_HI:
buf = os_memblock_get(&ble_hci_uart_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_uart_evt_lo_pool);
}
break;
case BLE_HCI_TRANS_BUF_EVT_LO:
buf = os_memblock_get(&ble_hci_uart_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;
/*
* XXX: this may look a bit odd, but the controller uses the command
* buffer to send back the command complete/status as an immediate
* response to the command. This was done to insure that the controller
* could always send back one of these events when a command was received.
* Thus, we check to see which pool the buffer came from so we can free
* it to the appropriate pool
*/
if (os_memblock_from(&ble_hci_uart_evt_hi_pool, buf)) {
rc = os_memblock_put(&ble_hci_uart_evt_hi_pool, buf);
assert(rc == 0);
} else if (os_memblock_from(&ble_hci_uart_evt_lo_pool, buf)) {
rc = os_memblock_put(&ble_hci_uart_evt_lo_pool, buf);
assert(rc == 0);
} else {
assert(os_memblock_from(&ble_hci_uart_cmd_pool, buf));
rc = os_memblock_put(&ble_hci_uart_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_uart_acl_pool.mpe_put_cb = cb;
ble_hci_uart_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_uart_pkt *pkt;
int rc;
/* Close the UART to prevent race conditions as the buffers are freed. */
rc = hal_uart_close(MYNEWT_VAL(BLE_HCI_UART_PORT));
if (rc != 0) {
return BLE_ERR_HW_FAIL;
}
ble_hci_uart_free_pkt(ble_hci_uart_state.rx_type,
ble_hci_uart_state.rx_cmd.data,
ble_hci_uart_state.rx_acl.buf);
ble_hci_uart_state.rx_type = BLE_HCI_UART_H4_NONE;
ble_hci_uart_free_pkt(ble_hci_uart_state.tx_type,
ble_hci_uart_state.tx_cmd.data,
ble_hci_uart_state.tx_pkt.tx_acl);
ble_hci_uart_state.tx_type = BLE_HCI_UART_H4_NONE;
while ((pkt = STAILQ_FIRST(&ble_hci_uart_state.tx_pkts)) != NULL) {
STAILQ_REMOVE(&ble_hci_uart_state.tx_pkts, pkt, ble_hci_uart_pkt,
next);
ble_hci_uart_free_pkt(pkt->type, pkt->data, pkt->data);
os_memblock_put(&ble_hci_uart_pkt_pool, pkt);
}
/* Reopen the UART. */
rc = ble_hci_uart_config();
if (rc != 0) {
return rc;
}
return 0;
}
/**
* Initializes the UART HCI transport module.
*
* @return 0 on success;
* A BLE_ERR_[...] error code on failure.
*/
void
ble_hci_uart_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = os_mempool_ext_init(&ble_hci_uart_acl_pool,
MYNEWT_VAL(BLE_ACL_BUF_COUNT),
ACL_BLOCK_SIZE,
ble_hci_uart_acl_buf,
"ble_hci_uart_acl_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
rc = os_mbuf_pool_init(&ble_hci_uart_acl_mbuf_pool,
&ble_hci_uart_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_uart_cmd_pool,
1,
BLE_HCI_TRANS_CMD_SZ,
ble_hci_uart_cmd_buf,
"ble_hci_uart_cmd_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
rc = os_mempool_init(&ble_hci_uart_evt_hi_pool,
MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT),
MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
ble_hci_uart_evt_hi_buf,
"ble_hci_uart_evt_hi_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
rc = os_mempool_init(&ble_hci_uart_evt_lo_pool,
MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT),
MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE),
ble_hci_uart_evt_lo_buf,
"ble_hci_uart_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_uart_pkt_pool,
BLE_HCI_UART_EVT_COUNT + 1 +
MYNEWT_VAL(BLE_HCI_ACL_OUT_COUNT),
sizeof (struct ble_hci_uart_pkt),
ble_hci_uart_pkt_buf,
"ble_hci_uart_pkt_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
rc = ble_hci_uart_config();
SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring UART HCI");
memset(&ble_hci_uart_state, 0, sizeof ble_hci_uart_state);
STAILQ_INIT(&ble_hci_uart_state.tx_pkts);
}