diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 64f034e94f0..cc89a83cf3d 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -524,16 +524,30 @@ config SIM_QSPIFLASH_PAGESIZE "wrap" causing the initial data sent to be overwritten. This is consistent with standard SPI FLASH operation. +choice + prompt "Simulated Prototype of HCI" + default SIM_HCISOCKET + depends on (HOST_LINUX && SIM_WALLTIME) + config SIM_HCISOCKET bool "Attach Host Bluetooth" - default false - depends on (WIRELESS_BLUETOOTH && HOST_LINUX && SIM_WALLTIME) + depends on WIRELESS_BLUETOOTH ---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. +config SIM_HCITTY + bool "Attach Host Bluetooth As TTY Device" + ---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. + +endchoice + config SIM_I2CBUS bool "Simulated I2C Bus" default n diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 9f9fa87bba1..637c8222c2f 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -201,6 +201,11 @@ ifeq ($(CONFIG_SIM_HCISOCKET),y) CSRCS += up_hcisocket.c endif +ifeq ($(CONFIG_SIM_HCITTY),y) + HOSTSRCS += up_hcisocket_host.c + CSRCS += up_hcitty.c +endif + ifeq ($(CONFIG_I2C_RESET),y) HOSTCFLAGS += -DCONFIG_I2C_RESET=1 endif diff --git a/arch/sim/src/sim/up_hcitty.c b/arch/sim/src/sim/up_hcitty.c new file mode 100644 index 00000000000..33d1f3fc333 --- /dev/null +++ b/arch/sim/src/sim/up_hcitty.c @@ -0,0 +1,366 @@ +/**************************************************************************** + * arch/sim/src/sim/up_hcitty.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 "up_internal.h" +#include "up_hcisocket_host.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CONFIG_HCI_RECVBUF_SIZE 1024 +#define CONFIG_HCI_SENDBUF_SIZE 1024 +#define CONFIG_HCI_NPOLLWAITERS 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct bthcitty_s +{ + uint8_t recvbuf[CONFIG_HCI_RECVBUF_SIZE]; + size_t recvpos; + size_t recvlen; + sem_t recvsem; + sem_t recvlock; + + uint8_t sendbuf[CONFIG_HCI_SENDBUF_SIZE]; + size_t sendlen; + sem_t sendlock; + + sem_t fdslock; + FAR struct pollfd *fds[CONFIG_HCI_NPOLLWAITERS]; + + unsigned short id; + int fd; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int bthcitty_open (FAR struct file *filep); +static int bthcitty_close (FAR struct file *filep); +static ssize_t bthcitty_read (FAR struct file *filep, + FAR char *buffer, size_t buflen); +static ssize_t bthcitty_write (FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static int bthcitty_ioctl (FAR struct file *filep, + int cmd, unsigned long arg); +static int bthcitty_poll (FAR struct file *filep, + FAR struct pollfd *fds, bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_hcitty_ops = +{ + .open = bthcitty_open, + .close = bthcitty_close, + .read = bthcitty_read, + .write = bthcitty_write, + .ioctl = bthcitty_ioctl, + .poll = bthcitty_poll +}; + +static struct bthcitty_s g_hcitty = +{ + .fd = -1, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void bthcitty_post(FAR sem_t *sem) +{ + int semcount; + + nxsem_get_value(sem, &semcount); + if (semcount < 1) + { + nxsem_post(sem); + } +} + +static void bthcitty_pollnotify(FAR struct bthcitty_s *dev, + pollevent_t eventset) +{ + int ret; + int i; + + ret = nxsem_wait_uninterruptible(&dev->fdslock); + if (ret < 0) + { + return ret; + } + + for (i = 0; i < CONFIG_HCI_NPOLLWAITERS; i++) + { + FAR struct pollfd *fds = dev->fds[i]; + + if (fds) + { + fds->revents |= (fds->events & eventset); + + if (fds->revents != 0) + { + bthcitty_post(fds->sem); + } + } + } + + bthcitty_post(&dev->recvsem); + + nxsem_post(&dev->fdslock); +} + +static int bthcitty_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct bthcitty_s *dev = inode->i_private; + int ret; + int fd; + + fd = bthcisock_host_open(dev->id); + if (fd < 0) + { + return fd; + } + + dev->sendlen = 0; + dev->recvpos = 0; + dev->recvlen = 0; + dev->fd = fd; + + return OK; +} + +static int bthcitty_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct bthcitty_s *dev = inode->i_private; + + bthcisock_host_close(dev->fd); + + dev->fd = -1; + + bthcitty_pollnotify(dev, POLLIN | POLLOUT); + + return 0; +} + +static ssize_t bthcitty_read(FAR struct file *filep, + FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct bthcitty_s *dev = inode->i_private; + size_t len = dev->recvlen; + int ret; + + ret = nxsem_wait_uninterruptible(&dev->recvlock); + if (ret < 0) + { + return ret; + } + + if (dev->recvpos >= dev->recvlen) + { + while (!bthcisock_host_avail(dev->fd)) + { + nxsem_wait_uninterruptible(&dev->recvsem); + } + + len = bthcisock_host_read(dev->fd, dev->recvbuf, + CONFIG_HCI_RECVBUF_SIZE); + if (len <= 0) + { + nxsem_post(&dev->recvlock); + return len; + } + + dev->recvpos = 0; + dev->recvlen = len; + } + + if (buflen > dev->recvlen - dev->recvpos) + { + buflen = dev->recvlen - dev->recvpos; + } + + memcpy(buffer, dev->recvbuf + dev->recvpos, buflen); + dev->recvpos += buflen; + + nxsem_post(&dev->recvlock); + + return buflen; +} + +static ssize_t bthcitty_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct bthcitty_s *dev = inode->i_private; + size_t minlen = 1 + 2 + 1; /* header + opcode + len */ + size_t len = buflen; + int ret; + + ret = nxsem_wait_uninterruptible(&dev->sendlock); + if (ret < 0) + { + return ret; + } + + if (dev->sendlen > 0 || buflen < minlen) + { + memcpy(dev->sendbuf + dev->sendlen, buffer, buflen); + dev->sendlen += buflen; + + buffer = dev->sendbuf; + len = dev->sendlen; + } + + if (len >= minlen) + { + ret = bthcisock_host_send(dev->fd, buffer, len); + if (ret >= 0) + { + dev->sendlen = 0; + } + } + + nxsem_post(&dev->sendlock); + + return ret < 0 ? ret : buflen; +} + +static int bthcitty_ioctl(FAR struct file *filep, + int cmd, unsigned long arg) +{ + return OK; +} + +static int bthcitty_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct bthcitty_s *dev = inode->i_private; + pollevent_t eventset; + int ret; + int i; + + ret = nxsem_wait_uninterruptible(&dev->fdslock); + if (ret < 0) + { + return ret; + } + + if (setup) + { + for (i = 0; i < CONFIG_HCI_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!dev->fds[i]) + { + /* Bind the poll structure and this slot */ + + dev->fds[i] = fds; + fds->priv = &dev->fds[i]; + break; + } + } + + if (i >= CONFIG_HCI_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + } + + if (bthcisock_host_avail(dev->fd)) + { + eventset |= (fds->events & POLLIN); + } + + eventset |= (fds->events & POLLOUT); + + if (eventset) + { + bthcitty_pollnotify(dev, eventset); + } + } + else if (fds->priv != NULL) + { + for (i = 0; i < CONFIG_HCI_NPOLLWAITERS; i++) + { + if (fds == dev->fds[i]) + { + dev->fds[i] = NULL; + fds->priv = NULL; + break; + } + } + } + + nxsem_post(&dev->fdslock); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void bthcitty_loop(void) +{ + if (bthcisock_host_avail(g_hcitty.fd)) + { + bthcitty_pollnotify(&g_hcitty, POLLIN); + } +} + +int bthcitty_register(int dev_id) +{ + unsigned char name[16]; + + snprintf(name, sizeof(name), "/dev/ttyBT%d", dev_id); + + g_hcitty.id = dev_id; + + nxsem_init(&g_hcitty.recvlock, 0, 1); + nxsem_init(&g_hcitty.sendlock, 0, 1); + nxsem_init(&g_hcitty.recvsem, 0, 0); + nxsem_set_protocol(&g_hcitty.recvsem, SEM_PRIO_NONE); + nxsem_init(&g_hcitty.fdslock, 0, 1); + + return register_driver(name, &g_hcitty_ops, 0666, &g_hcitty); +} diff --git a/arch/sim/src/sim/up_idle.c b/arch/sim/src/sim/up_idle.c index eacffeb660c..8bc09d5bfd0 100644 --- a/arch/sim/src/sim/up_idle.c +++ b/arch/sim/src/sim/up_idle.c @@ -113,6 +113,10 @@ void up_idle(void) bthcisock_loop(); #endif +#ifdef CONFIG_SIM_HCITTY + bthcitty_loop(); +#endif + #ifdef CONFIG_SIM_SOUND sim_audio_loop(); #endif diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index 8c1f77b44a3..f93af5506e5 100644 --- a/arch/sim/src/sim/up_internal.h +++ b/arch/sim/src/sim/up_internal.h @@ -398,6 +398,13 @@ int bthcisock_register(int dev_id); int bthcisock_loop(void); #endif +/* up_hcitty.c **************************************************************/ + +#ifdef CONFIG_SIM_HCITTY +int bthcitty_register(int dev_id); +void bthcitty_loop(void); +#endif + /* up_audio.c ***************************************************************/ #ifdef CONFIG_SIM_SOUND diff --git a/boards/sim/sim/sim/src/sim_bringup.c b/boards/sim/sim/sim/src/sim_bringup.c index 852cf0b13c8..643cbafd4da 100644 --- a/boards/sim/sim/sim/src/sim_bringup.c +++ b/boards/sim/sim/sim/src/sim_bringup.c @@ -344,6 +344,16 @@ int sim_bringup(void) } #endif +#ifdef CONFIG_SIM_HCITTY + /* Register the Host Bluetooth network device via HCI socket */ + + ret = bthcitty_register(0); /* Use HCI0 */ + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: bthcitty_register() failed: %d\n", ret); + } +#endif + #ifdef CONFIG_SIM_I2CBUS /* Initialize the i2c master bus device */