现在的位置: 首页 > 综合 > 正文

Linux那些事儿之我是UHCI(8)主机控制器的初始化(二)

2013年09月19日 ⁄ 综合 ⁄ 共 6007字 ⁄ 字号 评论关闭

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_hcdusb_hcd之间的转换.至于你说hcd->hcd_priv是什么?首先我们看到struct usb_hcd中有一个成员unsigned long hcd_priv[0],以前我天真的以为这表示数组的长度为1.直到有一天,在互联网上,我遇到了大侠albcamus,经他指点,我才恍然大悟,原来这就是传说中的零长度数组.除了感慨gcc的强大之外,更是感慨,读十年语文,不如聊半年QQ!网络真是个好东西!

你可以执行这个命令:

localhost:~ # info gcc "c ext" zero

你会知道什么是gcc中所谓的零长度数组.这是gccC的扩展.在标准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_sizeio_addr的赋值都很好懂.

然后是决定这个Root Hub到底有几个端口.端口号是从0开始的,UHCIRoot Hub最多不能超过8个端口,port号不能超过7.这段代码的含义注释里面说的很清楚,首先UHCI定义了这么一类寄存器,叫做PORTSC寄存器,全称就是PORT STATUS AND CONTROL REGISTER,端口状态和控制寄存器,只要有一个port就有这么一个寄存器,而每个这类寄存器都是16bits,即两个bytes,因此从地址安排上来说,每一个端口占两个bytes,Spec规定Port 1的地址位于基址开始的第10h11h,Port 2的地址向后顺推,即位于基址开始的第12h13h,再有更多就向后顺推,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,如果大于,那么说明出错了,于是设置port2,因为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 */

抱歉!评论已关闭.