summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2016-03-23 16:06:01 +0200
committerJohan Hedberg <johan.hedberg@intel.com>2016-05-10 20:58:20 +0300
commit88fcb6f8f42e9479ec7d3dacd45b7f9fd15e1e95 (patch)
tree75024c87a6f0a94fcdc43cb22947ec89d125d8ce
parentfc5d42d50c883d338a89e9982a11fb9fc0ab1c9a (diff)
apps/blehci: Add application for exposing HCI over UART
Add an application that exposes the HCI over UART using the H:4 transport protocol.
-rw-r--r--apps/blehci/pkg.yml29
-rwxr-xr-xapps/blehci/src/main.c392
2 files changed, 421 insertions, 0 deletions
diff --git a/apps/blehci/pkg.yml b/apps/blehci/pkg.yml
new file mode 100644
index 00000000..f36e383f
--- /dev/null
+++ b/apps/blehci/pkg.yml
@@ -0,0 +1,29 @@
+# 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: apps/blehci
+pkg.type: app
+pkg.description: BLE controller application exposing HCI over UART
+pkg.author: "Johan Hedberg <johan.hedberg@intel.com>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - libs/os
+ - net/nimble/controller
+ - libs/baselibc
+ - libs/console/stub
diff --git a/apps/blehci/src/main.c b/apps/blehci/src/main.c
new file mode 100755
index 00000000..ca2e39d9
--- /dev/null
+++ b/apps/blehci/src/main.c
@@ -0,0 +1,392 @@
+/**
+ * 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 "bsp/bsp.h"
+#include "os/os.h"
+#include "bsp/bsp.h"
+#include "hal/hal_gpio.h"
+#include "hal/hal_cputime.h"
+#include "hal/hal_uart.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/nimble_opt.h"
+#include "nimble/hci_transport.h"
+#include "controller/ble_ll.h"
+
+#define HCI_UART_SPEED 1000000
+#define HCI_UART CONSOLE_UART
+
+/* Nimble task priorities */
+#define BLE_LL_TASK_PRI (OS_TASK_PRI_HIGHEST)
+
+/* Create a mbuf pool of BLE mbufs */
+#define MBUF_NUM_MBUFS (7)
+#define MBUF_BUF_SIZE OS_ALIGN(BLE_MBUF_PAYLOAD_SIZE, 4)
+#define MBUF_MEMBLOCK_SIZE (MBUF_BUF_SIZE + BLE_MBUF_MEMBLOCK_OVERHEAD)
+#define MBUF_MEMPOOL_SIZE OS_MEMPOOL_SIZE(MBUF_NUM_MBUFS, MBUF_MEMBLOCK_SIZE)
+
+/* Our global device address (public) */
+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)
+
+#define HCI_CMD_BUF_SIZE (260)
+struct os_mempool g_hci_cmd_pool;
+static void *hci_cmd_buf;
+
+#define HCI_OS_EVENT_BUF_SIZE (sizeof(struct os_event))
+
+#define BLE_HOST_HCI_EVENT_CTLR_EVENT (OS_EVENT_T_PERUSER + 0)
+#define BLE_HOST_HCI_EVENT_CTLR_DATA (OS_EVENT_T_PERUSER + 1)
+
+struct os_mempool g_hci_os_event_pool;
+static void *hci_os_event_buf;
+
+os_membuf_t default_mbuf_mpool_data[MBUF_MEMPOOL_SIZE];
+
+struct os_mbuf_pool default_mbuf_pool;
+struct os_mempool default_mbuf_mpool;
+
+#define H4_NONE 0x00
+#define H4_CMD 0x01
+#define H4_ACL 0x02
+#define H4_SCO 0x03
+#define H4_EVT 0x04
+
+#define HCI_CMD_HDR_LEN 3
+#define HCI_ACL_HDR_LEN 4
+#define HCI_EVT_HDR_LEN 2
+
+struct memblock {
+ uint8_t *data; /* Pointer to memblock data */
+ uint16_t cur; /* Number of bytes read/written */
+ uint16_t len; /* Total number of bytes to read/write */
+};
+
+struct tx_acl {
+ struct os_mbuf *buf; /* Buffer containing the data */
+ uint16_t len; /* Target size when buf is considered complete */
+};
+
+static struct {
+ /* State of data from host to controller */
+ uint8_t tx_type; /* Pending packet type. 0 means nothing pending */
+ union {
+ struct memblock tx_cmd;
+ struct tx_acl tx_acl;
+ };
+
+ /* State of data from controller to host */
+ uint8_t rx_type; /* Pending packet type. 0 means nothing pending */
+ union {
+ struct memblock rx_evt;
+ struct os_mbuf *rx_acl;
+ };
+ STAILQ_HEAD(, os_event) rx_pkts; /* Packet queue to send to UART */
+} hci;
+
+int
+ble_hs_rx_data(struct os_mbuf *om)
+{
+ struct os_event *ev;
+ os_sr_t sr;
+
+ ev = os_memblock_get(&g_hci_os_event_pool);
+ if (!ev) {
+ os_mbuf_free_chain(om);
+ return -1;
+ }
+
+ ev->ev_type = BLE_HOST_HCI_EVENT_CTLR_DATA;
+ ev->ev_arg = om;
+ ev->ev_queued = 1;
+
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_INSERT_TAIL(&hci.rx_pkts, ev, ev_next);
+ OS_EXIT_CRITICAL(sr);
+
+ hal_uart_start_tx(HCI_UART);
+
+ return 0;
+}
+
+int
+ble_hci_transport_ctlr_event_send(uint8_t *hci_ev)
+{
+ struct os_event *ev;
+ os_sr_t sr;
+
+ ev = os_memblock_get(&g_hci_os_event_pool);
+ if (!ev) {
+ os_error_t err;
+
+ err = os_memblock_put(&g_hci_cmd_pool, hci_ev);
+ assert(err == OS_OK);
+
+ return -1;
+ }
+
+ ev->ev_type = BLE_HOST_HCI_EVENT_CTLR_EVENT;
+ ev->ev_arg = hci_ev;
+ ev->ev_queued = 1;
+
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_INSERT_TAIL(&hci.rx_pkts, ev, ev_next);
+ OS_EXIT_CRITICAL(sr);
+
+ hal_uart_start_tx(HCI_UART);
+
+ return 0;
+}
+
+static int
+uart_tx_pkt_type(void)
+{
+ struct os_event *ev;
+ os_sr_t sr;
+ int rc;
+
+ OS_ENTER_CRITICAL(sr);
+
+ ev = STAILQ_FIRST(&hci.rx_pkts);
+ if (!ev) {
+ OS_EXIT_CRITICAL(sr);
+ return -1;
+ }
+
+ STAILQ_REMOVE(&hci.rx_pkts, ev, os_event, ev_next);
+ ev->ev_queued = 0;
+
+ OS_EXIT_CRITICAL(sr);
+
+ switch (ev->ev_type) {
+ case BLE_HOST_HCI_EVENT_CTLR_EVENT:
+ hci.rx_type = H4_EVT;
+ hci.rx_evt.data = ev->ev_arg;
+ hci.rx_evt.cur = 0;
+ hci.rx_evt.len = hci.rx_evt.data[1] + HCI_EVT_HDR_LEN;
+ rc = H4_EVT;
+ break;
+ case BLE_HOST_HCI_EVENT_CTLR_DATA:
+ hci.rx_type = H4_ACL;
+ hci.rx_acl = ev->ev_arg;
+ rc = H4_ACL;
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ os_memblock_put(&g_hci_os_event_pool, ev);
+
+ return rc;
+}
+
+static int
+uart_tx_char(void *arg)
+{
+ int rc = -1;
+
+ switch (hci.rx_type) {
+ case H4_NONE: /* No pending packet, pick one from the queue */
+ rc = uart_tx_pkt_type();
+ break;
+ case H4_EVT:
+ rc = hci.rx_evt.data[hci.rx_evt.cur++];
+
+ if (hci.rx_evt.cur == hci.rx_evt.len) {
+ os_memblock_put(&g_hci_cmd_pool, hci.rx_evt.data);
+ hci.rx_type = H4_NONE;
+ }
+
+ break;
+ case H4_ACL:
+ rc = *OS_MBUF_DATA(hci.rx_acl, uint8_t *);
+ os_mbuf_adj(hci.rx_acl, 1);
+ if (!OS_MBUF_PKTLEN(hci.rx_acl)) {
+ os_mbuf_free_chain(hci.rx_acl);
+ hci.rx_type = H4_NONE;
+ }
+
+ break;
+ }
+
+ return rc;
+}
+
+static int
+uart_rx_pkt_type(uint8_t data)
+{
+ hci.tx_type = data;
+
+ switch (hci.tx_type) {
+ case H4_CMD:
+ hci.tx_cmd.data = os_memblock_get(&g_hci_cmd_pool);
+ hci.tx_cmd.len = 0;
+ hci.tx_cmd.cur = 0;
+ break;
+ case H4_ACL:
+ hci.tx_acl.buf = os_msys_get_pkthdr(HCI_ACL_HDR_LEN, 0);
+ hci.tx_acl.len = 0;
+ break;
+ default:
+ hci.tx_type = H4_NONE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+uart_rx_cmd(uint8_t data)
+{
+ hci.tx_cmd.data[hci.tx_cmd.cur++] = data;
+
+ if (hci.tx_cmd.cur < HCI_CMD_HDR_LEN) {
+ return 0;
+ } else if (hci.tx_cmd.cur == HCI_CMD_HDR_LEN) {
+ hci.tx_cmd.len = hci.tx_cmd.data[2] + HCI_CMD_HDR_LEN;
+ }
+
+ if (hci.tx_cmd.cur == hci.tx_cmd.len) {
+ ble_hci_transport_host_cmd_send(hci.tx_cmd.data);
+ hci.tx_type = H4_NONE;
+ }
+
+ return 0;
+}
+
+static int
+uart_rx_acl(uint8_t data)
+{
+ os_mbuf_append(hci.tx_acl.buf, &data, 1);
+
+ if (OS_MBUF_PKTLEN(hci.tx_acl.buf) < HCI_ACL_HDR_LEN) {
+ return 0;
+ } else if (OS_MBUF_PKTLEN(hci.tx_acl.buf) == HCI_ACL_HDR_LEN) {
+ os_mbuf_copydata(hci.tx_acl.buf, 2, sizeof(hci.tx_acl.len),
+ &hci.tx_acl.len);
+ hci.tx_acl.len = le16toh(&hci.tx_acl.len) + HCI_ACL_HDR_LEN;
+ }
+
+ if (OS_MBUF_PKTLEN(hci.tx_acl.buf) == hci.tx_acl.len) {
+ ble_hci_transport_host_acl_data_send(hci.tx_acl.buf);
+ hci.tx_type = H4_NONE;
+ }
+
+ return 0;
+}
+
+static int
+uart_rx_char(void *arg, uint8_t data)
+{
+ switch (hci.tx_type) {
+ case H4_NONE:
+ return uart_rx_pkt_type(data);
+ case H4_CMD:
+ return uart_rx_cmd(data);
+ case H4_ACL:
+ return uart_rx_acl(data);
+ default:
+ return -1;
+ }
+}
+
+static int
+uart_init(void)
+{
+ int rc;
+
+ memset(&hci, 0, sizeof(hci));
+
+ STAILQ_INIT(&hci.rx_pkts);
+
+ rc = hal_uart_init_cbs(HCI_UART, uart_tx_char, NULL, uart_rx_char, NULL);
+ if (rc) {
+ return rc;
+ }
+
+ return hal_uart_config(HCI_UART, HCI_UART_SPEED, 8, 1, HAL_UART_PARITY_NONE,
+ HAL_UART_FLOW_CTL_RTS_CTS);
+}
+
+int
+main(void)
+{
+ int rc;
+
+ /* Initialize OS */
+ os_init();
+
+ /* Set cputime to count at 1 usec increments */
+ rc = cputime_init(1000000);
+ assert(rc == 0);
+
+ rc = os_mempool_init(&default_mbuf_mpool, MBUF_NUM_MBUFS,
+ MBUF_MEMBLOCK_SIZE, default_mbuf_mpool_data,
+ "default_mbuf_data");
+ assert(rc == 0);
+
+ rc = os_mbuf_pool_init(&default_mbuf_pool, &default_mbuf_mpool,
+ MBUF_MEMBLOCK_SIZE, MBUF_NUM_MBUFS);
+ assert(rc == 0);
+
+ rc = os_msys_register(&default_mbuf_pool);
+ assert(rc == 0);
+
+ /* Initialize the BLE LL */
+ rc = ble_ll_init(BLE_LL_TASK_PRI, MBUF_NUM_MBUFS, BLE_MBUF_PAYLOAD_SIZE);
+ assert(rc == 0);
+
+ hci_cmd_buf = malloc(OS_MEMPOOL_BYTES(HCI_MAX_BUFS, HCI_CMD_BUF_SIZE));
+ assert(hci_cmd_buf != NULL);
+
+ /* Create memory pool of command buffers */
+ rc = os_mempool_init(&g_hci_cmd_pool, HCI_MAX_BUFS, HCI_CMD_BUF_SIZE,
+ hci_cmd_buf, "HCICmdPool");
+ assert(rc == 0);
+
+ hci_os_event_buf = malloc(OS_MEMPOOL_BYTES(HCI_MAX_BUFS,
+ HCI_OS_EVENT_BUF_SIZE));
+ assert(hci_os_event_buf != NULL);
+
+ /* Create memory pool of OS events */
+ rc = os_mempool_init(&g_hci_os_event_pool, HCI_MAX_BUFS,
+ HCI_OS_EVENT_BUF_SIZE, hci_os_event_buf,
+ "HCIOsEventPool");
+ assert(rc == 0);
+
+ rc = uart_init();
+ assert(rc == 0);
+
+ /* Start the OS */
+ os_start();
+
+ /* os start should never return. If it does, this should be an error */
+ assert(0);
+
+ return 0;
+}