设备热插拔
i2c,spi都是主机驱动初始化的时候,就会根据设备树来添加设备
pci的话,主机初始化的时候,会通过总线去遍历枚举card设备
sdio的话,主机驱动初始化的时候,会去枚举卡;插卡可能会触发主机中断,然后中断处理程序又去扫描枚举卡;也可能通过cd脚注册中断,来判断卡的在位情况
mdio的话,根据设备树,去枚举和创建phy设备,会给phy创建一个状态机,每秒去读phy口的网线连接状态
sim卡,有一个cd脚,注册一个中断,根据高低电平,判断是否插卡,拔卡
所以,对于能热插拔的设备,要么通过中断,要么通过轮询(定时器,延时队列等),去判断设备的插拔情况
注册主机
usb主机一般都是通过__usb_create_hcd和usb_add_hcd来注册一个usb主机
static int fsl_ehci_drv_probe(struct platform_device *pdev)
{
....
hcd = __usb_create_hcd(&fsl_ehci_hc_driver, pdev->dev.parent,
&pdev->dev, dev_name(&pdev->dev), NULL);
....
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
....
}
__usb_create_hcd的流程如下
__usb_create_hcd
rh_timer_func
usb_hcd_poll_rh_status
usb_hcd_giveback_urb
urb->complete (urb);
usb_add_hcd的流程如下
usb_add_hcd
usb_hcd_request_irqs
request_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd);
hcd->driver->irq
ehci_irq
usb_hcd_poll_rh_status
usb_hcd_giveback_urb
urb->complete (urb);
usb_hcd_poll_rh_status函数中,调用主机控制器的hub_status_data函数获取端口状态。如果端口的状态有变化,那么length > 0,把获取到的端口状态的数组拷贝到urb->transfer_buffer中,就是前面的hub->buffer中,同时调用usb_hcd_giveback_urb函数
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
int status;
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
if (unlikely(!hcd->rh_pollable))
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) {
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
if (urb->transfer_buffer_length >= length) {
status = 0;
} else {
status = -EOVERFLOW;
length = urb->transfer_buffer_length;
}
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
usb_hcd_giveback_urb(hcd, urb, status);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
}
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 in between */
if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
(length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
usb_hcd_giveback_urb函数中调用urb->complete (urb),而urb->complete = hub_irq;hub_irq会执行一次检查port状态的work;有设备插入就会去初始化port,注册新的usb设备
hub_probe
INIT_WORK(&hub->events, hub_event);
hub_configure
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
urb->complete = hub_irq;
初始化HUB
在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化。
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
/*
* The workqueue needs to be freezable to avoid interfering with
* USB-PERSIST port handover. Otherwise it might see that a full-speed
* device was gone before the EHCI controller had handed its port
* over to the companion full-speed controller.
*/
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
if (hub_wq)
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);
return -1;
}
在usb_hub_init函数中完成了注册hub驱动,然后创建一个工作队列hub_wq,每当有设备连接到USB接口时;根据上面----USB主机在中断后半段会回调complete函数(hub_irq)
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 hub_wq 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 hub_wq figure it out */
kick_hub_wq(hub);
resubmit:
hub_resubmit_irq_urb(hub);
}
在该函数中利用kick_hub_wq;来执行一次work;work的工作内容为hub->events;
static void kick_hub_wq(struct usb_hub *hub)
{
struct usb_interface *intf;
if (hub->disconnected || work_pending(&hub->events))
return;
/*
* Suppress autosuspend until the event is proceed.
*
* Be careful and make sure that the symmetric operation is
* always called. We are here only when there is no pending
* work for this hub. Therefore put the interface either when
* the new work is called or when it is canceled.
*/
intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface_no_resume(intf);
kref_get(&hub->kref);
if (queue_work(hub_wq, &hub->events))
return;
/* the work has already been scheduled */
usb_autopm_put_interface_async(intf);
kref_put(&hub->kref, hub_release);
}
hub->events去发现设备,初始化设备
INIT_WORK(&hub->events, hub_event);
hub_event
port_event
hub_port_connect_change
usb_alloc_dev
hub_port_init
usb_new_device