日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux那些事儿 之 戏说USB(25)设备的生命线(八)

發(fā)布時(shí)間:2023/11/27 生活经验 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux那些事儿 之 戏说USB(25)设备的生命线(八) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
回到struct usb_hcd,繼續(xù)努力的往下看。

7行,又見kref,usb主機(jī)控制器的引用計(jì)數(shù)。struct usb_hcd也有自己專用的引用計(jì)數(shù)函數(shù),看drivers/usb/core/hcd.c文件
static void hcd_release(struct kref *kref)
{struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);mutex_lock(&usb_port_peer_mutex);if (usb_hcd_is_primary_hcd(hcd))kfree(hcd->bandwidth_mutex);if (hcd->shared_hcd) {struct usb_hcd *peer = hcd->shared_hcd;peer->shared_hcd = NULL;if (peer->primary_hcd == hcd)peer->primary_hcd = NULL;}mutex_unlock(&usb_port_peer_mutex);kfree(hcd);
}struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
{if (hcd)kref_get (&hcd->kref);return hcd;
}
EXPORT_SYMBOL_GPL(usb_get_hcd);void usb_put_hcd (struct usb_hcd *hcd)
{if (hcd)kref_put (&hcd->kref, hcd_release);
}
EXPORT_SYMBOL_GPL(usb_put_hcd);

和struct urb的那幾個(gè)長(zhǎng)的也忒像了,像的俺都不好意思多介紹它們了,如果不明白就回去看看聊struct urb的時(shí)候怎么說的吧。
9行,product_desc,主機(jī)控制器的產(chǎn)品描述字符串,對(duì)于UHCI,它為“UHCI Host Controller”,對(duì)于EHCI,它為“EHCI Host Controller”。
14行,irq_descr[24],這里邊兒保存的是“ehci-hcd:usb1”之類的字符串,也就是驅(qū)動(dòng)的大名再加上總線編號(hào)。
18~20行,電源管理的,飄過。
25行,driver,每個(gè)男人心中都有一個(gè)狐貍精,每個(gè)女人心里都有一個(gè)洛麗塔,每個(gè)主機(jī)控制器驅(qū)動(dòng)都有一個(gè)struct hc_driver結(jié)構(gòu)體。看看它在hcd.h里的定義
struct hc_driver {const char	*description;	/* "ehci-hcd" etc */const char	*product_desc;	/* product/vendor string */size_t		hcd_priv_size;	/* size of private data *//* irq handler */irqreturn_t	(*irq) (struct usb_hcd *hcd);int	flags;
#define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
#define	HCD_LOCAL_MEM	0x0002		/* HC needs local memory */
#define	HCD_SHARED	0x0004		/* Two (or more) usb_hcds share HW */
#define	HCD_USB11	0x0010		/* USB 1.1 */
#define	HCD_USB2	0x0020		/* USB 2.0 */
#define	HCD_USB25	0x0030		/* Wireless USB 1.0 (USB 2.5)*/
#define	HCD_USB3	0x0040		/* USB 3.0 */
#define	HCD_MASK	0x0070
#define	HCD_BH		0x0100		/* URB complete in BH context *//* called to init HCD and root hub */int	(*reset) (struct usb_hcd *hcd);int	(*start) (struct usb_hcd *hcd);/* NOTE:  these suspend/resume calls relate to the HC as* a whole, not just the root hub; they're for PCI bus glue.*//* called after suspending the hub, before entering D3 etc */int	(*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);/* called after entering D0 (etc), before resuming the hub */int	(*pci_resume)(struct usb_hcd *hcd, bool hibernated);/* cleanly make HCD stop writing memory and doing I/O */void	(*stop) (struct usb_hcd *hcd);/* shutdown HCD */void	(*shutdown) (struct usb_hcd *hcd);/* return current frame number */int	(*get_frame_number) (struct usb_hcd *hcd);/* manage i/o requests, device state */int	(*urb_enqueue)(struct usb_hcd *hcd,struct urb *urb, gfp_t mem_flags);int	(*urb_dequeue)(struct usb_hcd *hcd,struct urb *urb, int status);/** (optional) these hooks allow an HCD to override the default DMA* mapping and unmapping routines.  In general, they shouldn't be* necessary unless the host controller has special DMA requirements,* such as alignment contraints.  If these are not specified, the* general usb_hcd_(un)?map_urb_for_dma functions will be used instead* (and it may be a good idea to call these functions in your HCD* implementation)*/int	(*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,gfp_t mem_flags);void    (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);/* hw synch, freeing endpoint resources that urb_dequeue can't */void	(*endpoint_disable)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);/* (optional) reset any endpoint state such as sequence numberand current window */void	(*endpoint_reset)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);/* root hub support */int	(*hub_status_data) (struct usb_hcd *hcd, char *buf);int	(*hub_control) (struct usb_hcd *hcd,u16 typeReq, u16 wValue, u16 wIndex,char *buf, u16 wLength);int	(*bus_suspend)(struct usb_hcd *);int	(*bus_resume)(struct usb_hcd *);int	(*start_port_reset)(struct usb_hcd *, unsigned port_num);/* force handover of high-speed port to full-speed companion */void	(*relinquish_port)(struct usb_hcd *, int);/* has a port been handed over to a companion? */int	(*port_handed_over)(struct usb_hcd *, int);/* CLEAR_TT_BUFFER completion callback */void	(*clear_tt_buffer_complete)(struct usb_hcd *,struct usb_host_endpoint *);/* xHCI specific functions *//* Called by usb_alloc_dev to alloc HC device structures */int	(*alloc_dev)(struct usb_hcd *, struct usb_device *);/* Called by usb_disconnect to free HC device structures */void	(*free_dev)(struct usb_hcd *, struct usb_device *);/* Change a group of bulk endpoints to support multiple stream IDs */int	(*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev,struct usb_host_endpoint **eps, unsigned int num_eps,unsigned int num_streams, gfp_t mem_flags);/* Reverts a group of bulk endpoints back to not using stream IDs.* Can fail if we run out of memory.*/int	(*free_streams)(struct usb_hcd *hcd, struct usb_device *udev,struct usb_host_endpoint **eps, unsigned int num_eps,gfp_t mem_flags);/* Bandwidth computation functions *//* Note that add_endpoint() can only be called once per endpoint before* check_bandwidth() or reset_bandwidth() must be called.* drop_endpoint() can only be called once per endpoint also.* A call to xhci_drop_endpoint() followed by a call to* xhci_add_endpoint() will add the endpoint to the schedule with* possibly new parameters denoted by a different endpoint descriptor* in usb_host_endpoint.  A call to xhci_add_endpoint() followed by a* call to xhci_drop_endpoint() is not allowed.*//* Allocate endpoint resources and add them to a new schedule */int	(*add_endpoint)(struct usb_hcd *, struct usb_device *,struct usb_host_endpoint *);/* Drop an endpoint from a new schedule */int	(*drop_endpoint)(struct usb_hcd *, struct usb_device *,struct usb_host_endpoint *);/* Check that a new hardware configuration, set using* endpoint_enable and endpoint_disable, does not exceed bus* bandwidth.  This must be called before any set configuration* or set interface requests are sent to the device.*/int	(*check_bandwidth)(struct usb_hcd *, struct usb_device *);/* Reset the device schedule to the last known good schedule,* which was set from a previous successful call to* check_bandwidth().  This reverts any add_endpoint() and* drop_endpoint() calls since that last successful call.* Used for when a check_bandwidth() call fails due to resource* or bandwidth constraints.*/void	(*reset_bandwidth)(struct usb_hcd *, struct usb_device *);/* Returns the hardware-chosen device address */int	(*address_device)(struct usb_hcd *, struct usb_device *udev);/* prepares the hardware to send commands to the device */int	(*enable_device)(struct usb_hcd *, struct usb_device *udev);/* Notifies the HCD after a hub descriptor is fetched.* Will block.*/int	(*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,struct usb_tt *tt, gfp_t mem_flags);int	(*reset_device)(struct usb_hcd *, struct usb_device *);/* Notifies the HCD after a device is connected and its* address is set*/int	(*update_device)(struct usb_hcd *, struct usb_device *);int	(*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);/* USB 3.0 Link Power Management *//* Returns the USB3 hub-encoded value for the U1/U2 timeout. */int	(*enable_usb3_lpm_timeout)(struct usb_hcd *,struct usb_device *, enum usb3_link_state state);/* The xHCI host controller can still fail the command to* disable the LPM timeouts, so this can return an error code.*/int	(*disable_usb3_lpm_timeout)(struct usb_hcd *,struct usb_device *, enum usb3_link_state state);int	(*find_raw_port_number)(struct usb_hcd *, int);
};
和usb_driver一樣,和pci_driver也一樣,所有的xxx_driver都有一堆函數(shù)指針,具體的主機(jī)控制器驅(qū)動(dòng)就靠它們來活的,不多說了,太深入了,做人要懂得適可而止,這里只說下函數(shù)指針之外的東西,也就是開頭兒的那三個(gè)。description直白點(diǎn)說就是驅(qū)動(dòng)的大名,比如對(duì)于UHCI,它是“uhci_hcd”,對(duì)于EHCI,它就是“ehci_hcd”。product_desc和struct usb_hcd里的那個(gè)是一個(gè)樣兒。hcd_priv_size還是有點(diǎn)兒意思的,前面喝那杯茶的時(shí)候提到過,每個(gè)主機(jī)控制器驅(qū)動(dòng)都會(huì)有一個(gè)私有結(jié)構(gòu)體,藏在struct usb_hcd最后的那個(gè)變長(zhǎng)數(shù)組里,這個(gè)變也是相對(duì)的,在創(chuàng)建usb_hcd的時(shí)候也得知道它能變多長(zhǎng),不然誰知道要申請(qǐng)多少內(nèi)存啊,這個(gè)長(zhǎng)度就hcd_priv_size。

38行,flags,屬于HCD的一些標(biāo)志,可用值就在39~44行。它們什么意思?書到用時(shí)方恨少,flags到用時(shí)才可知。

16,17,57~64,這幾行都是專為root hub服務(wù)的。佛說,一花一世界,一葉一如來。一個(gè)host controller對(duì)應(yīng)一個(gè)root hub,即使某些嵌入式系統(tǒng)里,硬件上host controller沒有集成root hub,軟件上也需要虛擬一個(gè)出來,也就是所謂的virtual root hub。它位置是特殊的,但需要提供的功能和其它hub是沒有什么差別的,僅僅是在和host controller的軟硬件接口上有一些特別的規(guī)定。root hub再怎么特別也始終是一個(gè)hub,是一個(gè)usb設(shè)備,也不能脫離usb這個(gè)大家庭,也要向組織注冊(cè),也要有自己的設(shè)備生命線,既然這里遇到了,就說說它的這條線。

/*------------------------------------------root hub的生命線-------------------------------------------------

還是要先聲明一下,root hub的生命線只是咱們的主線里的一個(gè)岔路口,不會(huì)沿著它去仔細(xì)的走,只會(huì)盡量的使用快鏡頭去展示一下。如果有哪里不明白,等走完了那條主線,你再回過頭來看看,很多事情都是在我們驀然回首的時(shí)候才明白的。

基于root hub和host controller這種極為曖昧極為特殊的關(guān)系,UHCI、EHCI等主機(jī)控制器驅(qū)動(dòng)程序在自己的初始化代碼里都有大量的篇幅大量的心思花在root hub上面。不管是UHCI,還是EHCI,一般來說都是PCI設(shè)備,是PCI設(shè)備都應(yīng)該有個(gè)struct pci_driver結(jié)構(gòu)體,都應(yīng)該有一個(gè)屬于自己的probe。在這個(gè)probe里,也都會(huì)調(diào)用usb_create_hcd()來創(chuàng)建一個(gè)屬于自己的usb_hcd,也都會(huì)調(diào)用usb_add_hcd()將這個(gè)剛剛創(chuàng)建的usb_hcd注冊(cè)到usb組織里。于是root hub便披著皇帝的新裝,走著貓步登場(chǎng)了。這里只貼與root hub相關(guān)的一些主要代碼

drivers/usb/core/hcd.c

	if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {dev_err(hcd->self.controller, "unable to allocate root hub\n");retval = -ENOMEM;goto err_allocate_root_hub;}mutex_lock(&usb_port_peer_mutex);hcd->self.root_hub = rhdev;mutex_unlock(&usb_port_peer_mutex);switch (hcd->speed) {case HCD_USB11:rhdev->speed = USB_SPEED_FULL;break;case HCD_USB2:rhdev->speed = USB_SPEED_HIGH;break;case HCD_USB25:rhdev->speed = USB_SPEED_WIRELESS;break;case HCD_USB3:rhdev->speed = USB_SPEED_SUPER;break;default:retval = -EINVAL;goto err_set_rh_speed;}

在usb_add_hcd函數(shù)里,首先會(huì)調(diào)用咱們已經(jīng)親密接觸過的usb_alloc_dev()來為root hub準(zhǔn)備一個(gè)struct usb_device結(jié)構(gòu)體,我唯一想再提一下的是這時(shí)root hub的設(shè)備類型被賦值為usb_device_type,所屬總線類型賦值為usb_bus_type。然后根據(jù)各個(gè)host controller的實(shí)際情況,設(shè)定它的速度。struct usb_device結(jié)構(gòu)體準(zhǔn)備妥當(dāng)之后,就要賦給struct usb_bus的root_hub元素。這些都只不過是準(zhǔn)備工作,重頭戲都在usb_add_hcd()最后調(diào)用的register_root_hub()里面。

register_root_hub()非常肯定的將root hub的設(shè)備號(hào)devnum設(shè)置為1,于是就少了選擇地址設(shè)置地址的過程,root hub直接跳躍式發(fā)展進(jìn)入了Address狀態(tài),咱們耗費(fèi)幾代人,期待數(shù)十年才能完成的壯舉,root hub輕而易舉的就實(shí)現(xiàn)了。當(dāng)然,是個(gè)人都知道在Address狀態(tài)是好處多多便利多多的,不然房地產(chǎn)就支柱不起來了。起碼這時(shí)可以很方便的獲得root hub的設(shè)備描述符,可以調(diào)用usb_new_device將root hub送給無所不能的設(shè)備模型,去尋找它命中的驅(qū)動(dòng)。

drivers/usb/core/hcd.c

	retval = usb_new_device (usb_dev);if (retval) {dev_err (parent_dev, "can't register root hub for %s, %d\n",dev_name(&usb_dev->dev), retval);} else {spin_lock_irq (&hcd_root_hub_lock);hcd->rh_registered = 1;spin_unlock_irq (&hcd_root_hub_lock);/* Did the HC die before the root hub was registered? */if (HCD_DEAD(hcd))usb_hc_died (hcd);	/* This time clean up */}
如果這些都一切順利的話,register_root_hub()在最后就會(huì)將struct usb_hcd的rh_registered設(shè)置為1,表示root hub已經(jīng)找到組織并加入到革命隊(duì)伍里了。

Linux設(shè)備模型根據(jù)root hub所屬的總線類型usb_bus_type將它添加到usb總線的那條有名的設(shè)備鏈表里,然后會(huì)輪詢usb總線的另外一條有名的驅(qū)動(dòng)鏈表,針對(duì)每個(gè)找到的驅(qū)動(dòng)去調(diào)用usb總線的match函數(shù),也就是咱們以前一再遇到以后不斷遇到的usb_device_match(),為root hub牽線搭橋?qū)ふ伊硪粋€(gè)匹配的半圓。要注意,root hub的設(shè)備類型是usb_device_type,所以在usb_device_match()里走的設(shè)備那條路,匹配成功的是那個(gè)對(duì)所有usb_device_type類型的設(shè)備都來者不拒的花心大蘿卜——usb世界里唯一的那個(gè)usb設(shè)備驅(qū)動(dòng)(不是usb接口驅(qū)動(dòng))struct device_driver結(jié)構(gòu)體對(duì)象usb_generic_driver。所以接下來要調(diào)用的就是usb_generic_driver的probe函數(shù)generic_probe(),去配置root hub,然后root hub就大步邁進(jìn)了Configured狀態(tài)。因?yàn)閞oot hub只有一個(gè)配置,所以generic_probe()配置root hub時(shí)并沒有多少選擇的煩惱,如果有所疑問,可以look下hcd.c的開頭就已經(jīng)設(shè)定好的root hub設(shè)備描述符
/* usb 2.0 root hub device descriptor */
static const u8 usb2_rh_dev_descriptor[18] = {0x12,       /*  __u8  bLength; */0x01,       /*  __u8  bDescriptorType; Device */0x00, 0x02, /*  __le16 bcdUSB; v2.0 */0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */0x00,	    /*  __u8  bDeviceSubClass; */0x00,       /*  __u8  bDeviceProtocol; [ usb 2.0 no TT ] */0x40,       /*  __u8  bMaxPacketSize0; 64 Bytes */0x6b, 0x1d, /*  __le16 idVendor; Linux Foundation 0x1d6b */0x02, 0x00, /*  __le16 idProduct; device 0x0002 */KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */0x03,       /*  __u8  iManufacturer; */0x02,       /*  __u8  iProduct; */0x01,       /*  __u8  iSerialNumber; */0x01        /*  __u8  bNumConfigurations; */
};
這張表是const的,也就是說在整個(gè)usb的世界里它都是只能去讀不能去修改的,要用嚴(yán)謹(jǐn)科學(xué)的態(tài)度去對(duì)待root hub。明眼人一眼就能看到躲在它最后的那個(gè)0x01,這就是root hub支持的配置數(shù)量。在它下邊兒還有針對(duì)usb 1.1的root hub設(shè)備描述符,大同小異就不貼了。

既然進(jìn)入了Configured狀態(tài),就可以無拘無束的使用root hub提供的所有功能了,不過,對(duì)于咱們使用usb的勞苦大眾來說,實(shí)際在起作用的還是設(shè)備里的接口,所以generic_probe()接下來就會(huì)根據(jù)root hub使用的配置,為它所有的接口準(zhǔn)備struct usb_interface結(jié)構(gòu)體對(duì)象,這時(shí)接口所屬的總線類型仍然為usb_bus_type,設(shè)備類型就不一樣了,為usb_if_device_type,早先說過的那句總線有總線的類型,設(shè)備有設(shè)備的類型到這里就應(yīng)該再加上一句,接口有接口的類型。usb_if_device_type在message.c里定義
struct device_type usb_if_device_type = {.name =		"usb_interface",.release =	usb_release_interface,.uevent =	usb_if_uevent,
};

為root hub的接口準(zhǔn)備struct usb_interface結(jié)構(gòu)體也沒費(fèi)太大功夫,因?yàn)閟pec里規(guī)定了hub上除了端點(diǎn)0,就只有一個(gè)中斷的IN端點(diǎn),并不用準(zhǔn)備太多的東西。root hub的配置描述符也已經(jīng)在hcd.c的開頭兒指定好了,確實(shí)只有一個(gè)接口,一個(gè)設(shè)置,一個(gè)端點(diǎn),去look一下

drivers/usb/core/hcd.c

/* Configuration descriptors for our root hubs */static const u8 fs_rh_config_descriptor[] = {/* one configuration */0x09,       /*  __u8  bLength; */0x02,       /*  __u8  bDescriptorType; Configuration */0x19, 0x00, /*  __le16 wTotalLength; */0x01,       /*  __u8  bNumInterfaces; (1) */0x01,       /*  __u8  bConfigurationValue; */0x00,       /*  __u8  iConfiguration; */0xc0,       /*  __u8  bmAttributes;Bit 7: must be set,6: Self-powered,5: Remote wakeup,4..0: resvd */0x00,       /*  __u8  MaxPower; *//* USB 1.1:* USB 2.0, single TT organization (mandatory):*	one interface, protocol 0** USB 2.0, multiple TT organization (optional):*	two interfaces, protocols 1 (like single TT)*	and 2 (multiple TT mode) ... config is*	sometimes settable*	NOT IMPLEMENTED*//* one interface */0x09,       /*  __u8  if_bLength; */0x04,       /*  __u8  if_bDescriptorType; Interface */0x00,       /*  __u8  if_bInterfaceNumber; */0x00,       /*  __u8  if_bAlternateSetting; */0x01,       /*  __u8  if_bNumEndpoints; */0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */0x00,       /*  __u8  if_bInterfaceSubClass; */0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */0x00,       /*  __u8  if_iInterface; *//* one endpoint (status change endpoint) */0x07,       /*  __u8  ep_bLength; */0x05,       /*  __u8  ep_bDescriptorType; Endpoint */0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */0x03,       /*  __u8  ep_bmAttributes; Interrupt */0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
};

9行的0x01,34行0x00,35行的0x01,分別指定了接口數(shù)量,使用的設(shè)置,端點(diǎn)的數(shù)目。也就是說,如果加上端點(diǎn)0,root hub只有兩個(gè)端點(diǎn),而另外一個(gè)端點(diǎn)為IN端點(diǎn),端點(diǎn)號(hào)為1,中斷類型,每次可以處理的最大字節(jié)數(shù)為2,期望host controller訪問自己的時(shí)間間隔為256ms。這里打個(gè)岔問個(gè)問題,為什么說這個(gè)中斷端點(diǎn)的wMaxPacketSize為2,46行不是明確寫著0x02,0x00么?呵呵,協(xié)議里說了,usb設(shè)備的各種描述符都是按照little-endian方式的字節(jié)序存儲(chǔ)的,先是低字節(jié)然后是高字節(jié)。

為root hub的那個(gè)獨(dú)苗兒接口準(zhǔn)備好struct usb_interface結(jié)構(gòu)體之后應(yīng)該怎么做?佛又說了,一個(gè)接口一個(gè)驅(qū)動(dòng),接下來顯然是應(yīng)該將它送給設(shè)備模型去尋找它命中注定的驅(qū)動(dòng)了。然后此處省略2008字,又到了usb_device_match()。不過你說這個(gè)時(shí)候設(shè)備和接口兩條路它應(yīng)該走哪條?它的類型已經(jīng)設(shè)置成usb_if_device_type了,設(shè)備那條路把門兒的根本就不會(huì)讓它進(jìn),所以它必須得去走接口那條路。

有關(guān)在接口這條路上它怎么去尋找另一半兒,咱現(xiàn)在不多說,日后在主線里說,現(xiàn)在只天馬行空的扯一下。首先回頭瞧下root hub的設(shè)備描述符,它的bDeviceClass 被指定為0x09,在include/linux/usb/ch9.h里,它對(duì)應(yīng)的是USB_CLASS_HUB,再回頭看看root hub配置描述符的36行,bInterfaceClass也被指定成0x09,也就是說同樣為USB_CLASS_HUB。你再去hub驅(qū)動(dòng)對(duì)應(yīng)的hub.c文件里看看那里的hub_id_table,是不是應(yīng)該明白點(diǎn)什么了?對(duì)頭,root hub里的那個(gè)接口所對(duì)應(yīng)的驅(qū)動(dòng)就是hub驅(qū)動(dòng),雖然它很特殊,可它也是個(gè)hub啊。root hub一邊和host controller相生相依,一邊和hub驅(qū)動(dòng)眉來眼去花前月下,多么巨大的諷刺。

接下來要做的就是調(diào)用hub驅(qū)動(dòng)里的hub_probe()。不管是root hub還是一般的hub,走到這一步都是必然的,不同的是對(duì)于root hub在host controller初始化的過程中就會(huì)去調(diào)用hub_probe()。再省略2008字后,hub_probe()通過hub_configure()創(chuàng)建并初始化了一個(gè)中斷的urb,然后在hub_activate()里調(diào)用usb_submit_urb()將它提交給core,接著就又回到了HCD。如果覺得省略了這么多讓你很不爽,那就再去看看《我是hub》吧。

每個(gè)hub都要需要進(jìn)行中斷傳輸來獲得hub的狀態(tài)變化,不然hub驅(qū)動(dòng)里那個(gè)著名的hub_events就活不下去了,你連在hub上的usb設(shè)備系統(tǒng)也就察覺不到了,你也就不能用usb攝像頭來泡mm了,都走到了21世紀(jì)了,都應(yīng)該知道中斷傳輸不會(huì)提交一次就完事兒了,這么一次就完事兒hub不會(huì)滿意,每個(gè)男人女人都不會(huì)滿意。你需要在你的結(jié)束處理函數(shù)里提交再提交,host controller都不嫌煩,你煩什么啊。中斷傳輸都是有個(gè)間隔時(shí)間的,這個(gè)時(shí)間不由你決定,也不由我決定,咱們只能期待,決定權(quán)在host controller那里。host controller就決定了,對(duì)于root hub要每隔250ms去訪問一次,你再看上面root hub的描述符,里面顯示它期待的時(shí)間為0xff,也就是256ms,250與256也差不了多少,root hub也應(yīng)該滿意了。這個(gè)固定的訪問頻率host controller是怎么實(shí)現(xiàn)的?就是通過struct usb_hcd里的那個(gè)定時(shí)器rh_timer,定時(shí)器就是專門干這種事兒的,root hub每提交一次urb,在HCD里都會(huì)對(duì)它初始化一次,指定250ms之后去獲得root hub的狀態(tài),然后就讓urb返回給root hub,于是root hub再提交,再250ms后返回,……。

但是這種對(duì)root hub的輪詢機(jī)制是早先版本里的,現(xiàn)在就不一樣了。struct usb_hcd里出現(xiàn)了uses_new_polling,看名字就知道是一種新機(jī)制出現(xiàn)了。這也是Alan Stern在2005年的陽春三月春心萌動(dòng)孕育出來的,舊機(jī)制主要靠輪詢,新機(jī)制主要靠中斷。在新的機(jī)制里,root hub仍然是要提交一個(gè)中斷urb的,不同的是這時(shí)它可以不用再請(qǐng)定時(shí)器來幫忙,在有設(shè)備插入時(shí),host controller會(huì)在自己的中斷處理函數(shù)里調(diào)用一個(gè)名叫usb_hcd_poll_rh_status的函數(shù)去獲得root hub的狀態(tài)并將urb的所有權(quán)歸還給hub驅(qū)動(dòng)。

那是不是就可以不需要定時(shí)器rh_timer了?事情沒這么簡(jiǎn)單,也不能就這么過河拆橋,由于某些不可言狀的原因,原來的機(jī)制還必須得保留著,給不同的host controller選擇使用。為了讓新舊機(jī)制和諧的運(yùn)作,usb_hcd_poll_rh_status多了一個(gè)職務(wù),就是作為rh_timer的定時(shí)器函數(shù),而且struct usb_hcd里除了uses_new_polling之外就又多了poll_rh等幾個(gè)元素。如果uses_new_polling沒有被設(shè)置,則一定會(huì)采用舊的輪詢機(jī)制,如果uses_new_polling已經(jīng)被設(shè)置了,但同時(shí)又設(shè)置了poll_rh,也是要采用舊機(jī)制,否則采用的就是新機(jī)制。至于status_urb,其實(shí)就是root hub提交的那個(gè)urb。

root hub的生命線就還是說到這里吧,適可而止適可而止。
-------------------------------------------------------------------------------------------------------------*/

回到struct usb_hcd,看92行,wireless,是不是無線usb。

72~75這幾行都是與主機(jī)控制器的娘家PCI有關(guān)的,說到PCI就不得不說到那張著名的表。PCI spec說了,要想在我這兒混,誰都必須不能少了那張表。

不過強(qiáng)制的也不一定都是壞事兒,起碼這張表不是,中斷號(hào)啊等很多有用的東西都在里面準(zhǔn)備好了。72行的irq就躲在上面表兒里的倒數(shù)第四個(gè)byte,HCD可以直接拿來用,根本就不用再去申請(qǐng),一提到申請(qǐng)個(gè)什么,誰都知道那是多么一個(gè)艱辛的過程。接下來的regs,rsrc_start,rsrc_len就與中間的那幾個(gè)Base Address0~5脫不開關(guān)系了,牽涉到所謂的I/O內(nèi)存和I/O端口,簡(jiǎn)單說一下。

大家都知道CPU牛X,是眾人矚目的焦點(diǎn),但是它再牛也不可能一個(gè)人戰(zhàn)斗,電腦運(yùn)轉(zhuǎn)是個(gè)集體項(xiàng)目,有很多千奇百怪的外設(shè)。CPU也需要跟各種外設(shè)配合交流,它需要訪問外設(shè)里的那些寄存器或者內(nèi)存。現(xiàn)在差別就出來了,主要是空間的差別,一些CPU芯片有兩個(gè)空間,即I/O空間和內(nèi)存空間,提供有專門訪問外設(shè)I/O端口的指令,而另外一些只有一個(gè)空間,即內(nèi)存空間。外設(shè)的I/O端口可以映射在I/O空間也可以映射到內(nèi)存空間,CPU通過訪問這兩個(gè)空間來訪問外設(shè),I/O空間有I/O空間訪問的接口,內(nèi)存空間有內(nèi)存空間訪問的接口。當(dāng)然某些外設(shè)不但有寄存器,還有內(nèi)存,也就是I/O內(nèi)存,比如EHCI/OHCI,它們需要映射到內(nèi)存空間。但是不管映射到哪個(gè)空間,訪問I/O端口還是I/O內(nèi)存,CPU必須知道它們映射后的地址,不然沒有辦法配合交流。

上面表里中間的那些Base Address保存的就是PCI設(shè)備里I/O內(nèi)存或I/O端口的首尾位置還有長(zhǎng)度,驅(qū)動(dòng)里使用時(shí)要首先把它們給讀出來,如果要映射到I/O空間,則要根據(jù)讀到的值向系統(tǒng)申請(qǐng)I/O端口資源,如果要映射到內(nèi)存空間,除了要申請(qǐng)內(nèi)存資源,還要使用ioremap等進(jìn)行映射。

74行的rsrc_start和75行的rsrc_len保存的就是從表里讀出來的host controller的I/O端口或內(nèi)存的首地址和長(zhǎng)度,73行的regs保存的是調(diào)用ioremap_nocache映射后的內(nèi)存地址。

76行,power_budget,能夠提供的電流。

98行,*pool [HCD_BUFFER_POOLS],幾個(gè)dma池。因?yàn)镠CD_BUFFER_POOLS在97行定義為4,所以這里就表示每個(gè)主機(jī)控制器可以有4個(gè)dma池。

我們知道主機(jī)控制器是可以進(jìn)行DMA傳輸?shù)?#xff0c;鏡頭再回到前面的struct urb,那里有兩個(gè)成員transfer_dma和setup_dma,當(dāng)時(shí)只是說你可以使用usb_buffer_alloc分配好dma緩沖區(qū)給它們,然后再告訴HCD你的urb已經(jīng)有了,HCD就可以不用再進(jìn)行復(fù)雜的DMA映射了,并沒有提到你這個(gè)獲取的dma緩沖區(qū)是從哪里來的。俺都暗示到這種地步了,你用屁股也能猜出來是從這里所說的dma池子里來的了。

混了這么久,俺對(duì)池子還是有比較美好的回憶的,還親手搭建過線程池、數(shù)據(jù)庫連接池等等池子,像線程啊數(shù)據(jù)庫連接啊什么的創(chuàng)建銷毀的時(shí)候不是比較的耗資源么,就先建個(gè)池子預(yù)先創(chuàng)建好一批線程什么的放里邊兒,用的時(shí)候就從里面取出來,不用的時(shí)候就再放里面,用的越多就越劃的來,號(hào)稱將負(fù)擔(dān)均分了。所以說,池子是多么的重要啊。

當(dāng)然,dma池沒這么幼稚,它還有其它的內(nèi)涵。一般來說DMA映射獲得的都是以頁為單位的內(nèi)存,urb需要不了這么大,如果需要比較小的DMA緩沖區(qū),就離不開DMA池了。還是看看主機(jī)控制器的這幾個(gè)池子是怎么創(chuàng)建的,在drivers/usb/core/buffer.c 文件里
int hcd_buffer_create(struct usb_hcd *hcd)
{char		name[16];int		i, size;if (!hcd->self.controller->dma_mask &&!(hcd->driver->flags & HCD_LOCAL_MEM))return 0;for (i = 0; i < HCD_BUFFER_POOLS; i++) {size = pool_max[i];if (!size)continue;snprintf(name, sizeof name, "buffer-%d", size);hcd->pool[i] = dma_pool_create(name, hcd->self.controller,size, size, 0);if (!hcd->pool[i]) {hcd_buffer_destroy(hcd);return -ENOMEM;}}return 0;
}
這里首先要判斷下這個(gè)主機(jī)控制器支持不支持DMA,如果不支持的話再創(chuàng)建什么DMA池就是純粹無稽之談了。如果支持DMA,就逐個(gè)適用dma_pool_alloc來創(chuàng)建DMA池,如果創(chuàng)建失敗了,就調(diào)用同一個(gè)文件里的hcd_buffer_destroy來將已經(jīng)創(chuàng)建成功的池子給銷毀掉。
void hcd_buffer_destroy(struct usb_hcd *hcd)
{int i;for (i = 0; i < HCD_BUFFER_POOLS; i++) {struct dma_pool *pool = hcd->pool[i];if (pool) {dma_pool_destroy(pool);hcd->pool[i] = NULL;}}
}
這里調(diào)用的dma_pool_destroy和上面的dma_pool_alloc也是相映成趣,都是DMA池子領(lǐng)域的小白領(lǐng)。帶翅膀的丘比特說了,每個(gè)人的人生都要找到四個(gè)人,第一個(gè)是自己,第二個(gè)是你最愛的人,第三個(gè)是最愛你的人,第四個(gè)是共度一生的人。他還說了,每個(gè)DMA池子都要找到四個(gè)函數(shù),一個(gè)用來創(chuàng)建,一個(gè)用來銷毀,一個(gè)用來取內(nèi)存,一個(gè)用來放內(nèi)存。上邊兒只遇到了創(chuàng)建和銷毀的,還少兩個(gè)取和放的,再去同一個(gè)文件里找找
void *hcd_buffer_alloc(struct usb_bus		*bus,size_t			size,gfp_t			mem_flags,dma_addr_t		*dma
)
{struct usb_hcd		*hcd = bus_to_hcd(bus);int			i;/* some USB hosts just use PIO */if (!bus->controller->dma_mask &&!(hcd->driver->flags & HCD_LOCAL_MEM)) {*dma = ~(dma_addr_t) 0;return kmalloc(size, mem_flags);}for (i = 0; i < HCD_BUFFER_POOLS; i++) {if (size <= pool_max[i])return dma_pool_alloc(hcd->pool[i], mem_flags, dma);}return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags);
}void hcd_buffer_free(struct usb_bus		*bus,size_t			size,void			*addr,dma_addr_t		dma
)
{struct usb_hcd		*hcd = bus_to_hcd(bus);int			i;if (!addr)return;if (!bus->controller->dma_mask &&!(hcd->driver->flags & HCD_LOCAL_MEM)) {kfree(addr);return;}for (i = 0; i < HCD_BUFFER_POOLS; i++) {if (size <= pool_max[i]) {dma_pool_free(hcd->pool[i], addr, dma);return;}}dma_free_coherent(hcd->self.controller, size, addr, dma);
}
又可以找到兩個(gè)dma_pool_alloc和dma_pool_free,現(xiàn)在可以湊齊四個(gè)了。不過咱們不用管它們四個(gè)到底什么長(zhǎng)相,只要它們能夠干活兒就成了,這里要注意的是幾個(gè)問題。第一個(gè)是即使你的主機(jī)控制器不支持DMA,這幾個(gè)函數(shù)也是可以用的,只不過創(chuàng)建的不是DMA池子,取的也不是DMA緩沖區(qū),此時(shí),DMA池子不存在,hcd_buffer_alloc獲取的只是適用kmalloc申請(qǐng)的普通內(nèi)存,當(dāng)然相應(yīng)的,你必須在它沒有利用價(jià)值的時(shí)候使用hcd_buffer_free將它釋放掉。

第二個(gè)問題是size的問題,也是每個(gè)男人女人的問題。看到它們里面都有個(gè)pool_max吧,這是個(gè)同一個(gè)文件里定義的數(shù)組
static const size_t	pool_max[HCD_BUFFER_POOLS] = {/* platforms without dma-friendly caches might need to* prevent cacheline sharing...*/32,128,512,PAGE_SIZE / 2/* bigger --> allocate pages */
};
這個(gè)數(shù)組里定義的就是四個(gè)池子中每個(gè)池子里保存的DMA緩沖區(qū)的size。注意這里雖說只定義了四種size,但是并不說明你使用hcd_buffer_alloc獲取DMA緩沖區(qū)的時(shí)候不能指定更大的size,如果誰誰太貪心了,欲望太強(qiáng)烈了,這幾個(gè)池子都滿足不了她的要求,那就會(huì)使用dma_alloc_coherent為她建立一個(gè)新的DMA映射。還有,每個(gè)人的情況都不一樣,不可能都會(huì)完全恰好和上面定義的四種size一致,那也不用怕,這不是病,使用這個(gè)size獲取DMA緩沖區(qū)的時(shí)候,池子會(huì)選擇一個(gè)略大一些的回饋過去。

還是讓咱們拋開size,回到struct usb_hcd中來,100行,state,主機(jī)控制器的狀態(tài),緊挨著它的下面那些行就是相關(guān)的可用值和宏定義。咱們自己的狀態(tài)還都稀里糊涂的,就先不說它們了。

123行,如果不是有短暫失憶癥或選擇性失憶癥的話,是會(huì)知道這是什么意思用來干嗎的。

總結(jié)

以上是生活随笔為你收集整理的Linux那些事儿 之 戏说USB(25)设备的生命线(八)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。