MYNEWT-388: HCI uart should not assert if a command, event or ACL buffer not 
available

There were a number of changes committed to fix a few BLE HCI issues
1) On HCI sync loss, the controller will now send a HW error and wait
to find a reset command in the byyte stream in order to re-sync.
2) If no ACL data buffer available, the ACL packet is skipped and a
data buffer overflow event is sent.
3) If no command buffer is available, the current command is simply
skipped. No response is sent in this case. The spec was not clear
and since this should never happen we simply decided to skip the
command.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/473e050f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/473e050f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/473e050f

Branch: refs/heads/master
Commit: 473e050f7fde744ad198d9ce3234115fa5940750
Parents: dd79d93
Author: William San Filippo <wi...@runtime.io>
Authored: Fri Sep 16 16:39:43 2016 -0700
Committer: William San Filippo <wi...@runtime.io>
Committed: Fri Sep 16 16:39:43 2016 -0700

----------------------------------------------------------------------
 apps/blehci/src/main.c                          |  20 +-
 .../controller/include/controller/ble_ll.h      |  13 +
 .../controller/include/controller/ble_ll_ctrl.h |   3 +
 net/nimble/controller/src/ble_ll.c              |  53 +++
 net/nimble/controller/src/ble_ll_hci_ev.c       |  44 +++
 net/nimble/include/nimble/ble.h                 |   4 +
 net/nimble/include/nimble/hci_common.h          |   4 +
 .../uart/include/transport/uart/ble_hci_uart.h  |   4 +-
 net/nimble/transport/uart/src/ble_hci_uart.c    | 377 ++++++++++++++++---
 9 files changed, 465 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/apps/blehci/src/main.c
----------------------------------------------------------------------
diff --git a/apps/blehci/src/main.c b/apps/blehci/src/main.c
index 0dd77e8..52aab99 100755
--- a/apps/blehci/src/main.c
+++ b/apps/blehci/src/main.c
@@ -23,6 +23,7 @@
 
 /* BLE */
 #include "nimble/ble.h"
+#include "nimble/hci_common.h"
 #include "controller/ble_ll.h"
 #include "transport/uart/ble_hci_uart.h"
 
@@ -41,8 +42,6 @@ uint8_t g_dev_addr[BLE_DEV_ADDR_LEN] = { 0 };
 /* Our random address (in case we need it) */
 uint8_t g_random_addr[BLE_DEV_ADDR_LEN] = { 0 };
 
-#define HCI_MAX_BUFS        (5)
-
 os_membuf_t default_mbuf_mpool_data[MBUF_MEMPOOL_SIZE];
 
 struct os_mbuf_pool default_mbuf_pool;
@@ -51,7 +50,7 @@ struct os_mempool default_mbuf_mpool;
 int
 main(void)
 {
-    const struct ble_hci_uart_cfg *hci_cfg;
+    struct ble_hci_uart_cfg hci_cfg;
     int rc;
 
     /* Initialize OS */
@@ -74,12 +73,19 @@ main(void)
     assert(rc == 0);
 
     /* Initialize the BLE LL */
-    hci_cfg = &ble_hci_uart_cfg_dflt;
-    rc = ble_ll_init(BLE_LL_TASK_PRI, hci_cfg->num_acl_bufs,
-                     BLE_MBUF_PAYLOAD_SIZE);
+    hci_cfg = ble_hci_uart_cfg_dflt;
+    rc = ble_ll_init(BLE_LL_TASK_PRI, hci_cfg.num_acl_bufs,
+                     BLE_MBUF_PAYLOAD_SIZE - BLE_HCI_DATA_HDR_SZ);
     assert(rc == 0);
 
-    rc = ble_hci_uart_init(hci_cfg);
+    /*
+     * XXX: for now, the developer MUST configure the number of msys buffers
+     * and not rely on the default value. The number of msys buffers must
+     * be >= to the number of pool elements registered to os_msys. In this
+     * project, we only register the default mbuf pool.
+     */
+    hci_cfg.num_msys_bufs = MBUF_NUM_MBUFS;
+    rc = ble_hci_uart_init(&hci_cfg);
     assert(rc == 0);
 
     /* Start the OS */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/controller/include/controller/ble_ll.h
----------------------------------------------------------------------
diff --git a/net/nimble/controller/include/controller/ble_ll.h 
b/net/nimble/controller/include/controller/ble_ll.h
index 7865cf8..ee4fe24 100644
--- a/net/nimble/controller/include/controller/ble_ll.h
+++ b/net/nimble/controller/include/controller/ble_ll.h
@@ -76,6 +76,12 @@ struct ble_ll_obj
     /* Packet transmit queue */
     struct os_event ll_tx_pkt_ev;
     struct ble_ll_pkt_q ll_tx_pkt_q;
+
+    /* Data buffer overflow event */
+    struct os_event ll_dbuf_overflow_ev;
+
+    /* HW error callout */
+    struct os_callout_func ll_hw_err_timer;
 };
 extern struct ble_ll_obj g_ble_ll_data;
 
@@ -130,6 +136,7 @@ extern STATS_SECT_DECL(ble_ll_stats) ble_ll_stats;
 #define BLE_LL_EVENT_CONN_SPVN_TMO  (OS_EVENT_T_PERUSER + 4)
 #define BLE_LL_EVENT_CONN_EV_END    (OS_EVENT_T_PERUSER + 5)
 #define BLE_LL_EVENT_TX_PKT_IN      (OS_EVENT_T_PERUSER + 6)
+#define BLE_LL_EVENT_DBUF_OVERFLOW  (OS_EVENT_T_PERUSER + 7)
 
 /* LL Features */
 #define BLE_LL_FEAT_LE_ENCRYPTION   (0x01)
@@ -323,6 +330,12 @@ void ble_ll_acl_data_in(struct os_mbuf *txpkt);
  */
 struct os_mbuf *ble_ll_rxpdu_alloc(uint16_t len);
 
+/* Tell the Link Layer there has been a data buffer overflow */
+void ble_ll_data_buffer_overflow(void);
+
+/* Tell the link layer there has been a hardware error */
+void ble_ll_hw_error(void);
+
 /*--- PHY interfaces ---*/
 struct ble_mbuf_hdr;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/controller/include/controller/ble_ll_ctrl.h
----------------------------------------------------------------------
diff --git a/net/nimble/controller/include/controller/ble_ll_ctrl.h 
b/net/nimble/controller/include/controller/ble_ll_ctrl.h
index f652faf..1d30749 100644
--- a/net/nimble/controller/include/controller/ble_ll_ctrl.h
+++ b/net/nimble/controller/include/controller/ble_ll_ctrl.h
@@ -244,6 +244,9 @@ void ble_ll_hci_ev_rd_rem_used_feat(struct ble_ll_conn_sm 
*connsm,
 void ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, uint8_t status);
 void ble_ll_hci_ev_encrypt_chg(struct ble_ll_conn_sm *connsm, uint8_t status);
 int ble_ll_hci_ev_ltk_req(struct ble_ll_conn_sm *connsm);
+int ble_ll_hci_ev_hw_err(uint8_t hw_err);
+void ble_ll_hci_ev_databuf_overflow(void);
+
 
 void ble_ll_calc_session_key(struct ble_ll_conn_sm *connsm);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/controller/src/ble_ll.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll.c 
b/net/nimble/controller/src/ble_ll.c
index 3beb28e..117d7fa 100644
--- a/net/nimble/controller/src/ble_ll.c
+++ b/net/nimble/controller/src/ble_ll.c
@@ -716,6 +716,49 @@ ble_ll_acl_data_in(struct os_mbuf *txpkt)
 }
 
 /**
+ * Called to post event to Link Layer when a data buffer overflow has
+ * occurred.
+ *
+ * Context: Interrupt
+ *
+ */
+void
+ble_ll_data_buffer_overflow(void)
+{
+    os_eventq_put(&g_ble_ll_data.ll_evq, &g_ble_ll_data.ll_dbuf_overflow_ev);
+}
+
+/**
+ * Called when a HW error occurs.
+ *
+ * Context: Interrupt
+ */
+void
+ble_ll_hw_error(void)
+{
+    os_callout_reset(&g_ble_ll_data.ll_hw_err_timer.cf_c, 0);
+}
+
+/**
+ * Called when the HW error timer expires.
+ *
+ * @param arg
+ */
+static void
+ble_ll_hw_err_timer_cb(void *arg)
+{
+    if (ble_ll_hci_ev_hw_err(BLE_HW_ERR_HCI_SYNC_LOSS)) {
+        /*
+         * Restart callout if failed to allocate event. Try to allocate an
+         * event every 50 milliseconds (or each OS tick if a tick is longer
+         * than 100 msecs).
+         */
+        os_callout_reset(&g_ble_ll_data.ll_hw_err_timer.cf_c,
+                         OS_TICKS_PER_SEC / 20);
+    }
+}
+
+/**
  * Called upon start of received PDU
  *
  * Context: Interrupt
@@ -951,6 +994,9 @@ ble_ll_task(void *arg)
         case BLE_LL_EVENT_TX_PKT_IN:
             ble_ll_tx_pkt_in();
             break;
+        case BLE_LL_EVENT_DBUF_OVERFLOW:
+            ble_ll_hci_ev_databuf_overflow();
+            break;
         case BLE_LL_EVENT_CONN_SPVN_TMO:
             ble_ll_conn_spvn_timeout(ev->ev_arg);
             break;
@@ -1175,6 +1221,13 @@ ble_ll_init(uint8_t ll_task_prio, uint8_t num_acl_pkts, 
uint16_t acl_pkt_size)
     /* Initialize transmit (from host) and receive packet (from phy) event */
     lldata->ll_rx_pkt_ev.ev_type = BLE_LL_EVENT_RX_PKT_IN;
     lldata->ll_tx_pkt_ev.ev_type = BLE_LL_EVENT_TX_PKT_IN;
+    lldata->ll_dbuf_overflow_ev.ev_type = BLE_LL_EVENT_DBUF_OVERFLOW;
+
+    /* Initialize the HW error timer */
+    os_callout_func_init(&g_ble_ll_data.ll_hw_err_timer,
+                         &g_ble_ll_data.ll_evq,
+                         ble_ll_hw_err_timer_cb,
+                         NULL);
 
     /* Initialize wait for response timer */
     cputime_timer_init(&g_ble_ll_data.ll_wfr_timer, ble_ll_wfr_timer_exp,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/controller/src/ble_ll_hci_ev.c
----------------------------------------------------------------------
diff --git a/net/nimble/controller/src/ble_ll_hci_ev.c 
b/net/nimble/controller/src/ble_ll_hci_ev.c
index 2548bdc..8e1f468 100644
--- a/net/nimble/controller/src/ble_ll_hci_ev.c
+++ b/net/nimble/controller/src/ble_ll_hci_ev.c
@@ -222,3 +222,47 @@ ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, 
uint8_t status)
         }
     }
 }
+
+/**
+ * Send a HW error to the host.
+ *
+ * @param hw_err
+ *
+ * @return int 0: event masked or event sent, -1 otherwise
+ */
+int
+ble_ll_hci_ev_hw_err(uint8_t hw_err)
+{
+    int rc;
+    uint8_t *evbuf;
+
+    rc = 0;
+    if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_HW_ERROR)) {
+        evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
+        if (evbuf) {
+            evbuf[0] = BLE_HCI_EVCODE_HW_ERROR;
+            evbuf[1] = BLE_HCI_EVENT_HW_ERROR_LEN;
+            evbuf[2] = hw_err;
+            ble_ll_hci_event_send(evbuf);
+        } else {
+            rc = -1;
+        }
+    }
+    return rc;
+}
+
+void
+ble_ll_hci_ev_databuf_overflow(void)
+{
+    uint8_t *evbuf;
+
+    if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_DATA_BUF_OVERFLOW)) {
+        evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
+        if (evbuf) {
+            evbuf[0] = BLE_HCI_EVCODE_DATA_BUF_OVERFLOW;
+            evbuf[1] = BLE_HCI_EVENT_DATABUF_OVERFLOW_LEN;
+            evbuf[2] = BLE_HCI_EVENT_ACL_BUF_OVERFLOW;
+            ble_ll_hci_event_send(evbuf);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/include/nimble/ble.h
----------------------------------------------------------------------
diff --git a/net/nimble/include/nimble/ble.h b/net/nimble/include/nimble/ble.h
index 6c4c90e..1ad6f29 100644
--- a/net/nimble/include/nimble/ble.h
+++ b/net/nimble/include/nimble/ble.h
@@ -229,4 +229,8 @@ enum ble_error_codes
 
 int ble_err_from_os(int os_err);
 
+/* HW error codes */
+#define BLE_HW_ERR_DO_NOT_USE           (0) /* XXX: reserve this one for now */
+#define BLE_HW_ERR_HCI_SYNC_LOSS        (1)
+
 #endif /* H_BLE_ */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/include/nimble/hci_common.h
----------------------------------------------------------------------
diff --git a/net/nimble/include/nimble/hci_common.h 
b/net/nimble/include/nimble/hci_common.h
index f094cc7..5bf7156 100644
--- a/net/nimble/include/nimble/hci_common.h
+++ b/net/nimble/include/nimble/hci_common.h
@@ -500,6 +500,10 @@
 /* Read remote version informaton */
 #define BLE_HCI_EVENT_RD_RM_VER_LEN         (8)
 
+/* Data buffer overflow event */
+#define BLE_HCI_EVENT_DATABUF_OVERFLOW_LEN  (1)
+#define BLE_HCI_EVENT_ACL_BUF_OVERFLOW      (0x01)
+
 /* Advertising report */
 #define BLE_HCI_ADV_RPT_EVTYPE_ADV_IND      (0)
 #define BLE_HCI_ADV_RPT_EVTYPE_DIR_IND      (1)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/transport/uart/include/transport/uart/ble_hci_uart.h
----------------------------------------------------------------------
diff --git a/net/nimble/transport/uart/include/transport/uart/ble_hci_uart.h 
b/net/nimble/transport/uart/include/transport/uart/ble_hci_uart.h
index de84ada..7b6a817 100644
--- a/net/nimble/transport/uart/include/transport/uart/ble_hci_uart.h
+++ b/net/nimble/transport/uart/include/transport/uart/ble_hci_uart.h
@@ -3,9 +3,11 @@
 
 struct ble_hci_uart_cfg {
     uint32_t baud;
-    uint16_t num_evt_bufs;
+    uint16_t num_evt_hi_bufs;
+    uint16_t num_evt_lo_bufs;
     uint16_t evt_buf_sz;
     uint16_t num_acl_bufs;
+    uint16_t num_msys_bufs;
     uint8_t uart_port;
     uint8_t flow_ctrl;
     uint8_t data_bits;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/473e050f/net/nimble/transport/uart/src/ble_hci_uart.c
----------------------------------------------------------------------
diff --git a/net/nimble/transport/uart/src/ble_hci_uart.c 
b/net/nimble/transport/uart/src/ble_hci_uart.c
index 8cf5b9e..663c817 100755
--- a/net/nimble/transport/uart/src/ble_hci_uart.c
+++ b/net/nimble/transport/uart/src/ble_hci_uart.c
@@ -36,17 +36,31 @@
 
 #include "transport/uart/ble_hci_uart.h"
 
+/* XXX: for now, define this here */
+#ifdef FEATURE_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
+
 /***
- * NOTE:
+ * NOTES:
  * The UART HCI transport doesn't use event buffer priorities.  All incoming
- * and outgoing events and commands use buffers from the same pool.
+ * 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.
  */
-
 #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
 
 /** Default configuration. */
 const struct ble_hci_uart_cfg ble_hci_uart_cfg_dflt = {
@@ -57,8 +71,11 @@ const struct ble_hci_uart_cfg ble_hci_uart_cfg_dflt = {
     .stop_bits = 1,
     .parity = HAL_UART_PARITY_NONE,
 
-    .num_evt_bufs = 8,
-    .evt_buf_sz = BLE_HCI_TRANS_CMD_SZ,
+    .num_evt_hi_bufs = 8,
+    .num_evt_lo_bufs = 8,
+
+    /* The largest event the nimble controller will send is 70 bytes. */
+    .evt_buf_sz = 70,
 
     .num_acl_bufs = 4
 };
@@ -69,8 +86,13 @@ 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_pool;
-static void *ble_hci_uart_evt_buf;
+static struct os_mempool ble_hci_uart_evt_hi_pool;
+static void *ble_hci_uart_evt_hi_buf;
+static struct os_mempool ble_hci_uart_evt_lo_pool;
+static void *ble_hci_uart_evt_lo_buf;
+
+static struct os_mempool ble_hci_uart_cmd_pool;
+static void *ble_hci_uart_cmd_buf;
 
 static struct os_mempool ble_hci_uart_pkt_pool;
 static void *ble_hci_uart_pkt_buf;
@@ -93,7 +115,9 @@ struct ble_hci_uart_cmd {
  */
 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 */
 };
 
 /**
@@ -124,6 +148,7 @@ static struct {
 } ble_hci_uart_state;
 
 static struct ble_hci_uart_cfg ble_hci_uart_cfg;
+static uint16_t ble_hci_uart_max_acl_datalen;
 
 /**
  * Allocates a buffer (mbuf) for ACL operation.
@@ -286,6 +311,26 @@ ble_hci_uart_tx_char(void *arg)
     return rc;
 }
 
+#ifdef FEATURE_BLE_DEVICE
+/**
+ * HCI uart sync loss.
+ *
+ * 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.
@@ -293,26 +338,31 @@ ble_hci_uart_tx_char(void *arg)
 static int
 ble_hci_uart_rx_pkt_type(uint8_t data)
 {
+    struct os_mbuf *m;
+
     ble_hci_uart_state.rx_type = data;
 
-    /* XXX: For now we assert that buffer allocation succeeds.  The correct
-     * thing to do is return -1 on allocation failure so that flow control is
-     * engaged.  Then, we will need to tell the UART to start receiving again
-     * as follows:
-     *     o flat buf: when we free a buffer.
-     *     o mbuf: periodically? (which task executes the callout?)
-     */
     switch (ble_hci_uart_state.rx_type) {
+    /* Host should never receive a command! */
+#ifdef FEATURE_BLE_DEVICE
     case BLE_HCI_UART_H4_CMD:
-        ble_hci_uart_state.rx_cmd.data =
-            ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
-        assert(ble_hci_uart_state.rx_cmd.data != NULL);
-
         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 */
+#ifdef FEATURE_BLE_HOST
     case BLE_HCI_UART_H4_EVT:
+        /*
+         * XXX: we should not assert if host cannot allocate an event. Need
+         * to determine what to do here.
+         */
         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);
@@ -320,22 +370,97 @@ ble_hci_uart_rx_pkt_type(uint8_t data)
         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.buf = ble_hci_trans_acl_buf_alloc();
-        assert(ble_hci_uart_state.rx_acl.buf != NULL);
-
         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:
+#ifdef FEATURE_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;
-        return -1;
+#endif
+        break;
     }
 
     return 0;
 }
 
+#ifdef FEATURE_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)
 {
@@ -364,6 +489,33 @@ ble_hci_uart_rx_cmd(uint8_t data)
 }
 
 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
+
+#ifdef FEATURE_BLE_HOST
+static void
 ble_hci_uart_rx_evt(uint8_t data)
 {
     int rc;
@@ -389,53 +541,111 @@ ble_hci_uart_rx_evt(uint8_t data)
         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;
 
-    os_mbuf_append(ble_hci_uart_state.rx_acl.buf, &data, 1);
-
-    pktlen = OS_MBUF_PKTLEN(ble_hci_uart_state.rx_acl.buf);
+    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 (pktlen < BLE_HCI_DATA_HDR_SZ) {
+    if (rxd_bytes < BLE_HCI_DATA_HDR_SZ) {
         return;
     }
 
-    if (pktlen == BLE_HCI_DATA_HDR_SZ) {
-        os_mbuf_copydata(ble_hci_uart_state.rx_acl.buf, 2,
-                         sizeof(ble_hci_uart_state.rx_acl.len),
-                         &ble_hci_uart_state.rx_acl.len);
-        ble_hci_uart_state.rx_acl.len =
-            le16toh(&ble_hci_uart_state.rx_acl.len) + BLE_HCI_DATA_HDR_SZ;
+    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 > ble_hci_uart_max_acl_datalen) {
+            os_mbuf_free_chain(ble_hci_uart_state.rx_acl.buf);
+            ble_hci_uart_sync_lost();
+        }
     }
 
-    if (pktlen == ble_hci_uart_state.rx_acl.len) {
-        assert(ble_hci_uart_rx_cmd_cb != NULL);
+    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 */
+#ifdef FEATURE_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);
+#ifdef FEATURE_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
+#ifdef FEATURE_BLE_HOST
     case BLE_HCI_UART_H4_EVT:
         ble_hci_uart_rx_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:
-        return -1;
+        /* This should never happen! */
+        assert(0);
+        return 0;
     }
 }
 
@@ -476,14 +686,20 @@ ble_hci_uart_free_pkt(uint8_t type, uint8_t *cmdevt, 
struct os_mbuf *acl)
 static void
 ble_hci_uart_free_mem(void)
 {
-    free(ble_hci_uart_evt_buf);
-    ble_hci_uart_evt_buf = NULL;
+    free(ble_hci_uart_evt_hi_buf);
+    ble_hci_uart_evt_hi_buf = NULL;
+
+    free(ble_hci_uart_evt_lo_buf);
+    ble_hci_uart_evt_lo_buf = NULL;
 
     free(ble_hci_uart_pkt_buf);
     ble_hci_uart_pkt_buf = NULL;
 
     free(ble_hci_uart_acl_buf);
     ble_hci_uart_acl_buf = NULL;
+
+    free(ble_hci_uart_cmd_buf);
+    ble_hci_uart_cmd_buf = NULL;
 }
 
 static int
@@ -642,9 +858,20 @@ ble_hci_trans_buf_alloc(int type)
 
     switch (type) {
     case BLE_HCI_TRANS_BUF_CMD:
-    case BLE_HCI_TRANS_BUF_EVT_LO:
+        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_pool);
+        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:
@@ -666,8 +893,25 @@ ble_hci_trans_buf_free(uint8_t *buf)
 {
     int rc;
 
-    rc = os_memblock_put(&ble_hci_uart_evt_pool, buf);
-    assert(rc == 0);
+    /*
+     * 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);
+    }
 }
 
 /**
@@ -737,10 +981,15 @@ ble_hci_uart_init(const struct ble_hci_uart_cfg *cfg)
     /*
      * XXX: For now, we will keep the ACL buffer size such that it can
      * accommodate BLE_MBUF_PAYLOAD_SIZE. It should be possible to make this
-     * user defined but more testing would need to be done in that case.
+     * user defined but more testing would need to be done in that case. The
+     * MBUF payload size must accommodate the HCI data header size plus the
+     * maximum ACL data packet length.
+     *
+     * XXX: Should the max acl data length be part of config?
      */
     acl_block_size = BLE_MBUF_PAYLOAD_SIZE + BLE_MBUF_MEMBLOCK_OVERHEAD;
     acl_block_size = OS_ALIGN(acl_block_size, OS_ALIGNMENT);
+    ble_hci_uart_max_acl_datalen = BLE_MBUF_PAYLOAD_SIZE - BLE_HCI_DATA_HDR_SZ;
     rc = mem_malloc_mempool(&ble_hci_uart_acl_pool,
                             cfg->num_acl_bufs,
                             acl_block_size,
@@ -755,21 +1004,51 @@ ble_hci_uart_init(const struct ble_hci_uart_cfg *cfg)
                            acl_block_size, cfg->num_acl_bufs);
     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 = mem_malloc_mempool(&ble_hci_uart_cmd_pool,
+                            1,
+                            BLE_HCI_TRANS_CMD_SZ,
+                            "ble_hci_uart_cmd_pool",
+                            &ble_hci_uart_cmd_buf);
+    if (rc != 0) {
+        rc = ble_err_from_os(rc);
+        goto err;
+    }
 
-    /* Create memory pool of HCI command / event buffers */
-    rc = mem_malloc_mempool(&ble_hci_uart_evt_pool,
-                            cfg->num_evt_bufs,
+    rc = mem_malloc_mempool(&ble_hci_uart_evt_hi_pool,
+                            cfg->num_evt_hi_bufs,
                             cfg->evt_buf_sz,
-                            "ble_hci_uart_evt_pool",
-                            &ble_hci_uart_evt_buf);
+                            "ble_hci_uart_evt_hi_pool",
+                            &ble_hci_uart_evt_hi_buf);
     if (rc != 0) {
         rc = ble_err_from_os(rc);
         goto err;
     }
 
-    /* Create memory pool of packet list nodes. */
+    rc = mem_malloc_mempool(&ble_hci_uart_evt_lo_pool,
+                            cfg->num_evt_lo_bufs,
+                            cfg->evt_buf_sz,
+                            "ble_hci_uart_evt_lo_pool",
+                            &ble_hci_uart_evt_lo_buf);
+    if (rc != 0) {
+        rc = ble_err_from_os(rc);
+        goto err;
+    }
+
+    /*
+     * 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_uart_pkt_pool,
-                            cfg->num_evt_bufs,
+                            cfg->num_evt_hi_bufs + cfg->num_evt_lo_bufs + 1 +
+                            cfg->num_msys_bufs,
                             sizeof (struct ble_hci_uart_pkt),
                             "ble_hci_uart_pkt_pool",
                             &ble_hci_uart_pkt_buf);

Reply via email to