博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux终端设备驱动 ----UART的驱动
阅读量:4137 次
发布时间:2019-05-25

本文共 24289 字,大约阅读时间需要 80 分钟。

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_TXSHIFTS3C2410_UFSTAT_RXSHIFT分别为40,代码清单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)和 URXHnUART Receive Buffer Register)分别是UART发送和接收数据寄存器,这2个寄存器存放着发送和接收的数据。
UTRSTATn
UART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断URXHn中是否有数据接收到或UTXHn是否为空,这个寄存器主要在非FIFO模式时使用。
UMCONn
UART Modem Control Register)用于S3C2410 UARTmodem控制,设置是否使用RTS流控,若采用流控,可选择自动流控(Auto Flow ControlAFC)或由软件控制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_CDCD 信号状态)、TIOCM_CTSCTS信号状态)、TIOCM_DSRDSR信号状态)、TIOCM_RIRI信号状态)等。如果信号被置为有效,则对应位将被置位。
端口启动函数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.226行)是一些针对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的设置(UCONnULCONnUFCONn寄存器等)被封装到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  /* 
复位2fifo */
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()之后,由于S3C2410UART是集成于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 }
    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

转载地址:http://bhlvi.baihongyu.com/

你可能感兴趣的文章
poj 1011 回溯+剪枝 木棒问题
查看>>
深刻理解Linux进程间通信(IPC)
查看>>
2013阿里巴巴实习笔试题 最后两题 明星问题+仓库运货
查看>>
最长递减子序列--动态规划
查看>>
最长公共子序列--动态规划
查看>>
rand7()构造rand10()
查看>>
异或运算 ^ 变量交换及找出现一次的数
查看>>
微软等面试100题系列--(61-80)
查看>>
在字符串中删除特定的字符
查看>>
求质数算法的N种境界 (N > 10)
查看>>
复杂链表的复制
查看>>
主存与Cache的地址映射
查看>>
socket编程
查看>>
求二进制数中1的个数
查看>>
TCP流量控制和拥塞控制
查看>>
MMSEG 分词算法
查看>>
搜索引擎技术介绍
查看>>
linux下添加用户并赋予root权限
查看>>
Linux 查看CPU信息、机器型号等硬件信息
查看>>
Vim配置文件(全平台可用)2012-05-01版
查看>>