第12章 Linux设备驱动的软件架构思想之设备驱动的分层思想(终端设备驱动)
12.3.5 终端设备驱动
在Linux系统中,终端是一种字符型设备,终端有多种类型,通常使用tty(Teletype)来简称各种类型的终端设备。对于嵌入式系统,最普遍采用的是UART(Universal Asynchronous Receiver/Transmitter)串行端口,简称串口。
Linux内核中tty的层次结构如图12.9所示。
图12.9 Linux内核中tty的层次结构
Linux内核中tty的层次结构包含tty核心tty_io.c、tty线路规程n_tty.c(实现N_TTY线路规程)和tty驱动实例xxx_tty.c,tty线路规程的工作是以特殊的方式格式化从一个用户或者硬件收到的数据,这种格式化常常采用一个协议转换的形式。
tty_io.c是一个标准的字符设备驱动,对上有字符设备的职责,实现file_operations成员函数。但是tty核心层对下又定义tty_driver的架构,这样tty设备驱动的主体工作就变成了填充tty_driver结构体中的成员,实现其中的tty_operations的成员函数,而不再是去实现file_operations这一级的工作。
代码清单12.17 tty_driver结构体
linux/tty_driver.h
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;
};
代码清单12.18 tty_operations结构体
linux/tty_driver.h
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);
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,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
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);
int (*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 (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
const struct file_operations *proc_fops;
};
如图12.10所示,tty设备发送数据的流程为:
图12.10 tty设备发送、接收数据流的流程
tty核心从一个用户获取将要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着数据被传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式。接收数据的流程为:从tty硬件接收到的数据向上交给tty驱动,接着进入tty线路规程驱动,再进入tty核心,在这里它被一个用户获取。
代码清单12.18中tty_driver操作集tty_operations的成员函数write()接收3个参数:tty_struct、发送数据指针及要发送的字节数。该函数是被file_operations的write()成员函数间接触发调用的。从接收角度看,tty驱动一般收到字符后会通过tty_flip_buffer_push()将接收缓冲区推到线路规程。
尽管一个特定的底层UART设备驱动完全可以遵循上述tty_driver的方法来设计,即定义tty_driver并实现tty_operations中的成员函数,但是鉴于串口之间的共性,Linux考虑在文件drivers/tty/serial/serial_core.c中
实现了UART设备的通用tty驱动层(称其为串口核心层)。这样,UART驱动的主要任务就进一步演变成了实现serial-core.c中定义的一组uart_xxx接口,如图12.11所示。
图12.11 串口核心层
因此,按照面向对象的思想,可以认为tty_driver是字符设备的泛化(类与类之间的继承关系、接口与接口之间的继承关系)、serial-core是tty_driver的泛化,而具体的串口驱动又是serial-core的泛化。
备注:串口核心层又定义了新的uart_driver结构体和其操作集uart_ops。一个底层的UART驱动需要创建和通
过uart_register_driver()注册一个uart_driver,代码清单12.19给出uart_driver的定义。
代码清单12.19 uart_driver结构体
linux/serial_core.h
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
uart_driver结构体在本质上是派生自tty_driver结构体,tty_operations在UART这个层面上也被进一步泛化(类与类之间的继承、接口与接口之间的继承)为了uart_ops,其定义如代码清单12.20所示。
linux/serial_core.h
/*
* This structure describes all the operations that can be done on the
* physical hardware. See Documentation/serial/driver for details.
*/
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*throttle)(struct uart_port *);
void (*unthrottle)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *, int new);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
void (*wake_peer)(struct uart_port *);
/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
由于drivers/tty/serial/serial_core.c是一个tty_driver,因此在serial_core.c中,存在一个tty_operations的实
例,这个实例的成员函数会进一步调用struct uart_ops的成员函数,从而把file_operations里的成员函数、
tty_operations的成员函数和uart_ops的成员函数串起来。