科创园

科创园地,分享技术知识,为科技助力发展,贡献一己之力。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

usb驱动开发15之设备生命线

Posted on 2014-03-31 18:23  科创园  阅读(1444)  评论(0编辑  收藏  举报

总算是进入了HCD的片儿区,既然来到一个片区,怎么都要去拜会一下山头几个大哥吧。,先回忆一些我们怎么到这里的?给你列举一个调用函数过程usb_control_msg->usb_internal_control_msg->usb_start_wait_urb->usb_submit_urb->usb_hcd_submit_urb。这个山头儿,王中之王就是drivers/usb/core/hcd.h里定义的struct usb_hcd。

/*-------------------------------------------------------------------------*/

/*

* USB Host Controller Driver (usb_hcd) framework

*

* Since "struct usb_bus" is so thin, you can't share much code in it.

* This framework is a layer over that, and should be more sharable.

*/

/*-------------------------------------------------------------------------*/

struct usb_hcd {

/*

* housekeeping

*/

struct usb_bus self; /* hcd is-a bus */

struct kref kref; /* reference counter */

const char *product_desc; /* product/vendor string */

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

struct work_struct wakeup_work; /* for remote wakeup */

#endif

/*

* hardware info/state

*/

const struct hc_driver *driver; /* hw-specific hooks */

/* Flags that need to be manipulated atomically */

unsigned long flags;

#define HCD_FLAG_HW_ACCESSIBLE 0x00000001

#define HCD_FLAG_SAW_IRQ 0x00000002

unsigned rh_registered:1;/* is root hub registered? */

/* 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 poll_rh:1; /* poll for rh status? */

unsigned poll_pending:1; /* status has changed? */

unsigned wireless:1; /* Wireless USB HCD */

int irq; /* irq allocated */

void __iomem *regs; /* device memory/io */

u64 rsrc_start; /* memory/io resource start */

u64 rsrc_len; /* memory/io resource length */

unsigned power_budget; /* in mA, 0 = no limit */

#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(unsigned long))));

};

看第一个结构体,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) */

char *bus_name; /* stable id (PCI slot_name etc) */

u8 uses_dma; /* Does the host controller use DMA? */

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? */

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 list_head bus_list; /* list of busses */

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 */

#ifdef CONFIG_USB_DEVICEFS

struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */

#endif

struct class_device *class_dev; /* class device for this bus */

#if defined(CONFIG_USB_MON)

struct mon_bus *mon_bus; /* non-null when associated */

int monitored; /* non-zero when monitored */

#endif

};

感叹一下,每一个结构体分析都不容易啊。先去喝杯茶晒晒微博,然后再继续分析usb_bus 结构体成员。

controller,struct usb_hcd那里含了个usb_bus,这里就回应了个controller,遥相呼应。通过struct usb_hcd里的self和struct usb_bus里的controller,能说下它们到底是什么关系?你当然可以说一个对应主机控制器,一个描述一条总线,但其实对于写代码的来说一个主机控制器和一条总线差不多是一码事,不用分的那么清,可以简单的说它们都是用来描述主机控制器的,那为什么又分成了两个结构?首先,前面说过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才是王中之王的原因。

busnum,总线编号,你的机子里总可以有多个主机控制器吧,自然也就可以有多条usb总线了,既然可以有多条,就要编个号方便确认了。有关总线编号,可以看看定义在drivesr/usb/core/hcd.c里的几行。

/* used when allocating bus numbers */

#define USB_MAXBUS 64

struct usb_busmap {

unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];

};

static struct usb_busmap busmap;

讲struct usb_device的devnum时候,说到过一个devicemap,这里又有个busmap,当时分析说devicemap一共有128位,同理可知,这里的busmap一共有64位,也就是说最多可以有64条usb总线。

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。

继续看usb_bus结构体的成员。

uses_dma,表明这个主机控制器是否支持DMA。主机控制器的一项重要工作就是在内存和USB总线之间传输数据,这个过程可以使用DMA或者不使用DMA,不使用DMA的方式即所谓的PIO方式。DMA代表着Direct Memory Access,即直接内存访问,不需要CPU去干预。具体的去看看PCI DMA的东东吧,因为一般来说主机控制器都是PCI设备,uses_dma都在它们自己的probe函数里设置了。

有关otg的,飘过,我也看不懂。

devnum_next,devmap,早就说过devmap这张表了,devnum_next中记录的就是这张表里下一个为0的位,里面为1的位都是已经被这条总线上的usb设备占据了的。

root_hub,就好像端点0在所有设备的端点里面那么的鹤立鸡群一样,root hub在所有的hub里面也是那么的特殊,还记得usb的那颗树么,它就是那颗树的根,和usb主机控制器绑定在一起,其它的hub和设备都必须从它这儿延伸出去。正是因为这种特殊的关系,就直接将它放在了struct usb_bus结构里,让他们永不分离。usb主机控制器,usb总线,root hub,数学关系是1比1比1。

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字段链接在这个队列上。

bandwidth_allocated,表明总线为中断传输和等时传输预留了多少带宽,协议里说了,对于高速来说,最多可以有80%,对于低速和全速要多点儿,可以达到90%。它的单位是微妙,表示一帧或微帧内有多少微妙可以留给中断/等时传输用。

bandwidth_int_reqs,bandwidth_isoc_reqs,分别表示当前中断传输和等时传输的数量。

usbfs_dentry是usbfs的,每条总线都对应于/proc/bus/usb下的一个目录。

class_dev,这里又牵涉到设备模型中的一个概念即设备的类。像前面提到的设备模型里的总线、设备、驱动三个核心概念,纯粹是从写驱动的角度看的,而这里的类则是面向于linux的广大用户的,它不管你是用什么接口,怎么去连接,它只管你对用户来说提供了什么功能,一个SCSI硬盘和一个ATA硬盘对驱动来说是八杆子打不着的两个东西,但是对于用户来说,它们都是硬盘,都是用来备份文件。

设备模型与sysfs是分不开的,class在sysfs里的体现就在/sys/class下面

看到里面的usb_host了吧,它就是所有usb主机控制器的类,这些目录都是怎么来的那?咱们还要回溯一下usb子系统的初始化函数usb_init,它里面有这么一段

retval = usb_host_init();

if (retval)

goto host_init_failed;

当时只是简单说这是用来初始化host controller的,现在鼓气勇气进去看看, usb_host_init所作的一切就是调用class_create创建了一个usb_host这样的类,你只要加载了usbcore模块就能在/sys/class下面看到有usb_host目录出现。既然usb_host目录表示的是usb主机控制器的类,那么它下面应该就对应各个具体的主机控制器了,你用ls 命令look一下就能看到usb_host1、usb_host2等等这样的目录,它们每个都对应一个在你系统里实际存在的主机控制器,实际上在hcd.c里的usb_register_bus函数有这么一行

bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0),

bus->controller, "usb_host%d", busnum);

这行就是使用class_device_create在/sys/class/usb_host下面为每条总线创建了一个目录,目录名里的数字代表的就是每条总线的编号,usb_register_bus函数是每个主机控制器驱动在probe里调用的,向usb core注册一条总线,也可以说是注册一个主机控制器。

最后看usb_bus的最后一个成员CONFIG_USB_MON,干吗用的?这要看看drivers/usb/mon目录下的Kconfig,文件里就这么多内容,从里面咱们可以知道,如果定义了CONFIG_USB_MON,一个所谓的usb Monitor,也就是usb监视器会编进内核。这个Monitor是用来监视usb总线上的底层通信流的,相关的文件都在drivers/usb/mon下面。

聊完了usb_bus的结构成员,现在把目光回到usb_hcd结构体。