Chinaunix首页 | 论坛 | 博客
  • 博客访问: 216038
  • 博文数量: 63
  • 博客积分: 3010
  • 博客等级: 中校
  • 技术积分: 620
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-05 14:36
文章存档

2010年(8)

2009年(55)

我的朋友

分类: LINUX

2009-12-26 17:51:20

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.214.5tty设备驱动的实例,它实现了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 < 0) {
31   put_tty_driver(normal);
32   kfree(drv->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 << 1))
32   #define UPF_SAK   ((__force upf_t) (1 << 2))
33   #define UPF_SPD_MASK  ((__force upf_t) (0x1030))
34   #define UPF_SPD_HI  ((__force upf_t) (0x0010))
35   #define UPF_SPD_VHI  ((__force upf_t) (0x0020))
36   #define UPF_SPD_CUST  ((__force upf_t) (0x0030))
37   #define UPF_SPD_SHI  ((__force upf_t) (0x1000))
38   #define UPF_SPD_WARP  ((__force upf_t) (0x1010))
39   #define UPF_SKIP_TEST  ((__force upf_t) (1 << 6))
40   #define UPF_AUTO_IRQ  ((__force upf_t) (1 << 7))
41   #define UPF_HARDPPS_CD  ((__force upf_t) (1 << 11))
42   #define UPF_LOW_LATENCY  ((__force upf_t) (1 << 13))
43   #define UPF_BUGGY_UART  ((__force upf_t) (1 << 14))
44   #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
45   #define UPF_CONS_FLOW  ((__force upf_t) (1 << 23))
46   #define UPF_SHARE_IRQ  ((__force upf_t) (1 << 24))
47   #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
48   #define UPF_IOREMAP  ((__force upf_t) (1 << 31))
49
50   #define UPF_CHANGE_MASK  ((__force upf_t) (0x17fff))
51   #define UPF_USR_MASK  ((__force upf_t)
52       (UPF_SPD_MASK|UPF_LOW_LATENCY))
53   unsigned int mctrl; /*
目前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_portuart_info成员,但是在发送时,从用户来的数据被保存在xmit(被定义为circ_buf,即环形缓冲 区)中,因此UART驱动在发送数据时(一般在发送中断处理函数中),需要从这个circ_buf获取上层传递下来的字符。
3
uart_ops
     uart_ops
定义了针对UART的一系列操作,包括发送、接收及线路设置等,如果说tty_driver中的tty_operations对于串口还较为抽象,那么 uart_ops则直接面向了串口的UART,其定义如代码清单14.16Linux驱动的这种层次非常类似于面向对象编程中基类、派生类的关系,派生类针对特定的事物会更加具体,而基类则站在更高的抽象层次上。
代码清单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_operationsuart_send_xchar()成员函数利用uart_opsstart_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_operationsuart_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_driveruart_opsuart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备 xxx的驱动可以将这些结构套在新定义的xxx_uart_driverxxx_uart_opsxxx_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控制器可以在 CPUUART控制器传送资料的时候产生中断或DMA请求。S3C2410集成的每个UART均具有16字节的FIFO,支持的最高波特率可达到 230.4Kbps
ULCONn
UART Line Control Register)寄存器用于S3C2410 UART的线路控制,用于设置模式、每帧的数据位数、停止位数及奇偶校验,如表14.1
14.1 S3C2410 UARTULCONn寄存器
ULCONn 
              描述
保留 [7] 
红外模式 [6]           0:正常模式  1:红外模式
奇偶校验 [5:3]         0xx:无校验  100:奇校验  101:偶校验  ...
停止位 [2]             01个停止位  12个停止位
字长 [1:0]             005 016 107 118
UCONn
UART Control Register)寄存器用于从整体上控制S3C2410 UART的中断模式及工作模式(DMA、中断、轮询)等,如表14.2
14.2 S3C2410 UARTUCONn寄存器
UCONn 
               描述
时钟选择 [10]            UART的波特率产生选择PCLKUCLK时钟
Tx
中断 [9]               0:脉冲 1:电平
Rx
中断 [8]               0:脉冲 1:电平
Rx
超时使能 [7]          UART被使能,使能/禁止Rx超时中断  0:禁止  1:使能
Rx
错误状态中断使能 [6]   使能接收异常中断(如break、帧错误、校验错、溢出等)
loopback [5]              0
:正常模式 1:回环
发送break [4]            设置该位将造成UART1帧的时间内发送break,当发送完break后,该位将自动被清除
发送模式 [3:2]            发送数据到UART的模式,00:禁止 01:中断或轮询 10DMA0(仅针对UART0)、DMA3(仅针对UART3 11DMA1(仅针对UART1
接收模式 [1:0]            UART接收数据的模式,00:禁止 01:中断或轮询 10DMA0(仅针对UART0
     UFCONn
UART FIFO Conrtol Register)寄存器用于S3C2410 UARTFIFO控制,用于控制FIFO中断的触发级别以及复位时是否清空FIFO中的内容,如表14.3
 
14.3 S3C2410 UARTUFCONn寄存器
UFCONn 
              描述
Tx FIFO
触发级别 [7:6]    决定发送FIFO的触发级别: 00:空 014字节 108字节 1112字节
Rx FIFO
触发级别 [5:4]    决定接收FIFO的触发级别: 004字节 018字节 1012字节 1116字节
Tx FIFO
复位 [2]          复位FIFO后自动清除FIFO  0:正常 1Tx FIFO复位
Rx FIFO
复位 [1]          复位FIFO后自动清除FIFO  0:正常 1Tx FIFO复位
FIFO
使能 [0]             0:禁止 1:使能
代码清单14.19给出了UFCONn寄存器的位掩码和默认设置(使能FIFOTx 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 UARTUFSTATn寄存器
UFSTATn 
              描述
保留 [15:10] 
Tx FIFO
 [9]            Tx FIFO满后,将自动被设置为1  00字节 ≤ Tx FIFO数据数 ≤ 15  1Tx FIFO数据数 = 15
Rx FIFO
 [8]            Rx FIFO满后,将自动被设置为1  00字节 ≤ Rx FIFO数据数 ≤ 15  1Tx 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分别为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信号的

阅读(916) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~