linux 设备发现,linux2.6 USB设备发现过程机制 飞思卡尔imx283

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.cstatic 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 voidkick_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);

}

*/

}

============================================================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值