核心使用2.6.29.4
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; //處理傳輸者資訊;用於低速、全速裝置和高速HUBint 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_CLASSstruct device *usb_classdev; //使用者空間訪問的為usbfs裝置建立的USB類裝置#endif#ifdef CONFIG_USB_DEVICEFSstruct dentry *usbfs_dentry; //裝置的usbfs入口#endifint 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; //跳過下個系統喚醒#endifstruct wusb_dev *wusb_dev; //(如果為無線USB)串連到WUSB特定的資料結構};
配置
一個USB裝置可以有多個配置,並可在它們之間轉換以改變裝置的狀態。比如一個裝置可以通過下載韌體(firmware)的方式改變裝置的使用狀態(我感覺類似FPGA或CPLD),那麼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 Storage和USB網路裝置上。
等時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 -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/../bus/hid/drivers/generic-usb
| | |-- power
| | | `-- wakeup
| | |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/../bus/hid
| | `-- uevent
| |-- bAlternateSetting
| |-- bInterfaceClass
| |-- bInterfaceNumber
| |-- bInterfaceProtocol
| |-- bInterfaceSubClass
| |-- bNumEndpoints
| |-- driver -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/bus/usb/drivers/usbhid
| |-- ep_81 -> usb_endpoint/usbdev3.4_ep81
| |-- input
| | `-- input6
| | |-- capabilities
| | | |-- abs
| | | |-- ev
| | | |-- ff
| | | |-- key
| | | |-- led
| | | |-- msc
| | | |-- rel
| | | |-- snd
| | | `-- sw
| | |-- device -> http://www.cnblogs.com/../3-1:1.0
| | |-- event3
| | | |-- dev
| | | |-- device -> http://www.cnblogs.com/input6
| | | |-- power
| | | | `-- wakeup
| | | |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/../class/input
| | | `-- uevent
| | |-- id
| | | |-- bustype
| | | |-- product
| | | |-- vendor
| | | `-- version
| | |-- modalias
| | |-- mouse1
| | | |-- dev
| | | |-- device -> http://www.cnblogs.com/input6
| | | |-- power
| | | | `-- wakeup
| | | |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/../class/input
| | | `-- uevent
| | |-- name
| | |-- phys
| | |-- power
| | | `-- wakeup
| | |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/class/input
| | |-- uevent
| | `-- uniq
| |-- modalias
| |-- power
| | `-- wakeup
| |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/bus/usb
| |-- supports_autosuspend
| |-- uevent
| `-- usb_endpoint
| `-- usbdev3.4_ep81
| |-- bEndpointAddress
| |-- bInterval
| |-- bLength
| |-- bmAttributes
| |-- dev
| |-- device -> http://www.cnblogs.com/../3-1:1.0
| |-- direction
| |-- interval
| |-- power
| | `-- wakeup
| |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/class/usb_endpoint
| |-- type
| |-- uevent
| `-- wMaxPacketSize
|-- authorized
|-- bConfigurationValue
|-- bDeviceClass
|-- bDeviceProtocol
|-- bDeviceSubClass
|-- bMaxPacketSize0
|-- bMaxPower
|-- bNumConfigurations
|-- bNumInterfaces
|-- bcdDevice
|-- bmAttributes
|-- busnum
|-- configuration
|-- descriptors
|-- dev
|-- devnum
|-- driver -> http://www.cnblogs.com/http://www.cnblogs.com/../bus/usb/drivers/usb
|-- ep_00 -> usb_endpoint/usbdev3.4_ep00
|-- idProduct
|-- idVendor
|-- manufacturer
|-- maxchild
|-- power
| |-- active_duration
| |-- autosuspend
| |-- connected_duration
| |-- level
| |-- persist
| `-- wakeup
|-- product
|-- quirks
|-- speed
|-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/../bus/usb
|-- uevent
|-- urbnum
|-- usb_endpoint
| `-- usbdev3.4_ep00
| |-- bEndpointAddress
| |-- bInterval
| |-- bLength
| |-- bmAttributes
| |-- dev
| |-- device -> http://www.cnblogs.com/../3-1
| |-- direction
| |-- interval
| |-- power
| | `-- wakeup
| |-- subsystem -> http://www.cnblogs.com/http://www.cnblogs.com/http://www.cnblogs.com/../class/usb_endpoint
| |-- type
| |-- uevent
| `-- wMaxPacketSize
`-- version
38 directories, 91 files
USB sysfs 裝置命名方法是: root_hub-hub_port:config.interface
隨著USB集線器層次的增加, 集線器連接埠號碼被添加到字串中緊隨著鏈中之前的集線器連接埠號碼。對一個 2 層的樹, 裝置為: root_hub-hub_port-hub_port:config.interface ,以此類推。