tty_read和tty_write

2023-05-16

一、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比较简单基本就这样

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

tty_read和tty_write 的相关文章

随机推荐

  • WARNING: [Vivado 12-13340] WARNING: [Vivado 12-13277]

    仿真的时候遇问题 xff1a WARNING Vivado 12 13340 Unable to auto find GCC executables from simulator install path path not set WARN
  • 二.因子图优化学习---董靖博士在深蓝学院的公开课学习(2)

    专栏系列文章如下 xff1a https blog csdn net weixin 36773706 article details 122440411 https blog csdn net weixin 36773706 article
  • 四.因子图优化学习---对因子图优化的粗浅理解

    专栏系列文章如下 xff1a 一 因子图优化学习 董靖博士在深蓝学院的公开课学习 xff08 1 xff09 goldqiu的博客 CSDN博客 二 因子图优化学习 董靖博士在深蓝学院的公开课学习 xff08 2 xff09 goldqiu
  • 多传感器融合SLAM、导航研究和学习专栏汇总

    从2021年9月份开始学习多传感器融合SLAM xff0c 期间也发了不少博客记录学习过程 xff0c 自己对SLAM的认识也逐渐加深 xff0c 以前一些博客中会有一些错误的地方还未及时去修正 xff0c 敬请谅解 由于课题组需要和自身发
  • 启航篇——四旋翼飞行器之入坑两年心路历程和毕设总结

    笔者今年大四毕业 xff0c 由于之前参加比赛及准备考研 xff0c 没有时间总结这两年来做四旋翼飞行器的心得体会 现在借毕业设计这个契机 xff0c 想把这件事做了 xff0c 算是两年的收尾工作 xff0c 也是个新的开始 先从介绍这两
  • Gazebo学习笔记(一)

    搭建一个移动车 ctrl 43 m 进入到gazebo编辑界面 ctrl 43 m 进入到gazebo编辑界面 1 搭建一个车底盘 xff0c 选择simple shapes 的box xff0c 点击后在编辑框中释放 2 利用resize
  • 1分钟教会你二进制撩妹(汉)读心术

    近些年来 xff0c 小魔发现 xff0c 对于年轻的男女而言 xff0c 一些传统的节日似乎都变成了情人节或者脱单节 xff0c 就连 光棍节 xff0c 实际上很多人都是抱着节前或者是当天脱单而过的 双11 光棍节 即将来临 xff0c
  • 树莓派光敏传感器控制LED小灯的开关

    今天来做一个关于光敏传感器的使用demo xff0c 如图 xff0c 我采用的是普通用于开发的光敏传感器 传感器的基板上有两个LED xff0c 上一个是表示DO的高低电平 xff0c 下一个表示是否通电 当挡住传感头之后 xff0c D
  • Opencv+YOLO3目标检测/C++

    1 引言 YOLO3能够快速识别图片和视频中的80种物体 xff0c 而且实时性强 xff0c 准确度接近SSD Opencv是目前最流行的开源图像处理库 xff0c 使用Opencv能够非常方便的对图像进行处理 Opencv4 0已经包含
  • Linux安装Docker

    安装前菜 如何卸载Docker 1 查询docker安装包 yum list installed grep docker 2 删除安装包 yum remove docker 3 删除镜像 容器等 rm rf var lib docker 下
  • 北邮初试+复试经验分享

    初试情况 xff1a 政治 53 英语 70 数学 122 计算机综合 xff08 803 xff09 110 一些相关的基本情况 xff1a 本科专业数学 本科期间学过数据结构 通过了英语四六级 本科成绩还算可以 初试材料选择 xff08
  • apt命令概述

    目录 Linux软件仓库 软件源配置 apt命令 常见的命令如下 xff1a 常见的用法 示例 xff1a 使用apt安装redis软件 Linux软件仓库 Ubuntu采用集中式的软件仓库机制 xff0c 将各式各样 的软件包分门别类地存
  • 索引覆盖与回表

    懂的越多 xff0c 不懂的越多 上回书说到 xff1a 什么情况下 xff0c 索引下推没办法提高sql查询效率 xff1f 表info主键id名称name值value别名realname 对于info表 xff0c 我们现在有 xff0
  • Ubuntu18.04安装实时内核Preempt-RT

    文章目录 1 安装环境2 安装依赖包3 查看内核版本4 下载新的内核和对应的Preempt RT补丁5 解压缩6 打补丁7 配置内核8 编译内核9 校验结果10 重启11 测试12 调整分辨率 1 安装环境 Ubuntu18 04内核版本
  • CAS 5.2.X 使用cas-overlay-template 搭建cas-server

    1 下载 cas overlay template 地址 xff1a https github com apereo cas overlay template 选择需要的版本 xff1a cas 6 x开始使用gradle构建 xff0c
  • Jupyter 更改主题解决 jt 不存在问题

    pip安装之后 xff0c 并不是像传统的那样安装到了envs中的script文件中 而是在C盘的appdata C Users Administrator AppData Roaming Python Python36 Scripts 中
  • 树莓派4 桌面图标显示 No wireless interface found

    树莓派4 桌面图标显示 No wireless interface found 解决方式 先使用有线链接网络 安装wpasupplicant wpagui libengine pkcs11 openssl sudo apt install
  • mysql _外键、实体关系与ER图

    外键约束 概念 A实体的某个字段指向 B实体的主键 则称A实体的那个字段为该实体的外键 一个表里可以有多个外键 span class hljs operator span class hljs keyword drop span span
  • hadoop系列之Configuration类解析

    前言 Configuration是hadoop中五大组件的公用类 xff0c 所以放在了core下 xff0c org apache hadoop conf Configruration 这个类是作业的配置信息类 xff0c 任何作用的配置
  • tty_read和tty_write

    一 tty read 对于tty read这个函数 xff0c 很多标志我也没有弄得太清楚 xff0c 但是问题不大 xff0c 以后有机会在看 我觉得重点是看数据怎么从cdc驱动到通过线路规划到tty xff0c 再从tty到用户空间 标