drivers/usb/core/usb.c
//这个是USB子系统初始化
subsys_initcall(usb_init);
static int __init usb_init(void)
{
int retval;
retval = usb_debugfs_init();
retval = bus_register(&usb_bus_type);
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
retval = usb_major_init();
retval = usb_register(&usbfs_driver);
retval = usb_devio_init();
retval = usbfs_init();
retval = usb_hub_init();
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
}
///////////////////////////
USB总线驱动
drivers/usb/core/driver.c
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.pm = &usb_bus_pm_ops,
};
////////////////////////////////
具体分析
情况一:当插入USB设备时USB host会检测到这一事件;然后通过USB core去匹配驱动。
当守护程序第一次运行(特殊USB设备USB hub就是这种情况)或usb port上状态发生变化
(其余所有USB设备插入都是这种情况)守护进程被唤醒时,
会运行hub_events函数、USB的枚举过程就是由它完成。
///////////////////////////////
从硬件层面来看,ehci主控器从PCI总线桥接,是PCI驱动程序实例。usb host做为pci总线下的一个设备存在.
drivers/usb/host/ehci-hcd.c
module_init(ehci_hcd_init);
//这个宏定义了的。飞思卡尔。
#ifdef CONFIG_USB_EHCI_ARC
#include "ehci-arc.c"
#define PLATFORM_DRIVER ehci_fsl_driver //利用定时器轮询
#endif
#ifdef CONFIG_PCI
#include "ehci-pci.c"
#define PCI_DRIVER ehci_pci_driver //利用pci中断
#endif
static int __init ehci_hcd_init(void)
{
#ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER);
#endif
#ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER);
#endif
}
/////////////////////////////
==============================================
定时器轮询:
kernel/drivers/usb/host/ehci-arc.c
static struct platform_driver ehci_fsl_driver = {
.probe = ehci_fsl_drv_probe,
.remove = ehci_fsl_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ehci_fsl_drv_suspend,
.resume = ehci_fsl_drv_resume,
#endif
.driver = {
.name = "fsl-ehci",
},
};
kernel/drivers/usb/host/ehci-arc.c
static int ehci_fsl_drv_probe(struct platform_device *pdev)
{
/* FIXME we only want one one probe() not two */
return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
}
kernel/drivers/usb/host/ehci-arc.c
/**
* usb_hcd_fsl_probe - initialize FSL-based HCDs
* @drvier: Driver to be used for this HCD
* @pdev: USB Host Controller being probed
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller.
*
*/
/**
* usb_hcd_fsl_probe - initialize FSL-based HCDs
* @drvier: Driver to be used for this HCD
* @pdev: USB Host Controller being probed
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller.
*
*/
int usb_hcd_fsl_probe(const struct hc_driver *driver,
struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata;
struct usb_hcd *hcd;
struct resource *res;
int irq;
int retval;
pr_debug("initializing FSL-SOC USB Controller\n");
/* Need platform data for setup */
pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
/*
* This is a host mode driver, verify that we're supposed to be
* in host mode.
*/
if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
(pdata->operating_mode == FSL_USB2_MPH_HOST) ||
(pdata->operating_mode == FSL_USB2_DR_OTG))) {
dev_err(&pdev->dev,
"Non Host Mode configured for %s. Wrong driver linked.\n",
dev_name(&pdev->dev));
return -ENODEV;
}
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq = res->start;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
if (pdata->operating_mode != FSL_USB2_DR_OTG) {
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description)) {
dev_dbg(&pdev->dev, "controller already in use\n");
retval = -EBUSY;
goto err2;
}
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
pdata->regs = hcd->regs;
fsl_platform_set_host_mode(hcd);
hcd->power_budget = pdata->power_budget;
/*
* The ehci_fsl_pre_irq must be registered before usb_hcd_irq, in that case
* it can be called before usb_hcd_irq when irq occurs
*/
retval = request_irq(irq, ehci_fsl_pre_irq, IRQF_SHARED,
"fsl ehci pre interrupt", (void *)pdev);
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
dbg("pdev=0x%p hcd=0x%p ehci=0x%p\n", pdev, hcd, ehci);
ehci->transceiver = otg_get_transceiver();
dbg("ehci->transceiver=0x%p\n", ehci->transceiver);
if (!ehci->transceiver) {
printk(KERN_ERR "can't find transceiver\n");
}
retval = otg_set_host(ehci->transceiver, &ehci_to_hcd(ehci)->self);
if (retval)
otg_put_transceiver(ehci->transceiver);
} else if ((pdata->operating_mode == FSL_USB2_MPH_HOST) || \
(pdata->operating_mode == FSL_USB2_DR_HOST))
fsl_platform_set_vbus_power(pdata, 1);
if (pdata->suspended) {
pdata->suspended = 0;
if (pdata->already_suspended)
pdata->already_suspended = 0;
}
fsl_platform_set_ahb_burst(hcd);
ehci_testmode_init(hcd_to_ehci(hcd));
}
//////////////////////////
kernel/drivers/usb/core/hcd.c
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name){
return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}
struct
usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct
device *dev, const char *bus_name, struct usb_hcd *primary_hcd){
init_timer(&hcd->rh_timer);
//定时器函数
hcd->rh_timer.function = rh_timer_func;
}
kernel/drivers/usb/core/hcd.c
//定时器轮询方式,定时器函数
static void rh_timer_func (unsigned long _hcd)
{
//重点函数,定时器轮询方式和PCI中断方式都会调用到这个函数
usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}
//////////////////////////////////////
当有pci中断发生后:
kernel/drivers/usb/host/ehci-pci.c
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ehci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
.probe = usb_hcd_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
};
kernel/drivers/usb/host/ehci-pci.c
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
.driver_data = (unsigned long) &ehci_pci_hc_driver,
},
{ /* end: all zeroes */ }
};
///////////////////////////////
kernel/drivers/usb/host/ehci-pci.c
static const struct hc_driver ehci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq, //kernel/drivers/usb/host/xhci-pci.c 中是.irq = xhci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_pci_setup,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
/////////////////////////////////
kernel/drivers/usb/host/ehci-hcd.c
/*-------------------------------------------------------------------------*/
static irqreturn_t ehci_irq (struct usb_hcd *hcd)
{
//可以对比查看drivers/usb/host/xhci-ring.c irqreturn_t xhci_irq(struct usb_hcd *hcd)函数
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 status, masked_status, pcd_status = 0, cmd;
int bh;
spin_lock (&ehci->lock);
status = ehci_readl(ehci, &ehci->regs->status);
/* e.g. cardbus physical eject */
if (status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n");
goto dead;
}
masked_status = status & INTR_MASK;
if (!masked_status) { /* irq sharing? */
spin_unlock(&ehci->lock);
return IRQ_NONE;
}
/* clear (just) interrupts */
ehci_writel(ehci, masked_status, &ehci->regs->status);
cmd = ehci_readl(ehci, &ehci->regs->command);
bh = 0;
/* INT, ERR, and IAA interrupt rates can be throttled */
/* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
if (likely ((status & STS_ERR) == 0))
COUNT (ehci->stats.normal);
else
COUNT (ehci->stats.error);
bh = 1;
}
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
/* guard against (alleged) silicon errata */
if (cmd & CMD_IAAD) {
ehci_writel(ehci, cmd & ~CMD_IAAD,
&ehci->regs->command);
ehci_dbg(ehci, "IAA with IAAD still set?\n");
}
if (ehci->reclaim) {
COUNT(ehci->stats.reclaim);
end_unlink_async(ehci);
} else
ehci_dbg(ehci, "IAA with nothing to reclaim?\n");
}
/* remote wakeup [4.3.1] */
if (status & STS_PCD) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
/* kick root hub later */
pcd_status = status;
/* resume root hub? */
if (!(cmd & CMD_RUN))
usb_hcd_resume_root_hub(hcd);
while (i--) {
int pstatus = ehci_readl(ehci,
&ehci->regs->port_status [i]);
if (pstatus & PORT_OWNER)
continue;
if (!(test_bit(i, &ehci->suspended_ports) &&
((pstatus & PORT_RESUME) ||
!(pstatus & PORT_SUSPEND)) &&
(pstatus & PORT_PE) &&
ehci->reset_done[i] == 0))
continue;
/* start 20 msec resume signaling from this port,
* and make khubd collect PORT_STAT_C_SUSPEND to
* stop that signaling. Use 5 ms extra for safety,
* like usb_port_resume() does.
*/
ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
}
}
/* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) {
ehci_err(ehci, "fatal error\n");
dbg_cmd(ehci, "fatal", cmd);
dbg_status(ehci, "fatal", status);
ehci_halt(ehci);
dead:
ehci_reset(ehci);
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
/* generic layer kills/unlinks all urbs, then
* uses ehci_stop to clean up the rest
*/
bh = 1;
}
if (bh)
ehci_work (ehci);
spin_unlock (&ehci->lock);
if (pcd_status)
usb_hcd_poll_rh_status(hcd);//重点函数,定时器轮询方式也会调用到这个函数
return IRQ_HANDLED;
}
////////////////////////////////
kernel/drivers/usb/core/hcd.c
/*
* Root Hub interrupt transfers are polled using a timer if the
* driver requests it; otherwise the driver is responsible for
* calling usb_hcd_poll_rh_status() when an event occurs.
*
* Completions are called in_interrupt(), but they may or may not
* be in_irq().
*/
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
if (unlikely(!hcd->rh_registered))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
length = hcd->driver->hub_status_data(hcd, buffer);
if (length > 0) {
/* try to complete the status urb */
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
if (urb) {
hcd->poll_pending = 0;
hcd->status_urb = NULL;
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock(&hcd_root_hub_lock);
//重点函数,定时器轮询方式和PCI中断方式都会调用到这个函数
usb_hcd_giveback_urb(hcd, urb, 0);
spin_lock(&hcd_root_hub_lock);
} else {
length = 0;
hcd->poll_pending = 1;
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
/* The USB 2.0 spec says 256 ms. This is close enough and won't
* exceed that limit if HZ is 100. The math is more clunky than
* maybe expected, this is to make sure that all timers for USB devices
* fire at the same time to give the CPU a break inbetween */
if (hcd->uses_new_polling ? hcd->poll_rh :
(length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
=====================================================================
从以上分析可以看出;不论是定时器轮询还是pci中断,最终都会执行usb_hcd_giveback_urb函数:
kernel/drivers/usb/core/hcd.c
/**
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
* @urb: urb being returned to the USB device driver.
* @status: completion status code for the URB.
* Context: in_interrupt()
*
* This hands the URB from HCD to its USB device driver, using its
* completion function. The HCD has freed all per-urb resources
* (and is done using urb->hcpriv). It also released all HCD locks;
* the device driver won't cause problems if it frees, modifies,
* or resubmits this URB.
*
* If @urb was unlinked, the value of @status will be overridden by
* @urb->unlinked. Erroneous short transfers are detected in case
* the HCD hasn't checked for them.
*/
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
urb->hcpriv = NULL;
if (unlikely(urb->unlinked))
status = urb->unlinked;
else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
urb->actual_length < urb->transfer_buffer_length &&
!status))
status = -EREMOTEIO;
unmap_urb_for_dma(hcd, urb);
usbmon_urb_complete(&hcd->self, urb, status);
usb_unanchor_urb(urb);
/* pass ownership to the completion handler */
urb->status = status;
urb->complete (urb);//hub.c static void hub_irq(struct urb *urb)
atomic_dec (&urb->use_count);
if (unlikely(atomic_read(&urb->reject)))
wake_up (&usb_kill_urb_queue);
usb_put_urb (urb);
}
//////////////////////////
而上处urb->complete函数其实就是如下的hub_irq函数,后边会分析:
kernel/drivers/usb/core/hub.c
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
unsigned i;
unsigned long bits;
switch (status) {
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
return;
default: /* presumably an error */
/* Cause a hub reset after 10 consecutive errors */
dev_dbg (hub->intfdev, "transfer --> %d\n", status);
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = status;
/* FALL THROUGH */
/* let khubd handle things */
case 0: /* we got data: port status changed */
bits = 0;
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))
<< (i*8);
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
//此处唤醒
kick_khubd(hub);
}
/////////////////////////////////////////////////////
kernel/drivers/usb/core/hub.c
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
/* Suppress autosuspend until khubd runs */
usb_autopm_get_interface_no_resume(
to_usb_interface(hub->intfdev));
//唤醒khubd_wait
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
=============================================
kernel/drivers/usb/core/hub.c
这里特别强调:hub设备是第一个USB设备,
也是必须的USB设备;它不需要通过USB总线定时器轮询或PCI总线中断来触发。
从下边代码也可以看出,在执行第一次hub_events之后(hub驱动的probe函数被执行、urv->complete被赋值hub_irq),
该线程才会睡眠!
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
}
static int hub_thread(void *__unused)
{
do {
hub_events(); //重要!最核心部分
//等待kick_khubd函数唤醒khubd_wait
wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) || kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
}
//内核守护线程khubd,它被kick_khubd唤醒(当prot上状态发生变化时,USB
host会调用usb_hcd_poll_rh_status去查询usb root hub port状态,并调用hub中的interrupt
urb的回调函数hub_irq,最终去唤醒usb内核守护线程)、通过自身调用wait_event_freezable进入睡眠。
static void hub_events(void)
{
if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange);
}
hub_port_connect_change,顾名思义,当hub端口上有连接变化时调用这个函数,这种变化既可以是物理变化也可以是逻辑变化.注释里说得也很清楚.有三种情况会调用这个函数,一个是连接有变化,一个是端口本身重新使能,即所谓的enable,这种情况通常就是为了对付电磁干扰的,正如我们前面的判断中所说的那样,第三种情况就是在复位一个设备的时候发现其描述符变了,这通常对应的是硬件本身有了升级.很显然,第一种情况是真正的物理变化,后两者就算是逻辑变化
kernel/drivers/usb/core/hub.c
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange){
status = hub_port_init(hub, udev, port1, i);
status = usb_new_device(udev);
}
int usb_new_device(struct usb_device *udev){
err = device_add(&udev->dev);
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
/*
kernel/drivers/usb/core/endpoint.c
int usb_create_ep_devs(struct device *parent,struct usb_host_endpoint *endpoint,struct usb_device *udev){
device_register(&ep_dev->dev);
}
*/
}
============================================================
阅读(1096) | 评论(0) | 转发(0) |