| Linux設(shè)備驅(qū)動程序?qū)W習(17)-USB 驅(qū)動程序(二) 內(nèi)核使用2.6.29.4 USB設(shè)備其實很復雜,但是Linux內(nèi)核提供了一個稱為USB core的子系統(tǒng)來處理了大部分的復雜工作,所以這里所描述的是驅(qū)動程序和USB core之間的接口。 在USB設(shè)備組織結(jié)構(gòu)中,從上到下分為設(shè)備(device)、配置(config)、接口(interface)和端點(endpoint)四個層次。 對于這四個層次的簡單描述如下: 設(shè)備通常具有一個或多個的配置 配置經(jīng)常具有一個或多個的接口 接口通常具有一個或多個的設(shè)置 接口沒有或具有一個以上的端點 設(shè)備 很明顯,地代表了一個插入的USB設(shè)備,在內(nèi)核使用數(shù)據(jù)結(jié)構(gòu) struct usb_device來描述整個USB設(shè)備。(include/linux/usb.h) struct usb_device { int devnum; //設(shè)備號,是在USB總線的地址 char devpath [16]; //用于消息的設(shè)備ID字符串 enum usb_device_state state; //設(shè)備狀態(tài):已配置、未連接等等 enum usb_device_speed speed; //設(shè)備速度:高速、全速、低速或錯誤 struct usb_tt *tt; //處理傳輸者信息;用于低速、全速設(shè)備和高速HUB int ttport; //位于tt HUB的設(shè)備口 unsigned int toggle[2]; //每個端點的占一位,表明端點的方向([0] = IN, [1] = OUT) struct usb_device *parent; //上一級HUB指針 struct usb_bus *bus; //總線指針 struct usb_host_endpoint ep0; //端點0數(shù)據(jù) struct device dev; //一般的設(shè)備接口數(shù)據(jù)結(jié)構(gòu) struct usb_device_descriptor descriptor; //USB設(shè)備描述符 struct usb_host_config *config; //設(shè)備的所有配置 struct usb_host_config *actconfig; //被激活的設(shè)備配置 struct usb_host_endpoint *ep_in[16]; //輸入端點數(shù)組 struct usb_host_endpoint *ep_out[16]; //輸出端點數(shù)組 char **rawdescriptors; //每個配置的raw描述符 unsigned short bus_mA; //可使用的總線電流 u8 portnum; //父端口號 u8 level; //USB HUB的層數(shù) 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 */ //設(shè)備的靜態(tài)字符串 char *product; //產(chǎn)品名 char *manufacturer; //廠商名 char *serial; //產(chǎn)品串號 struct list_head filelist; //此設(shè)備打開的usbfs文件 #ifdef CONFIG_USB_DEVICE_CLASS struct device *usb_classdev; //用戶空間訪問的為usbfs設(shè)備創(chuàng)建的USB類設(shè)備 #endif #ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; //設(shè)備的usbfs入口 #endif int maxchild; //(若為HUB)接口數(shù) struct usb_device *children[USB_MAXCHILDREN];//連接在這個HUB上的子設(shè)備 int pm_usage_cnt; //自動掛起的使用計數(shù) u32 quirks; atomic_t urbnum; //這個設(shè)備所提交的URB計數(shù) unsigned long active_duration; //激活后使用計時 #ifdef CONFIG_PM //電源管理相關(guān) 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; //掛起關(guān)閉 unsigned autoresume_disabled:1; //喚醒關(guān)閉 unsigned skip_sys_resume:1; //跳過下個系統(tǒng)喚醒 #endif struct wusb_dev *wusb_dev; //(如果為無線USB)連接到WUSB特定的數(shù)據(jù)結(jié)構(gòu) }; 配置 一個USB設(shè)備可以有多個配置,并可在它們之間轉(zhuǎn)換以改變設(shè)備的狀態(tài)。比如一個設(shè)備可以通過下載固件(firmware)的方式改變設(shè)備的使用狀態(tài)(我 感覺類似FPGA或CPLD),那么USB設(shè)備就要切換配置,來完成這個工作。一個時刻只能有一個配置可以被激活。Linux使用結(jié)構(gòu) struct usb_host_config 來描述USB配置。我們編寫的USB設(shè)備驅(qū)動通常不需要讀寫這些結(jié)構(gòu)的任何值??稍趦?nèi)核源碼的文件include/linux/usb.h中找到對它們的 描述。 struct usb_host_config { struct usb_config_descriptor desc; //配置描述符 char *string; /* 配置的字符串指針(如果存在) */ struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; //配置的接口聯(lián)合描述符鏈表 struct usb_interface *interface[USB_MAXINTERFACES]; //接口描述符鏈表 struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; unsigned char *extra; /* 額外的描述符 */ int extralen; }; 接口 USB 端點被綁為接口,USB接口只處理一種USB邏輯連接。一個USB接口代表一個基本功能,每個USB驅(qū)動控制一個接口。所以一個物理上的硬件設(shè)備可能需要 一個以上的驅(qū)動程序。這可以在“暈到死 差屁”系統(tǒng)中看出,有時插入一個USB設(shè)備后,系統(tǒng)會識別出多個設(shè)備,并安裝相應多個的驅(qū)動。 USB 接口可以有其他的設(shè)置,它是對接口參數(shù)的不同選擇. 接口的初始化的狀態(tài)是第一個設(shè)置,編號為0。 其他的設(shè)置可以以不同方式控制獨立的端點。 USB接口在內(nèi)核中使用 struct usb_interface 來描述。USB 核心將其傳遞給USB驅(qū)動,并由USB驅(qū)動負責后續(xù)的控制。 struct usb_interface { struct usb_host_interface *altsetting; /* 包含所有可用于該接口的可選設(shè)置的接口結(jié)構(gòu)數(shù)組。每個 struct usb_host_interface 包含一套端點配置(即struct usb_host_endpoint結(jié)構(gòu)所定義的端點配置。這些接口結(jié)構(gòu)沒有特別的順序。*/ struct usb_host_interface *cur_altsetting; /* 指向altsetting內(nèi)部的指針,表示當前激活的接口配置*/ unsigned num_altsetting; /* 可選設(shè)置的數(shù)量*/ /* If there is an interface association descriptor then it will list the associated interfaces */ struct usb_interface_assoc_descriptor *intf_assoc; int minor; /* 如果綁定到這個接口的 USB 驅(qū)動使用 USB 主設(shè)備號, 這個變量包含由 USB 核心分配給接口的次設(shè)備號. 這只在一個成功的調(diào)用 usb_register_dev后才有效。*/ /*以下的數(shù)據(jù)在我們寫的驅(qū)動中基本不用考慮,系統(tǒng)會自動設(shè)置*/ 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; /* 接口特定的設(shè)備信息 */ 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; /* 這個接口的所有端點結(jié)構(gòu)體的聯(lián)合數(shù)組*/ char *string; /* 接口描述字符串 */ unsigned char *extra; /* 額外的描述符 */ int extralen; }; 端點 USB 通訊的最基本形式是通過一個稱為端點的東西。一個USB端點只能向一個方向傳輸數(shù)據(jù)(從主機到設(shè)備(稱為輸出端點)或者從設(shè)備到主機(稱為輸入端點))。端點可被看作一個單向的管道。 一個 USB 端點有 4 種不同類型, 分別具有不同的數(shù)據(jù)傳送方式: 控制CONTROL 控 制端點被用來控制對 USB 設(shè)備的不同部分訪問. 通常用作配置設(shè)備、獲取設(shè)備信息、發(fā)送命令到設(shè)備或獲取設(shè)備狀態(tài)報告。這些端點通常較小。每個 USB 設(shè)備都有一個控制端點稱為"端點 0", 被 USB 核心用來在插入時配置設(shè)備。USB協(xié)議保證總有足夠的帶寬留給控制端點傳送數(shù)據(jù)到設(shè)備. 中斷INTERRUPT 每當 USB 主機向設(shè)備請求數(shù)據(jù)時,中斷端點以固定的速率傳送小量的數(shù)據(jù)。此為USB 鍵盤和鼠標的主要的數(shù)據(jù)傳送方法。它還用以傳送數(shù)據(jù)到 USB 設(shè)備來控制設(shè)備。通常不用來傳送大量數(shù)據(jù)。USB協(xié)議保證總有足夠的帶寬留給中斷端點傳送數(shù)據(jù)到設(shè)備. 批量BULK 批 量端點用以傳送大量數(shù)據(jù)。這些端點常比中斷端點大得多. 它們普遍用于不能有任何數(shù)據(jù)丟失的數(shù)據(jù)。USB 協(xié)議不保證傳輸在特定時間范圍內(nèi)完成。如果總線上沒有足夠的空間來發(fā)送整個BULK包,它被分為多個包進行傳輸。這些端點普遍用于打印機、USB Mass Storage和USB網(wǎng)絡設(shè)備上。 等時ISOCHRONOUS 等時端點也批量傳送大量數(shù)據(jù), 但是這個數(shù)據(jù)不被保證能送達。這些端點用在可以處理數(shù)據(jù)丟失的設(shè)備中,并且更多依賴于保持持續(xù)的數(shù)據(jù)流。如音頻和視頻設(shè)備等等。 控制和批量端點用于異步數(shù)據(jù)傳送,而中斷和同步端點是周期性的。這意味著這些端點被設(shè)置來在固定的時間連續(xù)傳送數(shù)據(jù),USB 核心為它們保留了相應的帶寬。 端點在內(nèi)核中使用結(jié)構(gòu) struct usb_host_endpoint 來描述,它所包含的真實端點信息在另一個結(jié)構(gòu)中:struct usb_endpoint_descriptor(端點描述符,包含所有的USB特定數(shù)據(jù))。 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位數(shù)據(jù)包含端點的方向,結(jié)合位掩碼 USB_DIR_OUT 和 USB_DIR_IN 使用, 確定這個端點的數(shù)據(jù)方向。*/ __u8 bmAttributes; //這是端點的類型,位掩碼如下 __le16 wMaxPacketSize; /*端點可以一次處理的最大字節(jié)數(shù)。驅(qū)動可以發(fā)送比這個值大的數(shù)據(jù)量到端點, 但是當真正傳送到設(shè)備時,數(shù)據(jù)會被分為 wMaxPakcetSize 大小的塊。對于高速設(shè)備, 通過使用高位部分幾個額外位,可用來支持端點的高帶寬模式。*/ __u8 bInterval; //如果端點是中斷類型,該值是端點的間隔設(shè)置,即端點的中斷請求間的間隔時間,以毫秒為單位 /* 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 數(shù)據(jù)方向掩碼 */ #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 物理設(shè)備的復雜性,設(shè)備在 sysfs 中的表示也非常復雜。物理 USB 設(shè)備(通過 struct usb_device 表示)和單個 USB 接口(由 struct usb_interface 表示)都作為單個設(shè)備出現(xiàn)在 sysfs 中,這是因為這兩個結(jié)構(gòu)都包含一個 struct device結(jié)構(gòu)。以下內(nèi)容是我的USB鼠標在 sysfs 中的目錄樹: /sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1 (表示 usb_device 結(jié)構(gòu)) . |-- 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_endpoint/usbdev3.4_ep81 | |-- input | | `-- input6 | | |-- capabilities | | | |-- abs | | | |-- ev | | | |-- ff | | | |-- key | | | |-- led | | | |-- msc | | | |-- rel | | | |-- snd | | | `-- sw | | |-- device -> ../../../3-1:1.0 | | |-- event3 | | | |-- dev | | | |-- device -> ../../input6 | | | |-- power | | | | `-- wakeup | | | |-- subsystem -> ../../../../../../../../../class/input | | | `-- uevent | | |-- id | | | |-- bustype | | | |-- product | | | |-- vendor | | | `-- version | | |-- modalias | | |-- mouse1 | | | |-- dev | | | |-- device -> ../../input6 | | | |-- power | | | | `-- wakeup | | | |-- subsystem -> ../../../../../../../../../class/input | | | `-- uevent | | |-- name | | |-- phys | | |-- power | | | `-- wakeup | | |-- subsystem -> ../../../../../../../../class/input | | |-- uevent | | `-- uniq | |-- modalias | |-- power | | `-- wakeup | |-- subsystem -> ../../../../../../bus/usb | |-- supports_autosuspend | |-- uevent | `-- usb_endpoint | `-- usbdev3.4_ep81 | |-- bEndpointAddress | |-- bInterval | |-- bLength | |-- bmAttributes | |-- dev | |-- device -> ../../../3-1:1.0 | |-- direction | |-- interval | |-- power | | `-- wakeup | |-- subsystem -> ../../../../../../../../class/usb_endpoint | |-- type | |-- uevent | `-- wMaxPacketSize |-- authorized |-- bConfigurationValue |-- bDeviceClass |-- bDeviceProtocol |-- bDeviceSubClass |-- bMaxPacketSize0 |-- bMaxPower |-- bNumConfigurations |-- bNumInterfaces |-- bcdDevice |-- bmAttributes |-- busnum |-- configuration |-- descriptors |-- dev |-- devnum |-- driver -> ../../../../../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 -> ../../../../../bus/usb |-- uevent |-- urbnum |-- usb_endpoint | `-- usbdev3.4_ep00 | |-- bEndpointAddress | |-- bInterval | |-- bLength | |-- bmAttributes | |-- dev | |-- device -> ../../../3-1 | |-- direction | |-- interval | |-- power | | `-- wakeup | |-- subsystem -> ../../../../../../../class/usb_endpoint | |-- type | |-- uevent | `-- wMaxPacketSize `-- version 38 directories, 91 files USB sysfs 設(shè)備命名方法是: root_hub-hub_port:config.interface 隨著USB集線器層次的增加, 集線器端口號被添加到字符串中緊隨著鏈中之前的集線器端口號。對一個 2 層的樹, 設(shè)備為: root_hub-hub_port-hub_port:config.interface ,以此類推。 | 
|  | 
來自: lchjczw > 《USB設(shè)備》