usbserial驱动流程解析_Part2_初始化流程_以probe为例(echo cat测试回环打印不停问题解决)

news2024/11/16 16:55:56


usb转串口设备需要注册usb侧和serial侧两侧的操作,本文将简要分析二者的初始化流程以及一些关键函数的初始化流程。

module_init(usb_serial_init);

tty设备初始化

内核会直接调用usb_serial_init,开始进行usb和serial的初始化,首先是进行tty设备(text typer,原有的字符打印机被称为tty,现在字符设备都称为tty设备,比如serial)初始化:

static const struct tty_operations serial_ops = {
	.open =			serial_open,
	.close =		serial_close,
	.write =		serial_write,
	.hangup =		serial_hangup,
	.write_room =		serial_write_room,
	.ioctl =		serial_ioctl,
	.set_termios =		serial_set_termios,
	.throttle =		serial_throttle,
	.unthrottle =		serial_unthrottle,
	.break_ctl =		serial_break,
	.chars_in_buffer =	serial_chars_in_buffer,
	.wait_until_sent =	serial_wait_until_sent,
	.tiocmget =		serial_tiocmget,
	.tiocmset =		serial_tiocmset,
	.get_icount =		serial_get_icount,
	.set_serial =		serial_set_serial,
	.get_serial =		serial_get_serial,
	.cleanup =		serial_cleanup,
	.install =		serial_install,
	.proc_show =		serial_proc_show,
};

这一部分是tty operation 的指定,包括串口设备的open close write writeroom settermios install等比较关键的函数,上一part进行了相应的描述,其中部分函数可以被替换掉。

	usb_serial_tty_driver->driver_name = "usbserial";
	usb_serial_tty_driver->name = "ttyUSB";
	usb_serial_tty_driver->major = USB_SERIAL_TTY_MAJOR;
	usb_serial_tty_driver->minor_start = 0;
	usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
	usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
	usb_serial_tty_driver->init_termios = tty_std_termios;
	usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
							| HUPCL | CLOCAL;
	usb_serial_tty_driver->init_termios.c_ispeed = 9600;
	usb_serial_tty_driver->init_termios.c_ospeed = 9600;

指定了init termios的值,比如波特率初始9600,初始的termios取tty_std_termios,这里有一个坑,将usb转串口模块的tx rx短接,试图测回环的时候,使用默认驱动会出现cat  /dev/ttyUSB0并echo xxx > /dev/ttyUSB0时,cat到的字符串越来越长,根本停不下来——

原因是默认情况下,rx收到包会再次从tx发出去,解决方法有2:

不短接tx rx:外接另一个usb转串口,tx接rx、rx接tx、共GND,这样在echo的时候,在对端可以看到echo过去的字符串,对端进行tx的时候,可以从cat获得字符串,同时,对端的rx也会收到发出去的字符串

初始化:使用putty或者minicom等软件,初始化一下usb转串口设备,再短接rx tx,再echo和cat就不会再有问题了

根本的原因是这样的:默认情况下termios取tty_std_termios导致的问题时,tty_std_termios定义在tty serial.c里面,里面init_termios.l_cflag取值有ECHO属性,换言之,read urb后调用write,是驱动以外的操作,是内核完成的,而这个属性,是ECHO属性,被内核读取到后,内核去进行以上操作。

usb设备初始化

 下图是usb部分的probe的指定调用流程图,图的顺序是从下往上看的:

在函数usb_serial_register_drivers中,注册了usb driver的关键内容:

    udriver->name = name;
	udriver->no_dynamic_id = 1;
	udriver->supports_autosuspend = 1;
	udriver->suspend = usb_serial_suspend;
	udriver->resume = usb_serial_resume;
	udriver->probe = usb_serial_probe;
	udriver->disconnect = usb_serial_disconnect;

	/* we only set the reset_resume field if the serial_driver has one */
	for (sd = serial_drivers; *sd; ++sd) {
		if ((*sd)->reset_resume) {
			udriver->reset_resume = usb_serial_reset_resume;
			break;
		}
	}

	rc = usb_register(udriver);

usb_register函数会把usb侧probe、disconnect等函数的实现传给内核

以USB的probe过程分析整体调用的流程

在USB转串口模块插入USB接口时,Linux内核会调用usb_serial_probe函数(上一个part只分析到调用到usb_serial_generic_probe函数,在调用usb_serial_generic_probe之后后面的操作并没有描述):

epds = kzalloc(sizeof(*epds), GFP_KERNEL);
	if (!epds) {
		retval = -ENOMEM;
		goto err_release_sibling;
	}

	find_endpoints(serial, epds, interface);
	if (serial->sibling)
		find_endpoints(serial, epds, serial->sibling);

	if (epds->num_bulk_in < type->num_bulk_in ||
			epds->num_bulk_out < type->num_bulk_out ||
			epds->num_interrupt_in < type->num_interrupt_in ||
			epds->num_interrupt_out < type->num_interrupt_out) {
		dev_err(ddev, "required endpoints missing\n");
		retval = -ENODEV;
		goto err_free_epds;
	}

	if (type->calc_num_ports) {
		retval = type->calc_num_ports(serial, epds);
		if (retval < 0)
			goto err_free_epds;
		num_ports = retval;
	}

	if (!num_ports)
		num_ports = type->num_ports;

	if (num_ports > MAX_NUM_PORTS) {
		dev_warn(ddev, "too many ports requested: %d\n", num_ports);
		num_ports = MAX_NUM_PORTS;
	}

usb的probe进行过程中,驱动会去find_endpoints(serial, epds, interface);,扫描endpoint后把扫到的addr存在serial的变量里面:

	serial->num_ports = (unsigned char)num_ports;
	serial->num_bulk_in = epds->num_bulk_in;
	serial->num_bulk_out = epds->num_bulk_out;
	serial->num_interrupt_in = epds->num_interrupt_in;
	serial->num_interrupt_out = epds->num_interrupt_out;

	/* found all that we need */
	dev_info(ddev, "%s converter detected\n", type->description);

	/* create our ports, we need as many as the max endpoints */
	/* we don't use num_ports here because some devices have more
	   endpoint pairs than ports */
	max_endpoints = max(epds->num_bulk_in, epds->num_bulk_out);
	max_endpoints = max(max_endpoints, epds->num_interrupt_in);
	max_endpoints = max(max_endpoints, epds->num_interrupt_out);
	max_endpoints = max(max_endpoints, serial->num_ports);
	serial->num_port_pointers = max_endpoints;

	dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints);
	for (i = 0; i < max_endpoints; ++i) {
		port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
		if (!port) {
			retval = -ENOMEM;
			goto err_free_epds;
		}
		tty_port_init(&port->port);
		port->port.ops = &serial_port_ops;
		port->serial = serial;
		spin_lock_init(&port->lock);
		/* Keep this for private driver use for the moment but
		   should probably go away */
		INIT_WORK(&port->work, usb_serial_port_work);
		serial->port[i] = port;
		port->dev.parent = &interface->dev;
		port->dev.driver = NULL;
		port->dev.bus = &usb_serial_bus_type;
		port->dev.release = &usb_serial_port_release;
		port->dev.groups = usb_serial_port_groups;
		device_initialize(&port->dev);
	}

存好了bulkin bulkout等地址就可以为后面的read和write的操作提供endpoint地址,保存后对这些endpoint进行初始化,初始化的过程就是先给urb进行一系列的初始化,指定bulkin bulkout的addr并绑定到对应的port,在read和write的时候直接修改urb里面的数据或者获取里面的数据,就可以直接submit urb即可。

	/* set up the endpoint information */
	for (i = 0; i < epds->num_bulk_in; ++i) {
		retval = setup_port_bulk_in(serial->port[i], epds->bulk_in[i]);
		if (retval)
			goto err_free_epds;
	}

	for (i = 0; i < epds->num_bulk_out; ++i) {
		retval = setup_port_bulk_out(serial->port[i],
				epds->bulk_out[i]);
		if (retval)
			goto err_free_epds;
	}

	if (serial->type->read_int_callback) {
		for (i = 0; i < epds->num_interrupt_in; ++i) {
			retval = setup_port_interrupt_in(serial->port[i],
					epds->interrupt_in[i]);
			if (retval)
				goto err_free_epds;
		}
	} else if (epds->num_interrupt_in) {
		dev_dbg(ddev, "The device claims to support interrupt in transfers, but read_int_callback is not defined\n");
	}

	if (serial->type->write_int_callback) {
		for (i = 0; i < epds->num_interrupt_out; ++i) {
			retval = setup_port_interrupt_out(serial->port[i],
					epds->interrupt_out[i]);
			if (retval)
				goto err_free_epds;
		}
	} else if (epds->num_interrupt_out) {
		dev_dbg(ddev, "The device claims to support interrupt out transfers, but write_int_callback is not defined\n");
	}

	usb_set_intfdata(interface, serial);

	/* if this device type has an attach function, call it */
	if (type->attach) {
		retval = type->attach(serial);
		if (retval < 0)
			goto err_free_epds;
		serial->attached = 1;
		if (retval > 0) {
			/* quietly accept this device, but don't bind to a
			   serial port as it's about to disappear */
			serial->num_ports = 0;
			goto exit;
		}
	} else {
		serial->attached = 1;
	}

	retval = allocate_minors(serial, num_ports);
	if (retval) {
		dev_err(ddev, "No more free serial minor numbers\n");
		goto err_free_epds;
	}

在probe的最后 ,在linux系统注册设备为TTYUSB0.

	/* register all of the individual ports with the driver core */
	for (i = 0; i < num_ports; ++i) {
		port = serial->port[i];
		dev_set_name(&port->dev, "ttyUSB%d", port->minor);
		dev_dbg(ddev, "registering %s\n", dev_name(&port->dev));
		device_enable_async_suspend(&port->dev);

		retval = device_add(&port->dev);
		if (retval)
			dev_err(ddev, "Error registering port device, continuing\n");
	}

	if (num_ports > 0)
		usb_serial_console_init(serial->port[0]->minor);

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

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

相关文章

全面探究 LangChain Text Splitters

全面探究 LangChain Text Splitters 0. 引言1. 文本拆分器的类型2. 探究各个文本拆分器2-1. Split by HTML header2-2. Split by HTML section2-3. Split by character2-4. Split code2-5. MarkdownHeaderTextSplitter2-6. Recursively split JSON2-7. Recursively split by ch…

lv17 CGI移植 5-1

简介 CGIC是一个支持CGI开发的开放源码的标准C库&#xff0c;可以免费使用&#xff0c;只需要在开发的站点和程序文档中有个公开声明即可&#xff0c;表明程序使用了CGIC库&#xff0c;用户也可以购买商业授权而无需公开声明。 CGIC能够提供以下功能&#xff1a; 分析数据&a…

疲劳驾驶预警系统项目知识点整理

参考&#xff1a; 重磅&#xff01;头部姿态估计「原理详解 实战代码」来啦&#xff01;-阿里云开发者社区 (aliyun.com) Dlib模型之驾驶员疲劳检测三&#xff08;瞌睡点头&#xff09;_疲劳检测 点头-CSDN博客 python毕业设计 深度学习疲劳检测 驾驶行为检测 - opencv cnn…

面试:HashMap

目录 1、底层数据结构&#xff0c;1.7 与1.8有何不同? 2、为何要用红黑树&#xff0c;为何一上来不树化&#xff0c;树化阈值为何是8&#xff0c;何时会树化&#xff0c;何时会退化为链表? 3、索引如何计算? hashCode都有了&#xff0c;为何还要提供hash()方法?数组容量为…

交换机与队列的简介

1.流程 首先先介绍一个简单的一个消息推送到接收的流程&#xff0c;提供一个简单的图 黄色的圈圈就是我们的消息推送服务&#xff0c;将消息推送到 中间方框里面也就是 rabbitMq的服务器&#xff0c;然后经过服务器里面的交换机、队列等各种关系&#xff08;后面会详细讲&…

保研复习数据结构-图(10)

一.图的定义和基本术语 1.什么是图&#xff1f; 图(Graph)是由顶点的有穷非空集合V(G)和顶点之间边的集合E(G)组成&#xff0c;通常表示为:G(V,E)&#xff0c;其中&#xff0c;G表示图&#xff0c;V是图G中顶点的集合&#xff0c;E是图G中边的集合。 2.什么是完全图&#xf…

【第十二篇】使用BurpSuite实现CSRF(实战案例)

CSRF存在前提:简单的身份验证只能保证请求是发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的 业务场景:新增、删除、收藏、编辑、保存使用Burp发现CSRF漏洞的过程如下。 1、如图,存在修改邮箱的功能点如下: 2、修改邮箱的流量包,此时邮箱已被修改: 思路:是…

12、最小覆盖子串

如何想到这个解法 问题的特点&#xff1a; 首先&#xff0c;认识到这是一个关于子串的问题&#xff0c;而且需要考虑子串的最小长度。这提示我们可能需要使用一种方式来逐步探索不同的子串。滑动窗口的适用性&#xff1a;滑动窗口是处理子串问题的常用技巧&#xff0c;特别是当…

X年后,ChatGPT会替代底层程序员吗?

能不能替代&#xff0c;真的很难说&#xff0c;因为机器换掉人&#xff0c;这其实是一个伦理问题。 其实说白了&#xff0c;任何行业在未来都会被AI或多或少的冲击到&#xff0c;因为ChatGPT做为一个可以持续提升智能的AI&#xff0c;在某些方面的智能程度超过人类并不是什么难…

笛卡尔树[天梯赛二叉树专项训练]

文章目录 题目描述思路AC代码 题目描述 输入样例1 6 8 27 5 1 9 40 -1 -1 10 20 0 3 12 21 -1 4 15 22 -1 -1 5 35 -1 -1 输出样例1 YES 输入样例2 6 8 27 5 1 9 40 -1 -1 10 20 0 3 12 11 -1 4 15 22 -1 -1 50 35 -1 -1 输出样例2 NO思路 见注释 AC代码 #include <bits/st…

5. 4 二重循环将二维数组的某列、某矩形转大写

5. 4 二重循环将二维数组的某列、某矩形转大写 1. 把每一行的b都变成大写 assume cs:codesg,ds:data,ss:stack data segmeNTstr db aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbccccc,$ data endsstack segmentdb 10 dup(0) stack endscodesg SEgments…

【Vue】Vue3中的OptionsAPI与CompositionAPI

文章目录 OptionsAPICompositionAPI对比总结 OptionsAPI 中文名:选项式API通过定义methods,computed,watch,data等属性方法&#xff0c;处理页面逻辑。以下是OptionsAPI代码结构 实例代码: <script lang"ts">// js或者tsimport { defineComponent } from vu…

豆瓣9.7,这部Java神作第3版重磅上市!

Java 程序员们开年就有重磅好消息&#xff0c;《Effective Java 中文版&#xff08;原书第 3 版&#xff09;》要上市啦&#xff01; 该书的第1版出版于 2001 年&#xff0c;当时就在业界流传开来&#xff0c;受到广泛赞誉。时至今日&#xff0c;已热销近20年&#xff0c;本书…

React - 连连看小游戏

简介 小时候经常玩连连看小游戏。在游戏中&#xff0c;当找到2个相同的元素就可以消除元素。 本文会借助react实现连连看小游戏。 实现效果 实现难点 1.item 生成 1. 每一个图片都是一个item&#xff0c;items数组的大小为size*size。 item对象包括grid布局的位置&#xff0c;…

UE小:UE5.3无法创建C++工程

当您在使用Unreal Engine (UE) 构建项目时&#xff0c;如果遇到以下问题&#xff1a; Running C:/Program Files/Epic Games/UE\_5.3/Engine/Build/BatchFiles/Build.bat -projectfiles -project"C:/UEProject/Shp\_1/Shp\_1.uproject" -game -rocket -progress Usi…

网络基础三——初识IP协议

网络基础三 ​ 数据通过应用层、传输层将数据传输到了网络层&#xff1b; ​ 传输层协议&#xff0c;如&#xff1a;TCP协议提供可靠性策略或者高效性策略&#xff0c;UDP提供实时性策略&#xff0c;保证向下层交付的数据是符合要求的的&#xff1b;而网络层&#xff0c;如&a…

websokcet服务端实现

一/websokcet服务端实现 步骤一&#xff1a; springboot底层帮我们自动配置了websokcet&#xff0c;引入maven依赖 1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</arti…

分享10个免费高可用的GPT3.5和4.0网站并做功能测试【第一个】

1.介绍 网址&#xff1a;直接点&#xff1a;aicnn 或者 www.aicnn.cn 基于ChatGPT可以实现智能聊天、绘画生成、高清文本转语音、论文润色等多种功能&#xff0c;基于sd和mj实现的绘画功能&#xff0c;下面是功能测试&#xff1a; 博主从 1.GPT3.5是否完全免费/是否限制频率、…

基于SSM的基于个人需求和地域特色的外卖推荐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的基于个人需求和地域特色的外卖推荐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

作为一个前端,在入职新公司如何快速安装好开发环境

由于电脑运行内存才16G有点卡&#xff0c;今天公司给我们换了32G内存&#xff0c;是直接整个主机都换了&#xff0c;环境自然得重新安装&#xff0c;在装的过程中&#xff0c;自己会有些心得体会&#xff0c;就是想着一个新人如何快速安装环境。 个人说一下我的思路&#xff1a…