Linux 驱动开发之USB设备分析1(基于Linux6.6)---基本概念
一、基本概念
1.1、USB 设备基本概念
USB 设备由多个不同层次的元素组成,以下是每个层次的简要说明:
1.USB 设备 (Device)
USB 设备是通过 USB 总线与主机(通常是计算机)连接的硬件设备。每个 USB 设备都具有唯一的设备地址,并且每个设备都可能包含多个配置。
- USB 设备地址:每个连接到 USB 总线的设备都有一个唯一的设备地址(通常是 1 到 127 之间的数字)。设备地址由 USB 主控制器分配。
2.配置 (Configuration)
每个 USB 设备可以有多个配置,每个配置都定义了设备的功能和特性。配置包含多个接口,而一个设备的活动配置决定了设备的工作模式。
- 配置描述符:每个配置都由配置描述符(Configuration Descriptor)定义,描述符中包含该配置的电源要求、接口数量等信息。
3.接口 (Interface)
接口是设备中实现特定功能的一部分。例如,一个多功能的 USB 设备(如 USB 摄像头)可能包含多个接口,每个接口负责不同的功能(例如视频、音频等)。
- 接口描述符:每个接口由接口描述符(Interface Descriptor)定义,描述符包含该接口的特性、协议等信息。
4.端点 (Endpoint)
每个接口可以有多个端点,用于数据传输。端点是 USB 设备和主机之间交换数据的最小单位。每个端点有一个方向(输入或输出),以及一个传输类型(控制、批量、等时、同步)。
- 端点描述符:每个端点由端点描述符(Endpoint Descriptor)定义,描述符包括端点的地址、方向、传输类型和最大包大小等信息。
5.端点类型 (Endpoint Transfer Types)
端点类型定义了数据传输的方式,常见的端点类型有:
- 控制传输(Control Transfer):用于发送控制命令,如设备初始化、请求设备状态等。
- 批量传输(Bulk Transfer):用于大容量数据传输,通常用于存储设备。
- 等时传输(Isochronous Transfer):用于实时数据传输,要求数据传输有固定时间间隔,常用于音频和视频设备。
- 同步传输(Interrupt Transfer):用于小量、周期性的数据传输,常用于键盘、鼠标等设备。
1.2、USB 设备、配置、接口、端点的关系
在 Linux 内核中,USB 设备是通过多个层次结构组织的。这些层次结构从设备本身到每个端点提供了一种层次化的方式来管理设备。
以下是 USB 设备、配置、接口和端点之间的层次关系:
- 设备:一个 USB 设备(例如一个 USB 键盘或 USB 存储设备)可以有多个配置。
- 配置:每个设备可以包含多个配置(每个配置定义了不同的工作模式)。在一个时刻,设备只能激活一个配置。
- 接口:每个配置下可以包含多个接口。每个接口负责实现设备的一种功能。
- 端点:每个接口下可以有多个端点。端点是实际传输数据的通道,通常每个端点都负责传输特定类型的数据。
1.3、USB 设备层次结构图
以下是 USB 设备结构的示意图,帮助理解设备、配置、接口和端点之间的关系:
USB 设备
└── 配置 1
├── 接口 1
│ ├── 端点 1 (IN)
│ ├── 端点 2 (OUT)
│ └── 端点 3 (IN)
├── 接口 2
│ ├── 端点 1 (IN)
│ └── 端点 2 (OUT)
└── 接口 3
├── 端点 1 (IN)
└── 端点 2 (OUT)
└── 配置 2
├── 接口 1
└── 接口 2
1.4、Linux 中的 USB 设备管理
在 Linux 内核中,USB 设备的管理通过 usbcore
子系统来完成。每当一个 USB 设备被插入时,内核会执行以下操作:
- 检测设备:内核通过 USB 总线协议检测到新的设备。
- 读取设备描述符:内核读取设备的描述符(例如设备描述符、配置描述符、接口描述符、端点描述符等),获取设备的基本信息。
- 选择配置:内核会选择一个配置并激活该配置。通常,Linux 驱动会选择第一个有效配置。
- 初始化驱动:内核根据设备的接口类型和端点信息加载相应的设备驱动(例如存储驱动、音频驱动等)。
USB 设备在 Linux 中的管理是通过设备、配置、接口和端点四个层次组织的,每个层次都有不同的功能和作用。理解这些概念对于开发 USB 设备驱动、调试 USB 设备问题以及了解 USB 通信机制非常重要。
- USB 设备:物理连接到主机的硬件。
- 配置:设备的不同工作模式。
- 接口:设备的具体功能或服务。
- 端点:数据传输的具体通道。
二、USB架构
USB设备使用各种描述符来说明其设备架构,包括设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符,他们通常被保存在USB设备的固件程序中。
2.1、设备描述符
设备代表一个USB设备,它由一个或多个配置组成。设备描述符用于说明设备的总体信息,并指明其所含的配置的个数。一个USB设备只能有一个设备描述符。
include/uapi/linux/usb/ch9.h
struct usb_device_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型编号
_ _le16 bcdUSB; //USB版本号
_ _u8 bDeviceClass; //USB分配的设备类code
_ _u8 bDeviceSubClass;// USB分配的子类code
_ _u8 bDeviceProtocol; //USB分配的协议code
_ _u8 bMaxPacketSize0; //endpoint0最大包大小
_ _le16 idVendor; //厂商编号
_ _le16 idProduct; //产品编号
_ _le16 bcdDevice; //设备出厂编号
_ _u8 iManufacturer; //描述厂商字符串的索引
_ _u8 iProduct; //描述产品字符串的索引
_ _u8 iSerialNumber; //描述设备序列号字符串的索引
_ _u8 bNumConfigurations; //可能的配置数量
} _ _attribute_ _ ((packed));
2.2、配置描述符
一个USB设备可以包含一个或多个配置,如USB设备的低功耗模式和高功耗模式可分别对应一个配置。在使用USB设备前,必须为其选择一个合适的配置。配置描述符用于说明USB设备中各个配置的特性,如配置所含接口的个数等。USB设备的每一个配置都必须有一个配置描述符。
include/uapi/linux/usb/ch9.h
struct usb_config_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型编号
_ _le16 wTotalLength; //配置所返回的所有数据的大小
_ _u8 bNumInterfaces; // 配置所支持的接口数
_ _u8 bConfigurationValue; //Set_Configuration命令需要的参数值
_ _u8 iConfiguration; //描述该配置的字符串的索引值
_ _u8 bmAttributes; //供电模式的选择
_ _u8 bMaxPower; //设备从总线提取的最大电流
} _ _attribute_ _ ((packed));
2.3、接口描述符
一个配置可以包含一个或多个接口,例如对一个光驱来说,当用于文件传输时,使用其大容量存储接口;而当用于播放CD时,使用其音频接口。接口是端点的集合,可以包含一个或多个可替换设置,用户能够在USB处于配置状态时改变当前接口所含的个数和特性。接口描述符用于说明设备中各个接口的特性,如接口所属的设备类及其子类等。USB设备的每个接口都必须有一个接口描述符。
include/uapi/linux/usb/ch9.h
struct usb_interface_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _u8 bInterfaceNumber; // 接口的编号
_ _u8 bAlternateSetting; //备用的接口描述符编号
_ _u8 bNumEndpoints; //该接口使用的端点数,不包括端点0
_ _u8 bInterfaceClass; //接口类型
_ _u8 bInterfaceSubClass; //接口子类型
_ _u8 bInterfaceProtocol; //接口所遵循的协议
_ _u8 iInterface; //描述该接口的字符串索引值
} _ _attribute_ _ ((packed));
2.4、端点描述符
端点是USB设备中的实际物理单元,USB数据传输就是在主机和USB设备各个端点之间进行的。端点一般由USB接口芯片提供,例如Freescale公司的MC68HC908JB8和MC9S12UF32。USB设备中的每一个端点都有唯一的端点号,每个端点所支持的数据传输方向一般而言也是确定的:或是输入(IN),或是输出(OUT)。也有些芯片提供的端点的数据方向是可以配置的,例如MC68HC908JB8包含有两个用于数据收发的端点:端点1和端点2。其中端点1只能用于数据发送,即支持输入(IN)操作;端点2既能用于数据发送,也可用于数据接收,即支持输入(IN)和输出(OUT)操作。而MC9S12UF32具有6个端点。
利用设备地址、端点号和传输方向就可以指定一个端点,并与它进行通信。端点的传输特性还决定了其与主机通信是所采用的传输类型,例如控制端点只能使用控制传输。根据端点的不同用途,可将端点分为两类:0号端点和非0号端点。
0号端点比较特殊,它有数据输入IN和数据输出OUT两个物理单元,且只能支持控制传输。所有的USB设备都必须含有一个0号端点,用作默认控制管道。USB系统软件就是使用该管道与USB逻辑设备进行配置通信的。0号端点在USB设备上的以后就可以使用,而非0号端点必须要在配置以后才可以使用。
根据具体应用的需要,USB设备还可以含有多个除0号端点以外的其他端点。对于低速设备,其附加的端点数最多为2个;对于全速/高速设备,其附加的端点数最多为15个。
include/uapi/linux/usb/ch9.h
struct usb_endpoint_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _u8 bEndpointAddress; //端点地址:0~3位是端点号,第7位是方向(0-OUT,1-IN)
_ _u8 bmAttributes; //端点属性:bit[0:1] 的值为00表示控制,为01表示同步,为02表示批量,为03表示中断
_ _le16 wMaxPacketSize; 本端点接收或发送的最大信息包的大小
_ _u8 bInterval;//轮询数据传送端点的时间间隔
//对于批量传送的端点以及控制传送的端点,此域忽略
//对于同步传送的端点,此域必须为1
_ _u8 bRefresh;
_ _u8 bSynchAddress;
} _ _attribute_ _ ((packed));
2.5、字符串描述符
在USB设备中通常还含有字符串描述符,以说明一些专用信息,如制造商的名称、设备的序列号等。它的内容以UNICODE的形式给出,且可以被客户软件所读取。对USB设备来说,字符串描述符是可选的。
include/uapi/linux/usb/ch9.h
struct usb_string_descriptor
{
_ _u8 bLength; //描述符长度
_ _u8 bDescriptorType; //描述符类型
_ _le16 wData[1];
} _ _attribute_ _ ((packed));
2.6、管道
在USB系统结构中,可以认为数据传输时在USB主机软件与USB设备的各个端点之间直接进行的,它们之间的连接称为管道。管道是在USB设备的配置过程中建立的。管道是对USB主机与USB设备间通信流的抽象,表示USB主机的数据缓冲区与USB设备的端点之间存在着逻辑数据传输,而实际的数据传输是由USB总线接口层来完成的。
管道与USB设备中的端点一一对应。一个USB设备含有多少个端点,其与USB主机进行通信时就可以使用多少条管道,且端点的类型决定了管道中数据的传输类型,例如中断端点对应中断管道,且该管道只能进行中断传输。不论存在着多少条管道,在各个管道中进行的数据传输都是相互独立的。
2.7、USB端点分类
USB 通讯的最基本形式是通过端点。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。
USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:
1. 控制传输(CONTROL)
- 用途:控制传输是 USB 协议的基础,用于设备与主机之间交换命令和设备状态,通常用于设备初始化、配置和状态查询等操作。
- 特点:
- 每个 USB 设备至少有一个控制端点,通常是端点 0。
- 控制传输包括三个阶段:设置请求阶段、数据阶段和状态阶段。
- 控制传输一般是短小的数据传输,通常用于命令和设备配置。
- 控制传输是双向的,既可以从主机到设备(OUT),也可以从设备到主机(IN)。
- 应用:设备的初始化和配置、获取设备信息、设置设备状态等。
2. 中断传输(INTERRUPT)
- 用途:中断传输用于设备定期报告状态,适用于那些需要频繁、周期性传输少量数据的设备。
- 特点:
- 数据传输是定时的,通常在短时间间隔内(几十毫秒到几百毫秒)。
- 通常用于报告设备的状态或输入事件,如键盘、鼠标、游戏控制器等。
- 延迟较低,确保数据及时传输。
- 数据量小,但要求周期性地进行传输。
- 应用:键盘、鼠标、游戏手柄、触摸屏等设备,实时报告设备状态或用户输入。
3. 批量传输(BULK)
- 用途:批量传输用于大数据量的传输,不要求实时性,通常用于大规模数据的传输,如文件传输、打印数据等。
- 特点:
- 没有严格的时间约束,适用于传输大数据量的设备,如USB存储设备、打印机等。
- 数据传输时会尽量高效,不会丢失数据,但如果带宽不足,可能会出现传输延迟。
- 支持大数据包,且传输速度较快。
- 不保证数据的实时性,也没有传输确认,因此,如果数据丢失会重新发送。
- 应用:USB 存储设备(如 USB 闪存、硬盘)、打印机、扫描仪等设备。
4. 等时传输(ISOCHRONOUS)
- 用途:等时传输用于实时数据流的传输,适用于音频、视频、语音通信等需要按固定时间间隔传输数据的设备。
- 特点:
- 保证数据按严格的时间要求传输,确保实时性。
- 通常用于音频和视频设备,如USB耳机、摄像头等。
- 即使在带宽紧张的情况下,也会尽量保证数据传输的连续性,不进行数据重传。
- 如果数据丢失或传输中断,会影响实时性能(例如,音频中断、视频卡顿)。
- 应用:USB 音频设备、USB 摄像头、麦克风、视频流等需要高实时性的设备。
控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。
struct usb_host_endpoint{
struct usb_endpoint_descriptor desc;//端点描述符
struct list_head urb_list;//此端点的URB对列,由USB核心维护
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char*extra;/* Extra descriptors */
int extralen;
int enabled;
};
当调用USB设备驱动调用usb_submit_urb提交urb请求时,将调用int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,对应数据结构struct usb_hcd )。