Linux那些事儿之我是UHCI(8)主机控制器的初始化(二)
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)
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 */
26 #define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */
27 #define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */
28 #define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */
29 #define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */
UHCI_USBLEGSUP是一个寄存器,它是一个比较特殊的寄存器,LEGSUP全称为LEGACY SUPPORT REGISTER,UHCI spec的第五章第二节专门介绍了这个寄存器.这里pci_read_config_word这个函数这么一调用就是把这个寄存器的值读出来,而结果被保存在变量legsup中,接着下一行就来判断它的值.这里首先我们介绍三个概念,我们注意到寄存器的每一位都有一个属性,比如RO,RW,RWC,前两个很好理解,RO就是Read Only,只读,RW就是Read/Write,可读可写.RWC就是Read/Write Clear.寄存器的某位如果是RWC的属性,那么表示该位可以被读可以被写,然而,与RW不同的是,如果你写了一个1到该位,将会把该位清为0,倘若你写的是一个0,则什么也不会发生,对这个世界不产生任何改变.(UHCI Spec中是这么说的:R/WC Read/Write Clear. A register bit with this attribute can be read and written. However, a write of a 1 clears (sets to 0) the corresponding bit and a write of a 0 has no effect.)
而UHCI_USBLEGSUP_RO为0x5040,即0101 0000 0100 0000,它用来标志着个LEGSUP寄存器的bit 6,bit 12,bit 14这三位为1,这几位是只读的(bit 14是保留位).UHCI_USBLEGSUP_RWC为0x8f00,即1000 1111 0000 0000,即标志着LEGSUP寄存器的bit 8,bit 9,bit 10,bit 11,bit 15为1,这几位的属性是RWC的.这里让这两个宏或一下,然后按位去反,然后让legsup和它们相与,其效果就是判断LEGSUP寄存器的bit 0,bit 1,bit 2,bit 3,bit 4,bit 5,bit 7,bit 13是否为1,这几位其实就是RW的.这里的注释说,这几位任何一位为1则表示需要reset,其实这种注释是不太负责任的,仔细看一下UHCI spec我们会发现,RW的这些位并不是因为它们是RW位它们就应该被reset,而是因为它们的作用,实际上bit0~bit5,bit7,bit13这几位都是一些enable/disable的开关,特别是中断相关的使能位,当我们还没有准备就绪的时候,我们理应把它们关掉,这就相当于我新买了一个手机,而我还没有号码,那我出于省电的考虑,基本上会选择把手机先关掉,等到我有了号了,我才会去把手机打开.而其它的位都是一些状态位,它们为0还是为1只是表明不同的状态,那还不随它们去,它们爱表示什么状态就表示什么状态呗,状态位对我们是没有什么影响的.
113行, UHCI_USBCMD表征UHCI的命令寄存器,这也是UHCI Spec中定义的寄存器.这个寄存器为00h和01h处.所以UHCI_USBCMD的值为0.
关于这个寄存器,需要考虑的是这么几位,首先是bit 0,这一位被称作RS bit,即Run/Stop,当这一位被设置为1,表示HC开始处理执行调度,调度什么?比如,传说中的URB.显然,现在时机还未成熟,所以这一位必须设置为0.咱们这里代码的意思是如果它为1,就执行reset.UHCI_USBCMD_RUN的值为0x0001,即表征bit 0.另两个需要考虑的是bit 3和bit 6.bit 3被称为EGSM,即Enter Global Suspend Mode,这一位为1表示HC进入Global Suspend Mode,这期间是不会有USB交易的.把这一位设置为0则是跳出这个模式,显然咱们这里的判断是如果这一位被设置为了0,就执行reset,否则就没有必要.因为reset的目的是为了清除当前存在于总线上的任何交易,让主机控制器和设备都忘了过去,重新开始新的生活.而bit 被称为CF,即Configure Flag,设置了这一位表示主机控制器当前正在被配置的过程中,显然如果主机控制器还在这个阶段我们就没有必要reset了.从逻辑上来说,关于这个寄存器的判断,我们的理念是,如果HC是停止的,并且它还是挂起的,并且它还是配置的.这种情况我们没有必要做reset的.
接下来,121行,读另一个寄存器,UHCI_USBINTR,值为4.UHCI spec中定义了一个中断使能寄存器.其I/O地址位于04h和05h.很显然一开始我们得关中断.关于这个寄存器的描述如下图所示:
谢天谢地,bit 15到bit 4是保留位,并且默认应该是0,所以我们无需理睬.而剩下几位在现阶段应该要关掉,即disable掉,或者说应该设置为0,唯有bit 1是个例外,Resume Interrupt Enable,正如uhci_check_and_reset_hc函数前的注释里说的一样,调用这个函数有两种可能的上下文,一种是这个主机控制器刚刚被发现的时候,这是一次性的工作,另一种是电源管理中的resume的时候,虽然此时此刻我们调用这个函数是处于第一种上下文,但显然第二种情景发生的频率更高,可能性更大.这也应了那句关于女人的老话,女人就像书架上的书,虽然你买了她,但在你买之前她多多少少被几个男人翻过.而对于resume的情况,显然这个Resume中断使能的寄存器必须被enable.(这里也能解释为何刚才我们不仅不应该清掉LEGSUP寄存器里面的状态位,还应该尽量保持它们.因为我们如果是从suspend回到resume,我们当然希望之前的状态得到保留,否则状态改变了那不就乱了么?那也就不叫恢复了.)
最终,127行,如果前面的三条goto语句都没有执行,那么说明并不需要执行reset,这里就直接返回了,返回值为0.反之如果前面任何一条goto语句执行了,那么就往下走,执行uhci_reset_hc,然后返回1.
函数uhci_reset_hc也来自drivers/usb/host/pci-quirks.c:
55 /*
56 * Make sure the controller is completely inactive, unable to
57 * generate interrupts or do DMA.
58 */
59 void uhci_reset_hc(struct pci_dev *pdev, unsigned long base)
60 {
61 /* Turn off PIRQ enable and SMI enable. (This also turns off the
62 * BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
63 */
64 pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC);
65
66 /* Reset the HC - this will force us to get a
67 * new notification of any already connected
68 * ports due to the virtual disconnect that it
69 * implies.
70 */
71 outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD);
72 mb();
73 udelay(5);
74 if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET)
75 dev_warn(&pdev->dev, "HCRESET not completed yet!/n");
76
77 /* Just to be safe, disable interrupt requests and
78 * make sure the controller is stopped.
79 */
80 outw(0, base + UHCI_USBINTR);
81 outw(0, base + UHCI_USBCMD);
82 }
这个函数其实就是一堆寄存器操作.读寄存器或者写寄存器.这种代码完全就是纸老虎,看上去挺恐怖,一堆的宏啊,寄存器啊,其实这些东西对我们这种经历过应试教育的人来说完全就是小case.
首先64行, pci_write_config_word就是写寄存器,写的还是UHCI_USBLEGSUP这个寄存器,即LEGSUP寄存器,写入UHCI_USBLEGSUP_RWC,根据我们对RWC的解释,这样做的后果就是让这几位都清零.凡是RWC的bit其实都是在传达某种事件的发生,而清零往往代表的是认可这件事情.
然后71行,outw的作用就是写端口地址,这里写的是UHCI_USBCMD,即写命令寄存器,而UHCI_USBCMD_HCRESET表示什么意思呢?UHCI spec中是这样说的:
Host Controller Reset (HCRESET). When this bit is set, the Host Controller module resets its internal timers, counters, state machines, etc. to their initial value. Any transaction currently in progress on USB is immediately terminated. This bit is reset by the Host Controller when the reset process is complete.
显然,这就是真正的执行硬件上的reset.把计时器,计数器,状态机全都给复位.当然这件事情是需要一段时间的,所以这里调用udelay来延时5ms.最后再读这一位,因为正如上面这段英文里所说的那样,当reset完成了之后,这一位会被硬件reset,即这一位应该为0.
最后80行和81行,写寄存器,把0写入中断使能寄存器和命令寄存器,这就是彻底的Reset,关掉中断请求,并且停止HC.
终于,我们结束了uhci_check_and_reset_hc,如果执行了reset,那么返回值应该为1.这种情况我们将执行finish_reset函数.这个函数的代码前面已经贴出来了,小学六年级的同学也应该能看懂,因为它只是做一些简单的赋值.唯一有一行写寄存器的循环操作,其含义又在注释里写的很明了了.至于这些赋值究竟意味着什么,等我们遇到了再说.
于是我们又跳出了check_and_reset_hc(uhci),这次我们回到了uhci_init,不过幸运的是,这个函数也该结束了.返回值为0,我们于是来了个三级跳,回到了usb_add_hcd.
更多推荐
所有评论(0)