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_RUNTIME
	struct 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	4
	struct 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_MON
	tristate "USB Monitor"
	help
	  If you select this option, a component which captures the USB traffic
	  between 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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值