From bdc10744fb338ae197692713a0b48a7ccc36f566 Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 26 Apr 2020 10:25:59 +0200 Subject: Add Nimble in libs directory --- .../socket/include/socket/ble_hci_socket.h | 34 + .../mynewt-nimble/nimble/transport/socket/pkg.yml | 38 + .../nimble/transport/socket/src/ble_hci_socket.c | 886 +++++++++++++++++++++ .../nimble/transport/socket/syscfg.yml | 79 ++ 4 files changed, 1037 insertions(+) create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c create mode 100644 src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml (limited to 'src/libs/mynewt-nimble/nimble/transport/socket') diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h b/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h new file mode 100644 index 00000000..d17074f5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/include/socket/ble_hci_socket.h @@ -0,0 +1,34 @@ +/* + * 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 _BLE_HCI_SOCKET_H_ +#define _BLE_HCI_SOCKET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct os_eventq; +void ble_hci_sock_set_evq(struct os_eventq *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml b/src/libs/mynewt-nimble/nimble/transport/socket/pkg.yml new file mode 100644 index 00000000..fb4144d3 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/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/socket +pkg.description: Provides HCI transport over socket interface +pkg.author: "Apache Mynewt " +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_sock_init: 'MYNEWT_VAL(BLE_SOCK_CLI_SYSINIT_STAGE)' diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c b/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c new file mode 100644 index 00000000..8bf56f39 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/src/ble_hci_socket.c @@ -0,0 +1,886 @@ +/* + * 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. + */ + +/* + * Provides HCI transport over sockets. Either over + * TCP sockets, or Linux bluetooth socket. + * + * E.g. to connect to TCP target, start in another window + * socat -x PIPE:/dev/ttyACM1 TCP4-LISTEN:14433,fork,reuseaddr + * When building this package, set BLE_SOCK_USE_TCP=1 and + * BLE_SOCK_TCP_PORT controls the target port this transport + * connects to. + * + * To use Linux Bluetooth sockets, create an interface: + * sudo hciattach -r -n /dev/ttyUSB0 any 57600 + * And build this package with BLE_SOCK_USE_LINUX_BLUE=1, + * BLE_SOCK_LINUX_DEV= + * + */ +#include "syscfg/syscfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +#include +#include +#include +#endif + +#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +#include +#define BTPROTO_HCI 1 +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIGETDEVLIST _IOR('H', 210, int) + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#endif + +#include +#include + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "mem/mem.h" + +#include "stats/stats.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "nimble/nimble_npl.h" +#include "nimble/ble_hci_trans.h" +#include "socket/ble_hci_socket.h" + +/*** + * 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. + */ + +STATS_SECT_START(hci_sock_stats) + STATS_SECT_ENTRY(imsg) + STATS_SECT_ENTRY(icmd) + STATS_SECT_ENTRY(ievt) + STATS_SECT_ENTRY(iacl) + STATS_SECT_ENTRY(ibytes) + STATS_SECT_ENTRY(ierr) + STATS_SECT_ENTRY(imem) + STATS_SECT_ENTRY(omsg) + STATS_SECT_ENTRY(oacl) + STATS_SECT_ENTRY(ocmd) + STATS_SECT_ENTRY(oevt) + STATS_SECT_ENTRY(obytes) + STATS_SECT_ENTRY(oerr) +STATS_SECT_END + +STATS_SECT_DECL(hci_sock_stats) hci_sock_stats; +STATS_NAME_START(hci_sock_stats) + STATS_NAME(hci_sock_stats, imsg) + STATS_NAME(hci_sock_stats, icmd) + STATS_NAME(hci_sock_stats, ievt) + STATS_NAME(hci_sock_stats, iacl) + STATS_NAME(hci_sock_stats, ibytes) + STATS_NAME(hci_sock_stats, ierr) + STATS_NAME(hci_sock_stats, imem) + STATS_NAME(hci_sock_stats, omsg) + STATS_NAME(hci_sock_stats, oacl) + STATS_NAME(hci_sock_stats, ocmd) + STATS_NAME(hci_sock_stats, oevt) + STATS_NAME(hci_sock_stats, obytes) + STATS_NAME(hci_sock_stats, oerr) +STATS_NAME_END(hci_sock_stats) + +/*** + * 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 + +#if MYNEWT + +#define BLE_SOCK_STACK_SIZE \ + OS_STACK_ALIGN(MYNEWT_VAL(BLE_SOCK_STACK_SIZE)) + +struct os_task ble_sock_task; + +#endif + +static struct os_mempool ble_hci_sock_evt_hi_pool; +static os_membuf_t ble_hci_sock_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_sock_evt_lo_pool; +static os_membuf_t ble_hci_sock_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_sock_cmd_pool; +static os_membuf_t ble_hci_sock_cmd_buf[ + OS_MEMPOOL_SIZE(1, BLE_HCI_TRANS_CMD_SZ) +]; + +static struct os_mempool ble_hci_sock_acl_pool; +static struct os_mbuf_pool ble_hci_sock_acl_mbuf_pool; + +#define ACL_BLOCK_SIZE OS_ALIGN(MYNEWT_VAL(BLE_ACL_BUF_SIZE) \ + + BLE_MBUF_MEMBLOCK_OVERHEAD \ + + BLE_HCI_DATA_HDR_SZ, OS_ALIGNMENT) +/* + * 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. + */ + +static os_membuf_t ble_hci_sock_acl_buf[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE) +]; + +static ble_hci_trans_rx_cmd_fn *ble_hci_sock_rx_cmd_cb; +static void *ble_hci_sock_rx_cmd_arg; +static ble_hci_trans_rx_acl_fn *ble_hci_sock_rx_acl_cb; +static void *ble_hci_sock_rx_acl_arg; + +static struct ble_hci_sock_state { + int sock; + struct ble_npl_eventq evq; + struct ble_npl_event ev; + struct ble_npl_callout timer; + + uint16_t rx_off; + uint8_t rx_data[512]; +} ble_hci_sock_state; + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_TCP_PORT); +#elif MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +static int s_ble_hci_device = MYNEWT_VAL(BLE_SOCK_LINUX_DEV); +#endif + +/** + * Allocates a buffer (mbuf) for ACL operation. + * + * @return The allocated buffer on success; + * NULL on buffer exhaustion. + */ +static struct os_mbuf * +ble_hci_trans_acl_buf_alloc(void) +{ + struct os_mbuf *m; + + /* + * XXX: note that for host only there would be no need to allocate + * a user header. Address this later. + */ + m = os_mbuf_get_pkthdr(&ble_hci_sock_acl_mbuf_pool, + sizeof(struct ble_mbuf_hdr)); + return m; +} + +static int +ble_hci_sock_acl_tx(struct os_mbuf *om) +{ + struct msghdr msg; + struct iovec iov[8]; + int i; + struct os_mbuf *m; + uint8_t ch; + + memset(&msg, 0, sizeof(msg)); + memset(iov, 0, sizeof(iov)); + + msg.msg_iov = iov; + + ch = BLE_HCI_UART_H4_ACL; + iov[0].iov_len = 1; + iov[0].iov_base = &ch; + i = 1; + for (m = om; m; m = SLIST_NEXT(m, om_next)) { + iov[i].iov_base = m->om_data; + iov[i].iov_len = m->om_len; + i++; + } + msg.msg_iovlen = i; + + STATS_INC(hci_sock_stats, omsg); + STATS_INC(hci_sock_stats, oacl); + STATS_INCN(hci_sock_stats, obytes, OS_MBUF_PKTLEN(om) + 1); + i = sendmsg(ble_hci_sock_state.sock, &msg, 0); + os_mbuf_free_chain(om); + if (i != OS_MBUF_PKTLEN(om) + 1) { + if (i < 0) { + dprintf(1, "sendmsg() failed : %d\n", errno); + } else { + dprintf(1, "sendmsg() partial write: %d\n", i); + } + STATS_INC(hci_sock_stats, oerr); + return BLE_ERR_MEM_CAPACITY; + } + return 0; +} + +static int +ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type) +{ + struct msghdr msg; + struct iovec iov[8]; + int len; + int i; + uint8_t ch; + + memset(&msg, 0, sizeof(msg)); + memset(iov, 0, sizeof(iov)); + + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + ch = h4_type; + iov[0].iov_len = 1; + iov[0].iov_base = &ch; + iov[1].iov_base = hci_ev; + if (h4_type == BLE_HCI_UART_H4_CMD) { + len = sizeof(struct ble_hci_cmd) + hci_ev[2]; + STATS_INC(hci_sock_stats, ocmd); + } else if (h4_type == BLE_HCI_UART_H4_EVT) { + len = sizeof(struct ble_hci_ev) + hci_ev[1]; + STATS_INC(hci_sock_stats, oevt); + } else { + assert(0); + } + iov[1].iov_len = len; + + STATS_INC(hci_sock_stats, omsg); + STATS_INCN(hci_sock_stats, obytes, len + 1); + + i = sendmsg(ble_hci_sock_state.sock, &msg, 0); + ble_hci_trans_buf_free(hci_ev); + if (i != len + 1) { + if (i < 0) { + dprintf(1, "sendmsg() failed : %d\n", errno); + } else { + dprintf(1, "sendmsg() partial write: %d\n", i); + } + STATS_INC(hci_sock_stats, oerr); + return BLE_ERR_MEM_CAPACITY; + } + + return 0; +} + +static int +ble_hci_sock_rx_msg(void) +{ + struct ble_hci_sock_state *bhss; + int len; + struct os_mbuf *m; + uint8_t *data; + int sr; + int rc; + + bhss = &ble_hci_sock_state; + if (bhss->sock < 0) { + return -1; + } + len = read(bhss->sock, bhss->rx_data + bhss->rx_off, + sizeof(bhss->rx_data) - bhss->rx_off); + if (len < 0) { + return -2; + } + if (len == 0) { + return -1; + } + bhss->rx_off += len; + STATS_INCN(hci_sock_stats, ibytes, len); + + while (bhss->rx_off > 0) { + switch (bhss->rx_data[0]) { +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_HCI_UART_H4_CMD: + if (bhss->rx_off < sizeof(struct ble_hci_cmd)) { + return -1; + } + len = 1 + sizeof(struct ble_hci_cmd) + bhss->rx_data[3]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, icmd); + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + if (!data) { + STATS_INC(hci_sock_stats, ierr); + break; + } + memcpy(data, &bhss->rx_data[1], len - 1); + OS_ENTER_CRITICAL(sr); + rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg); + OS_EXIT_CRITICAL(sr); + if (rc) { + ble_hci_trans_buf_free(data); + STATS_INC(hci_sock_stats, ierr); + break; + } + break; +#endif +#if MYNEWT_VAL(BLE_HOST) + case BLE_HCI_UART_H4_EVT: + if (bhss->rx_off < sizeof(struct ble_hci_ev)) { + return -1; + } + len = 1 + sizeof(struct ble_hci_ev) + bhss->rx_data[2]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, ievt); + data = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (!data) { + STATS_INC(hci_sock_stats, ierr); + break; + } + memcpy(data, &bhss->rx_data[1], len - 1); + OS_ENTER_CRITICAL(sr); + rc = ble_hci_sock_rx_cmd_cb(data, ble_hci_sock_rx_cmd_arg); + OS_EXIT_CRITICAL(sr); + if (rc) { + ble_hci_trans_buf_free(data); + STATS_INC(hci_sock_stats, ierr); + return 0; + } + break; +#endif + case BLE_HCI_UART_H4_ACL: + if (bhss->rx_off < BLE_HCI_DATA_HDR_SZ) { + return -1; + } + len = 1 + BLE_HCI_DATA_HDR_SZ + (bhss->rx_data[4] << 8) + + bhss->rx_data[3]; + if (bhss->rx_off < len) { + return -1; + } + STATS_INC(hci_sock_stats, imsg); + STATS_INC(hci_sock_stats, iacl); + m = ble_hci_trans_acl_buf_alloc(); + if (!m) { + STATS_INC(hci_sock_stats, imem); + break; + } + if (os_mbuf_append(m, &bhss->rx_data[1], len - 1)) { + STATS_INC(hci_sock_stats, imem); + os_mbuf_free_chain(m); + break; + } + OS_ENTER_CRITICAL(sr); + ble_hci_sock_rx_acl_cb(m, ble_hci_sock_rx_acl_arg); + OS_EXIT_CRITICAL(sr); + break; + default: + STATS_INC(hci_sock_stats, ierr); + break; + } + memmove(bhss->rx_data, &bhss->rx_data[len], bhss->rx_off - len); + bhss->rx_off -= len; + } + return 0; +} + +static void +ble_hci_sock_rx_ev(struct ble_npl_event *ev) +{ + int rc; + ble_npl_time_t timeout; + + rc = ble_hci_sock_rx_msg(); + if (rc == 0) { + ble_npl_eventq_put(&ble_hci_sock_state.evq, &ble_hci_sock_state.ev); + } else { + rc = ble_npl_time_ms_to_ticks(10, &timeout); + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + } +} + +#if MYNEWT_VAL(BLE_SOCK_USE_TCP) +static int +ble_hci_sock_config(void) +{ + struct ble_hci_sock_state *bhss = &ble_hci_sock_state; + struct sockaddr_in sin; + ble_npl_time_t timeout; + int s; + int rc; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(s_ble_hci_device); + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); +#ifdef MN_OSX + sin.sin_len = sizeof(sin); +#endif + +#if 0 + if (bhss->sock >= 0) { + close(bhss->sock); + bhss->sock = -1; + } +#endif + if (bhss->sock < 0) { + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + goto err; + } + + rc = connect(s, (struct sockaddr *)&sin, sizeof(sin)); + if (rc) { + dprintf(1, "connect() failed: %d\n", errno); + goto err; + } + + rc = 1; + + rc = ioctl(s, FIONBIO, (char *)&rc); + if (rc) { + goto err; + } + bhss->sock = s; + } + rc = ble_npl_time_ms_to_ticks(10, &timeout); + if (rc) { + goto err; + } + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + + return 0; +err: + if (s >= 0) { + close(s); + } + return BLE_ERR_HW_FAIL; +} +#endif + +#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE) +static int +ble_hci_sock_config(void) +{ + struct sockaddr_hci shci; + int s; + int rc; + ble_npl_time_t timeout; + + memset(&shci, 0, sizeof(shci)); + shci.hci_family = AF_BLUETOOTH; + shci.hci_dev = s_ble_hci_device; + shci.hci_channel = HCI_CHANNEL_USER; + + if (ble_hci_sock_state.sock >= 0) { + close(ble_hci_sock_state.sock); + ble_hci_sock_state.sock = -1; + } + + s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (s < 0) { + dprintf(1, "socket() failed %d\n", errno); + goto err; + } + + /* + * HCI User Channel requires exclusive access to the device. + * The device has to be down at the time of binding. + */ + ioctl(s, HCIDEVDOWN, shci.hci_dev); + + rc = bind(s, (struct sockaddr *)&shci, sizeof(shci)); + if (rc) { + dprintf(1, "bind() failed %d hci%d\n", errno, shci.hci_dev); + goto err; + } + + rc = 1; + + rc = ioctl(s, FIONBIO, (char *)&rc); + if (rc) { + goto err; + } + ble_hci_sock_state.sock = s; + + rc = ble_npl_time_ms_to_ticks(10, &timeout); + if (rc) { + goto err; + } + ble_npl_callout_reset(&ble_hci_sock_state.timer, timeout); + + return 0; +err: + if (s >= 0) { + close(s); + } + return BLE_ERR_HW_FAIL; +} +#endif +/** + * Sends an HCI event from the controller to the host. + * + * @param cmd The HCI event to send. This buffer must be + * allocated via ble_hci_trans_buf_alloc(). + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_evt_tx(uint8_t *cmd) +{ + return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_EVT); +} + +/** + * Sends ACL data from controller to host. + * + * @param om The ACL data packet to send. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_ll_acl_tx(struct os_mbuf *om) +{ + return ble_hci_sock_acl_tx(om); +} + +/** + * 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) +{ + return ble_hci_sock_cmdevt_tx(cmd, BLE_HCI_UART_H4_CMD); +} + +/** + * 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) +{ + return ble_hci_sock_acl_tx(om); +} + +/** + * 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_sock_rx_cmd_cb = cmd_cb; + ble_hci_sock_rx_cmd_arg = cmd_arg; + ble_hci_sock_rx_acl_cb = acl_cb; + ble_hci_sock_rx_acl_arg = 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_sock_rx_cmd_cb = cmd_cb; + ble_hci_sock_rx_cmd_arg = cmd_arg; + ble_hci_sock_rx_acl_cb = acl_cb; + ble_hci_sock_rx_acl_arg = 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_sock_cmd_pool); + break; + case BLE_HCI_TRANS_BUF_EVT_HI: + buf = os_memblock_get(&ble_hci_sock_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_sock_evt_lo_pool); + } + break; + + case BLE_HCI_TRANS_BUF_EVT_LO: + buf = os_memblock_get(&ble_hci_sock_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_sock_evt_hi_pool, buf)) { + rc = os_memblock_put(&ble_hci_sock_evt_hi_pool, buf); + assert(rc == 0); + } else if (os_memblock_from(&ble_hci_sock_evt_lo_pool, buf)) { + rc = os_memblock_put(&ble_hci_sock_evt_lo_pool, buf); + assert(rc == 0); + } else { + assert(os_memblock_from(&ble_hci_sock_cmd_pool, buf)); + rc = os_memblock_put(&ble_hci_sock_cmd_pool, buf); + assert(rc == 0); + } +} + +/** + * Resets the HCI UART transport to a clean state. Frees all buffers and + * reconfigures the UART. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +int +ble_hci_trans_reset(void) +{ + int rc; + + ble_npl_callout_stop(&ble_hci_sock_state.timer); + + /* Reopen the UART. */ + rc = ble_hci_sock_config(); + if (rc != 0) { + dprintf(1, "Failure restarting socket HCI\n"); + return rc; + } + + return 0; +} + +void +ble_hci_sock_ack_handler(void *arg) +{ + struct ble_npl_event *ev; + + while (1) { + ev = ble_npl_eventq_get(&ble_hci_sock_state.evq, BLE_NPL_TIME_FOREVER); + ble_npl_event_run(ev); + } +} + +static void +ble_hci_sock_init_task(void) +{ + ble_npl_eventq_init(&ble_hci_sock_state.evq); + ble_npl_callout_stop(&ble_hci_sock_state.timer); + ble_npl_callout_init(&ble_hci_sock_state.timer, &ble_hci_sock_state.evq, + ble_hci_sock_rx_ev, NULL); + +#if MYNEWT + { + os_stack_t *pstack; + + pstack = malloc(sizeof(os_stack_t)*BLE_SOCK_STACK_SIZE); + assert(pstack); + os_task_init(&ble_sock_task, "hci_sock", ble_hci_sock_ack_handler, NULL, + MYNEWT_VAL(BLE_SOCK_TASK_PRIO), BLE_NPL_TIME_FOREVER, pstack, + BLE_SOCK_STACK_SIZE); + } +#else +/* + * For non-Mynewt OS it is required that OS creates task for HCI SOCKET + * to run ble_hci_sock_ack_handler. + */ + +#endif + +} + +void +ble_hci_sock_set_device(int dev) +{ + s_ble_hci_device = dev; +} + +/** + * Initializes the UART HCI transport module. + * + * @return 0 on success; + * A BLE_ERR_[...] error code on failure. + */ +void +ble_hci_sock_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + memset(&ble_hci_sock_state, 0, sizeof(ble_hci_sock_state)); + ble_hci_sock_state.sock = -1; + + ble_hci_sock_init_task(); + ble_npl_event_init(&ble_hci_sock_state.ev, ble_hci_sock_rx_ev, NULL); + + rc = os_mempool_init(&ble_hci_sock_acl_pool, + MYNEWT_VAL(BLE_ACL_BUF_COUNT), + ACL_BLOCK_SIZE, + ble_hci_sock_acl_buf, + "ble_hci_sock_acl_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mbuf_pool_init(&ble_hci_sock_acl_mbuf_pool, + &ble_hci_sock_acl_pool, + 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_sock_cmd_pool, + 1, + BLE_HCI_TRANS_CMD_SZ, + &ble_hci_sock_cmd_buf, + "ble_hci_sock_cmd_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_sock_evt_hi_pool, + MYNEWT_VAL(BLE_HCI_EVT_HI_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + &ble_hci_sock_evt_hi_buf, + "ble_hci_sock_evt_hi_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = os_mempool_init(&ble_hci_sock_evt_lo_pool, + MYNEWT_VAL(BLE_HCI_EVT_LO_BUF_COUNT), + MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE), + ble_hci_sock_evt_lo_buf, + "ble_hci_sock_evt_lo_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_hci_sock_config(); + SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring socket HCI"); + + rc = stats_init_and_reg(STATS_HDR(hci_sock_stats), + STATS_SIZE_INIT_PARMS(hci_sock_stats, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(hci_sock_stats), "hci_socket"); + SYSINIT_PANIC_ASSERT(rc == 0); +} diff --git a/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml b/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml new file mode 100644 index 00000000..2050f646 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/transport/socket/syscfg.yml @@ -0,0 +1,79 @@ +# 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: 24 + 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_SOCK_USE_TCP: + description: 'Use TCP socket, connects to BLE_SOCK_TCP_PORT' + value: 1 + + BLE_SOCK_TCP_PORT: + description: 'ipv4 tcp port to connect to' + value: 14433 + + BLE_SOCK_USE_LINUX_BLUE: + description: 'Use Linux bluetooth raw socket' + value: 0 + + BLE_SOCK_LINUX_DEV: + description: 'linux kernel device' + value: 0 + + BLE_SOCK_TASK_PRIO: + description: 'Priority of the HCI socket task.' + type: task_priority + value: 9 + + BLE_SOCK_STACK_SIZE: + description: 'Size of the HCI socket stack (units=words).' + value: 80 + + BLE_SOCK_CLI_SYSINIT_STAGE: + description: > + Sysinit stage for the socket BLE transport. + value: 500 + +syscfg.vals.BLE_EXT_ADV: + BLE_HCI_EVT_BUF_SIZE: 257 -- cgit v1.2.3