Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

USB驱动函数总结

$
0
0

pipe 管道

  管道是USB设备通信的通道,内核中提供了创建管道的宏,从宏中我们可以分析出,管道是一个 int 型的变量,由设备号、端点地址、端点类型组合而成。

usb_[snd|rcv][ctrl|int|bulk|isoc]pipe(dev, endpoint)
例:
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_endpoint_descriptor *endpoint;
int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
#define usb_sndctrlpipe(dev,endpoint)   \
    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
#define usb_rcvctrlpipe(dev,endpoint)   \
    ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndisocpipe(dev,endpoint)   \
    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))
#define usb_rcvisocpipe(dev,endpoint)   \
    ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndbulkpipe(dev,endpoint)   \
    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
#define usb_rcvbulkpipe(dev,endpoint)   \
    ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndintpipe(dev,endpoint)    \
    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))
#define usb_rcvintpipe(dev,endpoint)    \
    ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
static inline unsigned int __create_pipe(struct usb_device *dev,
        unsigned int endpoint)
{
    return (dev->devnum << 8) | (endpoint << 15);
}

URB

分配URB

 usb_alloc_urb(int iso_packets, gfp_t mem_flags)

填充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)
{
    urb->dev  = dev;
    urb->pipe = pipe;
    urb->setup_packet = setup_packet;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = buffer_length;
    urb->complete = complete_fn;
    urb->context  = 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)
{
    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)   // 相比批量传输和控制传输,实时传输和中断传输多这个参数,表示周期
        urb->interval = 1 << (interval - 1);
    else
        urb->interval = interval;
    urb->start_frame = -1;
}

批量传输

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->dev = dev;
    urb->pipe = pipe;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = buffer_length;
    urb->complete = complete_fn;
    urb->context = context;
}

等时传输

  不幸的是,等时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化。例如:

urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev,uvd->video_endp-1);
urb->interval = 1;
urb->transfer_flags = URB_IOS_ASAP;
urb->transfer_buffer = can->sts_buf[i];
urb_complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PRE_DESC;
urb->transfer_buffer_lenth = FRAMES_PRE_DESC;
for (j=0; j < FRAMES_PRE_DESC; j++){
    urb->ios_frame_desc[j].offset  = j;
    urb->ios_frame_desc[j].length  = 1;
}  

同步提交URB

  有时候 USB 驱动程序只是要发送或者接收一些简单的 USB 数据,而不是想创建一个 struct urb ,初始化它,然后等待该 urb 接收函数运行这些麻烦事都走一遍。内核提供了同步提交 urb 的接口。

控制传输

int usb_control_msg(struct usb_device *dev, 
                    unsigned int pipe, 
                    __u8 request,
                    __u8 requesttype, 
                    __u16 value, 
                    __u16 index, 
                    void *data,
                    __u16 size, 
                    int timeout  // 超时时间,以jiffies为单位
                    )

  如果函数调用成功,返回值为 0 ,如果返回一个负数,表示发生一个错误。如果成功 actual_lenth 参数包含从该消息发送或者接收的字节数。
举例:

/* usb_get_descriptor */
    result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
            USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
            (type << 8) + index, 0, buf, size,
            USB_CTRL_GET_TIMEOUT);

  控制传输,通过 pipe 可以看出传输方向是设备到主机,和默认端点0通信,请求类型是请求描述符,具体的类型和索引。传输完成时,描述符就被存放在 buf 所指向的缓冲区中了。

usb_ctrl_msg 分析

int usb_control_msg(struct usb_device *dev, 
                    unsigned int pipe, 
                    __u8 request,
                    __u8 requesttype, 
                    __u16 value, 
                    __u16 index, 
                    void *data,
                    __u16 size, 
                    int timeout)
{
    struct usb_ctrlrequest *dr;
    int ret;

    dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
    if (!dr)
        return -ENOMEM;
    // 填充 setup packet
    dr->bRequestType = requesttype;
    dr->bRequest = request;
    dr->wValue   = cpu_to_le16(value);
    dr->wIndex   = cpu_to_le16(index);
    dr->wLength  = cpu_to_le16(size);

    /* dbg("usb_control_msg"); */

    ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);

    kfree(dr);

    return ret;
}
static int usb_internal_control_msg(struct usb_device *usb_dev,
                                    unsigned int pipe,
                                    struct usb_ctrlrequest *cmd,
                                    void *data, 
                                    int len, 
                                    int timeout)
{
    struct urb *urb;
    int retv;
    int length;

    urb = usb_alloc_urb(0, GFP_NOIO);
    if (!urb)
        return -ENOMEM;

    usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
                 len, usb_api_blocking_completion, NULL);

    retv = usb_start_wait_urb(urb, timeout, &length);
    if (retv < 0)
        return retv;
    else
        return length;
}
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)
{
    urb->dev  = dev;
    urb->pipe = pipe;
    urb->setup_packet = setup_packet;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = buffer_length;
    urb->complete = complete_fn;
    urb->context  = context;
}
struct api_context {
    struct completion   done;
    int         status;
};

static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
{
    struct api_context ctx;
    unsigned long expire;
    int retval;
    // 完成量
    init_completion(&ctx.done);
    urb->context = &ctx;
    urb->actual_length = 0;
    retval = usb_submit_urb(urb, GFP_NOIO);
    if (unlikely(retval))
        goto out;

    expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
    // 等待完成,返回值为0表示超时
    if (!wait_for_completion_timeout(&ctx.done, expire)) {
        usb_kill_urb(urb);
        retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);

        dev_dbg(&urb->dev->dev,
            "%s timed out on ep%d%s len=%u/%u\n",
            current->comm,
            usb_endpoint_num(&urb->ep->desc),
            usb_urb_dir_in(urb) ? "in" : "out",
            urb->actual_length,
            urb->transfer_buffer_length);
    } else
        retval = ctx.status;
out:
    if (actual_length)
        *actual_length = urb->actual_length;

    usb_free_urb(urb);
    return retval;
}
static void usb_api_blocking_completion(struct urb *urb)
{
    struct api_context *ctx = urb->context;

    ctx->status = urb->status;
    complete(&ctx->done);
}

  不难分析,内核帮助我们分配设置了 urb ,并且提供了一个统一的完成函数,在提交 urb 时有一点需要注意,内核初始化了一个完成量,并且内核在提交 urb 之后在 wait_for_completion_timeout ,等待的过程中线程自然就休眠了,何时完成呢?在统一的完成函数中 complete(&ctx->done) 。所以称这种方式为同步提交 urb 。

中断、批量传输

int usb_interrupt_msg(...)
{
  return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout);
}
int usb_bulk_msg(struct usb_device *usb_dev, 
unsigned int pipe,
         void *data, 
int *actual_length, 
int timeout
)

批量和控制传输的参数简单一点,举例:

/* 进行阻塞的批量读取,从设备获取数据 */
retval = usb_bulk_msg(dev->udev,
    usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),
    dev->bulk_in_buffer,
    min(dev->bulk_in_size,count),
    &count,
    HZ*10
)
/* 如果读取成功 */
if (!retval){
    if (copy_to_user(buffer, dev->bulk_in_buffer, count))
        retval = -EFAULT;
    else
        retval = count;
}

  批量传输,传输方向设备到主机,和端点bulk_in_endpoint通信。

usb_bulk_msg分析

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
         void *data, int len, int *actual_length, int timeout)
{
    struct urb *urb;
    struct usb_host_endpoint *ep;

    // 在 usb_device 的 ep_in[] 和 ep_out[] 数组中找到管道对应的断点
    ep = (usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out)
            [usb_pipeendpoint(pipe)];
    if (!ep || len < 0)
        return -EINVAL;

    // 分配 urb
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb)
        return -ENOMEM;

    // 如果是中断端点
    if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
            USB_ENDPOINT_XFER_INT) {
        pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
        usb_fill_int_urb(urb, usb_dev, pipe, data, len,
                usb_api_blocking_completion, NULL,
                ep->desc.bInterval);
    } else  // 如果是 bulk 端点
        usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                usb_api_blocking_completion, NULL);

    return usb_start_wait_urb(urb, timeout, actual_length);
}

异步提交urb

  异步提交 urb 需要我们自己去创建、设置、提交urb,并且提供一个完成函数。
举例:

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id){

    struct usb_device *dev = interface_to_usbdev(intf);
    struct usb_host_interface *interface;
    struct usb_endpoint_descriptor *endpoint;
    int pipe;
    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc; //endpoint = &intf->cur_altsetting->endpoint[0].desc

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    len = endpoint->wMaxPacketSize;
    //分配缓冲区,dma
    usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);
    //分配urb
    um_urb = usb_alloc_urb(0, GFP_KERNEL);
    //填充urb
    usb_fill_int_urb(um_urb, dev, pipe, usb_buf, len, usb_mouse_irq, NULL, endpoint->bInterval);

    um_urb->transfer_dma = usb_buf_phys;
    um_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //使用dma传输

    /* 使用URB */
    usb_submit_urb(um_urb, GFP_KERNEL);

    return 0;
}
static void usb_mouse_irq(struct urb *urb)
{
    static unsigned char pre_val;

    /* USB鼠标数据含义
     * data[0]: bit0-左键, 1-按下, 0-松开
     *          bit1-右键, 1-按下, 0-松开
     *          bit2-中键, 1-按下, 0-松开 
     *
     */
    if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
    {
        /* 左键发生了变化 */
        input_event(um_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
        input_sync(um_dev);
    }

    if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
    {
        /* 右键发生了变化 */
        input_event(um_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
        input_sync(um_dev);
    }

    if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
    {
        /* 中键发生了变化 */
        input_event(um_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
        input_sync(um_dev);
    }

    pre_val = usb_buf[0];

    /* 重新提交urb */
    usb_submit_urb(um_urb, GFP_KERNEL);
}

销毁URB

usb_free_urb(struct urb * urb)

取消URB

  应该调用 usb_fill_urb 或 usb_unlink_urb 函数来终止一个已经被提交到USB核心的urb。
  如果调用 usb_kill_urb ,urb 的生命周期将终止,通常是当设备从系统中断开时,在断开回调函数中调用此函数。
  对于某些驱动程序而言,应该使用 usb_ublink_urb 函数来告诉USB核心终止一个urb,该函数并不等到urb完全被终止之后才返回回调函数。这对于在中断处理例程中或者持有一个自旋锁时终止一个urb是很有用,因为等待一个urb完全被终止需要USB核心具有使调用进程休眠的能力。该函数需要被要求终止的urb中的URB_ASYNC_UNLINK标志值被设置才能正确地工作。

作者:lizuobin2 发表于2016/11/18 20:46:47 原文链接
阅读:42 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>