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)

6  #define S3C2410_UFCON_RESETBOTH   (3<<1)
7  #define S3C2410_UFCON_RESETTX   (1<<2)
8  #define S3C2410_UFCON_RESETRX   (1<<1)

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

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;

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标志 */

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  }

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;

6   dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)/n",
7       port->mapbase, port->membase);

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);

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 }
    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);

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);

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;

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个字符

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);

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。

转自:http://blog.csdn.net/wyl123/archive/2009/10/29/4741627.aspx

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值