Linux终端设备驱动(五)

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;//置端口发送使能状态为
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;//置端口发送使能状态为
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.34s3c24xx_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()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它 porttermios参数成员的值设置S3C2410 UARTULCONnUCONnUMCONn等寄存器,其实现如代码清单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    /* 设置是否采用RTSCTS自动流空 */
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.36get_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.

你可能感兴趣的:(C#)