usb_hcd_pci_probe带领我们开启了新的篇章.它就是神圣的PCI设备驱动程序.从此我们开始了PCI世界之旅,也将开始一段全新的体验.
细心的你或许注意到了,关于hcd的代码,被分布于两个目录,它们是drivers/usb/core/以及drivers/usb/host/,其中前者包含三个相关的文件,hcd-pci.c,hcd.c,hcd.h,这是一些公共的代码,因为我们知道,USB主机控制器有很多种,光从接口来说,目前就有UHCI,OHCI,EHCI,谁知道以后还会不会有更多呢.而这些主机控制器的驱动程序有一些代码是相同的,所以就把它提取出来,专门写在某几个文件里,因此有了这种格局.光就某一种具体的hcd的代码,还是在drivers/usb/host/下面,比如与uhci相关的代码就是以下几个文件,
localhost:/usr/src/linux-2.6.22.1/drivers/usb/host # ls uhci-*
uhci-debug.c uhci-hcd.c uhci-hcd.h uhci-hub.c uhci-q.c
就uhci的驱动来说,其四大函数指针probe/remove/suspend/resume都是指向一些公共的函数,都定义于drivers/usb/core/hcd-pci.c中,只有一个shutdown指针指向的函数uhci_shutdown是它自己定义的,来自drivers/usb/host/uhci-hcd.c中.
所以我们首先要看的probe函数,即usb_hcd_pci_probe是来自drivers/usb/core/hcd-pci.c,
46 /**
47 * usb_hcd_pci_probe - initialize PCI-based HCDs
48 * @dev: USB Host Controller being probed
49 * @id: pci hotplug id connecting controller to HCD framework
50 * Context: !in_interrupt()
51 *
52 * Allocates basic PCI resources for this USB host controller, and
53 * then invokes the start() method for the HCD associated with it
54 * through the hotplug entry's driver_data.
55 *
56 * Store this function in the HCD's struct pci_driver as probe().
57 */
58 int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
59 {
60 struct hc_driver *driver;
61 struct usb_hcd *hcd;
62 int retval;
63
64 if (usb_disabled())
65 return -ENODEV;
66
67 if (!id || !(driver = (struct hc_driver *) id->driver_data))
68 return -EINVAL;
69
70 if (pci_enable_device (dev) < 0)
71 return -ENODEV;
72 dev->current_state = PCI_D0;
73 dev->dev.power.power_state = PMSG_ON;
74
75 if (!dev->irq) {
76 dev_err (&dev->dev,
77 "Found HC with no IRQ. Check BIOS/PCI %s setup!/n",
78 pci_name(dev));
79 retval = -ENODEV;
80 goto err1;
81 }
82
83 hcd = usb_create_hcd (driver, &dev->dev, pci_name(dev));
84 if (!hcd) {
85 retval = -ENOMEM;
86 goto err1;
87 }
88
89 if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
90 hcd->rsrc_start = pci_resource_start (dev, 0);
91 hcd->rsrc_len = pci_resource_len (dev, 0);
92 if (!request_mem_region (hcd->rsrc_start, hcd->rsrc_len,
93 driver->description)) {
94 dev_dbg (&dev->dev, "controller already in use/n");
95 retval = -EBUSY;
96 goto err2;
97 }
98 hcd->regs = ioremap_nocache (hcd->rsrc_start, hcd->rsrc_len);
99 if (hcd->regs == NULL) {
100 dev_dbg (&dev->dev, "error mapping memory/n");
101 retval = -EFAULT;
102 goto err3;
103 }
104
105 } else { // UHCI
106 int region;
107
108 for (region = 0; region < PCI_ROM_RESOURCE; region++) {
109 if (!(pci_resource_flags (dev, region) &
110 IORESOURCE_IO))
111 continue;
112
113 hcd->rsrc_start = pci_resource_start (dev, region);
114 hcd->rsrc_len = pci_resource_len (dev, region);
115 if (request_region (hcd->rsrc_start, hcd->rsrc_len,
116 driver->description))
117 break;
118 }
119 if (region == PCI_ROM_RESOURCE) {
120 dev_dbg (&dev->dev, "no i/o regions available/n");
121 retval = -EBUSY;
122 goto err1;
123 }
124 }
125
126 pci_set_master (dev);
127
128 retval = usb_add_hcd (hcd, dev->irq, IRQF_SHARED);
129 if (retval != 0)
130 goto err4;
131 return retval;
132
133 err4:
134 if (driver->flags & HCD_MEMORY) {
135 iounmap (hcd->regs);
136 err3:
137 release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
138 } else
139 release_region (hcd->rsrc_start, hcd->rsrc_len);
140 err2:
141 usb_put_hcd (hcd);
142 err1:
143 pci_disable_device (dev);
144 dev_err (&dev->dev, "init %s fail, %d/n", pci_name(dev), retval);
145 return retval;
146 }
PCI设备驱动程序比USB设备驱动程序肯定要复杂,就像从未在各类国际级模特大赛T型台上出现过的林志玲被称为台湾第一名模一样,无可争议.其它我不说,光凭这幅经典的PCI标准配置寄存器的图就够我们这些新手们研究半天的.
看明白这张图就算对PCI设备驱动有了一点认识了,看不明白的话就说明还没入门.不过表慌,我也不懂,让我陪着你一起往下看,结合代码来看.不过你记住了,从此刻开始,这张图将被我们无数次的提起.为了便于称呼,我们给这张图取个好记的名字,就叫清明上坟图吧,下称上坟图.这张图在整个PCI世界里的作用就相当于我们学习化学的教材中最后几页里附上的那个化学元素周期表.写PCI设备驱动的人对于这张图的熟悉程度就要达到我们当时那种随口就能喊出氢氦锂铍硼碳氮氧氟氖钠镁铝硅磷硫氯氩钾钙的境界.
70行,pci_enable_device(),在一个pci设备可以被使用之前,必须调用pci_enable_device进行激活,该函数会调用底层代码激活PCI设备上的I/O资源和内存资源.而143行那个pci_disable_device则恰恰是做一些与之相反的事情.这两个函数是任何一个pci设备驱动程序都会调用的.只有在enable了设备之后,驱动程序才可以访问它的资源.
72行,73行,现在知道我在hub驱动中讲电源管理的原因了吧,老实说,不懂电源管理,在当今的Linux内核中那绝对是寸步难行.这里我们的dev是struct pci_dev结构体指针,它有一个成员,pci_power_t current_state.用来记录该设备的当前电源状态,这个世界上除了我们熟知的PCI Spec以外,还有一个规范叫做PCI Power Management Spec,它就专门为PCI设备定义那些电源管理方面的接口,按这个规范,PCI设备一共可以有四种电源状态,被称为D0,D1,D2,D3.正常的工作状态就是D0.而D3是耗电最少的状态.你要不求甚解的话就可以认为D3就意味着设备Power off了.在include/linux/pci.h中有关于这些状态的定义.
74 #define PCI_D0 ((pci_power_t __force) 0)
75 #define PCI_D1 ((pci_power_t __force) 1)
76 #define PCI_D2 ((pci_power_t __force) 2)
77 #define PCI_D3hot ((pci_power_t __force) 3)
78 #define PCI_D3cold ((pci_power_t __force) 4)
79 #define PCI_UNKNOWN ((pci_power_t __force) 5)
80 #define PCI_POWER_ERROR ((pci_power_t __force) -1)
而PMSG_ON我们在hub驱动中已经讲过了.不再多说.所以到现在你必须明白当初我的用心良苦了,当初分析电源管理的代码靠的就是一种男人的责任,<<奋斗>>中向南说过,责任不是说我们应该做什么,而是必须做什么.
75行,dev->irq,struct pci_dev有这么一个成员,unsigned int irq.这个意思很明显,中断号,它来自哪里?好,让我们第一次说一下这张清明上坟图了,每一个PCI设备都有一堆的寄存器,厂商就是按着这张图来设计自己的设备,这张图里全都是寄存器,但是并非所有的设备都要拥有这全部的寄存器,这其中有些是必选的,有些是可选的,就好比我们大学里面的必修课和选修课.比如,vendorID,deviceID,class这就是必选的,它们就用来标志一个设备,而subsystem vendorID,subsystem deviceID也是很多厂商会利用的,因为可以进一步的细分设备.这里这个class就是对应于我们前面提到过的那个class code.比如USB就是0x0c03.
仔细数一数,这张图里一共是64个bytes.而其中倒数第四个bytes,即byte 60,是IRQ Line.这个寄存器记录的正是该设备可以使用的中断号.在系统初始化的时候这个值就已经被写进去了,所以对于写设备驱动的人来说,不需要考虑太多,鲁迅先生对此有一个建议,叫做拿来主义,直接用就是了.这就是dev->irq这行的意思.USB Host Controller是必须有中断号的,否则肯定没法正常工作.
接下来,usb_create_hcd.这回才是正儿八经的进入到usb hcd的概念.这个函数来自drivers/usb/core/hcd.c:
1480 /**
1481 * usb_create_hcd - create and initialize an HCD structure
1482 * @driver: HC driver that will use this hcd
1483 * @dev: device for this HC, stored in hcd->self.controller
1484 * @bus_name: value to store in hcd->self.bus_name
1485 * Context: !in_interrupt()
1486 *
1487 * Allocate a struct usb_hcd, with extra space at the end for the
1488 * HC driver's private data. Initialize the generic members of the
1489 * hcd structure.
1490 *
1491 * If memory is unavailable, returns NULL.
1492 */
1493 struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
1494 struct device *dev, char *bus_name)
1495 {
1496 struct usb_hcd *hcd;
1497
1498 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
1499 if (!hcd) {
1500 dev_dbg (dev, "hcd alloc failed/n");
1501 return NULL;
1502 }
1503 dev_set_drvdata(dev, hcd);
1504 kref_init(&hcd->kref);
1505
1506 usb_bus_init(&hcd->self);
1507 hcd->self.controller = dev;
1508 hcd->self.bus_name = bus_name;
1509 hcd->self.uses_dma = (dev->dma_mask != NULL);
1510
1511 init_timer(&hcd->rh_timer);
1512 hcd->rh_timer.function = rh_timer_func;
1513 hcd->rh_timer.data = (unsigned long) hcd;
1514 #ifdef CONFIG_PM
1515 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
1516 #endif
1517
1518 hcd->driver = driver;
1519 hcd->product_desc = (driver->product_desc) ? driver->product_desc :
1520 "USB Host Controller";
1521
1522 return hcd;
1523 }