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

Posted Mculover666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。

(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);

3. ops函数指针实现

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。

二、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 这个函数:

以上是关于i.MX6ULL驱动开发 | 15 - Linux UART 驱动框架的主要内容,如果未能解决你的问题,请参考以下文章

i.MX6ULL驱动开发 | 09 -基于Linux自带的LED驱动点亮LED

i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED

i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架

i.MX6ULL驱动开发 | 26 - Linux内核的RTC驱动