Quellcode durchsuchen

sync smart & dfs (#8672)

Signed-off-by: xqyjlj <xqyjlj@126.com>
Signed-off-by: Shell <smokewood@qq.com>
Co-authored-by: xqyjlj <xqyjlj@126.com>
Shell vor 1 Jahr
Ursprung
Commit
83e95bdff4
100 geänderte Dateien mit 10161 neuen und 6272 gelöschten Zeilen
  1. 2 2
      bsp/qemu-vexpress-a9/.config
  2. 2 1
      bsp/qemu-vexpress-a9/rtconfig.h
  3. 0 25
      bsp/qemu-virt64-aarch64/applications/console.c
  4. 0 1
      bsp/qemu-virt64-aarch64/applications/main.c
  5. 8 0
      components/dfs/Kconfig
  6. 320 481
      components/dfs/dfs_v2/filesystems/devfs/devfs.c
  7. 4 1
      components/dfs/dfs_v2/filesystems/devfs/devfs.h
  8. 678 0
      components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c
  9. 1 1
      components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c
  10. 5 0
      components/dfs/dfs_v2/filesystems/ptyfs/README.md
  11. 11 0
      components/dfs/dfs_v2/filesystems/ptyfs/SConscript
  12. 658 0
      components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c
  13. 22 0
      components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.h
  14. 3 0
      components/dfs/dfs_v2/filesystems/romfs/romfs.c
  15. 27 11
      components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
  16. 12 0
      components/dfs/dfs_v2/include/dfs.h
  17. 8 3
      components/dfs/dfs_v2/include/dfs_file.h
  18. 3 1
      components/dfs/dfs_v2/include/dfs_fs.h
  19. 4 1
      components/dfs/dfs_v2/include/dfs_pcache.h
  20. 110 1
      components/dfs/dfs_v2/src/dfs.c
  21. 149 158
      components/dfs/dfs_v2/src/dfs_file.c
  22. 34 16
      components/dfs/dfs_v2/src/dfs_pcache.c
  23. 0 11
      components/drivers/Kconfig
  24. 8 0
      components/drivers/core/device.c
  25. 1 1
      components/drivers/include/drivers/pic.h
  26. 1 0
      components/drivers/include/drivers/serial.h
  27. 1 0
      components/drivers/include/drivers/serial_v2.h
  28. 1 0
      components/drivers/include/ipc/completion.h
  29. 38 0
      components/drivers/include/ipc/condvar.h
  30. 2 0
      components/drivers/include/ipc/pipe.h
  31. 1 0
      components/drivers/include/ipc/waitqueue.h
  32. 1 0
      components/drivers/include/rtdevice.h
  33. 30 2
      components/drivers/ipc/completion.c
  34. 173 0
      components/drivers/ipc/condvar.c
  35. 34 11
      components/drivers/ipc/pipe.c
  36. 83 6
      components/drivers/ipc/waitqueue.c
  37. 1 0
      components/drivers/ktime/inc/ktime.h
  38. 41 32
      components/drivers/ofw/base.c
  39. 3 0
      components/drivers/serial/SConscript
  40. 28 8
      components/drivers/serial/serial.c
  41. 321 0
      components/drivers/serial/serial_tty.c
  42. 0 10
      components/drivers/tty/SConscript
  43. 0 346
      components/drivers/tty/console.c
  44. 0 19
      components/drivers/tty/include/console.h
  45. 0 304
      components/drivers/tty/include/tty.h
  46. 0 56
      components/drivers/tty/include/tty_ldisc.h
  47. 0 2223
      components/drivers/tty/n_tty.c
  48. 0 326
      components/drivers/tty/pty.c
  49. 0 504
      components/drivers/tty/tty.c
  50. 0 122
      components/drivers/tty/tty_ioctl.c
  51. 0 178
      components/drivers/tty/tty_ldisc.c
  52. 8 0
      components/finsh/msh_file.c
  53. 13 8
      components/libc/compilers/common/ctime.c
  54. 5 1
      components/libc/compilers/common/include/dirent.h
  55. 4 3
      components/libc/compilers/common/include/sys/ioctl.h
  56. 2 0
      components/libc/compilers/common/include/sys/unistd.h
  57. 29 4
      components/libc/compilers/musl/fcntl.h
  58. 1 1
      components/libc/posix/Kconfig
  59. 44 24
      components/libc/posix/io/epoll/epoll.c
  60. 49 9
      components/libc/posix/io/poll/poll.c
  61. 14 0
      components/libc/posix/io/termios/termios.h
  62. 2 19
      components/lwp/Kconfig
  63. 6 5
      components/lwp/SConscript
  64. 57 3
      components/lwp/arch/aarch64/cortex-a/lwp_arch.c
  65. 14 5
      components/lwp/arch/aarch64/cortex-a/lwp_arch.h
  66. 67 3
      components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
  67. 6 0
      components/lwp/arch/arm/cortex-a/lwp_arch.c
  68. 1 1
      components/lwp/arch/arm/cortex-a/lwp_arch.h
  69. 7 0
      components/lwp/arch/risc-v/rv64/lwp_arch.c
  70. 3 3
      components/lwp/libc_musl.h
  71. 170 98
      components/lwp/lwp.c
  72. 138 21
      components/lwp/lwp.h
  73. 4 0
      components/lwp/lwp_arch_comm.h
  74. 1 0
      components/lwp/lwp_elf.c
  75. 15 14
      components/lwp/lwp_futex.c
  76. 56 13
      components/lwp/lwp_internal.c
  77. 102 9
      components/lwp/lwp_internal.h
  78. 86 0
      components/lwp/lwp_jobctrl.c
  79. 542 0
      components/lwp/lwp_pgrp.c
  80. 475 187
      components/lwp/lwp_pid.c
  81. 31 7
      components/lwp/lwp_pid.h
  82. 0 459
      components/lwp/lwp_pmutex.c
  83. 432 0
      components/lwp/lwp_session.c
  84. 0 27
      components/lwp/lwp_setsid.c
  85. 456 86
      components/lwp/lwp_signal.c
  86. 68 15
      components/lwp/lwp_signal.h
  87. 20 1
      components/lwp/lwp_sys_socket.h
  88. 308 363
      components/lwp/lwp_syscall.c
  89. 6 1
      components/lwp/lwp_syscall.h
  90. 15 6
      components/lwp/lwp_tid.c
  91. 21 14
      components/lwp/lwp_user_mm.c
  92. 4 0
      components/lwp/lwp_user_mm.h
  93. 13 0
      components/lwp/terminal/Kconfig
  94. 741 0
      components/lwp/terminal/bsd_porting.h
  95. 81 0
      components/lwp/terminal/bsd_ttydisc.h
  96. 180 0
      components/lwp/terminal/bsd_ttyqueue.h
  97. 1447 0
      components/lwp/terminal/freebsd/tty.c
  98. 711 0
      components/lwp/terminal/freebsd/tty_compat.c
  99. 507 0
      components/lwp/terminal/freebsd/tty_inq.c
  100. 370 0
      components/lwp/terminal/freebsd/tty_outq.c

+ 2 - 2
bsp/qemu-vexpress-a9/.config

@@ -81,7 +81,7 @@ CONFIG_RT_USING_CONSOLE=y
 CONFIG_RT_CONSOLEBUF_SIZE=256
 CONFIG_RT_CONSOLE_DEVICE_NAME="uart0"
 CONFIG_RT_VER_NUM=0x50100
-# CONFIG_RT_USING_STDC_ATOMIC is not set
+CONFIG_RT_USING_STDC_ATOMIC=y
 CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32
 CONFIG_RT_USING_CACHE=y
 CONFIG_RT_USING_HW_ATOMIC=y
@@ -203,7 +203,7 @@ CONFIG_RT_USING_SOFT_RTC=y
 CONFIG_RT_USING_SDIO=y
 CONFIG_RT_SDIO_STACK_SIZE=4096
 CONFIG_RT_SDIO_THREAD_PRIORITY=15
-CONFIG_RT_MMCSD_STACK_SIZE=4096
+CONFIG_RT_MMCSD_STACK_SIZE=16384
 CONFIG_RT_MMCSD_THREAD_PREORITY=22
 CONFIG_RT_MMCSD_MAX_PARTITION=16
 # CONFIG_RT_SDIO_DEBUG is not set

+ 2 - 1
bsp/qemu-vexpress-a9/rtconfig.h

@@ -54,6 +54,7 @@
 #define RT_CONSOLEBUF_SIZE 256
 #define RT_CONSOLE_DEVICE_NAME "uart0"
 #define RT_VER_NUM 0x50100
+#define RT_USING_STDC_ATOMIC
 #define RT_BACKTRACE_LEVEL_MAX_NR 32
 #define RT_USING_CACHE
 #define RT_USING_HW_ATOMIC
@@ -138,7 +139,7 @@
 #define RT_USING_SDIO
 #define RT_SDIO_STACK_SIZE 4096
 #define RT_SDIO_THREAD_PRIORITY 15
-#define RT_MMCSD_STACK_SIZE 4096
+#define RT_MMCSD_STACK_SIZE 16384
 #define RT_MMCSD_THREAD_PREORITY 22
 #define RT_MMCSD_MAX_PARTITION 16
 #define RT_USING_SPI

+ 0 - 25
bsp/qemu-virt64-aarch64/applications/console.c

@@ -10,10 +10,6 @@
 
 #include <rtthread.h>
 
-#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_SMART)
-#include <console.h>
-#endif
-
 #include <virtio_console.h>
 
 static int console_init()
@@ -36,8 +32,6 @@ static int console_init()
 }
 INIT_ENV_EXPORT(console_init);
 
-#ifdef FINSH_USING_MSH
-
 static int console(int argc, char **argv)
 {
     rt_err_t result = RT_EOK;
@@ -48,23 +42,6 @@ static int console(int argc, char **argv)
         {
             rt_kprintf("console change to %s\n", argv[2]);
             rt_console_set_device(argv[2]);
-
-        #ifdef RT_USING_POSIX_DEVIO
-            {
-                rt_device_t dev = rt_device_find(argv[2]);
-
-                if (dev != RT_NULL)
-                {
-                    #ifdef RT_USING_SMART
-                        console_set_iodev(dev);
-                    #else
-                        rt_kprintf("TODO not supported\n");
-                    #endif
-                }
-            }
-        #else
-            finsh_set_device(argv[2]);
-        #endif /* RT_USING_POSIX_DEVIO */
         }
         else
         {
@@ -81,5 +58,3 @@ static int console(int argc, char **argv)
     return result;
 }
 MSH_CMD_EXPORT(console, set console name);
-
-#endif /* FINSH_USING_MSH */

+ 0 - 1
bsp/qemu-virt64-aarch64/applications/main.c

@@ -8,7 +8,6 @@
  * 2020/10/7      bernard      the first version
  */
 
-#include <stdio.h>
 #include <rtthread.h>
 
 int main(void)

+ 8 - 0
components/dfs/Kconfig

@@ -45,6 +45,7 @@ endif
 
         config RT_USING_DFS_V2
             bool "DFS v2.0"
+            select RT_USING_DEVICE_OPS
     endchoice
 
 if RT_USING_DFS_V1
@@ -170,6 +171,13 @@ endif
         depends on RT_USING_DFS_ROMFS
         default n
 
+if RT_USING_SMART
+    config RT_USING_DFS_PTYFS
+        bool "Using Pseudo-Teletype Filesystem (UNIX98 PTY)"
+        depends on RT_USING_DFS_DEVFS
+        default y
+endif
+
     config RT_USING_DFS_CROMFS
         bool "Enable ReadOnly compressed file system on flash"
         default n

+ 320 - 481
components/dfs/dfs_v2/filesystems/devfs/devfs.c

@@ -13,6 +13,7 @@
 
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/unistd.h>
 
 #include <dfs.h>
 #include <dfs_fs.h>
@@ -20,663 +21,501 @@
 #include <dfs_dentry.h>
 #include <dfs_mnt.h>
 
-#include "devfs.h"
-
-struct device_dirent
-{
-    struct rt_device **devices;
-    uint32_t device_count;
-};
-
-int dfs_devfs_open(struct dfs_file *file);
-int dfs_devfs_close(struct dfs_file *file);
-off_t generic_dfs_lseek(struct dfs_file *file, off_t offset, int whence);
-ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos);
-ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos);
-int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args);
-int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count);
-static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req);
-int dfs_devfs_flush(struct dfs_file *file);
-off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece);
-int dfs_devfs_truncate(struct dfs_file *file, off_t offset);
-int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap);
-int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock);
-int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock);
-
-int dfs_devfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data);
-int dfs_devfs_umount(struct dfs_mnt *mnt);
-int dfs_devfs_unlink(struct dfs_dentry *dentry);
-int dfs_devfs_stat(struct dfs_dentry *dentry, struct stat *st);
-int dfs_devfs_statfs(struct dfs_mnt *mnt, struct statfs *buf);
-static struct dfs_vnode *dfs_devfs_lookup(struct dfs_dentry *dentry);
-struct dfs_vnode *dfs_devfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode);
-static int dfs_devfs_free_vnode(struct dfs_vnode *vnode);
-
-static const struct dfs_file_ops _dev_fops =
-{
-    .open       = dfs_devfs_open,
-    .close      = dfs_devfs_close,
-    .lseek      = generic_dfs_lseek,
-    .read       = dfs_devfs_read,
-    .write      = dfs_devfs_write,
-    .ioctl      = dfs_devfs_ioctl,
-    .getdents   = dfs_devfs_getdents,
-    .poll       = dfs_devfs_poll,
-
-    .flush      = dfs_devfs_flush,
-    .lseek      = dfs_devfs_lseek,
-    .truncate   = dfs_devfs_truncate,
-    .mmap       = dfs_devfs_mmap,
-    .lock       = dfs_devfs_lock,
-    .flock      = dfs_devfs_flock,
-};
-
-static const struct dfs_filesystem_ops _devfs_ops =
-{
-    .name = "devfs",
-
-    .default_fops = &_dev_fops,
-    .mount = dfs_devfs_mount,
-    .umount = dfs_devfs_umount,
-    .unlink = dfs_devfs_unlink,
-    .stat = dfs_devfs_stat,
-    .statfs = dfs_devfs_statfs,
-    .lookup = dfs_devfs_lookup,
-    .create_vnode = dfs_devfs_create_vnode,
-    .free_vnode = dfs_devfs_free_vnode,
-};
-
-static struct dfs_filesystem_type _devfs =
+static int dfs_devfs_open(struct dfs_file *file)
 {
-    .fs_ops = &_devfs_ops,
-};
+    int ret = RT_EOK;
 
-static int _device_to_mode(struct rt_device *device)
-{
-    int mode = 0;
+    RT_ASSERT(file != RT_NULL);
+    RT_ASSERT(file->vnode->ref_count > 0);
 
-    switch (device->type)
+    if (file->vnode->ref_count > 1)
     {
-    case RT_Device_Class_Char:
-        mode = S_IFCHR | 0777;
-        break;
-    case RT_Device_Class_Block:
-        mode = S_IFBLK | 0777;
-        break;
-    case RT_Device_Class_Pipe:
-        mode = S_IFIFO | 0777;
-        break;
-    default:
-        mode = S_IFCHR | 0777;
-        break;
+        if (file->vnode->type == FT_DIRECTORY
+            && !(file->flags & O_DIRECTORY))
+        {
+            return -ENOENT;
+        }
+        file->fpos = 0;
     }
 
-    return mode;
-}
-
-static int _devfs_root_dirent_update(struct dfs_vnode *vnode)
-{
-    rt_err_t result = RT_EOK;
-
-    if (vnode)
+    if (!S_ISDIR(file->vnode->mode))
     {
-        // result = rt_mutex_take(&vnode->lock, RT_WAITING_FOREVER);
-        result = dfs_file_lock();
-        if (result == RT_EOK)
+        rt_device_t device = RT_NULL;
+        struct dfs_dentry *de = file->dentry;
+        char *device_name = rt_malloc(DFS_PATH_MAX);
+
+        if (!device_name)
         {
-            rt_uint32_t count = 0;
-            struct device_dirent *root_dirent = (struct device_dirent*) vnode->data;
-            if (root_dirent) rt_free(root_dirent);
+            return -ENOMEM;
+        }
 
-            count = rt_object_get_length(RT_Object_Class_Device);
-            root_dirent = (struct device_dirent *)rt_malloc(sizeof(struct device_dirent) + count * sizeof(rt_device_t));
-            if (root_dirent != RT_NULL)
+        /* skip `/dev` */
+        rt_snprintf(device_name, DFS_PATH_MAX, "%s%s", de->mnt->fullpath + sizeof("/dev") - 1, de->pathname);
+
+        device = rt_device_find(device_name + 1);
+        if (device)
+        {
+            file->vnode->data = device;
+#ifdef RT_USING_POSIX_DEVIO
+            if (device->fops && device->fops->open)
             {
-                root_dirent->device_count = count;
-                if (count != 0)
+                ret = device->fops->open(file);
+                if (ret == RT_EOK || ret == -RT_ENOSYS)
                 {
-                    root_dirent->devices = (rt_device_t *)(root_dirent + 1);
-                    rt_object_get_pointers(RT_Object_Class_Device, (rt_object_t *)root_dirent->devices, count);
+                    ret = RT_EOK;
                 }
-                else
+            }
+            else if (device->ops && file->vnode->ref_count == 1)
+#else
+            if (device->ops && file->vnode->ref_count == 1)
+#endif /* RT_USING_POSIX_DEVIO */
+            {
+                ret = rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
+                if (ret == RT_EOK || ret == -RT_ENOSYS)
                 {
-                    root_dirent->devices = RT_NULL;
+                    ret = RT_EOK;
                 }
             }
-
-            vnode->data = root_dirent;
-            result = count;
-            dfs_file_unlock();
         }
     }
 
-    return result;
+    return ret;
 }
 
-static struct dfs_vnode *dfs_devfs_lookup(struct dfs_dentry *dentry)
+static int dfs_devfs_close(struct dfs_file *file)
 {
-    rt_device_t device = RT_NULL;
-    struct dfs_vnode *vnode = RT_NULL;
-    const char *pathname = dentry->pathname;
+    int ret = RT_EOK;
+    rt_device_t device;
 
-    DLOG(msg, "devfs", "vnode", DLOG_MSG, "dfs_vnode_create");
-    vnode = dfs_vnode_create();
-    if (vnode)
+    RT_ASSERT(file != RT_NULL);
+    RT_ASSERT(file->vnode->ref_count > 0);
+
+    if (file->vnode && file->vnode->data)
     {
-        if (pathname[0] == '/' && pathname[1] == '\0')
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
+
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->close)
         {
-            int count = _devfs_root_dirent_update(vnode);
-
-            vnode->mode = S_IFDIR | 0644;
-            vnode->size = count;
-            vnode->nlink = 1;
-            vnode->fops = &_dev_fops;
-            vnode->mnt = dentry->mnt;
-            vnode->type = FT_DIRECTORY;
+            ret = device->fops->close(file);
         }
-        else
+        else if (file->vnode->ref_count == 1)
+#else
+        if (device->ops && file->vnode->ref_count == 1)
+#endif /* RT_USING_POSIX_DEVIO */
         {
-            device = rt_device_find(&pathname[1]);
-            if (!device)
-            {
-                DLOG(msg, "devfs", "vnode", DLOG_MSG, "dfs_vnode_destroy(vnode), no-device");
-                dfs_vnode_destroy(vnode);
-                vnode = RT_NULL;
-            }
-            else
-            {
-                vnode->mode = _device_to_mode(device);
-                vnode->size = device->ref_count;
-                vnode->nlink = 1;
-                vnode->fops = &_dev_fops;
-                vnode->data = device;
-                vnode->mnt = dentry->mnt;
-                vnode->type = FT_DEVICE;
-            }
+            /* close device handler */
+            ret = rt_device_close(device);
         }
     }
 
-    return vnode;
+    return ret;
 }
 
-struct dfs_vnode *dfs_devfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode)
+static ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
 {
-#ifdef RT_USING_DEV_BUS
-    if (dentry && type == FT_DIRECTORY)
-    {
-        /* regester bus device */
-        if (rt_device_bus_create(&dentry->pathname[1], 0))
-        {
-            return dfs_devfs_lookup(dentry);
-        }
-    }
-#endif
+    ssize_t ret = -RT_EIO;
+    rt_device_t device;
 
-    return RT_NULL;
-}
+    RT_ASSERT(file != RT_NULL);
 
-int dfs_devfs_free_vnode(struct dfs_vnode *vnode)
-{
-    if (S_ISDIR(vnode->mode))
+    if (file->vnode && file->vnode->data)
     {
-        struct device_dirent *root_dirent;
-
-        root_dirent = (struct device_dirent *)vnode->data;
-        RT_ASSERT(root_dirent != RT_NULL);
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-        /* release dirent */
-        DLOG(msg, "devfs", "devfs", DLOG_MSG, "free root_dirent");
-        rt_free(root_dirent);
-        return RT_EOK;
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->read)
+        {
+            ret = device->fops->read(file, buf, count, pos);
+        }
+        else
+#else
+        if (device->ops)
+#endif /* RT_USING_POSIX_DEVIO */
+        {
+            /* read device data */
+            ret = rt_device_read(device, *pos, buf, count);
+            *pos += ret;
+        }
     }
 
-    /* which is a device */
-    vnode->data = RT_NULL;
-    return 0;
+    return ret;
 }
 
-int dfs_devfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data)
+static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
 {
-    RT_ASSERT(mnt != RT_NULL);
+    ssize_t ret = -RT_EIO;
+    rt_device_t device;
 
-    rt_atomic_add(&(mnt->ref_count), 1);
-    mnt->flags |= MNT_IS_LOCKED;
+    RT_ASSERT(file != RT_NULL);
 
-    return RT_EOK;
-}
+    if(file->vnode->data)
+    {
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-int dfs_devfs_umount(struct dfs_mnt *mnt)
-{
-    return RT_EOK;
-}
+        if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
+            return -RT_ENOSYS;
 
-int dfs_devfs_statfs(struct dfs_mnt *mnt, struct statfs *buf)
-{
-    if (mnt && buf)
-    {
-        buf->f_bsize  = 512;
-        buf->f_blocks = 2048 * 64; // 64M
-        buf->f_bfree  = buf->f_blocks;
-        buf->f_bavail = buf->f_bfree;
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->write)
+        {
+            ret = device->fops->write(file, buf, count, pos);
+        }
+        else
+#else
+        if (device->ops)
+#endif /* RT_USING_POSIX_DEVIO */
+        {
+            /* read device data */
+            ret = rt_device_write(device, *pos, buf, count);
+            *pos += ret;
+        }
     }
 
-    return RT_EOK;
+    return ret;
 }
 
-int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args)
+static int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args)
 {
-    rt_err_t result = RT_EOK;
+    int ret = RT_EOK;
     rt_device_t device;
 
     RT_ASSERT(file != RT_NULL);
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+    if (file->vnode && file->vnode->data)
+    {
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-    if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
-        return -RT_ENOSYS;
+        if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
+            return -RT_ENOSYS;
 
 #ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->ioctl)
-    {
-        result = device->fops->ioctl(file, cmd, args);
-    }
-    else
+        if (device->fops && device->fops->ioctl)
+        {
+            ret = device->fops->ioctl(file, cmd, args);
+        }
+        else
 #endif /* RT_USING_POSIX_DEVIO */
-    {
-        result = rt_device_control(device, cmd, args);
+        {
+            ret = rt_device_control(device, cmd, args);
+        }
     }
 
-    return result;
+    return ret;
 }
 
-ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
+static int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
 {
-    int result = -RT_EIO;
-    rt_device_t device;
+    int ret = -RT_ENOSYS;
 
     RT_ASSERT(file != RT_NULL);
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
-
-    if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
-        return -RT_ENOSYS;
-
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->read)
-    {
-        result = device->fops->read(file, buf, count, pos);
-    }
-    else
-#endif /* RT_USING_POSIX_DEVIO */
-    {
-        /* read device data */
-        result = rt_device_read(device, *pos, buf, count);
-        *pos += result;
-    }
-
-    return result;
+    return ret;
 }
 
-ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
+static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req)
 {
-    int result = -RT_EIO;
+    int mask = 0;
     rt_device_t device;
 
     RT_ASSERT(file != RT_NULL);
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
-
-    if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
-        return -RT_ENOSYS;
+    if (file->vnode && file->vnode->data)
+    {
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
 #ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->write)
-    {
-        result = device->fops->write(file, buf, count, pos);
-    }
-    else
+        if (device->fops && device->fops->poll)
+        {
+            mask = device->fops->poll(file, req);
+        }
 #endif /* RT_USING_POSIX_DEVIO */
-    {
-        /* read device data */
-        result = rt_device_write(device, *pos, buf, count);
-        *pos += result;
     }
 
-    return result;
+    return mask;
 }
 
-int dfs_devfs_close(struct dfs_file *file)
+static int dfs_devfs_flush(struct dfs_file *file)
 {
-    rt_err_t result = RT_EOK;
+    int ret = RT_EOK;
     rt_device_t device;
 
     RT_ASSERT(file != RT_NULL);
 
-    if (!S_ISDIR(file->vnode->mode))
+    if (file->vnode && file->vnode->data)
     {
         /* get device handler */
         device = (rt_device_t)file->vnode->data;
-        RT_ASSERT(device != RT_NULL);
 
 #ifdef RT_USING_POSIX_DEVIO
-        if (device->fops && device->fops->close)
+        if (device->fops && device->fops->flush)
         {
-            result = device->fops->close(file);
+            ret = device->fops->flush(file);
         }
-        else
 #endif /* RT_USING_POSIX_DEVIO */
-        {
-            /* close device handler */
-            result = rt_device_close(device);
-        }
     }
 
-    return result;
+    return ret;
 }
 
-int dfs_devfs_open(struct dfs_file *file)
+static off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
 {
-    rt_err_t result = RT_EOK;
+    off_t ret = 0;
+    rt_device_t device;
 
-    /* open root directory */
-    if ((file->dentry->pathname[0] == '/' && file->dentry->pathname[1] == '\0') ||
-        (S_ISDIR(file->vnode->mode)))
-    {
-        /* re-open the root directory for re-scan devices */
-        _devfs_root_dirent_update(file->vnode);
-        return RT_EOK;
-    }
-    else
+    RT_ASSERT(file != RT_NULL);
+
+    if (file->vnode && file->vnode->data)
     {
-        rt_device_t device = RT_NULL;
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-        device = (struct rt_device *)file->vnode->data;
-        if (device)
-        {
 #ifdef RT_USING_POSIX_DEVIO
-            if (device->fops && device->fops->open)
-            {
-                result = device->fops->open(file);
-                if (result == RT_EOK || result == -RT_ENOSYS)
-                {
-                    file->fops = &_dev_fops;
-                    return RT_EOK;
-                }
-            }
-            else
-#endif /* RT_USING_POSIX_DEVIO */
-            {
-                result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
-                if (result == RT_EOK || result == -RT_ENOSYS)
-                {
-                    file->fops = &_dev_fops;
-                    return RT_EOK;
-                }
-            }
+        if (device->fops && device->fops->lseek)
+        {
+            ret = device->fops->lseek(file, offset, wherece);
         }
+#endif /* RT_USING_POSIX_DEVIO */
     }
 
-    /* open device failed. */
-    return -EIO;
+    return ret;
 }
 
-int dfs_devfs_unlink(struct dfs_dentry *dentry)
+static int dfs_devfs_truncate(struct dfs_file *file, off_t offset)
 {
-#ifdef RT_USING_DEV_BUS
+    int ret = RT_EOK;
     rt_device_t device;
 
-    device = rt_device_find(&dentry->pathname[1]);
-    if (device == RT_NULL)
-    {
-        return -1;
-    }
-    if (device->type != RT_Device_Class_Bus)
-    {
-        return -1;
-    }
-    rt_device_bus_destroy(device);
-#endif
-    return RT_EOK;
-}
-
-int dfs_devfs_stat(struct dfs_dentry *dentry, struct stat *st)
-{
-    int ret = RT_EOK;
-    const char *path = RT_NULL;
-    struct dfs_vnode *vnode = RT_NULL;
+    RT_ASSERT(file != RT_NULL);
 
-    if (dentry && dentry->vnode)
+    if (file->vnode && file->vnode->data)
     {
-        path = dentry->pathname;
-        vnode = dentry->vnode;
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-        /* stat root directory */
-        if ((path[0] == '/') && (path[1] == '\0'))
-        {
-            st->st_dev = 0;
-            st->st_gid = vnode->gid;
-            st->st_uid = vnode->uid;
-            st->st_ino = 0;
-            st->st_mode  = vnode->mode;
-            st->st_nlink = vnode->nlink;
-            st->st_size  = vnode->size;
-            st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
-            st->st_mtim.tv_sec  = vnode->mtime.tv_sec;
-            st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
-            st->st_ctim.tv_sec  = vnode->ctime.tv_sec;
-            st->st_atim.tv_nsec = vnode->atime.tv_nsec;
-            st->st_atim.tv_sec  = vnode->atime.tv_sec;
-        }
-        else
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->truncate)
         {
-            rt_device_t device;
-
-            device = rt_device_find(&path[1]);
-            if (device != RT_NULL)
-            {
-                st->st_dev = 0;
-                st->st_gid = vnode->gid;
-                st->st_uid = vnode->uid;
-                st->st_ino = 0;
-                st->st_mode  = vnode->mode;
-                st->st_nlink = vnode->nlink;
-                st->st_size  = vnode->size;
-                st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
-                st->st_mtim.tv_sec  = vnode->mtime.tv_sec;
-                st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
-                st->st_ctim.tv_sec  = vnode->ctime.tv_sec;
-                st->st_atim.tv_nsec = vnode->atime.tv_nsec;
-                st->st_atim.tv_sec  = vnode->atime.tv_sec;
-            }
-            else
-            {
-                ret = -ENOENT;
-            }
+            ret = device->fops->truncate(file, offset);
         }
+#endif /* RT_USING_POSIX_DEVIO */
     }
 
     return ret;
 }
 
-int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
+static int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
 {
-    rt_uint32_t index;
-    rt_object_t object;
-    struct dirent *d;
-    struct device_dirent *root_dirent;
-
-    root_dirent = (struct device_dirent *)file->vnode->data;
-    RT_ASSERT(root_dirent != RT_NULL);
+    int ret = RT_EOK;
+    rt_device_t device;
 
-    /* make integer count */
-    count = (count / sizeof(struct dirent));
-    if (count == 0)
-        return -EINVAL;
+    RT_ASSERT(file != RT_NULL);
 
-    for (index = 0; index < count && index + file->fpos < root_dirent->device_count; index ++)
+    if (file->vnode && file->vnode->data)
     {
-        object = (rt_object_t)root_dirent->devices[file->fpos + index];
-
-        d = dirp + index;
-        d->d_type = DT_REG;
-        d->d_namlen = RT_NAME_MAX;
-        d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
-        rt_strncpy(d->d_name, object->name, RT_NAME_MAX);
-        d->d_name[RT_NAME_MAX] = '\0';
-    }
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
 
-    file->fpos += index;
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->mmap)
+        {
+            ret = device->fops->mmap(file, mmap);
+        }
+#endif /* RT_USING_POSIX_DEVIO */
+    }
 
-    return index * sizeof(struct dirent);
+    return ret;
 }
 
-static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req)
+static int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock)
 {
-    int mask = 0;
+    int ret = RT_EOK;
     rt_device_t device;
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+    RT_ASSERT(file != RT_NULL);
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->poll)
+    if (file->vnode && file->vnode->data)
     {
-        mask = device->fops->poll(file, req);
-    }
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
+
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->lock)
+        {
+            ret = device->fops->lock(file, flock);
+        }
 #endif /* RT_USING_POSIX_DEVIO */
+    }
 
-    return mask;
+    return ret;
 }
 
-int dfs_devfs_flush(struct dfs_file *file)
+static int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock)
 {
-    int ret = 0;
+    int ret = RT_EOK;
     rt_device_t device;
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+    RT_ASSERT(file != RT_NULL);
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->flush)
+    if (file->vnode && file->vnode->data)
     {
-        ret = device->fops->flush(file);
-    }
+        /* get device handler */
+        device = (rt_device_t)file->vnode->data;
+
+#ifdef RT_USING_POSIX_DEVIO
+        if (device->fops && device->fops->flock)
+        {
+            ret = device->fops->flock(file, operation, flock);
+        }
 #endif /* RT_USING_POSIX_DEVIO */
+    }
 
     return ret;
 }
 
-off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
+static const struct dfs_file_ops _dev_fops =
 {
-    off_t ret = 0;
-    rt_device_t device;
+    .open = dfs_devfs_open,
+    .close = dfs_devfs_close,
+    .lseek = generic_dfs_lseek,
+    .read = dfs_devfs_read,
+    .write = dfs_devfs_write,
+    .ioctl = dfs_devfs_ioctl,
+    .getdents = dfs_devfs_getdents,
+    .poll = dfs_devfs_poll,
+
+    .flush = dfs_devfs_flush,
+    .lseek = dfs_devfs_lseek,
+    .truncate = dfs_devfs_truncate,
+    .mmap = dfs_devfs_mmap,
+    .lock = dfs_devfs_lock,
+    .flock = dfs_devfs_flock,
+};
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+const struct dfs_file_ops *dfs_devfs_fops(void)
+{
+    return &_dev_fops;
+}
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->lseek)
+mode_t dfs_devfs_device_to_mode(struct rt_device *device)
+{
+    mode_t mode = 0;
+
+    switch (device->type)
     {
-        ret = device->fops->lseek(file, offset, wherece);
+    case RT_Device_Class_Char:
+        mode = S_IFCHR | 0777;
+        break;
+    case RT_Device_Class_Block:
+        mode = S_IFBLK | 0777;
+        break;
+    case RT_Device_Class_Pipe:
+        mode = S_IFIFO | 0777;
+        break;
+    default:
+        mode = S_IFCHR | 0777;
+        break;
     }
-#endif /* RT_USING_POSIX_DEVIO */
 
-    return ret;
+    return mode;
 }
 
-int dfs_devfs_truncate(struct dfs_file *file, off_t offset)
+static void dfs_devfs_mkdir(const char *fullpath, mode_t mode)
 {
-    int ret = 0;
-    rt_device_t device;
-
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+    int len = rt_strlen(fullpath);
+    char *path = (char *)rt_malloc(len);
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->truncate)
+    if (path)
     {
-        ret = device->fops->truncate(file, offset);
-    }
-#endif /* RT_USING_POSIX_DEVIO */
+        int index = len - 1;
 
-    return ret;
-}
+        rt_strcpy(path, fullpath);
 
-int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
-{
-    int ret = 0;
-    rt_device_t device;
+        if (path[index] == '/')
+        {
+            path[index] = '\0';
+            index --;
+        }
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+        while (path[index] != '/' && index >= 0)
+        {
+            index --;
+        }
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->mmap)
-    {
-        ret = device->fops->mmap(file, mmap);
-    }
-#endif /* RT_USING_POSIX_DEVIO */
+        path[index] = '\0';
 
-    return ret;
-}
+        if (index > 0 && access(path, 0) != 0)
+        {
+            int i = 0;
 
-int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock)
-{
-    int ret = 0;
-    rt_device_t device;
+            if (path[i] == '/')
+            {
+                i ++;
+            }
+
+            while (index > i)
+            {
+                if (path[i] == '/')
+                {
+                    path[i] = '\0';
+                    mkdir(path, mode);
+                    path[i] = '/';
+                }
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
+                i ++;
+            }
 
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->lock)
-    {
-        ret = device->fops->lock(file, flock);
+            mkdir(path, mode);
+        }
     }
-#endif /* RT_USING_POSIX_DEVIO */
-
-    return ret;
 }
 
-int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock)
+void dfs_devfs_device_add(rt_device_t device)
 {
-    int ret = 0;
-    rt_device_t device;
+    int fd;
+    char path[512];
 
-    /* get device handler */
-    device = (rt_device_t)file->vnode->data;
-    RT_ASSERT(device != RT_NULL);
-
-#ifdef RT_USING_POSIX_DEVIO
-    if (device->fops && device->fops->flock)
+    if (device)
     {
-        ret = device->fops->flock(file, operation, flock);
-    }
-#endif /* RT_USING_POSIX_DEVIO */
+        rt_snprintf(path, 512, "/dev/%s", device->parent.name);
 
-    return ret;
+        if (access(path, 0) != 0)
+        {
+            mode_t mode = dfs_devfs_device_to_mode(device);
+
+            dfs_devfs_mkdir(path, mode);
+
+            fd = open(path, O_RDWR | O_CREAT, mode);
+            if (fd >= 0)
+            {
+                close(fd);
+            }
+        }
+    }
 }
 
-int dfs_devfs_init(void)
+int dfs_devfs_update(void)
 {
-    /* register devfs file system */
-    dfs_register(&_devfs);
+    int count = rt_object_get_length(RT_Object_Class_Device);
+    if (count > 0)
+    {
+        rt_device_t *devices = rt_malloc(count * sizeof(rt_device_t));
+        if (devices)
+        {
+            rt_object_get_pointers(RT_Object_Class_Device, (rt_object_t *)devices, count);
 
-    dfs_mount(RT_NULL, "/dev", "devfs", 0, RT_NULL);
+            for (int index = 0; index < count; index ++)
+            {
+                dfs_devfs_device_add(devices[index]);
+            }
+            rt_free(devices);
+        }
+    }
 
-    return 0;
+    return count;
 }
-INIT_COMPONENT_EXPORT(dfs_devfs_init);
-

+ 4 - 1
components/dfs/dfs_v2/filesystems/devfs/devfs.h

@@ -10,6 +10,9 @@
 #ifndef __DEVICE_FS_H__
 #define __DEVICE_FS_H__
 
-int dfs_devfs_init(void);
+const struct dfs_file_ops *dfs_devfs_fops(void);
+mode_t dfs_devfs_device_to_mode(struct rt_device *device);
+void dfs_devfs_device_add(rt_device_t device);
+int dfs_devfs_update(void);
 
 #endif

+ 678 - 0
components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c

@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-10-24     flybreak     the first version
+ * 2023-02-01     xqyjlj       fix cannot open the same file repeatedly in 'w' mode
+ * 2023-09-20     zmq810150896 adds truncate functionality and standardized unlink adaptations
+ * 2023-12-02     Shell        Support of dynamic device
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <dfs_dentry.h>
+#include <dfs_file.h>
+#include <dfs_mnt.h>
+#include <devfs.h>
+#include <unistd.h>
+
+
+#define TMPFS_MAGIC         0x0B0B0B0B
+#define TMPFS_TYPE_FILE     0x00
+#define TMPFS_TYPE_DIR      0x01
+#define TMPFS_TYPE_DYN_DEV  0x02    /* dynamic device */
+
+struct devtmpfs_sb;
+
+struct devtmpfs_file
+{
+    char name[DIRENT_NAME_MAX]; /* file name */
+
+    rt_uint32_t     type;       /* file type */
+    rt_list_t       subdirs;    /* file subdir list */
+    rt_list_t       sibling;    /* file sibling list */
+
+    struct devtmpfs_sb *sb;    /* superblock ptr */
+
+    rt_uint32_t     mode;
+    char *link;
+};
+
+struct devtmpfs_sb
+{
+    rt_uint32_t             magic;      /* TMPFS_MAGIC */
+    struct devtmpfs_file    root;       /* root dir */
+    rt_size_t               df_size;    /* df size */
+    rt_list_t               sibling;    /* sb sibling list */
+    struct rt_spinlock      lock;       /* tmpfs lock */
+};
+
+static struct dfs_file_ops _default_fops = { 0 };
+
+static int _path_separate(const char *path, char *parent_path, char *file_name)
+{
+    const char *path_p, *path_q;
+
+    RT_ASSERT(path[0] == '/');
+
+    file_name[0] = '\0';
+    path_p = path_q = &path[1];
+__next_dir:
+    while (*path_q != '/' && *path_q != '\0')
+    {
+        path_q++;
+    }
+    if (path_q != path_p) /*sub dir*/
+    {
+        if (*path_q != '\0')
+        {
+            path_q++;
+            path_p = path_q;
+            goto __next_dir;
+        }
+        else /* Last level dir */
+        {
+            rt_memcpy(parent_path, path, path_p - path - 1);
+            parent_path[path_p - path - 1] = '\0';
+            rt_memcpy(file_name, path_p, path_q - path_p);
+            file_name[path_q - path_p] = '\0';
+        }
+    }
+    if (parent_path[0] == 0)
+    {
+        parent_path[0] = '/';
+        parent_path[1] = '\0';
+    }
+    //LOG_D("parent_path: %s", parent_path);
+    //LOG_D("file_name: %s", file_name);
+
+    return 0;
+}
+
+static int _get_subdir(const char *path, char *name)
+{
+    const char *subpath = path;
+    while (*subpath == '/' && *subpath)
+        subpath ++;
+    while (*subpath != '/' && *subpath)
+    {
+        *name = *subpath;
+        name ++;
+        subpath ++;
+    }
+    return 0;
+}
+
+#if 0
+static int _free_subdir(struct devtmpfs_file *dfile)
+{
+    struct devtmpfs_file *file;
+    rt_list_t *list, *temp_list;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(dfile->type == TMPFS_TYPE_DIR);
+
+    rt_list_for_each_safe(list, temp_list, &dfile->subdirs)
+    {
+        file = rt_list_entry(list, struct devtmpfs_file, sibling);
+        if (file->type == TMPFS_TYPE_DIR)
+        {
+            _free_subdir(file);
+        }
+
+        if (file->link)
+        {
+            rt_free(file->link);
+        }
+
+        superblock = file->sb;
+        RT_ASSERT(superblock);
+
+        rt_spin_lock(&superblock->lock);
+        rt_list_remove(&(file->sibling));
+        rt_spin_unlock(&superblock->lock);
+
+        rt_free(file);
+    }
+    return 0;
+}
+#endif
+
+static int devtmpfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data)
+{
+    struct devtmpfs_sb *superblock;
+
+    superblock = rt_calloc(1, sizeof(struct devtmpfs_sb));
+    if (superblock)
+    {
+        superblock->df_size = sizeof(struct devtmpfs_sb);
+        superblock->magic = TMPFS_MAGIC;
+        rt_list_init(&superblock->sibling);
+
+        superblock->root.name[0] = '/';
+        superblock->root.sb = superblock;
+        superblock->root.type = TMPFS_TYPE_DIR;
+        superblock->root.mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH);
+        rt_list_init(&superblock->root.sibling);
+        rt_list_init(&superblock->root.subdirs);
+
+        rt_spin_lock_init(&superblock->lock);
+
+        mnt->data = superblock;
+    }
+    else
+    {
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+
+static int devtmpfs_unmount(struct dfs_mnt *mnt)
+{
+#if 0
+    struct devtmpfs_sb *superblock;
+
+    /* FIXME: don't unmount on busy. */
+    superblock = (struct devtmpfs_sb *)mnt->data;
+    RT_ASSERT(superblock != NULL);
+
+    mnt->data = NULL;
+    _free_subdir(&(superblock->root));
+    rt_free(superblock);
+#endif
+    return -RT_ERROR;
+}
+
+static struct devtmpfs_file *devtmpfs_file_lookup(struct devtmpfs_sb *superblock, const char *path)
+{
+    const char *subpath, *curpath, *filename = RT_NULL;
+    char subdir_name[DIRENT_NAME_MAX];
+    struct devtmpfs_file *file, *curfile;
+    rt_list_t *list;
+
+    subpath = path;
+    while (*subpath == '/' && *subpath)
+        subpath ++;
+
+    if (! *subpath) /* is root directory */
+    {
+        return &(superblock->root);
+    }
+
+    curpath = subpath;
+    curfile = &superblock->root;
+
+find_subpath:
+    while (*subpath != '/' && *subpath)
+        subpath ++;
+
+    if (! *subpath) /* is last directory */
+        filename = curpath;
+    else
+        subpath ++; /* skip '/' */
+
+    memset(subdir_name, 0, DIRENT_NAME_MAX);
+    _get_subdir(curpath, subdir_name);
+
+    rt_spin_lock(&superblock->lock);
+
+    rt_list_for_each(list, &curfile->subdirs)
+    {
+        file = rt_list_entry(list, struct devtmpfs_file, sibling);
+        if (filename) /* find file */
+        {
+            if (rt_strcmp(file->name, filename) == 0)
+            {
+                rt_spin_unlock(&superblock->lock);
+                return file;
+            }
+        }
+        else if (rt_strcmp(file->name, subdir_name) == 0)
+        {
+            curpath = subpath;
+            curfile = file;
+            rt_spin_unlock(&superblock->lock);
+            goto find_subpath;
+        }
+    }
+    rt_spin_unlock(&superblock->lock);
+    /* not found */
+    return NULL;
+}
+
+static int devtmpfs_statfs(struct dfs_mnt *mnt, struct statfs *buf)
+{
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(mnt != NULL);
+    RT_ASSERT(buf != NULL);
+
+    superblock = (struct devtmpfs_sb *)mnt->data;
+    RT_ASSERT(superblock != NULL);
+
+    buf->f_bsize = 512;
+    buf->f_blocks = (superblock->df_size + 511) / 512;
+    buf->f_bfree = 1;
+    buf->f_bavail = buf->f_bfree;
+
+    return RT_EOK;
+}
+
+static int devtmpfs_stat(struct dfs_dentry *dentry, struct stat *st)
+{
+    struct dfs_vnode *vnode;
+
+    if (dentry && dentry->vnode)
+    {
+        vnode = dentry->vnode;
+
+        st->st_dev = (dev_t)(long)(dentry->mnt->dev_id);
+        st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry);
+
+        st->st_gid = vnode->gid;
+        st->st_uid = vnode->uid;
+        st->st_mode = vnode->mode;
+        st->st_nlink = vnode->nlink;
+        st->st_size = vnode->size;
+        st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
+        st->st_mtim.tv_sec = vnode->mtime.tv_sec;
+        st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
+        st->st_ctim.tv_sec = vnode->ctime.tv_sec;
+        st->st_atim.tv_nsec = vnode->atime.tv_nsec;
+        st->st_atim.tv_sec = vnode->atime.tv_sec;
+    }
+
+    return RT_EOK;
+}
+
+static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
+{
+    struct devtmpfs_file *d_file;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(file);
+    RT_ASSERT(file->dentry);
+    RT_ASSERT(file->dentry->mnt);
+
+    superblock = (struct devtmpfs_sb *)file->dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    d_file = devtmpfs_file_lookup(superblock, file->dentry->pathname);
+    if (d_file)
+    {
+        rt_size_t index, end;
+        struct dirent *d;
+        struct devtmpfs_file *n_file;
+        rt_list_t *list;
+
+        /* make integer count */
+        count = (count / sizeof(struct dirent));
+        if (count == 0)
+        {
+            return -EINVAL;
+        }
+
+        end = file->fpos + count;
+        index = 0;
+        count = 0;
+
+        rt_list_for_each(list, &d_file->subdirs)
+        {
+            if (index >= (rt_size_t)file->fpos)
+            {
+                n_file = rt_list_entry(list, struct devtmpfs_file, sibling);
+
+                d = dirp + count;
+                if (n_file->type == TMPFS_TYPE_FILE)
+                {
+                    d->d_type = DT_REG;
+                }
+                if (n_file->type == TMPFS_TYPE_DIR)
+                {
+                    d->d_type = DT_DIR;
+                }
+
+                d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
+                rt_strncpy(d->d_name, n_file->name, DIRENT_NAME_MAX);
+                d->d_namlen = rt_strlen(d->d_name);
+
+                count += 1;
+                file->fpos += 1;
+            }
+            index += 1;
+            if (index >= end)
+            {
+                break;
+            }
+        }
+    }
+
+    return count * sizeof(struct dirent);
+}
+
+static int devtmpfs_symlink(struct dfs_dentry *parent_dentry, const char *target, const char *linkpath)
+{
+    int ret = RT_EOK;
+    struct devtmpfs_file *p_file, *l_file;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(parent_dentry);
+    RT_ASSERT(parent_dentry->mnt);
+
+    superblock = (struct devtmpfs_sb *)parent_dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    p_file = devtmpfs_file_lookup(superblock, parent_dentry->pathname);
+    if (p_file)
+    {
+        l_file = (struct devtmpfs_file *)rt_calloc(1, sizeof(struct devtmpfs_file));
+        if (l_file)
+        {
+            superblock->df_size += sizeof(struct devtmpfs_file);
+
+            strncpy(l_file->name, linkpath, DIRENT_NAME_MAX - 1);
+
+            rt_list_init(&(l_file->subdirs));
+            rt_list_init(&(l_file->sibling));
+            l_file->sb = superblock;
+            l_file->type = TMPFS_TYPE_FILE;
+            l_file->mode = p_file->mode;
+            l_file->mode &= ~S_IFMT;
+            l_file->mode |= S_IFLNK;
+            l_file->link = rt_strdup(target);
+
+            rt_spin_lock(&superblock->lock);
+            rt_list_insert_after(&(p_file->subdirs), &(l_file->sibling));
+            rt_spin_unlock(&superblock->lock);
+        }
+    }
+
+    return ret;
+}
+
+static int devtmpfs_readlink(struct dfs_dentry *dentry, char *buf, int len)
+{
+    int ret = 0;
+    struct devtmpfs_file *d_file;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(dentry);
+    RT_ASSERT(dentry->mnt);
+
+    superblock = (struct devtmpfs_sb *)dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    d_file = devtmpfs_file_lookup(superblock, dentry->pathname);
+    if (d_file)
+    {
+        if (d_file->link)
+        {
+            if (d_file->type == TMPFS_TYPE_DYN_DEV)
+            {
+                rt_device_t device = (void *)d_file->link;
+                buf[0] = '\0';
+                ret = device->readlink(device, buf, len);
+                if (ret == 0)
+                {
+                    buf[len - 1] = '\0';
+                    ret = rt_strlen(buf);
+                }
+                else
+                {
+                    ret = 0;
+                }
+            }
+            else
+            {
+                rt_strncpy(buf, (const char *)d_file->link, len);
+                buf[len - 1] = '\0';
+                ret = rt_strlen(buf);
+            }
+        }
+    }
+
+    return ret;
+}
+
+static int devtmpfs_unlink(struct dfs_dentry *dentry)
+{
+    struct devtmpfs_file *d_file;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(dentry);
+    RT_ASSERT(dentry->mnt);
+
+    superblock = (struct devtmpfs_sb *)dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    d_file = devtmpfs_file_lookup(superblock, dentry->pathname);
+    if (d_file)
+    {
+        if (d_file->link && d_file->type != TMPFS_TYPE_DYN_DEV)
+        {
+            rt_free(d_file->link);
+        }
+
+        rt_spin_lock(&superblock->lock);
+        rt_list_remove(&(d_file->sibling));
+        rt_spin_unlock(&superblock->lock);
+
+        rt_free(d_file);
+    }
+
+    return RT_EOK;
+}
+
+static int devtmpfs_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr)
+{
+    struct devtmpfs_file *d_file;
+    struct devtmpfs_sb *superblock;
+
+    RT_ASSERT(dentry);
+    RT_ASSERT(dentry->mnt);
+
+    superblock = (struct devtmpfs_sb *)dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    d_file = devtmpfs_file_lookup(superblock, dentry->pathname);
+    if (d_file)
+    {
+        d_file->mode &= ~0xFFF;
+        d_file->mode |= attr->st_mode & 0xFFF;
+        return RT_EOK;
+    }
+
+    return -RT_ERROR;
+}
+
+static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int type, mode_t mode)
+{
+    struct dfs_vnode *vnode = RT_NULL;
+    struct devtmpfs_sb *superblock;
+    struct devtmpfs_file *d_file, *p_file;
+    char parent_path[DFS_PATH_MAX], file_name[DIRENT_NAME_MAX];
+
+    if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
+    {
+        return NULL;
+    }
+
+    superblock = (struct devtmpfs_sb *)dentry->mnt->data;
+    RT_ASSERT(superblock != NULL);
+
+    vnode = dfs_vnode_create();
+    if (vnode)
+    {
+        /* find parent file */
+        _path_separate(dentry->pathname, parent_path, file_name);
+        if (file_name[0] == '\0') /* it's root dir */
+        {
+            dfs_vnode_destroy(vnode);
+            return NULL;
+        }
+
+        /* open parent directory */
+        p_file = devtmpfs_file_lookup(superblock, parent_path);
+        if (p_file == NULL)
+        {
+            dfs_vnode_destroy(vnode);
+            return NULL;
+        }
+
+        /* create a file entry */
+        d_file = (struct devtmpfs_file *)rt_calloc(1, sizeof(struct devtmpfs_file));
+        if (d_file == NULL)
+        {
+            dfs_vnode_destroy(vnode);
+            return NULL;
+        }
+
+        superblock->df_size += sizeof(struct devtmpfs_file);
+
+        strncpy(d_file->name, file_name, DIRENT_NAME_MAX);
+
+        rt_list_init(&(d_file->subdirs));
+        rt_list_init(&(d_file->sibling));
+        d_file->sb = superblock;
+
+        vnode->nlink = 1;
+        vnode->size = 0;
+        vnode->mode = mode;
+        vnode->mnt = dentry->mnt;
+        vnode->fops = &_default_fops;
+
+        if (type == FT_DIRECTORY)
+        {
+            d_file->type = TMPFS_TYPE_DIR;
+            vnode->type = FT_DIRECTORY;
+            vnode->mode &= ~S_IFMT;
+            vnode->mode |= S_IFDIR;
+        }
+        else
+        {
+            d_file->type = TMPFS_TYPE_FILE;
+            vnode->type = FT_DEVICE;
+        }
+
+        d_file->mode = vnode->mode;
+
+        rt_spin_lock(&superblock->lock);
+        rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
+        rt_spin_unlock(&superblock->lock);
+    }
+
+    return vnode;
+}
+
+static struct dfs_vnode *devtmpfs_lookup(struct dfs_dentry *dentry)
+{
+    struct dfs_vnode *vnode = RT_NULL;
+    struct devtmpfs_sb *superblock;
+    struct devtmpfs_file *d_file;
+
+    if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
+    {
+        return NULL;
+    }
+
+    superblock = (struct devtmpfs_sb *)dentry->mnt->data;
+
+    d_file = devtmpfs_file_lookup(superblock, dentry->pathname);
+    if (d_file)
+    {
+        vnode = dfs_vnode_create();
+        if (vnode)
+        {
+            vnode->nlink = 1;
+            vnode->size = 0;
+            vnode->mnt = dentry->mnt;
+            vnode->fops = &_default_fops;
+            vnode->mode = d_file->mode;
+
+            if (d_file->type == TMPFS_TYPE_DIR)
+            {
+                vnode->type = FT_DIRECTORY;
+            }
+            else if (d_file->link)
+            {
+                vnode->type = FT_SYMLINK;
+            }
+            else
+            {
+                vnode->type = FT_DEVICE;
+            }
+        }
+    }
+    else
+    {
+        rt_device_t device = RT_NULL;
+
+        device = rt_device_find(&dentry->pathname[1]);
+        if (device)
+        {
+            vnode = devtmpfs_create_vnode(dentry, FT_REGULAR, dfs_devfs_device_to_mode(device));
+            if (device->flag & RT_DEVICE_FLAG_DYNAMIC)
+            {
+                d_file = devtmpfs_file_lookup(superblock, dentry->pathname);
+                d_file->type = TMPFS_TYPE_DYN_DEV;
+                d_file->link = (char *)device;
+            }
+        }
+    }
+
+    return vnode;
+}
+
+static int devtmpfs_free_vnode(struct dfs_vnode *vnode)
+{
+    return RT_EOK;
+}
+
+static const struct dfs_filesystem_ops _devtmpfs_ops =
+{
+    .name = "devtmpfs",
+    .flags = DFS_FS_FLAG_DEFAULT,
+    .default_fops = &_default_fops,
+
+    .mount = devtmpfs_mount,
+    .umount = devtmpfs_unmount,
+
+    .symlink = devtmpfs_symlink,
+    .readlink = devtmpfs_readlink,
+
+    .unlink = devtmpfs_unlink,
+    .setattr = devtmpfs_setattr,
+
+    .statfs = devtmpfs_statfs,
+    .stat = devtmpfs_stat,
+
+    .lookup = devtmpfs_lookup,
+    .create_vnode = devtmpfs_create_vnode,
+    .free_vnode = devtmpfs_free_vnode
+};
+
+static struct dfs_filesystem_type _devtmpfs =
+{
+    .fs_ops = &_devtmpfs_ops,
+};
+
+int dfs_devtmpfs_init(void)
+{
+    _default_fops = *dfs_devfs_fops();
+    _default_fops.getdents = devtmpfs_getdents;
+
+    /* register file system */
+    dfs_register(&_devtmpfs);
+
+    dfs_mount(RT_NULL, "/dev", "devtmpfs", 0, RT_NULL);
+    dfs_devfs_update();
+
+    return 0;
+}
+INIT_COMPONENT_EXPORT(dfs_devtmpfs_init);

+ 1 - 1
components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c

@@ -145,7 +145,7 @@ static struct dfs_vnode *dfs_mqueue_create_vnode(struct dfs_dentry *dentry, int
             }
             mq_file->msg_size = 8192;
             mq_file->max_msgs = 10;
-            strncpy(mq_file->name, dentry->pathname + 1, RT_NAME_MAX);
+            strncpy(mq_file->name, dentry->pathname + 1, RT_NAME_MAX - 1);
             dfs_mqueue_insert_after(&(mq_file->list));
         }
 

+ 5 - 0
components/dfs/dfs_v2/filesystems/ptyfs/README.md

@@ -0,0 +1,5 @@
+# The Pseudo Terminal Filesystem
+
+The device register on ptyfs is also registered in device frameworks with `rt_device_register()`.
+
+It's possible to mount a new ptyfs instance on another path. Each instance is isolated to each other. And they don't share the id system. But generally speaking, you have to mount the ptyfs on `/dev` root, since all the file nodes in ptyfs are devices.

+ 11 - 0
components/dfs/dfs_v2/filesystems/ptyfs/SConscript

@@ -0,0 +1,11 @@
+# RT-Thread building script for component
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_PTYFS'], CPPPATH = CPPPATH)
+
+Return('group')

+ 658 - 0
components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c

@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-12-02     Shell        init ver.
+ */
+#define DBG_TAG "filesystem.ptyfs"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "ptyfs.h"
+
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <dfs_dentry.h>
+#include <dfs_file.h>
+#include <dfs_mnt.h>
+#include <devfs.h>
+#include <rid_bitmap.h>
+#include <rthw.h>
+#include <rtthread.h>
+#include <terminal/terminal.h>
+
+#include <dirent.h>
+#include <unistd.h>
+
+#ifndef S_IRWXUGO
+#define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
+#endif /* S_IRWXUGO */
+#ifndef S_IALLUGO
+#define S_IALLUGO (S_ISUID | S_ISGID | S_ISVTX | S_IRWXUGO)
+#endif /* S_IALLUGO */
+#ifndef S_IRUGO
+#define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
+#endif /* S_IRUGO */
+#ifndef S_IWUGO
+#define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
+#endif /* S_IWUGO */
+#ifndef S_IXUGO
+#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif /* S_IXUGO */
+
+#define PTYFS_MAGIC           0x9D94A07D
+#define PTYFS_TYPE_DIR        0x00
+#define PTYFS_TYPE_FILE_PTMX  0x01
+#define PTYFS_TYPE_FILE_SLAVE 0x02
+
+/* TODO: using Symbolic permission, but not ours */
+#define PTMX_DEFAULT_FILE_MODE (S_IFCHR | 0666)
+#define PTS_DEFAULT_FILE_MODE  (S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP)
+#define ROOT_DEFUALT_FILE_MODE (S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR)
+
+struct ptyfs_sb;
+
+struct ptyfs_file
+{
+    char basename[DIRENT_NAME_MAX]; /* file name */
+    rt_uint32_t mode;               /* file modes allowed */
+    rt_uint32_t type;               /* file type */
+    rt_list_t subdirs;              /* file subdir list */
+    rt_list_t ent_node;             /* entry node in subdir list */
+    struct ptyfs_sb *sb;            /* superblock ptr */
+    rt_device_t device;             /* device binding on this file */
+};
+
+struct ptyfs_sb
+{
+    struct rt_device ptmx_device; /* ptmx device */
+    struct rt_mutex lock;         /* tmpfs lock */
+    struct ptyfs_file root_file;  /* root dir */
+    struct ptyfs_file ptmx_file;  /* `/ptmx` file */
+    struct rid_bitmap ptsno_pool; /* pts number pool */
+    rt_uint32_t magic;            /* PTYFS_MAGIC */
+    rt_size_t df_size;            /* df size */
+    rt_list_t sibling;            /* sb sibling list */
+    struct dfs_mnt *mount;        /* mount data */
+
+    /**
+     * Note: This upper limit is set to protect kernel memory from draining
+     * out by the application if it keeps allocating pty devices.
+     *
+     * Still, current implementation of bitmap can not efficiently use the
+     * memory
+     */
+    rt_bitmap_t
+        ptsno_pool_bitset[LWP_PTY_MAX_PARIS_LIMIT / (sizeof(rt_bitmap_t) * 8)];
+};
+
+static struct dfs_file_ops _default_fops;
+
+static int _split_out_subdir(const char *path, char *name)
+{
+    const char *subpath = path;
+
+    while (*subpath == '/' && *subpath)
+    {
+        subpath++;
+    }
+
+    while (*subpath != '/' && *subpath)
+    {
+        *name++ = *subpath++;
+    }
+
+    *name = '\0';
+    return 0;
+}
+
+static rt_err_t ptyfile_init(struct ptyfs_file *file, struct ptyfs_sb *sb,
+                             const char *name, rt_uint32_t type,
+                             rt_uint32_t mode, rt_device_t device)
+{
+    if (name)
+        strncpy(file->basename, name, sizeof(file->basename));
+
+    file->type = type;
+    file->mode = mode;
+    rt_list_init(&file->subdirs);
+    rt_list_init(&file->ent_node);
+    file->sb = sb;
+    file->device = device;
+
+    return 0;
+}
+
+static rt_err_t ptyfile_add_to_root(struct ptyfs_sb *sb,
+                                    struct ptyfs_file *new_file)
+{
+    struct ptyfs_file *root_file = &sb->root_file;
+
+    /* update super block */
+    sb->df_size += sizeof(struct ptyfs_file);
+
+    rt_mutex_take(&sb->lock, RT_WAITING_FOREVER);
+    rt_list_insert_after(&(root_file->subdirs), &(new_file->ent_node));
+    rt_mutex_release(&sb->lock);
+
+    return 0;
+}
+
+static rt_err_t ptyfile_remove_from_root(struct ptyfs_sb *sb,
+                                         struct ptyfs_file *rm_file)
+{
+    /* update super block */
+    sb->df_size -= sizeof(struct ptyfs_file);
+
+    rt_mutex_take(&sb->lock, RT_WAITING_FOREVER);
+    rt_list_remove(&(rm_file->ent_node));
+    rt_mutex_release(&sb->lock);
+
+    return 0;
+}
+
+static struct ptyfs_file *ptyfile_lookup(struct ptyfs_sb *superblock,
+                                         const char *path)
+{
+    const char *subpath_iter, *curpath_iter, *basename = RT_NULL;
+    char subdir_name[DIRENT_NAME_MAX];
+    struct ptyfs_file *curfile, *found_file = RT_NULL;
+    rt_list_t *list;
+    int do_path_resolve = 1;
+
+    subpath_iter = path;
+
+    /* skip starting "/" */
+    while (*subpath_iter == '/') subpath_iter++;
+    if (!*subpath_iter)
+    {
+        return &(superblock->root_file);
+    }
+
+    curpath_iter = subpath_iter;
+    curfile = &superblock->root_file;
+
+    /* resolve chain of files splited from path one by one */
+    while (do_path_resolve)
+    {
+        do_path_resolve = 0;
+
+        /* splitout sub-directory or basename */
+        while (*subpath_iter != '/' && *subpath_iter) subpath_iter++;
+        if (!*subpath_iter)
+        {
+            basename = curpath_iter;
+        }
+        else
+        {
+            _split_out_subdir(curpath_iter, subdir_name);
+
+            /* skip "/" for next search */
+            subpath_iter++;
+        }
+
+        rt_mutex_take(&superblock->lock, RT_WAITING_FOREVER);
+        rt_list_for_each(list, &curfile->subdirs)
+        {
+            struct ptyfs_file *file_iter;
+            file_iter = rt_list_entry(list, struct ptyfs_file, ent_node);
+            if (basename)
+            {
+                if (strcmp(file_iter->basename, basename) == 0)
+                {
+                    found_file = file_iter;
+                    break;
+                }
+            }
+            else if (strcmp(file_iter->basename, subdir_name) == 0)
+            {
+                curpath_iter = subpath_iter;
+                curfile = file_iter;
+                do_path_resolve = 1;
+                break;
+            }
+        }
+        rt_mutex_release(&superblock->lock);
+    }
+
+    return found_file;
+}
+
+const char *ptyfs_get_rootpath(rt_device_t ptmx)
+{
+    const char *rc;
+    struct ptyfs_sb *sb;
+    /* allocate id for it and register file */
+    sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
+    if (sb->magic != PTYFS_MAGIC)
+    {
+        rc = 0;
+    }
+    else
+    {
+        /* fullpath is always started with /dev/ */
+        return sb->mount->fullpath + 5;
+    }
+
+    return rc;
+}
+
+ptsno_t ptyfs_register_pts(rt_device_t ptmx, rt_device_t pts)
+{
+    ptsno_t rc;
+    struct ptyfs_sb *sb;
+    struct ptyfs_file *pts_file;
+    struct rid_bitmap *ptsno_pool;
+
+    /* allocate id for it and register file */
+    sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
+    if (sb->magic != PTYFS_MAGIC)
+    {
+        rc = -1;
+    }
+    else
+    {
+        ptsno_pool = &sb->ptsno_pool;
+        rc = rid_bitmap_get(ptsno_pool);
+        if (rc >= 0)
+        {
+            pts_file = rt_calloc(1, sizeof(struct ptyfs_file));
+            if (pts_file)
+            {
+                snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", rc);
+                ptyfile_init(pts_file, sb, 0, PTYFS_TYPE_FILE_SLAVE,
+                             PTS_DEFAULT_FILE_MODE, pts);
+                ptyfile_add_to_root(sb, pts_file);
+            }
+            else
+            {
+                rid_bitmap_put(ptsno_pool, rc);
+                rc = -1;
+            }
+        }
+        /* else rc == -1 */
+    }
+
+    return rc;
+}
+
+rt_err_t ptyfs_unregister_pts(rt_device_t ptmx, ptsno_t ptsno)
+{
+    ptsno_t rc;
+    struct ptyfs_sb *sb;
+    struct ptyfs_file *pts_file;
+    struct rid_bitmap *ptsno_pool;
+    char path_buf[DIRENT_NAME_MAX];
+
+    /* allocate id for it and register file */
+    sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
+    if (sb->magic != PTYFS_MAGIC || ptsno < 0)
+    {
+        rc = -EINVAL;
+    }
+    else
+    {
+        /* get path and findout device */
+        snprintf(path_buf, sizeof(path_buf), "%lu", ptsno);
+        pts_file = ptyfile_lookup(sb, path_buf);
+        if (pts_file)
+        {
+            ptyfile_remove_from_root(sb, pts_file);
+            ptsno_pool = &sb->ptsno_pool;
+            rid_bitmap_put(ptsno_pool, ptsno);
+            rc = 0;
+        }
+        else
+        {
+            rc = -ENOENT;
+        }
+    }
+
+    return rc;
+}
+
+#define DEVFS_PREFIX     "/dev/"
+#define DEVFS_PREFIX_LEN (sizeof(DEVFS_PREFIX) - 1)
+
+/**
+ * Create an new instance of ptyfs, and mount on target point
+ * 2 basic files are created: root, ptmx.
+ *
+ * todo: support of mount options?
+ */
+static int ptyfs_ops_mount(struct dfs_mnt *mnt, unsigned long rwflag,
+                           const void *data)
+{
+    struct ptyfs_sb *sb;
+    rt_device_t ptmx_device;
+    rt_err_t rc;
+
+    if (strncmp(mnt->fullpath, DEVFS_PREFIX, DEVFS_PREFIX_LEN) != 0)
+    {
+        LOG_I("%s() Not mounted on `/dev/'", __func__);
+        return -EINVAL;
+    }
+
+    sb = rt_calloc(1, sizeof(struct ptyfs_sb));
+    if (sb)
+    {
+        rt_mutex_init(&sb->lock, "ptyfs", RT_IPC_FLAG_PRIO);
+
+        /* setup the ptmx device */
+        ptmx_device = &sb->ptmx_device;
+        rc = lwp_ptmx_init(ptmx_device, mnt->fullpath + DEVFS_PREFIX_LEN);
+        if (rc == RT_EOK)
+        {
+            /* setup 2 basic files */
+            ptyfile_init(&sb->root_file, sb, "/", PTYFS_TYPE_DIR,
+                         ROOT_DEFUALT_FILE_MODE, 0);
+            ptyfile_init(&sb->ptmx_file, sb, "ptmx", PTYFS_TYPE_FILE_PTMX,
+                         PTMX_DEFAULT_FILE_MODE, ptmx_device);
+            ptyfile_add_to_root(sb, &sb->ptmx_file);
+
+            /* setup rid */
+            rid_bitmap_init(&sb->ptsno_pool, 0, LWP_PTY_MAX_PARIS_LIMIT,
+                            sb->ptsno_pool_bitset, &sb->lock);
+
+            /* setup properties and members */
+            sb->magic = PTYFS_MAGIC;
+
+            sb->df_size = sizeof(struct ptyfs_sb);
+            rt_list_init(&sb->sibling);
+
+            /* binding superblocks and mount point */
+            mnt->data = sb;
+            sb->mount = mnt;
+            rc = 0;
+        }
+        /* else just return rc */
+    }
+    else
+    {
+        rc = -ENOMEM;
+    }
+
+    return rc;
+}
+
+static int ptyfs_ops_umount(struct dfs_mnt *mnt)
+{
+    /* Not supported yet */
+    return -1;
+}
+
+static int ptyfs_ops_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr)
+{
+    struct ptyfs_file *pty_file;
+    struct ptyfs_sb *superblock;
+
+    RT_ASSERT(dentry);
+    RT_ASSERT(dentry->mnt);
+
+    superblock = (struct ptyfs_sb *)dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    /* find the device related to current pts slave device */
+    pty_file = ptyfile_lookup(superblock, dentry->pathname);
+    if (pty_file && pty_file->type == PTYFS_TYPE_FILE_SLAVE)
+    {
+        pty_file->mode &= ~0xFFF;
+        pty_file->mode |= attr->st_mode & 0xFFF;
+        return 0;
+    }
+
+    return -1;
+}
+
+#define OPTIMAL_BSIZE 1024
+
+static int ptyfs_ops_statfs(struct dfs_mnt *mnt, struct statfs *buf)
+{
+    struct ptyfs_sb *superblock;
+
+    RT_ASSERT(mnt != NULL);
+    RT_ASSERT(buf != NULL);
+
+    superblock = (struct ptyfs_sb *)mnt->data;
+    RT_ASSERT(superblock != NULL);
+
+    buf->f_bsize = OPTIMAL_BSIZE;
+    buf->f_blocks = (superblock->df_size + OPTIMAL_BSIZE - 1) / OPTIMAL_BSIZE;
+    buf->f_bfree = 1;
+    buf->f_bavail = buf->f_bfree;
+
+    return RT_EOK;
+}
+
+static int ptyfs_ops_stat(struct dfs_dentry *dentry, struct stat *st)
+{
+    struct dfs_vnode *vnode;
+
+    if (dentry && dentry->vnode)
+    {
+        vnode = dentry->vnode;
+
+        /* device id ? */
+        st->st_dev = (dev_t)(long)(dentry->mnt->dev_id);
+        st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry);
+
+        st->st_gid = vnode->gid;
+        st->st_uid = vnode->uid;
+        st->st_mode = vnode->mode;
+        st->st_nlink = vnode->nlink;
+        st->st_size = vnode->size;
+        st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
+        st->st_mtim.tv_sec = vnode->mtime.tv_sec;
+        st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
+        st->st_ctim.tv_sec = vnode->ctime.tv_sec;
+        st->st_atim.tv_nsec = vnode->atime.tv_nsec;
+        st->st_atim.tv_sec = vnode->atime.tv_sec;
+    }
+
+    return 0;
+}
+
+static struct dfs_vnode *ptyfs_ops_lookup(struct dfs_dentry *dentry)
+{
+    struct dfs_vnode *vnode = RT_NULL;
+    struct ptyfs_sb *superblock;
+    struct ptyfs_file *pty_file;
+
+    if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
+    {
+        return NULL;
+    }
+
+    superblock = (struct ptyfs_sb *)dentry->mnt->data;
+
+    pty_file = ptyfile_lookup(superblock, dentry->pathname);
+    if (pty_file)
+    {
+        vnode = dfs_vnode_create();
+        if (vnode)
+        {
+            vnode->data = pty_file->device;
+            vnode->nlink = 1;
+            vnode->size = 0;
+            vnode->mnt = dentry->mnt;
+            /* if it's root directory */
+            vnode->fops = &_default_fops;
+            vnode->mode = pty_file->mode;
+            vnode->type = pty_file->type == PTYFS_TYPE_DIR ? FT_DIRECTORY : FT_DEVICE;
+        }
+    }
+
+    return vnode;
+}
+
+static struct dfs_vnode *ptyfs_ops_create_vnode(struct dfs_dentry *dentry,
+                                                int type, mode_t mode)
+{
+    struct dfs_vnode *vnode = RT_NULL;
+    struct ptyfs_sb *sb;
+    struct ptyfs_file *pty_file;
+    char *vnode_path;
+
+    if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
+    {
+        return NULL;
+    }
+
+    sb = (struct ptyfs_sb *)dentry->mnt->data;
+    RT_ASSERT(sb != NULL);
+
+    vnode = dfs_vnode_create();
+    if (vnode)
+    {
+        vnode_path = dentry->pathname;
+
+        /* Query if file existed. Filter out illegal open modes */
+        pty_file = ptyfile_lookup(sb, vnode_path);
+        if (!pty_file || (~pty_file->mode & mode))
+        {
+            dfs_vnode_destroy(vnode);
+            return NULL;
+        }
+
+        vnode->data = pty_file->device;
+        vnode->nlink = 1;
+        vnode->size = 0;
+        vnode->mnt = dentry->mnt;
+        vnode->fops = pty_file->device ? pty_file->device->fops : RT_NULL;
+        vnode->mode &= pty_file->mode;
+
+        if (type == FT_DIRECTORY)
+        {
+            vnode->mode |= S_IFDIR;
+            vnode->type = FT_DIRECTORY;
+            LOG_I("%s: S_IFDIR created", __func__);
+        }
+        else if (type == FT_REGULAR)
+        {
+            vnode->mode |= S_IFCHR;
+            vnode->type = FT_DEVICE;
+            LOG_I("%s: S_IFDIR created", __func__);
+        }
+        else
+        {
+            /* unsupported types */
+            dfs_vnode_destroy(vnode);
+            return NULL;
+        }
+    }
+
+    return vnode;
+}
+
+static int ptyfs_ops_free_vnode(struct dfs_vnode *vnode)
+{
+    return RT_EOK;
+}
+
+static int devpty_deffops_getdents(struct dfs_file *file, struct dirent *dirp,
+                                   uint32_t count)
+{
+    struct ptyfs_file *d_file;
+    struct ptyfs_sb *superblock;
+
+    RT_ASSERT(file);
+    RT_ASSERT(file->dentry);
+    RT_ASSERT(file->dentry->mnt);
+
+    superblock = (struct ptyfs_sb *)file->dentry->mnt->data;
+    RT_ASSERT(superblock);
+
+    d_file = ptyfile_lookup(superblock, file->dentry->pathname);
+    if (d_file)
+    {
+        rt_size_t index, end;
+        struct dirent *d;
+        struct ptyfs_file *n_file;
+        rt_list_t *list;
+
+        /* make integer count */
+        count = (count / sizeof(struct dirent));
+        if (count == 0)
+        {
+            return -EINVAL;
+        }
+
+        end = file->fpos + count;
+        index = 0;
+        count = 0;
+
+        rt_list_for_each(list, &d_file->subdirs)
+        {
+            if (index >= (rt_size_t)file->fpos)
+            {
+                n_file = rt_list_entry(list, struct ptyfs_file, ent_node);
+
+                d = dirp + count;
+                if (n_file->type == PTYFS_TYPE_DIR)
+                {
+                    d->d_type = DT_DIR;
+                }
+                else
+                {
+                    /* ptmx(5,2) or slave(136,[0,1048575]) device, on Linux */
+                    d->d_type = DT_CHR;
+                }
+
+                d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
+                rt_strncpy(d->d_name, n_file->basename, DIRENT_NAME_MAX);
+                d->d_namlen = rt_strlen(d->d_name);
+
+                count += 1;
+                file->fpos += 1;
+            }
+            index += 1;
+            if (index >= end)
+            {
+                break;
+            }
+        }
+    }
+
+    return count * sizeof(struct dirent);
+}
+
+static const struct dfs_filesystem_ops _ptyfs_ops = {
+    .name = "ptyfs",
+    .flags = DFS_FS_FLAG_DEFAULT,
+    .default_fops = &_default_fops,
+
+    .mount = ptyfs_ops_mount,
+    .umount = ptyfs_ops_umount,
+
+    /* don't allow to create symbolic link */
+    .symlink = RT_NULL,
+    .readlink = RT_NULL,
+    .unlink = RT_NULL,
+
+    .setattr = ptyfs_ops_setattr,
+    .statfs = ptyfs_ops_statfs,
+    .stat = ptyfs_ops_stat,
+
+    .lookup = ptyfs_ops_lookup,
+    .create_vnode = ptyfs_ops_create_vnode,
+    .free_vnode = ptyfs_ops_free_vnode,
+};
+
+static struct dfs_filesystem_type _devptyfs = {
+    .fs_ops = &_ptyfs_ops,
+};
+
+static int _ptyfs_init(void)
+{
+    _default_fops = *dfs_devfs_fops();
+    _default_fops.getdents = devpty_deffops_getdents;
+
+    /* register file system */
+    dfs_register(&_devptyfs);
+
+    return 0;
+}
+INIT_COMPONENT_EXPORT(_ptyfs_init);

+ 22 - 0
components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-12-02     Shell        init ver.
+ */
+#ifndef __FS_PTYFS_H__
+#define __FS_PTYFS_H__
+#include <rtthread.h>
+
+typedef rt_base_t ptsno_t;
+
+ptsno_t ptyfs_register_pts(rt_device_t ptmx, rt_device_t pts);
+
+rt_err_t ptyfs_unregister_pts(rt_device_t ptmx, ptsno_t ptsno);
+
+const char *ptyfs_get_rootpath(rt_device_t ptmx);
+
+#endif /* __FS_PTYFS_H__ */

+ 3 - 0
components/dfs/dfs_v2/filesystems/romfs/romfs.c

@@ -29,6 +29,9 @@ rt_weak const struct romfs_dirent _root_dirent[] =
 {
     {ROMFS_DIRENT_DIR, "dev", RT_NULL, 0},
     {ROMFS_DIRENT_DIR, "mnt", RT_NULL, 0},
+    {ROMFS_DIRENT_DIR, "proc", RT_NULL, 0},
+    {ROMFS_DIRENT_DIR, "etc", RT_NULL, 0},
+    {ROMFS_DIRENT_DIR, "bin", RT_NULL, 0},
     {ROMFS_DIRENT_DIR, "dummy", (rt_uint8_t *)_dummy, sizeof(_dummy) / sizeof(_dummy[0])},
     {ROMFS_DIRENT_FILE, "dummy.txt", _dummy_txt, sizeof(_dummy_txt)},
 };

+ 27 - 11
components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c

@@ -202,6 +202,7 @@ int dfs_tmpfs_ioctl(struct dfs_file *file, int cmd, void *args)
 
     superblock = d_file->sb;
     RT_ASSERT(superblock != NULL);
+    RT_UNUSED(superblock);
 
     switch (cmd)
     {
@@ -317,26 +318,21 @@ static ssize_t dfs_tmpfs_read(struct dfs_file *file, void *buf, size_t count, of
     return length;
 }
 
-static ssize_t dfs_tmpfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
+static ssize_t _dfs_tmpfs_write(struct tmpfs_file *d_file, const void *buf, size_t count, off_t *pos)
 {
-    struct tmpfs_file *d_file;
     struct tmpfs_sb *superblock;
 
-    d_file = (struct tmpfs_file *)file->vnode->data;
     RT_ASSERT(d_file != NULL);
 
     superblock = d_file->sb;
     RT_ASSERT(superblock != NULL);
 
-    rt_mutex_take(&file->vnode->lock, RT_WAITING_FOREVER);
-
-    if (count + *pos > file->vnode->size)
+    if (count + *pos > d_file->size)
     {
         rt_uint8_t *ptr;
         ptr = rt_realloc(d_file->data, *pos + count);
         if (ptr == NULL)
         {
-            rt_mutex_release(&file->vnode->lock);
             rt_set_errno(-ENOMEM);
             return 0;
         }
@@ -347,7 +343,6 @@ static ssize_t dfs_tmpfs_write(struct dfs_file *file, const void *buf, size_t co
         /* update d_file and file size */
         d_file->data = ptr;
         d_file->size = *pos + count;
-        file->vnode->size = d_file->size;
         LOG_D("tmpfile ptr:%x, size:%d", ptr, d_file->size);
     }
 
@@ -356,6 +351,21 @@ static ssize_t dfs_tmpfs_write(struct dfs_file *file, const void *buf, size_t co
 
     /* update file current position */
     *pos += count;
+
+    return count;
+}
+
+static ssize_t dfs_tmpfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
+{
+    struct tmpfs_file *d_file;
+
+    d_file = (struct tmpfs_file *)file->vnode->data;
+    RT_ASSERT(d_file != NULL);
+
+    rt_mutex_take(&file->vnode->lock, RT_WAITING_FOREVER);
+
+    count = _dfs_tmpfs_write(d_file, buf, count, pos);
+
     rt_mutex_release(&file->vnode->lock);
 
     return count;
@@ -504,6 +514,7 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
 
     superblock  = d_file->sb;
     RT_ASSERT(superblock != RT_NULL);
+    RT_UNUSED(superblock);
 
     /* make integer count */
     count = (count / sizeof(struct dirent));
@@ -797,6 +808,8 @@ static ssize_t dfs_tmp_page_read(struct dfs_file *file, struct dfs_page *page)
 
 ssize_t dfs_tmp_page_write(struct dfs_page *page)
 {
+    off_t pos;
+    size_t count = 0;
     struct tmpfs_file *d_file;
 
     if (page->aspace->vnode->type == FT_DIRECTORY)
@@ -806,13 +819,16 @@ ssize_t dfs_tmp_page_write(struct dfs_page *page)
 
     d_file = (struct tmpfs_file *)(page->aspace->vnode->data);
     RT_ASSERT(d_file != RT_NULL);
+
     rt_mutex_take(&page->aspace->vnode->lock, RT_WAITING_FOREVER);
     if (page->len > 0)
-        memcpy(d_file->data + page->fpos, page->page, page->len);
-
+    {
+        pos = page->fpos;
+        count = _dfs_tmpfs_write(d_file, page->page, page->len, &pos);
+    }
     rt_mutex_release(&page->aspace->vnode->lock);
 
-    return F_OK;
+    return count;
 }
 #endif
 

+ 12 - 0
components/dfs/dfs_v2/include/dfs.h

@@ -25,6 +25,10 @@
 #include <rtatomic.h>
 #include <rtdevice.h>
 
+#ifndef ATTR_MODE_SET
+#define ATTR_MODE_SET	(1 << 6)
+#endif
+
 #ifndef ATTR_ATIME_SET
 #define ATTR_ATIME_SET	(1 << 7)
 #endif
@@ -33,6 +37,14 @@
 #define ATTR_MTIME_SET	(1 << 8)
 #endif
 
+#ifndef ATTR_UID_SET
+#define ATTR_UID_SET	(1 << 9)
+#endif
+
+#ifndef ATTR_GID_SET
+#define ATTR_GID_SET	(1 << 10)
+#endif
+
 #ifndef AT_SYMLINK_NOFOLLOW
 #define AT_SYMLINK_NOFOLLOW 0x100
 #endif

+ 8 - 3
components/dfs/dfs_v2/include/dfs_file.h

@@ -175,20 +175,25 @@ int dfs_file_isdir(const char *path);
 int dfs_file_access(const char *path, mode_t mode);
 int dfs_file_chdir(const char *path);
 char *dfs_file_getcwd(char *buf, size_t size);
-char *dfs_nolink_path(struct dfs_mnt **mnt, char *fullpath, int mode);
+
 #ifdef RT_USING_SMART
 int dfs_file_mmap2(struct dfs_file *file, struct dfs_mmap2_args *mmap2);
 
 int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2);
 #endif
 
-char *dfs_nolink_path(struct dfs_mnt **mnt, char *fullpath, int mode);
-
 /* 0x5254 is just a magic number to make these relatively unique ("RT") */
 #define RT_FIOFTRUNCATE  0x52540000U
 #define RT_FIOGETADDR    0x52540001U
 #define RT_FIOMMAP2      0x52540002U
 
+/* dfs_file_realpath mode */
+#define DFS_REALPATH_EXCEPT_LAST    0
+#define DFS_REALPATH_EXCEPT_NONE    1
+#define DFS_REALPATH_ONLY_LAST      3
+
+char *dfs_file_realpath(struct dfs_mnt **mnt, const char *fullpath, int mode);
+
 #ifdef __cplusplus
 }
 #endif

+ 3 - 1
components/dfs/dfs_v2/include/dfs_fs.h

@@ -32,7 +32,9 @@ struct dfs_partition
 struct dfs_attr
 {
     unsigned int ia_valid;
-    mode_t  st_mode;
+    uid_t st_uid;
+    gid_t st_gid;
+    mode_t st_mode;
     struct timespec ia_atime;
     struct timespec ia_mtime;
 };

+ 4 - 1
components/dfs/dfs_v2/include/dfs_pcache.h

@@ -23,6 +23,8 @@ extern "C"
 {
 #endif
 
+struct rt_varea;
+struct rt_aspace;
 struct dfs_vnode;
 struct dfs_dentry;
 struct dfs_aspace;
@@ -30,7 +32,8 @@ struct dfs_aspace;
 struct dfs_mmap
 {
     rt_list_t mmap_node;
-    struct rt_varea *varea;
+    struct rt_aspace *aspace;
+    void *vaddr;
 };
 
 struct dfs_page

+ 110 - 1
components/dfs/dfs_v2/src/dfs.c

@@ -557,6 +557,111 @@ exit:
     return newfd;
 }
 
+/**
+ * @brief  The fd in the current process dup to designate fd table.
+ *
+ * @param  oldfd is the fd in current process.
+ *
+ * @param  fdtab is the fd table to dup, if empty, use global (_fdtab).
+ *
+ * @return -1 on failed or the allocated file descriptor.
+ */
+int dfs_dup_to(int oldfd, struct dfs_fdtable *fdtab)
+{
+    int newfd = -1;
+    struct dfs_fdtable *fdt = NULL;
+
+    if (dfs_file_lock() != RT_EOK)
+    {
+        return -RT_ENOSYS;
+    }
+
+    if (fdtab == NULL)
+    {
+        fdtab = &_fdtab;
+    }
+
+    /* check old fd */
+    fdt = dfs_fdtable_get();
+    if ((oldfd < 0) || (oldfd >= fdt->maxfd))
+    {
+        goto exit;
+    }
+    if (!fdt->fds[oldfd])
+    {
+        goto exit;
+    }
+    /* get a new fd*/
+    newfd = _fdt_slot_alloc(fdtab, DFS_STDIO_OFFSET);
+    if (newfd >= 0)
+    {
+        fdtab->fds[newfd] = fdt->fds[oldfd];
+
+        /* inc ref_count */
+        rt_atomic_add(&(fdtab->fds[newfd]->ref_count), 1);
+    }
+exit:
+    dfs_file_unlock();
+
+    return newfd;
+}
+
+/**
+ * @brief  The fd in the designate fd table dup to current process.
+ *
+ * @param  oldfd is the fd in the designate fd table.
+ *
+ * @param  fdtab is the fd table for oldfd, if empty, use global (_fdtab).
+ *
+ * @return -1 on failed or the allocated file descriptor.
+ */
+int dfs_dup_from(int oldfd, struct dfs_fdtable *fdtab)
+{
+    int newfd = -1;
+    struct dfs_file *file;
+
+    if (dfs_file_lock() != RT_EOK)
+    {
+        return -RT_ENOSYS;
+    }
+
+    if (fdtab == NULL)
+    {
+        fdtab = &_fdtab;
+    }
+
+    /* check old fd */
+    if ((oldfd < 0) || (oldfd >= fdtab->maxfd))
+    {
+        goto exit;
+    }
+    if (!fdtab->fds[oldfd])
+    {
+        goto exit;
+    }
+    /* get a new fd*/
+    newfd = fd_new();
+    file = fd_get(newfd);
+    if (newfd >= 0 && file)
+    {
+        file->mode = fdtab->fds[oldfd]->mode;
+        file->flags = fdtab->fds[oldfd]->flags;
+        file->fops = fdtab->fds[oldfd]->fops;
+        file->dentry = dfs_dentry_ref(fdtab->fds[oldfd]->dentry);
+        file->vnode = fdtab->fds[oldfd]->vnode;
+        file->mmap_context = RT_NULL;
+        file->data = fdtab->fds[oldfd]->data;
+    }
+
+    dfs_file_close(fdtab->fds[oldfd]);
+
+exit:
+    fdt_fd_release(fdtab, oldfd);
+    dfs_file_unlock();
+
+    return newfd;
+}
+
 #ifdef RT_USING_SMART
 sysret_t sys_dup(int oldfd)
 #else
@@ -856,7 +961,11 @@ int dfs_fd_dump(int argc, char** argv)
 {
     int index;
 
-    dfs_file_lock();
+    if (dfs_file_lock() != RT_EOK)
+    {
+        return -RT_ENOSYS;
+    }
+
     for (index = 0; index < _fdtab.maxfd; index++)
     {
         struct dfs_file *file = _fdtab.fds[index];

+ 149 - 158
components/dfs/dfs_v2/src/dfs_file.c

@@ -29,22 +29,86 @@
 
 #define MAX_RW_COUNT 0xfffc0000
 
-rt_inline int _find_path_node(const char *path)
+rt_inline int _first_path_len(const char *path)
 {
     int i = 0;
 
-    while (path[i] != '\0')
+    if (path[i] == '/')
     {
-        if ('/' == path[i++])
+        i++;
+        while (path[i] != '\0' && path[i] != '/')
         {
-            break;
+            i++;
         }
     }
 
-    /* return path-note length */
     return i;
 }
 
+static int _get_parent_path(const char *fullpath, char *path)
+{
+    int len = 0;
+    char *str = 0;
+
+    str = strrchr(fullpath, '/');
+    if (str)
+    {
+        len = str - fullpath;
+        if (len > 0)
+        {
+            rt_memcpy(path, fullpath, len);
+            path[len] = '\0';
+        }
+    }
+
+    return len;
+}
+
+static int _try_readlink(const char *path, struct dfs_mnt *mnt, char *link)
+{
+    int ret = -1;
+    struct dfs_dentry *dentry = dfs_dentry_lookup(mnt, path, 0);
+
+    if (dentry && dentry->vnode->type == FT_SYMLINK)
+    {
+        if (mnt->fs_ops->readlink)
+        {
+            if (dfs_is_mounted(mnt) == 0)
+            {
+                ret = mnt->fs_ops->readlink(dentry, link, DFS_PATH_MAX);
+            }
+        }
+    }
+    dfs_dentry_unref(dentry);
+
+    return ret;
+}
+
+static int _insert_link_path(const char *link_fn, int link_len, char *tmp_path, int *index)
+{
+    int ret = -1;
+
+    if (link_fn[0] != '/')
+    {
+        if (link_len + 1 <= *index)
+        {
+            *index -= link_len;
+            rt_memcpy(tmp_path + *index, link_fn, link_len);
+            *index -= 1;
+            tmp_path[*index] = '/';
+            ret = 0;
+        }
+    }
+    else if (link_len <= *index)
+    {
+        *index -= link_len;
+        rt_memcpy(tmp_path + *index, link_fn, link_len);
+        ret = 1;
+    }
+
+    return ret;
+}
+
 /*
  * rw_verify_area doesn't like huge counts. We limit
  * them to something that fits in "int" so that others
@@ -149,166 +213,97 @@ static void dfs_file_unref(struct dfs_file *file)
     }
 }
 
-struct dfs_dentry* dfs_file_follow_link(struct dfs_dentry *dentry)
-{
-    int ret = 0;
-    struct dfs_dentry *tmp = dfs_dentry_ref(dentry);
-
-    if (dentry && dentry->vnode && dentry->vnode->type == FT_SYMLINK)
-    {
-        char *buf = NULL;
-
-        buf = (char *)rt_malloc(DFS_PATH_MAX);
-        if (buf)
-        {
-            do
-            {
-                if (dfs_is_mounted(tmp->mnt) == 0)
-                {
-                    ret = tmp->mnt->fs_ops->readlink(tmp, buf, DFS_PATH_MAX);
-                }
-
-                if (ret > 0)
-                {
-                    struct dfs_mnt *mnt = NULL;
-
-                    if (buf[0] != '/')
-                    {
-                        char *dir = dfs_dentry_pathname(tmp);
-
-                        /* is the relative directory */
-                        if (dir)
-                        {
-                            char *fullpath = dfs_normalize_path(dir, buf);
-                            if (fullpath)
-                            {
-                                strncpy(buf, fullpath, DFS_PATH_MAX);
-
-                                rt_free(fullpath);
-                            }
-                            rt_free(dir);
-                        }
-                    }
-
-                    mnt = dfs_mnt_lookup(buf);
-                    if (mnt)
-                    {
-                        struct dfs_dentry *de = dfs_dentry_lookup(mnt, buf, 0);
-
-                        /* release the old dentry */
-                        dfs_dentry_unref(tmp);
-                        tmp = de;
-                    }
-                }
-                else
-                {
-                    break;
-                }
-            } while (tmp && tmp->vnode->type == FT_SYMLINK);
-        }
-
-        rt_free(buf);
-    }
-
-    return tmp;
-}
-
 /*
  * this function is creat a nolink path.
  *
  * @param mnt
  * @param fullpath
- * @param mode 0 middle path nolink; 1 all path nolink.
+ * @param mode
  *
  * @return new path.
  */
-char *dfs_nolink_path(struct dfs_mnt **mnt, char *fullpath, int mode)
+char *dfs_file_realpath(struct dfs_mnt **mnt, const char *fullpath, int mode)
 {
-    int index = 0;
-    char *path = RT_NULL;
-    char *link_fn;
-    struct dfs_dentry *dentry = RT_NULL;
-
-    path = (char *)rt_malloc((DFS_PATH_MAX * 2) + 2); // path + \0 + link_fn + \0
-    if (!path)
-    {
-        return path;
-    }
-    link_fn = path + DFS_PATH_MAX + 1;
+    int path_len = 0, index = 0;
+    char *path = RT_NULL, *link_fn, *tmp_path;
+    struct dfs_mnt *tmp_mnt;
 
     if (*mnt && fullpath)
     {
-        int i = 0;
-        char *fp = fullpath;
+        int len, link_len;
+
+        path = (char *)rt_malloc((DFS_PATH_MAX * 3) + 3); // path + \0 + link_fn + \0 + tmp_path + \0
+        if (!path)
+        {
+            return RT_NULL;
+        }
+
+        link_fn = path + DFS_PATH_MAX + 1;
+        tmp_path = link_fn + (DFS_PATH_MAX + 1);
+
+        len = rt_strlen(fullpath);
+        if (len > DFS_PATH_MAX)
+        {
+            goto _ERR_RET;
+        }
+
+        index = (DFS_PATH_MAX - len);
+        rt_strcpy(tmp_path + index, fullpath);
+
+        if (mode == DFS_REALPATH_ONLY_LAST)
+        {
+            path_len = _get_parent_path(fullpath, path);
+            index += path_len;
+        }
 
-        while ((i = _find_path_node(fp)) > 0)
+        while ((len = _first_path_len(tmp_path + index)) > 0)
         {
-            if (i + index > DFS_PATH_MAX)
+            if (len + path_len > DFS_PATH_MAX)
             {
                 goto _ERR_RET;
             }
 
-            rt_memcpy(path + index, fp, i);
-            path[index + i] = '\0';
+            rt_memcpy(path + path_len, tmp_path + index, len);
+            path[path_len + len] = '\0';
+            index += len;
 
-            /* the last should by mode process. */
-            if ((fp[i] == '\0') && (!mode))
+            tmp_mnt = dfs_mnt_lookup(path);
+            if (tmp_mnt == RT_NULL)
             {
-                break;
+                goto _ERR_RET;
             }
 
-            fp += i;
+            *mnt = tmp_mnt;
 
-            dentry = dfs_dentry_lookup(*mnt, path, 0);
-            if (dentry && dentry->vnode->type == FT_SYMLINK)
+            /* the last should by mode process. */
+            if ((tmp_path[index] == '\0') && (mode == DFS_REALPATH_EXCEPT_LAST))
             {
-                int ret = -1;
+                break;
+            }
 
-                if ((*mnt)->fs_ops->readlink)
-                {
-                    if (dfs_is_mounted((*mnt)) == 0)
-                    {
-                        ret = (*mnt)->fs_ops->readlink(dentry, link_fn, DFS_PATH_MAX);
-                    }
-                }
+            link_len = _try_readlink(path, *mnt, link_fn);
+            if (link_len > 0)
+            {
+                int ret = _insert_link_path(link_fn, link_len, tmp_path, &index);
 
-                if (ret > 0)
+                if (ret == 1)
                 {
-                    int len = rt_strlen(link_fn);
-
-                    if (link_fn[0] != '/')
-                    {
-                        path[index] = '/';
-                    }
-                    else
-                    {
-                        index = 0;
-                    }
-
-                    if (len + index + 1 >= DFS_PATH_MAX)
-                    {
-                        goto _ERR_RET;
-                    }
-
-                    rt_memcpy(path + index, link_fn, len);
-                    index += len;
-                    path[index] = '\0';
-                    *mnt = dfs_mnt_lookup(path);
+                    /* link_fn[0] == '/' */
+                    path_len = 0;
                 }
-                else
+                else if (ret < 0)
                 {
                     goto _ERR_RET;
                 }
             }
             else
             {
-                index += i;
+                path_len += len;
             }
-            dfs_dentry_unref(dentry);
         }
-    }
-    else
-    {
+
+        return path;
+
 _ERR_RET:
         rt_free(path);
         path = RT_NULL;
@@ -349,7 +344,7 @@ int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mo
             mnt = dfs_mnt_lookup(fullpath);
             if (mnt)
             {
-                char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+                char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
                 if (tmp)
                 {
                     rt_free(fullpath);
@@ -370,9 +365,12 @@ int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mo
                     else
                     {
                         struct dfs_dentry *target_dentry = RT_NULL;
-
-                        /* follow symbol link */
-                        target_dentry = dfs_file_follow_link(dentry);
+                        char *path = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_ONLY_LAST);
+                        if (path)
+                        {
+                            target_dentry = dfs_dentry_lookup(mnt, path, oflags);
+                            rt_free(path);
+                        }
                         dfs_dentry_unref(dentry);
                         dentry = target_dentry;
                     }
@@ -507,7 +505,7 @@ int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mo
 
                         if (ret < 0)
                         {
-                            LOG_E("open %s failed in file system: %s", path, dentry->mnt->fs_ops->name);
+                            LOG_I("open %s failed in file system: %s", path, dentry->mnt->fs_ops->name);
                             DLOG(msg, mnt->fs_ops->name, "dfs_file", DLOG_MSG_RET, "open failed.");
                             dfs_file_unref(file);
                         }
@@ -791,7 +789,7 @@ int dfs_file_stat(const char *path, struct stat *buf)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 1);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -845,7 +843,7 @@ int dfs_file_lstat(const char *path, struct stat *buf)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -924,7 +922,7 @@ int dfs_file_setattr(const char *path, struct dfs_attr *attr)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -1097,7 +1095,7 @@ int dfs_file_unlink(const char *path)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -1199,7 +1197,7 @@ int dfs_file_link(const char *oldname, const char *newname)
             return -1;
         }
 
-        char *tmp = dfs_nolink_path(&mnt, old_fullpath, 0);
+        char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST);
         if (tmp)
         {
             rt_free(old_fullpath);
@@ -1210,7 +1208,7 @@ int dfs_file_link(const char *oldname, const char *newname)
     new_fullpath = dfs_normalize_path(NULL, newname);
     if (new_fullpath)
     {
-        char *tmp = dfs_nolink_path(&mnt, new_fullpath, 0);
+        char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST);
         if (tmp)
         {
             rt_free(new_fullpath);
@@ -1310,7 +1308,7 @@ int dfs_file_symlink(const char *target, const char *linkpath)
                 mnt = dfs_mnt_lookup(parent);
                 if (mnt)
                 {
-                    char *tmp = dfs_nolink_path(&mnt, parent, 0);
+                    char *tmp = dfs_file_realpath(&mnt, parent, DFS_REALPATH_EXCEPT_LAST);
                     if (tmp)
                     {
                         rt_free(parent);
@@ -1318,7 +1316,7 @@ int dfs_file_symlink(const char *target, const char *linkpath)
                     }
 
                     DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath);
-                    dentry = dfs_dentry_lookup(mnt, parent, 0);
+                    dentry = dfs_dentry_lookup(mnt, parent, DFS_REALPATH_EXCEPT_LAST);
                     if (dentry)
                     {
                         if (dentry->mnt->fs_ops->symlink)
@@ -1326,17 +1324,6 @@ int dfs_file_symlink(const char *target, const char *linkpath)
                             char *path = dfs_normalize_path(parent, target);
                             if (path)
                             {
-                                char *tmp = dfs_nolink_path(&mnt, path, 0);
-                                if (tmp)
-                                {
-                                    rt_free(path);
-                                    path = tmp;
-                                }
-                                else
-                                {
-                                    tmp = path;
-                                }
-
                                 ret = rt_strncmp(parent, path, strlen(parent));
                                 if (ret == 0)
                                 {
@@ -1346,6 +1333,10 @@ int dfs_file_symlink(const char *target, const char *linkpath)
                                         tmp ++;
                                     }
                                 }
+                                else
+                                {
+                                    tmp = path;
+                                }
 
                                 if (dfs_is_mounted(mnt) == 0)
                                 {
@@ -1401,7 +1392,7 @@ int dfs_file_readlink(const char *path, char *buf, int bufsize)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -1466,7 +1457,7 @@ int dfs_file_rename(const char *old_file, const char *new_file)
             return -1;
         }
 
-        char *tmp = dfs_nolink_path(&mnt, old_fullpath, 0);
+        char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST);
         if (tmp)
         {
             rt_free(old_fullpath);
@@ -1477,7 +1468,7 @@ int dfs_file_rename(const char *old_file, const char *new_file)
     new_fullpath = dfs_normalize_path(NULL, new_file);
     if (new_fullpath)
     {
-        char *tmp = dfs_nolink_path(&mnt, new_fullpath, 0);
+        char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST);
         if (tmp)
         {
             rt_free(new_fullpath);
@@ -1652,7 +1643,7 @@ int dfs_file_isdir(const char *path)
         mnt = dfs_mnt_lookup(fullpath);
         if (mnt)
         {
-            char *tmp = dfs_nolink_path(&mnt, fullpath, 1);
+            char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE);
             if (tmp)
             {
                 rt_free(fullpath);
@@ -1853,7 +1844,7 @@ void ls(const char *pathname)
                                 mnt = dfs_mnt_lookup(fullpath);
                                 if (mnt)
                                 {
-                                    char *tmp = dfs_nolink_path(&mnt, fullpath, 0);
+                                    char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
                                     if (tmp)
                                     {
                                         char *index;

+ 34 - 16
components/dfs/dfs_v2/src/dfs_pcache.c

@@ -269,7 +269,6 @@ static void dfs_pcache_thread(void *parameter)
                                 {
                                     page->len = page->size;
                                 }
-                                //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
                                 if (aspace->ops->write)
                                 {
                                     aspace->ops->write(page);
@@ -676,9 +675,14 @@ static int dfs_page_unmap(struct dfs_page *page)
 
         if (map)
         {
-            void *vaddr = dfs_aspace_vaddr(map->varea, page->fpos);
-            //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, vaddr, ARCH_PAGE_SIZE);
-            rt_varea_unmap_page(map->varea, vaddr);
+            rt_varea_t varea;
+            void *vaddr;
+
+            varea = rt_aspace_query(map->aspace, map->vaddr);
+            RT_ASSERT(varea);
+            vaddr = dfs_aspace_vaddr(varea, page->fpos);
+
+            rt_varea_unmap_page(varea, vaddr);
 
             rt_free(map);
         }
@@ -741,7 +745,6 @@ static void dfs_page_release(struct dfs_page *page)
             {
                 page->len = page->size;
             }
-            //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
             if (aspace->ops->write)
             {
                 aspace->ops->write(page);
@@ -995,7 +998,6 @@ static struct dfs_page *dfs_aspace_load_page(struct dfs_file *file, off_t pos)
             page->size = ARCH_PAGE_SIZE;
             page->fpos = pos / ARCH_PAGE_SIZE * ARCH_PAGE_SIZE;
             aspace->ops->read(file, page);
-            //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
             page->ref_count ++;
 
             dfs_page_insert(page);
@@ -1105,7 +1107,6 @@ int dfs_aspace_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
                 len = count > len ? len : count;
                 if (len)
                 {
-                    //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
                     rt_memcpy(ptr, page->page + *pos - page->fpos, len);
                     ptr += len;
                     *pos += len;
@@ -1158,7 +1159,6 @@ int dfs_aspace_write(struct dfs_file *file, const void *buf, size_t count, off_t
                 len = page->fpos + ARCH_PAGE_SIZE - *pos;
                 len = count > len ? len : count;
                 rt_memcpy(page->page + *pos - page->fpos, ptr, len);
-                //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
                 ptr += len;
                 *pos += len;
                 count -= len;
@@ -1225,7 +1225,7 @@ int dfs_aspace_flush(struct dfs_aspace *aspace)
                     {
                         page->len = page->size;
                     }
-                    //rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, page->page, page->size);
+
                     if (aspace->ops->write)
                     {
                         aspace->ops->write(page);
@@ -1277,6 +1277,7 @@ void *dfs_aspace_mmap(struct dfs_file *file, struct rt_varea *varea, void *vaddr
     void *ret = RT_NULL;
     struct dfs_page *page;
     struct dfs_aspace *aspace = file->vnode->aspace;
+    rt_aspace_t target_aspace = varea->aspace;
 
     page = dfs_page_lookup(file, dfs_aspace_fpos(varea, vaddr));
     if (page)
@@ -1284,7 +1285,8 @@ void *dfs_aspace_mmap(struct dfs_file *file, struct rt_varea *varea, void *vaddr
         struct dfs_mmap *map = (struct dfs_mmap *)rt_calloc(1, sizeof(struct dfs_mmap));
         if (map)
         {
-            void *pg_paddr = rt_kmem_v2p(page->page);
+            void *pg_vaddr = page->page;
+            void *pg_paddr = rt_kmem_v2p(pg_vaddr);
             int err = rt_varea_map_range(varea, vaddr, pg_paddr, page->size);
             if (err == RT_EOK)
             {
@@ -1301,10 +1303,12 @@ void *dfs_aspace_mmap(struct dfs_file *file, struct rt_varea *varea, void *vaddr
                  * fetching of the next instruction can see the coherent data with the data cache,
                  * TLB, MMU, main memory, and all the other observers in the computer system.
                  */
+                rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, vaddr, ARCH_PAGE_SIZE);
                 rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, vaddr, ARCH_PAGE_SIZE);
 
-                ret = page->page;
-                map->varea = varea;
+                ret = pg_vaddr;
+                map->aspace = target_aspace;
+                map->vaddr = vaddr;
                 dfs_aspace_lock(aspace);
                 rt_list_insert_after(&page->mmap_head, &map->mmap_node);
                 dfs_page_release(page);
@@ -1329,6 +1333,8 @@ int dfs_aspace_unmap(struct dfs_file *file, struct rt_varea *varea)
 {
     struct dfs_vnode *vnode = file->vnode;
     struct dfs_aspace *aspace = vnode->aspace;
+    void *unmap_start = varea->start;
+    void *unmap_end = (char *)unmap_start + varea->size;
 
     if (aspace)
     {
@@ -1347,20 +1353,32 @@ int dfs_aspace_unmap(struct dfs_file *file, struct rt_varea *varea)
                     {
                         rt_list_t *node, *tmp;
                         struct dfs_mmap *map;
+                        rt_varea_t map_varea = RT_NULL;
 
                         node = page->mmap_head.next;
 
                         while (node != &page->mmap_head)
                         {
+                            rt_aspace_t map_aspace;
                             map = rt_list_entry(node, struct dfs_mmap, mmap_node);
                             tmp = node;
                             node = node->next;
 
-                            if (map && varea == map->varea)
+                            if (map && varea->aspace == map->aspace
+                                && map->vaddr >= unmap_start && map->vaddr < unmap_end)
                             {
-                                void *vaddr = dfs_aspace_vaddr(map->varea, page->fpos);
+                                void *vaddr = map->vaddr;
+                                map_aspace = map->aspace;
+
+                                if (!map_varea || map_varea->aspace != map_aspace ||
+                                    vaddr < map_varea->start ||
+                                    vaddr >= map_varea->start + map_varea->size)
+                                {
+                                    /* lock the tree so we don't access uncompleted data */
+                                    map_varea = rt_aspace_query(map_aspace, vaddr);
+                                }
 
-                                rt_varea_unmap_page(map->varea, vaddr);
+                                rt_varea_unmap_page(map_varea, vaddr);
 
                                 if (varea->attr == MMU_MAP_U_RWCB && page->fpos < page->aspace->vnode->size)
                                 {
@@ -1405,7 +1423,7 @@ int dfs_aspace_page_unmap(struct dfs_file *file, struct rt_varea *varea, void *v
                 tmp = node;
                 node = node->next;
 
-                if (map && varea == map->varea)
+                if (map && varea->aspace == map->aspace && vaddr == map->vaddr)
                 {
                     if (varea->attr == MMU_MAP_U_RWCB)
                     {

+ 0 - 11
components/drivers/Kconfig

@@ -57,17 +57,6 @@ menuconfig RT_USING_SERIAL
             default 64
     endif
 
-config RT_USING_TTY
-    bool "Using TTY SYSTEM"
-    depends on RT_USING_SMART
-    default y
-
-if RT_USING_TTY
-    config RT_TTY_DEBUG
-        bool "Using TTY DEBUG"
-        default n
-endif
-
 config RT_USING_CAN
     bool "Using CAN device drivers"
     default n

+ 8 - 0
components/drivers/core/device.c

@@ -29,6 +29,10 @@
 #include <rtdevice.h> /* for wqueue_init */
 #endif /* RT_USING_POSIX_DEVIO */
 
+#ifdef RT_USING_DFS_V2
+#include <devfs.h>
+#endif /* RT_USING_DFS_V2 */
+
 #ifdef RT_USING_DEVICE
 
 #ifdef RT_USING_DEVICE_OPS
@@ -78,6 +82,10 @@ rt_err_t rt_device_register(rt_device_t dev,
     rt_wqueue_init(&(dev->wait_queue));
 #endif /* RT_USING_POSIX_DEVIO */
 
+#ifdef RT_USING_DFS_V2
+    dfs_devfs_device_add(dev);
+#endif /* RT_USING_DFS_V2 */
+
     return RT_EOK;
 }
 RTM_EXPORT(rt_device_register);

+ 1 - 1
components/drivers/include/drivers/pic.h

@@ -107,7 +107,7 @@ struct rt_pic_irq
     rt_uint32_t mode;
 
     rt_uint32_t priority;
-    RT_DECLARE_BITMAP(affinity, RT_CPUS_NR);
+    RT_BITMAP_DECLARE(affinity, RT_CPUS_NR);
 
     rt_list_t list;
     rt_list_t children_nodes;

+ 1 - 0
components/drivers/include/drivers/serial.h

@@ -179,4 +179,5 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
                                rt_uint32_t              flag,
                                void                    *data);
 
+rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial);
 #endif

+ 1 - 0
components/drivers/include/drivers/serial_v2.h

@@ -191,4 +191,5 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device      *serial,
                                       rt_uint32_t            flag,
                                       void                  *data);
 
+rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial);
 #endif

+ 1 - 0
components/drivers/include/ipc/completion.h

@@ -33,5 +33,6 @@ void rt_completion_init(struct rt_completion *completion);
 rt_err_t rt_completion_wait(struct rt_completion *completion,
                             rt_int32_t            timeout);
 void rt_completion_done(struct rt_completion *completion);
+rt_err_t rt_completion_wakeup(struct rt_completion *completion);
 
 #endif

+ 38 - 0
components/drivers/include/ipc/condvar.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-20     Shell        Add cond var API in kernel
+ */
+
+#ifndef __LWP_TERMINAL_CONDVAR_H__
+#define __LWP_TERMINAL_CONDVAR_H__
+
+#include <rtthread.h>
+
+typedef struct rt_condvar
+{
+#ifdef USING_RT_OBJECT
+    struct rt_object parent;
+#endif
+    rt_atomic_t waiters_cnt;
+    rt_atomic_t waiting_mtx;
+    struct rt_wqueue event;
+} *rt_condvar_t;
+
+void rt_condvar_init(rt_condvar_t cv, char *name);
+int rt_condvar_timedwait(rt_condvar_t cv, rt_mutex_t mtx, int suspend_flag,
+                         rt_tick_t timeout);
+int rt_condvar_signal(rt_condvar_t cv);
+int rt_condvar_broadcast(rt_condvar_t cv);
+
+rt_inline void rt_condvar_detach(rt_condvar_t cv)
+{
+    RT_UNUSED(cv);
+    return ;
+}
+
+#endif /* __LWP_TERMINAL_CONDVAR_H__ */

+ 2 - 0
components/drivers/include/ipc/pipe.h

@@ -12,6 +12,7 @@
 
 #include <rtdef.h>
 #include <rtconfig.h>
+#include "condvar.h"
 
 /**
  * Pipe Device
@@ -34,6 +35,7 @@ struct rt_pipe_device
     int writer;
     int reader;
 
+    struct rt_condvar waitfor_parter;
     struct rt_mutex lock;
 };
 typedef struct rt_pipe_device rt_pipe_t;

+ 1 - 0
components/drivers/include/ipc/waitqueue.h

@@ -49,6 +49,7 @@ int  rt_wqueue_wait(rt_wqueue_t *queue, int condition, int timeout);
 int  rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int timeout);
 int  rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int timeout);
 void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key);
+void rt_wqueue_wakeup_all(rt_wqueue_t *queue, void *key);
 
 #define DEFINE_WAIT_FUNC(name, function)                \
     struct rt_wqueue_node name = {                      \

+ 1 - 0
components/drivers/include/rtdevice.h

@@ -26,6 +26,7 @@
 #include "ipc/completion.h"
 #include "ipc/dataqueue.h"
 #include "ipc/workqueue.h"
+#include "ipc/condvar.h"
 #include "ipc/waitqueue.h"
 #include "ipc/pipe.h"
 #include "ipc/poll.h"

+ 30 - 2
components/drivers/ipc/completion.c

@@ -134,7 +134,7 @@ RTM_EXPORT(rt_completion_wait);
  *
  * @param completion is a pointer to a completion object.
  */
-void rt_completion_done(struct rt_completion *completion)
+static int _completion_done(struct rt_completion *completion)
 {
     rt_base_t level;
     rt_err_t error;
@@ -145,7 +145,7 @@ void rt_completion_done(struct rt_completion *completion)
     if (RT_COMPLETION_FLAG(completion) == RT_COMPLETED)
     {
         rt_spin_unlock_irqrestore(&_completion_lock, level);
-        return;
+        return -RT_EBUSY;
     }
 
     suspend_thread = RT_COMPLETION_THREAD(completion);
@@ -160,10 +160,38 @@ void rt_completion_done(struct rt_completion *completion)
             LOG_D("%s: failed to resume thread", __func__);
         }
     }
+    else
+    {
+        /* no thread waiting */
+        error = -RT_EEMPTY;
+    }
 
     completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_COMPLETED);
 
     rt_spin_unlock_irqrestore(&_completion_lock, level);
+
+    return error;
+}
+
+/**
+ * @brief This function indicates a completion has done.
+ *
+ * @param completion is a pointer to a completion object.
+ */
+void rt_completion_done(struct rt_completion *completion)
+{
+    _completion_done(completion);
 }
 RTM_EXPORT(rt_completion_done);
 
+/**
+ * @brief This function indicates a completion has done and wakeup the thread
+ *
+ * @param completion is a pointer to a completion object.
+ */
+rt_err_t rt_completion_wakeup(struct rt_completion *completion)
+{
+    return _completion_done(completion);
+}
+RTM_EXPORT(rt_completion_wakeup);
+

+ 173 - 0
components/drivers/ipc/condvar.c

@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-20     Shell        Support of condition variable
+ */
+#define DBG_TAG "ipc.condvar"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <rtdevice.h>
+#include <rtatomic.h>
+#include <rtthread.h>
+
+static struct rt_spinlock _local_cv_queue_lock = RT_SPINLOCK_INIT;
+
+#define CV_ASSERT_LOCKED(cv)                                       \
+    RT_ASSERT(!(cv)->waiting_mtx ||                                \
+              rt_mutex_get_owner((rt_mutex_t)(cv)->waiting_mtx) == \
+                  rt_thread_self())
+
+void rt_condvar_init(rt_condvar_t cv, char *name)
+{
+#ifdef USING_RT_OBJECT
+    /* TODO: support rt object */
+    rt_object_init();
+#endif
+
+    rt_wqueue_init(&cv->event);
+    rt_atomic_store(&cv->waiters_cnt, 0);
+    rt_atomic_store(&cv->waiting_mtx, 0);
+}
+
+static int _waitq_inqueue(rt_wqueue_t *queue, struct rt_wqueue_node *node,
+                          rt_tick_t timeout, int suspend_flag)
+{
+    rt_thread_t tcb = node->polling_thread;
+    rt_timer_t timer = &(tcb->thread_timer);
+    rt_err_t ret;
+
+    if (queue->flag != RT_WQ_FLAG_WAKEUP)
+    {
+        ret = rt_thread_suspend_with_flag(tcb, suspend_flag);
+        if (ret == RT_EOK)
+        {
+            rt_wqueue_add(queue, node);
+            if (timeout != RT_WAITING_FOREVER)
+            {
+                rt_timer_control(timer, RT_TIMER_CTRL_SET_TIME, &timeout);
+
+                rt_timer_start(timer);
+            }
+        }
+    }
+    else
+    {
+        ret = RT_EOK;
+    }
+
+    return ret;
+}
+
+#define INIT_WAITQ_NODE(node)                                  \
+    {                                                          \
+        .polling_thread = rt_thread_self(), .key = 0,          \
+        .wakeup = __wqueue_default_wake, .wqueue = &cv->event, \
+        .list = RT_LIST_OBJECT_INIT(node.list)                 \
+    }
+
+int rt_condvar_timedwait(rt_condvar_t cv, rt_mutex_t mtx, int suspend_flag,
+                         rt_tick_t timeout)
+{
+    rt_err_t acq_mtx_succ, rc;
+    rt_atomic_t waiting_mtx;
+    struct rt_wqueue_node node = INIT_WAITQ_NODE(node);
+
+    /* not allowed in IRQ & critical section */
+    RT_DEBUG_SCHEDULER_AVAILABLE(1);
+    CV_ASSERT_LOCKED(cv);
+
+    /**
+     * for the worst case, this is racy with the following works to reset field
+     * before mutex is taken. The spinlock then comes to rescue.
+     */
+    rt_spin_lock(&_local_cv_queue_lock);
+    waiting_mtx = rt_atomic_load(&cv->waiting_mtx);
+    if (!waiting_mtx)
+        acq_mtx_succ = rt_atomic_compare_exchange_strong(
+            &cv->waiting_mtx, &waiting_mtx, (size_t)mtx);
+    else
+        acq_mtx_succ = 0;
+
+    rt_spin_unlock(&_local_cv_queue_lock);
+
+    if (acq_mtx_succ == 1 || waiting_mtx == (size_t)mtx)
+    {
+        rt_atomic_add(&cv->waiters_cnt, 1);
+
+        rt_enter_critical();
+
+        if (suspend_flag == RT_INTERRUPTIBLE)
+            rc = _waitq_inqueue(&cv->event, &node, timeout, RT_INTERRUPTIBLE);
+        else /* UNINTERRUPTIBLE is forbidden, since it's not safe for user space */
+            rc = _waitq_inqueue(&cv->event, &node, timeout, RT_KILLABLE);
+
+        acq_mtx_succ = rt_mutex_release(mtx);
+        RT_ASSERT(acq_mtx_succ == 0);
+        rt_exit_critical();
+
+        if (rc == RT_EOK)
+        {
+            rt_schedule();
+
+            rc = rt_get_errno();
+            rc = rc > 0 ? -rc : rc;
+        }
+        else
+        {
+            LOG_D("%s() failed to suspend", __func__);
+        }
+
+        rt_wqueue_remove(&node);
+
+        rt_spin_lock(&_local_cv_queue_lock);
+        if (rt_atomic_add(&cv->waiters_cnt, -1) == 1)
+        {
+            waiting_mtx = (size_t)mtx;
+            acq_mtx_succ = rt_atomic_compare_exchange_strong(&cv->waiting_mtx,
+                                                      &waiting_mtx, 0);
+            RT_ASSERT(acq_mtx_succ == 1);
+        }
+        rt_spin_unlock(&_local_cv_queue_lock);
+
+        acq_mtx_succ = rt_mutex_take(mtx, RT_WAITING_FOREVER);
+        RT_ASSERT(acq_mtx_succ == 0);
+    }
+    else
+    {
+        LOG_D("%s: conflict waiting mutex", __func__);
+        rc = -EBUSY;
+    }
+
+    return rc;
+}
+
+/** Keep in mind that we always operating when cv.waiting_mtx is taken */
+
+int rt_condvar_signal(rt_condvar_t cv)
+{
+    CV_ASSERT_LOCKED(cv);
+
+    /* to avoid spurious wakeups */
+    if (rt_atomic_load(&cv->waiters_cnt) > 0)
+        rt_wqueue_wakeup(&cv->event, 0);
+
+    cv->event.flag = 0;
+    return 0;
+}
+
+int rt_condvar_broadcast(rt_condvar_t cv)
+{
+    CV_ASSERT_LOCKED(cv);
+
+    /* to avoid spurious wakeups */
+    if (rt_atomic_load(&cv->waiters_cnt) > 0)
+        rt_wqueue_wakeup_all(&cv->event, 0);
+
+    cv->event.flag = 0;
+    return 0;
+}

+ 34 - 11
components/drivers/ipc/pipe.c

@@ -9,11 +9,13 @@
  * 2017-11-08     JasonJiaJie  fix memory leak issue when close a pipe.
  * 2023-06-28     shell        return POLLHUP when writer closed its channel on poll()
  *                             fix flag test on pipe_fops_open()
+ * 2023-12-02     shell        Make read pipe operation interruptable.
  */
 #include <rthw.h>
 #include <rtdevice.h>
 #include <stdint.h>
 #include <sys/errno.h>
+#include <ipc/condvar.h>
 
 #if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE)
 #include <unistd.h>
@@ -69,12 +71,12 @@ static int pipe_fops_open(struct dfs_file *fd)
 
     if ((fd->flags & O_ACCMODE) == O_RDONLY)
     {
-        pipe->reader = 1;
+        pipe->reader += 1;
     }
 
     if ((fd->flags & O_ACCMODE) == O_WRONLY)
     {
-        pipe->writer = 1;
+        pipe->writer += 1;
     }
     if (fd->vnode->ref_count == 1)
     {
@@ -86,6 +88,21 @@ static int pipe_fops_open(struct dfs_file *fd)
         }
     }
 
+    if ((fd->flags & O_ACCMODE) == O_RDONLY && !pipe->writer)
+    {
+        /* wait for partner */
+        rc = rt_condvar_timedwait(&pipe->waitfor_parter, &pipe->lock,
+                                  RT_INTERRUPTIBLE, RT_WAITING_FOREVER);
+        if (rc != 0)
+        {
+            pipe->reader--;
+        }
+    }
+    else if ((fd->flags & O_ACCMODE) == O_WRONLY)
+    {
+        rt_condvar_broadcast(&pipe->waitfor_parter);
+    }
+
 __exit:
     rt_mutex_release(&pipe->lock);
 
@@ -117,12 +134,12 @@ static int pipe_fops_close(struct dfs_file *fd)
 
     if ((fd->flags & O_RDONLY) == O_RDONLY)
     {
-        pipe->reader = 0;
+        pipe->reader -= 1;
     }
 
     if ((fd->flags & O_WRONLY) == O_WRONLY)
     {
-        pipe->writer = 0;
+        pipe->writer -= 1;
         while (!rt_list_isempty(&pipe->reader_queue.waiting_list))
         {
             rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN);
@@ -234,7 +251,8 @@ static ssize_t pipe_fops_read(struct dfs_file *fd, void *buf, size_t count)
 
             rt_mutex_release(&pipe->lock);
             rt_wqueue_wakeup(&pipe->writer_queue, (void*)POLLOUT);
-            rt_wqueue_wait(&pipe->reader_queue, 0, -1);
+            if (rt_wqueue_wait_interruptible(&pipe->reader_queue, 0, -1) == -RT_EINTR)
+                return -EINTR;
             rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
         }
     }
@@ -309,7 +327,8 @@ static ssize_t pipe_fops_write(struct dfs_file *fd, const void *buf, size_t coun
         rt_mutex_release(&pipe->lock);
         rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN);
         /* pipe full, waiting on suspended write list */
-        rt_wqueue_wait(&pipe->writer_queue, 0, -1);
+        if (rt_wqueue_wait_interruptible(&pipe->writer_queue, 0, -1) == -RT_EINTR)
+            return -EINTR;
         rt_mutex_take(&pipe->lock, -1);
     }
     rt_mutex_release(&pipe->lock);
@@ -611,6 +630,8 @@ rt_pipe_t *rt_pipe_create(const char *name, int bufsz)
     rt_mutex_init(&pipe->lock, name, RT_IPC_FLAG_FIFO);
     rt_wqueue_init(&pipe->reader_queue);
     rt_wqueue_init(&pipe->writer_queue);
+    rt_condvar_init(&pipe->waitfor_parter, "piwfp");
+
     pipe->writer = 0;
     pipe->reader = 0;
 
@@ -674,6 +695,7 @@ int rt_pipe_delete(const char *name)
 
             pipe = (rt_pipe_t *)device;
 
+            rt_condvar_detach(&pipe->waitfor_parter);
             rt_mutex_detach(&pipe->lock);
 #if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE)
             resource_id_put(&id_mgr, pipe->pipeno);
@@ -736,11 +758,6 @@ int pipe(int fildes[2])
     pipe->is_named = RT_FALSE; /* unamed pipe */
     pipe->pipeno = pipeno;
     rt_snprintf(dev_name, sizeof(dev_name), "/dev/%s", dname);
-    fildes[0] = open(dev_name, O_RDONLY, 0);
-    if (fildes[0] < 0)
-    {
-        return -1;
-    }
 
     fildes[1] = open(dev_name, O_WRONLY, 0);
     if (fildes[1] < 0)
@@ -749,6 +766,12 @@ int pipe(int fildes[2])
         return -1;
     }
 
+    fildes[0] = open(dev_name, O_RDONLY, 0);
+    if (fildes[0] < 0)
+    {
+        return -1;
+    }
+
     return 0;
 }
 

+ 83 - 6
components/drivers/ipc/waitqueue.c

@@ -9,7 +9,11 @@
  *                             to blocked thread.
  * 2022-01-24     THEWON       let rt_wqueue_wait return thread->error when using signal
  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
+ * 2023-11-21     Shell        Support wakeup_all
  */
+#define DBG_TAG "ipc.waitqueue"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
 
 #include <stdint.h>
 #include <rthw.h>
@@ -64,7 +68,8 @@ int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
 }
 
 /**
- * @brief    This function will wake up a pending thread on the specified waiting queue that meets the conditions.
+ * @brief    This function will wake up a pending thread on the specified
+ * waiting queue that meets the conditions.
  *
  * @param    queue is a pointer to the wait queue.
  *
@@ -94,17 +99,89 @@ void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
             entry = rt_list_entry(node, struct rt_wqueue_node, list);
             if (entry->wakeup(entry, key) == 0)
             {
-                rt_thread_resume(entry->polling_thread);
-                need_schedule = 1;
+                /**
+                 * even though another thread may interrupt the thread and
+                 * wakeup it meanwhile, we can asuume that condition is ready
+                 */
+                entry->polling_thread->error = RT_EOK;
+                if (!rt_thread_resume(entry->polling_thread))
+                {
+                    need_schedule = 1;
+
+                    rt_list_remove(&(entry->list));
+
+                    break;
+                }
+            }
+        }
+    }
+    rt_spin_unlock_irqrestore(&(queue->spinlock), level);
+    if (need_schedule)
+        rt_schedule();
+
+    return;
+}
+
+/**
+ * @brief    This function will wake up all pending thread on the specified
+ *           waiting queue that meets the conditions.
+ *
+ * @param    queue is a pointer to the wait queue.
+ *
+ * @param    key is the wakeup conditions, but it is not effective now, because
+ *           default wakeup function always return 0.
+ *           If user wants to use it, user should define their own wakeup
+ *           function.
+ */
+void rt_wqueue_wakeup_all(rt_wqueue_t *queue, void *key)
+{
+    rt_base_t level;
+    int need_schedule = 0;
 
-                rt_list_remove(&(entry->list));
-                break;
+    rt_list_t *queue_list;
+    struct rt_list_node *node;
+    struct rt_wqueue_node *entry;
+
+    queue_list = &(queue->waiting_list);
+
+    level = rt_spin_lock_irqsave(&(queue->spinlock));
+    /* set wakeup flag in the queue */
+    queue->flag = RT_WQ_FLAG_WAKEUP;
+
+    if (!(rt_list_isempty(queue_list)))
+    {
+        for (node = queue_list->next; node != queue_list; )
+        {
+            entry = rt_list_entry(node, struct rt_wqueue_node, list);
+            if (entry->wakeup(entry, key) == 0)
+            {
+                /**
+                 * even though another thread may interrupt the thread and
+                 * wakeup it meanwhile, we can asuume that condition is ready
+                 */
+                entry->polling_thread->error = RT_EOK;
+                if (!rt_thread_resume(entry->polling_thread))
+                {
+                    need_schedule = 1;
+                }
+                else
+                {
+                    /* wakeup happened too soon that waker hadn't slept */
+                    LOG_D("%s: Thread resume failed", __func__);
+                }
+                node = node->next;
+            }
+            else
+            {
+                node = node->next;
             }
         }
     }
     rt_spin_unlock_irqrestore(&(queue->spinlock), level);
     if (need_schedule)
         rt_schedule();
+
+    return;
 }
 
 /**
@@ -184,7 +261,7 @@ __exit_wakeup:
 
     rt_wqueue_remove(&__wait);
 
-    return tid->error;
+    return tid->error > 0 ? -tid->error : tid->error;
 }
 
 int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)

+ 1 - 0
components/drivers/ktime/inc/ktime.h

@@ -136,6 +136,7 @@ rt_inline void rt_ktime_hrtimer_keep_errno(rt_ktime_hrtimer_t timer, rt_err_t er
     RT_ASSERT(timer != RT_NULL);
 
     timer->error = err;
+    rt_set_errno(-err);
 }
 
 /**

+ 41 - 32
components/drivers/ofw/base.c

@@ -1109,17 +1109,20 @@ struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id)
 
     if (tag && id >= 0)
     {
-        rt_list_for_each_entry(info, &_aliases_nodes, list)
+        if (!rt_list_isempty(&_aliases_nodes))
         {
-            if (rt_strncmp(info->tag, tag, info->tag_len))
+            rt_list_for_each_entry(info, &_aliases_nodes, list)
             {
-                continue;
-            }
+                if (rt_strncmp(info->tag, tag, info->tag_len))
+                {
+                    continue;
+                }
 
-            if (info->id == id)
-            {
-                np = info->np;
-                break;
+                if (info->id == id)
+                {
+                    np = info->np;
+                    break;
+                }
             }
         }
     }
@@ -1130,18 +1133,20 @@ struct rt_ofw_node *rt_ofw_get_alias_node(const char *tag, int id)
 int ofw_alias_node_id(struct rt_ofw_node *np)
 {
     int id;
-    struct alias_info *info;
+    struct alias_info *info = RT_NULL;
 
     if (np)
     {
         id = -1;
-
-        rt_list_for_each_entry(info, &_aliases_nodes, list)
+        if (!rt_list_isempty(&_aliases_nodes))
         {
-            if (info->np == np)
+            rt_list_for_each_entry(info, &_aliases_nodes, list)
             {
-                id = info->id;
-                break;
+                if (info->np == np)
+                {
+                    id = info->id;
+                    break;
+                }
             }
         }
     }
@@ -1161,18 +1166,20 @@ int rt_ofw_get_alias_id(struct rt_ofw_node *np, const char *tag)
     if (np && tag)
     {
         id = -1;
-
-        rt_list_for_each_entry(info, &_aliases_nodes, list)
+        if (!rt_list_isempty(&_aliases_nodes))
         {
-            if (rt_strncmp(info->tag, tag, info->tag_len))
+            rt_list_for_each_entry(info, &_aliases_nodes, list)
             {
-                continue;
-            }
+                if (rt_strncmp(info->tag, tag, info->tag_len))
+                {
+                    continue;
+                }
 
-            if (info->np == np)
-            {
-                id = info->id;
-                break;
+                if (info->np == np)
+                {
+                    id = info->id;
+                    break;
+                }
             }
         }
     }
@@ -1192,17 +1199,19 @@ int rt_ofw_get_alias_last_id(const char *tag)
     if (tag)
     {
         id = -1;
-
-        rt_list_for_each_entry(info, &_aliases_nodes, list)
+        if (!rt_list_isempty(&_aliases_nodes))
         {
-            if (rt_strncmp(info->tag, tag, info->tag_len))
+            rt_list_for_each_entry(info, &_aliases_nodes, list)
             {
-                continue;
-            }
+                if (rt_strncmp(info->tag, tag, info->tag_len))
+                {
+                    continue;
+                }
 
-            if (info->id > id)
-            {
-                id = info->id;
+                if (info->id > id)
+                {
+                    id = info->id;
+                }
             }
         }
     }
@@ -1465,7 +1474,7 @@ static const char *ofw_get_prop_fuzzy_name(const struct rt_ofw_node *np, const c
     char *sf, split_field[64];
     rt_size_t len = 0, max_ak = 0;
     const char *str, *result = RT_NULL;
-    RT_DECLARE_BITMAP(ak, sizeof(split_field));
+    RT_BITMAP_DECLARE(ak, sizeof(split_field));
     struct rt_ofw_prop *prop;
 
     /*

+ 3 - 0
components/drivers/serial/SConscript

@@ -8,6 +8,9 @@ src = []
 if not GetDepend(['RT_USING_SERIAL']):
     Return('group')
 
+if GetDepend(['RT_USING_SMART']):
+    src += Glob('serial_tty.c')
+
 if GetDepend(['RT_USING_SERIAL_V2']):
     src += ['serial_v2.c']
 else:

+ 28 - 8
components/drivers/serial/serial.c

@@ -909,7 +909,7 @@ static rt_ssize_t rt_serial_write(struct rt_device *dev,
     }
 }
 
-#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_TTY)
+#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_SMART)
 struct speed_baudrate_item
 {
     speed_t speed;
@@ -1058,7 +1058,7 @@ static rt_err_t rt_serial_control(struct rt_device *dev,
             }
             break;
 #ifdef RT_USING_POSIX_STDIO
-#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_TTY)
+#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_SMART)
         case TCGETA:
             {
                 struct termios *tio = (struct termios*)args;
@@ -1315,9 +1315,21 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
     device->fops        = &_serial_fops;
 #endif
 
+#if defined(RT_USING_SMART)
+    rt_hw_serial_register_tty(serial);
+#endif
+
     return ret;
 }
 
+#if defined(RT_USING_SMART) && defined(LWP_DEBUG)
+static volatile int _early_input = 0;
+int lwp_startup_debug_request(void)
+{
+    return _early_input;
+}
+#endif
+
 /* ISR for serial interrupt */
 void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
 {
@@ -1360,8 +1372,17 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
                 rt_spin_unlock_irqrestore(&(serial->spinlock), level);
             }
 
-            /* invoke callback */
-            if (serial->parent.rx_indicate != RT_NULL)
+            /**
+             * Invoke callback.
+             * First try notify if any, and if notify is existed, rx_indicate()
+             * is not callback. This seperate the priority and makes the reuse
+             * of same serial device reasonable for RT console.
+             */
+            if (serial->rx_notify.notify)
+            {
+                serial->rx_notify.notify(serial->rx_notify.dev);
+            }
+            else if (serial->parent.rx_indicate != RT_NULL)
             {
                 rt_size_t rx_length;
 
@@ -1376,10 +1397,9 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
                     serial->parent.rx_indicate(&serial->parent, rx_length);
                 }
             }
-            if (serial->rx_notify.notify)
-            {
-                serial->rx_notify.notify(serial->rx_notify.dev);
-            }
+        #if defined(RT_USING_SMART) && defined(LWP_DEBUG)
+            _early_input = 1;
+        #endif
             break;
         }
         case RT_SERIAL_EVENT_TX_DONE:

+ 321 - 0
components/drivers/serial/serial_tty.c

@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-21     Shell        init ver.
+ */
+
+#define DBG_TAG "drivers.serial"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <terminal/terminal.h>
+
+#define TTY_NAME_PREFIX "S" /* (S)erial */
+#define LWP_TTY_WORKQUEUE_PRIORITY 3
+
+struct serial_tty_context
+{
+    struct rt_serial_device *parent;
+    struct rt_device_notify backup_notify;
+    struct rt_work work;
+};
+
+static struct rt_workqueue *_ttyworkq; /* system work queue */
+
+static rt_atomic_t _device_id_counter = 0;
+
+static long get_dec_digits(rt_ubase_t val)
+{
+    long result = 1;
+    while (1)
+    {
+        if (val < 10)
+            return result;
+        if (val < 100)
+            return result + 1;
+        if (val < 1000)
+            return result + 2;
+        if (val < 10000)
+            return result + 3;
+        val /= 10000U;
+        result += 4;
+    }
+    return result;
+}
+
+static char *alloc_device_name(void)
+{
+    char *tty_dev_name;
+    unsigned int devid = rt_atomic_add(&_device_id_counter, 1);
+    long digits_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */
+                      + get_dec_digits(devid) + 1;  /* tailing \0 */
+
+    tty_dev_name = rt_malloc(digits_len);
+    if (tty_dev_name)
+        rt_sprintf(tty_dev_name, "%s%u", TTY_NAME_PREFIX, devid);
+    return tty_dev_name;
+}
+
+static void _tty_rx_notify(struct rt_device *device)
+{
+    lwp_tty_t tp;
+    struct serial_tty_context *softc;
+
+    tp = rt_container_of(device, struct lwp_tty, parent);
+    RT_ASSERT(tp);
+
+    softc = tty_softc(tp);
+
+    if (_ttyworkq)
+        rt_workqueue_submit_work(_ttyworkq, &softc->work, 0);
+}
+
+static void _tty_rx_worker(struct rt_work *work, void *data)
+{
+    char input;
+    rt_ssize_t readbytes;
+    lwp_tty_t tp = data;
+    struct serial_tty_context *softc;
+    struct rt_serial_device *serial;
+
+    tty_lock(tp);
+
+    while (1)
+    {
+        softc = tty_softc(tp);
+        serial = softc->parent;
+        readbytes = rt_device_read(&serial->parent, -1, &input, 1);
+        if (readbytes != 1)
+        {
+            break;
+        }
+
+        ttydisc_rint(tp, input, 0);
+    }
+
+    ttydisc_rint_done(tp);
+    tty_unlock(tp);
+}
+
+rt_inline void _setup_serial(struct rt_serial_device *serial, lwp_tty_t tp,
+                             struct serial_tty_context *softc)
+{
+    struct rt_device_notify notify;
+
+    softc->backup_notify = serial->rx_notify;
+    notify.dev = &tp->parent;
+    notify.notify = _tty_rx_notify;
+
+    rt_device_init(&serial->parent);
+
+    rt_work_init(&softc->work, _tty_rx_worker, tp);
+    rt_device_control(&serial->parent, RT_DEVICE_CTRL_NOTIFY_SET, &notify);
+}
+
+rt_inline void _restore_serial(struct rt_serial_device *serial, lwp_tty_t tp,
+                               struct serial_tty_context *softc)
+{
+    rt_device_control(&serial->parent, RT_DEVICE_CTRL_NOTIFY_SET, &softc->backup_notify);
+}
+
+static int _serial_isbusy(struct rt_serial_device *serial)
+{
+    rt_thread_t user_thread = rt_console_current_user();
+    rt_thread_t self_thread = rt_thread_self();
+
+    return rt_console_get_device() == &serial->parent &&
+           (user_thread != RT_NULL && user_thread != self_thread);
+}
+
+static void serial_tty_outwakeup(struct lwp_tty *tp)
+{
+    char out_char;
+    int len;
+    struct serial_tty_context *context = tty_softc(tp);
+    struct rt_serial_device *device;
+
+    if (!context || !context->parent)
+    {
+        LOG_E("%s: Data corruption", __func__);
+        return;
+    }
+
+    device = context->parent;
+
+    if (_serial_isbusy(device))
+    {
+        return ;
+    }
+
+    while ((len = ttydisc_getc(tp, &out_char, sizeof(out_char))) != 0)
+    {
+        device->ops->putc(device, out_char);
+
+        /* discard remaining if emergency output is happened */
+        if (_serial_isbusy(device))
+        {
+            break;
+        }
+    }
+}
+
+static int serial_tty_open(struct lwp_tty *tp)
+{
+    struct serial_tty_context *softc;
+    struct rt_serial_device *serial;
+    rt_err_t error;
+    int oflags;
+
+    softc = tty_softc(tp);
+    serial = softc->parent;
+
+    LOG_D("%s", __func__);
+
+    rt_device_control(&serial->parent, RT_DEVICE_CTRL_CONSOLE_OFLAG, &oflags);
+
+    error = rt_device_open(&serial->parent, oflags);
+
+    if (!error)
+    {
+        /**
+         * to avoid driver accesssing null data,
+         * these are setup only after tty is registered
+         */
+        _setup_serial(serial, tp, softc);
+    }
+    return error;
+}
+
+static void serial_tty_close(struct lwp_tty *tp)
+{
+    struct serial_tty_context *softc;
+    struct rt_serial_device *serial;
+    softc = tty_softc(tp);
+    serial = softc->parent;
+
+    LOG_D("%s", __func__);
+
+    _restore_serial(serial, tp, softc);
+    rt_device_close(&serial->parent);
+}
+
+static int serial_tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+                            struct rt_thread *td)
+{
+    int error;
+    switch (cmd)
+    {
+        default:
+            /**
+             * Note: for the most case, we don't let serial layer handle ioctl,
+             * for that they can't act properly regarding to the process
+             * management system, since it is unawared of that. So a ENOSYS is
+             * returned and caused the TTY layer to handle ioctl itself.
+             */
+            error = -ENOSYS;
+            break;
+    }
+    return error;
+}
+
+static struct lwp_ttydevsw serial_ttydevsw = {
+    .tsw_open = serial_tty_open,
+    .tsw_close = serial_tty_close,
+    .tsw_ioctl = serial_tty_ioctl,
+    .tsw_outwakeup = serial_tty_outwakeup,
+};
+
+rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial)
+{
+    rt_err_t rc;
+    lwp_tty_t tty;
+    char *dev_name;
+    struct serial_tty_context *softc;
+
+    if (serial->rx_notify.dev)
+    {
+        return -RT_EBUSY;
+    }
+
+    softc = rt_malloc(sizeof(struct serial_tty_context));
+    if (softc)
+    {
+        dev_name = alloc_device_name();
+        if (dev_name)
+        {
+            softc->parent = serial;
+            tty = lwp_tty_create(&serial_ttydevsw, softc);
+            if (tty)
+            {
+                rc = lwp_tty_register(tty, dev_name);
+
+                if (rc != RT_EOK)
+                {
+                    rt_free(tty);
+                    rt_free(softc);
+                }
+            }
+            else
+            {
+                rt_free(softc);
+                rc = -RT_ENOMEM;
+            }
+
+            rt_free(dev_name);
+        }
+        else
+        {
+            rt_free(softc);
+            rc = -RT_ENOMEM;
+        }
+    }
+    else
+    {
+        rc = -RT_ENOMEM;
+    }
+
+    return rc;
+}
+
+rt_err_t rt_hw_serial_unregister_tty(struct rt_serial_device *serial)
+{
+    rt_device_t tty_dev;
+    lwp_tty_t tp;
+    struct serial_tty_context *softc;
+
+    tty_dev = serial->rx_notify.dev;
+    tp = rt_container_of(tty_dev, struct lwp_tty, parent);
+
+    /* restore serial setting */
+    softc = tty_softc(tp);
+    serial->rx_notify = softc->backup_notify;
+
+    tty_rel_gone(tp);
+
+    /* device unregister? */
+    rt_device_destroy(&tp->parent);
+    /* resource free? */
+    lwp_tty_delete(tp);
+
+    return RT_EOK;
+}
+
+static int _tty_workqueue_init(void)
+{
+    if (_ttyworkq != RT_NULL)
+        return RT_EOK;
+
+    _ttyworkq = rt_workqueue_create("ttyworkq", RT_SYSTEM_WORKQUEUE_STACKSIZE,
+                                    LWP_TTY_WORKQUEUE_PRIORITY);
+    RT_ASSERT(_ttyworkq != RT_NULL);
+
+    return RT_EOK;
+}
+INIT_PREV_EXPORT(_tty_workqueue_init);

+ 0 - 10
components/drivers/tty/SConscript

@@ -1,10 +0,0 @@
-from building import *
-
-# The set of source files associated with this SConscript file.
-src = Glob('*.c')
-cwd = GetCurrentDir()
-CPPPATH = [cwd + "/include"]
-
-group = DefineGroup('tty', src, depend = ['RT_USING_SMART', 'RT_USING_TTY'], CPPPATH = CPPPATH)
-
-Return('group')

+ 0 - 346
components/drivers/tty/console.c

@@ -1,346 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#include <rtthread.h>
-#include <dfs_file.h>
-#include <dfs_fs.h>
-#include <tty.h>
-
-#define DBG_TAG               "CONSOLE"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-#include <ipc/waitqueue.h>
-#include <ipc/ringbuffer.h>
-
-static struct tty_struct console_dev;
-static struct rt_ringbuffer console_rx_ringbuffer;
-static struct rt_wqueue console_rx_wqueue;
-static rt_thread_t console_rx_thread;
-static const size_t rb_bufsz = 0x1000;
-
-static void console_rx_work(void *parameter)
-{
-    int len;
-    char ch;
-    int lens;
-    static char buf[0x1000];
-
-    struct tty_struct *console;
-    console = &console_dev;
-
-    while (1)
-    {
-        rt_wqueue_wait(&console_rx_wqueue, 0, RT_WAITING_FOREVER);
-        lens = 0;
-
-        while (lens < sizeof(buf))
-        {
-            len = rt_ringbuffer_get(&console_rx_ringbuffer, (void *)&ch, sizeof(ch));
-            if (len == 0)
-            {
-                break;
-            }
-            lens += len;
-            buf[lens-1] = ch;
-        }
-
-        if (lens && console->ldisc->ops->receive_buf)
-        {
-            console->ldisc->ops->receive_buf((struct tty_struct *)console, buf, lens);
-        }
-    }
-}
-
-static int rx_thread_init(void)
-{
-    void *rb_buffer;
-    rt_thread_t thread;
-
-    rb_buffer = rt_malloc(rb_bufsz);
-    rt_ringbuffer_init(&console_rx_ringbuffer, rb_buffer, rb_bufsz);
-    rt_wqueue_init(&console_rx_wqueue);
-
-    thread = rt_thread_create("console_rx", console_rx_work, &console_dev, rb_bufsz, 10, 10);
-    if (thread != RT_NULL)
-    {
-        rt_thread_startup(thread);
-        console_rx_thread = thread;
-    }
-
-    return 0;
-}
-INIT_COMPONENT_EXPORT(rx_thread_init);
-
-static void console_rx_notify(struct rt_device *dev)
-{
-    struct tty_struct *console = NULL;
-    int len = 0;
-    int lens = 0;
-    char ch = 0;
-
-    console = (struct tty_struct *)dev;
-    RT_ASSERT(console != RT_NULL);
-
-    while (1)
-    {
-        len = rt_device_read(console->io_dev, -1, &ch, 1);
-        if (len == 0)
-        {
-            break;
-        }
-        lens += len;
-        rt_ringbuffer_put(&console_rx_ringbuffer, (void *)&ch, sizeof(ch));
-        if (lens > rb_bufsz)
-        {
-            break;
-        }
-    }
-    if (console_rx_thread)
-        rt_wqueue_wakeup(&console_rx_wqueue, 0);
-}
-
-struct tty_struct *console_tty_get(void)
-{
-    return &console_dev;
-}
-
-static void iodev_close(struct tty_struct *console)
-{
-    struct rt_device_notify rx_notify;
-
-    rx_notify.notify = RT_NULL;
-    rx_notify.dev = RT_NULL;
-
-    /* clear notify */
-    rt_device_control(console->io_dev, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify);
-    rt_device_close(console->io_dev);
-}
-
-static rt_err_t iodev_open(struct tty_struct *console)
-{
-    rt_err_t ret = RT_EOK;
-    struct rt_device_notify rx_notify;
-    rt_uint16_t oflags = 0;
-
-    rt_device_control(console->io_dev, RT_DEVICE_CTRL_CONSOLE_OFLAG, &oflags);
-
-    ret = rt_device_open(console->io_dev, oflags);
-    if (ret != RT_EOK)
-    {
-        return -RT_ERROR;
-    }
-
-    rx_notify.notify = console_rx_notify;
-    rx_notify.dev = (struct rt_device *)console;
-    rt_device_control(console->io_dev, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify);
-    return RT_EOK;
-}
-
-struct rt_device *console_get_iodev(void)
-{
-    return console_dev.io_dev;
-}
-
-struct rt_device *console_set_iodev(struct rt_device *iodev)
-{
-    rt_base_t level = 0;
-    struct rt_device *io_before = RT_NULL;
-    struct tty_struct *console = RT_NULL;
-
-    RT_ASSERT(iodev != RT_NULL);
-
-    console = &console_dev;
-
-    level = rt_spin_lock_irqsave(&console->spinlock);
-
-    RT_ASSERT(console->init_flag >= TTY_INIT_FLAG_REGED);
-
-    io_before = console->io_dev;
-    if (iodev == io_before)
-    {
-        goto exit;
-    }
-
-    if (console->init_flag >= TTY_INIT_FLAG_INITED)
-    {
-        /* close old device */
-        iodev_close(console);
-    }
-
-    console->io_dev = iodev;
-    if (console->init_flag >= TTY_INIT_FLAG_INITED)
-    {
-        rt_err_t ret;
-        /* open new device */
-        ret = iodev_open(console);
-        RT_ASSERT(ret == RT_EOK);
-    }
-
-exit:
-    rt_spin_unlock_irqrestore(&console->spinlock, level);
-    return io_before;
-}
-
-/* RT-Thread Device Interface */
-/*
- * This function initializes console device.
- */
-static rt_err_t rt_console_init(struct rt_device *dev)
-{
-    rt_base_t level = 0;
-    rt_err_t result = RT_EOK;
-    struct tty_struct *console = RT_NULL;
-
-    RT_ASSERT(dev != RT_NULL);
-
-    console = (struct tty_struct *)dev;
-
-    level = rt_spin_lock_irqsave(&console->spinlock);
-
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_REGED);
-
-    result = iodev_open(console);
-    if (result != RT_EOK)
-    {
-        goto exit;
-    }
-
-    console->init_flag = TTY_INIT_FLAG_INITED;
-exit:
-    rt_spin_unlock_irqrestore(&console->spinlock, level);
-    return result;
-}
-
-static rt_err_t rt_console_open(struct rt_device *dev, rt_uint16_t oflag)
-{
-    rt_err_t result = RT_EOK;
-    struct tty_struct *console = RT_NULL;
-
-    RT_ASSERT(dev != RT_NULL);
-    console = (struct tty_struct *)dev;
-    RT_ASSERT(console != RT_NULL);
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
-    return result;
-}
-
-static rt_err_t rt_console_close(struct rt_device *dev)
-{
-    rt_err_t result = RT_EOK;
-    struct tty_struct *console = RT_NULL;
-
-    console = (struct tty_struct *)dev;
-    RT_ASSERT(console != RT_NULL);
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
-    return result;
-}
-
-static rt_ssize_t rt_console_read(struct rt_device *dev,
-        rt_off_t          pos,
-        void             *buffer,
-        rt_size_t         size)
-{
-    rt_size_t len = 0;
-    return len;
-}
-
-static rt_ssize_t rt_console_write(struct rt_device *dev,
-        rt_off_t          pos,
-        const void       *buffer,
-        rt_size_t         size)
-{
-    rt_size_t len = 0;
-    struct tty_struct *console = RT_NULL;
-
-    console = (struct tty_struct *)dev;
-    RT_ASSERT(console != RT_NULL);
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
-
-    len = rt_device_write((struct rt_device *)console->io_dev, -1, buffer, size);
-
-    return len;
-}
-
-static rt_err_t  rt_console_control(rt_device_t dev, int cmd, void *args)
-{
-    rt_size_t len = 0;
-    struct tty_struct *console = RT_NULL;
-
-    console = (struct tty_struct *)dev;
-    RT_ASSERT(console != RT_NULL);
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
-
-    len = rt_device_control((struct rt_device *)console->io_dev, cmd, args);
-
-    return len;
-}
-
-#ifdef RT_USING_DEVICE_OPS
-const static struct rt_device_ops console_ops =
-{
-    rt_console_init,
-    rt_console_open,
-    rt_console_close,
-    rt_console_read,
-    rt_console_write,
-    rt_console_control,
-};
-#endif
-
-/*
- * console register
- */
-static struct dfs_file_ops con_fops;
-rt_err_t console_register(const char *name, struct rt_device *iodev)
-{
-    rt_err_t ret = RT_EOK;
-    struct rt_device *device = RT_NULL;
-    struct tty_struct *console = &console_dev;
-
-    RT_ASSERT(iodev != RT_NULL);
-    RT_ASSERT(console->init_flag == TTY_INIT_FLAG_NONE);
-
-    tty_init(console, TTY_DRIVER_TYPE_CONSOLE, SERIAL_TYPE_NORMAL, iodev);
-    console_ldata_init(console);
-
-    device = &(console->parent);
-    device->type        = RT_Device_Class_Char;
-
-#ifdef RT_USING_DEVICE_OPS
-    device->ops         = &console_ops;
-#else
-    device->init        = rt_console_init;
-    device->open        = rt_console_open;
-    device->close       = rt_console_close;
-    device->read        = rt_console_read;
-    device->write       = rt_console_write;
-    device->control     = rt_console_control;
-#endif
-
-    /* register a character device */
-    ret = rt_device_register(device, name, 0);
-    if (ret != RT_EOK)
-    {
-        LOG_E("console driver register fail\n");
-    }
-    else
-    {
-#ifdef RT_USING_POSIX_DEVIO
-        /* set fops */
-        memcpy(&con_fops, tty_get_fops(), sizeof(struct dfs_file_ops));
-        device->fops = &con_fops;
-#endif
-    }
-
-    return ret;
-}

+ 0 - 19
components/drivers/tty/include/console.h

@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#ifndef __CONSOLE_
-#define __CONSOLE_
-#include <rtthread.h>
-#include "tty.h"
-
-struct tty_struct *console_tty_get(void);
-struct rt_device *console_get_iodev(void);
-struct rt_device *console_set_iodev(struct rt_device *iodev);
-rt_err_t console_register(const char *name, struct rt_device *iodev);
-#endif

+ 0 - 304
components/drivers/tty/include/tty.h

@@ -1,304 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#ifndef __TTY_H__
-#define __TTY_H__
-#include <rthw.h>
-#include <rtthread.h>
-#include <tty_ldisc.h>
-#ifdef RT_USING_SMART
-#include <lwp.h>
-#endif
-#if defined(RT_USING_POSIX_TERMIOS)
-#include <poll.h>
-#include <termios.h>
-#endif
-
-#ifndef ENOIOCTLCMD
-#define ENOIOCTLCMD     (515) /* No ioctl command */
-#endif
-
-#define current lwp_self()
-#define __DISABLED_CHAR '\0'
-
-struct tty_node
-{
-    struct rt_lwp *lwp;
-    struct tty_node *next;
-};
-
-void tty_initstack(struct tty_node *node);
-int tty_push(struct tty_node **head, struct rt_lwp *lwp);
-struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp);
-/*
- * When a break, frame error, or parity error happens, these codes are
- * stuffed into the flags buffer.
- */
-#define TTY_NORMAL  0
-#define TTY_BREAK   1
-#define TTY_FRAME   2
-#define TTY_PARITY  3
-#define TTY_OVERRUN 4
-
-#define INTR_CHAR(tty) ((tty)->init_termios.c_cc[VINTR])
-#define QUIT_CHAR(tty) ((tty)->init_termios.c_cc[VQUIT])
-#define ERASE_CHAR(tty) ((tty)->init_termios.c_cc[VERASE])
-#define KILL_CHAR(tty) ((tty)->init_termios.c_cc[VKILL])
-#define EOF_CHAR(tty) ((tty)->init_termios.c_cc[VEOF])
-#define TIME_CHAR(tty) ((tty)->init_termios.c_cc[VTIME])
-#define MIN_CHAR(tty) ((tty)->init_termios.c_cc[VMIN])
-#define SWTC_CHAR(tty) ((tty)->init_termios.c_cc[VSWTC])
-#define START_CHAR(tty) ((tty)->init_termios.c_cc[VSTART])
-#define STOP_CHAR(tty) ((tty)->init_termios.c_cc[VSTOP])
-#define SUSP_CHAR(tty) ((tty)->init_termios.c_cc[VSUSP])
-#define EOL_CHAR(tty) ((tty)->init_termios.c_cc[VEOL])
-#define REPRINT_CHAR(tty) ((tty)->init_termios.c_cc[VREPRINT])
-#define DISCARD_CHAR(tty) ((tty)->init_termios.c_cc[VDISCARD])
-#define WERASE_CHAR(tty) ((tty)->init_termios.c_cc[VWERASE])
-#define LNEXT_CHAR(tty) ((tty)->init_termios.c_cc[VLNEXT])
-#define EOL2_CHAR(tty) ((tty)->init_termios.c_cc[VEOL2])
-
-#define _I_FLAG(tty,f)  ((tty)->init_termios.c_iflag & (f))
-#define _O_FLAG(tty,f)  ((tty)->init_termios.c_oflag & (f))
-#define _C_FLAG(tty,f)  ((tty)->init_termios.c_cflag & (f))
-#define _L_FLAG(tty,f)  ((tty)->init_termios.c_lflag & (f))
-
-#define I_IGNBRK(tty)   _I_FLAG((tty),IGNBRK)
-#define I_BRKINT(tty)   _I_FLAG((tty),BRKINT)
-#define I_IGNPAR(tty)   _I_FLAG((tty),IGNPAR)
-#define I_PARMRK(tty)   _I_FLAG((tty),PARMRK)
-#define I_INPCK(tty)    _I_FLAG((tty),INPCK)
-#define I_ISTRIP(tty)   _I_FLAG((tty),ISTRIP)
-#define I_INLCR(tty)    _I_FLAG((tty),INLCR)
-#define I_IGNCR(tty)    _I_FLAG((tty),IGNCR)
-#define I_ICRNL(tty)    _I_FLAG((tty),ICRNL)
-#define I_IUCLC(tty)    _I_FLAG((tty),IUCLC)
-#define I_IXON(tty)     _I_FLAG((tty),IXON)
-#define I_IXANY(tty)    _I_FLAG((tty),IXANY)
-#define I_IXOFF(tty)    _I_FLAG((tty),IXOFF)
-#define I_IMAXBEL(tty)  _I_FLAG((tty),IMAXBEL)
-#define I_IUTF8(tty)    _I_FLAG((tty), IUTF8)
-
-#define O_OPOST(tty)    _O_FLAG((tty),OPOST)
-#define O_OLCUC(tty)    _O_FLAG((tty),OLCUC)
-#define O_ONLCR(tty)    _O_FLAG((tty),ONLCR)
-#define O_OCRNL(tty)    _O_FLAG((tty),OCRNL)
-#define O_ONOCR(tty)    _O_FLAG((tty),ONOCR)
-#define O_ONLRET(tty)   _O_FLAG((tty),ONLRET)
-#define O_OFILL(tty)    _O_FLAG((tty),OFILL)
-#define O_OFDEL(tty)    _O_FLAG((tty),OFDEL)
-#define O_NLDLY(tty)    _O_FLAG((tty),NLDLY)
-#define O_CRDLY(tty)    _O_FLAG((tty),CRDLY)
-#define O_TABDLY(tty)   _O_FLAG((tty),TABDLY)
-#define O_BSDLY(tty)    _O_FLAG((tty),BSDLY)
-#define O_VTDLY(tty)    _O_FLAG((tty),VTDLY)
-#define O_FFDLY(tty)    _O_FLAG((tty),FFDLY)
-
-#define C_BAUD(tty)     _C_FLAG((tty),CBAUD)
-#define C_CSIZE(tty)    _C_FLAG((tty),CSIZE)
-#define C_CSTOPB(tty)   _C_FLAG((tty),CSTOPB)
-#define C_CREAD(tty)    _C_FLAG((tty),CREAD)
-#define C_PARENB(tty)   _C_FLAG((tty),PARENB)
-#define C_PARODD(tty)   _C_FLAG((tty),PARODD)
-#define C_HUPCL(tty)    _C_FLAG((tty),HUPCL)
-#define C_CLOCAL(tty)   _C_FLAG((tty),CLOCAL)
-#define C_CIBAUD(tty)   _C_FLAG((tty),CIBAUD)
-#define C_CRTSCTS(tty)  _C_FLAG((tty),CRTSCTS)
-
-#define L_ISIG(tty)     _L_FLAG((tty),ISIG)
-#define L_ICANON(tty)   _L_FLAG((tty),ICANON)
-#define L_XCASE(tty)    _L_FLAG((tty),XCASE)
-#define L_ECHO(tty)     _L_FLAG((tty),ECHO)
-#define L_ECHOE(tty)    _L_FLAG((tty),ECHOE)
-#define L_ECHOK(tty)    _L_FLAG((tty),ECHOK)
-#define L_ECHONL(tty)   _L_FLAG((tty),ECHONL)
-#define L_NOFLSH(tty)   _L_FLAG((tty),NOFLSH)
-#define L_TOSTOP(tty)   _L_FLAG((tty),TOSTOP)
-#define L_ECHOCTL(tty)  _L_FLAG((tty),ECHOCTL)
-#define L_ECHOPRT(tty)  _L_FLAG((tty),ECHOPRT)
-#define L_ECHOKE(tty)   _L_FLAG((tty),ECHOKE)
-#define L_FLUSHO(tty)   _L_FLAG((tty),FLUSHO)
-#define L_PENDIN(tty)   _L_FLAG((tty),PENDIN)
-#define L_IEXTEN(tty)   _L_FLAG((tty),IEXTEN)
-#define L_EXTPROC(tty)  _L_FLAG((tty), EXTPROC)
-
-/*
- * Where all of the state associated with a tty is kept while the tty
- * is open.  Since the termios state should be kept even if the tty
- * has been closed --- for things like the baud rate, etc --- it is
- * not stored here, but rather a pointer to the real state is stored
- * here.  Possible the winsize structure should have the same
- * treatment, but (1) the default 80x24 is usually right and (2) it's
- * most often used by a windowing system, which will set the correct
- * size each time the window is created or resized anyway.
- *                      - TYT, 9/14/92
- */
-struct tty_struct
-{
-    struct rt_device parent;
-    int type;
-    int subtype;
-    int init_flag;
-    int index;  //for pty
-    int pts_lock;   //for pty
-
-    struct tty_struct *other_struct; //for pty
-
-    struct termios init_termios;
-    struct winsize winsize;
-    struct rt_mutex lock;
-    pid_t pgrp;
-    pid_t session;
-    struct rt_lwp *foreground;
-    struct tty_node *head;
-
-    struct tty_ldisc *ldisc;
-    void *disc_data;
-    struct rt_device *io_dev;
-
-    struct rt_wqueue wait_queue;
-
-#define RT_TTY_BUF 1024
-    rt_list_t tty_drivers;
-    struct rt_spinlock spinlock;
-};
-
-enum
-{
-    TTY_INIT_FLAG_NONE = 0,
-    TTY_INIT_FLAG_ALLOCED,
-    TTY_INIT_FLAG_REGED,
-    TTY_INIT_FLAG_INITED,
-};
-
-#define TTY_DRIVER_TYPE_SYSTEM      0x0001
-#define TTY_DRIVER_TYPE_CONSOLE     0x0002
-#define TTY_DRIVER_TYPE_SERIAL      0x0003
-#define TTY_DRIVER_TYPE_PTY         0x0004
-#define TTY_DRIVER_TYPE_SCC         0x0005  /* scc driver */
-#define TTY_DRIVER_TYPE_SYSCONS     0x0006
-
-/* tty magic number */
-#define TTY_MAGIC       0x5401
-
-/*
- * These bits are used in the flags field of the tty structure.
- *
- * So that interrupts won't be able to mess up the queues,
- * copy_to_cooked must be atomic with respect to itself, as must
- * tty->write.  Thus, you must use the inline functions set_bit() and
- * clear_bit() to make things atomic.
- */
-#define TTY_THROTTLED 0
-#define TTY_IO_ERROR 1
-#define TTY_OTHER_CLOSED 2
-#define TTY_EXCLUSIVE 3
-#define TTY_DEBUG 4
-#define TTY_DO_WRITE_WAKEUP 5
-#define TTY_PUSH 6
-#define TTY_CLOSING 7
-#define TTY_DONT_FLIP 8
-#define TTY_HW_COOK_OUT 14
-#define TTY_HW_COOK_IN 15
-#define TTY_PTY_LOCK 16
-#define TTY_NO_WRITE_SPLIT 17
-
-#define NR_LDISCS       30
-
-/* line disciplines */
-#define N_TTY       0
-#define N_SLIP      1
-#define N_MOUSE     2
-#define N_PPP       3
-#define N_STRIP     4
-#define N_AX25      5
-#define N_X25       6   /* X.25 async */
-#define N_6PACK     7
-#define N_MASC      8   /* Reserved for Mobitex module <kaz@cafe.net> */
-#define N_R3964     9   /* Reserved for Simatic R3964 module */
-#define N_PROFIBUS_FDL  10  /* Reserved for Profibus */
-#define N_IRDA      11  /* Linux IrDa - http://irda.sourceforge.net/ */
-#define N_SMSBLOCK  12  /* SMS block mode - for talking to GSM data */
-                /* cards about SMS messages */
-#define N_HDLC      13  /* synchronous HDLC */
-#define N_SYNC_PPP  14  /* synchronous PPP */
-#define N_HCI       15  /* Bluetooth HCI UART */
-#define N_GIGASET_M101  16  /* Siemens Gigaset M101 serial DECT adapter */
-#define N_SLCAN     17  /* Serial / USB serial CAN Adaptors */
-#define N_PPS       18  /* Pulse per Second */
-#define N_V253      19  /* Codec control over voice modem */
-#define N_CAIF      20      /* CAIF protocol for talking to modems */
-#define N_GSM0710   21  /* GSM 0710 Mux */
-#define N_TI_WL     22  /* for TI's WL BT, FM, GPS combo chips */
-#define N_TRACESINK 23  /* Trace data routing for MIPI P1149.7 */
-#define N_TRACEROUTER   24  /* Trace data routing for MIPI P1149.7 */
-#define N_NCI       25  /* NFC NCI UART */
-
-/* Used for packet mode */
-#define TIOCPKT_DATA         0
-#define TIOCPKT_FLUSHREAD    1
-#define TIOCPKT_FLUSHWRITE   2
-#define TIOCPKT_STOP         4
-#define TIOCPKT_START        8
-#define TIOCPKT_NOSTOP      16
-#define TIOCPKT_DOSTOP      32
-
-/* pty subtypes */
-#define PTY_TYPE_MASTER         0x0001
-#define PTY_TYPE_SLAVE          0x0002
-
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL  1
-
-#define max(a, b) ({\
-        typeof(a) _a = a;\
-        typeof(b) _b = b;\
-        _a > _b ? _a : _b; })
-
-#define min(a, b) ({\
-        typeof(a) _a = a;\
-        typeof(b) _b = b;\
-        _a < _b ? _a : _b; })
-
-void mutex_lock(rt_mutex_t mutex);
-void mutex_unlock(rt_mutex_t mutex);
-int __tty_check_change(struct tty_struct *tty, int sig);
-int tty_check_change(struct tty_struct *tty);
-
-rt_inline struct rt_wqueue *wait_queue_get(struct rt_lwp *lwp, struct tty_struct *tty)
-{
-    if (lwp == RT_NULL)
-    {
-        return &tty->wait_queue;
-    }
-    return &lwp->wait_queue;
-}
-
-rt_inline struct rt_wqueue *wait_queue_current_get(struct rt_lwp *lwp, struct tty_struct *tty)
-{
-    return wait_queue_get(lwp, tty);
-}
-
-rt_inline void tty_wakeup_check(struct tty_struct *tty)
-{
-    struct rt_wqueue *wq = NULL;
-
-    wq = wait_queue_current_get(tty->foreground, tty);
-    rt_wqueue_wakeup(wq, (void*)POLLIN);
-}
-
-int tty_init(struct tty_struct *tty, int type, int subtype, struct rt_device *iodev);
-const struct dfs_file_ops *tty_get_fops(void);
-
-int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *arg);
-
-void console_ldata_init(struct tty_struct *tty);
-int n_tty_receive_buf(struct tty_struct *tty, char *cp, int count);
-
-#endif /*__TTY_H__*/

+ 0 - 56
components/drivers/tty/include/tty_ldisc.h

@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#ifndef __TTY_LDISC_
-#define __TTY_LDISC_
-#include <rtthread.h>
-#include <dfs.h>
-#include <dfs_fs.h>
-#include <tty.h>
-#if defined(RT_USING_POSIX_TERMIOS)
-#include <termios.h>
-#endif
-struct tty_struct;
-
-struct tty_ldisc_ops
-{
-    char    *name;
-    int num;
-
-    int (*open)     (struct dfs_file *fd);
-    int (*close)    (struct tty_struct *tty);
-    int (*ioctl)    (struct dfs_file *fd, int cmd, void *args);
-    int (*read)     (struct dfs_file *fd, void *buf, size_t count);
-    int (*write)    (struct dfs_file *fd, const void *buf, size_t count);
-    int (*flush)    (struct dfs_file *fd);
-    int (*lseek)    (struct dfs_file *fd, off_t offset);
-    int (*getdents) (struct dfs_file *fd, struct dirent *dirp, uint32_t count);
-
-    int (*poll)     (struct dfs_file *fd, struct rt_pollreq *req);
-    void (*set_termios) (struct tty_struct *tty, struct termios *old);
-    int (*receive_buf) (struct tty_struct *tty,char *cp, int count);
-
-    int refcount;
-};
-
-struct tty_ldisc
-{
-    struct tty_ldisc_ops *ops;
-    struct tty_struct *tty;
-};
-
-#define TTY_LDISC_MAGIC 0x5403
-
-int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
-void tty_ldisc_kill(struct tty_struct *tty);
-void tty_ldisc_init(struct tty_struct *tty);
-void tty_ldisc_release(struct tty_struct *tty);
-void n_tty_init(void);
-
-#endif // __TTY_LDISC_

+ 0 - 2223
components/drivers/tty/n_tty.c

@@ -1,2223 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#include <stddef.h>
-#include <ctype.h>
-#include <tty.h>
-#include <lwp.h>
-#if defined(RT_USING_POSIX_TERMIOS)
-#include <termios.h>
-#endif
-#include <sys/errno.h>
-
-#define DBG_TAG               "N_TTY"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-/* number of characters left in xmit buffer before select has we have room */
-#define WAKEUP_CHARS 256
-
-/*
- * This defines the low- and high-watermarks for throttling and
- * unthrottling the TTY driver.  These watermarks are used for
- * controlling the space in the read buffer.
- */
-#define TTY_THRESHOLD_THROTTLE      128 /* now based on remaining room */
-#define TTY_THRESHOLD_UNTHROTTLE    128
-
-/*
- * Special byte codes used in the echo buffer to represent operations
- * or special handling of characters.  Bytes in the echo buffer that
- * are not part of such special blocks are treated as normal character
- * codes.
- */
-#define ECHO_OP_START 0xff
-#define ECHO_OP_MOVE_BACK_COL 0x80
-#define ECHO_OP_SET_CANON_COL 0x81
-#define ECHO_OP_ERASE_TAB 0x82
-
-#define ECHO_COMMIT_WATERMARK   256
-#define ECHO_BLOCK      256
-#define ECHO_DISCARD_WATERMARK  RT_TTY_BUF - (ECHO_BLOCK + 32)
-
-static struct rt_spinlock _spinlock = RT_SPINLOCK_INIT;
-
-struct n_tty_data
-{
-    /* producer-published */
-    size_t read_head;
-    size_t commit_head;
-    size_t canon_head;
-    size_t echo_head;
-    size_t echo_commit;
-    size_t echo_mark;
-    unsigned long char_map[256];
-
-    /* non-atomic */
-    rt_bool_t no_room;
-    /* must hold exclusive termios_rwsem to reset these */
-    unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
-    unsigned char push:1;
-
-    /* shared by producer and consumer */
-    char read_buf[RT_TTY_BUF];
-    unsigned long read_flags[RT_TTY_BUF];
-    unsigned char echo_buf[RT_TTY_BUF];
-
-    /* consumer-published */
-    size_t read_tail;
-    size_t line_start;
-
-    /* protected by output lock */
-    unsigned int column;
-    unsigned int canon_column;
-    size_t echo_tail;
-
-    rt_mutex_t atomic_read_lock;
-    rt_mutex_t output_lock;
-};
-
-rt_inline int set_bit(int nr,int *addr)
-{
-    int mask, retval;
-    rt_base_t level;
-
-    addr += nr >> 5;
-    mask = 1 << (nr & 0x1f);
-    level = rt_spin_lock_irqsave(&_spinlock);
-    retval = (mask & *addr) != 0;
-    *addr |= mask;
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-    return retval;
-}
-
-rt_inline int clear_bit(int nr, int *addr)
-{
-    int mask, retval;
-    rt_base_t level;
-
-    addr += nr >> 5;
-    mask = 1 << (nr & 0x1f);
-    level = rt_spin_lock_irqsave(&_spinlock);
-    retval = (mask & *addr) != 0;
-    *addr &= ~mask;
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-    return retval;
-}
-
-rt_inline int test_bit(int nr, int *addr)
-{
-    int mask;
-
-    addr += nr >> 5;
-    mask = 1 << (nr & 0x1f);
-    return ((mask & *addr) != 0);
-}
-
-rt_inline int test_and_clear_bit(int nr, volatile void *addr)
-{
-    int mask, retval;
-    rt_base_t level;
-    volatile unsigned int *a = addr;
-
-    a += nr >> 5;
-    mask = 1 << (nr & 0x1f);
-    level = rt_spin_lock_irqsave(&_spinlock);
-    retval = (mask & *a) != 0;
-    *a &= ~mask;
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-
-    return retval;
-}
-
-#ifdef __GNUC__
-rt_inline unsigned long __ffs(unsigned long word)
-{
-    return __builtin_ffsl(word);
-}
-#else
-rt_inline unsigned long __ffs(unsigned long word)
-{
-    int num = 0;
-
-#if BITS_PER_LONG == 64
-    if ((word & 0xffffffff) == 0)
-    {
-        num += 32;
-        word >>= 32;
-    }
-#endif
-    if ((word & 0xffff) == 0)
-    {
-        num += 16;
-        word >>= 16;
-    }
-    if ((word & 0xff) == 0)
-    {
-        num += 8;
-        word >>= 8;
-    }
-    if ((word & 0xf) == 0)
-    {
-        num += 4;
-        word >>= 4;
-    }
-    if ((word & 0x3) == 0)
-    {
-        num += 2;
-        word >>= 2;
-    }
-    if ((word & 0x1) == 0)
-    {
-        num += 1;
-    }
-
-    return num;
-}
-#endif
-
-#define BITS_PER_LONG       32
-#define BITOP_WORD(nr)      ((nr) / BITS_PER_LONG)
-
-/*
- * Find the next set bit in a memory region.
- */
-rt_inline unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
-                unsigned long offset)
-{
-    const unsigned long *p = addr + BITOP_WORD(offset);
-    unsigned long result = offset & ~(BITS_PER_LONG-1);
-    unsigned long tmp;
-
-    if (offset >= size)
-    {
-        return size;
-    }
-
-    size -= result;
-    offset %= BITS_PER_LONG;
-    if (offset)
-    {
-        tmp = *(p++);
-        tmp &= (~0UL << offset);
-        if (size < BITS_PER_LONG)
-        {
-            goto found_first;
-        }
-
-        if (tmp)
-        {
-            goto found_middle;
-        }
-
-        size -= BITS_PER_LONG;
-        result += BITS_PER_LONG;
-    }
-
-    while (size & ~(BITS_PER_LONG-1))
-    {
-        if ((tmp = *(p++)))
-        {
-            goto found_middle;
-        }
-
-        result += BITS_PER_LONG;
-        size -= BITS_PER_LONG;
-    }
-
-    if (!size)
-    {
-        return result;
-    }
-
-    tmp = *p;
-
-found_first:
-    tmp &= (~0UL >> (BITS_PER_LONG - size));
-    if (tmp == 0UL)     /* Are any bits set? */
-    {
-        return result + size;   /* Nope. */
-    }
-
-found_middle:
-    return result + __ffs(tmp);
-}
-
-rt_inline void tty_sigaddset(lwp_sigset_t *set, int _sig)
-{
-    unsigned long sig = _sig - 1;
-
-    if (_LWP_NSIG_WORDS == 1)
-    {
-        set->sig[0] |= 1UL << sig;
-    }
-    else
-    {
-        set->sig[sig / _LWP_NSIG_BPW] |= 1UL << (sig % _LWP_NSIG_BPW);
-    }
-}
-
-rt_inline size_t read_cnt(struct n_tty_data *ldata)
-{
-    return ldata->read_head - ldata->read_tail;
-}
-
-rt_inline char read_buf(struct n_tty_data *ldata, size_t i)
-{
-    return ldata->read_buf[i & (RT_TTY_BUF - 1)];
-}
-
-rt_inline char *read_buf_addr(struct n_tty_data *ldata, size_t i)
-{
-    return &ldata->read_buf[i & (RT_TTY_BUF - 1)];
-}
-
-rt_inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
-{
-    return ldata->echo_buf[i & (RT_TTY_BUF - 1)];
-}
-
-rt_inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i)
-{
-    return &ldata->echo_buf[i & (RT_TTY_BUF - 1)];
-}
-
-/**
- *  put_tty_queue       -   add character to tty
- *  @c: character
- *  @ldata: n_tty data
- *
- *  Add a character to the tty read_buf queue.
- *
- *  n_tty_receive_buf()/producer path:
- *      caller holds non-exclusive termios_rwsem
- */
-rt_inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
-{
-    *read_buf_addr(ldata, ldata->read_head) = c;
-    ldata->read_head++;
-}
-
-/**
- *  reset_buffer_flags  -   reset buffer state
- *  @tty: terminal to reset
- *
- *  Reset the read buffer counters and clear the flags.
- *  Called from n_tty_open() and n_tty_flush_buffer().
- *
- *  Locking: caller holds exclusive termios_rwsem
- *       (or locking is not required)
- */
-
-static void reset_buffer_flags(struct n_tty_data *ldata)
-{
-    ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
-    ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
-    ldata->commit_head = 0;
-    ldata->echo_mark = 0;
-    ldata->line_start = 0;
-
-    ldata->erasing = 0;
-    rt_memset(ldata->read_flags, 0, RT_TTY_BUF);
-    ldata->push = 0;
-}
-
-/**
- *  add_echo_byte   -   add a byte to the echo buffer
- *  @c: unicode byte to echo
- *  @ldata: n_tty data
- *
- *  Add a character or operation byte to the echo buffer.
- */
-
-rt_inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
-{
-    *echo_buf_addr(ldata, ldata->echo_head++) = c;
-}
-
-/**
- *  echo_move_back_col  -   add operation to move back a column
- *  @ldata: n_tty data
- *
- *  Add an operation to the echo buffer to move back one column.
- */
-
-static void echo_move_back_col(struct n_tty_data *ldata)
-{
-    add_echo_byte(ECHO_OP_START, ldata);
-    add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
-}
-
-/**
- *  echo_set_canon_col  -   add operation to set the canon column
- *  @ldata: n_tty data
- *
- *  Add an operation to the echo buffer to set the canon column
- *  to the current column.
- */
-
-static void echo_set_canon_col(struct n_tty_data *ldata)
-{
-    add_echo_byte(ECHO_OP_START, ldata);
-    add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
-}
-
-/**
- *  echo_erase_tab  -   add operation to erase a tab
- *  @num_chars: number of character columns already used
- *  @after_tab: true if num_chars starts after a previous tab
- *  @ldata: n_tty data
- *
- *  Add an operation to the echo buffer to erase a tab.
- *
- *  Called by the eraser function, which knows how many character
- *  columns have been used since either a previous tab or the start
- *  of input.  This information will be used later, along with
- *  canon column (if applicable), to go back the correct number
- *  of columns.
- */
-
-static void echo_erase_tab(unsigned int num_chars, int after_tab,
-               struct n_tty_data *ldata)
-{
-    add_echo_byte(ECHO_OP_START, ldata);
-    add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
-
-    /* We only need to know this modulo 8 (tab spacing) */
-    num_chars &= 7;
-
-    /* Set the high bit as a flag if num_chars is after a previous tab */
-    if (after_tab)
-    {
-        num_chars |= 0x80;
-    }
-
-    add_echo_byte(num_chars, ldata);
-}
-
-/**
- *  echo_char_raw   -   echo a character raw
- *  @c: unicode byte to echo
- *  @tty: terminal device
- *
- *  Echo user input back onto the screen. This must be called only when
- *  L_ECHO(tty) is true. Called from the driver receive_buf path.
- *
- *  This variant does not treat control characters specially.
- */
-
-static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
-{
-    if (c == ECHO_OP_START)
-    {
-        add_echo_byte(ECHO_OP_START, ldata);
-        add_echo_byte(ECHO_OP_START, ldata);
-    }
-    else
-    {
-        add_echo_byte(c, ldata);
-    }
-}
-
-/**
- *  echo_char   -   echo a character
- *  @c: unicode byte to echo
- *  @tty: terminal device
- *
- *  Echo user input back onto the screen. This must be called only when
- *  L_ECHO(tty) is true. Called from the driver receive_buf path.
- *
- *  This variant tags control characters to be echoed as "^X"
- *  (where X is the letter representing the control char).
- */
-
-static void echo_char(unsigned char c, struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (c == ECHO_OP_START)
-    {
-        add_echo_byte(ECHO_OP_START, ldata);
-        add_echo_byte(ECHO_OP_START, ldata);
-    }
-    else
-    {
-        if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
-        {
-            add_echo_byte(ECHO_OP_START, ldata);
-        }
-        add_echo_byte(c, ldata);
-    }
-}
-
-/**
- *  finish_erasing      -   complete erase
- *  @ldata: n_tty data
- */
-
-rt_inline void finish_erasing(struct n_tty_data *ldata)
-{
-    if (ldata->erasing)
-    {
-        echo_char_raw('/', ldata);
-        ldata->erasing = 0;
-    }
-}
-
-/**
- *  is_utf8_continuation    -   utf8 multibyte check
- *  @c: byte to check
- *
- *  Returns true if the utf8 character 'c' is a multibyte continuation
- *  character. We use this to correctly compute the on screen size
- *  of the character when printing
- */
-
-rt_inline int is_utf8_continuation(unsigned char c)
-{
-    return (c & 0xc0) == 0x80;
-}
-
-/**
- *  is_continuation     -   multibyte check
- *  @c: byte to check
- *
- *  Returns true if the utf8 character 'c' is a multibyte continuation
- *  character and the terminal is in unicode mode.
- */
-
-rt_inline int is_continuation(unsigned char c, struct tty_struct *tty)
-{
-    return I_IUTF8(tty) && is_utf8_continuation(c);
-}
-
-/**
- *  eraser      -   handle erase function
- *  @c: character input
- *  @tty: terminal device
- *
- *  Perform erase and necessary output when an erase character is
- *  present in the stream from the driver layer. Handles the complexities
- *  of UTF-8 multibyte symbols.
- *
- *  n_tty_receive_buf()/producer path:
- *      caller holds non-exclusive termios_rwsem
- */
-
-static void eraser(unsigned char c, struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    enum { KERASE, WERASE, KILL } kill_type;
-    size_t head = 0;
-    size_t cnt = 0;
-    int seen_alnums = 0;
-
-    if (ldata->read_head == ldata->canon_head)
-    {
-        /* process_output('\a', tty); */ /* what do you think? */
-        return;
-    }
-
-    if (c == ERASE_CHAR(tty))
-    {
-        kill_type = KERASE;
-    }
-    else if (c == WERASE_CHAR(tty))
-    {
-        kill_type = WERASE;
-    }
-    else
-    {
-        if (!L_ECHO(tty))
-        {
-            ldata->read_head = ldata->canon_head;
-            return;
-        }
-
-        if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty))
-        {
-            ldata->read_head = ldata->canon_head;
-            finish_erasing(ldata);
-            echo_char(KILL_CHAR(tty), tty);
-            /* Add a newline if ECHOK is on and ECHOKE is off. */
-            if (L_ECHOK(tty))
-            {
-                echo_char_raw('\n', ldata);
-            }
-            return;
-        }
-        kill_type = KILL;
-    }
-
-    seen_alnums = 0;
-    while (ldata->read_head != ldata->canon_head)
-    {
-        head = ldata->read_head;
-
-        /* erase a single possibly multibyte character */
-        do
-        {
-            head--;
-            c = read_buf(ldata, head);
-        } while (is_continuation(c, tty) && head != ldata->canon_head);
-
-        /* do not partially erase */
-        if (is_continuation(c, tty))
-        {
-            break;
-        }
-
-        if (kill_type == WERASE)
-        {
-            /* Equivalent to BSD's ALTWERASE. */
-            if (isalnum(c) || c == '_')
-            {
-                seen_alnums++;
-            }
-            else if (seen_alnums)
-            {
-                break;
-            }
-        }
-        cnt = ldata->read_head - head;
-        ldata->read_head = head;
-        if (L_ECHO(tty))
-        {
-            if (L_ECHOPRT(tty))
-            {
-                if (!ldata->erasing)
-                {
-                    echo_char_raw('\\', ldata);
-                    ldata->erasing = 1;
-                }
-                /* if cnt > 1, output a multi-byte character */
-                echo_char(c, tty);
-                while (--cnt > 0)
-                {
-                    head++;
-                    echo_char_raw(read_buf(ldata, head), ldata);
-                    echo_move_back_col(ldata);
-                }
-            }
-            else if (kill_type == KERASE && !L_ECHOE(tty))
-            {
-                echo_char(ERASE_CHAR(tty), tty);
-            }
-            else if (c == '\t')
-            {
-                unsigned int num_chars = 0;
-                int after_tab = 0;
-                size_t tail = ldata->read_head;
-
-                /*
-                 * Count the columns used for characters
-                 * since the start of input or after a
-                 * previous tab.
-                 * This info is used to go back the correct
-                 * number of columns.
-                 */
-                while (tail != ldata->canon_head)
-                {
-                    tail--;
-                    c = read_buf(ldata, tail);
-                    if (c == '\t')
-                    {
-                        after_tab = 1;
-                        break;
-                    }
-                    else if (iscntrl(c))
-                    {
-                        if (L_ECHOCTL(tty))
-                        {
-                            num_chars += 2;
-                        }
-                    }
-                    else if (!is_continuation(c, tty))
-                    {
-                        num_chars++;
-                    }
-                }
-                echo_erase_tab(num_chars, after_tab, ldata);
-            }
-            else
-            {
-                if (iscntrl(c) && L_ECHOCTL(tty))
-                {
-                    echo_char_raw('\b', ldata);
-                    echo_char_raw(' ', ldata);
-                    echo_char_raw('\b', ldata);
-                }
-                if (!iscntrl(c) || L_ECHOCTL(tty))
-                {
-                    echo_char_raw('\b', ldata);
-                    echo_char_raw(' ', ldata);
-                    echo_char_raw('\b', ldata);
-                }
-            }
-        }
-        if (kill_type == KERASE)
-        {
-            break;
-        }
-    }
-    if (ldata->read_head == ldata->canon_head && L_ECHO(tty))
-    {
-        finish_erasing(ldata);
-    }
-}
-
-/**
- *  isig        -   handle the ISIG optio
- *  @sig: signal
- *  @tty: terminal
- *
- *  Called when a signal is being sent due to terminal input.
- *  Called from the driver receive_buf path so serialized.
- *
- *  Performs input and output flush if !NOFLSH. In this context, the echo
- *  buffer is 'output'. The signal is processed first to alert any current
- *  readers or writers to discontinue and exit their i/o loops.
- *
- *  Locking: ctrl_lock
- */
-
-static void __isig(int sig, struct tty_struct *tty)
-{
-    struct rt_lwp *lwp = tty->foreground;
-    struct tty_ldisc *ld = RT_NULL;
-    struct termios old_termios;
-    struct termios *new_termios = get_old_termios();
-
-    if (lwp)
-    {
-        if (sig == SIGTSTP)
-        {
-            struct rt_lwp *old_lwp;
-
-            rt_memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios));
-            tty->init_termios = *new_termios;
-            ld = tty->ldisc;
-            if (ld != RT_NULL)
-            {
-                if (ld->ops->set_termios)
-                {
-                    ld->ops->set_termios(tty, &old_termios);
-                }
-            }
-            lwp_signal_kill(lwp, SIGTTOU, SI_USER, 0);
-            old_lwp = tty_pop(&tty->head, RT_NULL);
-            tty->foreground = old_lwp;
-        }
-        else
-        {
-            lwp_signal_kill(lwp, sig, SI_USER, 0);
-        }
-    }
-}
-
-static void isig(int sig, struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (L_NOFLSH(tty))
-    {
-        /* signal only */
-        __isig(sig, tty);
-
-    }
-    else
-    { /* signal and flush */
-        __isig(sig, tty);
-
-        /* clear echo buffer */
-        ldata->echo_head = ldata->echo_tail = 0;
-        ldata->echo_mark = ldata->echo_commit = 0;
-
-        /* clear input buffer */
-        reset_buffer_flags(tty->disc_data);
-    }
-}
-
-/**
- *  do_output_char          -   output one character
- *  @c: character (or partial unicode symbol)
- *  @tty: terminal device
- *  @space: space available in tty driver write buffer
- *
- *  This is a helper function that handles one output character
- *  (including special characters like TAB, CR, LF, etc.),
- *  doing OPOST processing and putting the results in the
- *  tty driver's write buffer.
- *
- *  Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY
- *  and NLDLY.  They simply aren't relevant in the world today.
- *  If you ever need them, add them here.
- *
- *  Returns the number of bytes of buffer space used or -1 if
- *  no space left.
- *
- *  Locking: should be called under the output_lock to protect
- *       the column state and space left in the buffer
- */
-
-static int do_output_char(unsigned char c, struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    int spaces = 0;
-    char *ch = RT_NULL;
-
-    switch (c)
-    {
-    case '\n':
-        if (O_ONLRET(tty))
-        {
-            ldata->column = 0;
-        }
-
-        if (O_ONLCR(tty))
-        {
-            ldata->canon_column = ldata->column = 0;
-            ch = "\r\n";
-            rt_device_write((rt_device_t)tty, -1, ch, 2);
-            return 2;
-        }
-        ldata->canon_column = ldata->column;
-        break;
-    case '\r':
-        if (O_ONOCR(tty) && ldata->column == 0)
-        {
-            return 0;
-        }
-
-        if (O_OCRNL(tty))
-        {
-            c = '\n';
-            if (O_ONLRET(tty))
-            {
-                ldata->canon_column = ldata->column = 0;
-            }
-            break;
-        }
-        ldata->canon_column = ldata->column = 0;
-        break;
-    case '\t':
-        spaces = 8 - (ldata->column & 7);
-        if (O_TABDLY(tty) == XTABS)
-        {
-            ldata->column += spaces;
-            ch = "        ";
-            rt_device_write((rt_device_t)tty, -1, &ch, spaces);
-            return spaces;
-        }
-        ldata->column += spaces;
-        break;
-    case '\b':
-        if (ldata->column > 0)
-        {
-            ldata->column--;
-        }
-        ch = "\b \b";
-        rt_device_write((rt_device_t)tty, -1, ch, strlen(ch));
-        return 1;
-    default:
-        if (!iscntrl(c))
-        {
-            if (O_OLCUC(tty))
-            {
-                c = toupper(c);
-            }
-
-            if (!is_continuation(c, tty))
-            {
-                ldata->column++;
-            }
-        }
-        break;
-    }
-
-    rt_device_write((rt_device_t)tty, -1, &c, 1);
-    return 1;
-}
-
-/**
- *  process_output          -   output post processor
- *  @c: character (or partial unicode symbol)
- *  @tty: terminal device
- *
- *  Output one character with OPOST processing.
- *  Returns -1 when the output device is full and the character
- *  must be retried.
- *
- *  Locking: output_lock to protect column state and space left
- *       (also, this is called from n_tty_write under the
- *        tty layer write lock)
- */
-
-static int process_output(unsigned char c, struct tty_struct *tty)
-{
-    int retval = 0;
-
-    retval = do_output_char(c, tty);
-    if (retval < 0)
-    {
-        return -1;
-    }
-    else
-    {
-        return 0;
-    }
-}
-
-/**
- *  process_output_block        -   block post processor
- *  @tty: terminal device
- *  @buf: character buffer
- *  @nr: number of bytes to output
- *
- *  Output a block of characters with OPOST processing.
- *  Returns the number of characters output.
- *
- *  This path is used to speed up block console writes, among other
- *  things when processing blocks of output data. It handles only
- *  the simple cases normally found and helps to generate blocks of
- *  symbols for the console driver and thus improve performance.
- *
- *  Locking: output_lock to protect column state and space left
- *       (also, this is called from n_tty_write under the
- *        tty layer write lock)
- */
-
-static ssize_t process_output_block(struct tty_struct *tty,
-                    const char *buf, unsigned int nr)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    int i = 0;
-    ssize_t size = 0;
-    const char *cp = RT_NULL;
-
-    for (i = 0, cp = buf; i < nr; i++, cp++)
-    {
-        char c = *cp;
-
-        switch (c)
-        {
-        case '\n':
-            if (O_ONLRET(tty))
-            {
-                ldata->column = 0;
-            }
-
-            if (O_ONLCR(tty))
-            {
-                goto break_out;
-            }
-            ldata->canon_column = ldata->column;
-            break;
-        case '\r':
-            if (O_ONOCR(tty) && ldata->column == 0)
-            {
-                goto break_out;
-            }
-
-            if (O_OCRNL(tty))
-            {
-                goto break_out;
-            }
-
-            ldata->canon_column = ldata->column = 0;
-            break;
-        case '\t':
-            goto break_out;
-        case '\b':
-            if (ldata->column > 0)
-            {
-                ldata->column--;
-            }
-            break;
-        default:
-            if (!iscntrl(c))
-            {
-                if (O_OLCUC(tty))
-                {
-                    goto break_out;
-                }
-
-                if (!is_continuation(c, tty))
-                {
-                    ldata->column++;
-                }
-            }
-            break;
-        }
-    }
-break_out:
-    size = rt_device_write((rt_device_t)tty, -1, buf, i);
-    return size;
-}
-
-static size_t __process_echoes(struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    size_t tail = 0;
-    unsigned char c = 0;
-    char ch = 0;
-    unsigned char num_chars = 0, num_bs = 0;
-
-    tail = ldata->echo_tail;
-    while (ldata->echo_commit != tail)
-    {
-        c = echo_buf(ldata, tail);
-        if (c == ECHO_OP_START)
-        {
-            unsigned char op = 0;
-
-            /*
-             * If the buffer byte is the start of a multi-byte
-             * operation, get the next byte, which is either the
-             * op code or a control character value.
-             */
-            op = echo_buf(ldata, tail + 1);
-
-            switch (op)
-            {
-            case ECHO_OP_ERASE_TAB:
-                num_chars = echo_buf(ldata, tail + 2);
-
-                /*
-                 * Determine how many columns to go back
-                 * in order to erase the tab.
-                 * This depends on the number of columns
-                 * used by other characters within the tab
-                 * area.  If this (modulo 8) count is from
-                 * the start of input rather than from a
-                 * previous tab, we offset by canon column.
-                 * Otherwise, tab spacing is normal.
-                 */
-                if (!(num_chars & 0x80))
-                {
-                    num_chars += ldata->canon_column;
-                }
-                num_bs = 8 - (num_chars & 7);
-
-                while (num_bs--)
-                {
-                    ch = '\b';
-                    rt_device_write((rt_device_t)tty, -1, &ch, 1);
-                    if (ldata->column > 0)
-                    {
-                        ldata->column--;
-                    }
-                }
-                tail += 3;
-                break;
-
-            case ECHO_OP_SET_CANON_COL:
-                ldata->canon_column = ldata->column;
-                tail += 2;
-                break;
-
-            case ECHO_OP_MOVE_BACK_COL:
-                if (ldata->column > 0)
-                {
-                    ldata->column--;
-                }
-                tail += 2;
-                break;
-
-            case ECHO_OP_START:
-                ch = ECHO_OP_START;
-                rt_device_write((rt_device_t)tty, -1, &ch, 1);
-                ldata->column++;
-                tail += 2;
-                break;
-
-            default:
-                /*
-                 * If the op is not a special byte code,
-                 * it is a ctrl char tagged to be echoed
-                 * as "^X" (where X is the letter
-                 * representing the control char).
-                 * Note that we must ensure there is
-                 * enough space for the whole ctrl pair.
-                 *
-                 */
-                ch = '^';
-                rt_device_write((rt_device_t)tty, -1, &ch, 1);
-                ch = op ^ 0100;
-                rt_device_write((rt_device_t)tty, -1, &ch, 1);
-                ldata->column += 2;
-                tail += 2;
-            }
-        }
-        else
-        {
-            if (O_OPOST(tty))
-            {
-                int retval = do_output_char(c, tty);
-                if (retval < 0)
-                {
-                    break;
-                }
-            }
-            else
-            {
-                rt_device_write((rt_device_t)tty, -1, &c, 1);
-            }
-            tail += 1;
-        }
-    }
-
-    /* If the echo buffer is nearly full (so that the possibility exists
-     * of echo overrun before the next commit), then discard enough
-     * data at the tail to prevent a subsequent overrun */
-    while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK)
-    {
-        if (echo_buf(ldata, tail) == ECHO_OP_START)
-        {
-            if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
-            {
-                tail += 3;
-            }
-            else
-            {
-                tail += 2;
-            }
-        }
-        else
-        {
-            tail++;
-        }
-    }
-
-    ldata->echo_tail = tail;
-    return 0;
-}
-
-static void commit_echoes(struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    size_t nr = 0, old = 0;
-    size_t head = 0;
-
-    head = ldata->echo_head;
-    ldata->echo_mark = head;
-    old = ldata->echo_commit - ldata->echo_tail;
-
-    /* Process committed echoes if the accumulated # of bytes
-     * is over the threshold (and try again each time another
-     * block is accumulated) */
-    nr = head - ldata->echo_tail;
-    if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
-    {
-        return;
-    }
-
-    ldata->echo_commit = head;
-    __process_echoes(tty);
-}
-
-static void process_echoes(struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (ldata->echo_mark == ldata->echo_tail)
-    {
-        return;
-    }
-
-    ldata->echo_commit = ldata->echo_mark;
-    __process_echoes(tty);
-}
-
-/* NB: echo_mark and echo_head should be equivalent here */
-static void flush_echoes(struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if ((!L_ECHO(tty) && !L_ECHONL(tty)) ||
-        ldata->echo_commit == ldata->echo_head)
-    {
-        return;
-    }
-
-    ldata->echo_commit = ldata->echo_head;
-    __process_echoes(tty);
-}
-/**
- *  n_tty_set_termios   -   termios data changed
- *  @tty: terminal
- *  @old: previous data
- *
- *  Called by the tty layer when the user changes termios flags so
- *  that the line discipline can plan ahead. This function cannot sleep
- *  and is protected from re-entry by the tty layer. The user is
- *  guaranteed that this function will not be re-entered or in progress
- *  when the ldisc is closed.
- *
- *  Locking: Caller holds tty->termios_rwsem
- */
-
-static void n_tty_set_termios(struct tty_struct *tty, struct termios *old)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (!old || (old->c_lflag ^ tty->init_termios.c_lflag) & (ICANON | EXTPROC))
-    {
-        rt_memset(ldata->read_flags, 0, RT_TTY_BUF);
-        ldata->line_start = ldata->read_tail;
-        if (!L_ICANON(tty) || !read_cnt(ldata))
-        {
-            ldata->canon_head = ldata->read_tail;
-            ldata->push = 0;
-        }
-        else
-        {
-            set_bit((ldata->read_head - 1) & (RT_TTY_BUF - 1),(int *)ldata->read_flags);
-            ldata->canon_head = ldata->read_head;
-            ldata->push = 1;
-        }
-        ldata->commit_head = ldata->read_head;
-        ldata->erasing = 0;
-        ldata->lnext = 0;
-    }
-
-    ldata->icanon = (L_ICANON(tty) != 0);
-
-    if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
-        I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
-        I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
-        I_PARMRK(tty))
-    {
-        rt_memset(ldata->char_map, 0, 256);
-        if (I_IGNCR(tty) || I_ICRNL(tty))
-        {
-            set_bit('\r', (int *)ldata->char_map);
-        }
-
-        if (I_INLCR(tty))
-        {
-            set_bit('\n', (int *)ldata->char_map);
-        }
-
-        if (L_ICANON(tty))
-        {
-            set_bit(ERASE_CHAR(tty), (int *)ldata->char_map);
-            set_bit(KILL_CHAR(tty),(int *) ldata->char_map);
-            set_bit(EOF_CHAR(tty), (int *)ldata->char_map);
-            set_bit('\n',(int *) ldata->char_map);
-            set_bit(EOL_CHAR(tty),(int *) ldata->char_map);
-            if (L_IEXTEN(tty))
-            {
-                set_bit(WERASE_CHAR(tty), (int *)ldata->char_map);
-                set_bit(LNEXT_CHAR(tty), (int *)ldata->char_map);
-                set_bit(EOL2_CHAR(tty), (int *)ldata->char_map);
-                if (L_ECHO(tty))
-                {
-                    set_bit(REPRINT_CHAR(tty),
-                        (int *)ldata->char_map);
-                }
-            }
-        }
-        if (I_IXON(tty))
-        {
-            set_bit(START_CHAR(tty), (int *)ldata->char_map);
-            set_bit(STOP_CHAR(tty), (int *)ldata->char_map);
-        }
-        if (L_ISIG(tty))
-        {
-            set_bit(INTR_CHAR(tty), (int *)ldata->char_map);
-            set_bit(QUIT_CHAR(tty), (int *)ldata->char_map);
-            set_bit(SUSP_CHAR(tty), (int *)ldata->char_map);
-        }
-        clear_bit(__DISABLED_CHAR, (int *)ldata->char_map);
-        ldata->raw = 0;
-        ldata->real_raw = 0;
-    }
-    else
-    {
-        ldata->raw = 1;
-        if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
-            (I_IGNPAR(tty) || !I_INPCK(tty))/* &&
-            (tty->driver->flags & TTY_DRIVER_REAL_RAW)*/)
-        {
-            ldata->real_raw = 1;
-        }
-        else
-        {
-            ldata->real_raw = 0;
-        }
-    }
-}
-
-void console_ldata_init(struct tty_struct *tty)
-{
-    struct n_tty_data *ldata = RT_NULL;
-
-    ldata = rt_malloc(sizeof(struct n_tty_data));
-    if (ldata == RT_NULL)
-    {
-        LOG_E("console_ldata_init ldata malloc fail");
-        return;
-    }
-    tty->disc_data = ldata;
-    reset_buffer_flags(ldata);
-    ldata->column = 0;
-    ldata->canon_column = 0;
-    ldata->no_room = 0;
-    ldata->lnext = 0;
-    n_tty_set_termios(tty, RT_NULL);
-    return;
-}
-
-static int n_tty_open(struct dfs_file *fd)
-{
-    int ret = 0;
-    struct n_tty_data *ldata = RT_NULL;
-    struct tty_struct *tty = (struct tty_struct*)fd->vnode->data;
-
-    ldata = rt_malloc(sizeof(struct n_tty_data));
-    if (ldata == RT_NULL)
-    {
-        LOG_E("n_tty_open ldata malloc fail");
-        return -1;
-    }
-
-    ldata->atomic_read_lock = rt_mutex_create("atomic_read_lock",RT_IPC_FLAG_FIFO);
-    if(ldata->atomic_read_lock == RT_NULL)
-    {
-        LOG_E("n_tty_open atomic_read_lock create fail");
-        return -1;
-    }
-
-    ldata->output_lock = rt_mutex_create("output_lock",RT_IPC_FLAG_FIFO);
-    if(ldata->output_lock == RT_NULL)
-    {
-        LOG_E("n_tty_open output_lock create fail");
-        return -1;
-    }
-
-    tty->disc_data = ldata;
-    reset_buffer_flags(ldata);
-    ldata->column = 0;
-    ldata->canon_column = 0;
-    ldata->lnext = 0;
-    n_tty_set_termios(tty, RT_NULL);
-    return ret;
-}
-
-rt_inline int input_available_p(struct tty_struct *tty, int poll)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1;
-
-    if (ldata->icanon && !L_EXTPROC(tty))
-    {
-        return ldata->canon_head != ldata->read_tail;
-    }
-    else
-    {
-        return ldata->commit_head - ldata->read_tail >= amt;
-    }
-}
-
-/**
- *  copy_from_read_buf  -   copy read data directly
- *  @tty: terminal device
- *  @b: user data
- *  @nr: size of data
- *
- *  Helper function to speed up n_tty_read.  It is only called when
- *  ICANON is off; it copies characters straight from the tty queue to
- *  user space directly.  It can be profitably called twice; once to
- *  drain the space from the tail pointer to the (physical) end of the
- *  buffer, and once to drain the space from the (physical) beginning of
- *  the buffer to head pointer.
- *
- *  Called under the ldata->atomic_read_lock sem
- *
- *  n_tty_read()/consumer path:
- *      caller holds non-exclusive termios_rwsem
- *      read_tail published
- */
-
-static int copy_from_read_buf(struct tty_struct *tty,char *b,size_t nr)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    size_t n = 0;
-    rt_bool_t is_eof = 0;
-    size_t head = ldata->commit_head;
-    size_t tail = ldata->read_tail & (RT_TTY_BUF - 1);
-
-    n = min(head - ldata->read_tail, RT_TTY_BUF - tail);
-    n = min(nr, n);
-    if (n)
-    {
-        const char *from = read_buf_addr(ldata, tail);
-        memcpy(b, from, n);
-        is_eof = n == 1 && *from == EOF_CHAR(tty);
-        ldata->read_tail += n;
-        /* Turn single EOF into zero-length read */
-        if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
-            (head == ldata->read_tail))
-        {
-            n = 0;
-        }
-
-    }
-    return n;
-}
-
-/**
- *  canon_copy_from_read_buf    -   copy read data in canonical mode
- *  @tty: terminal device
- *  @b: user data
- *  @nr: size of data
- *
- *  Helper function for n_tty_read.  It is only called when ICANON is on;
- *  it copies one line of input up to and including the line-delimiting
- *  character into the user-space buffer.
- *
- *  NB: When termios is changed from non-canonical to canonical mode and
- *  the read buffer contains data, n_tty_set_termios() simulates an EOF
- *  push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
- *  This causes data already processed as input to be immediately available
- *  as input although a newline has not been received.
- *
- *  Called under the atomic_read_lock mutex
- *
- *  n_tty_read()/consumer path:
- *      caller holds non-exclusive termios_rwsem
- *      read_tail published
- */
-
-static int canon_copy_from_read_buf(struct tty_struct *tty, char *b, size_t nr)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    size_t n = 0, size = 0, more = 0, c = 0;
-    size_t eol = 0;
-    size_t tail = 0;
-    int found = 0;
-
-    /* N.B. avoid overrun if nr == 0 */
-    if (nr == 0)
-    {
-        return 0;
-    }
-
-    n = min(nr + 1, ldata->canon_head - ldata->read_tail);
-
-    tail = ldata->read_tail & (RT_TTY_BUF - 1);
-    size = min(tail + n, RT_TTY_BUF);
-
-    eol = find_next_bit(ldata->read_flags, size, tail);
-    more = n - (size - tail);
-    if (eol == RT_TTY_BUF && more)
-    {
-        /* scan wrapped without finding set bit */
-        eol = find_next_bit(ldata->read_flags, more, 0);
-        found = eol != more;
-    }
-    else
-    {
-        found = eol != size;
-    }
-
-    n = eol - tail;
-    if (n > RT_TTY_BUF)
-    {
-        n += RT_TTY_BUF;
-    }
-    c = n + found;
-
-    if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
-    {
-        c = min(nr, c);
-        n = c;
-    }
-
-    size_t buf_size = RT_TTY_BUF - tail;
-    const void *from = read_buf_addr(ldata, tail);
-    size_t temp_n = n;
-    if (n > buf_size)
-    {
-        memcpy(b, from, buf_size);
-        b += buf_size;
-        temp_n -= buf_size;
-        from = ldata->read_buf;
-    }
-    memcpy(b, from, temp_n);
-
-    if (found)
-    {
-        clear_bit(eol, (int *)ldata->read_flags);
-    }
-    ldata->read_tail = ldata->read_tail + c;
-
-    if (found)
-    {
-        if (!ldata->push)
-        {
-            ldata->line_start = ldata->read_tail;
-        }
-        else
-        {
-            ldata->push = 0;
-        }
-    }
-    return n;
-}
-
-
-static int n_tty_close(struct tty_struct *tty)
-{
-    int ret = 0;
-    struct n_tty_data *ldata = RT_NULL;
-    struct tty_struct *o_tty = RT_NULL;
-
-    RT_ASSERT(tty != RT_NULL);
-    if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
-    {
-        o_tty = tty->other_struct;
-    }
-    else
-    {
-        o_tty = tty;
-    }
-
-    ldata = o_tty->disc_data;
-    rt_free(ldata);
-    o_tty->disc_data = RT_NULL;
-    return ret;
-}
-
-static int n_tty_ioctl(struct dfs_file *fd, int cmd, void *args)
-{
-    int ret = 0;
-    struct tty_struct *real_tty = RT_NULL;
-    struct tty_struct *tty = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-    if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
-    {
-        real_tty = tty->other_struct;
-    }
-    else
-    {
-        real_tty = tty;
-    }
-
-    switch(cmd)
-    {
-
-    default:
-        ret = n_tty_ioctl_extend(real_tty, cmd, args);
-        if (ret != -ENOIOCTLCMD)
-        {
-            return ret;
-        }
-    }
-
-    ret = rt_device_control((rt_device_t)real_tty, cmd, args);
-    if (ret != -ENOIOCTLCMD)
-    {
-        return ret;
-    }
-    return ret;
-}
-
-static void
-n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
-{
-    isig(signal, tty);
-    if (L_ECHO(tty))
-    {
-        echo_char(c, tty);
-        commit_echoes(tty);
-    }
-    else
-    {
-        process_echoes(tty);
-    }
-    return;
-}
-
-/**
- *  n_tty_receive_char  -   perform processing
- *  @tty: terminal device
- *  @c: character
- *
- *  Process an individual character of input received from the driver.
- *  This is serialized with respect to itself by the rules for the
- *  driver above.
- *
- *  n_tty_receive_buf()/producer path:
- *      caller holds non-exclusive termios_rwsem
- *      publishes canon_head if canonical mode is active
- *
- *  Returns 1 if LNEXT was received, else returns 0
- */
-
-static int n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (I_IXON(tty))
-    {
-        if (c == START_CHAR(tty)) /*ctrl + p realize missing*/
-        {
-            process_echoes(tty);
-            return 0;
-        }
-        if (c == STOP_CHAR(tty)) /*ctrl + s realize missing*/
-        {
-            return 0;
-        }
-    }
-
-    if (L_ISIG(tty))
-    {
-        if (c == INTR_CHAR(tty)) /*ctrl + c realize missing*/
-        {
-            n_tty_receive_signal_char(tty, SIGINT, c);
-            return 0;
-        }
-        else if (c == QUIT_CHAR(tty)) /*ctrl + d realize missing*/
-        {
-            n_tty_receive_signal_char(tty, SIGQUIT, c);
-            return 0;
-        }
-        else if (c == SUSP_CHAR(tty)) /*ctrl + z realize missing*/
-        {
-            n_tty_receive_signal_char(tty, SIGTSTP, c);
-            return 0;
-        }
-    }
-
-    if (c == '\r')
-    {
-        if (I_IGNCR(tty))
-        {
-            return 0;
-        }
-
-        if (I_ICRNL(tty))
-        {
-            c = '\n';
-        }
-    }
-    else if (c == '\n' && I_INLCR(tty))
-    {
-        c = '\r';
-    }
-
-    if (ldata->icanon)
-    {
-        if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
-            (c == WERASE_CHAR(tty) && L_IEXTEN(tty)))
-        {
-            eraser(c, tty);
-            commit_echoes(tty);
-            return 0;
-        }
-        if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty))
-        {
-            ldata->lnext = 1;
-            if (L_ECHO(tty))
-            {
-                finish_erasing(ldata);
-                if (L_ECHOCTL(tty))
-                {
-                    echo_char_raw('^', ldata);
-                    echo_char_raw('\b', ldata);
-                    commit_echoes(tty);
-                }
-            }
-            return 1;
-        }
-        if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty))
-        {
-            size_t tail = ldata->canon_head;
-
-            finish_erasing(ldata);
-            echo_char(c, tty);
-            echo_char_raw('\n', ldata);
-            while (tail != ldata->read_head)
-            {
-                echo_char(read_buf(ldata, tail), tty);
-                tail++;
-            }
-            commit_echoes(tty);
-            return 0;
-        }
-        if (c == '\n')
-        {
-            if (L_ECHO(tty) || L_ECHONL(tty))
-            {
-                echo_char_raw('\n', ldata);
-                commit_echoes(tty);
-            }
-            goto handle_newline;
-        }
-        if (c == EOF_CHAR(tty))
-        {
-            c = __DISABLED_CHAR;
-            goto handle_newline;
-        }
-        if ((c == EOL_CHAR(tty)) ||
-            (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))
-        {
-            /*
-             * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
-             */
-            if (L_ECHO(tty))
-            {
-                /* Record the column of first canon char. */
-                if (ldata->canon_head == ldata->read_head)
-                {
-                    echo_set_canon_col(ldata);
-                }
-
-                echo_char(c, tty);
-                commit_echoes(tty);
-            }
-            /*
-             * XXX does PARMRK doubling happen for
-             * EOL_CHAR and EOL2_CHAR?
-             */
-            if (c == (unsigned char) '\377' && I_PARMRK(tty))
-            {
-                put_tty_queue(c, ldata);
-            }
-
-handle_newline:
-            set_bit(ldata->read_head & (RT_TTY_BUF - 1), (int *)ldata->read_flags);
-            put_tty_queue(c, ldata);
-            ldata->canon_head = ldata->read_head;
-            tty_wakeup_check(tty);
-            return 0;
-        }
-    }
-
-    if (L_ECHO(tty))
-    {
-        finish_erasing(ldata);
-        if (c == '\n')
-        {
-            echo_char_raw('\n', ldata);
-        }
-        else
-        {
-            /* Record the column of first canon char. */
-            if (ldata->canon_head == ldata->read_head)
-            {
-                echo_set_canon_col(ldata);
-            }
-            echo_char(c, tty);
-        }
-        commit_echoes(tty);
-    }
-
-    /* PARMRK doubling check */
-    if (c == (unsigned char) '\377' && I_PARMRK(tty))
-    {
-        put_tty_queue(c, ldata);
-    }
-
-    put_tty_queue(c, ldata);
-    return 0;
-}
-
-rt_inline void n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (L_ECHO(tty))
-    {
-        finish_erasing(ldata);
-        /* Record the column of first canon char. */
-        if (ldata->canon_head == ldata->read_head)
-        {
-            echo_set_canon_col(ldata);
-        }
-
-        echo_char(c, tty);
-        commit_echoes(tty);
-    }
-    /* PARMRK doubling check */
-    if (c == (unsigned char) '\377' && I_PARMRK(tty))
-    {
-        put_tty_queue(c, ldata);
-    }
-
-    put_tty_queue(c, ldata);
-}
-
-static void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
-{
-    n_tty_receive_char_inline(tty, c);
-}
-
-static void n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    ldata->lnext = 0;
-    if (flag == TTY_NORMAL)
-    {
-        if (I_ISTRIP(tty))
-        {
-            c &= 0x7f;
-        }
-
-        if (I_IUCLC(tty) && L_IEXTEN(tty))
-        {
-            c = tolower(c);
-        }
-
-        n_tty_receive_char(tty, c);
-    }
-    else
-    {
-        //n_tty_receive_char_flagged(tty, c, flag);
-    }
-
-}
-
-static void n_tty_receive_buf_real_raw(struct tty_struct *tty, char *cp, int count)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    size_t n = 0, head = 0;
-
-    head = ldata->read_head & (RT_TTY_BUF - 1);
-    n = min(count, RT_TTY_BUF - head);
-    rt_memcpy(read_buf_addr(ldata, head), cp, n);
-    ldata->read_head += n;
-    cp += n;
-    count -= n;
-
-    head = ldata->read_head & (RT_TTY_BUF - 1);
-    n = min(count, RT_TTY_BUF - head);
-    rt_memcpy(read_buf_addr(ldata, head), cp, n);
-    ldata->read_head += n;
-}
-
-static void n_tty_receive_buf_raw(struct tty_struct *tty, char *cp, int count)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    char flag = TTY_NORMAL;
-
-    while (count--)
-    {
-        if (flag == TTY_NORMAL)
-        {
-            put_tty_queue(*cp++, ldata);
-        }
-    }
-}
-
-static void n_tty_receive_buf_standard(struct tty_struct *tty, char *cp, int count)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    char flag = TTY_NORMAL;
-
-    while (count--)
-    {
-        char c = *cp++;
-
-        if (I_ISTRIP(tty))
-        {
-            c &= 0x7f;
-        }
-
-        if (I_IUCLC(tty) && L_IEXTEN(tty))
-        {
-            c = tolower(c);
-        }
-
-        if (L_EXTPROC(tty))
-        {
-            put_tty_queue(c, ldata);
-            continue;
-        }
-
-        if (!test_bit(c, (int *)ldata->char_map))
-        {
-            n_tty_receive_char_inline(tty, c);
-        }
-        else if (n_tty_receive_char_special(tty, c) && count)
-        {
-            n_tty_receive_char_lnext(tty, *cp++, flag);
-            count--;
-        }
-    }
-}
-
-rt_inline void n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-
-    if (L_ECHO(tty))
-    {
-        finish_erasing(ldata);
-        /* Record the column of first canon char. */
-        if (ldata->canon_head == ldata->read_head)
-        {
-            echo_set_canon_col(ldata);
-        }
-
-        echo_char(c, tty);
-        commit_echoes(tty);
-    }
-    put_tty_queue(c, ldata);
-}
-
-static void n_tty_receive_buf_fast(struct tty_struct *tty, char *cp, int count)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    char flag = TTY_NORMAL;
-
-    while (count--)
-    {
-        unsigned char c = *cp++;
-
-        if (!test_bit(c, (int *)ldata->char_map))
-        {
-            n_tty_receive_char_fast(tty, c);
-        }
-        else if (n_tty_receive_char_special(tty, c) && count)
-        {
-            n_tty_receive_char_lnext(tty, *cp++, flag);
-            count--;
-        }
-    }
-}
-
-static void __receive_buf(struct tty_struct *tty, char *cp, int count)
-{
-    struct n_tty_data *ldata = tty->disc_data;
-    rt_bool_t preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
-
-    if (ldata->real_raw)
-    {
-        n_tty_receive_buf_real_raw(tty, cp, count);
-    }
-
-    else if (ldata->raw || (L_EXTPROC(tty) && !preops))
-    {
-        n_tty_receive_buf_raw(tty, cp, count);
-    }
-    else
-    {
-        if (!preops && !I_PARMRK(tty))
-        {
-            n_tty_receive_buf_fast(tty, cp, count);
-        }
-        else
-        {
-            n_tty_receive_buf_standard(tty, cp, count);
-        }
-        flush_echoes(tty);
-    }
-
-    if (ldata->icanon && !L_EXTPROC(tty))
-        return;
-
-    /* publish read_head to consumer */
-    ldata->commit_head = ldata->read_head;
-
-    if (read_cnt(ldata))
-    {
-        tty_wakeup_check(tty);
-    }
-}
-
-int n_tty_receive_buf(struct tty_struct *tty,char *cp, int count)
-{
-    int size = 0;
-    struct n_tty_data *ldata = tty->disc_data;
-    int room = 0, n = 0, rcvd = 0, overflow = 0;
-
-    size = count;
-    while(1)
-    {
-        size_t tail = ldata->read_tail;
-
-        room = RT_TTY_BUF - (ldata->read_head - tail);
-
-        if (I_PARMRK(tty))
-        {
-            room = (room +2)/3;
-        }
-        room--;
-        if (room <= 0)
-        {
-            overflow = ldata->icanon && ldata->canon_head == tail;
-            if (overflow && room < 0)
-            {
-                ldata->read_head--;
-            }
-
-            room = overflow;
-        }
-        else
-        {
-            overflow = 0;
-        }
-
-        n =  min(size, room);
-
-        if (!n)
-        {
-            break;
-        }
-
-        if (!overflow)
-        {
-            __receive_buf(tty, cp, n);
-        }
-
-        cp += n;
-        size -= n;
-        rcvd += n;
-    }
-    return count - size;
-}
-
-/**
- *  job_control     -   check job control
- *  @tty: tty
- *  @file: file handle
- *
- *  Perform job control management checks on this file/tty descriptor
- *  and if appropriate send any needed signals and return a negative
- *  error code if action should be taken.
- *
- *  Locking: redirected write test is safe
- *       current->signal->tty check is safe
- *       ctrl_lock to safely reference tty->pgrp
- */
-
-static int job_control(struct tty_struct *tty)
-{
-    return __tty_check_change(tty, SIGTTIN);
-}
-
-static struct rt_wqueue *_wait_queue_current_get(struct tty_struct *tty)
-{
-    struct rt_lwp *lwp;
-
-    lwp = lwp_self();
-    if (!lwp || !lwp->tty)
-        lwp = RT_NULL;
-
-    return wait_queue_get(lwp, tty);
-}
-
-static int n_tty_read(struct dfs_file *fd, void *buf, size_t count)
-{
-    rt_base_t level = 0;
-    char *b = (char *)buf;
-    struct tty_struct *tty = RT_NULL;
-    struct rt_wqueue *wq = RT_NULL;
-    int wait_ret = 0;
-    int retval = 0;
-    int c = 0;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-    c = job_control(tty);
-    if (c < 0)
-    {
-        return c;
-    }
-
-    struct n_tty_data *ldata = tty->disc_data;
-
-    wq = _wait_queue_current_get(tty);
-
-    while(count)
-    {
-        if ((!input_available_p(tty, 0)))
-        {
-            if (fd->flags & O_NONBLOCK)
-            {
-                retval = -EAGAIN;
-                break;
-            }
-
-            wait_ret = rt_wqueue_wait_interruptible(wq, 0, RT_WAITING_FOREVER);
-
-            if (wait_ret != 0)
-            {
-                break;
-            }
-        }
-
-        level = rt_spin_lock_irqsave(&tty->spinlock);
-        if (ldata->icanon && !L_EXTPROC(tty))
-        {
-            retval = canon_copy_from_read_buf(tty, b, count);
-        }
-        else
-        {
-            retval = copy_from_read_buf(tty, b, count);
-        }
-        rt_spin_unlock_irqrestore(&tty->spinlock, level);
-
-        if (retval >= 1)
-        {
-            break;
-        }
-    }
-
-    return retval;
-}
-
-static int n_tty_write(struct dfs_file *fd, const void *buf, size_t count)
-{
-    int retval = 0;
-    char *b = (char *)buf;
-    int c = 0;
-    struct tty_struct *tty = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-    retval = tty_check_change(tty);
-    if (retval)
-    {
-        return retval;
-    }
-
-    process_echoes(tty);
-    retval = count;
-    while(1)
-    {
-        if (O_OPOST(tty))
-        {
-            while (count > 0)
-            {
-                ssize_t num = process_output_block(tty, b, count);
-                if (num < 0)
-                {
-                    if (num == -EAGAIN)
-                    {
-                        break;
-                    }
-
-                    retval = num;
-                    goto break_out;
-                }
-                b += num;
-                count -= num;
-                if (count == 0)
-                {
-                    break;
-                }
-
-                c = *b;
-                if (process_output(c, tty) < 0)
-                {
-                    break;
-                }
-
-                b++;
-                count--;
-            }
-            retval -= count;
-        }
-        else
-        {
-            while (count > 0)
-            {
-                c = rt_device_write((rt_device_t)tty, -1, b, count);
-                if (c < 0)
-                {
-                    retval = c;
-                    goto break_out;
-                }
-                b += c;
-                count -= c;
-            }
-            retval -= count;
-        }
-
-        if (!count)
-        {
-            break;
-        }
-        if (fd->flags & O_NONBLOCK)
-        {
-            break;
-        }
-    }
-break_out:
-    return retval;
-}
-
-static int n_tty_flush(struct dfs_file *fd)
-{
-    return 0;
-}
-
-static int n_tty_lseek(struct dfs_file *fd, off_t offset)
-{
-    return 0;
-}
-
-static int n_tty_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count)
-{
-    return 0;
-}
-
-static int n_tty_poll(struct dfs_file *fd, struct rt_pollreq *req)
-{
-    rt_base_t level = 0;
-    int mask = POLLOUT;
-    struct tty_struct *tty = RT_NULL;
-    struct rt_wqueue *wq = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED);
-
-    wq = _wait_queue_current_get(tty);
-    rt_poll_add(wq, req);
-
-    level = rt_spin_lock_irqsave(&tty->spinlock);
-    if (input_available_p(tty, 1))
-    {
-        mask |= POLLIN;
-    }
-    rt_spin_unlock_irqrestore(&tty->spinlock, level);
-
-    return mask;
-}
-
-struct tty_ldisc_ops n_tty_ops = {
-    "n_tty",
-    0,
-    n_tty_open,
-    n_tty_close,
-    n_tty_ioctl,
-    n_tty_read,
-    n_tty_write,
-    n_tty_flush,
-    n_tty_lseek,
-    n_tty_getdents,
-    n_tty_poll,
-    n_tty_set_termios,
-    n_tty_receive_buf,
-    0,
-};

+ 0 - 326
components/drivers/tty/pty.c

@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021.12.07     linzhenxing      first version
- */
-#include <rtthread.h>
-#include <tty.h>
-#include <tty_ldisc.h>
-
-#define DBG_TAG               "PTY"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-#define PTY_PTS_SIZE 10
-static struct tty_struct ptm_dev;
-static struct tty_struct pts_devs[PTY_PTS_SIZE];
-static int pts_index = 0;
-
-static int pts_register(struct tty_struct *ptmx, struct tty_struct *pts, int pts_index);
-
-/* check free pts device */
-static struct tty_struct *find_freepts(void)
-{
-    for(int i = 0; i < PTY_PTS_SIZE; i++)
-    {
-        if (pts_devs[i].init_flag == TTY_INIT_FLAG_NONE)
-        {
-            pts_devs[i].init_flag = TTY_INIT_FLAG_ALLOCED;
-            return &pts_devs[i];
-        }
-    }
-    return RT_NULL;
-}
-
-/* Set the lock flag on a pty */
-static int pty_set_lock(struct tty_struct *tty, int *arg)
-{
-    int val = *arg;
-
-    if (val)
-    {
-        tty->pts_lock = val;
-    }
-    else
-    {
-        tty->pts_lock = val;
-    }
-    return 0;
-}
-
-static int pty_get_lock(struct tty_struct *tty, int *arg)
-{
-    *arg = tty->pts_lock;
-    return 0;
-}
-
-static int pty_get_index(struct tty_struct *tty, int *arg)
-{
-    *arg = tty->index;
-    return 0;
-}
-/* RT-Thread Device Interface */
-/*
- * This function initializes console device.
- */
-static rt_err_t pty_device_init(struct rt_device *dev)
-{
-    rt_err_t result = RT_EOK;
-    struct tty_struct *tty = RT_NULL;
-
-    RT_ASSERT(dev != RT_NULL);
-    tty = (struct tty_struct *)dev;
-
-    RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_REGED);
-    tty->init_flag = TTY_INIT_FLAG_INITED;
-
-    return result;
-}
-
-static rt_err_t pty_device_open(struct rt_device *dev, rt_uint16_t oflag)
-{
-    rt_err_t result = RT_EOK;
-    return result;
-}
-
-static rt_err_t pty_device_close(struct rt_device *dev)
-{
-    rt_err_t result = RT_EOK;
-    struct tty_struct *tty = (struct tty_struct*)dev;
-    //struct tty_struct *to = RT_NULL;
-
-    if (tty->subtype == PTY_TYPE_MASTER)
-    {
-        // to = tty->other_struct;
-        // to->init_flag = TTY_INIT_FLAG_NONE;
-        // to->other_struct = RT_NULL;
-        // to->foreground = RT_NULL;
-        // to->index = -1;
-        // tty_ldisc_kill(to);
-        // tty->other_struct = RT_NULL;
-    }
-    else
-    {
-        // to = tty->other_struct;
-        // to->other_struct = RT_NULL;
-        // tty->init_flag = TTY_INIT_FLAG_NONE;
-        // tty->other_struct = RT_NULL;
-        // tty->foreground = RT_NULL;
-        // tty->index = -1;
-        // tty->other_struct = RT_NULL;
-        // tty_ldisc_kill(tty);
-    }
-
-    return result;
-}
-
-static rt_ssize_t pty_device_read(struct rt_device *dev,
-        rt_off_t          pos,
-        void             *buffer,
-        rt_size_t         size)
-{
-    rt_size_t len = 0;
-
-    return len;
-}
-
-static rt_ssize_t pty_device_write(struct rt_device *dev,
-        rt_off_t          pos,
-        const void       *buffer,
-        rt_size_t         size)
-{
-    rt_size_t len = 0;
-    rt_base_t level = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_struct *to = RT_NULL;
-
-    tty = (struct tty_struct *)dev;
-    RT_ASSERT(tty != RT_NULL);
-    RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED);
-    to = tty->other_struct;
-
-    level = rt_spin_lock_irqsave(&tty->spinlock);
-    if (to->ldisc->ops->receive_buf)
-    {
-        len = to->ldisc->ops->receive_buf(to, (char *)buffer, size);
-    }
-    rt_spin_unlock_irqrestore(&tty->spinlock, level);
-
-    return len;
-}
-
-static rt_err_t  pty_device_control(rt_device_t dev, int cmd, void *args)
-{
-    struct tty_struct *tty = (struct tty_struct *)dev;
-
-    switch (cmd)
-    {
-    case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
-        return pty_set_lock(tty, (int *)args);
-    case TIOCGPTLCK: /* Get PT Lock status */
-        return pty_get_lock(tty, (int *)args);
-    case TIOCGPTN: /* Get PT Number */
-        return pty_get_index(tty, (int *)args);
-    }
-
-    return -ENOIOCTLCMD;
-}
-
-static int ptmx_open(struct dfs_file *fd)
-{
-    int ret = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_struct *pts = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    pts = find_freepts();
-    if (pts == RT_NULL)
-    {
-        LOG_E("No free PTS device found.\n");
-        return -1;
-    }
-    ret = pts_register(tty, pts, pts_index);
-    if (ret < 0)
-    {
-        LOG_E("pts register fail\n");
-        rt_free(pts);
-        return -1;
-    }
-    pts_index++;
-    tty->other_struct = pts;
-
-    ld = tty->ldisc;
-    if (ld->ops->open)
-    {
-        ret = ld->ops->open(fd);
-    }
-
-    rt_device_t device = (rt_device_t)fd->vnode->data;
-    if(fd->vnode->ref_count == 1)
-    {
-        ret = rt_device_open(device, fd->flags);
-    }
-
-    return ret;
-}
-#ifdef RT_USING_DEVICE_OPS
-const static struct rt_device_ops pty_device_ops =
-{
-    pty_device_init,
-    pty_device_open,
-    pty_device_close,
-    pty_device_read,
-    pty_device_write,
-    pty_device_control,
-};
-#endif /* RT_USING_DEVICE_OPS */
-
-static struct dfs_file_ops pts_fops;
-static struct dfs_file_ops ptmx_fops;
-static int pts_register(struct tty_struct *ptmx, struct tty_struct *pts, int pts_index)
-{
-    char name[20];
-    rt_err_t ret = RT_EOK;
-    struct rt_device *device = RT_NULL;
-
-    RT_ASSERT(ptmx!=RT_NULL);
-
-    if (pts->init_flag != TTY_INIT_FLAG_ALLOCED)
-    {
-        LOG_E("pts%d has been registered\n", pts_index);
-        ret = (-RT_EBUSY);
-    }
-    else
-    {
-        tty_init(pts, TTY_DRIVER_TYPE_PTY, PTY_TYPE_SLAVE, NULL);
-        pts->index = pts_index;
-        pts->pts_lock = 1;
-        pts->other_struct = ptmx;
-
-        device = &pts->parent;
-        device->type    = RT_Device_Class_Char;
-#ifdef RT_USING_DEVICE_OPS
-        device->ops     = &pty_device_ops;
-#else
-        device->init    = pty_device_init;
-        device->open    = pty_device_open;
-        device->close   = pty_device_close;
-        device->read    = pty_device_read;
-        device->write   = pty_device_write;
-        device->control = pty_device_control;
-#endif /* RT_USING_DEVICE_OPS */
-
-        rt_snprintf(name, sizeof(name), "pts%d", pts_index);
-        ret = rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
-        if (ret != RT_EOK)
-        {
-            LOG_E("pts%d register failed\n", pts_index);
-            ret = -RT_EIO;
-        }
-        else
-        {
-#ifdef RT_USING_POSIX_DEVIO
-            /* set fops */
-            memcpy(&pts_fops, tty_get_fops(), sizeof(struct dfs_file_ops));
-            device->fops = &pts_fops;
-#endif
-        }
-    }
-
-    return ret;
-}
-
-static int ptmx_register(void)
-{
-    rt_err_t ret = RT_EOK;
-    struct rt_device *device = RT_NULL;
-    struct tty_struct *ptmx = &ptm_dev;
-
-    RT_ASSERT(ptmx->init_flag == TTY_INIT_FLAG_NONE);
-
-    tty_init(ptmx, TTY_DRIVER_TYPE_PTY, PTY_TYPE_MASTER, NULL);
-
-    device = &(ptmx->parent);
-    device->type = RT_Device_Class_Char;
-
-#ifdef RT_USING_DEVICE_OPS
-    device->ops     = &pty_device_ops;
-#else
-    device->init    = pty_device_init;
-    device->open    = pty_device_open;
-    device->close   = pty_device_close;
-    device->read    = pty_device_read;
-    device->write   = pty_device_write;
-    device->control = pty_device_control;
-#endif /* RT_USING_DEVICE_OPS */
-
-    ret = rt_device_register(device, "ptmx", RT_DEVICE_FLAG_RDWR);
-    if (ret != RT_EOK)
-    {
-        LOG_E("ptmx register fail\n");
-        ret = -RT_EIO;
-    }
-    else
-    {
-#ifdef RT_USING_POSIX_DEVIO
-        /* set fops */
-        memcpy(&ptmx_fops, tty_get_fops(), sizeof(struct dfs_file_ops));
-        ptmx_fops.open = ptmx_open;
-        device->fops = &ptmx_fops;
-#endif
-    }
-
-    return ret;
-}
-INIT_DEVICE_EXPORT(ptmx_register);

+ 0 - 504
components/drivers/tty/tty.c

@@ -1,504 +0,0 @@
-/*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021-12-07     linzhenxing  first version
- * 2023-06-26     WangXiaoyao  fix bug on foreground app switch
- */
-#include <dfs_file.h>
-#include <dfs_fs.h>
-#include <lwp.h>
-#include <rtdevice.h>
-#include <rtthread.h>
-#include <tty.h>
-#include <tty_ldisc.h>
-#include <shell.h>
-
-#if defined(RT_USING_POSIX_DEVIO)
-#include <termios.h>
-#endif
-
-#define DBG_TAG               "TTY"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-const struct termios tty_std_termios = {  /* for the benefit of tty drivers  */
-    .c_iflag = IMAXBEL | IUCLC | INLCR | ICRNL | IGNPAR,
-    .c_oflag = OPOST,
-    .c_cflag = B38400 | CS8 | CREAD | HUPCL,
-    .c_lflag = ISIG | ECHOE  | TOSTOP | NOFLSH,
-    RT_NULL,/* .c_line = N_TTY, */
-    .c_cc = INIT_C_CC,
-    .__c_ispeed = 38400,
-    .__c_ospeed = 38400
-};
-
-void tty_initstack(struct tty_node *node)
-{
-    node->lwp = RT_NULL;
-    node->next = RT_NULL;
-}
-
-static struct tty_node tty_node_cache = { RT_NULL, RT_NULL };
-
-static struct tty_node *_tty_node_alloc(void)
-{
-    struct tty_node *node = tty_node_cache.next;
-
-    if (node == RT_NULL)
-    {
-        node = rt_calloc(1, sizeof(struct tty_node));
-    }
-    else
-    {
-        tty_node_cache.next = node->next;
-    }
-
-    return node;
-}
-
-static void _tty_node_free(struct tty_node *node)
-{
-    node->next = tty_node_cache.next;
-    tty_node_cache.next = node;
-}
-
-int tty_push(struct tty_node **head, struct rt_lwp *lwp)
-{
-    struct tty_node *node = _tty_node_alloc();
-
-    if (!node)
-    {
-        return -1;
-    }
-
-    node->lwp = lwp;
-    node->next = *head;
-    *head = node;
-
-    return 0;
-}
-
-struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp)
-{
-    struct tty_node *node;
-    struct rt_lwp *lwp = RT_NULL;
-
-    if (!head || !*head)
-    {
-        return RT_NULL;
-    }
-
-    node = *head;
-
-    if (target_lwp != RT_NULL && node->lwp != target_lwp)
-    {
-        struct tty_node *prev = RT_NULL;
-
-        while (node != RT_NULL && node->lwp != target_lwp)
-        {
-            prev = node;
-            node = node->next;
-        }
-
-        if (node != RT_NULL)
-        {
-            /* prev is impossible equ RT_NULL */
-            prev->next = node->next;
-            lwp = target_lwp;
-            _tty_node_free(node);
-        }
-    }
-    else
-    {
-        lwp = (*head)->lwp;
-        *head = (*head)->next;
-        node->lwp = RT_NULL;
-        _tty_node_free(node);
-    }
-
-    return lwp;
-}
-
-/**
- *  tty_check_change    -   check for POSIX terminal changes
- *  @tty: tty to check
- *
- *  If we try to write to, or set the state of, a terminal and we're
- *  not in the foreground, send a SIGTTOU.  If the signal is blocked or
- *  ignored, go ahead and perform the operation.  (POSIX 7.2)
- *
- *  Locking: ctrl_lock
- */
-
-int __tty_check_change(struct tty_struct *tty, int sig)
-{
-    pid_t pgrp = 0, tty_pgrp = 0;
-    int ret = 0;
-    struct rt_lwp *lwp;
-
-    lwp = lwp_self();
-
-    if (lwp == RT_NULL)
-    {
-        return 0;
-    }
-
-    if (lwp->tty != tty)
-    {
-        return 0;
-    }
-
-    pgrp = lwp->__pgrp;
-    tty_pgrp = tty->pgrp;
-
-    if (tty_pgrp && (pgrp != tty->pgrp))
-    {
-        lwp_signal_kill(lwp, sig, SI_USER, 0);
-    }
-
-    if (!tty_pgrp)
-    {
-        LOG_D("sig=%d, tty->pgrp == -1!\n", sig);
-    }
-    return ret;
-}
-
-int tty_check_change(struct tty_struct *tty)
-{
-    return __tty_check_change(tty, SIGTTOU);
-}
-
-static int tty_open(struct dfs_file *fd)
-{
-    int ret = 0;
-    int noctty = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    ld = tty->ldisc;
-    if (ld->ops->open)
-    {
-        ret = ld->ops->open(fd);
-    }
-    noctty = (fd->flags & O_NOCTTY);
-
-    rt_device_t device = (rt_device_t)fd->vnode->data;
-    if (fd->vnode->ref_count == 1)
-    {
-        ret = rt_device_open(device, fd->flags);
-    }
-    if (current == RT_NULL) //kernel mode not lwp
-    {
-        return ret;
-    }
-
-    if (!noctty &&
-        current->leader &&
-        !current->tty &&
-        tty->session == -1)
-    {
-        current->tty = tty;
-        current->tty_old_pgrp = 0;
-        tty->session = current->session;
-        tty->pgrp = current->__pgrp;
-        tty->foreground = current;
-    }
-
-    return ret;
-}
-
-static int tty_close(struct dfs_file *fd)
-{
-    int ret = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-    ld = tty->ldisc;
-    if (ld->ops->close)
-    {
-        //ld->ops->close(tty);
-    }
-    if (fd->vnode->ref_count == 1)
-    {
-        ret = rt_device_close((rt_device_t)tty);
-    }
-    return ret;
-}
-
-static int tiocsctty(struct tty_struct *tty, int arg)
-{
-    if (current->leader &&
-        (current->session == tty->session))
-    {
-        return 0;
-    }
-
-    /*
-     * The process must be a session leader and
-     * not have a controlling tty already.
-     */
-    if (!current->leader || current->tty)
-    {
-        return -EPERM;
-    }
-
-    if (tty->session > 0)
-    {
-        LOG_E("this tty have control process\n");
-
-    }
-    current->tty = tty;
-    current->tty_old_pgrp = 0;
-    tty->session = current->session;
-    tty->pgrp = current->__pgrp;
-    tty->foreground = current;
-    if (tty->type == TTY_DRIVER_TYPE_PTY)
-    {
-        tty->other_struct->foreground = current;
-    }
-    return 0;
-}
-
-static int tiocswinsz(struct tty_struct *tty, struct winsize *p_winsize)
-{
-    rt_kprintf("\x1b[8;%d;%dt", p_winsize->ws_col, p_winsize->ws_row);
-    return 0;
-}
-
-static int tiocgwinsz(struct tty_struct *tty, struct winsize *p_winsize)
-{
-    if(rt_thread_self() != rt_thread_find(FINSH_THREAD_NAME))
-    {
-        /* only can be used in tshell thread; otherwise, return default size */
-        p_winsize->ws_col = 80;
-        p_winsize->ws_row = 24;
-    }
-    else
-    {
-        #define _TIO_BUFLEN 20
-        char _tio_buf[_TIO_BUFLEN];
-        unsigned char cnt1, cnt2, cnt3, i;
-        char row_s[4], col_s[4];
-        char *p;
-
-        rt_memset(_tio_buf, 0, _TIO_BUFLEN);
-
-        /* send the command to terminal for getting the window size of the terminal */
-        rt_kprintf("\033[18t");
-
-        /* waiting for the response from the terminal */
-        i = 0;
-        while(i < _TIO_BUFLEN)
-        {
-            _tio_buf[i] = finsh_getchar();
-            if(_tio_buf[i] != 't')
-            {
-                i ++;
-            }
-            else
-            {
-                break;
-            }
-        }
-        if(i == _TIO_BUFLEN)
-        {
-            /* buffer overloaded, and return default size */
-            p_winsize->ws_col = 80;
-            p_winsize->ws_row = 24;
-            return 0;
-        }
-
-        /* interpreting data eg: "\033[8;1;15t" which means row is 1 and col is 15 (unit: size of ONE character) */
-        rt_memset(row_s,0,4);
-        rt_memset(col_s,0,4);
-        cnt1 = 0;
-        while(cnt1 < _TIO_BUFLEN && _tio_buf[cnt1] != ';')
-        {
-            cnt1++;
-        }
-        cnt2 = ++cnt1;
-        while(cnt2 < _TIO_BUFLEN && _tio_buf[cnt2] != ';')
-        {
-            cnt2++;
-        }
-        p = row_s;
-        while(cnt1 < cnt2)
-        {
-            *p++ = _tio_buf[cnt1++];
-        }
-        p = col_s;
-        cnt2++;
-        cnt3 = rt_strlen(_tio_buf) - 1;
-        while(cnt2 < cnt3)
-        {
-            *p++ = _tio_buf[cnt2++];
-        }
-
-        /* load the window size date */
-        p_winsize->ws_col = atoi(col_s);
-        p_winsize->ws_row = atoi(row_s);
-    #undef _TIO_BUFLEN
-    }
-
-    p_winsize->ws_xpixel = 0;/* unused */
-    p_winsize->ws_ypixel = 0;/* unused */
-
-    return 0;
-}
-
-static int tty_ioctl(struct dfs_file *fd, int cmd, void *args)
-{
-    int ret = 0;
-    void *p = (void *)args;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_struct *real_tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
-    {
-        real_tty = tty->other_struct;
-    }
-    else
-    {
-        real_tty = tty;
-    }
-
-    switch (cmd)
-    {
-    case TIOCSCTTY:
-        return tiocsctty(real_tty, 1);
-    case TIOCGWINSZ:
-        return tiocgwinsz(real_tty, p);
-    case TIOCSWINSZ:
-        return tiocswinsz(real_tty, p);
-    }
-
-    ld = tty->ldisc;
-    if (ld->ops->ioctl)
-    {
-        ret = ld->ops->ioctl(fd, cmd, args);
-    }
-    return ret;
-}
-
-#ifdef RT_USING_DFS_V2
-static ssize_t tty_read(struct dfs_file *fd, void *buf, size_t count, off_t *pos)
-#else
-static ssize_t tty_read(struct dfs_file *fd, void *buf, size_t count)
-#endif
-{
-    int ret = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    ld = tty->ldisc;
-    if (ld && ld->ops->read)
-    {
-        ret = ld->ops->read(fd, buf, count);
-    }
-    return ret;
-}
-
-#ifdef RT_USING_DFS_V2
-static ssize_t tty_write(struct dfs_file *fd, const void *buf, size_t count, off_t *pos)
-#else
-static ssize_t tty_write(struct dfs_file *fd, const void *buf, size_t count )
-#endif
-{
-    int ret = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-
-    ld = tty->ldisc;
-    if (ld && ld->ops->write)
-    {
-        ret = ld->ops->write(fd, buf, count);
-    }
-
-    return ret;
-}
-
-static int tty_poll(struct dfs_file *fd, struct rt_pollreq *req)
-{
-    int ret = 0;
-    struct tty_struct *tty = RT_NULL;
-    struct tty_ldisc *ld = RT_NULL;
-
-    tty = (struct tty_struct *)fd->vnode->data;
-    RT_ASSERT(tty != RT_NULL);
-    ld = tty->ldisc;
-    if (ld->ops->poll)
-    {
-        ret = ld->ops->poll(fd, req);
-    }
-    return ret;
-}
-
-static const struct dfs_file_ops tty_fops =
-{
-    .open   = tty_open,
-    .close  = tty_close,
-    .ioctl  = tty_ioctl,
-    .read   = tty_read,
-    .write  = tty_write,
-    .poll   = tty_poll,
-};
-
-const struct dfs_file_ops *tty_get_fops(void)
-{
-    return &tty_fops;
-}
-
-int tty_init(struct tty_struct *tty, int type, int subtype, struct rt_device *iodev)
-{
-    if (tty)
-    {
-        struct tty_node *node = NULL;
-
-        node = rt_calloc(1, sizeof(struct tty_node));
-        if (node)
-        {
-            tty->type = type;
-            tty->subtype = subtype;
-            tty->io_dev = iodev;
-
-            tty->head = node;
-            tty_initstack(tty->head);
-
-            tty->pgrp = -1;
-            tty->session = -1;
-            tty->foreground = RT_NULL;
-
-            rt_mutex_init(&tty->lock, "ttyLock", RT_IPC_FLAG_PRIO);
-            rt_wqueue_init(&tty->wait_queue);
-            rt_spin_lock_init(&tty->spinlock);
-
-            tty_ldisc_init(tty);
-            tty->init_termios = tty_std_termios;
-            tty->init_flag = TTY_INIT_FLAG_REGED;
-        }
-    }
-
-    return 0;
-}

+ 0 - 122
components/drivers/tty/tty_ioctl.c

@@ -1,122 +0,0 @@
-#include <stddef.h>
-#include <rtthread.h>
-#include <tty.h>
-#if defined(RT_USING_POSIX_DEVIO)
-#include <termios.h>
-#endif
-
-#define DBG_TAG               "TTY_IOCTL"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-/*
- * Internal flag options for termios setting behavior
- */
-#define TERMIOS_FLUSH   1
-#define TERMIOS_WAIT    2
-#define TERMIOS_TERMIO  4
-#define TERMIOS_OLD 8
-
-/**
- *  set_termios     -   set termios values for a tty
- *  @tty: terminal device
- *  @arg: user data
- *  @opt: option information
- *
- *  Helper function to prepare termios data and run necessary other
- *  functions before using tty_set_termios to do the actual changes.
- *
- *  Locking:
- *      Called functions take ldisc and termios_rwsem locks
- */
-
-static int set_termios(struct tty_struct *tty, void *arg, int opt)
-{
-    struct termios old_termios;
-    struct tty_ldisc *ld = RT_NULL;
-    struct termios *new_termios = (struct termios *)arg;
-    rt_base_t level = 0;
-    int retval = tty_check_change(tty);
-
-    if (retval)
-    {
-        return retval;
-    }
-
-    memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios));
-    level = rt_spin_lock_irqsave(&tty->spinlock);
-    tty->init_termios = *new_termios;
-    rt_spin_unlock_irqrestore(&tty->spinlock, level);
-    ld = tty->ldisc;
-    if (ld != NULL)
-    {
-        if (ld->ops->set_termios)
-        {
-            ld->ops->set_termios(tty, &old_termios);
-        }
-    }
-    return 0;
-}
-
-int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *args)
-{
-    int ret = 0;
-    void *p = (void *)args;
-    struct tty_struct *real_tty = RT_NULL;
-
-    if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
-    {
-        real_tty = tty->other_struct;
-    }
-    else
-    {
-        real_tty = tty;
-    }
-
-    switch(cmd)
-    {
-    case TCGETS:
-    case TCGETA:
-    {
-        struct termios *tio = (struct termios *)p;
-        if (tio == RT_NULL)
-        {
-            return -RT_EINVAL;
-        }
-
-        memcpy(tio, &real_tty->init_termios, sizeof(real_tty->init_termios));
-        return ret;
-    }
-    case TCSETSF:
-    {
-        return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD);
-    }
-    case TCSETSW:
-    {
-        return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD);
-    }
-    case TCSETS:
-    {
-        return set_termios(real_tty, p, TERMIOS_OLD);
-    }
-    case TCSETAF:
-    {
-        return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO);
-    }
-    case TCSETAW:
-    {
-        return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_TERMIO);
-    }
-    case TCSETA:
-    {
-        return set_termios(real_tty, p, TERMIOS_TERMIO);
-    }
-    default:
-        break;
-    }
-    return -ENOIOCTLCMD;
-}

+ 0 - 178
components/drivers/tty/tty_ldisc.c

@@ -1,178 +0,0 @@
-#include <tty.h>
-#include <tty_ldisc.h>
-
-#define DBG_TAG               "TTY_LDISC"
-#ifdef RT_TTY_DEBUG
-#define DBG_LVL               DBG_LOG
-#else
-#define DBG_LVL               DBG_INFO
-#endif /* RT_TTY_DEBUG */
-#include <rtdbg.h>
-
-extern struct tty_ldisc_ops n_tty_ops;
-static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS] = {
-    &n_tty_ops, /* N_TTY = 0 */
-};
-
-static struct rt_spinlock _spinlock = RT_SPINLOCK_INIT;
-
-static struct tty_ldisc_ops *get_ldops(int disc)
-{
-    struct tty_ldisc_ops *ldops = RT_NULL;
-    rt_base_t level = 0;
-    level = rt_spin_lock_irqsave(&_spinlock);
-    ldops = tty_ldiscs[disc];
-    if (ldops)
-    {
-        ldops->refcount++;
-    }
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-    return ldops;
-}
-
-static void put_ldops(struct tty_ldisc_ops *ldops)
-{
-    rt_base_t level = 0;
-
-    level = rt_spin_lock_irqsave(&_spinlock);
-    ldops->refcount--;
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-}
-
-static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
-{
-    struct tty_ldisc *ld = RT_NULL;
-    struct tty_ldisc_ops *ldops = RT_NULL;
-
-    if (disc < N_TTY || disc >= NR_LDISCS)
-    {
-        return RT_NULL;
-    }
-
-    ldops = get_ldops(disc);
-    if (ldops == RT_NULL)
-    {
-        LOG_E("tty ldisc get error\n");
-        return RT_NULL;
-    }
-
-    ld = rt_malloc(sizeof(struct tty_ldisc));
-    if (ld == RT_NULL)
-    {
-        ldops->refcount--;
-        return RT_NULL;
-    }
-
-    ld->ops = ldops;
-    ld->tty = tty;
-
-    return ld;
-}
-
-/**
- *  tty_ldisc_put       -   release the ldisc
- *
- *  Complement of tty_ldisc_get().
- */
-static void tty_ldisc_put(struct tty_ldisc *ld)
-{
-    if (ld)
-    {
-        put_ldops(ld->ops);
-        rt_free(ld);
-    }
-}
-
-/**
- *  tty_ldisc_close     -   close a line discipline
- *  @tty: tty we are opening the ldisc on
- *  @ld: discipline to close
- *
- *  A helper close method. Also a convenient debugging and check
- *  point.
- */
-static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
-{
-    if (ld && ld->ops->close)
-    {
-        ld->ops->close(tty);
-    }
-}
-
-/**
- *  tty_ldisc_kill  -   teardown ldisc
- *  @tty: tty being released
- *
- *  Perform final close of the ldisc and reset tty->ldisc
- */
-void tty_ldisc_kill(struct tty_struct *tty)
-{
-    if (tty && tty->ldisc)
-    {
-        /*
-        * Now kill off the ldisc
-        */
-        tty_ldisc_close(tty, tty->ldisc);
-        tty_ldisc_put(tty->ldisc);
-        /* Force an oops if we mess this up */
-        tty->ldisc = NULL;
-    }
-}
-
-int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
-{
-    int ret = 0;
-    rt_base_t level = 0;
-
-    if (disc < N_TTY || disc >= NR_LDISCS)
-    {
-        return -EINVAL;
-    }
-
-    level = rt_spin_lock_irqsave(&_spinlock);
-    tty_ldiscs[disc] = new_ldisc;
-    new_ldisc->num = disc;
-    new_ldisc->refcount = 0;
-    rt_spin_unlock_irqrestore(&_spinlock, level);
-
-    return ret;
-}
-
-/**
- *  tty_ldisc_release       -   release line discipline
- *  @tty: tty being shut down (or one end of pty pair)
- *
- *  Called during the final close of a tty or a pty pair in order to shut
- *  down the line discpline layer. On exit, each tty's ldisc is NULL.
- */
-
-void tty_ldisc_release(struct tty_struct *tty)
-{
-    struct tty_struct *o_tty = tty->other_struct;
-
-    /*
-     * Shutdown this line discipline. As this is the final close,
-     * it does not race with the set_ldisc code path.
-     */
-    tty_ldisc_kill(tty);
-    if (o_tty)
-    {
-        tty_ldisc_kill(o_tty);
-    }
-}
-
-/**
- *  tty_ldisc_init      -   ldisc setup for new tty
- *  @tty: tty being allocated
- *
- *  Set up the line discipline objects for a newly allocated tty. Note that
- *  the tty structure is not completely set up when this call is made.
- */
-
-void tty_ldisc_init(struct tty_struct *tty)
-{
-    if (tty)
-    {
-        tty->ldisc = tty_ldisc_get(tty, N_TTY);
-    }
-}

+ 8 - 0
components/finsh/msh_file.c

@@ -22,6 +22,10 @@
 #include <dfs_mnt.h>
 #endif
 
+#ifdef RT_USING_SMART
+#include "lwp.h"
+#endif /* RT_USING_SMART */
+
 static int msh_readline(int fd, char *line_buf, int size)
 {
     char ch;
@@ -159,7 +163,11 @@ static int cmd_ls(int argc, char **argv)
     if (argc == 1)
     {
 #ifdef DFS_USING_WORKDIR
+#ifdef RT_USING_SMART
+        ls(lwp_getcwd());
+#else
         ls(working_directory);
+#endif
 #else
         ls("/");
 #endif

+ 13 - 8
components/libc/compilers/common/ctime.c

@@ -25,6 +25,7 @@
  * 2023-08-12     Meco Man     re-implement RT-Thread lightweight timezone API
  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
  * 2023-10-23     Shell        add lock for _g_timerid
+ * 2023-11-16     Shell        Fixup of nanosleep if previous call was interrupted
  */
 
 #include "sys/time.h"
@@ -544,20 +545,24 @@ int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
     unsigned long ns = rqtp->tv_sec * NANOSECOND_PER_SECOND + rqtp->tv_nsec;
     rt_ktime_boottime_get_ns(&old_ts);
     rt_ktime_hrtimer_ndelay(ns);
-    if (rt_get_errno() == -RT_EINTR)
+    if (rt_get_errno() == RT_EINTR)
     {
         if (rmtp)
         {
+            rt_base_t rsec, rnsec;
             rt_ktime_boottime_get_ns(&new_ts);
 
-            rmtp->tv_sec = 0;
-            rmtp->tv_nsec =
-                (old_ts.tv_nsec + ns) - ((new_ts.tv_sec - old_ts.tv_sec) * NANOSECOND_PER_SECOND + new_ts.tv_nsec);
-
-            if (rmtp->tv_nsec > NANOSECOND_PER_SECOND)
+            rsec = old_ts.tv_sec + rqtp->tv_sec - new_ts.tv_sec;
+            rnsec = old_ts.tv_nsec + rqtp->tv_nsec - new_ts.tv_nsec;
+            if (rnsec < 0)
+            {
+                rmtp->tv_sec = rsec - 1;
+                rmtp->tv_nsec = NANOSECOND_PER_SECOND + rnsec;
+            }
+            else
             {
-                rmtp->tv_nsec %= NANOSECOND_PER_SECOND;
-                rmtp->tv_sec += rmtp->tv_nsec / NANOSECOND_PER_SECOND;
+                rmtp->tv_sec = rsec;
+                rmtp->tv_nsec = rnsec;
             }
         }
         rt_set_errno(EINTR);

+ 5 - 1
components/libc/compilers/common/include/dirent.h

@@ -32,9 +32,13 @@ extern "C" {
 
 #define DT_UNKNOWN      0x00
 #define DT_FIFO         0x01
-#define DT_SYMLINK      0x03
+#define DT_CHR          0x02
 #define DT_DIR          0x04
+#define DT_BLK          0x06
 #define DT_REG          0x08
+#define DT_LNK          0x0a
+#define DT_SOCK         0x0c
+#define DT_SYMLINK      DT_LNK
 
 #ifndef HAVE_DIR_STRUCTURE
 #define HAVE_DIR_STRUCTURE

+ 4 - 3
components/libc/compilers/common/include/sys/ioctl.h

@@ -15,6 +15,10 @@
 extern "C" {
 #endif
 
+#ifdef RT_USING_MUSLLIBC
+#include_next <sys/ioctl.h>
+#else
+
 struct winsize
 {
     unsigned short ws_row;
@@ -23,9 +27,6 @@ struct winsize
     unsigned short ws_ypixel;
 };
 
-#ifdef RT_USING_MUSLLIBC
-#include <bits/ioctl.h>
-#else
 /*
  * Direction bits, which any architecture can choose to override
  * before including this file.

+ 2 - 0
components/libc/compilers/common/include/sys/unistd.h

@@ -19,6 +19,8 @@
 extern "C" {
 #endif
 
+#define _POSIX_VDISABLE 0
+
 #define STDIN_FILENO    0       /* standard input file descriptor */
 #define STDOUT_FILENO   1       /* standard output file descriptor */
 #define STDERR_FILENO   2       /* standard error file descriptor */

+ 29 - 4
components/libc/compilers/musl/fcntl.h

@@ -13,12 +13,37 @@
 
 #include_next <fcntl.h>
 
-#ifndef O_DIRECTORY
-#define O_DIRECTORY 0x200000
-#endif
+#define O_CREAT        0100
+#define O_EXCL         0200
+#define O_NOCTTY       0400
+#define O_TRUNC       01000
+#define O_APPEND      02000
+#define O_NONBLOCK    04000
+#define O_DSYNC      010000
+#define O_SYNC     04010000
+#define O_RSYNC    04010000
+#define O_DIRECTORY  040000
+#define O_NOFOLLOW  0100000
+#define O_CLOEXEC  02000000
+
+#define O_ASYNC      020000
+#define O_DIRECT    0200000
+#define O_LARGEFILE 0400000
+#define O_NOATIME  01000000
+#define O_PATH    010000000
+#define O_TMPFILE 020040000
+#define O_NDELAY O_NONBLOCK
 
 #ifndef O_BINARY
-#define O_BINARY 0x10000
+#define O_BINARY 00
 #endif
 
+#ifndef O_SEARCH
+#define O_SEARCH  O_PATH
+#endif /* O_SEARCH */
+
+#ifndef O_EXEC
+#define O_EXEC    O_PATH
+#endif /* O_EXEC */
+
 #endif

+ 1 - 1
components/libc/posix/Kconfig

@@ -27,7 +27,7 @@ if RT_USING_POSIX_FS
         select RT_USING_POSIX_POLL
         default y if RT_USING_SMART
         default n
-    
+
     config RT_USING_POSIX_EVENTFD
         bool "Enable I/O event eventfd <sys/eventfd.h>"
         select RT_USING_POSIX_POLL

+ 44 - 24
components/libc/posix/io/epoll/epoll.c

@@ -4,9 +4,14 @@
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
- * Date           Author            Notes
- * 2023-07-29     zmq810150896      first version
- * 2024/03/26     TroyMitchelle     Add comments for all functions, members within structure members and fix incorrect naming of triggered
+ * Date           Author        Notes
+ * 2023-07-29     zmq810150896  first version
+ * 2024/03/26     TroyMitchelle Add comments for all functions, members within structure members and fix incorrect naming of triggered
+ * 2023-12-14     Shell         When poll goes to sleep before the waitqueue has added a
+ *                              record and finished enumerating all the fd's, it may be
+ *                              incorrectly woken up. This is basically because the poll
+ *                              mechanism wakeup algorithm does not correctly distinguish
+ *                              the current wait state.
  */
 
 #include <rtthread.h>
@@ -27,6 +32,12 @@
 
 struct rt_eventpoll;
 
+enum rt_epoll_status {
+    RT_EPOLL_STAT_INIT,
+    RT_EPOLL_STAT_TRIG,
+    RT_EPOLL_STAT_WAITING,
+};
+
 /* Monitor queue */
 struct rt_fd_list
 {
@@ -44,15 +55,15 @@ struct rt_fd_list
 
 struct rt_eventpoll
 {
-    rt_uint32_t triggered;      /**< Indicates if the wait thread is triggered */
-    rt_wqueue_t epoll_read;     /**< Epoll read queue */
-    rt_thread_t polling_thread; /**< Polling thread */
-    struct rt_mutex lock;       /**< Mutex lock */
-    struct rt_fd_list *fdlist;  /**< Monitor list */
-    int eventpoll_num;          /**< Number of ready lists */
-    rt_pollreq_t req;           /**< Poll request structure */
-    struct rt_spinlock spinlock;/**< Spin lock */
-    rt_slist_t rdl_head;        /**< Ready list head */
+    rt_wqueue_t epoll_read;      /**< Epoll read queue */
+    rt_thread_t polling_thread;  /**< Polling thread */
+    struct rt_mutex lock;        /**< Mutex lock */
+    struct rt_fd_list *fdlist;   /**< Monitor list */
+    int eventpoll_num;           /**< Number of ready lists */
+    rt_pollreq_t req;            /**< Poll request structure */
+    struct rt_spinlock spinlock; /**< Spin lock */
+    rt_slist_t rdl_head;         /**< Ready list head */
+    enum rt_epoll_status status; /* the waited thread whether triggered */
 };
 
 static int epoll_close(struct dfs_file *file);
@@ -85,8 +96,7 @@ static int epoll_close_fdlist(struct rt_fd_list *fdlist)
         while (list->next != RT_NULL)
         {
             fre_node = list->next;
-            if (fre_node->wqn.wqueue)
-                rt_wqueue_remove(&fre_node->wqn);
+            rt_wqueue_remove(&fre_node->wqn);
 
             list->next = fre_node->next;
             rt_free(fre_node);
@@ -188,13 +198,14 @@ static int epoll_wqueue_callback(struct rt_wqueue_node *wait, void *key)
     struct rt_fd_list *fdlist;
     struct rt_eventpoll *ep;
     rt_base_t level;
+    int is_waiting = 0;
 
     if (key && !((rt_ubase_t)key & wait->key))
         return -1;
 
     fdlist = rt_container_of(wait, struct rt_fd_list, wqn);
-
     ep = fdlist->ep;
+
     if (ep)
     {
         level = rt_spin_lock_irqsave(&ep->spinlock);
@@ -203,16 +214,21 @@ static int epoll_wqueue_callback(struct rt_wqueue_node *wait, void *key)
             rt_slist_append(&ep->rdl_head, &fdlist->rdl_node);
             fdlist->exclusive = 0;
             fdlist->is_rdl_node = RT_TRUE;
-            ep->triggered = 1;
             ep->eventpoll_num++;
+            is_waiting = (ep->status == RT_EPOLL_STAT_WAITING);
+            ep->status = RT_EPOLL_STAT_TRIG;
             rt_wqueue_wakeup(&ep->epoll_read, (void *)POLLIN);
         }
         rt_spin_unlock_irqrestore(&ep->spinlock, level);
     }
 
-    return __wqueue_default_wake(wait, key);
-}
+    if (is_waiting)
+    {
+        return __wqueue_default_wake(wait, key);
+    }
 
+    return -1;
+}
 
 /**
  * @brief   Adds a callback function to the wait queue associated with epoll.
@@ -265,8 +281,8 @@ static void epoll_ctl_install(struct rt_fd_list *fdlist, struct rt_eventpoll *ep
             rt_slist_append(&ep->rdl_head, &fdlist->rdl_node);
             fdlist->exclusive = 0;
             fdlist->is_rdl_node = RT_TRUE;
-            ep->triggered = 1;
-            ep->eventpoll_num++;
+            ep->status = RT_EPOLL_STAT_TRIG;
+            ep->eventpoll_num ++;
             rt_spin_unlock_irqrestore(&ep->spinlock, level);
             rt_mutex_release(&ep->lock);
         }
@@ -282,7 +298,7 @@ static void epoll_ctl_install(struct rt_fd_list *fdlist, struct rt_eventpoll *ep
  */
 static void epoll_member_init(struct rt_eventpoll *ep)
 {
-    ep->triggered = 0;
+    ep->status = RT_EPOLL_STAT_INIT;
     ep->eventpoll_num = 0;
     ep->polling_thread = rt_thread_self();
     ep->fdlist = RT_NULL;
@@ -678,7 +694,7 @@ static int epoll_wait_timeout(struct rt_eventpoll *ep, int msec)
 
     level = rt_spin_lock_irqsave(&ep->spinlock);
 
-    if (timeout != 0 && !ep->triggered)
+    if (timeout != 0 && ep->status != RT_EPOLL_STAT_TRIG)
     {
         if (rt_thread_suspend_with_flag(thread, RT_KILLABLE) == RT_EOK)
         {
@@ -690,15 +706,19 @@ static int epoll_wait_timeout(struct rt_eventpoll *ep, int msec)
                 rt_timer_start(&(thread->thread_timer));
             }
 
+            ep->status = RT_EPOLL_STAT_WAITING;
+
             rt_spin_unlock_irqrestore(&ep->spinlock, level);
 
             rt_schedule();
 
             level = rt_spin_lock_irqsave(&ep->spinlock);
+            if (ep->status == RT_EPOLL_STAT_WAITING)
+                ep->status = RT_EPOLL_STAT_INIT;
         }
     }
 
-    ret = !ep->triggered;
+    ret = !(ep->status == RT_EPOLL_STAT_TRIG);
     rt_spin_unlock_irqrestore(&ep->spinlock, level);
 
     return ret;
@@ -867,7 +887,7 @@ static int epoll_do(struct rt_eventpoll *ep, struct epoll_event *events, int max
         if (event_num || istimeout)
         {
             level = rt_spin_lock_irqsave(&ep->spinlock);
-            ep->triggered = 0;
+            ep->status = RT_EPOLL_STAT_INIT;
             rt_spin_unlock_irqrestore(&ep->spinlock, level);
             if ((timeout >= 0) || (event_num > 0))
                 break;

+ 49 - 9
components/libc/posix/io/poll/poll.c

@@ -7,6 +7,12 @@
  * Date           Author       Notes
  * 2016-12-28     Bernard      first version
  * 2018-03-09     Bernard      Add protection for pt->triggered.
+ * 2023-12-04     Shell        Fix return code and error verification
+ * 2023-12-14     Shell        When poll goes to sleep before the waitqueue has added a
+ *                             record and finished enumerating all the fd's, it may be
+ *                             incorrectly woken up. This is basically because the poll
+ *                             mechanism wakeup algorithm does not correctly distinguish
+ *                             the current wait state.
  */
 
 #include <stdint.h>
@@ -16,11 +22,16 @@
 #include "poll.h"
 
 struct rt_poll_node;
+enum rt_poll_status {
+    RT_POLL_STAT_INIT,
+    RT_POLL_STAT_TRIG,
+    RT_POLL_STAT_WAITING,
+};
 
 struct rt_poll_table
 {
     rt_pollreq_t req;
-    rt_uint32_t triggered; /* the waited thread whether triggered */
+    enum rt_poll_status status; /* the waited thread whether triggered */
     rt_thread_t polling_thread;
     struct rt_poll_node *nodes;
 };
@@ -36,15 +47,25 @@ static RT_DEFINE_SPINLOCK(_spinlock);
 
 static int __wqueue_pollwake(struct rt_wqueue_node *wait, void *key)
 {
+    rt_ubase_t level;
     struct rt_poll_node *pn;
+    int is_waiting;
 
     if (key && !((rt_ubase_t)key & wait->key))
         return -1;
 
     pn = rt_container_of(wait, struct rt_poll_node, wqn);
-    pn->pt->triggered = 1;
 
-    return __wqueue_default_wake(wait, key);
+    level = rt_spin_lock_irqsave(&_spinlock);
+    is_waiting = (pn->pt->status == RT_POLL_STAT_WAITING);
+
+    pn->pt->status = RT_POLL_STAT_TRIG;
+    rt_spin_unlock_irqrestore(&_spinlock, level);
+
+    if (is_waiting)
+        return __wqueue_default_wake(wait, key);
+
+    return -1;
 }
 
 static void _poll_add(rt_wqueue_t *wq, rt_pollreq_t *req)
@@ -71,7 +92,7 @@ static void _poll_add(rt_wqueue_t *wq, rt_pollreq_t *req)
 static void poll_table_init(struct rt_poll_table *pt)
 {
     pt->req._proc = _poll_add;
-    pt->triggered = 0;
+    pt->status = RT_POLL_STAT_INIT;
     pt->nodes = RT_NULL;
     pt->polling_thread = rt_thread_self();
 }
@@ -89,7 +110,7 @@ static int poll_wait_timeout(struct rt_poll_table *pt, int msec)
 
     level = rt_spin_lock_irqsave(&_spinlock);
 
-    if (timeout != 0 && !pt->triggered)
+    if (timeout != 0 && pt->status != RT_POLL_STAT_TRIG)
     {
         if (rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE) == RT_EOK)
         {
@@ -99,17 +120,31 @@ static int poll_wait_timeout(struct rt_poll_table *pt, int msec)
                         RT_TIMER_CTRL_SET_TIME,
                         &timeout);
                 rt_timer_start(&(thread->thread_timer));
+                rt_set_errno(RT_ETIMEOUT);
             }
-
+            else
+            {
+                rt_set_errno(0);
+            }
+            pt->status = RT_POLL_STAT_WAITING;
             rt_spin_unlock_irqrestore(&_spinlock, level);
 
             rt_schedule();
 
             level = rt_spin_lock_irqsave(&_spinlock);
+            if (pt->status == RT_POLL_STAT_WAITING)
+                pt->status = RT_POLL_STAT_INIT;
         }
     }
 
-    ret = !pt->triggered;
+    ret = rt_get_errno();
+    if (ret == RT_EINTR)
+        ret = -RT_EINTR;
+    else if (pt->status == RT_POLL_STAT_TRIG)
+        ret = RT_EOK;
+    else
+        ret = -RT_ETIMEOUT;
+
     rt_spin_unlock_irqrestore(&_spinlock, level);
 
     return ret;
@@ -170,7 +205,7 @@ static int poll_do(struct pollfd *fds, nfds_t nfds, struct rt_poll_table *pt, in
     {
         pf = fds;
         num = 0;
-        pt->triggered = 0;
+        pt->status = RT_POLL_STAT_INIT;
 
         for (n = 0; n < nfds; n ++)
         {
@@ -194,8 +229,13 @@ static int poll_do(struct pollfd *fds, nfds_t nfds, struct rt_poll_table *pt, in
         if (num || istimeout)
             break;
 
-        if (poll_wait_timeout(pt, msec))
+        ret = poll_wait_timeout(pt, msec);
+        if (ret == -RT_EINTR)
+            return -EINTR;
+        else if (ret == -RT_ETIMEOUT)
             istimeout = 1;
+        else
+            istimeout = 0;
     }
 
     return num;

+ 14 - 0
components/libc/posix/io/termios/termios.h

@@ -36,6 +36,20 @@ struct termios {
     speed_t __c_ospeed;
 };
 
+#ifndef NCC
+#define NCC 8
+
+struct termio
+{
+    unsigned short c_iflag;  /* input mode flags */
+    unsigned short c_oflag;  /* output mode flags */
+    unsigned short c_cflag;  /* control mode flags */
+    unsigned short c_lflag;  /* local mode flags */
+    unsigned char c_line;    /* line discipline */
+    unsigned char c_cc[NCC]; /* control characters */
+};
+#endif
+
 /* c_cc characters */
 #define VINTR     0
 #define VQUIT     1

+ 2 - 19
components/lwp/Kconfig

@@ -8,7 +8,7 @@ menuconfig RT_USING_LWP
 if RT_USING_LWP
     config LWP_DEBUG
         bool "Enable debugging features of LwP"
-        default n
+        default y
 
     config RT_LWP_MAX_NR
         int "The max number of light-weight process"
@@ -51,24 +51,6 @@ if RT_USING_LWP
             default y
     endif
 
-    config LWP_UNIX98_PTY
-        bool "The unix98 PTY support"
-        default n
-
-    if LWP_UNIX98_PTY
-        config LWP_PTY_INPUT_BFSZ
-            int "The unix98 PTY input buffer size"
-            default 1024
-
-        config LWP_PTY_PTS_SIZE
-            int "The unix98 PTY device max num"
-            default 3
-
-        config LWP_PTY_USING_DEBUG
-            bool "The unix98 PTY debug output"
-            default n
-    endif
-
     menuconfig RT_USING_LDSO
         bool "LDSO: dynamic load shared objects"
         depends on RT_USING_DFS_V2
@@ -85,5 +67,6 @@ if RT_USING_LWP
             default n
     endif
 
+source "$RTT_DIR/components/lwp/terminal/Kconfig"
 endif
 

+ 6 - 5
components/lwp/SConscript

@@ -22,11 +22,6 @@ if arch == 'risc-v':
     if cpu in rv64:
         cpu = 'rv64'
 
-if GetDepend('LWP_UNIX98_PTY'):
-    # print("LWP_UNIX98_PTY")
-    src += Glob('unix98pty/*.c')
-    CPPPATH += ['unix98pty/']
-
 if platform in platform_file.keys(): # support platforms
     if arch in support_arch.keys() and cpu in support_arch[arch]:
         asm_path = 'arch/' + arch + '/' + cpu + '/*_' + platform_file[platform]
@@ -40,6 +35,12 @@ if platform in platform_file.keys(): # support platforms
         CPPPATH = [cwd]
         CPPPATH += [cwd + '/arch/' + arch + '/' + cpu]
 
+# Terminal I/O Subsystem
+termios_path = ['./terminal/', './terminal/freebsd/']
+for item in termios_path:
+    src += Glob(item + '*.c')
+CPPPATH += ['./terminal/']
+
 group = DefineGroup('lwP', src, depend = ['RT_USING_SMART'], CPPPATH = CPPPATH)
 
 Return('group')

+ 57 - 3
components/lwp/arch/aarch64/cortex-a/lwp_arch.c

@@ -8,6 +8,7 @@
  * 2021-05-18     Jesven       first version
  * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
  * 2023-10-16     Shell        Support a new backtrace framework
+ * 2023-08-03     Shell        Support of syscall restart (SA_RESTART)
  */
 
 #include <armv8.h>
@@ -123,6 +124,7 @@ int arch_set_thread_context(void (*exit)(void), void *new_thread_stack,
 
 #define ALGIN_BYTES (16)
 
+/* the layout is part of ABI, dont change it */
 struct signal_ucontext
 {
     rt_int64_t sigreturn;
@@ -130,11 +132,62 @@ struct signal_ucontext
 
     siginfo_t si;
 
-    rt_align(16)
+    rt_align(ALGIN_BYTES)
     struct rt_hw_exp_stack frame;
 };
 
-void *arch_signal_ucontext_restore(rt_base_t user_sp)
+RT_STATIC_ASSERT(abi_offset_compatible, offsetof(struct signal_ucontext, si) == UCTX_ABI_OFFSET_TO_SI);
+
+void *arch_signal_ucontext_get_frame(struct signal_ucontext *uctx)
+{
+    return &uctx->frame;
+}
+
+/* internal used only */
+void arch_syscall_prepare_signal(rt_base_t rc, struct rt_hw_exp_stack *exp_frame)
+{
+    long x0 = exp_frame->x0;
+    exp_frame->x0 = rc;
+    exp_frame->x7 = x0;
+    return ;
+}
+
+void arch_syscall_restart(void *sp, void *ksp);
+
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+    struct rt_hw_exp_stack *exp_frame = eframe;
+    if (exp_frame->x0 == -expected)
+        exp_frame->x0 = -code;
+    return ;
+}
+
+void arch_signal_check_erestart(void *eframe, void *ksp)
+{
+    struct rt_hw_exp_stack *exp_frame = eframe;
+    long rc = exp_frame->x0;
+    long sys_id = exp_frame->x8;
+    (void)sys_id;
+
+    if (rc == -ERESTART)
+    {
+        LOG_D("%s(rc=%ld,sys_id=%ld,pid=%d)", __func__, rc, sys_id, lwp_self()->pid);
+        LOG_D("%s: restart rc = %ld", lwp_get_syscall_name(sys_id), rc);
+        exp_frame->x0 = exp_frame->x7;
+        arch_syscall_restart(eframe, ksp);
+    }
+
+    return ;
+}
+
+static void arch_signal_post_action(struct signal_ucontext *new_sp, rt_base_t kernel_sp)
+{
+    arch_signal_check_erestart(&new_sp->frame, (void *)kernel_sp);
+
+    return ;
+}
+
+void *arch_signal_ucontext_restore(rt_base_t user_sp, rt_base_t kernel_sp)
 {
     struct signal_ucontext *new_sp;
     new_sp = (void *)user_sp;
@@ -142,6 +195,7 @@ void *arch_signal_ucontext_restore(rt_base_t user_sp)
     if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
     {
         lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &new_sp->save_sigmask, RT_NULL);
+        arch_signal_post_action(new_sp, kernel_sp);
     }
     else
     {
@@ -157,7 +211,7 @@ void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
                                 lwp_sigset_t *save_sig_mask)
 {
     struct signal_ucontext *new_sp;
-    new_sp = (void *)(user_sp - sizeof(struct signal_ucontext));
+    new_sp = (void *)((user_sp - sizeof(struct signal_ucontext)) & ~0xf);
 
     if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
     {

+ 14 - 5
components/lwp/arch/aarch64/cortex-a/lwp_arch.h

@@ -11,8 +11,7 @@
 #ifndef  LWP_ARCH_H__
 #define  LWP_ARCH_H__
 
-#include <lwp.h>
-#include <lwp_arch_comm.h>
+#include <rtconfig.h>
 
 #ifdef ARCH_MM_MMU
 
@@ -26,6 +25,13 @@
 #define USER_VADDR_START  0x00200000UL
 #define USER_LOAD_VADDR   USER_VADDR_START
 
+#define UCTX_ABI_OFFSET_TO_SI 16
+
+#ifndef __ASSEMBLY__
+
+#include <lwp.h>
+#include <lwp_arch_comm.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -34,7 +40,7 @@ unsigned long rt_hw_ffz(unsigned long x);
 
 rt_inline void icache_invalid_all(void)
 {
-    asm volatile ("ic ialluis\n\tisb sy":::"memory");
+    __asm__ volatile ("ic ialluis\n\tisb sy":::"memory");
 }
 
 /**
@@ -57,11 +63,14 @@ void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
  * @param user_sp sp of user
  * @return void*
  */
-void *arch_signal_ucontext_restore(rt_base_t user_sp);
+void *arch_signal_ucontext_restore(rt_base_t user_sp, rt_base_t kernel_sp);
+void arch_syscall_restart(void *sp, void *ksp);
+
 #ifdef __cplusplus
 }
 #endif
+#endif /* __ASSEMBLY__ */
 
-#endif
+#endif /* ARCH_MM_MMU */
 
 #endif  /*LWP_ARCH_H__*/

+ 67 - 3
components/lwp/arch/aarch64/cortex-a/lwp_gcc.S

@@ -7,6 +7,7 @@
  * Date           Author       Notes
  * 2021-05-18     Jesven       first version
  * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
+ * 2023-08-03     Shell        Support of syscall restart (SA_RESTART)
  */
 
 #ifndef __ASSEMBLY__
@@ -17,6 +18,7 @@
 #include "asm-generic.h"
 #include "asm-fpu.h"
 #include "armv8.h"
+#include "lwp_arch.h"
 
 /*********************
  *      SPSR BIT     *
@@ -155,6 +157,14 @@ START_POINT_END(SVC_Handler)
 
 .global arch_syscall_exit
 arch_syscall_exit:
+
+    /**
+     * @brief back up former x0 which is required to restart syscall, then setup
+     * syscall return value in stack frame
+     */
+    mov x1, sp
+    bl arch_syscall_prepare_signal
+
     msr daifset, #3
 
     ldp x2, x3, [sp], #0x10  /* SPSR and ELR. */
@@ -177,7 +187,10 @@ arch_syscall_exit:
     ldp x12, x13, [sp], #0x10
     ldp x10, x11, [sp], #0x10
     ldp x8, x9, [sp], #0x10
-    add sp, sp, #0x40
+    ldp x6, x7, [sp], #0x10
+    ldp x4, x5, [sp], #0x10
+    ldp x2, x3, [sp], #0x10
+    ldp x0, x1, [sp], #0x10
     RESTORE_FPU sp
 
 /* the sp is reset to the outer most level, irq and fiq are disabled */
@@ -383,10 +396,53 @@ lwp_check_debug_quit:
     ldp x29, x30, [sp], #0x10
     ret
 
+.global arch_syscall_restart
+arch_syscall_restart:
+    msr daifset, 3
+
+    mov sp, x1
+    /* drop exception frame in user stack */
+    msr sp_el0, x0
+
+    /* restore previous exception frame */
+    msr spsel, #0
+
+    ldp x2, x3, [sp], #0x10
+    msr elr_el1, x2
+    msr spsr_el1, x3
+
+    ldp x29, x30, [sp], #0x10
+
+    ldp x28, x29, [sp], #0x10
+    msr fpcr, x28
+    msr fpsr, x29
+
+    ldp x28, x29, [sp], #0x10
+    ldp x26, x27, [sp], #0x10
+    ldp x24, x25, [sp], #0x10
+    ldp x22, x23, [sp], #0x10
+    ldp x20, x21, [sp], #0x10
+    ldp x18, x19, [sp], #0x10
+    ldp x16, x17, [sp], #0x10
+    ldp x14, x15, [sp], #0x10
+    ldp x12, x13, [sp], #0x10
+    ldp x10, x11, [sp], #0x10
+    ldp x8, x9, [sp], #0x10
+    ldp x6, x7, [sp], #0x10
+    ldp x4, x5, [sp], #0x10
+    ldp x2, x3, [sp], #0x10
+    ldp x0, x1, [sp], #0x10
+    RESTORE_FPU sp
+
+    msr spsel, #1
+
+    b vector_exception
+
 arch_signal_quit:
 
     /* drop current exception frame */
     add sp, sp, #CONTEXT_SIZE
+    mov x1, sp
     mrs x0, sp_el0
     bl  arch_signal_ucontext_restore
     add x0, x0, #-CONTEXT_SIZE
@@ -456,6 +512,11 @@ arch_thread_signal_enter:
 
     /* arch_signal_ucontext_save(user_sp, psiginfo, exp_frame, save_sig_mask); */
     bl arch_signal_ucontext_save
+    mov x22, x0
+    /* get and saved pointer to uframe */
+    bl arch_signal_ucontext_get_frame
+    mov x2, x0
+    mov x0, x22
 
     dc cvau, x0
     dsb sy
@@ -483,12 +544,15 @@ arch_thread_signal_enter:
     /** set the return address to the sigreturn */
     mov x30, x0
 
+    cbnz x21, 1f
+    mov x21, x30
+1:
     /** set the entry address of signal handler */
     msr elr_el1, x21
 
     /* siginfo is above the return address */
-    add x2, x30, 16
-    add x1, x2, #CONTEXT_SIZE
+    add x1, x30, UCTX_ABI_OFFSET_TO_SI
+    /* uframe is saved in x2 */
     mov x0, x19
 
     /**

+ 6 - 0
components/lwp/arch/arm/cortex-a/lwp_arch.c

@@ -192,6 +192,12 @@ void *arch_signal_ucontext_save(rt_base_t lr, siginfo_t *psiginfo,
     return new_sp;
 }
 
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+    /* NO support */
+    return ;
+}
+
 #ifdef LWP_ENABLE_ASID
 #define MAX_ASID_BITS 8
 #define MAX_ASID (1 << MAX_ASID_BITS)

+ 1 - 1
components/lwp/arch/arm/cortex-a/lwp_arch.h

@@ -35,7 +35,7 @@ rt_inline unsigned long rt_hw_ffz(unsigned long x)
 
 rt_inline void icache_invalid_all(void)
 {
-    asm volatile ("mcr p15, 0, r0, c7, c5, 0\ndsb\nisb":::"memory");//iciallu
+    __asm__ volatile ("mcr p15, 0, r0, c7, c5, 0\ndsb\nisb":::"memory");//iciallu
 }
 
 unsigned int arch_get_asid(struct rt_lwp *lwp);

+ 7 - 0
components/lwp/arch/risc-v/rv64/lwp_arch.c

@@ -242,6 +242,7 @@ int arch_set_thread_context(void (*exit)(void), void *new_thread_stack,
      * |                        |
      * +------------------------+ --> thread sp
      */
+    return 0;
 }
 
 #define ALGIN_BYTES (16)
@@ -317,6 +318,12 @@ void *arch_signal_ucontext_save(int signo, siginfo_t *psiginfo,
     return new_sp;
 }
 
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+    /* NO support */
+    return ;
+}
+
 /**
  * void lwp_exec_user(void *args, void *kernel_stack, void *user_entry)
  */

+ 3 - 3
components/lwp/libc_musl.h

@@ -28,9 +28,9 @@
 
 #define FUTEX_CLOCK_REALTIME 256
 
-#define FUTEX_WAITERS    0x80000000
-#define FUTEX_OWNER_DIED 0x40000000
-#define FUTEX_TID_MASK   0x3fffffff
+#define FUTEX_WAITERS		0x80000000
+#define FUTEX_OWNER_DIED	0x40000000
+#define FUTEX_TID_MASK		0x3fffffff
 
 struct robust_list
 {

+ 170 - 98
components/lwp/lwp.c

@@ -12,6 +12,8 @@
  * 2023-02-20     wangxiaoyao  inv icache before new app startup
  * 2023-02-20     wangxiaoyao  fix bug on foreground app switch
  * 2023-10-16     Shell        Support a new backtrace framework
+ * 2023-11-17     xqyjlj       add process group and session support
+ * 2023-11-30     Shell        add lwp_startup()
  */
 
 #define DBG_TAG "lwp"
@@ -39,7 +41,7 @@
 #include "lwp_arch_comm.h"
 #include "lwp_signal.h"
 #include "lwp_dbg.h"
-#include "console.h"
+#include <terminal/terminal.h>
 
 #ifdef ARCH_MM_MMU
 #include <lwp_user_mm.h>
@@ -59,16 +61,31 @@ static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
 #ifdef DFS_USING_WORKDIR
 extern char working_directory[];
 #endif
-static struct termios stdin_termios, old_stdin_termios;
 
-int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]);
-
-struct termios *get_old_termios(void)
+/**
+ * @brief The default console is only a backup device with lowest priority.
+ *        It's always recommended to scratch the console from the boot arguments.
+ *        And dont forget to register the device with a higher priority.
+ */
+static rt_err_t lwp_default_console_setup(void)
 {
-    return &old_stdin_termios;
+    rt_device_t bakdev = rt_device_find("ttyS0");
+    rt_err_t rc;
+
+    if (bakdev)
+    {
+        lwp_console_register_backend(bakdev, LWP_CONSOLE_LOWEST_PRIOR);
+        rc = RT_EOK;
+    }
+    else
+    {
+        rc = -RT_EINVAL;
+    }
+
+    return rc;
 }
 
-int lwp_component_init(void)
+static int lwp_component_init(void)
 {
     int rc;
     if ((rc = lwp_tid_init()) != RT_EOK)
@@ -83,10 +100,99 @@ int lwp_component_init(void)
     {
         LOG_E("%s: rt_channel_component_init failed", __func__);
     }
+    else if ((rc = lwp_futex_init()) != RT_EOK)
+    {
+        LOG_E("%s: lwp_futex_init() failed", __func__);
+    }
+    else if ((rc = lwp_default_console_setup()) != RT_EOK)
+    {
+        LOG_E("%s: lwp_default_console_setup() failed", __func__);
+    }
     return rc;
 }
 INIT_COMPONENT_EXPORT(lwp_component_init);
 
+rt_weak int lwp_startup_debug_request(void)
+{
+    return 0;
+}
+
+#define LATENCY_TIMES (3)
+#define LATENCY_IN_MSEC (128)
+#define LWP_CONSOLE_PATH "CONSOLE=/dev/console"
+const char *init_search_path[] = {
+    "/sbin/init",
+    "/bin/init",
+};
+
+/**
+ * Startup process 0 and do the essential works
+ * This is the "Hello World" point of RT-Smart
+ */
+static int lwp_startup(void)
+{
+    int error;
+
+    const char *init_path;
+    char *argv[] = {0, "&"};
+    char *envp[] = {LWP_CONSOLE_PATH, 0};
+
+#ifdef LWP_DEBUG
+    int command;
+    int countdown = LATENCY_TIMES;
+    while (countdown)
+    {
+        command = lwp_startup_debug_request();
+        if (command)
+        {
+            return 0;
+        }
+        rt_kprintf("Press any key to stop init process startup ... %d\n", countdown);
+        countdown -= 1;
+        rt_thread_mdelay(LATENCY_IN_MSEC);
+    }
+    rt_kprintf("Starting init ...\n");
+#endif
+
+    for (size_t i = 0; i < sizeof(init_search_path)/sizeof(init_search_path[0]); i++)
+    {
+        struct stat s;
+        init_path = init_search_path[i];
+        error = stat(init_path, &s);
+        if (error == 0)
+        {
+            argv[0] = (void *)init_path;
+            error = lwp_execve((void *)init_path, 0, sizeof(argv)/sizeof(argv[0]), argv, envp);
+            if (error < 0)
+            {
+                LOG_E("%s: failed to startup process 0 (init)\n"
+                    "Switching to legacy mode...", __func__);
+            }
+            else if (error != 1)
+            {
+                LOG_E("%s: pid 1 is already allocated", __func__);
+                error = -EBUSY;
+            }
+            else
+            {
+                rt_lwp_t p = lwp_from_pid_locked(1);
+                p->sig_protected = 0;
+
+                error = 0;
+            }
+            break;
+        }
+    }
+
+    if (error)
+    {
+        LOG_E("%s: init program not found\n"
+            "Switching to legacy mode...", __func__);
+    }
+    return error;
+}
+INIT_APP_EXPORT(lwp_startup);
+
 void lwp_setcwd(char *buf)
 {
     struct rt_lwp *lwp = RT_NULL;
@@ -100,11 +206,11 @@ void lwp_setcwd(char *buf)
     lwp = (struct rt_lwp *)rt_thread_self()->lwp;
     if (lwp)
     {
-        rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX);
+        rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX - 1);
     }
     else
     {
-        rt_strncpy(working_directory, buf, DFS_PATH_MAX);
+        rt_strncpy(working_directory, buf, DFS_PATH_MAX - 1);
     }
 
     return ;
@@ -114,8 +220,13 @@ char *lwp_getcwd(void)
 {
     char *dir_buf = RT_NULL;
     struct rt_lwp *lwp = RT_NULL;
+    rt_thread_t thread = rt_thread_self();
+
+    if (thread)
+    {
+        lwp = (struct rt_lwp *)thread->lwp;
+    }
 
-    lwp = (struct rt_lwp *)rt_thread_self()->lwp;
     if (lwp)
     {
         if(lwp->working_directory[0] != '/')
@@ -1077,25 +1188,35 @@ void lwp_cleanup(struct rt_thread *tid)
     return;
 }
 
-static void lwp_copy_stdio_fdt(struct rt_lwp *lwp)
+static void lwp_execve_setup_stdio(struct rt_lwp *lwp)
 {
-    struct dfs_file *d;
     struct dfs_fdtable *lwp_fdt;
+    struct dfs_file *cons_file;
+    int cons_fd;
 
     lwp_fdt = &lwp->fdt;
+
+    /* open console */
+    cons_fd = open("/dev/console", O_RDWR);
+    if (cons_fd < 0)
+    {
+        LOG_E("%s: Cannot open console tty", __func__);
+        return ;
+    }
+    LOG_D("%s: open console as fd %d", __func__, cons_fd);
+
     /* init 4 fds */
     lwp_fdt->fds = rt_calloc(4, sizeof(void *));
     if (lwp_fdt->fds)
     {
+        cons_file = fd_get(cons_fd);
         lwp_fdt->maxfd = 4;
-        d = fd_get(0);
-        fdt_fd_associate_file(lwp_fdt, 0, d);
-        d = fd_get(1);
-        fdt_fd_associate_file(lwp_fdt, 1, d);
-        d = fd_get(2);
-        fdt_fd_associate_file(lwp_fdt, 2, d);
+        fdt_fd_associate_file(lwp_fdt, 0, cons_file);
+        fdt_fd_associate_file(lwp_fdt, 1, cons_file);
+        fdt_fd_associate_file(lwp_fdt, 2, cons_file);
     }
 
+    close(cons_fd);
     return;
 }
 
@@ -1197,11 +1318,8 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
     int result;
     struct rt_lwp *lwp;
     char *thread_name;
-    char *argv_last = argv[argc - 1];
-    int bg = 0;
     struct process_aux *aux;
     int tid = 0;
-    int ret;
 
     if (filename == RT_NULL)
     {
@@ -1213,7 +1331,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
         return -EACCES;
     }
 
-    lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
+    lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID | LWP_CREATE_FLAG_NOTRACE_EXEC);
 
     if (lwp == RT_NULL)
     {
@@ -1236,12 +1354,6 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
     }
 #endif
 
-    if (argv_last[0] == '&' && argv_last[1] == '\0')
-    {
-        argc--;
-        bg = 1;
-    }
-
     if ((aux = lwp_argscopy(lwp, argc, argv, envp)) == RT_NULL)
     {
         lwp_tid_put(tid);
@@ -1263,7 +1375,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
         rt_thread_t thread = RT_NULL;
         rt_uint32_t priority = 25, tick = 200;
 
-        lwp_copy_stdio_fdt(lwp);
+        lwp_execve_setup_stdio(lwp);
 
         /* obtain the base name */
         thread_name = strrchr(filename, '/');
@@ -1284,88 +1396,46 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
         if (thread != RT_NULL)
         {
             struct rt_lwp *self_lwp;
+            rt_session_t session;
+            rt_processgroup_t group;
 
             thread->tid = tid;
             lwp_tid_set_thread(tid, thread);
             LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_size_t)thread->stack_addr,
                     (rt_size_t)thread->stack_addr + thread->stack_size);
             self_lwp = lwp_self();
+
+            /* when create init, self_lwp == null */
+            if (self_lwp == RT_NULL && lwp_to_pid(lwp) != 1)
+            {
+                self_lwp = lwp_from_pid_and_lock(1);
+            }
+
             if (self_lwp)
             {
-                //lwp->tgroup_leader = &thread; //add thread group leader for lwp
-                lwp->__pgrp = tid;
-                lwp->session = self_lwp->session;
                 /* lwp add to children link */
                 lwp_children_register(self_lwp, lwp);
             }
-            else
-            {
-                //lwp->tgroup_leader = &thread; //add thread group leader for lwp
-                lwp->__pgrp = tid;
-            }
-            if (!bg)
+
+            session = RT_NULL;
+            group = RT_NULL;
+
+            group = lwp_pgrp_create(lwp);
+            if (group)
             {
-                if (lwp->session == -1)
+                lwp_pgrp_insert(group, lwp);
+                if (self_lwp == RT_NULL)
                 {
-                    struct tty_struct *tty = RT_NULL;
-                    struct rt_lwp *old_lwp;
-                    tty = (struct tty_struct *)console_tty_get();
-                    old_lwp = tty->foreground;
-                    if (old_lwp)
-                    {
-                        rt_mutex_take(&tty->lock, RT_WAITING_FOREVER);
-                        ret = tty_push(&tty->head, old_lwp);
-                        rt_mutex_release(&tty->lock);
-
-                        if (ret < 0)
-                        {
-                            lwp_tid_put(tid);
-                            lwp_ref_dec(lwp);
-                            LOG_E("malloc fail!\n");
-                            return -ENOMEM;
-                        }
-                    }
-
-                    lwp->tty = tty;
-                    lwp->tty->pgrp = lwp->__pgrp;
-                    lwp->tty->session = lwp->session;
-                    lwp->tty->foreground = lwp;
-                    tcgetattr(1, &stdin_termios);
-                    old_stdin_termios = stdin_termios;
-                    stdin_termios.c_lflag |= ICANON | ECHO | ECHOCTL;
-                    tcsetattr(1, 0, &stdin_termios);
+                    session = lwp_session_create(lwp);
+                    lwp_session_insert(session, group);
                 }
                 else
                 {
-                    if (self_lwp != RT_NULL)
-                    {
-                        rt_mutex_take(&self_lwp->tty->lock, RT_WAITING_FOREVER);
-                        ret = tty_push(&self_lwp->tty->head, self_lwp);
-                        rt_mutex_release(&self_lwp->tty->lock);
-                        if (ret < 0)
-                        {
-                            lwp_tid_put(tid);
-                            lwp_ref_dec(lwp);
-                            LOG_E("malloc fail!\n");
-                            return -ENOMEM;
-                        }
-
-                        lwp->tty = self_lwp->tty;
-                        lwp->tty->pgrp = lwp->__pgrp;
-                        lwp->tty->session = lwp->session;
-                        lwp->tty->foreground = lwp;
-                    }
-                    else
-                    {
-                        lwp->tty = RT_NULL;
-                    }
-
+                    session = lwp_session_find(lwp_sid_get_byprocess(self_lwp));
+                    lwp_session_insert(session, group);
                 }
             }
-            else
-            {
-                lwp->background = RT_TRUE;
-            }
+
             thread->lwp = lwp;
 #ifndef ARCH_MM_MMU
             struct lwp_app_head *app_head = (struct lwp_app_head*)lwp->text_entry;
@@ -1381,6 +1451,8 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
 #endif /* not defined ARCH_MM_MMU */
             rt_list_insert_after(&lwp->t_grp, &thread->sibling);
 
+            lwp->did_exec = RT_TRUE;
+
             if (debug && rt_dbg_ops)
             {
                 lwp->debug = debug;
@@ -1482,30 +1554,30 @@ rt_err_t lwp_backtrace_frame(rt_thread_t uthread, struct rt_hw_backtrace_frame *
     char **argv;
     rt_lwp_t lwp;
 
-    if (uthread->lwp)
+    if (uthread && uthread->lwp && rt_scheduler_is_available())
     {
         lwp = uthread->lwp;
         argv = lwp_get_command_line_args(lwp);
         if (argv)
         {
-            LOG_RAW("please use: addr2line -e %s -a -f", argv[0]);
+            rt_kprintf("please use: addr2line -e %s -a -f", argv[0]);
             lwp_free_command_line_args(argv);
         }
         else
         {
-            LOG_RAW("please use: addr2line -e %s -a -f", lwp->cmd);
+            rt_kprintf("please use: addr2line -e %s -a -f", lwp->cmd);
         }
 
         while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
         {
-            LOG_RAW(" 0x%lx", frame->pc);
+            rt_kprintf(" 0x%lx", frame->pc);
             if (rt_hw_backtrace_frame_unwind(uthread, frame))
             {
                 break;
             }
             nesting++;
         }
-        LOG_RAW("\n");
+        rt_kprintf("\n");
         rc = RT_EOK;
     }
     return rc;

+ 138 - 21
components/lwp/lwp.h

@@ -9,6 +9,9 @@
  * 2019-10-12     Jesven       Add MMU and userspace support
  * 2020-10-08     Bernard      Architecture and code cleanup
  * 2021-08-26     linzhenxing  add lwp_setcwd\lwp_getcwd
+ * 2023-11-17     xqyjlj       add process group and session support
+ * 2023-12-02     Shell        Add macro to create lwp status and
+ *                             fix dead lock problem on pgrp
  */
 
 /*
@@ -48,10 +51,6 @@
 #include <locale.h>
 #endif /* RT_USING_MUSLLIBC */
 
-#ifdef  RT_USING_TTY
-struct tty_struct;
-#endif /* RT_USING_TTY */
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -76,12 +75,48 @@ struct rt_lwp_notify
     rt_slist_t list_node;
 };
 
+struct lwp_tty;
+
 #ifdef RT_USING_MUSLLIBC
-#define LWP_CREATE_STAT(exit_code) (((exit_code) & 0xff) << 8)
+#define LWP_COREDUMP_FLAG                         0x80
+#define LWP_CREATE_STAT_EXIT(exit_code)           (((exit_code)&0xff) << 8)
+#define LWP_CREATE_STAT_SIGNALED(signo, coredump) (((signo) & 0x7f) | (coredump ? LWP_COREDUMP_FLAG : 0))
+#define LWP_CREATE_STAT_STOPPED(signo)            (LWP_CREATE_STAT_EXIT(signo) | 0x7f)
+#define LWP_CREATE_STAT_CONTINUED                 (0xffff)
 #else
 #error "No compatible lwp set status provided for this libc"
 #endif
 
+typedef struct rt_lwp *rt_lwp_t;
+typedef struct rt_session *rt_session_t;
+typedef struct rt_processgroup *rt_processgroup_t;
+
+struct rt_session {
+    struct rt_object    object;
+    rt_lwp_t            leader;
+    rt_list_t           processgroup;
+    pid_t               sid;
+    pid_t               foreground_pgid;
+    struct rt_mutex     mutex;
+    struct lwp_tty      *ctty;
+};
+
+struct rt_processgroup {
+    struct rt_object    object;
+    rt_lwp_t            leader;
+    rt_list_t           process;
+    rt_list_t           pgrp_list_node;
+    pid_t               pgid;
+    pid_t               sid;
+    struct rt_session   *session;
+    struct rt_mutex     mutex;
+
+    rt_atomic_t         ref;
+
+    /* flags on process group */
+    unsigned int is_orphaned:1;
+};
+
 struct rt_lwp
 {
 #ifdef ARCH_MM_MMU
@@ -100,14 +135,21 @@ struct rt_lwp
     uint8_t lwp_type;
     uint8_t reserv[3];
 
-    struct rt_lwp *parent;
-    struct rt_lwp *first_child;
-    struct rt_lwp *sibling;
+    /* flags */
+    unsigned int terminated:1;
+    unsigned int background:1;
+    unsigned int term_ctrlterm:1;  /* have control terminal? */
+    unsigned int did_exec:1;       /* Whether exec has been performed */
+    unsigned int jobctl_stopped:1; /* job control: current proc is stopped */
+    unsigned int wait_reap_stp:1;  /* job control: has wait event for parent */
+    unsigned int sig_protected:1;  /* signal: protected proc cannot be killed or stopped */
+
+    struct rt_lwp *parent;          /* parent process */
+    struct rt_lwp *first_child;     /* first child process */
+    struct rt_lwp *sibling;         /* sibling(child) process */
 
-    rt_list_t wait_list;
-    rt_bool_t terminated;
-    rt_bool_t background;
-    int lwp_ret;
+    struct rt_wqueue waitpid_waiters;
+    lwp_status_t lwp_status;
 
     void *text_entry;
     uint32_t text_size;
@@ -118,15 +160,16 @@ struct rt_lwp
     void *args;
     uint32_t args_length;
     pid_t pid;
-    pid_t __pgrp; /*Accessed via process_group()*/
-    pid_t tty_old_pgrp;
-    pid_t session;
-    rt_list_t t_grp;
-    rt_list_t timer; /* POSIX timer object binding to a process */
+    pid_t sid;                      /* session ID */
+    pid_t pgid;                     /* process group ID */
+    struct rt_processgroup *pgrp;
+    rt_list_t pgrp_node;            /* process group node */
+    rt_list_t t_grp;                /* thread group */
+    rt_list_t timer;                /* POSIX timer object binding to a process */
 
-    int leader; /* boolean value for session group_leader*/
     struct dfs_fdtable fdt;
     char cmd[RT_NAME_MAX];
+    char *exe_file;                 /* process file path */
 
     /* POSIX signal */
     struct lwp_signal signal;
@@ -135,7 +178,7 @@ struct rt_lwp
     struct rt_mutex object_mutex;
     struct rt_user_context user_ctx;
 
-    struct rt_wqueue wait_queue; /*for console */
+    struct rt_wqueue wait_queue; /* for console */
     struct tty_struct *tty; /* NULL if no tty */
 
     struct lwp_avl_struct *address_search_head; /* for addressed object fast search */
@@ -152,8 +195,9 @@ struct rt_lwp
     uint64_t generation;
     unsigned int asid;
 #endif
+    struct rusage rt_rusage;
 };
-typedef struct rt_lwp *rt_lwp_t;
+
 
 struct rt_lwp *lwp_self(void);
 rt_err_t lwp_children_register(struct rt_lwp *parent, struct rt_lwp *child);
@@ -182,7 +226,7 @@ void lwp_tid_put(int tid);
  * @return rt_thread_t
  */
 rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid);
-
+rt_thread_t lwp_tid_get_thread_raw(int tid);
 /**
  * @brief Decrease a reference count
  *
@@ -216,6 +260,78 @@ rt_err_t lwp_futex_init(void);
 rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val,
                    const struct timespec *timeout, int *uaddr2, int val3);
 
+/* processgroup api */
+rt_inline pid_t lwp_pgid_get_bypgrp(rt_processgroup_t group)
+{
+    return group ? group->pgid : 0;
+}
+
+rt_inline pid_t lwp_pgid_get_byprocess(rt_lwp_t process)
+{
+    return process ? process->pgid : 0;
+}
+rt_processgroup_t lwp_pgrp_find(pid_t pgid);
+void lwp_pgrp_dec_ref(rt_processgroup_t pgrp);
+rt_processgroup_t lwp_pgrp_find_and_inc_ref(pid_t pgid);
+rt_processgroup_t lwp_pgrp_create(rt_lwp_t leader);
+int lwp_pgrp_delete(rt_processgroup_t group);
+
+/**
+ * Note: all the pgrp with process operation must be called in the context where
+ * process lock is taken. This is protect us from a possible dead lock condition
+ *
+ * The order is mandatory in the case:
+ *      PGRP_LOCK(pgrp);
+ *      LWP_LOCK(p);
+ *      ... bussiness logic
+ *      LWP_UNLOCK(p);
+ *      PGRP_UNLOCK(pgrp);
+ */
+int lwp_pgrp_insert(rt_processgroup_t group, rt_lwp_t process);
+int lwp_pgrp_remove(rt_processgroup_t group, rt_lwp_t process);
+int lwp_pgrp_move(rt_processgroup_t group, rt_lwp_t process);
+
+int lwp_pgrp_update_children_info(rt_processgroup_t group, pid_t sid, pid_t pgid);
+
+/* session api */
+rt_inline pid_t lwp_sid_get_bysession(rt_session_t session)
+{
+    return session ? session->sid : 0;
+}
+
+rt_inline pid_t lwp_sid_get_bypgrp(rt_processgroup_t group)
+{
+    return group ? group->sid : 0;
+}
+
+rt_inline pid_t lwp_sid_get_byprocess(rt_lwp_t process)
+{
+    return process ? process->sid : 0;
+}
+
+rt_session_t lwp_session_find(pid_t sid);
+rt_session_t lwp_session_create(struct rt_lwp *leader);
+int lwp_session_delete(rt_session_t session);
+
+/**
+ * Note: all the session operation must be called in the context where
+ * process lock is taken. This is protect us from a possible dead lock condition
+ *
+ * The order is mandatory in the case:
+ *      PGRP_LOCK(pgrp);
+ *      LWP_LOCK(p);
+ *      ... bussiness logic
+ *      LWP_UNLOCK(p);
+ *      PGRP_UNLOCK(pgrp);
+ */
+int lwp_session_insert(rt_session_t session, rt_processgroup_t group);
+int lwp_session_remove(rt_session_t session, rt_processgroup_t group);
+int lwp_session_move(rt_session_t session, rt_processgroup_t group);
+int lwp_session_update_children_info(rt_session_t session, pid_t sid);
+int lwp_session_set_foreground(rt_session_t session, pid_t pgid);
+
+/* complete the job control related bussiness on process exit */
+void lwp_jobctrl_on_exit(struct rt_lwp *lwp);
 
 #ifdef __cplusplus
 }
@@ -301,6 +417,7 @@ int dbg_step_type(void);
 void dbg_attach_req(void *pc);
 int dbg_check_suspend(void);
 void rt_hw_set_process_id(int pid);
+void lwp_futex_exit_robust_list(rt_thread_t thread);
 
 /* backtrace service */
 rt_err_t lwp_backtrace_frame(rt_thread_t uthread, struct rt_hw_backtrace_frame *frame);

+ 4 - 0
components/lwp/lwp_arch_comm.h

@@ -61,6 +61,10 @@ rt_noreturn void arch_thread_signal_enter(int signo, siginfo_t *psiginfo,
                                           void *exp_frame, void *entry_uaddr,
                                           lwp_sigset_t *save_sig_mask);
 
+void arch_signal_check_erestart(void *eframe, void *ksp);
+
+void arch_syscall_set_errno(void *eframe, int expected, int code);
+
 int arch_backtrace_uthread(rt_thread_t thread);
 
 #endif /* __LWP_ARCH_COMM__ */

+ 1 - 0
components/lwp/lwp_elf.c

@@ -797,6 +797,7 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_
 
     /* copy file name to process name */
     rt_strncpy(lwp->cmd, filename, RT_NAME_MAX);
+    lwp->exe_file = dfs_normalize_path(NULL, filename); // malloc
 
     ret = elf_file_load(&load_info);
     if (ret != RT_EOK)

+ 15 - 14
components/lwp/lwp_futex.c

@@ -475,6 +475,11 @@ static long _futex_wake(rt_futex_t futex, struct rt_lwp *lwp, int number,
         {
             number--;
             woken_cnt++;
+            is_empty = RT_FALSE;
+        }
+        else
+        {
+            is_empty = RT_TRUE;
         }
         _futex_unlock(lwp, op_flags);
     }
@@ -512,23 +517,16 @@ static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
      */
     while (nr_wake && !is_empty)
     {
-        rt_sched_lock_level_t slvl;
-        rt_sched_lock(&slvl);
-        is_empty = rt_list_isempty(&(futex1->waiting_thread));
-        if (!is_empty)
+        if (rt_susp_list_dequeue(&futex1->waiting_thread, RT_EOK))
         {
-            thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
-            /* remove from waiting list */
-            rt_list_remove(&RT_THREAD_LIST_NODE(thread));
-
-            thread->error = RT_EOK;
-            /* resume the suspended thread */
-            rt_thread_resume(thread);
-
             nr_wake--;
             woken_cnt++;
+            is_empty = RT_FALSE;
+        }
+        else
+        {
+            is_empty = RT_TRUE;
         }
-        rt_sched_unlock(slvl);
     }
     rtn = woken_cnt;
 
@@ -542,7 +540,10 @@ static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
     {
         rt_sched_lock_level_t slvl;
         rt_sched_lock(&slvl);
+
+        /* moving from one susp list to another */
         is_empty = rt_list_isempty(&(futex1->waiting_thread));
+
         if (!is_empty)
         {
             thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
@@ -914,7 +915,7 @@ void lwp_futex_exit_robust_list(rt_thread_t thread)
         rc = _fetch_robust_entry(&next_entry, &entry->next, &next_pi);
         if (entry != pending)
         {
-            if (_handle_futex_death((void *)entry + futex_offset, thread, pi,
+            if (_handle_futex_death((int *)((size_t)entry + futex_offset), thread, pi,
                                     RT_FALSE))
                 return;
         }

+ 56 - 13
components/lwp/lwp_internal.c

@@ -6,6 +6,7 @@
  * Change Logs:
  * Date           Author       Notes
  * 2023-07-25     Shell        first version
+ * 2023-11-25     Shell        Add pgrp, session lock API
  */
 
 #define DBG_TAG "lwp.internal"
@@ -15,7 +16,7 @@
 #include <stdlib.h>
 #include "lwp_internal.h"
 
-static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable)
+static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags)
 {
     LWP_DEF_RETURN_CODE(rc);
     int retry;
@@ -45,15 +46,15 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
 
         do {
             retry = 0;
-            if (interruptable)
+            if (flags & LWP_MTX_FLAGS_INTR)
                 rc = rt_mutex_take_interruptible(mtx, effect_timeout);
             else
-                rc = rt_mutex_take(mtx, effect_timeout);
+                rc = rt_mutex_take_killable(mtx, effect_timeout);
 
 #ifdef LWP_DEBUG
             if (rc == RT_EOK)
             {
-                if (rt_mutex_get_hold(mtx) > 1)
+                if (!(flags & LWP_MTX_FALGS_NESTED) && rt_mutex_get_hold(mtx) > 1)
                 {
                     LOG_W("Already hold the lock");
                     rt_backtrace();
@@ -88,6 +89,7 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
     }
     else
     {
+        rc = -RT_EINVAL;
         LOG_W("%s: mtx should not be NULL", __func__);
         RT_ASSERT(0);
     }
@@ -95,10 +97,10 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
     LWP_RETURN(rc);
 }
 
-rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable)
+rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags)
 {
     LWP_DEF_RETURN_CODE(rc);
-    rc = _mutex_take_safe(mtx, timeout, interruptable);
+    rc = _mutex_take_safe(mtx, timeout, flags);
     LWP_RETURN(rc);
 }
 
@@ -116,18 +118,17 @@ rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx)
     LWP_RETURN(rc);
 }
 
-rt_err_t lwp_critical_enter(struct rt_lwp *lwp)
+rt_err_t lwp_critical_enter(struct rt_lwp *lwp, int flags)
 {
     rt_err_t rc;
-    rc = lwp_mutex_take_safe(&lwp->lwp_lock, RT_WAITING_FOREVER, 0);
+    do {
+        rc = lwp_mutex_take_safe(&lwp->lwp_lock, RT_WAITING_FOREVER, flags);
+    } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
 
     /* if current process is force killed */
-    if (rc != RT_EOK)
+    if (rc != RT_EOK && rc != -RT_EINTR)
     {
-        if (rc == -RT_EINTR && lwp_self() != RT_NULL)
-            sys_exit(EXIT_SUCCESS);
-        else
-            LOG_I("%s: unexpected return code = %ld", __func__, rc);
+        LOG_I("%s: unexpected return code = %ld", __func__, rc);
     }
 
     return rc;
@@ -137,3 +138,45 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp)
 {
     return lwp_mutex_release_safe(&lwp->lwp_lock);
 }
+
+rt_err_t lwp_pgrp_critical_enter(struct rt_processgroup *pgrp, int flags)
+{
+    rt_err_t rc;
+    do {
+        rc = lwp_mutex_take_safe(&pgrp->mutex, RT_WAITING_FOREVER, flags);
+    } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
+
+    /* if current process is force killed */
+    if (rc != RT_EOK && rc != -RT_EINTR)
+    {
+        LOG_I("%s: unexpected return code = %ld", __func__, rc);
+    }
+
+    return rc;
+}
+
+rt_err_t lwp_pgrp_critical_exit(struct rt_processgroup *pgrp)
+{
+    return lwp_mutex_release_safe(&pgrp->mutex);
+}
+
+rt_err_t lwp_sess_critical_enter(struct rt_session *sess, int flags)
+{
+    rt_err_t rc;
+    do {
+        rc = lwp_mutex_take_safe(&sess->mutex, RT_WAITING_FOREVER, flags);
+    } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
+
+    /* if current process is force killed */
+    if (rc != RT_EOK && rc != -RT_EINTR)
+    {
+        LOG_I("%s: unexpected return code = %ld", __func__, rc);
+    }
+
+    return rc;
+}
+
+rt_err_t lwp_sess_critical_exit(struct rt_session *sess)
+{
+    return lwp_mutex_release_safe(&sess->mutex);
+}

+ 102 - 9
components/lwp/lwp_internal.h

@@ -6,21 +6,32 @@
  * Change Logs:
  * Date           Author       Notes
  * 2023-07-25     Shell        first version
+ * 2023-11-25     Shell        Add pgrp, session lock API
  */
 
 #ifndef __LWP_INTERNAL_H__
 #define __LWP_INTERNAL_H__
 
 #include "lwp.h"
+#include "lwp_arch.h"
 #include "lwp_user_mm.h"
+#include "lwp_mm.h"
 
 #include <rtthread.h>
 #include "libc_musl.h"
 
 struct rt_lwp;
-rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable);
+
+#define LWP_MTX_FLAGS_INTR   0x1 /* interruptible waiting */
+#define LWP_MTX_FALGS_NESTED 0x2 /* allow nested */
+rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags);
 rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx);
 
+rt_inline rt_bool_t lwp_in_user_space(const char *addr)
+{
+    return (addr >= (char *)USER_VADDR_START && addr < (char *)USER_VADDR_TOP);
+}
+
 #ifdef RT_USING_SMP
     #define LOCAL_IRQ_MASK() rt_hw_local_irq_disable()
     #define LOCAL_IRQ_UNMASK(level) rt_hw_local_irq_enable(level)
@@ -30,16 +41,34 @@ rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx);
 #endif
 
 #ifndef LWP_USING_CPUS_LOCK
-rt_err_t lwp_critical_enter(struct rt_lwp *lwp);
+rt_err_t lwp_sess_critical_enter(struct rt_session *sess, int flags);
+rt_err_t lwp_sess_critical_exit(struct rt_session *sess);
+rt_err_t lwp_pgrp_critical_enter(struct rt_processgroup *pgrp, int flags);
+rt_err_t lwp_pgrp_critical_exit(struct rt_processgroup *pgrp);
+rt_err_t lwp_critical_enter(struct rt_lwp *lwp, int flags);
 rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
 
-#define LWP_LOCK(lwp)                           \
-    do {                                        \
-        RT_DEBUG_SCHEDULER_AVAILABLE(1);        \
-        if (lwp_critical_enter(lwp) != RT_EOK)  \
-        {                                       \
-            RT_ASSERT(0);                       \
-        }                                       \
+#define LWP_ASSERT_LOCKED(proc) RT_ASSERT(rt_mutex_get_owner(&(proc)->lwp_lock) == rt_thread_self())
+#define PGRP_ASSERT_LOCKED(pgrp) RT_ASSERT(rt_mutex_get_owner(&(pgrp)->mutex) == rt_thread_self())
+
+#define LWP_LOCK(lwp)                             \
+    do                                            \
+    {                                             \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);          \
+        if (lwp_critical_enter(lwp, 0) != RT_EOK) \
+        {                                         \
+            RT_ASSERT(0);                         \
+        }                                         \
+    } while (0)
+
+#define LWP_LOCK_NESTED(lwp)                                         \
+    do                                                               \
+    {                                                                \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);                             \
+        if (lwp_critical_enter(lwp, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+        {                                                            \
+            RT_ASSERT(0);                                            \
+        }                                                            \
     } while (0)
 
 #define LWP_UNLOCK(lwp)                         \
@@ -50,10 +79,72 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
         }                                       \
     } while (0)
 
+#define PGRP_LOCK(pgrp)                                 \
+    do                                                  \
+    {                                                   \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);                \
+        if (lwp_pgrp_critical_enter(pgrp, 0) != RT_EOK) \
+        {                                               \
+            RT_ASSERT(0);                               \
+        }                                               \
+    } while (0)
+
+#define PGRP_LOCK_NESTED(pgrp)                                             \
+    do                                                                     \
+    {                                                                      \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);                                   \
+        if (lwp_pgrp_critical_enter(pgrp, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+        {                                                                  \
+            RT_ASSERT(0);                                                  \
+        }                                                                  \
+    } while (0)
+
+#define PGRP_UNLOCK(pgrp)                           \
+    do                                              \
+    {                                               \
+        if (lwp_pgrp_critical_exit(pgrp) != RT_EOK) \
+        {                                           \
+            RT_ASSERT(0);                           \
+        }                                           \
+    } while (0)
+
+#define SESS_LOCK(sess)                                 \
+    do                                                  \
+    {                                                   \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);                \
+        if (lwp_sess_critical_enter(sess, 0) != RT_EOK) \
+        {                                               \
+            RT_ASSERT(0);                               \
+        }                                               \
+    } while (0)
+
+#define SESS_LOCK_NESTED(sess)                                             \
+    do                                                                     \
+    {                                                                      \
+        RT_DEBUG_SCHEDULER_AVAILABLE(1);                                   \
+        if (lwp_sess_critical_enter(sess, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+        {                                                                  \
+            RT_ASSERT(0);                                                  \
+        }                                                                  \
+    } while (0)
+
+#define SESS_UNLOCK(sess)                           \
+    do                                              \
+    {                                               \
+        if (lwp_sess_critical_exit(sess) != RT_EOK) \
+        {                                           \
+            RT_ASSERT(0);                           \
+        }                                           \
+    } while (0)
+
 #else
 
 #define LWP_LOCK(lwp)           rt_base_t level = rt_hw_interrupt_disable()
 #define LWP_UNLOCK(lwp)         rt_hw_interrupt_enable(level)
+#define PGRP_LOCK(pgrp)         rt_base_t level = rt_hw_interrupt_disable()
+#define PGRP_UNLOCK(pgrp)       rt_hw_interrupt_enable(level)
+#define SESS_LOCK(sess)         rt_base_t level = rt_hw_interrupt_disable()
+#define SESS_UNLOCK(sess)       rt_hw_interrupt_enable(level)
 
 #endif /* LWP_USING_CPUS_LOCK */
 
@@ -95,4 +186,6 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
 #define LWP_RETURN(name)            {RT_ASSERT(name != _LWP_UNINITIALIZED_RC);return name;}
 #endif /* LWP_DEBUG */
 
+int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]);
+
 #endif /* __LWP_INTERNAL_H__ */

+ 86 - 0
components/lwp/lwp_jobctrl.c

@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <terminal/terminal.h>
+#include "lwp_internal.h"
+
+static void jobctrl_set_pgrp_orphaned(struct rt_processgroup *pgrp)
+{
+    rt_lwp_t proc, nx_proc;
+    PGRP_LOCK(pgrp);
+
+    pgrp->is_orphaned = 1;
+    rt_list_for_each_entry(proc, &pgrp->process, pgrp_node)
+    {
+        LWP_LOCK(proc);
+        if (proc->jobctl_stopped)
+        {
+            LWP_UNLOCK(proc);
+            rt_list_for_each_entry_safe(proc, nx_proc, &pgrp->process, pgrp_node)
+            {
+                LWP_LOCK(proc);
+                lwp_signal_kill(proc, SIGHUP, SI_KERNEL, 0);
+                lwp_signal_kill(proc, SIGCONT, SI_KERNEL, 0);
+                LWP_UNLOCK(proc);
+            }
+        }
+        LWP_UNLOCK(proc);
+    }
+
+    PGRP_UNLOCK(pgrp);
+}
+
+void lwp_jobctrl_on_exit(struct rt_lwp *lwp)
+{
+    rt_processgroup_t pgrp;
+    rt_session_t session;
+    lwp_tty_t tp;
+
+    pgrp = lwp->pgrp;
+    RT_ASSERT(pgrp);
+    session = pgrp->session;
+    RT_ASSERT(session);
+
+    /**
+     * as a session leader, we have to mark tty as freed. So others can race to
+     * take it before we actually close and released that tty
+     */
+    SESS_LOCK(session);
+    if (session->sid == lwp->pid)
+    {
+        tp = session->ctty;
+        session->leader = 0;
+
+        /* signal to foreground group that modem is disconnected */
+        if (tp)
+        {
+            tty_lock(tp);
+            if (tp->t_session == session)
+                lwp_tty_signal_pgrp(tp, SIGHUP);
+            tty_unlock(tp);
+        }
+
+        /* revoke tty vnode ? */
+
+        rt_list_for_each_entry(pgrp, &session->processgroup, pgrp_list_node)
+        {
+            jobctrl_set_pgrp_orphaned(pgrp);
+        }
+
+    }
+    SESS_UNLOCK(session);
+
+    /* release tty */
+    /* allow tty stolen? */
+}

+ 542 - 0
components/lwp/lwp_pgrp.c

@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-17     xqyjlj       the first version
+ * 2023-11-28     Shell        Add reference management for pgrp;
+ *                             Using lwp lock API and fix the dead lock problem
+ */
+
+#include "lwp.h"
+#include "lwp_internal.h"
+#include "lwp_syscall.h"
+
+#define DBG_TAG "lwp.pgrp"
+#define DBG_LVL DBG_WARNING
+#include <rtdbg.h>
+
+void lwp_pgrp_dec_ref(rt_processgroup_t pgrp)
+{
+    if (rt_atomic_add(&pgrp->ref, -1) == 1)
+    {
+        rt_mutex_detach(&(pgrp->mutex));
+
+        /* clear self pgid */
+        pgrp->pgid = 0;
+        rt_free(pgrp);
+    }
+}
+
+rt_processgroup_t lwp_pgrp_find_and_inc_ref(pid_t pgid)
+{
+    rt_processgroup_t group;
+
+    group = lwp_pgrp_find(pgid);
+    if (group)
+    {
+        rt_atomic_add(&(group->ref), 1);
+    }
+
+    return group;
+}
+
+rt_processgroup_t lwp_pgrp_find(pid_t pgid)
+{
+    rt_base_t level;
+    rt_processgroup_t group = RT_NULL;
+    rt_list_t *node = RT_NULL;
+    struct rt_object_information *information = RT_NULL;
+
+    information = rt_object_get_information(RT_Object_Class_ProcessGroup);
+
+    /* parameter check */
+    if ((pgid < 0) || (information == RT_NULL))
+    {
+        return RT_NULL;
+    }
+
+    if (pgid == 0)
+    {
+        pgid = lwp_getpid();
+    }
+
+    /* enter critical */
+    level = rt_spin_lock_irqsave(&(information->spinlock));
+
+    /* try to find process group */
+    rt_list_for_each(node, &(information->object_list))
+    {
+        group = (rt_processgroup_t)rt_list_entry(node, struct rt_object, list);
+        if (group->pgid == pgid)
+        {
+            rt_spin_unlock_irqrestore(&(information->spinlock), level);
+
+            return group;
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&(information->spinlock), level);
+
+    LOG_I("cannot find(pgid:%d)() by (pid:%d, pgid:%d)", pgid, lwp_getpid(), lwp_pgid_get_byprocess(lwp_self()));
+
+    return RT_NULL;
+}
+
+rt_processgroup_t lwp_pgrp_create(rt_lwp_t leader)
+{
+    rt_processgroup_t group = RT_NULL;
+
+    /* parameter check */
+    if (leader == RT_NULL)
+    {
+        return RT_NULL;
+    }
+
+    group = rt_malloc(sizeof(struct rt_processgroup));
+    if (group != RT_NULL)
+    {
+        rt_object_init(&(group->object), RT_Object_Class_ProcessGroup, "pgrp");
+        rt_list_init(&(group->process));
+        rt_list_init(&(group->pgrp_list_node));
+        rt_mutex_init(&(group->mutex), "pgrp", RT_IPC_FLAG_PRIO);
+        group->leader = leader;
+        group->sid = 0;
+        group->session = RT_NULL;
+        group->is_orphaned = 0;
+        group->pgid = lwp_to_pid(leader);
+        rt_atomic_store(&group->ref, 1);
+    }
+
+    LOG_I("create(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
+
+    return group;
+}
+
+#include <terminal/terminal.h>
+
+int lwp_pgrp_delete(rt_processgroup_t group)
+{
+    int retry = 1;
+    rt_session_t session = RT_NULL;
+    int is_session_free = 0;
+    lwp_tty_t ctty;
+
+    /* parameter check */
+    if (group == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    LOG_I("delete(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
+
+    while (retry)
+    {
+        retry = 0;
+        session = lwp_session_find(lwp_sid_get_bypgrp(group));
+        if (session)
+        {
+            ctty = session->ctty;
+            if (ctty)
+            {
+                /**
+                 * Note: it's safe to release pgrp even we do this multiple,
+                 * the neccessary check is done before the tty actually detach
+                 */
+                tty_lock(ctty);
+                tty_rel_pgrp(ctty, group); // tty_unlock
+            }
+
+            SESS_LOCK(session);
+            PGRP_LOCK_NESTED(group);
+            if (group->session == session && session->ctty == ctty)
+            {
+                rt_object_detach(&(group->object));
+                is_session_free = lwp_session_remove(session, group);
+            }
+            else
+            {
+                retry = 1;
+
+            }
+
+            PGRP_UNLOCK(group);
+            if (is_session_free != 1)
+                SESS_UNLOCK(session);
+        }
+        else
+        {
+            rt_object_detach(&(group->object));
+        }
+    }
+
+    lwp_pgrp_dec_ref(group);
+    return 0;
+}
+
+int lwp_pgrp_insert(rt_processgroup_t group, rt_lwp_t process)
+{
+    /* parameter check */
+    if (group == RT_NULL || process == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    PGRP_LOCK_NESTED(group);
+    LWP_LOCK_NESTED(process);
+    RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
+
+    process->pgid = group->pgid;
+    process->pgrp = group;
+    process->sid = group->sid;
+    rt_list_insert_after(&(group->process), &(process->pgrp_node));
+
+    LWP_UNLOCK(process);
+    PGRP_UNLOCK(group);
+
+    return 0;
+}
+
+int lwp_pgrp_remove(rt_processgroup_t group, rt_lwp_t process)
+{
+    rt_bool_t is_empty = RT_FALSE;
+
+    /* parameter check */
+    if (group == RT_NULL || process == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    PGRP_LOCK_NESTED(group);
+    LWP_LOCK_NESTED(process);
+    RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
+
+    rt_list_remove(&(process->pgrp_node));
+    /* clear children sid and pgid */
+    process->pgrp = RT_NULL;
+    process->pgid = 0;
+    process->sid = 0;
+
+    LWP_UNLOCK(process);
+
+    is_empty = rt_list_isempty(&(group->process));
+
+    PGRP_UNLOCK(group);
+
+    if (is_empty)
+    {
+        lwp_pgrp_delete(group);
+        return 1;
+    }
+
+    return 0;
+}
+
+int lwp_pgrp_move(rt_processgroup_t group, rt_lwp_t process)
+{
+    int retry = 1;
+    rt_processgroup_t old_group;
+
+    /* parameter check */
+    if (group == RT_NULL || process == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    if (lwp_pgid_get_bypgrp(group) == lwp_pgid_get_byprocess(process))
+    {
+        return 0;
+    }
+
+    PGRP_LOCK(group);
+    while (retry)
+    {
+        retry = 0;
+        old_group = lwp_pgrp_find_and_inc_ref(lwp_pgid_get_byprocess(process));
+
+        PGRP_LOCK(old_group);
+        LWP_LOCK(process);
+
+        if (process->pgrp == old_group)
+        {
+            lwp_pgrp_remove(old_group, process);
+            lwp_pgrp_insert(group, process);
+        }
+        else
+        {
+            retry = 1;
+        }
+        PGRP_UNLOCK(old_group);
+        LWP_UNLOCK(process);
+
+        lwp_pgrp_dec_ref(old_group);
+    }
+    PGRP_UNLOCK(group);
+
+    return 0;
+}
+
+int lwp_pgrp_update_children_info(rt_processgroup_t group, pid_t sid, pid_t pgid)
+{
+    rt_list_t *node = RT_NULL;
+    rt_lwp_t process = RT_NULL;
+
+    if (group == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    PGRP_LOCK_NESTED(group);
+
+    /* try to find process group */
+    rt_list_for_each(node, &(group->process))
+    {
+        process = (rt_lwp_t)rt_list_entry(node, struct rt_lwp, pgrp_node);
+        LWP_LOCK(process);
+        if (sid != -1)
+        {
+            process->sid = sid;
+        }
+        if (pgid != -1)
+        {
+            process->pgid = pgid;
+            process->pgrp = group;
+        }
+        LWP_UNLOCK(process);
+    }
+
+    PGRP_UNLOCK(group);
+    return 0;
+}
+
+/**
+ * setpgid() sets the PGID of the process specified by pid to pgid.
+ *  If pid is zero, then the process ID of the calling process is used.
+ *  If pgid is zero, then the PGID of the process specified by pid is made the same as its process ID.
+ *  If setpgid() is used to move a process from one process group to another (as is done by some shells when
+ *      creating pipelines), both process groups must be part of the same session (see setsid(2) and credentials(7)).
+ *      In this case, the pgid specifies an existing process group to be joined and the session ID of that group must
+ *      match the session ID of the joining process.
+ */
+sysret_t sys_setpgid(pid_t pid, pid_t pgid)
+{
+    rt_lwp_t process, self_process;
+    pid_t sid;
+    rt_processgroup_t group;
+    rt_session_t session;
+    sysret_t err = 0;
+
+    if (pgid == 0)
+    {
+        pgid = pid;
+    }
+    if (pgid < 0)
+    {
+        return -EINVAL;
+    }
+
+    self_process = lwp_self();
+
+    if (pid == 0)
+    {
+        pid = self_process->pid;
+        process = self_process;
+    }
+    else
+    {
+        lwp_pid_lock_take();
+        process = lwp_from_pid_locked(pid);
+        lwp_pid_lock_release();
+
+        if (process == RT_NULL)
+        {
+            return -ESRCH;
+        }
+    }
+
+    LWP_LOCK(process);
+
+    if (process->parent == self_process)
+    {
+        /**
+         * change the process group ID of one of the children of the calling process and the child was in
+         *  a different session
+         */
+        if (lwp_sid_get_byprocess(process) != lwp_sid_get_byprocess(self_process))
+        {
+            err = -EPERM;
+            LWP_UNLOCK(process);
+            goto exit;
+        }
+        /**
+         * An attempt was made to change the process group ID of one of the children of the calling process
+         *  and the child had already performed an execve(2)
+         */
+        if (process->did_exec)
+        {
+            err = -EACCES;
+            LWP_UNLOCK(process);
+            goto exit;
+        }
+    }
+    else
+    {
+        /**
+         * pid is not the calling process and not a child of the calling process.
+         */
+        if (process != self_process)
+        {
+            err = -ESRCH;
+            LWP_UNLOCK(process);
+            goto exit;
+        }
+    }
+
+    LWP_UNLOCK(process);
+
+    sid = lwp_sid_get_byprocess(self_process);
+    if (pgid != pid)
+    {
+        group = lwp_pgrp_find(pgid);
+        if (group == RT_NULL)
+        {
+            group = lwp_pgrp_create(process);
+        }
+        else
+        {
+            /**
+             * An attempt was made to move a process into a process group in a different session
+             */
+            if (sid != lwp_sid_get_bypgrp(group))
+            {
+                err = -EPERM;
+                goto exit;
+            }
+            /**
+             * or to change the process group ID of a session leader
+             */
+            if (sid == lwp_to_pid(process))
+            {
+                err = -EPERM;
+                goto exit;
+            }
+            lwp_pgrp_move(group, process);
+        }
+    }
+    else
+    {
+        group = lwp_pgrp_find(pgid);
+        if (group == RT_NULL)
+        {
+            group = lwp_pgrp_create(process);
+            lwp_pgrp_move(group, process);
+            session = lwp_session_find(sid);
+            if (session == RT_NULL)
+            {
+                LOG_E("the session of sid: %d cannot be found", sid);
+                err = -EPERM;
+                goto exit;
+            }
+            else
+            {
+                lwp_session_insert(session, group);
+            }
+        }
+        else // this represents repeated calls
+        {
+            /**
+             * or to change the process group ID of a session leader
+             */
+            if (lwp_sid_get_bypgrp(group) == lwp_pgid_get_bypgrp(group))
+            {
+                err = -EPERM;
+                goto exit;
+            }
+            else
+            {
+                err = 0;
+            }
+        }
+    }
+
+exit:
+    return err;
+}
+
+/**
+ * getpgid() returns the PGID of the process specified by pid.
+ *  If pid is zero, the process ID of the calling process is used. (Retrieving the PGID of a process other
+ *      than the caller is rarely necessary, and the POSIX.1 getpgrp() is preferred for that task.)
+ */
+sysret_t sys_getpgid(pid_t pid)
+{
+    rt_lwp_t process;
+
+    lwp_pid_lock_take();
+    process = lwp_from_pid_locked(pid);
+    lwp_pid_lock_release();
+
+    if (process == RT_NULL)
+    {
+        return -ESRCH;
+    }
+
+    return lwp_pgid_get_byprocess(process);
+}
+
+#ifdef RT_USING_FINSH
+
+#include "finsh.h"
+
+long list_processgroup(void)
+{
+    int count = 0, index;
+    rt_processgroup_t *groups;
+    rt_processgroup_t group;
+    rt_thread_t thread;
+    char name[RT_NAME_MAX];
+
+    rt_kprintf("PGID SID  leader process\n");
+    rt_kprintf("---- ---- ----------------\n");
+
+    count = rt_object_get_length(RT_Object_Class_ProcessGroup);
+    if (count > 0)
+    {
+        /* get  pointers */
+        groups = (rt_processgroup_t *)rt_calloc(count, sizeof(rt_processgroup_t));
+        if (groups)
+        {
+            index = rt_object_get_pointers(RT_Object_Class_ProcessGroup, (rt_object_t *)groups, count);
+            if (index > 0)
+            {
+                for (index = 0; index < count; index++)
+                {
+                    struct rt_processgroup pgrp;
+                    group = groups[index];
+                    PGRP_LOCK(group);
+                    rt_memcpy(&pgrp, group, sizeof(struct rt_processgroup));
+                    PGRP_UNLOCK(group);
+
+                    if (pgrp.leader)
+                    {
+                        thread = rt_list_entry(pgrp.leader->t_grp.prev, struct rt_thread, sibling);
+                        rt_strncpy(name, thread->parent.name, RT_NAME_MAX);
+                    }
+                    else
+                    {
+                        rt_strncpy(name, "nil", RT_NAME_MAX);
+                    }
+
+                    rt_kprintf("%4d %4d %-*.*s\n", pgrp.pgid, pgrp.sid, RT_NAME_MAX, RT_NAME_MAX, name);
+                }
+            }
+            rt_free(groups);
+        }
+    }
+
+    return 0;
+}
+MSH_CMD_EXPORT(list_processgroup, list process group);
+#endif

+ 475 - 187
components/lwp/lwp_pid.c

@@ -14,32 +14,47 @@
  *                             error
  * 2023-10-27     shell        Format codes of sys_exit(). Fix the data racing where lock is missed
  *                             Add reference on pid/tid, so the resource is not freed while using.
+ *                             Add support for waitpid(options=WNOHANG)
+ * 2023-11-16     xqyjlj       Fix the case where pid is 0
+ * 2023-11-17     xqyjlj       add process group and session support
+ * 2023-11-24     shell        Support of waitpid(options=WNOTRACED|WCONTINUED);
+ *                             Reimplement the waitpid with a wait queue method, and fixup problem
+ *                             with waitpid(pid=-1)/waitpid(pid=-pgid)/waitpid(pid=0) that only one
+ *                             process can be traced while waiter suspend
  * 2024-01-25     shell        porting to new sched API
  */
 
 /* includes scheduler related API */
 #define __RT_IPC_SOURCE__
 
-#include <rthw.h>
-#include <rtthread.h>
+/* for waitpid, we are compatible to GNU extension */
+#define _GNU_SOURCE
 
 #define DBG_TAG "lwp.pid"
 #define DBG_LVL DBG_INFO
 #include <rtdbg.h>
 
+#include "lwp_internal.h"
+
+#include <rthw.h>
+#include <rtthread.h>
 #include <dfs_file.h>
 #include <unistd.h>
 #include <stdio.h> /* rename() */
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/statfs.h> /* statfs() */
-
-#include "lwp_internal.h"
-#include "tty.h"
+#include <stdatomic.h>
 
 #ifdef ARCH_MM_MMU
 #include "lwp_user_mm.h"
 #endif
 
+#ifdef RT_USING_DFS_PROCFS
+#include "proc.h"
+#include "procfs.h"
+#endif
+
 #define PID_MAX 10000
 
 #define PID_CT_ASSERT(name, x) \
@@ -147,8 +162,23 @@ static void lwp_pid_put_locked(pid_t pid)
     }
 }
 
+#ifdef RT_USING_DFS_PROCFS
+    rt_inline void _free_proc_dentry(rt_lwp_t lwp)
+    {
+        char pid_str[64] = {0};
+
+        rt_snprintf(pid_str, 64, "%d", lwp->pid);
+        pid_str[63] = 0;
+        proc_remove_dentry(pid_str, 0);
+    }
+#else
+    #define _free_proc_dentry(lwp)
+#endif
+
 void lwp_pid_put(struct rt_lwp *lwp)
 {
+    _free_proc_dentry(lwp);
+
     lwp_pid_lock_take();
     lwp_pid_put_locked(lwp->pid);
     lwp_pid_lock_release();
@@ -168,6 +198,13 @@ static void lwp_pid_set_lwp_locked(pid_t pid, struct rt_lwp *lwp)
     {
         p->data = lwp;
         lwp_ref_inc(lwp);
+
+#ifdef RT_USING_DFS_PROCFS
+        if (pid)
+        {
+            proc_pid(pid);
+        }
+#endif
     }
 }
 
@@ -352,19 +389,23 @@ rt_lwp_t lwp_create(rt_base_t flags)
     if (new_lwp)
     {
         /* minimal setup of lwp object */
-        new_lwp->session = -1;
         new_lwp->ref = 1;
 #ifdef RT_USING_SMP
         new_lwp->bind_cpu = RT_CPUS_NR;
 #endif
-        rt_list_init(&new_lwp->wait_list);
+        new_lwp->exe_file = RT_NULL;
         rt_list_init(&new_lwp->t_grp);
+        rt_list_init(&new_lwp->pgrp_node);
         rt_list_init(&new_lwp->timer);
         lwp_user_object_lock_init(new_lwp);
         rt_wqueue_init(&new_lwp->wait_queue);
+        rt_wqueue_init(&new_lwp->waitpid_waiters);
         lwp_signal_init(&new_lwp->signal);
         rt_mutex_init(&new_lwp->lwp_lock, "lwp_lock", RT_IPC_FLAG_PRIO);
 
+        if (flags & LWP_CREATE_FLAG_NOTRACE_EXEC)
+            new_lwp->did_exec = RT_TRUE;
+
         /* lwp with pid */
         if (flags & LWP_CREATE_FLAG_ALLOC_PID)
         {
@@ -375,7 +416,7 @@ rt_lwp_t lwp_create(rt_base_t flags)
                 lwp_user_object_lock_destroy(new_lwp);
                 rt_free(new_lwp);
                 new_lwp = RT_NULL;
-                LOG_E("pid slot fulled!\n");
+                LOG_E("%s: pid slot fulled", __func__);
             }
             else
             {
@@ -384,6 +425,20 @@ rt_lwp_t lwp_create(rt_base_t flags)
             }
             lwp_pid_lock_release();
         }
+        rt_memset(&new_lwp->rt_rusage,0, sizeof(new_lwp->rt_rusage));
+
+        if (flags & LWP_CREATE_FLAG_INIT_USPACE)
+        {
+            rt_err_t error = lwp_user_space_init(new_lwp, 0);
+            if (error)
+            {
+                lwp_pid_put(new_lwp);
+                lwp_user_object_lock_destroy(new_lwp);
+                rt_free(new_lwp);
+                new_lwp = RT_NULL;
+                LOG_E("%s: failed to initialize user space", __func__);
+            }
+        }
     }
 
     LOG_D("%s(pid=%d) => %p", __func__, new_lwp ? new_lwp->pid : -1, new_lwp);
@@ -393,6 +448,8 @@ rt_lwp_t lwp_create(rt_base_t flags)
 /** when reference is 0, a lwp can be released */
 void lwp_free(struct rt_lwp* lwp)
 {
+    rt_processgroup_t group = RT_NULL;
+
     if (lwp == RT_NULL)
     {
         return;
@@ -406,6 +463,10 @@ void lwp_free(struct rt_lwp* lwp)
      *   all the reference is clear)
      */
     LOG_D("lwp free: %p", lwp);
+    rt_free(lwp->exe_file);
+    group = lwp_pgrp_find(lwp_pgid_get_byprocess(lwp));
+    if (group)
+        lwp_pgrp_remove(group, lwp);
 
     LWP_LOCK(lwp);
 
@@ -472,6 +533,15 @@ void lwp_free(struct rt_lwp* lwp)
 rt_inline rt_noreturn
 void _thread_exit(rt_lwp_t lwp, rt_thread_t thread)
 {
+    LWP_LOCK(lwp);
+    lwp->rt_rusage.ru_stime.tv_sec += thread->system_time / RT_TICK_PER_SECOND;
+    lwp->rt_rusage.ru_stime.tv_usec += thread->system_time % RT_TICK_PER_SECOND * (1000000 / RT_TICK_PER_SECOND);
+    lwp->rt_rusage.ru_utime.tv_sec += thread->user_time / RT_TICK_PER_SECOND;
+    lwp->rt_rusage.ru_utime.tv_usec += thread->user_time % RT_TICK_PER_SECOND * (1000000 / RT_TICK_PER_SECOND);
+    rt_list_remove(&thread->sibling);
+    LWP_UNLOCK(lwp);
+    lwp_futex_exit_robust_list(thread);
+
     /**
      * Note: the tid tree always hold a reference to thread, hence the tid must
      * be release before cleanup of thread
@@ -479,10 +549,6 @@ void _thread_exit(rt_lwp_t lwp, rt_thread_t thread)
     lwp_tid_put(thread->tid);
     thread->tid = 0;
 
-    LWP_LOCK(lwp);
-    rt_list_remove(&thread->sibling);
-    LWP_UNLOCK(lwp);
-
     rt_thread_delete(thread);
     rt_schedule();
     while (1) ;
@@ -497,11 +563,11 @@ rt_inline void _clear_child_tid(rt_thread_t thread)
 
         thread->clear_child_tid = RT_NULL;
         lwp_put_to_user(clear_child_tid, &t, sizeof t);
-        sys_futex(clear_child_tid, FUTEX_WAKE | FUTEX_PRIVATE, 1, RT_NULL, RT_NULL, 0);
+        sys_futex(clear_child_tid, FUTEX_WAKE, 1, RT_NULL, RT_NULL, 0);
     }
 }
 
-void lwp_exit(rt_lwp_t lwp, rt_base_t status)
+void lwp_exit(rt_lwp_t lwp, lwp_status_t status)
 {
     rt_thread_t thread;
 
@@ -523,7 +589,7 @@ void lwp_exit(rt_lwp_t lwp, rt_base_t status)
      * Brief: only one thread should calls exit_group(),
      * but we can not ensured that during run-time
      */
-    lwp->lwp_ret = LWP_CREATE_STAT(status);
+    lwp->lwp_status = status;
     LWP_UNLOCK(lwp);
 
     lwp_terminate(lwp);
@@ -550,7 +616,7 @@ void lwp_exit(rt_lwp_t lwp, rt_base_t status)
     _thread_exit(lwp, thread);
 }
 
-void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
+void lwp_thread_exit(rt_thread_t thread, int status)
 {
     rt_thread_t header_thr;
     struct rt_lwp *lwp;
@@ -568,7 +634,11 @@ void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
     header_thr = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
     if (header_thr == thread && thread->sibling.prev == &lwp->t_grp)
     {
-        lwp->lwp_ret = LWP_CREATE_STAT(status);
+        /**
+         * if thread exit, treated as process exit normally.
+         * This is reasonable since trap event is exited through lwp_exit()
+         */
+        lwp->lwp_status = LWP_CREATE_STAT_EXIT(status);
         LWP_UNLOCK(lwp);
 
         lwp_terminate(lwp);
@@ -582,38 +652,6 @@ void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
     _thread_exit(lwp, thread);
 }
 
-static void _pop_tty(rt_lwp_t lwp)
-{
-    if (!lwp->background)
-    {
-        struct termios *old_stdin_termios = get_old_termios();
-        struct rt_lwp *old_lwp = NULL;
-
-        if (lwp->session == -1)
-        {
-            tcsetattr(1, 0, old_stdin_termios);
-        }
-        if (lwp->tty != RT_NULL)
-        {
-            rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER);
-            if (lwp->tty->foreground == lwp)
-            {
-                old_lwp = tty_pop(&lwp->tty->head, RT_NULL);
-                lwp->tty->foreground = old_lwp;
-            }
-            else
-            {
-                tty_pop(&lwp->tty->head, lwp);
-            }
-            rt_mutex_release(&lwp->tty->lock);
-
-            LWP_LOCK(lwp);
-            lwp->tty = RT_NULL;
-            LWP_UNLOCK(lwp);
-        }
-    }
-}
-
 /** @note the reference is not for synchronization, but for the release of resource. the synchronization is done through lwp & pid lock */
 int lwp_ref_inc(struct rt_lwp *lwp)
 {
@@ -657,7 +695,7 @@ int lwp_ref_dec(struct rt_lwp *lwp)
     return ref;
 }
 
-struct rt_lwp* lwp_from_pid_locked(pid_t pid)
+struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid)
 {
     struct lwp_avl_struct *p;
     struct rt_lwp *lwp = RT_NULL;
@@ -671,6 +709,13 @@ struct rt_lwp* lwp_from_pid_locked(pid_t pid)
     return lwp;
 }
 
+struct rt_lwp* lwp_from_pid_locked(pid_t pid)
+{
+    struct rt_lwp* lwp;
+    lwp = pid ? lwp_from_pid_raw_locked(pid) : lwp_self();
+    return lwp;
+}
+
 pid_t lwp_to_pid(struct rt_lwp* lwp)
 {
     if (!lwp)
@@ -733,162 +778,377 @@ pid_t lwp_name2pid(const char *name)
 
 int lwp_getpid(void)
 {
-    return ((struct rt_lwp *)rt_thread_self()->lwp)->pid;
+    rt_lwp_t lwp = lwp_self();
+    return lwp ? lwp->pid : 1;
+    // return ((struct rt_lwp *)rt_thread_self()->lwp)->pid;
 }
 
-/**
- * @brief Wait for a child lwp to terminate. Do the essential recycling. Setup
- *        status code for user
- */
-static sysret_t _lwp_wait_and_recycle(struct rt_lwp *child, rt_thread_t cur_thr,
-                                      struct rt_lwp *self_lwp, int *status,
-                                      int options)
+rt_inline void _update_ru(struct rt_lwp *child, struct rt_lwp *self_lwp, struct rusage *uru)
+{
+    struct rusage rt_rusage;
+    if (uru != RT_NULL)
+    {
+        rt_rusage.ru_stime.tv_sec = child->rt_rusage.ru_stime.tv_sec;
+        rt_rusage.ru_stime.tv_usec = child->rt_rusage.ru_stime.tv_usec;
+        rt_rusage.ru_utime.tv_sec = child->rt_rusage.ru_utime.tv_sec;
+        rt_rusage.ru_utime.tv_usec = child->rt_rusage.ru_utime.tv_usec;
+        lwp_data_put(self_lwp, uru, &rt_rusage, sizeof(*uru));
+    }
+}
+
+/* do statistical summary and reap the child if neccessary */
+static rt_err_t _stats_and_reap_child(rt_lwp_t child, rt_thread_t cur_thr,
+                                      struct rt_lwp *self_lwp, int *ustatus,
+                                      int options, struct rusage *uru)
 {
-    sysret_t error;
-    int lwp_stat;
-    int terminated;
+    int lwp_stat = child->lwp_status;
 
-    if (!child)
+    /* report statistical data to process */
+    _update_ru(child, self_lwp, uru);
+
+    if (child->terminated && !(options & WNOWAIT))
+    {
+        /** Reap the child process if it's exited */
+        LOG_D("func %s: child detached", __func__);
+        lwp_pid_put(child);
+        lwp_children_unregister(self_lwp, child);
+    }
+
+    if (ustatus)
+        lwp_data_put(self_lwp, ustatus, &lwp_stat, sizeof(*ustatus));
+
+    return RT_EOK;
+}
+
+#define HAS_CHILD_BUT_NO_EVT (-1024)
+
+/* check if the process is already terminate */
+static sysret_t _query_event_from_lwp(rt_lwp_t child, rt_thread_t cur_thr, rt_lwp_t self_lwp,
+                                      int options, int *status)
+{
+    sysret_t rc;
+
+    LWP_LOCK(child);
+    if (child->terminated)
+    {
+        rc = child->pid;
+    }
+    else if ((options & WSTOPPED) && child->jobctl_stopped && !child->wait_reap_stp)
     {
-        error = -RT_ERROR;
+        child->wait_reap_stp = 1;
+        rc = child->pid;
     }
     else
+    {
+        rc = HAS_CHILD_BUT_NO_EVT;
+    }
+    LWP_UNLOCK(child);
+
+    LOG_D("%s(child_pid=%d ('%s'), stopped=%d) => %d", __func__, child->pid, child->cmd, child->jobctl_stopped, rc);
+    return rc;
+}
+
+/* verify if the process is child, and reap it */
+static pid_t _verify_child_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp,
+                                       pid_t wait_pid, int options, int *ustatus,
+                                       struct rusage *uru)
+{
+    sysret_t rc;
+    struct rt_lwp *child;
+
+    /* check if pid is reference to a valid child */
+    lwp_pid_lock_take();
+    child = lwp_from_pid_locked(wait_pid);
+    if (!child)
+        rc = -EINVAL;
+    else if (child->parent != self_lwp)
+        rc = -ESRCH;
+    else
+        rc = wait_pid;
+
+    lwp_pid_lock_release();
+
+    if (rc > 0)
+    {
+        rc = _query_event_from_lwp(child, cur_thr, self_lwp, options, ustatus);
+        if (rc > 0)
+        {
+            _stats_and_reap_child(child, cur_thr, self_lwp, ustatus, options, uru);
+        }
+    }
+    return rc;
+}
+
+/* try to reap any child */
+static pid_t _reap_any_child_pid(rt_thread_t cur_thr, rt_lwp_t self_lwp, pid_t pair_pgid,
+                                 int options, int *ustatus, struct rusage *uru)
+{
+    sysret_t rc = -ECHILD;
+    struct rt_lwp *child;
+
+    LWP_LOCK(self_lwp);
+    child = self_lwp->first_child;
+
+    /* find a exited child if any */
+    while (child)
+    {
+        if (pair_pgid && child->pgid != pair_pgid)
+            continue;
+
+        rc = _query_event_from_lwp(child, cur_thr, self_lwp, options, ustatus);
+        if (rc > 0)
+            break;
+
+        child = child->sibling;
+    }
+    LWP_UNLOCK(self_lwp);
+
+    if (rc > 0)
+    {
+        _stats_and_reap_child(child, cur_thr, self_lwp, ustatus, options, uru);
+    }
+    return rc;
+}
+
+rt_err_t lwp_waitpid_kick(rt_lwp_t parent, rt_lwp_t self_lwp)
+{
+    /* waker provide the message mainly through its lwp_status */
+    rt_wqueue_wakeup(&parent->waitpid_waiters, self_lwp);
+    return RT_EOK;
+}
+
+struct waitpid_handle {
+    struct rt_wqueue_node wq_node;
+    int options;
+    rt_lwp_t waker_lwp;
+};
+
+/* the IPC message is setup and notify the parent */
+static int _waitq_filter(struct rt_wqueue_node *wait_node, void *key)
+{
+    int can_accept_evt = 0;
+    rt_thread_t waiter = wait_node->polling_thread;
+    pid_t destiny = (pid_t)wait_node->key;
+    rt_lwp_t waker_lwp = key;
+    struct waitpid_handle *handle;
+    rt_ubase_t options;
+
+    handle = rt_container_of(wait_node, struct waitpid_handle, wq_node);
+
+    RT_ASSERT(waiter != RT_NULL);
+    options = handle->options;
+
+    /* filter out if waker is not the one */
+    if (destiny > 0)
     {
         /**
-         * Note: Critical Section
-         * - child lwp (RW. This will modify its parent if valid)
+         * in waitpid immediately return routine, we already do the check
+         * that pid is one of the child process of waiting thread
          */
-        LWP_LOCK(child);
-        if (child->terminated)
+        can_accept_evt = waker_lwp->pid == destiny;
+    }
+    else if (destiny == -1)
+    {
+        can_accept_evt = waker_lwp->parent == waiter->lwp;
+    }
+    else
+    {
+        /* destiny == 0 || destiny == -pgid */
+        pid_t waiter_pgid;
+        if (destiny == 0)
         {
-            error = child->pid;
+            waiter_pgid = lwp_pgid_get_byprocess(waiter->lwp);
         }
-        else if (rt_list_isempty(&child->wait_list))
+        else
         {
-            /**
-             * Note: only one thread can wait on wait_list.
-             * dont reschedule before mutex unlock
-             */
-            rt_enter_critical();
+            waiter_pgid = -destiny;
+        }
+        can_accept_evt = waiter_pgid == lwp_pgid_get_byprocess(waker_lwp);
+    }
 
-            error = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE);
-            if (error == 0)
-            {
-                rt_list_insert_before(&child->wait_list, &RT_THREAD_LIST_NODE(cur_thr));
-                LWP_UNLOCK(child);
+    /* filter out if event is not desired */
+    if (can_accept_evt)
+    {
+        if ((options & WEXITED) && waker_lwp->terminated)
+            can_accept_evt = 1;
+        else if ((options & WSTOPPED) && WIFSTOPPED(waker_lwp->lwp_status))
+            can_accept_evt = 1;
+        else if ((options & WCONTINUED) && WIFCONTINUED(waker_lwp->lwp_status))
+            can_accept_evt = 1;
+        else
+            can_accept_evt = 0;
+    }
 
-                rt_set_errno(RT_EINTR);
-                rt_exit_critical();
-                rt_schedule();
+    /* setup message for waiter if accepted */
+    if (can_accept_evt)
+        handle->waker_lwp = waker_lwp;
 
-                /**
-                 * Since parent is holding a reference to children this lock will
-                 * not be freed before parent dereference to it.
-                 */
-                LWP_LOCK(child);
+    /* 0 if event is accepted, otherwise discard */
+    return !can_accept_evt;
+}
 
-                error = rt_get_errno();
-                if (error == RT_EINTR)
-                {
-                    error = -EINTR;
-                }
-                else if (error != RT_EOK)
-                {
-                    LOG_W("%s: unexpected error code %ld", __func__, error);
-                }
-                else
-                {
-                    error = child->pid;
-                }
-            }
-            else
-                rt_exit_critical();
-        }
-        else
-            error = -RT_EINTR;
+/* the waiter cleanup IPC message and wait for desired event here */
+static rt_err_t _wait_for_event(rt_thread_t cur_thr, rt_lwp_t self_lwp,
+                                struct waitpid_handle *handle, pid_t destiny)
+{
+    rt_err_t ret;
 
-        lwp_stat = child->lwp_ret;
-        terminated = child->terminated;
-        LWP_UNLOCK(child);
+    /* current context checking */
+    RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
 
-        if (error > 0)
-        {
-            if (terminated)
-            {
-                LOG_D("func %s: child detached", __func__);
-                /** Reap the child process if it's exited */
-                lwp_pid_put(child);
-                lwp_children_unregister(self_lwp, child);
-            }
+    handle->wq_node.polling_thread = cur_thr;
+    handle->wq_node.key = destiny;
+    handle->wq_node.wakeup = _waitq_filter;
+    handle->wq_node.wqueue = &self_lwp->waitpid_waiters;
+    rt_list_init(&handle->wq_node.list);
 
-            if (status)
-                lwp_data_put(self_lwp, status, &lwp_stat, sizeof(*status));
-        }
+    cur_thr->error = RT_EOK;
+
+    LOG_D("%s(self_lwp=%d) wait for event", __func__, self_lwp->pid);
+
+    rt_enter_critical();
+    ret = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE);
+    if (ret == RT_EOK)
+    {
+        rt_wqueue_add(handle->wq_node.wqueue, &handle->wq_node);
+        rt_exit_critical();
+
+        rt_schedule();
+
+        ret = cur_thr->error;
+
+        /**
+         * cur_thr error is a positive value, but some legacy implementation
+         * use a negative one. So we check to avoid errors
+         */
+        ret = ret > 0 ? -ret : ret;
+
+        /**
+         * we dont rely on this actually, but we cleanup it since wakeup API
+         * set this up durint operation, and this will cause some messy condition
+         */
+        handle->wq_node.wqueue->flag = RT_WQ_FLAG_CLEAN;
+        rt_wqueue_remove(&handle->wq_node);
+    }
+    else
+    {
+        /* failed to suspend, return immediately with failure */
+        rt_exit_critical();
     }
 
-    return error;
+    return ret;
 }
 
-pid_t waitpid(pid_t pid, int *status, int options) __attribute__((alias("lwp_waitpid")));
+/* wait for IPC event and do the cleanup if neccessary */
+static sysret_t _wait_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp, const pid_t pid,
+                               int options, int *ustatus, struct rusage *uru)
+{
+    sysret_t rc;
+    struct waitpid_handle handle;
+    rt_lwp_t waker;
+
+    /* wait for SIGCHLD or other async events */
+    handle.options = options;
+    handle.waker_lwp = 0;
+    rc = _wait_for_event(cur_thr, self_lwp, &handle, pid);
+
+    waker = handle.waker_lwp;
+    if (waker != RT_NULL)
+    {
+        rc = waker->pid;
+
+        /* check out if any process exited */
+        LOG_D("%s: woken up by lwp=%d", __func__, waker->pid);
+        _stats_and_reap_child(waker, cur_thr, self_lwp, ustatus, options, uru);
+    }
+    /**
+     * else if (rc != RT_EOK)
+     * unable to do a suspend, or wakeup unexpectedly
+     * -> then returned a failure
+     */
+
+    return rc;
+}
 
-pid_t lwp_waitpid(const pid_t pid, int *status, int options)
+pid_t lwp_waitpid(const pid_t pid, int *status, int options, struct rusage *ru)
 {
     pid_t rc = -1;
-    struct rt_thread *thread;
-    struct rt_lwp *child;
+    struct rt_thread *cur_thr;
     struct rt_lwp *self_lwp;
 
-    thread = rt_thread_self();
+    cur_thr = rt_thread_self();
     self_lwp = lwp_self();
 
-    if (!self_lwp)
+    if (!cur_thr || !self_lwp)
     {
-        rc = -RT_EINVAL;
+        rc = -EINVAL;
     }
     else
     {
+        /* check if able to reap desired child immediately */
         if (pid > 0)
         {
-            lwp_pid_lock_take();
-            child = lwp_from_pid_locked(pid);
-            if (child->parent != self_lwp)
-                rc = -RT_ERROR;
-            else
-                rc = RT_EOK;
-            lwp_pid_lock_release();
-
-            if (rc == RT_EOK)
-                rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
+            /* if pid is child then try to reap it */
+            rc = _verify_child_and_reap(cur_thr, self_lwp, pid, options, status, ru);
         }
         else if (pid == -1)
         {
-            LWP_LOCK(self_lwp);
-            child = self_lwp->first_child;
-            LWP_UNLOCK(self_lwp);
-            RT_ASSERT(!child || child->parent == self_lwp);
-
-            rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
+            /* any terminated child */
+            rc = _reap_any_child_pid(cur_thr, self_lwp, 0, options, status, ru);
         }
         else
         {
-            /* not supported yet */
-            rc = -RT_EINVAL;
+            /**
+             * (pid < -1 || pid == 0)
+             * any terminated child with matched pgid
+             */
+
+            pid_t pair_pgid;
+            if (pid == 0)
+            {
+                pair_pgid = lwp_pgid_get_byprocess(self_lwp);
+            }
+            else
+            {
+                pair_pgid = -pid;
+            }
+            rc = _reap_any_child_pid(cur_thr, self_lwp, pair_pgid, options, status, ru);
         }
-    }
 
-    if (rc > 0)
-    {
-        LOG_D("%s: recycle child id %ld (status=0x%x)", __func__, (long)rc, status ? *status : 0);
-    }
-    else
-    {
-        RT_ASSERT(rc != 0);
-        LOG_D("%s: wait failed with code %ld", __func__, (long)rc);
+        if (rc == HAS_CHILD_BUT_NO_EVT)
+        {
+            if (!(options & WNOHANG))
+            {
+                /* otherwise, arrange a suspend and wait for async event */
+                options |= WEXITED;
+                rc = _wait_and_reap(cur_thr, self_lwp, pid, options, status, ru);
+            }
+            else
+            {
+                /**
+                 * POSIX.1: If waitpid() was invoked with WNOHANG set in options,
+                 * it has at least one child process specified by pid for which
+                 * status is not available, and status is not available for any
+                 * process specified by pid, 0 is returned
+                 */
+                rc = 0;
+            }
+        }
+        else
+        {
+            RT_ASSERT(rc != 0);
+        }
     }
 
+    LOG_D("waitpid() => %d, *status=0x%x", rc, status ? *status:0);
     return rc;
 }
 
+pid_t waitpid(pid_t pid, int *status, int options)
+{
+    return lwp_waitpid(pid, status, options, RT_NULL);
+}
+
 #ifdef RT_USING_FINSH
 /* copy from components/finsh/cmd.c */
 static void object_split(int len)
@@ -910,7 +1170,7 @@ static void print_thread_info(struct rt_thread* thread, int maxlen)
     else
         rt_kprintf("%-*.*s N/A %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority);
 #else
-    rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->current_priority);
+    rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority);
 #endif /*RT_USING_SMP*/
 
     stat = (RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK);
@@ -1044,7 +1304,7 @@ static void cmd_kill(int argc, char** argv)
         }
     }
     lwp_pid_lock_take();
-    lwp_signal_kill(lwp_from_pid_locked(pid), sig, SI_USER, 0);
+    lwp_signal_kill(lwp_from_pid_raw_locked(pid), sig, SI_USER, 0);
     lwp_pid_lock_release();
 }
 MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process);
@@ -1061,7 +1321,7 @@ static void cmd_killall(int argc, char** argv)
     while((pid = lwp_name2pid(argv[1])) > 0)
     {
         lwp_pid_lock_take();
-        lwp_signal_kill(lwp_from_pid_locked(pid), SIGKILL, SI_USER, 0);
+        lwp_signal_kill(lwp_from_pid_raw_locked(pid), SIGKILL, SI_USER, 0);
         lwp_pid_lock_release();
         rt_thread_mdelay(100);
     }
@@ -1073,15 +1333,15 @@ MSH_CMD_EXPORT_ALIAS(cmd_killall, killall, kill processes by name);
 int lwp_check_exit_request(void)
 {
     rt_thread_t thread = rt_thread_self();
-    rt_base_t expected = LWP_EXIT_REQUEST_TRIGGERED;
+    rt_size_t expected = LWP_EXIT_REQUEST_TRIGGERED;
 
     if (!thread->lwp)
     {
         return 0;
     }
 
-    return rt_atomic_compare_exchange_strong(&thread->exit_request, &expected,
-                                             LWP_EXIT_REQUEST_IN_PROCESS);
+    return atomic_compare_exchange_strong(&thread->exit_request, &expected,
+                                          LWP_EXIT_REQUEST_IN_PROCESS);
 }
 
 static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread);
@@ -1119,7 +1379,7 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
     rt_sched_lock_level_t slvl;
     rt_list_t *list;
     rt_thread_t thread;
-    rt_base_t expected = LWP_EXIT_REQUEST_NONE;
+    rt_size_t expected = LWP_EXIT_REQUEST_NONE;
 
     /* broadcast exit request for sibling threads */
     LWP_LOCK(lwp);
@@ -1127,8 +1387,8 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
     {
         thread = rt_list_entry(list, struct rt_thread, sibling);
 
-        rt_atomic_compare_exchange_strong(&thread->exit_request, &expected,
-                                          LWP_EXIT_REQUEST_TRIGGERED);
+        atomic_compare_exchange_strong(&thread->exit_request, &expected,
+                                       LWP_EXIT_REQUEST_TRIGGERED);
 
         rt_sched_lock(&slvl);
         /* dont release, otherwise thread may have been freed */
@@ -1213,8 +1473,43 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
     }
 }
 
+static void _notify_parent(rt_lwp_t lwp)
+{
+    int si_code;
+    int signo_or_exitcode;
+    lwp_siginfo_ext_t ext;
+    lwp_status_t lwp_status = lwp->lwp_status;
+    rt_lwp_t parent = lwp->parent;
+
+    if (WIFSIGNALED(lwp_status))
+    {
+        si_code = (lwp_status & LWP_COREDUMP_FLAG) ? CLD_DUMPED : CLD_KILLED;
+        signo_or_exitcode = WTERMSIG(lwp_status);
+    }
+    else
+    {
+        si_code = CLD_EXITED;
+        signo_or_exitcode = WEXITSTATUS(lwp->lwp_status);
+    }
+
+    lwp_waitpid_kick(parent, lwp);
+
+    ext = rt_malloc(sizeof(struct lwp_siginfo));
+
+    if (ext)
+    {
+        rt_thread_t cur_thr = rt_thread_self();
+        ext->sigchld.status = signo_or_exitcode;
+        ext->sigchld.stime = cur_thr->system_time;
+        ext->sigchld.utime = cur_thr->user_time;
+    }
+    lwp_signal_kill(parent, SIGCHLD, si_code, ext);
+}
+
 static void _resr_cleanup(struct rt_lwp *lwp)
 {
+    lwp_jobctrl_on_exit(lwp);
+
     LWP_LOCK(lwp);
     lwp_signal_detach(&lwp->signal);
 
@@ -1254,8 +1549,6 @@ static void _resr_cleanup(struct rt_lwp *lwp)
     }
     LWP_UNLOCK(lwp);
 
-    _pop_tty(lwp);
-
     /**
      * @brief Wakeup parent if it's waiting for this lwp, otherwise a signal
      *        will be sent to parent
@@ -1264,29 +1557,26 @@ static void _resr_cleanup(struct rt_lwp *lwp)
      * - the parent lwp (RW.)
      */
     LWP_LOCK(lwp);
-    if (lwp->parent)
+    if (lwp->parent &&
+        !lwp_sigismember(&lwp->parent->signal.sig_action_nocldwait, SIGCHLD))
     {
-        struct rt_thread *thread;
-
+        /* if successfully race to setup lwp->terminated before parent detach */
         LWP_UNLOCK(lwp);
-        if (!rt_list_isempty(&lwp->wait_list))
-        {
-            thread = RT_THREAD_LIST_NODE_ENTRY(lwp->wait_list.next);
-            thread->error = RT_EOK;
-            thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret;
-            rt_thread_resume(thread);
-        }
-        else
-        {
-            /* children cannot detach itself and must wait for parent to take care of it */
-            lwp_signal_kill(lwp->parent, SIGCHLD, CLD_EXITED, 0);
-        }
+
+        /**
+         * Note: children cannot detach itself and must wait for parent to take
+         * care of it
+         */
+        _notify_parent(lwp);
     }
     else
     {
         LWP_UNLOCK(lwp);
 
-        /* INFO: orphan hasn't parents to do the reap of pid */
+        /**
+         * if process is orphan, it doesn't have parent to do the recycling.
+         * Otherwise, its parent had setup a flag to mask out recycling event
+         */
         lwp_pid_put(lwp);
     }
 
@@ -1315,10 +1605,8 @@ static int _lwp_setaffinity(pid_t pid, int cpu)
     int ret = -1;
 
     lwp_pid_lock_take();
-    if(pid == 0)
-        lwp = lwp_self();
-    else
-        lwp = lwp_from_pid_locked(pid);
+    lwp = lwp_from_pid_locked(pid);
+
     if (lwp)
     {
 #ifdef RT_USING_SMP

+ 31 - 7
components/lwp/lwp_pid.h

@@ -11,14 +11,14 @@
 #ifndef LWP_PID_H__
 #define LWP_PID_H__
 
-#include "lwp.h"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#define LWP_CREATE_FLAG_NONE        0x0000
-#define LWP_CREATE_FLAG_ALLOC_PID   0x0001  /* allocate pid on lwp object create */
+#define LWP_CREATE_FLAG_NONE         0x0000
+#define LWP_CREATE_FLAG_ALLOC_PID    0x0001  /* allocate pid on lwp object create */
+#define LWP_CREATE_FLAG_INIT_USPACE  0x0002  /* do user space initialization */
+#define LWP_CREATE_FLAG_NOTRACE_EXEC 0x0004  /* not trace if execve() after fork() */
 
 struct rt_lwp;
 
@@ -46,6 +46,7 @@ void lwp_free(struct rt_lwp* lwp);
 int lwp_ref_inc(struct rt_lwp *lwp);
 int lwp_ref_dec(struct rt_lwp *lwp);
 
+struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid);
 struct rt_lwp* lwp_from_pid_locked(pid_t pid);
 pid_t lwp_to_pid(struct rt_lwp* lwp);
 
@@ -54,7 +55,29 @@ char* lwp_pid2name(int32_t pid);
 
 int lwp_getpid(void);
 
-pid_t lwp_waitpid(const pid_t pid, int *status, int options);
+struct rusage
+{
+    struct timeval ru_utime;
+    struct timeval ru_stime;
+
+    long ru_maxrss;
+    long ru_ixrss;
+    long ru_idrss;
+    long ru_isrss;
+    long ru_minflt;
+    long ru_majflt;
+    long ru_nswap;
+    long ru_inblock;
+    long ru_oublock;
+    long ru_msgsnd;
+    long ru_msgrcv;
+    long ru_nsignals;
+    long ru_nvcsw;
+    long ru_nivcsw;
+    long reserved[16];
+};
+pid_t lwp_waitpid(const pid_t pid, int *status, int options, struct rusage *ru);
+rt_err_t lwp_waitpid_kick(struct rt_lwp *parent, struct rt_lwp *self_lwp);
 pid_t waitpid(pid_t pid, int *status, int options);
 long list_process(void);
 
@@ -85,8 +108,9 @@ rt_inline void lwp_from_pid_release_lock(struct rt_lwp *lwp)
         lwp_ref_dec(lwp);
 }
 
-void lwp_thread_exit(rt_thread_t thread, rt_base_t status);
-void lwp_exit(struct rt_lwp *lwp, rt_base_t status);
+typedef rt_base_t lwp_status_t;
+void lwp_thread_exit(rt_thread_t thread, int status);
+void lwp_exit(struct rt_lwp *lwp, lwp_status_t status);
 
 #ifdef __cplusplus
 }

+ 0 - 459
components/lwp/lwp_pmutex.c

@@ -1,459 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- * 2021/01/02     bernard      the first version
- * 2022/12/18     bernard      fix the _m_lock to tid in user land.
- */
-
-#include "lwp_internal.h"
-#include <rtthread.h>
-#ifdef ARCH_MM_MMU
-#include <lwp_user_mm.h>
-#endif
-#include <sys/time.h>
-#include <syscall_generic.h>
-
-#define PMUTEX_NORMAL     0 /* Unable to recursion */
-#define PMUTEX_RECURSIVE  1 /* Can be recursion */
-#define PMUTEX_ERRORCHECK 2 /* This type of mutex provides error checking */
-
-struct rt_pmutex
-{
-    union
-    {
-        rt_mutex_t kmutex;
-        rt_sem_t ksem; /* use sem to emulate the mutex without recursive */
-    } lock;
-
-    struct lwp_avl_struct node;
-    struct rt_object *custom_obj;
-    rt_uint8_t type; /* pmutex type */
-};
-
-/*
- * userspace mutex definitions in musl
- */
-struct rt_umutex
-{
-    union
-    {
-        int __i[6];
-        volatile int __vi[6];
-        volatile void *volatile __p[6];
-    } __u;
-};
-#define _m_type     __u.__i[0]
-#define _m_lock     __u.__vi[1]
-#define _m_waiters  __u.__vi[2]
-#define _m_prev     __u.__p[3]
-#define _m_next     __u.__p[4]
-#define _m_count    __u.__i[5]
-
-static struct rt_mutex _pmutex_lock;
-
-static int pmutex_system_init(void)
-{
-    rt_mutex_init(&_pmutex_lock, "pmtxLock", RT_IPC_FLAG_FIFO);
-    return 0;
-}
-INIT_PREV_EXPORT(pmutex_system_init);
-
-static rt_err_t pmutex_destory(void *data)
-{
-    rt_err_t ret = -1;
-    struct rt_pmutex *pmutex = (struct rt_pmutex *)data;
-
-    if (pmutex)
-    {
-        lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 0);
-        /* remove pmutex from pmutext avl */
-        lwp_avl_remove(&pmutex->node, (struct lwp_avl_struct **)pmutex->node.data);
-        lwp_mutex_release_safe(&_pmutex_lock);
-
-        if (pmutex->type == PMUTEX_NORMAL)
-        {
-            rt_sem_delete(pmutex->lock.ksem);
-        }
-        else
-        {
-            rt_mutex_delete(pmutex->lock.kmutex);
-        }
-
-        /* release object */
-        rt_free(pmutex);
-        ret = 0;
-    }
-    return ret;
-}
-
-static struct rt_pmutex* pmutex_create(void *umutex, struct rt_lwp *lwp)
-{
-    struct rt_pmutex *pmutex = RT_NULL;
-    struct rt_object *obj = RT_NULL;
-    rt_ubase_t type;
-
-    if (!lwp)
-    {
-        return RT_NULL;
-    }
-
-    long *p = (long *)umutex;
-    /* umutex[0] bit[0-1] saved mutex type */
-    type = *p & 3;
-    if (type != PMUTEX_NORMAL && type != PMUTEX_RECURSIVE && type != PMUTEX_ERRORCHECK)
-    {
-        return RT_NULL;
-    }
-
-    pmutex = (struct rt_pmutex *)rt_malloc(sizeof(struct rt_pmutex));
-    if (!pmutex)
-    {
-        return RT_NULL;
-    }
-
-    if (type == PMUTEX_NORMAL)
-    {
-        pmutex->lock.ksem = rt_sem_create("pmutex", 1, RT_IPC_FLAG_PRIO);
-        if (!pmutex->lock.ksem)
-        {
-            rt_free(pmutex);
-            return RT_NULL;
-        }
-    }
-    else
-    {
-        pmutex->lock.kmutex = rt_mutex_create("pmutex", RT_IPC_FLAG_PRIO);
-        if (!pmutex->lock.kmutex)
-        {
-            rt_free(pmutex);
-            return RT_NULL;
-        }
-    }
-
-    obj = rt_custom_object_create("pmutex", (void *)pmutex, pmutex_destory);
-    if (!obj)
-    {
-        if (pmutex->type == PMUTEX_NORMAL)
-        {
-            rt_sem_delete(pmutex->lock.ksem);
-        }
-        else
-        {
-            rt_mutex_delete(pmutex->lock.kmutex);
-        }
-        rt_free(pmutex);
-        return RT_NULL;
-    }
-    pmutex->node.avl_key = (avl_key_t)umutex;
-    pmutex->node.data = &lwp->address_search_head;
-    pmutex->custom_obj = obj;
-    pmutex->type = type;
-
-    /* insert into pmutex head */
-    lwp_avl_insert(&pmutex->node, &lwp->address_search_head);
-    return pmutex;
-}
-
-static struct rt_pmutex* pmutex_get(void *umutex, struct rt_lwp *lwp)
-{
-    struct rt_pmutex *pmutex = RT_NULL;
-    struct lwp_avl_struct *node = RT_NULL;
-
-    node = lwp_avl_find((avl_key_t)umutex, lwp->address_search_head);
-    if (!node)
-    {
-        return RT_NULL;
-    }
-    pmutex = rt_container_of(node, struct rt_pmutex, node);
-    return pmutex;
-}
-
-static int _pthread_mutex_init(void *umutex)
-{
-    struct rt_lwp *lwp = RT_NULL;
-    struct rt_pmutex *pmutex = RT_NULL;
-    rt_err_t lock_ret = 0;
-
-    /* umutex union is 6 x (void *) */
-    if (!lwp_user_accessable(umutex, sizeof(void *) * 6))
-    {
-        rt_set_errno(EINVAL);
-        return -EINVAL;
-    }
-
-    lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
-    if (lock_ret != RT_EOK)
-    {
-        rt_set_errno(EAGAIN);
-        return -EAGAIN;
-    }
-
-    lwp = lwp_self();
-    pmutex = pmutex_get(umutex, lwp);
-    if (pmutex == RT_NULL)
-    {
-        /* create a pmutex according to this umutex */
-        pmutex = pmutex_create(umutex, lwp);
-        if (pmutex == RT_NULL)
-        {
-            rt_mutex_release(&_pmutex_lock);
-            rt_set_errno(ENOMEM);
-            return -ENOMEM;
-        }
-        if (lwp_user_object_add(lwp, pmutex->custom_obj) != 0)
-        {
-            rt_custom_object_destroy(pmutex->custom_obj);
-            rt_set_errno(ENOMEM);
-            return -ENOMEM;
-        }
-    }
-    else
-    {
-        lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 1);
-        if (pmutex->type == PMUTEX_NORMAL)
-        {
-            pmutex->lock.ksem->value = 1;
-        }
-        else
-        {
-            pmutex->lock.kmutex->owner    = RT_NULL;
-            pmutex->lock.kmutex->priority = 0xFF;
-            pmutex->lock.kmutex->hold     = 0;
-            pmutex->lock.kmutex->ceiling_priority = 0xFF;
-        }
-        lwp_mutex_release_safe(&_pmutex_lock);
-    }
-
-    rt_mutex_release(&_pmutex_lock);
-
-    return 0;
-}
-
-static int _pthread_mutex_lock_timeout(void *umutex, struct timespec *timeout)
-{
-    struct rt_lwp *lwp = RT_NULL;
-    struct rt_pmutex *pmutex = RT_NULL;
-    struct rt_umutex *umutex_p = (struct rt_umutex*)umutex;
-    rt_err_t lock_ret = 0;
-    rt_int32_t time = RT_WAITING_FOREVER;
-
-    if (!lwp_user_accessable((void *)umutex, sizeof(struct rt_umutex)))
-    {
-        rt_set_errno(EINVAL);
-        return -EINVAL;
-    }
-
-    if (timeout)
-    {
-        if (!lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
-        {
-            rt_set_errno(EINVAL);
-            return -EINVAL;
-        }
-        time = rt_timespec_to_tick(timeout);
-    }
-
-    lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
-    if (lock_ret != RT_EOK)
-    {
-        rt_set_errno(EINTR);
-        return -EINTR;
-    }
-
-    lwp = lwp_self();
-    pmutex = pmutex_get(umutex, lwp);
-    if (pmutex == RT_NULL)
-    {
-        rt_mutex_release(&_pmutex_lock);
-        rt_set_errno(EINVAL);
-        return -ENOMEM;  /* umutex not recored in kernel */
-    }
-
-    rt_mutex_release(&_pmutex_lock);
-
-    switch (pmutex->type)
-    {
-    case PMUTEX_NORMAL:
-        lock_ret = rt_sem_take_interruptible(pmutex->lock.ksem, time);
-        break;
-    case PMUTEX_RECURSIVE:
-        lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time);
-        if (lock_ret == RT_EOK)
-        {
-            umutex_p->_m_lock = rt_thread_self()->tid;
-        }
-        break;
-    case PMUTEX_ERRORCHECK:
-        lock_ret = lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 1);
-        if (lock_ret != RT_EOK)
-        {
-            return -EINTR;
-        }
-        if (pmutex->lock.kmutex->owner == rt_thread_self())
-        {
-            lwp_mutex_release_safe(&_pmutex_lock);
-            return -EDEADLK;
-        }
-        lwp_mutex_release_safe(&_pmutex_lock);
-        lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time);
-        if (lock_ret == RT_EOK)
-        {
-            umutex_p->_m_lock = rt_thread_self()->tid;
-        }
-        break;
-    default: /* unknown type */
-        return -EINVAL;
-    }
-
-    if (lock_ret != RT_EOK)
-    {
-        if (lock_ret == -RT_ETIMEOUT)
-        {
-            if (time == 0) /* timeout is 0, means try lock failed */
-            {
-                rt_set_errno(EBUSY);
-                return -EBUSY;
-            }
-            else
-            {
-                rt_set_errno(ETIMEDOUT);
-                return -ETIMEDOUT;
-            }
-        }
-        else if (lock_ret == -RT_EINTR)
-        {
-            rt_set_errno(EINTR);
-            return -EINTR;
-        }
-        else
-        {
-            rt_set_errno(EAGAIN);
-            return -EAGAIN;
-        }
-    }
-    return 0;
-}
-
-static int _pthread_mutex_unlock(void *umutex)
-{
-    rt_err_t lock_ret = 0;
-    struct rt_lwp *lwp = RT_NULL;
-    struct rt_pmutex *pmutex = RT_NULL;
-    struct rt_umutex *umutex_p = (struct rt_umutex*)umutex;
-
-    lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
-    if (lock_ret != RT_EOK)
-    {
-        rt_set_errno(EAGAIN);
-        return -EAGAIN;
-    }
-
-    lwp = lwp_self();
-    pmutex = pmutex_get(umutex, lwp);
-    if (pmutex == RT_NULL)
-    {
-        rt_mutex_release(&_pmutex_lock);
-        rt_set_errno(EPERM);
-        return -EPERM;//unlock static mutex of unlock state
-    }
-
-    rt_mutex_release(&_pmutex_lock);
-
-    switch (pmutex->type)
-    {
-    case PMUTEX_NORMAL:
-        if(pmutex->lock.ksem->value >=1)
-        {
-            rt_set_errno(EPERM);
-            return -EPERM;//unlock dynamic mutex of unlock state
-        }
-        else
-        {
-            lock_ret = rt_sem_release(pmutex->lock.ksem);
-        }
-        break;
-    case PMUTEX_RECURSIVE:
-    case PMUTEX_ERRORCHECK:
-        lock_ret = rt_mutex_release(pmutex->lock.kmutex);
-        if ((lock_ret == RT_EOK) && pmutex->lock.kmutex->owner == NULL)
-        {
-            umutex_p->_m_lock = 0;
-        }
-        break;
-    default: /* unknown type */
-        return -EINVAL;
-    }
-
-    if (lock_ret != RT_EOK)
-    {
-        rt_set_errno(EPERM);
-        return -EPERM;
-    }
-    return 0;
-}
-
-static int _pthread_mutex_destroy(void *umutex)
-{
-    struct rt_lwp *lwp = RT_NULL;
-    struct rt_pmutex *pmutex = RT_NULL;
-    rt_err_t lock_ret = 0;
-
-    lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
-    if (lock_ret != RT_EOK)
-    {
-        rt_set_errno(EAGAIN);
-        return -EAGAIN;
-    }
-
-    lwp = lwp_self();
-    pmutex = pmutex_get(umutex, lwp);
-    if (pmutex == RT_NULL)
-    {
-        rt_mutex_release(&_pmutex_lock);
-        rt_set_errno(EINVAL);
-        return -EINVAL;
-    }
-
-    lwp_user_object_delete(lwp, pmutex->custom_obj);
-    rt_mutex_release(&_pmutex_lock);
-
-    return 0;
-}
-
-sysret_t sys_pmutex(void *umutex, int op, void *arg)
-{
-    int ret = -EINVAL;
-
-    switch (op)
-    {
-        case PMUTEX_INIT:
-            ret = _pthread_mutex_init(umutex);
-            break;
-        case PMUTEX_LOCK:
-            ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg);
-            if (ret == -ENOMEM)
-            {
-                /* lock not init, try init it and lock again. */
-                ret = _pthread_mutex_init(umutex);
-                if (ret == 0)
-                {
-                    ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg);
-                }
-            }
-            break;
-        case PMUTEX_UNLOCK:
-            ret = _pthread_mutex_unlock(umutex);
-            break;
-        case PMUTEX_DESTROY:
-            ret = _pthread_mutex_destroy(umutex);
-            break;
-        default:
-            rt_set_errno(EINVAL);
-            break;
-    }
-    return ret;
-}

+ 432 - 0
components/lwp/lwp_session.c

@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-17     xqyjlj       the first version
+ * 2023-11-29     Shell        Add direct reference of sess for group
+ */
+
+#include "lwp.h"
+#include "lwp_internal.h"
+#include "lwp_syscall.h"
+#include "terminal/terminal.h"
+
+#define DBG_TAG "lwp.session"
+#define DBG_LVL DBG_WARNING
+#include <rtdbg.h>
+
+rt_session_t lwp_session_find(pid_t sid)
+{
+    rt_base_t level;
+    rt_session_t session = RT_NULL;
+    rt_list_t *node = RT_NULL;
+    struct rt_object_information *information = RT_NULL;
+
+    information = rt_object_get_information(RT_Object_Class_Session);
+
+    /* parameter check */
+    if ((sid < 0) || (information == RT_NULL))
+    {
+        return RT_NULL;
+    }
+
+    if (sid == 0)
+    {
+        sid = lwp_getpid();
+    }
+
+    /* enter critical */
+    level = rt_spin_lock_irqsave(&(information->spinlock));
+
+    /* try to find session */
+    rt_list_for_each(node, &(information->object_list))
+    {
+        session = (rt_session_t)rt_list_entry(node, struct rt_object, list);
+        if (session->sid == sid)
+        {
+            rt_spin_unlock_irqrestore(&(information->spinlock), level);
+
+            return session;
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&(information->spinlock), level);
+
+    return RT_NULL;
+}
+
+rt_session_t lwp_session_create(rt_lwp_t leader)
+{
+    rt_session_t session = RT_NULL;
+
+    /* parameter check */
+    if (leader == RT_NULL)
+    {
+        return RT_NULL;
+    }
+
+    session = rt_malloc(sizeof(struct rt_session));
+    if (session != RT_NULL)
+    {
+        rt_object_init(&(session->object), RT_Object_Class_Session, "session");
+        rt_list_init(&(session->processgroup));
+        rt_mutex_init(&(session->mutex), "session", RT_IPC_FLAG_PRIO);
+        session->leader = leader;
+        session->sid = leader->pid;
+        lwp_pgrp_update_children_info(leader->pgrp, session->sid, leader->pgid);
+        session->foreground_pgid = session->sid;
+        session->ctty = RT_NULL;
+    }
+    return session;
+}
+
+int lwp_session_delete(rt_session_t session)
+{
+    int retry = 1;
+    lwp_tty_t ctty;
+
+    /* parameter check */
+    if (session == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    /* clear children sid */
+    lwp_session_update_children_info(session, 0);
+
+    while (retry)
+    {
+        retry = 0;
+        ctty = session->ctty;
+        SESS_LOCK_NESTED(session);
+
+        if (session->ctty == ctty)
+        {
+            if (ctty)
+            {
+                SESS_UNLOCK(session);
+
+                /**
+                 * Note: it's safe to release the session lock now. Even if someone
+                 * race to acquire the tty, it's safe under protection of tty_lock()
+                 * and the check inside
+                 */
+                tty_lock(ctty);
+                tty_rel_sess(ctty, session);
+                session->ctty = RT_NULL;
+            }
+            else
+            {
+                SESS_UNLOCK(session);
+            }
+        }
+        else
+        {
+            SESS_UNLOCK(session);
+            retry = 1;
+        }
+    }
+
+    rt_object_detach(&(session->object));
+    rt_mutex_detach(&(session->mutex));
+    rt_free(session);
+
+    return 0;
+}
+
+int lwp_session_insert(rt_session_t session, rt_processgroup_t group)
+{
+    /* parameter check */
+    if (session == RT_NULL || group == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    SESS_LOCK_NESTED(session);
+    PGRP_LOCK_NESTED(group);
+
+    group->sid = session->sid;
+    group->session = session;
+    lwp_pgrp_update_children_info(group, session->sid, group->pgid);
+    rt_list_insert_after(&(session->processgroup), &(group->pgrp_list_node));
+
+    PGRP_UNLOCK(group);
+    SESS_UNLOCK(session);
+
+    return 0;
+}
+
+int lwp_session_remove(rt_session_t session, rt_processgroup_t group)
+{
+    rt_bool_t is_empty = RT_FALSE;
+
+    /* parameter check */
+    if (session == RT_NULL || group == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    SESS_LOCK_NESTED(session);
+    PGRP_LOCK_NESTED(group);
+
+    rt_list_remove(&(group->pgrp_list_node));
+    /* clear children sid */
+    lwp_pgrp_update_children_info(group, 0, group->pgid);
+    group->sid = 0;
+    group->session = RT_NULL;
+
+    PGRP_UNLOCK(group);
+
+    is_empty = rt_list_isempty(&(session->processgroup));
+
+    SESS_UNLOCK(session);
+
+    if (is_empty)
+    {
+        lwp_session_delete(session);
+        return 1;
+    }
+
+    return 0;
+}
+
+int lwp_session_move(rt_session_t session, rt_processgroup_t group)
+{
+    rt_session_t prev_session;
+
+    /* parameter check */
+    if (session == RT_NULL || group == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    if (lwp_sid_get_bysession(session) == lwp_sid_get_bypgrp(group))
+    {
+        return 0;
+    }
+
+    SESS_LOCK(session);
+
+    prev_session = group->session;
+    if (prev_session)
+    {
+        SESS_LOCK(prev_session);
+        lwp_session_remove(prev_session, group);
+        SESS_UNLOCK(prev_session);
+    }
+
+    lwp_session_insert(session, group);
+
+    SESS_UNLOCK(session);
+
+    return 0;
+}
+
+int lwp_session_update_children_info(rt_session_t session, pid_t sid)
+{
+    rt_list_t *node = RT_NULL;
+    rt_processgroup_t group = RT_NULL;
+
+    if (session == RT_NULL)
+    {
+        return -EINVAL;
+    }
+
+    SESS_LOCK_NESTED(session);
+
+    rt_list_for_each(node, &(session->processgroup))
+    {
+        group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node);
+        PGRP_LOCK_NESTED(group);
+        if (sid != -1)
+        {
+            group->sid = sid;
+            group->session = session;
+            lwp_pgrp_update_children_info(group, sid, group->pgid);
+        }
+        PGRP_UNLOCK(group);
+    }
+
+    SESS_UNLOCK(session);
+    return 0;
+}
+
+int lwp_session_set_foreground(rt_session_t session, pid_t pgid)
+{
+    rt_processgroup_t group = RT_NULL;
+    rt_list_t *node = RT_NULL;
+    rt_bool_t is_contains = RT_FALSE;
+
+    /* parameter check */
+    if (session == RT_NULL || pgid <= 0)
+    {
+        return -EINVAL;
+    }
+
+    SESS_LOCK(session);
+
+    rt_list_for_each(node, &(session->processgroup))
+    {
+        group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node);
+        PGRP_LOCK(group);
+        if (group->pgid == pgid)
+        {
+            is_contains = RT_TRUE;
+        }
+        PGRP_UNLOCK(group);
+    }
+
+    if (is_contains)
+    {
+        session->foreground_pgid = pgid;
+        // TODO: maybe notify tty
+    }
+
+    SESS_UNLOCK(session);
+
+    return is_contains ? 0 : -EINVAL;
+}
+
+/**
+ * setsid() creates a new session if the calling process is not a process group leader.
+ *  The calling process is the leader of the new session (i.e., its session ID is made the same as its process ID).
+ *  The calling process also becomes the process group leader of a new process group in the session
+ *      (i.e., its process group ID is made the same as its process ID).
+ */
+sysret_t sys_setsid(void)
+{
+    rt_lwp_t process;
+    pid_t pid;
+    rt_processgroup_t group;
+    rt_session_t session;
+    sysret_t err = 0;
+
+    process = lwp_self();
+    pid = lwp_to_pid(process);
+
+    /**
+     * if the calling process is already a process group leader.
+     */
+    if (lwp_pgrp_find(pid))
+    {
+        err = -EPERM;
+        goto exit;
+    }
+
+    group = lwp_pgrp_create(process);
+    if (group)
+    {
+        lwp_pgrp_move(group, process);
+        session = lwp_session_create(process);
+        if (session)
+        {
+            lwp_session_move(session, group);
+        }
+        else
+        {
+            lwp_pgrp_delete(group);
+        }
+        err = lwp_sid_get_bysession(session);
+    }
+    else
+    {
+        err = -ENOMEM;
+    }
+
+
+exit:
+    return err;
+}
+
+/**
+ * getsid() returns the session ID of the process with process ID pid.
+ *  If pid is 0, getsid() returns the session ID of the calling process.
+ */
+sysret_t sys_getsid(pid_t pid)
+{
+    rt_lwp_t process, self_process;
+    pid_t sid;
+
+    lwp_pid_lock_take();
+    process = lwp_from_pid_locked(pid);
+    lwp_pid_lock_release();
+
+    if (process == RT_NULL)
+    {
+        return -ESRCH;
+    }
+
+    self_process = lwp_self();
+    sid = lwp_sid_get_byprocess(process);
+
+    if (sid != lwp_sid_get_byprocess(self_process))
+    {
+        /**
+         * A process with process ID pid exists, but it is not in the same session as the calling process,
+         *  and the implementation considers this an error.
+         *
+         * Note: Linux does not return EPERM.
+         */
+        return -EPERM;
+    }
+
+    return sid;
+}
+
+#ifdef RT_USING_FINSH
+
+#include "finsh.h"
+
+long list_session(void)
+{
+    int count = 0, index;
+    rt_session_t *sessions;
+    rt_session_t session;
+    rt_thread_t thread;
+    char name[RT_NAME_MAX];
+
+    rt_kprintf("SID  leader process\n");
+    rt_kprintf("---- ----------------\n");
+
+    count = rt_object_get_length(RT_Object_Class_Session);
+    if (count > 0)
+    {
+        /* get  pointers */
+        sessions = (rt_session_t *)rt_calloc(count, sizeof(rt_session_t));
+        if (sessions)
+        {
+            index = rt_object_get_pointers(RT_Object_Class_Session, (rt_object_t *)sessions, count);
+            if (index > 0)
+            {
+                for (index = 0; index < count; index++)
+                {
+                    struct rt_session se;
+                    session = sessions[index];
+                    SESS_LOCK(session);
+                    rt_memcpy(&se, session, sizeof(struct rt_session));
+                    SESS_UNLOCK(session);
+
+                    if (se.leader && se.leader)
+                    {
+                        thread = rt_list_entry(se.leader->t_grp.prev, struct rt_thread, sibling);
+                        rt_strncpy(name, thread->parent.name, RT_NAME_MAX);
+                    }
+                    else
+                    {
+                        rt_strncpy(name, "nil", RT_NAME_MAX);
+                    }
+
+                    rt_kprintf("%4d %-*.*s\n", se.sid, RT_NAME_MAX, RT_NAME_MAX, name);
+                }
+            }
+            rt_free(sessions);
+        }
+    }
+
+    return 0;
+}
+MSH_CMD_EXPORT(list_session, list session);
+#endif

+ 0 - 27
components/lwp/lwp_setsid.c

@@ -1,27 +0,0 @@
-#include <rthw.h>
-#include <rtthread.h>
-
-#include "lwp.h"
-//#include "lwp_tid.h"
-#include "lwp_pid.h"
-
-int setsid(void)
-{
-    int err = -EPERM;
-    struct rt_thread *current_thread = rt_thread_self();
-    struct rt_lwp *current_lwp = (struct rt_lwp *)rt_thread_self()->lwp;
-
-    if (current_lwp->session == current_thread->tid)
-    {
-        return err;
-    }
-
-    current_lwp->session = current_thread->tid;
-    current_lwp->__pgrp = current_thread->tid;
-    current_lwp->leader = 1;
-    current_lwp->tty = RT_NULL;
-    current_lwp->tty_old_pgrp = 0;
-
-    err = current_lwp->session;
-    return err;
-}

+ 456 - 86
components/lwp/lwp_signal.c

@@ -10,6 +10,9 @@
  * 2023-07-04     Shell        Support siginfo, sigqueue
  *                             remove lwp_signal_backup/restore() to reduce architecture codes
  *                             update the generation, pending and delivery routines
+ * 2023-11-22     Shell        Support for job control signal. Fixup of signal catch while
+ *                             some of the signals is blocked, but no more further dequeue is applied.
+ *                             Add itimer support
  */
 #define __RT_IPC_SOURCE__
 #define DBG_TAG "lwp.signal"
@@ -24,7 +27,7 @@
 #include "sys/signal.h"
 #include "syscall_generic.h"
 
-static lwp_siginfo_t siginfo_create(int signo, int code, int value)
+static lwp_siginfo_t siginfo_create(rt_thread_t current, int signo, int code, lwp_siginfo_ext_t ext)
 {
     lwp_siginfo_t siginfo;
     struct rt_lwp *self_lwp;
@@ -35,10 +38,10 @@ static lwp_siginfo_t siginfo_create(int signo, int code, int value)
     {
         siginfo->ksiginfo.signo = signo;
         siginfo->ksiginfo.code = code;
-        siginfo->ksiginfo.value = value;
+        siginfo->ext = ext;
 
-        self_lwp = lwp_self();
-        self_thr = rt_thread_self();
+        self_thr = current;
+        self_lwp = current->lwp;
         if (self_lwp)
         {
             siginfo->ksiginfo.from_pid = self_lwp->pid;
@@ -56,6 +59,12 @@ static lwp_siginfo_t siginfo_create(int signo, int code, int value)
 
 rt_inline void siginfo_delete(lwp_siginfo_t siginfo)
 {
+    if (siginfo->ext)
+    {
+        rt_free(siginfo->ext);
+        siginfo->ext = RT_NULL;
+    }
+
     rt_free(siginfo);
 }
 
@@ -296,10 +305,30 @@ static lwp_siginfo_t sigqueue_dequeue(lwp_sigqueue_t sigqueue, int signo)
     return found;
 }
 
+/**
+ * Discard all the signal matching `signo` in sigqueue
+ */
 static void sigqueue_discard(lwp_sigqueue_t sigqueue, int signo)
 {
     lwp_siginfo_t queuing_si;
-    while (!sigqueue_isempty(sigqueue))
+    while (sigqueue_ismember(sigqueue, signo))
+    {
+        queuing_si = sigqueue_dequeue(sigqueue, signo);
+        siginfo_delete(queuing_si);
+    }
+}
+
+/**
+ * Discard all the queuing signals in sigset
+ */
+static void sigqueue_discard_sigset(lwp_sigqueue_t sigqueue, lwp_sigset_t *sigset)
+{
+    lwp_siginfo_t queuing_si;
+    lwp_sigset_t mask;
+    int signo;
+
+    _signotsets(&mask, sigset);
+    while ((signo = sigqueue_peek(sigqueue, &mask)) != 0)
     {
         queuing_si = sigqueue_dequeue(sigqueue, signo);
         siginfo_delete(queuing_si);
@@ -312,11 +341,21 @@ RT_STATIC_ASSERT(lp_width_same, sizeof(void *) == sizeof(long));
 /** translate lwp siginfo to user siginfo_t  */
 rt_inline void siginfo_k2u(lwp_siginfo_t ksigi, siginfo_t *usigi)
 {
+    int signo = ksigi->ksiginfo.signo;
     usigi->si_code = ksigi->ksiginfo.code;
-    usigi->si_signo = ksigi->ksiginfo.signo;
-    usigi->si_value.sival_ptr = (void *)ksigi->ksiginfo.value;
+    usigi->si_signo = signo;
     usigi->si_pid = ksigi->ksiginfo.from_pid;
 
+    if (ksigi->ext)
+    {
+        if (signo == SIGCHLD)
+        {
+            usigi->si_status = ksigi->ext->sigchld.status;
+            usigi->si_utime = ksigi->ext->sigchld.stime;
+            usigi->si_stime = ksigi->ext->sigchld.utime;
+        }
+    }
+
     /* deprecated field */
     usigi->si_errno = 0;
 }
@@ -417,7 +456,10 @@ rt_err_t lwp_signal_init(struct lwp_signal *sig)
     memset(&sig->sig_action_onstack, 0, sizeof(sig->sig_action_onstack));
     memset(&sig->sig_action_restart, 0, sizeof(sig->sig_action_restart));
     memset(&sig->sig_action_siginfo, 0, sizeof(sig->sig_action_siginfo));
+    memset(&sig->sig_action_nocldstop, 0, sizeof(sig->sig_action_nocldstop));
+    memset(&sig->sig_action_nocldwait, 0, sizeof(sig->sig_action_nocldwait));
     lwp_sigqueue_init(&sig->sig_queue);
+
     return rc;
 }
 
@@ -433,17 +475,19 @@ rt_err_t lwp_signal_detach(struct lwp_signal *signal)
 
 int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag)
 {
-    struct rt_lwp *lwp = (struct rt_lwp*)thread->lwp;
+    struct rt_lwp *lwp = (struct rt_lwp *)thread->lwp;
+    lwp_sigset_t sigmask = thread->signal.sigset_mask;
     int ret = 0;
 
+    _sigaddset(&sigmask, SIGCONT);
     switch (suspend_flag)
     {
         case RT_INTERRUPTIBLE:
-            if (!sigqueue_isempty(_SIGQ(thread)))
+            if (sigqueue_peek(_SIGQ(thread), &sigmask))
             {
                 break;
             }
-            if (thread->lwp && !sigqueue_isempty(_SIGQ(lwp)))
+            if (thread->lwp && sigqueue_peek(_SIGQ(lwp), &sigmask))
             {
                 break;
             }
@@ -470,84 +514,202 @@ int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag)
     return ret;
 }
 
-void lwp_thread_signal_catch(void *exp_frame)
+rt_inline rt_bool_t _is_jobctl_signal(rt_lwp_t lwp, int signo)
 {
-    int signo = 0;
-    struct rt_thread *thread;
-    struct rt_lwp *lwp;
-    lwp_siginfo_t siginfo = 0;
-    lwp_sigqueue_t pending;
-    lwp_sigset_t *sig_mask;
-    lwp_sigset_t save_sig_mask;
-    lwp_sigset_t new_sig_mask;
-    lwp_sighandler_t handler = 0;
-    siginfo_t usiginfo;
-    siginfo_t *p_usi = RT_NULL;
+    lwp_sigset_t jobctl_sigset = lwp_sigset_init(LWP_SIG_JOBCTL_SET);
 
-    thread = rt_thread_self();
-    lwp = (struct rt_lwp*)thread->lwp;
+    return lwp_sigismember(&jobctl_sigset, signo);
+}
 
-    RT_ASSERT(!!lwp);
-    LWP_LOCK(lwp);
+rt_inline rt_bool_t _is_stop_signal(rt_lwp_t lwp, int signo)
+{
+    lwp_sigset_t stop_sigset = lwp_sigset_init(LWP_SIG_STOP_SET);
+
+    return lwp_sigismember(&stop_sigset, signo);
+}
+
+rt_inline rt_bool_t _need_notify_status_changed(rt_lwp_t lwp, int signo)
+{
+    RT_ASSERT(lwp_sigismember(&lwp_sigset_init(LWP_SIG_JOBCTL_SET), signo));
+    return !lwp_sigismember(&lwp->signal.sig_action_nocldstop, SIGCHLD);
+}
+
+/**
+ * wakeup the waitpid_waiters if any, and try to generate SIGCHLD if they are
+ * not disable explicitly by user.
+ *
+ * TODO: This event is always per-process and doesn't make whole lot of
+ * sense for ptracers, who shouldn't consume the state via wait(2) either,
+ * but, for backward compatibility, notify the ptracer of the group leader
+ * too unless it's gonna be a duplicate.
+ */
+static void _notify_parent_and_leader(rt_lwp_t child_lwp, rt_thread_t child_thr, int trig_signo, rt_bool_t is_stop)
+{
+    int si_code;
+    lwp_siginfo_ext_t ext;
+    rt_lwp_t parent_lwp = child_lwp->parent;
+
+    if (!parent_lwp)
+        return ;
 
-    /* check if signal exist */
-    if (!sigqueue_isempty(_SIGQ(thread)))
+    /* prepare the event data for parent to query */
+    if (is_stop)
     {
-        pending = _SIGQ(thread);
-        sig_mask = &thread->signal.sigset_mask;
+        si_code = CLD_STOPPED;
+        child_lwp->lwp_status = LWP_CREATE_STAT_STOPPED(trig_signo);
     }
-    else if (!sigqueue_isempty(_SIGQ(lwp)))
+    else
     {
-        pending = _SIGQ(lwp);
-        sig_mask = &thread->signal.sigset_mask;
+        si_code = CLD_CONTINUED;
+        child_lwp->lwp_status = LWP_CREATE_STAT_CONTINUED;
     }
-    else
+
+    /* wakeup waiter on waitpid(2) */
+    lwp_waitpid_kick(parent_lwp, child_lwp);
+
+    if (_need_notify_status_changed(parent_lwp, trig_signo))
     {
-        pending = RT_NULL;
+        ext = rt_malloc(sizeof(struct lwp_siginfo_ext));
+        if (ext)
+        {
+            ext->sigchld.status = trig_signo;
+
+            /* TODO: signal usage is not supported */
+            ext->sigchld.stime = child_thr->system_time;
+            ext->sigchld.utime = child_thr->user_time;
+        }
+
+        /* generate SIGCHLD for parent */
+        lwp_signal_kill(parent_lwp, SIGCHLD, si_code, ext);
     }
+}
+
+static int _do_signal_wakeup(rt_thread_t thread, int sig);
+static rt_err_t _stop_thread_locked(rt_lwp_t self_lwp, rt_thread_t cur_thr, int signo,
+                                    lwp_siginfo_t si, lwp_sigqueue_t sq)
+{
+    rt_err_t error;
+    int jobctl_stopped = self_lwp->jobctl_stopped;
+    rt_thread_t iter;
 
-    if (pending)
+    /* race to setup jobctl stopped flags */
+    if (!jobctl_stopped)
     {
-        /* peek the pending signal */
-        signo = sigqueue_peek(pending, sig_mask);
-        if (signo)
+        self_lwp->jobctl_stopped = RT_TRUE;
+        self_lwp->wait_reap_stp = RT_FALSE;
+        rt_list_for_each_entry(iter, &self_lwp->t_grp, sibling)
         {
-            siginfo = sigqueue_dequeue(pending, signo);
-            RT_ASSERT(siginfo != RT_NULL);
-            handler = _get_sighandler_locked(lwp, signo);
+            if (iter != cur_thr)
+                _do_signal_wakeup(iter, signo);
+        }
+    }
 
-            /* IGN signal will never be queued */
-            RT_ASSERT(handler != LWP_SIG_ACT_IGN);
+    /**
+     * raise the event again so siblings is able to catch it again.
+     * `si` will be discarded while SIGCONT is generatd
+     */
+    sigqueue_enqueue(sq, si);
 
-            /* copy the blocked signal mask from the registered signal action */
-            memcpy(&new_sig_mask, &lwp->signal.sig_action_mask[signo - 1], sizeof(new_sig_mask));
+    /* release the lwp lock so we can happily suspend */
+    LWP_UNLOCK(self_lwp);
 
-            if (!_sigismember(&lwp->signal.sig_action_nodefer, signo))
-                _sigaddset(&new_sig_mask, signo);
+    rt_set_errno(RT_EOK);
 
-            _thread_signal_mask(thread, LWP_SIG_MASK_CMD_BLOCK, &new_sig_mask, &save_sig_mask);
+    /* After suspension, only the SIGKILL and SIGCONT will wake this thread up */
+    error = rt_thread_suspend_with_flag(cur_thr, RT_KILLABLE);
+    if (error == RT_EOK)
+    {
+        rt_schedule();
+        error = rt_get_errno();
+        error = error > 0 ? -error : error;
+    }
 
-            /* siginfo is need for signal action */
-            if (_sigismember(&lwp->signal.sig_action_siginfo, signo))
-            {
-                siginfo_k2u(siginfo, &usiginfo);
-                p_usi = &usiginfo;
-            }
-            else
-                p_usi = RT_NULL;
-        }
+    if (!jobctl_stopped &&
+        (sigqueue_ismember(_SIGQ(self_lwp), SIGCONT) ||
+        sigqueue_ismember(_SIGQ(cur_thr), SIGCONT)))
+    {
+        /**
+         * if we are resumed by a SIGCONT and we are the winner of racing
+         * notify parent of the incoming event
+         */
+        _notify_parent_and_leader(self_lwp, cur_thr, SIGCONT, RT_FALSE);
     }
+
+    /* reacquire the lock since we release it before */
+    LWP_LOCK(self_lwp);
+
+    return error;
+}
+
+static void _catch_signal_locked(rt_lwp_t lwp, rt_thread_t thread, int signo,
+                                 lwp_siginfo_t siginfo, lwp_sighandler_t handler,
+                                 void *exp_frame)
+{
+    lwp_sigset_t new_sig_mask;
+    lwp_sigset_t save_sig_mask;
+    siginfo_t usiginfo;
+    siginfo_t *p_usi;
+
+    /* siginfo is need for signal action */
+    if (_sigismember(&lwp->signal.sig_action_siginfo, signo))
+    {
+        siginfo_k2u(siginfo, &usiginfo);
+        p_usi = &usiginfo;
+    }
+    else
+    {
+        p_usi = RT_NULL;
+    }
+
+    /**
+     * lock is acquired by caller. Release it so that we can happily go to the
+     * signal handler in user space
+     */
     LWP_UNLOCK(lwp);
 
-    if (pending && signo)
+    siginfo_delete(siginfo);
+
+    /* signal default handler */
+    if (handler == LWP_SIG_ACT_DFL)
     {
-        siginfo_delete(siginfo);
+        lwp_sigset_t ign_sigset;
 
-        /* signal default handler */
-        if (handler == LWP_SIG_ACT_DFL)
+        ign_sigset = lwp_sigset_init(LWP_SIG_IGNORE_SET);
+        if (signo == SIGCONT)
+        {
+            arch_syscall_set_errno(exp_frame, EINTR, ERESTART);
+            arch_thread_signal_enter(signo, p_usi, exp_frame, 0, &thread->signal.sigset_mask);
+        }
+        else if (!lwp_sigismember(&ign_sigset, signo) && !lwp->sig_protected)
         {
+            /* for those defautl handler is to terminate process */
             LOG_D("%s: default handler; and exit", __func__);
-            sys_exit_group(0);
+
+            /* TODO: coredump if neccessary */
+            lwp_exit(lwp, LWP_CREATE_STAT_SIGNALED(signo, 0));
+        }
+        /**
+         * otherwise is to ignore the signal,
+         * -> then reacquire the lock and return
+         */
+    }
+    else if (handler == LWP_SIG_ACT_IGN)
+    {
+        /* do nothing */
+    }
+    else
+    {
+        /* copy the blocked signal mask from the registered signal action */
+        memcpy(&new_sig_mask, &lwp->signal.sig_action_mask[signo - 1], sizeof(new_sig_mask));
+
+        if (!_sigismember(&lwp->signal.sig_action_nodefer, signo))
+            _sigaddset(&new_sig_mask, signo);
+
+        _thread_signal_mask(thread, LWP_SIG_MASK_CMD_BLOCK, &new_sig_mask, &save_sig_mask);
+
+        if (_sigismember(&lwp->signal.sig_action_restart, signo))
+        {
+            arch_syscall_set_errno(exp_frame, EINTR, ERESTART);
         }
 
         /**
@@ -557,18 +719,92 @@ void lwp_thread_signal_catch(void *exp_frame)
          */
         LOG_D("%s: enter signal handler(signo=%d) at %p", __func__, signo, handler);
         arch_thread_signal_enter(signo, p_usi, exp_frame, handler, &save_sig_mask);
+
         /* the arch_thread_signal_enter() never return */
         RT_ASSERT(0);
     }
+
+    /* reacquire the lock because we release it before */
+    LWP_LOCK(lwp);
 }
+
+void lwp_thread_signal_catch(void *exp_frame)
+{
+    struct rt_thread *thread;
+    struct rt_lwp *lwp;
+    lwp_sigqueue_t pending;
+    lwp_sigset_t *sig_mask;
+    int retry_signal_catch;
+    int signo;
+
+    thread = rt_thread_self();
+    lwp = (struct rt_lwp *)thread->lwp;
+
+    RT_ASSERT(!!lwp);
+    LWP_LOCK(lwp);
+
+    do {
+        /* if stopped process resume, we will retry to catch the signal */
+        retry_signal_catch = 0;
+        signo = 0;
+
+        /* try to peek a signal which is pending and not blocked by this thread */
+        if (!sigqueue_isempty(_SIGQ(thread)))
+        {
+            pending = _SIGQ(thread);
+            sig_mask = &thread->signal.sigset_mask;
+            signo = sigqueue_peek(pending, sig_mask);
+        }
+        if (!signo && !sigqueue_isempty(_SIGQ(lwp)))
+        {
+            pending = _SIGQ(lwp);
+            sig_mask = &thread->signal.sigset_mask;
+            signo = sigqueue_peek(pending, sig_mask);
+        }
+
+        if (signo)
+        {
+            lwp_siginfo_t siginfo;
+            lwp_sighandler_t handler;
+
+            LOG_D("%s(signo=%d)", __func__, signo);
+
+            siginfo = sigqueue_dequeue(pending, signo);
+            RT_ASSERT(siginfo != RT_NULL);
+            handler = _get_sighandler_locked(lwp, signo);
+
+            if (_is_stop_signal(lwp, signo) && handler == LWP_SIG_ACT_DFL)
+            {
+                /* notify the status update for parent process */
+                _notify_parent_and_leader(lwp, thread, signo, RT_TRUE);
+
+                LOG_D("%s: pid=%d stopped", __func__, lwp->pid);
+                _stop_thread_locked(lwp, thread, signo, siginfo, pending);
+                LOG_D("%s: pid=%d continued", __func__, lwp->pid);
+
+                /* wakeup and retry to catch signals send to us */
+                retry_signal_catch = 1;
+            }
+            else
+            {
+                /* do a normal, non-jobctl signal handling */
+                _catch_signal_locked(lwp, thread, signo, siginfo, handler, exp_frame);
+            }
+        }
+    } while (retry_signal_catch);
+
+    LWP_UNLOCK(lwp);
+}
+
 static int _do_signal_wakeup(rt_thread_t thread, int sig)
 {
     int need_schedule;
     rt_sched_lock_level_t slvl;
     if (!_sigismember(&thread->signal.sigset_mask, sig))
     {
+        int stat;
         rt_sched_lock(&slvl);
-        int stat = rt_sched_thread_get_stat(thread);
+        stat = rt_sched_thread_get_stat(thread);
         if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
         {
             if ((stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK)
@@ -599,6 +835,8 @@ static int _do_signal_wakeup(rt_thread_t thread, int sig)
             rt_sched_unlock(slvl);
             need_schedule = 0;
         }
+
+        RT_SCHED_DEBUG_IS_UNLOCKED;
     }
     else
         need_schedule = 0;
@@ -689,7 +927,63 @@ rt_inline rt_bool_t _sighandler_cannot_caught(struct rt_lwp *lwp, int signo)
     return signo == SIGKILL || signo == SIGSTOP;
 }
 
-rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
+/* before signal is killed to target process/thread */
+static void _before_sending_jobctl_signal(int signo, rt_lwp_t target_lwp, lwp_siginfo_t si)
+{
+    rt_thread_t thr_iter;
+    rt_sched_lock_level_t slvl;
+    lwp_sigset_t jobctl_sigset = lwp_sigset_init(LWP_SIG_JOBCTL_SET);
+
+    LWP_ASSERT_LOCKED(target_lwp);
+
+    /**
+     * dequeue all the pending jobctl signals (including
+     * the one we are adding, since we don't want to pend it)
+     */
+    sigqueue_discard_sigset(_SIGQ(target_lwp), &jobctl_sigset);
+
+    if (signo == SIGCONT)
+    {
+        target_lwp->jobctl_stopped = RT_FALSE;
+        rt_list_for_each_entry(thr_iter, &target_lwp->t_grp, sibling)
+        {
+            rt_base_t stat;
+            sigqueue_discard_sigset(_SIGQ(thr_iter), &jobctl_sigset);
+
+            /**
+             * Note: all stopped thread will be resumed
+             */
+            rt_sched_lock(&slvl);
+            stat = rt_sched_thread_get_stat(thr_iter);
+            if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK &&
+                (stat & RT_SIGNAL_KILL_WAKEUP_MASK) == 0)
+            {
+                thr_iter->error = RT_EINTR;
+
+                /**
+                 * don't matter if we failed to resume the thread, since we
+                 * only care about the event passing, but not ordering here
+                 */
+                rt_sched_unlock(slvl);
+                rt_thread_wakeup(thr_iter);
+            }
+            else
+            {
+                rt_sched_unlock(slvl);
+            }
+
+        }
+    }
+    else
+    {
+        rt_list_for_each_entry(thr_iter, &target_lwp->t_grp, sibling)
+        {
+            sigqueue_discard_sigset(_SIGQ(thr_iter), &jobctl_sigset);
+        }
+    }
+}
+
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, lwp_siginfo_ext_t value)
 {
     rt_err_t ret = -1;
 
@@ -700,10 +994,15 @@ rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
     /** must be able to be suspended */
     RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
 
-    if (!lwp || signo <= 0 || signo > _LWP_NSIG)
+    if (!lwp || signo < 0 || signo > _LWP_NSIG)
     {
         ret = -RT_EINVAL;
     }
+    else if (signo == 0)
+    {
+        /* process exist and current process have privileges */
+        ret = 0;
+    }
     else
     {
         LOG_D("%s(lwp=%p \"%s\",signo=%ld,code=%ld,value=%ld)",
@@ -715,19 +1014,25 @@ rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
         terminated = lwp->terminated;
 
         /* short-circuit code for inactive task, ignored signals */
-        if (terminated || _sighandler_is_ignored(lwp, signo))
+        if (terminated)
         {
+            /* no one rely on this, then free the resource */
+            if (value)
+                rt_free(value);
             ret = 0;
         }
         else
         {
-            siginfo = siginfo_create(signo, code, value);
+            siginfo = siginfo_create(rt_thread_self(), signo, code, value);
 
             if (siginfo)
             {
+                if (_is_jobctl_signal(lwp, signo))
+                    _before_sending_jobctl_signal(signo, lwp, siginfo);
+
                 need_schedule = _siginfo_deliver_to_lwp(lwp, siginfo);
-                ret = 0;
                 lwp_signal_notify(&lwp->signalfd_notify_head, siginfo);
+                ret = 0;
             }
             else
             {
@@ -755,6 +1060,10 @@ static void _signal_action_flag_k2u(int signo, struct lwp_signal *signal, struct
         flags |= SA_RESTART;
     if (_sigismember(&signal->sig_action_siginfo, signo))
         flags |= SA_SIGINFO;
+    if (_sigismember(&signal->sig_action_nocldstop, signo))
+        flags |= SA_NOCLDSTOP;
+    if (_sigismember(&signal->sig_action_nocldwait, signo))
+        flags |= SA_NOCLDWAIT;
 
     act->sa_flags = flags;
 }
@@ -770,6 +1079,25 @@ static void _signal_action_flag_u2k(int signo, struct lwp_signal *signal, const
         _sigaddset(&signal->sig_action_restart, signo);
     if (flags & SA_SIGINFO)
         _sigaddset(&signal->sig_action_siginfo, signo);
+    if (signo == SIGCHLD)
+    {
+        /* These flags are meaningful only when establishing a handler for SIGCHLD */
+        if (flags & SA_NOCLDSTOP)
+            _sigaddset(&signal->sig_action_nocldstop, signo);
+        if (flags & SA_NOCLDWAIT)
+            _sigaddset(&signal->sig_action_nocldwait, signo);
+    }
+
+    #define _HANDLE_FLAGS (SA_RESTORER | SA_NODEFER | SA_ONSTACK | SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP | SA_NOCLDWAIT)
+    if (flags & ~_HANDLE_FLAGS)
+        LOG_W("Unhandled flags: 0x%lx", flags & ~_HANDLE_FLAGS);
+}
+
+rt_bool_t lwp_sigisign(struct rt_lwp *lwp, int _sig)
+{
+    unsigned long sig = _sig - 1;
+
+    return lwp->signal.sig_action[sig] == LWP_SIG_ACT_IGN;
 }
 
 rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
@@ -793,6 +1121,7 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
             oact->sa_restorer = RT_NULL;
             _signal_action_flag_k2u(signo, &lwp->signal, oact);
         }
+
         if (act)
         {
             /**
@@ -800,15 +1129,29 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
              * argument succeed, even in the case of signals that cannot be caught or ignored
              */
             if (_sighandler_cannot_caught(lwp, signo))
-                ret = -RT_EINVAL;
+                ret = -EINVAL;
             else
             {
                 prev_handler = _get_sighandler_locked(lwp, signo);
                 lwp->signal.sig_action_mask[signo - 1] = act->sa_mask;
                 if (act->__sa_handler._sa_handler == SIG_IGN)
-                    lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_IGN;
+                {
+                    lwp_sigset_t no_ign_set = lwp_sigset_init(LWP_SIG_NO_IGN_SET);
+                    if (!lwp_sigismember(&no_ign_set, signo))
+                    {
+                        /* except those unignorable signals, discard them for proc */
+                        lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_IGN;
+                    }
+                    else
+                    {
+                        /* POSIX.1: SIG_IGN and SIG_DFL are equivalent for SIGCONT */
+                        lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_DFL;
+                    }
+                }
                 else
+                {
                     lwp->signal.sig_action[signo - 1] = act->__sa_handler._sa_handler;
+                }
 
                 _signal_action_flag_u2k(signo, &lwp->signal, act);
 
@@ -837,12 +1180,12 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
         LWP_UNLOCK(lwp);
     }
     else
-        ret = -RT_EINVAL;
+        ret = -EINVAL;
 
     return ret;
 }
 
-rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value)
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, lwp_siginfo_ext_t value)
 {
     rt_err_t ret = -1;
 
@@ -855,10 +1198,15 @@ rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long
 
     LOG_D("%s(signo=%d)", __func__, signo);
 
-    if (!thread || signo <= 0 || signo >= _LWP_NSIG)
+    if (!thread || signo < 0 || signo >= _LWP_NSIG)
     {
         ret = -RT_EINVAL;
     }
+    else if (signo == 0)
+    {
+        /* thread exist and current thread have privileges */
+        ret = 0;
+    }
     else
     {
         lwp = thread->lwp;
@@ -874,7 +1222,7 @@ rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long
             ret = 0;
         else
         {
-            siginfo = siginfo_create(signo, code, value);
+            siginfo = siginfo_create(rt_thread_self(), signo, code, value);
 
             if (siginfo)
             {
@@ -919,7 +1267,7 @@ rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
 
     if (thread)
     {
-        lwp = (struct rt_lwp*)thread->lwp;
+        lwp = (struct rt_lwp *)thread->lwp;
         LWP_LOCK(lwp);
 
         if (!lwp)
@@ -948,13 +1296,14 @@ static int _dequeue_signal(rt_thread_t thread, lwp_sigset_t *mask, siginfo_t *us
     lwp_sigset_t *pending;
     lwp_sigqueue_t sigqueue;
 
+    lwp = thread->lwp;
+    RT_ASSERT(lwp);
+
     sigqueue = _SIGQ(thread);
     pending = &sigqueue->sigset_pending;
     signo = _next_signal(pending, mask);
     if (!signo)
     {
-        lwp = thread->lwp;
-        RT_ASSERT(lwp);
         sigqueue = _SIGQ(lwp);
         pending = &sigqueue->sigset_pending;
         signo = _next_signal(pending, mask);
@@ -978,6 +1327,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
     rt_err_t ret;
     lwp_sigset_t saved_sigset;
     lwp_sigset_t blocked_sigset;
+    lwp_sigset_t dontwait_sigset;
     int sig;
     struct rt_lwp *lwp = thread->lwp;
 
@@ -990,10 +1340,10 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
     /* Create a mask of signals user dont want or cannot catch */
     _sigdelset(sigset, SIGKILL);
     _sigdelset(sigset, SIGSTOP);
-    _signotsets(sigset, sigset);
+    _signotsets(&dontwait_sigset, sigset);
 
     LWP_LOCK(lwp);
-    sig = _dequeue_signal(thread, sigset, usi);
+    sig = _dequeue_signal(thread, &dontwait_sigset, usi);
     LWP_UNLOCK(lwp);
     if (sig)
         return sig;
@@ -1007,13 +1357,12 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
      * Note: If the pending signal arrives before thread suspend, the suspend
      * operation will return a failure
      */
-    _sigandsets(&blocked_sigset, &thread->signal.sigset_mask, sigset);
+    _sigandsets(&blocked_sigset, &thread->signal.sigset_mask, &dontwait_sigset);
     _thread_signal_mask(thread, LWP_SIG_MASK_CMD_SET_MASK, &blocked_sigset, &saved_sigset);
     if (timeout)
     {
-        rt_uint32_t time;
-        time = rt_timespec_to_tick(timeout);
-
+        rt_tick_t time;
+        time = (timeout->tv_sec * RT_TICK_PER_SECOND) + ((timeout->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND);
         /**
          * Brief: POSIX
          * If the timespec structure pointed to by timeout is zero-valued and
@@ -1023,11 +1372,13 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
         if (time == 0)
             return -EAGAIN;
 
+        rt_enter_critical();
         ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
         rt_timer_control(&(thread->thread_timer),
                          RT_TIMER_CTRL_SET_TIME,
                          &time);
         rt_timer_start(&(thread->thread_timer));
+        rt_exit_critical();
 
     }
     else
@@ -1040,7 +1391,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
     {
         rt_schedule();
         /* If thread->error reliable? */
-        if (thread->error == -RT_EINTR)
+        if (thread->error == RT_EINTR)
             ret = -EINTR;
         else
             ret = -EAGAIN;
@@ -1049,7 +1400,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
     _thread_signal_mask(thread, LWP_SIG_MASK_CMD_SET_MASK, &saved_sigset, RT_NULL);
 
     LWP_LOCK(lwp);
-    sig = _dequeue_signal(thread, sigset, usi);
+    sig = _dequeue_signal(thread, &dontwait_sigset, usi);
     LWP_UNLOCK(lwp);
 
     return sig ? sig : ret;
@@ -1072,3 +1423,22 @@ void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *pending)
         _sigandsets(pending, pending, &thread->signal.sigset_mask);
     }
 }
+
+rt_err_t lwp_pgrp_signal_kill(rt_processgroup_t pgrp, long signo, long code,
+                              lwp_siginfo_ext_t value)
+{
+    struct rt_lwp *lwp;
+    rt_err_t rc = 0;
+
+    PGRP_ASSERT_LOCKED(pgrp);
+
+    if (pgrp)
+    {
+        rt_list_for_each_entry(lwp, &pgrp->process, pgrp_node)
+        {
+            lwp_signal_kill(lwp, signo, code, value);
+        }
+    }
+
+    return rc;
+}

+ 68 - 15
components/lwp/lwp_signal.h

@@ -7,6 +7,7 @@
  * Date           Author       Notes
  * 2020-02-23     Jesven       first version.
  * 2023-07-06     Shell        update the generation, pending and delivery API
+ * 2023-11-22     Shell        support for job control signal
  */
 
 #ifndef __LWP_SIGNAL_H__
@@ -17,23 +18,33 @@
 #include <rtthread.h>
 #include <sys/signal.h>
 
-struct timespec;
-struct itimerspec;
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #define _USIGNAL_SIGMASK(signo) (1u << ((signo)-1))
-#define LWP_SIG_IGNORE_SET      (_USIGNAL_SIGMASK(SIGCHLD) | _USIGNAL_SIGMASK(SIGURG))
-#define LWP_SIG_ACT_DFL         ((lwp_sighandler_t)0)
-#define LWP_SIG_ACT_IGN         ((lwp_sighandler_t)1)
-#define LWP_SIG_USER_SA_FLAGS                                               \
-    (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART |   \
+#define LWP_SIG_NO_IGN_SET                                   \
+    (_USIGNAL_SIGMASK(SIGCONT) | _USIGNAL_SIGMASK(SIGSTOP) | \
+     _USIGNAL_SIGMASK(SIGKILL))
+#define LWP_SIG_IGNORE_SET                                  \
+    (_USIGNAL_SIGMASK(SIGCHLD) | _USIGNAL_SIGMASK(SIGURG) | \
+     _USIGNAL_SIGMASK(SIGWINCH) /* from 4.3 BSD, not POSIX.1 */)
+#define LWP_SIG_JOBCTL_SET                                   \
+    (_USIGNAL_SIGMASK(SIGCONT) | _USIGNAL_SIGMASK(SIGSTOP) | \
+     _USIGNAL_SIGMASK(SIGTSTP) | _USIGNAL_SIGMASK(SIGTTIN) | \
+     _USIGNAL_SIGMASK(SIGTTOU))
+#define LWP_SIG_STOP_SET                                     \
+    (_USIGNAL_SIGMASK(SIGSTOP) | _USIGNAL_SIGMASK(SIGTSTP) | \
+     _USIGNAL_SIGMASK(SIGTTIN) | _USIGNAL_SIGMASK(SIGTTOU))
+#define LWP_SIG_ACT_DFL ((lwp_sighandler_t)0)
+#define LWP_SIG_ACT_IGN ((lwp_sighandler_t)1)
+#define LWP_SIG_USER_SA_FLAGS                                             \
+    (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | \
      SA_NODEFER | SA_RESETHAND | SA_EXPOSE_TAGBITS)
 #define LWP_SIG_INVALID_TIMER ((timer_t)-1)
 
-typedef enum {
+typedef enum
+{
     LWP_SIG_MASK_CMD_BLOCK,
     LWP_SIG_MASK_CMD_UNBLOCK,
     LWP_SIG_MASK_CMD_SET_MASK,
@@ -43,7 +54,8 @@ typedef enum {
 /**
  * LwP implementation of POSIX signal
  */
-struct lwp_signal {
+struct lwp_signal
+{
     timer_t real_timer;
     struct lwp_sigqueue sig_queue;
     rt_thread_t sig_dispatch_thr[_LWP_NSIG];
@@ -55,9 +67,12 @@ struct lwp_signal {
     lwp_sigset_t sig_action_onstack;
     lwp_sigset_t sig_action_restart;
     lwp_sigset_t sig_action_siginfo;
+    lwp_sigset_t sig_action_nocldstop;
+    lwp_sigset_t sig_action_nocldwait;
 };
 
 struct rt_lwp;
+struct rt_processgroup;
 
 #ifndef ARCH_MM_MMU
 void lwp_sighandler_set(int sig, lwp_sighandler_t func);
@@ -93,12 +108,14 @@ rt_inline void lwp_thread_signal_detach(struct lwp_thread_signal *tsig)
  * @param signo the signal number
  * @param code as in siginfo
  * @param value as in siginfo
- * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
  *
  * @note the *signal_kill have the same definition of a successful return as
  *       kill() in IEEE Std 1003.1-2017
  */
-rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value);
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code,
+                         lwp_siginfo_ext_t value);
 
 /**
  * @brief set or examine the signal action of signo
@@ -119,9 +136,11 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
  * @param signo the signal number
  * @param code as in siginfo
  * @param value as in siginfo
- * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
  */
-rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value);
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code,
+                                lwp_siginfo_ext_t value);
 
 /**
  * @brief set signal mask of target thread
@@ -136,7 +155,8 @@ rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
                                 const lwp_sigset_t *sigset, lwp_sigset_t *oset);
 
 /**
- * @brief Catch signal if exists and no return, otherwise return with no side effect
+ * @brief Catch signal if exists and no return, otherwise return with no
+ * side effect
  *
  * @param exp_frame the exception frame on kernel stack
  */
@@ -172,10 +192,43 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
  */
 void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *sigset);
 
+/**
+ * @brief send a signal to the process group
+ *
+ * @param pgrp target process group
+ * @param signo the signal number
+ * @param code as in siginfo
+ * @param value as in siginfo
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
+ */
+rt_err_t lwp_pgrp_signal_kill(struct rt_processgroup *pgrp, long signo,
+                              long code, lwp_siginfo_ext_t value);
+
+rt_inline int lwp_sigismember(lwp_sigset_t *set, int _sig)
+{
+    unsigned long sig = _sig - 1;
+
+    if (_LWP_NSIG_WORDS == 1)
+    {
+        return 1 & (set->sig[0] >> sig);
+    }
+    else
+    {
+        return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW));
+    }
+}
+
+struct itimerspec;
+
+rt_bool_t lwp_sigisign(struct rt_lwp *lwp, int _sig);
+
 rt_err_t lwp_signal_setitimer(struct rt_lwp *lwp, int which,
                               const struct itimerspec *restrict new,
                               struct itimerspec *restrict old);
 
+rt_bool_t lwp_signal_restart_syscall(struct rt_lwp *lwp, int error_code);
+
 #ifdef __cplusplus
 }
 #endif

+ 20 - 1
components/lwp/lwp_sys_socket.h

@@ -113,7 +113,7 @@ struct musl_ifreq
 {
     union
     {
-#define IFNAMSIZ	16
+#define IFNAMSIZ    16
         char ifrn_name[IFNAMSIZ];
     } ifr_ifrn;
     union
@@ -133,4 +133,23 @@ struct musl_ifreq
     } ifr_ifru;
 };
 
+struct musl_rtentry
+{
+    unsigned long int rt_pad1;
+    struct musl_sockaddr rt_dst;
+    struct musl_sockaddr rt_gateway;
+    struct musl_sockaddr rt_genmask;
+    unsigned short int rt_flags;
+    short int rt_pad2;
+    unsigned long int rt_pad3;
+    unsigned char rt_tos;
+    unsigned char rt_class;
+    short int rt_pad4[sizeof(long)/2-1];
+    short int rt_metric;
+    char *rt_dev;
+    unsigned long int rt_mtu;
+    unsigned long int rt_window;
+    unsigned short int rt_irtt;
+};
+
 #endif /* __LWP_SYS_SOCKET_H__ */

Datei-Diff unterdrückt, da er zu groß ist
+ 308 - 363
components/lwp/lwp_syscall.c


+ 6 - 1
components/lwp/lwp_syscall.h

@@ -108,10 +108,15 @@ sysret_t sys_log(const char* log, int size);
 
 #ifdef ARCH_MM_MMU
 sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3);
-sysret_t sys_pmutex(void *umutex, int op, void *arg);
 sysret_t sys_cacheflush(void *addr, int len, int cache);
 #endif /* ARCH_MM_MMU */
 
+sysret_t sys_setsid(void);
+sysret_t sys_getsid(pid_t pid);
+sysret_t sys_setpgid(pid_t pid, pid_t pgid);
+sysret_t sys_getpgid(pid_t pid);
+
+
 #ifdef __cplusplus
 }
 #endif

+ 15 - 6
components/lwp/lwp_tid.c

@@ -6,6 +6,7 @@
  * Change Logs:
  * Date           Author       Notes
  * 2021-01-15     shaojinchun  first version
+ * 2023-11-16     xqyjlj       Fix the case where tid is 0
  */
 
 #define DBG_TAG    "lwp.tid"
@@ -126,20 +127,28 @@ void lwp_tid_put(int tid)
         lwp_mutex_release_safe(&tid_lock);
 }
 
-rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid)
+rt_thread_t lwp_tid_get_thread_raw(int tid)
 {
     struct lwp_avl_struct *p;
     rt_thread_t thread = RT_NULL;
-    lwp_mutex_take_safe(&tid_lock, RT_WAITING_FOREVER, 0);
 
     p  = lwp_avl_find(tid, lwp_tid_root);
     if (p)
     {
         thread = (rt_thread_t)p->data;
-        if (thread != RT_NULL)
-        {
-            thread->tid_ref_count += 1;
-        }
+    }
+    return thread;
+}
+
+rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid)
+{
+    rt_thread_t thread = RT_NULL;
+
+    lwp_mutex_take_safe(&tid_lock, RT_WAITING_FOREVER, 0);
+    thread = tid ? lwp_tid_get_thread_raw(tid) : rt_thread_self();
+    if (thread != RT_NULL)
+    {
+        thread->tid_ref_count += 1;
     }
     lwp_mutex_release_safe(&tid_lock);
     return thread;

+ 21 - 14
components/lwp/lwp_user_mm.c

@@ -23,10 +23,7 @@
 
 #ifdef ARCH_MM_MMU
 
-#include <lwp.h>
-#include <lwp_arch.h>
-#include <lwp_mm.h>
-#include <lwp_user_mm.h>
+#include "lwp_internal.h"
 
 #include <mm_aspace.h>
 #include <mm_fault.h>
@@ -161,10 +158,10 @@ void lwp_aspace_switch(struct rt_thread *thread)
 
 void lwp_unmap_user_space(struct rt_lwp *lwp)
 {
-    arch_user_space_free(lwp);
+    if (lwp->aspace)
+        arch_user_space_free(lwp);
 }
 
-
 static void *_lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size,
                            int text)
 {
@@ -566,6 +563,14 @@ int lwp_munmap(struct rt_lwp *lwp, void *addr, size_t length)
     return lwp_errno_to_posix(ret);
 }
 
+void *lwp_mremap(struct rt_lwp *lwp, void *old_address, size_t old_size,
+                    size_t new_size, int flags, void *new_address)
+{
+    RT_ASSERT(lwp);
+
+    return rt_aspace_mremap_range(lwp->aspace, old_address, old_size, new_size, flags, new_address);
+}
+
 size_t lwp_get_from_user(void *dst, void *src, size_t size)
 {
     struct rt_lwp *lwp = RT_NULL;
@@ -621,11 +626,6 @@ size_t lwp_put_to_user(void *dst, void *src, size_t size)
     return lwp_data_put(lwp, dst, src, size);
 }
 
-rt_inline rt_bool_t _in_user_space(const char *addr)
-{
-    return (addr >= (char *)USER_VADDR_START && addr < (char *)USER_VADDR_TOP);
-}
-
 rt_inline rt_bool_t _can_unaligned_access(const char *addr)
 {
     return rt_kmem_v2p((char *)addr) - PV_OFFSET == addr;
@@ -636,9 +636,9 @@ void *lwp_memcpy(void * __restrict dst, const void * __restrict src, size_t size
     void *rc = dst;
     long len;
 
-    if (_in_user_space(dst))
+    if (lwp_in_user_space(dst))
     {
-        if (!_in_user_space(src))
+        if (!lwp_in_user_space(src))
         {
             len = lwp_put_to_user(dst, (void *)src, size);
             if (!len)
@@ -654,7 +654,7 @@ void *lwp_memcpy(void * __restrict dst, const void * __restrict src, size_t size
     }
     else
     {
-        if (_in_user_space(src))
+        if (lwp_in_user_space(src))
         {
             len = lwp_get_from_user(dst, (void *)src, size);
             if (!len)
@@ -979,6 +979,13 @@ size_t lwp_user_strlen(const char *s)
     return lwp_user_strlen_ext(lwp, s);
 }
 
+size_t lwp_strlen(struct rt_lwp *lwp, const char *s)
+{
+    if (lwp_in_user_space(s))
+        return lwp_user_strlen_ext(lwp, s);
+    else
+        return strlen(s);
+}
 
 char** lwp_get_command_line_args(struct rt_lwp *lwp)
 {

+ 4 - 0
components/lwp/lwp_user_mm.h

@@ -56,6 +56,9 @@ void* lwp_mmap2(struct rt_lwp *lwp, void *addr, size_t length, int prot, int fla
  */
 int lwp_munmap(struct rt_lwp *lwp, void *addr, size_t length);
 
+void *lwp_mremap(struct rt_lwp *lwp, void *old_address, size_t old_size,
+                    size_t new_size, int flags, void *new_address);
+
 /**
  * @brief Test if address from user is accessible address by user
  *
@@ -163,6 +166,7 @@ rt_base_t lwp_brk(void *addr);
 
 size_t lwp_user_strlen(const char *s);
 size_t lwp_user_strlen_ext(struct rt_lwp *lwp, const char *s);
+size_t lwp_strlen(struct rt_lwp *lwp, const char *s);
 
 int lwp_fork_aspace(struct rt_lwp *dest_lwp, struct rt_lwp *src_lwp);
 

+ 13 - 0
components/lwp/terminal/Kconfig

@@ -0,0 +1,13 @@
+menuconfig LWP_USING_TERMINAL
+    bool "Terminal I/O Subsystem"
+    depends on RT_USING_SMART
+    default y
+
+if LWP_USING_TERMINAL
+    config LWP_PTY_MAX_PARIS_LIMIT
+        int "Max number of pty devices registered at the same time"
+        default 64
+        help
+            This upper limit is set to protect kernel memory from draining
+            out by the application if it keeps allocating pty devices.
+endif

+ 741 - 0
components/lwp/terminal/bsd_porting.h

@@ -0,0 +1,741 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        Add compatible layer for FreeBSD
+ */
+
+#ifndef __LWP_TTY_BSD_PORTING_H__
+#define __LWP_TTY_BSD_PORTING_H__
+
+#include <rtthread.h>
+#include <lwp_internal.h>
+#define _KERNEL
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/* functionability of bsd tty layer */
+#if 0
+#define USING_BSD_HOOK
+#endif
+
+/* Only for devfs d_close() flags. */
+#define FLASTCLOSE O_DIRECTORY
+#define FREVOKE    0x00200000
+
+/*
+ * Output flags - software output processing
+ */
+#if !((OPOST | OLCUC | ONLCR) & 0x8)
+#define ONOEOT 0x0008 /* discard EOT's (^D) on output) */
+#endif
+
+/*
+ * Kernel encoding of open mode; separate read and write bits that are
+ * independently testable: 1 greater than the above.
+ *
+ * XXX
+ * FREAD and FWRITE are excluded from the #ifdef _KERNEL so that TIOCFLUSH,
+ * which was documented to use FREAD/FWRITE, continues to work.
+ */
+#define FREAD  0x0001
+#define FWRITE 0x0002
+
+/*
+ * Flags to memory allocation functions.
+ */
+#define M_NOWAIT      0x0001 /* do not block */
+#define M_WAITOK      0x0002 /* ok to block */
+#define M_NORECLAIM   0x0080 /* do not reclaim after failure */
+#define M_ZERO        0x0100 /* bzero the allocation */
+#define M_NOVM        0x0200 /* don't ask VM for pages */
+#define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */
+#define M_NODUMP      0x0800 /* don't dump pages in this allocation */
+#define M_FIRSTFIT    0x1000 /* only for vmem, fast fit */
+#define M_BESTFIT     0x2000 /* only for vmem, low fragmentation */
+#define M_EXEC        0x4000 /* allocate executable space */
+#define M_NEXTFIT     0x8000 /* only for vmem, follow cursor */
+
+#define M_VERSION 2020110501
+
+/*
+ * The INVARIANTS-enabled mtx_assert() functionality.
+ *
+ * The constants need to be defined for INVARIANT_SUPPORT infrastructure
+ * support as _mtx_assert() itself uses them and the latter implies that
+ * _mtx_assert() must build.
+ */
+#define MA_OWNED       (1)
+#define MA_NOTOWNED    (2)
+#define MA_RECURSED    (4)
+#define MA_NOTRECURSED (8)
+
+/*
+ * Indentification of modem control signals.  These definitions match
+ * the TIOCMGET definitions in <sys/ttycom.h> shifted a bit down, and
+ * that identity is enforced with CTASSERT at the bottom of kern/tty.c
+ * Both the modem bits and delta bits must fit in 16 bit.
+ */
+#define SER_DTR 0x0001 /* data terminal ready */
+#define SER_RTS 0x0002 /* request to send */
+#define SER_STX 0x0004 /* secondary transmit */
+#define SER_SRX 0x0008 /* secondary receive */
+#define SER_CTS 0x0010 /* clear to send */
+#define SER_DCD 0x0020 /* data carrier detect */
+#define SER_RI  0x0040 /* ring indicate */
+#define SER_DSR 0x0080 /* data set ready */
+
+#define SER_MASK_STATE 0x00ff
+
+/*
+ * Flags for ioflag. (high 16 bits used to ask for read-ahead and
+ * help with write clustering)
+ * NB: IO_NDELAY and IO_DIRECT are linked to fcntl.h
+ */
+#if 0
+#define IO_UNIT   0x0001 /* do I/O as atomic unit */
+#define IO_APPEND 0x0002 /* append write to end */
+#endif                   /* not porting */
+
+#define IO_NDELAY 0x0004 /* FNDELAY flag set in file table */
+#if 0
+#define IO_NODELOCKED  0x0008 /* underlying node already locked */
+#define IO_ASYNC       0x0010 /* bawrite rather then bdwrite */
+#define IO_VMIO        0x0020 /* data already in VMIO space */
+#define IO_INVAL       0x0040 /* invalidate after I/O */
+#define IO_SYNC        0x0080 /* do I/O synchronously */
+#define IO_DIRECT      0x0100 /* attempt to bypass buffer cache */
+#define IO_NOREUSE     0x0200 /* VMIO data won't be reused */
+#define IO_EXT         0x0400 /* operate on external attributes */
+#define IO_NORMAL      0x0800 /* operate on regular data */
+#define IO_NOMACCHECK  0x1000 /* MAC checks unnecessary */
+#define IO_BUFLOCKED   0x2000 /* ffs flag; indir buf is locked */
+#define IO_RANGELOCKED 0x4000 /* range locked */
+#define IO_DATASYNC    0x8000 /* do only data I/O synchronously */
+
+#define IO_SEQMAX   0x7F /* seq heuristic max value */
+#define IO_SEQSHIFT 16   /* seq heuristic in upper 16 bits */
+#endif                   /* not porting */
+
+/** Used to distinguish between normal, callout, lock and init devices.
+ * Note: this is not used in smart system.
+ */
+#define TTYUNIT_INIT    0x1
+#define TTYUNIT_LOCK    0x2
+#define TTYUNIT_CALLOUT 0x4
+
+/*
+ * TTY privileges.
+ */
+#define PRIV_TTY_CONSOLE   250 /* Set console to tty. */
+#define PRIV_TTY_DRAINWAIT 251 /* Set tty drain wait time. */
+#define PRIV_TTY_DTRWAIT   252 /* Set DTR wait on tty. */
+#define PRIV_TTY_EXCLUSIVE 253 /* Override tty exclusive flag. */
+#define _PRIV_TTY_PRISON   254 /* Removed. */
+#define PRIV_TTY_STI       255 /* Simulate input on another tty. */
+#define PRIV_TTY_SETA      256 /* Set tty termios structure. */
+
+#define MPASS(ex) RT_ASSERT(ex)
+
+#if !defined(MIN)
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#if !defined(MAX)
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define curthread rt_thread_self()
+
+#ifdef USING_BSD_HOOK
+#define ttyhook_hashook(tp, hook) \
+    ((tp)->t_hook != NULL && (tp)->t_hook->th_##hook != NULL)
+#else
+#define ttyhook_hashook(tp, hook) (RT_FALSE)
+#endif
+
+/* condvar API */
+#include <rtdevice.h>
+
+#define cv_init(cvp, name) rt_condvar_init(cvp, name)
+#define cv_destroy(cvp)    rt_condvar_detach(cvp)
+#define cv_wait(cvp, mp) \
+    rt_condvar_timedwait(cvp, mp, RT_KILLABLE, RT_WAITING_FOREVER)
+#define cv_wait_sig(cvp, mp) \
+    rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, RT_WAITING_FOREVER)
+#define cv_signal(cvp)           rt_condvar_signal(cvp)
+#define cv_broadcast(cvp)        rt_condvar_broadcast(cvp)
+#define cv_timedwait(cvp, mp, t) rt_condvar_timedwait(cvp, mp, RT_KILLABLE, t)
+#define cv_timedwait_sig(cvp, mp, t) \
+    rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, t)
+
+struct lwp_tty;
+struct uio;
+
+/* TODO: just a place holder since devfs is not capable of doing this currently
+ */
+struct file
+{
+};
+
+typedef rt_base_t sbintime_t;
+typedef rt_ubase_t vm_offset_t;
+typedef rt_base_t vm_ooffset_t;
+typedef rt_ubase_t vm_paddr_t;
+typedef rt_ubase_t vm_pindex_t;
+typedef rt_ubase_t vm_size_t;
+typedef char *rt_caddr_t;
+
+/*
+ * The exact set of memory attributes is machine dependent.  However,
+ * every machine is required to define VM_MEMATTR_DEFAULT and
+ * VM_MEMATTR_UNCACHEABLE.
+ */
+typedef char vm_memattr_t; /* memory attribute codes */
+
+typedef int d_open_t(struct lwp_tty *tp, int oflags, int devtype,
+                     struct rt_thread *td);
+typedef int d_fdopen_t(struct lwp_tty *tp, int oflags, struct rt_thread *td,
+                       struct file *fp);
+typedef int d_close_t(struct lwp_tty *tp, int fflag, int devtype,
+                      struct rt_thread *td);
+
+#ifdef USING_BSD_DEVICE_STRATEGY
+typedef void d_strategy_t(struct bio *bp);
+#endif
+
+typedef int d_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+                      int fflag, struct rt_thread *td);
+
+typedef int d_read_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
+typedef int d_write_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
+typedef int d_poll_t(struct lwp_tty *tp, rt_pollreq_t *req,
+                     struct rt_thread *td);
+
+#ifdef USING_BSD_KNOTE
+typedef int d_kqfilter_t(struct lwp_tty *tp, struct knote *kn);
+#endif /* USING_BSD_KNOTE */
+
+typedef int d_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset, vm_paddr_t *paddr,
+                     int nprot, vm_memattr_t *memattr);
+
+#ifdef USING_BSD_MMAP_SINGLE
+typedef int d_mmap_single_t(struct cdev *cdev, vm_ooffset_t *offset,
+                            vm_size_t size, struct vm_object **object,
+                            int nprot);
+#endif /* USING_BSD_MMAP_SINGLE */
+
+typedef void d_purge_t(struct lwp_tty *tp);
+
+/*
+ * Character device switch table
+ */
+struct cdevsw
+{
+#ifdef USING_BSD_RAW_CDEVSW
+    int d_version;
+    u_int d_flags;
+    const char *d_name;
+#endif /* USING_BSD_RAW_CDEVSW */
+
+    d_open_t *d_open;
+    d_fdopen_t *d_fdopen;
+    d_close_t *d_close;
+    d_read_t *d_read;
+    d_write_t *d_write;
+    d_ioctl_t *d_ioctl;
+    d_poll_t *d_poll;
+    d_mmap_t *d_mmap;
+#ifdef USING_BSD_DEVICE_STRATEGY
+    d_strategy_t *d_strategy;
+#endif /* USING_BSD_DEVICE_STRATEGY */
+
+#ifdef USING_BSD_RAW_CDEVSW
+    void *d_spare0;
+    d_kqfilter_t *d_kqfilter;
+    d_purge_t *d_purge;
+    d_mmap_single_t *d_mmap_single;
+
+    int32_t d_spare1[3];
+    void *d_spare2[3];
+
+    /* These fields should not be messed with by drivers */
+    LIST_HEAD(, cdev) d_devs;
+    int d_spare3;
+    union
+    {
+        struct cdevsw *gianttrick;
+        SLIST_ENTRY(cdevsw) postfree_list;
+    } __d_giant;
+#endif
+};
+
+struct iovec
+{
+    void *iov_base; /* Base address. */
+    size_t iov_len; /* Length. */
+};
+
+enum uio_rw
+{
+    UIO_READ,
+    UIO_WRITE
+};
+
+struct uio
+{
+    struct iovec *uio_iov; /* scatter/gather list */
+    int uio_iovcnt;        /* length of scatter/gather list */
+    off_t uio_offset;      /* offset in target object */
+    ssize_t uio_resid;     /* remaining bytes to process */
+#ifdef USING_BSD_UIO
+    enum uio_seg uio_segflg; /* address space */
+#endif
+    enum uio_rw uio_rw; /* operation */
+#ifdef USING_BSD_UIO
+    struct rt_thread *uio_td; /* owner */
+
+#endif /* USING_BSD_UIO */
+};
+
+#include <lwp_user_mm.h>
+rt_inline int uiomove(void *operand, int n, struct uio *uio)
+{
+    switch (uio->uio_rw)
+    {
+        case UIO_READ:
+            memcpy(uio->uio_iov->iov_base, operand, n);
+            break;
+        case UIO_WRITE:
+            memcpy(operand, uio->uio_iov->iov_base, n);
+            break;
+        default:
+            return -1;
+    }
+
+    uio->uio_iov->iov_base += n;
+    uio->uio_iov->iov_len--;
+    uio->uio_offset += n;
+    uio->uio_resid -= n;
+    return 0;
+}
+
+/* privileges checking: 0 if okay */
+rt_inline int priv_check(struct rt_thread *td, int priv)
+{
+    return 0;
+}
+
+/* Disable console redirection to a tty. */
+rt_inline int constty_clear(struct lwp_tty *tp)
+{
+    // rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
+    return 0;
+}
+
+rt_inline int constty_set(struct lwp_tty *tp)
+{
+    // rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
+    return 0;
+}
+
+/**
+ * UMA (Universal Memory Allocator)
+ */
+#define UMA_ALIGN_PTR (sizeof(void *) - 1) /* Alignment fit for ptr */
+
+typedef int (*uma_ctor)(void *mem, int size, void *arg, int flags);
+typedef void (*uma_dtor)(void *mem, int size, void *arg);
+typedef int (*uma_init)(void *mem, int size, int flags);
+typedef void (*uma_fini)(void *mem, int size);
+
+struct uma_zone
+{
+    char *name;
+    int align;
+    int size;
+};
+
+/* Opaque type used as a handle to the zone */
+typedef struct uma_zone *uma_zone_t;
+
+rt_inline uma_zone_t uma_zcreate(char *name, int size, uma_ctor ctor,
+                                 uma_dtor dtor, uma_init zinit, uma_fini zfini,
+                                 int align, uint16_t flags)
+{
+    uma_zone_t zone = rt_malloc(sizeof(struct uma_zone));
+    if (zone)
+    {
+        RT_ASSERT(ctor == RT_NULL);
+        RT_ASSERT(dtor == RT_NULL);
+        RT_ASSERT(zinit == RT_NULL);
+        RT_ASSERT(zfini == RT_NULL);
+
+        zone->size = size;
+        zone->name = name;
+        zone->align = align;
+    }
+    return zone;
+}
+
+rt_inline void *uma_zalloc(uma_zone_t zone, int flags)
+{
+    void *buf = rt_malloc_align(zone->size, zone->align + 1);
+    if (buf)
+        rt_memset(buf, 0, sizeof(zone->size));
+    return buf;
+}
+
+rt_inline void uma_zfree(uma_zone_t zone, void *item)
+{
+    rt_free_align(item);
+}
+
+/**
+ * bsd type of speed to linux type.
+ * Note: with switch blocks, compiler can generate the optimized version for us
+ */
+#include <termios.h>
+rt_inline long bsd_speed_to_integer(speed_t speed)
+{
+    long speed_value;
+    switch (speed)
+    {
+        case B0:
+            speed_value = 0;
+            break;
+        case B50:
+            speed_value = 50;
+            break;
+        case B75:
+            speed_value = 75;
+            break;
+        case B110:
+            speed_value = 110;
+            break;
+        case B134:
+            speed_value = 134;
+            break;
+        case B150:
+            speed_value = 150;
+            break;
+        case B200:
+            speed_value = 200;
+            break;
+        case B300:
+            speed_value = 300;
+            break;
+        case B600:
+            speed_value = 600;
+            break;
+        case B1200:
+            speed_value = 1200;
+            break;
+        case B1800:
+            speed_value = 1800;
+            break;
+        case B2400:
+            speed_value = 2400;
+            break;
+        case B4800:
+            speed_value = 4800;
+            break;
+        case B9600:
+            speed_value = 9600;
+            break;
+        case B19200:
+            speed_value = 19200;
+            break;
+        case B38400:
+            speed_value = 38400;
+            break;
+        case B57600:
+            speed_value = 57600;
+            break;
+        case B115200:
+            speed_value = 115200;
+            break;
+        case B230400:
+            speed_value = 230400;
+            break;
+        case B460800:
+            speed_value = 460800;
+            break;
+        case B500000:
+            speed_value = 500000;
+            break;
+        case B576000:
+            speed_value = 576000;
+            break;
+        case B921600:
+            speed_value = 921600;
+            break;
+        case B1000000:
+            speed_value = 1000000;
+            break;
+        case B1152000:
+            speed_value = 1152000;
+            break;
+        case B1500000:
+            speed_value = 1500000;
+            break;
+        case B2000000:
+            speed_value = 2000000;
+            break;
+        case B2500000:
+            speed_value = 2500000;
+            break;
+        case B3000000:
+            speed_value = 3000000;
+            break;
+        case B3500000:
+            speed_value = 3500000;
+            break;
+        case B4000000:
+            speed_value = 4000000;
+            break;
+        default:
+            speed_value = -1; // invalid speed
+            break;
+    }
+    return speed_value;
+}
+
+/* time.h */
+
+/* Operations on timevals. */
+
+#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#define timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timevalcmp(tvp, uvp, cmp)                                          \
+    (((tvp)->tv_sec == (uvp)->tv_sec) ? ((tvp)->tv_usec cmp(uvp)->tv_usec) \
+                                      : ((tvp)->tv_sec cmp(uvp)->tv_sec))
+
+rt_inline void getmicrotime(struct timeval *now)
+{
+    gettimeofday(now, RT_NULL);
+}
+
+rt_inline void timevalfix(struct timeval *tv)
+{
+    if (tv->tv_usec < 0)
+    {
+        tv->tv_sec--;
+        tv->tv_usec += 1000000;
+    }
+    if (tv->tv_usec >= 1000000)
+    {
+        tv->tv_sec++;
+        tv->tv_usec -= 1000000;
+    }
+}
+
+rt_inline void timevaladd(struct timeval *op1, const struct timeval *op2)
+{
+    op1->tv_sec += op2->tv_sec;
+    op1->tv_usec += op2->tv_usec;
+    timevalfix(op1);
+}
+
+rt_inline void timevalsub(struct timeval *op1, const struct timeval *op2)
+{
+    op1->tv_sec -= op2->tv_sec;
+    op1->tv_usec -= op2->tv_usec;
+    timevalfix(op1);
+}
+
+rt_inline rt_tick_t tvtohz(struct timeval *tv)
+{
+    rt_tick_t rc;
+    rc = tv->tv_sec * RT_TICK_PER_SECOND;
+    rc += tv->tv_usec * RT_TICK_PER_SECOND / MICROSECOND_PER_SECOND;
+    return rc;
+}
+
+/* ioctl */
+#define _BSD_TIOCTL(val) ((val) << 16)
+enum bsd_ioctl_cmd
+{
+    BSD_TIOCDRAIN = 1,
+    BSD_TIOCFLUSH,
+    BSD_TIOCSTART,
+    BSD_TIOCSTOP,
+    BSD_TIOCSTAT,
+    BSD_TIOCGDRAINWAIT,
+    BSD_TIOCSDRAINWAIT,
+    BSD_TIOCSDTR,
+    BSD_TIOCCDTR,
+};
+
+#ifndef TIOCGETA /* get termios struct */
+#define TIOCGETA TCGETS
+#endif
+#ifndef TIOCSETA /* set termios struct */
+#define TIOCSETA TCSETS
+#endif
+#ifndef TIOCSETAW /* drain output, set */
+#define TIOCSETAW TCSETSW
+#endif
+#ifndef TIOCSETAF /* drn out, fls in, set */
+#define TIOCSETAF TCSETSF
+#endif
+#ifndef TIOCDRAIN /* wait till output drained */
+#define TIOCDRAIN _BSD_TIOCTL(BSD_TIOCDRAIN)
+#endif
+#ifndef TIOCFLUSH /* flush buffers */
+#define TIOCFLUSH _BSD_TIOCTL(BSD_TIOCFLUSH)
+#endif
+#ifndef TIOCSTART /* start output, like ^Q */
+#define TIOCSTART _BSD_TIOCTL(BSD_TIOCSTART)
+#endif
+#ifndef TIOCSTOP /* stop output, like ^S */
+#define TIOCSTOP _BSD_TIOCTL(BSD_TIOCSTOP)
+#endif
+#ifndef TIOCSTAT /* simulate ^T status message */
+#define TIOCSTAT _BSD_TIOCTL(BSD_TIOCSTAT)
+#endif
+#ifndef TIOCGDRAINWAIT /* get ttywait timeout */
+#define TIOCGDRAINWAIT _BSD_TIOCTL(BSD_TIOCGDRAINWAIT)
+#endif
+#ifndef TIOCSDRAINWAIT /* set ttywait timeout */
+#define TIOCSDRAINWAIT _BSD_TIOCTL(BSD_TIOCSDRAINWAIT)
+#endif
+#ifndef TIOCSDTR /* set data terminal ready */
+#define TIOCSDTR _BSD_TIOCTL(BSD_TIOCSDTR)
+#endif
+#ifndef TIOCCDTR /* clear data terminal ready */
+#define TIOCCDTR _BSD_TIOCTL(BSD_TIOCCDTR)
+#endif
+
+#define ENOIOCTL ENOSYS
+#define NO_PID   -1
+
+/* line discipline */
+#define TTYDISC      0 /* termios tty line discipline */
+#define SLIPDISC     4 /* serial IP discipline */
+#define PPPDISC      5 /* PPP discipline */
+#define NETGRAPHDISC 6 /* Netgraph tty node discipline */
+#define H4DISC       7 /* Netgraph Bluetooth H4 discipline */
+
+/*
+ * Control flags - hardware control of terminal
+ */
+#if __BSD_VISIBLE
+#define CIGNORE    0x00000001 /* ignore control flags */
+#define CCTS_OFLOW 0x00010000 /* CTS flow control of output */
+#define CRTSCTS    (CCTS_OFLOW | CRTS_IFLOW)
+#define CRTS_IFLOW 0x00020000 /* RTS flow control of input */
+#define CDTR_IFLOW 0x00040000 /* DTR flow control of input */
+#define CDSR_OFLOW 0x00080000 /* DSR flow control of output */
+#define CCAR_OFLOW 0x00100000 /* DCD flow control of output */
+#define CNO_RTSDTR 0x00200000 /* Do not assert RTS or DTR automatically */
+#else
+#define CIGNORE    0 /* ignore control flags */
+#define CCTS_OFLOW 0 /* CTS flow control of output */
+#define CRTS_IFLOW 0 /* RTS flow control of input */
+#define CDTR_IFLOW 0 /* DTR flow control of input */
+#define CDSR_OFLOW 0 /* DSR flow control of output */
+#define CCAR_OFLOW 0 /* DCD flow control of output */
+#define CNO_RTSDTR 0 /* Do not assert RTS or DTR automatically */
+#endif
+
+#ifndef CRTSCTS
+#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
+#endif
+
+#ifndef howmany
+#define howmany(x, y) (((x) + ((y)-1)) / (y))
+#endif
+
+struct ucred
+{
+};
+#define NOCRED ((struct ucred *)0)  /* no credential available */
+#define FSCRED ((struct ucred *)-1) /* filesystem credential */
+
+/* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */
+#include <fcntl.h>
+#define FFLAGS(oflags) ((oflags)&O_EXEC ? (oflags) : (oflags) + 1)
+#define OFLAGS(fflags) \
+    (((fflags) & (O_EXEC | O_PATH)) != 0 ? (fflags) : (fflags)-1)
+
+typedef int fo_rdwr_t(struct lwp_tty *tp, struct uio *uio,
+                      struct ucred *active_cred, int flags,
+                      struct rt_thread *td);
+typedef int fo_truncate_t(struct lwp_tty *tp, off_t length,
+                          struct ucred *active_cred, struct rt_thread *td);
+typedef int fo_ioctl_t(struct lwp_tty *tp, rt_ubase_t com, void *data,
+                       struct ucred *active_cred, int fflags, struct rt_thread *td);
+typedef int fo_poll_t(struct lwp_tty *tp, struct rt_pollreq *rq, struct ucred *active_cred,
+                      struct rt_thread *td);
+typedef int fo_stat_t(struct lwp_tty *tp, struct stat *sb,
+                      struct ucred *active_cred);
+typedef int fo_close_t(struct lwp_tty *tp, struct rt_thread *td);
+
+#ifdef USING_BSD_FO_EXT
+typedef int fo_chmod_t(struct file *fp, mode_t mode, struct ucred *active_cred,
+                       struct rt_thread *td);
+typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
+                       struct ucred *active_cred, struct rt_thread *td);
+typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio,
+                          struct uio *trl_uio, off_t offset, size_t nbytes,
+                          off_t *sent, int flags, struct rt_thread *td);
+typedef int fo_seek_t(struct file *fp, off_t offset, int whence,
+                      struct rt_thread *td);
+
+typedef int fo_kqfilter_t(struct file *fp, struct knote *kn);
+typedef int fo_fill_kinfo_t(struct file *fp, struct kinfo_file *kif,
+                            struct filedesc *fdp);
+typedef int fo_mmap_t(struct file *fp, vm_map_t map, vm_offset_t *addr,
+                      vm_size_t size, vm_prot_t prot, vm_prot_t cap_maxprot,
+                      int flags, vm_ooffset_t foff, struct rt_thread *td);
+typedef int fo_aio_queue_t(struct file *fp, struct kaiocb *job);
+
+typedef int fo_add_seals_t(struct file *fp, int flags);
+typedef int fo_get_seals_t(struct file *fp, int *flags);
+typedef int fo_fallocate_t(struct file *fp, off_t offset, off_t len,
+                           struct rt_thread *td);
+typedef int fo_fspacectl_t(struct file *fp, int cmd, off_t *offset,
+                           off_t *length, int flags, struct ucred *active_cred,
+                           struct rt_thread *td);
+typedef int fo_spare_t(struct file *fp);
+#endif /* USING_BSD_FO_EXT */
+
+typedef int fo_flags_t;
+
+struct bsd_fileops
+{
+    fo_rdwr_t *fo_read;
+    fo_rdwr_t *fo_write;
+    fo_truncate_t *fo_truncate;
+    fo_ioctl_t *fo_ioctl;
+    fo_poll_t *fo_poll;
+    fo_stat_t *fo_stat;
+    fo_close_t *fo_close;
+#ifdef USING_BSD_FO_EXT
+    fo_chmod_t *fo_chmod;
+    fo_chown_t *fo_chown;
+    fo_sendfile_t *fo_sendfile;
+    fo_seek_t *fo_seek;
+    fo_kqfilter_t *fo_kqfilter;
+    fo_fill_kinfo_t *fo_fill_kinfo;
+    fo_mmap_t *fo_mmap;
+    fo_aio_queue_t *fo_aio_queue;
+    fo_add_seals_t *fo_add_seals;
+    fo_get_seals_t *fo_get_seals;
+    fo_fallocate_t *fo_fallocate;
+    fo_fspacectl_t *fo_fspacectl;
+    fo_spare_t *fo_spares[8]; /* Spare slots */
+#endif
+    fo_flags_t fo_flags; /* DFLAG_* below */
+};
+
+#define DFLAG_PASSABLE	0x01	/* may be passed via unix sockets. */
+#define DFLAG_SEEKABLE	0x02	/* seekable / nonsequential */
+
+#endif /* __LWP_TTY_BSD_PORTING_H__ */

+ 81 - 0
components/lwp/terminal/bsd_ttydisc.h

@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TTYDISC_H_
+#define _SYS_TTYDISC_H_
+
+#ifndef __LWP_TERMINAL_H__
+#error "can only be included through <terminal.h>"
+#endif /* !__LWP_TERMINAL_H__ */
+
+#include <rtdef.h>
+
+struct rt_wqueue;
+struct rt_thread;
+struct lwp_tty;
+struct uio;
+
+/* Top half routines. */
+void ttydisc_open(struct lwp_tty *tp);
+void ttydisc_close(struct lwp_tty *tp);
+int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag);
+int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag);
+void ttydisc_optimize(struct lwp_tty *tp);
+
+/* Bottom half routines. */
+void ttydisc_modem(struct lwp_tty *tp, int open);
+#define ttydisc_can_bypass(tp) ((tp)->t_flags & TF_BYPASS)
+int ttydisc_rint(struct lwp_tty *tp, char c, int flags);
+size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len);
+size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len);
+void ttydisc_rint_done(struct lwp_tty *tp);
+size_t ttydisc_rint_poll(struct lwp_tty *tp);
+size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len);
+int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio);
+size_t ttydisc_getc_poll(struct lwp_tty *tp);
+
+/* Error codes for ttydisc_rint(). */
+#define TRE_FRAMING 0x01
+#define TRE_PARITY  0x02
+#define TRE_OVERRUN 0x04
+#define TRE_BREAK   0x08
+
+#endif /* !_SYS_TTYDISC_H_ */

+ 180 - 0
components/lwp/terminal/bsd_ttyqueue.h

@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+#include "bsd_porting.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TTYQUEUE_H_
+#define _SYS_TTYQUEUE_H_
+
+#ifndef __LWP_TERMINAL_H__
+#error "can only be included through <teminal.h>"
+#endif /* !__LWP_TERMINAL_H__ */
+
+struct lwp_tty;
+struct ttyinq_block;
+struct ttyoutq_block;
+struct uio;
+
+/* Data input queue. */
+struct ttyinq
+{
+    struct ttyinq_block *ti_firstblock;
+    struct ttyinq_block *ti_startblock;
+    struct ttyinq_block *ti_reprintblock;
+    struct ttyinq_block *ti_lastblock;
+    unsigned int ti_begin;
+    unsigned int ti_linestart;
+    unsigned int ti_reprint;
+    unsigned int ti_end;
+    unsigned int ti_nblocks;
+    unsigned int ti_quota;
+};
+#define TTYINQ_DATASIZE 128
+
+/* Data output queue. */
+struct ttyoutq
+{
+    struct ttyoutq_block *to_firstblock;
+    struct ttyoutq_block *to_lastblock;
+    unsigned int to_begin;
+    unsigned int to_end;
+    unsigned int to_nblocks;
+    unsigned int to_quota;
+};
+#define TTYOUTQ_DATASIZE (256 - sizeof(struct ttyoutq_block *))
+
+/* Input queue handling routines. */
+int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t len);
+void ttyinq_free(struct ttyinq *ti);
+int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
+                    size_t readlen, size_t flushlen);
+size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t len, int quote);
+int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t len,
+                        int quote);
+void ttyinq_canonicalize(struct ttyinq *ti);
+size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
+                       char *lastc);
+void ttyinq_flush(struct ttyinq *ti);
+int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote);
+void ttyinq_unputchar(struct ttyinq *ti);
+void ttyinq_reprintpos_set(struct ttyinq *ti);
+void ttyinq_reprintpos_reset(struct ttyinq *ti);
+
+rt_inline size_t ttyinq_getsize(struct ttyinq *ti)
+{
+    return (ti->ti_nblocks * TTYINQ_DATASIZE);
+}
+
+rt_inline size_t ttyinq_getallocatedsize(struct ttyinq *ti)
+{
+    return (ti->ti_quota * TTYINQ_DATASIZE);
+}
+
+rt_inline size_t ttyinq_bytesleft(struct ttyinq *ti)
+{
+    size_t len;
+
+    /* Make sure the usage never exceeds the length. */
+    len = ti->ti_nblocks * TTYINQ_DATASIZE;
+    MPASS(len >= ti->ti_end);
+
+    return (len - ti->ti_end);
+}
+
+rt_inline size_t ttyinq_bytescanonicalized(struct ttyinq *ti)
+{
+    MPASS(ti->ti_begin <= ti->ti_linestart);
+
+    return (ti->ti_linestart - ti->ti_begin);
+}
+
+rt_inline size_t ttyinq_bytesline(struct ttyinq *ti)
+{
+    MPASS(ti->ti_linestart <= ti->ti_end);
+
+    return (ti->ti_end - ti->ti_linestart);
+}
+
+/* Input buffer iteration. */
+typedef void ttyinq_line_iterator_t(void *data, char c, int flags);
+void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
+                                        ttyinq_line_iterator_t *iterator,
+                                        void *data);
+void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
+                                         ttyinq_line_iterator_t *iterator,
+                                         void *data);
+
+/* Output queue handling routines. */
+void ttyoutq_flush(struct ttyoutq *to);
+int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t len);
+void ttyoutq_free(struct ttyoutq *to);
+size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len);
+int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio);
+size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t len);
+int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t len);
+
+rt_inline size_t ttyoutq_getsize(struct ttyoutq *to)
+{
+    return (to->to_nblocks * TTYOUTQ_DATASIZE);
+}
+
+rt_inline size_t ttyoutq_getallocatedsize(struct ttyoutq *to)
+{
+    return (to->to_quota * TTYOUTQ_DATASIZE);
+}
+
+rt_inline size_t ttyoutq_bytesleft(struct ttyoutq *to)
+{
+    size_t len;
+
+    /* Make sure the usage never exceeds the length. */
+    len = to->to_nblocks * TTYOUTQ_DATASIZE;
+    MPASS(len >= to->to_end);
+
+    return (len - to->to_end);
+}
+
+rt_inline size_t ttyoutq_bytesused(struct ttyoutq *to)
+{
+    return (to->to_end - to->to_begin);
+}
+
+#endif /* !_SYS_TTYQUEUE_H_ */

+ 1447 - 0
components/lwp/terminal/freebsd/tty.c

@@ -0,0 +1,1447 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+#include "../bsd_porting.h"
+#include "../tty_config.h"
+#include "../terminal.h"
+#include "../tty_internal.h"
+
+#include <rtdef.h>
+#include <sys/ioctl.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static void tty_rel_free(struct lwp_tty *tp);
+
+/* Character device of /dev/console. */
+static struct rt_device *dev_console;
+#ifdef USING_BSD_CONSOLE_NAME
+static const char *dev_console_filename;
+#endif
+
+/*
+ * Flags that are supported and stored by this implementation.
+ */
+#ifndef ALTWERASE
+#define ALTWERASE 0
+#endif
+#ifndef NOKERNINFO
+#define NOKERNINFO 0
+#endif
+#define TTYSUP_IFLAG                                                      \
+    (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | \
+     ICRNL | IXON | IXOFF | IXANY | IMAXBEL | IUTF8)
+#define TTYSUP_OFLAG (OPOST | ONLCR | TAB3 | ONOEOT | OCRNL | ONOCR | ONLRET)
+#define TTYSUP_LFLAG                                                     \
+    (ECHOKE | ECHOE | ECHOK | ECHO | ECHONL | ECHOPRT | ECHOCTL | ISIG | \
+     ICANON | ALTWERASE | IEXTEN | TOSTOP | FLUSHO | NOKERNINFO | NOFLSH)
+#define TTYSUP_CFLAG                                                       \
+    (CIGNORE | CSIZE | CSTOPB | CREAD | PARENB | PARODD | HUPCL | CLOCAL | \
+     CCTS_OFLOW | CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW |      \
+     CNO_RTSDTR)
+
+/*
+ * Set TTY buffer sizes.
+ */
+
+#define TTYBUF_MAX 65536
+
+#ifdef PRINTF_BUFR_SIZE
+#define TTY_PRBUF_SIZE PRINTF_BUFR_SIZE
+#else
+#define TTY_PRBUF_SIZE 256
+#endif
+
+/* Note: access to struct cdev:si_drv0.
+    Since pull-in, pull-out is not provided, this field
+    is constant value 0 in smart system */
+#define dev2unit(d)        (0)
+#define TTY_CALLOUT(tp, d) (dev2unit(d) & TTYUNIT_CALLOUT)
+
+/*
+ * Allocate buffer space if necessary, and set low watermarks, based on speed.
+ * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty
+ * lock during memory allocation.  They will return ENXIO if the tty disappears
+ * while unlocked.
+ */
+static int tty_watermarks(struct lwp_tty *tp)
+{
+    size_t bs = 0;
+    int error;
+
+    /* Provide an input buffer for 2 seconds of data. */
+    if (tp->t_termios.c_cflag & CREAD)
+        bs =
+            MIN(bsd_speed_to_integer(tp->t_termios.__c_ispeed) / 5, TTYBUF_MAX);
+    error = ttyinq_setsize(&tp->t_inq, tp, bs);
+    if (error != 0)
+        return error;
+
+    /* Set low watermark at 10% (when 90% is available). */
+    tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10;
+
+    /* Provide an output buffer for 2 seconds of data. */
+    bs = MIN(bsd_speed_to_integer(tp->t_termios.__c_ospeed) / 5, TTYBUF_MAX);
+    error = ttyoutq_setsize(&tp->t_outq, tp, bs);
+    if (error != 0)
+        return error;
+
+    /* Set low watermark at 10% (when 90% is available). */
+    tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10;
+
+    return 0;
+}
+
+/**
+ * Drain outq
+ */
+static int tty_drain(struct lwp_tty *tp, int leaving)
+{
+    rt_tick_t timeout_tick;
+    size_t bytes;
+    int error;
+
+#ifdef USING_BSD_HOOK
+    if (ttyhook_hashook(tp, getc_inject))
+        /* buffer is inaccessible */
+        return 0;
+#endif /* USING_BSD_HOOK */
+
+    /*
+     * For close(), use the recent historic timeout of "1 second without
+     * making progress".  For tcdrain(), use t_drainwait as the timeout,
+     * with zero meaning "no timeout" which gives POSIX behavior.
+     */
+    if (leaving)
+        timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND;
+    else if (tp->t_drainwait != 0)
+        timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND * tp->t_drainwait;
+    else
+        timeout_tick = 0;
+
+    /*
+     * Poll the output buffer and the hardware for completion, at 10 Hz.
+     * Polling is required for devices which are not able to signal an
+     * interrupt when the transmitter becomes idle (most USB serial devs).
+     * The unusual structure of this loop ensures we check for busy one more
+     * time after tty_timedwait() returns EWOULDBLOCK, so that success has
+     * higher priority than timeout if the IO completed in the last 100mS.
+     */
+    error = 0;
+    bytes = ttyoutq_bytesused(&tp->t_outq);
+    for (;;)
+    {
+        if (ttyoutq_bytesused(&tp->t_outq) == 0 && !ttydevsw_busy(tp))
+            return 0;
+        if (error != 0)
+            return error;
+        ttydevsw_outwakeup(tp);
+        error = tty_timedwait(tp, &tp->t_outwait, RT_TICK_PER_SECOND / 10);
+        if (error != 0 && error != EWOULDBLOCK)
+            return error;
+        else if (timeout_tick == 0 || rt_tick_get() < timeout_tick)
+            error = 0;
+        else if (leaving && ttyoutq_bytesused(&tp->t_outq) < bytes)
+        {
+            /* In close, making progress, grant an extra second. */
+            error = 0;
+            timeout_tick += RT_TICK_PER_SECOND;
+            bytes = ttyoutq_bytesused(&tp->t_outq);
+        }
+    }
+}
+
+/*
+ * Though ttydev_enter() and ttydev_leave() seem to be related, they
+ * don't have to be used together. ttydev_enter() is used by the cdev
+ * operations to prevent an actual operation from being processed when
+ * the TTY has been abandoned. ttydev_leave() is used by ttydev_open()
+ * and ttydev_close() to determine whether per-TTY data should be
+ * deallocated.
+ */
+
+rt_inline int ttydev_enter(struct lwp_tty *tp)
+{
+    rt_err_t error = tty_lock(tp);
+    if (error)
+        RT_ASSERT(0);
+
+    if (tty_gone(tp) || !tty_opened(tp))
+    {
+        /* Device is already gone. */
+        tty_unlock(tp);
+        return -ENXIO;
+    }
+
+    return 0;
+}
+
+static void ttydev_leave(struct lwp_tty *tp)
+{
+    tty_assert_locked(tp);
+
+    if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE)
+    {
+        /* Device is still opened somewhere. */
+        tty_unlock(tp);
+        return;
+    }
+
+    tp->t_flags |= TF_OPENCLOSE;
+
+    /* Remove console TTY. */
+    constty_clear(tp);
+
+    /* Drain any output. */
+    if (!tty_gone(tp))
+        tty_drain(tp, 1);
+
+    ttydisc_close(tp);
+
+    /* Free i/o queues now since they might be large. */
+    ttyinq_free(&tp->t_inq);
+    tp->t_inlow = 0;
+    ttyoutq_free(&tp->t_outq);
+    tp->t_outlow = 0;
+
+#ifdef USING_BSD_KNOTE
+    knlist_clear(&tp->t_inpoll.si_note, 1);
+    knlist_clear(&tp->t_outpoll.si_note, 1);
+#endif
+
+    if (!tty_gone(tp))
+        ttydevsw_close(tp);
+
+    tp->t_flags &= ~TF_OPENCLOSE;
+    cv_broadcast(&tp->t_dcdwait);
+    tty_rel_free(tp);
+}
+
+/*
+ * Operations that are exposed through the character device in /dev.
+ */
+static int ttydev_open(struct lwp_tty *tp, int oflags, int devtype,
+                       struct rt_thread *td)
+{
+    rt_device_t dev = &tp->parent;
+    int error;
+
+    error = 0;
+    tty_lock(tp);
+    if (tty_gone(tp))
+    {
+        /* Device is already gone. */
+        tty_unlock(tp);
+        return -ENXIO;
+    }
+
+    /*
+     * Block when other processes are currently opening or closing
+     * the TTY.
+     */
+    while (tp->t_flags & TF_OPENCLOSE)
+    {
+        error = tty_wait(tp, &tp->t_dcdwait);
+        if (error != 0)
+        {
+            tty_unlock(tp);
+            return error;
+        }
+    }
+    tp->t_flags |= TF_OPENCLOSE;
+
+    /*
+     * Make sure the "tty" and "cua" device cannot be opened at the
+     * same time.  The console is a "tty" device.
+     */
+    if (TTY_CALLOUT(tp, dev))
+    {
+        if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN))
+        {
+            error = EBUSY;
+            goto done;
+        }
+    }
+    else
+    {
+        if (tp->t_flags & TF_OPENED_OUT)
+        {
+            error = EBUSY;
+            goto done;
+        }
+    }
+
+    if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE))
+    {
+        error = EBUSY;
+        goto done;
+    }
+
+    if (!tty_opened(tp))
+    {
+        /* Set proper termios flags. */
+        if (TTY_CALLOUT(tp, dev))
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+            tp->t_termios = tp->t_termios_init_out;
+#else
+            ;
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+        else
+            tp->t_termios = tp->t_termios_init_in;
+
+        ttydevsw_param(tp, &tp->t_termios);
+        /* Prevent modem control on callout devices and /dev/console. */
+        if (TTY_CALLOUT(tp, dev) || dev == dev_console)
+            tp->t_termios.c_cflag |= CLOCAL;
+
+        if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0)
+            ttydevsw_modem(tp, SER_DTR | SER_RTS, 0);
+
+        error = ttydevsw_open(tp);
+        if (error != 0)
+            goto done;
+
+        ttydisc_open(tp);
+        error = tty_watermarks(tp);
+        if (error != 0)
+            goto done;
+    }
+
+    /* Wait for Carrier Detect. */
+    if ((oflags & O_NONBLOCK) == 0 && (tp->t_termios.c_cflag & CLOCAL) == 0)
+    {
+        while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0)
+        {
+            error = tty_wait(tp, &tp->t_dcdwait);
+            if (error != 0)
+                goto done;
+        }
+    }
+
+    if (dev == dev_console)
+        tp->t_flags |= TF_OPENED_CONS;
+    else if (TTY_CALLOUT(tp, dev))
+        tp->t_flags |= TF_OPENED_OUT;
+    else
+        tp->t_flags |= TF_OPENED_IN;
+    MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
+          (tp->t_flags & TF_OPENED_OUT) == 0);
+
+done:
+    tp->t_flags &= ~TF_OPENCLOSE;
+    cv_broadcast(&tp->t_dcdwait);
+    ttydev_leave(tp);
+
+    return error;
+}
+
+static int ttydev_close(struct lwp_tty *tp, int fflag, int devtype __unused,
+                        struct rt_thread *td __unused)
+{
+    rt_device_t dev = &tp->parent;
+
+    tty_lock(tp);
+
+    /*
+     * Don't actually close the device if it is being used as the
+     * console.
+     */
+    MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
+          (tp->t_flags & TF_OPENED_OUT) == 0);
+    if (dev == dev_console)
+        tp->t_flags &= ~TF_OPENED_CONS;
+    else
+        tp->t_flags &= ~(TF_OPENED_IN | TF_OPENED_OUT);
+
+    if (tp->t_flags & TF_OPENED)
+    {
+        tty_unlock(tp);
+        return 0;
+    }
+
+    /* If revoking, flush output now to avoid draining it later. */
+    if (fflag & FREVOKE)
+        tty_flush(tp, FWRITE);
+
+    tp->t_flags &= ~TF_EXCLUDE;
+
+    /* Properly wake up threads that are stuck - revoke(). */
+    tp->t_revokecnt++;
+    tty_wakeup(tp, FREAD | FWRITE);
+    cv_broadcast(&tp->t_bgwait);
+    cv_broadcast(&tp->t_dcdwait);
+
+    ttydev_leave(tp);
+
+    return 0;
+}
+
+int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig)
+{
+    struct rt_lwp *p;
+    struct rt_processgroup *pg;
+    int error;
+
+    MPASS(sig == SIGTTIN || sig == SIGTTOU);
+    tty_assert_locked(tp);
+
+    p = td->lwp;
+    for (;;)
+    {
+        pg = p->pgrp;
+        PGRP_LOCK(pg);
+        LWP_LOCK(p);
+
+        /*
+         * pg may no longer be our process group.
+         * Re-check after locking.
+         */
+        if (p->pgrp != pg)
+        {
+            LWP_UNLOCK(p);
+            PGRP_UNLOCK(pg);
+            continue;
+        }
+
+        /*
+         * The process should only sleep, when:
+         * - This terminal is the controlling terminal
+         * - Its process group is not the foreground process
+         *   group
+         * - The parent process isn't waiting for the child to
+         *   exit
+         * - the signal to send to the process isn't masked
+         */
+        if (!tty_is_ctty(tp, p) || pg == tp->t_pgrp)
+        {
+            /* Allow the action to happen. */
+            LWP_UNLOCK(p);
+            PGRP_UNLOCK(pg);
+            return 0;
+        }
+
+        /* Note: process itself don't have a sigmask in smart */
+        if (lwp_sigisign(p, sig) ||
+            lwp_sigismember(&td->signal.sigset_mask, sig))
+        {
+            /* Only allow them in write()/ioctl(). */
+            LWP_UNLOCK(p);
+            PGRP_UNLOCK(pg);
+            return (sig == SIGTTOU ? 0 : -EIO);
+        }
+
+#ifdef USING_VFORK_FLAG
+        if ((p->p_flag & P_PPWAIT) != 0 || pg->is_orphaned)
+#else
+        if (pg->is_orphaned)
+#endif
+        {
+            /* Don't allow the action to happen. */
+            LWP_UNLOCK(p);
+            PGRP_UNLOCK(pg);
+            return -EIO;
+        }
+        LWP_UNLOCK(p);
+
+        /*
+         * Send the signal and sleep until we're the new
+         * foreground process group.
+         */
+        if (sig != 0)
+        {
+            lwp_pgrp_signal_kill(pg, sig, SI_KERNEL, 0);
+        }
+
+        PGRP_UNLOCK(pg);
+
+        error = tty_wait(tp, &tp->t_bgwait);
+        if (error)
+            return error;
+    }
+}
+
+static int ttydev_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+    int error;
+
+    error = ttydev_enter(tp);
+    if (error)
+        goto done;
+    error = ttydisc_read(tp, uio, ioflag);
+    tty_unlock(tp);
+
+    /*
+     * The read() call should not throw an error when the device is
+     * being destroyed. Silently convert it to an EOF.
+     */
+done:
+    if (error == -ENXIO)
+        error = 0;
+    return error;
+}
+
+static int ttydev_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+#ifdef USING_BSD_DEFER_STOP
+    int defer;
+#endif
+    int error;
+
+    error = ttydev_enter(tp);
+    if (error)
+        return error;
+
+    if (tp->t_termios.c_lflag & TOSTOP)
+    {
+        error = tty_wait_background(tp, curthread, SIGTTOU);
+        if (error)
+            goto done;
+    }
+
+    if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT)
+    {
+        /* Allow non-blocking writes to bypass serialization. */
+        error = ttydisc_write(tp, uio, ioflag);
+    }
+    else
+    {
+        /* Serialize write() calls. */
+        while (tp->t_flags & TF_BUSY_OUT)
+        {
+            error = tty_wait(tp, &tp->t_outserwait);
+            if (error)
+                goto done;
+        }
+
+        tp->t_flags |= TF_BUSY_OUT;
+#ifdef USING_BSD_DEFER_STOP
+        defer = sigdeferstop(SIGDEFERSTOP_ERESTART);
+#endif
+        error = ttydisc_write(tp, uio, ioflag);
+#ifdef USING_BSD_DEFER_STOP
+        sigallowstop(defer);
+#endif
+        tp->t_flags &= ~TF_BUSY_OUT;
+        cv_signal(&tp->t_outserwait);
+    }
+
+done:
+    tty_unlock(tp);
+    return error;
+}
+
+static int ttydev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
+                        struct rt_thread *td)
+{
+    int error;
+
+    error = ttydev_enter(tp);
+    if (error)
+        return (error);
+
+    switch (cmd)
+    {
+        case TIOCCBRK:
+        case TIOCCONS:
+        case TIOCDRAIN:
+        case TIOCEXCL:
+        case TIOCFLUSH:
+        case TIOCNXCL:
+        case TIOCSBRK:
+        case TIOCSCTTY:
+        case TIOCSETA:
+        case TIOCSETAF:
+        case TIOCSETAW:
+        case TIOCSPGRP:
+        case TIOCSTART:
+        case TIOCSTAT:
+        case TIOCSTI:
+        case TIOCSTOP:
+        case TIOCSWINSZ:
+#if USING_BSD_TIOCSDRAINWAIT
+    case TIOCSDRAINWAIT:
+    case TIOCSETD:
+#endif /* USING_BSD_TIOCSDRAINWAIT */
+#ifdef COMPAT_43TTY
+        case TIOCLBIC:
+        case TIOCLBIS:
+        case TIOCLSET:
+        case TIOCSETC:
+        case OTIOCSETD:
+        case TIOCSETN:
+        case TIOCSETP:
+        case TIOCSLTC:
+#endif /* COMPAT_43TTY */
+            /*
+             * If the ioctl() causes the TTY to be modified, let it
+             * wait in the background.
+             */
+            error = tty_wait_background(tp, curthread, SIGTTOU);
+            if (error)
+                goto done;
+    }
+
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+    if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF)
+    {
+        struct termios *old = &tp->t_termios;
+        struct termios *new = (struct termios *)data;
+        struct termios *lock = TTY_CALLOUT(tp, dev) ? &tp->t_termios_lock_out
+                                                    : &tp->t_termios_lock_in;
+        int cc;
+
+        /*
+         * Lock state devices.  Just overwrite the values of the
+         * commands that are currently in use.
+         */
+        new->c_iflag =
+            (old->c_iflag & lock->c_iflag) | (new->c_iflag & ~lock->c_iflag);
+        new->c_oflag =
+            (old->c_oflag & lock->c_oflag) | (new->c_oflag & ~lock->c_oflag);
+        new->c_cflag =
+            (old->c_cflag & lock->c_cflag) | (new->c_cflag & ~lock->c_cflag);
+        new->c_lflag =
+            (old->c_lflag & lock->c_lflag) | (new->c_lflag & ~lock->c_lflag);
+        for (cc = 0; cc < NCCS; ++cc)
+            if (lock->c_cc[cc])
+                new->c_cc[cc] = old->c_cc[cc];
+        if (lock->__c_ispeed)
+            new->__c_ispeed = old->__c_ispeed;
+        if (lock->__c_ospeed)
+            new->__c_ospeed = old->__c_ospeed;
+    }
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+
+    error = tty_ioctl(tp, cmd, data, fflag, td);
+done:
+    tty_unlock(tp);
+
+    return (error);
+}
+
+static int ttydev_poll(struct lwp_tty *tp, rt_pollreq_t *req, struct rt_thread *td)
+{
+    int events = req->_key;
+    int error, revents = 0;
+
+    error = ttydev_enter(tp);
+    if (error)
+        return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
+
+    if (events & (POLLIN | POLLRDNORM))
+    {
+        /* See if we can read something. */
+        if (ttydisc_read_poll(tp) > 0)
+            revents |= events & (POLLIN | POLLRDNORM);
+    }
+
+    if (tp->t_flags & TF_ZOMBIE)
+    {
+        /* Hangup flag on zombie state. */
+        revents |= POLLHUP;
+    }
+    else if (events & (POLLOUT | POLLWRNORM))
+    {
+        /* See if we can write something. */
+        if (ttydisc_write_poll(tp) > 0)
+            revents |= events & (POLLOUT | POLLWRNORM);
+    }
+
+    if (revents == 0)
+    {
+        if (events & (POLLIN | POLLRDNORM))
+            rt_poll_add(&tp->t_inpoll, req);
+        if (events & (POLLOUT | POLLWRNORM))
+            rt_poll_add(&tp->t_outpoll, req);
+    }
+
+    tty_unlock(tp);
+
+    return revents;
+}
+
+static struct cdevsw ttydev_cdevsw = {
+    .d_open = ttydev_open,
+    .d_close = ttydev_close,
+    .d_read = ttydev_read,
+    .d_write = ttydev_write,
+    .d_ioctl = ttydev_ioctl,
+#if 0
+    .d_kqfilter = ttydev_kqfilter,
+#endif
+    .d_poll = ttydev_poll,
+#if 0
+    .d_mmap = ttydev_mmap,
+#endif
+
+#ifdef USING_BSD_RAW_CDEVSW
+    .d_version = D_VERSION.d_name = "ttydev",
+    .d_flags = D_TTY,
+#endif /* USING_BSD_RAW_CDEVSW */
+};
+
+extern struct cdevsw bsd_ttydev_methods __attribute__((alias("ttydev_cdevsw")));
+
+/*
+ * Standard device routine implementations, mostly meant for
+ * pseudo-terminal device drivers. When a driver creates a new terminal
+ * device class, missing routines are patched.
+ */
+#define panic(msg) RT_ASSERT(0 && msg)
+
+static int ttydevsw_defopen(struct lwp_tty *tp __unused)
+{
+    return 0;
+}
+
+static void ttydevsw_defclose(struct lwp_tty *tp __unused)
+{
+}
+
+static void ttydevsw_defoutwakeup(struct lwp_tty *tp __unused)
+{
+    panic("Terminal device has output, while not implemented");
+}
+
+static void ttydevsw_definwakeup(struct lwp_tty *tp __unused)
+{
+}
+
+static int ttydevsw_defioctl(struct lwp_tty *tp __unused, rt_ubase_t cmd __unused,
+                             rt_caddr_t data __unused,
+                             struct rt_thread *td __unused)
+{
+    return -ENOSYS;
+}
+
+static int ttydevsw_defcioctl(struct lwp_tty *tp __unused, int unit __unused,
+                              rt_ubase_t cmd __unused, rt_caddr_t data __unused,
+                              struct rt_thread *td __unused)
+{
+    return -ENOSYS;
+}
+
+static int ttydevsw_defparam(struct lwp_tty *tp __unused, struct termios *t)
+{
+    /*
+     * Allow the baud rate to be adjusted for pseudo-devices, but at
+     * least restrict it to 115200 to prevent excessive buffer
+     * usage.  Also disallow 0, to prevent foot shooting.
+     */
+    if (t->__c_ispeed < B50)
+        t->__c_ispeed = B50;
+    else if (t->__c_ispeed > B115200)
+        t->__c_ispeed = B115200;
+    if (t->__c_ospeed < B50)
+        t->__c_ospeed = B50;
+    else if (t->__c_ospeed > B115200)
+        t->__c_ospeed = B115200;
+    t->c_cflag |= CREAD;
+
+    return 0;
+}
+
+static int ttydevsw_defmodem(struct lwp_tty *tp __unused, int sigon __unused,
+                             int sigoff __unused)
+{
+    /* Simulate a carrier to make the TTY layer happy. */
+    return (SER_DCD);
+}
+
+static int ttydevsw_defmmap(struct lwp_tty *tp __unused,
+                            vm_ooffset_t offset __unused,
+                            vm_paddr_t *paddr __unused, int nprot __unused,
+                            vm_memattr_t *memattr __unused)
+{
+    return (-1);
+}
+
+static void ttydevsw_defpktnotify(struct lwp_tty *tp __unused,
+                                  char event __unused)
+{
+}
+
+static void ttydevsw_deffree(void *softc __unused)
+{
+    panic("Terminal device freed without a free-handler");
+}
+
+static rt_bool_t ttydevsw_defbusy(struct lwp_tty *tp __unused)
+{
+    return (RT_FALSE);
+}
+
+void bsd_devsw_init(struct lwp_ttydevsw *tsw)
+{
+    /* Make sure the driver defines all routines. */
+#define PATCH_FUNC(x)                       \
+    do                                      \
+    {                                       \
+        if (tsw->tsw_##x == NULL)           \
+            tsw->tsw_##x = ttydevsw_def##x; \
+    } while (0)
+
+    PATCH_FUNC(open);
+    PATCH_FUNC(close);
+    PATCH_FUNC(outwakeup);
+    PATCH_FUNC(inwakeup);
+    PATCH_FUNC(ioctl);
+    PATCH_FUNC(cioctl);
+    PATCH_FUNC(param);
+    PATCH_FUNC(modem);
+    PATCH_FUNC(mmap);
+    PATCH_FUNC(pktnotify);
+    PATCH_FUNC(free);
+    PATCH_FUNC(busy);
+#undef PATCH_FUNC
+}
+
+/* release tty, and free the cdev resource */
+static void tty_rel_free(struct lwp_tty *tp)
+{
+#ifdef USING_BSD_CHAR_DEVICE
+    struct cdev *dev;
+#endif
+
+    tty_assert_locked(tp);
+
+#define TF_ACTIVITY (TF_GONE | TF_OPENED | TF_HOOK | TF_OPENCLOSE)
+    if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE)
+    {
+        /* TTY is still in use. */
+        tty_unlock(tp);
+        return;
+    }
+
+#ifdef USING_BSD_AIO
+    /* Stop asynchronous I/O. */
+    funsetown(&tp->t_sigio);
+#endif /* USING_BSD_AIO */
+
+#ifdef USING_BSD_CHAR_DEVICE
+    /* TTY can be deallocated. */
+    dev = tp->t_dev;
+    tp->t_dev = NULL;
+#endif /* USING_BSD_CHAR_DEVICE */
+
+    tty_unlock(tp);
+
+#ifdef USING_BSD_CHAR_DEVICE
+    if (dev != NULL)
+    {
+        sx_xlock(&tty_list_sx);
+        TAILQ_REMOVE(&tty_list, tp, t_list);
+        tty_list_count--;
+        sx_xunlock(&tty_list_sx);
+        destroy_dev_sched_cb(dev, tty_dealloc, tp);
+    }
+#else
+    lwp_tty_delete(tp);
+#endif
+}
+
+void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pg)
+{
+    MPASS(tp->t_sessioncnt > 0);
+    tty_assert_locked(tp);
+
+    if (tp->t_pgrp == pg)
+        tp->t_pgrp = NULL;
+
+    tty_unlock(tp);
+}
+
+void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess)
+{
+    MPASS(tp->t_sessioncnt > 0);
+
+    /* Current session has left. */
+    if (tp->t_session == sess)
+    {
+        tp->t_session = NULL;
+        MPASS(tp->t_pgrp == NULL);
+    }
+    tp->t_sessioncnt--;
+    tty_rel_free(tp);
+}
+
+/* deallocate the tty */
+void tty_rel_gone(struct lwp_tty *tp)
+{
+    tty_assert_locked(tp);
+    MPASS(!tty_gone(tp));
+
+    /* Simulate carrier removal. */
+    ttydisc_modem(tp, 0);
+
+    /* Wake up all blocked threads. */
+    tty_wakeup(tp, FREAD | FWRITE);
+    cv_broadcast(&tp->t_bgwait);
+    cv_broadcast(&tp->t_dcdwait);
+
+    tp->t_flags |= TF_GONE;
+    tty_rel_free(tp);
+}
+
+static int tty_drop_ctty(struct lwp_tty *tp, struct rt_lwp *p)
+{
+    struct rt_session *session;
+#ifdef USING_BSD_VNODE
+    struct vnode *vp;
+#endif
+
+    /*
+     * This looks terrible, but it's generally safe as long as the tty
+     * hasn't gone away while we had the lock dropped.  All of our sanity
+     * checking that this operation is OK happens after we've picked it back
+     * up, so other state changes are generally not fatal and the potential
+     * for this particular operation to happen out-of-order in a
+     * multithreaded scenario is likely a non-issue.
+     */
+    tty_unlock(tp);
+    LWP_LOCK(p);
+    tty_lock(tp);
+    if (tty_gone(tp))
+    {
+        LWP_UNLOCK(p);
+        return -ENODEV;
+    }
+
+    /*
+     * If the session doesn't have a controlling TTY, or if we weren't
+     * invoked on the controlling TTY, we'll return ENOIOCTL as we've
+     * historically done.
+     */
+    session = p->pgrp->session;
+    if (session->ctty == NULL || session->ctty != tp)
+    {
+        LWP_UNLOCK(p);
+        return -ENOTTY;
+    }
+
+    if (!is_sess_leader(p))
+    {
+        LWP_UNLOCK(p);
+        return -EPERM;
+    }
+
+    SESS_LOCK(session);
+#ifdef USING_BSD_VNODE
+    vp = session->s_ttyvp;
+#endif
+    session->ctty = NULL;
+#ifdef USING_BSD_VNODE
+    session->s_ttyvp = NULL;
+    session->s_ttydp = NULL;
+#endif
+    SESS_UNLOCK(session);
+
+    tp->t_sessioncnt--;
+    p->term_ctrlterm = RT_FALSE;
+    LWP_UNLOCK(p);
+
+#ifdef USING_BSD_VNODE
+    /*
+     * If we did have a vnode, release our reference.  Ordinarily we manage
+     * these at the devfs layer, but we can't necessarily know that we were
+     * invoked on the vnode referenced in the session (i.e. the vnode we
+     * hold a reference to).  We explicitly don't check VBAD/VIRF_DOOMED here
+     * to avoid a vnode leak -- in circumstances elsewhere where we'd hit a
+     * VIRF_DOOMED vnode, release has been deferred until the controlling TTY
+     * is either changed or released.
+     */
+    if (vp != NULL)
+        devfs_ctty_unref(vp);
+#endif
+
+    return 0;
+}
+
+void tty_wakeup(struct lwp_tty *tp, int flags)
+{
+#ifdef USING_BSD_AIO
+    if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL)
+        pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
+#endif
+
+    if (flags & FWRITE)
+    {
+        cv_broadcast(&tp->t_outwait);
+#ifdef USING_BSD_POLL
+        selwakeup(&tp->t_outpoll);
+        KNOTE_LOCKED(&tp->t_outpoll.si_note, 0);
+#else
+        rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLOUT);
+#endif
+    }
+    if (flags & FREAD)
+    {
+        cv_broadcast(&tp->t_inwait);
+#ifdef USING_BSD_POLL
+        selwakeup(&tp->t_inpoll);
+        KNOTE_LOCKED(&tp->t_inpoll.si_note, 0);
+#else
+        rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLIN);
+#endif
+    }
+}
+
+int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv)
+{
+    int error;
+    int revokecnt = tp->t_revokecnt;
+
+    tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+    MPASS(!tty_gone(tp));
+
+    error = cv_wait_sig(cv, tp->t_mtx);
+
+    /* Bail out when the device slipped away. */
+    if (tty_gone(tp))
+        return -ENXIO;
+
+    /* Restart the system call when we may have been revoked. */
+    if (tp->t_revokecnt != revokecnt)
+        return -ERESTART;
+
+    return error;
+}
+
+int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout)
+{
+    int error;
+    int revokecnt = tp->t_revokecnt;
+
+    tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+    MPASS(!tty_gone(tp));
+
+    error = cv_timedwait_sig(cv, tp->t_mtx, timeout);
+
+    /* Bail out when the device slipped away. */
+    if (tty_gone(tp))
+        return -ENXIO;
+
+    /* Restart the system call when we may have been revoked. */
+    if (tp->t_revokecnt != revokecnt)
+        return -ERESTART;
+
+    return error;
+}
+
+/* discard data in I/O buffers */
+void tty_flush(struct lwp_tty *tp, int flags)
+{
+    if (flags & FWRITE)
+    {
+        tp->t_flags &= ~TF_HIWAT_OUT;
+        ttyoutq_flush(&tp->t_outq);
+        tty_wakeup(tp, FWRITE);
+        if (!tty_gone(tp))
+        {
+            ttydevsw_outwakeup(tp);
+            ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE);
+        }
+    }
+    if (flags & FREAD)
+    {
+        tty_hiwat_in_unblock(tp);
+        ttyinq_flush(&tp->t_inq);
+        tty_wakeup(tp, FREAD);
+        if (!tty_gone(tp))
+        {
+            ttydevsw_inwakeup(tp);
+            ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD);
+        }
+    }
+}
+
+void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz)
+{
+    if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0)
+        return;
+    tp->t_winsize = *wsz;
+    lwp_tty_signal_pgrp(tp, SIGWINCH);
+}
+
+static int tty_generic_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
+                             int fflag, struct rt_thread *td)
+{
+    int error;
+
+    switch (cmd)
+    {
+        /*
+         * Modem commands.
+         * The SER_* and TIOCM_* flags are the same, but one bit
+         * shifted. I don't know why.
+         */
+        case TIOCSDTR:
+            ttydevsw_modem(tp, SER_DTR, 0);
+            return 0;
+        case TIOCCDTR:
+            ttydevsw_modem(tp, 0, SER_DTR);
+            return 0;
+        case TIOCMSET: {
+            int bits = *(int *)data;
+            ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1,
+                           ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+            return 0;
+        }
+        case TIOCMBIS: {
+            int bits = *(int *)data;
+            ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0);
+            return 0;
+        }
+        case TIOCMBIC: {
+            int bits = *(int *)data;
+            ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+            return 0;
+        }
+        case TIOCMGET:
+            *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1);
+            return 0;
+
+        case FIOASYNC:
+            if (*(int *)data)
+                tp->t_flags |= TF_ASYNC;
+            else
+                tp->t_flags &= ~TF_ASYNC;
+            return 0;
+        case FIONBIO:
+            /* This device supports non-blocking operation. */
+            return 0;
+        case FIONREAD:
+            *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq);
+            return 0;
+        case FIONWRITE:
+        case TIOCOUTQ:
+            *(int *)data = ttyoutq_bytesused(&tp->t_outq);
+            return 0;
+#if BSD_USING_FIO_OWNERSHIP
+        case FIOSETOWN:
+            if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
+                /* Not allowed to set ownership. */
+                return -ENOTTY;
+
+            /* Temporarily unlock the TTY to set ownership. */
+            tty_unlock(tp);
+            error = fsetown(*(int *)data, &tp->t_sigio);
+            tty_lock(tp);
+            return (error);
+        case FIOGETOWN:
+            if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
+                /* Not allowed to set ownership. */
+                return -ENOTTY;
+
+            /* Get ownership. */
+            *(int *)data = fgetown(&tp->t_sigio);
+            return 0;
+#endif
+        case TIOCGETA:
+            /* Obtain terminal flags through tcgetattr(). */
+            *(struct termios *)data = tp->t_termios;
+            return 0;
+        case TIOCSETA:
+        case TIOCSETAW:
+        case TIOCSETAF: {
+            struct termios *t = data;
+
+            /*
+             * Who makes up these funny rules? According to POSIX,
+             * input baud rate is set equal to the output baud rate
+             * when zero.
+             */
+            if (t->__c_ispeed == 0)
+                t->__c_ispeed = t->__c_ospeed;
+
+            /* Discard any unsupported bits. */
+            t->c_iflag &= TTYSUP_IFLAG;
+            t->c_oflag &= TTYSUP_OFLAG;
+            t->c_lflag &= TTYSUP_LFLAG;
+            t->c_cflag &= TTYSUP_CFLAG;
+
+            /* Set terminal flags through tcsetattr(). */
+            if (cmd == TIOCSETAW || cmd == TIOCSETAF)
+            {
+                error = tty_drain(tp, 0);
+                if (error)
+                    return (error);
+                if (cmd == TIOCSETAF)
+                    tty_flush(tp, FREAD);
+            }
+
+            /*
+             * Only call param() when the flags really change.
+             */
+            if ((t->c_cflag & CIGNORE) == 0 &&
+                (tp->t_termios.c_cflag != t->c_cflag ||
+                 ((tp->t_termios.c_iflag ^ t->c_iflag) &
+                  (IXON | IXOFF | IXANY)) ||
+                 tp->t_termios.__c_ispeed != t->__c_ispeed ||
+                 tp->t_termios.__c_ospeed != t->__c_ospeed))
+            {
+                error = ttydevsw_param(tp, t);
+                if (error)
+                    return (error);
+
+                /* XXX: CLOCAL? */
+
+                tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE;
+                tp->t_termios.__c_ispeed = t->__c_ispeed;
+                tp->t_termios.__c_ospeed = t->__c_ospeed;
+
+                /* Baud rate has changed - update watermarks. */
+                error = tty_watermarks(tp);
+                if (error)
+                    return (error);
+            }
+
+            /* Copy new non-device driver parameters. */
+            tp->t_termios.c_iflag = t->c_iflag;
+            tp->t_termios.c_oflag = t->c_oflag;
+            tp->t_termios.c_lflag = t->c_lflag;
+            memcpy(&tp->t_termios.c_cc, t->c_cc, sizeof t->c_cc);
+
+            ttydisc_optimize(tp);
+
+            if ((t->c_lflag & ICANON) == 0)
+            {
+                /*
+                 * When in non-canonical mode, wake up all
+                 * readers. Canonicalize any partial input. VMIN
+                 * and VTIME could also be adjusted.
+                 */
+                ttyinq_canonicalize(&tp->t_inq);
+                tty_wakeup(tp, FREAD);
+            }
+
+            /**
+             * For packet mode: notify the PTY consumer that VSTOP
+             * and VSTART may have been changed.
+             *
+             * TODO: change the _CONTROL('S') to a CSTOP?
+             */
+            if (tp->t_termios.c_iflag & IXON &&
+                tp->t_termios.c_cc[VSTOP] == _CONTROL('S') &&
+                tp->t_termios.c_cc[VSTART] == _CONTROL('Q'))
+                ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
+            else
+                ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
+            return 0;
+        }
+        case TIOCGETD:
+            /* For compatibility - we only support TTYDISC. */
+            *(int *)data = TTYDISC;
+            return 0;
+        case TIOCGPGRP:
+            if (!tty_is_ctty(tp, td->lwp))
+                return -ENOTTY;
+
+            if (tp->t_pgrp != NULL)
+                *(int *)data = tp->t_pgrp->pgid;
+            else
+                *(int *)data = NO_PID;
+            return 0;
+        case TIOCGSID:
+            if (!tty_is_ctty(tp, td->lwp))
+                return -ENOTTY;
+
+            MPASS(tp->t_session);
+            *(int *)data = tp->t_session->sid;
+            return 0;
+        case TIOCNOTTY:
+            return tty_drop_ctty(tp, td->lwp);
+        case TIOCSCTTY:
+            return lwp_tty_set_ctrl_proc(tp, td);
+        case TIOCSPGRP: {
+            int pgid;
+            if (lwp_in_user_space((void *)data))
+            {
+                if (lwp_get_from_user(&pgid, data, sizeof(int)) != sizeof(int))
+                    return -EFAULT;
+            }
+            else
+            {
+                pgid = *(int *)data;
+            }
+            return lwp_tty_assign_foreground(tp, td, pgid);
+        }
+        case TIOCFLUSH: {
+            int flags = *(int *)data;
+
+            if (flags == 0)
+                flags = (FREAD | FWRITE);
+            else
+                flags &= (FREAD | FWRITE);
+            tty_flush(tp, flags);
+            return 0;
+        }
+        case TIOCDRAIN:
+            /* Drain TTY output. */
+            return tty_drain(tp, 0);
+        case TIOCGDRAINWAIT:
+            *(int *)data = tp->t_drainwait;
+            return 0;
+        case TIOCSDRAINWAIT:
+            error = priv_check(td, PRIV_TTY_DRAINWAIT);
+            if (error == 0)
+                tp->t_drainwait = *(int *)data;
+            return (error);
+        case TIOCCONS:
+            /* Set terminal as console TTY. */
+            if (*(int *)data)
+            {
+                error = priv_check(td, PRIV_TTY_CONSOLE);
+                if (error)
+                    return (error);
+                error = constty_set(tp);
+            }
+            else
+            {
+                error = constty_clear(tp);
+            }
+            return (error);
+        case TIOCGWINSZ:
+            /* Obtain window size. */
+            *(struct winsize *)data = tp->t_winsize;
+            return 0;
+        case TIOCSWINSZ:
+            /* Set window size. */
+            tty_set_winsize(tp, data);
+            return 0;
+        case TIOCEXCL:
+            tp->t_flags |= TF_EXCLUDE;
+            return 0;
+        case TIOCNXCL:
+            tp->t_flags &= ~TF_EXCLUDE;
+            return 0;
+        case TIOCSTOP:
+            tp->t_flags |= TF_STOPPED;
+            ttydevsw_pktnotify(tp, TIOCPKT_STOP);
+            return 0;
+        case TIOCSTART:
+            tp->t_flags &= ~TF_STOPPED;
+            tp->t_termios.c_lflag &= ~FLUSHO;
+            ttydevsw_outwakeup(tp);
+            ttydevsw_pktnotify(tp, TIOCPKT_START);
+            return 0;
+        case TIOCSTAT:
+            tty_info(tp);
+            return 0;
+        case TIOCSTI:
+            if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI))
+                return -EPERM;
+            if (!tty_is_ctty(tp, td->lwp) && priv_check(td, PRIV_TTY_STI))
+                return -EACCES;
+            ttydisc_rint(tp, *(char *)data, 0);
+            ttydisc_rint_done(tp);
+            return 0;
+    }
+
+#ifdef COMPAT_43TTY
+    return tty_ioctl_compat(tp, cmd, data, fflag, td);
+#else  /* !COMPAT_43TTY */
+    return -ENOIOCTL;
+#endif /* COMPAT_43TTY */
+}
+
+int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
+              struct rt_thread *td)
+{
+    int error;
+
+    tty_assert_locked(tp);
+
+    if (tty_gone(tp))
+        return -ENXIO;
+
+    error = ttydevsw_ioctl(tp, cmd, data, td);
+    if (error == -ENOIOCTL)
+        error = tty_generic_ioctl(tp, cmd, data, fflag, td);
+
+    return error;
+}
+
+int tty_checkoutq(struct lwp_tty *tp)
+{
+    /* 256 bytes should be enough to print a log message. */
+    return (ttyoutq_bytesleft(&tp->t_outq) >= 256);
+}
+
+void tty_hiwat_in_block(struct lwp_tty *tp)
+{
+    if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF &&
+        tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE)
+    {
+        /*
+         * Input flow control. Only enter the high watermark when we
+         * can successfully store the VSTOP character.
+         */
+        if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTOP], 1) ==
+            0)
+            tp->t_flags |= TF_HIWAT_IN;
+    }
+    else
+    {
+        /* No input flow control. */
+        tp->t_flags |= TF_HIWAT_IN;
+    }
+}
+
+void tty_hiwat_in_unblock(struct lwp_tty *tp)
+{
+    if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF &&
+        tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE)
+    {
+        /*
+         * Input flow control. Only leave the high watermark when we
+         * can successfully store the VSTART character.
+         */
+        if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTART], 1) ==
+            0)
+            tp->t_flags &= ~TF_HIWAT_IN;
+    }
+    else
+    {
+        /* No input flow control. */
+        tp->t_flags &= ~TF_HIWAT_IN;
+    }
+
+    if (!tty_gone(tp))
+        ttydevsw_inwakeup(tp);
+}

+ 711 - 0
components/lwp/terminal/freebsd/tty_compat.c

@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * (tty_compat.c)
+ * The compatible layer which interacts with process management core (lwp)
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "../tty_config.h"
+#include "../tty_internal.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* is the tty and session leader already binding ? */
+static rt_bool_t _is_already_binding(lwp_tty_t tp, rt_lwp_t p)
+{
+    rt_bool_t rc;
+    rt_processgroup_t pgrp = p->pgrp;
+
+    /* lwp is already locked */
+    RT_ASSERT(pgrp);
+
+    /* Note: pgrp->session is constant after process group is created */
+    if (tp->t_session && tp->t_session == pgrp->session)
+    {
+        rc = RT_TRUE;
+    }
+    else
+    {
+        rc = RT_FALSE;
+    }
+    return rc;
+}
+
+static rt_bool_t _is_tty_or_sess_busy(lwp_tty_t tp, rt_lwp_t p)
+{
+    rt_bool_t rc;
+    rt_session_t sess = p->pgrp->session;
+
+    SESS_LOCK(sess);
+    if (sess->ctty)
+    {
+        rc = RT_TRUE;
+    }
+    else if (tp->t_session)
+    {
+        /**
+         * TODO: allow TTY stolen if the sess leader is killed while resource
+         * had not been collected
+         */
+        if (tp->t_session->leader == RT_NULL)
+            rc = RT_FALSE;
+        else
+            rc = RT_TRUE;
+    }
+    else
+    {
+        rc = RT_FALSE;
+    }
+    SESS_UNLOCK(sess);
+    return rc;
+}
+
+int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv)
+{
+    int error;
+    int revokecnt = tp->t_revokecnt;
+    rt_lwp_t self_lwp;
+    rt_thread_t header_thr;
+    rt_thread_t cur_thr = rt_thread_self();
+    int jobctl_stopped;
+
+    self_lwp = cur_thr->lwp;
+    RT_ASSERT(self_lwp);
+
+    jobctl_stopped = self_lwp->jobctl_stopped;
+
+    tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+    MPASS(!tty_gone(tp));
+
+    LWP_LOCK(self_lwp);
+    header_thr = rt_list_entry(self_lwp->t_grp.prev, struct rt_thread, sibling);
+    if (!jobctl_stopped && header_thr == cur_thr &&
+        cur_thr->sibling.prev == &self_lwp->t_grp)
+    {
+        /* update lwp status */
+        jobctl_stopped = self_lwp->jobctl_stopped = RT_TRUE;
+    }
+    LWP_UNLOCK(self_lwp);
+
+    error = cv_wait(cv, tp->t_mtx);
+
+    if (jobctl_stopped)
+    {
+        self_lwp->jobctl_stopped = RT_FALSE;
+    }
+
+    /* Bail out when the device slipped away. */
+    if (tty_gone(tp))
+        return -ENXIO;
+
+    /* Restart the system call when we may have been revoked. */
+    if (tp->t_revokecnt != revokecnt)
+        return -ERESTART;
+
+    return error;
+}
+
+/* process management */
+int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td)
+{
+    int rc = -1;
+    struct rt_lwp *p = td->lwp;
+
+    tty_unlock(tp);
+    LWP_LOCK(p);
+    tty_lock(tp);
+
+    if (is_sess_leader(p))
+    {
+        if (_is_already_binding(tp, p))
+        {
+            rc = 0;
+        }
+        else if (_is_tty_or_sess_busy(tp, p))
+        {
+            rc = -EPERM;
+        }
+        else
+        {
+            /**
+             * Binding controlling process
+             * note: p->pgrp is protected by lwp lock;
+             *       pgrp->session is always constant.
+             */
+            tp->t_session = p->pgrp->session;
+            tp->t_session->ctty = tp;
+            tp->t_sessioncnt++;
+
+            /* Assign foreground process group */
+            tp->t_pgrp = p->pgrp;
+            p->term_ctrlterm = RT_TRUE;
+
+            LOG_D("%s(sid=%d)", __func__, tp->t_session->sid);
+            rc = 0;
+        }
+    }
+    else
+    {
+        rc = -EPERM;
+    }
+
+    LWP_UNLOCK(p);
+
+    return rc;
+}
+
+int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid)
+{
+    struct rt_processgroup *pg;
+    rt_lwp_t cur_lwp = td->lwp;
+
+    tty_unlock(tp);
+    pg = lwp_pgrp_find_and_inc_ref(pgid);
+    if (pg == NULL || cur_lwp == NULL)
+    {
+        tty_lock(tp);
+        return -EPERM;
+    }
+    else
+    {
+        PGRP_LOCK(pg);
+
+        if (pg->sid != cur_lwp->sid)
+        {
+            PGRP_UNLOCK(pg);
+            lwp_pgrp_dec_ref(pg);
+            LOG_D("%s: NoPerm current process (pid=%d, pgid=%d, sid=%d), "
+                  "tagget group (pgid=%d, sid=%d)", __func__,
+                  cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
+            tty_lock(tp);
+            return -EPERM;
+        }
+    }
+    tty_lock(tp);
+
+    /**
+     * Determine if this TTY is the controlling TTY after
+     * relocking the TTY.
+     */
+    if (!tty_is_ctty(tp, td->lwp))
+    {
+        PGRP_UNLOCK(pg);
+        LOG_D("%s: NoCTTY current process (pid=%d, pgid=%d, sid=%d), "
+              "tagget group (pgid=%d, sid=%d)", __func__,
+              cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
+        return -ENOTTY;
+    }
+    tp->t_pgrp = pg;
+    PGRP_UNLOCK(pg);
+    lwp_pgrp_dec_ref(pg);
+
+    /* Wake up the background process groups. */
+    cv_broadcast(&tp->t_bgwait);
+
+    LOG_D("%s: Foreground group %p (pgid=%d)", __func__, tp->t_pgrp,
+          tp->t_pgrp ? tp->t_pgrp->pgid : -1);
+
+    return 0;
+}
+
+/**
+ * Signalling processes.
+ */
+
+void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig)
+{
+    struct rt_lwp *p;
+    struct rt_session *s;
+
+    tty_assert_locked(tp);
+    MPASS(sig >= 1 && sig < _LWP_NSIG);
+
+    /* Make signals start output again. */
+    tp->t_flags &= ~TF_STOPPED;
+    tp->t_termios.c_lflag &= ~FLUSHO;
+
+    /**
+     * Load s.leader exactly once to avoid race where s.leader is
+     * set to NULL by a concurrent invocation of killjobc() by the
+     * session leader.  Note that we are not holding t_session's
+     * lock for the read.
+     */
+    if ((s = tp->t_session) != NULL &&
+        (p = (void *)rt_atomic_load((rt_atomic_t *)&s->leader)) != NULL)
+    {
+        lwp_signal_kill(p, sig, SI_KERNEL, 0);
+    }
+}
+
+void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig)
+{
+    tty_assert_locked(tp);
+    MPASS(sig >= 1 && sig < _LWP_NSIG);
+
+    /* Make signals start output again. */
+    tp->t_flags &= ~TF_STOPPED;
+    tp->t_termios.c_lflag &= ~FLUSHO;
+
+#ifdef USING_BSD_SIGINFO
+    if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
+        tty_info(tp);
+#endif /* USING_BSD_SIGINFO */
+
+    if (tp->t_pgrp != NULL)
+    {
+        PGRP_LOCK(tp->t_pgrp);
+        lwp_pgrp_signal_kill(tp->t_pgrp, sig, SI_KERNEL, 0);
+        PGRP_UNLOCK(tp->t_pgrp);
+    }
+}
+
+/* bsd_ttydev_methods.d_ioctl */
+
+rt_inline size_t _copy_to_user(void *to, void *from, size_t n)
+{
+    return lwp_put_to_user(to, from, n) == n ? 0 : -EFAULT;
+}
+
+rt_inline size_t _copy_from_user(void *to, void *from, size_t n)
+{
+    return lwp_get_from_user(to, from, n) == n ? 0 : -EFAULT;
+}
+
+static void termios_to_termio(struct termios *tios, struct termio *tio)
+{
+    memset(tio, 0, sizeof(*tio));
+    tio->c_iflag = tios->c_iflag;
+    tio->c_oflag = tios->c_oflag;
+    tio->c_cflag = tios->c_cflag;
+    tio->c_lflag = tios->c_lflag;
+    tio->c_line = tios->c_line;
+    memcpy(tio->c_cc, tios->c_cc, NCC);
+}
+
+static void termio_to_termios(struct termio *tio, struct termios *tios)
+{
+    int i;
+
+    tios->c_iflag = tio->c_iflag;
+    tios->c_oflag = tio->c_oflag;
+    tios->c_cflag = tio->c_cflag;
+    tios->c_lflag = tio->c_lflag;
+    for (i = NCC; i < NCCS; i++)
+        tios->c_cc[i] = _POSIX_VDISABLE;
+    memcpy(tios->c_cc, tio->c_cc, NCC);
+}
+
+#define IOCTL(cmd, data, fflags, td) \
+    bsd_ttydev_methods.d_ioctl(tp, cmd, data, fflags, td)
+
+int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td)
+{
+    long fflags = FFLAGS(oflags);
+    struct termios tios;
+    struct termio tio;
+    int error;
+
+    LOG_D("%s(cmd=0x%x, args=%p)", __func__, cmd, args);
+    switch (cmd & 0xffff)
+    {
+        case TCGETS:
+            error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
+            if (error)
+                break;
+            error = _copy_to_user(args, &tios, sizeof(tios));
+            break;
+
+        case TCSETS:
+            error = _copy_from_user(&tios, args, sizeof(tios));
+            if (error)
+                break;
+            error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCSETSW:
+            error = _copy_from_user(&tios, args, sizeof(tios));
+            if (error)
+                break;
+            error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCSETSF:
+            error = _copy_from_user(&tios, args, sizeof(tios));
+            if (error)
+                break;
+            error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCGETA:
+            error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
+            if (error)
+                break;
+            termios_to_termio(&tios, &tio);
+            error = _copy_to_user((void *)args, &tio, sizeof(tio));
+            break;
+
+        case TCSETA:
+            error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+            if (error)
+                break;
+            termio_to_termios(&tio, &tios);
+            error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCSETAW:
+            error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+            if (error)
+                break;
+            termio_to_termios(&tio, &tios);
+            error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCSETAF:
+            error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+            if (error)
+                break;
+            termio_to_termios(&tio, &tios);
+            error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
+            break;
+
+        case TCSBRK:
+            if (args != 0)
+            {
+                /**
+                 * Linux manual: SVr4, UnixWare, Solaris, and Linux treat
+                 * tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd).
+                 */
+                error = IOCTL(TIOCDRAIN, (rt_caddr_t)&tios, fflags, td);
+            }
+            else
+            {
+                /**
+                 * Linux manual: If the terminal is using asynchronous serial
+                 * data transmission, and arg is zero, then send a break (a
+                 * stream of zero bits) for between 0.25 and 0.5 seconds.
+                 */
+                LOG_D("%s: ioctl TCSBRK arg 0 not implemented", __func__);
+                error = -ENOSYS;
+            }
+            break;
+
+#ifdef USING_BSD_IOCTL_EXT
+        /* Software flow control */
+        case TCXONC: {
+            switch (args->arg)
+            {
+                case TCOOFF:
+                    args->cmd = TIOCSTOP;
+                    break;
+                case TCOON:
+                    args->cmd = TIOCSTART;
+                    break;
+                case TCIOFF:
+                case TCION: {
+                    int c;
+                    struct write_args wr;
+                    error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags,
+                                     td);
+                    if (error)
+                        break;
+                    fdrop(fp, td);
+                    c = (args->arg == TCIOFF) ? VSTOP : VSTART;
+                    c = tios.c_cc[c];
+                    if (c != _POSIX_VDISABLE)
+                    {
+                        wr.fd = args->fd;
+                        wr.buf = &c;
+                        wr.nbyte = sizeof(c);
+                        return (sys_write(td, &wr));
+                    }
+                    else
+                        return 0;
+                }
+                default:
+                    fdrop(fp, td);
+                    return -EINVAL;
+            }
+            args->arg = 0;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+        }
+#endif /* USING_BSD_IOCTL_EXT */
+        case TCFLSH: {
+            int val;
+            error = 0;
+            switch ((rt_base_t)args)
+            {
+                case TCIFLUSH:
+                    val = FREAD;
+                    break;
+                case TCOFLUSH:
+                    val = FWRITE;
+                    break;
+                case TCIOFLUSH:
+                    val = FREAD | FWRITE;
+                    break;
+                default:
+                    error = -EINVAL;
+                    break;
+            }
+            if (!error)
+                error = (IOCTL(TIOCFLUSH, (rt_caddr_t)&val, fflags, td));
+            break;
+        }
+
+#ifdef USING_BSD_IOCTL_EXT
+        case TIOCEXCL:
+            args->cmd = TIOCEXCL;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCNXCL:
+            args->cmd = TIOCNXCL;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+#endif /* USING_BSD_IOCTL_EXT */
+
+        /* Controlling terminal */
+        case TIOCSCTTY:
+        case TIOCNOTTY:
+
+        /* Process group and session ID */
+        case TIOCGPGRP:
+        case TIOCSPGRP:
+        case TIOCGSID:
+
+            /* TIOCOUTQ */
+            /* TIOCSTI */
+        case TIOCGWINSZ:
+        case TIOCSWINSZ:
+            error = IOCTL(cmd, (rt_caddr_t)args, fflags, td);
+            break;
+#ifdef USING_BSD_IOCTL_EXT
+        case TIOCMGET:
+            args->cmd = TIOCMGET;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCMBIS:
+            args->cmd = TIOCMBIS;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCMBIC:
+            args->cmd = TIOCMBIC;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCMSET:
+            args->cmd = TIOCMSET;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+            /* TIOCGSOFTCAR */
+            /* TIOCSSOFTCAR */
+
+        case FIONREAD: /* TIOCINQ */
+            args->cmd = FIONREAD;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+            /* TIOCLINUX */
+
+        case TIOCCONS:
+            args->cmd = TIOCCONS;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCGSERIAL: {
+            struct linux_serial_struct lss;
+
+            bzero(&lss, sizeof(lss));
+            lss.type = PORT_16550A;
+            lss.flags = 0;
+            lss.close_delay = 0;
+            error = copyout(&lss, (void *)args->arg, sizeof(lss));
+            break;
+        }
+
+        case TIOCSSERIAL: {
+            struct linux_serial_struct lss;
+            error = copyin((void *)args->arg, &lss, sizeof(lss));
+            if (error)
+                break;
+            /* XXX - It really helps to have an implementation that
+             * does nothing. NOT!
+             */
+            error = 0;
+            break;
+        }
+
+        case TIOCPKT:
+            args->cmd = TIOCPKT;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case FIONBIO:
+            args->cmd = FIONBIO;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCSETD: {
+            int line;
+            switch (args->arg)
+            {
+                case N_TTY:
+                    line = TTYDISC;
+                    break;
+                case N_SLIP:
+                    line = SLIPDISC;
+                    break;
+                case N_PPP:
+                    line = PPPDISC;
+                    break;
+                default:
+                    fdrop(fp, td);
+                    return -EINVAL;
+            }
+            error = (ioctl_emit(TIOCSETD, (rt_caddr_t)&line, fflags, td));
+            break;
+        }
+
+        case TIOCGETD: {
+            int linux_line;
+            int bsd_line = TTYDISC;
+            error =
+                ioctl_emit(TIOCGETD, (rt_caddr_t)&bsd_line, fflags, td);
+            if (error)
+                break;
+            switch (bsd_line)
+            {
+                case TTYDISC:
+                    linux_line = N_TTY;
+                    break;
+                case SLIPDISC:
+                    linux_line = N_SLIP;
+                    break;
+                case PPPDISC:
+                    linux_line = N_PPP;
+                    break;
+                default:
+                    fdrop(fp, td);
+                    return -EINVAL;
+            }
+            error = (copyout(&linux_line, (void *)args->arg, sizeof(int)));
+            break;
+        }
+
+            /* TCSBRKP */
+            /* TIOCTTYGSTRUCT */
+
+        case FIONCLEX:
+            args->cmd = FIONCLEX;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case FIOCLEX:
+            args->cmd = FIOCLEX;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case FIOASYNC:
+            args->cmd = FIOASYNC;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+            /* TIOCSERCONFIG */
+            /* TIOCSERGWILD */
+            /* TIOCSERSWILD */
+            /* TIOCGLCKTRMIOS */
+            /* TIOCSLCKTRMIOS */
+
+        case TIOCSBRK:
+            args->cmd = TIOCSBRK;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+
+        case TIOCCBRK:
+            args->cmd = TIOCCBRK;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+        case TIOCGPTN: {
+            int nb;
+
+            error = ioctl_emit(TIOCGPTN, (rt_caddr_t)&nb, fflags, td);
+            if (!error)
+                error = copyout(&nb, (void *)args->arg, sizeof(int));
+            break;
+        }
+        case TIOCGPTPEER:
+            linux_msg(td, "unsupported ioctl TIOCGPTPEER");
+            error = -ENOIOCTL;
+            break;
+        case TIOCSPTLCK:
+            /*
+             * Our unlockpt() does nothing. Check that fd refers
+             * to a pseudo-terminal master device.
+             */
+            args->cmd = TIOCPTMASTER;
+            error = (sys_ioctl(td, (struct ioctl_args *)args));
+            break;
+#endif /* USING_BSD_IOCTL_EXT */
+
+        /**
+         * those are for current implementation of devfs, and we dont want to
+         * log them
+         */
+        case F_DUPFD:
+        case F_DUPFD_CLOEXEC:
+        case F_GETFD:
+        case F_SETFD:
+        case F_GETFL:
+        case F_SETFL:
+            /* fall back to fs */
+            error = -ENOIOCTL;
+            break;
+        default:
+            LOG_I("%s: unhandle commands 0x%x", __func__, cmd);
+            error = -ENOSYS;
+            break;
+    }
+
+    return (error);
+}

+ 507 - 0
components/lwp/terminal/freebsd/tty_inq.c

@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+#include "../bsd_porting.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * TTY input queue buffering.
+ *
+ * Unlike the output queue, the input queue has more features that are
+ * needed to properly implement various features offered by the TTY
+ * interface:
+ *
+ * - Data can be removed from the tail of the queue, which is used to
+ *   implement backspace.
+ * - Once in a while, input has to be `canonicalized'. When ICANON is
+ *   turned on, this will be done after a CR has been inserted.
+ *   Otherwise, it should be done after any character has been inserted.
+ * - The input queue can store one bit per byte, called the quoting bit.
+ *   This bit is used by TTYDISC to make backspace work on quoted
+ *   characters.
+ *
+ * In most cases, there is probably less input than output, so unlike
+ * the outq, we'll stick to 128 byte blocks here.
+ */
+
+static int ttyinq_flush_secure = 1;
+
+#define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE)
+#define BMSIZE           32
+#define GETBIT(tib, boff) \
+    ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE)))
+#define SETBIT(tib, boff) \
+    ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE)))
+#define CLRBIT(tib, boff) \
+    ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE)))
+
+struct ttyinq_block
+{
+    struct ttyinq_block *tib_prev;
+    struct ttyinq_block *tib_next;
+    uint32_t tib_quotes[TTYINQ_QUOTESIZE];
+    char tib_data[TTYINQ_DATASIZE];
+};
+
+static uma_zone_t ttyinq_zone;
+
+#define TTYINQ_INSERT_TAIL(ti, tib)                     \
+    do                                                  \
+    {                                                   \
+        if (ti->ti_end == 0)                            \
+        {                                               \
+            tib->tib_prev = NULL;                       \
+            tib->tib_next = ti->ti_firstblock;          \
+            ti->ti_firstblock = tib;                    \
+        }                                               \
+        else                                            \
+        {                                               \
+            tib->tib_prev = ti->ti_lastblock;           \
+            tib->tib_next = ti->ti_lastblock->tib_next; \
+            ti->ti_lastblock->tib_next = tib;           \
+        }                                               \
+        if (tib->tib_next != NULL)                      \
+            tib->tib_next->tib_prev = tib;              \
+        ti->ti_nblocks++;                               \
+    } while (0)
+
+#define TTYINQ_REMOVE_HEAD(ti)                           \
+    do                                                   \
+    {                                                    \
+        ti->ti_firstblock = ti->ti_firstblock->tib_next; \
+        if (ti->ti_firstblock != NULL)                   \
+            ti->ti_firstblock->tib_prev = NULL;          \
+        ti->ti_nblocks--;                                \
+    } while (0)
+
+#define TTYINQ_RECYCLE(ti, tib)             \
+    do                                      \
+    {                                       \
+        if (ti->ti_quota <= ti->ti_nblocks) \
+            uma_zfree(ttyinq_zone, tib);    \
+        else                                \
+            TTYINQ_INSERT_TAIL(ti, tib);    \
+    } while (0)
+
+int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t size)
+{
+    struct ttyinq_block *tib;
+
+    ti->ti_quota = howmany(size, TTYINQ_DATASIZE);
+
+    while (ti->ti_quota > ti->ti_nblocks)
+    {
+        /*
+         * List is getting bigger.
+         * Add new blocks to the tail of the list.
+         *
+         * We must unlock the TTY temporarily, because we need
+         * to allocate memory. This won't be a problem, because
+         * in the worst case, another thread ends up here, which
+         * may cause us to allocate too many blocks, but this
+         * will be caught by the loop below.
+         */
+        tty_unlock(tp);
+        tib = uma_zalloc(ttyinq_zone, M_WAITOK);
+        tty_lock(tp);
+
+        if (tty_gone(tp))
+        {
+            uma_zfree(ttyinq_zone, tib);
+            return -ENXIO;
+        }
+
+        TTYINQ_INSERT_TAIL(ti, tib);
+    }
+    return 0;
+}
+
+void ttyinq_free(struct ttyinq *ti)
+{
+    struct ttyinq_block *tib;
+
+    ttyinq_flush(ti);
+    ti->ti_quota = 0;
+
+    while ((tib = ti->ti_firstblock) != NULL)
+    {
+        TTYINQ_REMOVE_HEAD(ti);
+        uma_zfree(ttyinq_zone, tib);
+    }
+
+    MPASS(ti->ti_nblocks == 0);
+}
+
+int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
+                    size_t rlen, size_t flen)
+{
+    MPASS(rlen <= uio->uio_resid);
+
+    while (rlen > 0)
+    {
+        int error;
+        struct ttyinq_block *tib;
+        size_t cbegin, cend, clen;
+
+        /* See if there still is data. */
+        if (ti->ti_begin == ti->ti_linestart)
+            return 0;
+        tib = ti->ti_firstblock;
+        if (tib == NULL)
+            return 0;
+
+        /*
+         * The end address should be the lowest of these three:
+         * - The write pointer
+         * - The blocksize - we can't read beyond the block
+         * - The end address if we could perform the full read
+         */
+        cbegin = ti->ti_begin;
+        cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), TTYINQ_DATASIZE);
+        clen = cend - cbegin;
+        MPASS(clen >= flen);
+        rlen -= clen;
+
+        /*
+         * We can prevent buffering in some cases:
+         * - We need to read the block until the end.
+         * - We don't need to read the block until the end, but
+         *   there is no data beyond it, which allows us to move
+         *   the write pointer to a new block.
+         */
+        if (cend == TTYINQ_DATASIZE || cend == ti->ti_end)
+        {
+            /*
+             * Fast path: zero copy. Remove the first block,
+             * so we can unlock the TTY temporarily.
+             */
+            TTYINQ_REMOVE_HEAD(ti);
+            ti->ti_begin = 0;
+
+            /*
+             * Because we remove the first block, we must
+             * fix up the block offsets.
+             */
+#define CORRECT_BLOCK(t)          \
+    do                            \
+    {                             \
+        if (t <= TTYINQ_DATASIZE) \
+            t = 0;                \
+        else                      \
+            t -= TTYINQ_DATASIZE; \
+    } while (0)
+            CORRECT_BLOCK(ti->ti_linestart);
+            CORRECT_BLOCK(ti->ti_reprint);
+            CORRECT_BLOCK(ti->ti_end);
+#undef CORRECT_BLOCK
+
+            /*
+             * Temporary unlock and copy the data to
+             * userspace. We may need to flush trailing
+             * bytes, like EOF characters.
+             */
+            tty_unlock(tp);
+            error = uiomove(tib->tib_data + cbegin, clen - flen, uio);
+            tty_lock(tp);
+
+            /* Block can now be readded to the list. */
+            TTYINQ_RECYCLE(ti, tib);
+        }
+        else
+        {
+            char ob[TTYINQ_DATASIZE - 1];
+
+            /*
+             * Slow path: store data in a temporary buffer.
+             */
+            memcpy(ob, tib->tib_data + cbegin, clen - flen);
+            ti->ti_begin += clen;
+            MPASS(ti->ti_begin < TTYINQ_DATASIZE);
+
+            /* Temporary unlock and copy the data to userspace. */
+            tty_unlock(tp);
+            error = uiomove(ob, clen - flen, uio);
+            tty_lock(tp);
+        }
+
+        if (error != 0)
+            return error;
+        if (tty_gone(tp))
+            return -ENXIO;
+    }
+
+    return 0;
+}
+
+rt_inline void ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset,
+                                 size_t length, int value)
+{
+    if (value)
+    {
+        /* Set the bits. */
+        for (; length > 0; length--, offset++) SETBIT(tib, offset);
+    }
+    else
+    {
+        /* Unset the bits. */
+        for (; length > 0; length--, offset++) CLRBIT(tib, offset);
+    }
+}
+
+size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes,
+                    int quote)
+{
+    const char *cbuf = buf;
+    struct ttyinq_block *tib;
+    unsigned int boff;
+    size_t l;
+
+    while (nbytes > 0)
+    {
+        boff = ti->ti_end % TTYINQ_DATASIZE;
+
+        if (ti->ti_end == 0)
+        {
+            /* First time we're being used or drained. */
+            MPASS(ti->ti_begin == 0);
+            tib = ti->ti_firstblock;
+            if (tib == NULL)
+            {
+                /* Queue has no blocks. */
+                break;
+            }
+            ti->ti_lastblock = tib;
+        }
+        else if (boff == 0)
+        {
+            /* We reached the end of this block on last write. */
+            tib = ti->ti_lastblock->tib_next;
+            if (tib == NULL)
+            {
+                /* We've reached the watermark. */
+                break;
+            }
+            ti->ti_lastblock = tib;
+        }
+        else
+        {
+            tib = ti->ti_lastblock;
+        }
+
+        /* Don't copy more than was requested. */
+        l = MIN(nbytes, TTYINQ_DATASIZE - boff);
+        MPASS(l > 0);
+        memcpy(tib->tib_data + boff, cbuf, l);
+
+        /* Set the quoting bits for the proper region. */
+        ttyinq_set_quotes(tib, boff, l, quote);
+
+        cbuf += l;
+        nbytes -= l;
+        ti->ti_end += l;
+    }
+
+    return (cbuf - (const char *)buf);
+}
+
+int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes,
+                        int quote)
+{
+    size_t ret __unused;
+
+    if (ttyinq_bytesleft(ti) < nbytes)
+        return -1;
+
+    /* We should always be able to write it back. */
+    ret = ttyinq_write(ti, buf, nbytes, quote);
+    MPASS(ret == nbytes);
+
+    return 0;
+}
+
+void ttyinq_canonicalize(struct ttyinq *ti)
+{
+    ti->ti_linestart = ti->ti_reprint = ti->ti_end;
+    ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock;
+}
+
+size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
+                       char *lastc)
+{
+    struct ttyinq_block *tib = ti->ti_firstblock;
+    unsigned int boff = ti->ti_begin;
+    unsigned int bend =
+        MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), ti->ti_begin + maxlen);
+
+    MPASS(maxlen > 0);
+
+    if (tib == NULL)
+        return 0;
+
+    while (boff < bend)
+    {
+        if (strchr(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff))
+        {
+            *lastc = tib->tib_data[boff];
+            return (boff - ti->ti_begin + 1);
+        }
+        boff++;
+    }
+
+    /* Not found - just process the entire block. */
+    return (bend - ti->ti_begin);
+}
+
+void ttyinq_flush(struct ttyinq *ti)
+{
+    struct ttyinq_block *tib;
+
+    ti->ti_begin = 0;
+    ti->ti_linestart = 0;
+    ti->ti_reprint = 0;
+    ti->ti_end = 0;
+
+    /* Zero all data in the input queue to get rid of passwords. */
+    if (ttyinq_flush_secure)
+    {
+        for (tib = ti->ti_firstblock; tib != NULL; tib = tib->tib_next)
+            memset(&tib->tib_data, 0, sizeof tib->tib_data);
+    }
+}
+
+int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote)
+{
+    unsigned int boff;
+    struct ttyinq_block *tib = ti->ti_lastblock;
+
+    if (ti->ti_linestart == ti->ti_end)
+        return -1;
+
+    MPASS(ti->ti_end > 0);
+    boff = (ti->ti_end - 1) % TTYINQ_DATASIZE;
+
+    *c = tib->tib_data[boff];
+    *quote = GETBIT(tib, boff);
+
+    return 0;
+}
+
+void ttyinq_unputchar(struct ttyinq *ti)
+{
+    MPASS(ti->ti_linestart < ti->ti_end);
+
+    if (--ti->ti_end % TTYINQ_DATASIZE == 0)
+    {
+        /* Roll back to the previous block. */
+        ti->ti_lastblock = ti->ti_lastblock->tib_prev;
+        /*
+         * This can only fail if we are unputchar()'ing the
+         * first character in the queue.
+         */
+        MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0));
+    }
+}
+
+void ttyinq_reprintpos_set(struct ttyinq *ti)
+{
+    ti->ti_reprint = ti->ti_end;
+    ti->ti_reprintblock = ti->ti_lastblock;
+}
+
+void ttyinq_reprintpos_reset(struct ttyinq *ti)
+{
+    ti->ti_reprint = ti->ti_linestart;
+    ti->ti_reprintblock = ti->ti_startblock;
+}
+
+static void ttyinq_line_iterate(struct ttyinq *ti,
+                                ttyinq_line_iterator_t *iterator, void *data,
+                                unsigned int offset, struct ttyinq_block *tib)
+{
+    unsigned int boff;
+
+    /* Use the proper block when we're at the queue head. */
+    if (offset == 0)
+        tib = ti->ti_firstblock;
+
+    /* Iterate all characters and call the iterator function. */
+    for (; offset < ti->ti_end; offset++)
+    {
+        boff = offset % TTYINQ_DATASIZE;
+        MPASS(tib != NULL);
+
+        /* Call back the iterator function. */
+        iterator(data, tib->tib_data[boff], GETBIT(tib, boff));
+
+        /* Last byte iterated - go to the next block. */
+        if (boff == TTYINQ_DATASIZE - 1)
+            tib = tib->tib_next;
+    }
+}
+
+void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
+                                        ttyinq_line_iterator_t *iterator,
+                                        void *data)
+{
+    ttyinq_line_iterate(ti, iterator, data, ti->ti_linestart,
+                        ti->ti_startblock);
+}
+
+void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
+                                         ttyinq_line_iterator_t *iterator,
+                                         void *data)
+{
+    ttyinq_line_iterate(ti, iterator, data, ti->ti_reprint,
+                        ti->ti_reprintblock);
+}
+
+static int ttyinq_startup(void)
+{
+    ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), NULL, NULL,
+                              NULL, NULL, UMA_ALIGN_PTR, 0);
+    return 0;
+}
+INIT_PREV_EXPORT(ttyinq_startup);
+
+#if 0
+
+SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL);
+#endif

+ 370 - 0
components/lwp/terminal/freebsd/tty_outq.c

@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-11-13     Shell        init ver.
+ */
+
+#include "../bsd_porting.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * TTY output queue buffering.
+ *
+ * The previous design of the TTY layer offered the so-called clists.
+ * These clists were used for both the input queues and the output
+ * queue. We don't use certain features on the output side, like quoting
+ * bits for parity marking and such. This mechanism is similar to the
+ * old clists, but only contains the features we need to buffer the
+ * output.
+ */
+
+struct ttyoutq_block
+{
+    struct ttyoutq_block *tob_next;
+    char tob_data[TTYOUTQ_DATASIZE];
+};
+
+static uma_zone_t ttyoutq_zone;
+
+#define TTYOUTQ_INSERT_TAIL(to, tob)                    \
+    do                                                  \
+    {                                                   \
+        if (to->to_end == 0)                            \
+        {                                               \
+            tob->tob_next = to->to_firstblock;          \
+            to->to_firstblock = tob;                    \
+        }                                               \
+        else                                            \
+        {                                               \
+            tob->tob_next = to->to_lastblock->tob_next; \
+            to->to_lastblock->tob_next = tob;           \
+        }                                               \
+        to->to_nblocks++;                               \
+    } while (0)
+
+#define TTYOUTQ_REMOVE_HEAD(to)                          \
+    do                                                   \
+    {                                                    \
+        to->to_firstblock = to->to_firstblock->tob_next; \
+        to->to_nblocks--;                                \
+    } while (0)
+
+#define TTYOUTQ_RECYCLE(to, tob)            \
+    do                                      \
+    {                                       \
+        if (to->to_quota <= to->to_nblocks) \
+            uma_zfree(ttyoutq_zone, tob);   \
+        else                                \
+            TTYOUTQ_INSERT_TAIL(to, tob);   \
+    } while (0)
+
+void ttyoutq_flush(struct ttyoutq *to)
+{
+    to->to_begin = 0;
+    to->to_end = 0;
+}
+
+int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t size)
+{
+    struct ttyoutq_block *tob;
+
+    to->to_quota = howmany(size, TTYOUTQ_DATASIZE);
+
+    while (to->to_quota > to->to_nblocks)
+    {
+        /*
+         * List is getting bigger.
+         * Add new blocks to the tail of the list.
+         *
+         * We must unlock the TTY temporarily, because we need
+         * to allocate memory. This won't be a problem, because
+         * in the worst case, another thread ends up here, which
+         * may cause us to allocate too many blocks, but this
+         * will be caught by the loop below.
+         */
+        tty_unlock(tp);
+        tob = uma_zalloc(ttyoutq_zone, M_WAITOK);
+        tty_lock(tp);
+
+        if (tty_gone(tp))
+        {
+            uma_zfree(ttyoutq_zone, tob);
+            return -ENXIO;
+        }
+
+        TTYOUTQ_INSERT_TAIL(to, tob);
+    }
+    return 0;
+}
+
+void ttyoutq_free(struct ttyoutq *to)
+{
+    struct ttyoutq_block *tob;
+
+    ttyoutq_flush(to);
+    to->to_quota = 0;
+
+    while ((tob = to->to_firstblock) != NULL)
+    {
+        TTYOUTQ_REMOVE_HEAD(to);
+        uma_zfree(ttyoutq_zone, tob);
+    }
+
+    MPASS(to->to_nblocks == 0);
+}
+
+size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len)
+{
+    char *cbuf = buf;
+
+    while (len > 0)
+    {
+        struct ttyoutq_block *tob;
+        size_t cbegin, cend, clen;
+
+        /* See if there still is data. */
+        if (to->to_begin == to->to_end)
+            break;
+        tob = to->to_firstblock;
+        if (tob == NULL)
+            break;
+
+        /*
+         * The end address should be the lowest of these three:
+         * - The write pointer
+         * - The blocksize - we can't read beyond the block
+         * - The end address if we could perform the full read
+         */
+        cbegin = to->to_begin;
+        cend = MIN(MIN(to->to_end, to->to_begin + len), TTYOUTQ_DATASIZE);
+        clen = cend - cbegin;
+
+        /* Copy the data out of the buffers. */
+        memcpy(cbuf, tob->tob_data + cbegin, clen);
+        cbuf += clen;
+        len -= clen;
+
+        if (cend == to->to_end)
+        {
+            /* Read the complete queue. */
+            to->to_begin = 0;
+            to->to_end = 0;
+        }
+        else if (cend == TTYOUTQ_DATASIZE)
+        {
+            /* Read the block until the end. */
+            TTYOUTQ_REMOVE_HEAD(to);
+            to->to_begin = 0;
+            to->to_end -= TTYOUTQ_DATASIZE;
+            TTYOUTQ_RECYCLE(to, tob);
+        }
+        else
+        {
+            /* Read the block partially. */
+            to->to_begin += clen;
+        }
+    }
+
+    return cbuf - (char *)buf;
+}
+
+/*
+ * An optimized version of ttyoutq_read() which can be used in pseudo
+ * TTY drivers to directly copy data from the outq to userspace, instead
+ * of buffering it.
+ *
+ * We can only copy data directly if we need to read the entire block
+ * back to the user, because we temporarily remove the block from the
+ * queue. Otherwise we need to copy it to a temporary buffer first, to
+ * make sure data remains in the correct order.
+ */
+int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio)
+{
+    while (uio->uio_resid > 0)
+    {
+        int error;
+        struct ttyoutq_block *tob;
+        size_t cbegin, cend, clen;
+
+        /* See if there still is data. */
+        if (to->to_begin == to->to_end)
+            return 0;
+        tob = to->to_firstblock;
+        if (tob == NULL)
+            return 0;
+
+        /*
+         * The end address should be the lowest of these three:
+         * - The write pointer
+         * - The blocksize - we can't read beyond the block
+         * - The end address if we could perform the full read
+         */
+        cbegin = to->to_begin;
+        cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid),
+                   TTYOUTQ_DATASIZE);
+        clen = cend - cbegin;
+
+        /*
+         * We can prevent buffering in some cases:
+         * - We need to read the block until the end.
+         * - We don't need to read the block until the end, but
+         *   there is no data beyond it, which allows us to move
+         *   the write pointer to a new block.
+         */
+        if (cend == TTYOUTQ_DATASIZE || cend == to->to_end)
+        {
+            /*
+             * Fast path: zero copy. Remove the first block,
+             * so we can unlock the TTY temporarily.
+             */
+            TTYOUTQ_REMOVE_HEAD(to);
+            to->to_begin = 0;
+            if (to->to_end <= TTYOUTQ_DATASIZE)
+                to->to_end = 0;
+            else
+                to->to_end -= TTYOUTQ_DATASIZE;
+
+            /* Temporary unlock and copy the data to userspace. */
+            tty_unlock(tp);
+            error = uiomove(tob->tob_data + cbegin, clen, uio);
+            tty_lock(tp);
+
+            /* Block can now be readded to the list. */
+            TTYOUTQ_RECYCLE(to, tob);
+        }
+        else
+        {
+            char ob[TTYOUTQ_DATASIZE - 1];
+
+            /*
+             * Slow path: store data in a temporary buffer.
+             */
+            memcpy(ob, tob->tob_data + cbegin, clen);
+            to->to_begin += clen;
+            MPASS(to->to_begin < TTYOUTQ_DATASIZE);
+
+            /* Temporary unlock and copy the data to userspace. */
+            tty_unlock(tp);
+            error = uiomove(ob, clen, uio);
+            tty_lock(tp);
+        }
+
+        if (error != 0)
+            return error;
+    }
+
+    return 0;
+}
+
+size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes)
+{
+    const char *cbuf = buf;
+    struct ttyoutq_block *tob;
+    unsigned int boff;
+    size_t l;
+
+    while (nbytes > 0)
+    {
+        boff = to->to_end % TTYOUTQ_DATASIZE;
+
+        if (to->to_end == 0)
+        {
+            /* First time we're being used or drained. */
+            MPASS(to->to_begin == 0);
+            tob = to->to_firstblock;
+            if (tob == NULL)
+            {
+                /* Queue has no blocks. */
+                break;
+            }
+            to->to_lastblock = tob;
+        }
+        else if (boff == 0)
+        {
+            /* We reached the end of this block on last write. */
+            tob = to->to_lastblock->tob_next;
+            if (tob == NULL)
+            {
+                /* We've reached the watermark. */
+                break;
+            }
+            to->to_lastblock = tob;
+        }
+        else
+        {
+            tob = to->to_lastblock;
+        }
+
+        /* Don't copy more than was requested. */
+        l = MIN(nbytes, TTYOUTQ_DATASIZE - boff);
+        MPASS(l > 0);
+        memcpy(tob->tob_data + boff, cbuf, l);
+
+        cbuf += l;
+        nbytes -= l;
+        to->to_end += l;
+    }
+
+    return (cbuf - (const char *)buf);
+}
+
+int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes)
+{
+    size_t ret __unused;
+
+    if (ttyoutq_bytesleft(to) < nbytes)
+        return -1;
+
+    /* We should always be able to write it back. */
+    ret = ttyoutq_write(to, buf, nbytes);
+    MPASS(ret == nbytes);
+
+    return 0;
+}
+
+static int ttyoutq_startup(void)
+{
+    ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), NULL,
+                               NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+    return 0;
+}
+INIT_PREV_EXPORT(ttyoutq_startup);
+
+#if 0
+SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL);
+#endif

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.