您的位置:首页 > 运维架构 > Linux

Linux终端设备驱动(四)

2009-09-09 17:11 465 查看
14.7实例:S3C2410 UART的驱动14.7.1 S3C2410串口硬件描述
S3C2410 内部具有3个独立的UART控制器,每个控制器都可以工作在Interrupt(中断)模式或DMA(直接内存访问)模式,也就是说UART控制器可以在 CPU与UART控制器传送资料的时候产生中断或DMA请求。S3C2410集成的每个UART均具有16字节的FIFO,支持的最高波特率可达到 230.4Kbps。
ULCONn(UART Line Control Register)寄存器用于S3C2410 UART的线路控制,用于设置模式、每帧的数据位数、停止位数及奇偶校验,如表14.1。
表14.1 S3C2410 UART的ULCONn寄存器
ULCONn 位 描述
保留 [7]
红外模式 [6] 0:正常模式 1:红外模式
奇偶校验 [5:3] 0xx:无校验 100:奇校验 101:偶校验 ...
停止位 [2] 0:1个停止位 1:2个停止位
字长 [1:0] 00:5位 01:6位 10:7位 11:8位
UCONn(UART Control Register)寄存器用于从整体上控制S3C2410 UART的中断模式及工作模式(DMA、中断、轮询)等,如表14.2。
表14.2 S3C2410 UART的UCONn寄存器
UCONn 位 描述
时钟选择 [10] 为UART的波特率产生选择PCLK或UCLK时钟
Tx中断 [9] 0:脉冲 1:电平
Rx中断 [8] 0:脉冲 1:电平
Rx超时使能 [7] 当UART被使能,使能/禁止Rx超时中断 0:禁止 1:使能
Rx错误状态中断使能 [6] 使能接收异常中断(如break、帧错误、校验错、溢出等)
loopback [5] 0:正常模式 1:回环
发送break [4] 设置该位将造成UART在1帧的时间内发送break,当发送完break后,该位将自动被清除
发送模式 [3:2] 发送数据到UART的模式,00:禁止 01:中断或轮询10:DMA0(仅针对UART0)、DMA3(仅针对UART3) 11:DMA1(仅针对UART1)
接收模式 [1:0] 从UART接收数据的模式,00:禁止 01:中断或轮询10:DMA0(仅针对UART0)
UFCONn(UART FIFO Conrtol Register)寄存器用于S3C2410 UART的FIFO控制,用于控制FIFO中断的触发级别以及复位时是否清空FIFO中的内容,如表14.3。
表14.3 S3C2410 UART的UFCONn寄存器
UFCONn 位 描述
Tx FIFO触发级别 [7:6] 决定发送FIFO的触发级别: 00:空 01:4字节10:8字节 11:12字节
Rx FIFO触发级别 [5:4] 决定接收FIFO的触发级别: 00:4字节 01:8字节10:12字节 11:16字节
Tx FIFO复位 [2] 复位FIFO后自动清除FIFO 0:正常 1:Tx FIFO复位
Rx FIFO复位 [1] 复位FIFO后自动清除FIFO 0:正常 1:Tx FIFO复位
FIFO使能 [0] 0:禁止 1:使能
代码清单14.19给出了UFCONn寄存器的位掩码和默认设置(使能FIFO、Tx FIFO为空时触发中断、Rx FIFO中包含8个字节时触发中断)。
代码清单14.19 S3C2410 UART UFCONn寄存器的位掩码和默认设置
1 #define S3C2410_UFCON_FIFOMODE (1<<0)
2 #define S3C2410_UFCON_TXTRIG0 (0<<6)
3 #define S3C2410_UFCON_RXTRIG8 (1<<4)
4 #define S3C2410_UFCON_RXTRIG12 (2<<4)
5
6 #define S3C2410_UFCON_RESETBOTH (3<<1)
7 #define S3C2410_UFCON_RESETTX (1<<2)
8 #define S3C2410_UFCON_RESETRX (1<<1)
9
10 #define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | /
11 S3C2410_UFCON_TXTRIG0 | /
12 S3C2410_UFCON_RXTRIG8 )
UFSTATn(UART FIFO Status Register)寄存器用于表征UART FIFO的状态,如表14.4。
表14.4 S3C2410 UART的UFSTATn寄存器
UFSTATn 位 描述
保留 [15:10]
Tx FIFO满 [9] 当Tx FIFO满后,将自动被设置为1 0:0字节 ≤ Tx FIFO数据数 ≤ 15 1:Tx FIFO数据数 = 15
Rx FIFO满 [8] 当Rx FIFO满后,将自动被设置为1 0:0字节 ≤ Rx FIFO数据数 ≤ 15 1:Tx FIFO数据数 = 15
Tx FIFO数据数 [7:4]

Rx FIFO数据数 [3:0]
由于UFSTATn寄存器中的Tx FIFO数据数和Rx FIFO数据数分别占据[7:4]和[3:0]位,因此定义S3C2410_UFSTAT_TXSHIFT和S3C2410_UFSTAT_RXSHIFT分别为4和0,代码清单14.20给出了UFSTATn寄存器的位掩码等信息。
代码清单14.20 S3C2410 UART UFSTATn寄存器的位掩码
1 #define S3C2410_UFSTAT_TXFULL (1<<9)
2 #define S3C2410_UFSTAT_RXFULL (1<<8)
3 #define S3C2410_UFSTAT_TXMASK (15<<4)
4 #define S3C2410_UFSTAT_TXSHIFT (4)
5 #define S3C2410_UFSTAT_RXMASK (15<<0)
6 #define S3C2410_UFSTAT_RXSHIFT (0)
UTXHn(UART Transmit Buffer Register)和 URXHn(UART Receive Buffer Register)分别是UART发送和接收数据寄存器,这2个寄存器存放着发送和接收的数据。
UTRSTATn(UART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断URXHn中是否有数据接收到或UTXHn是否为空,这个寄存器主要在非FIFO模式时使用。
UMCONn(UART Modem Control Register)用于S3C2410 UART的modem控制,设置是否使用RTS流控,若采用流控,可选择自动流控(Auto Flow Control,AFC)或由软件控制RTS信号的“高”或“低”电平。
14.7.2 S3C2410串口驱动数据结构
S3C2410串口驱动中uart_driver结构体实例的定义如代码清单14.21,设备名为“s3c2410_serial”,驱动名为“ttySAC”。
代码清单14.21 S3C2410串口驱动uart_driver结构体
1 #define S3C24XX_SERIAL_NAME "ttySAC"
2 #define S3C24XX_SERIAL_DEVFS "tts/"
3 #define S3C24XX_SERIAL_MAJOR 204
4 #define S3C24XX_SERIAL_MINOR 64
5
6 static struct uart_driver s3c24xx_uart_drv =
7 {
8 .owner = THIS_MODULE,
9 .dev_name = "s3c2410_serial",
10 .nr = 3,
11 .cons = S3C24XX_SERIAL_CONSOLE,
12 .driver_name = S3C24XX_SERIAL_NAME,
13 .devfs_name = S3C24XX_SERIAL_DEVFS,
14 .major = S3C24XX_SERIAL_MAJOR,
15 .minor = S3C24XX_SERIAL_MINOR,
16 };
S3C2410 串口驱动中定义了结构体s3c24xx_uart_port,该结构体中封装了uart_port结构体及一些针对S3C2410 UART的附加信息,代码清单14.22给出了s3c24xx_uart_port结构体及其实例s3c24xx_serial_ports[]数组。
代码清单14.22 S3C2410串口驱动s3c24xx_uart_port结构体
1 struct s3c24xx_uart_port
2 {
3 unsigned char rx_claimed;
4 unsigned char tx_claimed;
5
6 struct s3c24xx_uart_info *info;
7 struct s3c24xx_uart_clksrc *clksrc;
8 struct clk *clk;
9 struct clk *baudclk;
10 struct uart_port port;
11 };
12
13 static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
14 [0] = {
15 .port = {
16 .lock = SPIN_LOCK_UNLOCKED,
17 .iotype = UPIO_MEM,
18 .irq = IRQ_S3CUART_RX0,
19 .uartclk = 0,
20 .fifosize = 16,
21 .ops = &s3c24xx_serial_ops,
22 .flags = UPF_BOOT_AUTOCONF,
23 .line = 0,//端口索引:0
24 }
25 },
26 [1] = {
27 .port = {
28 .lock = SPIN_LOCK_UNLOCKED,
29 .iotype = UPIO_MEM,
30 .irq = IRQ_S3CUART_RX1,
31 .uartclk = 0,
32 .fifosize = 16,
33 .ops = &s3c24xx_serial_ops,
34 .flags = UPF_BOOT_AUTOCONF,
35 .line = 1, //端口索引:1
36 }
37 },
38 #if NR_PORTS > 2
39
40 [2] = {
41 .port = {
42 .lock = SPIN_LOCK_UNLOCKED,
43 .iotype = UPIO_MEM,
44 .irq = IRQ_S3CUART_RX2,
45 .uartclk = 0,
46 .fifosize = 16,
47 .ops = &s3c24xx_serial_ops,
48 .flags = UPF_BOOT_AUTOCONF,
49 .line = 2, //端口索引:2
50 }
51 }
52 #endif
53 };
S3C2410串口驱动中uart_ops结构体实例的定义如代码清单14.23,将一系列s3c24xx_serial_函数赋值给了uart_ops结构体的成员。
代码清单14.23 S3C2410串口驱动uart_ops结构体
1 static struct uart_ops s3c24xx_serial_ops =
2 {
3 .pm = s3c24xx_serial_pm,
4 .tx_empty = s3c24xx_serial_tx_empty,//发送缓冲区空
5 .get_mctrl = s3c24xx_serial_get_mctrl,//得到modem控制设置
6 .set_mctrl = s3c24xx_serial_set_mctrl, //设置modem控制(MCR)
7 .stop_tx = s3c24xx_serial_stop_tx, //停止接收字符
8 .start_tx = s3c24xx_serial_start_tx,//开始传输字符
9 .stop_rx = s3c24xx_serial_stop_rx, //停止接收字符
10 .enable_ms = s3c24xx_serial_enable_ms,// modem状态中断使能
11 .break_ctl = s3c24xx_serial_break_ctl,// 控制break信号的传输
12 .startup = s3c24xx_serial_startup,//启动端口
13 .shutdown = s3c24xx_serial_shutdown,// 禁用端口
14 .set_termios = s3c24xx_serial_set_termios,//改变端口参数
15 .type = s3c24xx_serial_type,//返回描述特定端口的常量字符串指针
16 .release_port = s3c24xx_serial_release_port,//释放端口占用的内存及IO资源
17 .request_port = s3c24xx_serial_request_port,//申请端口所需的内存和IO资源
18 .config_port = s3c24xx_serial_config_port,//执行端口所需的自动配置步骤
19 .verify_port = s3c24xx_serial_verify_port,//验证新的串行端口信息
20 };
set_mctrl()函数的原型为:
void (*set_mctrl)(struct uart_port *port, u_int mctrl);
它将参数port所对应的调制解调器控制线的值设为参数mctrl的值。
get_mctrl()函数的原型为:
unsigned int (*get_mctrl)(struct uart_port *port);
该函数返回调制解调器控制输入的现有状态,这些状态信息包括:TIOCM_CD(CD 信号状态)、TIOCM_CTS(CTS信号状态)、TIOCM_DSR(DSR信号状态)、TIOCM_RI(RI信号状态)等。如果信号被置为有效,则对应位将被置位。
端口启动函数startup()的原型为:
int (*startup)(struct uart_port *port, struct uart_info *info);
该函数申请所有中断资源,初始化底层驱动状态,并开启端口为可接收数据的状态。
shutdown()函数完成与startup()函数的作用相反,其原型:
void (*shutdown)(struct uart_port *port, struct uart_info *info);
这个函数禁用端口,释放所有的中断资源。
回过头来看s3c24xx_uart_port结构体,其中的s3c24xx_uart_info成员(代码清单14.22第6行)是一些针对S3C2410 UART的信息,其定义如代码清单14.24。
代码清单14.24 S3C2410串口驱动s3c24xx_uart_info结构体
1 static struct s3c24xx_uart_info s3c2410_uart_inf =
2 {
3 .name = "Samsung S3C2410 UART",
4 .type = PORT_S3C2410,
5 .fifosize = 16,
6 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
7 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
8 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
9 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
10 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
11 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
12 .get_clksrc = s3c2410_serial_getsource,
13 .set_clksrc = s3c2410_serial_setsource,
14 .reset_port = s3c2410_serial_resetport,
15 };
在S3C2410串口驱动中,针对UART的设置(UCONn、ULCONn、UFCONn寄存器等)被封装到s3c2410_uartcfg结构体中,其定义如代码清单14.25。
代码清单14.25 S3C2410串口驱动s3c2410_uartcfg结构体
1 struct s3c2410_uartcfg
2 {
3 unsigned char hwport; /* 硬件端口号 */
4 unsigned char unused;
5 unsigned short flags;
6 unsigned long uart_flags; /* 缺省的uart标志 */
7
8 unsigned long ucon; /* 端口的ucon值 */
9 unsigned long ulcon; /* 端口的ulcon值 */
10 unsigned long ufcon; /* 端口的ufcon值 */
11
12 struct s3c24xx_uart_clksrc *clocks;
13 unsigned int clocks_size;
14 };
14.7.3 S3C2410串口驱动初始化与释放
在S3C2410 串口驱动的模块加载函数中会调用uart_register_driver()注册s3c24xx_uart_drv这个uart_driver,同时经过s3c2410_serial_init()→s3c24xx_serial_init()→platform_driver_register()的调用导致s3c24xx_serial_probe()被执行,而s3c24xx_serial_probe()函数中会调用s3c24xx_serial_init_port()初始化UART端口并调用uart_add_one_port()添加端口,整个过程的对应代码如 清单14.26。
代码清单14.26 S3C2410串口驱动初始化过程
1 static int __init s3c24xx_serial_modinit(void)
2 {
3 int ret;
4 //注册uart_driver
5 ret = uart_register_driver(&s3c24xx_uart_drv);
6 if (ret < 0) {
7 printk(KERN_ERR "failed to register UART driver/n");
8 return -1;
9 }
10 //初始化s3c2410的串口
11 s3c2410_serial_init();
12
13 return 0;
14 }
15
16 static inline int s3c2410_serial_init(void)
17 {
18 return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
19 }
20
21 static int s3c24xx_serial_init(struct platform_driver *drv,
22 struct s3c24xx_uart_info *info)
23 {
24 dbg("s3c24xx_serial_init(%p,%p)/n", drv, info);
25 return platform_driver_register(drv);//注册平台驱动
26 }
27
28 //平台驱动probe()函数
29 static int s3c24xx_serial_probe(struct platform_device *dev,
30 struct s3c24xx_uart_info *info)
31 {
32 struct s3c24xx_uart_port *ourport;
33 int ret;
34
35 dbg("s3c24xx_serial_probe(%p, %p) %d/n", dev, info, probe_index);
36
37 ourport = &s3c24xx_serial_ports[probe_index];
38 probe_index++;
39
40 dbg("%s: initialising port %p.../n", __FUNCTION__, ourport);
41 //初始化uart端口
42 ret = s3c24xx_serial_init_port(ourport, info, dev);
43 if (ret < 0)
44 goto probe_err;
45
46 dbg("%s: adding port/n", __FUNCTION__);
47 //添加uart_port
48 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
49 platform_set_drvdata(dev, &ourport->port);
50
51 return 0;
52
53 probe_err:
54 return ret;
55 }
56 //42行调用的s3c24xx_serial_init_port()函数
57 static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
58 struct s3c24xx_uart_info *info,
59 struct platform_device *platdev)
60 {
61 struct uart_port *port = &ourport->port;
62 struct s3c2410_uartcfg *cfg;
63 struct resource *res;
64
65 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p/n", port, platdev);
66
67 if (platdev == NULL)
68 return -ENODEV;
69
70 cfg = s3c24xx_dev_to_cfg(&platdev->dev);
71
72 if (port->mapbase != 0)
73 return 0;
74
75 if (cfg->hwport > 3)
76 return -EINVAL;
77
78 /* 为端口设置info成员 */
79 port->dev = &platdev->dev;
80 ourport->info = info;
81
82 /* 初始化fifosize */
83 ourport->port.fifosize = info->fifosize;
84
85 dbg("s3c24xx_serial_init_port: %p (hw %d).../n", port, cfg->hwport);
86
87 port->uartclk = 1;
88 /* 如果使用流控 */
89 if (cfg->uart_flags & UPF_CONS_FLOW) {
90 dbg("s3c24xx_serial_init_port: enabling flow control/n");
91 port->flags |= UPF_CONS_FLOW;
92 }
93
94 /* 利用平台资源中记录的信息初始化uart端口的基地址、中断号 */
95 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
96 if (res == NULL) {
97 printk(KERN_ERR "failed to find memory resource for uart/n");
98 return -EINVAL;
99 }
100
101 dbg("resource %p (%lx..%lx)/n", res, res->start, res->end);
102
103 port->mapbase = res->start;
104 port->membase = S3C24XX_VA_UART+(res->start - S3C24XX_PA_UART);
105 port->irq = platform_get_irq(platdev, 0);
106
107 ourport->clk = clk_get(&platdev->dev, "uart");
108
109 dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld/n",
110 port->mapbase, port->membase, port->irq, port->uartclk);
111
112 /* 复位fifo并设置uart */
113 s3c24xx_serial_resetport(port, cfg);
114 return 0;
115 }
116 //113行调用的s3c24xx_serial_resetport()函数
117 static inline int s3c24xx_serial_resetport(struct uart_port * port,
118 struct s3c2410_uartcfg *cfg)
119 {
120 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
121
122 return (info->reset_port)(port, cfg);
123 }
124 //122行调用的info->reset_port()函数
125 static int s3c2410_serial_resetport(struct uart_port *port,
126 struct s3c2410_uartcfg *cfg)
127 {
128 dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p/n",
129 port, port->mapbase, cfg);
130
131 wr_regl(port, S3C2410_UCON, cfg->ucon);
132 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
133
134 /* 复位2个fifo */
135 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
136 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
137
138 return 0;
139 }
在S3C2410串口驱动模块被卸载时,它会最终调用uart_remove_one_port()释放uart_port并调用uart_unregister_driver()注销uart_driver,如代码清单14.27所示。
代码清单14.27 S3C2410串口驱动释放过程
1 static void __exit s3c24xx_serial_modexit(void)
2 {
3 s3c2410_serial_exit();
4 //注销uart_driver
5 uart_unregister_driver(&s3c24xx_uart_drv);
6 }
7
8 static inline void s3c2410_serial_exit(void)
9 {
10 //注销平台驱动
11 platform_driver_unregister(&s3c2410_serial_drv);
12 }
13
14 static int s3c24xx_serial_remove(struct platform_device *dev)
15 {
16 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
17
18 //移除uart端口
19 if (port)
20 uart_remove_one_port(&s3c24xx_uart_drv, port);
21
22 return 0;
23 }
上述代码中对S3C24xx_serial_remove()的调用发生在platform_driver_unregister()之后,由于S3C2410的UART是集成于SoC芯片内部的一个独立的硬件单元,因此也被作为1个平台设备而定义。
14.7.4 S3C2410串口数据收发
S3C2410串口驱动uart_ops结构体的startup ()成员函数s3c24xx_serial_startup()用于启动端口,申请端口的发送、接收中断,使能端口的发送和接收,其实现如代码清单14.28。
代码清单14.28 S3C2410串口驱动startup ()函数
1 static int s3c24xx_serial_startup(struct uart_port *port)
2 {
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4 int ret;
5
6 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)/n",
7 port->mapbase, port->membase);
8
9 rx_enabled(port) = 1;//置接收使能状态为1
10 //申请接收中断
11 ret = request_irq(RX_IRQ(port),
12 s3c24xx_serial_rx_chars, 0,
13 s3c24xx_serial_portname(port), ourport);
14
15 if (ret != 0) {
16 printk(KERN_ERR "cannot get irq %d/n", RX_IRQ(port));
17 return ret;
18 }
19
20 ourport->rx_claimed = 1;
21
22 dbg("requesting tx irq.../n");
23
24 tx_enabled(port) = 1;//置发送使能状态为1
25 //申请发送中断
26 ret = request_irq(TX_IRQ(port),
27 s3c24xx_serial_tx_chars, 0,
28 s3c24xx_serial_portname(port), ourport);
29
30 if (ret) {
31 printk(KERN_ERR "cannot get irq %d/n", TX_IRQ(port));
32 goto err;
33 }
34
35 ourport->tx_claimed = 1;
36
37 dbg("s3c24xx_serial_startup ok/n");
38
39 /* 端口复位代码应该已经为端口控制设置了正确的寄存器 */
40
41 return ret;
42
43 err:
44 s3c24xx_serial_shutdown(port);
45 return ret;
46 }
s3c24xx_serial_startup()的“反函数”为s3c24xx_serial_shutdown(),其释放中断,禁止发送和接收,实现如代码清单14.29。
代码清单14.29 S3C2410串口驱动shutdown ()函数
1 static void s3c24xx_serial_shutdown(struct uart_port *port)
2 {
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4
5 if (ourport->tx_claimed) {
6 free_irq(TX_IRQ(port), ourport);
7 tx_enabled(port) = 0; //置发送使能状态为0
8 ourport->tx_claimed = 0;
9 }
10
11 if (ourport->rx_claimed) {
12 free_irq(RX_IRQ(port), ourport);
13 ourport->rx_claimed = 0;
14 rx_enabled(port) = 0; //置接收使能状态为1
15 }
16 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: