tty_read和tty_write

一、tty_read

对于tty_read这个函数,很多标志我也没有弄得太清楚,但是问题不大,以后有机会在看。我觉得重点是看数据怎么从cdc驱动到通过线路规划到tty,再从tty到用户空间。标志位等东西都是为这一个数据的流程服务的。

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
{
    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);

}

第4行,tty_ldisc_ref_wait获取到ld,这个是open中设置好的线路规划操作函数集。

第6行,利用函数集中的read夺取数据,这个read对应的是n_tty_read。

第9行,唤醒其他的等待获取ld的tty_ldisc_ref_wait

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
			 unsigned char __user *buf, size_t nr)
{
	while (nr) {

		if (!input_available_p(tty, 0)) {}

		if (ldata->icanon && !L_EXTPROC(tty)) {
			while (nr && ldata->read_cnt) {
				c = ldata->read_buf[ldata->read_tail];
				ldata->read_tail = ((ldata->read_tail+1) &
						  (N_TTY_BUF_SIZE-1));
				ldata->read_cnt--;
				if (tty_put_user(tty, c, b++)) 
					nr--;
			}
		} else {
			uncopied = copy_from_read_buf(tty, &b, &nr);
			uncopied += copy_from_read_buf(tty, &b, &nr);
		}
	}

	return retval;
}

1、input_available_p这个函数中调用了tty_flush_to_ldisc(tty)这个是利用队列将cdc中的数据存储到线路规划的缓存中,后面在具体分析。

2、接下来很长一段代码都是从线路规划的read_buf中读取数据,并送到用户空间,分为两种方式,一是一个一个的读取缓存中的数据,一个是整个的读取出来。

看到这里我们只能在代码中寻找哪里给read_buf赋值了,我们再看看cdc的中断,看看数据怎么从cdc驱动中送到线路规划中

acm_read_bulk_callback
    acm_process_read_urb(acm, urb)
        tty_insert_flip_string(&acm->port, urb->transfer_buffer,urb->actual_length)
            tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size)
        tty_flip_buffer_push(&acm->port);

这里是cdc的回掉,这里有一部分数据的流动过程。

int tty_insert_flip_string_fixed_flag(struct tty_port *port,
		const unsigned char *chars, char flag, size_t size)
{
	int copied = 0;
	do {
		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
		int space = tty_buffer_request_room(port, goal);
		struct tty_buffer *tb = port->buf.tail;
		/* If there is no space then tb may be NULL */
		if (unlikely(space == 0)) {
			break;
		}
		memcpy(tb->char_buf_ptr + tb->used, chars, space);
		memset(tb->flag_buf_ptr + tb->used, flag, space);
		tb->used += space;
		copied += space;
		chars += space;
		/* There is a small chance that we need to split the data over
		   several buffers. If this is the case we must loop */
	} while (unlikely(size > copied));
	return copied;
}

从这个函数是把cdc回掉中的数据存储到对应port的buf.tail的buf中。

void tty_flip_buffer_push(struct tty_port *port)
{
	struct tty_bufhead *buf = &port->buf;
	unsigned long flags;

	spin_lock_irqsave(&buf->lock, flags);
	if (buf->tail != NULL)
		buf->tail->commit = buf->tail->used;
	spin_unlock_irqrestore(&buf->lock, flags);

	if (port->low_latency)
		flush_to_ldisc(&buf->work);
	else
		schedule_work(&buf->work);
}

这里可以直接调用flush_to_ldisc处理数据后者使用队列来处理数据。buf->work的处理函数就是flush_to_ldisc。

static void flush_to_ldisc(struct work_struct *work)
{
	struct tty_port *port = container_of(work, struct tty_port, buf.work);
	struct tty_bufhead *buf = &port->buf;
	struct tty_struct *tty;
	unsigned long 	flags;
	struct tty_ldisc *disc;

	tty = port->itty;
	if (tty == NULL)
		return;

	disc = tty_ldisc_ref(tty);
	if (disc == NULL)	/*  !TTY_LDISC */
		return;

	spin_lock_irqsave(&buf->lock, flags);

	if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {
		struct tty_buffer *head;
		while ((head = buf->head) != NULL) {
			int count;
			char *char_buf;
			unsigned char *flag_buf;

			count = head->commit - head->read;
			if (!count) {
				if (head->next == NULL)
					break;
				buf->head = head->next;
				tty_buffer_free(port, head);
				continue;
			}
			if (!tty->receive_room)
				break;
			if (count > tty->receive_room)
				count = tty->receive_room;
			char_buf = head->char_buf_ptr + head->read;
			flag_buf = head->flag_buf_ptr + head->read;
			head->read += count;
			spin_unlock_irqrestore(&buf->lock, flags);
			disc->ops->receive_buf(tty, char_buf,
							flag_buf, count);
			spin_lock_irqsave(&buf->lock, flags);
			/* Ldisc or user is trying to flush the buffers.
			   We may have a deferred request to flush the
			   input buffer, if so pull the chain under the lock
			   and empty the queue */
			if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) {
				__tty_buffer_flush(port);
				clear_bit(TTYP_FLUSHPENDING, &port->iflags);
				wake_up(&tty->read_wait);
				break;
			}
		}
		clear_bit(TTYP_FLUSHING, &port->iflags);
	}

	spin_unlock_irqrestore(&buf->lock, flags);

	tty_ldisc_deref(disc);
}

这个就是调用 disc->ops->receive_buf来处理port->buf中的数据

static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
			      char *fp, int count)
{
	struct n_tty_data *ldata = tty->disc_data;
	const unsigned char *p;
	char *f, flags = TTY_NORMAL;
	int	i;
	char	buf[64];
	unsigned long cpuflags;

	if (ldata->real_raw) {
		raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
		i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
			N_TTY_BUF_SIZE - ldata->read_head);
		i = min(count, i);
		memcpy(ldata->read_buf + ldata->read_head, cp, i);
		ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
		ldata->read_cnt += i;
		cp += i;
		count -= i;

		i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
			N_TTY_BUF_SIZE - ldata->read_head);
		i = min(count, i);
		memcpy(ldata->read_buf + ldata->read_head, cp, i);
		ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
		ldata->read_cnt += i;
		raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
	} else {
		for (i = count, p = cp, f = fp; i; i--, p++) {
			if (f)
				flags = *f++;
			switch (flags) {
			case TTY_NORMAL:
				n_tty_receive_char(tty, *p);
				break;
			case TTY_BREAK:
				n_tty_receive_break(tty);
				break;
			case TTY_PARITY:
			case TTY_FRAME:
				n_tty_receive_parity_error(tty, *p);
				break;
			case TTY_OVERRUN:
				n_tty_receive_overrun(tty);
				break;
			default:
				printk(KERN_ERR "%s: unknown flag %d\n",
				       tty_name(tty, buf), flags);
				break;
			}
		}
		if (tty->ops->flush_chars)
			tty->ops->flush_chars(tty);
	}

	n_tty_set_room(tty);

	if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) ||
		L_EXTPROC(tty)) {
		kill_fasync(&tty->fasync, SIGIO, POLL_IN);
		if (waitqueue_active(&tty->read_wait))
			wake_up_interruptible(&tty->read_wait);
	}

	/*
	 * Check the remaining room for the input canonicalization
	 * mode.  We don't want to throttle the driver if we're in
	 * canonical mode and don't have a newline yet!
	 */
	while (1) {
		tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
		if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
			break;
		if (!tty_throttle_safe(tty))
			break;
	}
	__tty_set_flow_change(tty, 0);
}

我的记得open的时候说这个数据是不处理的,所以走上面直接复制的分支。

所以我们可以看到数据的整个流程是这样的,cdc的callback把数据存储到port的buf中,再到线路规划的read_buf,在送到用户空间,跟这个线索,其他的部分可以碰到具体问题时在分析。

 

二、tty_write

tty_write
    do_tty_write(ld->ops->write, tty, file, buf, count);
        write(tty, file, tty->write_buf, size)//这个write就是上面的ld->ops->write, tty
           n_tty_write
               tty->ops->write(tty, b, nr);
                    acm_tty_write

write比较简单基本就这样

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
tty->flags中常见的标志有: - TTY_NORMAL: 正常模式 - TTY_RAW: 原始模式 - TTY_ECHO: 回显模式 - TTY_CEDIT: 可编辑模式 - TTY_ICANON: 规范模式 - TTY_CCBREAK: 字符模式 - TTY_XANY: 任意字符模式 - TTY_EXTPROC: 扩展处理模式 - TTY_IO_ERROR: IO错误 - TTY_DO_WRITE_WAKEUP: 允许写唤醒 - TTY_DO_READ_WAKEUP: 允许读唤醒 - TTY_DO_FLUSH: 允许刷新 - TTY_DO_STOP: 允许停止 - TTY_DO_START: 允许启动 - TTY_DO_HUP: 允许挂机 - TTY_DO_WAIT_FOR_LEADER: 等待主进程 - TTY_DO_SAK: 允许SAK信号 - TTY_RESET_TERMIOS: 重置终端参数 - TTY_LSIZE: 等待行大小 - TTY_LDISC: 等待线路规程 - TTY_PEND_UNBLOCK: 等待解锁 - TTY_OTHER_CLOSED: 其他进程关闭 - TTY_EXCLUSIVE: 独占访问 - TTY_DO_WRITE_WAKEUP: 允许写唤醒 - TTY_DRIVER_INITIALIZED: 驱动程序已初始化 - TTY_DRIVER_SUSPENDED: 驱动程序已暂停 - TTY_DRIVER_CLOSED: 驱动程序已关闭 - TTY_DRIVER_RESETTING: 驱动程序正在重置 - TTY_DRIVER_DYNAMIC_DEV: 驱动程序为动态设备 - TTY_DRIVER_REAL_RAW: 驱动程序支持真正的原始模式 - TTY_DRIVER_UNNUMBERED_NODE: 驱动程序的节点未编号 - TTY_DRIVER_DEVPTS_MEM: 驱动程序的节点在/dev/pts内存文件系统中 - TTY_DRIVER_DEVPTS_NEWinstance: 驱动程序的节点是/dev/pts新实例 - TTY_DRIVER_SUPPRESS_UNMAPPED: 驱动程序会抑制未映射的字符 - TTY_DRIVER_RESET_TERMIOS: 驱动程序会重置终端参数 - TTY_DRIVER_NO_DEVFS: 驱动程序没有/dev文件系统 - TTY_DRIVER_NO_SYMLINKS: 驱动程序没有符号链接 - TTY_DRIVER_DEVPTS_MULTIPLE: 驱动程序的节点是/dev/pts多个实例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值