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。

S3C2410 UART驱动相关推荐

  1. Linux设备驱动之UART驱动结构

    一.对于串口驱动Linux系统中UART驱动属于终端设备驱动,应该说是实现串口驱动和终端驱动来实现串口终端设备的驱动.要了解串口终端的驱动在Linux系统的结构就先要了解终端设备驱动在Linux系统中 ...

  2. linux设备驱动之串口移植,Linux设备驱动之UART驱动结构

    一.对于串口驱动Linux系统中UART驱动属于终端设备驱动,应该说是实现串口驱动和终端驱动来实现串口终端设备的驱动.要了解串口终端的驱动在Linux系统的结构就先要了解终端设备驱动在Linux系统中 ...

  3. uart驱动框架及编程方法

    一.UART介绍 UART(Universal Asynchronous Receiver/Transmitter),中文全称为通用异步收发传输器,是一种异步收发传输器,它将要传输的数据通过并行到串行 ...

  4. 超详细Uart驱动框架及编程方法

    一.UART介绍 UART(Universal Asynchronous Receiver/Transmitter),中文全称为通用异步收发传输器,是一种异步收发传输器,它将要传输的数据通过并行到串行 ...

  5. s3c6140 UART驱动设计

    s3c6140 UART驱动设计

  6. 移植基于linux-2.6.26.5内核s3c2410触摸屏驱动移植

    移植基于linux-2.6.26.5内核s3c2410触摸屏驱动移植的过程记录下来: (1)首先打一个补丁:s3c2410_touchscreen.patch, 在内核解压的根目录下 patch -N ...

  7. usb serial port 驱动_tty初探 — uart驱动框架分析

    写在前面: 我们没有讲UART驱动,不过我们认为,只要系统学习了第2期,应该具备分析UART驱动的能力,小编做答疑几年以来,陆陆续续有不少人问到UART驱动怎么写,所以今天就分享一篇深度长文(1700 ...

  8. Linux uart驱动框架

    Linux uart驱动框架 串口驱动框架包括两部分 struct uart_driver int uart_register_driver(struct uart_driver *uart); vo ...

  9. Hi3519v101 uart驱动

    一.说明 1.Hi3519v101 SDK已经将uart驱动写好了,可以直接使用.但内核默认只添加了uart0驱动,其他端口需要手动添加.uart驱动路径为: drivers/tty/serial/a ...

最新文章

  1. final finally finalize 的区别
  2. 使用火炬之光资源(转)
  3. Spring使用Quartz配置调度事务
  4. SQL注入漏洞解决方法
  5. Glusterfs初试
  6. MapReduce作业提交到YARN上运行的步骤
  7. 数据恢复工程师视角看腾讯云静默损坏事件
  8. 修辞结构理论论文集合
  9. MATLAB中zero和ones函数(转载)
  10. 运维派送福利,就是这么任性!
  11. android裁剪部分放大动画,【Android】图片放大被截了一部分之认识clipChildren属性的用法...
  12. 一个初学者的辛酸路程-常用模块-6
  13. 【VS2015】 C++实现硬件ID的查询
  14. 网友在各自领域中所用到的芯片总结(转载)
  15. macOS Big Sur 11.6.5 (20G527) 正式版 ISO、PKG、DMG、IPSW 下载
  16. 如何提高(怎样才能不断提升自己)
  17. node对接微信支付 sdk tenpay
  18. Object.defineProperty 以及 Proxy对比和基本语法 实干vue3数据响应
  19. leetcode简单之603.连续空余座位
  20. Unity发布WebGL不显示中文字体问题

热门文章

  1. 软件测试的前景怎么样?要不要转行软件测试?
  2. 【java基础】java异常,捕获与抛出
  3. 免费OA系统平台在企业发展中的优势(转载)
  4. android logcat 包名过滤日志
  5. 我是如何从测试转为产品的
  6. 小程序开发(首页设计)
  7. 【Java版oj】逆波兰表达式求值
  8. redis的持久化操作2 AOF
  9. ehchache验证缓存过期的api_SpringBoot - 缓存的使用详解1(使用Ehcache 2.x缓存)
  10. 视频播放器GSYVideoPlayer