This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 89c4b8ccaf03f1c6d33624d93ef640d8d176a78d
Author: Lingao Meng <[email protected]>
AuthorDate: Tue Sep 8 17:18:25 2026 +0800

    arch/sim: Add runtime HCI socket target option
    
    Allow sim HCI socket users to select the host-side HCI target at runtime
    with --bt-dev.  Passing --bt-dev=hciN overrides CONFIG_SIM_HCISOCKET_DEVID
    for the BlueZ HCI user channel, while omitting the option keeps the existing
    configured default behavior.
    
    Also allow --bt-dev=/path/to/socket to connect to an H:4 stream exposed
    through a Unix-domain socket.  This lets sim applications use a controller
    provided by another host process or by a UART-to-Unix-socket bridge without
    requiring BlueZ raw HCI privileges for the NuttX process.
    
    Use host-side output for early --bt-dev parse errors, since NuttX stdio is
    not initialized before nx_start().
    
    Document the BlueZ and Unix socket modes, including the capability
    requirements for BlueZ and the socat bridge example for Unix socket mode.
    
    Testing:
    
      Host: Ubuntu 22.04 x86_64
      Board/config: sim:bthcisock
    
      Style checks:
    
        git diff --check HEAD~2..HEAD
        PATH=/home/mi/bsim-auto-test/.venv/bin:$PATH \
          ./tools/checkpatch.sh -c -u -m -g HEAD~2..HEAD
    
      Clean build:
    
        make distclean
        ./tools/configure.sh -l -a ../../nuttx-apps sim:bthcisock
        kconfig-tweak --file .config --set-val STACK_USAGE_WARNING 0
        make olddefconfig
        make -j16
    
      Invalid runtime argument smoke test:
    
        ./nuttx --bt-dev=invalid
    
      Verified the command exits with status 1 and reports the invalid target
      without crashing before nx_start().
    
      Unix socket HCI smoke test:
    
        socat -d -d UNIX-LISTEN:/tmp/hci.sock,fork,reuseaddr \
          /dev/ttyACM2,b1000000,raw,echo=0,crtscts=1
        printf 'ifconfig\nbt bnep0 info\npoweroff\n' | \
          timeout 20s ./nuttx --bt-dev=/tmp/hci.sock
    
      Verified the sim registers the Bluetooth network device as bnep0 and
      bt bnep0 info reads the controller state through the Unix-socket HCI
      path, including BDAddr aa:bb:cc:dd:ee:ff from the attached controller.
    
    Assisted-by: OpenAI Codex
    Signed-off-by: Lingao Meng <[email protected]>
---
 .../platforms/sim/sim/boards/sim/index.rst         |  35 ++++-
 arch/sim/Kconfig                                   |   5 +-
 arch/sim/src/sim/posix/sim_hosthcisocket.c         | 146 ++++++++++++++++++++-
 arch/sim/src/sim/sim_head.c                        |  13 ++
 arch/sim/src/sim/sim_hosthcisocket.h               |   1 +
 5 files changed, 191 insertions(+), 9 deletions(-)

diff --git a/Documentation/platforms/sim/sim/boards/sim/index.rst 
b/Documentation/platforms/sim/sim/boards/sim/index.rst
index 4326dac0318..c11abb6f7c7 100644
--- a/Documentation/platforms/sim/sim/boards/sim/index.rst
+++ b/Documentation/platforms/sim/sim/boards/sim/index.rst
@@ -651,18 +651,49 @@ 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 bluetooth 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:
+with the configured default HCI device, you must give the ``nuttx`` ELF
+additional capabilities:
 
 .. code:: console
 
    $ sudo setcap 'cap_net_raw,cap_net_admin=eip' ./nuttx
 
-You can then monitor the HCI traffic on the host with WireShark or ``btmon``:
+The default HCI device is selected by ``CONFIG_SIM_HCISOCKET_DEVID``. It may be
+overridden at runtime with ``--bt-dev=hciN``:
+
+.. code:: console
+
+   $ ./nuttx --bt-dev=hci1
+
+You can then monitor the BlueZ HCI traffic on the host with WireShark or
+``btmon``:
 
 .. code:: console
 
    $ sudo btmon
 
+The sim target can also connect to an HCI H:4 stream exposed through a Unix
+domain socket by passing an absolute socket path:
+
+.. code:: console
+
+   $ ./nuttx --bt-dev=/tmp/hci0.sock
+
+This Unix socket mode does not require a BlueZ HCI device. Since ``nuttx`` only
+connects to a normal Unix domain socket, the ``nuttx`` process does not need to
+run as root and does not need the ``setcap`` command above. This is useful when
+the controller is provided by another host process, or when a UART controller 
is
+bridged into a Unix socket with a tool such as ``socat``:
+
+.. code:: console
+
+   $ socat UNIX-LISTEN:/tmp/hci0.sock,fork,reuseaddr \
+       /dev/ttyACM0,b1000000,raw,echo=0,crtscts=1
+
+The process listening on the socket must be started before NuttX. If that
+process opens a real UART device, it still needs permission to access that UART
+device.
+
 configdata
 ----------
 
diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig
index 29675843803..7aaedd23700 100644
--- a/arch/sim/Kconfig
+++ b/arch/sim/Kconfig
@@ -672,6 +672,9 @@ config SIM_HCISOCKET
                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.
+               The default BlueZ device can be overridden at runtime
+               with --bt-dev=hciN, or replaced by a Unix-domain HCI
+               socket with --bt-dev=/path/to/socket.
 
 config SIM_HCISOCKET_DEVID
        int "Bluetooth Device ID"
@@ -679,7 +682,7 @@ config SIM_HCISOCKET_DEVID
        depends on SIM_HCISOCKET
        ---help---
                Attached the local bluetooth device use specific
-               Bluetooth HCI number id.
+               Bluetooth HCI number id when --bt-dev is not passed.
 
 config SIM_I2CBUS
        bool "Simulated I2C Bus"
diff --git a/arch/sim/src/sim/posix/sim_hosthcisocket.c 
b/arch/sim/src/sim/posix/sim_hosthcisocket.c
index e2bde0aede2..d4235e13712 100644
--- a/arch/sim/src/sim/posix/sim_hosthcisocket.c
+++ b/arch/sim/src/sim/posix/sim_hosthcisocket.c
@@ -28,9 +28,12 @@
 #include <sys/uio.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <sys/un.h>
 
+#include <stddef.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
@@ -60,10 +63,126 @@ struct sockaddr_hci
   unsigned short  hci_channel;
 };
 
+enum bthcisock_target_e
+{
+  BTHCISOCK_TARGET_DEFAULT = 0,
+  BTHCISOCK_TARGET_BLUEZ,
+  BTHCISOCK_TARGET_UNIX
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static enum bthcisock_target_e g_bthcisock_target;
+static int g_bthcisock_devid;
+static char g_bthcisock_path[sizeof(((struct sockaddr_un *)0)->sun_path)];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int host_bthcisock_parse_devid(const char *target, int *devid)
+{
+  if (strncmp(target, "hci", 3) != 0 || target[3] == '\0')
+    {
+      return -EINVAL;
+    }
+
+  target += 3;
+  *devid = atoi(target);
+  return 0;
+}
+
+static int host_bthcisock_open_unix(const char *path)
+{
+  struct sockaddr_un addr;
+  size_t len;
+  int ret;
+  int fd;
+
+  len = strlen(path);
+  if (len == 0)
+    {
+      return -EINVAL;
+    }
+
+  if (len >= sizeof(addr.sun_path))
+    {
+      return -ENAMETOOLONG;
+    }
+
+  fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+  if (fd < 0)
+    {
+      return -errno;
+    }
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path, path, len + 1);
+
+  ret = connect(fd, (struct sockaddr *)&addr,
+                offsetof(struct sockaddr_un, sun_path) + len + 1);
+  if (ret < 0)
+    {
+      ret = -errno;
+      close(fd);
+      return ret;
+    }
+
+  return fd;
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: host_bthcisock_configure
+ *
+ * Description:
+ *   Override the default HCI target. Accepted values:
+ *   - "hci<n>" for BlueZ HCI user channel
+ *   - "/path/to.sock" for Unix-domain HCI socket
+ *
+ ****************************************************************************/
+
+int host_bthcisock_configure(const char *target)
+{
+  size_t len;
+  int ret;
+  int devid;
+
+  if (target == NULL || target[0] == '\0')
+    {
+      return -EINVAL;
+    }
+
+  if (target[0] == '/')
+    {
+      len = strlen(target);
+      if (len >= sizeof(g_bthcisock_path))
+        {
+          return -ENAMETOOLONG;
+        }
+
+      memcpy(g_bthcisock_path, target, len + 1);
+      g_bthcisock_target = BTHCISOCK_TARGET_UNIX;
+      return 0;
+    }
+
+  ret = host_bthcisock_parse_devid(target, &devid);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  g_bthcisock_devid = devid;
+  g_bthcisock_target = BTHCISOCK_TARGET_BLUEZ;
+  return 0;
+}
+
 /****************************************************************************
  * Name: host_bthcisock_avail
  *
@@ -127,7 +246,7 @@ int host_bthcisock_send(int fd, const void *data, size_t 
len)
           continue;
         }
 
-      return -1;
+      return -errno;
     }
 
   return 0;
@@ -160,7 +279,7 @@ int host_bthcisock_receive(int fd, void *data, size_t len)
     {
       /* Both an empty read and an error are "error" conditions */
 
-      return -1;
+      return err < 0 ? -errno : -ECONNRESET;
     }
 
   /* Return the number of bytes written to data */
@@ -189,11 +308,23 @@ int host_bthcisock_open(int dev_idx)
 {
   int err;
   struct sockaddr_hci addr;
-  int fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                  BTPROTO_HCI);
+  int fd;
+
+  if (g_bthcisock_target == BTHCISOCK_TARGET_UNIX)
+    {
+      return host_bthcisock_open_unix(g_bthcisock_path);
+    }
+
+  if (g_bthcisock_target == BTHCISOCK_TARGET_BLUEZ)
+    {
+      dev_idx = g_bthcisock_devid;
+    }
+
+  fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+              BTPROTO_HCI);
   if (fd < 0)
     {
-      return fd;
+      return -errno;
     }
 
   /* We must bring the device down before binding to user channel */
@@ -201,6 +332,8 @@ int host_bthcisock_open(int dev_idx)
   err = ioctl(fd, HCIDEVDOWN, dev_idx);
   if (err < 0)
     {
+      err = -errno;
+      close(fd);
       return err;
     }
 
@@ -212,6 +345,7 @@ int host_bthcisock_open(int dev_idx)
   err = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
   if (err < 0)
     {
+      err = -errno;
       close(fd);
       return err;
     }
@@ -236,5 +370,5 @@ int host_bthcisock_open(int dev_idx)
 
 int host_bthcisock_close(int fd)
 {
-  return close(fd);
+  return close(fd) < 0 ? -errno : 0;
 }
diff --git a/arch/sim/src/sim/sim_head.c b/arch/sim/src/sim/sim_head.c
index 1e6a2176b19..58b4ff18010 100644
--- a/arch/sim/src/sim/sim_head.c
+++ b/arch/sim/src/sim/sim_head.c
@@ -41,6 +41,9 @@
 #include <nuttx/syslog/syslog_rpmsg.h>
 
 #include "sim_internal.h"
+#ifdef CONFIG_SIM_HCISOCKET
+#  include "sim_hosthcisocket.h"
+#endif
 
 /****************************************************************************
  * Public Data
@@ -189,6 +192,16 @@ int main(int argc, char **argv, char **envp)
         {
           host_set_timeratio(atoi(argv[i] + 15));
         }
+#ifdef CONFIG_SIM_HCISOCKET
+      else if (strncmp(argv[i], "--bt-dev=", 9) == 0)
+        {
+          if (host_bthcisock_configure(argv[i] + 9) < 0)
+            {
+              host_printf("invalid --bt-dev target: %s\n", argv[i] + 9);
+              return EXIT_FAILURE;
+            }
+        }
+#endif
 #ifdef CONFIG_SIM_BSIM_TIME
       else if (strncmp(argv[i], "--sim-bsim-sid=", 15) == 0)
         {
diff --git a/arch/sim/src/sim/sim_hosthcisocket.h 
b/arch/sim/src/sim/sim_hosthcisocket.h
index b5e015fda5e..4d3574733ec 100644
--- a/arch/sim/src/sim/sim_hosthcisocket.h
+++ b/arch/sim/src/sim/sim_hosthcisocket.h
@@ -35,6 +35,7 @@
  ****************************************************************************/
 
 int host_bthcisock_open(int dev_idx);
+int host_bthcisock_configure(const char *target);
 int host_bthcisock_send(int fd, const void *data, size_t len);
 int host_bthcisock_receive(int fd, void *data, size_t len);
 int host_bthcisock_avail(int fd);

Reply via email to