Linux那些事儿 之 戏说USB(24)设备的生命线(七)
生活随笔
收集整理的這篇文章主要介紹了
Linux那些事儿 之 戏说USB(24)设备的生命线(七)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
算是進入了HCD的片兒區,這里的老大不是幫派頭目也不是巡邏片兒警,而是幾個結構。在HCD這個片兒區,這個山頭兒,王中之王就是include/linux/usb/hcd.h里定義的struct usb_hcd。
struct usb_hcd {/** housekeeping*/struct usb_bus self; /* hcd is-a bus */struct kref kref; /* reference counter */const char *product_desc; /* product/vendor string */int speed; /* Speed for this roothub.* May be different from* hcd->driver->flags & HCD_MASK*/char irq_descr[24]; /* driver + bus # */struct timer_list rh_timer; /* drives root-hub polling */struct urb *status_urb; /* the current status urb */
#ifdef CONFIG_PM_RUNTIMEstruct work_struct wakeup_work; /* for remote wakeup */
#endif/** hardware info/state*/const struct hc_driver *driver; /* hw-specific hooks *//** OTG and some Host controllers need software interaction with phys;* other external phys should be software-transparent*/struct usb_phy *usb_phy;struct phy *phy;/* Flags that need to be manipulated atomically because they can* change while the host controller is running. Always use* set_bit() or clear_bit() to change their values.*/unsigned long flags;
#define HCD_FLAG_HW_ACCESSIBLE 0 /* at full power */
#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */
#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */
#define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */
#define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */
#define HCD_FLAG_DEAD 6 /* controller has died? *//* The flags can be tested using these macros; they are likely to* be slightly faster than test_bit().*/
#define HCD_HW_ACCESSIBLE(hcd) ((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE))
#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH))
#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING))
#define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
#define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING))
#define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD))/* Flags that get set only during HCD registration or removal. */unsigned rh_registered:1;/* is root hub registered? */unsigned rh_pollable:1; /* may we poll the root hub? */unsigned msix_enabled:1; /* driver has MSI-X enabled? */unsigned remove_phy:1; /* auto-remove USB phy *//* The next flag is a stopgap, to be removed when all the HCDs* support the new root-hub polling mechanism. */unsigned uses_new_polling:1;unsigned wireless:1; /* Wireless USB HCD */unsigned authorized_default:1;unsigned has_tt:1; /* Integrated TT in root hub */unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */unsigned can_do_streams:1; /* HC supports streams */unsigned tpl_support:1; /* OTG & EH TPL support */unsigned int irq; /* irq allocated */void __iomem *regs; /* device memory/io */resource_size_t rsrc_start; /* memory/io resource start */resource_size_t rsrc_len; /* memory/io resource length */unsigned power_budget; /* in mA, 0 = no limit */struct giveback_urb_bh high_prio_bh;struct giveback_urb_bh low_prio_bh;/* bandwidth_mutex should be taken before adding or removing* any new bus bandwidth constraints:* 1. Before adding a configuration for a new device.* 2. Before removing the configuration to put the device into* the addressed state.* 3. Before selecting a different configuration.* 4. Before selecting an alternate interface setting.** bandwidth_mutex should be dropped after a successful control message* to the device, or resetting the bandwidth after a failed attempt.*/struct mutex *bandwidth_mutex;struct usb_hcd *shared_hcd;struct usb_hcd *primary_hcd;#define HCD_BUFFER_POOLS 4struct dma_pool *pool[HCD_BUFFER_POOLS];int state;
# define __ACTIVE 0x01
# define __SUSPEND 0x04
# define __TRANSIENT 0x80# define HC_STATE_HALT 0
# define HC_STATE_RUNNING (__ACTIVE)
# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)
# define HC_STATE_SUSPENDED (__SUSPEND)#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)/* more shared queuing code would be good; it should support* smarter scheduling, handle transaction translators, etc;* input size of periodic table to an interrupt scheduler.* (ohci 32, uhci 1024, ehci 256/512/1024).*//* The HC driver's private data is stored at the end of* this structure.*/unsigned long hcd_priv[0]__attribute__ ((aligned(sizeof(s64))));
};經過了血與火,熊市與牛市的洗禮,我們都應該對這種變態結構習以為常了,男人么,圖不了房子圖不了車子圖不了美女,能圖的還有啥?不就是一顆平常心么。
6行,又一個結構體,struct usb_bus,還名曰self,struct usb_hcd里還有self,看來這家伙是雙子座的,以為能再分裂出一個自己。
為什么這里會用這么一個戲劇性的詞匯self?俺在前面的某處提到過那么一下,一個主機控制器就會連出一條usb總線,主機控制器驅動用struct usb_hcd結構表示,一條總線用struct usb_bus結構表示,它們是相生相依的關系。struct usb_bus在include/linux/usb.h里定義
struct usb_bus {struct device *controller; /* host/master side hardware */int busnum; /* Bus number (in order of reg) */const char *bus_name; /* stable id (PCI slot_name etc) */u8 uses_dma; /* Does the host controller use DMA? */u8 uses_pio_for_control; /** Does the host controller use PIO* for control transfers?*/u8 otg_port; /* 0, or number of OTG/HNP port */unsigned is_b_host:1; /* true during some HNP roleswitches */unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */unsigned no_stop_on_short:1; /** Quirk: some controllers don't stop* the ep queue on a short transfer* with the URB_SHORT_NOT_OK flag set.*/unsigned no_sg_constraint:1; /* no sg constraint */unsigned sg_tablesize; /* 0 or largest number of sg list entries */int devnum_next; /* Next open device number in* round-robin allocation */struct usb_devmap devmap; /* device address allocation map */struct usb_device *root_hub; /* Root hub */struct usb_bus *hs_companion; /* Companion EHCI bus, if any */struct list_head bus_list; /* list of busses */struct mutex usb_address0_mutex; /* unaddressed device mutex */int bandwidth_allocated; /* on this bus: how much of the time* reserved for periodic (intr/iso)* requests is used, on average?* Units: microseconds/frame.* Limits: Full/low speed reserve 90%,* while high speed reserves 80%.*/int bandwidth_int_reqs; /* number of Interrupt requests */int bandwidth_isoc_reqs; /* number of Isoc. requests */unsigned resuming_ports; /* bit array: resuming root-hub ports */#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)struct mon_bus *mon_bus; /* non-null when associated */int monitored; /* non-zero when monitored */
#endif
};2行,controller,struct usb_hcd那里含了個usb_bus,這里就回應了個controller。那現在通過struct usb_hcd里的self和struct usb_bus里的controller這兩個很有樂觀主義精神的詞兒,你能不能說下它們到底是什么關系?你當然可以說是一個對應主機控制器,一個描述一條總線,但其實對于寫代碼的來說一個主機控制器和一條總線差不多是一碼事,不用分的那么清,可以簡單的說它們都是用來描述主機控制器的,那為什么又分成了兩個結構,難道Greg他們現在又不信奉簡約主義了?
這個問題的答案我也很想知道,但知道了又能怎么樣?所以也不用去知道了。不過思索了一杯茶的時間,還是有那么點兒線索。
前面說過linux里和小李飛刀齊名的就是設備模型了,usb主機控制器當然也是一個設備,而且更多的時候它還是一個PCI設備,那它就應該納入這個設備模型范疇之內,struct usb_hcd結構里就得嵌入類似struct device或struct pci_dev這樣的一個結構體,但是你仔細瞅瞅,能不能在它里面發現這么一個成員?不能,對于一個設備來說,這可是大逆不道的。但是你再瞅瞅struct usb_bus,第一個就是一個struct device結構體。好,第一條線索就先到這兒。
再利用這杯茶的時間挑個具體的主機控制器驅動程序快速的走一下,就UHCI吧,都在host目錄下的uhci-族文件里,首先它是個pci設備,要使用pci_register_driver注冊一個struct pci_driver結構體uhci_pci_driver,uhci_pci_driver里又有個熟悉的probe,在這個probe里,它調用usb_create_hcd來創建一個usb_hcd,初始化里面的self,還將這個self里的controller設定為描述主機控制器的那個pci_dev里的struct device結構體,從而將usb_hcd、usb_bus和pci_dev,甚至設備模型都連接起來了。
這杯茶應該還沒有這么快就喝的完,那就再接著巡視一下uhci-文件里定義的那些函數,只用看它們的參數,你會發現參數里不是struct usb_hcd就是struct uhci_hcd,如果你和我一樣無聊愿意多看點的話,你會看到那些函數的前面幾行常常會有hcd_to_uhci或者uhci_to_hcd這樣的函數在struct usb_hcd和struct uhci_hcd之間做著轉換。struct uhci_hcd是什么?它是uhci自己私有的一個結構體,就像每個成功的男人背后都有一個女人一樣,每個具體的主機控制器都有這么一個類似的結構體。如果你再無聊一下,順便瞧了下hcd_to_uhci或者uhci_to_hcd的定義,你就會明白,每個主機控制器的這個私有結構體都藏在struct usb_hcd結構最后的那個hcd_priv變長數組里。
通過這杯茶,你能悟出什么?如果說鏡頭閃的太快,讓你看的不太明白,那就只管聽俺說好了。對于具體的主機控制器驅動來說,它們的眼里只有struct usb_hcd,struct usb_hcd結構之于主機控制器驅動,就如同struct usb_device或struct usb_interface之于usb驅動。沒有usb_create_hcd去創建usb_hcd,就不會有usb_bus的存在。而對于linux設備模型來說,struct usb_bus無疑要更親切一些。總之,你可以把struct usb_bus當作只是嵌入到struct usb_hcd里面的一個結構體,它將struct usb_hcd要完成的一部分工作進行了封裝,因為要描述一個主機控制器太復雜太難,于是就開了struct usb_bus這么一個窗戶去專門面對設備模型、sysfs等等。這也就是俺開頭兒就說這個片兒區,struct usb_hcd才是王中之王的原因。
你知道Greg他們是怎么描述這種奇妙的關系么?他們把這個叫作HCD bus-glue layer,并致力于flatten out it. 這個關系早先是比較混沌的,現在要清晰些,以后只會更清晰,struct usb_hcd越來越走上臺前,struct usb_bus越來越走向幕后。就好像我們一開始是天地混沌,然后是女媧造人,有了社會有了階級,再然后才有了新中國一樣。
3行,busnum,總線編號,你的機子里總可以有多個主機控制器吧,自然也就可以有多條usb總線了,既然可以有多條,就要編個號方便確認了。有關總線編號,可以看看定義在drivesr/usb/core/hcd.c里的這幾行
/* used when allocating bus numbers */
#define USB_MAXBUS 64
static DECLARE_BITMAP(busmap, USB_MAXBUS);
include/linux/types.h
#define DECLARE_BITMAP(name,bits) \unsigned long name[BITS_TO_LONGS(bits)]講struct usb_device的devnum時候,說到過一個devicemap,這里又有個busmap,當時分析說devicemap一共有128位,同理可知,這里的busmap一共有64位,也就是說最多可以有64條usb總線,如果你還覺得不夠,言一聲,我可以躲你遠遠的。
4行,bus_name,bus總線,name名字,bus_name總線的名字,什么樣的名字?要知道大多數情況下主機控制器都是一個PCI設備,那么bus_name應該就是用來在PCI總線上標識usb主機控制器的名字,PCI總線使用標準的PCI ID來標識PCI設備,所以bus_name里保存的應該就是主機控制器對應的PCI ID。UHCI等調用usb_create_hcd創建usb_hcd的時候確實是將它們對應PCI ID賦給了bus_name。
現在簡單說說這個PCI ID。PCI spec允許單個系統可以最多有256條PCI總線,對咱們當然是太多了,但是對于一些極變態,需求極為旺盛的系統,它可能還覺得這滿足不了要求,于是所有的PCI總線又被劃分為domain,每個PCI domain又可以最多擁有256條總線,這下總該夠了吧,而每條總線上又可以支持32個設備,這些設備里邊兒還都可以是多功能板,它們還都可以最多支持8種功能。那系統怎么來區分每種功能?總要知道它在哪個domain,哪條總線,哪個設備板上吧。這么說還是太籠統了,你可以用lspci命令看一下
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.2 USB Controller: Intel Corporation 82371AB/EB/MB PIIX4 USB
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:0f.0 VGA compatible controller: VMware Inc [VMware SVGA II] PCI Display Adapter
00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
00:11.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
00:12.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97] (rev 02)
每行前面的數字就是所謂的PCI ID,每個PCI ID由domain號(16位),總線編號(8位),設備號(5位),功能號(3位)組成,不過這里lspci沒有標明domain號,但對于一臺普通PC而言,一般也就只有一個domain,0x0000。
5行,uses_dma,表明這個主機控制器支持不支持DMA。主機控制器的一項重要工作就是在內存和USB總線之間傳輸數據,這個過程可以使用DMA或者不使用DMA,不使用DMA的方式即所謂的PIO方式。DMA代表著Direct Memory Access,即直接內存訪問,不需要CPU去干預。具體的去看看PCI DMA的東東吧,因為一般來說主機控制器都是PCI設備,uses_dma都在它們自己的probe函數里設置了。
10~12行,有關otg的,飄過。
21行,devnum_next,24行,devmap,早就說過devmap這張表了,devnum_next中記錄的就是這張表里下一個為0的位,里面為1的位都是已經被這條總線上的usb設備占據了的,名花有主的。
25行,root_hub,就好像端點0在所有設備的端點里面那么的鶴立雞群一樣,root hub在所有的hub里面也是那么的特殊,還記得usb的那顆樹么,它就是那顆樹的根,和usb主機控制器綁定在一起,其它的hub和設備都必須從它這兒延伸出去。正是因為這種特殊的關系,寫代碼的哥們兒也素有成人之心,就直接將它放在了struct usb_bus結構里,讓他們永不分離。usb主機控制器,usb總線,root hub,1比1比1。
27行,bus_list,在drivers/usb/core/hcd.c中定義有一個全局隊列usb_bus_list
/* host controllers we manage */
LIST_HEAD (usb_bus_list);
EXPORT_SYMBOL_GPL (usb_bus_list);它就是所有usb總線的組織。每次一條總線新添加進來,都要向這個組織靠攏,都要使用bus_list字段鏈接在這個隊列上。
31行,bandwidth_allocated,表明總線為中斷傳輸和等時傳輸預留了多少帶寬,協議里說了,對于高速來說,最多可以有80%,對于低速和全速要多點兒,可以達到90%。它的單位是微秒,表示一幀或微幀內有多少微秒可以留給中斷/等時傳輸用。
38行,bandwidth_int_reqs,39行,bandwidth_isoc_reqs,分別表示當前中斷傳輸和等時傳輸的數量。
43行,CONFIG_USB_MON是干嗎用的?這要看看drivers/usb/mon目錄下的Kconfig
#
# USB Monitor configuration
#config USB_MONtristate "USB Monitor"helpIf you select this option, a component which captures the USB trafficbetween peripheral-specific drivers and HC drivers will be built.For more information, see <file:Documentation/usb/usbmon.txt>.If unsure, say Y, if allowed, otherwise M.
文件里就這么多內容,從里面咱們可以知道,如果定義了CONFIG_USB_MON,一個所謂的usb Monitor,也就是usb監視器的東東就會編進內核。這個Monitor是用來監視usb總線上的底層通信流的,相關的文件都在drivers/usb/mon下面。2005年的陽春三月,Greg大俠春心思動,于是就孕育出了這個usb Monitor。
總結
以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(24)设备的生命线(七)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux那些事儿 之 戏说USB(22
- 下一篇: Linux那些事儿 之 戏说USB(25