i.MX6ULL驱动开发 | 15 - Linux UART 驱动框架

Linux UART 驱动框架比较简单,不需要设备驱动,只需要UART设备驱动即可。

一、Linux UART驱动框架

1. uart_driver结构体

Linux将 UART 驱动抽象为 uart_driver结构体,定义在include/linux/serial_core.h文件中,如下:

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;
};
  • owner:模块拥有者
  • driver_name:驱动名称
  • dev_name:设备名称
  • major:主设备号
  • minor:次设备号
  • nr:设备数
  • cons:控制台

uart_driver结构体注册/注销API如下。

(1)向内核注册uart_driver

int uart_register_driver(struct uart_driver *uart);

(2)从内核注销uart_driver

void uart_unregister_driver(struct uart_driver *uart);

2. uart_port结构体

uart_port用于表示一个具体的串口,也定义在该文件中,如下:

struct uart_port {
    
    
	spinlock_t		lock;			/* port lock */
	unsigned long		iobase;			/* in/out[bwl] */
	unsigned char __iomem	*membase;		/* read/write[bwl] */
	unsigned int		(*serial_in)(struct uart_port *, int);
	void			(*serial_out)(struct uart_port *, int, int);
	void			(*set_termios)(struct uart_port *,
				               struct ktermios *new,
				               struct ktermios *old);
	void			(*set_mctrl)(struct uart_port *, unsigned int);
	int			(*startup)(struct uart_port *port);
	void			(*shutdown)(struct uart_port *port);
	void			(*throttle)(struct uart_port *port);
	void			(*unthrottle)(struct uart_port *port);
	int			(*handle_irq)(struct uart_port *);
	void			(*pm)(struct uart_port *, unsigned int state,
				      unsigned int old);
	void			(*handle_break)(struct uart_port *);
	int			(*rs485_config)(struct uart_port *,
						struct serial_rs485 *rs485);
	unsigned int		irq;			/* irq number */
	unsigned long		irqflags;		/* irq flags  */
	unsigned int		uartclk;		/* base uart clock */
	unsigned int		fifosize;		/* tx fifo size */
	unsigned char		x_char;			/* xon/xoff char */
	unsigned char		regshift;		/* reg offset shift */
	unsigned char		iotype;			/* io access style */
	unsigned char		unused1;

#define UPIO_PORT		(SERIAL_IO_PORT)	/* 8b I/O port access */
#define UPIO_HUB6		(SERIAL_IO_HUB6)	/* Hub6 ISA card */
#define UPIO_MEM		(SERIAL_IO_MEM)		/* 8b MMIO access */
#define UPIO_MEM32		(SERIAL_IO_MEM32)	/* 32b little endian */
#define UPIO_AU			(SERIAL_IO_AU)		/* Au1x00 and RT288x type IO */
#define UPIO_TSI		(SERIAL_IO_TSI)		/* Tsi108/109 type IO */
#define UPIO_MEM32BE		(SERIAL_IO_MEM32BE)	/* 32b big endian */

	unsigned int		read_status_mask;	/* driver specific */
	unsigned int		ignore_status_mask;	/* driver specific */
	struct uart_state	*state;			/* pointer to parent state */
	struct uart_icount	icount;			/* statistics */

	struct console		*cons;			/* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
	unsigned long		sysrq;			/* sysrq timeout */
#endif

	/* flags must be updated while holding port mutex */
	upf_t			flags;

	/*
	 * These flags must be equivalent to the flags defined in
	 * include/uapi/linux/tty_flags.h which are the userspace definitions
	 * assigned from the serial_struct flags in uart_set_info()
	 * [for bit definitions in the UPF_CHANGE_MASK]
	 *
	 * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable
	 * except bit 15 (UPF_NO_TXEN_TEST) which is masked off.
	 * The remaining bits are serial-core specific and not modifiable by
	 * userspace.
	 */
#define UPF_FOURPORT		((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
#define UPF_SAK			((__force upf_t) ASYNC_SAK            /* 2  */ )
#define UPF_SPD_HI		((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
#define UPF_SPD_VHI		((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
#define UPF_SPD_CUST		((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
#define UPF_SPD_WARP		((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
#define UPF_SPD_MASK		((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
#define UPF_SKIP_TEST		((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
#define UPF_AUTO_IRQ		((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
#define UPF_HARDPPS_CD		((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
#define UPF_SPD_SHI		((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
#define UPF_LOW_LATENCY		((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
#define UPF_BUGGY_UART		((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
#define UPF_NO_TXEN_TEST	((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER	((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )

/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS		((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS		((__force upf_t) (1 << 21))
#define UPF_HARD_FLOW		((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR		((__force upf_t) (1 << 25))
#define UPF_BUG_THRE		((__force upf_t) (1 << 26))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE		((__force upf_t) (1 << 27))
#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	0x17fff
#define UPF_CHANGE_MASK		((__force upf_t) __UPF_CHANGE_MASK)
#define UPF_USR_MASK		((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

#if __UPF_CHANGE_MASK > ASYNC_FLAGS
#error Change mask not equivalent to userspace-visible bit defines
#endif

	/*
	 * Must hold termios_rwsem, port mutex and port lock to change;
	 * can hold any one lock to read.
	 */
	upstat_t		status;

#define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
#define UPSTAT_DCD_ENABLE	((__force upstat_t) (1 << 1))
#define UPSTAT_AUTORTS		((__force upstat_t) (1 << 2))
#define UPSTAT_AUTOCTS		((__force upstat_t) (1 << 3))
#define UPSTAT_AUTOXOFF		((__force upstat_t) (1 << 4))

	int			hw_stopped;		/* sw-assisted CTS flow state */
	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 */
	unsigned int		minor;
	resource_size_t		mapbase;		/* for ioremap */
	resource_size_t		mapsize;
	struct device		*dev;			/* parent device */
	unsigned char		hub6;			/* this should be in the 8250 driver */
	unsigned char		suspended;
	unsigned char		irq_wake;
	unsigned char		unused[2];
	struct attribute_group	*attr_group;		/* port specific attributes */
	const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */
	struct serial_rs485     rs485;
	void			*private_data;		/* generic platform data pointer */
};

在uart_port中,ops成员中包含了具体的串口驱动函数,每个串口都有一个uart_port。

3. ops函数指针实现

uart_ops结构体是 serial_core 和具体的硬件驱动之间最主要的接口,包含了控制硬件的所有方法。

Linux内核最终收发数据调用的都是ops中的函数,定义如下:

/*
 * This structure describes all the operations that can be done on the
 * physical hardware.  See Documentation/serial/driver for details.
 */
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		(*throttle)(struct uart_port *);
	void		(*unthrottle)(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 *, struct ktermios *);
	void		(*pm)(struct uart_port *, unsigned int state,
			      unsigned int oldstate);

	/*
	 * 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
	int		(*poll_init)(struct uart_port *);
	void		(*poll_put_char)(struct uart_port *, unsigned char);
	int		(*poll_get_char)(struct uart_port *);
#endif
};

关于ops成员中每个函数指针的意义,可以参考文档:Documentation/serial/driver

(1)tx_empty

该函数测试串口的发送FIFO是否为空。如果为空,返回TIOCSER_TEMT,否则返回0,如果该串口不支持此操作,则返回TIOCSER_TEMT。

(2)set_mctrl:这个函数将’port’所描述的端口的调制解调器控制线设置为mctrl所描述的状态。

(3)get_mctrl:返回调制解调器控制输入的当前状态。

(4)stop_tx:停止传输字符。

(5)start_tx:开始传输字符。

(6)send_xchar:发送一个高优先级的字符,甚至是端口已经停止的收。

(7)stop_rx:停止接收字符。

(8)stop_rx:启用modem状态中断。

(9)break_ctl:控制中断信号的传输。

(10)startup:获取任何中断资源并初始化任何底层驱动状态。

(11)shutdown:禁用端口,禁用任何可能生效的中断条件,并释放任何中断资源。

(12)flush_buffer:刷新任何写缓冲区,重置任何DMA状态,并停止任何正在进行的DMA传输。

(13)set_termios:更改端口参数,包括字长、奇偶校验、停止位。

(14)pm:在指定端口上执行电源管理相关操作。

(15)type:返回一个指向描述指定端口的字符串常量的指针,或者返回NULL,在这种情况下,字符串’unknown’被替换。

(16)release_port:释放端口当前使用的内存和IO区域资源。

(17)request_port:请求端口所需的任何内存和IO区域资源。

(18)config_port:执行端口所需的任何自动配置步骤。

(19)verify_port

(20)ioctl

(21)poll_init

(22)poll_put_char

(23)poll_get_char

4. 向uart_driver添加/移除某个uart_port

(1)向uart_driver添加一个uart_port:

int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);

(2)从uart_driver中移除某个uart_port:

int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);

二、NXP对于UART驱动框架的实现

在设备树中找到uart3节点:

根据兼容性找对应的驱动文件:

grep -nR "fsl,imx21-uart" *

1. UART驱动的platform框架

(1)驱动模块的加载与卸载

可以看到,在驱动模块的加载函数中,调用 uart_register_driver 向内核注册 uart_driver,接着注册platform设备驱动。

在驱动模块的卸载中,首先注销platform设备驱动,然后从内核注销uart_driver。

(2)uart_driver驱动实现

(3)platform驱动框架实现

2. uart_port的添加/移除

在probe挂载函数中,经过一系列操作,最终调用 uart_add_one_port 函数添加了一个uart_port:

在remove卸载函数中,调用 uart_remove_one_port 函数移除对应的 uart_port:

这里添加和移除uart_port来自于imx定义的sport结构体,如下:

3. uart_pos函数指针实现

在probe函数中,对ops成员的初始化如下:

imx_pops的定义如下:

这其中的函数都是imx开头,是与底层SOC的UART外设相关操作函数,比如 imx_tx_empty 这个函数:

猜你喜欢

转载自blog.csdn.net/Mculover666/article/details/124143195