Linux TTY驱动--Serial Core层

本文深入剖析Linux TTY驱动的Serial Core层,讲解了如何通过`uart_ops`结构体作为接口进行通信,并详细描述了`tty_driver`和`tty_operations`结构体的作用。注册过程包括:`alloc_tty_driver`初始化`tty_driver`,`tty_set_operations`设置`uart_ops`,然后通过`tty_register_driver`向TTY层注册。此外,还探讨了`tty_open`等方法的调用链路,以及`/dev/tty`、`/dev/console`的注册。

接上一节:

Linux TTY驱动--Uart_driver底层

一. 为了给USB-Serial类型的串口打基础(USB-Serial和Serial Core一样,构造了一个tty_driver和tty_operations,叫做usb-serial层),这里仔细分析Serial Core层完成的工作,实现代码为/drivers/serial/serial_core.c(kernel 2.6.28)。从哪里讲起呢,还是找找module_init,发现没有,在/drivers/serial/*众多文件里寻找没有,怀疑Serial Core层不是一个驱动模块,只能导出了一些上篇文章的函数而已。那就只有从uart_register_driver函数开始了,不过首先看看serial_core.c文件中使用的结构体或者常量,发现了如下:

static const struct tty_operations uart_ops = {
    .open        = uart_open,
    .close        = uart_close,
    .write        = uart_write,
    .put_char    = uart_put_char,
    .flush_chars    = uart_flush_chars,
    .write_room    = uart_write_room,
    .chars_in_buffer= uart_chars_in_buffer,
    .flush_buffer    = uart_flush_buffer,
    .ioctl        = uart_ioctl,
    .throttle    = uart_throttle,
    .unthrottle    = uart_unthrottle,
    .send_xchar    = uart_send_xchar,
    .set_termios    = uart_set_termios,
    .set_ldisc    = uart_set_ldisc,
    .stop        = uart_stop,
    .start        = uart_start,
    .hangup        = uart_hangup,
    .break_ctl    = uart_break_ctl,
    .wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
    .read_proc    = uart_read_proc,
#endif
    .tiocmget    = uart_tiocmget,
    .tiocmset    = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
    .poll_init    = uart_poll_init,
    .poll_get_char    = uart_poll_get_char,
    .poll_put_char    = uart_poll_put_char,
#endif
};

该结构体非常重要,是Core层和TTY层沟通的接口。在uart_register_driver的过程为:

1. 调用alloc_tty_driver(tty层函数)初始化一个tty_driver

2. 调用tty_set_operations(tty层) 给tty_driver 赋值上面的 uart_ops

3.  通过tty_register_driver向tty层注册tty_driver(/drivers/char/tty_io.c中实现kernel2.6.28)。以上的uart_ops被tty层在需要时调用。


二. 那么重点看一下 tty_driver、tty_operations两个结构体

struct tty_driver {
    int    magic;        /* magic number for this structure */
    struct kref kref;    /* Reference management */
    struct cdev cdev;
    struct module    *owner;
    const char    *driver_name;
    const char    *name;
    int    name_base;    /* offset of printed name */
    int    major;        /* major device number */
    int    minor_start;    /* start of minor device number */
    int    minor_num;    /* number of *possible* devices */
    int    num;        /* number of devices allocated */
    short    type;        /* type of tty driver */
    short    subtype;    /* subtype of tty driv

### ### 嵌入式Linux TTY设备驱动架构原理 嵌入式 Linux 中的 TTY 设备驱动架构是操作系统与硬件交互的关键部分,负责管理串口通信、终端输入输出等核心功能。TTY 驱动的设计基于分结构,确保了硬件抽象、协议处理和用户接口的统一性。 #### TTY 子系统的组成 LinuxTTY 子系统由三个主要组件构成:**tty 核心(core)**、**tty 线路规程(line discipline)** 和 **tty 驱动(driver)**。其中: - **tty 核心** 是对整个 TTY 设备的抽象,提供统一的接口给用户空间程序使用; - **tty 线路规程** 负责数据格式化,例如回车换行转换、信号处理等; - **tty 驱动** 则是面向具体硬件的实现,完成底数据收发操作[^2]。 这种分设计使得开发者可以在不同次上扩展和定制功能,同时保持接口的一致性。 #### 串口驱动调用关系 在 TTY 子系统中,串口驱动通常基于 UART 控制器实现。用户空间程序通过标准 I/O 接口(如 `read()` 和 `write()`)访问 TTY 设备,最终请求会传递到线路规程进行预处理,再由 tty 核心转发至具体的底驱动执行物理传输操作[^2]。 对于嵌入式平台而言,UART 驱动需要实现 `uart_ops` 结构体中的回调函数,这些函数直接与 UART 寄存器交互,完成诸如发送字符、接收中断处理等功能。驱动编写人员需根据特定 SoC 的寄存器手册实现相应逻辑[^4]。 #### TTY 驱动程序框架 TTY 驱动程序框架引入了“线路规程”机制,用于处理数据流中的特殊字符和控制序列。例如,在接收到数据时,线路规程会调用 `receive_buf()` 函数将原始字节流送入输入缓冲区,并按需进行转义或过滤处理。这种方式简化了上应用对终端行为的理解和控制[^5]。 此外,随着技术演进,传统的 console 功能逐渐弱化,现代 Linux 中的 console 主要承担日志输出的任务,而其他交互功能已由普通 TTY 终端接管[^3]。 #### 示例代码:注册一个简单的 TTY 驱动 以下是一个简化的 TTY 驱动注册示例,展示了如何定义并初始化一个基于 UART 的 TTY 驱动: ```c #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> static struct tty_driver *my_tty_driver; static int my_open(struct tty_struct *tty, struct file *filp) { // 实现打开设备的操作 return 0; } static void my_close(struct tty_struct *tty, struct file *filp) { // 实现关闭设备的操作 } static int my_write(struct tty_struct *tty, const unsigned char *buf, int count) { // 实现写入数据到硬件的操作 return count; } static int __init my_tty_init(void) { int ret; my_tty_driver = alloc_tty_driver(1); if (!my_tty_driver) return -ENOMEM; my_tty_driver->owner = THIS_MODULE; my_tty_driver->driver_name = "my_tty"; my_tty_driver->name = "ttyMY"; my_tty_driver->major = TTY_MAJOR; my_tty_driver->minor_start = 64; my_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; my_tty_driver->subtype = SERIAL_TYPE_NORMAL; my_tty_driver->init_termios = tty_std_termios; tty_set_operations(my_tty_driver, &my_ops); ret = tty_register_driver(my_tty_driver); if (ret) { put_tty_driver(my_tty_driver); return ret; } return 0; } static void __exit my_tty_exit(void) { tty_unregister_driver(my_tty_driver); put_tty_driver(my_tty_driver); } module_init(my_tty_init); module_exit(my_tty_exit); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值