Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1183526
  • 博文数量: 221
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2139
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(221)

文章存档

2024年(6)

2023年(8)

2022年(2)

2021年(2)

2020年(29)

2019年(11)

2018年(23)

2017年(41)

2016年(76)

2015年(23)

我的朋友
最近访客

分类: LINUX

2020-09-04 00:39:43

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) |
给主人留下些什么吧!~~