diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 6ffd1a1fd41..28f8cf4e7e7 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -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 diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index addcf7d48e8..b66c8997592 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -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 diff --git a/arch/sim/src/sim/up_hcisocket.c b/arch/sim/src/sim/up_hcisocket.c new file mode 100644 index 00000000000..2c0ba837602 --- /dev/null +++ b/arch/sim/src/sim/up_hcisocket.c @@ -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 + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#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; +} diff --git a/arch/sim/src/sim/up_hcisocket_host.c b/arch/sim/src/sim/up_hcisocket_host.c new file mode 100644 index 00000000000..d785c4240a3 --- /dev/null +++ b/arch/sim/src/sim/up_hcisocket_host.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/arch/sim/src/sim/up_hcisocket_host.h b/arch/sim/src/sim/up_hcisocket_host.h new file mode 100644 index 00000000000..5ba3a13a9e2 --- /dev/null +++ b/arch/sim/src/sim/up_hcisocket_host.h @@ -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 +#include + +/**************************************************************************** + * 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_ */ diff --git a/arch/sim/src/sim/up_idle.c b/arch/sim/src/sim/up_idle.c index 64cf77459e2..e5be5cd826e 100644 --- a/arch/sim/src/sim/up_idle.c +++ b/arch/sim/src/sim/up_idle.c @@ -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 */ diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index 33a875a3b13..26e3f5102bb 100644 --- a/arch/sim/src/sim/up_internal.h +++ b/arch/sim/src/sim/up_internal.h @@ -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 diff --git a/boards/sim/sim/sim/README.txt b/boards/sim/sim/sim/README.txt index 8ee4e9e08c9..284b86e6184 100644 --- a/boards/sim/sim/sim/README.txt +++ b/boards/sim/sim/sim/README.txt @@ -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. diff --git a/boards/sim/sim/sim/configs/bthcisock/defconfig b/boards/sim/sim/sim/configs/bthcisock/defconfig new file mode 100644 index 00000000000..1e9f9a53559 --- /dev/null +++ b/boards/sim/sim/sim/configs/bthcisock/defconfig @@ -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 diff --git a/boards/sim/sim/sim/src/sim_bringup.c b/boards/sim/sim/sim/src/sim_bringup.c index 9cad7fb4eb1..d6e02710934 100644 --- a/boards/sim/sim/sim/src/sim_bringup.c +++ b/boards/sim/sim/sim/src/sim_bringup.c @@ -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; }