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 出现的目的就是为了替换前面三个。
USB 协议简析
USB 描述符
USB 描述符就是用来描述 USB 信息的,描述符就是一串按照一定规则构建的字符串, USB 设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如下表所示:
描述符类型 | 值 | |
Device Descriptor | 设备描述符 | 1 |
Configuration Descriptor | 配置描述符 | 2 |
String Descriptor | 字符串描述符 | 3 |
Interface Descriptor | 接口字符串 | 4 |
Endpoint Descriptor | 端点描述符 | 5 |
设备描述符
设备描述符用于描述 USB 设备的一般信息, USB 设备只有一个设备描述符。设备描述符里面记录了设备的 USB 版本号、设备类型、 VID(厂商 ID)、 PID(产品 ID)、设备序列号等。设备描述符结构如表所示:
偏移 | 域 | 大小(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 | 索引 | 可能的配置描述符数目 |
配置描述符
设备描述符的 bNumConfigurations 域定义了一个 USB 设备的配置描述符数量,一个 USB设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、
供电信息等,配置描述符结构如表所示:
偏移 | 域 | 大小(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) |
字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都
必须为 0,字符串描述符结构如表所示:
偏移 | 域 | 大小(B) | 值类型 | 描述 |
0 | bLength | 1 | N+2 | 此字符串描述符长度 |
1 | bDescriptorType | 1 | 常量 | 字符串描述符类型,为 0X03 |
2 | wLANGID[0] | 2 | 数字 | 语言标识码 0 |
… | … | 2 | 数字 | … |
N | wLANGID[x] | 2 | 数字 | 语言标识码 x |
wLANGID[0]~wLANGID[x] 指 明 了 设 备 支 持 的 语 言 , 具 体 含 义 要 查 阅 文 档《 USB_LANGIDs.pdf》.
主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字符串索引值和语言。设备返回 Unicode 编码的字符串描述符,结构如表所示:
偏移 | 域 | 大小(B) | 值类型 | 描述 |
0 | bLength | 1 | N+2 | 此字符串描述符长度 |
1 | bDescriptorType | 1 | 常量 | 字符串描述符类型,为 0X03 |
2 | bString | N | 数字 | Unicode 编码的字符串 |
接口描述符
配置描述符中指定了该配置下的接口数量,配置可以提供一个或多个接口,接口描述符用于描述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口描述符结构如表所示:
偏移 | 域 | 大小(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 | 索引 | 当前接口字符串的索引值 |
端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端点 0 是双向端口,其他的端口都是单向的。端点描述符描述了树传输类型、方向、数据包大小、
端点号等信息,端点描述符结构如表所示:
偏移 | 域 | 大小(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。 |
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。每种类型的包又有多种具体的包,如表所示:
一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、
包目标端点(ENDP)、数据、帧索引、 CRC 等,这个要具体数据包具体分析。接下来简单看一下这些数据包的结构。
令牌包
令牌包结构如图所示:
上图是一个 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 位的帧号域。
数据包
数据包结构如图所示:
数据包比较简单,同样的,数据包从 SYNC 同步域开始,然后紧跟着是 PID,这里就是DATA0, PID 值为 0XC3。接下来就是具体的数据,数据完了以后就是 16 位的 CRC 校验值,最后是 EOP。
握手包
握手包结构如图所示:
上图是 ACK 握手包,很简单,首先是 SYNC 同步域,然后就是 ACK 包的 PID,为0X4B,最后就是 EOP。其他的 NAK、 STALL、 NYET 和 ERR 握手包结构都是一样的,只是其中的 PID 不同而已。
USB 传输类型
在端点描述符中 bmAttributes 指定了端点的传输类型,一共有 4 种,本节我们来看一下这四种传输类型的区别。
控制传输
控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA)和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用 SETUP 令牌包, SETUP 使用DATA0 包。数据阶段是 0 个、 1 个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务必须是同一个方向的,比如都为 IN 或都为 OUT。数据阶段的第一个数据包必须是 DATA1,每次正确传输以后就在 DATA0 和 DATA1 之间进行切换。数据阶段完成以后就是状态阶段,状态阶段的传输方向要和数据阶段相反,比如数据阶段为 IN 的话状态阶段就要为 OUT,状态阶段使用 DATA1 包。比如一个读控制传输格式如图所示:
同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要求很高,但是不要求数据 100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使数据传输出错了也不会重传。
批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大块数据的,这些数据对实时性没有要求,比如 MSD 类设备(存储设备), U 盘之类的。批量传输分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的 IN 令牌包,如果是批量写那么第一阶段就是 OUT 令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出 OUT 令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果 OUT 令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备就会向主机返回一个 ACK 握手信号。
批量读的过程刚好相反:
①、主机发出 IN 令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就进入到数据接收状态,等待设备返回数据。
②、如果 IN 令牌包正确的话,设备就会将一个 DATA 包放到总线上发送给主机。主机收到这个 DATA 包以后就会向设备发送一个 ACK 握手信号。
中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传输。
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 设备驱动。
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 设备驱动
大家可以将光标放到图中“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”这两个配置项也是可以的。
完成以后重新编译 linux 内核并且使用得到的 zImage 启动开发板。启动以后插入 USB 鼠标,会有如图所示的提示信息:
从图可以看出,系统检测到了 Logitech(罗技)的鼠标,如果成功驱动的话就会在/dev/input 目录下生成一个名为 eventX(X=0,1,2,3…)的文件,这个就是我们前面讲的输入子系统,鼠标和键盘都是作为输入子系统设备的。笔者这里对应的就是/dev/input/event3 这个设备,使用如下命令查看鼠标的原始输入值,结果如图所示:
上图就是鼠标作为输入子系统设备的原始输入值,这里就不去分析了,我们在移植GUI 图形库以后就可以直接使用鼠标,比如 QT 等。
注意,有些鼠标可能会出现隔一段时间自动断开重连的现象,比如我测试的电脑城送的几块钱的罗技鼠标就会这样,我用 NXP 官方的 EVK 开发板测试也有这个问题!我自己用的雷蛇鼠标就不会有这种现象。但是,如果你一直使用这个鼠标,比如用 hexdump 命令一直查看鼠标上报值,或者在 QT 里面一直使用鼠标,那么这些鼠标就不会自动断开并重连。最后再来测试一下 USB 键盘,屏幕已经驱动起来了,所以我们可以直接将屏幕作为终端,然后接上键盘直接输入命令来进行各种操作。首先将屏幕设置为控制台,打开开发板根文件系统中的/etc/inittab 文件,然后在里面加入下面这一行:
tty1::askfirst:-/bin/sh
完成以后重启开发板,此时屏幕就会作为终端控制台,会有“Please press Enter to activatethis console.”这样提示,如图所示:
接上键盘,然后根据图中的提示,按下键盘上的 Enter(回车)键即可使能 LCD 屏幕控制台,然后我们就可以输入各种命令来执行相应的操作,如图所示:
U 盘实验
NXP 提供的 Linux 内核默认也已经使能了 U 盘驱动,因此我们可以直接插上去使用。但是我们还是需要学习一下如何手动配置 Linux 内核,使能 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 大容量存储设备
准备好一个 U 盘,注意 U 盘要为 FAT32 格式的!NTFS 和 exFAT 由于版权问题所以在 Linux下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。准备好以后将 U 盘插入到开发板 USB HUB 扩展出来的 HOST 接口上,此时会输出如图所示信息:
从图可以看出,系统检测到 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 盘文件。
至此 U 盘就能正常读写操作了,直接对/mnt/usb_disk 目录进行操作就行了。如果要拔出 U盘要执行一个 sync 命令进行同步,然后在使用 unmount 进行 U 盘卸载,命令如下所示:
sync //同步
cd / //如果处于/mnt/usb_disk 目录的话先退出来,否则卸载的时候提示设备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载
Linux 内核自带 OTG 实验
OTG 主机实验
系统重启成功以后就可以正常使用 USB OTG1 接口, OTG 既可以做主机,也可以做从机,做主机的话测试方法和上一小节一模一样,直接在 ALPHA 的 OTG HOST 接口上插入 USB 鼠标键盘、 U 盘等设备。
OTG 从机实验
OTG 从机就是将开发板作为一个 USB 设备连接到其他的主机上,这里我们来做两个 USB从机实验:模拟 U 盘以及 USB 声卡。
模拟 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/xxx/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_mass_storage.ko /home/xxx/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_mass_storage.ko /home/xxx/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 接口与电脑连接起来。连接好以后依次加载 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 盘就是我们开发板模拟的,如图所示:
我们可以直接在电脑上对这个 U 盘进行读写操作,实际上操作的就是插在开发板上的 U盘。操作完成以后要退出的话执行如下命令:
rmmod g_mass_storage.ko
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
注意,这里也是编译为驱动模块,配置完成以后重新编译内核,得到新的 zImage 和三个.ko驱动模块文件:
drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_uac1.ko
drivers/usb/gadget/legacy/g_audio.ko
将上述三个.ko 模块拷贝到开发板根文件系统中。拷贝完成以后使用新编译出来的 zImage 启动开发板,首先按照音频驱动章节讲解的方法配置 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”设备,如图所示:
图中的“AC Interface”就是开发板模拟出来的 USB 声卡,设置 windows,选择音频输出使用“AC Interface”, Windows10 设置如图所示:
一切设置好以后就可以从开发板上听到电脑输出的声音,此时开发板就完全是一个 USB 声卡设备了。
关于 USB 驱动就讲解到这里,本章并没有深入到 USB 驱动具体编写方式,只是对 USB 的协议做了简单的介绍,后面讲解了一下 Linux 内核自带的 USB HOST 和 DEVICE 驱动的使用,Linux 内核已经集成了大量的 USB 设备驱动。