diff options
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/transport/uart')
4 files changed, 1330 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h b/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h new file mode 100644 index 00000000..e5e10841 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/include/transport/uart/ble_hci_uart.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_HCI_UART_ +#define H_BLE_HCI_UART_ + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_hci_uart_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml new file mode 100644 index 00000000..95681373 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: nimble/transport/uart +pkg.description: XXX +pkg.author: "Apache Mynewt <dev@mynewt.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/util/mem" + - nimble + +pkg.apis: + - ble_transport + +pkg.init: + ble_hci_uart_init: 'MYNEWT_VAL(BLE_TRANS_UART_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c b/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c new file mode 100644 index 00000000..ac6af28e --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/src/ble_hci_uart.c @@ -0,0 +1,1187 @@ +/* + * 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_CONTROLLER) +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_CONTROLLER) + 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] + + sizeof(struct ble_hci_cmd); + 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] + + sizeof(struct ble_hci_ev); + 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_CONTROLLER) +/** + * 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_CONTROLLER) + 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_CONTROLLER) + /* + * 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_CONTROLLER) +/** + * 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 < sizeof(struct ble_hci_cmd)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_cmd)) { + ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[2] + + sizeof(struct ble_hci_cmd); + } + + 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 < sizeof(struct ble_hci_cmd)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_cmd)) { + ble_hci_uart_state.rx_cmd.len = data + sizeof(struct ble_hci_cmd); + } + + 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(void) +{ + 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 < sizeof(struct ble_hci_ev)) { + return; + } + + if (ble_hci_uart_state.rx_cmd.cur == sizeof(struct ble_hci_ev)) { + ble_hci_uart_state.rx_cmd.len = ble_hci_uart_state.rx_cmd.data[1] + + sizeof(struct ble_hci_ev); + } + + 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 == sizeof(struct ble_hci_ev)) { + /* LE Meta Event parameter length is never 0 */ + assert(data != 0); + ble_hci_uart_state.rx_cmd.len = data + sizeof(struct ble_hci_ev); + 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 - sizeof(struct ble_hci_ev); + } + + 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_CONTROLLER) + 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_CONTROLLER) + 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_CONTROLLER) + 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); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml new file mode 100644 index 00000000..43486a8b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/uart/syscfg.yml @@ -0,0 +1,72 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BLE_HCI_EVT_BUF_SIZE: + description: 'The size of the allocated event buffers' + value: 70 + BLE_HCI_EVT_HI_BUF_COUNT: + description: 'The number of high priority event buffers' + value: 8 + BLE_HCI_EVT_LO_BUF_COUNT: + description: 'The number of low priority event buffers' + value: 8 + BLE_ACL_BUF_COUNT: + description: 'The number of ACL data buffers' + value: 12 + 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_UART_PORT: + description: 'The uart to use for the HCI uart interface' + value: 0 + BLE_HCI_UART_BAUD: + description: 'The baud rate of the HCI uart interface' + value: 1000000 + BLE_HCI_UART_DATA_BITS: + description: 'Number of data bits used for HCI uart interface' + value: 8 + BLE_HCI_UART_STOP_BITS: + description: 'Number of stop bits used for HCI uart interface' + value: 1 + BLE_HCI_UART_PARITY: + description: 'Parity used for HCI uart interface' + value: HAL_UART_PARITY_NONE + BLE_HCI_UART_FLOW_CTRL: + description: 'Flow control used for HCI uart interface' + value: HAL_UART_FLOW_CTL_RTS_CTS + BLE_TRANS_UART_SYSINIT_STAGE: + description: > + Sysinit stage for the UART BLE transport. + value: 500 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 |