1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
第六十七章 Linux USB驱动实验
USB是很常用的接口,目前大多数的设备都是USB接口的,比如鼠标、键盘、USB摄像头等,我们在实际开发中也常常遇到USB接口的设备,本章我们就来学习一下如何使能Linux内核自带的USB驱动。注意!本章并不讲解具体的USB开发,因为USB接口很复杂,不同的设备其协议也不同,这不是简简单单一章内容就能说完的,USB驱动开发本身就是一门复杂的课程。所以,如果想要学习如何编写代码开发一个全新的USB设备那就可以跳过本章。
67.1 USB接口简介
关于USB详细的协议内容请参考《USB2.0协议中文版.pdf》和《USB3.0协议中文版.pdf》,这两份文档已经放到了开发板光盘中,存放在“4、参考资料” 中。
67.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接口,比如STM32和ALPHA开发板所使用的I.MX6ULL。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插头和插座,如图67.1.1.1所示:
图67.1.1.1 USB A插头(左)和插座(右)
使用过JLINK调试器的朋友应该还见过USB B插头和插座,USB B插头和插座如图67.1.1.2所示:
图67.1.1.2 USB B插头(左)和插座(右)
USB插头在不断的缩小,由此产生了Mini USB接口,正点原子的I.MX6ULL-ALPHA开发板使用的就是Mini USB,Mini USB插头和插座如图67.1.1.3所示:
图67.1.1.3 Mini USB插头(左)和插座(右)
比Mini USB更小的就是Micro USB接口了,以前的智能手机基本都是Micro USB接口的,Micro USB插头和插座如图67.1.1.4所示:
图67.1.1.4 Micro USB插头(左)和插座(右)
现在最流行的就是USB Typec了,USB Typec插头和插座如图67.1.1.5所示:
图67.1.1.5 USB Typec插头(左)和插座(右)
67.1.2 USB电气特性
由于正点原子I.MX6U-ALPHA开发板使用的Mini USB接口,因此我们就以Mini USB为例讲解一下USB的基本电气属性。Mini USB线一般都是一头为USB A插头,一头为Mini USB插头。一共有四个触点,也就是4根线,这四根线的顺序如图67.1.2.1所示:
图67.1.2.1 USB A插头线序
如图67.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的电流。
67.1.3 USB拓扑结构
USB是主从结构的,也就是分为主机和从机两部分,一般主机叫做Host,从机叫做Device。主机就是提供USB A插座来连接外部的设备,比如电脑作为主机,对外提供USB A插座,我们可以通过USB线来连接一些USB设备,比如声卡、手机等。因此电脑带的USB A插座数量就决定了你能外接多少个USB设备,如果不够用的话我们可以购买USB集线器来扩展电脑的USB插口,USB集线器也叫做USB HUB,USB HUB如图67.1.3.1所示:
图67.1.3.1 USB HUB
图67.1.3.1是一个一拖四的USB HUB,也就是将一个USB接口扩展为4个。主机一般会带几个原生的USB主控制器,比如I.MX6ULL就有两个原生的USB主控制器,因此I.MX6ULL对外提供两个USB接口,这两个接口肯定不够用,正点原子的ALPHA开发板上有4个HOST接口,其中一路是USB1的OTG接口,其他的三路就是USB2通过USB HUB芯片扩展出来的,稍后我们会讲解其原理图。
虽然我们可以对原生的USB口数量进行扩展,但是我们不能对原生USB口的带宽进行扩展,比如I.MX6ULL的两个原生USB口都是USB2.0的,带宽最大为480Mbps,因此接到下面的所有USB设备总带宽最大为480Mbps。
USB只能主机与设备之间进行数据通信,USB主机与主机、设备与设备之间是不能通信的。因此两个正常通信的USB接口之间必定有一个主机,一个设备。为此使用了不同的插头和插座来区分主机与设备,比如主机提供USB A插座,从机提供Mini USB、Micro USB等插座。在一个USB系统中,仅有一个USB主机,但是可以有多个USB设备,包括USB功能设备和USB HUB,最多支持127个设备。一个USB主控制器支持128个地址,地址0是默认地址,只有在设备枚举的时候才会使用,地址0不会分配给任何一个设备。所以一个USB主控制器最多可以分配127个地址。整个USB的拓扑结构就是一个分层的金字塔形,如图67.1.3.2所示(参考自USB2.0协议中文版.pdf):
图67.1.3.2 USB金字塔拓扑
图67.1.3.2中可以看出从Root Hub开始,一共有7层,金字塔顶部是Root Hub,这个是USB控制器内部的。图中的Hub就是连接的USB集线器,Func就是具体的USB设备。
USB主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个USB设备一旦上电就会存在一个管道,也就是默认管道,USB主机通过管道来获取从机的描述符、配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个特定的端点。
67.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线的接口,比如正点原子的I.MX6ULL-ALPHA开发板的USB_OTG接口就是支持OTG模式的,USB_OTG连接到了I.MX6ULL的USB1接口上。如果只有一个Mini USB或者Micro USB接口的话如果要使用OTG的主机模式,那么就需要一根OTG线,Mini USB的OTG线如图67.1.4.1所示:
图67.1.4.1 Mini USB OTG线
可以看出,Mini USB OTG线一头是USB A插座,一头是Mini USB插头,将Mini USB插头插入机器的Mini USB口上,需要连接的USB设备插到另一端的USB A插座上,比如U盘啥的。USB OTG线会将ID线拉低,这样机器就知道自己要做为一个主机,用来连接外部的从机设备(U盘)。
67.1.5 I.MX6ULL USB接口简介
I.MX6ULL内部集成了两个独立的USB控制器,这两个USB控制器都支持OTG功能。I.MX6ULL内部USB控制器特性如下:
①、有两个USB2.0控制器内核分别为Core0和Core1,这两个Core分别连接到OTG1和OTG2。
②、两个USB2.0控制器都支持HS、FS和LS模式,不管是主机还是从机模式都支持HS/FS/LS,硬件支持OTG信号、会话请求协议和主机协商协议,支持8个双向端点。
③、支持低功耗模式,本地或远端可以唤醒。
④、每个控制器都有一个DMA。
每个USB控制器都有两个模式:正常模式(normal mode)和低功耗模式(low power mode)。每个USB OTG控制器都可以运行在高速模式(HS 480Mbps)、全速模式(LS 12Mbps)和低速模式(1.5Mbps)。正常模式下每个OTG控制器都可以工作在主机(HOST)或从机(DEVICE)模式下,每个USB控制器都有其对应的接口。低功耗模式顾名思义就是为了节省功耗,USB2.0协议中要求,设备在上行端口检测到空闲状态以后就可以进入挂起状态。在从机(DEVICE)模式下,端口停止活动3ms以后OTG控制器内核进入挂起状态。在主机(HOST)模式下,OTG控制器内核不会自动进入挂起状态,但是可以通过软件设置。不管是本地还是远端的USB主从机都可以通过产生唤醒序列来重新开始USB通信。
两个USB控制器都兼容EHCI,这里我们简单提一下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控制器标准。I.MX6ULL的两个USB控制器都是2.0的,因此兼容EHCI标准。EHCI仅提供USB2.0的高速功能,至于全速和低速功能就由OHCI或UHCI来提供。
xHCI:全称是eXtensible Host Controller Interface,是目前最流行的USB3.0控制器标准,在速度、能效和虚拟化等方面比前三个都有较大的提高。xHCI支持所有速度种类的USB设备,xHCI出现的目的就是为了替换前面三个。
关于I.MX6ULL的USB控制器就简单的讲解到这里,至于更详细的内容请参考I.MX6ULL参考手册中的“Chapter 56 Universal Serial Bus Controller(USB)”章节。
67.2 硬件原理图分析
正点原子的I.MX6ULL-ALPHA开发板USB部分原理图可以分为两部分:USB HUB以及USB OTG,我们依次来看一下这两部分的硬件原理图。
67.2.1 USB HUB原理图分析
首先来看一下USB HUB原理图,I.MX6ULL-ALPHA使用GL850G这个HUB芯片将I.MX6ULL的USB OTG2扩展成了4路HOST接口,其中一路供4G模块使用,因此就剩下了三个通用的USB A插座,原理图如图67.2.1.1所示:
图67.2.1.1 USB HUB原理图
图67.2.1.1中U10就是USB HUB芯片GL850G,GL850G是一款符合USB2.0标准的USB HUB芯片,支持一拖四扩展,可以将一路USB扩展为4路USB HOST接口。这里我们将I.MX6ULL的USB OTG2扩展出了4路USB HOST接口,分别为HUB_DP1/DM1、HUB_DP2/DM2、HUB_DP3/DM3和HUB_DP4/DM4。其中HUB_DP4/DM4用于4G模块,因此对外提供的只有三个USB HOST接口,这三个USB HOST接口如图67.2.1.3所示:
图67.2.1.3 扩展出来的USB HSOT
注意,使用GL850G扩展出来的4路USB接口只能用作HOST!
67.2.2 USB OTG原理图分析
I.MX6U-ALPHA开发板上还有一路USB OTG接口,使用I.MX6ULL的USB OTG1接口。此路USB OTG既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG功能,原理图如图67.2.2.1所示:
图67.2.2.1 USB OTG原理图
图67.2.2.1中左侧的为Mini USB插座,当OTG作为从机(DEVICE)的时候USB线接入此接口。右侧为USB A插座,当OTG作为主机的时候将USB设备插入到此接口中。前面我们讲了,如果只有一个Mini USB插座的话如果要学习OTG那么就需要再购买一个OTG线,这样不方便我们使用。为此正点原子在开发板上集成了一个USB HOST接口,这样在做OTG实验的时候就不需要再另外单独购买一根USB OTG线了。这里就涉及到硬件对USB ID线的处理,图67.2.2.1中R111和R31就是完成此功能的,我们分两部分来分析,既OTG分别工作在HOST和DEVICE的时候硬件工作方式:
从机(DEVICE)模式:图67.2.2.1中USB_OTG_VBUS是Mini USB的电源线,只有插入Mini USB线以后USB_OTG_VBUS才有效(5V)。插入Mini USB线就表示开发板此时要做从机(此时不考虑接OTG线的情况),USB_OTG_VBUS就是电脑供的5V电压,由于分压电阻R111和R31的作用,此时USB_OTG1_ID的电压就是4.5V左右,很明显这一个高电平。前面我们讲了,当ID线为高的时候就表示OTG工作在从机模式。
主机(HOST)模式:主机模式下必须将Mini USB线拔出来,将USB设备连接到对应的USB HOST接口上。Mini USB线拔出来以后USB_OTG_VBUS就没有电压了,此时USB_OTG1_ID线就被R31这个100K电阻下拉到地,因此USB_OTG1_ID线的电压就为0,当ID线为0的时候就表示OTG工作在主机模式。
优点就是省去了购买一根Mini USB OTG线的麻烦,方便我们学习开发,但是在使用的时候要注意一下几点:
①、我们需要软件设置USB_OTG1_ID这个IO的电气属性,默认设置为下拉,也就是默认工作在主机(HOST)模式下。
②、由于我们修改了OTG硬件电路,因此就不能在Mini USB接口上接OTG线了,如果要使用HOST功能就将设备插到开发板板载的USB HOST接口上。
I.MX6U-ALPHA开发板上的USB OTG接口如图67.2.2.2所示:
图67.2.2.2 OTG的HOST以及DEVICE接口
图67.2.2.2中上面的就是主机(HOST)接口,下面的是从机(DEVICE)接口,两个不能同时使用!
67.3 USB协议简析
USB协议中有很多的基础概念,本节就来看一下这些概念。
67.3.1 USB描述符
顾名思义,USB描述符就是用来描述USB信息的,描述符就是一串按照一定规则构建的字符串,USB设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如表67.3.1.1所示:
描述符类型 名字 值
Device Descriptor 设备描述符 1
Configuration Descriptor 配置描述符 2
String Descriptor 字符串描述符 3
Interface Descriptor 接口字符串 4
Endpoint Descriptor 端点描述符 5
表67.3.1.1 USB设备常用描述符
我们依次来看一下表67.3.1.1中这5个描述符的含义:
1、设备描述符
设备描述符用于描述USB设备的一般信息,USB设备只有一个设备描述符。设备描述符里面记录了设备的USB版本号、设备类型、VID(厂商ID)、PID(产品ID)、设备序列号等。设备描述符结构如表67.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 索引 可能的配置描述符数目
表67.3.1.2 设备描述符结构
2、配置描述符
设备描述符的bNumConfigurations域定义了一个USB设备的配置描述符数量,一个USB设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、供电信息等,配置描述符结构如表67.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)
表67.3.1.3 配置描述符结构
3、字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都必须为0,字符串描述符结构如表67.3.1.4所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 N+2 此字符串描述符长度
1 bDescriptorType 1 常量 字符串描述符类型,为0X03
2 wLANGID[0] 2 数字 语言标识码0
… … 2 数字 …
N wLANGID[x] 2 数字 语言标识码x
表67.3.1.4 字符串描述符结构
wLANGID[0]~wLANGID[x]指明了设备支持的语言,具体含义要查阅文档《USB_LANGIDs.pdf》,此文档已经放到了开发板光盘中,路径为:4、参考资料-> USB_LANGIDs.pdf。
主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字符串索引值和语言。设备返回Unicode编码的字符串描述符,结构如表67.3.1.5所示:
偏移 域 大小(B) 值类型 描述
0 bLength 1 N+2 此字符串描述符长度
1 bDescriptorType 1 常量 字符串描述符类型,为0X03
2 bString N 数字 Unicode编码的字符串
表67.3.1.5 Unicode编码的字符串描述符结构
4、接口描述符
配置描述符中指定了该配置下的接口数量,配置可以提供一个或多个接口,接口描述符用于描述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口描述符结构如表67.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 索引 当前接口字符串的索引值
表67.3.1.6接口描述符结构
5、端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端点0是双向端口,其他的端口都是单向的。端点描述符描述了树传输类型、方向、数据包大小、端点号等信息,端点描述符结构如表67.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。
表67.3.1.7端点描述符结构
67.3.3 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。每种类型的包又有多种具体的包,如表67.3.3.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 保留
表67.3.3.1 数据包结构
一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、包目标端点(ENDP)、数据、帧索引、CRC等,这个要具体数据包具体分析。接下来简单看一下这些数据包的结构。
1、令牌包
令牌包结构如图67.3.3.1所示:
图67.3.3.1 SETUP令牌包
图67.3.3.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、数据包
数据包结构如图67.3.3.2所示:
图67.3.3.2 数据包
数据包比较简单,同样的,数据包从SYNC同步域开始,然后紧跟着是PID,这里就是DATA0,PID值为0XC3。接下来就是具体的数据,数据完了以后就是16位的CRC校验值,最后是EOP。
3、握手包
握手包结构如图67.3.3.3所示:
图67.3.3.3 ACK握手包
图67.3.3.3是ACK握手包,很简单,首先是SYNC同步域,然后就是ACK包的PID,为0X4B,最后就是EOP。其他的NAK、STALL、NYET和ERR握手包结构都是一样的,只是其中的PID不同而已。
67.3.4 USB传输类型
在端点描述符中bmAttributes指定了端点的传输类型,一共有4种,本节我们来看一下这四种传输类型的区别。
1、控制传输
控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA)和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用SETUP令牌包,SETUP使用DATA0包。数据阶段是0个、1个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务必须是同一个方向的,比如都为IN或都为OUT。数据阶段的第一个数据包必须是DATA1,每次正确传输以后就在DATA0和DATA1之间进行切换。数据阶段完成以后就是状态阶段,状态阶段的传输方向要和数据阶段相反,比如数据阶段为IN的话状态阶段就要为OUT,状态阶段使用DATA1包。比如一个读控制传输格式如图67.3.4.1所示:
图67.3.4.1 读控制传输阶段
2、同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要求很高,但是不要求数据100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使数据传输出错了也不会重传。
3、批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大块数据的,这些数据对实时性没有要求,比如MSD类设备(存储设备),U盘之类的。批量传输分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的IN令牌包,如果是批量写那么第一阶段就是OUT令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出OUT令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果OUT令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备就会向主机返回一个ACK握手信号。
批量读的过程刚好相反:
①、主机发出IN令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就进入到数据接收状态,等待设备返回数据。
②、如果IN令牌包正确的话,设备就会将一个DATA包放到总线上发送给主机。主机收到这个DATA包以后就会向设备发送一个ACK握手信号。
4、中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传输。
67.3.5 USB枚举
当USB设备与USB主机连接以后主机就会对USB设备进行枚举,通过枚举来获取设备的描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。USB枚举过程如下:
①、第一回合,当USB主机检测到USB设备插入以后机会发出总线复位信号来复位设备。USB设备复位完成以后地址为0,主机向地址0的端点0发送数据,请求设备的描述符。设备得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。
②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址0的端点0发送设置地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过程,等待主机请求状态返回,收到以后设备就会向主机发送一个0字节状态数据包,表明设备已经设置好地址了,主机收到这个0字节状态数据包以后会返回一个确认包(ACK)。设备收到主机发送的ACK包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。
③、第三回合,主机向新的设备地址端点0发送请求设备描述符数据包,这一次主机要获取整个设备描述符,一共是18个字节。
④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。
67.4 Linux内核自带HOST实验
67.4.1 USB鼠标键盘测试
首先做一下USB HOST试验,也就是I.MX6U-ALPHA开发板做USB主机,然后外接USB设备,比如USB鼠标键盘、USB转TTL串口线、U盘等设备。Linux内核已经集成了大量的USB设备驱动,尤其是我们常见的USB鼠标键盘、U盘等,本节我们就来学习一下如何使能Linux内核常见的USB设备驱动。
1、USB鼠标键盘驱动使能
注意,NXP官方的Linux内核默认已经使能了USB键盘鼠标驱动!
USB鼠标键盘属于HID设备,内核已经集成了相应的驱动,NXP官方提供的linux内核默认已经使能了USB鼠标键盘驱动,但是我们还要学习一下如何手动使能这些驱动。输入“make menuconfig”打开linux内核配置界面,首先打开HID驱动,按照如下路径到相应的配置项目:
-> Device Drivers
-> HID support
-> HID bus support (HID [=y])
-> <*> Generic HID driver //使能通用HID驱动
使能以后如图67.4.1.1所示:
图67.4.1.1 使能HID驱动
接下来需要使能USB键盘和鼠标驱动,配置路径如下:
-> Device Drivers
-> HID support
-> USB HID support
-> <*> USB HID transport layer //USB键盘鼠标等HID设备驱动
使能以后如图67.4.1.2所示:
图67.4.1.2 使能USB鼠标键盘驱动
大家可以将光标放到图67.4.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鼠标和键盘
完成以后重新编译linux内核并且使用得到的zImage启动开发板。启动以后插入USB鼠标,会有如图67.4.1.3所示的提示信息:
图67.4.1.3 USB鼠标log信息
从图67.4.1.3可以看出,系统检测到了Logitech(罗技)的鼠标,如果成功驱动的话就会在/dev/input目录下生成一个名为eventX(X=0,1,2,3…)的文件,这个就是我们前面讲的输入子系统,鼠标和键盘都是作为输入子系统设备的。笔者这里对应的就是/dev/input/event3这个设备,使用如下命令查看鼠标的原始输入值,结果如图67.4.1.4所示:
图67.4.1.4 鼠标原始输入值
图67.4.1.4就是鼠标作为输入子系统设备的原始输入值,这里就不去分析了,我们在移植GUI图形库以后就可以直接使用鼠标,比如QT等。
注意,有些鼠标可能会出现隔一段时间自动断开重连的现象,比如我测试的电脑城送的几块钱的罗技鼠标就会这样,我用NXP官方的EVK开发板测试也有这个问题!我自己用的雷蛇鼠标就不会有这种现象。但是,如果你一直使用这个鼠标,比如用hexdump命令一直查看鼠标上报值,或者在QT里面一直使用鼠标,那么这些鼠标就不会自动断开并重连。
最后再来测试一下USB键盘,屏幕已经驱动起来了,所以我们可以直接将屏幕作为终端,然后接上键盘直接输入命令来进行各种操作。首先将屏幕设置为控制台,打开开发板根文件系统中的/etc/inittab文件,然后在里面加入下面这一行:
tty1::askfirst:-/bin/sh
完成以后重启开发板,此时屏幕就会作为终端控制台,会有“Please press Enter to activate this console.”这样提示,如图67.4.1.5所示:
图67.4.1.5 LCD屏幕作为终端
接上键盘,然后根据图67.4.1.5中的提示,按下键盘上的Enter(回车)键即可使能LCD屏幕控制台,然后我们就可以输入各种命令来执行相应的操作,如图67.4.1.6所示:
图67.4.1.6 键盘操作终端
67.4.2 U盘实验
注意,NXP官方的Linux内核默认已经使能了U盘!
NXP提供的Linux内核默认也已经使能了U盘驱动,因此我们可以直接插上去使用。但是我们还是需要学习一下如何手动配置Linux内核,使能U盘驱动。
1、使能U盘驱动
U盘使用SCSI协议,因此要先使能Linux内核中的SCSI协议,配置路径如下:
-> Device Drivers
-> SCSI device support
-> <*> SCSI disk support //选中此选项
结果如图67.4.2.1所示:
图67.4.2.1 使能SCSI
我们还需要使能USB Mass Storage,也就是USB接口的大容量存储设备,配置路径如下:
-> Device Drivers
-> USB support (USB_SUPPORT [=y])
-> Support for Host-side USB (USB [=y])
-> <*> USB Mass Storage support //USB大容量存储设备
结果如图67.4.2.2所示:
图67.4.2.2 使能USB大容量存储设备
2、U盘测试
准备好一个U盘,注意U盘要为FAT32格式的!NTFS和exFAT由于版权问题所以在Linux下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。准备好以后将U盘插入到开发板USB HUB扩展出来的HOST接口上,此时会输出如图67.4.2.3所示信息:
图67.4.2.3 U盘log信息
从图67.4.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盘文件,如图67.4.2.4所示:
图67.4.2.4 U盘文件
至此U盘就能正常读写操作了,直接对/mnt/usb_disk目录进行操作就行了。如果要拔出U盘要执行一个sync命令进行同步,然后在使用unmount进行U盘卸载,命令如下所示:
sync //同步
cd / //如果处于/mnt/usb_disk目录的话先退出来,否则卸载的时候提示设
//备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载
67.5 Linux内核自带USB OTG实验
67.5.1 修改设备树
注意,如果使用的是正点原子I.MX6U-ALPHA开发板,那么就需要修改OTG ID引脚的电气属性,因为ALPHA开发板为了在板子上集成OTG的主机和从机接口对ID线做了修改,至于原因已经在67.2.2小节讲过了。如果使用的其他6ULL开发板,就要去咨询一下厂商,看看需不需要修改ID引脚的电气属性。
查阅原理图可以知道,USB OTG1的ID引脚连接到了I.MX6ULL的GPIO1_IO00这个引脚上,在67.2.2小节分析ALPHA开发板USB OTG原理图的时候已经说过了,USB OTG默认工作在主机(HOST)模式下,因此ID线应该是低电平。这里需要修改设备树中GPIO1_IO00这个引脚的电气属性,将其设置为默认下拉。打开设备树imx6ull-alientek-emmc.dts,在iomuxc节点的pinctrl_hog_1子节点下添加GPIO1_IO00引脚信息,如下所示:
示例代码67.5.1.1 GPIO1_IOI00引脚描述信息
1 &iomuxc {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_hog_1>;
4 imx6ul-evk {
5 pinctrl_hog_1: hoggrp-1 {
6 fsl,pins = <
7 ......
8 MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /*OTG1 ID */
9 >;
10 };
11 ......
12 };
第8行就是将GPIO1_IO00复用为OTG1 ID,并且设置电气属性为0X13058,默认下拉,设备树修改好以后重新编译并用新的设备树启动系统。
67.5.2 OTG主机实验
系统重启成功以后就可以正常使用USB OTG1接口,OTG既可以做主机,也可以做从机,做主机的话测试方法和67.4小节一模一样,直接在ALPHA的OTG HOST接口上插入USB鼠标键盘、U盘等设备。
注意!如果使用正点原子的ALPHA开发板,切记不要使用Mini OTG线来外接USB设备,原因已经在67.2.2小节说明了,只需要将USB设备插入到开发板上的OTG HOST接口上即可!
67.5.3 OTG从机实验
OTG从机就是将开发板作为一个USB设备连接到其他的主机上,这里我们来做两个USB从机实验:模拟U盘以及USB声卡。
1、模拟U盘实验
模拟U盘实验就是将开发板当做一个U盘,可以将开发板上的U盘或者TF卡挂载到PC上去,首先需要配置Linux,配置路径如下:
-> Device Drivers
-> USB support (USB_SUPPORT [=y])
-> USB Gadget Support (USB_GADGET [=y]
-> [M]USB Gadget Drivers ( [=m]) //选中USB Gadget驱动
->[M]Mass Storage Gadget //大容量存储
如图67.5.3.1所示:
图67.5.3.1 USB Gadget中大容量存储设备驱动
这里我们需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好以后重新编译Linux内核,会得到三个.ko驱动模块(带路径):
drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_mass_storage.ko
drivers/usb/gadget/legacy/g_mass_storage.ko
将上述三个.ko模块拷贝到开发板根文件系统中,命令如下:
cd drivers/usb/gadget/ //进入gadget目录下
sudo cp libcomposite.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_mass_storage.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_mass_storage.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
拷贝完成以后使用新编译出来的zImage启动开发板,在开发板上插入一个U盘,记住这个U盘对应的设备文件,比如我们这里是/dev/sda和/dev/sda1,以后要将/dev/sda1挂载到PC上,也就是把/dev/sda1作为模拟U盘的存储区域。
使用Mini USB线将开发板的USB OTG Mini接口与电脑连接起来,如图67.5.3.2所示:
图67.5.3.2 Mini USB OTG接口
连接好以后依次加载libcomposite.ko、usb_f_mass_storage.ko和g_mass_storage.ko这三个驱动文件,顺序不能错了!命令如下:
depmod
modprobe libcomposite.ko
modprobe usb_f_mass_storage.ko
modprobe g_mass_storage.ko file=/dev/sda1 removable=1
加载g_mass_storage.ko的时候使用file参数指定使用的大容量存储设备,我这里使用U盘对应的/dev/sda1。如果加载成功的话电脑就会出现一个U盘,这个U盘就是我们开发板模拟的,如图67.5.3.3所示:
图67.5.3.3 电脑识别出来的U盘
我们可以直接在电脑上对这个U盘进行读写操作,实际上操作的就是插在开发板上的U盘。操作完成以后要退出的话执行如下命令:
rmmod g_mass_storage.ko
注意!不要将开发板上的EMMC或者NAND作为模拟U盘的存储区域,因为linux下EMMC和NAND使用的文件系统一般都是EXT3/EXT4和UBIFS,这些文件系统类型和windows下的不兼容,如果挂载的话就会在windows下提示要你格式化U盘!
2、USB声卡实验
USB声卡就是USB接口的外置声卡,一般电脑内部都自带了声卡,但是内部自带的声卡效果相对来说比较差,不能满足很多HIFI玩家的需求。USB声卡通过USB接口来传递音频数据,具体的ADC和DAC过程由声卡完成,摆脱了电脑主板体积的限制,外置USB声卡就可以做的很好。ALPHA开发板板载了音频解码芯片,因此可以将ALPHA开发板作为一个外置USB声卡,配置Linux内核,配置路径如下:
-> Device Drivers
-> USB support (USB_SUPPORT [=y])
-> USB Gadget Support (USB_GADGET [=y]
-> [M]USB Gadget Drivers //选中USB Gadget驱动
->[M] Audio Gadget //选中音频
->UAC 1.0 (Legacy) //选中UAC
配置如图67.5.3.4所示:
图67.5.3.4 使能USB Gadget下的音频驱动
注意,这里也是编译为驱动模块,配置完成以后重新编译内核,得到新的zImage和三个.ko驱动模块文件:
drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_uac1.ko
drivers/usb/gadget/legacy/g_audio.ko
将上述三个.ko模块拷贝到开发板根文件系统中,命令如下:
cd drivers/usb/gadget/
sudo cp libcomposite.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_uac1.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_audio.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
拷贝完成以后使用新编译出来的zImage启动开发板,首先按照65.5.2小节讲解的方法配置ALPHA的声卡,保证声卡播放正常!使用Mini USB线将开发板与电脑连接起来,最后依次加载libcomposite.ko、usb_f_uac1.ko和g_audio.ko这三个驱动模块,命令如下:
depmod
modprobe libcomposite.ko
modprobe usb_f_uac1.ko
modprobe g_audio.ko
加载完成以后稍等一会虚拟出一个USB声卡,打开电脑的设备管理器,选择“声音、视频和游戏控制器”,会发现有一个名为“AC Interface”设备,如图67.5.3.5所示:
图67.5.3.5 模拟出来的USB声卡
图67.5.3.5中的“AC Interface”就是开发板模拟出来的USB声卡,设置windows,选择音频输出使用“AC Interface”,Windows10设置如图67.5.3.6所示:
图67.5.3.5 声卡输出设置为AC Interface
一切设置好以后就可以从开发板上听到电脑输出的声音,此时开发板就完全是一个USB声卡设备了。
关于USB驱动就讲解到这里,本章并没有深入到USB驱动具体编写方式,只是对USB的协议做了简单的介绍,后面讲解了一下Linux内核自带的USB HOST和DEVICE驱动的使用,Linux内核已经集成了大量的USB设备驱动,至于其他特殊的就需要具体情况具体分析了,比如本教程后面讲解的USB WIFI和4G模块驱动。