其实,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。
下面是一幅图:
下面好好的说说这个函数!