Linux终端设备驱动 ----UART的驱动

2024-02-03 19:38
文章标签 linux 驱动 uart 终端设备

本文主要是介绍Linux终端设备驱动 ----UART的驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

这篇关于Linux终端设备驱动 ----UART的驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/675179

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo