summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/dfs/Kconfig8
-rw-r--r--components/dfs/dfs_v2/filesystems/devfs/devfs.c797
-rw-r--r--components/dfs/dfs_v2/filesystems/devfs/devfs.h5
-rw-r--r--components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c678
-rw-r--r--components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c2
-rw-r--r--components/dfs/dfs_v2/filesystems/ptyfs/README.md5
-rw-r--r--components/dfs/dfs_v2/filesystems/ptyfs/SConscript11
-rw-r--r--components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c658
-rw-r--r--components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.h22
-rw-r--r--components/dfs/dfs_v2/filesystems/romfs/romfs.c3
-rw-r--r--components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c38
-rw-r--r--components/dfs/dfs_v2/include/dfs.h12
-rw-r--r--components/dfs/dfs_v2/include/dfs_file.h11
-rw-r--r--components/dfs/dfs_v2/include/dfs_fs.h4
-rw-r--r--components/dfs/dfs_v2/include/dfs_pcache.h5
-rw-r--r--components/dfs/dfs_v2/src/dfs.c111
-rw-r--r--components/dfs/dfs_v2/src/dfs_file.c307
-rw-r--r--components/dfs/dfs_v2/src/dfs_pcache.c50
-rwxr-xr-xcomponents/drivers/Kconfig11
-rw-r--r--components/drivers/core/device.c8
-rwxr-xr-xcomponents/drivers/include/drivers/pic.h2
-rw-r--r--components/drivers/include/drivers/serial.h1
-rw-r--r--components/drivers/include/drivers/serial_v2.h1
-rw-r--r--components/drivers/include/ipc/completion.h1
-rw-r--r--components/drivers/include/ipc/condvar.h38
-rw-r--r--components/drivers/include/ipc/pipe.h2
-rw-r--r--components/drivers/include/ipc/waitqueue.h1
-rw-r--r--components/drivers/include/rtdevice.h1
-rw-r--r--components/drivers/ipc/completion.c32
-rw-r--r--components/drivers/ipc/condvar.c173
-rw-r--r--components/drivers/ipc/pipe.c45
-rw-r--r--components/drivers/ipc/waitqueue.c89
-rw-r--r--components/drivers/ktime/inc/ktime.h1
-rw-r--r--components/drivers/ofw/base.c73
-rw-r--r--components/drivers/serial/SConscript3
-rw-r--r--components/drivers/serial/serial.c36
-rw-r--r--components/drivers/serial/serial_tty.c321
-rw-r--r--components/drivers/tty/SConscript10
-rw-r--r--components/drivers/tty/console.c346
-rw-r--r--components/drivers/tty/include/console.h19
-rw-r--r--components/drivers/tty/include/tty.h304
-rw-r--r--components/drivers/tty/include/tty_ldisc.h56
-rw-r--r--components/drivers/tty/n_tty.c2223
-rw-r--r--components/drivers/tty/pty.c326
-rw-r--r--components/drivers/tty/tty.c504
-rw-r--r--components/drivers/tty/tty_ioctl.c122
-rw-r--r--components/drivers/tty/tty_ldisc.c178
-rw-r--r--components/finsh/msh_file.c8
-rw-r--r--components/libc/compilers/common/ctime.c21
-rw-r--r--components/libc/compilers/common/include/dirent.h6
-rw-r--r--components/libc/compilers/common/include/sys/ioctl.h7
-rw-r--r--components/libc/compilers/common/include/sys/unistd.h2
-rw-r--r--components/libc/compilers/musl/fcntl.h33
-rw-r--r--components/libc/posix/Kconfig2
-rw-r--r--components/libc/posix/io/epoll/epoll.c68
-rw-r--r--components/libc/posix/io/poll/poll.c58
-rw-r--r--components/libc/posix/io/termios/termios.h14
-rw-r--r--components/lwp/Kconfig21
-rw-r--r--components/lwp/SConscript11
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_arch.c60
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_arch.h19
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_gcc.S70
-rw-r--r--components/lwp/arch/arm/cortex-a/lwp_arch.c6
-rw-r--r--components/lwp/arch/arm/cortex-a/lwp_arch.h2
-rw-r--r--components/lwp/arch/risc-v/rv64/lwp_arch.c7
-rw-r--r--components/lwp/libc_musl.h6
-rw-r--r--components/lwp/lwp.c268
-rw-r--r--components/lwp/lwp.h159
-rw-r--r--components/lwp/lwp_arch_comm.h4
-rw-r--r--components/lwp/lwp_elf.c1
-rw-r--r--components/lwp/lwp_futex.c29
-rw-r--r--components/lwp/lwp_internal.c69
-rw-r--r--components/lwp/lwp_internal.h111
-rw-r--r--components/lwp/lwp_jobctrl.c86
-rw-r--r--components/lwp/lwp_pgrp.c542
-rw-r--r--components/lwp/lwp_pid.c662
-rw-r--r--components/lwp/lwp_pid.h38
-rw-r--r--components/lwp/lwp_pmutex.c459
-rw-r--r--components/lwp/lwp_session.c432
-rw-r--r--components/lwp/lwp_setsid.c27
-rw-r--r--components/lwp/lwp_signal.c542
-rw-r--r--components/lwp/lwp_signal.h83
-rw-r--r--components/lwp/lwp_sys_socket.h21
-rw-r--r--components/lwp/lwp_syscall.c751
-rw-r--r--components/lwp/lwp_syscall.h7
-rw-r--r--components/lwp/lwp_tid.c21
-rw-r--r--components/lwp/lwp_user_mm.c35
-rw-r--r--components/lwp/lwp_user_mm.h4
-rw-r--r--components/lwp/terminal/Kconfig13
-rw-r--r--components/lwp/terminal/bsd_porting.h741
-rw-r--r--components/lwp/terminal/bsd_ttydisc.h81
-rw-r--r--components/lwp/terminal/bsd_ttyqueue.h180
-rw-r--r--components/lwp/terminal/freebsd/tty.c1447
-rw-r--r--components/lwp/terminal/freebsd/tty_compat.c711
-rw-r--r--components/lwp/terminal/freebsd/tty_inq.c507
-rw-r--r--components/lwp/terminal/freebsd/tty_outq.c370
-rw-r--r--components/lwp/terminal/freebsd/tty_pts.c837
-rw-r--r--components/lwp/terminal/freebsd/tty_ttydisc.c1479
-rw-r--r--components/lwp/terminal/terminal.h415
-rw-r--r--components/lwp/terminal/tty_config.h136
-rw-r--r--components/lwp/terminal/tty_cons.c128
-rw-r--r--components/lwp/terminal/tty_ctty.c106
-rw-r--r--components/lwp/terminal/tty_device.c456
-rw-r--r--components/lwp/terminal/tty_internal.h67
-rw-r--r--components/lwp/terminal/tty_ptmx.c350
-rw-r--r--components/mm/mm_page.c12
-rwxr-xr-xcomponents/utilities/libadt/bitmap/bitmap.h2
-rw-r--r--components/utilities/resource/SConscript4
-rw-r--r--components/utilities/resource/rid_bitmap.c116
-rw-r--r--components/utilities/resource/rid_bitmap.h34
-rw-r--r--components/utilities/ulog/ulog.c2
111 files changed, 14362 insertions, 6258 deletions
diff --git a/components/dfs/Kconfig b/components/dfs/Kconfig
index 6af2555a14..2c822cf779 100644
--- a/components/dfs/Kconfig
+++ b/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
diff --git a/components/dfs/dfs_v2/filesystems/devfs/devfs.c b/components/dfs/dfs_v2/filesystems/devfs/devfs.c
index 7ebac59868..7818ae19be 100644
--- a/components/dfs/dfs_v2/filesystems/devfs/devfs.c
+++ b/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')
- {
- int count = _devfs_root_dirent_update(vnode);
+ /* get device handler */
+ device = (rt_device_t)file->vnode->data;
- vnode->mode = S_IFDIR | 0644;
- vnode->size = count;
- vnode->nlink = 1;
- vnode->fops = &_dev_fops;
- vnode->mnt = dentry->mnt;
- vnode->type = FT_DIRECTORY;
+#ifdef RT_USING_POSIX_DEVIO
+ if (device->fops && device->fops->close)
+ {
+ 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)
+ ssize_t ret = -RT_EIO;
+ rt_device_t device;
+
+ RT_ASSERT(file != RT_NULL);
+
+ if (file->vnode && file->vnode->data)
{
- /* regester bus device */
- if (rt_device_bus_create(&dentry->pathname[1], 0))
+ /* get device handler */
+ device = (rt_device_t)file->vnode->data;
+
+#ifdef RT_USING_POSIX_DEVIO
+ if (device->fops && device->fops->read)
{
- return dfs_devfs_lookup(dentry);
+ 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;
}
}
-#endif
- return RT_NULL;
+ return ret;
}
-int dfs_devfs_free_vnode(struct dfs_vnode *vnode)
+static ssize_t dfs_devfs_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
{
- if (S_ISDIR(vnode->mode))
+ ssize_t ret = -RT_EIO;
+ rt_device_t device;
+
+ RT_ASSERT(file != RT_NULL);
+
+ if(file->vnode->data)
{
- struct device_dirent *root_dirent;
+ /* get device handler */
+ device = (rt_device_t)file->vnode->data;
- root_dirent = (struct device_dirent *)vnode->data;
- RT_ASSERT(root_dirent != RT_NULL);
+ if ((file->dentry->pathname[0] == '/') && (file->dentry->pathname[1] == '\0'))
+ return -RT_ENOSYS;
- /* 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->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;
+ }
}
- /* 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 int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args)
{
- RT_ASSERT(mnt != RT_NULL);
+ int ret = RT_EOK;
+ 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 && 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;
+
+#ifdef RT_USING_POSIX_DEVIO
+ if (device->fops && device->fops->ioctl)
+ {
+ ret = device->fops->ioctl(file, cmd, args);
+ }
+ else
+#endif /* RT_USING_POSIX_DEVIO */
+ {
+ ret = rt_device_control(device, cmd, args);
+ }
+ }
+
+ return ret;
}
-int dfs_devfs_statfs(struct dfs_mnt *mnt, struct statfs *buf)
+static int dfs_devfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
{
- 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;
- }
+ int ret = -RT_ENOSYS;
- return RT_EOK;
+ RT_ASSERT(file != RT_NULL);
+
+ return ret;
}
-int dfs_devfs_ioctl(struct dfs_file *file, int cmd, void *args)
+static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req)
{
- rt_err_t result = RT_EOK;
+ 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->ioctl)
- {
- result = device->fops->ioctl(file, cmd, args);
- }
- else
+ if (device->fops && device->fops->poll)
+ {
+ mask = device->fops->poll(file, req);
+ }
#endif /* RT_USING_POSIX_DEVIO */
- {
- result = rt_device_control(device, cmd, args);
}
- return result;
+ return mask;
}
-ssize_t dfs_devfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
+static int dfs_devfs_flush(struct dfs_file *file)
{
- int result = -RT_EIO;
+ 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->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->read)
- {
- result = device->fops->read(file, buf, count, pos);
- }
- else
+ if (device->fops && device->fops->flush)
+ {
+ ret = device->fops->flush(file);
+ }
#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 off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
{
- int result = -RT_EIO;
+ off_t ret = 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->lseek)
+ {
+ ret = device->fops->lseek(file, offset, wherece);
+ }
#endif /* RT_USING_POSIX_DEVIO */
- {
- /* read device data */
- result = rt_device_write(device, *pos, buf, count);
- *pos += result;
}
- return result;
+ return ret;
}
-int dfs_devfs_close(struct dfs_file *file)
+static int dfs_devfs_truncate(struct dfs_file *file, off_t offset)
{
- 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->truncate)
{
- result = device->fops->close(file);
+ ret = device->fops->truncate(file, offset);
}
- 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 int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
{
- rt_err_t result = RT_EOK;
+ int ret = RT_EOK;
+ 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->mmap)
+ {
+ ret = device->fops->mmap(file, mmap);
}
+#endif /* RT_USING_POSIX_DEVIO */
}
- /* open device failed. */
- return -EIO;
+ return ret;
}
-int dfs_devfs_unlink(struct dfs_dentry *dentry)
+static int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock)
{
-#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->lock)
{
- 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->lock(file, flock);
}
+#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_flock(struct dfs_file *file, int operation, struct file_lock *flock)
{
- 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];
+ /* get device handler */
+ device = (rt_device_t)file->vnode->data;
- 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';
+#ifdef RT_USING_POSIX_DEVIO
+ if (device->fops && device->fops->flock)
+ {
+ ret = device->fops->flock(file, operation, flock);
+ }
+#endif /* RT_USING_POSIX_DEVIO */
}
- file->fpos += index;
-
- return index * sizeof(struct dirent);
+ return ret;
}
-static int dfs_devfs_poll(struct dfs_file *file, struct rt_pollreq *req)
+static const struct dfs_file_ops _dev_fops =
{
- int mask = 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,
- /* 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->poll)
- {
- mask = device->fops->poll(file, req);
- }
-#endif /* RT_USING_POSIX_DEVIO */
+ .flush = dfs_devfs_flush,
+ .lseek = dfs_devfs_lseek,
+ .truncate = dfs_devfs_truncate,
+ .mmap = dfs_devfs_mmap,
+ .lock = dfs_devfs_lock,
+ .flock = dfs_devfs_flock,
+};
- return mask;
+const struct dfs_file_ops *dfs_devfs_fops(void)
+{
+ return &_dev_fops;
}
-int dfs_devfs_flush(struct dfs_file *file)
+mode_t dfs_devfs_device_to_mode(struct rt_device *device)
{
- int ret = 0;
- rt_device_t device;
+ mode_t mode = 0;
- /* 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->flush)
+ switch (device->type)
{
- ret = device->fops->flush(file);
+ 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;
}
-off_t dfs_devfs_lseek(struct dfs_file *file, off_t offset, int wherece)
+static void dfs_devfs_mkdir(const char *fullpath, mode_t mode)
{
- off_t ret = 0;
- rt_device_t device;
+ int len = rt_strlen(fullpath);
+ char *path = (char *)rt_malloc(len);
- /* 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->lseek)
+ if (path)
{
- ret = device->fops->lseek(file, offset, wherece);
- }
-#endif /* RT_USING_POSIX_DEVIO */
+ int index = len - 1;
- return ret;
-}
+ rt_strcpy(path, fullpath);
-int dfs_devfs_truncate(struct dfs_file *file, off_t offset)
-{
- 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->truncate)
- {
- ret = device->fops->truncate(file, offset);
- }
-#endif /* RT_USING_POSIX_DEVIO */
+ path[index] = '\0';
- return ret;
-}
+ if (index > 0 && access(path, 0) != 0)
+ {
+ int i = 0;
-int dfs_devfs_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
-{
- int ret = 0;
- rt_device_t device;
+ if (path[i] == '/')
+ {
+ i ++;
+ }
- /* get device handler */
- device = (rt_device_t)file->vnode->data;
- RT_ASSERT(device != RT_NULL);
+ while (index > i)
+ {
+ if (path[i] == '/')
+ {
+ path[i] = '\0';
+ mkdir(path, mode);
+ path[i] = '/';
+ }
-#ifdef RT_USING_POSIX_DEVIO
- if (device->fops && device->fops->mmap)
- {
- ret = device->fops->mmap(file, mmap);
- }
-#endif /* RT_USING_POSIX_DEVIO */
+ i ++;
+ }
- return ret;
+ mkdir(path, mode);
+ }
+ }
}
-int dfs_devfs_lock(struct dfs_file *file, struct file_lock *flock)
+void dfs_devfs_device_add(rt_device_t device)
{
- int ret = 0;
- rt_device_t device;
-
- /* get device handler */
- device = (rt_device_t)file->vnode->data;
- RT_ASSERT(device != RT_NULL);
+ int fd;
+ char path[512];
-#ifdef RT_USING_POSIX_DEVIO
- if (device->fops && device->fops->lock)
+ if (device)
{
- ret = device->fops->lock(file, flock);
- }
-#endif /* RT_USING_POSIX_DEVIO */
+ rt_snprintf(path, 512, "/dev/%s", device->parent.name);
- return ret;
-}
-
-int dfs_devfs_flock(struct dfs_file *file, int operation, struct file_lock *flock)
-{
- int ret = 0;
- rt_device_t device;
+ if (access(path, 0) != 0)
+ {
+ mode_t mode = dfs_devfs_device_to_mode(device);
- /* get device handler */
- device = (rt_device_t)file->vnode->data;
- RT_ASSERT(device != RT_NULL);
+ dfs_devfs_mkdir(path, mode);
-#ifdef RT_USING_POSIX_DEVIO
- if (device->fops && device->fops->flock)
- {
- ret = device->fops->flock(file, operation, flock);
+ fd = open(path, O_RDWR | O_CREAT, mode);
+ if (fd >= 0)
+ {
+ close(fd);
+ }
+ }
}
-#endif /* RT_USING_POSIX_DEVIO */
-
- return ret;
}
-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);
-
diff --git a/components/dfs/dfs_v2/filesystems/devfs/devfs.h b/components/dfs/dfs_v2/filesystems/devfs/devfs.h
index acaae11f2f..e80b44ee09 100644
--- a/components/dfs/dfs_v2/filesystems/devfs/devfs.h
+++ b/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
diff --git a/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c b/components/dfs/dfs_v2/filesystems/devfs/devtmpfs.c
new file mode 100644
index 0000000000..3bea0c16df
--- /dev/null
+++ b/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);
diff --git a/components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c b/components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c
index 70b34ef3cb..4546455a31 100644
--- a/components/dfs/dfs_v2/filesystems/mqueue/dfs_mqueue.c
+++ b/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));
}
diff --git a/components/dfs/dfs_v2/filesystems/ptyfs/README.md b/components/dfs/dfs_v2/filesystems/ptyfs/README.md
new file mode 100644
index 0000000000..1f805df80c
--- /dev/null
+++ b/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.
diff --git a/components/dfs/dfs_v2/filesystems/ptyfs/SConscript b/components/dfs/dfs_v2/filesystems/ptyfs/SConscript
new file mode 100644
index 0000000000..c0f8f39587
--- /dev/null
+++ b/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')
diff --git a/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c b/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.c
new file mode 100644
index 0000000000..c6f4f3d52d
--- /dev/null
+++ b/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);
diff --git a/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.h b/components/dfs/dfs_v2/filesystems/ptyfs/ptyfs.h
new file mode 100644
index 0000000000..69b2fb66fd
--- /dev/null
+++ b/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__ */
diff --git a/components/dfs/dfs_v2/filesystems/romfs/romfs.c b/components/dfs/dfs_v2/filesystems/romfs/romfs.c
index 91465116b3..45279bcb83 100644
--- a/components/dfs/dfs_v2/filesystems/romfs/romfs.c
+++ b/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)},
};
diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
index f289fc4419..8644f51c26 100644
--- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
+++ b/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
diff --git a/components/dfs/dfs_v2/include/dfs.h b/components/dfs/dfs_v2/include/dfs.h
index 6815f5e0ab..8b8c80188f 100644
--- a/components/dfs/dfs_v2/include/dfs.h
+++ b/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
diff --git a/components/dfs/dfs_v2/include/dfs_file.h b/components/dfs/dfs_v2/include/dfs_file.h
index d62aec1e89..72539f3bbe 100644
--- a/components/dfs/dfs_v2/include/dfs_file.h
+++ b/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
diff --git a/components/dfs/dfs_v2/include/dfs_fs.h b/components/dfs/dfs_v2/include/dfs_fs.h
index 9dc99ee367..d30967aac5 100644
--- a/components/dfs/dfs_v2/include/dfs_fs.h
+++ b/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;
};
diff --git a/components/dfs/dfs_v2/include/dfs_pcache.h b/components/dfs/dfs_v2/include/dfs_pcache.h
index 356df6c4f9..01abfe3150 100644
--- a/components/dfs/dfs_v2/include/dfs_pcache.h
+++ b/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
diff --git a/components/dfs/dfs_v2/src/dfs.c b/components/dfs/dfs_v2/src/dfs.c
index ad90822b93..939e4dd66e 100644
--- a/components/dfs/dfs_v2/src/dfs.c
+++ b/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];
diff --git a/components/dfs/dfs_v2/src/dfs_file.c b/components/dfs/dfs_v2/src/dfs_file.c
index 5f55eaaba1..c51208a5f2 100644
--- a/components/dfs/dfs_v2/src/dfs_file.c
+++ b/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;
diff --git a/components/dfs/dfs_v2/src/dfs_pcache.c b/components/dfs/dfs_v2/src/dfs_pcache.c
index 11f6172d08..57f4510e93 100644
--- a/components/dfs/dfs_v2/src/dfs_pcache.c
+++ b/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)
{
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index df4ba06a91..5763ab8015 100755
--- a/components/drivers/Kconfig
+++ b/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
diff --git a/components/drivers/core/device.c b/components/drivers/core/device.c
index 30c8adfd60..c99cddf62b 100644
--- a/components/drivers/core/device.c
+++ b/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);
diff --git a/components/drivers/include/drivers/pic.h b/components/drivers/include/drivers/pic.h
index 33c8c390bf..8429007d16 100755
--- a/components/drivers/include/drivers/pic.h
+++ b/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;
diff --git a/components/drivers/include/drivers/serial.h b/components/drivers/include/drivers/serial.h
index 9435249cd8..b6c3a74e73 100644
--- a/components/drivers/include/drivers/serial.h
+++ b/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
diff --git a/components/drivers/include/drivers/serial_v2.h b/components/drivers/include/drivers/serial_v2.h
index 76c44333bd..399b3d0a59 100644
--- a/components/drivers/include/drivers/serial_v2.h
+++ b/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
diff --git a/components/drivers/include/ipc/completion.h b/components/drivers/include/ipc/completion.h
index efa2b7840d..266ddd281e 100644
--- a/components/drivers/include/ipc/completion.h
+++ b/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
diff --git a/components/drivers/include/ipc/condvar.h b/components/drivers/include/ipc/condvar.h
new file mode 100644
index 0000000000..1211df420d
--- /dev/null
+++ b/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__ */
diff --git a/components/drivers/include/ipc/pipe.h b/components/drivers/include/ipc/pipe.h
index 94ac10f635..fdba84a57e 100644
--- a/components/drivers/include/ipc/pipe.h
+++ b/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;
diff --git a/components/drivers/include/ipc/waitqueue.h b/components/drivers/include/ipc/waitqueue.h
index 073300a72e..dcc01ceff9 100644
--- a/components/drivers/include/ipc/waitqueue.h
+++ b/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 = { \
diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h
index 5da3c955d6..2445a65823 100644
--- a/components/drivers/include/rtdevice.h
+++ b/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"
diff --git a/components/drivers/ipc/completion.c b/components/drivers/ipc/completion.c
index d74fcdc8d1..824f538129 100644
--- a/components/drivers/ipc/completion.c
+++ b/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);
+
diff --git a/components/drivers/ipc/condvar.c b/components/drivers/ipc/condvar.c
new file mode 100644
index 0000000000..1eb42dc9ad
--- /dev/null
+++ b/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;
+}
diff --git a/components/drivers/ipc/pipe.c b/components/drivers/ipc/pipe.c
index acd0c2def6..c39cd6bdc6 100644
--- a/components/drivers/ipc/pipe.c
+++ b/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;
}
diff --git a/components/drivers/ipc/waitqueue.c b/components/drivers/ipc/waitqueue.c
index 03901a9ba3..75e14e5844 100644
--- a/components/drivers/ipc/waitqueue.c
+++ b/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_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_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)
diff --git a/components/drivers/ktime/inc/ktime.h b/components/drivers/ktime/inc/ktime.h
index 5fd5ffb763..44e2578bcf 100644
--- a/components/drivers/ktime/inc/ktime.h
+++ b/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);
}
/**
diff --git a/components/drivers/ofw/base.c b/components/drivers/ofw/base.c
index 8b081bc9d4..4578758a23 100644
--- a/components/drivers/ofw/base.c
+++ b/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;
/*
diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript
index 2211134ef3..7ee41a4331 100644
--- a/components/drivers/serial/SConscript
+++ b/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:
diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c
index 0382f3c856..8604c78db7 100644
--- a/components/drivers/serial/serial.c
+++ b/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:
diff --git a/components/drivers/serial/serial_tty.c b/components/drivers/serial/serial_tty.c
new file mode 100644
index 0000000000..311a422dce
--- /dev/null
+++ b/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);
diff --git a/components/drivers/tty/SConscript b/components/drivers/tty/SConscript
deleted file mode 100644
index d972879b70..0000000000
--- a/components/drivers/tty/SConscript
+++ /dev/null
@@ -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')
diff --git a/components/drivers/tty/console.c b/components/drivers/tty/console.c
deleted file mode 100644
index a9c9b88731..0000000000
--- a/components/drivers/tty/console.c
+++ /dev/null
@@ -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;
-}
diff --git a/components/drivers/tty/include/console.h b/components/drivers/tty/include/console.h
deleted file mode 100644
index 653b31fade..0000000000
--- a/components/drivers/tty/include/console.h
+++ /dev/null
@@ -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
diff --git a/components/drivers/tty/include/tty.h b/components/drivers/tty/include/tty.h
deleted file mode 100644
index 79e30b6f51..0000000000
--- a/components/drivers/tty/include/tty.h
+++ /dev/null
@@ -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 <[email protected]> */
-#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__*/
diff --git a/components/drivers/tty/include/tty_ldisc.h b/components/drivers/tty/include/tty_ldisc.h
deleted file mode 100644
index 9b95fd3591..0000000000
--- a/components/drivers/tty/include/tty_ldisc.h
+++ /dev/null
@@ -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_
diff --git a/components/drivers/tty/n_tty.c b/components/drivers/tty/n_tty.c
deleted file mode 100644
index bf1156ba38..0000000000
--- a/components/drivers/tty/n_tty.c
+++ /dev/null
@@ -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,
-};
diff --git a/components/drivers/tty/pty.c b/components/drivers/tty/pty.c
deleted file mode 100644
index b6cbda5574..0000000000
--- a/components/drivers/tty/pty.c
+++ /dev/null
@@ -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);
diff --git a/components/drivers/tty/tty.c b/components/drivers/tty/tty.c
deleted file mode 100644
index 075b177d5c..0000000000
--- a/components/drivers/tty/tty.c
+++ /dev/null
@@ -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;
-}
diff --git a/components/drivers/tty/tty_ioctl.c b/components/drivers/tty/tty_ioctl.c
deleted file mode 100644
index 92206266c3..0000000000
--- a/components/drivers/tty/tty_ioctl.c
+++ /dev/null
@@ -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;
-}
diff --git a/components/drivers/tty/tty_ldisc.c b/components/drivers/tty/tty_ldisc.c
deleted file mode 100644
index f2aef64bcc..0000000000
--- a/components/drivers/tty/tty_ldisc.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/components/finsh/msh_file.c b/components/finsh/msh_file.c
index 105dad3570..07e22a8206 100644
--- a/components/finsh/msh_file.c
+++ b/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
diff --git a/components/libc/compilers/common/ctime.c b/components/libc/compilers/common/ctime.c
index 44822ec379..a1bb3c439d 100644
--- a/components/libc/compilers/common/ctime.c
+++ b/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);
diff --git a/components/libc/compilers/common/include/dirent.h b/components/libc/compilers/common/include/dirent.h
index f1a45d2986..51451e2d59 100644
--- a/components/libc/compilers/common/include/dirent.h
+++ b/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
diff --git a/components/libc/compilers/common/include/sys/ioctl.h b/components/libc/compilers/common/include/sys/ioctl.h
index ab0c069550..908074863a 100644
--- a/components/libc/compilers/common/include/sys/ioctl.h
+++ b/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.
diff --git a/components/libc/compilers/common/include/sys/unistd.h b/components/libc/compilers/common/include/sys/unistd.h
index b9c64f3bb6..824891d57e 100644
--- a/components/libc/compilers/common/include/sys/unistd.h
+++ b/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 */
diff --git a/components/libc/compilers/musl/fcntl.h b/components/libc/compilers/musl/fcntl.h
index 05d466f848..9388f10de1 100644
--- a/components/libc/compilers/musl/fcntl.h
+++ b/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
diff --git a/components/libc/posix/Kconfig b/components/libc/posix/Kconfig
index d8627c242a..7d6539234f 100644
--- a/components/libc/posix/Kconfig
+++ b/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
diff --git a/components/libc/posix/io/epoll/epoll.c b/components/libc/posix/io/epoll/epoll.c
index cd313c9247..26527fb611 100644
--- a/components/libc/posix/io/epoll/epoll.c
+++ b/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;
diff --git a/components/libc/posix/io/poll/poll.c b/components/libc/posix/io/poll/poll.c
index 105727e4b3..2abc1cde6b 100644
--- a/components/libc/posix/io/poll/poll.c
+++ b/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;
diff --git a/components/libc/posix/io/termios/termios.h b/components/libc/posix/io/termios/termios.h
index 4a504daa7d..884159ae2e 100644
--- a/components/libc/posix/io/termios/termios.h
+++ b/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
diff --git a/components/lwp/Kconfig b/components/lwp/Kconfig
index 719e8ffca4..9b79e51207 100644
--- a/components/lwp/Kconfig
+++ b/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
diff --git a/components/lwp/SConscript b/components/lwp/SConscript
index 3c43dcd86e..5f4a618d48 100644
--- a/components/lwp/SConscript
+++ b/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')
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.c b/components/lwp/arch/aarch64/cortex-a/lwp_arch.c
index b9f9ec58dd..3df6b2f4f7 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_arch.c
+++ b/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)))
{
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.h b/components/lwp/arch/aarch64/cortex-a/lwp_arch.h
index c7d9c8d74e..191f8e1bdc 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_arch.h
+++ b/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__*/
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S b/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
index e33127b62b..f5a93698d9 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
+++ b/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
/**
diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.c b/components/lwp/arch/arm/cortex-a/lwp_arch.c
index 0d8f965429..0582ea70b7 100644
--- a/components/lwp/arch/arm/cortex-a/lwp_arch.c
+++ b/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)
diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.h b/components/lwp/arch/arm/cortex-a/lwp_arch.h
index 7262f446c8..6f5518e4c4 100644
--- a/components/lwp/arch/arm/cortex-a/lwp_arch.h
+++ b/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);
diff --git a/components/lwp/arch/risc-v/rv64/lwp_arch.c b/components/lwp/arch/risc-v/rv64/lwp_arch.c
index 4b3dfc2e19..62383f91fa 100644
--- a/components/lwp/arch/risc-v/rv64/lwp_arch.c
+++ b/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)
*/
diff --git a/components/lwp/libc_musl.h b/components/lwp/libc_musl.h
index 3129a0a6b7..fbc2ad5cb4 100644
--- a/components/lwp/libc_musl.h
+++ b/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
{
diff --git a/components/lwp/lwp.c b/components/lwp/lwp.c
index 7e85aaff9e..27d9f2f234 100644
--- a/components/lwp/lwp.c
+++ b/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)
- {
- if (lwp->session == -1)
- {
- 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;
- }
- }
+ session = RT_NULL;
+ group = RT_NULL;
- 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);
+ group = lwp_pgrp_create(lwp);
+ if (group)
+ {
+ lwp_pgrp_insert(group, lwp);
+ if (self_lwp == RT_NULL)
+ {
+ 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;
diff --git a/components/lwp/lwp.h b/components/lwp/lwp.h
index 1ac98493a1..c504ef60ad 100644
--- a/components/lwp/lwp.h
+++ b/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 */
- rt_list_t wait_list;
- rt_bool_t terminated;
- rt_bool_t background;
- int lwp_ret;
+ struct rt_lwp *parent; /* parent process */
+ struct rt_lwp *first_child; /* first child process */
+ struct rt_lwp *sibling; /* sibling(child) process */
+
+ 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);
diff --git a/components/lwp/lwp_arch_comm.h b/components/lwp/lwp_arch_comm.h
index 885cd63ca9..3eec407862 100644
--- a/components/lwp/lwp_arch_comm.h
+++ b/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__ */
diff --git a/components/lwp/lwp_elf.c b/components/lwp/lwp_elf.c
index 0610769b26..f654adc5b7 100644
--- a/components/lwp/lwp_elf.c
+++ b/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)
diff --git a/components/lwp/lwp_futex.c b/components/lwp/lwp_futex.c
index f32b4a18f4..a49a86680a 100644
--- a/components/lwp/lwp_futex.c
+++ b/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;
}
diff --git a/components/lwp/lwp_internal.c b/components/lwp/lwp_internal.c
index bb31650790..e460a1ac3f 100644
--- a/components/lwp/lwp_internal.c
+++ b/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);
+}
diff --git a/components/lwp/lwp_internal.h b/components/lwp/lwp_internal.h
index 1a605b007a..c942be0136 100644
--- a/components/lwp/lwp_internal.h
+++ b/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__ */
diff --git a/components/lwp/lwp_jobctrl.c b/components/lwp/lwp_jobctrl.c
new file mode 100644
index 0000000000..5f4848f500
--- /dev/null
+++ b/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? */
+}
diff --git a/components/lwp/lwp_pgrp.c b/components/lwp/lwp_pgrp.c
new file mode 100644
index 0000000000..700565b165
--- /dev/null
+++ b/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
diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c
index 7479df0357..5b9f1f4098 100644
--- a/components/lwp/lwp_pid.c
+++ b/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
diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h
index c9e8b945b7..5e170f652d 100644
--- a/components/lwp/lwp_pid.h
+++ b/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
}
diff --git a/components/lwp/lwp_pmutex.c b/components/lwp/lwp_pmutex.c
deleted file mode 100644
index 08c8b3b082..0000000000
--- a/components/lwp/lwp_pmutex.c
+++ /dev/null
@@ -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;
-}
diff --git a/components/lwp/lwp_session.c b/components/lwp/lwp_session.c
new file mode 100644
index 0000000000..6d51948267
--- /dev/null
+++ b/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
diff --git a/components/lwp/lwp_setsid.c b/components/lwp/lwp_setsid.c
deleted file mode 100644
index d72363bde5..0000000000
--- a/components/lwp/lwp_setsid.c
+++ /dev/null
@@ -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;
-}
diff --git a/components/lwp/lwp_signal.c b/components/lwp/lwp_signal.c
index 9e947ce7de..1f20ad5096 100644
--- a/components/lwp/lwp_signal.c
+++ b/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;
+}
diff --git a/components/lwp/lwp_signal.h b/components/lwp/lwp_signal.h
index 3b5fd91321..f98b43dd3f 100644
--- a/components/lwp/lwp_signal.h
+++ b/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
diff --git a/components/lwp/lwp_sys_socket.h b/components/lwp/lwp_sys_socket.h
index 527d5ebbe8..28a0887203 100644
--- a/components/lwp/lwp_sys_socket.h
+++ b/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__ */
diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c
index 718636e102..5d9cf05b63 100644
--- a/components/lwp/lwp_syscall.c
+++ b/components/lwp/lwp_syscall.c
@@ -13,6 +13,9 @@
* 2023-03-13 WangXiaoyao Format & fix syscall return value
* 2023-07-06 Shell adapt the signal API, and clone, fork to new implementation of lwp signal
* 2023-07-27 Shell Move tid_put() from lwp_free() to sys_exit()
+ * 2023-11-16 xqyjlj fix some syscalls (about sched_*, get/setpriority)
+ * 2023-11-17 xqyjlj add process group and session support
+ * 2023-11-30 Shell Fix sys_setitimer() and exit(status)
*/
#define __RT_IPC_SOURCE__
#define _GNU_SOURCE
@@ -22,7 +25,6 @@
#include <rthw.h>
#include <board.h>
-#include <console.h>
#include <string.h>
#include <stdint.h>
@@ -57,6 +59,7 @@
#include <sys/stat.h>
#include <sys/statfs.h> /* statfs() */
#include <sys/timerfd.h>
+#include <sys/ioctl.h>
#ifdef RT_USING_MUSLLIBC
#include <sys/signalfd.h>
#endif
@@ -88,7 +91,6 @@
#define SYSCALL_USPACE(f) SYSCALL_SIGN(sys_notimpl)
#endif /* defined(RT_USING_DFS) && defined(ARCH_MM_MMU) */
-#include <tty.h>
#include "lwp_ipc_internal.h"
#include <sched.h>
@@ -329,10 +331,14 @@ static void _crt_thread_entry(void *parameter)
sysret_t sys_exit_group(int value)
{
sysret_t rc = 0;
+ lwp_status_t lwp_status;
struct rt_lwp *lwp = lwp_self();
if (lwp)
- lwp_exit(lwp, value);
+ {
+ lwp_status = LWP_CREATE_STAT_EXIT(value);
+ lwp_exit(lwp, lwp_status);
+ }
else
{
LOG_E("Can't find matching process of current thread");
@@ -350,7 +356,10 @@ sysret_t sys_exit(int status)
tid = rt_thread_self();
if (tid && tid->lwp)
+ {
lwp_thread_exit(tid, status);
+ }
+ else
{
LOG_E("Can't find matching process of current thread");
rc = -EINVAL;
@@ -616,102 +625,9 @@ sysret_t sys_fstat(int file, struct stat *buf)
#endif
}
-/* DFS and lwip definitions */
-#define IMPL_POLLIN (0x01)
-
-#define IMPL_POLLOUT (0x02)
-
-#define IMPL_POLLERR (0x04)
-#define IMPL_POLLHUP (0x08)
-#define IMPL_POLLNVAL (0x10)
-
-/* musl definitions */
-#define INTF_POLLIN 0x001
-#define INTF_POLLPRI 0x002
-#define INTF_POLLOUT 0x004
-#define INTF_POLLERR 0x008
-#define INTF_POLLHUP 0x010
-#define INTF_POLLNVAL 0x020
-#define INTF_POLLRDNORM 0x040
-#define INTF_POLLRDBAND 0x080
-#define INTF_POLLWRNORM 0x100
-#define INTF_POLLWRBAND 0x200
-#define INTF_POLLMSG 0x400
-#define INTF_POLLRDHUP 0x2000
-
-#define INTF_POLLIN_MASK (INTF_POLLIN | INTF_POLLRDNORM | INTF_POLLRDBAND | INTF_POLLPRI)
-#define INTF_POLLOUT_MASK (INTF_POLLOUT | INTF_POLLWRNORM | INTF_POLLWRBAND)
-
-static void musl2dfs_events(short *events)
-{
- short origin_e = *events;
- short result_e = 0;
-
- if (origin_e & INTF_POLLIN_MASK)
- {
- result_e |= IMPL_POLLIN;
- }
-
- if (origin_e & INTF_POLLOUT_MASK)
- {
- result_e |= IMPL_POLLOUT;
- }
-
- if (origin_e & INTF_POLLERR)
- {
- result_e |= IMPL_POLLERR;
- }
-
- if (origin_e & INTF_POLLHUP)
- {
- result_e |= IMPL_POLLHUP;
- }
-
- if (origin_e & INTF_POLLNVAL)
- {
- result_e |= IMPL_POLLNVAL;
- }
-
- *events = result_e;
-}
-
-static void dfs2musl_events(short *events)
-{
- short origin_e = *events;
- short result_e = 0;
-
- if (origin_e & IMPL_POLLIN)
- {
- result_e |= INTF_POLLIN_MASK;
- }
-
- if (origin_e & IMPL_POLLOUT)
- {
- result_e |= INTF_POLLOUT_MASK;
- }
-
- if (origin_e & IMPL_POLLERR)
- {
- result_e |= INTF_POLLERR;
- }
-
- if (origin_e & IMPL_POLLHUP)
- {
- result_e |= INTF_POLLHUP;
- }
-
- if (origin_e & IMPL_POLLNVAL)
- {
- result_e |= INTF_POLLNVAL;
- }
-
- *events = result_e;
-}
-
sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int ret = -1;
- int i = 0;
#ifdef ARCH_MM_MMU
struct pollfd *kfds = RT_NULL;
@@ -728,43 +644,20 @@ sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout)
lwp_get_from_user(kfds, fds, nfds * sizeof *kfds);
- for (i = 0; i < nfds; i++)
- {
- musl2dfs_events(&kfds[i].events);
- }
ret = poll(kfds, nfds, timeout);
if (ret > 0)
{
- for (i = 0; i < nfds; i++)
- {
- dfs2musl_events(&kfds->revents);
- }
lwp_put_to_user(fds, kfds, nfds * sizeof *kfds);
}
kmem_put(kfds);
return ret;
#else
-#ifdef RT_USING_MUSLLIBC
- for (i = 0; i < nfds; i++)
- {
- musl2dfs_events(&fds->events);
- }
-#endif /* RT_USING_MUSLLIBC */
if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds))
{
return -EFAULT;
}
ret = poll(fds, nfds, timeout);
-#ifdef RT_USING_MUSLLIBC
- if (ret > 0)
- {
- for (i = 0; i < nfds; i++)
- {
- dfs2musl_events(&fds->revents);
- }
- }
-#endif /* RT_USING_MUSLLIBC */
return ret;
#endif /* ARCH_MM_MMU */
}
@@ -1014,9 +907,9 @@ sysret_t sys_exec(char *filename, int argc, char **argv, char **envp)
sysret_t sys_kill(int pid, int signo)
{
- rt_err_t kret;
+ rt_err_t kret = 0;
sysret_t sysret;
- struct rt_lwp *lwp;
+ struct rt_lwp *lwp = RT_NULL;
/* handling the semantics of sys_kill */
if (pid > 0)
@@ -1027,31 +920,60 @@ sysret_t sys_kill(int pid, int signo)
* - pid tree (READ. since the lwp is fetch from the pid tree, it must stay there)
*/
lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
+ lwp = lwp_from_pid_raw_locked(pid);
if (lwp)
{
lwp_ref_inc(lwp);
lwp_pid_lock_release();
-
- kret = lwp_signal_kill(lwp, signo, SI_USER, 0);
- lwp_ref_dec(lwp);
- kret = 0;
}
else
{
lwp_pid_lock_release();
kret = -RT_ENOENT;
}
+
+ if (lwp)
+ {
+ kret = lwp_signal_kill(lwp, signo, SI_USER, 0);
+ lwp_ref_dec(lwp);
+ }
}
- else if (pid == 0)
+ else if (pid < -1 || pid == 0)
{
- /**
- * sig shall be sent to all processes (excluding an unspecified set
- * of system processes) whose process group ID is equal to the process
- * group ID of the sender, and for which the process has permission to
- * send a signal.
- */
- kret = -RT_ENOSYS;
+ pid_t pgid = 0;
+ rt_processgroup_t group;
+
+ if (pid == 0)
+ {
+ /**
+ * sig shall be sent to all processes (excluding an unspecified set
+ * of system processes) whose process group ID is equal to the process
+ * group ID of the sender, and for which the process has permission to
+ * send a signal.
+ */
+ pgid = lwp_pgid_get_byprocess(lwp_self());
+ }
+ else
+ {
+ /**
+ * sig shall be sent to all processes (excluding an unspecified set
+ * of system processes) whose process group ID is equal to the absolute
+ * value of pid, and for which the process has permission to send a signal.
+ */
+ pgid = -pid;
+ }
+
+ group = lwp_pgrp_find(pgid);
+ if (group != RT_NULL)
+ {
+ PGRP_LOCK(group);
+ kret = lwp_pgrp_signal_kill(group, signo, SI_USER, 0);
+ PGRP_UNLOCK(group);
+ }
+ else
+ {
+ kret = -ECHILD;
+ }
}
else if (pid == -1)
{
@@ -1062,15 +984,6 @@ sysret_t sys_kill(int pid, int signo)
*/
kret = -RT_ENOSYS;
}
- else /* pid < -1 */
- {
- /**
- * sig shall be sent to all processes (excluding an unspecified set
- * of system processes) whose process group ID is equal to the absolute
- * value of pid, and for which the process has permission to send a signal.
- */
- kret = -RT_ENOSYS;
- }
switch (kret)
{
@@ -1102,6 +1015,22 @@ sysret_t sys_getpid(void)
return lwp_getpid();
}
+sysret_t sys_getppid(void)
+{
+ rt_lwp_t process;
+
+ process = lwp_self();
+ if (process->parent == RT_NULL)
+ {
+ LOG_E("%s: process %d has no parent process", __func__, lwp_to_pid(process));
+ return 0;
+ }
+ else
+ {
+ return lwp_to_pid(process->parent);
+ }
+}
+
/* syscall: "getpriority" ret: "int" args: "int" "id_t" */
sysret_t sys_getpriority(int which, id_t who)
{
@@ -1112,10 +1041,7 @@ sysret_t sys_getpriority(int which, id_t who)
struct rt_lwp *lwp = RT_NULL;
lwp_pid_lock_take();
- if(who == 0)
- lwp = lwp_self();
- else
- lwp = lwp_from_pid_locked(who);
+ lwp = lwp_from_pid_locked(who);
if (lwp)
{
@@ -1137,10 +1063,7 @@ sysret_t sys_setpriority(int which, id_t who, int prio)
struct rt_lwp *lwp = RT_NULL;
lwp_pid_lock_take();
- if(who == 0)
- lwp = lwp_self();
- else
- lwp = lwp_from_pid_locked(who);
+ lwp = lwp_from_pid_locked(who);
if (lwp && prio >= 0 && prio < RT_THREAD_PRIORITY_MAX)
{
@@ -1317,7 +1240,7 @@ sysret_t sys_munmap(void *addr, size_t length)
void *sys_mremap(void *old_address, size_t old_size,
size_t new_size, int flags, void *new_address)
{
- return (void *)-1;
+ return lwp_mremap(lwp_self(), old_address, old_size, new_size, flags, new_address);
}
sysret_t sys_madvise(void *addr, size_t len, int behav)
@@ -1475,6 +1398,34 @@ sysret_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
return ret;
}
+rt_weak int syslog_ctrl(int type, char *buf, int len)
+{
+ return -EINVAL;
+}
+
+sysret_t sys_syslog(int type, char *buf, int len)
+{
+ char *tmp;
+ int ret = -1;
+
+ if (!lwp_user_accessable((void *)buf, len))
+ {
+ return -EFAULT;
+ }
+
+ tmp = (char *)rt_malloc(len);
+ if (!tmp)
+ {
+ return -ENOMEM;
+ }
+
+ ret = syslog_ctrl(type, tmp, len);
+ lwp_put_to_user(buf, tmp, len);
+ rt_free(tmp);
+
+ return ret;
+}
+
rt_mq_t sys_mq_create(const char *name,
rt_size_t msg_size,
rt_size_t max_msgs,
@@ -2012,13 +1963,21 @@ static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src)
dst->data_entry = src->data_entry;
dst->data_size = src->data_size;
dst->args = src->args;
- dst->leader = 0;
- dst->session = src->session;
dst->background = src->background;
- dst->tty_old_pgrp = 0;
- dst->__pgrp = src->__pgrp;
dst->tty = src->tty;
+
+ /* terminal API */
+ dst->term_ctrlterm = src->term_ctrlterm;
+
rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX);
+ if (src->exe_file)
+ {
+ if (dst->exe_file)
+ {
+ rt_free(dst->exe_file);
+ }
+ dst->exe_file = strndup(src->exe_file, DFS_PATH_MAX);
+ }
rt_memcpy(&dst->signal.sig_action, &src->signal.sig_action, sizeof(dst->signal.sig_action));
rt_memcpy(&dst->signal.sig_action_mask, &src->signal.sig_action_mask, sizeof(dst->signal.sig_action_mask));
@@ -2026,6 +1985,8 @@ static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src)
rt_memcpy(&dst->signal.sig_action_onstack, &src->signal.sig_action_onstack, sizeof(dst->signal.sig_action_onstack));
rt_memcpy(&dst->signal.sig_action_restart, &dst->signal.sig_action_restart, sizeof(dst->signal.sig_action_restart));
rt_memcpy(&dst->signal.sig_action_siginfo, &dst->signal.sig_action_siginfo, sizeof(dst->signal.sig_action_siginfo));
+ rt_memcpy(&dst->signal.sig_action_nocldstop, &dst->signal.sig_action_nocldstop, sizeof(dst->signal.sig_action_nocldstop));
+ rt_memcpy(&dst->signal.sig_action_nocldwait, &dst->signal.sig_action_nocldwait, sizeof(dst->signal.sig_action_nocldwait));
rt_strcpy(dst->working_directory, src->working_directory);
}
@@ -2071,6 +2032,7 @@ sysret_t _sys_fork(void)
rt_thread_t thread = RT_NULL;
rt_thread_t self_thread = RT_NULL;
void *user_stack = RT_NULL;
+ rt_processgroup_t group;
/* new lwp */
lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
@@ -2145,6 +2107,17 @@ sysret_t _sys_fork(void)
lwp_children_register(self_lwp, lwp);
+ /* set pgid and sid */
+ group = lwp_pgrp_find(lwp_pgid_get_byprocess(self_lwp));
+ if (group)
+ {
+ lwp_pgrp_insert(group, lwp);
+ }
+ else
+ {
+ LOG_W("the process group of pid: %d cannot be found", lwp_pgid_get_byprocess(self_lwp));
+ }
+
/* copy kernel stack context from self thread */
lwp_memcpy(thread->stack_addr, self_thread->stack_addr, self_thread->stack_size);
lwp_tid_set_thread(tid, thread);
@@ -2157,28 +2130,6 @@ sysret_t _sys_fork(void)
(void *)((char *)thread->stack_addr + thread->stack_size),
user_stack, &thread->sp);
- /* new thread never reach there */
- LWP_LOCK(lwp);
- if (lwp->tty != RT_NULL)
- {
- int ret;
- struct rt_lwp *old_lwp;
-
- old_lwp = lwp->tty->foreground;
- rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER);
- ret = tty_push(&lwp->tty->head, old_lwp);
- rt_mutex_release(&lwp->tty->lock);
- if (ret < 0)
- {
- LOG_E("malloc fail!\n");
- SET_ERRNO(ENOMEM);
- goto fail;
- }
-
- lwp->tty->foreground = lwp;
- }
- LWP_UNLOCK(lwp);
-
rt_thread_startup(thread);
return lwp_to_pid(lwp);
fail:
@@ -2246,7 +2197,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
nsize = new_argc * sizeof(char *);
for (i = 0; i < new_argc; i++)
{
- nsize += lwp_user_strlen(new_argv[i]) + 1;
+ nsize += lwp_strlen(lwp_self(), new_argv[i]) + 1;
}
if (nsize + args->size > ARCH_PAGE_SIZE)
{
@@ -2260,7 +2211,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < new_argc; i++)
{
nargv[i] = p;
- len = lwp_user_strlen(new_argv[i]) + 1;
+ len = lwp_strlen(lwp_self(), new_argv[i]) + 1;
lwp_memcpy(p, new_argv[i], len);
p += len;
}
@@ -2269,7 +2220,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < args->argc; i++)
{
nargv[i] = p;
- len = lwp_user_strlen(args->argv[i]) + 1;
+ len = lwp_strlen(lwp_self(), args->argv[i]) + 1;
lwp_memcpy(p, args->argv[i], len);
p += len;
}
@@ -2278,7 +2229,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < args->envc; i++)
{
nenvp[i] = p;
- len = lwp_user_strlen(args->envp[i]) + 1;
+ len = lwp_strlen(lwp_self(), args->envp[i]) + 1;
lwp_memcpy(p, args->envp[i], len);
p += len;
}
@@ -2427,7 +2378,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
{
break;
}
- len = lwp_user_strlen_ext(lwp, (const char *)argv[argc]);
+ len = lwp_strlen(lwp, (const char *)argv[argc]);
size += sizeof(char *) + len + 1;
argc++;
}
@@ -2440,7 +2391,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
{
break;
}
- len = lwp_user_strlen_ext(lwp, (const char *)envp[envc]);
+ len = lwp_strlen(lwp, (const char *)envp[envc]);
size += sizeof(char *) + len + 1;
envc++;
}
@@ -2461,7 +2412,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
for (i = 0; i < argc; i++)
{
kargv[i] = p;
- len = lwp_user_strlen_ext(lwp, argv[i]) + 1;
+ len = lwp_strlen(lwp, argv[i]) + 1;
lwp_memcpy(p, argv[i], len);
p += len;
}
@@ -2473,7 +2424,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
for (i = 0; i < envc; i++)
{
kenvp[i] = p;
- len = lwp_user_strlen_ext(lwp, envp[i]) + 1;
+ len = lwp_strlen(lwp, envp[i]) + 1;
lwp_memcpy(p, envp[i], len);
p += len;
}
@@ -2523,7 +2474,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
ret = lwp_load("/lib/ld.so", lwp, RT_NULL, 0, aux);
- rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX);
+ rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX - 1);
quit:
if (page)
{
@@ -2751,6 +2702,8 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
strncpy(thread->parent.name, run_name + last_backslash, RT_NAME_MAX);
strncpy(lwp->cmd, new_lwp->cmd, RT_NAME_MAX);
+ rt_free(lwp->exe_file);
+ lwp->exe_file = strndup(new_lwp->exe_file, DFS_PATH_MAX);
rt_pages_free(page, 0);
@@ -4412,7 +4365,7 @@ sysret_t sys_waitpid(int32_t pid, int *status, int options)
}
else
{
- ret = lwp_waitpid(pid, status, options);
+ ret = lwp_waitpid(pid, status, options, RT_NULL);
}
#else
if (!lwp_user_accessable((void *)status, sizeof(int)))
@@ -4715,7 +4668,7 @@ sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret,
rt_strncpy(ptr, k_name, buflen - (ptr - buf));
ret->h_name = ptr;
- ptr += lwp_user_strlen(k_name);
+ ptr += strlen(k_name);
ret->h_addr_list = (char**)ptr;
ptr += cnt * sizeof(char *);
@@ -4733,6 +4686,10 @@ sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret,
#endif
ret_val = 0;
}
+ else
+ {
+ SET_ERRNO(EINVAL);
+ }
__exit:
if (ret_val < 0)
@@ -4794,6 +4751,7 @@ sysret_t sys_chdir(const char *path)
#ifdef ARCH_MM_MMU
int err = 0;
int len = 0;
+ int errcode;
char *kpath = RT_NULL;
len = lwp_user_strlen(path);
@@ -4815,16 +4773,46 @@ sysret_t sys_chdir(const char *path)
}
err = chdir(kpath);
+ errcode = err != 0 ? GET_ERRNO() : 0;
kmem_put(kpath);
- return (err < 0 ? GET_ERRNO() : err);
+ return errcode;
#else
int ret = chdir(path);
return (ret < 0 ? GET_ERRNO() : ret);
#endif
}
+sysret_t sys_fchdir(int fd)
+{
+ int errcode = -ENOSYS;
+#ifdef ARCH_MM_MMU
+#ifdef RT_USING_DFS_V2
+ int err = -1;
+ struct dfs_file *d;
+ char *kpath;
+
+ d = fd_get(fd);
+ if (!d || !d->vnode)
+ {
+ return -EBADF;
+ }
+ kpath = dfs_dentry_full_path(d->dentry);
+ if (!kpath)
+ {
+ return -EACCES;
+ }
+
+ err = chdir(kpath);
+ errcode = err != 0 ? GET_ERRNO() : 0;
+
+ kmem_put(kpath);
+#endif
+#endif
+ return errcode;
+}
+
sysret_t sys_mkdir(const char *path, mode_t mode)
{
#ifdef ARCH_MM_MMU
@@ -5055,6 +5043,11 @@ sysret_t sys_pipe(int fd[2])
return (ret < 0 ? GET_ERRNO() : ret);
}
+sysret_t sys_wait4(pid_t pid, int *status, int options, struct rusage *ru)
+{
+ return lwp_waitpid(pid, status, options, ru);
+}
+
sysret_t sys_clock_settime(clockid_t clk, const struct timespec *ts)
{
int ret = 0;
@@ -5271,7 +5264,7 @@ sysret_t sys_getrlimit(unsigned int resource, unsigned long rlim[2])
}
lwp_put_to_user((void *)rlim, krlim, sizeof(unsigned long [2]));
- kmem_put(krlim);
+
return (ret < 0 ? GET_ERRNO() : ret);
}
@@ -5280,13 +5273,6 @@ sysret_t sys_setrlimit(unsigned int resource, struct rlimit *rlim)
return -ENOSYS;
}
-sysret_t sys_setsid(void)
-{
- int ret = 0;
- ret = setsid();
- return (ret < 0 ? GET_ERRNO() : ret);
-}
-
sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags)
{
int ret = -1;
@@ -5353,12 +5339,16 @@ sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags)
return ret;
}
+/**
+ * readlink() places the contents of the symbolic link pathname in the buffer buf, which has size bufsiz.
+ * readlink() does not append a null byte to buf.
+ * It will (silently) truncate the contents(to a length of bufsiz characters),
+ * in case the buffer is too small to hold all of the contents.
+ */
ssize_t sys_readlink(char* path, char *buf, size_t bufsz)
{
size_t len, copy_len;
int err, rtn;
- int fd = -1;
- struct dfs_file *d;
char *copy_path;
len = lwp_user_strlen(path);
@@ -5381,91 +5371,27 @@ ssize_t sys_readlink(char* path, char *buf, size_t bufsz)
copy_len = lwp_get_from_user(copy_path, path, len);
copy_path[copy_len] = '\0';
- /* musl __procfdname */
- err = sscanf(copy_path, "/proc/self/fd/%d", &fd);
-
- if (err != 1)
+ char *link_fn = (char *)rt_malloc(DFS_PATH_MAX);
+ if (link_fn)
{
- rtn = 0;
- if (access(copy_path, 0))
+ err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX);
+ if (err > 0)
{
- rtn = -ENOENT;
- LOG_E("readlink: path not is /proc/self/fd/* and path not exits, call by musl __procfdname()?");
+ rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz - 1);
}
else
{
-#ifdef RT_USING_DFS_V2
- char *link_fn = (char *)rt_malloc(DFS_PATH_MAX);
- if (link_fn)
- {
- err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX);
- if (err > 0)
- {
- rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz - 1);
- }
- else
- {
- rtn = -EIO;
- }
- rt_free(link_fn);
- }
- else
- {
- rtn = -ENOMEM;
- }
-#else
- rtn = lwp_put_to_user(buf, copy_path, copy_len);
-#endif
+ rtn = -EIO;
}
- rt_free(copy_path);
- return rtn;
+ rt_free(link_fn);
}
else
{
- rt_free(copy_path);
- }
-
- d = fd_get(fd);
- if (!d)
- {
- return -EBADF;
- }
-
- if (!d->vnode)
- {
- return -EBADF;
+ rtn = -ENOMEM;
}
-#ifdef RT_USING_DFS_V2
- {
- char *fullpath = dfs_dentry_full_path(d->dentry);
- if (fullpath)
- {
- copy_len = strlen(fullpath);
- if (copy_len > bufsz)
- {
- copy_len = bufsz;
- }
-
- bufsz = lwp_put_to_user(buf, fullpath, copy_len);
- rt_free(fullpath);
- }
- else
- {
- bufsz = 0;
- }
- }
-#else
- copy_len = strlen(d->vnode->fullpath);
- if (copy_len > bufsz)
- {
- copy_len = bufsz;
- }
-
- bufsz = lwp_put_to_user(buf, d->vnode->fullpath, copy_len);
-#endif
-
- return bufsz;
+ rt_free(copy_path);
+ return rtn;
}
sysret_t sys_sched_setaffinity(pid_t pid, size_t size, void *set)
@@ -5511,7 +5437,6 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
LWP_DEF_RETURN_CODE(rc);
void *mask;
struct rt_lwp *lwp;
- rt_bool_t need_release = RT_FALSE;
if (size <= 0 || size > sizeof(cpu_set_t))
{
@@ -5529,16 +5454,8 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
CPU_ZERO_S(size, mask);
- if (pid == 0)
- {
- lwp = lwp_self();
- }
- else
- {
- need_release = RT_TRUE;
- lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
- }
+ lwp_pid_lock_take();
+ lwp = lwp_from_pid_locked(pid);
if (!lwp)
{
@@ -5569,8 +5486,7 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
rc = size;
}
- if (need_release)
- lwp_pid_lock_release();
+ lwp_pid_lock_release();
kmem_put(mask);
@@ -5626,12 +5542,10 @@ sysret_t sys_sysinfo(void *info)
#endif
}
-sysret_t sys_sched_setparam(pid_t pid, void *param)
+sysret_t sys_sched_setparam(pid_t tid, void *param)
{
struct sched_param *sched_param = RT_NULL;
- struct rt_lwp *lwp = NULL;
- rt_thread_t main_thread;
- rt_bool_t need_release = RT_FALSE;
+ rt_thread_t thread;
int ret = -1;
if (!lwp_user_accessable(param, sizeof(struct sched_param)))
@@ -5651,26 +5565,15 @@ sysret_t sys_sched_setparam(pid_t pid, void *param)
return -EINVAL;
}
- if (pid > 0)
- {
- need_release = RT_TRUE;
- lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
- }
- else if (pid == 0)
- {
- lwp = lwp_self();
- }
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
- if (lwp)
+ if (thread)
{
- main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
- if (need_release)
- lwp_pid_lock_release();
-
- ret = rt_thread_control(main_thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority);
+ ret = rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority);
}
+ lwp_tid_dec_ref(thread);
+
kmem_put(sched_param);
return ret;
@@ -5767,37 +5670,24 @@ sysret_t sys_sched_setscheduler(int tid, int policy, void *param)
return ret;
}
-sysret_t sys_sched_getscheduler(int tid, int *policy, void *param)
+sysret_t sys_sched_getscheduler(int tid)
{
- struct sched_param *sched_param = RT_NULL;
rt_thread_t thread = RT_NULL;
+ int rtn;
- if (!lwp_user_accessable(param, sizeof(struct sched_param)))
- {
- return -EFAULT;
- }
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
+ lwp_tid_dec_ref(thread);
- sched_param = kmem_get(sizeof(struct sched_param));
- if (sched_param == RT_NULL)
+ if (thread)
{
- return -ENOMEM;
+ rtn = SCHED_RR;
}
-
- if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param))
+ else
{
- kmem_put(sched_param);
- return -EINVAL;
+ rtn = -ESRCH;
}
- thread = lwp_tid_get_thread_and_inc_ref(tid);
- sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority;
- lwp_tid_dec_ref(thread);
-
- lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param));
- kmem_put(sched_param);
-
- *policy = 0;
- return 0;
+ return rtn;
}
sysret_t sys_fsync(int fd)
@@ -6409,24 +6299,31 @@ sysret_t sys_epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
int ret = 0;
struct epoll_event *kev = RT_NULL;
- if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event)))
+ if (ev)
+ {
+ if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event)))
return -EFAULT;
- kev = kmem_get(sizeof(struct epoll_event));
- if (kev == RT_NULL)
- {
- return -ENOMEM;
- }
+ kev = kmem_get(sizeof(struct epoll_event));
+ if (kev == RT_NULL)
+ {
+ return -ENOMEM;
+ }
- if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event))
- {
- kmem_put(kev);
- return -EINVAL;
- }
+ if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event))
+ {
+ kmem_put(kev);
+ return -EINVAL;
+ }
- ret = epoll_ctl(fd, op, fd2, kev);
+ ret = epoll_ctl(fd, op, fd2, kev);
- kmem_put(kev);
+ kmem_put(kev);
+ }
+ else
+ {
+ ret = epoll_ctl(fd, op, fd2, RT_NULL);
+ }
return (ret < 0 ? GET_ERRNO() : ret);
}
@@ -6537,45 +6434,93 @@ sysret_t sys_utimensat(int __fd, const char *__path, const struct timespec __tim
#endif
}
-sysret_t sys_chmod(const char *fileName, mode_t mode)
+sysret_t sys_chmod(const char *pathname, mode_t mode)
{
- char *copy_fileName;
- size_t len_fileName, copy_len_fileName;
-#ifdef RT_USING_DFS_V2
- struct dfs_attr attr;
+ char *copy_file;
+ size_t len_file, copy_len_file;
+ struct dfs_attr attr = {0};
+ int ret = 0;
+
+ len_file = lwp_user_strlen(pathname);
+ if (len_file <= 0)
+ {
+ return -EFAULT;
+ }
+
+ copy_file = (char*)rt_malloc(len_file + 1);
+ if (!copy_file)
+ {
+ return -ENOMEM;
+ }
+
+ copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file);
+
attr.st_mode = mode;
-#endif
+ attr.ia_valid |= ATTR_MODE_SET;
+
+ copy_file[copy_len_file] = '\0';
+ ret = dfs_file_setattr(copy_file, &attr);
+ rt_free(copy_file);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+sysret_t sys_chown(const char *pathname, uid_t owner, gid_t group)
+{
+ char *copy_file;
+ size_t len_file, copy_len_file;
+ struct dfs_attr attr = {0};
int ret = 0;
- len_fileName = lwp_user_strlen(fileName);
- if (len_fileName <= 0)
+ len_file = lwp_user_strlen(pathname);
+ if (len_file <= 0)
{
return -EFAULT;
}
- copy_fileName = (char*)rt_malloc(len_fileName + 1);
- if (!copy_fileName)
+ copy_file = (char*)rt_malloc(len_file + 1);
+ if (!copy_file)
{
return -ENOMEM;
}
- copy_len_fileName = lwp_get_from_user(copy_fileName, (void *)fileName, len_fileName);
- copy_fileName[copy_len_fileName] = '\0';
-#ifdef RT_USING_DFS_V2
- ret = dfs_file_setattr(copy_fileName, &attr);
-#else
- SET_ERRNO(EFAULT);
-#endif
- rt_free(copy_fileName);
+ copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file);
+
+ if(owner >= 0)
+ {
+ attr.st_uid = owner;
+ attr.ia_valid |= ATTR_UID_SET;
+ }
+
+ if(group >= 0)
+ {
+ attr.st_gid = group;
+ attr.ia_valid |= ATTR_GID_SET;
+ }
+
+ copy_file[copy_len_file] = '\0';
+ ret = dfs_file_setattr(copy_file, &attr);
+ rt_free(copy_file);
return (ret < 0 ? GET_ERRNO() : ret);
}
-sysret_t sys_reboot(int magic)
+#include <sys/reboot.h>
+sysret_t sys_reboot(int magic, int magic2, int type)
{
- rt_hw_cpu_reset();
+ sysret_t rc;
+ switch (type)
+ {
+ /* TODO add software poweroff protocols */
+ case RB_AUTOBOOT:
+ case RB_POWER_OFF:
+ rt_hw_cpu_reset();
+ break;
+ default:
+ rc = -ENOSYS;
+ }
- return 0;
+ return rc;
}
ssize_t sys_pread64(int fd, void *buf, int size, size_t offset)
@@ -6824,6 +6769,59 @@ sysret_t sys_setitimer(int which, const struct itimerspec *restrict new, struct
return rc;
}
+sysret_t sys_set_robust_list(struct robust_list_head *head, size_t len)
+{
+ if (len != sizeof(*head))
+ return -EINVAL;
+
+ rt_thread_self()->robust_list = head;
+ return 0;
+}
+
+sysret_t sys_get_robust_list(int tid, struct robust_list_head **head_ptr, size_t *len_ptr)
+{
+ rt_thread_t thread;
+ size_t len;
+ struct robust_list_head *head;
+
+ if (!lwp_user_accessable((void *)head_ptr, sizeof(struct robust_list_head *)))
+ {
+ return -EFAULT;
+ }
+ if (!lwp_user_accessable((void *)len_ptr, sizeof(size_t)))
+ {
+ return -EFAULT;
+ }
+
+ if (tid == 0)
+ {
+ thread = rt_thread_self();
+ head = thread->robust_list;
+ }
+ else
+ {
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
+ if (thread)
+ {
+ head = thread->robust_list;
+ lwp_tid_dec_ref(thread);
+ }
+ else
+ {
+ return -ESRCH;
+ }
+ }
+
+ len = sizeof(*(head));
+
+ if (!lwp_put_to_user(len_ptr, &len, sizeof(size_t)))
+ return -EFAULT;
+ if (!lwp_put_to_user(head_ptr, &head, sizeof(struct robust_list_head *)))
+ return -EFAULT;
+
+ return 0;
+}
+
const static struct rt_syscall_def func_table[] =
{
SYSCALL_SIGN(sys_exit), /* 01 */
@@ -6994,22 +6992,22 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_clock_settime),
SYSCALL_SIGN(sys_clock_gettime),
SYSCALL_SIGN(sys_clock_getres),
- SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */
+ SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_futex)),
- SYSCALL_USPACE(SYSCALL_SIGN(sys_pmutex)),
+ SYSCALL_SIGN(sys_notimpl), /* discarded: sys_pmutex */
SYSCALL_SIGN(sys_dup),
SYSCALL_SIGN(sys_dup2),
- SYSCALL_SIGN(sys_rename), /* 135 */
+ SYSCALL_SIGN(sys_rename), /* 135 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_fork)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_execve)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_vfork)),
SYSCALL_SIGN(sys_gettid),
- SYSCALL_SIGN(sys_prlimit64), /* 140 */
+ SYSCALL_SIGN(sys_prlimit64), /* 140 */
SYSCALL_SIGN(sys_getrlimit),
SYSCALL_SIGN(sys_setrlimit),
SYSCALL_SIGN(sys_setsid),
SYSCALL_SIGN(sys_getrandom),
- SYSCALL_SIGN(sys_readlink), // SYSCALL_SIGN(sys_readlink) /* 145 */
+ SYSCALL_SIGN(sys_readlink), /* 145 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_mremap)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_madvise)),
SYSCALL_SIGN(sys_sched_setparam),
@@ -7044,7 +7042,7 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_umount2),
SYSCALL_SIGN(sys_link),
SYSCALL_SIGN(sys_symlink),
- SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */
+ SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */
SYSCALL_SIGN(sys_sysinfo),
SYSCALL_SIGN(sys_chmod),
SYSCALL_SIGN(sys_reboot),
@@ -7069,12 +7067,21 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_setitimer),
SYSCALL_SIGN(sys_utimensat),
#ifdef RT_USING_POSIX_SOCKET
- SYSCALL_SIGN(sys_notimpl),
- SYSCALL_SIGN(sys_socketpair), /* 205 */
+ SYSCALL_SIGN(sys_syslog),
+ SYSCALL_SIGN(sys_socketpair), /* 205 */
#else
SYSCALL_SIGN(sys_notimpl),
- SYSCALL_SIGN(sys_notimpl),
+ SYSCALL_SIGN(sys_notimpl), /* 205 */
#endif
+ SYSCALL_SIGN(sys_wait4),
+ SYSCALL_SIGN(sys_set_robust_list),
+ SYSCALL_SIGN(sys_get_robust_list),
+ SYSCALL_SIGN(sys_setpgid),
+ SYSCALL_SIGN(sys_getpgid), /* 210 */
+ SYSCALL_SIGN(sys_getsid),
+ SYSCALL_SIGN(sys_getppid),
+ SYSCALL_SIGN(sys_fchdir),
+ SYSCALL_SIGN(sys_chown),
};
const void *lwp_get_sys_api(rt_uint32_t number)
diff --git a/components/lwp/lwp_syscall.h b/components/lwp/lwp_syscall.h
index b7e7b09f55..1527a764f1 100644
--- a/components/lwp/lwp_syscall.h
+++ b/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
diff --git a/components/lwp/lwp_tid.c b/components/lwp/lwp_tid.c
index fdac54a36f..3e330027de 100644
--- a/components/lwp/lwp_tid.c
+++ b/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;
diff --git a/components/lwp/lwp_user_mm.c b/components/lwp/lwp_user_mm.c
index 3c23e59e05..084b5bcf28 100644
--- a/components/lwp/lwp_user_mm.c
+++ b/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)
{
diff --git a/components/lwp/lwp_user_mm.h b/components/lwp/lwp_user_mm.h
index cfc5b57152..232038c914 100644
--- a/components/lwp/lwp_user_mm.h
+++ b/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);
diff --git a/components/lwp/terminal/Kconfig b/components/lwp/terminal/Kconfig
new file mode 100644
index 0000000000..7fd77e7505
--- /dev/null
+++ b/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
diff --git a/components/lwp/terminal/bsd_porting.h b/components/lwp/terminal/bsd_porting.h
new file mode 100644
index 0000000000..fba8f47fb3
--- /dev/null
+++ b/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__ */
diff --git a/components/lwp/terminal/bsd_ttydisc.h b/components/lwp/terminal/bsd_ttydisc.h
new file mode 100644
index 0000000000..3d25f61e12
--- /dev/null
+++ b/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 <[email protected]>
+ * 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_ */
diff --git a/components/lwp/terminal/bsd_ttyqueue.h b/components/lwp/terminal/bsd_ttyqueue.h
new file mode 100644
index 0000000000..96b7ae3d8b
--- /dev/null
+++ b/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 <[email protected]>
+ * 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_ */
diff --git a/components/lwp/terminal/freebsd/tty.c b/components/lwp/terminal/freebsd/tty.c
new file mode 100644
index 0000000000..cdd9006279
--- /dev/null
+++ b/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 <[email protected]>
+ * 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);
+}
diff --git a/components/lwp/terminal/freebsd/tty_compat.c b/components/lwp/terminal/freebsd/tty_compat.c
new file mode 100644
index 0000000000..0f2c70a5c5
--- /dev/null
+++ b/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);
+}
diff --git a/components/lwp/terminal/freebsd/tty_inq.c b/components/lwp/terminal/freebsd/tty_inq.c
new file mode 100644
index 0000000000..1dc748d6c2
--- /dev/null
+++ b/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 <[email protected]>
+ * 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
diff --git a/components/lwp/terminal/freebsd/tty_outq.c b/components/lwp/terminal/freebsd/tty_outq.c
new file mode 100644
index 0000000000..214309813e
--- /dev/null
+++ b/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 <[email protected]>
+ * 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
diff --git a/components/lwp/terminal/freebsd/tty_pts.c b/components/lwp/terminal/freebsd/tty_pts.c
new file mode 100644
index 0000000000..2c75ff3009
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_pts.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 Shell init ver.
+ */
+
+#include <ipc/condvar.h>
+#include <rid_bitmap.h>
+#include <terminal/terminal.h>
+#include <terminal/tty_internal.h>
+#include <ptyfs.h>
+
+#include <rtthread.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * 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.
+ */
+#define PTS_EXTERNAL
+
+/*
+ * Per-PTS structure.
+ *
+ * List of locks
+ * (t) locked by tty_lock()
+ * (c) const until freeing
+ */
+struct pts_softc
+{
+ int pts_unit; /* (c) Device unit number. */
+ unsigned int pts_flags; /* (t) Device flags. */
+#define PTS_PKT 0x1 /* Packet mode. */
+#define PTS_FINISHED 0x2 /* Return errors on read()/write(). */
+#define PTS_PTLOCKED 0x4 /* ioctl %TIOCSPTLCK/%TIOCGPTLCK */
+ char pts_pkt; /* (t) Unread packet mode data. */
+
+ struct rt_condvar pts_inwait; /* (t) Blocking write() on master. */
+ struct rt_wqueue pts_inpoll; /* (t) Select queue for write(). */
+ struct rt_condvar pts_outwait; /* (t) Blocking read() on master. */
+ struct rt_wqueue pts_outpoll; /* (t) Select queue for read(). */
+
+ struct ucred *pts_cred; /* (c) Resource limit. */
+ rt_device_t pts_master; /** (c) Master device.
+ * (Note: in rtsmart kernel, we support
+ * multi-instance ptmx )
+ */
+};
+
+/**
+ * Controller-side file operations.
+ * (P)seudo-(T)erminal (M)ultiple(X)er
+ */
+
+static int ptsdev_read(struct lwp_tty *tp, struct uio *uio,
+ struct ucred *active_cred, int oflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0;
+ char pkt;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ tty_lock(tp);
+
+ for (;;)
+ {
+ /*
+ * Implement packet mode. When packet mode is turned on,
+ * the first byte contains a bitmask of events that
+ * occurred (start, stop, flush, window size, etc).
+ */
+ if (psc->pts_flags & PTS_PKT && psc->pts_pkt)
+ {
+ pkt = psc->pts_pkt;
+ psc->pts_pkt = 0;
+ tty_unlock(tp);
+
+ error = uiomove(&pkt, 1, uio);
+ return (error);
+ }
+
+ /*
+ * Transmit regular data.
+ *
+ * XXX: We shouldn't use ttydisc_getc_poll()! Even
+ * though in this implementation, there is likely going
+ * to be data, we should just call ttydisc_getc_uio()
+ * and use its return value to sleep.
+ */
+ if (ttydisc_getc_poll(tp))
+ {
+ if (psc->pts_flags & PTS_PKT)
+ {
+ /*
+ * XXX: Small race. Fortunately PTY
+ * consumers aren't multithreaded.
+ */
+
+ tty_unlock(tp);
+ pkt = TIOCPKT_DATA;
+ error = uiomove(&pkt, 1, uio);
+ if (error)
+ return (error);
+ tty_lock(tp);
+ }
+
+ error = ttydisc_getc_uio(tp, uio);
+ break;
+ }
+
+ /* Maybe the device isn't used anyway. */
+ if (psc->pts_flags & PTS_FINISHED)
+ break;
+
+ /* Wait for more data. */
+ if (oflags & O_NONBLOCK)
+ {
+ error = EWOULDBLOCK;
+ break;
+ }
+ error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
+ if (error != 0)
+ break;
+ }
+
+ tty_unlock(tp);
+
+ return (error);
+}
+
+static int ptsdev_write(struct lwp_tty *tp, struct uio *uio,
+ struct ucred *active_cred, int oflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ char ib[256], *ibstart;
+ size_t iblen, rintlen;
+ int error = 0;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ for (;;)
+ {
+ ibstart = ib;
+ iblen = MIN(uio->uio_resid, sizeof ib);
+ error = uiomove(ib, iblen, uio);
+
+ tty_lock(tp);
+ if (error != 0)
+ {
+ iblen = 0;
+ goto done;
+ }
+
+ /*
+ * When possible, avoid the slow path. rint_bypass()
+ * copies all input to the input queue at once.
+ */
+ MPASS(iblen > 0);
+ do
+ {
+ rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
+ ibstart += rintlen;
+ iblen -= rintlen;
+ if (iblen == 0)
+ {
+ /* All data written. */
+ break;
+ }
+
+ /* Maybe the device isn't used anyway. */
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ error = -EIO;
+ goto done;
+ }
+
+ /* Wait for more data. */
+ if (oflags & O_NONBLOCK)
+ {
+ error = -EWOULDBLOCK;
+ goto done;
+ }
+
+ /* Wake up users on the slave side. */
+ ttydisc_rint_done(tp);
+ error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
+ if (error != 0)
+ goto done;
+ } while (iblen > 0);
+
+ if (uio->uio_resid == 0)
+ break;
+ tty_unlock(tp);
+ }
+
+done:
+ ttydisc_rint_done(tp);
+ tty_unlock(tp);
+
+ /*
+ * Don't account for the part of the buffer that we couldn't
+ * pass to the TTY.
+ */
+ uio->uio_resid += iblen;
+ return (error);
+}
+
+static int ptsdev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
+ struct ucred *active_cred, int fflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0, sig;
+
+ switch (cmd)
+ {
+#ifdef USING_BSD_IOCTL_EXT
+ case FIODTYPE:
+ *(int *)data = D_TTY;
+ return (0);
+#endif
+ case FIONBIO:
+ /* This device supports non-blocking operation. */
+ return (0);
+ case FIONREAD:
+ tty_lock(tp);
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ /* Force read() to be called. */
+ *(int *)data = 1;
+ }
+ else
+ {
+ *(int *)data = ttydisc_getc_poll(tp);
+ }
+ tty_unlock(tp);
+ return (0);
+#ifdef USING_BSD_IOCTL_EXT
+ case FIODGNAME:
+#ifdef COMPAT_FREEBSD32
+ case FIODGNAME_32:
+#endif
+ {
+ struct fiodgname_arg *fgn;
+ const char *p;
+ int i;
+
+ /* Reverse device name lookups, for ptsname() and ttyname(). */
+ fgn = data;
+ p = tty_devname(tp);
+ i = strlen(p) + 1;
+ if (i > fgn->len)
+ return -EINVAL;
+ return (copyout(p, fiodgname_buf_get_ptr(fgn, cmd), i));
+ }
+#endif
+ /*
+ * We need to implement TIOCGPGRP and TIOCGSID here again. When
+ * called on the pseudo-terminal master, it should not check if
+ * the terminal is the foreground terminal of the calling
+ * process.
+ *
+ * TIOCGETA is also implemented here. Various Linux PTY routines
+ * often call isatty(), which is implemented by tcgetattr().
+ */
+ case TIOCGETA:
+ /* Obtain terminal flags through tcgetattr(). */
+ tty_lock(tp);
+ *(struct termios *)data = tp->t_termios;
+ tty_unlock(tp);
+ return (0);
+ case TIOCSETAF:
+ case TIOCSETAW:
+ /*
+ * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
+ * TCSADRAIN into something different. If an application would
+ * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
+ * deadlock waiting for all data to be read.
+ */
+ cmd = TIOCSETA;
+ break;
+ case TIOCGPTN:
+ /*
+ * Get the device unit number.
+ */
+ if (psc->pts_unit < 0)
+ return -ENOTTY;
+ *(unsigned int *)data = psc->pts_unit;
+ return (0);
+ case TIOCGPGRP:
+ /* Get the foreground process group ID. */
+ tty_lock(tp);
+ if (tp->t_pgrp != NULL)
+ *(int *)data = tp->t_pgrp->pgid;
+ else
+ *(int *)data = NO_PID;
+ tty_unlock(tp);
+ return (0);
+ case TIOCGSID:
+ /* Get the session leader process ID. */
+ tty_lock(tp);
+ if (tp->t_session == NULL)
+ error = -ENOTTY;
+ else
+ *(int *)data = tp->t_session->sid;
+ tty_unlock(tp);
+ return (error);
+#ifdef USING_BSD_IOCTL_EXT
+ case TIOCPTMASTER:
+ /* Yes, we are a pseudo-terminal master. */
+ return (0);
+#endif /* USING_BSD_IOCTL_EXT */
+ case TIOCSIG:
+ /* Signal the foreground process group. */
+ sig = *(int *)data;
+ if (sig < 1 || sig >= _LWP_NSIG)
+ return -EINVAL;
+
+ tty_lock(tp);
+ lwp_tty_signal_pgrp(tp, sig);
+ tty_unlock(tp);
+ return (0);
+ case TIOCPKT:
+ /* Enable/disable packet mode. */
+ tty_lock(tp);
+ if (*(int *)data)
+ psc->pts_flags |= PTS_PKT;
+ else
+ psc->pts_flags &= ~PTS_PKT;
+ tty_unlock(tp);
+ return (0);
+ }
+
+ /* Just redirect this ioctl to the slave device. */
+ tty_lock(tp);
+ error = tty_ioctl(tp, cmd, data, fflags, td);
+ tty_unlock(tp);
+ if (error == -ENOIOCTL)
+ error = -ENOTTY;
+
+ return error;
+}
+
+static int ptsdev_poll(struct lwp_tty *tp, struct rt_pollreq *req,
+ struct ucred *active_cred, struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int revents = 0;
+ int events = req->_key;
+
+ tty_lock(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ /* Slave device is not opened. */
+ tty_unlock(tp);
+ return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
+ }
+
+ if (events & (POLLIN | POLLRDNORM))
+ {
+ /* See if we can getc something. */
+ if (ttydisc_getc_poll(tp) || (psc->pts_flags & PTS_PKT && psc->pts_pkt))
+ revents |= events & (POLLIN | POLLRDNORM);
+ }
+ if (events & (POLLOUT | POLLWRNORM))
+ {
+ /* See if we can rint something. */
+ if (ttydisc_rint_poll(tp))
+ revents |= events & (POLLOUT | POLLWRNORM);
+ }
+
+ /*
+ * No need to check for POLLHUP here. This device cannot be used
+ * as a callout device, which means we always have a carrier,
+ * because the master is.
+ */
+
+ if (revents == 0)
+ {
+ /*
+ * This code might look misleading, but the naming of
+ * poll events on this side is the opposite of the slave
+ * device.
+ */
+ if (events & (POLLIN | POLLRDNORM))
+ rt_poll_add(&psc->pts_outpoll, req);
+ if (events & (POLLOUT | POLLWRNORM))
+ rt_poll_add(&psc->pts_inpoll, req);
+ }
+
+ tty_unlock(tp);
+
+ return (revents);
+}
+
+#if USING_BSD_KQUEUE
+/*
+ * kqueue support.
+ */
+
+static void pts_kqops_read_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
+}
+
+static int pts_kqops_read_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ }
+ else
+ {
+ kn->kn_data = ttydisc_getc_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static void pts_kqops_write_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
+}
+
+static int pts_kqops_write_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ }
+ else
+ {
+ kn->kn_data = ttydisc_rint_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static struct filterops pts_kqops_read = {
+ .f_isfd = 1,
+ .f_detach = pts_kqops_read_detach,
+ .f_event = pts_kqops_read_event,
+};
+static struct filterops pts_kqops_write = {
+ .f_isfd = 1,
+ .f_detach = pts_kqops_write_detach,
+ .f_event = pts_kqops_write_event,
+};
+
+static int ptsdev_kqfilter(struct file *fp, struct knote *kn)
+{
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0;
+
+ tty_lock(tp);
+
+ switch (kn->kn_filter)
+ {
+ case EVFILT_READ:
+ kn->kn_fop = &pts_kqops_read;
+ knlist_add(&psc->pts_outpoll.si_note, kn, 1);
+ break;
+ case EVFILT_WRITE:
+ kn->kn_fop = &pts_kqops_write;
+ knlist_add(&psc->pts_inpoll.si_note, kn, 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ tty_unlock(tp);
+ return (error);
+}
+#endif
+
+#if USING_BSD_STAT
+static int ptsdev_stat(struct file *fp, struct stat *sb,
+ struct ucred *active_cred)
+{
+ struct lwp_tty *tp = fp->f_data;
+#ifdef PTS_EXTERNAL
+ struct pts_softc *psc = tty_softc(tp);
+#endif /* PTS_EXTERNAL */
+ struct cdev *dev = tp->t_dev;
+
+ /*
+ * According to POSIX, we must implement an fstat(). This also
+ * makes this implementation compatible with Linux binaries,
+ * because Linux calls fstat() on the pseudo-terminal master to
+ * obtain st_rdev.
+ *
+ * XXX: POSIX also mentions we must fill in st_dev, but how?
+ */
+
+ bzero(sb, sizeof *sb);
+#ifdef PTS_EXTERNAL
+ if (psc->pts_cdev != NULL)
+ sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
+ else
+#endif /* PTS_EXTERNAL */
+ sb->st_ino = sb->st_rdev = tty_udev(tp);
+
+ sb->st_atim = dev->si_atime;
+ sb->st_ctim = dev->si_ctime;
+ sb->st_mtim = dev->si_mtime;
+ sb->st_uid = dev->si_uid;
+ sb->st_gid = dev->si_gid;
+ sb->st_mode = dev->si_mode | S_IFCHR;
+
+ return (0);
+}
+#endif
+
+static int ptsdev_close(struct lwp_tty *tp, struct rt_thread *td)
+{
+ /* Deallocate TTY device. */
+ tty_lock(tp);
+ tty_rel_gone(tp);
+
+#ifdef USING_BSD_VNODE
+ /* TODO: consider the vnode operation on DFS */
+
+ /*
+ * Open of /dev/ptmx or /dev/ptyXX changes the type of file
+ * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
+ * use count, we need to decrement it, and possibly do other
+ * required cleanup.
+ */
+ if (fp->f_vnode != NULL)
+ return (vnops.fo_close(fp, td));
+#endif /* USING_BSD_VNODE */
+
+ return 0;
+}
+
+#ifdef USING_BSD_KINFO
+static int ptsdev_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+ struct lwp_tty *tp;
+
+ kif->kf_type = KF_TYPE_PTS;
+ tp = fp->f_data;
+ kif->kf_un.kf_pts.kf_pts_dev = tty_udev(tp);
+ kif->kf_un.kf_pts.kf_pts_dev_freebsd11 =
+ kif->kf_un.kf_pts.kf_pts_dev; /* truncate */
+ strlcpy(kif->kf_path, tty_devname(tp), sizeof(kif->kf_path));
+ return (0);
+}
+#endif
+
+struct bsd_fileops bsd_ptsdev_methods = {
+ .fo_read = ptsdev_read,
+ .fo_write = ptsdev_write,
+ // .fo_truncate = invfo_truncate,
+ .fo_ioctl = ptsdev_ioctl,
+ .fo_poll = ptsdev_poll,
+ // .fo_kqfilter = ptsdev_kqfilter,
+ // .fo_stat = ptsdev_stat,
+ .fo_close = ptsdev_close,
+ // .fo_chmod = invfo_chmod,
+ // .fo_chown = invfo_chown,
+ // .fo_sendfile = invfo_sendfile,
+ // .fo_fill_kinfo = ptsdev_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+/*
+ * Driver-side hooks.
+ */
+
+static void ptsdrv_outwakeup(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ cv_broadcast(&psc->pts_outwait);
+ rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLIN);
+}
+
+static void ptsdrv_inwakeup(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ cv_broadcast(&psc->pts_inwait);
+ rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLOUT);
+}
+
+static int ptsdrv_open(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /* for ioctl(TIOCSPTLCK) */
+ if (psc->pts_flags & PTS_PTLOCKED)
+ return -EIO;
+
+ psc->pts_flags &= ~PTS_FINISHED;
+
+ return 0;
+}
+
+static void ptsdrv_close(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /* Wake up any blocked readers/writers. */
+ psc->pts_flags |= PTS_FINISHED;
+ ptsdrv_outwakeup(tp);
+ ptsdrv_inwakeup(tp);
+}
+
+static void ptsdrv_pktnotify(struct lwp_tty *tp, char event)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /*
+ * Clear conflicting flags.
+ */
+
+ switch (event)
+ {
+ case TIOCPKT_STOP:
+ psc->pts_pkt &= ~TIOCPKT_START;
+ break;
+ case TIOCPKT_START:
+ psc->pts_pkt &= ~TIOCPKT_STOP;
+ break;
+ case TIOCPKT_NOSTOP:
+ psc->pts_pkt &= ~TIOCPKT_DOSTOP;
+ break;
+ case TIOCPKT_DOSTOP:
+ psc->pts_pkt &= ~TIOCPKT_NOSTOP;
+ break;
+ }
+
+ psc->pts_pkt |= event;
+
+ /**
+ * Note: on smart, we don't wakeup master until it's willing to accept
+ * packet event. Because on poll, we setup POLLIN for PTS_PKT only. So There
+ * is a chance when we wakeup ipc but we can't wakeup user again. Since
+ * current wakeup will remove the wakequeue node on the meanwhile
+ */
+ if (psc->pts_flags & PTS_PKT)
+ ptsdrv_outwakeup(tp);
+}
+
+static void ptsdrv_free(void *softc)
+{
+ struct pts_softc *psc = softc;
+
+ /* Make device number available again. */
+ if (psc->pts_unit >= 0)
+ ptyfs_unregister_pts(psc->pts_master, psc->pts_unit);
+
+#ifdef USING_BSD_UCRED
+ chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
+ racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
+ crfree(psc->pts_cred);
+#endif
+
+ rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLHUP);
+ rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLHUP);
+
+ rt_free(psc);
+}
+
+static struct lwp_ttydevsw pts_class = {
+ .tsw_flags = TF_NOPREFIX,
+ .tsw_outwakeup = ptsdrv_outwakeup,
+ .tsw_inwakeup = ptsdrv_inwakeup,
+ .tsw_open = ptsdrv_open,
+ .tsw_close = ptsdrv_close,
+ .tsw_pktnotify = ptsdrv_pktnotify,
+ .tsw_free = ptsdrv_free,
+};
+
+int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file)
+{
+ int unit;
+ struct lwp_tty *tp;
+ struct pts_softc *psc;
+ char name_buf[DIRENT_NAME_MAX];
+ const char *rootpath;
+ rt_device_t ptmx_device = ptm_file->vnode->data;
+
+#ifdef USING_BSD_UCRED
+ struct rt_lwp *p = td->lwp;
+ int ok, error;
+ struct ucred *cred = td->td_ucred;
+#endif
+
+ /* Resource limiting. */
+#ifdef USING_BSD_UCRED
+ LWP_LOCK(p);
+ error = racct_add(p, RACCT_NPTS, 1);
+ if (error != 0)
+ {
+ LWP_UNLOCK(p);
+ return -EAGAIN;
+ }
+ ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
+ if (!ok)
+ {
+ racct_sub(p, RACCT_NPTS, 1);
+ LWP_UNLOCK(p);
+ return -EAGAIN;
+ }
+ LWP_UNLOCK(p);
+#endif
+
+ /* Allocate TTY and softc. */
+ psc = rt_calloc(1, sizeof(struct pts_softc));
+ cv_init(&psc->pts_inwait, "ptsin");
+ cv_init(&psc->pts_outwait, "ptsout");
+ rt_wqueue_init(&psc->pts_inpoll);
+ rt_wqueue_init(&psc->pts_outpoll);
+
+ psc->pts_master = ptmx_device;
+#ifdef USING_BSD_UCRED
+ psc->pts_cred = crhold(cred);
+#else
+ psc->pts_cred = 0;
+#endif
+
+ tp = lwp_tty_create(&pts_class, psc);
+ if (!tp)
+ {
+ rt_free(psc);
+ rt_condvar_detach(&psc->pts_inwait);
+ rt_condvar_detach(&psc->pts_outwait);
+ return -ENOMEM;
+ }
+
+ /* Try to allocate a new pts uint*/
+ unit = ptyfs_register_pts(ptmx_device, &tp->parent);
+ if (unit < 0)
+ {
+#ifdef USING_BSD_UCRED
+ racct_sub(p, RACCT_NPTS, 1);
+ chgptscnt(cred->cr_ruidinfo, -1, 0);
+#endif
+ lwp_tty_delete(tp);
+ return -EAGAIN;
+ }
+ psc->pts_unit = unit;
+
+ /* Expose the slave device as well. */
+#ifdef USING_BSD_UCRED
+ tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
+#else
+ rootpath = ptyfs_get_rootpath(ptmx_device);
+ RT_ASSERT(rootpath[strlen(rootpath) - 1] != '/');
+ snprintf(name_buf, DIRENT_NAME_MAX, "%s/%u", rootpath, psc->pts_unit);
+
+ /* setup the pts */
+ lwp_tty_register(tp, name_buf);
+
+ /* now this file operating on new pty */
+ ptm_file->vnode->data = tp;
+#endif
+
+ return 0;
+}
+
+void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ if (is_lock)
+ psc->pts_flags |= PTS_PTLOCKED;
+ else
+ psc->pts_flags &= ~PTS_PTLOCKED;
+}
+
+rt_bool_t pts_is_locked(lwp_tty_t pts)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ return !!(psc->pts_flags & PTS_PTLOCKED);
+}
+
+int pts_get_pktmode(lwp_tty_t pts)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ return !!(psc->pts_flags & PTS_PKT);
+}
diff --git a/components/lwp/terminal/freebsd/tty_ttydisc.c b/components/lwp/terminal/freebsd/tty_ttydisc.c
new file mode 100644
index 0000000000..932ef2ec00
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_ttydisc.c
@@ -0,0 +1,1479 @@
+/*
+ * 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 "../tty_internal.h"
+#include "../terminal.h"
+
+#include <rtdef.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * 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.
+ */
+
+/*
+ * Standard TTYDISC `termios' line discipline.
+ */
+
+/* Statistics. */
+static rt_atomic_t tty_nin = 0;
+#ifdef USING_BSD_SYSCTL
+SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tty_nin, 0,
+ "Total amount of bytes received");
+#endif
+
+static rt_atomic_t tty_nout = 0;
+#ifdef USING_BSD_SYSCTL
+SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tty_nout, 0,
+ "Total amount of bytes transmitted");
+#endif
+
+/* termios comparison macro's. */
+#define CMP_CC(v, c) \
+ (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && tp->t_termios.c_cc[v] == (c))
+#define CMP_FLAG(field, opt) (tp->t_termios.c_##field##flag & (opt))
+
+/* Characters that cannot be modified through c_cc. */
+#define CTAB '\t'
+#define CNL '\n'
+#define CCR '\r'
+
+/* Character is a control character. */
+#define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20)
+/* Control character should be processed on echo. */
+#define CTL_ECHO(c, q) \
+ (!(q) && ((c) == CERASE2 || (c) == CTAB || (c) == CNL || (c) == CCR))
+/* Control character should be printed using ^X notation. */
+#define CTL_PRINT(c, q) \
+ ((c) == 0x7f || \
+ ((unsigned char)(c) < 0x20 && ((q) || ((c) != CTAB && (c) != CNL))))
+/* Character is whitespace. */
+#define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB)
+/* Character is alphanumeric. */
+#define CTL_ALNUM(c) \
+ (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= 'A' && (c) <= 'Z'))
+/* Character is UTF8-encoded. */
+#define CTL_UTF8(c) (!!((c)&0x80))
+/* Character is a UTF8 continuation byte. */
+#define CTL_UTF8_CONT(c) (((c)&0xc0) == 0x80)
+
+#define TTY_STACKBUF 256
+#define UTF8_STACKBUF 4
+
+void ttydisc_open(struct lwp_tty *tp)
+{
+ ttydisc_optimize(tp);
+}
+
+void ttydisc_close(struct lwp_tty *tp)
+{
+ /* Clean up our flags when leaving the discipline. */
+ tp->t_flags &= ~(TF_STOPPED | TF_HIWAT | TF_ZOMBIE);
+ tp->t_termios.c_lflag &= ~FLUSHO;
+
+ /*
+ * POSIX states that we must drain output and flush input on
+ * last close. Draining has already been done if possible.
+ */
+ tty_flush(tp, FREAD | FWRITE);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, close))
+ ttyhook_close(tp);
+#endif
+}
+
+static int ttydisc_read_canonical(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ char breakc[4] = {CNL}; /* enough to hold \n, VEOF and VEOL. */
+ int error;
+ size_t clen, flen = 0, n = 1;
+ unsigned char lastc = _POSIX_VDISABLE;
+
+#define BREAK_ADD(c) \
+ do \
+ { \
+ if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \
+ breakc[n++] = tp->t_termios.c_cc[c]; \
+ } while (0)
+ /* Determine which characters we should trigger on. */
+ BREAK_ADD(VEOF);
+ BREAK_ADD(VEOL);
+#undef BREAK_ADD
+ breakc[n] = '\0';
+
+ do
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ /*
+ * Quite a tricky case: unlike the old TTY
+ * implementation, this implementation copies data back
+ * to userspace in large chunks. Unfortunately, we can't
+ * calculate the line length on beforehand if it crosses
+ * ttyinq_block boundaries, because multiple reads could
+ * then make this code read beyond the newline.
+ *
+ * This is why we limit the read to:
+ * - The size the user has requested
+ * - The blocksize (done in tty_inq.c)
+ * - The amount of bytes until the newline
+ *
+ * This causes the line length to be recalculated after
+ * each block has been copied to userspace. This will
+ * cause the TTY layer to return data in chunks using
+ * the blocksize (except the first and last blocks).
+ */
+ clen =
+ ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, (char *)&lastc);
+
+ /* No more data. */
+ if (clen == 0)
+ {
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ continue;
+ }
+
+ /* Don't send the EOF char back to userspace. */
+ if (CMP_CC(VEOF, lastc))
+ flen = 1;
+
+ MPASS(flen <= clen);
+
+ /* Read and throw away the EOF character. */
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen);
+ if (error)
+ return error;
+
+ /** Notes: proceed when read buf frees and not reaching breaks */
+ } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE);
+
+ return 0;
+}
+
+static int ttydisc_read_raw_no_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ size_t vmin = tp->t_termios.c_cc[VMIN];
+ ssize_t oresid = uio->uio_resid;
+ int error;
+
+ MPASS(tp->t_termios.c_cc[VTIME] == 0);
+
+ /*
+ * This routine implements the easy cases of read()s while in
+ * non-canonical mode, namely case B and D, where we don't have
+ * any timers at all.
+ */
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /* We have to wait for more. */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ }
+}
+
+static int ttydisc_read_raw_read_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag, int oresid)
+{
+ size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1);
+ unsigned int vtime = tp->t_termios.c_cc[VTIME];
+ struct timeval end, now, left;
+ int error, hz;
+
+ MPASS(tp->t_termios.c_cc[VTIME] != 0);
+
+ /* Determine when the read should be expired. */
+ end.tv_sec = vtime / 10;
+ end.tv_usec = (vtime % 10) * 100000;
+ getmicrotime(&now);
+ timevaladd(&end, &now);
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /* Calculate how long we should wait. */
+ getmicrotime(&now);
+ if (timevalcmp(&now, &end, >))
+ return 0;
+ left = end;
+ timevalsub(&left, &now);
+ hz = tvtohz(&left);
+
+ /*
+ * We have to wait for more. If the timer expires, we
+ * should return a 0-byte read.
+ */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_timedwait(tp, &tp->t_inwait, hz);
+ if (error)
+ return (error == -EWOULDBLOCK ? 0 : error);
+ }
+
+ return 0;
+}
+
+static int ttydisc_read_raw_interbyte_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ size_t vmin = tp->t_termios.c_cc[VMIN];
+ ssize_t oresid = uio->uio_resid;
+ int error;
+
+ MPASS(tp->t_termios.c_cc[VMIN] != 0);
+ MPASS(tp->t_termios.c_cc[VTIME] != 0);
+
+ /*
+ * When using the interbyte timer, the timer should be started
+ * after the first byte has been received. We just call into the
+ * generic read timer code after we've received the first byte.
+ */
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /*
+ * Not enough data, but we did receive some, which means
+ * we'll now start using the interbyte timer.
+ */
+ if (oresid != uio->uio_resid)
+ break;
+
+ /* We have to wait for more. */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ }
+
+ return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid);
+}
+
+int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+ int error;
+
+ tty_assert_locked(tp);
+
+ if (uio->uio_resid == 0)
+ return 0;
+
+ if (CMP_FLAG(l, ICANON))
+ error = ttydisc_read_canonical(tp, uio, ioflag);
+ else if (tp->t_termios.c_cc[VTIME] == 0)
+ error = ttydisc_read_raw_no_timer(tp, uio, ioflag);
+ else if (tp->t_termios.c_cc[VMIN] == 0)
+ error = ttydisc_read_raw_read_timer(tp, uio, ioflag, uio->uio_resid);
+ else
+ error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag);
+
+ if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow ||
+ ttyinq_bytescanonicalized(&tp->t_inq) == 0)
+ {
+ /* Unset the input watermark when we've got enough space. */
+ tty_hiwat_in_unblock(tp);
+ }
+
+ return error;
+}
+
+/* return the offset to special character from string start */
+rt_inline unsigned int ttydisc_findchar(const char *obstart, unsigned int oblen)
+{
+ const char *c = obstart;
+
+ while (oblen--)
+ {
+ if (CTL_VALID(*c))
+ break;
+ c++;
+ }
+
+ return (c - obstart);
+}
+
+static int ttydisc_write_oproc(struct lwp_tty *tp, char c)
+{
+ unsigned int scnt, error;
+
+ MPASS(CMP_FLAG(o, OPOST));
+ MPASS(CTL_VALID(c));
+
+#define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1)
+ switch (c)
+ {
+ case CEOF:
+ /* End-of-text dropping. */
+ if (CMP_FLAG(o, ONOEOT))
+ return 0;
+ return PRINT_NORMAL();
+
+ case CERASE2:
+ /* Handle backspace to fix tab expansion. */
+ if (PRINT_NORMAL() != 0)
+ return (-1);
+ if (tp->t_column > 0)
+ tp->t_column--;
+ return 0;
+
+ case CTAB:
+ /* Tab expansion. */
+ scnt = 8 - (tp->t_column & 7);
+ if (CMP_FLAG(o, TAB3))
+ {
+ error = ttyoutq_write_nofrag(&tp->t_outq, " ", scnt);
+ }
+ else
+ {
+ error = PRINT_NORMAL();
+ }
+ if (error)
+ return (-1);
+
+ tp->t_column += scnt;
+ MPASS((tp->t_column % 8) == 0);
+ return 0;
+
+ case CNL:
+ /* Newline conversion. */
+ if (CMP_FLAG(o, ONLCR))
+ {
+ /* Convert \n to \r\n. */
+ error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2);
+ }
+ else
+ {
+ error = PRINT_NORMAL();
+ }
+ if (error)
+ return (-1);
+
+ if (CMP_FLAG(o, ONLCR | ONLRET))
+ {
+ tp->t_column = tp->t_writepos = 0;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ }
+ return 0;
+
+ case CCR:
+ /* Carriage return to newline conversion. */
+ if (CMP_FLAG(o, OCRNL))
+ c = CNL;
+ /* Omit carriage returns on column 0. */
+ if (CMP_FLAG(o, ONOCR) && tp->t_column == 0)
+ return 0;
+ if (PRINT_NORMAL() != 0)
+ return (-1);
+
+ tp->t_column = tp->t_writepos = 0;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ return 0;
+ }
+
+ /*
+ * Invisible control character. Print it, but don't
+ * increase the column count.
+ */
+ return PRINT_NORMAL();
+#undef PRINT_NORMAL
+}
+
+/*
+ * Just like the old TTY implementation, we need to copy data in chunks
+ * into a temporary buffer. One of the reasons why we need to do this,
+ * is because output processing (only TAB3 though) may allow the buffer
+ * to grow eight times.
+ */
+int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+ char ob[TTY_STACKBUF];
+ char *obstart;
+ int error = 0;
+ unsigned int oblen = 0;
+
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_ZOMBIE)
+ return -EIO;
+
+ /*
+ * We don't need to check whether the process is the foreground
+ * process group or if we have a carrier. This is already done
+ * in ttydev_write().
+ */
+
+ while (uio->uio_resid > 0)
+ {
+ unsigned int nlen;
+
+ MPASS(oblen == 0);
+
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ uio->uio_offset += uio->uio_resid;
+ uio->uio_resid = 0;
+ return 0;
+ }
+
+ /* Step 1: read data. */
+ obstart = ob;
+ nlen = MIN(uio->uio_resid, sizeof ob);
+ tty_unlock(tp);
+ error = uiomove(ob, nlen, uio);
+ tty_lock(tp);
+ if (error != 0)
+ break;
+ oblen = nlen;
+
+ if (tty_gone(tp))
+ {
+ error = -ENXIO;
+ break;
+ }
+
+ MPASS(oblen > 0);
+
+ /* Step 2: process data. */
+ do
+ {
+ unsigned int plen, wlen;
+
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ uio->uio_offset += uio->uio_resid;
+ uio->uio_resid = 0;
+ return 0;
+ }
+
+ /* Search for special characters for post processing. */
+ if (CMP_FLAG(o, OPOST))
+ {
+ plen = ttydisc_findchar(obstart, oblen);
+ }
+ else
+ {
+ plen = oblen;
+ }
+
+ if (plen == 0)
+ {
+ /*
+ * We're going to process a character
+ * that needs processing
+ */
+ if (ttydisc_write_oproc(tp, *obstart) == 0)
+ {
+ obstart++;
+ oblen--;
+
+ tp->t_writepos = tp->t_column;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ continue;
+ }
+ }
+ else
+ {
+ /* We're going to write regular data. */
+ wlen = ttyoutq_write(&tp->t_outq, obstart, plen);
+ obstart += wlen;
+ oblen -= wlen;
+ tp->t_column += wlen;
+
+ tp->t_writepos = tp->t_column;
+ ttyinq_reprintpos_set(&tp->t_inq);
+
+ if (wlen == plen)
+ continue;
+ }
+
+ /* Watermark reached. Try to sleep. */
+ tp->t_flags |= TF_HIWAT_OUT;
+
+ if (ioflag & IO_NDELAY)
+ {
+ error = EWOULDBLOCK;
+ goto done;
+ }
+
+ /*
+ * The driver may write back the data
+ * synchronously. Be sure to check the high
+ * water mark before going to sleep.
+ */
+ ttydevsw_outwakeup(tp);
+ if ((tp->t_flags & TF_HIWAT_OUT) == 0)
+ continue;
+
+ error = tty_wait(tp, &tp->t_outwait);
+ if (error)
+ goto done;
+
+ if (tp->t_flags & TF_ZOMBIE)
+ {
+ error = EIO;
+ goto done;
+ }
+ } while (oblen > 0);
+ }
+
+done:
+ if (!tty_gone(tp))
+ ttydevsw_outwakeup(tp);
+
+ /*
+ * Add the amount of bytes that we didn't process back to the
+ * uio counters. We need to do this to make sure write() doesn't
+ * count the bytes we didn't store in the queue.
+ */
+ uio->uio_resid += oblen;
+ return error;
+}
+
+/* set optimize flags if possible */
+void ttydisc_optimize(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ if (ttyhook_hashook(tp, rint_bypass))
+ {
+ tp->t_flags |= TF_BYPASS;
+ }
+ else if (ttyhook_hashook(tp, rint))
+ {
+ tp->t_flags &= ~TF_BYPASS;
+ }
+ else if (!CMP_FLAG(i, ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON) &&
+ (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) &&
+ (!CMP_FLAG(i, PARMRK) ||
+ CMP_FLAG(i, IGNPAR | IGNBRK) == (IGNPAR | IGNBRK)) &&
+ !CMP_FLAG(l, ECHO | ICANON | IEXTEN | ISIG | PENDIN))
+ {
+ tp->t_flags |= TF_BYPASS;
+ }
+ else
+ {
+ tp->t_flags &= ~TF_BYPASS;
+ }
+}
+
+void ttydisc_modem(struct lwp_tty *tp, int open)
+{
+ tty_assert_locked(tp);
+
+ if (open)
+ cv_broadcast(&tp->t_dcdwait);
+
+ /*
+ * Ignore modem status lines when CLOCAL is turned on, but don't
+ * enter the zombie state when the TTY isn't opened, because
+ * that would cause the TTY to be in zombie state after being
+ * opened.
+ */
+ if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL))
+ return;
+
+ if (open == 0)
+ {
+ /*
+ * Lost carrier.
+ */
+ tp->t_flags |= TF_ZOMBIE;
+
+ lwp_tty_signal_sessleader(tp, SIGHUP);
+ tty_flush(tp, FREAD | FWRITE);
+ }
+ else
+ {
+ /*
+ * Carrier is back again.
+ */
+
+ /* XXX: what should we do here? */
+ }
+}
+
+static int ttydisc_echo_force(struct lwp_tty *tp, char c, int quote)
+{
+ if (CMP_FLAG(l, FLUSHO))
+ return 0;
+
+ if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote))
+ {
+ /*
+ * Only perform postprocessing when OPOST is turned on
+ * and the character is an unquoted BS/TB/NL/CR.
+ */
+ return ttydisc_write_oproc(tp, c);
+ }
+ else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote))
+ {
+ /*
+ * Only use ^X notation when ECHOCTL is turned on and
+ * we've got an quoted control character.
+ *
+ * Print backspaces when echoing an end-of-file.
+ */
+ char ob[4] = "^?\b\b";
+
+ /* Print ^X notation. */
+ if (c != 0x7f)
+ ob[1] = c + 'A' - 1;
+
+ if (!quote && CMP_CC(VEOF, c))
+ {
+ return ttyoutq_write_nofrag(&tp->t_outq, ob, 4);
+ }
+ else
+ {
+ tp->t_column += 2;
+ return ttyoutq_write_nofrag(&tp->t_outq, ob, 2);
+ }
+ }
+ else
+ {
+ /* Can just be printed. */
+ tp->t_column++;
+ return ttyoutq_write_nofrag(&tp->t_outq, &c, 1);
+ }
+}
+
+static int ttydisc_echo(struct lwp_tty *tp, char c, int quote)
+{
+ /*
+ * Only echo characters when ECHO is turned on, or ECHONL when
+ * the character is an unquoted newline.
+ */
+ if (!CMP_FLAG(l, ECHO) && (!CMP_FLAG(l, ECHONL) || c != CNL || quote))
+ return 0;
+
+ return ttydisc_echo_force(tp, c, quote);
+}
+
+static void ttydisc_reprint_char(void *d, char c, int quote)
+{
+ struct lwp_tty *tp = d;
+
+ ttydisc_echo(tp, c, quote);
+}
+
+static void ttydisc_reprint(struct lwp_tty *tp)
+{
+ cc_t c;
+
+ /* Print ^R\n, followed by the line. */
+ c = tp->t_termios.c_cc[VREPRINT];
+ if (c != _POSIX_VDISABLE)
+ ttydisc_echo(tp, c, 0);
+ ttydisc_echo(tp, CNL, 0);
+ ttyinq_reprintpos_reset(&tp->t_inq);
+
+ ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp);
+}
+
+struct ttydisc_recalc_length
+{
+ struct lwp_tty *tp;
+ unsigned int curlen;
+};
+
+static void ttydisc_recalc_charlength(void *d, char c, int quote)
+{
+ struct ttydisc_recalc_length *data = d;
+ struct lwp_tty *tp = data->tp;
+
+ if (CTL_PRINT(c, quote))
+ {
+ if (CMP_FLAG(l, ECHOCTL))
+ data->curlen += 2;
+ }
+ else if (c == CTAB)
+ {
+ data->curlen += 8 - (data->curlen & 7);
+ }
+ else
+ {
+ data->curlen++;
+ }
+}
+
+static unsigned int ttydisc_recalc_linelength(struct lwp_tty *tp)
+{
+ struct ttydisc_recalc_length data = {tp, tp->t_writepos};
+
+ ttyinq_line_iterate_from_reprintpos(&tp->t_inq, ttydisc_recalc_charlength,
+ &data);
+ return (data.curlen);
+}
+
+static int ttydisc_rubchar(struct lwp_tty *tp)
+{
+ char c;
+ int quote;
+ unsigned int prevpos, tablen;
+
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return (-1);
+ ttyinq_unputchar(&tp->t_inq);
+
+ if (CMP_FLAG(l, ECHO))
+ {
+ /*
+ * Remove the character from the screen. This is even
+ * safe for characters that span multiple characters
+ * (tabs, quoted, etc).
+ */
+ if (tp->t_writepos >= tp->t_column)
+ {
+ /* Retype the sentence. */
+ ttydisc_reprint(tp);
+ }
+ else if (CMP_FLAG(l, ECHOE))
+ {
+ if (CTL_PRINT(c, quote))
+ {
+ /* Remove ^X formatted chars. */
+ if (CMP_FLAG(l, ECHOCTL))
+ {
+ tp->t_column -= 2;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
+ }
+ }
+ else if (c == ' ')
+ {
+ /* Space character needs no rubbing. */
+ tp->t_column -= 1;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b", 1);
+ }
+ else if (c == CTAB)
+ {
+ /*
+ * Making backspace work with tabs is
+ * quite hard. Recalculate the length of
+ * this character and remove it.
+ *
+ * Because terminal settings could be
+ * changed while the line is being
+ * inserted, the calculations don't have
+ * to be correct. Make sure we keep the
+ * tab length within proper bounds.
+ */
+ prevpos = ttydisc_recalc_linelength(tp);
+ if (prevpos >= tp->t_column)
+ tablen = 1;
+ else
+ tablen = tp->t_column - prevpos;
+ if (tablen > 8)
+ tablen = 8;
+
+ tp->t_column = prevpos;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b\b\b\b\b\b\b", tablen);
+ return 0;
+ }
+#ifdef USING_BSD_UTF8
+ else if ((tp->t_termios.c_iflag & IUTF8) != 0 && CTL_UTF8(c))
+ {
+ uint8_t bytes[UTF8_STACKBUF] = {0};
+ int curidx = UTF8_STACKBUF - 1, cwidth = 1, nb = 0;
+ teken_char_t codepoint;
+
+ /* Save current byte. */
+ bytes[curidx] = c;
+ curidx--;
+ nb++;
+ /* Loop back through inq until we hit the
+ * leading byte. */
+ while (CTL_UTF8_CONT(c) && nb < UTF8_STACKBUF)
+ {
+ /*
+ * Check if we've reached the beginning
+ * of the line.
+ */
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ break;
+ ttyinq_unputchar(&tp->t_inq);
+ bytes[curidx] = c;
+ curidx--;
+ nb++;
+ }
+ /*
+ * Shift array so that the leading
+ * byte ends up at idx 0.
+ */
+ if (nb < UTF8_STACKBUF)
+ memmove(&bytes[0], &bytes[curidx + 1],
+ nb * sizeof(uint8_t));
+ /* Check for malformed UTF8 characters. */
+ if (nb == UTF8_STACKBUF && CTL_UTF8_CONT(bytes[0]))
+ {
+ /*
+ * Place all bytes back into the inq and
+ * delete the last byte only.
+ */
+ ttyinq_write(&tp->t_inq, bytes, UTF8_STACKBUF, 0);
+ ttyinq_unputchar(&tp->t_inq);
+ }
+ else
+ {
+ /* Find codepoint and width. */
+ codepoint = teken_utf8_bytes_to_codepoint(bytes, nb);
+ if (codepoint == TEKEN_UTF8_INVALID_CODEPOINT ||
+ (cwidth = teken_wcwidth(codepoint)) == -1)
+ {
+ /*
+ * Place all bytes back into the
+ * inq and fall back to
+ * default behaviour.
+ */
+ cwidth = 1;
+ ttyinq_write(&tp->t_inq, bytes, nb, 0);
+ ttyinq_unputchar(&tp->t_inq);
+ }
+ }
+ tp->t_column -= cwidth;
+ /*
+ * Delete character by punching
+ * 'cwidth' spaces over it.
+ */
+ if (cwidth == 1)
+ ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
+ else if (cwidth == 2)
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
+ }
+#endif
+ else
+ {
+ /*
+ * Remove a regular character by
+ * punching a space over it.
+ */
+ tp->t_column -= 1;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
+ }
+ }
+ else
+ {
+ /* Don't print spaces. */
+ ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0);
+ }
+ }
+
+ return 0;
+}
+
+static void ttydisc_rubword(struct lwp_tty *tp)
+{
+ char c;
+ int quote;
+#ifdef ALTWERASE
+ int alnum;
+#endif
+
+ /* Strip whitespace first. */
+ for (;;)
+ {
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return;
+ if (!CTL_WHITE(c))
+ break;
+ ttydisc_rubchar(tp);
+ }
+
+ /*
+ * Record whether the last character from the previous iteration
+ * was alphanumeric or not. We need this to implement ALTWERASE.
+ */
+#ifdef ALTWERASE
+ alnum = CTL_ALNUM(c);
+#endif
+ for (;;)
+ {
+ ttydisc_rubchar(tp);
+
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return;
+ if (CTL_WHITE(c))
+ return;
+#ifdef ALTWERASE
+ if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum)
+ return;
+#endif
+ }
+}
+
+int ttydisc_rint(struct lwp_tty *tp, char c, int flags)
+{
+ int signal, quote = 0;
+ char ob[3] = {0xff, 0x00};
+ size_t ol;
+
+ tty_assert_locked(tp);
+
+ rt_atomic_add(&tty_nin, 1);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint))
+ return ttyhook_rint(tp, c, flags);
+#endif
+
+ if (tp->t_flags & TF_BYPASS)
+ goto processed;
+
+ if (flags)
+ {
+ if (flags & TRE_BREAK)
+ {
+ if (CMP_FLAG(i, IGNBRK))
+ {
+ /* Ignore break characters. */
+ return 0;
+ }
+ else if (CMP_FLAG(i, BRKINT))
+ {
+ /* Generate SIGINT on break. */
+ tty_flush(tp, FREAD | FWRITE);
+ lwp_tty_signal_pgrp(tp, SIGINT);
+ return 0;
+ }
+ else
+ {
+ /* Just print it. */
+ goto parmrk;
+ }
+ }
+ else if (flags & TRE_FRAMING ||
+ (flags & TRE_PARITY && CMP_FLAG(i, INPCK)))
+ {
+ if (CMP_FLAG(i, IGNPAR))
+ {
+ /* Ignore bad characters. */
+ return 0;
+ }
+ else
+ {
+ /* Just print it. */
+ goto parmrk;
+ }
+ }
+ }
+
+ /* Allow any character to perform a wakeup. */
+ if (CMP_FLAG(i, IXANY))
+ {
+ tp->t_flags &= ~TF_STOPPED;
+ tp->t_termios.c_lflag &= ~FLUSHO; /* FLUSHO: Output is being flushed */
+ }
+
+ /* Remove the top bit. */
+ if (CMP_FLAG(i, ISTRIP))
+ c &= ~0x80;
+
+ /* Skip input processing when we want to print it literally. */
+ if (tp->t_flags & TF_LITERAL)
+ {
+ tp->t_flags &= ~TF_LITERAL;
+ quote = 1;
+ goto processed;
+ }
+
+ /* Special control characters that are implementation dependent. */
+ if (CMP_FLAG(l, IEXTEN))
+ {
+ /* Accept the next character as literal. */
+ if (CMP_CC(VLNEXT, c))
+ {
+ if (CMP_FLAG(l, ECHO))
+ {
+ if (CMP_FLAG(l, ECHOE))
+ ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2);
+ else
+ ttydisc_echo(tp, c, 0);
+ }
+ tp->t_flags |= TF_LITERAL;
+ return 0;
+ }
+ /* Discard processing */
+ if (CMP_CC(VDISCARD, c))
+ {
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ tp->t_termios.c_lflag &= ~FLUSHO;
+ }
+ else
+ {
+ tty_flush(tp, FWRITE);
+ ttydisc_echo(tp, c, 0);
+ if (tp->t_inq.ti_end > 0)
+ ttydisc_reprint(tp);
+ tp->t_termios.c_lflag |= FLUSHO;
+ }
+ }
+ }
+
+ /*
+ * Handle signal processing.
+ */
+ if (CMP_FLAG(l, ISIG))
+ {
+#ifdef USING_BSD_SIGINFO
+ if (CMP_FLAG(l, ICANON | IEXTEN) == (ICANON | IEXTEN))
+ {
+ if (CMP_CC(VSTATUS, c))
+ {
+ tty_signal_pgrp(tp, SIGINFO);
+ return 0;
+ }
+ }
+#endif /* USING_BSD_SIGINFO */
+
+ /*
+ * When compared to the old implementation, this
+ * implementation also flushes the output queue. POSIX
+ * is really brief about this, but does makes us assume
+ * we have to do so.
+ */
+ signal = 0;
+ if (CMP_CC(VINTR, c))
+ {
+ signal = SIGINT;
+ }
+ else if (CMP_CC(VQUIT, c))
+ {
+ signal = SIGQUIT;
+ }
+ else if (CMP_CC(VSUSP, c))
+ {
+ signal = SIGTSTP;
+ }
+
+ if (signal != 0)
+ {
+ /*
+ * Echo the character before signalling the
+ * processes.
+ */
+ if (!CMP_FLAG(l, NOFLSH))
+ tty_flush(tp, FREAD | FWRITE);
+ ttydisc_echo(tp, c, 0);
+ lwp_tty_signal_pgrp(tp, signal);
+ return 0;
+ }
+ }
+
+ /*
+ * Handle start/stop characters.
+ */
+ if (CMP_FLAG(i, IXON))
+ {
+ if (CMP_CC(VSTOP, c))
+ {
+ /* Stop it if we aren't stopped yet. */
+ if ((tp->t_flags & TF_STOPPED) == 0)
+ {
+ tp->t_flags |= TF_STOPPED;
+ return 0;
+ }
+ /*
+ * Fallthrough:
+ * When VSTART == VSTOP, we should make this key
+ * toggle it.
+ */
+ if (!CMP_CC(VSTART, c))
+ return 0;
+ }
+ if (CMP_CC(VSTART, c))
+ {
+ tp->t_flags &= ~TF_STOPPED;
+ return 0;
+ }
+ }
+
+ /* Conversion of CR and NL. */
+ switch (c)
+ {
+ case CCR:
+ if (CMP_FLAG(i, IGNCR))
+ return 0;
+ if (CMP_FLAG(i, ICRNL))
+ c = CNL;
+ break;
+ case CNL:
+ if (CMP_FLAG(i, INLCR))
+ c = CCR;
+ break;
+ }
+
+ /* Canonical line editing. */
+ if (CMP_FLAG(l, ICANON))
+ {
+ if (CMP_CC(VERASE, c)
+#ifdef VERASE2
+ || CMP_CC(VERASE2, c)
+#endif
+ )
+ {
+ ttydisc_rubchar(tp);
+ return 0;
+ }
+ else if (CMP_CC(VKILL, c))
+ {
+ while (ttydisc_rubchar(tp) == 0)
+ ;
+ return 0;
+ }
+ else if (CMP_FLAG(l, IEXTEN))
+ {
+ if (CMP_CC(VWERASE, c))
+ {
+ ttydisc_rubword(tp);
+ return 0;
+ }
+ else if (CMP_CC(VREPRINT, c))
+ {
+ ttydisc_reprint(tp);
+ return 0;
+ }
+ }
+ }
+
+processed:
+ if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff)
+ {
+ /* Print 0xff 0xff. */
+ ob[1] = 0xff;
+ ol = 2;
+ quote = 1;
+ }
+ else
+ {
+ ob[0] = c;
+ ol = 1;
+ }
+
+ goto print;
+
+parmrk:
+ if (CMP_FLAG(i, PARMRK))
+ {
+ /* Prepend 0xff 0x00 0x.. */
+ ob[2] = c;
+ ol = 3;
+ quote = 1;
+ }
+ else
+ {
+ ob[0] = c;
+ ol = 1;
+ }
+
+print:
+ /* See if we can store this on the input queue. */
+ if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0)
+ {
+ if (CMP_FLAG(i, IMAXBEL))
+ ttyoutq_write_nofrag(&tp->t_outq, "\a", 1);
+
+ /*
+ * Prevent a deadlock here. It may be possible that a
+ * user has entered so much data, there is no data
+ * available to read(), but the buffers are full anyway.
+ *
+ * Only enter the high watermark if the device driver
+ * can actually transmit something.
+ */
+ if (ttyinq_bytescanonicalized(&tp->t_inq) == 0)
+ return 0;
+
+ tty_hiwat_in_block(tp);
+ return (-1);
+ }
+
+ /*
+ * In raw mode, we canonicalize after receiving a single
+ * character. Otherwise, we canonicalize when we receive a
+ * newline, VEOL or VEOF, but only when it isn't quoted.
+ */
+ if (!CMP_FLAG(l, ICANON) ||
+ (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c))))
+ {
+ ttyinq_canonicalize(&tp->t_inq);
+ }
+
+ ttydisc_echo(tp, c, quote);
+
+ return 0;
+}
+
+size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len)
+{
+ const char *cbuf;
+
+ if (ttydisc_can_bypass(tp))
+ return (ttydisc_rint_bypass(tp, buf, len));
+
+ for (cbuf = buf; len-- > 0; cbuf++)
+ {
+ if (ttydisc_rint(tp, *cbuf, 0) != 0)
+ break;
+ }
+
+ return (cbuf - (const char *)buf);
+}
+
+size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len)
+{
+ size_t ret;
+
+ tty_assert_locked(tp);
+
+ MPASS(tp->t_flags & TF_BYPASS);
+
+ rt_atomic_add(&tty_nin, len);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_bypass))
+ return ttyhook_rint_bypass(tp, buf, len);
+#endif
+
+ ret = ttyinq_write(&tp->t_inq, buf, len, 0);
+ ttyinq_canonicalize(&tp->t_inq);
+ if (ret < len)
+ tty_hiwat_in_block(tp);
+
+ return (ret);
+}
+
+void ttydisc_rint_done(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_done))
+ ttyhook_rint_done(tp);
+#endif
+
+ /* Wake up readers. */
+ tty_wakeup(tp, FREAD);
+ /* Wake up driver for echo. */
+ ttydevsw_outwakeup(tp);
+}
+
+size_t ttydisc_rint_poll(struct lwp_tty *tp)
+{
+ size_t l;
+
+ tty_assert_locked(tp);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_poll))
+ return ttyhook_rint_poll(tp);
+#endif
+
+ /*
+ * XXX: Still allow character input when there's no space in the
+ * buffers, but we haven't entered the high watermark. This is
+ * to allow backspace characters to be inserted when in
+ * canonical mode.
+ */
+ l = ttyinq_bytesleft(&tp->t_inq);
+ if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0)
+ return (1);
+
+ return (l);
+}
+
+static void ttydisc_wakeup_watermark(struct lwp_tty *tp)
+{
+ size_t c;
+
+ c = ttyoutq_bytesleft(&tp->t_outq);
+ if (tp->t_flags & TF_HIWAT_OUT)
+ {
+ /* Only allow us to run when we're below the watermark. */
+ if (c < tp->t_outlow)
+ return;
+
+ /* Reset the watermark. */
+ tp->t_flags &= ~TF_HIWAT_OUT;
+ }
+ else
+ {
+ /* Only run when we have data at all. */
+ if (c == 0)
+ return;
+ }
+ tty_wakeup(tp, FWRITE);
+}
+
+size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len)
+{
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_inject))
+ return ttyhook_getc_inject(tp, buf, len);
+#endif
+
+ len = ttyoutq_read(&tp->t_outq, buf, len);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_capture))
+ ttyhook_getc_capture(tp, buf, len);
+#endif
+
+ ttydisc_wakeup_watermark(tp);
+ rt_atomic_add(&tty_nout, len);
+
+ return (len);
+}
+
+int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio)
+{
+ int error = 0;
+ ssize_t obytes = uio->uio_resid;
+ size_t len;
+ char buf[TTY_STACKBUF];
+
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+ /*
+ * When a TTY hook is attached, we cannot perform unbuffered
+ * copying to userspace. Just call ttydisc_getc() and
+ * temporarily store data in a shadow buffer.
+ */
+ if (ttyhook_hashook(tp, getc_capture) || ttyhook_hashook(tp, getc_inject))
+ {
+ while (uio->uio_resid > 0)
+ {
+ /* Read to shadow buffer. */
+ len = ttydisc_getc(tp, buf, MIN(uio->uio_resid, sizeof buf));
+ if (len == 0)
+ break;
+
+ /* Copy to userspace. */
+ tty_unlock(tp);
+ error = uiomove(buf, len, uio);
+ tty_lock(tp);
+
+ if (error != 0)
+ break;
+ }
+ }
+ else
+ {
+ error = ttyoutq_read_uio(&tp->t_outq, tp, uio);
+
+ ttydisc_wakeup_watermark(tp);
+ rt_atomic_add(&tty_nout, obytes - uio->uio_resid);
+ }
+
+ return error;
+}
+
+size_t ttydisc_getc_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+#if USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_poll))
+ return ttyhook_getc_poll(tp);
+#endif
+
+ return ttyoutq_bytesused(&tp->t_outq);
+}
diff --git a/components/lwp/terminal/terminal.h b/components/lwp/terminal/terminal.h
new file mode 100644
index 0000000000..df0dc04128
--- /dev/null
+++ b/components/lwp/terminal/terminal.h
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+
+#ifndef __LWP_TERMINAL_H__
+#define __LWP_TERMINAL_H__
+
+#include "bsd_ttyqueue.h"
+#include "bsd_ttydisc.h"
+
+#ifdef USING_BSD_HOOK
+#include "bsd_ttyhook.h"
+#endif
+
+#include <lwp.h>
+#include <rtdef.h>
+
+/* include kernel header for termios base definitions */
+#include <termios.h>
+/* for _POSIX_VDISABLE */
+#include <unistd.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * 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.
+ */
+
+struct lwp_tty;
+
+/*
+ * Driver routines that are called from the line discipline to adjust
+ * hardware parameters and such.
+ */
+typedef int tsw_open_t(struct lwp_tty *tp);
+typedef void tsw_close_t(struct lwp_tty *tp);
+typedef void tsw_outwakeup_t(struct lwp_tty *tp);
+typedef void tsw_inwakeup_t(struct lwp_tty *tp);
+typedef int tsw_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td);
+typedef int tsw_cioctl_t(struct lwp_tty *tp, int unit, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td);
+typedef int tsw_param_t(struct lwp_tty *tp, struct termios *t);
+typedef int tsw_modem_t(struct lwp_tty *tp, int sigon, int sigoff);
+typedef int tsw_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset,
+ vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr);
+
+typedef void tsw_pktnotify_t(struct lwp_tty *tp, char event);
+typedef void tsw_free_t(void *softc);
+typedef rt_bool_t tsw_busy_t(struct lwp_tty *tp);
+
+struct lwp_ttydevsw
+{
+ unsigned int tsw_flags; /* Default TTY flags. */
+
+ tsw_open_t *tsw_open; /* Device opening. */
+ tsw_close_t *tsw_close; /* Device closure. */
+
+ tsw_outwakeup_t *tsw_outwakeup; /* Output available. */
+ tsw_inwakeup_t *tsw_inwakeup; /* Input can be stored again. */
+
+ tsw_ioctl_t *tsw_ioctl; /* ioctl() hooks. */
+ tsw_cioctl_t *tsw_cioctl; /* ioctl() on control devices. */
+ tsw_param_t *tsw_param; /* TIOCSETA device parameter setting. */
+ tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */
+
+ tsw_mmap_t *tsw_mmap; /* mmap() hooks. */
+ tsw_pktnotify_t *tsw_pktnotify; /* TIOCPKT events. */
+
+ tsw_free_t *tsw_free; /* Destructor. */
+
+ tsw_busy_t *tsw_busy; /* Draining output. */
+
+ void *tsw_spare[3]; /* For future use. */
+};
+typedef struct lwp_ttydevsw *lwp_ttydevsw_t;
+
+struct lwp_tty
+{
+ struct rt_device parent; /* inherit from Class:RT_Device */
+ struct rt_mutex *t_mtx; /* TTY lock. */
+ struct rt_mutex t_mtxobj; /* Per-TTY lock (when not borrowing). */
+ rt_list_t t_list; /* (l) TTY list entry. */
+ int t_drainwait; /* (t) TIOCDRAIN timeout seconds. */
+ unsigned int t_flags; /* (t) Terminal option flags. */
+/* Keep flags in sync with db_show_tty and pstat(8). */
+#define TF_NOPREFIX 0x00001 /* Don't prepend "tty" to device name. */
+#define TF_INITLOCK 0x00002 /* Create init/lock state devices. */
+#define TF_CALLOUT 0x00004 /* Create "cua" devices. */
+#define TF_OPENED_IN 0x00008 /* "tty" node is in use. */
+#define TF_OPENED_OUT 0x00010 /* "cua" node is in use. */
+#define TF_OPENED_CONS 0x00020 /* Device in use as console. */
+#define TF_OPENED (TF_OPENED_IN | TF_OPENED_OUT | TF_OPENED_CONS)
+#define TF_GONE 0x00040 /* Device node is gone. */
+#define TF_OPENCLOSE 0x00080 /* Device is in open()/close(). */
+#define TF_ASYNC 0x00100 /* Asynchronous I/O enabled. */
+#define TF_LITERAL 0x00200 /* Accept the next character literally. */
+#define TF_HIWAT_IN 0x00400 /* We've reached the input watermark. */
+#define TF_HIWAT_OUT 0x00800 /* We've reached the output watermark. */
+#define TF_HIWAT (TF_HIWAT_IN | TF_HIWAT_OUT)
+#define TF_STOPPED 0x01000 /* Output flow control - stopped. */
+#define TF_EXCLUDE 0x02000 /* Exclusive access. */
+#define TF_BYPASS 0x04000 /* Optimized input path. */
+#define TF_ZOMBIE 0x08000 /* Modem disconnect received. */
+#define TF_HOOK 0x10000 /* TTY has hook attached. */
+#define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */
+#define TF_BUSY_OUT 0x40000 /* Process busy in write(). */
+#define TF_BUSY (TF_BUSY_IN | TF_BUSY_OUT)
+ unsigned int t_revokecnt; /* (t) revoke() count. */
+
+ /* Buffering mechanisms. */
+ struct ttyinq t_inq; /* (t) Input queue. */
+ size_t t_inlow; /* (t) Input low watermark. */
+ struct ttyoutq t_outq; /* (t) Output queue. */
+ size_t t_outlow; /* (t) Output low watermark. */
+
+ /* Sleeping mechanisms. */
+ struct rt_condvar t_inwait; /* (t) Input wait queue. */
+ struct rt_condvar t_outwait; /* (t) Output wait queue. */
+ struct rt_condvar t_outserwait; /* (t) Serial output wait queue. */
+ struct rt_condvar t_bgwait; /* (t) Background wait queue. */
+ struct rt_condvar t_dcdwait; /* (t) Carrier Detect wait queue. */
+
+ struct rt_wqueue t_inpoll; /* (t) Input poll queue. */
+ struct rt_wqueue t_outpoll; /* (t) Output poll queue. */
+
+#ifdef USING_BSD_AIO
+ struct sigio *t_sigio; /* (t) Asynchronous I/O. */
+#endif
+
+ struct termios t_termios; /* (t) I/O processing flags. */
+ struct winsize t_winsize; /* (t) Window size. */
+ unsigned int t_column; /* (t) Current cursor position. */
+ unsigned int t_writepos; /* (t) Where input was interrupted. */
+ int t_compatflags; /* (t) COMPAT_43TTY flags. */
+
+ /* Init/lock-state devices. */
+ struct termios t_termios_init_in; /* tty%s.init. */
+ struct termios t_termios_lock_in; /* tty%s.lock. */
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ struct termios t_termios_init_out; /* cua%s.init. */
+ struct termios t_termios_lock_out; /* cua%s.lock. */
+
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+
+ struct lwp_ttydevsw *t_devsw; /* (c) Driver hooks. */
+#ifdef USING_BSD_HOOK
+ struct lwp_ttyhook *t_hook; /* (t) Capture/inject hook. */
+#endif
+
+ /* Process signal delivery. */
+ struct rt_processgroup *t_pgrp; /* (t) Foreground process group. */
+ struct rt_session *t_session; /* (t) Associated session. */
+ unsigned int t_sessioncnt; /* (t) Backpointing sessions. */
+
+ void *t_devswsoftc; /* (c) Soft config, for drivers. */
+#ifdef USING_BSD_HOOK
+ void *t_hooksoftc; /* (t) Soft config, for hooks. */
+#endif
+#ifdef USING_BSD_CHAR_DEVICE
+ struct cdev *t_dev; /* (c) Primary character device. */
+#endif /* USING_BSD_CHAR_DEVICE */
+#ifdef USING_BSD_SIGINFO
+ size_t t_prbufsz; /* (t) SIGINFO buffer size. */
+ char t_prbuf[]; /* (t) SIGINFO buffer. */
+#endif /* USING_BSD_SIGINFO */
+};
+typedef struct lwp_tty *lwp_tty_t;
+
+/* Allocation and deallocation. */
+void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pgrp);
+void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess);
+void tty_rel_gone(struct lwp_tty *tp);
+
+/* tty locking mechanism */
+#define tty_getlock(tp) ((tp)->t_mtx)
+#define tty_lock(tp) rt_mutex_take(tty_getlock(tp), RT_WAITING_FOREVER);
+#define tty_unlock(tp) rt_mutex_release(tty_getlock(tp))
+#define tty_lock_owned(tp) \
+ (rt_mutex_get_owner(tty_getlock(tp)) == rt_thread_self())
+#define tty_lock_notrecused(tp) (rt_mutex_get_hold(tty_getlock(tp)) == 1)
+#define tty_assert_locked(tp) RT_ASSERT(tty_lock_owned(tp))
+#define tty_lock_assert(tp, option) \
+ (((option) == (MA_OWNED | MA_NOTRECURSED)) \
+ ? (tty_lock_owned(tp) && tty_lock_notrecused(tp)) \
+ : rt_assert_handler("Operation not allowed", __func__, __LINE__))
+
+/* System messages. */
+int tty_checkoutq(struct lwp_tty *tp);
+int tty_putchar(struct lwp_tty *tp, char c);
+int tty_putstrn(struct lwp_tty *tp, const char *p, size_t n);
+
+int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
+ struct rt_thread *td);
+int tty_ioctl_compat(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
+ struct rt_thread *td);
+void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz);
+void tty_init_console(struct lwp_tty *tp, speed_t speed);
+void tty_flush(struct lwp_tty *tp, int flags);
+void tty_hiwat_in_block(struct lwp_tty *tp);
+void tty_hiwat_in_unblock(struct lwp_tty *tp);
+dev_t tty_udev(struct lwp_tty *tp);
+
+/* tesing on tty */
+#define tty_opened(tp) ((tp)->t_flags & TF_OPENED)
+#define tty_gone(tp) ((tp)->t_flags & TF_GONE)
+#define tty_softc(tp) ((tp)->t_devswsoftc)
+#define tty_devname(tp) ((tp)->parent.parent.name)
+
+/**
+ * @brief TTY registeration on device subsystem
+ *
+ * @warning It's the duty of the caller to ensure that the name is not
+ * identical to any existed registered devices.
+ *
+ * @param terminal the target tty device
+ * @param name name of the device (must be exclusive)
+ * @return rt_err_t RT_EOK on success
+ */
+rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name);
+
+/**
+ * @brief TTY allocation and deallocation. TTY devices can be deallocated when
+ * the driver doesn't use it anymore, when the TTY isn't a session's
+ * controlling TTY and when the device node isn't opened through devfs.
+ *
+ * @param handle device handle of tty
+ * @param softc device configuration binding on tty
+ * @param prefix device name prefix
+ * @param cutom_mtx the lock provided to protect tty
+ * @return lwp_tty_t NULL on failure
+ */
+lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
+ rt_mutex_t custom_mtx);
+
+/**
+ * @brief Handful version of lwp_tty_create_ext
+ *
+ * @param softc device configuration binding on tty
+ * @param cutom_mtx the lock provided to protect tty
+ * @param prefix device name prefix
+ * @return lwp_tty_t NULL on failure
+ */
+lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc);
+
+void lwp_tty_delete(lwp_tty_t tp);
+
+void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig);
+
+void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig);
+
+/**
+ * @brief Create a new pseudo-terminal multiplexer
+ *
+ * @param root_path path of root mount point of ptyfs
+ * @return rt_device_t new device object if succeed, otherwise NULL
+ */
+rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path);
+
+#define LWP_CONSOLE_LOWEST_PRIOR 0
+#define LWP_CONSOLE_HIGHEST_PRIO INT_MAX
+/**
+ * @brief Register an alternative backend tty device as console
+ */
+rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio);
+
+rt_inline int ttydevsw_open(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_open(tp));
+}
+
+rt_inline void ttydevsw_close(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ tp->t_devsw->tsw_close(tp);
+}
+
+rt_inline void ttydevsw_outwakeup(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ /* Prevent spurious wakeups. */
+ if (ttydisc_getc_poll(tp) == 0)
+ return;
+
+ tp->t_devsw->tsw_outwakeup(tp);
+}
+
+rt_inline void ttydevsw_inwakeup(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ /* Prevent spurious wakeups. */
+ if (tp->t_flags & TF_HIWAT_IN)
+ return;
+
+ tp->t_devsw->tsw_inwakeup(tp);
+}
+
+rt_inline int ttydevsw_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td));
+}
+
+rt_inline int ttydevsw_cioctl(struct lwp_tty *tp, int unit, rt_ubase_t cmd,
+ rt_caddr_t data, struct rt_thread *td)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_cioctl(tp, unit, cmd, data, td));
+}
+
+rt_inline int ttydevsw_param(struct lwp_tty *tp, struct termios *t)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_param(tp, t));
+}
+
+rt_inline int ttydevsw_modem(struct lwp_tty *tp, int sigon, int sigoff)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_modem(tp, sigon, sigoff));
+}
+
+rt_inline int ttydevsw_mmap(struct lwp_tty *tp, vm_ooffset_t offset,
+ vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot, memattr));
+}
+
+rt_inline void ttydevsw_pktnotify(struct lwp_tty *tp, char event)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ tp->t_devsw->tsw_pktnotify(tp, event);
+}
+
+rt_inline void ttydevsw_free(struct lwp_tty *tp)
+{
+ MPASS(tty_gone(tp));
+
+ tp->t_devsw->tsw_free(tty_softc(tp));
+}
+
+rt_inline rt_bool_t ttydevsw_busy(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_busy(tp));
+}
+
+rt_inline size_t ttydisc_read_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ return ttyinq_bytescanonicalized(&tp->t_inq);
+}
+
+rt_inline size_t ttydisc_write_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ return ttyoutq_bytesleft(&tp->t_outq);
+}
+
+#endif /* __LWP_TERMINAL_H__ */
diff --git a/components/lwp/terminal/tty_config.h b/components/lwp/terminal/tty_config.h
new file mode 100644
index 0000000000..a1fe75dc7a
--- /dev/null
+++ b/components/lwp/terminal/tty_config.h
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#ifndef __TTY_CONFIG_H__
+#define __TTY_CONFIG_H__
+
+/* default buffer size of tty siginfo */
+#define LWP_TTY_PRBUF_SIZE 256
+
+/*
+ * System wide defaults for terminal state.
+ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)ttydefaults.h 8.4 (Berkeley) 1/21/94
+ */
+
+/*
+ * Defaults on "first" open.
+ */
+#define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY | IUTF8)
+#define TTYDEF_OFLAG (OPOST | ONLCR)
+#define TTYDEF_LFLAG_NOECHO (ICANON | ISIG | IEXTEN)
+#define TTYDEF_LFLAG_ECHO \
+ (TTYDEF_LFLAG_NOECHO | ECHO | ECHOE | ECHOKE | ECHOCTL)
+#define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO
+#define TTYDEF_CFLAG (CREAD | CS8 | HUPCL)
+#define TTYDEF_SPEED (B9600)
+
+/*
+ * Control Character Defaults
+ */
+/*
+ * XXX: A lot of code uses lowercase characters, but control-character
+ * conversion is actually only valid when applied to uppercase
+ * characters. We just treat lowercase characters as if they were
+ * inserted as uppercase.
+ */
+#define _CONTROL(c) \
+ ((c) >= 'a' && (c) <= 'z' ? ((c) - 'a' + 1) : (((c) - 'A' + 1) & 0x7f))
+#define CEOF _CONTROL('D')
+#define CEOL 0xff /* XXX avoid _POSIX_VDISABLE */
+#define CERASE 0x7f
+#define CERASE2 _CONTROL('H')
+#define CINTR _CONTROL('C')
+#define CSTATUS _CONTROL('T')
+#define CKILL _CONTROL('U')
+#define CMIN 1
+#define CQUIT _CONTROL('\\')
+#define CSUSP _CONTROL('Z')
+#define CTIME 0
+#define CDSUSP _CONTROL('Y')
+#define CSTART _CONTROL('Q')
+#define CSTOP _CONTROL('S')
+#define CLNEXT _CONTROL('V')
+#define CDISCARD _CONTROL('O')
+#define CWERASE _CONTROL('W')
+#define CREPRINT _CONTROL('R')
+#define CEOT CEOF
+/* compat */
+#define CBRK CEOL
+#define CRPRNT CREPRINT
+#define CFLUSH CDISCARD
+
+/* PROTECTED INCLUSION ENDS HERE */
+#endif /* !__TTY_CONFIG_H__ */
+
+/*
+ * #define TTY_CONF_INCLUDE_CCHARS to include an array of default control
+ * characters.
+ */
+#ifdef TTY_CONF_INCLUDE_CCHARS
+#include <rtdef.h>
+#include <termios.h>
+#include <unistd.h>
+
+static const cc_t tty_ctrl_charset[NCCS] = {
+ [VINTR] = CINTR,
+ [VQUIT] = CQUIT,
+ [VERASE] = CERASE,
+ [VKILL] = CKILL,
+ [VEOF] = CEOF,
+ [VSTART] = CSTART,
+ [VSTOP] = CSTOP,
+ [VSUSP] = CSUSP,
+ [VREPRINT] = CREPRINT,
+ [VDISCARD] = CDISCARD,
+ [VWERASE] = CWERASE,
+ [VLNEXT] = CLNEXT,
+ [VMIN] = CMIN
+#undef _CONTROL
+};
+
+#undef TTY_CONF_INCLUDE_CCHARS
+#endif /* __TTY_CONFIG_H__ */
diff --git a/components/lwp/terminal/tty_cons.c b/components/lwp/terminal/tty_cons.c
new file mode 100644
index 0000000000..42434f3568
--- /dev/null
+++ b/components/lwp/terminal/tty_cons.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-11 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 "bsd_porting.h"
+#include "terminal.h"
+
+#include <fcntl.h>
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops cons_rtdev_ops;
+#endif
+
+struct backend_entry
+{
+ rt_list_t bakend_list_node;
+ int prio;
+
+ rt_device_t bakdev;
+};
+
+static rt_list_t _bakend_list;
+
+static void _bent_enqueue(struct backend_entry *bent)
+{
+ struct backend_entry *idx;
+ rt_bool_t inserted = RT_FALSE;
+ rt_list_for_each_entry(idx, &_bakend_list, bakend_list_node)
+ {
+ if (idx->prio < bent->prio)
+ {
+ rt_list_insert_before(&idx->bakend_list_node, &bent->bakend_list_node);
+ inserted = RT_TRUE;
+ break;
+ }
+ }
+
+ if (!inserted)
+ rt_list_insert_after(&_bakend_list, &bent->bakend_list_node);
+ return ;
+}
+
+rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio)
+{
+ rt_err_t ret = RT_EOK;
+ struct backend_entry *bent;
+ bent = rt_malloc(sizeof(struct backend_entry));
+ if (bent)
+ {
+ rt_list_init(&bent->bakend_list_node);
+ bent->prio = prio;
+ bent->bakdev = bakdev;
+
+ _bent_enqueue(bent);
+ }
+ else
+ {
+ ret = -RT_ENOMEM;
+ }
+ return ret;
+}
+
+static struct rt_device _cons_rtdev;
+
+static int fops_open(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops _cons_fops = {
+ .open = fops_open,
+};
+
+static rt_err_t _cons_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = -EIO;
+ struct backend_entry *bent;
+ if (!rt_list_isempty(&_bakend_list))
+ {
+ bent = rt_list_first_entry(&_bakend_list, struct backend_entry, bakend_list_node);
+ if (bent)
+ {
+ RT_ASSERT(bent->bakdev);
+ strncpy(buf, bent->bakdev->parent.name, MIN(len, RT_NAME_MAX));
+ LOG_D("%s: backend device %s", __func__, buf);
+ rc = 0;
+ }
+ }
+
+ if (rc != 0)
+ {
+ LOG_W("%s: No backend device", __func__);
+ }
+
+ return rc;
+}
+
+static int _cons_init(void)
+{
+ rt_err_t rc;
+
+ rt_list_init(&_bakend_list);
+
+ /* setup system level device */
+ _cons_rtdev.type = RT_Device_Class_Char;
+ _cons_rtdev.ops = &cons_rtdev_ops;
+ rc = rt_device_register(&_cons_rtdev, "console", RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ _cons_rtdev.readlink = &_cons_readlink;
+ _cons_rtdev.fops = &_cons_fops;
+ }
+
+ return rc;
+}
+INIT_DEVICE_EXPORT(_cons_init);
diff --git a/components/lwp/terminal/tty_ctty.c b/components/lwp/terminal/tty_ctty.c
new file mode 100644
index 0000000000..f0de0001f8
--- /dev/null
+++ b/components/lwp/terminal/tty_ctty.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-30 Shell init ver.
+ */
+#define DBG_TAG "lwp.ctty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define TTY_CONF_INCLUDE_CCHARS
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "terminal.h"
+
+static int fops_open(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static rt_err_t ctty_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = -ENXIO;
+ lwp_tty_t tp;
+ rt_session_t sess;
+ rt_processgroup_t pgrp;
+ rt_lwp_t lwp;
+
+ lwp = lwp_self();
+ if (lwp)
+ {
+ pgrp = lwp->pgrp;
+ if (pgrp)
+ {
+ sess = pgrp->session;
+ if (sess)
+ {
+ tp = sess->ctty;
+ if (tp)
+ {
+ tty_lock(tp);
+
+ if (lwp->pgrp == pgrp && pgrp->session == sess && sess->ctty == tp)
+ {
+ rt_strncpy(buf, tp->parent.parent.name, len);
+ rc = RT_EOK;
+ }
+
+ tty_unlock(tp);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static struct dfs_file_ops ctty_file_ops = {
+ .open = fops_open,
+};
+
+/* character device for tty */
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops tty_dev_ops = {
+ /* IO directly through device is not allowed */
+};
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+
+rt_inline void device_setup(rt_device_t ctty)
+{
+ ctty->type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ ctty->ops = &tty_dev_ops;
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+}
+
+/* register device to DFS */
+static int lwp_ctty_register(rt_device_t ctty)
+{
+ rt_err_t rc = -RT_ENOMEM;
+ const char *tty_name = "tty";
+
+ device_setup(ctty);
+ rc = rt_device_register(ctty, tty_name, RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ ctty->readlink = &ctty_readlink;
+ ctty->fops = &ctty_file_ops;
+ }
+ return rc;
+}
+
+static struct rt_device ctty;
+
+static int lwp_ctty_init(void)
+{
+ return lwp_ctty_register(&ctty);
+}
+INIT_DEVICE_EXPORT(lwp_ctty_init);
diff --git a/components/lwp/terminal/tty_device.c b/components/lwp/terminal/tty_device.c
new file mode 100644
index 0000000000..17364fabc1
--- /dev/null
+++ b/components/lwp/terminal/tty_device.c
@@ -0,0 +1,456 @@
+/*
+ * 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>
+
+#define TTY_CONF_INCLUDE_CCHARS
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "terminal.h"
+
+/* configure option: timeout of tty drain wait */
+static int tty_drainwait = 5 * 60;
+
+#define TTY_NAME_PREFIX "tty"
+static char *alloc_device_name(const char *name)
+{
+ char *tty_dev_name;
+ long name_buf_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */
+ + rt_strlen(name) /* custom name */
+ + 1; /* tailing \0 */
+
+ tty_dev_name = rt_malloc(name_buf_len);
+ if (tty_dev_name)
+ sprintf(tty_dev_name, "%s%s", TTY_NAME_PREFIX, name);
+ return tty_dev_name;
+}
+
+/* character device for tty */
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops tty_dev_ops = {
+ /* IO directly through device is not allowed */
+};
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+
+static int tty_fops_open(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ int devtype = 0; /* unused */
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ttydev_methods.d_open(tp, file->flags, devtype,
+ rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int tty_fops_close(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ int fflags = FFLAGS(file->flags);
+ int devtype = 0; /* unused */
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ttydev_methods.d_close(tp, fflags, devtype, rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int tty_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = lwp_tty_ioctl_adapter(tp, cmd, file->flags, arg, rt_thread_self());
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static ssize_t tty_fops_read(struct dfs_file *file, void *buf, size_t count,
+ off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int ioflags;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup ioflags */
+ ioflags = 0;
+ if (oflags & O_NONBLOCK)
+ ioflags |= IO_NDELAY;
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_READ;
+
+ rc = count;
+ error = bsd_ttydev_methods.d_read(tp, &uio, ioflags);
+ rc -= uio.uio_resid;
+ if (error)
+ {
+ LOG_D("%s: failed to write %d bytes of data. error code %d",
+ __func__, uio.uio_resid, error);
+ rc = error;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+
+ if (rc)
+ LOG_D("%s(len=%d, buf=%c \"%d\")", __func__, rc, *((char *)buf),
+ *((char *)buf));
+ return rc;
+}
+
+static ssize_t tty_fops_write(struct dfs_file *file, const void *buf,
+ size_t count, off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int ioflags;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup ioflags */
+ ioflags = 0;
+ if (oflags & O_NONBLOCK)
+ ioflags |= IO_NDELAY;
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_WRITE;
+
+ rc = count;
+ error = bsd_ttydev_methods.d_write(tp, &uio, ioflags);
+ if (error)
+ {
+ rc = error;
+ LOG_D("%s: failed to write %d bytes of data. error code %d",
+ __func__, uio.uio_resid, error);
+ }
+ else
+ {
+ rc -= uio.uio_resid;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+ return rc;
+}
+
+static int tty_fops_flush(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static off_t tty_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_truncate(struct dfs_file *file, off_t offset)
+{
+ /**
+ * regarding to POSIX.1, TRUNC is not supported for tty device.
+ * return 0 always to make filesystem happy
+ */
+ return 0;
+}
+
+static int tty_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
+{
+ int rc;
+ rt_device_t device;
+ struct lwp_tty *tp;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ rc = bsd_ttydev_methods.d_poll(tp, req, rt_thread_self());
+ }
+ else
+ {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int tty_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_lock(struct dfs_file *file, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops tty_file_ops = {
+ .open = tty_fops_open,
+ .close = tty_fops_close,
+ .ioctl = tty_fops_ioctl,
+ .read = tty_fops_read,
+ .write = tty_fops_write,
+ .flush = tty_fops_flush,
+ .lseek = tty_fops_lseek,
+ .truncate = tty_fops_truncate,
+ .poll = tty_fops_poll,
+ .mmap = tty_fops_mmap,
+ .lock = tty_fops_lock,
+ .flock = tty_fops_flock,
+};
+
+rt_inline void device_setup(lwp_tty_t terminal)
+{
+ terminal->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ terminal->parent.ops = &tty_dev_ops;
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+}
+
+/* register TTY device */
+rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name)
+{
+ rt_err_t rc = -RT_ENOMEM;
+ const char *tty_name;
+ char *alloc_name;
+
+ if (terminal->t_devsw->tsw_flags & TF_NOPREFIX)
+ {
+ alloc_name = RT_NULL;
+ tty_name = name;
+ }
+ else
+ {
+ alloc_name = alloc_device_name(name);
+ tty_name = alloc_name;
+ }
+
+ if (tty_name)
+ {
+ device_setup(terminal);
+ rc = rt_device_register(&terminal->parent, tty_name, 0);
+ if (rc == RT_EOK)
+ {
+ terminal->parent.fops = &tty_file_ops;
+
+ LOG_D("%s() /dev/%s device registered", __func__, tty_name);
+ }
+
+ rt_free(alloc_name);
+ }
+ return rc;
+}
+
+static void tty_init_termios(lwp_tty_t tp)
+{
+ struct termios *t = &tp->t_termios_init_in;
+
+ t->c_cflag = TTYDEF_CFLAG;
+ t->c_iflag = TTYDEF_IFLAG;
+ t->c_lflag = TTYDEF_LFLAG;
+ t->c_oflag = TTYDEF_OFLAG;
+ t->__c_ispeed = TTYDEF_SPEED;
+ t->__c_ospeed = TTYDEF_SPEED;
+
+ memcpy(&t->c_cc, tty_ctrl_charset,
+ sizeof(tty_ctrl_charset) / sizeof(tty_ctrl_charset[0]));
+
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ tp->t_termios_init_out = *t;
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+}
+
+lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
+ rt_mutex_t custom_mtx)
+{
+ lwp_tty_t tp;
+
+ tp = rt_calloc(1, sizeof(struct lwp_tty)
+ #ifdef USING_BSD_SIGINFO
+ + LWP_TTY_PRBUF_SIZE
+ #endif
+ );
+
+ if (!tp)
+ return tp;
+
+ bsd_devsw_init(handle);
+
+#ifdef USING_BSD_SIGINFO
+ tp->t_prbufsz = LWP_TTY_PRBUF_SIZE;
+#endif
+ tp->t_devsw = handle;
+ tp->t_devswsoftc = softc;
+ tp->t_flags = handle->tsw_flags;
+ tp->t_drainwait = tty_drainwait;
+
+ tty_init_termios(tp);
+
+ cv_init(&tp->t_inwait, "ttyin");
+ cv_init(&tp->t_outwait, "ttyout");
+ cv_init(&tp->t_outserwait, "ttyosr");
+ cv_init(&tp->t_bgwait, "ttybg");
+ cv_init(&tp->t_dcdwait, "ttydcd");
+
+ rt_wqueue_init(&tp->t_inpoll);
+ rt_wqueue_init(&tp->t_outpoll);
+
+ /* Allow drivers to use a custom mutex to lock the TTY. */
+ if (custom_mtx != NULL)
+ {
+ tp->t_mtx = custom_mtx;
+ }
+ else
+ {
+ tp->t_mtx = &tp->t_mtxobj;
+ rt_mutex_init(&tp->t_mtxobj, "ttydev", RT_IPC_FLAG_PRIO);
+ }
+
+#ifdef USING_BSD_POLL
+ knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx);
+ knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
+#endif
+
+ return tp;
+}
+
+lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc)
+{
+ return lwp_tty_create_ext(handle, softc, NULL);
+}
+
+void lwp_tty_delete(lwp_tty_t tp)
+{
+ /*
+ * ttyydev_leave() usually frees the i/o queues earlier, but it is
+ * not always called between queue allocation and here. The queues
+ * may be allocated by ioctls on a pty control device without the
+ * corresponding pty slave device ever being open, or after it is
+ * closed.
+ */
+ ttyinq_free(&tp->t_inq);
+ ttyoutq_free(&tp->t_outq);
+ rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLHUP);
+ rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLHUP);
+
+#ifdef USING_BSD_POLL
+ knlist_destroy(&tp->t_inpoll.si_note);
+ knlist_destroy(&tp->t_outpoll.si_note);
+#endif
+
+ cv_destroy(&tp->t_inwait);
+ cv_destroy(&tp->t_outwait);
+ cv_destroy(&tp->t_bgwait);
+ cv_destroy(&tp->t_dcdwait);
+ cv_destroy(&tp->t_outserwait);
+
+ if (tp->t_mtx == &tp->t_mtxobj)
+ rt_mutex_detach(&tp->t_mtxobj);
+ ttydevsw_free(tp);
+ rt_device_unregister(&tp->parent);
+ rt_free(tp);
+}
+
+/*
+ * Report on state of foreground process group.
+ */
+void tty_info(struct lwp_tty *tp)
+{
+ /* TODO */
+ return;
+}
diff --git a/components/lwp/terminal/tty_internal.h b/components/lwp/terminal/tty_internal.h
new file mode 100644
index 0000000000..8bf336cb89
--- /dev/null
+++ b/components/lwp/terminal/tty_internal.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+#ifndef __LWP_TTY_INTERNAL_H__
+#define __LWP_TTY_INTERNAL_H__
+
+#include "lwp.h"
+#include "terminal.h"
+
+extern struct cdevsw bsd_ttydev_methods;
+
+extern struct bsd_fileops bsd_ptsdev_methods;
+
+/* bsd devsw porting */
+void bsd_devsw_init(struct lwp_ttydevsw *tsw);
+
+/**
+ * Do not assert RTS or DTR automatically. If CNO_RTSDTR is set then the RTS and
+ * DTR lines will not be asserted when the device is opened. As a result, this
+ * flag is only useful on initial-state devices.
+ *
+ * Note: this feature is not using on smart system, so this flag is always 0.
+ */
+#define CNO_RTSDTR 0
+
+/* Waking up readers/writers. */
+int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv);
+int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig);
+int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout);
+void tty_wakeup(struct lwp_tty *tp, int flags);
+
+void tty_info(struct lwp_tty *tp);
+
+void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock);
+rt_bool_t pts_is_locked(lwp_tty_t pts);
+int pts_get_pktmode(lwp_tty_t pts);
+int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file);
+
+int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td);
+
+int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td);
+int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid);
+int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv);
+
+rt_inline rt_bool_t is_sess_leader(rt_lwp_t p)
+{
+ /**
+ * Note: a pgrp leader is never lose its group, so once it's
+ * true then it's always true
+ */
+ return p->pid == p->sid;
+}
+
+rt_inline int tty_is_ctty(struct lwp_tty *tp, struct rt_lwp *p)
+{
+ tty_assert_locked(tp);
+
+ return p->pgrp->session == tp->t_session && p->term_ctrlterm;
+}
+
+#endif /* __LWP_TTY_INTERNAL_H__ */
diff --git a/components/lwp/terminal/tty_ptmx.c b/components/lwp/terminal/tty_ptmx.c
new file mode 100644
index 0000000000..4158292251
--- /dev/null
+++ b/components/lwp/terminal/tty_ptmx.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 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 "bsd_porting.h"
+#include "terminal.h"
+
+#include <fcntl.h>
+
+static struct dfs_file_ops ptm_fops;
+
+static int ptm_fops_open(struct dfs_file *file)
+{
+ int rc;
+ rt_uint32_t oflags = file->flags;
+ rt_thread_t cur_thr = rt_thread_self();
+
+ /* we don't check refcnt because each open will create a new device */
+ if (file->vnode && file->vnode->data)
+ {
+ /**
+ * Filter out illegal flags
+ */
+ if ((oflags & ~(O_RDWR | O_NOCTTY | O_CLOEXEC | O_LARGEFILE)) == 0)
+ {
+ rc = pts_alloc(FFLAGS(oflags & O_ACCMODE), cur_thr, file);
+
+ /* detached operation from devfs */
+ if (rc == 0)
+ file->vnode->fops = &ptm_fops;
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_close(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ptsdev_methods.fo_close(tp, rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static ssize_t ptm_fops_read(struct dfs_file *file, void *buf, size_t count,
+ off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_READ;
+
+ rc = count;
+ error =
+ bsd_ptsdev_methods.fo_read(tp, &uio, 0, oflags, rt_thread_self());
+ rc -= uio.uio_resid;
+ if (error)
+ {
+ rc = error;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+
+ return rc;
+}
+
+static ssize_t ptm_fops_write(struct dfs_file *file, const void *buf,
+ size_t count, off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_WRITE;
+
+ rc = count;
+ error =
+ bsd_ptsdev_methods.fo_write(tp, &uio, 0, oflags, rt_thread_self());
+ if (error)
+ {
+ rc = error;
+ }
+ else
+ {
+ rc -= uio.uio_resid;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+ return rc;
+}
+
+static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ rt_ubase_t cmd_normal = (unsigned int)cmd;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ switch (cmd_normal)
+ {
+ case TIOCSPTLCK:
+ {
+ int is_lock;
+ if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ pts_set_lock(tp, is_lock);
+ rc = 0;
+ }
+ break;
+ case TIOCGPTLCK:
+ {
+ int is_lock = pts_is_locked(tp);
+ if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ rc = 0;
+ }
+ break;
+ case TIOCGPKT:
+ {
+ int pktmode = pts_get_pktmode(tp);
+ if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ rc = 0;
+ }
+ break;
+ default:
+ rc = bsd_ptsdev_methods.fo_ioctl(
+ tp, cmd_normal, arg, 0, FFLAGS(file->flags), rt_thread_self());
+ break;
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_flush(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static off_t ptm_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_truncate(struct dfs_file *file, off_t offset)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
+{
+ int rc;
+ rt_device_t device;
+ struct lwp_tty *tp;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ rc = bsd_ptsdev_methods.fo_poll(tp, req, 0, rt_thread_self());
+ }
+ else
+ {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_lock(struct dfs_file *file, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops ptm_fops = {
+ .open = ptm_fops_open,
+ .close = ptm_fops_close,
+ .ioctl = ptm_fops_ioctl,
+ .read = ptm_fops_read,
+ .write = ptm_fops_write,
+ .flush = ptm_fops_flush,
+ .lseek = ptm_fops_lseek,
+ .truncate = ptm_fops_truncate,
+ .poll = ptm_fops_poll,
+ .mmap = ptm_fops_mmap,
+ .lock = ptm_fops_lock,
+ .flock = ptm_fops_flock,
+};
+
+rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path)
+{
+ char *device_name;
+ int root_len;
+ const char *dev_rel_path;
+ rt_err_t rc;
+
+ root_len = strlen(root_path);
+ dev_rel_path = "/ptmx";
+ device_name = rt_malloc(root_len + sizeof("/ptmx"));
+
+ if (device_name)
+ {
+ /* Register device */
+ sprintf(device_name, "%s%s", root_path, dev_rel_path);
+ rt_device_register(ptmx_device, device_name, 0);
+
+ /* Setup fops */
+ ptmx_device->fops = &ptm_fops;
+
+ rt_free(device_name);
+
+ rc = RT_EOK;
+ }
+ else
+ {
+ rc = -RT_ENOMEM;
+ }
+
+ return rc;
+}
+
+/* system level ptmx */
+static struct rt_device sysptmx;
+
+static struct dfs_file_ops sysptmx_file_ops;
+
+static rt_err_t sysptmx_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = 0;
+
+ /* TODO: support multi-root ? */
+ strncpy(buf, "pts/ptmx", len);
+
+ return rc;
+}
+
+static int _sys_ptmx_init(void)
+{
+ rt_err_t rc;
+ rt_device_t sysptmx_rtdev = &sysptmx;
+
+ /* setup system level device */
+ sysptmx_rtdev->type = RT_Device_Class_Char;
+ sysptmx_rtdev->ops = RT_NULL;
+ rc = rt_device_register(sysptmx_rtdev, "ptmx", RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ sysptmx_rtdev->readlink = &sysptmx_readlink;
+ sysptmx_rtdev->fops = &sysptmx_file_ops;
+ }
+ return rc;
+}
+INIT_DEVICE_EXPORT(_sys_ptmx_init);
diff --git a/components/mm/mm_page.c b/components/mm/mm_page.c
index cc46ed46da..fcae953a2d 100644
--- a/components/mm/mm_page.c
+++ b/components/mm/mm_page.c
@@ -622,27 +622,27 @@ void list_page(void)
struct rt_page *lp = page_list_low[i];
struct rt_page *hp = page_list_high[i];
- LOG_RAW("level %d ", i);
+ rt_kprintf("level %d ", i);
while (lp)
{
free += (1UL << i);
- LOG_RAW("[0x%08p]", rt_page_page2addr(lp));
+ rt_kprintf("[0x%08p]", rt_page_page2addr(lp));
lp = lp->next;
}
while (hp)
{
free += (1UL << i);
- LOG_RAW("[0x%08p]", rt_page_page2addr(hp));
+ rt_kprintf("[0x%08p]", rt_page_page2addr(hp));
hp = hp->next;
}
LOG_RAW("\n");
}
rt_spin_unlock_irqrestore(&_spinlock, level);
- LOG_RAW("-------------------------------\n");
- LOG_RAW("Page Summary:\n => free/installed: 0x%lx/0x%lx (%ld/%ld KB)\n", free, installed, PGNR2SIZE(free), PGNR2SIZE(installed));
- LOG_RAW("-------------------------------\n");
+ rt_kprintf("-------------------------------\n");
+ rt_kprintf("Page Summary:\n => free/installed: 0x%lx/0x%lx (%ld/%ld KB)\n", free, installed, PGNR2SIZE(free), PGNR2SIZE(installed));
+ rt_kprintf("-------------------------------\n");
}
MSH_CMD_EXPORT(list_page, show page info);
diff --git a/components/utilities/libadt/bitmap/bitmap.h b/components/utilities/libadt/bitmap/bitmap.h
index 72fe54333a..f1b689b72e 100755
--- a/components/utilities/libadt/bitmap/bitmap.h
+++ b/components/utilities/libadt/bitmap/bitmap.h
@@ -18,7 +18,7 @@ typedef rt_ubase_t rt_bitmap_t;
#define RT_BITMAP_BITS_MIN (sizeof(rt_bitmap_t) * 8)
#define RT_BITMAP_LEN(bits) (((bits) + (RT_BITMAP_BITS_MIN) - 1) / (RT_BITMAP_BITS_MIN))
#define RT_BITMAP_BIT_LEN(nr) (nr * RT_BITMAP_BITS_MIN)
-#define RT_DECLARE_BITMAP(name, bits) rt_bitmap_t name[RT_BITMAP_LEN(bits)]
+#define RT_BITMAP_DECLARE(name, bits) rt_bitmap_t name[RT_BITMAP_LEN(bits)]
rt_inline void rt_bitmap_set_bit(rt_bitmap_t *bitmap, rt_uint32_t bit)
{
diff --git a/components/utilities/resource/SConscript b/components/utilities/resource/SConscript
index 6d572130a8..4410eaf937 100644
--- a/components/utilities/resource/SConscript
+++ b/components/utilities/resource/SConscript
@@ -3,6 +3,10 @@ from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
+
+if GetDepend('RT_USING_ADT_BITMAP') == False:
+ SrcRemove(src, ['rid_bitmap.c'])
+
group = DefineGroup('Utilities', src, depend = ['RT_USING_RESOURCE_ID'], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/utilities/resource/rid_bitmap.c b/components/utilities/resource/rid_bitmap.c
new file mode 100644
index 0000000000..ebadece2a4
--- /dev/null
+++ b/components/utilities/resource/rid_bitmap.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 Shell First version
+ */
+
+#include "rid_bitmap.h"
+#include <bitmap.h>
+#include <rtdef.h>
+
+void rid_bitmap_init(rid_bitmap_t mgr, int min_id, int total_id_count,
+ rt_bitmap_t *set, struct rt_mutex *id_lock)
+{
+ mgr->min_id = min_id;
+ mgr->total_id_count = total_id_count;
+ mgr->bitset = set;
+ mgr->id_lock = id_lock;
+
+ return;
+}
+
+long rid_bitmap_get(rid_bitmap_t mgr)
+{
+ long id;
+ long overflow;
+ if (mgr->id_lock)
+ {
+ rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
+ }
+
+ overflow = mgr->total_id_count;
+ id = rt_bitmap_next_clear_bit(mgr->bitset, 0, overflow);
+ if (id == overflow)
+ {
+ id = -1;
+ }
+ else
+ {
+ rt_bitmap_set_bit(mgr->bitset, id);
+ id += mgr->min_id;
+ }
+
+ if (mgr->id_lock)
+ {
+ rt_mutex_release(mgr->id_lock);
+ }
+ return id;
+}
+
+long rid_bitmap_get_named(rid_bitmap_t mgr, long no)
+{
+ long id_relative;
+ long overflow;
+ long min;
+
+ if (mgr->id_lock)
+ {
+ rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
+ }
+
+ min = mgr->min_id;
+ id_relative = no - min;
+ overflow = mgr->total_id_count;
+ if (id_relative >= min && id_relative < overflow)
+ {
+ if (rt_bitmap_test_bit(mgr->bitset, id_relative))
+ {
+ id_relative = -RT_EBUSY;
+ }
+ else
+ {
+ rt_bitmap_set_bit(mgr->bitset, id_relative);
+ id_relative += min;
+ }
+ }
+ else
+ {
+ id_relative = -1;
+ }
+
+ if (mgr->id_lock)
+ {
+ rt_mutex_release(mgr->id_lock);
+ }
+ return id_relative;
+}
+
+void rid_bitmap_put(rid_bitmap_t mgr, long no)
+{
+ long id_relative;
+ long overflow;
+ long min;
+
+ if (mgr->id_lock)
+ {
+ rt_mutex_take(mgr->id_lock, RT_WAITING_FOREVER);
+ }
+
+ min = mgr->min_id;
+ id_relative = no - min;
+ overflow = mgr->total_id_count;
+ if (id_relative >= min && id_relative < overflow &&
+ rt_bitmap_test_bit(mgr->bitset, id_relative))
+ {
+ rt_bitmap_clear_bit(mgr->bitset, id_relative);
+ }
+
+ if (mgr->id_lock)
+ {
+ rt_mutex_release(mgr->id_lock);
+ }
+}
diff --git a/components/utilities/resource/rid_bitmap.h b/components/utilities/resource/rid_bitmap.h
new file mode 100644
index 0000000000..d1e4e1ccc4
--- /dev/null
+++ b/components/utilities/resource/rid_bitmap.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 Shell First version
+ */
+
+#ifndef __RID_BITMAP_H__
+#define __RID_BITMAP_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <bitmap.h>
+
+#define RESOURCE_ID_INIT {0}
+
+typedef struct rid_bitmap
+{
+ struct rt_mutex *id_lock;
+ long min_id;
+ long total_id_count;
+ rt_bitmap_t *bitset;
+} *rid_bitmap_t;
+
+void rid_bitmap_init(rid_bitmap_t mgr, int min_id, int total_id_count,
+ rt_bitmap_t *set, struct rt_mutex *id_lock);
+long rid_bitmap_get(rid_bitmap_t mgr);
+long rid_bitmap_get_named(rid_bitmap_t mgr, long no);
+void rid_bitmap_put(rid_bitmap_t mgr, long no);
+
+#endif /* __RID_BITMAP_H__ */
diff --git a/components/utilities/ulog/ulog.c b/components/utilities/ulog/ulog.c
index 4e091f9a2b..cb0790b9de 100644
--- a/components/utilities/ulog/ulog.c
+++ b/components/utilities/ulog/ulog.c
@@ -1272,7 +1272,7 @@ rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool
backend->support_color = support_color;
backend->out_level = LOG_FILTER_LVL_ALL;
- rt_strncpy(backend->name, name, RT_NAME_MAX);
+ rt_strncpy(backend->name, name, RT_NAME_MAX - 1);
level = rt_spin_lock_irqsave(&_spinlock);
rt_slist_append(&ulog.backend_list, &backend->list);