UART驱动情景分析-read

news2024/11/29 16:37:28

一、源码框架回顾

在这里插入图片描述
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.coloradmin.cn/o/528189.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytorch 测量模型运行时间,GPU时间和CPU时间,model.eval()介绍

文章目录 1. 测量时间的方式2. model.eval(), model.train(), torch.no_grad()方法介绍2.1 model.train()和model.eval()2.2 model.eval()和torch.no_grad() 3. 模型推理时间方式4. 一个完整的测试模型推理时间的代码5. 参考&#xff1a; 1. 测量时间的方式 time.time() time.…

使用qt creator编译zlib

zlib被设计为一个免费的&#xff0c;通用的&#xff0c;法律上不受限制的-即不受任何专利保护的无损数据压缩库&#xff0c;几乎可以在任何计算机硬件和操作系统上使用。 官网&#xff1a;http://www.zlib.net/ 下载zlib源码:http://www.zlib.net/zlib1213.zip 备用地址&#x…

关于使用API接口获取商品数据的那些事

随着电商行业的不断发展&#xff0c;越来越多的企业和个人需要获取各大电商平台上的商品数据。而最常用的方法是使用API接口获取商品数据。本文将为您介绍使用API接口获取商品数据的步骤和注意事项。 一、选择API接口 首先需要了解各大电商平台提供的API接口&#xff0c;目前…

由浅入深理解java集合(一)——集合框架 Collection、Map

Java 提供了一套完整的集合类&#xff08;也可以叫做容器类&#xff09;来管理一组长度可变的对象&#xff08;也就是集合的元素&#xff09;&#xff0c;其中常见的类型包括 List、Set、Queue 和 Map。从我个人的编程经验来看&#xff0c;List 的实现类 ArrayList 和 Map 的实…

华为OD机试 - 查找树中元素(Python)

题目描述 已知树形结构的所有节点信息,现要求根据输入坐标(x,y)找到该节点保存的内容值,其中x表示节点所在的层数,根节点位于第0层,根节点的子节点位于第1层,依次类推;y表示节点在该层内的相对偏移,从左至右,第一个节点偏移0,第二个节点偏移1,依次类推; 举例:…

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

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

【入土级】详解C++类对象(中篇)

目录 前言&#xff1a;类的6个默认成员函数 一&#xff0c; 构造函数 1. 概念 2. 特性 二&#xff0c; 析构函数 2.1 概念 2.2 特性 2.3 牛刀小试 三&#xff0c; 拷贝构造函数 3.1概念 3. 2 特点 四&#xff0c; 赋值运算符重载 4. 1 运算符重载 五&#xff0…

网站测试的主要方法

网站测试的主要方法 网站测试是保证网站质量的重要手段&#xff0c;通过对网站进行测试可以及时发现问题并修复&#xff0c;提高用户体验和网站的可靠性。本文将介绍网站测试的主要方法。 1.功能测试&#xff1a;测试网站的所有功能是否正常。通过模拟用户的操作&#xff0c;确…

最新水果FLStudio21中文版下载及快捷键操作教程

任何一款软件&#xff0c;其快捷键永远扮演着至关重要的作用&#xff0c;熟练运用快捷键不仅能够节省时间&#xff0c;提高工作效率&#xff0c;还有助于熟练掌握所使用的软件。作为一款功能强大的音乐编曲软件&#xff0c;FL Studio有着大量的快捷键&#xff0c;这些快捷键在一…

【ArcGIS Pro二次开发】(28):用地图斑导出用地用海汇总表

本工具的作用是将现状用地或规划用地导出Excel格式的用地用海汇总表。 实现这个功能的Arcpy脚本工具我之前已经做过&#xff0c;详见&#xff1a;ArcGisPro脚本工具【8】——用地图斑导出用地用海汇总表 这次试着在ArcGIS Pro SDK中来实现同样的功能。 一、要实现的功能 如上…

【存储数据恢复】H3C存储卷中的数据恢复案例

存储数据恢复环境&故障&#xff1a; H3C FlexStorage某型号存储&#xff0c;25块磁盘组建的RAID5&#xff0c;其中包含一块热备盘。 工作人员误操作将存储设备中原先的2个卷删除&#xff0c;删除之后又使用和删除2个卷同样大小的空间重建了一个卷。用户希望恢复删除的2个卷…

springboot缓存

1. 认识缓存 一级缓存 - 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质 - 使用缓存可以有效的减少低速数据读取过程的次数&#xff0c;提高系统性能 Service public class BookServiceImplCache implements BookService {Autowiredprivate BookDao book…

玩转服务器之环境篇:PHP和Python环境部署指南 | 京东云技术团队

前几篇文章中讲解了如何搭建docker和Java Web环境的方法&#xff0c;本篇文章来教大家搭建一个好的PHP和Python环境&#xff0c;可以帮助开发和运行PHP和Python应用程序&#xff0c;使其更加高效和稳定。 一、 PHP环境介绍 好的开发环境无疑会大大提升编码效率&#xff0c;近…

搭载RK3588的Orange Pi 5 Plus来了!首发“亲民价”649元起!

Orange Pi 5 Plus来了。令人惊艳的Orange Pi 5 家族将又添一个重量级新成员! 作为香橙派首款搭载瑞芯微RK3588的开发板&#xff0c;Orange Pi 5 Plus将成为顶级高性能开发板的臻选。先说几个令人惊艳的亮点&#xff1a;两个PCIe扩展的2.5G以太网接口&#xff1b;eMMC 闪存插座…

黑马Redis笔记高级篇 | 分布式缓存

黑马Redis笔记高级篇 | 分布式缓存 1、Redis持久化&#xff08;解决数据丢失&#xff09;1.1 RDB持久化1.1.1 定义1.1.2 异步持久化bgsave原理 1.2 AOF持久化1.3 RDB和AOF比较 2、Redis主从&#xff08;解决并发问题&#xff09;2.1 搭建主从架构2.2 主从数据同步原理2.2.1 全量…

这样连交换机和路由器,多少网工没试过?

大家好&#xff0c;我是老杨。 交换机已经说过很多遍了&#xff0c;园区组网案例、交换机级联、光模块配件解读等等…… 有群友说想要看更多的配置案例&#xff0c;这次就给你说说&#xff0c;交换机和路由器对接上网配置。 还搞不明白交换机和路由器的&#xff0c;复习一下…

webpack开发服务器配置

&#x1f482; 个人网站:[【紫陌】【笔记分享网】](http://zimo.aizhaiyu.com/) &#x1f485; 想寻找共同学习交流、共同成长的伙伴&#xff0c;[请点击【前端学习交流群】](http://zimo.aizhaiyu.com/wechat/wechat.html) 文章最后有作者l联系方式&#xff08;备注进群&am…

写给程序员Android Framework 开发,

前言 在 Android 开发者技能中&#xff0c;如果想进大厂&#xff0c;一般拥有较好的学历可能有优势一些。但是如果你靠硬实力也是有机会的&#xff0c;例如死磕Framework。Framework 知识广泛应用在Android各个领域中&#xff0c;重要性显而易见。 成为一名Android Framework…

【2023程序员必看】大数据行业分析

1、政策重点扶持&#xff0c;市场前景广阔 2014年&#xff0c;大数据首次写入政府工作报告&#xff0c;大数据逐渐成为各级政府关注的热点。 2015年9月&#xff0c;国务院发布《促进大数据发展的行动纲要》&#xff0c;大数据正式上升至国家战略层面&#xff0c;十九大报告提…

网络突发环路,原来可以这么解决啊

大家好&#xff0c;我是老杨。 我相信&#xff0c;任何一个网工都遇到过网络环路&#xff0c;遇到这个情况&#xff0c;你的应对方法是什么&#xff1f; 我了解到大部分的初阶网工&#xff0c;最开始都只能用拔插网线和重启观测法来排除回路。 简单来说&#xff0c;就是先给…