351 lines
7.7 KiB
C
351 lines
7.7 KiB
C
/*
|
|
* 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->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);
|