Linux USB 驱动实验

目录

  • USB 接口简介
    • 什么是USB?
    • USB 电气特性(4根线)
    • USB 拓扑结构
    • 什么是USB OTG?
    • I.MX6ULL USB 接口简介
  • 硬件原理图分析
    • USB HUB 原理图分析
    • V2.4 版本以前底板USB OTG 原理图分析
    • V2.4 及以后版本底板USB OTG 原理图分析
  • USB 协议简析
    • USB 描述符
    • USB 数据包类型
    • USB 传输类型
    • USB 枚举
  • Linux 内核自带HOST 实验
    • USB 鼠标键盘测试
    • U 盘实验
  • Linux 内核自带USB OTG 实验
    • 修改设备树
    • OTG 主机实验
    • OTG 从机实验

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

USB 接口简介

关于USB 详细的协议内容请参考《USB2.0 协议中文版.pdf》和《USB3.0 协议中文版.pdf》,这两份文档已经放到了开发板光盘中,存放在“4、参考资料”中。

什么是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.2Gen2、USB3.2 Gen 2X2。

  • USB4.0:目前还在标准定制中,目前还没有设备搭载,据说是在Inter 的雷电接口上改进而
    来。USB4.0 的速度将提升到了40Gbps,最高支持100W 的供电能力,只需要一根线就可以完
    成数据传输与供电,极大的简化了设备之间的链接线数,期待USB4.0 设备上市。

如果按照接口类型划分的话USB 就要分为很多种了,最常见的就是USB A 插头和插座,
如图67.1.1.1 所示:

Linux USB 驱动实验_第1张图片

使用过JLINK 调试器的朋友应该还见过USB B 插头和插座,USB B 插头和插座如图67.1.1.2所示:

Linux USB 驱动实验_第2张图片

USB 插头在不断的缩小,由此产生了Mini USB 接口,正点原子的I.MX6ULL-ALPHA 开发板使用的就是Mini USB,Mini USB 插头和插座如图67.1.1.3 所示:

Linux USB 驱动实验_第3张图片

比Mini USB 更小的就是Micro USB 接口了,以前的智能手机基本都是Micro USB 接口的,Micro USB 插头和插座如图67.1.1.4 所示:

Linux USB 驱动实验_第4张图片

现在最流行的就是USB Typec 了,USB Typec 插头和插座如图67.1.1.5 所示:

Linux USB 驱动实验_第5张图片

USB 电气特性(4根线)

由于正点原子I.MX6U-ALPHA 开发板使用的Mini USB 接口,因此我们就以Mini USB 为例讲解一下USB 的基本电气属性。Mini USB 线一般都是一头为USB A 插头,一头为Mini USB插头。一共有四个触点,也就是4 根线,这四根线的顺序如图67.1.2.1 所示:

Linux USB 驱动实验_第6张图片

如图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 的电流。

USB 拓扑结构

USB 是主从结构的,也就是分为主机和从机两部分,一般主机叫做Host,从机叫做Device。

主机就是提供USB A 插座来连接外部的设备,比如电脑作为主机,对外提供USB A 插座,我们可以通过USB 线来连接一些USB 设备,比如声卡、手机等。因此电脑带的USB A 插座数量就决定了你能外接多少个USB 设备,如果不够用的话我们可以购买USB 集线器来扩展电脑的USB 插口,USB 集线器也叫做USB HUB,USB HUB 如图67.1.3.1 所示:

Linux USB 驱动实验_第7张图片

图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):

Linux USB 驱动实验_第8张图片

图67.1.3.2 中可以看出从Root Hub 开始,一共有7 层,金字塔顶部是Root Hub,这个是USB 控制器内部的。图中的Hub 就是连接的USB 集线器,Func 就是具体的USB 设备。

USB 主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个USB设备一旦上电就会存在一个管道,也就是默认管道,USB 主机通过管道来获取从机的描述符、配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个
特定的端点。

什么是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 接口一般都是那些带有ID 线的接口,比如正点原子的I.MX6ULL-ALPHA 开发板的USB_OTG 接口就是支持OTG 模式的,USB_OTG 连接到了I.MX6ULL 的USB1 接口上。如果只有一个Mini USB 或者Type-C USB 接口的话如果要使用OTG 的主机模式,那么就需要一根OTG 线,Mini USB 的OTG 线如图67.1.4.1 所示:

Linux USB 驱动实验_第9张图片

Type-C USB OTG 线如图67.1.4.2 所示:

Linux USB 驱动实验_第10张图片

可以看出,不管是Mini USB OTG 还是Type-C USB OTG 线,一头都是USB A 插座,另外一头是Mini USB 或者Type-C USB 插头,将Mini USB 或者Type-C USB 插头插入机器的Mini USB 或Type-C 接口上,需要连接的USB 设备插到另一端的USB A 插座上,比如U 盘啥的。
USB OTG 线会将ID 线拉低,这样机器就知道自己要做为一个主机,用来连接外部的从机设备(U 盘)。

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)”章节。

硬件原理图分析

正点原子的I.MX6ULL-ALPHA 开发板USB 部分原理图可以分为两部分:USB HUB 以及
USB OTG,我们依次来看一下这两部分的硬件原理图。

USB HUB 原理图分析

1、V2.4 版本以前底板USB HUB 原理图分析
首先来看一下USB HUB 原理图,I.MX6ULL-ALPHA 使用GL850G 这个HUB 芯片将
I.MX6ULL 的USB OTG2 扩展成了4 路HOST 接口,其中一路供4G 模块使用,因此就剩下了
三个通用的USB A 插座,原理图如图67.2.1.1 所示:

Linux USB 驱动实验_第11张图片

2、V2.4 版本及以后底板USB HUB 原理图分析
V2.4 及以后版本的I.MX6ULL-ALPHA 使用SL2.1A 这个HUB 芯片将I.MX6ULL 的USB OTG2 扩展成了4 路HOST 接口,其中一路供4G 模块使用,因此就剩下了三个通用的USB A
插座,原理图如图67.2.1.2 所示:

Linux USB 驱动实验_第12张图片

图67.2.1.1 和图67.2.1.2 中的GL850G 和SL2.1A,这两个都是符合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 所示:

Linux USB 驱动实验_第13张图片

注意,使用GL850G 扩展出来的4 路USB 接口只能用作HOST!

V2.4 版本以前底板USB OTG 原理图分析

I.MX6U-ALPHA 开发板上还有一路USB OTG 接口,使用I.MX6ULL 的USB OTG1 接口。
此路USB OTG 既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG 功
能,原理图如图67.2.2.1 所示:

Linux USB 驱动实验_第14张图片

图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 所示:

Linux USB 驱动实验_第15张图片
图67.2.2.2 中上面的就是主机(HOST)接口,下面的是从机(DEVICE)接口,两个不能同时使
用!

V2.4 及以后版本底板USB OTG 原理图分析

I.MX6U-ALPHA 开发板上有一路USB OTG 接口,使用I.MX6ULL 的USB OTG1 接口。此
路USB OTG 既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG 功能,
原理图如图67.2.3.1 所示:

Linux USB 驱动实验_第16张图片

图67.2.3.1 中只有一个Type-C USB 插座,当OTG 作为从机(DEVICE)的时候USB 线接入
此接口。当OTG 作为主机的时候需要使用Type-C OTG 线。
USB OTG 通过ID 线来完成主从机切换,这里就涉及到硬件对USB ID 线的处理,图67.2.2.1中Type-C 的CC1 和CC2 引脚就是完成传统的USB ID 线功能的。我们简单分析一下主从机切换原理。
从机(DEVICE)模式:图67.2.3.1 中R111 这一个49.9K 的电阻,默认将USB_OTG1_ID 线
拉高,前面我们讲了,当ID 线为高的时候就表示OTG 工作在从机模式。此时由于USB_OTG1_ID
为高电平,因此MOS1(SI2302)导通,因此MT9700HT5 的EN 脚就接地,此时MT9700HT5 的
OUT 引脚就没有输出,所以USB_OTG_VBUS 电压关闭。USB_OTG_VBUS 电压用于在OTG
做HOST 功能的时候,向外部设备提供5V 电源。很明显,在OTG 做从机的时候,OTG 就不
需要向外界提供USB_OTG_VBUS 电源了。这里使用MT9700HT5 这个芯片来实VBUS 电源的
开关控制。

主机(HOST)模式:如果要使用OTG 的HOST 功能,那么必须要使用到Type-C OTG 线。
Type-C OTG 线会将CC1 和CC2 拉低,因此USB_OTG1_ID 线也会被拉低,当ID 线为0 的时
候就表示OTG 工作在主机模式。此时由于USB_OTG1_ID 为低,因此MOS1(SI2302)不导通,
,因此MT9700HT5 的EN 脚就会被R31 这个10K 电阻上拉到5V,所以MT9700HT5 的OUT
引脚就会输出5V 电压,也就是说USB_OTG_VBUS 此时是5V,可以向外部设备提供5V 电源。

USB 协议简析

USB 协议中有很多的基础概念,本节就来看一下这些概念。

USB 描述符

顾名思义,USB 描述符就是用来描述USB 信息的,描述符就是一串按照一定规则构建的
字符串,USB 设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如表67.3.1.1所示:

Linux USB 驱动实验_第17张图片
我们依次来看一下表67.3.1.1 中这5 个描述符的含义:

1、设备描述符
设备描述符用于描述USB 设备的一般信息,USB 设备只有一个设备描述符。设备描述符
里面记录了设备的USB 版本号、设备类型、VID(厂商ID)、PID(产品ID)、设备序列号等。设
备描述符结构如表67.3.1.2 所示:

Linux USB 驱动实验_第18张图片
Linux USB 驱动实验_第19张图片
2、配置描述符
设备描述符的bNumConfigurations 域定义了一个USB 设备的配置描述符数量,一个USB
设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、
供电信息等,配置描述符结构如表67.3.1.3 所示:

Linux USB 驱动实验_第20张图片

3、字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、
设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都
必须为0,字符串描述符结构如表67.3.1.4 所示:

Linux USB 驱动实验_第21张图片

wLANGID[0]~wLANGID[x] 指明了设备支持的语言,具体含义要查阅文档
《USB_LANGIDs.pdf 》,此文档已经放到了开发板光盘中,路径为:4 、参考资料-> USB_LANGIDs.pdf。
主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字
符串索引值和语言。设备返回Unicode 编码的字符串描述符,结构如表67.3.1.5 所示:

Linux USB 驱动实验_第22张图片

4、接口描述符
配置描述符中指定了该配置下的接口数量,可以提供一个或多个接口,接口描述符用于描
述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口
描述符结构如表67.3.1.6 所示:

Linux USB 驱动实验_第23张图片

5、端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端
点0 是双向端口,其他的端口都是单向的。端点描述符描述了传输类型、方向、数据包大小、
端点号等信息,端点描述符结构如表67.3.1.7 所示:

Linux USB 驱动实验_第24张图片
Linux USB 驱动实验_第25张图片

USB 数据包类型

USB 是串行通信,需要一位一位的去传输数据,USB 传输的时候先将原始数据进行打包,
所以USB 中传输的基本单元就是数据包。根据用途的不同,USB 协议定义了4 种不同的包结
构:令牌(Token)包、数据(Data)包、握手(Handshake)包和特殊(Special)包。这四种包通过包标识
符PID 来区分,PID 共有8 位,USB 协议使用低4 位PID3PID0,另外的高四位PID7PID4 是
PID3~PID0 的取反,传输顺序是PID0、PID1、PID2、PID3…PID7。令牌包的PID1~0 为01,数
据包的PID1~0 为11,握手包的PID1~0 为10,特殊包的PID1~0 为00。每种类型的包又有多
种具体的包,如表67.3.3.1 所示:

Linux USB 驱动实验_第26张图片
一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符
(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、
包目标端点(ENDP)、数据、帧索引、CRC 等,这个要具体数据包具体分析。接下来简单看一下
这些数据包的结构。

1、令牌包
令牌包结构如图67.3.3.1 所示:

在这里插入图片描述

图67.3.3.1 是一个SETUP 令牌包结构,首先是SYNC 同步域,包同步域为00000001,也
就是连续7 个0,后面跟一个1,如果是高速设备的话就是31 个0 后面跟一个1。紧跟着是PID,
这里是SETUP 包,为0XB4,大家可能会好奇为什么不是0X2D(00101101),0XB4 的原因如下:
①、SETUP 包的PID3~PID0 为1101,因此对应的PID7~PID4 就是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 所示:

在这里插入图片描述

数据包比较简单,同样的,数据包从SYNC 同步域开始,然后紧跟着是PID,这里就是DATA0,PID 值为0XC3。接下来就是具体的数据,数据完了以后就是16 位的CRC 校验值,最后是EOP。
3、握手包
握手包结构如图67.3.3.3 所示:
Linux USB 驱动实验_第27张图片
图67.3.3.3 是ACK 握手包,很简单,首先是SYNC 同步域,然后就是ACK 包的PID,为0X4B,最后就是EOP。其他的NAK、STALL、NYET 和ERR 握手包结构都是一样的,只是其中的PID 不同而已。

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 所示:

在这里插入图片描述

2、同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要
求很高,但是不要求数据100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使
数据传输出错了也不会重传。
3、批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大
块数据的,这些数据对实时性没有要求,比如MSD 类设备(存储设备),U 盘之类的。批量传输
分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的IN 令牌包,如果是批量写那
么第一阶段就是OUT 令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出OUT 令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果OUT 令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个
数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备
就会向主机返回一个ACK 握手信号。
批量读的过程刚好相反:
①、主机发出IN 令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就
进入到数据接收状态,等待设备返回数据。
②、如果IN 令牌包正确的话,设备就会将一个DATA 包放到总线上发送给主机。主机收
到这个DATA 包以后就会向设备发送一个ACK 握手信号。
4、中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断
传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断
的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传
输。

USB 枚举

当USB 设备与USB 主机连接以后主机就会对USB 设备进行枚举,通过枚举来获取设备的
描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。USB 枚举
过程如下:
①、第一回合,当USB 主机检测到USB 设备插入以后主机会发出总线复位信号来复位设
备。USB 设备复位完成以后地址为0,主机向地址0 的端点0 发送数据,请求设备的描述符。
设备得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。
②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址0 的端点0 发送设置
地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过
程,等待主机请求状态返回,收到以后设备就会向主机发送一个0 字节状态数据包,表明设备
已经设置好地址了,主机收到这个0 字节状态数据包以后会返回一个确认包(ACK)。设备收到
主机发送的ACK 包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。
③、第三回合,主机向新的设备地址端点0 发送请求设备描述符数据包,这一次主机要获
取整个设备描述符,一共是18 个字节。
④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。

Linux 内核自带HOST 实验

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 所示:

Linux USB 驱动实验_第28张图片

接下来需要使能USB 键盘和鼠标驱动,配置路径如下:

-> Device Drivers
	-> HID support
		-> USB HID support
			-> <*> USB HID transport layer 	//USB 键盘鼠标等HID 设备驱动

使能以后如图67.4.1.2 所示:

Linux USB 驱动实验_第29张图片

大家可以将光标放到图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 可以看出,系统检测到了Logitech(罗技)的鼠标,如果成功驱动的话就会在/dev/input 目录下生成一个名为eventX(X=0,1,2,3…)的文件,这个就是我们前面讲的输入子系统,鼠标和键盘都是作为输入子系统设备的。笔者这里对应的就是/dev/input/event3 这个设备,使用如下命令查看鼠标的原始输入值,结果如图67.4.1.4 所示:

Linux USB 驱动实验_第30张图片

图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 所示:

Linux USB 驱动实验_第31张图片

接上键盘,然后根据图67.4.1.5 中的提示,按下键盘上的Enter(回车)键即可使能LCD 屏幕控制台,然后我们就可以输入各种命令来执行相应的操作,如图67.4.1.6 所示:

Linux USB 驱动实验_第32张图片

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 所示:

Linux USB 驱动实验_第33张图片

我们还需要使能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 所示:

Linux USB 驱动实验_第34张图片

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

Linux USB 驱动实验_第35张图片

从图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 所示:

Linux USB 驱动实验_第36张图片

至此U 盘就能正常读写操作了,直接对/mnt/usb_disk 目录进行操作就行了。如果要拔出U盘要执行一个sync 命令进行同步,然后在使用unmount 进行U 盘卸载,命令如下所示:

sync 		//同步
cd / 		//如果处于/mnt/usb_disk 目录的话先退出来,否则卸载的时候提示设
			//备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载

Linux 内核自带USB OTG 实验

修改设备树

注意,如果使用的是正点原子I.MX6U-ALPHA 开发板,那么就需要修改OTG ID 引脚的电气属性。如果使用的其他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,默认下拉,设备树修改好以后重新编译并用新的设备树启动系统。

OTG 主机实验

系统重启成功以后就可以正常使用USB OTG1 接口,OTG 既可以做主机,也可以做从机,做主机的话测试方法和67.4 小节一模一样。如果是V2.4 版本以前的底板,直接在ALPHA 的OTG HOST 接口上插入USB 鼠标键盘、U 盘等设备。如果是V2.4 及以后版本底板,要使用
Type-C OTG 线连接USB 鼠标键盘,U 盘等设备。

注意!如果使用正点原子的V2.4 版本以前的ALPHA 开发板,切记不要使用Mini OTG 线来外接USB 设备,原因已经在67.2.2 小节说明了,只需要将USB 设备插入到开发板上的OTG HOST 接口上即可!

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 (<choice> [=m]) //选中USB Gadget 驱动
				->[M]Mass Storage Gadget 			 //大容量存储

如图67.5.3.1 所示:

Linux USB 驱动实验_第37张图片

这里我们需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好以后重新编译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 盘的存储区域。
V2.4 版本以前的底板使用Mini USB 线将开发板的USB OTG Mini 接口与电脑连接起来,

如图67.5.3.2 所示:
Linux USB 驱动实验_第38张图片

V2.4 及以后版本的底板使用Type-C 线将开发板的USB Type-C 接口与电脑连接起来,如图67.5.3.3 所示:

Linux USB 驱动实验_第39张图片

连接好以后依次加载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 所示:

在这里插入图片描述

我们可以直接在电脑上对这个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 所示:

Linux USB 驱动实验_第40张图片

注意,这里也是编译为驱动模块,配置完成以后重新编译内核,得到新的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 所示:

Linux USB 驱动实验_第41张图片

图67.5.3.5 中的“AC Interface”就是开发板模拟出来的USB 声卡,设置windows,选择音频输出使用“AC Interface”,Windows10 设置如图67.5.3.6 所示:

Linux USB 驱动实验_第42张图片

一切设置好以后就可以从开发板上听到电脑输出的声音,此时开发板就完全是一个USB 声卡设备了。

关于USB 驱动就讲解到这里,本章并没有深入到USB 驱动具体编写方式,只是对USB 的协议做了简单的介绍,后面讲解了一下Linux 内核自带的USB HOST 和DEVICE 驱动的使用,Linux 内核已经集成了大量的USB 设备驱动,至于其他特殊的就需要具体情况具体分析了,比
如本教程后面讲解的USB WIFI 和4G 模块驱动。

你可能感兴趣的:(正点IMX6ULL驱动开发,linux,单片机,驱动开发)