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

381 lines
9.3 KiB
C

/*
* Copyright (c) 2006-2021, 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_driver;
static struct tty_struct pts_drivers[PTY_PTS_SIZE];
static int pts_index = 0;
static int pts_register(struct tty_struct *ptm_drv, struct tty_struct *pts_drv, 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_drivers[i].init_flag == TTY_INIT_FLAG_NONE)
{
pts_drivers[i].init_flag = TTY_INIT_FLAG_ALLOCED;
return &pts_drivers[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;
int level = 0;
struct tty_struct *tty = RT_NULL;
RT_ASSERT(dev != RT_NULL);
tty = (struct tty_struct *)dev;
level = rt_hw_interrupt_disable();
RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_REGED);
tty->init_flag = TTY_INIT_FLAG_INITED;
rt_hw_interrupt_enable(level);
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_base_t level = 0;
rt_size_t len = 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_hw_interrupt_disable();
if (to->ldisc->ops->receive_buf)
{
len = to->ldisc->ops->receive_buf(to, (char *)buffer, size);
}
rt_hw_interrupt_enable(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_fd *fd)
{
int ret = 0;
struct tty_struct *tty = RT_NULL;
struct tty_struct *pts_drv = RT_NULL;
struct tty_ldisc *ld = RT_NULL;
struct rt_lwp *lwp = RT_NULL;
struct rt_wqueue *wq = RT_NULL;
tty = (struct tty_struct *)fd->vnode->data;
RT_ASSERT(tty != RT_NULL);
pts_drv = find_freepts();
if (pts_drv == RT_NULL)
{
LOG_E("free pts driver find fail\n");
return -1;
}
ret = pts_register(tty, pts_drv, pts_index);
if (ret < 0)
{
LOG_E("pts register fail\n");
rt_free(pts_drv);
return -1;
}
pts_index++;
lwp = (struct rt_lwp *)(rt_thread_self()->lwp);
wq = wait_queue_get(lwp, tty);
pts_drv->wait_queue = *wq;
tty->other_struct = pts_drv;
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 *ptm_drv, struct tty_struct *pts_drv, int pts_index)
{
rt_err_t ret = RT_EOK;
rt_base_t level = 0;
struct rt_device *device = RT_NULL;
char name[20];
RT_ASSERT(ptm_drv!=RT_NULL);
level = rt_hw_interrupt_disable();
if (pts_drv->init_flag != TTY_INIT_FLAG_ALLOCED)
{
LOG_E("pts%d has been registered\n", pts_index);
ret = (-RT_EBUSY);
goto _exit;
}
device = &pts_drv->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;
goto _exit;
}
#ifdef RT_USING_POSIX_DEVIO
/* set fops */
tty_set_fops(&pts_fops);
device->fops = &pts_fops;
#endif
pts_drv->type = TTY_DRIVER_TYPE_PTY;
pts_drv->subtype = PTY_TYPE_SLAVE;
pts_drv->pgrp = -1;
pts_drv->session = -1;
pts_drv->foreground = RT_NULL;
pts_drv->index = pts_index;
pts_drv->pts_lock = 1;
rt_wqueue_init(&pts_drv->wait_queue);
tty_ldisc_init(pts_drv);
extern struct termios tty_std_termios;
pts_drv->init_termios = tty_std_termios;
pts_drv->init_termios.c_cflag = B38400 | CS8 | CREAD;
pts_drv->init_termios.c_lflag |= ECHO | ICANON;
pts_drv->init_termios.c_oflag |= ONLCR;
pts_drv->init_termios.__c_ispeed = 38400;
pts_drv->init_termios.__c_ospeed = 38400;
pts_drv->other_struct = ptm_drv;
pts_drv->init_flag = TTY_INIT_FLAG_REGED;
_exit:
rt_hw_interrupt_enable(level);
return ret;
}
static int ptmx_register(void)
{
rt_base_t level = 0;
rt_err_t ret = RT_EOK;
struct rt_device *device = RT_NULL;
struct tty_struct *ptm_drv = &ptm_driver;
level = rt_hw_interrupt_disable();
RT_ASSERT(ptm_drv->init_flag == TTY_INIT_FLAG_NONE);
device = &(ptm_drv->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;
goto _exit;
}
#ifdef RT_USING_POSIX_DEVIO
/* set fops */
tty_set_fops(&ptmx_fops);
ptmx_fops.open = ptmx_open;
device->fops = &ptmx_fops;
#endif
ptm_drv->type = TTY_DRIVER_TYPE_PTY;
ptm_drv->subtype = PTY_TYPE_MASTER;
ptm_drv->head = rt_calloc(1, sizeof(struct tty_node));
if (!ptm_drv->head)
{
return -RT_ENOMEM;
}
tty_initstack(ptm_drv->head);
ptm_drv->pgrp = -1;
ptm_drv->session = -1;
ptm_drv->foreground = RT_NULL;
rt_wqueue_init(&ptm_drv->wait_queue);
tty_ldisc_init(ptm_drv);
extern struct termios tty_std_termios;
ptm_drv->init_termios.c_iflag = 0;
ptm_drv->init_termios.c_oflag = 0;
ptm_drv->init_termios.c_cflag = B38400 | CS8 | CREAD;
ptm_drv->init_termios.c_lflag = 0;
ptm_drv->init_termios.__c_ispeed = 38400;
ptm_drv->init_termios.__c_ospeed = 38400;
ptm_drv->init_flag = TTY_INIT_FLAG_REGED;
_exit:
rt_hw_interrupt_enable(level);
return ret;
}
INIT_DEVICE_EXPORT(ptmx_register);