usb_submit_urb()

来源:互联网 发布:拜尔电动牙刷 知乎 编辑:程序博客网 时间:2024/06/05 17:44
hub_irq() --> usb_submit_urb()
usb_start_wait_urb() --> usb_submit_urb()

一旦urb被USB驱动程序正确地创建和初始化后,就可以递交到USB核心以发送到USB设备了。如果函数调用成功,当urb被HC处理结束的时候,urb的结束处理例程(urb->complete)正好被调用一次,当该结束处理函数被调用时,USB核心就结束了对urb的处理,此刻对urb的控制器权就返回给设备驱动程序了。

函数usb_submit_urb()用来递交URB,它在对URB进行设置后,调用主机控制器函数usb_hcd_submit_urb()来完成递交操作。

错误代码:
-ENOMEM        内存不足
-ENODEV        没有设备可用
-EPIPE         端点停止
-EAGAIN        排队等候同步传输的太多
-EFBIG         请求ISO frame的太多
-EINVAL        无效的中断间隔

函数usb_submit_urb递交URB后,urb->status为-EINPROGRESS.
--------------------------------------------------------
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
    int            pipe, temp, max;
    struct usb_device    *dev;
    int            is_out;

    if (!urb || urb->hcpriv || !urb->complete)
        return -EINVAL;
    if (!(dev = urb->dev) ||
        (dev->state < USB_STATE_DEFAULT) ||
        (!dev->bus) || (dev->devnum <= 0))
        return -ENODEV;
    if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
            || dev->state == USB_STATE_SUSPENDED)
        return -EHOSTUNREACH;

    urb->status = -EINPROGRESS;
    urb->actual_length = 0;
    pipe = urb->pipe;
    temp = usb_pipetype(pipe);
    is_out = usb_pipeout(pipe);

    if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED)
        return -ENODEV;

    //获取usb设备dev所能传输的数据包的最大值(单位是字节)
    max = usb_maxpacket(dev, pipe, is_out);
    if (max <= 0) {
        dev_dbg(&dev->dev,
            "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
            usb_pipeendpoint(pipe), is_out ? "out" : "in",
            __FUNCTION__, max);
        return -EMSGSIZE;
    }


处理“负责实时传输的urb”
|---------------------------------------------------------|
|    if (temp == PIPE_ISOCHRONOUS) {                      |
|        int    n, len;                                   |
|        if (dev->speed == USB_SPEED_HIGH) {              |
|            int    mult = 1 + ((max >> 11) & 0x03);      |
|            max &= 0x07ff;                               |
|            max *= mult;                                 |
|        }                                                |
|        if (urb->number_of_packets <= 0)                 |
|            return -EINVAL;                              |
|        for (n = 0; n < urb->number_of_packets; n++) {   |
|            len = urb->iso_frame_desc[n].length;         |
|            if (len < 0 || len > max)                    |
|                return -EMSGSIZE;                        |
|            urb->iso_frame_desc[n].status = -EXDEV;      |
|            urb->iso_frame_desc[n].actual_length = 0;    |
|        }                                                |
|    }                                                    |
|---------------------------------------------------------|

    if (urb->transfer_buffer_length < 0)
        return -EMSGSIZE;


--------------------------------------------------------
#ifdef DEBUG
    {
    unsigned int    orig_flags = urb->transfer_flags;
    unsigned int    allowed;
    allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
            URB_NO_INTERRUPT);
    switch (temp) {
    case PIPE_BULK:
        if (is_out)
            allowed |= URB_ZERO_PACKET;
    case PIPE_CONTROL:
        allowed |= URB_NO_FSBR;    
    default:            
        if (!is_out)
            allowed |= URB_SHORT_NOT_OK;
        break;
    case PIPE_ISOCHRONOUS:
        allowed |= URB_ISO_ASAP;
        break;
    }
    urb->transfer_flags &= allowed;
    if (urb->transfer_flags != orig_flags) {
        err("BOGUS urb flags, %x --> %x",
            orig_flags, urb->transfer_flags);
        return -EINVAL;
    }
    }
#endif
--------------------------------------------------------


设置urb的interval域
********************************************************
    switch (temp) {
    case PIPE_ISOCHRONOUS:
    case PIPE_INTERRUPT:
        if (urb->interval <= 0)
            return -EINVAL;
        switch (dev->speed) {
        case USB_SPEED_HIGH:    
            if (urb->interval > (1024 * 8))
                urb->interval = 1024 * 8;
            temp = 1024 * 8;
            break;
        case USB_SPEED_FULL:    
        case USB_SPEED_LOW:
            if (temp == PIPE_INTERRUPT) {
                if (urb->interval > 255)
                    return -EINVAL;
                temp = 128;
            } else {
                if (urb->interval > 1024)
                    urb->interval = 1024;
                temp = 1024;
            }
            break;
        default:
            return -EINVAL;
        }
        while (temp > urb->interval)
            temp >>= 1;
        urb->interval = temp;
    }
********************************************************


    return usb_hcd_submit_urb(urb, mem_flags);

}

   urb参数是指向urb的指针,mem_flags参数与传递给kmalloc()函数参数的意义相同,它用于告知USB核心如何在此时分配内存缓冲区。

在提交urb到USB核心后,直到完成函数被调用之前,不要访问urb中的任何成员。

usb_submit_urb()在原子上下文和进程上下文中都可以被调用,mem_flags变量需根据调用环境进行相应的设置,如下所示。

    l GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。

    l GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志;

    l GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。

如果usb_submit_urb()调用成功,即urb的控制权被移交给USB核心,该函数返回0;否则,返回错误号。


取消 urb
        使用以下函数停止一个已经提交给 USB 核心的 urb: 
[cpp] view plaincopy
  1. void usb_kill_urb(struct urb *urb);  
  2. int usb_unlink_urb(struct urb *urb);   
        如果调用usb_kill_urb函数,则 urb 的生命周期将被终止. 这通常在设备从系统移除时,在断开回调函数(disconnect callback)中调用. 
对一些驱动, 应当调用 usb_unlink_urb 函数来使 USB 核心停止 urb. 这个函数不会等待 urb 完全停止才返回. 这对于在中断处理例程中或者持有一个自旋锁时去停止 urb 是很有用的, 因为等待一个 urb 完全停止需要 USB 核心有使调用进程休眠的能力(wait_event()函数).


原创粉丝点击