UART驱动情景分析-read

这篇具有很好参考价值的文章主要介绍了UART驱动情景分析-read。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、源码框架回顾

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函数里有注册中断函数的过程:文章来源地址https://www.toymoban.com/news/detail-450708.html

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);
}


到了这里,关于UART驱动情景分析-read的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SA8295P 源码分析】13 - Android GVM 虚拟机 QUPv3 UART / SPI / I2C功能配置及透传配置

    【源码分析】 因为一些原因,本文需要移除, 对于已经购买的兄弟,不用担心,不是跑路, 我会继续持续提供技术支持, 有什么模块想学习的,或者有什么问题有疑问的, 请私聊我,我们 +VX 沟通技术问题,一起学习,一起进步 接下来,我一一私聊已经购买的兄弟添加V

    2024年02月12日
    浏览(39)
  • 消费者行为分析VR情景模拟演练系统

    VR虚拟现实技术是一种先进的技术,利用VR开展消费者行为分析课程是一种创新的教育方式,它可以提高学生的学习兴趣和效果,同时也可以为企业提供更好的人才培训和发展机会。 1.帮助学生更好地理解和应用心理学概念:VR技术可以让学生身临其境地体验各种情境,从而更

    2024年02月16日
    浏览(46)
  • Contractfuzzer 框架分析(源码分析)

    最近阅读了Contractfuzzer的源代码,总结了Contractfuzzer的系统框架图  Contractfuzzer 是第一篇智能合约fuzz的文章,其主要分为三个模块,分别是测试用例生成模块、交易自动化发送模块和交易执行与漏洞检测模块。 一、测试用例生成模块(对应文件Contract_fuzzer) 测试用例生成模块

    2024年01月20日
    浏览(24)
  • Shell——read命令的使用

    read命令可以从标准输入中读取一行并将其拆分为字段 read命令读取单个变量 : 因为read命令是读取一行的内容,因此空格不会作为分隔符。 read命令还可以读取多个变量(变量中间用空格间隔)例如 : 此外,read还有丰富的可添加选项,其格式: read [可添加选项] [变量名] ,

    2024年02月13日
    浏览(19)
  • 【SA8295P 源码分析】66 - Android 侧内核层 TouchScreen Panel(TP)触摸屏驱动源码分析

    【源码分析】 因为一些原因,本文需要移除, 对于已经购买的兄弟,不用担心,不是跑路, 我会继续持续提供技术支持, 有什么模块想学习的,或者有什么问题有疑问的, 请私聊我,我们 +VX 沟通技术问题,一起学习,一起进步 接下来,我一一私聊已经购买的兄弟添加V

    2024年02月11日
    浏览(62)
  • 【框架源码】SpringBoot核心源码解读之启动类源码分析

    首先我们要先带着我们的疑问,spring boot是如何启动应用程序?去分析SpringBoot的启动源码。 我们在新建SpringBoot项目时,核心方法就是主类的run方法。 SpringApplication.run(ArchWebApplication.class, args) 我们点击run方法进入到源码中,这块传入的了一个我们当前程序主类的类对象以及主

    2024年02月06日
    浏览(44)
  • 海思芯片pcie启动——pcie_mcc驱动框架的booter程序分析

    (1)源码目录:pcie_mcc/multi_boot/example/boot_test.c; (2)调用命令:./booter start_device; (3)booter程序的作用:在主片将pcie启动相关的驱动加载完成后,调用booter来引导从片pcie启动; (1)调用pcie启动相关驱动,知道当前pcie接口连接了多少个从片; (2)先传输uboot的前80KB数据到36A的内部RAM中

    2024年02月06日
    浏览(77)
  • Linux shell编程学习笔记36:read命令

     *更新日志  *2023-12-18 1.根据[美] 威廉·肖特斯 (Willian shotts)所著《Linux命令行大全(第2版)》                         更新了-e、-i、-r选项的说明                       2.更新了 2.8 的实例,增加了gif动图                       3.补充了-i的应用实例 2.12 目录 目录

    2024年02月04日
    浏览(46)
  • 【生态系统服务】构建生态安全格局--权衡与协同动态分析--多情景模拟预测--社会价值评估

    生态系统服务 生态系统服务(ecosystem services)是指人类从生态系统获得的所有惠益,包括供给服务(如提供食物和水)、调节服务(如控制洪水和疾病)、文化服务(如精神、娱乐和文化收益)以及支持服务(如维持地球生命生存环境的养分循环) 生态安全是指生态系统的

    2024年02月12日
    浏览(64)
  • Java集合框架之ArrayList源码分析

    ArrayList是Java提供的线性集合,本篇笔记将从源码(java SE 17)的角度学习ArrayList: 什么是ArrayList? ArrayList底层数据结构是怎么实现的? 作为一个容器,分析增删改查的过程 ArrayList的扩容机制 由ArrayList的定义可知,ArrayList继承了AbstractList抽象类,实现了List、RandomAccess、Cloneabl

    2024年02月07日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包