Linux UART子系统【1】- uart_register_driver驱动

系列文章目录

Linux UART子系统【1】- uart_register_driver驱动
Linux UART子系统【2】- imx6ul uart 入口函数 uart_add_one_port
Linux UART子系统【3】- Linux串口驱动(3) - open详解
Linux UART子系统【4】- Linux串口驱动(2) - 线路规程
Linux UART子系统【5】- UART的RTS和CTS
Linux UART子系统【6】- write详解
Linux UART子系统【7】- read详解

UART BUG-系列文章目录

Linux UART子系统-BUG【1】- 串口通信失败



前言


一、uart_driver 注册与注销

I2CSPI 一样,Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱
动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也
已经由 NXP官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信
息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成
/dev/ttymxcX(X=0….n)文件。
uart_driver 结构体表示 UART 驱动,uart_driver 定义在 include/linux/serial_core.h 文件中,内容如下:

1.1 调用流程

基于Linux的tty架构及UART驱动详解相关阅读2.3.1.1注册uart_driver

--->static struct uart_driver imx_reg
--->int uart_register_driver(struct uart_driver *drv)
--->struct tty_driver *normal = alloc_tty_driver(drv->nr)/*分配一个可注册的tty_driver,并告知设备数据量*/
  --->struct tty_driver *driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
--->将用户struct uart_driver信息赋值给struct tty_driver *normal
--->tty_port_init(port);/*初始化8个uart通道,指明port的ops*/
--->tty_register_driver(normal);/*注册struct tty_driver,最终要注册的实体*/
  --->register_chrdev_region();/*将char_device_struct变量注册到内核*/


1.2 相关结构体

在这里插入图片描述

每个串口驱动都需要定义一个 uart_driver,加载驱动的时候通过 uart_register_driver 函数,向系统注册这个 uart_driver,此函数原型如下:

#define DRIVER_NAME "IMX-uart"
static struct uart_driver imx_reg = {
	.owner          = THIS_MODULE,
	.driver_name    = DRIVER_NAME,				/*/proc/tty/driver/IMX-uart*/
	.dev_name       = DEV_NAME,					/*/dev/tty/ttymxcX(0~10)*/
	.major          = SERIAL_IMX_MAJOR,
	.minor          = MINOR_START,
	.nr             = ARRAY_SIZE(imx_ports),
	.cons           = IMX_CONSOLE,
};
/*
 * This is the state information which is persistent across opens.
 */
struct uart_state {
	struct tty_port		port;

	enum uart_pm_state	pm_state;
	struct circ_buf		xmit;

	struct uart_port	*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;
};
struct tty_driver {
	int	magic;		/* magic number for this structure */
	struct kref kref;	/* Reference management */
	struct cdev *cdevs;
	struct module	*owner;
	const char	*driver_name;
	const char	*name;
	int	name_base;	/* offset of printed name */
	int	major;		/* major device number */
	int	minor_start;	/* start of minor device number */
	unsigned int	num;	/* number of devices allocated */
	short	type;		/* type of tty driver */
	short	subtype;	/* subtype of tty driver */
	struct ktermios init_termios; /* Initial termios */
	unsigned long	flags;		/* tty driver flags */
	struct proc_dir_entry *proc_entry; /* /proc fs entry */
	struct tty_driver *other; /* only used for the PTY driver */

	/*
	 * Pointer to the tty data structures
	 */
	struct tty_struct **ttys;
	struct tty_port **ports;
	struct ktermios **termios;
	void *driver_state;

	/*
	 * Driver methods
	 */

	const struct tty_operations *ops;
	struct list_head tty_drivers;
};
/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM		0x0001
#define TTY_DRIVER_TYPE_CONSOLE		0x0002
#define TTY_DRIVER_TYPE_SERIAL		0x0003
#define TTY_DRIVER_TYPE_PTY		0x0004
#define TTY_DRIVER_TYPE_SCC		0x0005	/* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS		0x0006



static struct char_device_struct {
	struct char_device_struct *next;
	unsigned int major;
	unsigned int baseminor;
	int minorct;
	char name[64];
	struct cdev *cdev;		/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];	// 255


1.3 uart_register_driver

UART驱动主要涉及的驱动文件是imx.c、serial_core.c两个文件。首先我们找到驱动的入口函数module_init(imx_serial_init),在函数imx_serial_init中调用uart_register_driver向内核注册了一个驱动.

/*函数参数和返回值含义如下:drv:要注册的 uart_driver。
返回值:0,成功;负值,失败。*/
drivers/tty/serial/serial_core.c
(
	int uart_register_driver(struct uart_driver *drv)
	{
		drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
		struct tty_driver *normal = alloc_tty_driver(drv->nr);
		drv->tty_driver = normal;
	
		normal->driver_name	= drv->driver_name;
		normal->name		= drv->dev_name;
		normal->major		= drv->major;
		normal->minor_start	= drv->minor;
		normal->type		= TTY_DRIVER_TYPE_SERIAL;
		normal->subtype		= SERIAL_TYPE_NORMAL;
		normal->init_termios	= tty_std_termios;
		normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
		normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
		normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
		normal->driver_state    = drv;
		tty_set_operations(normal, &uart_ops);
			/*
		 * Initialise the UART state(s).
		 */
		for (i = 0; i < drv->nr; i++) {
			struct uart_state *state = drv->state + i;
			struct tty_port *port = &state->port;
	
			tty_port_init(port);
			port->ops = &uart_port_ops;
		}
		retval = tty_register_driver(normal);/*list_add(&driver->tty_drivers, &tty_drivers);*/
		if (retval >= 0)
			return retval;
	    for (i = 0; i < drv->nr; i++)
	   		tty_port_destroy(&drv->state[i].port);
		put_tty_driver(normal);
	out_kfree:
		kfree(drv->state);
	out:
		return -ENOMEM;
	}
)

drivers/tty/tty_io.c
(
	struct tty_driver *normal = alloc_tty_driver(drv->nr);
	tty_alloc_driver(lines, 0);
	struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
			unsigned long flags)
	{
		struct tty_driver *driver;
		unsigned int cdevs = 1;
		int err;
	
		if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
			return ERR_PTR(-EINVAL);
	
		driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
		if (!driver)
			return ERR_PTR(-ENOMEM);
	
		kref_init(&driver->kref);
		driver->magic = TTY_DRIVER_MAGIC;
		driver->num = lines;
		driver->owner = owner;
		driver->flags = flags;
	
		if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
			driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
					GFP_KERNEL);
			driver->termios = kcalloc(lines, sizeof(*driver->termios),
					GFP_KERNEL);
			if (!driver->ttys || !driver->termios) {
				err = -ENOMEM;
				goto err_free_all;
			}
		}
	
		if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
			driver->ports = kcalloc(lines, sizeof(*driver->ports),
					GFP_KERNEL);
			cdevs = lines;
		}
	
		driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
		return driver;
	err_free_all:
		kfree(driver->ports);
		kfree(driver->ttys);
		kfree(driver->termios);
		kfree(driver);
		return ERR_PTR(err);
	}
)

先是调用tty_set_operationsuart_ops这一个tty设备的操作函数集设置到了tty驱动中,同时调用tty_register_driver函数向内核注册了tty驱动,其中uart_ops的数据类型及内容如下:

static const struct tty_operations uart_ops = {
	.open		= uart_open,
	.close		= uart_close,
	.write		= uart_write,
	.put_char	= uart_put_char,
	.flush_chars	= uart_flush_chars,
	.write_room	= uart_write_room,
	.chars_in_buffer= uart_chars_in_buffer,
	.flush_buffer	= uart_flush_buffer,
	.ioctl		= uart_ioctl,
	.throttle	= uart_throttle,
	.unthrottle	= uart_unthrottle,
	.send_xchar	= uart_send_xchar,
	.set_termios	= uart_set_termios,
	.set_ldisc	= uart_set_ldisc,
	.stop		= uart_stop,
	.start		= uart_start,
	.hangup		= uart_hangup,
	.break_ctl	= uart_break_ctl,
	.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
	.proc_fops	= &uart_proc_fops,
#endif
	.tiocmget	= uart_tiocmget,
	.tiocmset	= uart_tiocmset,
	.get_icount	= uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
	.poll_init	= uart_poll_init,
	.poll_get_char	= uart_poll_get_char,
	.poll_put_char	= uart_poll_put_char,
#endif
};

1.3 tty_port_init

tty.h
(
	struct tty_buffer {
		union {
			struct tty_buffer *next;
			struct llist_node free;
		};
		int used;
		int size;
		int commit;
		int read;
		int flags;
		/* Data points here */
		unsigned long data[0];
	};
	struct tty_bufhead {
		struct tty_buffer *head;	/* Queue head */
		struct work_struct work;
		struct mutex	   lock;
		atomic_t	   priority;
		struct tty_buffer sentinel;
		struct llist_head free;		/* Free queue head */
		atomic_t	   mem_used;    /* In-use buffers excluding free list */
		int		   mem_limit;
		struct tty_buffer *tail;	/* Active buffer */
	};
	struct tty_port {
		struct tty_bufhead	buf;		/* Locked internally */
		struct tty_struct	*tty;		/* Back pointer */
		struct tty_struct	*itty;		/* internal back ptr */
		const struct tty_port_operations *ops;	/* Port operations */
		spinlock_t		lock;		/* Lock protecting tty field */
		int			blocked_open;	/* Waiting to open */
		int			count;		/* Usage count */
		wait_queue_head_t	open_wait;	/* Open waiters */
		wait_queue_head_t	close_wait;	/* Close waiters */
		wait_queue_head_t	delta_msr_wait;	/* Modem status change */
		unsigned long		flags;		/* TTY flags ASY_*/
		unsigned char		console:1,	/* port is a console */
					low_latency:1;	/* optional: tune for latency */
		struct mutex		mutex;		/* Locking */
		struct mutex		buf_mutex;	/* Buffer alloc lock */
		unsigned char		*xmit_buf;	/* Optional buffer */
		unsigned int		close_delay;	/* Close port delay */
		unsigned int		closing_wait;	/* Delay for output */
		int			drain_delay;	/* Set to zero if no pure time
							   based drain is needed else
							   set to size of fifo */
		struct kref		kref;		/* Ref counter */
	};
)

tty_port.c
(
	void tty_port_init(struct tty_port *port)
	{
		memset(port, 0, sizeof(*port));
		tty_buffer_init(port);
		init_waitqueue_head(&port->open_wait);
		init_waitqueue_head(&port->close_wait);
		init_waitqueue_head(&port->delta_msr_wait);
		mutex_init(&port->mutex);
		mutex_init(&port->buf_mutex);
		spin_lock_init(&port->lock);
		port->close_delay = (50 * HZ) / 100;
		port->closing_wait = (3000 * HZ) / 100;
		kref_init(&port->kref);
	}
)
tty_buffer.c
(
	/*tty_buffer_init - prepare a tty buffer structure*/
	void tty_buffer_init(struct tty_port *port)
	{
		struct tty_bufhead *buf = &port->buf;
	
		mutex_init(&buf->lock);
		tty_buffer_reset(&buf->sentinel, 0);
		buf->head = &buf->sentinel;
		buf->tail = &buf->sentinel;
		init_llist_head(&buf->free);
		atomic_set(&buf->mem_used, 0);
		atomic_set(&buf->priority, 0);
		INIT_WORK(&buf->work, flush_to_ldisc);
		buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
	}
	static void tty_buffer_reset(struct tty_buffer *p, size_t size)
	{
		p->used = 0;
		p->size = size;
		p->next = NULL;
		p->commit = 0;
		p->read = 0;
		p->flags = 0;
	}
)

1.4 tty_register_driver

tty_io.c
(
	/*
 * Called by a tty driver to register itself.
 */
	int tty_register_driver(struct tty_driver *driver)
	{
		int error;
		int i;
		dev_t dev;
		struct device *d;
	
		if (!driver->major) {
			error = alloc_chrdev_region(&dev, driver->minor_start,
							driver->num, driver->name);
			if (!error) {
				driver->major = MAJOR(dev);
				driver->minor_start = MINOR(dev);
			}
		} else {
			dev = MKDEV(driver->major, driver->minor_start);
			error = register_chrdev_region(dev, driver->num, driver->name);/*Linux 设备模型【4】- register_chrdev_region解析*/
		}
		if (error < 0)
			goto err;
		/*normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;*/
		if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
			error = tty_cdev_add(driver, dev, 0, driver->num);
			if (error)
				goto err_unreg_char;
		}
	
		mutex_lock(&tty_mutex);
		list_add(&driver->tty_drivers, &tty_drivers);
		mutex_unlock(&tty_mutex);
	
		if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
			for (i = 0; i < driver->num; i++) {
				d = tty_register_device(driver, i, NULL);
				if (IS_ERR(d)) {
					error = PTR_ERR(d);
					goto err_unreg_devs;
				}
			}
		}
		proc_tty_register_driver(driver);
		driver->flags |= TTY_DRIVER_INSTALLED;
		return 0;
	
	err_unreg_devs:
		for (i--; i >= 0; i--)
			tty_unregister_device(driver, i);
	
		mutex_lock(&tty_mutex);
		list_del(&driver->tty_drivers);
		mutex_unlock(&tty_mutex);
	
	err_unreg_char:
		unregister_chrdev_region(dev, driver->num);
	err:
		return error;
	}
)
proc_tty.c
(
	/*
	 * This function is called by tty_register_driver() to handle
	 * registering the driver's /proc handler into /proc/tty/driver/<foo>
	 */
	void proc_tty_register_driver(struct tty_driver *driver)
	{
		struct proc_dir_entry *ent;
			
		if (!driver->driver_name || driver->proc_entry ||
		    !driver->ops->proc_fops)
			return;
		/*proc_create_data函数在proc下创建节点*/
		/*driver->driver_name =DRIVER_NAME "IMX-uart"*//*/proc/tty/driver/IMX-uart*/
		ent = proc_create_data(driver->driver_name, 0, proc_tty_driver,
				       driver->ops->proc_fops, driver);
		driver->proc_entry = ent;
	}
)

1.5 tty_port_destroy

drivers/tty/tty_post.c
(
	 /**
	 * tty_port_destroy -- destroy inited port
	 * @port: tty port to be doestroyed
	 *
	 * When a port was initialized using tty_port_init, one has to destroy the
	 * port by this function. Either indirectly by using tty_port refcounting
	 * (tty_port_put) or directly if refcounting is not used.
	 */
	void tty_port_destroy(struct tty_port *port)
	{
		cancel_work_sync(&port->buf.work);
		tty_buffer_free_all(port);
	}
)
drivers/tty/tty_buffer.c
{
	/**
	 *	tty_buffer_free_all		-	free buffers used by a tty
	 *	@tty: tty to free from
	 *
	 *	Remove all the buffers pending on a tty whether queued with data
	 *	or in the free ring. Must be called when the tty is no longer in use
	 */
	
	void tty_buffer_free_all(struct tty_port *port)
	{
		struct tty_bufhead *buf = &port->buf;
		struct tty_buffer *p, *next;
		struct llist_node *llist;
	
		while ((p = buf->head) != NULL) {
			buf->head = p->next;
			if (p->size > 0)
				kfree(p);
		}
		llist = llist_del_all(&buf->free);
		llist_for_each_entry_safe(p, next, llist, free)
			kfree(p);
	
		tty_buffer_reset(&buf->sentinel, 0);
		buf->head = &buf->sentinel;
		buf->tail = &buf->sentinel;
	
		atomic_set(&buf->mem_used, 0);
	}
}

1.6 uart_unregister_driver

drivers/tty/serial/serial_core.c
(
	/**
	 *	uart_unregister_driver - remove a driver from the uart core layer
	 *	@drv: low level driver structure
	 *
	 *	Remove all references to a driver from the core driver.  The low
	 *	level driver must have removed all its ports via the
	 *	uart_remove_one_port() if it registered them with uart_add_one_port().
	 *	(ie, drv->port == NULL)
	 */
	void uart_unregister_driver(struct uart_driver *drv)
	{
		struct tty_driver *p = drv->tty_driver;
		unsigned int i;
	
		tty_unregister_driver(p);
		put_tty_driver(p);
		for (i = 0; i < drv->nr; i++)
			tty_port_destroy(&drv->state[i].port);
		kfree(drv->state);
		drv->state = NULL;
		drv->tty_driver = NULL;
	}
)
drivers/tty/tty_io.c
(
	/*
	 * Called by a tty driver to unregister itself.
	 */
	int tty_unregister_driver(struct tty_driver *driver)
	{
	#if 0
		/* FIXME */
		if (driver->refcount)
			return -EBUSY;
	#endif
		unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
					driver->num);
		(
		/*static struct char_device_struct *__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)*/
			kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
		)
		mutex_lock(&tty_mutex);
		list_del(&driver->tty_drivers);
		mutex_unlock(&tty_mutex);
		return 0;
	}
)

总结

  • 0
    点赞
  • 3
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大城市的小蜗牛

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值