【正点原子Linux连载】 第三十四章 Linux USB驱动实验 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三十四章 Linux USB驱动实验

USB是很常见的接口,目前大多数的设备都是USB接口的,比如鼠标、键盘、USB摄像头等,我们在实际开发中也常常遇到USB接口的设备,本章我们就来学习一下如何使能Linux内核自带的USB驱动。注意!本章并不讲解具体的USB开发,因为USB接口很复杂,不同的设备其协议也不同,这不是简简单单一章内容就能说完的,USB驱动开发本身就是一门复杂的课程。所以,如果想要学习如何编写代码开发一个全新的USB设备那就可以跳过本章。

34.1 USB接口简介
关于USB详细的协议内容请参考《USB2.0协议中文版.pdf》和《USB3.0协议中文版.pdf》,这两份文档已经放到了开发板光盘中,存放在“06、参考资料” 中。
34.1.1 什么是USB?
USB全称为Universal Serial Bus,翻译过来就是通用串行总线。由英特尔与众多电脑公司提出来,用于规范电脑与外部设备的连接与通讯。目前USB接口已经得到了大范围的应用,已经是电脑、手机等终端设备的必配接口,甚至取代了大量的其他接口。比如最新的智能手机均采用USB Typec取到了传统的3.5mm耳机接口,苹果最新的MacBook只有USB Typec接口,至于其他的HDMI、网口等均可以通过USB Typec扩展坞来扩展。
按照大版本划分,USB目前可以划分为USB1.0、USB2.0、USB3.0以及正在即将到来的USB4.0。
USB1.0:USB规范于1995年第一次发布,由Inter、IBM、Microsoft等公司组成的USB-IF(USB Implement Forum)组织提出。USB-IF与1996年正式发布USB1.0,理论速度为1.5Mbps。1998年USBIF在USB1.0的基础上提出了USB1.1规范。
USB2.0:USB2.0依旧由Inter、IBM、Microsoft等公司提出并发布,USB2.0分为两个版本:Full-Speed和High-Speed,也就是全速(FS)和高速(HS)。USB2.0 FS的速度为12Mbps,USB2.0 HS速度为480Mbps。目前大多数单片机以及低端Cortex-A芯片配置的都是USB2.0接口,比如ATK-DLRK3568,USB2.0全面兼容USB1.0标准。
USB3.0:USB3.0同样由Inter等公司发起的,USB3.0最大理论传输速度为5.0Gbps,USB3.0引入了全双工数据传输,USB2.0的480Mbps为半双工。USB3.0中两根线用于发送数据,另外两根用于接收数据。在USB3.0的基础上又提出了USB3.1、USB3.2等规范,USB3.1理论传输速度提升到了10Gbps,USB3.2理论传输速度为20Gbps。为了规范USB3.0标准的命名,USB-IF公布了最新的USB命名规范,原来的USB3.0和USB3.1命名将不会采用,所有的3.0版本的USB都命名为USB3.2,以前的USB3.0、USB3.1和USB3.2分别叫做USB3.2 Gen1、USB3.2 Gen2、USB3.2 Gen 2X2。
USB4.0:目前还在标准定制中,目前还没有设备搭载,据说是在Inter的雷电3接口上改进而来。USB4.0的速度将提升到了40Gbps,最高支持100W的供电能力,只需要一根线就可以完成数据传输与供电,极大的简化了设备之间的链接线数,期待USB4.0设备上市。
如果按照接口类型划分的话USB就要分为很多种了,最常见的就是USB A插头和插座,如图34.1.1.1所示:
在这里插入图片描述

图34.1.1.1 USB A插头(左)和插座(右)
使用过JLINK调试器的朋友应该还见过USB B插头和插座,USB B插头和插座如图34.1.1.2所示:
在这里插入图片描述

图34.1.1.2 USB B插头(左)和插座(右)
USB插头在不断的缩小,由此产生了Mini USB接口, Mini USB插头和插座如图34.1.1.3所示:
在这里插入图片描述

图34.1.1.3 Mini USB插头(左)和插座(右)
比Mini USB更小的就是Micro USB接口了,以前的智能手机基本都是Micro USB接口的,Micro USB插头和插座如图34.1.1.4所示:
在这里插入图片描述

图34.1.1.4 Micro USB插头(左)和插座(右)
现在最流行的就是USB Typec了,正点原子的STM32MP1开发板使用的是USB Typec接口,USB Typec插头和插座如图34.1.1.5所示:
在这里插入图片描述

图34.1.1.5 USB Typec插头(左)和插座(右)
34.1.2 USB电气特性
1、Mini USB电气属性
我们先以Mini USB为例讲解一下USB的基本电气属性。Mini USB线一般都是一头为USB A插头,一头为Mini USB插头。一共有四个触点,也就是4根线,这四根线的顺序如图34.1.2.1所示:
在这里插入图片描述

图34.1.2.1 USB A插头线序
如图50.1.2.1所示,USB A插头从左到右线序依次为1,2,3,4,第1根线为VBUS,电压为5V,第2根线为D-,第3根线为D+,第4根线为GND。USB采用差分信号来传输数据,因此有D-和D+两根差分信号线。大家仔细观察的话会发现USB A插头的1和4这两个触点比较长,2和3这两个触点比较短。1和4分别为VBUS和GND,也就是供电引脚,当插入USB的时候会先供电,然后再接通数据线。拔出的时候先断开数据线,然后再断开电源线。
大家再观察一下Mini USB插头,会发现Mini USB插头有5个触点,也就是5根线,线序从左往右依次是1~5。第1根线为VCC(5V),第2根线为D-,第3根线为D+,第4根线为ID,第5根线为GND。可以看出Mini USB插头相比USB A插头多了一个ID线,这个ID线用于实现OTG功能,通过ID线来判断当前连接的是主设备(HOST)还是从设备(SLAVE)。
USB是一种支持热插拔的总线接口,使用差分线(D-和D+)来传输数据,USB支持两种供电模式:总线供电和自供电,总线供电就是由USB接口为外部设备供电,在USB2.0下,总线供电最大可以提供500mA的电流。
34.1.3 USB拓扑结构
USB是主从结构的,也就是分为主机和从机两部分,一般主机叫做Host,从机叫做Device。主机通过USB A插座来连接外部的设备,比如电脑作为主机,对外提供USB A插座,我们可以通过USB线来连接一些USB设备,比如声卡、手机等。因此电脑带的USB A插座数量就决定了你能外接多少个USB设备,如果不够用的话我们可以购买USB集线器来扩展电脑的USB插口,USB集线器也叫做USB HUB,USB HUB如图34.1.3.1所示:
在这里插入图片描述

图34.1.3.1 USB HUB
图34.1.3.1是一个一拖四的USB HUB,也就是将一个USB接口扩展为4个。USB只能主机与设备之间进行数据通信,USB主机与主机、设备与设备之间是不能通信的。因此两个正常通信的USB接口之间必定有一个主机,一个设备。为此使用了不同的插头和插座来区分主机与设备,比如主机提供USB A插座,从机提供Mini USB、Micro USB等插座。在一个USB系统中,仅有一个USB主机,但是可以有多个USB设备,包括USB功能设备和USB HUB,最多支持127个设备。一个USB主控制器支持128个地址,地址0是默认地址,只有在设备枚举的时候才会使用,地址0不会分配给任何一个设备。所以一个USB主控制器最多可以分配127个地址。整个USB的拓扑结构就是一个分层的金字塔形,如图34.1.3.2所示(参考自USB2.0协议中文版.pdf):
在这里插入图片描述

图34.1.3.2 USB金字塔拓扑
图34.1.3.2中可以看出从Root Hub开始,一共有7层,金字塔顶部是Root Hub,这个是USB控制器内部的。图中的Hub就是连接的USB集线器,Func就是具体的USB设备。
USB主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个USB设备一旦上电就会存在一个管道,也就是默认管道,USB主机通过管道来获取从机的描述符、配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个特定的端点。
34.1.4 什么是USB OTG?
前面我们讲了,USB分为HOST(主机)和从机(或DEVICE),有些设备可能有时候需要做HOST,有时候又需要做DEVICE,配两个USB口当然可以实现,但是太浪费资源了。如果一个USB接口既可以做HOST又可以做DEVICE那就太好了,使用起来就方便很多。为此,USB OTG应运而生,OTG是On-The-Go的缩写,支持USB OTG功能的USB接口既可以做HOST,也可以做DEVICE。那么问题来了,一个USB接口如何知道应该工作在HOST还是DEVICE呢?这里就引入了ID线这个概念,前面讲解USB电气属性的时候已经说过了,Mini USB插头有5根线,其中一条就是ID线。ID线的高低电平表示USB口工作在HOST还是DEVICE模式:
ID=1:OTG设备工作在从机模式。
ID=0:OTG设备工作在主机模式。
支持OTG模式的USB接口一般都是Mini USB、Micro USB等这些带有ID线的接口。正点原子的ATK-DLRK3568开发板OTG模式是使用USB Type C做接口,没有ID线。USB Type C有自己的识别方法,稍后会讲解TypeC接口电气属性。正点原子的ATK-DLRK3568开发板USB_OTG连接到了USB3接口上。如果要使用OTG的主机模式,那么就需要一根OTG线,TypeC接口的OTG线如图34.1.4.1所示:

在这里插入图片描述

图34.1.4.1 Typec OTG线
可以看出,TypeC OTG线一头是USB A插座,一头是Typec插头,将TypeC插头插入机器的TypeC口上,需要连接的USB设备插到另一端的USB A插座上,比如U盘啥的。TypeC的CC引脚检查到USB设备已经接入,就会建立USB设备为主模式,机器就知道自己要做为一个主机,用来连接外部的从机设备(U盘)。
34.1.5 RK3568 USB接口简介
ATK-DLRK3568提供2个原生的USB2.0 HOST接口,这两个USB接口都是支持高速模式,也就是480Mbit/S。提供了2个USB3.0 HOST接口(OTG3.0+HOST3.0),其中一路用于连接5G模组,另外一路作为板载的USB 3.0接口。
RK3568内部集成了三个跟USB相关的控制器名字分别为:USB OTG控制器、USB Host控制器和USB HS PHY控制器,提供一个简称方便书写:OTG、USBH和PHY。接着我们分析一下这三个控制器是如何工作的。
1、PHY控制器
PHY(英语:Port Physical Layer),中文可称之为端口物理层,是一个对OSI模型物理层的共同简称。RK3568有两个USB 2.0 PHY,USB 3.0 OTG控制器和USB 3.0 Host_1的控制器分别使用USB 2.0 Comb PHY_0的port0和port1;USB 2.0 Host_2控制器和USB 2.0 Host_3控制器分别使用USB 2.0 Comb PHY_1的port0和port1。
2、OTG控制器
OTG控制器有两个模式:正常模式(normal mode)和低功耗模式(low power mode)。 OTG控制器都可以运行在高速模式(HS 480Mbps)、全速模式(LS 12Mbps)和低速模式(1.5Mbps)。正常模式下每个OTG控制器都可以工作在主机(HOST)或从机(DEVICE)模式下,每个USB控制器都有其对应的接口。低功耗模式顾名思义就是为了节省功耗,USB2.0协议中要求,设备在上行端口检测到空闲状态以后就可以进入挂起状态。在从机(DEVICE)模式下,端口停止活动3ms以后OTG控制器内核进入挂起状态。在主机(HOST)模式下,OTG控制器内核不会自动进入挂起状态,但是可以通过软件设置。不管是本地还是远端的USB主从机都可以通过产生唤醒序列来重新开始USB通信。
3、USBH控制器
USBH控制器这是一个主机控制器,此控制器由EHCI控制器和OHCI控制器组成。USBH控制器只能做主机模式。
这里我们简单提一下OHCI、UHCI、EHCI和xHCI,这三个是用来描述USB控制器规格的,区别如下:
OHCI:全称为Open Host Controller Interface,这是一种USB控制器标准,厂商在设计USB控制器的时候需要遵循此标准,用于USB1.1标准。OHCI不仅仅用于USB,也支持一些其他的接口,比如苹果的Firewire等,OHCI由于硬件比较难,所以软件要求就降低了,软件相对来说比较简单。OHCI主要用于非X86的USB,比如扩展卡、嵌入式USB控制器。
UHCI:全称是Universal Host Controller Interface,UHCI是Inter主导的一个用于USB1.0/1.1的标准,与OHCI不兼容。与OHCI相比UHCI硬件要求低,但是软件要求相应就高了,因此硬件成本上就比较低。
EHCI:全称是Enhanced Host Controller Interface,是Inter主导的一个用于USB2.0的USB控制器标准。EHCI仅提供USB2.0的高速功能,至于全速和低速功能就由OHCI或UHCI来提供。
xHCI:全称是eXtensible Host Controller Interface,是目前最流行的USB3.0控制器标准,在速度、能效和虚拟化等方面比前三个都有较大的提高。xHCI支持所有速度种类的USB设备,xHCI出现的目的就是为了替换前面三个。
通俗来讲,OHCI就是FS模式,也就是低速模式,EHCI是HS模式,也就是高速模式。关于RK3568的USB控制器就简单的讲解到这里。。
34.1.6 USB Typec电气属性
1、USB TypeC接口引脚定义
目前USB TypeC接口非常普及,大家一般都有很多USB TypeC线材,相反,Mini USB或者Micro USB的线材基本上都没有了,所以为了方便大家使用,正点原子ATK-DLRK3568开发板上的USB接口采用了TypeC。
接下来就看一下USB TypeC接口(根据USB协议,也叫做USB3.1接口)电气属性,由于TypeC功能比较复杂,比如支持PD充电、显示、音频等,这里我们只讲解一下TypeC的数据通信部分。首先来看一下TypeC的接口定义,这里我们只看母头的引脚定义,如图34.1.6.1所示:
在这里插入图片描述

图34.1.6.1 标准USB TypeC母头引脚
从图34.1.6.1中可以看出,标准的USB TypeC有24根线,分为上下两部分,明显比前面讲的Mini USB要多不少引脚,这些引脚按照功能分类:
A1、A12、B1、B12:这4个引脚为GND。
A2、A3、B2、B3:这4个引脚是USB3.1特有的,为超高速差分发送信号线,这个是USB2.0和USB1.0所没有的。
A10、A11、B10、B11:这4个引脚也是USB3.1特有的,为超高速差分接收信号线,这个也是USB2.0和USB1.0所没有的。
A4、A9、B4、B9:VBUS引脚,这个USB2.0也有。
A5、B5:CC1和CC2这2个引脚为USB3.1特有的配置通道引脚。Type-C的插座中有两个CC脚,USB通信中的角色检测都是通过CC脚进行的。对于TypeC插头或者线缆正常只有一个CC引脚,两个端口连接在一起之后,只存在一个CC引脚连接,通过检测哪一个CC有连接,就可以判断连接的方向。如果USB线缆中有需供电的器件,其中一个CC引脚将作为VCONN供电。
对于本章教程来说,我们主要使用CC1和CC2引脚来实现USB OTG功能,稍后会讲解USB3.1下的设备类型。
A8、B8:SBU1和SBU2引脚,这两个并不是USB信号,而是用作其他功能的,比如TypeC作为DP接口使用的时候SBU用作音频传输通道。
A6、A7、B6、B7:这4个引脚大家应该就很熟悉了,就是USB2.0的D+和D-,因为USB3.1要兼容USB2.0和USB1.0接口,所以必须要有D+和D-。
大家仔细观察图34.1.6.1会发现,上下两排引脚是对应的,比如(A1,B1)、(A2、B2)等,一直到(A12,B12)。这是因为USB3.1要有能够正反插,因此上下两排引脚肯定要是一样的,这样才能在正反插的时候都能正常工作。
图40.1.6.1是TypeC接口标准的24P母头引脚,大家在进行TypeC母头选型的时候会发现也有16P甚至6P封装的接口。比如正点原子ATK-DLRK3568开发板上使用的就是16P的TypeC母座,16P的母座引脚结构如图34.1.6.2所示:
在这里插入图片描述

图34.1.6.2 16P TypeC引脚示意图
图34.1.6.2就是16P的TypeC引脚示意图,从左到右依次是1~16脚,注意1,2脚、3,4脚、13,14脚和15,16脚离得很近,看起来像是一个引脚,其实他们是两个引脚。有些封装为了方便,会将这些离的近的引脚画在一起,看起来就是12P的座子,这里一定要注意这点,尤其是自己做板子,画TypeC的时候。16P对应的引脚功能如表34.1.6.3所示:
在这里插入图片描述

可以看出,相比于标准24P引脚定义,16P的母座少了8根USB3.1高速差分收发数据线,这个在本章是用不到的。
2、USB TypeC的Data Role
USB2.0根据数据传输方向定义了HOST/Device/OTG这三种类型的设备角色,其中OTG既可以做HOST也可以做Device,USB2.0的OTG设备接口通过ID线来区分HOST还是Device。在TypeC领域,也有类似的概念,但是名字变了:
①、DFP
DFP全称是Downstream Facing Port,也就是下行端口,可以理解为HOST,DFP提供VBUS、VCONN,可以接收数据。
②、UFP
UFP全称是Upstream Facing Port,也就是上行端口,可以理解为Device,UFP从VBUS中取电源,UFP设备也可以传输数据,比如U盘,键盘鼠标等。
③、DRP
DRP全称是Dual Role Port,也就是双角色端口,可以理解为OTG,DRP既可以做DFP,也可以做UFP。也可以在DFP和UFP之间进行动态切换。这个切换过程就用到了CC引脚,具体识别过程比较复杂,这里就不讲解了。如果要在USB TypeC接口上实现DRP功能,那么就需要使用到外置的TypeC芯片!
34.2 硬件原理图分析
正点原子的ATK-DLRK3568开发板USB部分原理图可以分为两部分:USB HUB以及USB OTG。开发板使用了TypeC接口,但是本质上还是USB2.0协议,所以大家不要看到TypeC就以为支持USB3.0!
34.2.1 USB HUB原理图分析
首先来看一下USB HUB原理图,如图34.2.1.1所示:
在这里插入图片描述

图34.2.1.1 USB HUB原理图
图34.2.1.1中的是两个USB2.0 HOST接口,RK3568原生支持2路USB2.0 HOST外设,速度会比USB3.0慢,可以用来连接U盘、鼠标等设备。
34.2.2 USB OTG原理图分析
ATKDL-RK3568开发板上有一路USB OTG接口,使用的是RK3568的USB3_OTG0接口。此路USB OTG既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG功能,原理图如图34.2.2.1所示:
在这里插入图片描述

图34.2.2.1 USB OTG原理图
图中USB2就是开发板的USB OTG接口,为USB Type-C类型。USB OTG 通过 ID 线来完成主从机切换,这里就涉及到硬件对 USB ID 线的处理,图 中 Type-C 的 CC1 和 CC2 引脚就是完成传统的 USB ID 线功能的。我们简单分析一下主从机切换原理。
从机(DEVICE)模式:使用配套赠送的USB Type-C连接线连接OTG接口到PC(电脑),USB Type-C线会将CC1和CC2拉高,因此USB3_OTG0_ID线为高,表示OTG工作在从机模式。烧录和ADB功能都是使用这种模式的。
主机(HOST)模式:如果要使用OTG的HOST功能,那么必须要使用到Type-c OTG先。Type-C OTG线会将CC1和CC2拉低,因此USB3_OTG0_ID线为低,表示OTG工作在主机模式。USB3_OTG0_ID默认为低电平。
图中J10就是开发板的USB 3.0接口,USB3.0 可以当主机或者从机,用作烧录或者外接 U 盘。
注意:这两个接口同时只能接一个!因为他们是同一个USB3.0 OTG口。
34.3 USB协议简析
USB协议中有很多的基础概念,本节就来看一下这些概念。
34.3.1 USB描述符
顾名思义,USB描述符就是用来描述USB信息的,描述符就是一串按照一定规则构建的字符串,USB设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如表34.3.1.1所示:
描述符类型 名字 值
Device Descriptor 设备描述符 1
Configuration Descriptor 配置描述符 2
String Descriptor 字符串描述符 3
Interface Descriptor 接口字符串 4
Endpoint Descriptor 端点描述符 5
表34.3.1.1 USB设备常用描述符
我们依次来看一下表34.3.1.1中这5个描述符的含义:
1、设备描述符
设备描述符用于描述USB设备的一般信息,USB设备只有一个设备描述符。设备描述符里面记录了设备的USB版本号、设备类型、VID(厂商ID)、PID(产品ID)、设备序列号等。设备描述符结构如表34.3.1.2所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 数字 此设备描述符长度,18个字节
1 bDescriptorType 1 常量 描述符类型,为0X01
2 bcdUSB 2 BCD码 USB版本号
4 bDeviceClass 1 类 设备类
5 bDeviceSubClass 1 子类 设备子类
6 bDevicePortocol 1 协议 设备协议
7 bMaxPacketSize0 1 数字 端点0的最大包长度
8 idVendor 2 ID 厂商ID
10 idProduct 2 ID 产品ID
12 bcdDevice 2 BCD码 设备版本号
14 iManufacturer 1 索引 厂商信息字符串描述符索引值
15 iProduct 1 索引 产品信息字符串描述符索引值
16 iSerialNumber 1 索引 产品序列号字符串描述符索引值
17 bNumConfigurations 1 索引 可能的配置描述符数目
表34.3.1.2 设备描述符结构
2、配置描述符
设备描述符的bNumConfigurations域定义了一个USB设备的配置描述符数量,一个USB设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、供电信息等,配置描述符结构如表34.3.1.3所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 数字 此配置描述符长度,9个字节
1 bDescriptorType 1 常量 配置描述符类型,为0X02
2 wTotalLength 2 数字 整个配置信息总长度(包括配置、接口、端点、设备类和厂家定义的描述符)
4 bNumInterfaces 1 数字 此配置所支持的接口数
5 bConfigurationValue 1 数字 该配置的值,一个设备支持多种配置,通过配置值来区分不同的配置。
6 bConfiguration 1 数字 描述此配置的字符串描述索引
7 bmAttributes 1 数字 该设备的属性:
D7:保留
D6:自给电源
D5:远程唤醒
D4:0:保留
8 bMaxPower 1 数字 此配置下所需的总线电流(单位2mA)
表34.3.1.3 配置描述符结构
3、字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都必须为0,字符串描述符结构如表34.3.1.4所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 N+2 此字符串描述符长度
1 bDescriptorType 1 常量 字符串描述符类型,为0X03
2 wLANGID[0] 2 数字 语言标识码0
… … 2 数字 …
N wLANGID[x] 2 数字 语言标识码x
表34.3.1.4 字符串描述符结构
wLANGID[0]~wLANGID[x]指明了设备支持的语言,具体含义要查阅文档《USB_LANGIDs.pdf》,此文档已经放到了开发板光盘中,路径为:4、参考资料-> USB_LANGIDs.pdf。
主机会再次根据自己所需的语言向设备请求字符串描述符,这次主机会指明要得到的字符串索引值和语言。设备返回Unicode编码的字符串描述符,结构如表34.3.1.5所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 N+2 此字符串描述符长度
1 bDescriptorType 1 常量 字符串描述符类型,为0X03
2 bString N 数字 Unicode编码的字符串
表34.3.1.5 Unicode编码的字符串描述符结构
4、接口描述符
配置描述符中指定了该配置下的接口数量,配置可以提供一个或多个接口,接口描述符用于描述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口描述符结构如表34.3.1.6所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 数字 此接口描述符长度,9个字节
1 bDescriptorType 1 常量 描述符类型,为0X04
2 bInterfaceNumber 1 数字 当前接口编号,从0开始
3 bAlternateSetting 1 数字 当前接口备用编号
4 bNumEndpoints 1 数字 当前接口的端点数量
5 bInterfaceClass 1 类 当前接口所属的类
6 bInterfaceSubClass 1 子类 当前接口所属的子类
7 bInterfaceProtocol 1 协议 当前接口所使用的协议
8 iInterface 1 索引 当前接口字符串的索引值
表34.3.1.6接口描述符结构
5、端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端点0是双向端口,其他的端口都是单向的。端点描述符描述了树传输类型、方向、数据包大小、端点号等信息,端点描述符结构如表34.3.1.7所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 数字 此端点描述符长度,7个字节
1 bDescriptorType 1 常量 描述符类型,为0X05
2 bEndpointAddress 1 数字 端点地址和方向:
bit3:0:端点号
bit6:4:保留,为零。
bit7:方向,0输出端点(主机到设备),1输入端点(设备到主机)
3 bmAttributes 1 数字 端点属性,bit1:0表示传输类型:
00:控制传输
01:同步传输
10:批量传输
11:中断传输
其他位保留
4 wMaxPacketSize 2 数字 端点能发送或接收的最大数据包长度
6 bInterval 1 子类 端点数据传输中周期时间间隙值,此域对于批量传输和控制传输无效,同步传输的话此域必须为1ms,中断传输此域可以设置1ms~255ms。
表34.3.1.7端点描述符结构
34.3.2 USB数据包类型
USB是串行通信,需要一位一位的去传输数据, USB传输的时候先将原始数据进行打包,所以USB中传输的基本单元就是数据包。根据用途的不同,USB协议定义了4种不同的包结构:令牌(Token)包、数据(Data)包、握手(Handshake)包和特殊(Special)包。这四种包通过包标识符PID来区分,PID共有8位,USB协议使用低4位PID3PID0,另外的高四位PID7PID4是PID3PID0的取反,传输顺序是PID0、PID1、PID2、PID3…PID7。令牌包的PID10为01,数据包的PID10为11,握手包的PID10为10,特殊包的PID1~0为00。每种类型的包又有多种具体的包,如表34.3.2.1所示:
PID类型 PID名字 PID<3:0> 描述
令牌包 OUT 0001 主机向从机通知将要输出数据
IN 1001 主机向从机通知将要输入数据
SOF 0101 帧起始包
SETUP 1101 开始一个控制传输
数据包 DATA0 0011 偶数据包
DATA1 1011 奇数据包
DATA2 0111 适用于高速和等时传输的数据包
MDATA 1111
握手包 ACK 0010 确认包,传输正确
NAK 1010 不确认包,传输错误
STALL 1110 设备部支持该请求,或者端点挂起
NYET 0110 接收未响应,未准备好
特殊包 PRE 1100 前导,令牌包
ERR 1100 错误,握手包
SPLIT 1000 分裂传输(Split),令牌包
PING 0100 PING测试,令牌包
Reserved 0000 保留
表34.3.2.1 数据包结构
一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、包目标端点(ENDP)、数据、帧索引、CRC等,这个要具体数据包具体分析。接下来简单看一下这些数据包的结构。
1、令牌包
令牌包结构如图50.3.2.1所示:
在这里插入图片描述

图34.3.2.1 SETUP令牌包
图50.3.2.1是一个SETUP令牌包结构,首先是SYNC同步域,包同步域为00000001,也就是连续7个0,后面跟一个1,如果是高速设备的话就是31个0后面跟一个1。紧跟着是PID,这里是SETUP包,为0XB4,大家可能会好奇为什么不是0X2D(00101101),0XB4的原因如下:
①、SETUP包的PID3PID0为1101,因此对应的PID7PID4就是0010。
②、PID传输顺序为PID0、PID1、PID2…PID7,因此按照传输顺序排列的话此处的PID就是10110100=0XB4,并不是0X2D。
PID后面跟着地址域(ADDR)和端点域(ENDP),为目标设备的地址和端点号。CRC5域是5位CRC值,是ADDR和ENDP这两个域的校验值。最后就是包结束域(EOP),标记本数据包结束。其他令牌包的结构和SETUP基本类似,只是SOF令包中间没有ADDR和ENDP这两个域,而是只有一个11位的帧号域。
2、数据包
数据包结构如图34.3.2.2所示:
在这里插入图片描述

图34.3.3.2 数据包
数据包比较简单,同样的,数据包从SYNC同步域开始,然后紧跟着是PID,这里就是DATA0,PID值为0XC3。接下来就是具体的数据,数据完了以后就是16位的CRC校验值,最后是EOP。
3、握手包
握手包结构如图34.3.2.3所示:
在这里插入图片描述

图34.3.3.3 ACK握手包
图34.3.2.3是ACK握手包,很简单,首先是SYNC同步域,然后就是ACK包的PID,为0X4B,最后就是EOP。其他的NAK、STALL、NYET和ERR握手包结构都是一样的,只是其中的PID不同而已。
34.3.3 USB传输类型
在端点描述符中bmAttributes指定了端点的传输类型,一共有4种,本节我们来看一下这四种传输类型的区别。
1、控制传输
控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA)和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用SETUP令牌包,SETUP使用DATA0包。数据阶段是0个、1个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务必须是同一个方向的,比如都为IN或都为OUT。数据阶段的第一个数据包必须是DATA1,每次正确传输以后就在DATA0和DATA1之间进行切换。数据阶段完成以后就是状态阶段,状态阶段的传输方向要和数据阶段相反,比如数据阶段为IN的话状态阶段就要为OUT,状态阶段使用DATA1包。比如一个读控制传输格式如图34.3.3.1所示:
在这里插入图片描述

图34.3.3.1 读控制传输阶段
2、同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要求很高,但是不要求数据100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使数据传输出错了也不会重传。
3、批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大块数据的,这些数据对实时性没有要求,比如MSD类设备(存储设备),U盘之类的。批量传输分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段就是IN令牌包,如果是批量写那么第一阶段就是OUT令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出OUT令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果OUT令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备就会向主机返回一个ACK握手信号。
批量读的过程刚好相反:
①、主机发出IN令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就进入到数据接收状态,等待设备返回数据。
②、如果IN令牌包正确的话,设备就会将一个DATA包放到总线上发送给主机。主机收到这个DATA包以后就会向设备发送一个ACK握手信号。
4、中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传输。
34.3.4 USB枚举
当USB设备与USB主机连接以后主机就会对USB设备进行枚举,通过枚举来获取设备的描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。USB枚举过程如下:
①、第一回合,当USB主机检测到USB设备插入以后机会发出总线复位信号来复位设备。USB设备复位完成以后地址为0,主机向地址0的端点0发送数据,请求设备的描述符。设备得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。
②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址0的端点0发送设置地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过程,等待主机请求状态返回,收到以后设备就会向主机发送一个0字节状态数据包,表明设备已经设置好地址了,主机收到这个0字节状态数据包以后会返回一个确认包(ACK)。设备收到主机发送的ACK包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。
③、第三回合,主机向新的设备地址端点0发送请求设备描述符数据包,这一次主机要获取整个设备描述符,一共是18个字节。
④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。
34.4 RK3568 USB HOST驱动编写
接下来我们就开始编写USB HOST的驱动。USB子系统是一个标准和复杂的接口,所以驱动基本我们不用写,都是内核里有现成的,我们只需要在设备树提供对应的设备节点即可。
在34.1.5小节里告诉各位了USBH是只能主机模式,要编写USB HOST就用USBH控制器即可。瑞芯微官方已经配置好了USBH的节点信息,所以我们直接参考此节点即可,这样开发板就能够使用USB Host模式。
1、USBH控制器节点信息
打开“rk3568.dtsi”文件,找到USBH控制器的节点信息,如下示例代码所示:
示例代码34.4.1 USBH下两个控制器节点信息

1   usb_host0_ehci: usb@fd800000 {
2       compatible = "generic-ehci";
3       reg = <0x0 0xfd800000 0x0 0x40000>;
4       interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
5       clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>,
6            <&cru PCLK_USB>, <&usb2phy1>;
7       clock-names = "usbhost", "arbiter", "pclk", "utmi";
8       phys = <&u2phy1_otg>;
9       phy-names = "usb2-phy";
10      status = "disabled";
11  };
12
13  usb_host0_ohci: usb@fd840000 {
14      compatible = "generic-ohci";
15      reg = <0x0 0xfd840000 0x0 0x40000>;
16      interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
17      clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>,
18           <&cru PCLK_USB>, <&usb2phy1>;
19      clock-names = "usbhost", "arbiter", "pclk", "utmi";
20      phys = <&u2phy1_otg>;
21      phy-names = "usb2-phy";
22      status = "disabled";
23  };
24
25  usb_host1_ehci: usb@fd880000 {
26      compatible = "generic-ehci";
27      reg = <0x0 0xfd880000 0x0 0x40000>;
28      interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
29      clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>,
30           <&cru PCLK_USB>, <&usb2phy1>;
31      clock-names = "usbhost", "arbiter", "pclk", "utmi";
32      phys = <&u2phy1_host>;
33      phy-names = "usb2-phy";
34      status = "disabled";
35  };
36
37  usb_host1_ohci: usb@fd8c0000 {
38      compatible = "generic-ohci";
39      reg = <0x0 0xfd8c0000 0x0 0x40000>;
40      interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
41      clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>,
42           <&cru PCLK_USB>, <&usb2phy1>;
43      clock-names = "usbhost", "arbiter", "pclk", "utmi";
44      phys = <&u2phy1_host>;
45      phy-names = "usb2-phy";
46      status = "disabled";
47  };
48
49  usbdrd30: usbdrd {
50      compatible = "rockchip,rk3568-dwc3", "rockchip,rk3399-dwc3";
51      clocks = <&cru CLK_USB3OTG0_REF>, <&cru CLK_USB3OTG0_SUSPEND>,
52           <&cru ACLK_USB3OTG0>, <&cru PCLK_PIPE>;
53      clock-names = "ref_clk", "suspend_clk",
54                "bus_clk", "pipe_clk";
55      #address-cells = <2>;
56      #size-cells = <2>;
57      ranges;
58      status = "disabled";
59
60      usbdrd_dwc3: dwc3@fcc00000 {
61          compatible = "snps,dwc3";
62          reg = <0x0 0xfcc00000 0x0 0x400000>;
63          interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
64          dr_mode = "otg";
65          phys = <&u2phy0_otg>, <&combphy0_us PHY_TYPE_USB3>;
66          phy-names = "usb2-phy", "usb3-phy";
67          phy_type = "utmi_wide";
68          power-domains = <&power RK3568_PD_PIPE>;
69          resets = <&cru SRST_USB3OTG0>;
70          reset-names = "usb3-otg";
71          snps,dis_enblslpm_quirk;
72          snps,dis-u1u2-quirk;
73          snps,dis-u2-freeclk-exists-quirk;
74          snps,dis-del-phy-power-chg-quirk;
75          snps,dis-tx-ipgap-linecheck-quirk;
76          snps,dis_rxdet_inp3_quirk;
77          snps,xhci-trb-ent-quirk;
78          status = "disabled";
79      };
80  };

使用 USB2.0 就要配置 usb_hostX_ehci(X=0,1)节点,使用 USB1.1 就要配置 usb_hostX_ohci (X=0,1)节点。使用 USB3.0 就要配置 usbdrd30节点。这两个节点的信息我们是不需要修改的,这是RK3568一些通用配置信息。
2、PHY控制器
我们先去了解一下PHY控制器,一些通用配置,打开“rk3568.dtsi”文件,找的如下内容所示:
示例代码34.4.2 phyc控制器节点信息

1   usb2phy1: usb2-phy@fe8b0000 {
2       compatible = "rockchip,rk3568-usb2phy";
3       reg = <0x0 0xfe8b0000 0x0 0x10000>;
4       interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
5       clocks = <&pmucru CLK_USBPHY1_REF>;
6       clock-names = "phyclk";
7       #clock-cells = <0>;
8       rockchip,usbgrf = <&usb2phy1_grf>;
9       status = "disabled";
10
11      u2phy1_host: host-port {
12          #phy-cells = <0>;
13          status = "disabled";
14      };
15
16      u2phy1_otg: otg-port {
17          #phy-cells = <0>;
18          status = "disabled";
19      };
20  };
21  usb2phy0: usb2-phy@fe8a0000 {
22      compatible = "rockchip,rk3568-usb2phy";
23      reg = <0x0 0xfe8a0000 0x0 0x10000>;
24      interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>;
25      clocks = <&pmucru CLK_USBPHY0_REF>;    
26      clock-names = "phyclk";
27      #clock-cells = <0>;
28      assigned-clocks = <&cru USB480M>;
29      assigned-clock-parents = <&usb2phy0>;
30      clock-output-names = "usb480m_phy";
31      rockchip,usbgrf = <&usb2phy0_grf>;
32      status = "disabled";
33
34      u2phy0_host: host-port {
35          #phy-cells = <0>;
36          status = "disabled";
37      };
38
39      u2phy0_otg: otg-port {
40          #phy-cells = <0>;
41          status = "disabled";
42      };
43  };
示例代码34.4.2中usb2phy1和usb2phy0就是前面所说的RK3568的两个USB2.0 PHY。每个PHY控制器有两个端口,usb2phy1节点里的两个子节点usbphy1_host和usbphy1_otg分别对应usb_host1_X(X=ehci,ohci)和usb_host0_X(X=ehci,ohci)节点,同理usb2phy0节点里的子节点usbphy0_otg对应的是usbdrd30节点。
3、配置和使能PHY子节点
最后我们在rk3568-atk-evb1-ddr4-v10.dtsi和rk3568-evb.dtsi文件中使能并配置指定的PHY端口。

示例代码34.4.3 追加PHY节点内容

1 &u2phy0_host {
2   phy-supply = <&vcc5v0_host>;
3   status = "okay";
4 };
5 
6 &u2phy0_otg {
7   vbus-supply = <&vcc5v0_otg>;
8   status = "okay";
9 };
10
11&u2phy1_host {
12  phy-supply = <&vcc5v0_host>;
13  status = "okay";
14};
15
16&u2phy1_otg {
17  phy-supply = <&vcc5v0_host>;
18  status = "okay";
19};
	从示例代码34.4.3中把status的属性改为“okay”,使能相关的节点。
示例代码34.4.4 追加PHY节点内容
1 &u2phy0_host {
2   phy-supply = <&vcc5v0_usb>;
3 };
4 
5 &u2phy1_host {
6   phy-supply = <&vcc5v0_usb>;
7 };
8 
9 &u2phy1_otg {
10  phy-supply = <&vcc5v0_usb>;
11};
从示例代码34.4.4中给相关节点的phy-supply属性添加一个电源管理属性。

34.5 Linux内核自带HOST实验
34.5.1 USB鼠标键盘测试
首先做一下USB HOST试验,也就是正点原子的STM32MP1开发板做USB主机,然后外接USB设备,比如USB鼠标键盘、USB转TTL串口线、U盘等设备。Linux内核已经集成了大量的USB设备驱动,尤其是我们常见的USB鼠标键盘、U盘等,本节我们就来学习一下如何使能Linux内核常见的USB设备驱动。
1、USB鼠标键盘驱动使能
注意,瑞芯微官方的Linux内核默认已经使能了USB键盘鼠标驱动!
USB鼠标键盘属于HID设备,内核已经集成了相应的驱动,瑞芯微官方提供的linux内核默认已经使能了USB鼠标键盘驱动,但是我们还要学习一下如何手动使能这些驱动。输入“make ARCH=arm64 menuconfig”,打开linux内核配置界面,首先打开HID驱动,按照如下路径到相应的配置项目:

-> Device Drivers 
	-> HID support                                                                                 
   		-> HID bus support 
			->  <*>  Generic HID driver		//使能通用HID驱动
使能以后如图34.5.1.1所示:

在这里插入图片描述

图34.5.1.1 使能HID驱动
接下来需要使能USB键盘和鼠标驱动,配置路径如下:
-> Device Drivers
-> HID support
-> USB HID support
-> <*> USB HID transport layer //USB键盘鼠标等HID设备驱动
使能以后如图34.5.1.2所示:
在这里插入图片描述

图34.5.1.2 使能USB鼠标键盘驱动
大家可以将光标放到图34.5.1.2中“USB HID Transport layer”这一行,然后按下“?”键打开对应的帮助信息就可以看到对于这个配置项的描述,简单总结一下:
此选项对应配置项就是CONFIG_USB_HID,也就是USB接口的HID设备。如果要使用USB接口的keyboards(键盘)、mice(鼠标)、joysticks(摇杆)、graphic tablets(绘图板)等其他的HID设备,那么就需要选中“USB HID Transport layer”。但是要注意一点,此驱动和HIDBP(Boot Protocol)键盘、鼠标的驱动不能一起使用!所以大家要是在网上查阅linux内核USB键盘鼠标驱动的时候,发现推荐使用“USB HIDBP Keyboard (simple Boot) support”和“USB HIDBP Mouse (simple Boot) support”这两个配置项的时候也不要觉得教程这里写错了。
2、测试USB鼠标和键盘
启动开发板,启动以后插入USB鼠标,会有相关的提示信息,表示系统检测到了鼠标,如果成功驱动的话就会在/dev/input目录下生成一个名为eventX(X=0,1,2,3…)的文件,这个就是我们前面讲的输入子系统,鼠标和键盘都是作为输入子系统设备的。笔者这里对应的就是/dev/input/event8这个设备,使用如下命令查看鼠标的原始输入值,结果如图34.5.1.3所示:
在这里插入图片描述

图34.5.1.3 鼠标原始输入值
图34.5.1.3就是鼠标作为输入子系统设备的原始输入值,这里就不去分析了,在正点原子出厂系统Qt界面可以使用过鼠标操作。
34.5.2 U盘实验
注意,瑞芯微官方的Linux内核默认已经使能了U盘!
正点原子提供的Linux内核默认也已经使能了U盘驱动,因此我们可以直接插上去使用。但是我们还是需要学习一下如何手动配置Linux内核,使能U盘驱动。
1、使能U盘驱动
U盘使用SCSI协议,因此要先使能Linux内核中的SCSI协议,配置路径如下:
-> Device Drivers
-> SCSI device support
-> <*> SCSI disk support //选中此选项
结果如图34.5.2.1所示:
在这里插入图片描述

图34.5.2.1 使能SCSI
我们还需要使能USB Mass Storage,也就是USB接口的大容量存储设备,配置路径如下:
-> Device Drivers
-> USB support
-> USB Gadget Support
-> USB Gadget functions configurable through configfs
-> [*] Mass storage //选中
结果如图34.5.2.2所示:
在这里插入图片描述

图34.5.2.2 使能USB大容量存储设备
2、U盘测试
准备好一个U盘,注意U盘要为FAT32格式的!NTFS和exFAT由于版权问题所以在Linux下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。准备好以后将U盘插入到开发板USB HUB扩展出来的HOST接口上,此时会输出如图34.5.2.3所示信息:
在这里插入图片描述

图34.5.2.3 U盘log信息
从图34.5.2.3可以看出,系统检测到U盘插入,大小为32GB对应的设备文件为/dev/sda和/dev/sda1,大家可以查看一下/dev目录下有没有sda和sda1这两个文件。/dev/sda是整个U盘,/dev/sda1是U盘的第一个分区,我们一般使用U盘的时候都是只有一个分区。要想访问U盘我们需要先对U盘进行挂载,理论上挂载到任意一个目录下都可以,这里我创建一个/mnt/usb_disk目录,然后将U盘挂载到/mnt/usb_disk目录下,命令如下:
mkdir /mnt/usb_disk -p //创建目录
mount /dev/sda1 /mnt/usb_disk/ -t vfat -o iocharset=utf8 //挂载
-t指定挂载所使用的文件系统类型,这里设置为vfat,也就是FAT文件系统,“-o iocharset”设置硬盘编码格式为utf8,否则的话U盘里面的中文会显示乱码!
挂载成功以后进入到/mnt/usb_disk目录下,输入ls命令查看U盘文件,如图34.5.2.4所示:
在这里插入图片描述

图34.5.2.4 U盘文件
至此U盘就能正常读写操作了,直接对/mnt/usb_disk目录进行操作就行了。如果要拔出U盘要执行一个sync命令进行同步,然后在使用unmount进行U盘卸载,命令如下所示:
sync //同步
cd / //如果处于/mnt/usb_disk目录的话先退出来,否则卸载的时候提示设
//备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载
34.6 USB OTG实验
从34.2.2小节我们可以得知USB Type-C和USB3.0接口是共用了一个USB3.0 OTG口,这两个接口同时只能接一个!硬件原理图可以看出USB3.0 OTG口默认是低电平,因此是主机模式(HOST)。
34.6.1 OTG主机实验
启动开发板系统以后就可以正常使用 USB_OTG 接口,OTG 既可以做主机,也可以做从机,做主机的话测试方法和 34.5 小节一模一样,直接在正点原子的 USB_OTG 接口上使用 Typec OTG 线接入鼠标键盘、U 盘等设备。
34.6.2 OTG从机实验
OTG从机就是将开发板作为一个USB设备连接到其他的主机上,ADB功能就是其中之一,这里我们来做模拟U盘实验。
1、模拟U盘实验
正点原子出厂内核默认是没有这个功能的,需要编译成模块,首先需要配置Linux,配置路径如下:
-> Device Drivers
-> USB support
-> USB Gadget Support
-> < > USB Gadget functions configurable through configfs //不要编译进内核
-> USB Gadget precomposed configurations ( [=m])
-> Mass Storage Gadget //大容量存储
如图34.6.2.1所示:
在这里插入图片描述

图34.6.2.1 USB Gadget中大容量存储设备驱动
这里我们需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好
以后重新编译一下内核,编译完成之后会得到 3 个.ko 内核模块文件,它们对应的路径为:

drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_mass_storage.ko
drivers/usb/gadget/legacy/g_mass_storage.ko

将上述三个.ko模块拷贝到开发板根文件系统/lib/modules/4.19.232目录下,拷贝完成以后使用新编译出来的boot.img替换开发板中的boot.img,在开发板上插入一个U盘,记住这个U盘对应的设备文件,比如我们这里是/dev/sda1,以后要将/dev/sda1挂载到PC上,也就是把/dev/sda1作为模拟U盘的存储区域。
依次加载libcomposite.ko、usb_f_mass_storage.ko和g_mass_storage.ko这三个驱动文件,顺序不能错了!命令如下:
insmod libcomposite.ko
insmod usb_f_mass_storage.ko
insmod g_mass_storage.ko file=/dev/sda1 removable=1
图34.6.2.2 操作步骤
加载g_mass_storage.ko的时候使用file参数指定使用的大容量存储设备,我这里使用U盘对应的/dev/sda1。如果加载成功的话电脑就会出现一个U盘,这个U盘就是我们开发板模拟的,如图50.6.4.3所示:
在这里插入图片描述

图34.6.2.3 电脑识别出来的U盘
我们可以直接在电脑上对这个U盘进行读写操作,实际上操作的就是插在开发板上的U盘。操作完成以后要退出的话执行如下命令:
rmmod g_mass_storage.ko
rmmod usb_f_mass_storage.ko
rmmod libcomposite.ko
注意!不要将开发板上的EMMC或者NAND作为模拟U盘的存储区域,因为linux下EMMC和NAND使用的文件系统一般都是EXT3/EXT4和UBIFS,这些文件系统类型和windows下的不兼容,如果挂载的话就会在windows下提示要你格式化U盘!
关于USB驱动就讲解到这里,本章并没有深入到USB驱动具体编写方式,只是对USB的协议做了简单的介绍,后面讲解了一下Linux内核自带的USB HOST和DEVICE驱动的使用,Linux内核已经集成了大量的USB设备驱动,至于其他特殊的就需要具体情况具体分析了。

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

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

相关文章

模块化 手写实现webpack

模块化 common.js 的导入导出方法&#xff1a; require \ export 和 module.exports export 和 module.export nodejs 内存1.4G -> 2.8G cjs ESModule 主要区别&#xff1a; require属于动态类型&#xff1a;加载执行 同步 esmodul是静态类型&#xff1a;引入时并不会真的去…

mysql事故复盘: 单行字节最大阈值65535字节(原创)

背景 记得还在银行做开发&#xff0c;投产上线时&#xff0c;项目发版前&#xff0c;要提DDL的sql工单&#xff0c;mysql加1个字段&#xff0c;因为这张表为下游数据入湖入仓用的&#xff0c;长度较大。在测试库加字段没问题&#xff0c;但生产库字段加不上。 先说结论 投产…

[前端]NVM管理器安装、nodejs、npm、yarn配置

NVM管理器安装、nodejs、npm、yarn配置 NVM管理器安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 nvm下载地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases 1.全部…

手撕sql面试题:根据分数进行排名,不使用窗口函数

分享一道面试题&#xff1a; 有一个分数表id 是该表的主键。该表的每一行都包含了一场考试的分数。Score 是一个有两位小数点的浮点值。 以下是表结构和数据&#xff1a; Create table Scores ( id int(11) NOT NULL AUTO_INCREMENT, score DECIMAL(3,2), PRIMARY KEY…

Linux shell编程学习笔记47:lsof命令

0 前言 今天国产电脑提示磁盘空间已耗尽&#xff0c;使用用df命令检查文件系统情况&#xff0c;发现/dev/sda2已使用100%。 Linux shell编程学习笔记39&#xff1a;df命令https://blog.csdn.net/Purpleendurer/article/details/135577571于是开始清理磁盘空间。 第一步是查看…

LeetCode_链表的回文结构

✨✨所属专栏&#xff1a;LeetCode刷题专栏✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 题目描述&#xff1a; 对于一个链表&#xff0c;请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法&#xff0c;判断其是否为回文结构。给定一个链表的头指针A&#xff0c;请返回一个bo…

【telnet 命令安装】centos8 linux下安装telnet命令

在CentOS 8上安装Telnet服务&#xff0c;您需要分别安装Telnet客户端和服务器端。以下是安装步骤的概述&#xff1a; 检查是否已安装Telnet&#xff1a; 您可以使用rpm命令来检查系统是否已经安装了Telnet客户端或服务器端。例如&#xff1a; rpm -qa | grep telnet-client rpm…

标准 数字化

政策法规&#xff1a; 标准化建设相关政策&#xff0c;包括《国家标准化发展纲要》&#xff0c;《重庆市的标准化条例》 标准数字化转型路线&#xff1a;标准数字化转型的白皮书、发展跟踪报告之类 相关文献&#xff1a;标准数字化转型发展现状与工作路线(大多是电力方面)、数…

uni-app canvas 签名

调用方法 import Signature from "/components/signature.vue" const base64Img ref() //监听getSignImg uni.$on(getSignImg, ({ base64, path }) > {base64Img.value base64//console.log(签名base64, path >, base64, path) //拿到的图片数据// 之后取消…

基于51单片机的矩阵按键扫描的proteus仿真

文章目录 一、按键按键按键消抖 二、独立按键仿真图仿真程序 三、矩阵按键仿真图仿真程序 四、总结 一、按键 按键 按键通常指的是电子设备上的一种输入装置&#xff0c;用于在按下时发送信号&#xff0c;以便设备执行相应的操作。按键可以分为独立按键和矩阵按键两种类型。 …

无人机GB42590接收端 +接收端模组,同时支持2.4G与5.8G双频

严格按照GB42590的协议开发的发射端&#xff0c;通过串口和模块通讯&#xff0c;默认波特率 921600。 http://www.doit.am/深圳四博智联科技有限公司https://shenzhendoit.taobao.com/category-1734422372.htm?spma1z10.1-c-s.0.0.560c74d77eT01G&searchy&catNameGB4…

Qt开发(二)打包发布

注意qt6生成的exe不能再win7&#xff08;包含win7&#xff09;以下运行 1、编译程序 编译程序不演示 2、找到exe文件 在这个路径下找到该exe文件 3、打包 新建一个文件夹 将exe放在该文件夹下除了exe开始这里面没有其他文件 找到安装目录下 在cmd中运行 把这个文件和编…

【Java】文件大小转换工具类(B,KB,MB,G,TB,PB)

说明 使用方法&#xff1a;FileMemoryUtil.prettyByteSize(35871)&#xff0c;参数为字节个数 返回结果&#xff1a;保留一位小数的自适应结果&#xff08;例如&#xff1a;4.1KB&#xff09;。可以留意在浏览器上下载的文件&#xff0c;会根据文件大小展示不同的单位&#xff…

Docker创建redis容器

Docker运行Redis 一&#xff1a;Docker安装Redis docker search redis二&#xff1a;Docker拉取镜像 下面两个命令看自己的需求 docker pull <镜像名称>&#xff1a;<版本号> #需要自己清楚自己需要什么般本的redisdocker pull redis #这个命令会自动下载最新…

C语言入门课程学习笔记3

C语言入门课程学习笔记3 第12课 - if 语句编程练习第13课 - switch 多分支选择语句第14课 - 程序中的循环结构第15课 - while 语句编程练习第16课 - do...while 与 for第17课 - break 与 continue 本文学习自狄泰软件学院 唐佐林老师的 C语言入门课程&#xff0c;图片全部来源于…

BUUCTF-Misc21

[GXYCTF2019]SXMgdGhpcyBiYXNlPw1 1.打开附件 是一个文本文档 里面有很多字符串 2.PuzzleSolver 用PuzzleSolver工具进行多组base64解码 3.得到flag 间谍启示录1 1.打开附件 是一个.iso文件 2.foremost 用foremost 分离文件 查看分离的文件 发现一个压缩包 3.运行 解压之…

Python | Leetcode Python题解之第47题全排列II

题目&#xff1a; 题解&#xff1a; class Solution:def permuteUnique(self, nums: List[int]) -> List[List[int]]:def dfs(x):if x len(nums) - 1:res.append(list(nums)) # 添加排列方案returndic set()for i in range(x, len(nums)):if nums[i] in dic: continue …

历史遗留问题-Oracle 19c RAC 安装时节点连接性问题

测试服务器的二节点数据库宕掉了&#xff0c;原因不明&#xff0c;需要产环境重新安装。我想上次在自己虚拟机安装实验过一次&#xff0c;应该一天能搞定&#xff0c;事实证明&#xff0c;你永远有学不完的bug&#xff01;&#xff01;&#xff01;&#xff01; 首先查看一下系…

算法基础:并查集详解

并查集 并查集&#xff0c;在一些有N个元素的集合应用问题中&#xff0c;我们通常是在开始时让每个元素构成一个单元素的集合&#xff0c;然后按一定顺序将属于同一组的元素所在的集合合并&#xff0c;其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学…

Web前端开发之HTML_2

HTML5简介与基础骨架标题标签标签之段落、换行、水平线标签之图片标签之超文本链接标签之文本列表标签之有序列表列表标签之无序列表 1. HTML5简介与基础骨架 1.1 HTML5简介 HTML5是用来描述网页的一种语言&#xff0c;被称为超文本标记语言。用HTML5编写的文件&#xff0c;后…
最新文章