UART驱动学习三(TTY驱动部分源码解析)

目录

  • 全局框架图
  • 一、tty_io.c 分析
    • 1. 关键数据结构和定义
    • 2. 文件操作结构体
    • 3. 初始化和注册
    • 4. 读写操作
    • 5. 挂起和恢复
    • 6. 信号处理
    • 7. 设备类
    • 8. 控制台通知
    • 9. 辅助函数
    • 10. 代码功能
    • 11. 带有注释的部分tty_io.c源码
  • 二、tty_ldisc.c 分析
    • 1. 关键数据结构和定义
    • 2. 行规程操作函数
    • 3. 引用计数和模块加载
    • 4. 序列化和锁
    • 5. 行规程引用和解锁
    • 6. 行规程锁定和解锁
    • 7. 行规程挂起和恢复
    • 8. 行规程设置和释放
    • 9. 行规程初始化和清理
    • 10. 系统控制(sysctl)
    • 11. 行规程操作的辅助函数
    • 12. 行规程队列处理
    • 13. 行规程配置
    • 14. 代码结构
  • 三、n_tty.c 分析
    • 1. 关键数据结构
    • 2. 行规程操作函数
    • 3. 辅助函数
    • 4. 行规程初始化和注册
    • 5. 行规程继承
    • 6. 行规程锁定和解锁
    • 7. 行规程数据处理
    • 8. 行规程状态检查
    • 9. 行规程辅助宏
  • 四、serial_core.c
    • 1. 主要数据结构
    • 2. 关键函数和操作
    • 3. 辅助函数
    • 4. 行规程(Line Discipline)相关函数
    • 5. 注册和注销驱动
    • 6. 添加和移除端口
    • 7. 控制台支持
    • 8. 电源管理
    • 9. 串行控制台支持
    • 10. 设备树支持
    • 11. 系统控制台序列支持

全局框架图

在这里插入图片描述

一、tty_io.c 分析

1. 关键数据结构和定义

  • struct tty_struct:定义了与TTY设备相关的所有信

  • struct ktermios tty_std_termios:用于存储终端线的配置信息,如输入输出标志、控制标志等。

2. 文件操作结构体

  • tty_fops:定义了与TTY设备相关的系统调用操作,包括:
    • open:打开TTY设备。
    • release:关闭TTY设备。
    • read_iter:从TTY设备读取数据。
    • write_iter:向TTY设备写入数据。
    • poll:检查TTY设备的状态。
    • unlocked_ioctl:执行与TTY设备相关的控制操作。

3. 初始化和注册

  • tty_init:初始化TTY子系统。
  • tty_register_driver:注册TTY设备驱动。
  • tty_unregister_driver:注销TTY设备驱动。

4. 读写操作

  • tty_read:实现TTY设备的读取操作。
  • tty_write:实现TTY设备的写入操作。

5. 挂起和恢复

  • stop_tty:挂起TTY设备。
  • start_tty:恢复TTY设备。

6. 信号处理

  • do_tty_hangup:处理TTY设备的挂起事件。

7. 设备类

  • tty_class:定义了Linux设备模型中的一个类,用于管理TTY设备。

8. 控制台通知

  • console_sysfs_notify:通知系统关于控制台设备状态的变化。

9. 辅助函数

  • tty_paranoia_check:在调试模式下检查TTY设备的一致性。
  • tty_ldisc_init:初始化行纪律。
  • tty_ldisc_release:释放行纪律。
  • tty_std_termios:定义了默认的终端线配置。

10. 代码功能

  1. 设备初始化:分配和初始化TTY结构,设置默认值。
  2. 设备注册:在系统设备模型中注册TTY设备。
  3. 文件操作:定义了文件操作函数,处理用户空间的读写请求。
  4. 控制操作:处理ioctl请求,如设置终端类型、获取/设置窗口大小等。
  5. 挂起和恢复:控制TTY设备的挂起和恢复状态。
  6. 信号处理:处理如挂起信号等信号。
  7. 设备类:创建和管理TTY设备类。
  8. 通知:在控制台设备状态变化时通知系统。

11. 带有注释的部分tty_io.c源码

struct ktermios tty_std_termios = {/* for the benefit of tty drivers  */.c_iflag = ICRNL | IXON,.c_oflag = OPOST | ONLCR,.c_cflag = B38400 | CS8 | CREAD | HUPCL,.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE |IEXTEN,.c_cc = INIT_C_CC,.c_ispeed = 38400,.c_ospeed = 38400,/* .c_line = N_TTY, */
};/*** 定义串行终端设备的文件操作结构体* 该结构体包含了对串行终端设备进行各种操作的函数指针,包括读、写、poll、ioctl等操作*/
static const struct file_operations tty_fops = {.llseek = no_llseek,  // 禁用llseek操作,串行设备不支持随机访问.read_iter = tty_read,  // 读操作函数指针.write_iter = tty_write,  // 写操作函数指针.splice_read = generic_file_splice_read,  // 链接读操作函数指针.splice_write = iter_file_splice_write,  // 链接写操作函数指针.poll = tty_poll,  // poll操作函数指针,用于等待设备变为可读/可写.unlocked_ioctl = tty_ioctl,  // ioctl操作函数指针,用于设备控制.compat_ioctl = tty_compat_ioctl,  // 兼容ioctl操作函数指针,用于处理32位和64位进程的ioctl兼容性问题.open = tty_open,  // 打开设备的函数指针.release = tty_release,  // 关闭设备的函数指针.fasync = tty_fasync,  // 异步读/写控制函数指针.show_fdinfo = tty_show_fdinfo,  // 显示文件描述符信息的函数指针
};/*** 定义控制台设备的文件操作结构体* 该结构体基于串行终端设备的文件操作,但重写了写操作和某些其他函数指针,以适应控制台设备的特殊需求*/
static const struct file_operations console_fops = {.llseek = no_llseek,  // 禁用llseek操作,控制台设备同样不支持随机访问.read_iter = tty_read,  // 读操作函数指针,与串行终端设备相同.write_iter = redirected_tty_write,  // 写操作函数指针,重写以适应控制台设备的特殊写操作需求.splice_read = generic_file_splice_read,  // 链接读操作函数指针,与串行终端设备相同.splice_write = iter_file_splice_write,  // 链接写操作函数指针,与串行终端设备相同.poll = tty_poll,  // poll操作函数指针,与串行终端设备相同.unlocked_ioctl = tty_ioctl,  // ioctl操作函数指针,与串行终端设备相同.compat_ioctl = tty_compat_ioctl,  // 兼容ioctl操作函数指针,与串行终端设备相同.open = tty_open,  // 打开设备的函数指针,与串行终端设备相同.release = tty_release,  // 关闭设备的函数指针,与串行终端设备相同.fasync = tty_fasync,  // 异步读/写控制函数指针,与串行终端设备相同
};
static const struct file_operations hung_up_tty_fops = {.llseek = no_llseek,.read_iter = hung_up_tty_read,.write_iter = hung_up_tty_write,.poll = hung_up_tty_poll,.unlocked_ioctl = hung_up_tty_ioctl,.compat_ioctl = hung_up_tty_compat_ioctl,.release = tty_release,.fasync = hung_up_tty_fasync,
};/*** tty_hangup - 触发挂起事件* @tty: 要挂起的tty设备** 当这个线上发生载波丢失(实际或虚拟的)时,调度一个挂起序列在该事件后运行。* 触发挂起事件是为了处理连接的终止,确保所有相关的资源被正确释放。*/
void tty_hangup(struct tty_struct *tty)
{// 调试信息,表明挂起事件的发生tty_debug_hangup(tty, "hangup\n");// 调度挂起工作,开始挂起序列schedule_work(&tty->hangup_work);
}// 导出该符号,使得其他模块可以调用这个函数
EXPORT_SYMBOL(tty_hangup);/** 实现“安全注意键”(Secure Attention Key)功能,当用户按下安全注意键时,* 杀死与此终端(tty)关联的所有进程。设计用于超级偏执的应用程序,* 详见橙皮书(Orange Book)以获取更多详情。 ** 该代码可以更优雅;理想情况下,应先发送一个HUP信号,等待几秒钟,* 然后发送一个INT信号,最后发送KILL信号。但这样需要与init进程协调,* 确保在新的getty被允许生成之前,与当前tty关联的所有进程都已终止。** 当前代码存在一个严重的漏洞 - 它没有捕获“飞行中的文件”(files in flight)。* 我们可能会通过AF_UNIX套接字发送描述符,关闭它,并稍后从套接字中获取。* 这需要修复(FIXME)。** 一个严重的bug:do_SAK在中断上下文中被调用。这可能导致死锁。* 我们将其转移到进程上下文中处理。 AKPM - 2001年3月16日*/
void __do_SAK(struct tty_struct *tty)
{
#ifdef TTY_SOFT_SAK// 当启用软件SAK时,直接挂起终端tty_hangup(tty);
#else// 获取与该tty关联的会话IDstruct task_struct *g, *p;struct pid *session;int i;unsigned long flags;// 如果tty为空,则直接返回if (!tty)return;// 以原子方式获取会话IDspin_lock_irqsave(&tty->ctrl_lock, flags);session = get_pid(tty->session);spin_unlock_irqrestore(&tty->ctrl_lock, flags);// 清空终端的输入输出缓冲区tty_ldisc_flush(tty);tty_driver_flush_buffer(tty);// 加锁以保护任务列表read_lock(&tasklist_lock);// 杀死整个会话中的所有进程do_each_pid_task(session, PIDTYPE_SID, p){tty_notice(tty, "SAK: killed process %d (%s): by session\n",task_pid_nr(p), p->comm);group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);}while_each_pid_task(session, PIDTYPE_SID, p);// 杀死任何打开了该tty的进程do_each_thread(g, p){if (p->signal->tty == tty) {tty_notice(tty,"SAK: killed process %d (%s): by controlling tty\n",task_pid_nr(p), p->comm);group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,PIDTYPE_SID);continue;}task_lock(p);i = iterate_fd(p->files, 0, this_tty, tty);if (i != 0) {tty_notice(tty,"SAK: killed process %d (%s): by fd#%d\n",task_pid_nr(p), p->comm, i - 1);group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,PIDTYPE_SID);}task_unlock(p);}while_each_thread(g, p);// 解锁任务列表read_unlock(&tasklist_lock);// 释放会话ID的引用put_pid(session);
#endif
}/** 这里的 tty 队列处理(tq)稍微有些竞态条件问题 —— tty->SAK_work 可能已经被排队。* 幸运的是,我们不需要担心这个问题,因为如果 ->SAK_work 已经被排队,* 我们写入它的值将与它已经拥有的值相同。这确保了数据处理的一致性和正确性。--akpm*/
/*** 触发指定串行终端结构的信号异步键盘(SAK)处理。** @param tty 指向串行终端结构的指针,不能为空。** 此函数调度与 tty 结构关联的 SAK_work 以执行。如果输入参数 tty 为空指针,* 函数不做任何处理并立即返回。这是为了避免在 tty 结构指针无效时进行不必要的处理或潜在错误。*/
void do_SAK(struct tty_struct *tty)
{// 检查 tty 指针是否为空,如果为空,则不做任何处理并立即返回。if (!tty)return;// 调度与 tty 结构关联的 SAK_work 以执行。schedule_work(&tty->SAK_work);
}
// 导出 do_SAK 函数符号,允许其他模块或组件使用此函数。
EXPORT_SYMBOL(do_SAK);/*** tty_put_char - 向终端输出一个字符* @tty: 终端结构指针* @ch: 待输出的字符** 该函数通过终端的操作接口向终端设备输出一个字节。如果提供了 put_char 方法,则使用该方法;* 否则,使用 write 方法。返回成功输出的字符数。** 注意:驱动层中的特定 put_char 操作可能很快会被废弃。不要直接调用它,使用此函数。*/
int tty_put_char(struct tty_struct *tty, unsigned char ch)
{if (tty->ops->put_char)return tty->ops->put_char(tty, ch);return tty->ops->write(tty, &ch, 1);
}
EXPORT_SYMBOL_GPL(tty_put_char);/*** tty_register_device_attr - 注册一个tty设备* @driver: 描述tty设备的tty驱动程序* @index: 该tty设备在tty驱动程序中的索引* @device: 与此tty设备关联的struct device,如果不存在,则可以为NULL* @drvdata: 要设置给设备的驱动数据* @attr_grp: 要在设备上设置的属性组** 返回此tty设备的struct device指针,如果出错则返回ERR_PTR(-EFOO)** 如果tty驱动程序的标志设置了TTY_DRIVER_DYNAMIC_DEV位,则需要调用此函数来注册单个tty设备。* 如果没有设置该位,tty驱动程序不应调用此函数。** 锁定:??*/
struct device *tty_register_device_attr(struct tty_driver *driver,unsigned index, struct device *device,void *drvdata,const struct attribute_group **attr_grp)
{char name[64];dev_t devt = MKDEV(driver->major, driver->minor_start) + index;struct ktermios *tp;struct device *dev;int retval;// 检查索引有效性if (index >= driver->num) {pr_err("%s: Attempt to register invalid tty line number (%d)\n",driver->name, index);return ERR_PTR(-EINVAL);}// 生成设备名称if (driver->type == TTY_DRIVER_TYPE_PTY)pty_line_name(driver, index, name);elsetty_line_name(driver, index, name);// 分配并设置设备结构dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return ERR_PTR(-ENOMEM);dev->devt = devt;dev->class = tty_class;dev->parent = device;dev->release = tty_device_create_release;dev_set_name(dev, "%s", name);dev->groups = attr_grp;dev_set_drvdata(dev, drvdata);// 抑制uevent事件dev_set_uevent_suppress(dev, 1);// 注册设备retval = device_register(dev);if (retval)goto err_put;// 如果不是动态分配,则处理termios数据和字符设备注册if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {tp = driver->termios[index];if (tp) {driver->termios[index] = NULL;kfree(tp);}retval = tty_cdev_add(driver, devt, index, 1);if (retval)goto err_del;}// 启用uevent事件dev_set_uevent_suppress(dev, 0);kobject_uevent(&dev->kobj, KOBJ_ADD);return dev;err_del:device_del(dev);
err_put:put_device(dev);return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(tty_register_device_attr);/*** tty_unregister_device - 取消注册一个tty设备* @driver: 描述tty设备的tty驱动程序* @index: 该tty设备在tty驱动程序中的索引** 如果tty设备已通过tty_register_device()注册,则在设备移除时必须调用此函数。** 锁定:??*/
void tty_unregister_device(struct tty_driver *driver, unsigned index)
{// 销毁设备device_destroy(tty_class,MKDEV(driver->major, driver->minor_start) + index);if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {cdev_del(driver->cdevs[index]);driver->cdevs[index] = NULL;}
}
EXPORT_SYMBOL(tty_unregister_device);/*** tty_driver_kref_put - 释放tty驱动程序的引用计数* @driver: 指向tty驱动程序的指针** 此函数通过kref(空闲对象引用计数)机制来管理tty驱动程序的引用计数。* 当引用计数降到零时,调用destructor函数来释放相关资源。*/
void tty_driver_kref_put(struct tty_driver *driver)
{kref_put(&driver->kref, destruct_tty_driver);
}
EXPORT_SYMBOL(tty_driver_kref_put);/*** tty_set_operations - 设置tty驱动程序的操作集合* @driver: 指向tty驱动程序的指针* @op: 指向包含tty操作的结构的指针** 此函数用于将一组操作函数关联到tty驱动程序中,这些操作函数可以* 包括打开、关闭、读取、写入等设备控制操作。*/
void tty_set_operations(struct tty_driver *driver,const struct tty_operations *op)
{driver->ops = op;
};
EXPORT_SYMBOL(tty_set_operations);/*** put_tty_driver - 释放tty驱动程序的引用* @d: 指向tty驱动程序的指针** 这是一个方便函数,调用tty_driver_kref_put来释放tty驱动程序的引用。* 当不再需要使用tty驱动程序时,可以通过这个函数来释放其资源。*/
void put_tty_driver(struct tty_driver *d)
{tty_driver_kref_put(d);
}
EXPORT_SYMBOL(put_tty_driver);/** 该函数用于TTY驱动程序注册自身。** @param driver: 要注册的TTY驱动结构体指针* @return: 注册成功返回0,失败返回负数错误码*/int tty_register_driver(struct tty_driver *driver)
{// 错误处理码int error;// 用于循环迭代int i;// 设备标识符dev_t dev;// 设备结构体指针struct device *d;// 如果未指定主设备号,尝试动态分配设备号范围if (!driver->major) {// 尝试分配字符设备号范围error = alloc_chrdev_region(&dev, driver->minor_start,driver->num, driver->name);if (!error) {// 分配成功,更新主设备号和次设备号起始值driver->major = MAJOR(dev);driver->minor_start = MINOR(dev);}} else {// 如果指定了主设备号,直接注册指定的设备号dev = MKDEV(driver->major, driver->minor_start);error = register_chrdev_region(dev, driver->num, driver->name);}if (error < 0)goto err;// 对于动态分配设备号的情况,将字符设备添加到系统中if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {error = tty_cdev_add(driver, dev, 0, driver->num);if (error)goto err_unreg_char;}// 锁定互斥锁以确保线程安全地操作TTY驱动列表mutex_lock(&tty_mutex);// 将驱动添加到TTY驱动列表中list_add(&driver->tty_drivers, &tty_drivers);mutex_unlock(&tty_mutex);// 如果使用静态设备号,注册每个设备if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {for (i = 0; i < driver->num; i++) {d = tty_register_device(driver, i, NULL);if (IS_ERR(d)) {error = PTR_ERR(d);goto err_unreg_devs;}}}// 在/proc文件系统中注册与驱动相关的信息proc_tty_register_driver(driver);// 标记驱动已安装driver->flags |= TTY_DRIVER_INSTALLED;return 0;err_unreg_devs:// 如果设备注册失败,撤销之前的注册操作for (i--; i >= 0; i--)tty_unregister_device(driver, i);// 再次锁定互斥锁以从列表中移除驱动mutex_lock(&tty_mutex);list_del(&driver->tty_drivers);mutex_unlock(&tty_mutex);err_unreg_char:// 撤销字符设备号的注册unregister_chrdev_region(dev, driver->num);
err:// 返回错误码return error;
}
// 导出此符号以便系统其他部分调用此函数
EXPORT_SYMBOL(tty_register_driver);/** 该函数由 tty 驱动调用以注销自身。* 此函数主要执行清理工作,包括注销设备号,并从 tty 驱动列表中移除驱动。** 参数:*   driver: 要注销的 tty 驱动指针。** 返回值:*   0: 注销成功。*   -EBUSY: 驱动仍在使用中,无法注销。*/
int tty_unregister_driver(struct tty_driver *driver)
{
#if 0/* FIXME */// 检查驱动是否仍在使用,如果是,则返回错误表示驱动忙。if (driver->refcount)return -EBUSY;
#endif// 注销与 tty 驱动关联的字符设备区域。unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),driver->num);// 锁定互斥锁以确保线程安全地修改 tty 驱动列表。mutex_lock(&tty_mutex);// 从 tty 驱动列表中移除驱动。list_del(&driver->tty_drivers);// 解锁互斥锁以释放锁。mutex_unlock(&tty_mutex);// 返回 0 表示注销成功。return 0;
}
EXPORT_SYMBOL(tty_unregister_driver);/*** tty_class_init函数用于在Linux内核中初始化tty类* 它创建一个名为"tty"的类对象,并指定其devnode操作函数* * @return 返回0表示成功,非0表示出错*/
static int __init tty_class_init(void)
{// 创建一个名为"tty"的类,关联到当前模块tty_class = class_create(THIS_MODULE, "tty");if (IS_ERR(tty_class))return PTR_ERR(tty_class);// 指定tty类的devnode操作函数为tty_devnodetty_class->devnode = tty_devnode;return 0;
}
postcore_initcall(tty_class_init);/*** 向系统通知控制台设备的状态变化** 当控制台设备的状态发生改变时,通过sysfs_notify函数通知系统,以便系统可以更新其缓存或其他状态信息* 这对于维护系统对控制台设备的准确了解至关重要*/
void console_sysfs_notify(void)
{// 如果consdev指向的设备对象不为空,则通过sysfs_notify函数发送通知if (consdev)sysfs_notify(&consdev->kobj, NULL, "active");
}/** 初始化tty设备的函数。* 这个函数负责初始化剩余的tty设备,并进行内存分配、中断设置等操作。*/
int __init tty_init(void)
{// 初始化tty系统的控制参数tty_sysctl_init();// 初始化tty字符设备的cdev和文件操作cdev_init(&tty_cdev, &tty_fops);// 注册/tty设备if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)panic("Couldn't register /dev/tty driver\n");// 创建tty设备device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");// 初始化console字符设备的cdev和文件操作cdev_init(&console_cdev, &console_fops);// 注册/dev/console设备if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") <0)panic("Couldn't register /dev/console driver\n");// 创建console设备,如果出错则设consdev为NULLconsdev = device_create_with_groups(tty_class, NULL,MKDEV(TTYAUX_MAJOR, 1), NULL,cons_dev_groups, "console");if (IS_ERR(consdev))consdev = NULL;// 如果配置了虚拟终端,则初始化vty
#ifdef CONFIG_VTvty_init(&console_fops);
#endifreturn 0;
}

二、tty_ldisc.c 分析

1. 关键数据结构和定义

  • tty_ldisc_ops:定义了行规程的操作集,包括打开、关闭、读取、写入等操作。
  • tty_ldisc:表示一个行规程实例,包含指向其操作的指针。

2. 行规程操作函数

  • tty_register_ldisc:注册一个新的行规程。
  • tty_unregister_ldisc:注销一个行规程。
  • get_ldops:获取行规程的操作结构。
  • put_ldops:释放行规程的操作结构。

3. 引用计数和模块加载

  • tty_ldisc_get:获取行规程的引用,并尝试自动加载行规程模块。
  • tty_ldisc_put:释放行规程的引用。

4. 序列化和锁

  • tty_ldiscs_lock:用于保护行规程列表的锁。
  • tty_ldisc_sem:用于控制对行规程的访问。

5. 行规程引用和解锁

  • tty_ldisc_ref_wait:等待行规程的引用。
  • tty_ldisc_ref:获取行规程的引用。
  • tty_ldisc_deref:释放行规程的引用。

6. 行规程锁定和解锁

  • tty_ldisc_lock:锁定行规程。
  • tty_ldisc_unlock:解锁行规程。

7. 行规程挂起和恢复

  • tty_ldisc_hangup:处理行规程的挂起事件。
  • tty_ldisc_restore:恢复行规程。

8. 行规程设置和释放

  • tty_set_ldisc:设置TTY的行规程。
  • tty_ldisc_release:释放TTY的行规程。

9. 行规程初始化和清理

  • tty_ldisc_init:初始化TTY的行规程。
  • tty_ldisc_deinit:清理TTY的行规程。

10. 系统控制(sysctl)

  • tty_sysctl_init:初始化行规程的系统控制表。

11. 行规程操作的辅助函数

  • tty_ldisc_open:打开行规程。
  • tty_ldisc_close:关闭行规程。
  • tty_ldisc_failto:行规程失败回退。
  • tty_ldisc_setup:设置行规程。
  • tty_ldisc_kill:杀死行规程。

12. 行规程队列处理

  • tty_ldisc_flush:刷新行规程的队列。

13. 行规程配置

  • tty_set_termios_ldisc:设置TTY的termios结构体中的行规程字段。

14. 代码结构

  这段代码中涉及的结构关系主要围绕着TTY(Teletype)设备和行规程(line discipline)的管理。

  1. tty_struct:代表一个TTY设备实例。它包含了TTY设备的所有状态信息,包括指向行规程操作的指针。

  2. tty_ldisc:代表一个行规程实例。它包含了指向行规程操作的指针,以及一个指向tty_struct的指针,表示该行规程所属的TTY设备。

  3. tty_ldisc_ops:定义了行规程的操作集。这些操作包括打开、关闭、读取、写入等。每个行规程必须实现这些操作。

  4. tty_driver:代表一个TTY设备驱动。它包含了驱动操作的集合,以及驱动所管理的TTY设备的列表。

  5. tty_ldiscs:一个数组,包含了所有可能的行规程的操作指针。

  6. tty_ldiscs_lock:一个锁,用于保护对tty_ldiscs数组的访问,确保在注册或注销行规程时的线程安全。

  7. tty_ldisc_sem:一个信号量,用于保护对tty_struct中行规程的访问。

以下是这些结构之间的一些关键关系:

  • 每个tty_struct都有一个指向tty_ldisc的指针,表示当前TTY设备的行规程。
  • 每个tty_ldisc都有一个指向tty_ldisc_ops的指针,用于执行行规程的操作。
  • tty_ldiscs数组中的每个元素都对应一个可能的行规程,并且包含了一个指向tty_ldisc_ops的指针。
  • 当注册或注销行规程时,会对tty_ldiscs数组进行操作,并且需要获取tty_ldiscs_lock锁。
  • 当对TTY设备的行规程进行操作时,如打开或关闭,需要获取tty_ldisc_sem信号量。

代码中的函数和操作通常遵循以下模式:

  • 使用tty_ldisc_get函数获取tty_ldisc实例。
  • 使用tty_ldisc_put函数释放tty_ldisc实例。
  • 使用tty_ldisc_ref_wait和tty_ldisc_deref函数来引用和释放行规程。
  • 使用tty_set_ldisc函数设置TTY设备的行规程。
  • 使用tty_ldisc_open和tty_ldisc_close函数来打开和关闭行规程。
  • 使用tty_ldisc_hangup函数来处理行规程的挂起事件。

这些结构和函数共同协作,管理TTY设备的行规程,确保数据的正确传输和处理。

三、n_tty.c 分析

  N_TTY 是 Linux 中的一个术语,指的是 “normal tty”,即标准的终端行规程。以下是代码的关键部分和功能的详细解释:

1. 关键数据结构

  • n_tty_data:定义了 N_TTY 行规程的数据结构,包括缓冲区、读写指针、标志位等。

2. 行规程操作函数

  • n_tty_open:初始化 N_TTY 行规程,为新的 TTY 设备分配资源。
  • n_tty_close:清理 N_TTY 行规程,释放资源。
  • n_tty_read:实现从 TTY 设备读取数据的逻辑。
  • n_tty_write:实现向 TTY 设备写入数据的逻辑。
  • n_tty_ioctl:处理 TTY 设备的控制命令。
  • n_tty_set_termios:处理行规程的终端参数设置。
  • n_tty_poll:实现 TTY 设备的轮询逻辑。
  • n_tty_receive_buf:处理从 TTY 设备接收的数据缓冲区。
  • n_tty_write_wakeup:在可写时唤醒等待的进程。

3. 辅助函数

  • process_output:处理输出字符,包括处理换行、回车、制表符等特殊字符。
  • do_output_char:输出单个字符,处理 OPOST 行规程。
  • __process_echoes:处理回显缓冲区,将回显字符写入 TTY 设备。
  • add_echo_byte:向回显缓冲区添加字符。
  • echo_char:回显字符,处理控制字符的回显。
  • eraser:处理擦除字符的逻辑。
  • isig:处理信号字符的逻辑。

4. 行规程初始化和注册

  • n_tty_init:初始化 N_TTY 行规程,并在系统启动时注册。

5. 行规程继承

  • n_tty_inherit_ops:允许其他行规程继承 N_TTY 的操作函数。

6. 行规程锁定和解锁

  • n_tty_lock:锁定行规程,防止并发访问。
  • n_tty_unlock:解锁行规程。

7. 行规程数据处理

  • n_tty_receive_char:处理接收到的单个字符。
  • n_tty_receive_buf_common:处理接收到的字符缓冲区。

8. 行规程状态检查

  • job_control:检查作业控制状态,决定是否发送信号。

9. 行规程辅助宏

  • MASK:用于处理循环缓冲区的索引。
  • read_cnt:计算缓冲区中字符的数量。

四、serial_core.c

  这段代码是 Linux 内核中串行驱动的核心部分,它提供了UART(通用异步接收/发送器)设备的驱动支持。以下是代码中的主要结构和它们之间的关系:

1. 主要数据结构

  1. uart_state:表示一个UART设备的实例,包含设备的状态和配置信息。

  2. uart_port:表示一个UART端口,包含端口的硬件参数和操作函数。

  3. uart_driver:表示一个UART驱动,包含驱动的名称、端口数量和操作函数。

2. 关键函数和操作

  1. uart_open:打开UART设备时调用的函数。

  2. uart_close:关闭UART设备时调用的函数。

  3. uart_write:向UART设备写数据的函数。

  4. uart_read:从UART设备读取数据的函数。

  5. uart_ioctl:处理UART设备的控制命令。

  6. uart_set_termios:设置UART设备的终端参数。

  7. uart_startup:启动UART设备时调用的函数。

  8. uart_shutdown:关闭UART设备时调用的函数。

  9. uart_hangup:挂断UART设备时调用的函数。

3. 辅助函数

  1. uart_port_startup:启动UART端口时调用的函数。

  2. uart_port_shutdown:关闭UART端口时调用的函数。

  3. uart_change_speed:改变UART设备波特率的函数。

  4. uart_change_pm:改变UART设备的电源管理状态。

  5. uart_write_wakeup:当UART设备准备好写入时调用的函数。

  6. uart_stop:停止UART设备发送数据的函数。

  7. uart_start:开始UART设备发送数据的函数。

  8. uart_update_mctrl:更新UART设备的调制解调器控制信号。

  9. uart_port_dtr_rts:设置UART端口的DTR和RTS信号。

4. 行规程(Line Discipline)相关函数

  1. uart_ldisc_open:打开行规程时调用的函数。

  2. uart_ldisc_close:关闭行规程时调用的函数。

  3. uart_ldisc_setup:设置行规程时调用的函数。

  4. uart_ldisc_release:释放行规程时调用的函数。

5. 注册和注销驱动

  1. uart_register_driver:注册UART驱动到内核。

  2. uart_unregister_driver:注销UART驱动。

6. 添加和移除端口

  1. uart_add_one_port:向驱动添加一个UART端口。

  2. uart_remove_one_port:从驱动移除一个UART端口。

7. 控制台支持

  1. uart_console_device:获取控制台设备的UART驱动。

8. 电源管理

  1. uart_change_pm:改变UART设备的电源管理状态。

9. 串行控制台支持

  1. uart_get_console:获取串行控制台的UART端口。

  2. uart_set_options:设置串行控制台的选项。

10. 设备树支持

  1. uart_get_rs485_mode:从设备树获取RS485模式的配置。

11. 系统控制台序列支持

  1. uart_try_toggle_sysrq:尝试通过UART端口切换系统控制台序列。

  本文章内容为自己整理总结而来。水平有限,欢迎各位在评论区指导交流!!!😁😁😁

  未完待续哦…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/440708.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

判断是否为二叉排序树(二叉搜索树,二叉查找树)

1.判断给定的二叉树是否为二叉排序树&#xff0c;如果是返回1&#xff0c;不是返回0。 思想&#xff1a; 二叉树是左子树<根<右子树。中序遍历是递增有序&#xff0c;可以通过比较当前结点与前驱关系来进行判断。 代码&#xff1a; //pre为全局变量&#xff0c;保存当…

数学与生活

多学科交叉 信号处理 小波 经济 政策 计算机 统计 信号处理与市场分析 经济与数据分析 政策与统计 过去的数学家没有一个是纯粹的数学家&#xff1b;生活中各方面工程的&#xff0c;物理的&#xff0c;天文&#xff0c;地理的&#xff0c;赌博&#xff0c;政治的&#xff1b…

5-25 JQuery

jQuery简介 jQuery是什么 jQuery基本语法 测试jQuery <head> <meta charset"utf-8"> <title>无标题文档</title><script type"text/javascript" src"jquery-3.5.1.js"></script><script type"tex…

FastAdmin Apache下设置伪静态

FastAdmin Apache下设置伪静态 一、引言 FastAdmin 是一个基于ThinkPHP和Bootstrap框架开发的快速后台开发框架&#xff0c;它以其简洁、高效、易于扩展的特点&#xff0c;广受开发者的喜爱。在部署FastAdmin项目时&#xff0c;为了提高访问速度和用户体验&#xff0c;我们通…

Redis介绍及整合Spring

目录 Redis介绍 Spring与Redis集成 Redis介绍 Redis是内存数据库&#xff0c;Key-value型NOSQL数据库&#xff0c;项目上经常将一些不经常变化并且反复查询的数据放入Redis缓存&#xff0c;由于数据放在内存中&#xff0c;所以查询、维护的速度远远快于硬盘方式操作数据&#…

【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解

目录 1、FileChannel (1&#xff09;获取 FileChannel (2&#xff09;读取文件 (3&#xff09;写入文件 (4&#xff09;关闭通道 (5&#xff09;当前位置与文件大小 (6&#xff09;强制写入磁盘 2、两个 FileChannel 之间的数据传输 (1&#xff09;使用 transferTo()…

leetcode-42. 接雨水 单调栈

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表…

微软发布Windows 11 2024更新,新型Copilot+ AI PC功能亮相

前言 微软在Windows 11的2024更新中加强了对人工智能的应用&#xff0c;推出了新功能Copilot。 此次更新的版本号为26100.1742&#xff0c;Copilot将首先在Windows Insider中推出&#xff0c;计划于11月向特定设备和市场推广&#xff0c;用户需开启“尽快获取最新更新”选项以…

RESTful风格接口+Swagger生成Web API文档

RESTful风格接口Swagger生成Web API文档 文章目录 RESTful风格接口Swagger生成Web API文档1.RESTful风格接口RESTful简介RESTful详细图示常见http状态码springboot实现RESTfulRESTful springboot设计实例demo 2.Swagger生产Web API文档Swagger简介使用Swagger1.加入依赖2.配置S…

基于STM32的超声波测距仪设计

引言 本项目将基于STM32微控制器设计一个超声波测距仪&#xff0c;通过超声波传感器实现距离测量&#xff0c;并将结果显示在液晶屏上。该项目展示了STM32微控制器与超声波传感器、LCD显示器的接口通信&#xff0c;以及信号处理和距离计算的过程。 环境准备 1. 硬件设备 ST…

技术速递|Python in Visual Studio Code 2024年9月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布将于 2024 年 9 月发布适用于 Visual Studio Code 的 Python 和 Jupyter 扩展&#xff01; 此版本包括以下公告&#xff1a; Django 单元测试支持使用 Pylance 从 inlay 提示转到定义 如果您有兴趣&#xff0c;可以在我们的 Pyth…

提升 CI/CD 稳定性:Jenkins 开机自检与推送通知

简介&#xff1a;Jenkins 是一个广泛使用的开源自动化服务器&#xff0c;常用于持续集成和持续交付。在某些情况下&#xff0c;服务器重启可能导致 Jenkins 构建任务中断或失败。为了解决这个问题&#xff0c;可以使用一个自检服务&#xff0c;定期检查系统的启动时间&#xff…

算法题总结(十)——二叉树上

#二叉树的递归遍历 // 前序遍历递归LC144_二叉树的前序遍历 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new ArrayList<Integer>(); //也可以把result 作为全局变量&#xff0c;只需要一个函数即可。…

Open3D实现点云数据的序列化与网络传输

转载自个人博客&#xff1a;Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候&#xff0c;有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输&#xff0c;但Open3D源码在实现RPC功能时就提供了一套序列化及传输的…

今日指数day8实战补充用户管理模块(下)

ps : 由于前端将userId封装为BigInt类型 , 导致有精度损失, 传入的userId不正确 , 部分功能无法正确实现 , 但是代码已经完善 1.4 更新用户角色信息接口说明 1&#xff09;原型效果 2&#xff09;接口说明 功能描述&#xff1a;更新用户角色信息 服务路径&#xff1a;/user/…

vue-scrollto实现页面组件锚点定位

文章目录 前言背景操作指南安装及配置步骤vue组件中使用 参考文章 前言 博主介绍&#xff1a;✌目前全网粉丝3W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&#xff1a;Java后端、大数据…

php获取远程https内容时提示 PHP Warning: copy(): Unable to find the wrapper “https“ 解决方法

异常信息&#xff1a; php -r "copy(https://getcomposer.org/installer, composer-setup.php);" PHP Warning: copy(): Unable to find the wrapper "https" - did you forget to enable it when you configured PHP? in Command line code on line 1 P…

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…

万界星空科技MES数据集成平台

制造执行系统MES作为连接企业上层ERP系统和现场控制系统的桥梁&#xff0c;承担了实时数据采集、处理、分析和传递的重要任务。MES数据集成平台是一个集成各类数据源&#xff0c;将数据进行整合和统一管理的系统&#xff0c;通过提供标准化接口和协议&#xff0c;实现数据的无缝…