Linux serial构架分析及驱动开发

前面介绍了tty核心分析及tty驱动开发的方法,tty设备包括串口、终端、伪终端三大类,其中终端和伪终端驱动内核都帮我们实现好了,很少需要改动。因此我们主要介绍串口驱动的开发及其在内核中的构架(其核心实现源码主要在/drivers/serial_core.c中),这一节中我们主要分析向内核中加入一个serial驱动用到的数据结构。

      serial core是构建在tty core之上的。注册一个串口驱动即在tty core层注册一个tty驱动。下面我们看看串口驱动中用到的两个最重要的数据机构 struct uart_driver 表示一个serial驱动,struct uart_port 表示一个串口端口。

struct uart_driver {
 struct module  *owner;
 const char  *driver_name;  //驱动名称
 const char  *dev_name;     //设备名基础
 int    major;                         //主设备号
 int    minor;                         //起始次设备号
 int    nr;                              //设备个数
 struct console  *cons;        //关联的控制台

 /*
  * these are private; the low level driver should not
  * touch these; they should be initialised to NULL
  */
 struct uart_state *state;        //串口驱动操作设备数组
 struct tty_driver *tty_driver;  //表征串口驱动的tty驱动
};

      上面结构中struct uart_state *state指向驱动操作的串口设备相关数据结构,其中struct uart_port *port就是我们下面要介绍的描述串口设备的结构,struct uart_info *info 指向相关联的struct tty_struct 结构和数据发射时环形缓冲区struct circ_buf xmit,即串口打开时的描述信息。uart_info有两个成员在底层串口驱动会用到:xmit和tty。用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过tty将接收到的数据传递给行规则层。具体的成员分析我们在后面介绍具体的操作时再分析。

struct uart_port {
 spinlock_t  lock;   /* port lock */   //串口端口锁
 unsigned int  iobase;   /* in/out[bwl] */    //io端口基地址
 unsigned char __iomem *membase;  /* read/write[bwl] */  //io内存基地址,虚拟地址
 unsigned int  irq;   /* irq number */      //中断号
 unsigned int  uartclk;  /* base uart clock */  //串口时钟
 unsigned int  fifosize;  /* tx fifo size */       //串口fifo缓冲大小
 unsigned char  x_char;   /* xon/xoff char */  //xon/xoff字符
 unsigned char  regshift;  /* reg offset shift */       //j寄存器移位      
 unsigned char  iotype;   /* io access style */    //io访问方式
 unsigned char  unused1;

#define UPIO_PORT  (0)   //端口
#define UPIO_HUB6  (1)
#define UPIO_MEM  (2)    //内存
#define UPIO_MEM32  (3)
#define UPIO_AU   (4)   /* Au1x00 type IO */
#define UPIO_TSI  (5)   /* Tsi108/109 type IO */
#define UPIO_DWAPB  (6)   /* DesignWare APB UART */
#define UPIO_RM9000  (7)   /* RM9000 type IO */

 unsigned int  read_status_mask; /* driver specific */   //关心的rx error status
 unsigned int  ignore_status_mask; /* driver specific */ //忽略的rx error status

 struct uart_info *info;   /* pointer to parent info */
 struct uart_icount icount;   /* statistics */

 struct console  *cons;   /* struct console, if any */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
 unsigned long  sysrq;   /* sysrq timeout */
#endif

 upf_t   flags;

#define UPF_FOURPORT  ((__force upf_t) (1 << 1))
#define UPF_SAK   ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK  ((__force upf_t) (0x1030))
#define UPF_SPD_HI  ((__force upf_t) (0x0010))
#define UPF_SPD_VHI  ((__force upf_t) (0x0020))
#define UPF_SPD_CUST  ((__force upf_t) (0x0030))
#define UPF_SPD_SHI  ((__force upf_t) (0x1000))
#define UPF_SPD_WARP  ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST  ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ  ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD  ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY  ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART  ((__force upf_t) (1 << 14))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW  ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ  ((__force upf_t) (1 << 24))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT  ((__force upf_t) (1 << 29))
#define UPF_DEAD  ((__force upf_t) (1 << 30))
#define UPF_IOREMAP  ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK  ((__force upf_t) (0x17fff))
#define UPF_USR_MASK  ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

 unsigned int  mctrl;   /* current modem ctrl settings */
 unsigned int  timeout;  /* character-based timeout */
 unsigned int  type;   /* port type */
 const struct uart_ops *ops;   //具体端口的相关操作函数
 unsigned int  custom_divisor;
 unsigned int  line;   /* port index */
 resource_size_t  mapbase;  /* for ioremap */  //io内存物理地址
 struct device  *dev;   /* parent device */
 unsigned char  hub6;   /* this should be in the 8250 driver */
 unsigned char  suspended;
 unsigned char  unused[2]; //允许串口收发字符标志
 void   *private_data;  /* generic platform data pointer */
};


uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。在串口的发送中断处理函数和接收中断处理函数中,我们需要管理这些计数。

struct uart_icount {
 __u32 cts;
 __u32 dsr;
 __u32 rng;
 __u32 dcd;
 __u32 rx;             //接收字符数
 __u32 tx;             //发送字符数
 __u32 frame;       //错误帧计数
 __u32 overrun;    //rx fifo溢出计数
 __u32 parity;        //帧校验错误计数
 __u32 brk;            //break计数
 __u32 buf_overrun;
};


对于实现一个串口驱动,主要的工作量就是实现struct uart_ops *ops中的各个操作函数。
 * This structure describes all the operations that can be
 * done on the physical hardware.
 */
struct uart_ops {
 unsigned int (*tx_empty)(struct uart_port *);  //串口tx FIFO缓存是否为空
 void  (*set_mctrl)(struct uart_port *, unsigned int mctrl);  //设置串口modem控制
 unsigned int (*get_mctrl)(struct uart_port *);                    //获得串口的modem控制
 void  (*stop_tx)(struct uart_port *);                                   //停止串口发送
 void  (*start_tx)(struct uart_port *);                                   //使能串口发送
 void  (*send_xchar)(struct uart_port *, char ch);                //发送xchar
 void  (*stop_rx)(struct uart_port *);                                   //禁止串口接收
 void  (*enable_ms)(struct uart_port *);                              //使能modem状态信号
 void  (*break_ctl)(struct uart_port *, int ctl);                      //设置break信号
 int  (*startup)(struct uart_port *);                                      //启动串口
 void  (*shutdown)(struct uart_port *);                                //关闭串口
 void  (*flush_buffer)(struct uart_port *);                             //刷新缓存
 void  (*set_termios)(struct uart_port *, struct ktermios *new,
           struct ktermios *old);                                               //设置串口参数
 void  (*set_ldisc)(struct uart_port *);                                 //设置线路规程
 void  (*pm)(struct uart_port *, unsigned int state,
         unsigned int oldstate);    //电源管理
 int  (*set_wake)(struct uart_port *, unsigned int state);

 /*
  * Return a string describing the type of the port
  */
 const char *(*type)(struct uart_port *);

 /*
  * Release IO and memory resources used by the port.
  * This includes iounmap if necessary.
  */
 void  (*release_port)(struct uart_port *);

 /*
  * Request IO and memory resources used by the port.
  * This includes iomapping the port if necessary.
  */
 int  (*request_port)(struct uart_port *);
 void  (*config_port)(struct uart_port *, int);   //执行串口所需的自动配置
 int  (*verify_port)(struct uart_port *, struct serial_struct *);  //核实串口信息
 int  (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
 void (*poll_put_char)(struct uart_port *, unsigned char);
 int  (*poll_get_char)(struct uart_port *);
#endif
};

相关推荐