免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 884 | 回复: 0
打印 上一主题 下一主题

Linux终端设备驱动2 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-12-26 17:51 |只看该作者 |倒序浏览
14.6 UART设备驱动
    尽管一个特定的UART设备驱动完全可以遵循14.2~14.5的方法来设计,即定义tty_driver并实现其中的成员函数,但是Linux已经在文件 serial_core.c中实现了UART设备的通用tty驱动层(姑且称其为串口核心层),这样,UART驱动的主要任务演变成实现serial- core.c中定义的一组uart_xxx接口而非tty_xxx接口,如图14.5所示。
    serial_core.c串口核心层完全可以被当作14.2~14.5节tty设备驱动的实例,它实现了UART设备的tty驱动。
    提示:Linux驱动的这种分层思想在许多类型的设备驱动中都得到了体现,例如上一章IDE设备驱动中,内核实现了通用的IDE层用于处理块设备I/O请求,而具体的IDE则只需使用ide_xxx这样的接口,甚至不必理会复杂的块设备驱动结构。
            

                        图14.5 串口核心层
串口核心层为串口设备驱动提供了如下3个结构体:
1、uart_driver
uart_driver包含串口设备的驱动名、设备名、设备号等信息,它封装了tty_driver,使得底层的UART驱动无需关心tty_driver,其定义如代码清单14.13。
代码清单14.13 uart_driver结构体
1  struct uart_driver
2  {
3    struct module *owner;
4    const char *driver_name; //驱动名
5    const char *dev_name;    //设备名
6    const char *devfs_name;  //设备文件系统名
7    int major;  //主设备号
8    int minor;   //次设备号
9    int nr;
10   struct console *cons;
11
12   /* 私有的,底层驱动不应该访问这些成员,应该被初始化为NULL */
13   struct uart_state *state;
14   struct tty_driver *tty_driver;
15 };
一个tty驱动必须注册/注销tty_driver,而一个UART驱动则演变为注册/注销uart_driver,使用如下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
实际上,uart_register_driver()和uart_unregister_driver()中分别包含了tty_register_driver()和tty_unregister_driver()的操作,如代码清单14.14所示。
代码清单14.14 uart_register_driver()和uart_unregister_driver()函数
1  int uart_register_driver(struct uart_driver *drv)
2  {
3   struct tty_driver *normal = NULL;
4   int i, retval;
5   ...
6    /* 分配tty_driver */
7   normal  = alloc_tty_driver(drv->nr);
8   if (!normal)
9    goto out;
10  drv->tty_driver = normal;
11   /* 初始化tty_driver */
12  normal->owner  = drv->owner;
13  normal->driver_name = drv->driver_name;
14  normal->devfs_name = drv->devfs_name;
15  normal->name  = drv->dev_name;
16  normal->major  = drv->major;
17  normal->minor_start = drv->minor;
18  normal->type  = TTY_DRIVER_TYPE_SERIAL;
19  normal->subtype  = SERIAL_TYPE_NORMAL;
20  normal->init_termios = tty_std_termios;
21  normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
22  normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
23  normal->driver_state    = drv;
24  tty_set_operations(normal, &uart_ops);
25
26  ...
27  /* 注册tty驱动 */
28  retval = tty_register_driver(normal);
29  out:
30  if (retval state);
33  }
34  return retval;
35 }
36
37 void uart_unregister_driver(struct uart_driver *drv)
38 {
39  struct tty_driver *p = drv->tty_driver;
40  tty_unregister_driver(p);  /* 注销tty驱动 */
41  put_tty_driver(p);
42  kfree(drv->state);
43  drv->tty_driver = NULL;
44 }
2、uart_port
uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息,其定义如代码清单14.15。
代码清单14.15 uart_port结构体
1  struct uart_port
2  {
3    spinlock_t lock; /* 端口锁 */
4    unsigned int iobase; /* IO端口基地址 */
5    unsigned char __iomem *membase; /* IO内存基地址 */
6    unsigned int irq; /* 中断号 */
7    unsigned int uartclk; /* UART时钟 */
8    unsigned char fifosize; /* 传输fifo大小 */
9    unsigned char x_char; /* xon/xoff字符 */
10   unsigned char regshift; /* 寄存器位移 */
11   unsigned char iotype; /* IO存取类型 */
12
13   #define UPIO_PORT  (0)  /* IO端口*/
14   #define UPIO_HUB6  (1)
15   #define UPIO_MEM  (2)  /* IO内存*/
16   #define UPIO_MEM32  (3)
17   #define UPIO_AU   (4)   /* Au1x00类型IO */
18
19   unsigned int read_status_mask; /* 驱动相关的 */
20   unsigned int ignore_status_mask; /* 驱动相关的 */
21   struct uart_info *info; /* 指向parent信息 */
22   struct uart_icount icount; /* 计数 */
23
24   struct console *cons; /* console结构体 */
25   #ifdef CONFIG_SERIAL_CORE_CONSOLE
26     unsigned long sysrq; /* sysrq超时 */
27   #endif
28
29   upf_t flags;
30
31   #define UPF_FOURPORT  ((__force upf_t) (1 目前modem控制设置 */
54   unsigned int timeout; /* 基于字符的超时 */
55   unsigned int type; /* 端口类型 */
56   const struct uart_ops *ops; /* UART操作集 */
57   unsigned int custom_divisor;
58   unsigned int line; /* 端口索引 */
59   unsigned long mapbase; /* ioremap后基地址 */
60   struct device *dev; /* parent设备 */
61   unsigned char hub6;
62   unsigned char unused[3];
63 };
串口核心层提供如下函数来添加1个端口:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
    对上述函数的调用应该发生在uart_register_driver()之后,uart_add_one_port()的一个最重要作用是封装了tty_register_device()。
    uart_add_one_port()的“反函数”是uart_remove_one_port(),其中会调用tty_unregister_device(),原型为:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
    驱动中虽然不需要处理uart_port的uart_info成员,但是在发送时,从用户来的数据被保存在xmit(被定义为circ_buf,即环形缓冲 区)中,因此UART驱动在发送数据时(一般在发送中断处理函数中),需要从这个circ_buf获取上层传递下来的字符。
3、uart_ops
     uart_ops 定义了针对UART的一系列操作,包括发送、接收及线路设置等,如果说tty_driver中的tty_operations对于串口还较为抽象,那么 uart_ops则直接面向了串口的UART,其定义如代码清单14.16。Linux驱动的这种层次非常类似于面向对象编程中基类、派生类的关系,派生类针对特定的事物会更加具体,而基类则站在更高的抽象层次上。
代码清单14.16 uart_ops结构体
1  struct uart_ops
2  {
3    unsigned int(*tx_empty)(struct uart_port*);
4    void(*set_mctrl)(struct uart_port *, unsigned int mctrl);
5    unsigned int(*get_mctrl)(struct uart_port*);
6    void(*stop_tx)(struct uart_port*);    //停止发送
7    void(*start_tx)(struct uart_port*);   //开始发送
8    void(*send_xchar)(struct uart_port *, char ch); //发送xchar
9    void(*stop_rx)(struct uart_port*);   //停止接收
10   void(*enable_ms)(struct uart_port*);
11   void(*break_ctl)(struct uart_port *, int ctl);
12   int(*startup)(struct uart_port*);
13   void(*shutdown)(struct uart_port*);
14   void(*set_termios)(struct uart_port *, struct termios *new, struct termios
15     *old);        //设置termios
16   void(*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
17   int(*set_wake)(struct uart_port *, unsigned int state);
18
19   /* 返回1个描述端口类型的字符串 */
20   const char *(*type)(struct uart_port*);
21
22   /* 释放端口使用的IO和内存资源,必要的情况下,应该进行iounmap操作 */
23   void(*release_port)(struct uart_port*);
24   /* 申请端口使用的IO和内存资源 */
25   int(*request_port)(struct uart_port*);
26   
27   void(*config_port)(struct uart_port *, int);
28   int(*verify_port)(struct uart_port *, struct serial_struct*);
29   int(*ioctl)(struct uart_port *, unsigned int, unsigned long);
30 };
    serial_core.c 中定义了tty_operations的实例,包含uart_open()、uart_close()、uart_write()、 uart_send_xchar()等成员函数(如代码清单14.17),这些函数会借助uart_ops结构体中的成员函数来完成具体的操作,代码清单 14.18给出了tty_operations的uart_send_xchar()成员函数利用uart_ops中start_tx()、 send_xchar()成员函数的例子。
代码清单14.17 串口核心层的tty_operations实例
1  static struct tty_operations uart_ops =
2  {
3   .open  = uart_open,//串口打开
4   .close  = uart_close,//串口关闭
5   .write  = uart_write,//串口发送
6   .put_char = uart_put_char,//...
7   .flush_chars = uart_flush_chars,
8   .write_room = uart_write_room,
9   .chars_in_buffer= uart_chars_in_buffer,
10  .flush_buffer = uart_flush_buffer,
11  .ioctl  = uart_ioctl,
12  .throttle = uart_throttle,
13  .unthrottle = uart_unthrottle,
14  .send_xchar = uart_send_xchar,
15  .set_termios = uart_set_termios,
16  .stop  = uart_stop,
17  .start  = uart_start,
18  .hangup  = uart_hangup,
19  .break_ctl = uart_break_ctl,
20  .wait_until_sent= uart_wait_until_sent,
21 #ifdef CONFIG_PROC_FS
22  .read_proc = uart_read_proc, //proc入口读函数
23 #endif
24  .tiocmget = uart_tiocmget,
25  .tiocmset = uart_tiocmset,
26 };
代码清单14.18 串口核心层的tty_operations与uart_ops关系
1  static void uart_send_xchar(struct tty_struct *tty, char ch)
2  {
3    struct uart_state *state = tty->driver_data;
4    struct uart_port *port = state->port;
5    unsigned long flags;
6    //如果uart_ops中实现了send_xchar成员函数
7    if (port->ops->send_xchar)
8      port->ops->send_xchar(port, ch);  
9    else //uart_ops中未实现send_xchar成员函数
10   {
11     port->x_char = ch; //xchar赋值
12     if (ch)
13     {
14       spin_lock_irqsave(&port->lock, flags);
15       port->ops->start_tx(port);  //发送xchar
16       spin_unlock_irqrestore(&port->lock, flags);
17     }
18   }
19 }
注意:  整个调用流程为:  系统调用write()->uart_write()(tty_driver)->port->ops->start_tx();
  在使用串口核心层这个通用串口tty驱动层的接口后,一个串口驱动要完成的主要工作将包括:
•  定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备 xxx的驱动可以将这些结构套在新定义的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之内。
•  在模块初始化时调用uart_register_driver()和uart_add_one_port()以注册UART驱动并添加端口,在模块卸载时 调用uart_unregister_driver()和uart_remove_one_port()以注销UART驱动并移除端口。
•  根据具体硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为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(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(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信号的“高”

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/103860/showart_2131571.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP