/* * 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 #include "tty_config.h" #include "tty_internal.h" #include "bsd_porting.h" #include "terminal.h" #include 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->data) { if (file->vnode->ref_count != 1) { rc = 0; } else { device = (rt_device_t)file->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->data) { device = (rt_device_t)file->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->data) { device = (rt_device_t)file->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->data) { device = (rt_device_t)file->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->data) { device = (rt_device_t)file->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);