diff --git a/arch/sim/src/sim/up_hcisocket.c b/arch/sim/src/sim/up_hcisocket.c index 0fd130f352..f1eda8e4b2 100644 --- a/arch/sim/src/sim/up_hcisocket.c +++ b/arch/sim/src/sim/up_hcisocket.c @@ -39,6 +39,10 @@ #include #include +#if defined(CONFIG_UART_BTH4) + #include +#endif + #include "up_internal.h" #include "up_hcisocket_host.h" @@ -219,6 +223,9 @@ static FAR struct bthcisock_s *bthcisock_alloc(int dev_id) int bthcisock_register(int dev_id) { FAR struct bthcisock_s *dev; +#if defined(CONFIG_UART_BTH4) + char name[32]; +#endif int ret; dev = bthcisock_alloc(dev_id); @@ -227,7 +234,12 @@ int bthcisock_register(int dev_id) return -ENOMEM; } +#if defined(CONFIG_UART_BTH4) + snprintf(name, sizeof(name), "/dev/ttyHCI%d", dev_id); + ret = uart_bth4_register(name, &dev->drv); +#else ret = bt_netdev_register(&dev->drv); +#endif if (ret < 0) { kmm_free(dev); diff --git a/boards/sim/sim/sim/configs/btuart/defconfig b/boards/sim/sim/sim/configs/btuart/defconfig new file mode 100644 index 0000000000..aecc21d845 --- /dev/null +++ b/boards/sim/sim/sim/configs/btuart/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_BLUETOOTH_UART=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_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_START_DAY=3 +CONFIG_START_MONTH=4 +CONFIG_SYSTEM_NSH=y +CONFIG_UART_BTH4=y +CONFIG_USERMAIN_STACKSIZE=4096 +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WIRELESS=y +CONFIG_WIRELESS_BLUETOOTH=y diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index df266481ba..3611fa4e87 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -617,4 +617,37 @@ config PSEUDOTERM_TXBUFSIZE endif # PSEUDOTERM +menuconfig UART_BTH4 + bool "BT H4 uart pseudo device" + default n + select MM_CIRCBUF + ---help--- + Enable support for Bluetooth H4 UART Pseudo Device(eg. /dev/ttyHCI). + This instantiates a serial-like interface over an existing bluetooth + controller via HCI interface. Useful for external Bluetooth + stacks working this way instead of the socket based interface. + +if UART_BTH4 + +config UART_BTH4_TXBUFSIZE + int "BT H4 uart TX buffer size" + default 1024 + ---help--- + H4 UART TX buffer size. Default: 1024 + +config UART_BTH4_RXBUFSIZE + int "BT H4 uart RX buffer size" + default 1024 + ---help--- + H4 UART RX buffer size. Default: 1024 + +config UART_BTH4_NPOLLWAITERS + int "Number of poll threads" + default 2 + ---help--- + Maximum number of threads than can be waiting for POLL events. + Default: 2 + +endif # UART_BTH4 + endif # SERIAL diff --git a/drivers/serial/Make.defs b/drivers/serial/Make.defs index d301572985..d75e693871 100644 --- a/drivers/serial/Make.defs +++ b/drivers/serial/Make.defs @@ -51,6 +51,12 @@ ifeq ($(CONFIG_PSEUDOTERM_SUSV1),y) endif endif +# Bluetooth H:4 UART driver + +ifeq ($(CONFIG_UART_BTH4),y) + CSRCS += uart_bth4.c +endif + # Include serial build support DEPPATH += --dep-path serial diff --git a/drivers/serial/uart_bth4.c b/drivers/serial/uart_bth4.c new file mode 100644 index 0000000000..c1acd35c59 --- /dev/null +++ b/drivers/serial/uart_bth4.c @@ -0,0 +1,455 @@ +/**************************************************************************** + * drivers/serial/uart_bth4.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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +union bt_hdr_u +{ + struct bt_hci_cmd_hdr_s cmd; + struct bt_hci_acl_hdr_s acl; + struct bt_hci_evt_hdr_s evt; + struct bt_hci_iso_hdr_s iso; +}; + +struct uart_bth4_s +{ + FAR struct bt_driver_s *drv; + + FAR struct circbuf_s circbuf; + + sem_t recvsem; + + uint8_t sendbuf[CONFIG_UART_BTH4_TXBUFSIZE]; + size_t sendlen; + sem_t sendlock; + + FAR struct pollfd *fds[CONFIG_UART_BTH4_NPOLLWAITERS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int uart_bth4_open (FAR struct file *filep); +static int uart_bth4_close(FAR struct file *filep); +static ssize_t uart_bth4_read (FAR struct file *filep, + FAR char *buffer, size_t buflen); +static ssize_t uart_bth4_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static int uart_bth4_ioctl(FAR struct file *filep, + int cmd, unsigned long arg); +static int uart_bth4_poll (FAR struct file *filep, + FAR struct pollfd *fds, bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_uart_bth4_ops = +{ + .open = uart_bth4_open, + .close = uart_bth4_close, + .read = uart_bth4_read, + .write = uart_bth4_write, + .ioctl = uart_bth4_ioctl, + .poll = uart_bth4_poll +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void uart_bth4_post(FAR sem_t *sem) +{ + int semcount; + + nxsem_get_value(sem, &semcount); + if (semcount < 1) + { + nxsem_post(sem); + } +} + +static void uart_bth4_pollnotify(FAR struct uart_bth4_s *dev, + pollevent_t eventset) +{ + int i; + + for (i = 0; i < CONFIG_UART_BTH4_NPOLLWAITERS; i++) + { + FAR struct pollfd *fds = dev->fds[i]; + + if (fds) + { + fds->revents |= (fds->events & eventset); + + if (fds->revents != 0) + { + uart_bth4_post(fds->sem); + } + } + } + + if ((eventset & POLLIN) != 0) + { + uart_bth4_post(&dev->recvsem); + } +} + +static int uart_bth4_receive(FAR struct bt_driver_s *drv, + enum bt_buf_type_e type, + FAR void *buffer, size_t buflen) +{ + FAR struct uart_bth4_s *dev = drv->priv; + int ret = buflen; + irqstate_t flags; + uint8_t htype; + + flags = enter_critical_section(); + + if (circbuf_space(&dev->circbuf) >= + buflen + H4_HEADER_SIZE) + { + if (type == BT_EVT) + { + htype = H4_EVT; + } + else if (type == BT_ACL_IN) + { + htype = H4_ACL; + } + else if (type == BT_ISO_IN) + { + htype = H4_ISO; + } + else + { + ret = -EINVAL; + } + + if (ret >= 0) + { + circbuf_write(&dev->circbuf, &htype, H4_HEADER_SIZE); + circbuf_write(&dev->circbuf, buffer, buflen); + uart_bth4_pollnotify(dev, POLLIN); + } + } + + leave_critical_section(flags); + return ret; +} + +static int uart_bth4_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct uart_bth4_s *dev = inode->i_private; + int ret; + + ret = dev->drv->open(dev->drv); + if (ret < 0) + { + return ret; + } + + dev->sendlen = 0; + + return OK; +} + +static int uart_bth4_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct uart_bth4_s *dev = inode->i_private; + + dev->drv->close(dev->drv); + + uart_bth4_pollnotify(dev, POLLIN | POLLOUT); + return OK; +} + +static ssize_t uart_bth4_read(FAR struct file *filep, + FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct uart_bth4_s *dev = inode->i_private; + irqstate_t flags; + ssize_t nread; + + flags = enter_critical_section(); + + for (; ; ) + { + nread = circbuf_read(&dev->circbuf, buffer, buflen); + if (nread != 0 || (filep->f_oflags & O_NONBLOCK)) + { + break; + } + + while (circbuf_is_empty(&dev->circbuf)) + { + nxsem_wait_uninterruptible(&dev->recvsem); + } + } + + leave_critical_section(flags); + return nread; +} + +static ssize_t uart_bth4_write(FAR struct file *filep, + FAR const char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct uart_bth4_s *dev = inode->i_private; + FAR union bt_hdr_u *hdr; + enum bt_buf_type_e type; + size_t reserved; + uint8_t *data; + size_t pktlen; + size_t hdrlen; + int ret; + + ret = nxsem_wait_uninterruptible(&dev->sendlock); + if (ret < 0) + { + return ret; + } + + if (dev->drv->head_reserve < H4_HEADER_SIZE) + { + reserved = H4_HEADER_SIZE; + } + else + { + reserved = dev->drv->head_reserve; + } + + data = dev->sendbuf + reserved; + + if (dev->sendlen + buflen > CONFIG_UART_BTH4_TXBUFSIZE - reserved) + { + ret = -E2BIG; + goto err; + } + + memcpy(data - H4_HEADER_SIZE + dev->sendlen, + buffer, buflen); + dev->sendlen += buflen; + + hdr = (FAR union bt_hdr_u *)data; + + for (; ; ) + { + switch (*(data - H4_HEADER_SIZE)) + { + case H4_CMD: + hdrlen = sizeof(struct bt_hci_cmd_hdr_s); + pktlen = hdr->cmd.param_len; + type = BT_CMD; + break; + case H4_ACL: + hdrlen = sizeof(struct bt_hci_acl_hdr_s); + pktlen = hdr->acl.len; + type = BT_ACL_OUT; + break; + case H4_ISO: + hdrlen = sizeof(struct bt_hci_iso_hdr_s); + pktlen = hdr->iso.len; + type = BT_ISO_OUT; + break; + default: + ret = -EINVAL; + goto err; + } + + /* Reassembly is incomplete ? */ + + hdrlen += H4_HEADER_SIZE; + + if (dev->sendlen < hdrlen) + { + goto out; + } + + pktlen += hdrlen; + if (dev->sendlen < pktlen) + { + goto out; + } + + /* Got the full packet, send out */ + + ret = dev->drv->send(dev->drv, type, + data, pktlen - H4_HEADER_SIZE); + if (ret < 0) + { + goto err; + } + + dev->sendlen -= pktlen; + if (dev->sendlen == 0) + { + goto out; + } + + memmove(data - H4_HEADER_SIZE, + dev->sendbuf + pktlen, dev->sendlen); + } + +err: + dev->sendlen = 0; +out: + nxsem_post(&dev->sendlock); + return ret < 0 ? ret : buflen; +} + +static int uart_bth4_ioctl(FAR struct file *filep, + int cmd, unsigned long arg) +{ + return OK; +} + +static int uart_bth4_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct uart_bth4_s *dev = inode->i_private; + pollevent_t eventset = 0; + irqstate_t flags; + int ret = 0; + int i; + + flags = enter_critical_section(); + + if (setup) + { + for (i = 0; i < CONFIG_UART_BTH4_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_UART_BTH4_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + } + + if (!circbuf_is_empty(&dev->circbuf)) + { + eventset |= (fds->events & POLLIN); + } + + eventset |= (fds->events & POLLOUT); + + if (eventset) + { + uart_bth4_pollnotify(dev, eventset); + } + } + else if (fds->priv != NULL) + { + for (i = 0; i < CONFIG_UART_BTH4_NPOLLWAITERS; i++) + { + if (fds == dev->fds[i]) + { + dev->fds[i] = NULL; + fds->priv = NULL; + break; + } + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int uart_bth4_register(FAR const char *path, FAR struct bt_driver_s *drv) +{ + FAR struct uart_bth4_s *dev; + int ret; + + dev = (FAR struct uart_bth4_s *) + kmm_zalloc(sizeof(struct uart_bth4_s)); + if (dev == NULL) + { + return -ENOMEM; + } + + ret = circbuf_init(&dev->circbuf, NULL, + CONFIG_UART_BTH4_RXBUFSIZE); + if (ret < 0) + { + kmm_free(dev); + return -ENOMEM; + } + + dev->drv = drv; + drv->receive = uart_bth4_receive; + drv->priv = dev; + + nxsem_init(&dev->sendlock, 0, 1); + nxsem_init(&dev->recvsem, 0, 0); + + nxsem_set_protocol(&dev->recvsem, SEM_PRIO_NONE); + + ret = register_driver(path, &g_uart_bth4_ops, 0666, dev); + if (ret < 0) + { + nxsem_destroy(&dev->sendlock); + nxsem_destroy(&dev->recvsem); + circbuf_uninit(&dev->circbuf); + kmm_free(dev); + } + + return ret; +} diff --git a/include/nuttx/serial/uart_bth4.h b/include/nuttx/serial/uart_bth4.h new file mode 100644 index 0000000000..f0e35b52f7 --- /dev/null +++ b/include/nuttx/serial/uart_bth4.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * include/nuttx/serial/uart_bth4.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 __INCLUDE_NUTTX_SERIAL_UART_BTH4_H +#define __INCLUDE_NUTTX_SERIAL_UART_BTH4_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: uart_bth4_register + * + * Description: + * Register bluetooth H:4 UART driver. + * + ****************************************************************************/ + +int uart_bth4_register(FAR const char *path, FAR struct bt_driver_s *drv); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_SERIAL_UART_BTH4_H */