一、usb_fill_*_urb 函数调用流程
// 控制
static inline void usb_fill_control_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
// 中断
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval)
// 批量static inline void usb_fill_bulk_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
// 实时
// 实时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化
拿其中的 usb_fill_int_urb 具体分析
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval)
{
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
/* make sure interval is within allowed range */
interval = clamp(interval, 1, 16);urb->interval = 1 << (interval - 1);
} else {
urb->interval = interval;
}urb->start_frame = -1;
}
上面含义就是填充urb 成员,其中的complete_fn 函数,是urb 执行完毕后,usb core 执行的回调函数。
完成函数的作用:
比如:调用usb_fill_int_urb(),其中complete_fn相当于中断函数,即主机读取完数据会放到指定的内存data_dma中。然后通知CPU处理这些数据,这些数据只有USB设备驱动程序知道其含义。所以要在这个完成函数中处理这些数据,如上报。
1、urb的complete 执行流程
以usb鼠标驱动为例,具体分析如下:
usb_mouse_probe->
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval); //填充 urb
usb_mouse_irq 为 urb 的complete 函数。
那么urb->complete 执行流程:
usb_mouse_irq <- __usb_hcd_giveback_urb <- usb_giveback_urb_bh
usb_giveback_urb_bh 这个函数是怎么初始化并触发执行?
在增加usb 控制器驱动中,会调用 usb_add_hcd 函数 ,接着,在usb_add_hcd 函数中,会初始化两个软中断:
/* initialize tasklets */
init_giveback_urb_bh(&hcd->high_prio_bh);
init_giveback_urb_bh(&hcd->low_prio_bh);
接着看 init_giveback_urb_bh :
static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{spin_lock_init(&bh->lock);
INIT_LIST_HEAD(&bh->head);
tasklet_setup(&bh->bh, usb_giveback_urb_bh);
}
从上面可知,init_giveback_urb_bh 是初始化tasklet 软中断函数。
如何调度到这个 tasklet 函数:usb_giveback_urb_bh ???
答案:在主控驱动中,注册hcd 时,会调用usb_create_hcd:
ohci_hcd_s3c2410_probe ->
usb_create_hcd->
__usb_create_hcd(driver, dev, dev, bus_name, NULL);->
timer_setup(&hcd->rh_timer, rh_timer_func, 0);
这里创建了 timer 定时,初始化定时函数为 rh_timer_func。
rh_timer_func
/* timer callback */
static void rh_timer_func (struct timer_list *t)
{
struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer);usb_hcd_poll_rh_status(_hcd);
}
usb_hcd_poll_rh_status
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); //检测hub是否有数据到来
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));
}
EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
usb_hcd_giveback_urb
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct giveback_urb_bh *bh;
bool running, high_prio_bh;
dump_stack();
/* pass status to tasklet via unlinked */
if (likely(!urb->unlinked))
urb->unlinked = status;if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
__usb_hcd_giveback_urb(urb);
return;
}if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
bh = &hcd->high_prio_bh;
high_prio_bh = true;
} else {
bh = &hcd->low_prio_bh;
high_prio_bh = false;
}spin_lock(&bh->lock);
list_add_tail(&urb->urb_list, &bh->head);
running = bh->running;
spin_unlock(&bh->lock);if (running)
;
else if (high_prio_bh)
tasklet_hi_schedule(&bh->bh);
else
tasklet_schedule(&bh->bh);
}
EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb);
上面的tasklet 函数会调用到 tasklet_hi_schedule(&bh->bh);或者 tasklet_schedule(&bh->bh);
这样就会触发usb_giveback_urb_bh 。
初始化后这个定时函数后,什么时候使用mod_timer 触发呢?
答案:
ohci_hcd_s3c2410_probe ->
usb_add_hcd ->
register_root_hub->
usb_get_device_descriptor->
usb_get_descriptor->
usb_control_msg->
usb_internal_control_msg->
usb_fill_control_urb->
usb_submit_urb->
从下面的usb_submit_urb 分析可知,这是主控驱动注册root_hub,那么调用rh_urb_enqueue
usb_submit_urb->
usb_hcd_submit_urb->
rh_urb_enqueue->
rh_queue_status->
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
上面可知,主控会注册一个timer 定时函数,进行检测hub 的端口是否有数据到来,然后处理数据,处理之后,调用处理完成函数。和之前分析的 hub_wq 工作队列检测是否有usb设备连接和断开 是不一样的。工作函数hub_event会被循环执行检测是否有设备连接和断开。
二、usb_submit_urb
usb_submit_urb 调用流程:
usb_submit_urb->
usb_hcd_submit_urb
usb_hcd_submit_urb 函数:
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);/* increment urb's reference count as part of giving it to the HCD
* (which will control it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
usbmon_urb_submit(&hcd->self, urb);/* NOTE requirements on root-hub callers (usbfs and the hub
* driver, for now): URBs' urb->transfer_buffer must be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data. Also, control
* URBs must be submitted in process context with interrupts
* enabled.
*/if (is_root_hub(urb->dev)) { -------------------------------------(1)
status = rh_urb_enqueue(hcd, urb);
} else {
status = map_urb_for_dma(hcd, urb, mem_flags);
if (likely(status == 0)) {
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); -----------(2)
if (unlikely(status))
unmap_urb_for_dma(hcd, urb);
}
}if (unlikely(status)) {
usbmon_urb_submit_error(&hcd->self, urb, status);
urb->hcpriv = NULL;
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
/*
* Order the write of urb->use_count above before the read
* of urb->reject below. Pairs with the memory barriers in
* usb_kill_urb() and usb_poison_urb().
*/
smp_mb__after_atomic();atomic_dec(&urb->dev->urbnum);
if (atomic_read(&urb->reject))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return status;
}
(1)如果是 root_hub,则调用 rh_urb_enqueue 函数,
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
if (usb_endpoint_xfer_int(&urb->ep->desc)) //中断传输
return rh_queue_status (hcd, urb);
if (usb_endpoint_xfer_control(&urb->ep->desc)) //控制传输
return rh_call_control (hcd, urb);
return -EINVAL;
}
共有两个事务。
(2)如果非root_hub ,则调用回调函数urb_enqueue
拿ohci 举例:
static const struct hc_driver ohci_hc_driver = {
.description = hcd_name,
.product_desc = "OHCI Host Controller",
.hcd_priv_size = sizeof(struct ohci_hcd),/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB11,/*
* basic lifecycle operations
*/
.reset = ohci_setup,
.start = ohci_start,
.stop = ohci_stop,
.shutdown = ohci_shutdown,/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
.urb_enqueue = ohci_urb_enqueue,调用ohci_urb_enqueue 函数,
ohci_urb_enqueue ->td_submit_urb->ohci_writel
留一个问题:ohci 执行完urb 后,如何触发 compelte 函数?
root hub 有一个定时timer函数周期性检测,然后触发 tasklet 函数,调用urb->compelete 函数。