linux tty driver

本文深入探讨Linux的TTY驱动框架,从终端概念到TTY设备、控制台终端、注册流程等方面,介绍了TTY驱动的核心数据结构、接口及其实现。重点阐述了TTY驱动的注册过程,包括tty_driver、discipline、console的注册,以及open、write、read操作的流程。通过理解TTY驱动,有助于开发者编写自己的TTY设备驱动并加深对TTY核心层的理解。
摘要由CSDN通过智能技术生成

- 1.前言

上世纪,六七十年代,虽然已经有电脑了,但还没有普及,电脑还是极其昂贵的,普通老百姓都买不起这么贵的一台电脑。那时候,UNIX的发明人——汤普森,为了实现多用户能够同时登陆UNIX系统的目的,使用非常廉价的TTY设备连接电脑,通过TTY设备发送和接收来自UNIX系统的信息,实现系统连接多个TTY设备的功能,多个用户可以同时登陆UNIX系统。 Linux继承和发展了TTY功能,不仅仅可以通过串口连接TTY设备,多个用户通过TTY设备登陆linux系统,更支持通过网络连接TTY设备了。接下来,本文会介绍什么为终端,什么为TTY。

- 1.1.何为终端

终端是一种可以同时显示和接收信息的设备,通俗的来讲,终端是一种用于人机交互的设备。用户通过终端发送信息给系统,终端接收来自系统的反馈信息,显示给用户。例如,对于一台电脑来说,显示器和键盘就是一个终端,键盘接收用户输入的信息,发送给系统,系统接收到信息后,处理该信息,把处理结果发送给显示器,显示出来给用户。

目前,终端的种类有很多,包括控制台终端、虚拟终端、串口终端、软件终端、USB或网络终端和图形终端等。其中,软件终端是指,通过串口或者USB等连接到另外一台电脑,该电脑上的一个软件模拟出一个终端,在该电脑可以实现终端的输入和输出。

- 1.2.何为TTY设备

电传打印机是一种基于电报技术的远距离信息传送的设备,通常有键盘、收发报器和打印机构等部件。用户敲击键盘的某一个按键,打印机就会自动把该按键代表的信息发送到信道里面,这就是我们所说的发报;电传打印即接收来自信道的电码信号,打印机构打印该信号所代表的字符,这就是收报。

那时候,电传打印机很便宜,使用一根线就可以连接电传打印。发现电传打印机的优点后,汤普森使用串口连接打印机,敲击打印机的键盘发送信息给UNIX系统,UNIX系统接收到信息后,处理用户的信息,把结果发送给打印机,打印机打印UNIX系统的信息到纸上,查看纸上的打印信息,用户可以获取到系统的状态。

电传打印机的英文名为Teletype,缩写为TTY,由于第一台TTY设备为电传打印机,所以现在称呼所有的这类型设备为TTY设备。

- 1.3.何为控制台终端console

除了外接的TTY设备,电脑自己的显示器和键盘也是一个TTY设备,为了显示电脑自身的TTY设备的高贵身份,美其名曰,控制台终端console。与普通的TTY设备比较,console具有管理员权限,打印系统日志信息。

一般情况下,电脑的控制台终端只有一个输出设备(显示器),任一个时刻仅仅只能被一个应用程序占有。现代系统都支持多任务的操作环境,有时候,将控制台终端切换给另一个应用程序前,往往需要保留上一个应用程序的控制台终端上的输出,下一次切到该应用程序的时候,方便查看上一次输出信息。

因此,在控制台终端的基础上,UNIX/Linux系统虚拟出了6个终端——虚拟终端,分别为tty1~tty6,。各个应用程序可以在虚拟终端上独立输出,使用键盘组合键(CTRL+ALT+F1~F6)在各个虚拟终端间切换。

- 1.4.目的与意义

本文的目的是,介绍linux kernel的TTY驱动框架、tty设备和驱动的注册流程、用户空间open、read和write TTY设备节点的驱动操作过程。本文的意义是,通过了解TTY驱动的框架和使用方法,驱动人员熟悉怎么编写自己的TTY设备驱动,对TTY核心层有更深的了解和认识。

- 2.TTY框架

TTY框架包括三部分,分别是tty driver、discipline和tty core,它们之间的关系如下图所示:

这里写图片描述
TTY框架

tty core是TTY的核心层,它抽象tty的核心数据结构,例如tty_struct、tty_driver等,提供接口给下面的discipline和tty driver层,discipline和tty driver使用tty core提供的注册接口,把discipline和tty driver注册到tty core。当用户操作设备节点的时候,tty core接收用户的请求,决定下一步是到discipline,还是tty driver层,例如,用户open、read和write tty设备节点的时候,tty core处理完后,交给discipline处理,最后再到tty driver处理,但ioctl的时候,tty core会直接就到tty driver层,没有经过discipline层。

discipline的中文意思是规范,在这里表示线路的规范。discipline接收tty core或者tty driver的数据,进行一些格式的转换,比如换行等等等,处理完后,再上传给tty core或者传给tty driver层。

tty driver是low level driver,它直接操作硬件,完成硬件的初始化,接收硬件的数据,上报数据给discipline或者tty core层;接收来自tty core或者discipline的数据,发送数据给硬件等等。


- 3.tty核心

上一节介绍了tty的整体框架,tty核心定义了tty需要使用的数据结构,提供各种各样的函数接口给下面的层使用,管理注册到tty核心的tty设备和驱动。本节将会详细的介绍tty核心提供的功能,囊括tty core提供的数据结构和接口等等。

- 3.1.tty的数据结构

在代码的世界里,数据结构代表程序里面的一个个抽象出来的实体,就好像代码里面的一个个零件,由这些零件才能组成一个健全的程序,由此可见,数据结构是一个程序的基本,使用抽象出来的数据结构,不仅有利于代码的编写和理解,更有利于代码的维护!

Linux的设备驱动模型有两大部分,分别是驱动和设备,每一个驱动都会有对应描述驱动和设备的数据结构。tty框架也一样,tty框架使用了tty_driver和tty_struct结构描述tty驱动和tty设备。

tty driver结构如下:

struct tty_driver {
        int     magic;          /* magic number for this structure */
        struct kref kref;       /* Reference management */
        struct cdev *cdevs;
        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 */
        unsigned int    num;    /* number of devices allocated */
        short   type;           /* type of tty driver */
        short   subtype;        /* subtype of tty driver */
        struct ktermios init_termios; /* Initial termios */
        unsigned long   flags;          /* tty driver flags */
        struct proc_dir_entry *proc_entry; /* /proc fs entry */
        struct tty_driver *other; /* only used for the PTY driver */

        /*  
         * Pointer to the tty data structures
         */
        struct tty_struct **ttys;
        struct tty_port **ports;
        struct ktermios **termios;
        void *driver_state;

        /*  
         * Driver methods
         */

        const struct tty_operations *ops;
        struct list_head tty_drivers;
};

@magic:该tty_driver的幻数;
@kref:该结构的计数,当该计数小于或者等于零的时候,该结构才可以被释放;
@cdevs:该结构注册的字符设备;
@driver_name:tty_driver注册的时候,创建proc文件的名称;
@name:该结构的名称,字符设备的名称也是该名称;
@name_base:和name字段一起构成设备的名称;
@major:该tty_driver的主设备号;
@minor_start:次设备号的开始位;
@num:申请次设备号的数量;
@type:tty_driver的类型;
@subtype:tty_driver的子类型;
@init_termios:终端的初始化参数;
@flags:tty_driver的标志;
以下是指向tty data的结构
@ttys:存放打开的tty_struct;
@ports:存放tty_driver注册的tty_port;
@termios:运行时的终端配置信息;
@driver_state:指向tty_driver的数据,例如,指向uart_driver;
以下是tty_driver的操作函数
@ops:该ops会在打开tty设备节点的时候,赋给tty_struct结构的ops,该ops被discipline层ops调用,最后会打开tty_driver层;
@tty_drivers:链表头,用于挂载到tty_drivers链表;

驱动人员注册一个tty_driver的时候,必须初始化tty_driver的一些必要的字段,然后调用tty core提供的接口注册到tty core。

tty core使用tty_struct结构描述一个tty设备。一般情况下,设备和驱动都会有一个匹配的过程,就好像platform_device和platform_driver通过虚拟总线匹配的过程一样,不过,tty_struct和tty_driver并没有通过总线来进行匹配,仿佛tty core弱化了tty device,驱动人员不需要创建一个tty device,而是,tty_driver注册接口注册一个指定范围设备号的字符设备驱动,再创建并注册对应设备号范围的设备。这样,字符设备驱动就会与新建的设备匹配,当用户空间open一个tty设备节点的时候,调用字符设备驱动的ops的open函数,tty_struct就在该open函数创建的,这意味着,一个tty设备对应着一个tty_struct。

如下所示为tty_struct的定义:

struct tty_struct {
        int     magic;
        struct kref kref;
        struct device *dev;
        struct tty_driver *driver;
        const struct tty_operations *ops;
        int index;

        /* Protects ldisc changes: Lock tty not pty */
        struct ld_semaphore ldisc_sem;
        struct tty_ldisc *ldisc;
        --cut—
        --cut--
        struct tty_struct *link;
        struct fasync_struct *fasync;
        int alt_speed;          /* For magic substitution of 38400 bps */
        wait_queue_head_t write_wait;
        wait_queue_head_t read_wait;
        struct work_struct hangup_work;
        void *disc_data;
        void *driver_data;
        struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096

        unsigned char closing:1;
        unsigned char *write_buf;
        int write_cnt;
        /* If the tty has a pending do_SAK, queue it here - akpm */
        struct work_struct SAK_work;
        struct tty_port *port;
};

@magic:该tty_struct的幻数;
@kref:tty_struct的计数;
@driver:tty_struct的tty_driver;
@ops:open设备节点的时候,tty_driver的ops会赋给该ops;
@index:tty_struct在tty_driver中的索引;
@ldisc:tty_struct的discipline;
@write_wait:写等待队列头,N_TTY discipline的ops->write把当前的进程添加到该等待队列头,进入while(1)循环,如果while循环调用了schedule,当前进程进入休眠,设备节点的下一次open,或者reset discipline的时候,会wake_up该等待队列头的等待队列,以继续进行write操作;
@read_wait:与write_wait相似,所不同的是,是在N_TTY discipline的read函数;
@disc_data:保存discipline用到的数据;
@tty_files:链表头,保存file文件;
@write_buf:写缓冲区;
@write_cnt:申请输出的数量;
@port:tty_struct对应的tty_port;

struct tty_operation结构是tty_driver的操作函数集,tty_driver的字符设备操作函数或者discipline的操作函数会调用到 tty_driver的操作函数集,tty_driver的函数集再调用更低一层的操作函数,比如,对于串口来说,tty_driver的函数集会调用uart_port的操作函数。

tty_operation的定义如下:

struct tty_operations {
        struct tty_struct * (*lookup)(struct tty_driver *driver,
                        struct inode *inode, int idx);
        int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
        void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
        int  (*open)(struct tty_struct * tty, struct file * filp);
        void (*close)(struct tty_struct * tty, struct file * filp);
        void (*shutdown)(struct tty_struct *tty);
        void (*cleanup)(struct tty_struct *tty);
        int  (*write)(struct tty_struct * tty,
                      const unsigned char *buf, int count);
        int  (*put_char)(struct tty_struct *tty, unsigned char ch);
        int  (*write_room)(struct tty_struct *tty);
        int  (*ioctl)(struct tty_struct *tty,
                    unsigned int cmd, unsigned long arg);
        void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
        int (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
};

@lookup:tty设备节点的open函数使用该回调函数查找tty_struct,如果没有该回调,则直接访问driver->ttys数组,该数组保存着打开的tty_struct;
@install:tty设备节点的open函数新建一个tty_struct的时候,使用该回调把tty_struct安装到tty_driver,如果没有提供该回调,使用tty_standard_insatll函数,把tty_struct存在tty_driver的ttys数组中;
@remove:与install的作用相反,把ttt_struct从tty_driver中去除;
@open:tty设备节点的open函数调用discipline ops的open后,调用该open回调,该回调再调用下一层的回调;
@close:与open相反,close回调在tty设备节点的release函数被调用;
@shutdown:关掉该tty_struct;
@cleanup:tty_struct计数为零,触发queue_release_one_tty被调用,接着release_one_tty被调用,release_one_tty检测tty->ops->cleanup是否存在,如果存在则调用该cleanup回调,该回调释放tty->ops申请的资源;
@write:输出一个buf数据;
@put_char:输出一个字符;
@write_room:返回输出buffer剩下的空间大小;
@ioctl:ioctl接口;
@set_termios:设置tty_struct的配置,在discipline的ioctl会调用该回调;
@stop:暂定tty_struct flow,discipline的ioctl会调用这回调;
@start:开始tty_struct flow,比如,uart driver的start会调用port的start_tx回调,开始发送数据;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值