歡迎您光臨本站 註冊首頁

Linux下USB內核之學習筆記

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

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..900, where 900 = 90% of a 1-millisecond frame */當前子系統的帶寬使用情況,單位是毫秒/幀,取值範圍[0,900]

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_register和usb_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_connect和usb_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_bus和usb_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下USB內核之學習筆記已經有1057次圍觀

http://coctec.com/docs/linux/show-post-189407.html