添加rtthread相关代码
This commit is contained in:
		
							
								
								
									
										24
									
								
								riscv/rtthread/components/drivers/serial/SConscript
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										24
									
								
								riscv/rtthread/components/drivers/serial/SConscript
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| from building import * | ||||
|  | ||||
| cwd = GetCurrentDir() | ||||
| CPPPATH = [cwd + '/../include'] | ||||
| group = [] | ||||
| 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: | ||||
|     src += ['serial.c'] | ||||
|  | ||||
| if GetDepend(['RT_USING_DM']): | ||||
|     src += ['serial_dm.c'] | ||||
|  | ||||
| group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) | ||||
|  | ||||
| Return('group') | ||||
							
								
								
									
										1547
									
								
								riscv/rtthread/components/drivers/serial/serial.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1547
									
								
								riscv/rtthread/components/drivers/serial/serial.c
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										154
									
								
								riscv/rtthread/components/drivers/serial/serial_dm.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										154
									
								
								riscv/rtthread/components/drivers/serial/serial_dm.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| /* | ||||
|  * Copyright (c) 2006-2022, RT-Thread Development Team | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  * | ||||
|  * Change Logs: | ||||
|  * Date           Author       Notes | ||||
|  * 2022-11-16     GuEe-GUI     first version | ||||
|  */ | ||||
|  | ||||
| #include <rtatomic.h> | ||||
| #include "serial_dm.h" | ||||
|  | ||||
| int serial_dev_set_name(struct rt_serial_device *sdev) | ||||
| { | ||||
|     int id = -1; | ||||
|     static int uid_min = -1; | ||||
|     static volatile rt_atomic_t uid = 0; | ||||
|  | ||||
|     RT_ASSERT(sdev != RT_NULL); | ||||
|  | ||||
| #ifdef RT_USING_OFW | ||||
|     if (sdev->parent.ofw_node) | ||||
|     { | ||||
|         id = rt_ofw_get_alias_id(sdev->parent.ofw_node, "serial"); | ||||
|  | ||||
|         if (id < 0) | ||||
|         { | ||||
|             id = rt_ofw_get_alias_id(sdev->parent.ofw_node, "uart"); | ||||
|         } | ||||
|  | ||||
|         if (uid_min < 0) | ||||
|         { | ||||
|             uid_min = rt_ofw_get_alias_last_id("serial"); | ||||
|  | ||||
|             if (uid_min < 0) | ||||
|             { | ||||
|                 uid_min = rt_ofw_get_alias_last_id("uart"); | ||||
|             } | ||||
|  | ||||
|             uid_min = uid_min < 0 ? 0 : (uid_min + 1); | ||||
|  | ||||
|             rt_hw_atomic_store(&uid, uid_min); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (id < 0) | ||||
|     { | ||||
|         id = (int)rt_hw_atomic_add(&uid, 1); | ||||
|     } | ||||
|  | ||||
|     return rt_dm_dev_set_name(&sdev->parent, "uart%u", id); | ||||
| } | ||||
|  | ||||
| void *serial_base_from_args(char *str) | ||||
| { | ||||
|     rt_ubase_t base = 0; | ||||
|  | ||||
|     while (*str && !(*str == 'x' || *str == 'X')) | ||||
|     { | ||||
|         ++str; | ||||
|     } | ||||
|     ++str; | ||||
|  | ||||
|     /* The str may get from bootargs that we need check it */ | ||||
|     while (*str) | ||||
|     { | ||||
|         if ((*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) | ||||
|         { | ||||
|             base = (base << 4) | (((*str | ' ') - 'a') + 10); | ||||
|         } | ||||
|         else if (*str >= '0' && *str <= '9') | ||||
|         { | ||||
|             base = (base << 4) | (*str - '0'); | ||||
|         } | ||||
|         else break; | ||||
|  | ||||
|         ++str; | ||||
|     } | ||||
|  | ||||
|     return (void *)base; | ||||
| } | ||||
|  | ||||
| struct serial_configure serial_cfg_from_args(char *str) | ||||
| { | ||||
|     struct serial_configure cfg = RT_SERIAL_CONFIG_DEFAULT; | ||||
|  | ||||
|     /* Format baudrate/parity/bits/flow (BBBBPNF), Default is 115200n8 */ | ||||
|     if (str && *str) | ||||
|     { | ||||
|         rt_uint32_t baudrate = 0; | ||||
|  | ||||
|         /* BBBB is the speed */ | ||||
|         while (*str && (*str >= '0' && *str <= '9')) | ||||
|         { | ||||
|             baudrate *= 10; | ||||
|             baudrate += *str - '0'; | ||||
|             ++str; | ||||
|         } | ||||
|  | ||||
|         if (baudrate) | ||||
|         { | ||||
|             cfg.baud_rate = baudrate; | ||||
|         } | ||||
|  | ||||
|         /* P is parity (n/o/e) */ | ||||
|         switch (*str) | ||||
|         { | ||||
|         case 'n': | ||||
|             cfg.parity = PARITY_NONE; | ||||
|             break; | ||||
|         case 'o': | ||||
|             cfg.parity = PARITY_ODD; | ||||
|             break; | ||||
|         case 'e': | ||||
|             cfg.parity = PARITY_EVEN; | ||||
|             break; | ||||
|         default: | ||||
|             --str; | ||||
|             break; | ||||
|         } | ||||
|         ++str; | ||||
|  | ||||
|         /* N is number of bits */ | ||||
|         if (*str && (*str >= '0' && *str <= '9')) | ||||
|         { | ||||
|             cfg.data_bits = *str - '0'; | ||||
|             ++str; | ||||
|         } | ||||
|  | ||||
|         /* F is flow ontrol ('r' for RTS) */ | ||||
|         if (*str) | ||||
|         { | ||||
|             cfg.flowcontrol = (*str == 'r' ? RT_SERIAL_FLOWCONTROL_CTSRTS : RT_SERIAL_FLOWCONTROL_NONE); | ||||
|             ++str; | ||||
|         } | ||||
|  | ||||
|     #ifdef RT_USING_OFW | ||||
|         if (*str == '\0') | ||||
|         { | ||||
|             const char earlycon_magic[] = { 'O', 'F', 'W', '\0' }; | ||||
|  | ||||
|             if (!rt_strcmp(++str, earlycon_magic)) | ||||
|             { | ||||
|                 /* Is OFW earlycon, we should ACK it */ | ||||
|                 rt_memset(str, 0, RT_ARRAY_SIZE(earlycon_magic)); | ||||
|             } | ||||
|         } | ||||
|     #endif | ||||
|     } | ||||
|  | ||||
|     return cfg; | ||||
| } | ||||
							
								
								
									
										29
									
								
								riscv/rtthread/components/drivers/serial/serial_dm.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										29
									
								
								riscv/rtthread/components/drivers/serial/serial_dm.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * Copyright (c) 2006-2022, RT-Thread Development Team | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  * | ||||
|  * Change Logs: | ||||
|  * Date           Author       Notes | ||||
|  * 2022-11-16     GuEe-GUI     first version | ||||
|  */ | ||||
|  | ||||
| #ifndef __SERIAL_DM_H__ | ||||
| #define __SERIAL_DM_H__ | ||||
|  | ||||
| #include <rtthread.h> | ||||
| #include <rtdevice.h> | ||||
| #include <string.h> | ||||
|  | ||||
| int serial_dev_set_name(struct rt_serial_device *sdev); | ||||
|  | ||||
| void *serial_base_from_args(char *str); | ||||
| struct serial_configure serial_cfg_from_args(char *str); | ||||
|  | ||||
| #define serial_for_each_args(arg, args)                                 \ | ||||
|     for (char *context = (arg = (typeof(arg))args, (void *)RT_NULL),    \ | ||||
|         *context_end = rt_strchrnul((char *)args, ' ');                 \ | ||||
|          (arg = strtok_r(arg, ",", &context)) && arg < context_end;     \ | ||||
|          arg = RT_NULL) | ||||
|  | ||||
| #endif /* __SERIAL_DM_H__ */ | ||||
							
								
								
									
										330
									
								
								riscv/rtthread/components/drivers/serial/serial_tty.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										330
									
								
								riscv/rtthread/components/drivers/serial/serial_tty.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,330 @@ | ||||
| /* | ||||
|  * 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) | ||||
|     { | ||||
|         case TCSETS: | ||||
|         case TCSETSW: | ||||
|         case TCSETSF: | ||||
|             RT_ASSERT(tp->t_devswsoftc); | ||||
|             struct serial_tty_context *softc = (struct serial_tty_context *)(tp->t_devswsoftc); | ||||
|             struct rt_serial_device *serial = softc->parent; | ||||
|             struct termios *termios = (struct termios *)data; | ||||
|             rt_device_control(&(serial->parent), cmd, termios); | ||||
|             error = -ENOIOCTL; | ||||
|         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); | ||||
							
								
								
									
										1633
									
								
								riscv/rtthread/components/drivers/serial/serial_v2.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1633
									
								
								riscv/rtthread/components/drivers/serial/serial_v2.c
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user