Linux下USB内核之学习笔记(一)

USB内核(USB驱动,USBD )处于系统的中心,对于它进行研究是能够进行USB驱动开发(包括客户驱动和主机驱动)的第一步。它为客户端驱动和主机控制器驱动提供了主要数据结构和接口函数,主要有四类功能:客户端驱动管理,USB设备的配置和管理,主机控制器的管理,协议控制命令集和数据传输的管理。具体代码主要集中在linux/drivers/usb下的usb.c, usb.h中.
主要数据结构分析
主要有四个数据结构,分别是
USB设备usb_device,保存了一个USB设备的信息,包括设备地址,设备描述符,配置描述符,等等
USB总线系统usb_bus,保存了一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。一个USB总线系统肯定有一个主机控制器和一个根集线器。Linux支持多USB总线系统
客户端驱动程序usb_driver,保存了客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等
(USB Request Block)urb,是进行USB通信的数据结构。Linux的USB子系统只使用这么一种数据结构来进行USB通信,urb包含了建立任何 USB传输所需的所有信息,并贯穿于USB协议栈对数据处理的整个过程。
下面是对各部分进行详细分析。
struct usb_device {             //代表一个USB设备
      int devnum;              //分配的设备地址,1-127
      enum {
    USB_SPEED_UNKNOWN = 0,               /* enumerating */
    USB_SPEED_LOW, USB_SPEED_FULL,              /* usb 1.1 */
    USB_SPEED_HIGH                       /* usb 2.0 */
    } speed;                       //设备速度,低速/全速/高速
    struct usb_device *tt;              /* usb1.1 device on usb2.0 bus */,事务处理解释器
    int ttport;               /* device/hub port on that tt */设备所连接的具有事务处理解释器功能的集线器端口
    atomic_t refcnt;            /* Reference count */引用计数
    struct semaphore serialize; //用于同步
    unsigned int toggle[2];              /* one bit for each endpoint ([0] = IN, [1] = OUT) */用于同步切换的位图,每个端点占用1位,[0]表示输入,[1]输出
    unsigned int halted[2];        /* endpoint halts; one bit per endpoint # & direction;  [0] = IN, [1] = OUT */表示端点是否处于停止状态的位图
       int epmaxpacketin[16];          /* INput endpoint specific maximums */输入端点的最大包长
       int epmaxpacketout[16];              /* OUTput endpoint specific maximums */输出端点的最大包长
       struct usb_device *parent;   //表示设备所连的上游集线器指针
       struct usb_bus *bus;        /* Bus we’re part of */设备所属的USB总线系统
       struct usb_device_descriptor descriptor;/* Descriptor */ 设备描述符
       struct usb_config_descriptor *config; /* All of the configs */指向设备的配置描述符和其所包含的接口描述符,端点描述符的指针
       struct usb_config_descriptor *actconfig;/* the active configuration */当前的配置描述符指针
       char **rawdescriptors;            /* Raw descriptors for each config */
       int have_langid;           /* whether string_langid is valid yet *// 是否有string_langid
       int string_langid;         /* language ID for strings */和字符描述符相关的语言ID
      void *hcpriv;                /* Host Controller private data */设备在HCD层占用的资源指针,对USB内核层是透明的
    /* usbdevfs inode list */ 设备在usbdevfs中的inode列表
       struct list_head inodes;
       struct list_head filelist;
       /*
        * Child devices - these can be either new devices
        * (if this is a hub device), or different instances
        * of this same device.
        *
        * Each instance needs its own set of data structures.
        */只对当前设备是集线器的情况有效
      int maxchild;                     /* Number of ports if hub */ hub的下游端口数
       struct usb_device *children[USB_MAXCHILDREN]; hub所连设备指针
};
struct usb_bus { // USB总线系统
       int busnum;                     /* Bus number (in order of reg) */当前总线系统的序列号,Linux支持多总线系统并为它们编号
#ifdef DEVNUM_ROUND_ROBIN
       int devnum_next;     /* Next open device number in round-robin allocation */
#endif /* DEVNUM_ROUND_ROBIN */给连接到子系统上的设备分配设备号的数据结构
       struct usb_devmap devmap;       /* Device map */给连接到子系统上的设备分配设备号的数据结构           
       struct usb_operations * op;      /* Operations (specific to the HC) */HCD为USB内核提供的一系统函数集指针
       struct usb_device *root_hub;    /* Root hub */指向根Hub的指针
       struct list_head bus_list;       双向链表指针,USB内核用一个双向链表来维护系统中所有USB总线系统
       void *hcpriv; /* Host Controller private data */与主机控制器相关数据,对USB内核层是透明
       int bandwidth_allocated;       /* on this Host Controller; applies to Int. and Isoc. pipes; measured in microseconds/frame; range is 0..Array00, where Array00 = Array0% of a 1-millisecond frame */当前子系统的带宽使用情况,单位是毫秒/帧,取值范围[0,Array00]
       int bandwidth_int_reqs;        /* number of Interrupt requesters */子系统中当前的中断传输的数量
       int bandwidth_isoc_reqs;       /* number of Isoc. requesters */子系统中当前的实时传输的数量
       /* usbdevfs inode list */ 在usbdevfs中的inode列表       struct list_head inodes;
       atomic_t refcnt;
};
struct usb_driver { //客户端驱动程序为USB内核提供的调用接口
       const char *name;    //客户端驱动程序的字符串名称,用于避免重复安装和卸载
       void *(* probe)(//给USB内核提供的函数,用于判断驱动程序是否能对设备的某个接口进行驱动,如能则分配资源
           struct usb_device *dev,              /* the device */
           unsigned intf,                /* what interface */
           const struct usb_device_id *id   /* from id_table */
           );
       void (* disconnect)(struct usb_device *, void *);//给USB内核提供的函数,用于释放设备的某个接口所占用的资源
       struct list_head driver_list;//对应的双向指针,USB内核通过一个双向指针链表维护USB子系统中所用的客户端驱动程序
       struct file_operations * fops;
       int minor; 驱动的次版本号
       struct semaphore serialize;
       /* ioctl -- userspace apps can talk to drivers through usbdevfs */
       int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);
       /* support for "new-style" USB hotplugging
        * binding policy can be driven from user mode too
        */
       const struct usb_device_id *id_table;
       /* suspend before the bus suspends;
        * disconnect or resume when the bus resumes */
       // void (*suspend)(struct usb_device *dev);
       // void (*resume)(struct usb_device *dev);
};
typedef struct urb // USB Request Block,包含了建立任何 USB传输所需的所有信息,并贯穿于USB协议栈对数据处理的整个过程

{
       spinlock_t lock;              // lock for the URB
       void *hcpriv;                // private data for host controller与主机控制器相关数据,对USB内核层是透明
       struct list_head urb_list;       // list pointer to all active urbs双向指针,用于将此URB连接到处于活动的URB双向链表中
       struct urb *next;            // pointer to next URB 指向下一个URB的指针
       struct usb_device *dev;       // pointer to associated USB device 接受此URB的USB设备指针
       unsigned int pipe;// pipe information表示设备的某个端点和客户端驱动程序之间的管道
       int status;                     // returned status 返回状态
       unsigned int transfer_flags;       // USB_DISABLE_SPD | USB_ISO_ASAP | etc.
              USB_DISABLE_SPD   //拒绝短数据包,即比最大传输包长度小的数据包
USB_ISO_ASAP     //用于实时传输,告诉主机控制器立即进行此请求的数据传输。如果没有置位,则需要给start_frame赋值,用来通知主机控制器该在哪个帧上开始此请求的数据传输
USB_ASYNC_UNLINK  //告诉USBD采用异步方式取消请求
USB_QUEUE_BULK    //表示批量请求可以排队,一般一个设备的批量请求端点只有一个URB
USB_NO_FSBR       //表示全速传输站用的带宽不要回收
USB_ZERO_PACKET //表示批量传输的数据长度等于端点的包最大长度时,主机控制器在发送完数据后,再发送一个零长度的包表示数据结束
USB_TIMEOUT_KILLED //本位只被HCD设置,表示发生了超时。客户驱动可以给URB的处理设置一个超时时间,如果处理超时,则要求USBD结束对此URB的处理,URB的返回信息中会反映该此状态。
       void * transfer_buffer;            // associated data buffer传输数据缓存区指针,接收或发送设备的数据,它必须是物理连续的,不可换页的内存块,用kmalloc(,GFP_KERNEL)分配
       int transfer_buffer_length;     // data buffer length缓存区长度
       int actual_length;      // actual data buffer length 实际数据长度     
       int bandwidth;                   // bandwidth for this transfer request (INT or ISO) 此请求每次占用一帧的带宽,只适用实时/中断传输
       unsigned char *setup_packet;       // setup packet (control only) 用于指向控制传输中控制命令的指针,只适用控制传输
       int start_frame;    // start frame (iso/irq only)此请求所开始传输的帧号,只适用实时/中断传输。中断传输时,表示返回启动此请求的第一次中断传输的帧号。实时传输时,指明处理第一个实时请求数据报包的帧号,如果设置了USB_ISO_ASAP,此变量表示返回启动第一次实时传输的帧号。
       int number_of_packets;  // number of packets in this request (iso)此请求所包含的数据包数,只适合实时传输
       int interval; // polling interval (irq only) 中断传输的周期,1〈= interval〈=255
       int error_count;   // number of errors in this transfer (iso only)发生传输错误次数的累加值,只适用实时传输
       int timeout;       // timeout (in jiffies)      
       void *context;               // context for completion routine回调函数中的参数
       usb_complete_t complete;       // pointer to completion routine 指向回调函数的指针。当数据传输完成后,主机控制器驱动会回调该函数。
       iso_packet_descriptor_t iso_frame_desc[0]; 要进行实时传输的结构数组,每个结构表示一次数据传输
} urb_t, *purb_t;
详细分析USB内核(USBD)提供的功能
主要有四类功能:客户端驱动管理,USB设备的配置和管理,主机控制器的管理,协议控制命令集和数据传输的管理
(1)       客户端驱动管理
USB内核通过一个双向链表usb_driver_list来管理所有客户端驱动,具体管理功能为安装和卸载两部分,对应于 usb_registerusb_deregister,USB内核是动态安装和卸载设备驱动的。
int usb_register(struct usb_driver *new_driver)
客户端驱动程序应该在初始化函数中调用usb_register,先检查驱动是否初次安装,根据USBD保存的次版本号数组(目前是16个)中该驱动对应项是否为空,如果不是则返回错误。如果是,将它加入到usb_driver_list中,并进行设备接口扫描 usb_scan_devices,用来探测系统中哪些设备的接口可以被此驱动程序驱动,将会调用驱动提供的probe函数。usb_register还通过深度优先算法按系统所具有的树型结构搜索系统中所有设备未被驱动的接口,由驱动检验能否驱动。如能,则分配必要的软件资源,配置并让其工作。
void usb_deregister(struct usb_driver *driver)
当客户端驱动需要从系统中卸掉时,会调用usb_deregister,将USBD保存的次版本号数组该驱动对应项设为NULL,然后将它从usb_driver_list卸掉,然后断开驱动中所有被它驱动的设备接口连接,释放所有资源。usb_deregister还会通知系统中可用的客户驱动程序,检验这些失去资源的设备接口能否被其他驱动程序驱动,如可用的话,则为其分配资源,让它们正常工作。
驱动可以调用的其他接口管理函数
void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
int usb_interface_claimed(struct usb_interface *iface)
void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
根据给定接口或设备,从usb_device_id数组中查找第一个相符的设备ID。它一般在驱动绑定接口时调用。
const struct usb_device_id *
usb_match_id(struct usb_device *dev, struct usb_interface *interface, const struct usb_device_id *id)
(2)       USB设备的配置和管理
支持USB设备的热插拔,USBD提供了对设备进行配置和管理,包括
插入设备:
       设备插入时,与之相联的集线器首先发现设备的插入信息,通过中断传输将信息传送给集线器的驱动,通过信息分析,确认有新设备插入到总线上,集线器驱动调用 usb_connectusb_new_device来配置设备,并将其与对应的设备驱动建立联系。
void usb_connect(struct usb_device *dev)
设定新设备信息。目前是查找并分配设备地址dev->devnum,真正发USB命令进行配置工作是由 usb_new_device来完成的
int usb_new_device(struct usb_device *dev)
参照协议,完成新设备的配置工作,包括usb_set_address来分配地址,usb_get_descriptor来获得设备描述符,usb_get_configuration来获得设备所有配置描述符,usb_set_configuration来激活缺省配置,usbdevfs_add_device来加入一个/proc/bus/usb入口,通过usb_find_drivers为缺省配置0的每个接口查找相应的驱动程序来进行驱动。
总线上的第一个设备根集线器和主机控制器是一体的,在启动时就认为是插上的,默认地址为0。Linux支持多USB总线,即多个主机控制器和根集线器,它们各自的设备地址是不相关的。
拔下设备:
       设备拔下时,与之相联的集线器首先检测到设备的拔下信号,通过中断传输将信息传送给集线器的驱动,集线器的驱动先验证设备是否被拔下,如果是则调用 usb_disconnect进行处理。
void usb_disconnect(struct usb_device **pdev)
断开设备后的处理。找到设备当前活动配置的每个接口的驱动程序,调用它们提供的disconnect接口函数,中断它们与各个接口的数据传输操作,释放它们为每个接口分配的资源。如果此设备是集线器,则递归调用 usb_disconnect来处理它的子设备。释放设备地址,并通过usbdevfs_remove_device释放给设备创建的inode(/proc/bus/usb入口),usb_free_dev释放USBD给设备分配的资源。
设备复位:
       int usb_reset_device(struct usb_device *dev) hub.c中
USBD提供了usb_reset_device来进行设备的复位操作。它首先复位设备连接的集线器端口,然后与 usb_new_device函数相似的步骤重新完成对设备的配置操作。调用此函数一定要慎重,如果处理不当,将会影响设备的工作
(3)       主机控制器的管理
每个主机控制器拥有一个USB系统,称为一个USB总线。USBD支持多个主机控制器,即多个USB总线。当每增加一个主机控制器时,会给它分配一个usb_bus结构。USBD动态安装和卸载主机驱动。
主机驱动安装时,它的初始化函数一方面完成主机控制器硬件的配置和初始化工作,另一方面调用 usb_alloc_bus和usb_register_bus来将自己注册到USBD中去,供USB子系统访问。
       struct usb_bus * usb_alloc_bus(struct usb_operations *op)
       创建主机控制器对应的总线结构usb_bus,保存主机控制器给USBD提供的函数接口,并进行初始化。每个主机控制器都为USBD提供了一套函数接口,来进行实际的USB通信操作。
       struct usb_operations {
       int (*allocate)(struct usb_device *); //为设备分配物理层的资源
       int (*deallocate)(struct usb_device *); //释放设备占有的物理层资源
       int (*get_frame_number) (struct usb_device *usb_dev); //提供当前主机控制器所使用的帧号,一般用于实时传输
       int (*submit_urb) (struct urb* purb);//进行实际数据传输
       int (*unlink_urb) (struct urb* purb); //结束数据传输请求
};
       void usb_register_bus(struct usb_bus *bus)
将USB总线结构usb_bus注册到USBD中,即将其加入到USB内核的总线双向链表中usb_bus_list,并创建一个/proc/bus/usb入口
主机驱动卸载时,调用 usb_deregister_bususb_free_bus来释放资源
为主机驱动提供的接口函数有:
设备管理
struct usb_device * usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
void usb_free_dev(struct usb_device *dev)
void usb_inc_dev_use(struct usb_device *dev)
带宽管理
int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
协议控制命令集和数据传输管理
[1]协议控制命令集
USBD为设备的客户端驱动提供了一套控制命令的接口函数,实现对设备的配置,控制和通信。具体可见 P256。这些接口函数都是通过usb_control_msg来进行实际发送的,客户端也可以通过调用usb_control_msg来完成自己的设备命令。usb_control_msg属于同步通信函数,不采用异步回调方式。
标准设备命令集和USBD提供的函数的对应
协议定义的标准命令
函数接口
CLEAR_FEATURE
int usb_clear_halt(struct usb_device *dev, int pipe)只提供针对停止工作的端点的清除操作,没有提供清除设备的远程唤醒的操作
GET_CONFIGURATION
int usb_get_configuration(struct usb_device *dev)
GET_DESCRIPTOR
int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
int usb_get_device_descriptor(struct usb_device *dev)
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr){
GET_INTERFACE

GET_STATUS
int usb_get_status(struct usb_device *dev, int type, int target, void *data)
SET_ADDRESS
int usb_set_address(struct usb_device *dev)
SET_CONFIGURATION
int usb_set_configuration(struct usb_device *dev, int configuration)
SET_DESCRIPTOR
无,因为一般设备不支持该命令,不允许增加新描述符
SET_FEATURE

SET_INTERFACE
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
SYNCH_FRAME

设备类命令集和USBD提供的函数的对应
协议定义的类命令
函数接口
GET_DESCRIPTOR (class)
int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
              unsigned char type, unsigned char id, void *buf, int size)
GET_PROTOCOL
int usb_get_protocol(struct usb_device *dev, int ifnum)
SET_PROTOCOL
int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
GET_REPORT
int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
SET_REPORT
int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
SET_IDLE
int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
[2]数据传输的管理
数据传输都是使用USB内核提供的URB(USB Request Block)。具体可参见上面的数据结构。有些变量是针对特定传输类型的。
批量传输:无
控制传输:setup_packet
中断传输:start_frame, interval
实时传输:start_frame, number_packets, error_count, timeout, iso_frame_desc
USBD提供的用于处理URB的接口函数有:
urb_t * usb_alloc_urb(int iso_packets)
用于给客户驱动分配URB,iso_packets为该URB中需要传输的实时数据包的个数,其他传输为0
void usb_free_urb(urb_t* urb)
与usb_alloc_urb对应,释放分配的URB
int usb_submit_urb(urb_t *urb)
将一个或多个urb异步发送给USB内核处理。其中urb可以是一个,也可以是多个,并且可以对应于不同的端点
int usb_unlink_urb(urb_t *urb)
表示在urb传输处理完成之前,取消对它们的数据处理。一般是设备在工作过程中被拔下,或软件主动取消对某个数据传输的处理,或URB传输超时时调用
当主机控制器驱动将URB中的数据传输处理完成后,将会调用urb-> complete回调函数来通知客户驱动。当URB处理超时,客户端驱动会调用 usb_unlink_urb来通知HCD取消对此URB数据的传输
还为控制传输(参见4协议控制命令集)提供接口函数
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)
构建一控制传输的URB,发送并等待完成或超时。该函数不能用在中断处理函数中,包括后半部分处理函数。如果需要发送异步信息或在中断处理函数中发送信息,则使用usb_submit_urb。
为批量传输提供接口函数
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
构建一批量传输的URB,发送并等待完成或超时。同 usb_control_msg类似,该函数不能用在中断处理函数中,包括后半部分处理函数。如果需要发送异步信息或在中断处理函数中发送信息,则使用usb_submit_urb。

你可能感兴趣的:(数据结构,linux,struct,list,interface,Descriptor)