/*
|
* 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=<interface index of the target interface>
|
*
|
*/
|
#include "nimble_syscfg.h"
|
|
#include <assert.h>
|
#include <string.h>
|
#include <stdio.h>
|
#include <stdint.h>
|
#include <stdlib.h>
|
#include <sys/types.h>
|
#include <sys/uio.h>
|
#include <unistd.h>
|
#include <sys/socket.h>
|
|
#if MYNEWT_VAL(BLE_SOCK_USE_TCP)
|
#include <sys/errno.h>
|
#include <netinet/in.h>
|
#include <arpa/inet.h>
|
#endif
|
|
#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
|
#include <sys/errno.h>
|
#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;
|
};
|
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
|
#include <errno.h>
|
#include <netpacket/bluetooth.h>
|
#endif
|
|
#include <fcntl.h>
|
#include <sys/ioctl.h>
|
|
#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/transport.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 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);
|
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
|
static int s_ble_hci_device = 0;
|
#endif
|
|
#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
|
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;
|
}
|
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
|
static int
|
ble_hci_sock_acl_tx(struct os_mbuf *om)
|
{
|
size_t len;
|
uint8_t *buf;
|
int i;
|
struct os_mbuf *m;
|
struct sockaddr_hci addr;
|
|
addr.hci_family = AF_BLUETOOTH;
|
addr.hci_channel = HCI_CHANNEL_RAW;
|
addr.hci_dev = 0;
|
|
memcpy(&addr, &addr, sizeof(struct sockaddr_hci));
|
|
len = 1;
|
|
for (m = om; m; m = SLIST_NEXT(m, om_next)) {
|
len += m->om_len;
|
}
|
|
buf = (uint8_t *)pvPortMalloc(len);
|
|
buf[0] = BLE_HCI_UART_H4_ACL;
|
|
i = 1;
|
for (m = om; m; m = SLIST_NEXT(m, om_next)) {
|
memcpy(&buf[i], m->om_data, m->om_len);
|
i += m->om_len;
|
}
|
|
STATS_INC(hci_sock_stats, omsg);
|
STATS_INC(hci_sock_stats, oacl);
|
STATS_INCN(hci_sock_stats, obytes, OS_MBUF_PKTLEN(om) + 1);
|
|
i = sendto(ble_hci_sock_state.sock, buf, len, 0,
|
(struct sockaddr *)&addr, sizeof(struct sockaddr_hci));
|
|
free(buf);
|
|
os_mbuf_free_chain(om);
|
if (i != OS_MBUF_PKTLEN(om) + 1) {
|
if (i < 0) {
|
dprintf(1, "sendto() failed : %d\n", errno);
|
} else {
|
dprintf(1, "sendto() partial write: %d\n", i);
|
}
|
STATS_INC(hci_sock_stats, oerr);
|
return BLE_ERR_MEM_CAPACITY;
|
}
|
return 0;
|
}
|
#endif
|
|
#if MYNEWT_VAL(BLE_SOCK_USE_LINUX_BLUE)
|
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);
|
return BLE_ERR_UNKNOWN_HCI_CMD;
|
}
|
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_transport_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;
|
}
|
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
|
static int
|
ble_hci_sock_cmdevt_tx(uint8_t *hci_ev, uint8_t h4_type)
|
{
|
uint8_t *buf;
|
size_t len;
|
struct sockaddr_hci addr;
|
int i;
|
|
addr.hci_family = AF_BLUETOOTH;
|
addr.hci_channel = HCI_CHANNEL_RAW;
|
addr.hci_dev = 0;
|
|
memcpy(&addr, &addr, sizeof(struct sockaddr_hci));
|
|
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);
|
}
|
|
STATS_INC(hci_sock_stats, omsg);
|
STATS_INCN(hci_sock_stats, obytes, len + 1);
|
|
buf = (uint8_t *)pvPortMalloc(len + 1);
|
|
buf[0] = h4_type;
|
memcpy(&buf[1], hci_ev, len);
|
|
i = sendto(ble_hci_sock_state.sock, buf, len + 1, 0,
|
(struct sockaddr *)&addr, sizeof(struct sockaddr_hci));
|
|
free(buf);
|
ble_hci_trans_buf_free(hci_ev);
|
if (i != len + 1) {
|
if (i < 0) {
|
dprintf(1, "sendto() failed : %d\n", errno);
|
} else {
|
dprintf(1, "sendto() partial write: %d\n", i);
|
}
|
STATS_INC(hci_sock_stats, oerr);
|
return BLE_ERR_MEM_CAPACITY;
|
}
|
|
return 0;
|
}
|
#endif
|
|
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_transport_alloc_cmd();
|
if (!data) {
|
STATS_INC(hci_sock_stats, ierr);
|
break;
|
}
|
memcpy(data, &bhss->rx_data[1], len - 1);
|
OS_ENTER_CRITICAL(sr);
|
rc = ble_transport_to_ll_cmd(data);
|
OS_EXIT_CRITICAL(sr);
|
if (rc) {
|
ble_transport_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_transport_alloc_evt(0);
|
if (!data) {
|
STATS_INC(hci_sock_stats, ierr);
|
break;
|
}
|
memcpy(data, &bhss->rx_data[1], len - 1);
|
OS_ENTER_CRITICAL(sr);
|
rc = ble_transport_to_hs_evt(data);
|
OS_EXIT_CRITICAL(sr);
|
if (rc) {
|
ble_transport_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);
|
#if MYNEWT_VAL(BLE_CONTROLLER)
|
m = ble_transport_alloc_acl_from_hs();
|
#else
|
m = ble_transport_alloc_acl_from_ll();
|
#endif
|
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);
|
#if MYNEWT_VAL(BLE_CONTROLLER)
|
ble_transport_to_ll_acl(m);
|
#else
|
ble_transport_to_hs_acl(m);
|
#endif
|
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;
|
}
|
#elif MYNEWT_VAL(BLE_SOCK_USE_NUTTX)
|
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 = 0;
|
shci.hci_channel = HCI_CHANNEL_RAW;
|
|
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;
|
}
|
|
rc = bind(s, (struct sockaddr *)&shci, sizeof(shci));
|
if (rc) {
|
dprintf(1, "bind() failed %d hci%d\n", errno, shci.hci_dev);
|
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);
|
}
|
|
/**
|
* 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 = pvPortMalloc(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 = 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);
|
}
|
|
void
|
ble_transport_ll_init(void)
|
{
|
ble_hci_sock_init();
|
}
|
|
int
|
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
|
{
|
return ble_hci_trans_hs_acl_tx(om);
|
}
|
|
int
|
ble_transport_to_ll_cmd_impl(void *buf)
|
{
|
return ble_hci_trans_hs_cmd_tx(buf);
|
}
|
|
/* TODO: add ll-to-hs side if needed */
|