Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(17)-USB 驅(qū)動(dòng)程序(二) 內(nèi)核使用2.6.29.4 USB設(shè)備其實(shí)很復(fù)雜,但是Linux內(nèi)核提供了一個(gè)稱(chēng)為USB core的子系統(tǒng)來(lái)處理了大部分的復(fù)雜工作,所以這里所描述的是驅(qū)動(dòng)程序和USB core之間的接口。 在USB設(shè)備組織結(jié)構(gòu)中,從上到下分為設(shè)備(device)、配置(config)、接口(interface)和端點(diǎn)(endpoint)四個(gè)層次。 對(duì)于這四個(gè)層次的簡(jiǎn)單描述如下: 設(shè)備通常具有一個(gè)或多個(gè)的配置 配置經(jīng)常具有一個(gè)或多個(gè)的接口 接口通常具有一個(gè)或多個(gè)的設(shè)置 接口沒(méi)有或具有一個(gè)以上的端點(diǎn) 設(shè)備 很明顯,地代表了一個(gè)插入的USB設(shè)備,在內(nèi)核使用數(shù)據(jù)結(jié)構(gòu) struct usb_device來(lái)描述整個(gè)USB設(shè)備。(include/linux/usb.h) struct usb_device { int devnum; //設(shè)備號(hào),是在USB總線(xiàn)的地址 char devpath [16]; //用于消息的設(shè)備ID字符串 enum usb_device_state state; //設(shè)備狀態(tài):已配置、未連接等等 enum usb_device_speed speed; //設(shè)備速度:高速、全速、低速或錯(cuò)誤 struct usb_tt *tt; //處理傳輸者信息;用于低速、全速設(shè)備和高速HUB int ttport; //位于tt HUB的設(shè)備口 unsigned int toggle[2]; //每個(gè)端點(diǎn)的占一位,表明端點(diǎn)的方向([0] = IN, [1] = OUT) struct usb_device *parent; //上一級(jí)HUB指針 struct usb_bus *bus; //總線(xiàn)指針 struct usb_host_endpoint ep0; //端點(diǎn)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]; //輸入端點(diǎn)數(shù)組 struct usb_host_endpoint *ep_out[16]; //輸出端點(diǎn)數(shù)組 char **rawdescriptors; //每個(gè)配置的raw描述符 unsigned short bus_mA; //可使用的總線(xiàn)電流 u8 portnum; //父端口號(hào) u8 level; //USB HUB的層數(shù) unsigned can_submit:1; //URB可被提交標(biāo)志 unsigned discon_suspended:1; //暫停時(shí)斷開(kāi)標(biāo)志 unsigned persist_enabled:1; //USB_PERSIST使能標(biāo)志 unsigned have_langid:1; //string_langid存在標(biāo)志 unsigned authorized:1; unsigned authenticated:1; unsigned wusb:1; //無(wú)線(xiàn)USB標(biāo)志 int string_langid; //字符串語(yǔ)言ID /* static strings from the device */ //設(shè)備的靜態(tài)字符串 char *product; //產(chǎn)品名 char *manufacturer; //廠商名 char *serial; //產(chǎn)品串號(hào) struct list_head filelist; //此設(shè)備打開(kāi)的usbfs文件 #ifdef CONFIG_USB_DEVICE_CLASS struct device *usb_classdev; //用戶(hù)空間訪(fǎng)問(wèn)的為usbfs設(shè)備創(chuàng)建的USB類(lèi)設(shè)備 #endif #ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; //設(shè)備的usbfs入口 #endif int maxchild; //(若為HUB)接口數(shù) struct usb_device *children[USB_MAXCHILDREN];//連接在這個(gè)HUB上的子設(shè)備 int pm_usage_cnt; //自動(dòng)掛起的使用計(jì)數(shù) u32 quirks; atomic_t urbnum; //這個(gè)設(shè)備所提交的URB計(jì)數(shù) unsigned long active_duration; //激活后使用計(jì)時(shí) #ifdef CONFIG_PM //電源管理相關(guān) struct delayed_work autosuspend; //自動(dòng)掛起的延時(shí) struct work_struct autoresume; //(中斷的)自動(dòng)喚醒需求 struct mutex pm_mutex; //PM的互斥鎖 unsigned long last_busy; //最后使用的時(shí)間 int autosuspend_delay; unsigned long connect_time; //第一次連接的時(shí)間 unsigned auto_pm:1; //自動(dòng)掛起/喚醒 unsigned do_remote_wakeup:1; //遠(yuǎn)程喚醒 unsigned reset_resume:1; //使用復(fù)位替代喚醒 unsigned autosuspend_disabled:1; //掛起關(guān)閉 unsigned autoresume_disabled:1; //喚醒關(guān)閉 unsigned skip_sys_resume:1; //跳過(guò)下個(gè)系統(tǒng)喚醒 #endif struct wusb_dev *wusb_dev; //(如果為無(wú)線(xiàn)USB)連接到WUSB特定的數(shù)據(jù)結(jié)構(gòu) }; 配置 一個(gè)USB設(shè)備可以有多個(gè)配置,并可在它們之間轉(zhuǎn)換以改變?cè)O(shè)備的狀態(tài)。比如一個(gè)設(shè)備可以通過(guò)下載固件(firmware)的方式改變?cè)O(shè)備的使用狀態(tài)(我 感覺(jué)類(lèi)似FPGA或CPLD),那么USB設(shè)備就要切換配置,來(lái)完成這個(gè)工作。一個(gè)時(shí)刻只能有一個(gè)配置可以被激活。Linux使用結(jié)構(gòu) struct usb_host_config 來(lái)描述USB配置。我們編寫(xiě)的USB設(shè)備驅(qū)動(dòng)通常不需要讀寫(xiě)這些結(jié)構(gòu)的任何值??稍趦?nèi)核源碼的文件include/linux/usb.h中找到對(duì)它們的 描述。 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 端點(diǎn)被綁為接口,USB接口只處理一種USB邏輯連接。一個(gè)USB接口代表一個(gè)基本功能,每個(gè)USB驅(qū)動(dòng)控制一個(gè)接口。所以一個(gè)物理上的硬件設(shè)備可能需要 一個(gè)以上的驅(qū)動(dòng)程序。這可以在“暈到死 差屁”系統(tǒng)中看出,有時(shí)插入一個(gè)USB設(shè)備后,系統(tǒng)會(huì)識(shí)別出多個(gè)設(shè)備,并安裝相應(yīng)多個(gè)的驅(qū)動(dòng)。 USB 接口可以有其他的設(shè)置,它是對(duì)接口參數(shù)的不同選擇. 接口的初始化的狀態(tài)是第一個(gè)設(shè)置,編號(hào)為0。 其他的設(shè)置可以以不同方式控制獨(dú)立的端點(diǎn)。 USB接口在內(nèi)核中使用 struct usb_interface 來(lái)描述。USB 核心將其傳遞給USB驅(qū)動(dòng),并由USB驅(qū)動(dòng)負(fù)責(zé)后續(xù)的控制。 struct usb_interface { struct usb_host_interface *altsetting; /* 包含所有可用于該接口的可選設(shè)置的接口結(jié)構(gòu)數(shù)組。每個(gè) struct usb_host_interface 包含一套端點(diǎn)配置(即struct usb_host_endpoint結(jié)構(gòu)所定義的端點(diǎn)配置。這些接口結(jié)構(gòu)沒(méi)有特別的順序。*/ struct usb_host_interface *cur_altsetting; /* 指向altsetting內(nèi)部的指針,表示當(dāng)前激活的接口配置*/ 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; /* 如果綁定到這個(gè)接口的 USB 驅(qū)動(dòng)使用 USB 主設(shè)備號(hào), 這個(gè)變量包含由 USB 核心分配給接口的次設(shè)備號(hào). 這只在一個(gè)成功的調(diào)用 usb_register_dev后才有效。*/ /*以下的數(shù)據(jù)在我們寫(xiě)的驅(qū)動(dòng)中基本不用考慮,系統(tǒng)會(huì)自動(dò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; /* 這個(gè)接口的所有端點(diǎn)結(jié)構(gòu)體的聯(lián)合數(shù)組*/ char *string; /* 接口描述字符串 */ unsigned char *extra; /* 額外的描述符 */ int extralen; }; 端點(diǎn) USB 通訊的最基本形式是通過(guò)一個(gè)稱(chēng)為端點(diǎn)的東西。一個(gè)USB端點(diǎn)只能向一個(gè)方向傳輸數(shù)據(jù)(從主機(jī)到設(shè)備(稱(chēng)為輸出端點(diǎn))或者從設(shè)備到主機(jī)(稱(chēng)為輸入端點(diǎn)))。端點(diǎn)可被看作一個(gè)單向的管道。 一個(gè) USB 端點(diǎn)有 4 種不同類(lèi)型, 分別具有不同的數(shù)據(jù)傳送方式: 控制CONTROL 控 制端點(diǎn)被用來(lái)控制對(duì) USB 設(shè)備的不同部分訪(fǎng)問(wèn). 通常用作配置設(shè)備、獲取設(shè)備信息、發(fā)送命令到設(shè)備或獲取設(shè)備狀態(tài)報(bào)告。這些端點(diǎn)通常較小。每個(gè) USB 設(shè)備都有一個(gè)控制端點(diǎn)稱(chēng)為"端點(diǎn) 0", 被 USB 核心用來(lái)在插入時(shí)配置設(shè)備。USB協(xié)議保證總有足夠的帶寬留給控制端點(diǎn)傳送數(shù)據(jù)到設(shè)備. 中斷INTERRUPT 每當(dāng) USB 主機(jī)向設(shè)備請(qǐng)求數(shù)據(jù)時(shí),中斷端點(diǎn)以固定的速率傳送小量的數(shù)據(jù)。此為USB 鍵盤(pán)和鼠標(biāo)的主要的數(shù)據(jù)傳送方法。它還用以傳送數(shù)據(jù)到 USB 設(shè)備來(lái)控制設(shè)備。通常不用來(lái)傳送大量數(shù)據(jù)。USB協(xié)議保證總有足夠的帶寬留給中斷端點(diǎn)傳送數(shù)據(jù)到設(shè)備. 批量BULK 批 量端點(diǎn)用以傳送大量數(shù)據(jù)。這些端點(diǎn)常比中斷端點(diǎn)大得多. 它們普遍用于不能有任何數(shù)據(jù)丟失的數(shù)據(jù)。USB 協(xié)議不保證傳輸在特定時(shí)間范圍內(nèi)完成。如果總線(xiàn)上沒(méi)有足夠的空間來(lái)發(fā)送整個(gè)BULK包,它被分為多個(gè)包進(jìn)行傳輸。這些端點(diǎn)普遍用于打印機(jī)、USB Mass Storage和USB網(wǎng)絡(luò)設(shè)備上。 等時(shí)ISOCHRONOUS 等時(shí)端點(diǎn)也批量傳送大量數(shù)據(jù), 但是這個(gè)數(shù)據(jù)不被保證能送達(dá)。這些端點(diǎn)用在可以處理數(shù)據(jù)丟失的設(shè)備中,并且更多依賴(lài)于保持持續(xù)的數(shù)據(jù)流。如音頻和視頻設(shè)備等等。 控制和批量端點(diǎn)用于異步數(shù)據(jù)傳送,而中斷和同步端點(diǎn)是周期性的。這意味著這些端點(diǎn)被設(shè)置來(lái)在固定的時(shí)間連續(xù)傳送數(shù)據(jù),USB 核心為它們保留了相應(yīng)的帶寬。 端點(diǎn)在內(nèi)核中使用結(jié)構(gòu) struct usb_host_endpoint 來(lái)描述,它所包含的真實(shí)端點(diǎn)信息在另一個(gè)結(jié)構(gòu)中:struct usb_endpoint_descriptor(端點(diǎn)描述符,包含所有的USB特定數(shù)據(jù))。 struct usb_host_endpoint { struct usb_endpoint_descriptor desc; //端點(diǎn)描述符 struct list_head urb_list; //此端點(diǎn)的URB對(duì)列,由USB核心維護(hù) 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; /*這個(gè)特定端點(diǎn)的 USB 地址,這個(gè)8位數(shù)據(jù)包含端點(diǎn)的方向,結(jié)合位掩碼 USB_DIR_OUT 和 USB_DIR_IN 使用, 確定這個(gè)端點(diǎn)的數(shù)據(jù)方向。*/ __u8 bmAttributes; //這是端點(diǎn)的類(lèi)型,位掩碼如下 __le16 wMaxPacketSize; /*端點(diǎn)可以一次處理的最大字節(jié)數(shù)。驅(qū)動(dòng)可以發(fā)送比這個(gè)值大的數(shù)據(jù)量到端點(diǎn), 但是當(dāng)真正傳送到設(shè)備時(shí),數(shù)據(jù)會(huì)被分為 wMaxPakcetSize 大小的塊。對(duì)于高速設(shè)備, 通過(guò)使用高位部分幾個(gè)額外位,可用來(lái)支持端點(diǎn)的高帶寬模式。*/ __u8 bInterval; //如果端點(diǎn)是中斷類(lèi)型,該值是端點(diǎn)的間隔設(shè)置,即端點(diǎn)的中斷請(qǐng)求間的間隔時(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 端點(diǎn)的 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 由于單個(gè) USB 物理設(shè)備的復(fù)雜性,設(shè)備在 sysfs 中的表示也非常復(fù)雜。物理 USB 設(shè)備(通過(guò) struct usb_device 表示)和單個(gè) USB 接口(由 struct usb_interface 表示)都作為單個(gè)設(shè)備出現(xiàn)在 sysfs 中,這是因?yàn)檫@兩個(gè)結(jié)構(gòu)都包含一個(gè) struct device結(jié)構(gòu)。以下內(nèi)容是我的USB鼠標(biāo)在 sysfs 中的目錄樹(shù): /sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1 (表示 usb_device 結(jié)構(gòu)) . |-- 3-1:1.0 (鼠標(biāo)所對(duì)應(yīng)的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集線(xiàn)器層次的增加, 集線(xiàn)器端口號(hào)被添加到字符串中緊隨著鏈中之前的集線(xiàn)器端口號(hào)。對(duì)一個(gè) 2 層的樹(shù), 設(shè)備為: root_hub-hub_port-hub_port:config.interface ,以此類(lèi)推。 |
|
來(lái)自: lchjczw > 《USB設(shè)備》