USB驱动之几个重要结构体分析

本文深入解析了USB设备在Linux内核中的表示方式,包括设备、配置、接口和端点的结构体,以及它们如何通过USB核心子系统进行交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

USB驱动之几个重要结构体分析

arm linux设备驱 2010-07-12 22:20:37 阅读192 评论0   字号:大中小 订阅

USB设备其实很复杂,但是Linux内核提供了一个称为USB core的子系统来处理了大部分的复杂工作,所以这里所描述的是驱动程序和USB core之间的接口。

USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。
对于这四个层次的简单描述如下:
   
设备通常具有一个或多个的配置
   
配置经常具有一个或多个的接口
   
接口通常具有一个或多个的设置
   
接口没有或具有一个以上的端点
设备
 很明显,地代表了一个插入的USB设备,在内核使用数据结构 struct usb_device来描述整个USB设备。(include/linux/usb.h

struct usb_device {
   int devnum; //设备号,是在USB总线的地址
   char devpath [16]; //用于消息的设备ID字符串
   enum usb_device_state state; //设备状态:已配置、未连接等等
   enum usb_device_speed speed; //设备速度:高速、全速、低速或错误
   struct usb_tt *tt; //处理传输者信息;用于低速、全速设备和高速HUB
   int ttport; //位于tt HUB的设备口
  
   unsigned int toggle[2]; //每个端点的占一位,表明端点的方向([0] = IN, [1] = OUT)  
   struct usb_device *parent; //上一级HUB指针
   struct usb_bus *bus; //总线指针
   struct usb_host_endpoint ep0; //端点0数据

struct device dev; //一般的设备接口数据结构
 
struct usb_device_descriptor descriptor; //USB
设备描述符
struct usb_host_config *config; //
设备的所有配置

struct usb_host_config *actconfig; //被激活的设备配置
struct usb_host_endpoint *ep_in[16]; //
输入端点数组
struct usb_host_endpoint *ep_out[16]; //
输出端点数组
  
char **rawdescriptors; //
每个配置的raw描述符
  
unsigned short bus_mA; //
可使用的总线电流
u8 portnum; //
父端口号
u8 level; //USB HUB
的层数
  
unsigned can_submit:1; //URB
可被提交标志
unsigned discon_suspended:1; //
暂停时断开标志
unsigned persist_enabled:1; //USB_PERSIST
使能标志
unsigned have_langid:1; //string_langid
存在标志
unsigned authorized:1;
unsigned authenticated:1;
unsigned wusb:1; //
无线USB标志

int string_langid; //字符串语言ID


/* static strings from the device */ //
设备的静态字符串

char *product; //产品名
char *manufacturer; //
厂商名
char *serial; //
产品串号
  
struct list_head filelist; //
此设备打开的usbfs文件

#ifdef CONFIG_USB_DEVICE_CLASS
 struct device *usb_classdev; //用户空间访问的为usbfs设备创建的USB类设备
#endif
#ifdef CONFIG_USB_DEVICEFS
 struct dentry *usbfs_dentry; //设备的usbfs入口
#endif
  
int maxchild; //
(若为HUB)接口数
struct usb_device *children[USB_MAXCHILDREN];//
连接在这个HUB上的子设备
int pm_usage_cnt; //
自动挂起的使用计数
u32 quirks;
atomic_t urbnum; //
这个设备所提交的URB计数
  
unsigned long active_duration; //
激活后使用计时


  #ifdef CONFIG_PM //电源管理相关
            struct delayed_work autosuspend; //自动挂起的延时
          struct work_struct autoresume; //(中断的)自动唤醒需求
            struct mutex pm_mutex; //PM的互斥锁 


            unsigned long last_busy; //最后使用的时间
            int autosuspend_delay;
            unsigned long connect_time; //第一次连接的时间
  
            unsigned auto_pm:1; //自动挂起/唤醒
            unsigned do_remote_wakeup:1; //远程唤醒
            unsigned reset_resume:1; //使用复位替代唤醒
          unsigned autosuspend_disabled:1; //挂起关闭
            unsigned autoresume_disabled:1; //唤醒关闭
             unsigned skip_sys_resume:1; //跳过下个系统唤醒
  #endif
  struct wusb_dev *wusb_dev; //(如果为无线USB)连接到WUSB特定的数据结构

};

配置
   
 一个USB设备可以有多个配置,并可在它们之间转换以改变设备的状态。比如一个设备可以通过下载固件(firmware)的方式改变设备的使用状态(我 感觉类似FPGACPLD),那么USB设备就要切换配置,来完成这个工作。一个时刻只能有一个配置可以被激活。Linux使用结构 struct usb_host_config 来描述USB配置。我们编写的USB设备驱动通常不需要读写这些结构的任何值。可在内核源码的文件include/linux/usb.h中找到对它们的 描述。
struct usb_host_config {
    struct usb_config_descriptor desc; //
配置描述符

char *string; /* 配置的字符串指针(如果存在) */
    struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; //
配置的接口联合描述符链表
    struct usb_interface *interface[USB_MAXINTERFACES]; //
接口描述符链表
    struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
    unsigned char *extra; /*
额外的描述符 */
    int extralen;
};
 
接口
    USB 端点被绑为接口,USB接口只处理一种USB逻辑连接。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要 一个以上的驱动程序。这可以在晕到死 差屁系统中看出,有时插入一个USB设备后,系统会识别出多个设备,并安装相应多个的驱动。
USB
接口可以有其他的设置,它是对接口参数的不同选择. 接口的初始化的状态是第一个设置,编号为0 其他的设置可以以不同方式控制独立的端点。

USB接口在内核中使用 struct usb_interface 来描述。USB 核心将其传递给USB驱动,并由USB驱动负责后续的控制。
struct usb_interface {
   
  struct usb_host_interface *altsetting; /* 包含所有可用于该接口的可选设置的接口结构数组。每个          

struct usb_host_interface 包含一套端点配置(即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序。*/

struct usb_host_interface *cur_altsetting; /* 指向altsetting内部的指针,表示当前激活的接口配置*/
    unsigned num_altsetting; /*
可选设置的数量*/

/* If there is an interface association descriptor then it will list the associated interfaces */
    struct usb_interface_assoc_descriptor *intf_assoc;

int minor; /* 如果绑定到这个接口的 USB 驱动使用 USB 主设备号, 这个变量包含由 USB 核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效。*/
    /*
以下的数据在我们写的驱动中基本不用考虑,系统会自动设置*/
    enum usb_interface_condition condition; /* state of binding */
    unsigned is_active:1; /* the interface is not suspended */
    unsigned sysfs_files_created:1; /* the sysfs attributes exist */
    unsigned ep_devs_created:1; /* endpoint "devices" exist */
    unsigned unregistering:1; /* unregistration is in progress */
    unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
    unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
    unsigned needs_binding:1; /* needs delayed unbind/rebind */
    unsigned reset_running:1;

struct device dev; /* 接口特定的设备信息 */
    struct device *usb_dev;
    int pm_usage_cnt; /* usage counter for autosuspend */
    struct work_struct reset_ws; /* for resets in atomic context */
};

struct usb_host_interface {
    struct usb_interface_descriptor desc; //
接口描述符

struct usb_host_endpoint *endpoint; /* 这个接口的所有端点结构体的联合数组*/
    char *string; /*
接口描述字符串 */
    unsigned char *extra; /*
额外的描述符 */
    int extralen;
};

端点
USB
通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。

一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:

控制CONTROL
制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.

中断INTERRUPT
每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.

批量BULK
量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass StorageUSB网络设备上。

等时ISOCHRONOUS
等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

端点在内核中使用结构 struct usb_host_endpoint 来描述,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的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_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
    __u8 bLength;
    __u8 bDescriptorType;

__u8 bEndpointAddress; /*这个特定端点的 USB 地址,这个8位数据包含端点的方向,结合位掩码 USB_DIR_OUT USB_DIR_IN 使用, 确定这个端点的数据方向。*/

__u8 bmAttributes; //这是端点的类型,位掩码如下

__le16 wMaxPacketSize; /*端点可以一次处理的最大字节数。驱动可以发送比这个值大的数据量到端点, 但是当真正传送到设备时,数据会被分为 wMaxPakcetSize 大小的块。对于高速设备, 通过使用高位部分几个额外位,可用来支持端点的高带宽模式。*/
    __u8 bInterval; //
如果端点是中断类型,该值是端点的间隔设置,即端点的中断请求间的间隔时间,以毫秒为单位

/* NOTE: these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8 bRefresh;
    __u8 bSynchAddress;
} __attribute__ ((packed));

#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */

/*
 * Endpoints
 */
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress
端点的 USB 地址掩码 */
#define USB_ENDPOINT_DIR_MASK 0x80 /* in bEndpointAddress
数据方向掩码 */


#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */

#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* bmAttributes 的位掩码*/
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
/*-------------------------------------------------------------------------*/
 

USB sysfs
由于单个 USB 物理设备的复杂性,设备在 sysfs 中的表示也非常复杂。物理 USB 设备(通过 struct usb_device 表示)和单个 USB 接口( struct usb_interface 表示)都作为单个设备出现在 sysfs 中,这是因为这两个结构都包含一个 struct device结构。以下内容是我的USB鼠标在 sysfs 中的目录树:

/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1 (表示 usb_device 结构)
.
|-- 3-1:1.0
(鼠标所对应的usb_interface
| |-- 0003:046D:C018.0003
| | |-- driver -> ../../../../../../../bus/hid/drivers/generic-usb
| | |-- power
| | | `-- wakeup
| | |-- subsystem -> ../../../../../../../bus/hid
| | `-- uevent
| |-- bAlternateSetting
| |-- bInterfaceClass
| |-- bInterfaceNumber
| |-- bInterfaceProtocol
| |-- bInterfaceSubClass
| |-- bNumEndpoints
| |-- driver -> ../../../../../../bus/usb/drivers/usbhid
| |-- ep_81 -


USB urb
USB request block
内核使用2.6.29.4

USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb struct urb 结构描述(include/linux/usb.h )。
 urb
以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。

一个 urb 的典型生命循环如下:
 
1)被创建;
 
2)被分配给一个特定 USB 设备的特定端点;
 
3)被提交给 USB 核心;
 
4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;
 
5)被 USB 主机控制器驱动处理, 并传送到设备;
 
6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。
 
 urb
也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。

--------------------------------------------------------------------------------

struct urb
 
struct urb {
    /* private: usb core and host controller only fields in the urb */
    struct kref kref;        /* URB
引用计数 */
    void *hcpriv;            /* host
控制器的私有数据 */
    atomic_t use_count;        /*
当前提交计数 */
    atomic_t reject;        /*
提交失败计数 */
    int unlinked;            /*
连接失败代码 */

/* public: documented fields in the urb that can be used by drivers */
    struct list_head urb_list;    /* list head for use by the urb's
                     * current owner */
    struct list_head anchor_list;    /* the URB may be anchored */
    struct usb_anchor *anchor;
    struct usb_device *dev;     /*
指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.*/
    struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */
    unsigned int pipe;        /*
这个 urb 所要发送到的特定struct usb_device的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/
    int status;            /*
urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */
    unsigned int transfer_flags;    /*
传输设置*/
    void *transfer_buffer;        /*
指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。 对控制端点, 这个缓冲区用于数据中转*/
    dma_addr_t transfer_dma;    /*
用于以 DMA 方式传送数据到 USB 设备的缓冲区*/
    int transfer_buffer_length;    /* transfer_buffer
或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个 USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/
    int actual_length;        /*
当这个 urb 完成后, 该变量被设置为这个 urb (对于 OUT urb)发送或(对于 IN urb)接受数据的真实长度.对于 IN urb, 必须是用此变量而非 transfer_buffer_length , 因为接收的数据可能比整个缓冲小*/
    unsigned char *setup_packet;    /*
指向控制urb的设置数据包指针.它在传送缓冲中的数据之前被传送(用于控制 urb*/
    dma_addr_t setup_dma;        /*
控制 urb 用于设置数据包的 DMA 缓冲区地址,它在传送普通缓冲区中的数据之前被传送(用于控制 urb*/
    int start_frame;        /*
设置或返回初始的帧数量(用于等时urb */
    int number_of_packets;        /*
指定urb所处理的等时传输缓冲区的数量(用于等时urb,在urb被发送到USB核心前,必须设置) */
    int interval;            /*urb
被轮询的时间间隔. 仅对中断或等时 urb 有效. 这个值的单位依据设备速度而不同. 对于低速和高速的设备, 单位是帧, 它等同于毫秒. 对于其他设备, 单位是微帧, 等同于 1/8 毫秒. urb被发送到 USB 核心之前,此值必须设置.*/
    int error_count;        /*
等时urb的错误计数,由USB核心设置 */
    void *context;            /*
指向一个可以被USB驱动模块设置的数据块. urb 被返回到驱动时,可在结束处理例程中使用. */
    usb_complete_t complete;    /*
结束处理例程函数指针, urb 被完全传送或发生错误,它将被 USB 核心调用. 此函数检查这个 urb, 并决定释放它或重新提交给另一个传输中*/
    struct usb_iso_packet_descriptor iso_frame_desc[0];
                    /*
(仅用于等时urbusb_iso_packet_descriptor结构体允许单个urb一次定义许多等时传输,它用于收集每个单独的传输状态*/
    };   

struct usb_iso_packet_descriptor {
    unsigned int offset;        /*
该数据包的数据在传输缓冲区中的偏移量(第一个字节为0 */
    unsigned int length;        /*
该数据包的传输缓冲区大小 */
    unsigned int actual_length;    /*
等时数据包接收到传输缓冲区中的数据长度 */
    int status;            /*
该数据包的单个等时传输状态。它可以把相同的返回值作为主struct urb 结构体的状态变量 */
};

 

linux内核中,用usb_hcd结构体描述usb主机控制器驱动,它包含usb主机控制器的“家务”信息,硬件资源,状态描述和用于操作主机控制器的hc_driver等。

struct usb_hcd {

         /** housekeeping**/

         struct usb_bus              self;                               /* hcd is-a bus */

         struct kref                    kref;                              /* reference counter */

         const char                  *product_desc;            /* 产品/厂商 字符串 */

         char                              irq_descr[24];               /* driver + bus # */

         struct timer_list  rh_timer;                                /* 驱动根集线器 polling */

         struct urb                     *status_urb;                 /* 当前状态urb */

         #ifdef CONFIG_PM

                   struct work_struct       wakeup_work;    /*用于远程唤醒 */

         #endif

         /** 硬件信息/状态**/

         const struct hc_driver  *driver;                        /* 硬件特定的钩子函数 */

         /* 需要被自动操作的标志 */

         unsigned long                flags;

         #define HCD_FLAG_HW_ACCESSIBLE       0x00000001

         #define HCD_FLAG_SAW_IRQ   0x00000002

         unsigned              rh_registered:1;                      /* 根集线器(hub)已被注册?*/

/* 下一个标志的采用只是“权宜之计”,当所有

hcds支持新的跟hcd轮询机制后将被移除*/

         unsigned              uses_new_polling:1;

         unsigned              poll_rh:1;                               /* 轮询跟hub状态?*/

         unsigned              poll_pending:1;                     /* 状态改变了吗? */

         unsigned              wireless:1;                              /* 无线usb hub?*/

         int                        irq;                                         /* 分配的中断号*/

         void __iomem               *regs;                            /* 设备内存或IO*/

         u64                      rsrc_start;                              /* 内存或IO 资源的开始位置 */

         u64                      rsrc_len;                                 /* 内存或IO资源的大小 */

         unsigned              power_budget;                       /* in mA, 0 = no limit */

#define HCD_BUFFER_POOLS   4

         struct dma_pool           *pool [HCD_BUFFER_POOLS];

         int                        state;

#define       __ACTIVE                   0x01

#define       __SUSPEND                0x04

#define       __TRANSIENT           0x80

#define       HC_STATE_HALT              0

#define       HC_STATE_RUNNING      (__ACTIVE)

#define       HC_STATE_QUIESCING   (__SUSPEND|__TRANSIENT|__ACTIVE)

#define       HC_STATE_RESUMING    (__SUSPEND|__TRANSIENT)

#define       HC_STATE_SUSPENDED  (__SUSPEND)

#define       HC_IS_RUNNING(state) ((state) & __ACTIVE)

#define       HC_IS_SUSPENDED(state) ((state) & __SUSPEND)

         /* more shared queuing code would be good; it should support

          * smarter scheduling, handle transaction translators, etc;

          * input size of periodic table to an interrupt scheduler.

          * (ohci 32, uhci 1024, ehci 256/512/1024).

          */

         /* 主机控制器驱动的私有数据*/

         unsigned long hcd_priv[0]

                            __attribute__ ((aligned (sizeof(unsigned long))));

};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值