S3C2410 串口驱动uart_ops结构体的tx_empty()成员函数s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空,其实现 如代码清单14.30,当使能FIFO模式的时候,判断UFSTATn寄存器,否则判断UTRSTATn寄存器的相应位。
代码清单14.30 S3C2410串口驱动tx_empty()函数
1 /* 检查发送缓冲区/FIFO是否为空 */
2 static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
3 {
4 //fifo模式,检查UFSTATn寄存器
5 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
6 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
7 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
8
9 if (ufcon &S3C2410_UFCON_FIFOMODE)
10 {
11 if ((ufstat &info->tx_fifomask) != 0 || //Tx fifo数据数非0
12 (ufstat &info->tx_fifofull)) // Tx fifo满
13 return 0; //0:非空
14
15 return 1; //1:空
16 }
17
18 return s3c24xx_serial_txempty_nofifo(port);
19 }
20
21 static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
22 {
23 //非fifo模式,检查UTRSTATn寄存器
24 return (rd_regl(port, S3C2410_UTRSTAT) &S3C2410_UTRSTAT_TXE);
25 }
S3C2410 串口驱动uart_ops结构体的start_tx ()成员函数s3c24xx_serial_start_tx()用于启动发送,而stop_rx()成员函数 s3c24xx_serial_stop_rx()用于停止发送,代码清单14.31给出了这2个函数的实现。
代码清单14.31 S3C2410串口驱动start_tx ()、stop_rx()函数
1 static void s3c24xx_serial_start_tx(struct uart_port *port)
2 {
3 if (!tx_enabled(port)) {//如果端口发送未使能
4 if (port->flags & UPF_CONS_FLOW)
5 s3c24xx_serial_rx_disable(port);
6
7 enable_irq(TX_IRQ(port));//使能发送中断
8 tx_enabled(port) = 1;//置端口发送使能状态为1
9 }
10 }
11
12 static void s3c24xx_serial_stop_tx(struct uart_port *port)
13 {
14 if (tx_enabled(port)) {//如果端口发送已使能
15 disable_irq(TX_IRQ(port));//禁止发送中断
16 tx_enabled(port) = 0;//置端口发送使能状态为0
17 if (port->flags & UPF_CONS_FLOW)
18 s3c24xx_serial_rx_enable(port);
19 }
20 }
S3C2410 串口驱动uart_ops结构体的stop_rx ()成员函数s3c24xx_serial_stop_rx ()用于停止接收,代码清单14.32给出了这个函数的实现。注意uart_ops中没有start_rx()成员,因为接收并不是由“我方”启动,而是 由“它方”的发送触发“我方”的接收中断,“我方”再被动响应。
代码清单14.32 S3C2410串口驱动stop_rx ()函数
1 static void s3c24xx_serial_stop_rx(struct uart_port *port)
2 {
3 if (rx_enabled(port)) {//如果接收为使能
4 dbg("s3c24xx_serial_stop_rx: port=%p/n", port);
5 disable_irq(RX_IRQ(port));//禁止接收中断
6 rx_enabled(port) = 0;//置接收使能状态为0
7 }
8 }
在S3C2410 串口驱动中,与数据收发关系最密切的函数不是上述uart_ops成员函数,而是s3c24xx_serial_startup()为发送和接收中断注册 的中断处理函数s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。s3c24xx_serial_rx_chars ()读取URXHn寄存器以获得接收到的字符,并调用uart_insert_char()将该字符添加了tty设备的flip缓冲区中,当接收到64个 字符或者不再能接受到字符后,调用tty_flip_buffer_push()函数向上层“推”tty设备的flip缓冲,其实现如代码清单 14.33。
代码清单14.33 S3C2410串口驱动接收中断处理函数
1 static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id, struct
2 pt_regs *regs)
3 {
4 struct s3c24xx_uart_port *ourport = dev_id;
5 struct uart_port *port = &ourport->port; //获得uart_port
6 struct tty_struct *tty = port->info->tty; //获得tty_struct
7 unsigned int ufcon, ch, flag, ufstat, uerstat;
8 int max_count = 64;
9
10 while (max_count-- > 0)
11 {
12 ufcon = rd_regl(port, S3C2410_UFCON);
13 ufstat = rd_regl(port, S3C2410_UFSTAT);
14 //如果接收到0个字符
15 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
16 break;
17
18 uerstat = rd_regl(port, S3C2410_UERSTAT);
19 ch = rd_regb(port, S3C2410_URXH); //读出字符
20
21 if (port->flags &UPF_CONS_FLOW)
22 {
23 int txe = s3c24xx_serial_txempty_nofifo(port);
24
25 if (rx_enabled(port)) //如果端口为使能接收状态
26 {
27 if (!txe) //如果发送缓冲区为空
28 {
29 rx_enabled(port) = 0; //置端口为使能接收状态为0
30 continue;
31 }
32 }
33 else //端口为禁止接收状态
34 {
35 if (txe) //如果发送缓冲区非空
36 {
37 ufcon |= S3C2410_UFCON_RESETRX;
38 wr_regl(port, S3C2410_UFCON, ufcon);
39 rx_enabled(port) = 1;//置端口为使能接收状态为1
40 goto out;
41 }
42 continue;
43 }
44 }
45
46 /* 将接收到的字符写入buffer */
47 flag = TTY_NORMAL;
48 port->icount.rx++;
49
50 if (unlikely(uerstat &S3C2410_UERSTAT_ANY))
51 {
52 dbg("rxerr: port ch=0x%02x, rxs=0x%08x/n", ch, uerstat);
53
54 if (uerstat &S3C2410_UERSTAT_BREAK)
55 {
56 dbg("break!/n");
57 port->icount.brk++;
58 if (uart_handle_break(port))
59 goto ignore_char;
60 }
61
62 if (uerstat &S3C2410_UERSTAT_FRAME)
63 port->icount.frame++;
64 if (uerstat &S3C2410_UERSTAT_OVERRUN)
65 port->icount.overrun++;
66
67 uerstat &= port->read_status_mask;
68
69 if (uerstat &S3C2410_UERSTAT_BREAK)
70 flag = TTY_BREAK;
71 else if (uerstat &S3C2410_UERSTAT_PARITY)
72 flag = TTY_PARITY;
73 else if (uerstat &(S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
74 flag = TTY_FRAME;
75 }
76
77 if (uart_handle_sysrq_char(port, ch, regs)) //处理sysrq字符
78 goto ignore_char;
79 //插入字符到tty设备的flip 缓冲
80 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
81
82 ignore_char: continue;
83 }
84 tty_flip_buffer_push(tty); //刷新tty设备的flip 缓冲
85
86 out: return IRQ_HANDLED;
87 }
上述代码第80行的uart_insert_char()函数是串口核心层对tty_insert_flip_char()的封装,它作为内联函数被定义于serial_core.h文件中。
如代码清单14.34,s3c24xx_serial_tx_chars()读取uart_info中环形缓冲区中的字符,写入调用UTXHn寄存器。
代码清单14.34 S3C2410串口驱动发送中断处理函数
1 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id, struct pt_regs
2 *regs)
3 {
4 struct s3c24xx_uart_port *ourport = id;
5 struct uart_port *port = &ourport->port;
6 struct circ_buf *xmit = &port->info->xmit; //得到环形缓冲区
7 int count = 256; //最多1次发256个字符
8
9 if (port->x_char) //如果定义了xchar,发送
10 {
11 wr_regb(port, S3C2410_UTXH, port->x_char);
12 port->icount.tx++;
13 port->x_char = 0;
14 goto out;
15 }
16
17 /* 如果没有更多的字符需要发送,或者uart Tx停止,则停止uart并退出 */
18 if (uart_circ_empty(xmit) || uart_tx_stopped(port))
19 {
20 s3c24xx_serial_stop_tx(port);
21 goto out;
22 }
23
24 /* 尝试把环行buffer中的数据发空 */
25 while (!uart_circ_empty(xmit) && count-- > 0)
26 {
27 if (rd_regl(port, S3C2410_UFSTAT) &ourport->info->tx_fifofull)
28 break;
29
30 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
31 xmit->tail = (xmit->tail + 1) &(UART_XMIT_SIZE - 1);
32 port->icount.tx++;
33 }
34 /* 如果环形缓冲区中剩余的字符少于WAKEUP_CHARS,唤醒上层 */
35 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
36 uart_write_wakeup(port);
37
38 if (uart_circ_empty(xmit)) //如果发送环形buffer为空
39 s3c24xx_serial_stop_tx(port); //停止发送
40
41 out: return IRQ_HANDLED;
42 }
上述代码第35行的宏WAKEUP_CHARS的含义为:当发送环形缓冲区中的字符数小于该数时,驱动将请求上层向下传递更多的数据,uart_write_wakeup()完成此目的。
uart_circ_chars_pending()、uart_circ_empty()是定义于serial_core.h中的宏,分别返回环形缓冲区剩余的字符数以及判断缓冲区是否为空。
14.7.5 S3C2410串口线路设置
S3C2410 串口驱动uart_ops结构体的set_termios()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它 的port、termios参数成员的值设置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器,其实现如代码清单14.35。
代码清单14.35 S3C2410串口驱动set_termios()函数
1 static void s3c24xx_serial_set_termios(struct uart_port *port,
2 struct termios *termios,
3 struct termios *old)
4 {
5 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
6 struct s3c24xx_uart_port *ourport = to_ourport(port);
7 struct s3c24xx_uart_clksrc *clksrc = NULL;
8 struct clk *clk = NULL;
9 unsigned long flags;
10 unsigned int baud, quot;
11 unsigned int ulcon;
12 unsigned int umcon;
13
14 /* 不支持modem控制信号线 */
15 termios->c_cflag &= ~(HUPCL | CMSPAR);
16 termios->c_cflag |= CLOCAL;
17
18 /* 请求内核计算分频以便产生对应的波特率 */
19 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
20
21 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
22 quot = port->custom_divisor;
23 else
24 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
25
26 /* 检查以确定是否需要改变时钟源 */
27 if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
28 s3c24xx_serial_setsource(port, clksrc);
29
30 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
31 clk_disable(ourport->baudclk);
32 ourport->baudclk = NULL;
33 }
34
35 clk_enable(clk);
36
37 ourport->clksrc = clksrc;
38 ourport->baudclk = clk;
39 }
40
41 /* 设置字长 */
42 switch (termios->c_cflag & CSIZE) {
43 case CS5:
44 dbg("config: 5bits/char/n");
45 ulcon = S3C2410_LCON_CS5;
46 break;
47 case CS6:
48 dbg("config: 6bits/char/n");
49 ulcon = S3C2410_LCON_CS6;
50 break;
51 case CS7:
52 dbg("config: 7bits/char/n");
53 ulcon = S3C2410_LCON_CS7;
54 break;
55 case CS8:
56 default:
57 dbg("config: 8bits/char/n");
58 ulcon = S3C2410_LCON_CS8;
59 break;
60 }
61
62 /* 保留以前的lcon IR设置 */
63 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
64
65 if (termios->c_cflag & CSTOPB)
66 ulcon |= S3C2410_LCON_STOPB;
67 /* 设置是否采用RTS、CTS自动流空 */
68 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
69
70 if (termios->c_cflag & PARENB) {
71 if (termios->c_cflag & PARODD)
72 ulcon |= S3C2410_LCON_PODD;//计校验
73 else
74 ulcon |= S3C2410_LCON_PEVEN;//偶校验
75 } else {
76 ulcon |= S3C2410_LCON_PNONE;//无校验
77 }
78
79 spin_lock_irqsave(&port->lock, flags);
80
81 dbg("setting ulcon to %08x, brddiv to %d/n", ulcon, quot);
82
83 wr_regl(port, S3C2410_ULCON, ulcon);
84 wr_regl(port, S3C2410_UBRDIV, quot);
85 wr_regl(port, S3C2410_UMCON, umcon);
86
87 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x/n",
88 rd_regl(port, S3C2410_ULCON),
89 rd_regl(port, S3C2410_UCON),
90 rd_regl(port, S3C2410_UFCON));
91
92 /* 更新端口的超时 */
93 uart_update_timeout(port, termios->c_cflag, baud);
94
95 /* 我们对什么字符状态状态标志感兴趣?*/
96 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
97 if (termios->c_iflag & INPCK)
98 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
99
100 /* 我们要忽略什么字符状态标志?*/
101 port->ignore_status_mask = 0;
102 if (termios->c_iflag & IGNPAR)
103 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
104 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
105 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
106
107 /* 如果CREAD未设置,忽略所用字符 */
108 if ((termios->c_cflag & CREAD) == 0)
109 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
110
111 spin_unlock_irqrestore(&port->lock, flags);
112 }
由于S3C2410集成UART并不包含完整的Modem控制信号线,因此其uart_ops结构体的get_mctrl()、set_mctrl()成员 函数的实现非常简单,如代码清单14.36,get_mctrl()返回DSR一直有效,而CTS则根据UMSTATn寄存器的内容获得, set_mctrl()目前为空。
代码清单14.36 S3C2410串口驱动get_mctrl()和set_mctrl()函数
1 static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
2 {
3 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
4
5 if (umstat & S3C2410_UMSTAT_CTS)//CTS信号有效(低电平)
6 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
7 else
8 return TIOCM_CAR | TIOCM_DSR;
9 }
10
11 static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
12 {
13 /* todo:可能移除AFC,并手工进行CTS */
14 }
14.8总结
TTY设备驱动的主体工作围绕tty_driver这个结构体的成员函数展开,主要应实现其中的数据发送和接收流程以及tty设备线路设置接口函数。
针对串口,内核实现了串口核心层,这个层实现了串口设备通用的tty_driver。因此,串口设备驱动的主体工作从tty_driver转移到了uart_driver.