设备热插拔
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_hcdrh_timer_funcusb_hcd_poll_rh_statususb_hcd_giveback_urburb->complete (urb);
usb_add_hcd的流程如下
usb_add_hcdusb_hcd_request_irqsrequest_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd);hcd->driver->irqehci_irqusb_hcd_poll_rh_statususb_hcd_giveback_urburb->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_probeINIT_WORK(&hub->events, hub_event);hub_configureusb_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_eventport_eventhub_port_connect_changeusb_alloc_devhub_port_initusb_new_device