RootHub感应设备的插拔

本文深入探讨了RootHub的中断传输机制,特别是在xHCI控制器中的实现细节。讲解了如何通过定时器轮询RootHub,并介绍了相关的函数流程,如usb_submit_urb、rh_urb_enqueue等,以及初始化定时器的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实,RootHub的中断传输 是使用定时器来实现的。下面仔细欣赏。
这里写图片描述

首先你要明白的是: xHC控制器有两个RootHub,
一个是RootHub2.0的。 (遇到的开发板中它引出8个port.这些port对应寄存器为PORTSC0~7),
一个是RootHub3.0的。(引出7个port,PORTSC8~13),
那么就对应了两条bus总线了,但是这两条总线对应这同一个控制器xhc
这里写图片描述
port 状态变化 这块 很复杂,在后面会慢慢道来。往下看吧。

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
    .... //先跳过,后面详细分析。
    return usb_hcd_submit_urb(urb, mem_flags);
}

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
        if (is_root_hub(urb->dev))  //RootHub
        {
            status = rh_urb_enqueue(hcd, urb);
        } else {
            //非RootHub的先跳过。
        }
}

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
    if (usb_endpoint_xfer_int(&urb->ep->desc))  //中断传输
    {
        return rh_queue_status (hcd, urb);
    }   
    if (usb_endpoint_xfer_control(&urb->ep->desc))//控制传输
    {
        return rh_call_control (hcd, urb);
    }   
    return -EINVAL;
}

static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
    int     retval;
    unsigned long   flags;
    unsigned    len = 1 + (urb->dev->maxchild / 8);

    spin_lock_irqsave (&hcd_root_hub_lock, flags);
    /**
        如果roothub已经在处理中断传输或者是非法的urb
    */
    if (hcd->status_urb || urb->transfer_buffer_length < len) 
    {
        dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
        retval = -EINVAL;
        goto done;
    }
    /**
        将urb添加到endpoint的urb队列上。
    */
    retval = usb_hcd_link_urb_to_ep(hcd, urb);
    if (retval)
    {
        goto done;
    }
    hcd->status_urb = urb;
    urb->hcpriv = hcd;  
    /**
        支持新的轮询方式。
        问:
            那么他是再什么时候被赋值的呢?

            int xhci_run(struct usb_hcd *hcd)
            {
                u32 temp;
                u64 temp_64;
                int ret;
                struct xhci_hcd *xhci = hcd_to_xhci(hcd);

                hcd->uses_new_polling = 1;         //支持新的轮询方式。
                if (!usb_hcd_is_primary_hcd(hcd))  //RootHub 3.0
                {
                    return xhci_run_finished(xhci);
                }
                ...
            }
            看,他是在xhci_run()中被赋值的。那么,xhci_run()函数又是在什么时候被调用的?
            往下看。

            xhci_register_pci()
            {
                return pci_register_driver(&xhci_pci_driver);
            }

            static struct pci_driver xhci_pci_driver = 
            {
                .name =     (char *) hcd_name,
                .id_table = pci_ids,
                .probe =    xhci_pci_probe,
                .remove =   xhci_pci_remove,
            };

    /*
        看他的porbe()函数。

        注册了两个roothub。
        一个是3.0的
        一个是2.0的
        但是 2.0的必须现注册.(为什么是2.0的要先注册?)
    */
        static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        {
            ...
            /**
                注册2.0的roothub . 必须先注册 2.0的 roothub.
            */
            retval = usb_hcd_pci_probe(dev, id);
            /**
                这个函数中会初始化定时器,并设定定时器到期执行的函数。
                看,在xhci_register_pci()函数完成的时候 定时器轮询roothub就已经准备好了

                init_timer(&hcd->rh_timer);
                hcd->rh_timer.function = rh_timer_func;
                hcd->rh_timer.data = (unsigned long) hcd;

                这个rh_timer_func()在下面详细分析。
            */
            xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,pci_name(dev), hcd);
            .... 
            retval = usb_add_hcd(xhci->shared_hcd, dev->irq,IRQF_SHARED);       
        }

        int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags)
        {
            ...
            ...
             /*
               调用reset( )函数
              */ 
            if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) 
            {
                dev_err(hcd->self.controller, "can't setup: %d\n", retval);
                goto err_hcd_driver_setup;
            }
            /*
                设置可轮询
            */  
            hcd->rh_pollable = 1;

            /*
                注册了irq,也就使能了中断。
            */  
            if (usb_hcd_is_primary_hcd(hcd) && irqnum) 
            {
                retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
                if (retval)
                {
                    goto err_request_irq;
                }   
            }
            /*
                调用start函数。 这里的start函数就是xhci_run()函数。
            */  
            retval = hcd->driver->start(hcd);

            /*
                注册RootHub
            */  
            if ((retval = register_root_hub(hcd)) != 0)
            {
                goto err_register_root_hub;
            }   

            /*
                创建属性文件
            */  
            retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
            if (retval < 0) 
            {
                printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n", retval);
                goto error_create_attr_group;
            }   
        }
    }
*/
    if (!hcd->uses_new_polling)  //如果不支持新的轮询方式。
    {
        mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
    }else if (HCD_POLL_PENDING(hcd))
    {
        mod_timer(&hcd->rh_timer, jiffies);
    }   
    retval = 0;
 done:
    spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
    return retval;
}

这里写图片描述

从头开始说把。
这里写图片描述

这个probe函数只要做了一下事情:

一 调用usb_hcd_pci_probe()注册roothub2.0,
USB core must know to register the USB 2.0 roothub first
上面是内核中的注释, 说注册3.0的RootHub之前,必须要先注册2.0的RootHub。

二 :调用usb_create_shared_hcd()创建和初始化一个HCD structure。
在这个函数中 它初始化了一个定时器,这个定时器的到期执行函数 会 轮询RootHub。
下面是一幅图:
这里写图片描述
下面好好的说说这个函数!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值