UART驱动情景分析-read

一、源码框架回顾

在这里插入图片描述
shell读数据,一开始的时候没有就休眠。数据从串口发送到驱动,驱动接收到中断,驱动读取串口数据,这个数据会传给行规程。
行规程获取到数据后,会回显。按下删除就删除一个字符,按下回车,就返回一个命令。如果找不到就提示找不到命令。

drivers/tty/tty_ldisc.c

int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)		//行规程注册函数

搜索它的使用情况就可以看到有多少的行规程

   #   line  filename / context / line
   1    701  include/linux/tty.h <<GLOBAL>>
             extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
   2     77  drivers/tty/tty_ldisc.c <<GLOBAL>>
             EXPORT_SYMBOL(tty_register_ldisc);
   3    701  include/linux/tty.h <<GLOBAL>>
             extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
   4    837  drivers/bluetooth/hci_ldisc.c <<hci_uart_init>>
             err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
   5    298  input/serio/serport.c <<serport_init>>
             retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
   6    763  isdn/gigaset/ser-gigaset.c <<ser_gigaset_init>>
             rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
   7    858  misc/ti-st/st_core.c <<st_core_init>>
             err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops);
   8    405  net/caif/caif_serial.c <<register_ldisc>>
             result = tty_register_ldisc(N_CAIF, &caif_ldisc);
//...
  16    137  pps/clients/pps-ldisc.c <<pps_tty_init>>
             err = tty_register_ldisc(N_PPS, &pps_ldisc_ops);
  17    189  staging/speakup/spk_ttyio.c <<spk_ttyio_register_ldisc>>
             if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
//...
  26    473  nfc/nci/uart.c <<nci_uart_init>>
             return tty_register_ldisc(N_NCI, &nci_uart_ldisc);
  27    503  soc/omap/ams-delta.c <<ams_delta_cx20442_init>>
             ret = tty_register_ldisc(N_V253, &cx81801_ops);

有各种行规程,声卡的、网络的、蓝牙的。找一个最通用的驱动来分析drivers\tty\n_tty.c

二、读之前通过open确定了行规层

2.1 行规程注册

在tty设备中最常用的就是它,n_tty
文件:drivers\tty\n_tty.c

void __init n_tty_init(void)
{
	tty_register_ldisc(N_TTY, &n_tty_ops);		//使用N_TTY来找到行规程
}

以后可以通过标号N_TTY找到这个行规程。搜索使用的地方就是:

Cscope tag: N_TTY
   #   line  filename / context / line
   //...
   7   2501  drivers/tty/n_tty.c <<n_tty_init>>
             tty_register_ldisc(N_TTY, &n_tty_ops);
   8     66  drivers/tty/tty_ldisc.c <<tty_register_ldisc>>
             if (disc < N_TTY || disc >= NR_LDISCS)
   9     96  drivers/tty/tty_ldisc.c <<tty_unregister_ldisc>>
             if (disc < N_TTY || disc >= NR_LDISCS)
  10    171  drivers/tty/tty_ldisc.c <<tty_ldisc_get>>
             if (disc < N_TTY || disc >= NR_LDISCS)
  11    532  drivers/tty/tty_ldisc.c <<tty_ldisc_restore>>
             if (tty_ldisc_failto(tty, N_TTY) < 0 &&
  12    676  drivers/tty/tty_ldisc.c <<tty_ldisc_reinit>>
             BUG_ON(disc == N_TTY);
  13    747  drivers/tty/tty_ldisc.c <<tty_ldisc_hangup>>
             tty_ldisc_reinit(tty, N_TTY) < 0)
  14    821  drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
             struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);		//这里就获得了行规程的结构

继续反向查找

   #   line  filename / context / line
   1    706  include/linux/tty.h <<GLOBAL>>
             extern int __must_check tty_ldisc_init(struct tty_struct *tty);
   2    706  include/linux/tty.h <<GLOBAL>>
             extern int __must_check tty_ldisc_init(struct tty_struct *tty);
   3   2842  drivers/tty/tty_io.c <<alloc_tty_struct>>
             if (tty_ldisc_init(tty)) {			//在这里调用了tty_ldisc_init
   4    819  drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
             int tty_ldisc_init(struct tty_struct *tty)

2.2 open设备时确定行规程

tty_open:打开串口时,会调用tty_open
    tty_open_by_driver:调用driver来打开一个tty结构体
    	tty_init_dev:分配和设置tty结构体
    		tty = alloc_tty_struct(driver, idx);		//分配一个行规程
					tty_ldisc_init(tty);		//初始化行规程
						struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);		//根据N_TTY整数找到之前的行规程
						tty->ldisc = ld;	//行规程会放在tty结构体里面,以后使用它来处理数据

三、read过程分析

流程为:

  • APP读
    • 使用行规程来读
    • 无数据则休眠
  • UART接收到数据,产生中断
    • 中断程序从硬件上读入数据
  • 发给行规程
    • 行规程处理后存入buffer
    • 行规程唤醒APP
  • APP被唤醒后,从行规程buffer中读入数据,返回

3.1 tty_read

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

	//...
	if (ld->ops->read)
		i = ld->ops->read(tty, file, buf, count);		//用到了行规程里的ops的read函数
	else
		i = -EIO;
	tty_ldisc_deref(ld);

	if (i > 0)
		tty_update_time(&inode->i_atime);

	return i;
}

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
			 unsigned char __user *buf, size_t nr)
{
	struct n_tty_data *ldata = tty->disc_data;
	unsigned char __user *b = buf;
	DEFINE_WAIT_FUNC(wait, woken_wake_function);			//这里申明了一个等待队列,没有数据就休眠
	int c;
	int minimum, time;
	ssize_t retval = 0;
	long timeout;
	int packet;
	size_t tail;

	c = job_control(tty, file);
	if (c < 0)
		return c;

	/*
	 *	Internal serialization of reads.
	 */
	if (file->f_flags & O_NONBLOCK) {
		if (!mutex_trylock(&ldata->atomic_read_lock))
			return -EAGAIN;
	} else {
		if (mutex_lock_interruptible(&ldata->atomic_read_lock))
			return -ERESTARTSYS;
	}

	down_read(&tty->termios_rwsem);

	minimum = time = 0;
	timeout = MAX_SCHEDULE_TIMEOUT;
	//...

	packet = tty->packet;
	tail = ldata->read_tail;

	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
		/* First test for status change. */
		if (packet && tty->link->ctrl_status) {
			//...
		}

		if (!input_available_p(tty, 0)) {
			up_read(&tty->termios_rwsem);
			tty_buffer_flush_work(tty->port);
			down_read(&tty->termios_rwsem);
			if (!input_available_p(tty, 0)) {	//如果没有数据就等待
				//...

				timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
						timeout);			//没有数据就休眠

				down_read(&tty->termios_rwsem);
				continue;
			}
		}

		if (ldata->icanon && !L_EXTPROC(tty)) {
			retval = canon_copy_from_read_buf(tty, &b, &nr);	//有数据后读取数据、返回
			if (retval)
				break;
		} else {
			int uncopied;

			/* Deal with packet mode. */
			if (packet && b == buf) {
				if (put_user(TIOCPKT_DATA, b)) {
					retval = -EFAULT;
					break;
				}
				b++;
				nr--;
			}

			uncopied = copy_from_read_buf(tty, &b, &nr);		//行规程读函数
			uncopied += copy_from_read_buf(tty, &b, &nr);
			if (uncopied) {
				retval = -EFAULT;
				break;
			}
		}

		n_tty_check_unthrottle(tty);
		//...
	}
	//...

	return retval;
}

copy_from_read_buf函数解析

static int copy_from_read_buf(struct tty_struct *tty,
				      unsigned char __user **b,
				      size_t *nr)

{
	//...
	if (n) {
		unsigned char *from = read_buf_addr(ldata, tail);		//从行规程读取数据
		retval = copy_to_user(*b, from, n);		//返回给App层
		n -= retval;
		is_eof = n == 1 && *from == EOF_CHAR(tty);
		tty_audit_add_data(tty, from, n);
		zero_buffer(tty, from, n);
		smp_store_release(&ldata->read_tail, ldata->read_tail + n);
		//...
	}
	return retval;
}

四、数据的源头

drivers\tty\serial\imx.c
函数:imx_rxint,数据存入对应的tty_port,也就是每个tty_port对应一个串口

static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
{
	struct imx_port *sport = dev_id;
	unsigned int rx, flg, ignored = 0;
	struct tty_port *port = &sport->port.state->port;
	unsigned long flags;

	spin_lock_irqsave(&sport->port.lock, flags);

	while (imx_uart_readl(sport, USR2) & USR2_RDR) {		//读取硬件状态
		u32 usr2;

		flg = TTY_NORMAL;
		sport->port.icount.rx++;		//在对应的uart_port中更新统计信息

		rx = imx_uart_readl(sport, URXD0);		//得到数据

		usr2 = imx_uart_readl(sport, USR2);
		if (usr2 & USR2_BRCD) {
			imx_uart_writel(sport, USR2_BRCD, USR2);		
			if (uart_handle_break(&sport->port))
				continue;
		}

		//各种记录数据

		if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
			goto out;

		if (tty_insert_flip_char(port, rx, flg) == 0)		//把数据存入tty_port里的tty_buffer
			sport->port.icount.buf_overrun++;
	}

out:
	spin_unlock_irqrestore(&sport->port.lock, flags);
	tty_flip_buffer_push(port);		//通知行规程来处理
	return IRQ_HANDLED;
}

void tty_flip_buffer_push(struct tty_port *port)
{
	tty_schedule_flip(port);
}

void tty_schedule_flip(struct tty_port *port)
{
	struct tty_bufhead *buf = &port->buf;

	/* paired w/ acquire in flush_to_ldisc(); ensures
	 * flush_to_ldisc() sees buffer data.
	 */
	smp_store_release(&buf->tail->commit, buf->tail->used);
	queue_work(system_unbound_wq, &buf->work);		//使用工作队列来处理,对应flush_to_ldisc函数
}

在imx.c的porbe函数里有注册中断函数的过程:

static int imx_uart_probe(struct platform_device *pdev)
{
	//...

	rxirq = platform_get_irq(pdev, 0);			//从设备树里获取的rxirq的中断号
	txirq = platform_get_irq(pdev, 1);
	rtsirq = platform_get_irq(pdev, 2);

	sport->port.dev = &pdev->dev;
	sport->port.mapbase = res->start;
	sport->port.membase = base;
	sport->port.type = PORT_IMX,
	sport->port.iotype = UPIO_MEM;
	sport->port.irq = rxirq;
	sport->port.fifosize = 32;
	sport->port.ops = &imx_uart_pops;
	sport->port.rs485_config = imx_uart_rs485_config;
	sport->port.flags = UPF_BOOT_AUTOCONF;
	timer_setup(&sport->timer, imx_uart_timeout, 0);

	//...
	if (txirq > 0) {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
				       dev_name(&pdev->dev), sport);		//在这里使用rxirq和imx_uart_rxint注册中断函数
		//...
	} else {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
				       dev_name(&pdev->dev), sport);
		if (ret) {
			dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
			return ret;
		}
	}

	imx_uart_ports[sport->port.line] = sport;

	platform_set_drvdata(pdev, sport);

	return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/20798.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux升级之路】3_Linux进程概念

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux升级之路】 ✒️✒️本篇内容&#xff1a;认识冯诺依曼系统&#xff0c;操作系统概念与定位&#xff0c;深入理解进程概念&#xff08;了解PCB&…

【#ifndef, #define, 和 #endif】

前言 学习AFNetWoring源码的时候&#xff0c;在AFN的h借接口文件又看到了这几个宏定义&#xff0c;学习记录一下。 作用 #ifndef, #define, 和 #endif是C/CPP的预处理指令&#xff0c;常常用来条件编译和防止头文件重复包含。 简介 #ifndef 它是if not define的简写&…

Cy5.5-PEG-SH近红外荧光PEG试剂 Cyanine5.5-PEG-SH,Thiol-PEG-Cy5.5可用于活体成像

Cy5.5-PEG-SH &#xff0c;Cy5.5聚乙二醇巯基 英文名称&#xff1a;Cy5.5-PEG-SH 中文名称&#xff1a;Cy5.5聚乙二醇巯基 性状: 深蓝色固体或粘性液体&#xff0c;取决于分子量 溶剂&#xff1a;溶于水、 DMSO等常规性有机溶剂 激发/发射波长&#xff1a;684 nm/710 nm …

学习HCIP的day.06

十一、OSFP扩展知识点 1、关于OSPF状态机的问题 &#xff08;1&#xff09;在MA网络中&#xff08;要进行DR/BDR选举&#xff09;存在7种状态机&#xff0c;init是路由器A收到邻居B的hello包&#xff0c;但该hello包中没有A的RID&#xff1b; &#xff08;2&#xff09;在点到…

js跨域的解决方案

一、什么是跨域&#xff1f; 指的是浏览器不能执行其他网站的脚本&#xff0c;简单来说是浏览器同源政策的限制&#xff0c;浏览器针对于ajax的限制。 同源政策 两个页面拥有相同的 协议&#xff0c;端口&#xff0c;域名 就是同源&#xff0c;如果有一个不相同就是不同源…

手把手教你用代码画架构图 | 京东云技术团队

作者&#xff1a;京东物流 覃玉杰 1. 前言 本文将给大家介绍一种简洁明了软件架构可视化模型——C4模型&#xff0c;并手把手教大家如何使用代码绘制出精美的C4架构图。 阅读本文之后&#xff0c;读者画的架构图将会是这样的&#xff1a; 注&#xff1a;该图例仅作绘图示例使…

【PCIE体系结构十一】部分物理层发送接收逻辑细节

&#x1f449;个人主页&#xff1a;highman110 &#x1f449;作者简介&#xff1a;一名硬件工程师&#xff0c;持续学习&#xff0c;不断记录&#xff0c;保持思考&#xff0c;输出干货内容 参考书籍&#xff1a;《PCI.EXPRESS系统体系结构标准教材 Mindshare》 目录 物理层…

【复杂网络建模】——通过图神经网络来建模分析复杂网络

目录 一、复杂网络介绍 二、复杂网络建模分析方法 三、基于图神经网络来建模 1、数据准备 2、构建图神经网络模型 3、学习节点和边的表示 4、特征提取和预测 5、模型评估和优化 四、可视化建模分析 1、初始网络可视化 2、特征可视化 一、复杂网络介绍 复杂网络是指…

软件测试专业应届生应如何提高职场竞争力

一&#xff1a;巩固专业知识 背景&#xff1a;笔者已经做了几年的打工人&#xff0c;以个人经验给软件测试专业应届生一些建议。 推荐需要掌握的知识&#xff1a; 1、软件测试基础知识&#xff08;软件生命周期每个阶段工作需了解&#xff09; 2、熟悉SQL/MySQL/Oracle数据库&…

【C++】哈希/散列详细解析

前言&#xff1a;上篇文章介绍了unordered_set和unordered_map序列关联式容器&#xff0c;它们之所以效率比较高&#xff0c;是因为其底层使用了哈希结构。&#xff0c;所以这篇文章我们就来详细讲解一下哈希表。有关unordered序列关联式容器的知识&#xff0c;请移步至这篇文章…

python3+pytest+requests+allure+yaml测试框架搭建

目录 设计框架的原则 1.框架整体结构 2.框架各个模块说明 3.示例 3.1 先写一个测试用例 3.2 对上面的用例进行分层封装&#xff08;可根据业务复杂度分两层或者三层&#xff0c;此处演示分三层&#xff09; 3.3生成allure测试报告并查看 设计框架的原则 封装基类方法 对…

山东移动:全业务域核心系统升级,实现大幅降本增效

本文介绍了山东移动引入 OceanBase 到山东省 BOSS/CRM 核心系统领域的相关情况。欢迎访问 OceanBase 官网获取更多信息&#xff1a;https://www.oceanbase.com/ 中国移动通信集团山东有限公司&#xff08;以下简称"山东移动"&#xff09; 隶属于中国移动通信集团公司…

发现一个国产BI软件,做财务数据分析效果绝了

如果是一般的财务数据分析&#xff0c;BI软件们都能做&#xff0c;但如果真要深入了解财务痛点&#xff0c;逐个击破财务数据分析难点&#xff0c;实现多维立体自助式的财务数据分析&#xff0c;那就难。就目前而言&#xff0c;财务数据分析做得好的国产BI软件也就一个奥威BI软…

java版Spring Cloud+SpringBoot+mybatis+uniapp b2b2c 多商户入驻商城 直播 电子商务

J2EE企业分布式微服务云快速开发架构 Spring CloudSpring Boot2MybatisOauth2ElementUI 前后端分离 1. 鸿鹄Cloud架构清单 2. Commonservice&#xff08;通用服务&#xff09; 通用服务&#xff1a;对spring Cloud组件的使用&封装&#xff0c;是一套完整的针对于分布式微…

原神QQ机器人BOT搭建教程Ubuntu系统

原神QQ机器人BOT搭建教程Ubuntu系统 大家好我是艾西&#xff0c;今天跟大家分享的是YUAN神qi鹅群机器人bot搭建方式以及详细的操作步骤。跟上艾西的节奏准备发车啦&#xff01; 前言&#xff1a;&#xff08;xxxx即为xxxx&#xff09;&#xff08;zzz即为zzz&#xff09; qi…

RK 平台MIPI 点屏注意事项

转自&#xff1a;https://www.cnblogs.com/chorm590/p/11658360.html rk 平台关于 MIPI 屏幕的点屏流程已经非常完善了&#xff0c;基本上只要确定了硬件没问题、接线没问题、屏幕没问题&#xff0c;再稍稍配置一下 dtsi 里的参数就可以的了。 MIPI 点屏流程大致可以概括为以下…

i春秋 Misc Web 爆破-1

打开链接是PHP源码 代码审计&#xff1a; include "flag.php"; 表示文件中包含flag.php文件&#xff0c;即根目录下存在flag.php $a $_REQUEST[hello]; 命名一个变量a来接收超全局变量$_REQUEST&#xff08;接收表单’hello’数据&#xff0c;请求一个为hello的参…

顶级白帽黑客必备的十大黑客技术

1.熟悉Linux系统和命令行操作&#xff1a; Linux是黑客的基石&#xff0c;几乎所有黑客工具和技术都是在Linux平台上运行的&#xff0c;熟悉Linux系统和命令行操作是必须的。 2.掌握网络协议和TCP/IP模型&#xff1a; 了解TCP/IP模型、网络协议和通信流程是黑客攻击的基础&a…

Java并发编程:并发问题和多线程技术的应用和优化

章节一&#xff1a;引言 在当今的软件开发领域中&#xff0c;多线程编程是一项至关重要的技术。随着处理器核心数量的增加和计算机系统的并行性的不断提高&#xff0c;充分利用多核心处理器的能力已成为现代软件开发的关键要素之一。Java作为一种强大的编程语言&#xff0c;在…

世界超高清大会发布重大技术成果:博冠自主创新推动8K摄像机攻关

一、世界超高清大会背景介绍&#xff1a; 近日&#xff0c;由工业和信息化部、国家广播电视总局、中央广播电视总台、广东省人民政府主办的2023世界超高清视频产业发展大会在广州越秀国际会议展览中心盛大召开。自2018年创办以来&#xff0c;大会已成功举办四届&#xff0c;成…