rt-thread/components/drivers/tty/pty.c

327 lines
7.7 KiB
C

/*
* 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);