diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 6ffd1a1fd418e..28f8cf4e7e79a 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 addcf7d48e82f..b66c89975927b 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 0000000000000..8bffdab784192 --- /dev/null +++ b/arch/sim/src/sim/up_hcisocket.c @@ -0,0 +1,225 @@ +/**************************************************************************** + * 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 */ + +/* This frame buffer is used to handle the frame read from the host + * NOTE: This is smaller than what the Linux kernel lists as its max frame + * so we will need to resolve that at some point. + */ + +static uint8_t bt_frame[BLUETOOTH_MAX_FRAMELEN + 1]; + +/**************************************************************************** + * 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; + } + + bthcisock_read(); + + 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_read + * + * Description: + * Register the Linux HCI interface with the Bluetooth stack + * + * Input Parameters: + * None + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +void bthcisock_read() +{ + struct bt_buf_s *outbuf; + uint8_t type; + size_t len; + + if (bt_fd < 0) + { + /* Internal socket has not yet been created */ + + return; + } + + len = bthcisock_host_read(bt_fd, &type, bt_frame, sizeof(bt_frame)); + if (len < 0) + { + /* Failed to read, but that is likely because there is no packet */ + + return; + } + + switch (type) + { + case HCI_EVENT_PKT: + { + outbuf = bt_buf_alloc(BT_EVT, NULL, 0); + if (outbuf == NULL) + { + wlerr("ERROR: Failed to allocate buffer\n"); + return -ENOMEM; + } + + /* First byte represents the type, so we strip that off */ + + outbuf->len = len - 1; + memcpy(outbuf->data, &bt_frame[1], outbuf->len); + bt_hci_receive(outbuf); + break; + } + + default: + break; + } +} 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 0000000000000..79d498abef71c --- /dev/null +++ b/arch/sim/src/sim/up_hcisocket_host.c @@ -0,0 +1,191 @@ +/**************************************************************************** + * 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 + +/* HCI data types */ + +#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 + ****************************************************************************/ + +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; + while ((err = recv(fd, buf, len, 0)) < 0 && (errno == EINTR)); + if (err < 0) + { + return err; + } + + *type = ((uint8_t *)buf)[0]; + return err; +} + +/**************************************************************************** + * 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 0000000000000..5ba3a13a9e2c1 --- /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 64cf77459e29f..d95babc12c925 100644 --- a/arch/sim/src/sim/up_idle.c +++ b/arch/sim/src/sim/up_idle.c @@ -116,4 +116,8 @@ void up_idle(void) up_timer_update(); #endif + +#ifdef CONFIG_SIM_HCISOCKET + bthcisock_read(); +#endif } diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index 33a875a3b13dd..e8f1c50ea0168 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); +void bthcisock_read(void); +#endif + /* Debug ********************************************************************/ #ifdef CONFIG_STACK_COLORATION diff --git a/boards/sim/sim/sim/README.txt b/boards/sim/sim/sim/README.txt index 8ee4e9e08c965..284b86e618427 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 0000000000000..1e9f9a53559cb --- /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 9cad7fb4eb1f1..d6e027109349d 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; }