分享免费的编程资源和教程

网站首页 > 技术教程 正文

Linux系统USB子系统2---数据结构篇

goqiw 2025-02-03 12:41:54 技术教程 42 ℃ 0 评论

USB协议相关结构体列表和目录结构:

---struct usb_bus: usb 总线定义

---struct bus_type: usb总线具体类型定义

--- struct usb_device: USB设备

------struct usb_driver: USB驱动

---struct usb_device_id: usb设备信息表

---struct usb_hub: usb hub设备

---struct usb_hcd: usb host设备定义

--- struct hc_driver:usb host 驱动

---struct usb_interface:usb 接口定义;

--- struct usb_host_interface: usb host接口定义

---struct usb_device_descriptor:设备描述符

---struct usb_config_descriptor:配置描述符

--- struct usb_interface_descriptor:接口描述符

---struct usb_endpoint_descriptor:端口描述符

---struct usb_string_descriptor:字符串描述符

--- struct hid_descriptor HID描述符

---struct usb_report_descriptor 报告描述符

--- struct urb USB 请求块数据结构


一、 linux系统下的USB总线/设备/驱动结构体说明

二、 USB描述符相关结构体说明

三、 USB数据结构体URB说明:



一、Linux系统USB总线/设备/驱动结构体说明


1.Bus、driver和device角色说明

Linux Driver Model里面有抽象出Bus,device和driver。Device和Driver都要归属于某一个bus,bus会根据device或driver的注册来遍历匹配的driver或device。

1) Bus:
---总线是处理器和设备之间的通道。总线有多种类型,每种总线可以挂载多个设备。如:usb、pci、i2c和platform(虚拟)等;

---总线上有两个重要的链表:1)设备(device)链表;2)驱动(driver)链表;
---每种总线下面可以挂载许多设备;
---每种总线下可以用很多驱动;

2) driver
驱动程序是在CPU运行时,提供操作的软件接口。所有的设备必须有与之配套驱动程序才能正常工作。一个驱动程序可以驱动多个类似或者完全不同的设备。

3) device
设备就是连接在总线上的物理实体。设备是有功能之分的。具有相同功能的设备被归到一个类,如输入设备(鼠标,键盘,游戏杆等)、输出设备等。

2.Device和driver两者关系;

两者有如下关系:

1) 每次出现一个设备就要向总线汇报,每次出现一个驱动,也要向总线注册

2) 系统初始化的时候,会扫描连接了哪些设备,为每一个设备建立起一个struct device的变量,并加入设备链表;

3) 每次注册一个驱动,就要准备一个struct device_driver结构的变量,并加入驱动链表;

4) 这样所有的设备都挂载到总线上,当加载驱动时,驱动就去总线上找到自己对应的设备,或者先把驱动注册上,来了一个设备就去总线找驱动。

5) 如果只有设备却没有对应的驱动,那么设备无法工作;如果只有驱动却没有设备,驱动也起不了任何作用。

3.USB子系统相关的结构体定义及说明

1) struct usb_bus /* include/linux/usb.h*/

usb_bus保存一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。以下是 struct usb_bus 的定义及其各个字段的详细说明:

 struct usb_bus {
    struct device *controller; /* 控制器的设备结构体 */
    struct device *self;  /* USB 总线的设备结构体 */
    int busnum;  /* USB 总线号 */
    char *bus_name;  /* USB 总线名称 */
    unsigned int is_b_host:1; /* 是否为 B 机主控制器 */
    struct usb_hcd *hcd;  /* 对应的 HCD 结构体 */
    struct list_head bus_list; /* 连接到总线上的所有 USB 总线的列表 */
    struct list_head devices;  /* 连接到这条总线的所有 USB 设备 */
    struct usb_device *root_hub; /* 根集线器的设备结构体 */
    int route_string_count;
    char *route_string;  /* 总线路由的字符串表示 */
    unsigned long route_can_sleep;
 
 /* ... ... */
};

作用:

a . 该结构体的使用通常涉及到USB设备的枚举、配置、数据传输等操作。

b. 在USB设备连接或断开时,内核会更新这个结构体的信息,以反映当前USB总线的状态。

2) struct bus_type:

struct bus_type 是 Linux 内核中用于表示一种特定类型总线的数据结构。在内核中,每种总线类型(如 USB、PCI、I2C 等)都有自己的 bus_type 实例,用于定义和管理该总线上设备的注册、枚举和驱动程序绑定等操作。

struct bus_type {
         const char *name;        // 总线名称,如:usb, pci等字符串;
         struct bus_attribute *attributes; // 总线属性列表
         struct device_driver *drivers;   // 驱动程序列表
         struct device *devices;      	 // 设备列表
        // ... 其他字段 ...
};

 
 ----作用:

· 抽象层: struct bus_type 为设备驱动程序提供了一个抽象层,使得它们可以与不同类型的总线交互,而不需要关心底层的硬件细节。

· 设备管理: 通过 bus_type 结构,内核可以管理设备的生命周期,包括设备的添加、删除和驱动程序的绑定。

· 驱动程序匹配: 总线类型是驱动程序和设备匹配过程中的关键部分,它帮助内核确定哪个驱动程序可以管理特定的设备。

· sysfs 接口: 总线的属性可以通过 sysfs 接口暴露给用户空间,允许用户和应用程序获取总线和设备的信息。

3) struct usb_device

struct usb_device:保存一个USB设备的信息,包括设备地址,设备描述符,配置描述符等。一个 USB总线系统至少有一个主机控制器和一个根集线器,Linux系统支持多USB总线系统。

struct usb_device {
    struct usb_bus *bus;                // 指向所属USB总线的指针
    struct usb_device *parent;          // 指向父设备的指针,对于根设备为NULL
    struct usb_interface *interface;    // 指向连接到该设备的接口的指针
    struct usb_device_descriptor desc;   // USB设备描述符
    struct usb_configuration *config;   // 指向当前配置的指针
    struct usb_host_config *host_config; // 指向USB主机配置的指针
    struct usb_driver *driver;          // 指向设备驱动的指针
    struct usb_endpoint *ep0;           // 指向端点0的指针
    unsigned int devnum;                // 设备在总线上的编号
    unsigned char address;              // 设备的地址
    unsigned char dev_class;            // 设备类别
    unsigned char dev_subclass;         // 设备子类别
    unsigned char dev_protocol;         // 设备协议
    unsigned char speed;                // 设备速度
    unsigned char Tusbspeed;            // USB版本号
    unsigned int portsc;                // 端口状态寄存器
    unsigned char bConfigurationValue;  // 当前配置的值
    unsigned char bNumConfigurations;   // 配置数量
    unsigned char bNumInterfaces;       // 接口数量
    unsigned char bInterfaceNumber;     // 当前接口的编号
    unsigned char bAlternateSetting;    // 当前接口的备选设置
    unsigned char bNumEndpoints;        // 端点数量
    unsigned char bInterfaceClass;      // 接口类别
    unsigned char bInterfaceSubClass;   // 接口子类别
    unsigned char bInterfaceProtocol;   // 接口协议
    unsigned char bInterfaceAssociation; // 接口关联
    struct usb_ctrlrequest req;         // 控制请求缓冲区
    struct list_head config_list;        // 配置链表
    struct list_head interface_list;     // 接口链表
    struct list_head endpoint_list;      // 端点链表
    spinlock_t lock;                    // 自旋锁,用于保护设备结构体
    unsigned long flags;                // 标志位
    // 其他可能的字段...
};

 
其他说明
  • struct usb_device 结构体包含了USB设备的所有相关信息,包括设备描述符、配置、接口、端点等。
  • 该结构体是USB设备驱动模型的基础,所有与USB设备交互的代码都需要使用这个结构体。
  • 在内核中,struct usb_device 结构体通常与USB设备驱动程序相关联,用于管理设备的生命周期和功能。

4) struct usb_driver

struct usb_driver 是用于表示USB设备驱动程序的数据结构。它定义了驱动程序如何与USB核心通信,以及如何响应和处理USB设备的事件。以下是 struct usb_driver 结构体的详细说明:

struct usb_driver 
{
       /* 驱动程序的名称, 用于标识驱动程序*/
       const char *name;           
    
          /*用于识别设备的ID表, 包含了驱动程序支持的所有设备的供应商ID、产品ID、设备类等信息*/
          struct usb_device_id id_table;           
                    
          /*探测函数, 当USB核心发现一个新的USB设备时,它会调用这个函数来尝试绑定一个驱动程序到该设备。如果探测成功,函数返回0*/
          int (*probe)(struct usb_interface *, const struct usb_device_id *); 
          
          /*断开连接函数, 当USB设备被断开或被卸载时,USB核心会调用这个函数*/
          void (*disconnect)(struct usb_interface *);        
 
          /* 挂起函数, 当USB设备进入挂起状态时,USB核心会调用这个函数*/
          int (*suspend)(struct usb_interface *, int); 
 
          /*恢复函数, 当USB设备从挂起状态恢复时,USB核心会调用这个函数*/
          int (*resume)(struct usb_interface *, int); 
 
          // 其他的函数指针...
};
 
其他函数指针

除了上述的函数指针,struct usb_driver 可能还包含以下函数指针:

l ------ int (*pre_reset)(struct usb_interface *):在设备重置之前调用的函数指针。

int (*post_reset)(struct usb_interface *):在设备重置之后调用的函数指针。

int (*set_config)(struct usb_interface *, int):当设备的配置更改时调用的函数指针.

l int (*get_altsetting)(struct usb_interface *, int):当需要获取接口的替代设置时调用的函数指针

5)struct usb_device_id

a. 说明:

struct usb_device_id 结构体是USB驱动程序中用于设备匹配的重要数据结构。通过定义设备ID表,驱动程序可以告诉内核它支持哪些USB设备,从而在设备连接时能够正确地进行绑定和处理。

b.数据结构定义如下:

struct usb_device_id {
    __u16 idVendor;         // 设备的供应商ID
    __u16 idProduct;        // 设备的产品ID
    __u16 bcdDevice_lo;     // 设备版本号(低字节)
    __u16 bcdDevice_hi;     // 设备版本号(高字节)
    __u8 bDeviceClass;      // 设备类别
    __u8 bDeviceSubClass;   // 设备子类别
    __u8 bDeviceProtocol;   // 设备协议
    __u8 bInterfaceClass;   // 接口类别
    __u8 bInterfaceSubClass; // 接口子类别
    __u8 bInterfaceProtocol; // 接口协议
    __u32 driver_info;      // 驱动程序私有信息
    // 其他字段...
};

 

c. 使用示例:

在USB驱动程序中,通常会定义一个设备ID表,包含多个 struct usb_device_id 结构体的实例,用于匹配支持的设备。例如:

 static struct usb_device_id my_usb_table[] = {
    { USB_DEVICE(0x1234, 0x5678) }, // 供应商ID为0x1234,产品ID为0x5678的设备
    { USB_DEVICE_INFO(0x1234, 0x5678, 0x0100, 0x0200, 0x0300) }, // 设备版本等信息
    { } 
};
MODULE_DEVICE_TABLE(usb, my_usb_table);

6) struct usb_hcd

a.说明:

在Linux内核中,struct usb_hcd(USB Host Controller Driver)是用于表示USB主控制器驱动的核心数据结构。它提供了USB主控制器的相关信息,并为USB设备的管理和交互提供了接口。

b. struct usb_hcd 结构体定义

struct usb_hcd {
    struct usb_bus bus;                     // USB总线结构体
    struct device *self;                    // 指向主控制器设备的指针
    struct usb_host_config *host_config;    // 指向主机配置的指针
    struct list_head root_hub;              // 根集线器的设备列表
    struct usb_hcd *next;                   // 指向下一个HCD的指针
    spinlock_t lock;                         // 自旋锁,用于保护HCD的状态
    unsigned int flags;                      // 标志位
    // 其他可能的字段...
};

c. 作用:

struct usb_hcd 提供了USB主控制器驱动所需的基本信息和管理功能。通过这个结构体,usb core能够与USB主控制器进行交互,包括:

  • 管理连接和断开USB设备。
  • 处理USB设备的枚举和配置。
  • 处理USB数据传输。
  • 维护USB设备的状态信息。


7)struct hc_driver

a. 说明:

struct hc_driver 结构体定义了USB主机控制器驱动程序与USB核心进行交互的行为。通过实现这些函数指针,驱动程序能够处理设备的连接、断开、挂起和恢复等事件。USB核心使用这些函数来管理设备生命周期,并确保设备能够与系统中的其他组件正确地通信。

b. struct hc_driver 结构体定义

struct hc_driver {
    const char *description; // 驱动程序的描述
    int (*probe) (struct usb_hcd *hcd); // 探测函数
    void (*remove) (struct usb_hcd *hcd); // 移除函数
    int (*start) (struct usb_hcd *hcd); // 开始函数
    void (*stop) (struct usb_hcd *hcd); // 停止函数
    int (*reset) (struct usb_hcd *hcd); // 重置函数
    int (*start_port_reset) (struct usb_hcd *hcd, unsigned int portnum); // 开始端口重置函数
    int (*start_root_hub) (struct usb_hcd *hcd); // 开始根集线器函数
    int (*stop_root_hub) (struct usb_hcd *hcd); // 停止根集线器函数
    int (*port_handed_over) (struct usb_hcd *hcd, int portnum); // 端口交出函数
    int (*port_reset) (struct usb_hcd *hcd, unsigned int portnum); // 端口重置函数
    int (*port_suspend) (struct usb_hcd *hcd, unsigned int portnum, bool suspend); // 端口挂起函数
    int (*port_resume) (struct usb_hcd *hcd, unsigned int portnum); // 端口恢复函数
    int (*get_root_hub_descriptor) (struct usb_hcd *hcd, struct usb_hub_descriptor *desc); // 获取根集线器描述符函数
    int (*hub_status_data) (struct usb_hcd *hcd, char *buf); // 获取集线器状态数据函数
    int (*port_status) (struct usb_hcd *hcd, char *buf, unsigned int portnum); // 获取端口状态函数
    int (*submit_urb) (struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); // 提交URB函数
    int (*address_device) (struct usb_hcd *hcd, unsigned int portnum, u8 address); // 地址设备函数
    int (*allocate_bandwidth) (struct usb_hcd *hcd, struct usb_host_endpoint *ep, int bandwidth); // 分配带宽函数
    int (*deallocate_bandwidth) (struct usb_hcd *hcd, struct usb_host_endpoint *ep); // 释放带宽函数
int (*get_frame_number) (struct usb_hcd *hcd); // 获取帧编号函数
/*  USB 传输请求1: 将 URB(USB Request Block)添加到队列中,等待传输*/
    int (*urb_enqueue)(struct usb_hcd *hcd,    struct urb *urb, gfp_t mem_flags);
/*  USB 传输请求2:从队列中取出一个 URB 并开始传输*/
int (*urb_dequeue)(struct usb_hcd *hcd,  struct urb *urb, int status);   
 // 其他可能的函数指针...
};

 
C. 主要作用

--- 定义接口:struct hc_driver 提供了一组函数指针,这些指针指向与 USB 主机控制器相关的操作,如初始化、提交 URB(USB Request Block)、中断处理等。这使得不同的主机控制器可以实现相同的接口,从而统一管理。

l ---设备管理: 通过 hc_driver,内核可以管理 USB 设备的连接、断开和枚举等操作。它负责识别和处理连接到主机控制器的 USB 设备。

l ---控制传输: hc_driver 定义了用于处理控制传输的函数,这些函数负责处理 USB 设备的请求和响应。

l ---数据传输: 提供提交和处理 URB 的方法,以支持数据的发送和接收。这包括批量、等时和中断数据传输。

l ---中断处理:定义中断服务例程,用于处理来自 USB 设备的中断请求。

l ----资源管理:管理与主机控制器相关的资源,如内存分配、端点配置等。


8)struct usb_interface

a. 说明:

Linux内核中,struct usb_interface 是用于表示USB接口的结构体。每个USB设备可以有一个或多个接口,每个接口可以支持不同的功能或协议。struct usb_interface 提供了描述和管理这些接口的必要信息。struct usb_interface 是USB接口管理的重要数据结构。它帮助驱动程序描述和管理USB设备的接口,通过与USB核心的交互,驱动程序可以有效地控制和操作USB设备的各个接口。

b. struct usb_interface 结构体定义

struct usb_interface {
    struct usb_device *dev;                // 指向所属USB设备的指针
    struct usb_host_interface *altsetting;  // 指向当前选择的接口设置
    unsigned int num_altsetting;            // 备用设置的数量
    struct usb_host_interface *cur_altsetting; // 当前选择的备用设置
    struct usb_driver *driver;              // 指向接口驱动的指针
    void *dev_id;                           // 与设备相关的私有数据
    struct list_head list;                  // 链表,用于连接多个接口
    // 其他可能的字段...
};


c. 功能和用途
 struct usb_interface 结构体用于管理和描述USB设备的接口。以下是其主要功能和用途:

· 描述接口:提供有关接口的详细信息,如接口的类别、子类别、协议等。

· 管理备用设置:允许驱动程序在不同的备用设置之间切换,从而支持不同的功能或数据流。

· 驱动程序绑定:允许驱动程序与特定的接口进行绑定,以便正确处理该接口的请求和数据传输。

· 与USB设备交互:提供与设备的交互接口,使驱动程序能够发送控制请求、读取数据等。


9) struct usb_host_interface

a.说明:

usb_host_interface结构体主要用于描述USB接口的详细信息,包括接口的描述符、类驱动、USB驱动以及该接口下的所有端点信息。通过这个结构体,可以方便地管理和操作USB接口,包括设置、配置和传输数据等操作。

usb_host_interface和usb_interface都是USB接口类型,但usb_host_interface比较特殊,用于支持 USB OTG 协议;而 usb_interface 是通用的 USB 接口类型,用于连接 USB 设备到主机控制器。

b.结构体定义:

 struct usb_host_interface {
    struct usb_interface_descriptor desc;  // 接口描述符
    struct usb_host_endpoint *endpoint;     // 指向端点数组的指针
    struct usb_host_interface *altsetting;  // 可选的替代设置
    int num_altsetting;                     // 替代设置的数量
};

c. 用途

---USB 设备驱动: usb_host_interface 结构体在 USB 设备驱动中被广泛使用,驱动程序可以通过这个结构体访问和操作 USB 设备的接口和端点。

---设备描述: 它帮助驱动程序获取和解析 USB 设备的接口描述信息,以便于正确地与设备进行交互。

· ---多接口支持: 当一个 USB 设备有多个接口时,这个结构体可以帮助管理这些接口及其替代设置。


10) struct usb_hub

a. 说明:

usb_hub 是 Linux 内核中用于表示 USB 集线器的数据结构。在 USB 集线器中,它会连接多个 USB 设备,并允许它们共享同一个 USB 主控制器。 总之,usb_hub 结构是 Linux 内核中处理 USB 集线器逻辑的核心数据结构,它负责管理集线器的端口、监控设备连接和枚举过程,以及与 USB 设备的交互。

b. 结构定义(drivers/usb/core/hub.h):

struct usb_hub {
    struct usb_device *hub; // 指向集线器自身的USB设备结构体
    struct usb_device **ports; // 指向所有端口的指针数组
    int port_count;  // 端口数量
    struct usb_controller *controller; // 集线器控制的USB控制器
    spinlock_t lock; // 用于同步访问的锁
    struct usb_device *self; // 指向集线器自身的USB设备
    struct usb_device *child; // 指向集线器下的第一个设备
    unsigned char descriptor[18]; // 集线器的USB描述符
    struct usb_device *parent; // 指向集线器的父设备
    int overcurrentdetected; // 检测到过流
    int lpm_support; // 支持低功耗模式
    int lpm_active; // 当前是否处于低功耗模式
    int lpm_seen; // 是否检测到低功耗模式的设备
    int overcurrentReporting; // 是否报告过流
    unsigned int port_status[USB_MAX_PORTS]; // 每个端口的当前状态
    unsigned int port_change[USB_MAX_PORTS]; // 每个端口的变更状态
   struct delayed_work reset_work; // 重置工作的延迟工作结构体
   // ... 其他成员 ...
};
 
c.作用:

· ---集线器管理: usb_hub 结构用于管理 USB 集线器的状态,包括端口状态、重置操作、低功耗模式等。

---设备连接: 当 USB 设备连接到集线器的端口时,usb_hub 结构负责检测连接、更新状态并通知相应的设备驱动程序。

---设备枚举: 当 USB 设备被连接到集线器时,集线器会进行枚举过程,usb_hub 结构在这个过程中扮演重要角色。

---端口监控: usb_hub结构监控每个端口的连接状态,并在端口状态发生变化时做出响应。


二、USB描述符相关结构体:

1. USB描述符

USB描述符信息存储在USB设备中,在枚举过程中,USB主机会向USB设备发送GetDescriptor请求,USB设备在收到这个请求之后,会将USB描述符信息返回给USB主机,USB主机分析返回来的数据,判断出该设备是哪一种USB设备,建立相应的数据链接通道。

通用的USB描述符信息包括设备描述符、配置描述符、接口描述符和端点描述符,具体不同的USB设备还包括其它类型的描述符,例如,USB鼠标、键盘还包括HID描述符和报告描述符,还有可能包括字符串描述符,所有的描述符信息都是通过发送GetDescriptor请求得到的,但是USB设备也不知道你要获取的是哪种描述符,所以还需要在GetDescriptor请求中指定描述符的类型以及描述符的长度,这样USB设备才能正确的返回描述符信息。

2. USB 总线,配置,接口,设置,endpoint描述符的关系:

1) 一个USB配置有多个接口(Interface);

2)一个接口有多个设置;(接口用struct usb_interface表示)

3)接口里的设置使用struct usb_host_interface表示;(根据接口统一编号 )

4)每个设置都会有endpoint描述;

5) endpoint属于设置;用来传输数据,且为单工(要么为input, 要么为output, endpoint 0除外),每次传输的大小 不一样,都由endpoint 描述来描述的;

3. 通过命令lsusb –v -d vid:pid列出指定USB设备的各个描述符信息:

1).struct usb_device_descriptor

a.说明:

struct usb_device_descriptor 是 Linux 内核中用于描述 USB 设备的基本属性的数据结构。当 USB 设备连接到计算机时,它会发送其设备描述符,这个描述符包含了设备的基本信息,如设备类、子类、协议、制造商、产品名称和序列号等。struct usb_device_descriptor 是 USB 设备通信中非常重要的一个部分,它为操作系统和用户提供了设备的基本信息,是设备识别和驱动程序加载过程中的关键数据结构。

b.结构体定义:

在 Linux 内核中,usb_device_descriptor 的定义大致如下:

struct usb_device_descriptor {
     /*描述符的总长度,以字节为单位。对于设备描述符,这个值固定为18。*/
     __u8 bLength; 
     
     /*描述符类型,为USB_DT_DEVICE,这个值固定为0x01,*/
     __u8 bDescriptorType;

     /* USB 版本号,以二进制编码的十进制数表示。例如,0x0200 表示 USB 2.0*/
     __le16 bcdUSB;

     /*设备类代码,用于标识设备的主要功能类别*/
     __u8 bDeviceClass;

    /*设备子类代码,用于进一步细分设备类*/
    __u8 bDeviceSubClass;

    /*设备协议代码,用于指定与设备通信所需的协议*/
     __u8 bDeviceProtocol;

    /*第一个端点(通常是端点 0)的最大数据包大小,以字节为单位*/
    __le16 bMaxPacketSize0;
   
    /*制造商的供应商 ID */
    __u8 bVendorID;

    /*设备的产品 ID */
    __u8 bProductID;

   /*设备序列号,通常是可配置的或由制造商提供的*/
   __u8 bDeviceSerialNumber[18];

   /*产品字符串索引,用于指向包含产品名称的字符串描述符*/
   __u8 iProduct;

    /*供应商字符串索引,用于指向包含制造商名称的字符串描述符。*/
    __u8 iVendor;

    /*序列号字符串索引,用于指向包含序列号的字符串描述符*/
    __u8 iSerialNumber;

   /*设备支持的配置数量*/
    __u8 bNumConfigurations;
    // ... 其他可能的字段 ...
};

c. 用途:

---设备识别: 当 USB 设备连接到计算机时,操作系统会读取设备描述符来识别设备类型和制造商。

---驱动程序加载: 根据设备描述符中的信息,操作系统可以加载适当的驱动程序来与设备通信。

---用户界面: 设备描述符中的信息也可以用于构建用户界面,显示给用户设备名称、制造商和其他相关信息。

---配置和枚举: 设备描述符中的配置数量字段指示设备支持的配置数量,操作系统会根据这些配置来枚举设备。


2. struct usb_config_descriptor

a.说明:

struct usb_config_descriptor 是 Linux 内核中用于描述 USB 配置的元数据的数据结构。每个 USB 设备可以有多个配置,每个配置包含一组接口描述符和端点描述

b. 结构定义

struct usb_config_descriptor {
     /*描述符的总长度,以字节为单位。对于配置描述符,这个值固定9。*/
     __u8 bLength;

     /*描述符类型,对于配置描述符,这个值固定为2*/
    __u8 bDescriptorType;
   
    /*整个配置描述符的总长度,以字节为单位。包括此字段在内的所有字段都应使用大端序编码*/ 
    __le16 wTotalLength;

    /*此配置中的接口数量*/
    __le16 bNumInterfaces;

    /*此配置的唯一标识符。每个配置都有一个唯一的值。*/
    __u8 bConfigurationValue;

    /*如果此字段被设置为 1,则表示这是一个可重置的配置。否则,它是一个不可重置的配置*/
     __u8 iConfiguration;

     /*每个接口的最大功率消耗,以毫瓦为单位。如果此字段未被设置,则默认为 0xff */
      __u8 bMaxPower;
     
// ... 其他可能的字段 ...
};

c. 用途

· ---设备识别: 当 USB 设备连接到计算机时,操作系统会读取设备的配置描述符来确定其支持的接口和端点的数量以及它们的属性。这有助于操作系统加载适当的驱动程序来与设备通信。

---用户界面: 通过解析设备的配置描述符,操作系统可以构建用户界面,显示给用户可用的接口和端点的信息。例如,用户可以选择连接哪些接口或使用哪些功能。

---电源管理: bMaxPower 字段提供了有关设备功耗的信息,这对电源管理和节能非常重要。例如,如果一个 USB HUB 支持低功耗模式,那么它的 bMaxPower 可以设置为较低的值,从而降低功耗并延长电池寿命。


3. struct usb_interface_descriptor

a.说明:
此结构体用来描述USB设备的接口描述符。接口描述符包含了接口的信息,如接口的编号、类别、端点等。


b.结构体:

struct usb_interface_descriptor {
    __u8 bLength;               // 描述符的长度
    __u8 bDescriptorType;       // 描述符类型,0x04代表接口描述符
0
    __u8 bAlternateSetting;     //接口的替代设置编号
16
    __u8 bInterfaceClass;       // 接口类代码, 用于标识接口的功能类别。例如,音频设备、通信设备、人机接口设备等都有各自的类代码
    __u8 bInterfaceSubClass;    // 接口子类代码,用于进一步细分接口的类。例如,某些音频设备的子类可能表示音频控制、音频流等
    __u8 bInterfaceProtocol;    // 接口协议代码,用于指定与接口通信所需的协议。例如,某些 HID 设备可能使用特定的协议进行数据传输
    __u8 iInterface;            // 接口字符串索引,指向包含接口名称的字符串描述符的索引
};
   

c. 用途

· ---设备枚举: 当 USB 设备连接到系统时,操作系统会读取接口描述符以获取接口的基本信息。

---驱动程序绑定: 接口描述符中的类、子类和协议信息用于确定哪个驱动程序应该处理该接口。

---端点配置: bNumEndpoints 字段用于识别和配置该接口使用的端点,确保数据可以正确传输。

---用户界面: 通过 iInterface 字段,操作系统可以向用户显示接口名称,以提供更友好的用户体验。

---多功能接口: 允许设备通过替代设置支持不同的功能或模式。例如,一个 USB 音频设备可能有一个用于音频输入的接口和一个用于音频输出的接口。


4. struct usb_endpoint_descriptor

a.说明:

struct usb_endpoint_descriptor 是 Linux 内核中用于描述 USB 端点属性的数据结构。USB 端点是 USB 设备与主机之间进行数据传输的逻辑通道。每个 USB 设备可以具有多个端点,每个端点可以用于不同类型的数据传输(如控制、数据和中断传输)。

b.结构定义:

struct usb_endpoint_descriptor {
    __u8 bLength;          //描述符的长度,对于端点描述符这个值通常为7  
    __u8 bDescriptorType;  // 端点描述符类型,固定为 5
    __u8 bEndpointAddress; // 端点地址,包含了端点编号和方向,最高位(第 7 位)表示方向(0 表示主机到设备,1 表示设备到主机),其余位表示端点编号;
    __u8 bmAttributes;     // 端点属性,包含端点的类型;0x00:控制端点;0x01:等时端点;0x02:中断端点;0x03:批量(bulk)端点;
    __le16 wMaxPacketSize;     // 每个数据包的最大大小,对于 USB 2.0,最大值为 64 字节;对于 USB 3.0,最大值可以更大
__u8 bInterval;            //轮询间隔,单位是毫秒。这个字段主要用于中断和等时端点,表示主机向设备请求数据的频率    
 
// ... 其他可能的字段 ...
};

c. 用途

· ---数据传输配置: USB 设备在与主机通信时,使用端点进行数据传输。端点描述符提供了关于每个端点的数据包大小、类型和地址的信息。

--- 接口识别: 端点描述符帮助操作系统识别设备的不同数据传输通道,从而正确配置与设备的通信。

---驱动程序绑定: 通过解析端点描述符中的属性信息,操作系统可以选择合适的驱动程序来处理设备的特定端点。

---性能优化: 端点的最大包大小和轮询间隔可以影响数据传输的性能。开发者可以根据这些字段的值来优化数据传输。

---多种传输类型: USB 设备可以根据需要使用控制、批量、等时和中断传输,因此每个端点的属性可以帮助确定最佳的传输方式。


5. struct usb_string_descriptor

a.说明:

struct usb_string_descriptor 是 Linux 内核中用于描述 USB 字符串描述符的数据结构。字符串描述符通常包含设备的名称、制造商、产品描述、序列号等信息,这些信息以人类可读的字符串形式提供。USB 设备必须至少包含一个字符串描述符,通常是设备名称。

b. 结构定义

struct usb_string_descriptor {
    __u8 bLength;        //描述符的总长度,以字节为单位
    __u8 bDescriptorType; //描述符的类型,对于字符串描述符,这个值固定为3
    __le16 wData[1];     //字符串数据,以 Unicode 编码的 16 位字符形式存储;字符串数据是可变的,取决于实际存储的字符串长度       
};
 


c.用途

--- 设备信息: 字符串描述符用于提供设备信息,如设备名称、制造商、产品描述等,这些信息可以在用户界面中显示,以便用户识别设备。

· ---语言支持: USB 设备可以支持多个语言,每个语言可以有一个对应的字符串描述符。bDescriptorType 字段可以包含一个语言ID,用于标识字符串所使用的语言。

· ---用户界面: 操作系统和应用程序可以使用这些字符串描述符来构建用户界面,例如在设备列表中显示设备名称。


d. 示例

以下是一个简单的 usb_string_descriptor 示例,它表示一个设备名称为 "USB Device" 的字符串描述符:

struct usb_string_descriptor {
    __u8 bLength = 12;         // 字符串长度 + 2
    __u8 bDescriptorType = 3;  // 字符串描述符类型
    __le16 wData[] = {         // Unicode 编码的字符串 "USB Device"
        0x00, 'U', 0x00, 'S', 0x00, 'B', 0x00, ' ', 0x00, 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e'
    };
};

6.struct hid_descriptor HID描述符:

a. 说明:

在Linux内核中,struct hid_descriptor 结构体用于描述一个USB HID(Human Interface Device,人机接口设备)的附加信息。这个结构体通常在USB接口描述符之后出现,用于提供HID特定的信息;HID描述符提供了关于HID设备功能的重要信息,例如键盘、鼠标或游戏控制器等。它通常与报告描述符一起使用,后者定义了设备如何接收和发送数据。

b. 结构体定义:

struct hid_descriptor {
  __u8  bLength;//描述符长度
  __u8  bDescriptorType;//描述符类型,HID描述符的类型为0x21(HID_DESCRIPTOR_TYPE)
  __le16 bcdHID; //所遵循的HID协议版本
  __u8  bCountryCode;//国家代码,用于指示设备支持的区域设置
  __u8  bNumDescriptors;//下级描述符数量,通常至少需要一个报告描述符
  struct hid_class_descriptor desc[1];//下级描述符类型,例如报告描述符
  //其它描述符
} 

7. struct usb_report_descriptor

a.说明:

在USB(Universal Serial Bus)规范中,报告描述符(Report Descriptor)是用于描述HID(Human Interface Device)设备如何发送和接收数据的结构。报告描述符是HID类设备特有的,它定义了设备的报告格式,包括按键、滑动条、旋钮等输入设备的具体布局和功能。

b.结构定义:

 struct usb_report_descriptor {
     u8 bLength; // 描述符的长度
     u8 bDescriptorType; // 描述符类型,对于报告描述符是USB_DT_REPORT
     u16 wReportSize; // 报告字段的大小(以位为单位), 这个值定义了单个报告条目的最大大小
     u16 wReportCount; // 报告条目的数量, 即设备可以发送或接收的不同报告的数量
     u8 bReportType; // 报告类型,通常是INPUT(0x01)、OUTPUT(0x02)和FEATURE: 特性报告类型,用于描述控制设备功能的数据。
     u8 bReportID; //报告标识符,用于区分不同的报告条目
     // ... 接下来是报告数据 ...
};

三、 Urb数据结构

  1. 作用:

struct urb 是 Linux 内核中用于表示 USB 请求块(USB Request Block)的数据结构。它是 USB 驱动程序与 USB 子系统之间进行通信的核心结构。urb 用于描述从主机到设备或从设备到主机的数据传输请求。

类似比喻如下,usb总线就像一条高速公路,货物、人物之类的可以看成是系统与设备交互的具体数据,而urb就可以看成是汽车。USB的endpoint有4种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是汽车(urb)可以运载这四种数据,不过需要先告诉司机运送的是什么,目的地是哪里。

2. 数据结构(include/linux/usb.h):

struct urb {
    struct list_head        urb_list;        // 链接到其他 URB 的链表, 用于管理 URB 的列表
    struct usb_device       *dev;            // 指向相关 USB 设备的指针, 指示该 URB 所属的 USB 设备
    void                    *context;        // 用户定义的上下文指针, 可以在 URB 完成时用于传递特定的信息
    int                     status;          // 传输状态, 表示 URB 的当前状态。成功时为 0,失败时通常为负值
    unsigned char           *transfer_buffer; //数据传输缓冲区的指针,存储要发送或接收的数据
    unsigned int            transfer_buffer_length; // 传输缓冲区长度
    unsigned int            actual_length;   //实际传输的字节数,表示在完成传输后实际传输的数据量
    unsigned int            number_of_packets; // 数据包数量, 适用于批量和等时传输
    struct usb_sg_request   *sg;             // SG(Scatter-Gather)请求, 用于描述分散的缓冲区
    unsigned int            pipe;            // 端点管道信息, 表示传输的方向和端点。
    unsigned char           timeout;         // 超时时间, 指示传输请求的最大等待时间

    unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ,这个变量可被设置为不同的位值,根据这个位,usb驱动可以设置urb传输的状态*/

   /* 发送数据到设备或从设备接收数据的缓冲区 *,指向缓冲区的指针,这个指针可以是OUT urb 或者是In urb。*/
    void *transfer_buffer; 
     dma_addr_t transfer_dma; /*用来以DMA方式向设备传输数据的缓冲区*/
    int transfer_buffer_length;/*transfer_buffer或transfer_dma指向缓冲区的大小=wMaxPacketSize,USB2.0 全时速端点长度分别包括:8、16、32、64字节 */                     
    int actual_length; /* URB结束后,发送或接收数据的实际长度 */
    unsigned char *setup_packet; /* 指向控制URB的设置数据包的指针*/
    dma_addr_t setup_dma; 		/*控制URB的设置数据包的DMA缓冲区*/
    // ... 其他字段 ...
};


3 .URB处理流程

1).在USB设备驱动中创建URB

struct urb *usb_alloc_urb(int iso_packets,int mem_flags);

2).初始化传输端点类型

---void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

---void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

---void usb_fill_int_urb(struct urb *urb,struct usb_device *dev, unsigned int pipe, void *transfer_buffer,intbuffer_length, usb_complete_t complete, void*context, int interval);

3).USB设备驱动将URB提交给USB核心

int usb_submit_urb(struct urb *urb, intmem_flags);

---GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。

---GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志;

---GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。

4).USB核心将URB提交给USB主机控制器驱动

5).USB主机控制器处理,进行一次数据传送

6).当一次URB传输完成后,USB主机控制器驱动再原路返回通知USB设备驱动

4. 示例

  一个简单的示例可能如下所示:
struct urb *my_urb;
my_urb = usb_alloc_urb(0, GFP_KERNEL); // 分配 URB
my_urb->dev = my_usb_device; // 设置设备
my_urb->transfer_buffer = my_buffer; // 设置传输缓冲区
my_urb->transfer_buffer_length = BUFFER_SIZE; // 设置缓冲区长度
my_urb->complete = my_urb_complete_callback; // 设置完成回调
usb_submit_urb(my_urb, GFP_KERNEL); // 提交 URB

在这个例子中,my_urb 被分配并配置,用于与 USB 设备进行数据传输。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表