Linux之 USB驱动框架-usb_submit_urb和usb_fill_*_urb分析(6)

发布于:2024-04-22 ⋅ 阅读:(173) ⋅ 点赞:(0)

一、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 执行的回调函数。

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 是软中断函数。

  所以  usb_mouse_irq 是周期性 检测端口是否有鼠标按键的数据到来。        

 二、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 函数?目前还未在代码中找到逻辑。