14.6 UART设备驱动
尽管一个特定的UART设备驱动完全可以遵循14.2~14.5的方法来设计,即定义tty_driver并实现其中的成员函数,但是Linux已经在文件 serial_core.c中实现了UART设备的通用tty驱动层(姑且称其为串口核心层),这样,UART驱动的主要任务演变成实现serial- core.c中定义的一组uart_xxx接口而非tty_xxx接口,如图14.5所示。
serial_core.c串口核心层完全可以被当作14.2~14.5节tty设备驱动的实例,它实现了UART设备的tty驱动。
提示:Linux驱动的这种分层思想在许多类型的设备驱动中都得到了体现,例如上一章IDE设备驱动中,内核实现了通用的IDE层用于处理块设备I/O请求,而具体的IDE则只需使用ide_xxx这样的接口,甚至不必理会复杂的块设备驱动结构。
图14.5 串口核心层
串口核心层为串口设备驱动提供了如下3个结构体:
1、uart_driver
uart_driver包含串口设备的驱动名、设备名、设备号等信息,它封装了tty_driver,使得底层的UART驱动无需关心tty_driver,其定义如代码清单14.13。
代码清单14.13 uart_driver结构体
1 struct uart_driver
2 {
3 struct module *owner;
4 const char *driver_name; //驱动名
5 const char *dev_name; //设备名
6 const char *devfs_name; //设备文件系统名
7 int major; //主设备号
8 int minor; //次设备号
9 int nr;
10 struct console *cons;
11
12 /* 私有的,底层驱动不应该访问这些成员,应该被初始化为NULL */
13 struct uart_state *state;
14 struct tty_driver *tty_driver;
15 };
一个tty驱动必须注册/注销tty_driver,而一个UART驱动则演变为注册/注销uart_driver,使用如下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
实际上,uart_register_driver()和uart_unregister_driver()中分别包含了tty_register_driver()和tty_unregister_driver()的操作,如代码清单14.14所示。
代码清单14.14 uart_register_driver()和uart_unregister_driver()函数
1 int uart_register_driver(struct uart_driver *drv)
2 {
3 struct tty_driver *normal = NULL;
4 int i, retval;
5 ...
6 /* 分配tty_driver */
7 normal = alloc_tty_driver(drv->nr);
8 if (!normal)
9 goto out;
10 drv->tty_driver = normal;
11 /* 初始化tty_driver */
12 normal->owner = drv->owner;
13 normal->driver_name = drv->driver_name;
14 normal->devfs_name = drv->devfs_name;
15 normal->name = drv->dev_name;
16 normal->major = drv->major;
17 normal->minor_start = drv->minor;
18 normal->type = TTY_DRIVER_TYPE_SERIAL;
19 normal->subtype = SERIAL_TYPE_NORMAL;
20 normal->init_termios = tty_std_termios;
21 normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
22 normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
23 normal->driver_state = drv;
24 tty_set_operations(normal, &uart_ops);
25
26 ...
27 /* 注册tty驱动 */
28 retval = tty_register_driver(normal);
29 out:
30 if (retval < 0) {
31 put_tty_driver(normal);
32 kfree(drv->state);
33 }
34 return retval;
35 }
36
37 void uart_unregister_driver(struct uart_driver *drv)
38 {
39 struct tty_driver *p = drv->tty_driver;
40 tty_unregister_driver(p); /* 注销tty驱动 */
41 put_tty_driver(p);
42 kfree(drv->state);
43 drv->tty_driver = NULL;
44 }
2、uart_port
uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息,其定义如代码清单14.15。
代码清单14.15 uart_port结构体
1 struct uart_port
2 {
3 spinlock_t lock; /* 端口锁 */
4 unsigned int iobase; /* IO端口基地址 */
5 unsigned char __iomem *membase; /* IO内存基地址 */
6 unsigned int irq; /* 中断号 */
7 unsigned int uartclk; /* UART时钟 */
8 unsigned char fifosize; /* 传输fifo大小 */
9 unsigned char x_char; /* xon/xoff字符 */
10 unsigned char regshift; /* 寄存器位移 */
11 unsigned char iotype; /* IO存取类型 */
12
13 #define UPIO_PORT (0) /* IO端口*/
14 #define UPIO_HUB6 (1)
15 #define UPIO_MEM (2) /* IO内存*/
16 #define UPIO_MEM32 (3)
17 #define UPIO_AU (4) /* Au1x00类型IO */
18
19 unsigned int read_status_mask; /* 驱动相关的 */
20 unsigned int ignore_status_mask; /* 驱动相关的 */
21 struct uart_info *info; /* 指向parent信息 */
22 struct uart_icount icount; /* 计数 */
23
24 struct console *cons; /* console结构体 */
25 #ifdef CONFIG_SERIAL_CORE_CONSOLE
26 unsigned long sysrq; /* sysrq超时 */
27 #endif
28
29 upf_t flags;
30
31 #define UPF_FOURPORT ((__force upf_t) (1 << 1))
32 #define UPF_SAK ((__force upf_t) (1 << 2))
33 #define UPF_SPD_MASK ((__force upf_t) (0x1030))
34 #define UPF_SPD_HI ((__force upf_t) (0x0010))
35 #define UPF_SPD_VHI ((__force upf_t) (0x0020))
36 #define UPF_SPD_CUST ((__force upf_t) (0x0030))
37 #define UPF_SPD_SHI ((__force upf_t) (0x1000))
38 #define UPF_SPD_WARP ((__force upf_t) (0x1010))
39 #define UPF_SKIP_TEST ((__force upf_t) (1 << 6))
40 #define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))
41 #define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))
42 #define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
43 #define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
44 #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
45 #define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
46 #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
47 #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
48 #define UPF_IOREMAP ((__force upf_t) (1 << 31))
49
50 #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
51 #define UPF_USR_MASK ((__force upf_t)
52 (UPF_SPD_MASK|UPF_LOW_LATENCY))
53 unsigned int mctrl; /* 目前modem控制设置 */
54 unsigned int timeout; /* 基于字符的超时 */
55 unsigned int type; /* 端口类型 */
56 const struct uart_ops *ops; /* UART操作集 */
57 unsigned int custom_divisor;
58 unsigned int line; /* 端口索引 */
59 unsigned long mapbase; /* ioremap后基地址 */
60 struct device *dev; /* parent设备 */
61 unsigned char hub6;
62 unsigned char unused[3];
63 };
串口核心层提供如下函数来添加1个端口:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
对上述函数的调用应该发生在uart_register_driver()之后,uart_add_one_port()的一个最重要作用是封装了tty_register_device()。
uart_add_one_port()的“反函数”是uart_remove_one_port(),其中会调用tty_unregister_device(),原型为:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
驱动中虽然不需要处理uart_port的uart_info成员,但是在发送时,从用户来的数据被保存在xmit(被定义为circ_buf,即环形缓冲 区)中,因此UART驱动在发送数据时(一般在发送中断处理函数中),需要从这个circ_buf获取上层传递下来的字符。
3、uart_ops
uart_ops 定义了针对UART的一系列操作,包括发送、接收及线路设置等,如果说tty_driver中的tty_operations对于串口还较为抽象,那么 uart_ops则直接面向了串口的UART,其定义如代码清单14.16。Linux驱动的这种层次非常类似于面向对象编程中基类、派生类的关系,派生类针对特定的事物会更加具体,而基类则站在更高的抽象层次上。
代码清单14.16 uart_ops结构体
1 struct uart_ops
2 {
3 unsigned int(*tx_empty)(struct uart_port*);
4 void(*set_mctrl)(struct uart_port *, unsigned int mctrl);
5 unsigned int(*get_mctrl)(struct uart_port*);
6 void(*stop_tx)(struct uart_port*); //停止发送
7 void(*start_tx)(struct uart_port*); //开始发送
8 void(*send_xchar)(struct uart_port *, char ch); //发送xchar
9 void(*stop_rx)(struct uart_port*); //停止接收
10 void(*enable_ms)(struct uart_port*);
11 void(*break_ctl)(struct uart_port *, int ctl);
12 int(*startup)(struct uart_port*);
13 void(*shutdown)(struct uart_port*);
14 void(*set_termios)(struct uart_port *, struct termios *new, struct termios
15 *old); //设置termios
16 void(*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
17 int(*set_wake)(struct uart_port *, unsigned int state);
18
19 /* 返回1个描述端口类型的字符串 */
20 const char *(*type)(struct uart_port*);
21
22 /* 释放端口使用的IO和内存资源,必要的情况下,应该进行iounmap操作*/
23 void(*release_port)(struct uart_port*);
24 /* 申请端口使用的IO和内存资源 */
25 int(*request_port)(struct uart_port*);
26
27 void(*config_port)(struct uart_port *, int);
28 int(*verify_port)(struct uart_port *, struct serial_struct*);
29 int(*ioctl)(struct uart_port *, unsigned int, unsigned long);
30 };
serial_core.c 中定义了tty_operations的实例,包含uart_open()、uart_close()、uart_write()、 uart_send_xchar()等成员函数(如代码清单14.17),这些函数会借助uart_ops结构体中的成员函数来完成具体的操作,代码清单 14.18给出了tty_operations的uart_send_xchar()成员函数利用uart_ops中start_tx()、 send_xchar()成员函数的例子。
代码清单14.17 串口核心层的tty_operations实例
1 static struct tty_operations uart_ops =
2 {
3 .open = uart_open,//串口打开
4 .close = uart_close,//串口关闭
5 .write = uart_write,//串口发送
6 .put_char = uart_put_char,//...
7 .flush_chars = uart_flush_chars,
8 .write_room = uart_write_room,
9 .chars_in_buffer= uart_chars_in_buffer,
10 .flush_buffer = uart_flush_buffer,
11 .ioctl = uart_ioctl,
12 .throttle = uart_throttle,
13 .unthrottle = uart_unthrottle,
14 .send_xchar = uart_send_xchar,
15 .set_termios = uart_set_termios,
16 .stop = uart_stop,
17 .start = uart_start,
18 .hangup = uart_hangup,
19 .break_ctl = uart_break_ctl,
20 .wait_until_sent= uart_wait_until_sent,
21 #ifdef CONFIG_PROC_FS
22 .read_proc = uart_read_proc, //proc入口读函数
23 #endif
24 .tiocmget = uart_tiocmget,
25 .tiocmset = uart_tiocmset,
26 };
代码清单14.18 串口核心层的tty_operations与uart_ops关系
1 static void uart_send_xchar(struct tty_struct *tty, char ch)
2 {
3 struct uart_state *state = tty->driver_data;
4 struct uart_port *port = state->port;
5 unsigned long flags;
6 //如果uart_ops中实现了send_xchar成员函数
7 if (port->ops->send_xchar)
8 port->ops->send_xchar(port, ch);
9 else //uart_ops中未实现send_xchar成员函数
10 {
11 port->x_char = ch; //xchar赋值
12 if (ch)
13 {
14 spin_lock_irqsave(&port->lock, flags);
15 port->ops->start_tx(port); //发送xchar
16 spin_unlock_irqrestore(&port->lock, flags);
17 }
18 }
19 }
注意: 整个调用流程为: 系统调用write()->uart_write()(tty_driver)->port->ops->start_tx();
在使用串口核心层这个通用串口tty驱动层的接口后,一个串口驱动要完成的主要工作将包括:
• 定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备 xxx的驱动可以将这些结构套在新定义的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之内。
• 在模块初始化时调用uart_register_driver()和uart_add_one_port()以注册UART驱动并添加端口,在模块卸载时 调用uart_unregister_driver()和uart_remove_one_port()以注销UART驱动并移除端口。
• 根据具体硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作