485行,hcd_to_uhci,来自drivers/usb/host/uhci-hcd.h,
429 /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
430 static inline struct uhci_hcd *hcd_to_uhci(struct usb_hcd *hcd)
431 {
432 return (struct uhci_hcd *) (hcd->hcd_priv);
433 }
434 static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
435 {
436 return container_of((void *) uhci, struct usb_hcd, hcd_priv);
437 }
很显然,这两个函数完成的就是uhci_hcd和usb_hcd之间的转换.至于你说hcd->hcd_priv是什么?首先我们看到struct usb_hcd中有一个成员unsigned long hcd_priv[0],以前我天真的以为这表示数组的长度为1.直到有一天,在互联网上,我遇到了大侠albcamus,经他指点,我才恍然大悟,原来这就是传说中的零长度数组.除了感慨gcc的强大之外,更是感慨,读十年语文,不如聊半年QQ啊!网络真是个好东西!
你可以执行这个命令:
localhost:~ # info gcc "c ext" zero
你会知道什么是gcc中所谓的零长度数组.这是gcc对C的扩展.在标准C中我们定义数组时其长度至少为1,而我们在Linux内核中结构体的定义里却经常看到最后一个元素定义为这种零长度数组,它不占结构的空间,但它意味着这个结构体的长度是充满了变数的,即我们这里sizeof(hcd_priv)==0,其作用就相当于一个占位符,用我们大学校园里的话来说,就是我一向深恶痛绝的占座一族.然后当我们要申请空间的时候我们可以这样做,
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
实际上,这就是usb_create_hcd中的一行,只不过当时我们没讲,然而现在我知道,逃避不是办法,我们必须面对.driver->hcd_priv_size对我们来说,我们可以在uhci_driver中找到,它就是sizeof(struct uhci_hcd),所以最终hcd->hcd_priv代表的就是这个struct uhci_hcd,只不过需要一个强制转换.这样我们就能理解hcd_to_uhci了吧.当然,反过来,uhci_to_hcd的意思就更不用说了.不过我倒是想友情提醒一下,我们现在是在讲uhci主机控制器的驱动程序,那么在我们这个故事里,有一些结构体变量是唯一的,比如以后我们凡是见到那个struct uhci_hcd的结构体指针,那就是咱们这里这个uhci,凡是见到那个struct usb_hcd的结构体指针,那就是咱们这里这个hcd.即以后我们如果见到某个指针名字叫做uhci或者叫做hcd,那就不用再多解释了.此uhci即彼uhci,此hcd即彼hcd.
接下来, io_size和io_addr的赋值都很好懂.
然后是决定这个Root Hub到底有几个端口.端口号是从0开始的,UHCI的Root Hub最多不能超过8个端口,即port号不能超过7.这段代码的含义注释里面说的很清楚,首先UHCI定义了这么一类寄存器,叫做PORTSC寄存器,全称就是PORT STATUS AND CONTROL REGISTER,端口状态和控制寄存器,只要有一个port就有这么一个寄存器,而每个这类寄存器都是16个bits,即两个bytes,因此从地址安排上来说,每一个端口占两个bytes,而Spec规定Port 1的地址位于基址开始的第10h和11h,Port 2的地址向后顺推,即位于基址开始的第12h和13h,再有更多就向后顺推,USBPORTSC1这个宏的值是16,还有一个叫做USBPORTSC2的宏值为18,这两个宏用来标志Port的偏移量,显然16就是10h,而18就是12h,UHCI定义的寄存器中,PORTSC是最后一类寄存器,它后面没有更多的寄存器了,但是它究竟有几个PORTSC寄存器就是我们所不知道的了,否则我们也就不用判断有多少个端口了.于是这段代码的意思就是从USBPORTSC1开始往后走,一直循环下去,读取这个寄存器的值,即portstatus,按SPEC规定,这个寄存器的值中bit7是一个保留位,应该一直为1,所以如果不为1,那么就不用往下走了,说明已经没有寄存器了.另一种常见的错误是读出来都为1,经验表明,这种情况也表示没有寄存器了.说明一下,inw就是读IO端口的函数,w就表示按word读.很显然这里要按word读,因为PORTSC寄存器是16bits,一个word.inw所接的参数就是具体的IO地址,即基址加偏移量.
510行,UHCI_RH_MAXCHILD就是7,port不能大于7,如果大于,那么说明出错了,于是设置port为2,因为UHCI spec规定每个Root Hub最少有两个port.
于是,uhci->rh_numports最后用来记录Root Hub的端口数.
然后是520行,check_and_reset_hc(),这个函数特虚伪,看似超简单其实暴复杂.定义于drivers/usb/host/uhci-hcd.c中.
164 /*
165 * Initialize a controller that was newly discovered or has lost power
166 * or otherwise been reset while it was suspended. In none of these cases
167 * can we be sure of its previous state.
168 */
169 static void check_and_reset_hc(struct uhci_hcd *uhci)
170 {
171 if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr))
172 finish_reset(uhci);
173 }
看上去就两行,可这两行足以让我等菜鸟们看半个小时了.首先第一个函数,uhci_check_and_reset_hc来自drivers/usb/host/pci-quirks.c,
85 /*
86 * Initialize a controller that was newly discovered or has just been
87 * resumed. In either case we can't be sure of its previous state.
88 *
89 * Returns: 1 if the controller was reset, 0 otherwise.
90 */
91 int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
92 {
93 u16 legsup;
94 unsigned int cmd, intr;
95
96 /*
97 * When restarting a suspended controller, we expect all the
98 * settings to be the same as we left them:
99 *
100 * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
101 * Controller is stopped and configured with EGSM set;
102 * No interrupts enabled except possibly Resume Detect.
103 *
104 * If any of these conditions are violated we do a complete reset.
105 */
106 pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
107 if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
108 dev_dbg(&pdev->dev, "%s: legsup = 0x%04x/n",
109 __FUNCTION__, legsup);
110 goto reset_needed;
111 }
112
113 cmd = inw(base + UHCI_USBCMD);
114 if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
115 !(cmd & UHCI_USBCMD_EGSM)) {
116 dev_dbg(&pdev->dev, "%s: cmd = 0x%04x/n",
117 __FUNCTION__, cmd);
118 goto reset_needed;
119 }
120
121 intr = inw(base + UHCI_USBINTR);
122 if (intr & (~UHCI_USBINTR_RESUME)) {
123 dev_dbg(&pdev->dev, "%s: intr = 0x%04x/n",
124 __FUNCTION__, intr);
125 goto reset_needed;
126 }
127 return 0;
128
129 reset_needed:
130 dev_dbg(&pdev->dev, "Performing full reset/n");
131 uhci_reset_hc(pdev, base);
132 return 1;
133 }
而第二个函数finish_reset来自drivers/usb/host/uhci-hcd.c.
126 /*
127 * Finish up a host controller reset and update the recorded state.
128 */
129 static void finish_reset(struct uhci_hcd *uhci)
130 {
131 int port;
132
133 /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
134 * bits in the port status and control registers.
135 * We have to clear them by hand.
136 */
137 for (port = 0; port < uhci->rh_numports; ++port)
138 outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
139
140 uhci->port_c_suspend = uhci->resuming_ports = 0;
141 uhci->rh_state = UHCI_RH_RESET;
142 uhci->is_stopped = UHCI_IS_STOPPED;
143 uhci_to_hcd(uhci)->state = HC_STATE_HALT;
144 uhci_to_hcd(uhci)->poll_rh = 0;
145
146 uhci->dead = 0; /* Full reset resurrects the controller */
147 }
这两个函数我们一起来看.
pci_read_config_word这个函数的作用正如同它的字面一一一样.读寄存器,读什么寄存器?就是那张上坟图呗.
在drivers/usb/host/quirks.c中有一打的关于这些宏的定义,
20 #define UHCI_USBLEGSUP 0xc0 /* legacy support */
21 #define UHCI_USBCMD 0 /* command register */
22 #define UHCI_USBINTR 4 /* interrupt register */
23 #define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
24 #define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
25 #define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */