前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux串口分析open

Linux串口分析open

作者头像
DragonKingZhu
发布2022-05-08 15:39:38
10.1K0
发布2022-05-08 15:39:38
举报
代码语言:javascript
复制
/*
 * uart 打开分析
 *
 * 问题: 当应用程序调用open系统调用函数,那么是如何open串口的?
 * 
 * */

/*Samsung.c 函数的模块入口函数
 *它是一个公用的借口,不管2440/6410都会调用该函数
 * */
static int __init s3c24xx_serial_modinit(void)
{
	int ret;

	ret = uart_register_driver(&s3c24xx_uart_drv);
	if (ret < 0) {
		printk(KERN_ERR "failed to register UART driver\n");
		return -1;
	}

	return 0;
}

int uart_register_driver(struct uart_driver *drv)
{
	/*无关的代码忽略**/
	struct tty_driver *normal;

	normal = alloc_tty_driver(drv->nr);
	if (!normal)
		goto out_kfree;

	/*把normal 赋值给 uart驱动的tty_driver*/
	drv->tty_driver = normal;

	/*设置normal的operations*/
	tty_set_operations(normal, &uart_ops);

	/*注册tty驱动*/
	tty_register_driver(normal);


	
}

int tty_register_driver(struct tty_driver *driver)
{
	/*分配主设备号*/
	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);
	
	
	/*初始化字符设备,注册字符设备*/
	cdev_init(&driver->cdev, &tty_fops);
	driver->cdev.owner = driver->owner;
	error = cdev_add(&driver->cdev, dev, driver->num);

}


/*
 * 当应用程序调用open系统调用后,sys_open就会调用字符驱动的file_operations中的open函数
 * 也就是tty_fops中的open函数
 * */

static int tty_open(struct inode *inode, struct file *filp)
{


	/*首先判断打开的设备是否是:
	 *MAJOR = 5, MINOR = 0 
	 *MAJOR = 5, MINOR = 1 
	 *MAJOR = 4, MINOR = 0
	 * 其实打开的是2440_Serial0设备
	 *
	 * MAJOR = 204 MINOR = 64
	 * */

	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
		tty = get_current_tty();
		if (!tty) {
			tty_unlock();
			mutex_unlock(&tty_mutex);
			return -ENXIO;
		}
		driver = tty_driver_kref_get(tty->driver);
		index = tty->index;
		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
		/* noctty = 1; */
		/* FIXME: Should we take a driver reference ? */
		tty_kref_put(tty);
		goto got_driver;
	}
#ifdef CONFIG_VT
	if (device == MKDEV(TTY_MAJOR, 0)) {
		extern struct tty_driver *console_driver;
		driver = tty_driver_kref_get(console_driver);
		index = fg_console;
		noctty = 1;
		goto got_driver;
	}
#endif
	if (device == MKDEV(TTYAUX_MAJOR, 1)) {
		struct tty_driver *console_driver = console_device(&index);
		if (console_driver) {
			driver = tty_driver_kref_get(console_driver);
			if (driver) {
				/* Don't let /dev/console block */
				filp->f_flags |= O_NONBLOCK;
				noctty = 1;
				goto got_driver;
			}
		}
		tty_unlock();
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}


	/*当上面的判断不正确时,就执行这句*/
	driver = get_tty_driver(device, &index);
	if (!driver) {
		tty_unlock();
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}

	/*如果tty存在,不存在则重新初始化一个tty_struct的结构*/
	if (tty) {
		retval = tty_reopen(tty);
		if (retval)
			tty = ERR_PTR(retval);
	} else

		/*该函数主要是分配tty,然后初始化tty. 
		 * 接着将tty_struct存放到tty_driver的tty_struct[]数组中
		 * 根据tty_struct->index*/
		tty = tty_init_dev(driver, index, 0);



	/*它接着会调用tty->ops->open。 其实这个open函数就是
	 * uart_opsz中的uart_open函数
	 *
	 * */
	if (tty->ops->open)
		retval = tty->ops->open(tty, filp);
	
}

/* 通过device设备号来找到设备对应的tty_driver.并且将索引保存到index中
 *
 * **/
static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
	struct tty_driver *p;

	list_for_each_entry(p, &tty_drivers, tty_drivers) {
		dev_t base = MKDEV(p->major, p->minor_start);
		if (device < base || device >= base + p->num)
			continue;
		*index = device - base;
		return tty_driver_kref_get(p);
	}
	return NULL;
}

/*初始化tty中的重要的几条语句
 *
 * 1. 将tty和线路规程联系在一起.其中通过索引号N_TTY。将来
 *    tty_struct中的ldisc成员将为tty_ldisc_N_TTY
 * 2. 将tty_struct中的ops设置为tty_driver中的ops。而tty_driver中的ops
 *    是通过tty_set_operations(normal, &uart_ops);设置进去的。 也就是uart_ops
 * */
void initialize_tty_struct(struct tty_struct *tty,
		struct tty_driver *driver, int idx)
{
	tty_ldisc_init(tty);
	tty->driver = driver;
	tty->ops = driver->ops;
	tty->index = idx;
}
/*
 *  找到保存在tty_driver中的uart_state.
 *  其实uart_state就是在初始化保存进uart_driver中
 * 
 *  然后调用uart_start初始化serial port
 * 
 * */
static int uart_open(struct tty_struct *tty, struct file *filp)
{
	/*
	 * We take the semaphore inside uart_get to guarantee that we won't
	 * be re-entered while allocating the state structure, or while we
	 * request any IRQs that the driver may need.  This also has the nice
	 * side-effect that it delays the action of uart_hangup, so we can
	 * guarantee that state->port.tty will always contain something
	 * reasonable.
	 */
	state = uart_get(drv, line);
	if (IS_ERR(state)) {
		retval = PTR_ERR(state);
		goto fail;
	}
	port = &state->port;

	/*
	 * Once we set tty->driver_data here, we are guaranteed that
	 * uart_close() will decrement the driver module use count.
	 * Any failures from here onwards should not touch the count.
	 */
	tty->driver_data = state;
	state->uart_port->state = state;
	tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
	tty->alt_speed = 0;
	tty_port_tty_set(port, tty);


	/*
	 * Start up the serial port.
	 */
	retval = uart_startup(tty, state, 0);

}

static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
{
	/*其中最重要的一句就是调用ops->startup. 也就是初始化uart_port中的ops. 也就是s3c24xx_serial_ops*/
	uport->ops->startup(uport);	
}

/*
* 此函数就是去打开rx和tx的中断,然后使能中断
* 然后就是一直等待中断的到来。然后跳到中断处理程序去处理中断
*/

static int s3c24xx_serial_startup(struct uart_port *port)
{
	struct s3c24xx_uart_port *ourport = to_ourport(port);
	int ret;

	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
	    port->mapbase, port->membase);

	rx_enabled(port) = 1;

	ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);

	if (ret != 0) {
		printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
		return ret;
	}

	ourport->rx_claimed = 1;

	dbg("requesting tx irq...\n");

	tx_enabled(port) = 1;

	ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
			  s3c24xx_serial_portname(port), ourport);

	if (ret) {
		printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
		goto err;
	}

	ourport->tx_claimed = 1;

	dbg("s3c24xx_serial_startup ok\n");

	/* the port reset code should have done the correct
	 * register setup for the port controls */
	return ret;
}


总结:
open的主要作用是 在内核通过创建并初始化一个tty_struct来描述具体对应的一个硬件设备,比如这里就是用一个tty_struct来描述s3c24xx上的uart0的,然后找到uart_port

中ops的startup方法初始化uart的硬件。

具体的tty_struct初始化过程中最重要的几步如下

1.初始化tty-struct的ops,就是将tty_driver中的ops赋值给tty_struct

2.初始化tty线路规程操作集

3.初始化tty_struct中的uart_state,uart_state中包含uart_port信息,这一步通过步骤1中ops中的open方法来完成。

4.根据步骤3中找到的uart_state,找到里面的uart_port的ops中的startup方法来初始化uart硬件
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档