Linux tty驱动学习 - UART驱动的read操作流程

这篇博客深入探讨了Linux tty驱动中的UART读操作流程。从用户空间的tty设备读操作开始,通过系统调用进入tty核心层的tty_read()函数,接着获取tty_struct和线路规程描述符。文章详细阐述了tty_insert_filp_char()函数的工作原理,包括如何管理tty_buffer和tty_bufhead,以及如何在没有空闲buffer时进行动态分配。最后,数据被存入tty_buffer并由tty_flip_buffer_push()提交到read_buf,完成读取过程。
摘要由CSDN通过智能技术生成

在用户空间对tty设备进行读操作,经过系统调用进入到tty核心层执行的第一个函数是tty_read()。在tty_read()函数中,从文件描述符file的私有数据结构中获得tty_struct,然后再从tty_struct中获取线路规程描述符。取得线路规程描述符后,直接调用线路规程的read()函数。

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
			loff_t *ppos)
{
	int i;
	struct tty_struct *tty;
	struct inode *inode;
	struct tty_ldisc *ld;

	tty = (struct tty_struct *)file->private_data;
	inode = file->f_path.dentry->d_inode;
	if (tty_paranoia_check(tty, inode, "tty_read"))
		return -EIO;
	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
		return -EIO;

	/* We want to wait for the line discipline to sort out in this
	   situation */
	ld = tty_ldisc_ref_wait(tty);
	if (ld->ops->read)
		i = (ld->ops->read)(tty, file, buf, count);
	else
		i = -EIO;
	tty_ldisc_deref(ld);
	if (i > 0)
		inode->i_atime = current_fs_time(inode->i_sb);
	return i;
}

线路规程N_TTY的读操作函数为n_tty_read(),在该函数中把数据拷贝到用户空间分两种情况处理,一种是标准模式,另一种是非标准模式。在标准模式下,如果没有设置O_NONBLOCK,读操作只有在遇到文件结束符或者各行的字符都已编辑完毕后才返回。在非标准始模式下,由VMIN和VTIME两个设定来控制读操作的行为。第一种情况:VMIN和VTIME都为0,不管有没有读到数据,read()会立即返回;第二种情况:VMIN 为0,VTIME大于0,如果有读到数据,read()立即返回,如果没有读到数据,则等待VTIME时间后返回;第三种情况:VMIN大于0,VTIME等于0,read()一直阻塞直到读到VMIN个数据后返回;第四种情况:VMIN和VTIME都大于0,read()保持阻塞直到读到第一个字符,读到第一个字符后开始计时,此后如果时间到了VTIME或者时间没到但读到了VMIN个字符则返回,若在时间未到之前又读到一个字符(此时字符总数不够)则重新计时。回到n_tty_read()中,首先判断是否有数据可读,如果没有则调用schedule_timeout()把当前进程挂起,直到有数据可读或者超时后被唤醒继续执行。接下来是数据的读取过程,如果是标准模式,直接从tty->read_buf中读取字符,然后调用tty_put_user()把字符拷贝到用户空间。在非标准模式下,利用copy_from_read_buf直接把字符拷贝到用户空间。copy_from_read_buf()执行了两次,那是因为read_buf是个环形缓存区,第一次读是从read_tail到buf_end这部分数据,第二次读是从buf_end(因为是环形,所以也是buf_start)到read_head这部分数据。在copy_from_read_buf()里,用copy_to_user()直接把tty->read_buf里面的数据拷贝到用户空间。

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
			 unsigned char __user *buf, size_t nr)
{
<span style="white-space:pre">	</span>……
	while (nr) {
		/* First test for status change. */
		if (packet && tty->link->ctrl_status) {
			unsigned char cs;
			if (b != buf)
				break;
			spin_lock_irqsave(&tty->link->ctrl_lock, flags);
			cs = tty->link->ctrl_status;
			tty->link->ctrl_status = 0;
			spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
			if (tty_put_user(tty, cs, b++)) {
				retval = -EFAULT;
				b--;
				break;
			}
			nr--;
			break;
		}
		/* This statement must be first before checking for input
		   so that any interrupt will set the state back to
		   TASK_RUNNING. */
		set_current_state(TASK_INTERRUPTIBLE);

		if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
		    ((minimum - (b - buf)) >= 1))
			tty->minimum_to_wake = (minimum - (b - buf));

		if (!input_available_p(tty, 0)) {
			if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
				retval = -EIO;
				break;
			}
			if (tty_hung_up_p(file))
				break;
			if (!timeout)
				break;
			if (file->f_flags & O_NONBLOCK) {
				retval = -EAGAIN;
				break;
			}
			if (signal_pending(current)) {
				retval = -ERESTARTSYS;
				break;
			}
			/* FIXME: does n_tty_set_room need locking ? */
			n_tty_set_room(tty);
			timeout = schedule_timeout(timeo
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值