mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-23 02:10:01 +00:00
net/nimble; add HCI transport which has Linux bluetooth socket,
or TCP socket as it's target. X-Original-Commit: 9fcbd0d0d5d45b4495a93f71fe17b23d7761e44f
This commit is contained in:
@@ -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
|
||||
@@ -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: net/nimble/transport/socket
|
||||
pkg.description: Provides HCI transport over socket interface
|
||||
pkg.author: "Apache Mynewt <[email protected]>"
|
||||
pkg.homepage: "http://mynewt.apache.org/"
|
||||
pkg.keywords:
|
||||
- ble
|
||||
- bluetooth
|
||||
|
||||
pkg.deps:
|
||||
- hw/hal
|
||||
- kernel/os
|
||||
- net/nimble
|
||||
- util/mem
|
||||
|
||||
pkg.apis:
|
||||
- ble_transport
|
||||
|
||||
pkg.init:
|
||||
ble_hci_sock_init: 500
|
||||
Executable
+797
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* 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 "syscfg/syscfg.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.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 HCIGETDEVLIST _IOR('H', 210, int)
|
||||
|
||||
struct sockaddr_hci {
|
||||
sa_family_t hci_family;
|
||||
unsigned short hci_dev;
|
||||
unsigned short hci_channel;
|
||||
};
|
||||
#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/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
|
||||
|
||||
static struct os_mempool ble_hci_sock_evt_hi_pool;
|
||||
static void *ble_hci_sock_evt_hi_buf;
|
||||
static struct os_mempool ble_hci_sock_evt_lo_pool;
|
||||
static void *ble_hci_sock_evt_lo_buf;
|
||||
|
||||
static struct os_mempool ble_hci_sock_cmd_pool;
|
||||
static void *ble_hci_sock_cmd_buf;
|
||||
|
||||
static struct os_mbuf_pool ble_hci_sock_acl_mbuf_pool;
|
||||
static struct os_mempool ble_hci_sock_acl_pool;
|
||||
static void *ble_hci_sock_acl_buf;
|
||||
|
||||
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 os_eventq *evq;
|
||||
struct os_event ev;
|
||||
struct os_callout timer;
|
||||
|
||||
uint8_t rx_off;
|
||||
uint8_t rx_data[512];
|
||||
} ble_hci_sock_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;
|
||||
|
||||
/*
|
||||
* 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 = BLE_HCI_CMD_HDR_LEN + hci_ev[2];
|
||||
STATS_INC(hci_sock_stats, ocmd);
|
||||
} else if (h4_type == BLE_HCI_UART_H4_EVT) {
|
||||
len = BLE_HCI_EVENT_HDR_LEN + 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_DEVICE)
|
||||
case BLE_HCI_UART_H4_CMD:
|
||||
if (bhss->rx_off < BLE_HCI_CMD_HDR_LEN) {
|
||||
return -1;
|
||||
}
|
||||
len = 1 + BLE_HCI_CMD_HDR_LEN + 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 < BLE_HCI_EVENT_HDR_LEN) {
|
||||
return -1;
|
||||
}
|
||||
len = 1 + BLE_HCI_EVENT_HDR_LEN + 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 os_event *ev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_hci_sock_rx_msg();
|
||||
if (rc == 0) {
|
||||
os_eventq_put(ble_hci_sock_state.evq, &ble_hci_sock_state.ev);
|
||||
} else {
|
||||
os_callout_reset(&ble_hci_sock_state.timer, OS_TICKS_PER_SEC / 100);
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
int s;
|
||||
int rc;
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(MYNEWT_VAL(BLE_SOCK_TCP_PORT));
|
||||
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;
|
||||
}
|
||||
os_callout_reset(&ble_hci_sock_state.timer, OS_TICKS_PER_SEC / 100);
|
||||
|
||||
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;
|
||||
|
||||
memset(&shci, 0, sizeof(shci));
|
||||
shci.hci_family = AF_BLUETOOTH;
|
||||
shci.hci_dev = MYNEWT_VAL(BLE_SOCK_LINUX_DEV);
|
||||
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;
|
||||
}
|
||||
|
||||
rc = bind(s, (struct sockaddr *)&shci, sizeof(shci));
|
||||
if (rc) {
|
||||
dprintf(1, "bind() failed %d\n", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
|
||||
rc = ioctl(s, FIONBIO, (char *)&rc);
|
||||
if (rc) {
|
||||
goto err;
|
||||
}
|
||||
ble_hci_sock_state.sock = s;
|
||||
|
||||
os_callout_reset(&ble_hci_sock_state.timer, OS_TICKS_PER_SEC / 100);
|
||||
|
||||
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;
|
||||
|
||||
os_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_set_evq(struct os_eventq *evq)
|
||||
{
|
||||
ble_hci_sock_state.evq = evq;
|
||||
os_callout_stop(&ble_hci_sock_state.timer);
|
||||
os_callout_init(&ble_hci_sock_state.timer, ble_hci_sock_state.evq,
|
||||
ble_hci_sock_rx_ev, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the UART HCI transport module.
|
||||
*
|
||||
* @return 0 on success;
|
||||
* A BLE_ERR_[...] error code on failure.
|
||||
*/
|
||||
void
|
||||
ble_hci_sock_init(void)
|
||||
{
|
||||
int acl_data_length;
|
||||
int acl_block_size;
|
||||
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_set_evq(os_eventq_dflt_get());
|
||||
ble_hci_sock_state.ev.ev_cb = ble_hci_sock_rx_ev;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
acl_data_length = MYNEWT_VAL(BLE_ACL_BUF_SIZE);
|
||||
acl_block_size = acl_data_length + BLE_MBUF_MEMBLOCK_OVERHEAD +
|
||||
BLE_HCI_DATA_HDR_SZ;
|
||||
acl_block_size = OS_ALIGN(acl_block_size, OS_ALIGNMENT);
|
||||
rc = mem_malloc_mempool(&ble_hci_sock_acl_pool,
|
||||
MYNEWT_VAL(BLE_ACL_BUF_COUNT),
|
||||
acl_block_size,
|
||||
"ble_hci_sock_acl_pool",
|
||||
&ble_hci_sock_acl_buf);
|
||||
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 = mem_malloc_mempool(&ble_hci_sock_cmd_pool,
|
||||
1,
|
||||
BLE_HCI_TRANS_CMD_SZ,
|
||||
"ble_hci_sock_cmd_pool",
|
||||
&ble_hci_sock_cmd_buf);
|
||||
SYSINIT_PANIC_ASSERT(rc == 0);
|
||||
|
||||
rc = mem_malloc_mempool(&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_pool",
|
||||
&ble_hci_sock_evt_hi_buf);
|
||||
SYSINIT_PANIC_ASSERT(rc == 0);
|
||||
|
||||
rc = mem_malloc_mempool(&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_pool",
|
||||
&ble_hci_sock_evt_lo_buf);
|
||||
SYSINIT_PANIC_ASSERT(rc == 0);
|
||||
|
||||
rc = ble_hci_sock_config();
|
||||
SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failure configuring socket HCI");
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
# Package: net/nimble/transport/uart
|
||||
|
||||
syscfg.defs:
|
||||
BLE_HCI_EVT_BUF_SIZE:
|
||||
description: 'The size of the allocated event buffers'
|
||||
# The largest event the nimble controller will send is 70 bytes.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user