第14章 Linux终端设备驱动之终端设备驱动架构

14.2 终端设备驱动架构

    Linux 内核中 tty 的层次结构如图 14.1 所示,包含 tty 核心、tty 线路规程和 tty 驱动,tty 线路规程的工作是以特殊的方式格式化从一个用户或者硬件收到的数据,这种格式化常常采用一个协议转换的形式,例如 PPP(端到端协议) 和 Bluetooth。

    

    发送数据的流程(向设备写数据)为:tty 核心从一个用户获取将要发送给一个 tty 设备的数据,tty 核心将数据传递给 tty 线路规程驱动,接着数据被传递到 tty 驱动,tty 驱动将数据转换为可以发送给硬件的格式。

   接收数据的流程(从设备读数据)为:从 tty 硬件接收到的数据向上交给 tty 驱动,进入 tty 线路规程驱动,再进入 tty 核心,在这里它被一个用户获取。

    尽管大多数时候 tty 核心和 tty 之间的数据传输会经历 tty 线路规程的转换,但是tty 驱动与 tty 核心之间也可以直接传输数据。

    图 14.2 显示与 tty 相关的主要源文件及数据的流向。


14.2 tty 主要源文件关系及数据流向

分析:

        drivers/char/tty_io.c 定义 tty 设备通用的 file_operations 结构体并实现了接口函数 tty_register_driver()用于注册 tty 设备,利用fs/char_dev.c提供的接口函数注册字符设备,与具体设备对应的 tty 驱动将实现 tty_driver 结构体中的成员函数。同时 tty_io.c 也提供了 tty_register_ldisc()接口函数用于注册线路规程。

扫描二维码关注公众号,回复: 1674222 查看本文章

        从图 14.2 可以看出,特定 tty 设备驱动的主体工作是填充 tty_driver 结构体中的成员,实现其

中的成员函数,tty_driver 结构体的定义如代码清单14.1。

    代码清单 14.1 tty_driver 结构体

include/linux/tty_driver.h

struct tty_driver {
        int     magic;          /* magic number for this structure */
        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 driver */
        struct ktermios init_termios; /* Initial termios */
        int     flags;          /* tty driver flags */
        int     refcount;       /* for loadable tty drivers */
        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 ktermios **termios;
        struct ktermios **termios_locked;
        void *driver_state;     /* only used for the PTY driver */

        /*
         * Interface routines from the upper tty layer to the tty
         * driver.      Will be replaced with struct tty_operations.
         */
        int  (*open)(struct tty_struct * tty, struct file * filp);
        void (*close)(struct tty_struct * tty, struct file * filp);
        int  (*write)(struct tty_struct * tty,
                      const unsigned char *buf, int count);
        void (*put_char)(struct tty_struct *tty, unsigned char ch);
        void (*flush_chars)(struct tty_struct *tty);
        int  (*write_room)(struct tty_struct *tty);
        int  (*chars_in_buffer)(struct tty_struct *tty);
        int  (*ioctl)(struct tty_struct *tty, struct file * file,
                    unsigned int cmd, unsigned long arg);
        long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
                             unsigned int cmd, unsigned long arg);
        void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
        void (*throttle)(struct tty_struct * tty);
        void (*unthrottle)(struct tty_struct * tty);
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
        void (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, char ch);
        int (*read_proc)(char *page, char **start, off_t off,
                          int count, int *eof, void *data);
        int (*write_proc)(struct file *file, const char __user *buffer,
                          unsigned long count, void *data);
        int (*tiocmget)(struct tty_struct *tty, struct file *file);
        int (*tiocmset)(struct tty_struct *tty, struct file *file,
                        unsigned int set, unsigned int clear);

        struct list_head tty_drivers;

};

分析:

        tty_driver 结构体中的 magic 表示给这个结构体的“幻数”,设为 TTY_DRIVER_MAGIC(即0x5402),在 alloc_tty_driver()函数中被初始化。

drivers/char/tty_io.c

struct tty_driver *alloc_tty_driver(int lines)
{
struct tty_driver *driver;

driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
memset(driver, 0, sizeof(struct tty_driver));
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
/* later we'll move allocation of tables here */
}
return driver;

}

    name 与 driver_name 的不同在于driver_name 表示驱动的名字,用在 /proc/tty 和 sysfs 中,而name 表示驱动的设备节点名。

    type 与 subtype 描述 tty 驱动的类型和子类型,subtype 的值依赖于 type。

include/linux/tty_driver.h

/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM 0x0001
#define TTY_DRIVER_TYPE_CONSOLE 0x0002
#define TTY_DRIVER_TYPE_SERIAL 0x0003
#define TTY_DRIVER_TYPE_PTY 0x0004
#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS 0x0006

/* system subtypes (magic, used by tty_io.c) */
#define SYSTEM_TYPE_TTY 0x0001
#define SYSTEM_TYPE_CONSOLE 0x0002
#define SYSTEM_TYPE_SYSCONS 0x0003
#define SYSTEM_TYPE_SYSPTMX 0x0004

/* pty subtypes (magic, used by tty_io.c) */
#define PTY_TYPE_MASTER 0x0001
#define PTY_TYPE_SLAVE 0x0002

/* serial subtype definitions */

#define SERIAL_TYPE_NORMAL 1

init_termios 为初始线路设置,为一个ktermios结构体,被用来提供一个线路设置集合。

termios 用于保存当前的线路设置,这些线路设置控制当前波特率、数据大小、数据流控设置等,termios结构体定义在include/asm-arm/termbits.h中。

struct ktermios {
        tcflag_t c_iflag;               /* input mode flags */
        tcflag_t c_oflag;               /* output mode flags */
        tcflag_t c_cflag;               /* control mode flags */
        tcflag_t c_lflag;               /* local mode flags */
        cc_t c_line;                    /* line discipline */
        cc_t c_cc[NCCS];                /* control characters */
        speed_t c_ispeed;               /* input speed */
        speed_t c_ospeed;               /* output speed */

};

        驱动会使用一个标准的数值集初始化这个成员,它拷贝自 tty_std_termios 变量,tty_std_termos在 tty 核心中的定义如代码清单 14.2。

代码清单 14.2 tty_std_termios 变量

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
};

EXPORT_SYMBOL(tty_std_termios);

    tty_driver 结构体中的 major、minor_start、minor_num 表示主设备号、次设备号及可能的次设备数,name 表示设备名(如 ttyS)。

     tty_operations 结构体定义如代码清单 14.3,其成员函数通常需在特定设备 tty 驱动模块初始化函数中被赋值。

代码清单 14.3 tty_operations 结构体

struct tty_operations {
        int  (*open)(struct tty_struct * tty, struct file * filp);
        void (*close)(struct tty_struct * tty, struct file * filp);
        int  (*write)(struct tty_struct * tty, const unsigned char *buf, int count);
        void (*put_char)(struct tty_struct *tty, unsigned char ch);
        void (*flush_chars)(struct tty_struct *tty);
        int  (*write_room)(struct tty_struct *tty);
        int  (*chars_in_buffer)(struct tty_struct *tty);
        int  (*ioctl)(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
        long (*compat_ioctl)(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
        void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
        void (*throttle)(struct tty_struct * tty);
        void (*unthrottle)(struct tty_struct * tty);
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
        void (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, char ch);
        int (*read_proc)(char *page, char **start, off_t off, int count, int *eof, void *data);
        int (*write_proc)(struct file *file, const char __user *buffer, unsigned long count, void *data);
        int (*tiocmget)(struct tty_struct *tty, struct file *file);
        int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);

};

分析:

put_char:单字节写函数,当单个字节被写入设备时这个函数被 tty 核心调用,如果一个 tty驱动没有定义这个函数,将使用 count 参数为 1 的 write()函数。

flush_chars()与 wait_until_sent():刷新数据到硬件

write_room:指示有多少缓冲区空闲

chars_in_buffer:指示缓冲区中包含的数据数

 当在 tty 设备的设备节点上执行 IOCTL 操作时,tty_operations 结构体的 ioctl()函数会被 tty 核心调用。

当设备的 termios 设置被改变时,set_termios()函数将被 tty 核心调用。

throttle()、unthrottle()、stop()和 start()为数据抑制函数,这些函数用来辅助控制 tty 核心的输入缓冲区。当 tty 核心的输入缓冲区满时,throttle()函数将被调用,tty 驱动试图通知设备不应当发送字符给它。当 tty 核心的输入缓冲区已被清空时,unthrottle()函数将被调用以暗示设备可以接收数据。sop()和 start()函数表示 tty 驱动停止发送数据给设备以及恢复发送数据。

当 tty 驱动挂起 tty 设备时,hangup()函数被调用,在此函数中进行相关的硬件操作。

当 tty 驱动要在 RS-232 端口上打开或关闭线路的 BREAK 状态时,break_ctl()线路中断控制函数被调用。如果 state 状态设为-1,BREAK 状态打开,如果状态设为 0,BREAK 状态关闭。如果tty 驱动实现这个函数 ,而 tty 核心将处理 TCSBRK、TCSBRKP、TIOCSBRK 和 TIOCCBRK 这些 IOCTL 命令。

    flush_buffer:刷新缓冲区并丢弃任何剩下的数据。

set_ldisc():设置线路规程,当 tty 核心改变 tty 驱动的线路规程时这个函数被调用,这个函数通常不需要被驱动定义。

send_xchar(): X-类型字符发送函数,这个函数用来发送一个高优先级 XON 或者 XOFF 字符给 tty 设备,要被发送的字符在第 2 个参数 ch 中指定。

read_proc()和 write_proc()为/proc 读和写函数。

tiocmget:获得 tty 设备的线路设置

tiocmset:设置 tty 设备的线路设置,参数 set 和 clear 包含要设置或者清除的线路设置。

Linux 内核提供一组函数用于操作 tty_driver 结构体及 tty 设备,如下所示。

(1)分配 tty 驱动

struct tty_driver *alloc_tty_driver(int lines);

这个函数返回tty_driver 指针,其参数为要分配的设备数量。

(2)注册 tty 驱动

int tty_register_driver(struct tty_driver *driver);

参数为由 alloc_tty_driver ()分配的 tty_driver 结构体指针,注册 tty 驱动成功时返回 0。

(3)注销 tty 驱动

 int tty_unregister_driver(struct tty_driver *driver);

这个函数与 tty_register_driver ()对应,tty 驱动最终会调用上述函数注销 tty_driver。

(4)注册 tty 设备

struct device *tty_register_device(struct tty_driver *driver,

                                          unsigned index, struct device *dev);

分析:

仅有 tty_driver 是不够的,驱动必须依附于设备,tty_register_device()函数用于注册关联于tty_driver 的设备,index 为设备的索引(范围是 0~driver->num),如:

for (i = 0; i < XXX_TTY_MINORS; ++i)

        tty_register_device(xxx_tty_driver, i, NULL);

(5)注销 tty 设备

    void tty_unregister_device(struct tty_driver *driver, unsigned index);

上述函数与 tty_register_device()对应,用于注销 tty 设备,其使用方法如:

for (i = 0; i < XXX_TTY_MINORS; ++i)

        tty_unregister_device(xxx_tty_driver, i);

(6)设置 tty 驱动操作

void tty_set_operations(struct tty_driver *driver, struct tty_operations *op);

上述函数会将 tty_operations 结构体中的函数指针拷贝给 tty_driver 对应的函数指针。

终端设备驱动都围绕 tty_driver 结构体展开,终端设备驱动应包含如下组成:

(1)终端设备驱动模块加载函数和卸载函数,完成注册和注销 tty_driver,初始化和释放终端设备对应的 tty_driver 结构体成员及硬件资源。

(2)实现 tty_operations 结构体中的一系列成员函数,主要是实现 open()、close()、write()、tiocmget()、tiocmset()等函数。



猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80687834
今日推荐