正点原子imx6ull-mini-Linux驱动之Linux USB 驱动实验

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

1:USB 接口简介

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

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 等公司组成的 USBIF(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 所示:

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

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

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

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

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 插头从左到右线序依次为 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 的电流。

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,也就是将一个 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 中可以看出从 Root Hub 开始,一共有 7 层,金字塔顶部是 Root Hub,这个是 USB 控制器内部的。图中的 Hub 就是连接的 USB 集线器,Func 就是具体的 USB 设备。 USB 主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个 USB 设备一旦上电就会存在一个管道,也就是默认管道,USB 主机通过管道来获取从机的描述符、 配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个 特定的端点。

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

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

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

2:硬件原理图分析

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

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 中 U10 就是 USB HUB 芯片 GL850G,GL850G 是一款符合 USB2.0 标准的 USBHUB 芯片,支持一拖四扩展,可以将一路 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 所示:

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

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 中左侧的为 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 中上面的就是主机(HOST)接口,下面的是从机(DEVICE)接口,两个不能同时使 用!

3:USB 协议简析

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

3.1:USB 描述符

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

 我们依次来看一下表 67.3.1.1 中这 5 个描述符的含义:

3.1.1:设备描述符

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

3.1.2:配置描述符

设备描述符的 bNumConfigurations 域定义了一个 USB 设备的配置描述符数量,一个 USB 设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、 供电信息等,配置描述符结构如表 67.3.1.3 所示:

3.1.3:字符串描述符

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

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

3.1.4:接口描述符

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

3.1.5:端口描述符

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

3.2:USB 数据包类型

USB 是串行通信,需要一位一位的去传输数据,USB 传输的时候先将原始数据进行打包, 所以 USB 中传输的基本单元就是数据包。根据用途的不同,USB 协议定义了 4 种不同的包结 构:

令牌(Token)包、数据(Data)包、握手(Handshake)包和特殊(Special)包。

这四种包通过包标识 符 PID 来区分,PID 共有 8 位,USB 协议使用低 4 位 PID3~PID0,另外的高四位 PID7~PID4 是 PID3~PID0 的取反,传输顺序是 PID0、PID1、PID2、PID3…PID7。令牌包的 PID1~0 为 01,数 据包的 PID1~0 为 11,握手包的 PID1~0 为 10,特殊包的 PID1~0 为 00。每种类型的包又有多 种具体的包,如表 67.3.3.1 所示:

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

3.2.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 位的帧号域。

3.2.2:数据包

数据包结构如图 67.3.3.2 所示:

数据包比较简单,同样的,数据包从 SYNC 同步域开始,然后紧跟着是 PID,这里就是 DATA0,PID 值为 0XC3。接下来就是具体的数据,数据完了以后就是 16 位的 CRC 校验值,最 后是 EOP。

3.2.3:握手包

握手包结构如图 67.3.3.3 所示:

图 67.3.3.3 是 ACK 握手包,很简单,首先是 SYNC 同步域,然后就是 ACK 包的 PID,为 0X4B,最后就是 EOP。其他的 NAK、STALL、NYET 和 ERR 握手包结构都是一样的,只是其 中的 PID 不同而已。

3.3:USB 传输类型

在端点描述符中 bmAttributes 指定了端点的传输类型,一共有 4 种,本节我们来看一下这 四种传输类型的区别。

3.3.1:控制传输

控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描 述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA) 和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用 SETUP 令牌包,SETUP 使用 DATA0 包。数据阶段是 0 个、1 个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务 必须是同一个方向的,比如都为 IN 或都为 OUT。数据阶段的第一个数据包必须是 DATA1,每 次正确传输以后就在 DATA0 和 DATA1 之间进行切换。数据阶段完成以后就是状态阶段,状态 阶段的传输方向要和数据阶段相反,比如数据阶段为 IN 的话状态阶段就要为 OUT,状态阶段 使用 DATA1 包。比如一个读控制传输格式如图 67.3.4.1 所示:

3.3.2:同步传输

同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要 求很高,但是不要求数据 100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使 数据传输出错了也不会重传。

3.3.3:批量传输

提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大 块数据的,这些数据对实时性没有要求,比如 MSD 类设备(存储设备),U 盘之类的。批量传输 分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的 IN 令牌包,如果是批量写那 么第一阶段就是 OUT 令牌包。 我们就以批量写为例简单介绍一下批量传输过程:

①、主机发出 OUT 令牌包,令牌包里面包含了设备地址、端点等信息。

②、如果 OUT 令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个 数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备 就会向主机返回一个 ACK 握手信号。

批量读的过程刚好相反:

①、主机发出 IN 令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就 进入到数据接收状态,等待设备返回数据。

②、如果 IN 令牌包正确的话,设备就会将一个 DATA 包放到总线上发送给主机。主机收 到这个 DATA 包以后就会向设备发送一个 ACK 握手信号。

3.3.4:中断传输

这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断 传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断 的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传 输。

3.4:USB 枚举

当 USB 设备与 USB 主机连接以后主机就会对 USB 设备进行枚举,通过枚举来获取设备的 描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。USB 枚举 过程如下:

①、第一回合,当 USB 主机检测到 USB 设备插入以后机会发出总线复位信号来复位设备。 USB 设备复位完成以后地址为 0,主机向地址 0 的端点 0 发送数据,请求设备的描述符。设备 得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描 述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。

②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址 0 的端点 0 发送设置 地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过 程,等待主机请求状态返回,收到以后设备就会向主机发送一个 0 字节状态数据包,表明设备 已经设置好地址了,主机收到这个 0 字节状态数据包以后会返回一个确认包(ACK)。设备收到 主机发送的 ACK 包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。 ③、第三回合,主机向新的设备地址端点 0 发送请求设备描述符数据包,这一次主机要获 取整个设备描述符,一共是 18 个字节。 ④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。

4:Linux 内核自带 HOST 实验

4.1:USB 鼠标键盘测试

首先做一下 USB HOST 试验,也就是 I.MX6U-ALPHA 开发板做 USB 主机,然后外接 USB 设备,比如 USB 鼠标键盘、USB 转 TTL 串口线、U 盘等设备。Linux 内核已经集成了大量的 USB 设备驱动,尤其是我们常见的 USB 鼠标键盘、U 盘等,本节我们就来学习一下如何使能 Linux 内核常见的 USB 设备驱动。

4.1.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 驱动

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

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

大家可以将光标放到图 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”这两个配置项的时候也不要觉得教程这里写错了。

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

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

4.2:U 盘实验

注意,NXP 官方的 Linux 内核默认已经使能了 U 盘! NXP 提供的 Linux 内核默认也已经使能了 U 盘驱动,因此我们可以直接插上去使用。但是 我们还是需要学习一下如何手动配置 Linux 内核,使能 U 盘驱动。

4.2.1:使能 U 盘驱动

U 盘使用 SCSI 协议,因此要先使能 Linux 内核中的 SCSI 协议,配置路径如下:

-> Device Drivers 
    -> SCSI device support
        -> <*> SCSI disk support //选中此选项

我们还需要使能 USB Mass Storage,也就是 USB 接口的大容量存储设备,配置路径如下:

-> Device Drivers 
    -> USB support (USB_SUPPORT [=y]) 
         -> Support for Host-side USB (USB [=y]) 
            -> <*> USB Mass Storage support //USB 大容量存储设备

4.2.2:U 盘测试

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

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

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

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

5:Linux 内核自带 USB OTG 实验

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 引脚信息,如下所示:

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,默认下拉, 设备树修改好以后重新编译并用新的设备树启动系统。

5.2:OTG 主机实验

系统重启成功以后就可以正常使用 USB OTG1 接口,OTG 既可以做主机,也可以做从机, 做主机的话测试方法和 67.4 小节一模一样,直接在 ALPHA 的 OTG HOST 接口上插入 USB 鼠 标键盘、U 盘等设备。

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

5.3:OTG 从机实验

OTG 从机就是将开发板作为一个 USB 设备连接到其他的主机上,这里我们来做两个 USB 从机实验:模拟 U 盘以及 USB 声卡。

5.3.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 //大容量存储

这里我们需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好以 后重新编译 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 所示:

连接好以后依次加载 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 盘!

5.3.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 所示

注意,这里也是编译为驱动模块,配置完成以后重新编译内核,得到新的 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 中的“AC Interface”就是开发板模拟出来的 USB 声卡,设置 windows,选择音 频输出使用“AC Interface”,Windows10 设置如图 67.5.3.6 所示:

 一切设置好以后就可以从开发板上听到电脑输出的声音,此时开发板就完全是一个 USB 声 卡设备了。 关于 USB 驱动就讲解到这里,本章并没有深入到 USB 驱动具体编写方式,只是对 USB 的 协议做了简单的介绍,后面讲解了一下 Linux 内核自带的 USB HOST 和 DEVICE 驱动的使用, Linux 内核已经集成了大量的 USB 设备驱动,至于其他特殊的就需要具体情况具体分析了,比 如本教程后面讲解的 USB WIFI 和 4G 模块驱动。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值