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

354 lines
8.1 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 <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)
{
rt_base_t level = 0;
struct rt_device *iodev = RT_NULL;
level = rt_hw_interrupt_disable();
iodev = console_dev.io_dev;
rt_hw_interrupt_enable(level);
return iodev;
}
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_hw_interrupt_disable();
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_hw_interrupt_enable(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_hw_interrupt_disable();
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_hw_interrupt_enable(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;
}