diff options
| author | Shell <[email protected]> | 2024-03-28 23:42:56 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-03-28 23:42:56 +0800 |
| commit | 83e95bdff4c48c661b50e1eec56d5fd293b28fbb (patch) | |
| tree | 9e0c2065950f0804e5009da71c8e0f590224cade /components/drivers/serial | |
| parent | 40e26f4909fa61ada775ba86b44d8300aaf5d250 (diff) | |
sync smart & dfs (#8672)
Signed-off-by: xqyjlj <[email protected]>
Signed-off-by: Shell <[email protected]>
Co-authored-by: xqyjlj <[email protected]>
Diffstat (limited to 'components/drivers/serial')
| -rw-r--r-- | components/drivers/serial/SConscript | 3 | ||||
| -rw-r--r-- | components/drivers/serial/serial.c | 36 | ||||
| -rw-r--r-- | components/drivers/serial/serial_tty.c | 321 |
3 files changed, 352 insertions, 8 deletions
diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript index 2211134ef3..7ee41a4331 100644 --- a/components/drivers/serial/SConscript +++ b/components/drivers/serial/SConscript @@ -8,6 +8,9 @@ src = [] if not GetDepend(['RT_USING_SERIAL']): Return('group') +if GetDepend(['RT_USING_SMART']): + src += Glob('serial_tty.c') + if GetDepend(['RT_USING_SERIAL_V2']): src += ['serial_v2.c'] else: diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index 0382f3c856..8604c78db7 100644 --- a/components/drivers/serial/serial.c +++ b/components/drivers/serial/serial.c @@ -909,7 +909,7 @@ static rt_ssize_t rt_serial_write(struct rt_device *dev, } } -#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_TTY) +#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_SMART) struct speed_baudrate_item { speed_t speed; @@ -1058,7 +1058,7 @@ static rt_err_t rt_serial_control(struct rt_device *dev, } break; #ifdef RT_USING_POSIX_STDIO -#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_TTY) +#if defined(RT_USING_POSIX_TERMIOS) && !defined(RT_USING_SMART) case TCGETA: { struct termios *tio = (struct termios*)args; @@ -1315,9 +1315,21 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, device->fops = &_serial_fops; #endif +#if defined(RT_USING_SMART) + rt_hw_serial_register_tty(serial); +#endif + return ret; } +#if defined(RT_USING_SMART) && defined(LWP_DEBUG) +static volatile int _early_input = 0; +int lwp_startup_debug_request(void) +{ + return _early_input; +} +#endif + /* ISR for serial interrupt */ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) { @@ -1360,8 +1372,17 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) rt_spin_unlock_irqrestore(&(serial->spinlock), level); } - /* invoke callback */ - if (serial->parent.rx_indicate != RT_NULL) + /** + * Invoke callback. + * First try notify if any, and if notify is existed, rx_indicate() + * is not callback. This seperate the priority and makes the reuse + * of same serial device reasonable for RT console. + */ + if (serial->rx_notify.notify) + { + serial->rx_notify.notify(serial->rx_notify.dev); + } + else if (serial->parent.rx_indicate != RT_NULL) { rt_size_t rx_length; @@ -1376,10 +1397,9 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) serial->parent.rx_indicate(&serial->parent, rx_length); } } - if (serial->rx_notify.notify) - { - serial->rx_notify.notify(serial->rx_notify.dev); - } + #if defined(RT_USING_SMART) && defined(LWP_DEBUG) + _early_input = 1; + #endif break; } case RT_SERIAL_EVENT_TX_DONE: diff --git a/components/drivers/serial/serial_tty.c b/components/drivers/serial/serial_tty.c new file mode 100644 index 0000000000..311a422dce --- /dev/null +++ b/components/drivers/serial/serial_tty.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-21 Shell init ver. + */ + +#define DBG_TAG "drivers.serial" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#include <rthw.h> +#include <rtthread.h> +#include <rtdevice.h> +#include <terminal/terminal.h> + +#define TTY_NAME_PREFIX "S" /* (S)erial */ +#define LWP_TTY_WORKQUEUE_PRIORITY 3 + +struct serial_tty_context +{ + struct rt_serial_device *parent; + struct rt_device_notify backup_notify; + struct rt_work work; +}; + +static struct rt_workqueue *_ttyworkq; /* system work queue */ + +static rt_atomic_t _device_id_counter = 0; + +static long get_dec_digits(rt_ubase_t val) +{ + long result = 1; + while (1) + { + if (val < 10) + return result; + if (val < 100) + return result + 1; + if (val < 1000) + return result + 2; + if (val < 10000) + return result + 3; + val /= 10000U; + result += 4; + } + return result; +} + +static char *alloc_device_name(void) +{ + char *tty_dev_name; + unsigned int devid = rt_atomic_add(&_device_id_counter, 1); + long digits_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */ + + get_dec_digits(devid) + 1; /* tailing \0 */ + + tty_dev_name = rt_malloc(digits_len); + if (tty_dev_name) + rt_sprintf(tty_dev_name, "%s%u", TTY_NAME_PREFIX, devid); + return tty_dev_name; +} + +static void _tty_rx_notify(struct rt_device *device) +{ + lwp_tty_t tp; + struct serial_tty_context *softc; + + tp = rt_container_of(device, struct lwp_tty, parent); + RT_ASSERT(tp); + + softc = tty_softc(tp); + + if (_ttyworkq) + rt_workqueue_submit_work(_ttyworkq, &softc->work, 0); +} + +static void _tty_rx_worker(struct rt_work *work, void *data) +{ + char input; + rt_ssize_t readbytes; + lwp_tty_t tp = data; + struct serial_tty_context *softc; + struct rt_serial_device *serial; + + tty_lock(tp); + + while (1) + { + softc = tty_softc(tp); + serial = softc->parent; + readbytes = rt_device_read(&serial->parent, -1, &input, 1); + if (readbytes != 1) + { + break; + } + + ttydisc_rint(tp, input, 0); + } + + ttydisc_rint_done(tp); + tty_unlock(tp); +} + +rt_inline void _setup_serial(struct rt_serial_device *serial, lwp_tty_t tp, + struct serial_tty_context *softc) +{ + struct rt_device_notify notify; + + softc->backup_notify = serial->rx_notify; + notify.dev = &tp->parent; + notify.notify = _tty_rx_notify; + + rt_device_init(&serial->parent); + + rt_work_init(&softc->work, _tty_rx_worker, tp); + rt_device_control(&serial->parent, RT_DEVICE_CTRL_NOTIFY_SET, ¬ify); +} + +rt_inline void _restore_serial(struct rt_serial_device *serial, lwp_tty_t tp, + struct serial_tty_context *softc) +{ + rt_device_control(&serial->parent, RT_DEVICE_CTRL_NOTIFY_SET, &softc->backup_notify); +} + +static int _serial_isbusy(struct rt_serial_device *serial) +{ + rt_thread_t user_thread = rt_console_current_user(); + rt_thread_t self_thread = rt_thread_self(); + + return rt_console_get_device() == &serial->parent && + (user_thread != RT_NULL && user_thread != self_thread); +} + +static void serial_tty_outwakeup(struct lwp_tty *tp) +{ + char out_char; + int len; + struct serial_tty_context *context = tty_softc(tp); + struct rt_serial_device *device; + + if (!context || !context->parent) + { + LOG_E("%s: Data corruption", __func__); + return; + } + + device = context->parent; + + if (_serial_isbusy(device)) + { + return ; + } + + while ((len = ttydisc_getc(tp, &out_char, sizeof(out_char))) != 0) + { + device->ops->putc(device, out_char); + + /* discard remaining if emergency output is happened */ + if (_serial_isbusy(device)) + { + break; + } + } +} + +static int serial_tty_open(struct lwp_tty *tp) +{ + struct serial_tty_context *softc; + struct rt_serial_device *serial; + rt_err_t error; + int oflags; + + softc = tty_softc(tp); + serial = softc->parent; + + LOG_D("%s", __func__); + + rt_device_control(&serial->parent, RT_DEVICE_CTRL_CONSOLE_OFLAG, &oflags); + + error = rt_device_open(&serial->parent, oflags); + + if (!error) + { + /** + * to avoid driver accesssing null data, + * these are setup only after tty is registered + */ + _setup_serial(serial, tp, softc); + } + return error; +} + +static void serial_tty_close(struct lwp_tty *tp) +{ + struct serial_tty_context *softc; + struct rt_serial_device *serial; + softc = tty_softc(tp); + serial = softc->parent; + + LOG_D("%s", __func__); + + _restore_serial(serial, tp, softc); + rt_device_close(&serial->parent); +} + +static int serial_tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, + struct rt_thread *td) +{ + int error; + switch (cmd) + { + default: + /** + * Note: for the most case, we don't let serial layer handle ioctl, + * for that they can't act properly regarding to the process + * management system, since it is unawared of that. So a ENOSYS is + * returned and caused the TTY layer to handle ioctl itself. + */ + error = -ENOSYS; + break; + } + return error; +} + +static struct lwp_ttydevsw serial_ttydevsw = { + .tsw_open = serial_tty_open, + .tsw_close = serial_tty_close, + .tsw_ioctl = serial_tty_ioctl, + .tsw_outwakeup = serial_tty_outwakeup, +}; + +rt_err_t rt_hw_serial_register_tty(struct rt_serial_device *serial) +{ + rt_err_t rc; + lwp_tty_t tty; + char *dev_name; + struct serial_tty_context *softc; + + if (serial->rx_notify.dev) + { + return -RT_EBUSY; + } + + softc = rt_malloc(sizeof(struct serial_tty_context)); + if (softc) + { + dev_name = alloc_device_name(); + if (dev_name) + { + softc->parent = serial; + tty = lwp_tty_create(&serial_ttydevsw, softc); + if (tty) + { + rc = lwp_tty_register(tty, dev_name); + + if (rc != RT_EOK) + { + rt_free(tty); + rt_free(softc); + } + } + else + { + rt_free(softc); + rc = -RT_ENOMEM; + } + + rt_free(dev_name); + } + else + { + rt_free(softc); + rc = -RT_ENOMEM; + } + } + else + { + rc = -RT_ENOMEM; + } + + return rc; +} + +rt_err_t rt_hw_serial_unregister_tty(struct rt_serial_device *serial) +{ + rt_device_t tty_dev; + lwp_tty_t tp; + struct serial_tty_context *softc; + + tty_dev = serial->rx_notify.dev; + tp = rt_container_of(tty_dev, struct lwp_tty, parent); + + /* restore serial setting */ + softc = tty_softc(tp); + serial->rx_notify = softc->backup_notify; + + tty_rel_gone(tp); + + /* device unregister? */ + rt_device_destroy(&tp->parent); + /* resource free? */ + lwp_tty_delete(tp); + + return RT_EOK; +} + +static int _tty_workqueue_init(void) +{ + if (_ttyworkq != RT_NULL) + return RT_EOK; + + _ttyworkq = rt_workqueue_create("ttyworkq", RT_SYSTEM_WORKQUEUE_STACKSIZE, + LWP_TTY_WORKQUEUE_PRIORITY); + RT_ASSERT(_ttyworkq != RT_NULL); + + return RT_EOK; +} +INIT_PREV_EXPORT(_tty_workqueue_init); |
