SIM: Add support Linux HCI Socket as BLE adaptor

Signed-off-by: Brennan Ashton <bashton@brennanashton.com>
This commit is contained in:
Brennan Ashton 2020-08-26 17:38:44 -07:00 committed by Xiang Xiao
parent 8a2c480a48
commit 8934f2ed79
10 changed files with 599 additions and 0 deletions

View File

@ -475,4 +475,14 @@ config SIM_QSPIFLASH_PAGESIZE
"wrap" causing the initial data sent to be overwritten.
This is consistent with standard SPI FLASH operation.
config SIM_HCISOCKET
bool "Attach Host Bluetooth"
default false
depends on (WIRELESS_BLUETOOTH && HOST_LINUX)
---help---
Attached the local bluetooth device to the simulation
target via HCI_CHANNEL_USER. This gives NuttX full
control of the device, but is abstracted from the
physical interface which is still handled by Linux.
endif # ARCH_SIM

View File

@ -196,6 +196,11 @@ else ifeq ($(CONFIG_SIM_NETDEV_VPNKIT),y)
HOSTSRCS += protocol.c negotiate.c
endif
ifeq ($(CONFIG_SIM_HCISOCKET),y)
HOSTSRCS += up_hcisocket_host.c
CSRCS += up_hcisocket.c
endif
ifeq ($(CONFIG_RPTUN),y)
CSRCS += up_rptun.c
endif

View File

@ -0,0 +1,247 @@
/****************************************************************************
* arch/sim/src/sim/up_hcisocket.c
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <nuttx/wireless/bluetooth/bt_driver.h>
#include <nuttx/net/bluetooth.h>
#include "up_internal.h"
#include "up_hcisocket_host.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* HCI data types as defined by Linux Kernel */
#define HCI_COMMAND_PKT 0x01
#define HCI_ACLDATA_PKT 0x02
#define HCI_SCODATA_PKT 0x03
#define HCI_EVENT_PKT 0x04
#define HCI_ISODATA_PKT 0x05
#define HCI_DIAG_PKT 0xf0
#define HCI_VENDOR_PKT 0xff
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int bthcisock_send(FAR const struct bt_driver_s *dev,
FAR struct bt_buf_s *buf);
static int bthcisock_open(FAR const struct bt_driver_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct bt_driver_s g_bt_hcisock =
{
0, /* head_reserve */
bthcisock_open, /* open */
bthcisock_send /* send */
};
static int bt_fd = -1; /* Host HCI socket fd */
static int host_dev_id = 0; /* Host HCI interface number */
/* Hold a receive frame buffer here. This allows us to not allocate and free
* on every socket read since most of the time there will be no data to
* actually process.
*/
struct bt_buf_s *read_buf = NULL;
/****************************************************************************
* Private Functions
****************************************************************************/
static int bthcisock_send(FAR const struct bt_driver_s *dev,
FAR struct bt_buf_s *buf)
{
uint8_t pkt_type;
switch (buf->type)
{
case BT_CMD:
{
pkt_type = HCI_COMMAND_PKT;
break;
}
case BT_ACL_OUT:
{
pkt_type = HCI_ACLDATA_PKT;
break;
}
default:
{
wlerr("Unexpected HCI packet type %d", buf->type);
return buf->len;
}
}
if (bthcisock_host_send(bt_fd, pkt_type, buf->data, buf->len) < 0)
{
return -1;
}
return buf->len;
}
static int bthcisock_open(FAR const struct bt_driver_s *dev)
{
int fd = bthcisock_host_open(host_dev_id);
if (fd < 0)
{
return -1;
}
bt_fd = fd;
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bthcisock_register
*
* Description:
* Register the Linux HCI interface with the Bluetooth stack
*
* Input Parameters:
* dev_id: This is the interface number known to the Linux Kernel
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bthcisock_register(int dev_id)
{
/* Register the driver with the Bluetooth stack */
host_dev_id = dev_id;
return bt_netdev_register(&g_bt_hcisock);
}
/****************************************************************************
* Name: bthcisock_loop
*
* Description:
* Feed pending packets on the host sockets into the Bluetooth stack.
*
* Input Parameters:
* None
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bthcisock_loop()
{
uint8_t type;
int len;
if (bt_fd < 0)
{
/* Internal socket has not yet been created */
return -EBADF;
}
if (read_buf == NULL)
{
/* NOTE: This shared allocation only works because the allocation size
* is currently the same for all frame types. If this changes we will
* need to allocate for the largest frame or use an intermediate buffer
* to copy from
*/
read_buf = bt_buf_alloc(BT_DUMMY, NULL, 0);
if (read_buf == NULL)
{
wlerr("ERROR: Failed to allocate buffer\n");
return -ENOMEM;
}
}
len = bthcisock_host_read(bt_fd, &type, read_buf->data,
BLUETOOTH_MAX_FRAMELEN);
if (len < 0)
{
return OK;
}
read_buf->len = len;
switch (type)
{
case HCI_EVENT_PKT:
{
read_buf->type = BT_EVT;
break;
}
case HCI_ACLDATA_PKT:
{
read_buf->type = BT_ACL_IN;
break;
}
default:
{
wlerr("Unknown packet type %d\n", type);
return OK;
}
}
bt_hci_receive(read_buf);
/* Make sure to allocate a new buffer for the next read. Bluetooth
* stack will clean up the allocation of this buffer when it has been
* handled.
*/
read_buf = NULL;
return OK;
}

View File

@ -0,0 +1,192 @@
/****************************************************************************
* arch/sim/src/sim/up_hcisocket.c
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "up_internal.h"
#include "up_hcisocket_host.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BTPROTO_HCI 1
#define HCI_CHANNEL_RAW 0
#define HCI_CHANNEL_USER 1
#define HCIDEVDOWN 0x400448ca
/****************************************************************************
* Private Types
****************************************************************************/
struct sockaddr_hci
{
sa_family_t hci_family;
unsigned short hci_dev;
unsigned short hci_channel;
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bthcisock_host_send
*
* Description:
* Send a Bluetooth packet out via the host user socket.
*
* Input Parameters:
* fd: Host Bluetooth socket fd
* pkt_type: Packet type as known to the Linux Bluetooth stack
* data: Pointer to the HCI packet
* len: Length of packet
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bthcisock_host_send(int fd, uint8_t pkt_type, uint8_t *data, size_t len)
{
struct iovec iv[2];
iv[0].iov_base = &pkt_type;
iv[0].iov_len = 1;
iv[1].iov_base = data;
iv[1].iov_len = len;
while (writev(fd, iv, 2) < 0)
{
if (errno == EAGAIN || errno == EINTR)
continue;
return -1;
}
return 0;
}
/****************************************************************************
* Name: bthcisock_host_read
*
* Description:
* Read from the Host HCI socket interface.
*
* Input Parameters:
* fd: Host Bluetooth socket fd
* data: Pointer to store HCI packet
* len: Maximum length of packet
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bthcisock_host_read(int fd, uint8_t *type, void *buf, size_t len)
{
int err;
struct iovec iv[2];
iv[0].iov_base = type;
iv[0].iov_len = 1;
iv[1].iov_base = buf;
iv[1].iov_len = len;
while ((err = readv(fd, iv, 2)) < 0 && (errno == EINTR));
if (err <= 0)
{
/* Both an empty read and an error are "error" conditions */
return -1;
}
/* Return the number of bytes written to buf so remove the header byte */
return (err - 1);
}
/****************************************************************************
* Name: bthcisock_host_open
*
* Description:
* Open a User Channel HCI socket on the Host for the given device.
* This will also disconnect the device from existing management. It can
* still be monitored using an HCI monitor socket.
*
* Input Parameters:
* dev_idx: This is the device index to be connected to. HCI0 would be 0.
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bthcisock_host_open(int dev_idx)
{
int err;
struct sockaddr_hci addr;
int fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
BTPROTO_HCI);
if (fd < 0)
{
return fd;
}
/* We must bring the device down before binding to user channel */
err = ioctl(fd, HCIDEVDOWN, 0);
if (err < 0)
{
return err;
}
memset(&addr, 0, sizeof(addr));
addr.hci_family = AF_BLUETOOTH;
addr.hci_dev = dev_idx;
addr.hci_channel = HCI_CHANNEL_USER;
err = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0)
{
close(fd);
return err;
}
return fd;
}

View File

@ -0,0 +1,39 @@
/****************************************************************************
* arch/sim/src/sim/up_hcisocket_host.h
*
* 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 _ARCH_SIM_SRC_SIM_HCISOCKET_HOST_H_
#define _ARCH_SIM_SRC_SIM_HCISOCKET_HOST_H_
/****************************************************************************
* Included Files
****************************************************************************/
#include <errno.h>
#include <stdint.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int bthcisock_host_send(int fd, uint8_t pkt_type, uint8_t *data, size_t len);
int bthcisock_host_read(int fd, uint8_t *type, void *buf, size_t len);
int bthcisock_host_open(int dev_idx);
#endif /* _ARCH_SIM_SRC_SIM_HCISOCKET_HOST_H_ */

View File

@ -111,6 +111,10 @@ void up_idle(void)
up_rptun_loop();
#endif
#ifdef CONFIG_SIM_HCISOCKET
bthcisock_loop();
#endif
#ifdef CONFIG_ONESHOT
/* Driver the simulated interval timer */

View File

@ -365,6 +365,13 @@ struct spi_dev_s *up_spiflashinitialize(const char *name);
struct qspi_dev_s *up_qspiflashinitialize(void);
#endif
/* up_hcisocket.c ***********************************************************/
#ifdef CONFIG_SIM_HCISOCKET
int bthcisock_register(int dev_id);
int bthcisock_loop(void);
#endif
/* Debug ********************************************************************/
#ifdef CONFIG_STACK_COLORATION

View File

@ -544,6 +544,17 @@ bluetooth
apps/wireless/bluetooth/btsak and the NULL Bluetooth device at
drivers/wireless/bluetooth/bt_null.c
There is also support on a Linux Host for attaching the bluetooth hardware
from the host to the NuttX bluetoooth stack via the HCI Socket interface
over the User Channel. This is enabled in the bthcisock configuration.
In order to use this you must give the nuttx elf additional capabilities:
sudo setcap 'cap_net_raw,cap_net_admin=eip' ./nuttx
You can then monitor the HCI traffic on the host with wireshark or btmon
sudo btmon
configdata
A unit test for the MTD configuration data driver.

View File

@ -0,0 +1,74 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_NET_ETHERNET is not set
# CONFIG_NET_IPv4 is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="sim"
CONFIG_ARCH_BOARD="sim"
CONFIG_ARCH_BOARD_SIM=y
CONFIG_ARCH_CHIP="sim"
CONFIG_ARCH_SIM=y
CONFIG_BLUETOOTH_MAX_CONN=2
CONFIG_BLUETOOTH_MAX_PAIRED=2
CONFIG_BLUETOOTH_SMP_SELFTEST=y
CONFIG_BOARDCTL_POWEROFF=y
CONFIG_BOARD_LOOPSPERMSEC=0
CONFIG_BOOT_RUNFROMEXTSRAM=y
CONFIG_BTSAK=y
CONFIG_BUILTIN=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_LOOP=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_BLUETOOTH=y
CONFIG_DRIVERS_WIRELESS=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
CONFIG_FSUTILS_PASSWD=y
CONFIG_FSUTILS_PASSWD_READONLY=y
CONFIG_FS_FAT=y
CONFIG_FS_PROCFS=y
CONFIG_FS_ROMFS=y
CONFIG_IDLETHREAD_STACKSIZE=4096
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIB_ENVPATH=y
CONFIG_LIB_HOSTNAME="NuttX-SIM"
CONFIG_MAX_TASKS=64
CONFIG_NET=y
CONFIG_NETDEVICES=y
CONFIG_NETDEV_LATEINIT=y
CONFIG_NETDEV_WIRELESS_IOCTL=y
CONFIG_NETINIT_NETLOCAL=y
CONFIG_NET_BLUETOOTH=y
CONFIG_NET_STATISTICS=y
CONFIG_NFILE_DESCRIPTORS=32
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_ARCHROMFS=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FATDEVNO=2
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_NSH_ROMFSDEVNO=1
CONFIG_NSH_ROMFSETC=y
CONFIG_PATH_INITIAL="/bin"
CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048
CONFIG_PREALLOC_MQ_MSGS=64
CONFIG_READLINE_TABCOMPLETION=y
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_ONEXIT=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_SIM_HCISOCKET=y
CONFIG_SIM_WALLTIME=y
CONFIG_START_DAY=3
CONFIG_START_MONTH=4
CONFIG_SYSTEM_NSH=y
CONFIG_USERMAIN_STACKSIZE=4096
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_WIRELESS=y
CONFIG_WIRELESS_BLUETOOTH=y

View File

@ -301,5 +301,15 @@ int sim_bringup(void)
}
#endif
#ifdef CONFIG_SIM_HCISOCKET
/* Register the Host Bluetooth network device via HCI socket */
ret = bthcisock_register(0); /* Use HCI0 */
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: bthcisock_register() failed: %d\n", ret);
}
#endif
return ret;
}