Linux tty驱动学习 - 串口通信,UART及UART驱动概述

数据在单条传输线上,一位接一位地按顺序传送的方式称为串行通信。串行通信有两种方式:异步方式和同步方式。实现串行通信的接口叫串行接口,它主要的功能就是实现CPU与输入输出设备之间的串并行转换。把从CPU传送过来的并行数据转换成串行从输出设备输出,以及把从输入设备输入的串行数据转换成并行数据送给CPU。串行通信中,因为数据是一位一位地传输,所以为了是接收端能够识别接收到的每位数据的具体意思,收发双方就需要遵守相同的约定,即串口通信协议,它用来保证接收方知晓数据传输的开始及结束,以及判断接收数据的正确性。在异步串行通信中,在发送数据之前会先发送一个开始位,表示数据传输的开始,在发送完所有的数据后,会再多发送一个结束位,表示整个传输过程结束。为了保证数据的正确性,通常会采用奇偶校验法来验证接收到的数据,所以在传输的原始数据后面会再加上奇偶校验位。在同步串行通信中,收发双方采用同一个时钟源来双方的同步,所以传输数据时无需起始位和停止位。同步的方法有外同步和内同步两种,外同步法是在发送数据之前向接收端发送一串时钟脉冲,接收端按这个时钟频率调整自己的时序,是接收时钟频率锁定在接收到的时钟频率上,内同步法是接收端从接收到的数据信息波形本身提取同步的方法。

RS-232C定义了串行通信的物理接口,主要包括机械指标和电气指标。RS-232C标准的连接器有DB25和DB9两种,目前PC上常见的COM口就是DB9类型的连接器。DB9包含9个信号Pin,分别是TxD/RxD,RTS/CTS,DTR/DSR,SG/DCD/RI。TxD/RxD是用来发送和接收数据的,RTS/CTS是用来实现硬件流量控制的,当数据终端设备准备好数据时,会发出RTS信号通知数据通信设备,如果数据通信设备已经准备好接收数据,就会发送一个CTS信号来响应RTS信号。DTR/DSR是数据终端设备和数据通信设备之间的联络信号,DTR表明数据终端设备准备就绪,而DSR用来表明数据通信设备准备就绪。SG为信号地,用来提供参考电平。当通信双方之间有接调制解调器的时候,DCD/RI用来反映调制解调器所侦测到的状态。

异步串行通信的核心是通用异步收发传输器,通常称为UART。常见的UART芯片有INS8250,PC16550,PC16650,PC16750。以PC16650为例,与CPU连接一侧,它有8条并行数据线实现与CPU的并行数据传输,另外还有一个选择和控制逻辑单元,负责实现芯片片选以及IO寄存器选址功能。与输入输出设备连接一侧,由接收移位寄存器和发送移位寄存器实现串行数据的接收和发送。同时还有一个Modem控制逻辑单元,负责数据终端设备和数据通信设备之间的联络信号以及流量控制。在芯片内部,有12个8位寄存器,但是PC16550与CPU之间只有三个寻址Pin脚用来寻址,所以最多只能访问到8个IO端口,也就是说只能存取8个寄存器,所以需要结合读写信号和线路控制寄存器LCR的最高位DLAB位来协同寻址。

DLAB A2A1A0 被访问的寄存器
0 000 接收数据寄存器RBR(读)
发送保持寄存器THR(写)
0 001 中断允许寄存器IER
1 000 波特率除数寄存器(低字节)
1 001 波特率除数寄存器(高字节)
X 010 中断识别寄存器IIR(只读)
FIFO控制器FCR(只写)
X 011 线路控制寄存器LCR
X 100 MODEM控制寄存器MCR
X 101 线路状态寄存器LSR
X 110 MODEM状态寄存器MSR
X 111 暂存

在Linux中,UART属于TTY设备三大类别中的串口,所以UART驱动完全遵循TTY驱动的架构。但事实上,Linux内核在TTY驱动框架下又封装一层UART驱动。内核中用uart_driver来表示一个UART驱动。driver_name和dev_name分别表示驱动的名字和该驱动所管理的设备名字,在系统/dev目录下以ttyS开头的就是串口的名字。major和minor为主次设备号,nr表示该驱动支持的设备数目。tty_dirver就是UART驱动中用来对接TTY驱动框架的接口,uart_state指向一个串口设备数组。

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;
};

uart_state用来表示一个具体的串口设备,tty_port是从tty核心层的角度来表示一个串口设备,其中就封装了tty核心层要用到的数据结构tty_struct。uart_port用来表示设备的具体信息,包括设备的控制和状态信息。xmit是数据发送缓冲区,用来接收上层用户传递过来的数据。在uart_port包含了两个重要的数据结构,分别是uart_icount和uart_ops。

struct uart_state {
	struct tty_port		port;

	int			pm_state;
	struct circ_buf		xmit;

	struct tasklet_struct	tlet;
	struct uart_port	*uart_port;
};

uart_icount为串口信息计数器,包含了发送字符计数、接受字符计数等,在串口的发送中断处理函数和接收中断处理函数中,需要管理这些计数信息。uart_ops 跟tty_operations类似,是实现对UART操作的函数接口集合。

struct uart_icount {
	__u32	cts;
	__u32	dsr;
	__u32	rng;
	__u32	dcd;
	__u32	rx;
	__u32	tx;
	__u32	frame;
	__u32	overrun;
	__u32	parity;
	__u32	brk;
	__u32	buf_overrun;
};

struct uart_ops {
	unsigned int	(*tx_empty)(struct uart_port *);
	void		(*set_mctrl)(struct uart_port *, unsigned int mctrl);
	unsigned int	(*get_mctrl)(struct uart_port *);
	void		(*stop_tx)(struct uart_port *);
	void		(*start_tx)(struct uart_port *);
	void		(*send_xchar)(struct uart_port *, char ch);
	void		(*stop_rx)(struct uart_port *);
	void		(*enable_ms)(struct uart_port *);
	void		(*break_ctl)(struct uart_port *, int ctl);
	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
};








你可能感兴趣的:(Linux内核和驱动学习)