精华内容
下载资源
问答
  • 688USB摄像头驱动程序

    2009-05-20 15:29:46
    688USB摄像头驱动程序688USB摄像头驱动程序
  • 学习目标:根据vivi驱动架构和linux-2.6.31/linux-2.6.31.14/drivers/media/video/uvc/Uvc_driver.c驱动源码,分析usb摄像头驱动程序框架; 一、摄像头拓扑结构 由数据手册USB_Video_Example 1.5和UVC 1.5 Class ...

    学习目标:根据vivi驱动架构和linux-2.6.31/linux-2.6.31.14/drivers/media/video/uvc/Uvc_driver.c驱动源码,分析usb摄像头驱动程序框架;

    一、摄像头拓扑结构

    由数据手册USB_Video_Example 1.5和UVC 1.5 Class specification分析摄像头的拓扑结构,如下图所示:

    usb摄像头功能由摄像头接口完成,每个摄像头功能包括一个 VideoControl interface (VC)和几个VideoStreaming interface (VS)。

    1) CS主要用于控制,它又抽象出两个概念:unit和termiral。unit用于里面的相关控制。termiral用于内外连接。其中,unit中包括:a.  Selector Unit (SU) 选择(CT (Camera Terminal) 和IT(Input Terminal))哪一路 。b. Processing Unit (PU) 用于亮度等属性参数控制。

    2) VS用于传输,来读视频数据,。需要设置类型type和格式format,选择实时传输还是批量传输Bulk。

    二、usb摄像头源码框架分析 

    在Linux内核里自带usb摄像头驱动程序,它支持uvc规格的摄像头(uvc即usb video class),进入源码程序分析。

    1、进入uvc_init入口函数

          --> result = usb_register(&uvc_driver.driver);

    struct uvc_driver uvc_driver = {
        .driver = {
            .name        = "uvcvideo",
            .probe        = uvc_probe,
            .disconnect    = uvc_disconnect,
            .suspend    = uvc_suspend,
            .resume        = uvc_resume,
            .reset_resume    = uvc_reset_resume,
            .id_table    = uvc_ids,
            .supports_autosuspend = 1,
        },
    };

    注册了 uvc_driver结构体,根据.id_table匹配后,进入uvc_probe函数:
    uvc_register_video(dev)

      -->vdev = video_device_alloc();

       vdev->fops = &uvc_fops;

       vdev->release = video_device_release;

       video_register_device(vdev, VFL_TYPE_GRABBER, -1)

    const struct v4l2_file_operations uvc_fops = {
        .owner        = THIS_MODULE,
        .open        = uvc_v4l2_open,
        .release    = uvc_v4l2_release,
        .ioctl        = uvc_v4l2_ioctl,
        .read        = uvc_v4l2_read,
        .mmap        = uvc_v4l2_mmap,
        .poll        = uvc_v4l2_poll,
    };

    由此可知,写一个USB摄像头驱动程序需要以下步骤:
    1)构造一个usb_driver结构体(uvc结构体)
    2)设置
       probe:
            2.1. 分配video_device:video_device_alloc
            2.2. 设置
               .fops
               .ioctl_ops (里面需要设置11项)
               如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops
            2.3. 注册: video_register_device     
      id_table: 表示支持哪些USB设备     
    3)注册: usb_register

     2、分析UVC驱动调用过程

    const struct v4l2_file_operations uvc_fops = {
        .owner        = THIS_MODULE,
        .open        = uvc_v4l2_open,
        .release    = uvc_v4l2_release,
        .ioctl        = uvc_v4l2_ioctl,
        .read        = uvc_v4l2_read,
        .mmap        = uvc_v4l2_mmap,
        .poll        = uvc_v4l2_poll,
    };

    1) open函数

      uvc_v4l2_open //一系列设置

    2) uvc_v4l2_ioctl函数

    -->video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);//将用户提供的参数拷贝到内核态,并调用uvc_v4l2_do_ioctl函数

      -->uvc_v4l2_do_ioctl

        struct video_device *vdev = video_devdata(file);//根据次设备号,找到video_device

        switch (cmd) { //根据cmd执行一系列的ioctl函数
         /* Query capabilities */
         case VIDIOC_QUERYCAP:

         .......

    接下来具体分析ioctl过程:

    1)VIDIOC_QUERYCAP

    if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
                cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
                          | V4L2_CAP_STREAMING;
            else
                cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
                          | V4L2_CAP_STREAMING;
            break;

    // video->streaming->type 应该是在设备被枚举时分析描述符时设置的。(对用功能拓扑的VS)

    2)VIDIOC_ENUM_FMT // format数组应是在设备被枚举时设置的

      format = &video->streaming->format[fmt->index];

    3)VIDIOC_G_FMT

      --> uvc_v4l2_get_format(video, arg); // USB摄像头支持多种格式fromat, 每种格式下有多种frame(比如分辨率)

         struct uvc_format *format = video->streaming->cur_format;
         struct uvc_frame *frame = video->streaming->cur_frame;

    4)VIDIOC_TRY_FMT

      -->uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);

        /* Check if the hardware supports the requested format. */
        /* Find the closest image size.*/找到最接近的图像格式

    5)VIDIOC_S_FMT  // 只是把参数保存起来,还没有发给USB摄像头

      -->uvc_v4l2_set_format(video, arg);

        uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);

         memcpy(&video->streaming->ctrl, &probe, sizeof probe);
         video->streaming->cur_format = format;
         video->streaming->cur_frame = frame;

    6)VIDIOC_REQBUFS

      -->uvc_alloc_buffers(&video->queue, rb->count, bufsize);

           unsigned int bufsize = video->streaming->ctrl.dwMaxVideoFrameSize; //bufsize已经设置并获取,vmalloc时只需传进来应用程序的rb->count数值

        /* 根据rb->count来分配多少个buf*/

        for (; nbuffers > 0; --nbuffers) {
          mem = vmalloc_32(nbuffers * bufsize);
          if (mem != NULL)
           break;
         }   

     7)VIDIOC_QUERYBUF

      -->uvc_query_buffer(&video->queue, buf);

                __uvc_query_buffer
                    memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);  // 复制参数

    8)mmap //查询buf参数后使用mmap把buf映射给应用程序

      uvc_v4l2_mmap

    9)VIDIOC_QBUF 

      -->uvc_queue_buffer(&video->queue, arg);

         list_add_tail(&buf->stream, &queue->mainqueue);
         list_add_tail(&buf->queue, &queue->irqqueue);

    10)VIDIOC_STREAMON

      -->uvc_video_enable(video, 1)  // 把所设置的参数发给硬件,然后启动摄像头

        /* Commit the streaming parameters.  /* 传输
          uvc_commit_video(video, &video->streaming->ctrl) //*在前面VIDIOC_S_FMT赋值的video->streaming->ctrl 

          -->uvc_set_video_ctrl(video, probe, 0);///* 设置格式fromat, frame */

            ret = __uvc_query_ctrl(video->dev,/* 哪一个USB设备 */ SET_CUR, 0,  video->streaming->intfnum, /* 哪一个接口: VS */
                      probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                      uvc_timeout_param);

    注意:此处用到了__uvc_query_ctrl函数

            /* *启动 Initialize isochronous/bulk URBs and allocate transfer buffers. */

            uvc_init_video(video, GFP_KERNEL);

            uvc_init_video_isoc(video, ep, gfp_flags);(实时传输)/uvc_init_video_bulk(video, ep, gfp_flags)(批量传输)

              -->usb_alloc_urb

              设置urb结构体(urb用于usb数据传输的结构体)

               urb->complete = uvc_video_complete;(收到数据后此函数被调用,它又调用video->decode(urb, video, buf); ==>uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);)

          -->usb_submit_urb(video->urb[i], gfp_flags)) ; /* Submit the URBs. */
    11) poll
            --> uvc_v4l2_poll           
                uvc_queue_poll
                    poll_wait(file, &buf->wait, wait);  // 休眠等待有数据   

    12)  VIDIOC_DQBUF //有数据时,把buf从队列中取出,并从原始队列里面删除

       --> uvc_dequeue_buffer(&video->queue, arg, file->f_flags & O_NONBLOCK);

          list_del(&buf->stream);  

    13)VIDIOC_STREAMOFF 

       uvc_video_enable(video, 0); //0表示关闭

       uvc_uninit_video(video, 1);

      --> usb_kill_urb(urb);
         usb_free_urb(urb);

    -----以上均涉及到VS(Video Streaming Interface)接口的操作,不涉及Video Control Interface-----

    下面以设置亮度过程为例,操作VC(Video Control Interface)接口(实质上是向VC发包):

    ioctl: VIDIOC_S_CTRL

    --->uvc_ctrl_set(video, &xctrl);//设置
         uvc_ctrl_commit(video); //传送
                    __uvc_ctrl_commit(video, 0);
                        uvc_ctrl_commit_entity(video->dev, entity, rollback);
                       ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个unit/terminal */,
                        dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
                        uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),ctrl->info->size);

    三、总结

    1. UVC设备是基于2个interface: VideoControl Interface VC, VideoStreaming Interface VS 进行操作的;

    2. 1) VC 用于控制属性参数,比如设置亮度。它内部有多个Unit/Terminal(在程序里Unit/Terminal都称为entity)
       可以通过类似的函数来访问:
                       ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个unit/terminal */,
                        dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
                        uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                        ctrl->info->size);

      2) VS用于获得视频数据,也可以用来选择fromat/frame(VS可能有多种format, 一个format支持多种frame, frame用来表示分辨率等信息)
        可以通过类似的函数来访问:
                         ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,
                          video->streaming->intfnum  /* 哪一个接口: VS */,
                          probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                          uvc_timeout_param);

    3. 设置FORMAT时只是简单的使用video->streaming->format[fmt->index]等数据,这些数据应是设备被枚举时设置的,也就是分析它的描述符时设置的。

    4. UVC驱动的重点在于: usb摄像头描述符的分析
       属性的控制: 通过VideoControl Interface(VC)来设置
       格式的选择:通过VideoStreaming Interface(VS)来设置
       数据的获得:通过VideoStreaming Interface(VS)的URB-->(usb设备驱动数据传输的结构体格式)来获得。

     


    参考:百问网(韦东山视频)

    转载于:https://www.cnblogs.com/lxl-lennie/p/10312900.html

    展开全文
  • USB摄像头以其良好的性能和低廉的价格得到广泛应用。同时因其灵活、方便的特性,易于集成到嵌入式系统中。...本文首先介绍在Linux系统下USB摄像头驱动编制的一般方法,然后说明在此基础上如何提高帧速。
  • WinCE系统下USB摄像头驱动程序开发的研究
  • 楼主耗尽千心万苦终于写出来驱动USB网络摄像头驱动程序了,特别注意:需要下载NI的VAS_September_2011,直接百度搜就可以了!
  • 从0写USB摄像头驱动程序

    千次阅读 2015-10-01 14:58:41
    从0写USB摄像头驱动程序 1.构造一个usb_driver结构体 .id_table .probe 1.分配video_device结构体 2.设置 3.注册 2.下面具体分析probe函数中的内容: 定义:static struct video_device *myuvc_vdev; myuvc_vdev=...

    从0写USB摄像头驱动程序

    1.构造一个usb_driver结构体

    .id_table

    .probe

    1.分配video_device结构体

    2.设置

    3.注册

    2.下面具体分析probe函数中的内容:

    定义:static struct video_device *myuvc_vdev;

    myuvc_vdev=video_device_alloc();

    注册:video_register_device(myuvc_vdev,VFL_TYPE_GRABBER,-1);

    设置操作待会再说。

     

    3.myuvc_disconnect函数中:

    Video_unregister_device(myuvc_vdev);

    video_device_release(myuvc_vdev);

     

    4.probe函数中的设置操作有:

    myuvc_vdev->release=myuvc_release;

    myuvc_vdev->fops=&myuvc_fops;

    myuvc_vdev->ioctl_fops=&myuvc_ioctl_fops

    涉及的定义有:

    static const struct v4l2_file_operations myuvc_fops = {

    .owner = THIS_MODULE,

        .open       = myuvc_open,

        .release    = myuvc_close,

        .mmap       = myuvc_mmap,

        .ioctl      = video_ioctl2, /* V4L2 ioctl handler */

        .poll       = myuvc_poll,

    };

     

    static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {

            // 表示它是一个摄像头设备

            .vidioc_querycap      = myuvc_vidioc_querycap,

     

            /* 用于列举、获得、测试、设置摄像头的数据的格式 */

            .vidioc_enum_fmt_vid_cap  = myuvc_vidioc_enum_fmt_vid_cap,

            .vidioc_g_fmt_vid_cap     = myuvc_vidioc_g_fmt_vid_cap,

            .vidioc_try_fmt_vid_cap   = myuvc_vidioc_try_fmt_vid_cap,

            .vidioc_s_fmt_vid_cap     = myuvc_vidioc_s_fmt_vid_cap,

            

            /* 缓冲区操作: 申请/查询/放入队列/取出队列 */

            .vidioc_reqbufs       = myuvc_vidioc_reqbufs,

            .vidioc_querybuf      = myuvc_vidioc_querybuf,

            .vidioc_qbuf          = myuvc_vidioc_qbuf,

            .vidioc_dqbuf         = myuvc_vidioc_dqbuf,

            

            // 启动/停止

            .vidioc_streamon      = myuvc_vidioc_streamon,

            .vidioc_streamoff     = myuvc_vidioc_streamoff,   

    };

     

    当应用层调用open等函数时会执行驱动中myuvc_fops结构体中的相应函数;当应用层调用一些ioctl函数时会执行驱动的myuvc_fops结构体中的成员video_ioctl2,它又会去调用myuvc_ioctl_ops结构体中的相应ioctl函数。

    5.注意:ioctl缓冲区操作步骤中:先分配缓冲区,然后再进行mmap地址映射,映射到用户空间中,然后再将缓冲区放入队列。

     

    6.关于poll函数:APP通过poll/select确定有数据后,把缓存从队列中取出来。之前已经通过mmap映射了缓存,APP可以直接读数据。读完数据后再把换酬勤放入队列。然后进行上述循环。

     

    如何打开虚拟机的USB服务:

    先点击虚拟机界面中菜单栏中的VM选项。

    VM->Removable Devices ->USB2.0Camera -> Connect 

     

    如果卸载ubuntu上原来的UVC驱动程序:

    Rmmod uvcvideo

     

    所谓的UVC规范就是接到电脑上不用装驱动的摄像头。

     

    下面来细讲每个ioctl函数:

    /****************************/

    static int myuvc_vidioc_querycap(struct file *file, void  *priv,

    struct v4l2_capability *cap)

    {    

        memset(cap, 0, sizeof *cap);

        strcpy(cap->driver, "myuvc");

        strcpy(cap->card, "myuvc");

        cap->version = 1;

        cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

    return 0;

    }

     

    最重要的是最后一句说明摄像头是捕获设备以及通过ioctl和mmap映射等方式读取设备而不是通过read,write等函数来获取数据。

    /******************************/

    /* A3 列举支持哪种格式

     * 参考: uvc_fmts 数组

     */

    static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,

    struct v4l2_fmtdesc *f)

    {

        /* 人工查看描述符可知我们用的摄像头只支持1种格式 */

    if (f->index >= 1)

    return -EINVAL;

     

        /* 支持什么格式呢?

         * 查看VideoStreaming Interface的描述符,

         * 得到GUID为"59 55 59 32 00 00 10 00 80 00 00 aa 00 38 9b 71"

         */

    strcpy(f->description, "4:2:2, packed, YUYV");

    f->pixelformat = V4L2_PIX_FMT_YUYV;    

        

    return 0;

    }

    在uvc_fmts数组中可找到GUID与格式的对应关系。可知当前摄像头是YUYV格式。

    /****************************/

    static struct v4l2_format myuvc_format;//全局变量

    /* A4 返回当前所使用的格式 */

    static int myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv,

    struct v4l2_format *f)

    {

        memcpy(f, &myuvc_format, sizeof(myuvc_format));

    return (0);

    }

    设置的全局变量用于保存当前的格式,要获取当前的格式时,只要将这个全局变量拷贝出去即可。

    /******************************************************************************/

    static struct frame_desc frames[] = {{640, 480}, {352, 288}, {320, 240}, {176, 144}, {160, 120}};

    static int frame_idx = 1;

    static int bBitsPerPixel = 16; /* lsusb -v -d 0x1e4e:  "bBitsPerPixel" */

     

    /* A5 测试驱动程序是否支持某种格式, 强制设置该格式 

     * 参考: uvc_v4l2_try_format

     *       myvivi_vidioc_try_fmt_vid_cap

     */

    static int myuvc_vidioc_try_fmt_vid_cap(struct file *file, void *priv,

    struct v4l2_format *f)

    {

        if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)

        {

            return -EINVAL;

        }

     

        if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)

            return -EINVAL;

        

        /* 调整format的width, height, 

         * 计算bytesperline, sizeimage

         */

     

        /* 人工查看描述符, 确定支持哪几种分辨率 */

        f->fmt.pix.width  = frames[frame_idx].width;

        f->fmt.pix.height = frames[frame_idx].height;

        

    f->fmt.pix.bytesperline =

    (f->fmt.pix.width * bBitsPerPixel) >> 3;

    f->fmt.pix.sizeimage =

    f->fmt.pix.height * f->fmt.pix.bytesperline;

        

        return 0;

    }

    try中强制设定的格式内容包括是不是捕捉设备、是不是YUYV格式、设置图像的分辨率一个像素点的位宽。

    /******************************/

     

    /* A6 参考 myvivi_vidioc_s_fmt_vid_cap */

    static int myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,

    struct v4l2_format *f)

    {

    int ret = myuvc_vidioc_try_fmt_vid_cap(file, NULL, f);

    if (ret < 0)

    return ret;

     

        memcpy(&myuvc_format, f, sizeof(myuvc_format));

        

        return 0;

    }

    设置格式就是把期望的格式复制到全局变量中,复制前先try一下是否符合要求。然后再进行复制。故正确的逻辑顺序是应用程序设置格式,然后再才能获得格式,不然全局变量是空的。

    /*****************************/

    /* 参考uvc_video_queue定义一些结构体 */

    struct myuvc_buffer {    

        struct v4l2_buffer buf;

        int state;

        int vma_use_count; /* 表示是否已经被mmap */

        wait_queue_head_t wait;  /* APP要读某个缓冲区,如果无数据,在此休眠 */

    struct list_head stream;

    struct list_head irq;    

    };

     

    struct myuvc_queue {

        void *mem;

        int count;

        int buf_size;    

        struct myuvc_buffer buffer[32];

     

    struct urb *urb[32];

    char *urb_buffer[32];

    dma_addr_t urb_dma[32];

    unsigned int urb_size;

     

    struct list_head mainqueue;   /* 供APP消费用 */

    struct list_head irqqueue;    /* 供底层驱动生产用 */

    };

     

    /* A7 APP调用该ioctl让驱动程序分配若干个缓存, APP将从这些缓存中读到视频数据 

     * 参考: uvc_alloc_buffers

     */

    static int myuvc_vidioc_reqbufs(struct file *file, void *priv,

      struct v4l2_requestbuffers *p)

    {

        int nbuffers = p->count;//缓冲区个数

        int bufsize  = PAGE_ALIGN(myuvc_format.fmt.pix.sizeimage);//一个缓冲区大小

        unsigned int i;

        void *mem = NULL;//所有缓冲区的起始地址

        int ret;

     

        if ((ret = myuvc_free_buffers()) < 0)

            goto done;

     

        /* Bail out if no buffers should be allocated. */

        if (nbuffers == 0)

            goto done;

     

        /* Decrement the number of buffers until allocation succeeds. */

        for (; nbuffers > 0; --nbuffers) {//分配缓冲区,逐级降低分配

            mem = vmalloc_32(nbuffers * bufsize);

            if (mem != NULL)

                break;

        }

     

        if (mem == NULL) {

            ret = -ENOMEM;

            goto done;

        }

     

        /* 这些缓存是一次性作为一个整体来分配的 */

        memset(&myuvc_queue, 0, sizeof(myuvc_queue));

     

    INIT_LIST_HEAD(&myuvc_queue.mainqueue);

    INIT_LIST_HEAD(&myuvc_queue.irqqueue);

     

        for (i = 0; i < nbuffers; ++i) { //设置每个缓冲区

            myuvc_queue.buffer[i].buf.index = i;

            myuvc_queue.buffer[i].buf.m.offset = i * bufsize;

            myuvc_queue.buffer[i].buf.length = myuvc_format.fmt.pix.sizeimage;

            myuvc_queue.buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

            myuvc_queue.buffer[i].buf.sequence = 0;

            myuvc_queue.buffer[i].buf.field = V4L2_FIELD_NONE;

            myuvc_queue.buffer[i].buf.memory = V4L2_MEMORY_MMAP;

            myuvc_queue.buffer[i].buf.flags = 0;

            myuvc_queue.buffer[i].state     = VIDEOBUF_IDLE;

            init_waitqueue_head(&myuvc_queue.buffer[i].wait);

        }

     

        myuvc_queue.mem = mem;

        myuvc_queue.count = nbuffers;

        myuvc_queue.buf_size = bufsize;

        ret = nbuffers;

     

    done:

        return ret;

    }

    分配缓冲区/****************************/

    static int myuvc_free_buffers(void)

    {

        if (myuvc_queue.mem)

        {

            vfree(myuvc_queue.mem);

            memset(&myuvc_queue, 0, sizeof(myuvc_queue));

            myuvc_queue.mem = NULL;

        }

        return 0;

    }

    在上面的分配缓冲区时先被调用,如果已经分配过缓冲区,则要先释放之前分配的缓冲区。

    /****************************/

     

    /* A8 查询缓存状态比如地址信息(APP可以用mmap进行映射

     * 参考 uvc_query_buffer

     */

    static int myuvc_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)

    {

        int ret = 0;

        

    if (v4l2_buf->index >= myuvc_queue.count) {

    ret = -EINVAL;

    goto done;

    }

     

        memcpy(v4l2_buf, &myuvc_queue.buffer[v4l2_buf->index].buf, sizeof(*v4l2_buf));

     

        /* 更新flags */

    if (myuvc_queue.buffer[v4l2_buf->index].vma_use_count)

    v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;

     

     

    switch (myuvc_queue.buffer[v4l2_buf->index].state) {

         case VIDEOBUF_ERROR:

         case VIDEOBUF_DONE:

         v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;

         break;

         case VIDEOBUF_QUEUED:

         case VIDEOBUF_ACTIVE:

         v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;

         break;

         case VIDEOBUF_IDLE:

         default:

         break;

    }

     

    done:    

    return ret;

    }

    这个查询函数根据缓冲区的序列号找到相应的缓冲区并全部拷贝出来。除此之外还要获得相应的标志位如是否done,是否放入队列,地址是否映射等等,方便以后使用。

    /****************************/

     

    /* A10 把缓冲区放入队列底层的硬件操作函数将会把数据放入这个队列的缓存 

     * 参考: uvc_queue_buffer

     */

    static int myuvc_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)

    {

        struct myuvc_buffer *buf;

        int ret;

     

        /* 0. APP传入的v4l2_buf可能有问题要做判断 */

     

    if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||

        v4l2_buf->memory != V4L2_MEMORY_MMAP) {

    return -EINVAL;

    }

     

    if (v4l2_buf->index >= myuvc_queue.count) {

    return -EINVAL;

    }

     

        buf = &myuvc_queue.buffer[v4l2_buf->index];

     

    if (buf->state != VIDEOBUF_IDLE) {

    return -EINVAL;

    }

     

     

        /* 1. 修改状态 */

    buf->state = VIDEOBUF_QUEUED;

    buf->buf.bytesused = 0;

     

        /* 2. 放入2个队列 */

        /* 队列1: APP使用 

         * 当缓冲区没有数据时,放入mainqueue队列

         * 当缓冲区有数据时, APPmainqueue队列中取出

         */

    list_add_tail(&buf->stream, &myuvc_queue.mainqueue);

     

        /* 队列2: 供产生数据的函数使用

         * 当采集到数据时,irqqueue队列中取出第1个缓冲区,存入数据

         */

    list_add_tail(&buf->irq, &myuvc_queue.irqqueue);

        

    return 0;

    }

     

    /********************************************/

    启动摄像头:

    1.USB摄像头设置参数,如使用哪个format,那个frame。这些参数虽然在vidioc_s_fmt这个ioctl函数中被赋值给了一个结构体,但是并未发送到USB硬件中,而这里的streamon启动函数中才真正将这个结构体的信息发送到USB硬件中。

     

     

     


    展开全文
  • 天兴阳光168摄像头驱动安装文件为USBPCCAM-168_v.exe,在安装时首先必须确实摄像头usb接口已插到电脑上,安装方法也是相当的简单容易,需要的用户快下载体验吧!软件介绍天兴阳光摄像头驱动168系列是一款专门为天兴...
  • uvc驱动程序主要位于3.42内核\drivers\media\video\uvc文件夹中。由于本身十分复杂,在此只做简单分析。 这幅图来源于UVC的规格书。可以看到一个完整的UVC设备主要分为两个部分: 通过VideoControl Interface来控制...

    uvc驱动程序主要位于3.42内核\drivers\media\video\uvc文件夹中。由于本身十分复杂,在此只做简单分析。
    在这里插入图片描述这幅图来源于UVC的规格书。可以看到一个完整的UVC设备主要分为两个部分:
    通过VideoControl Interface来控制,
    通过VideoStreaming Interface来读视频数据,
    VC里含有多个Unit/Terminal等功能模块,可以通过访问这些模块进行控制,比如调亮度。

    分析UVC驱动调用过程

    在uvc_v4l2.c中,有:

    const struct v4l2_file_operations uvc_fops = {
    	.owner		= THIS_MODULE,
    	.open		= uvc_v4l2_open,
    	.release	= uvc_v4l2_release,
    	.unlocked_ioctl	= uvc_v4l2_ioctl,
    #ifdef CONFIG_COMPAT
    	.compat_ioctl32	= uvc_v4l2_compat_ioctl32,
    #endif
    	.read		= uvc_v4l2_read,
    	.mmap		= uvc_v4l2_mmap,
    	.poll		= uvc_v4l2_poll,
    #ifndef CONFIG_MMU
    	.get_unmapped_area = uvc_v4l2_get_unmapped_area,
    #endif
    };
    

    通过以上这些函数接口实现应用程序的系统调用。
    下面一个一个分析:

    1. open:
      uvc_v4l2_open

    ioctr:下面的调用通过uvc_v4l2_ioctl函数实现的:

    1. VIDIOC_QUERYCAP // video->streaming->type 应该是在设备被枚举时分析描述符时设置的
    		if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
    			cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
    					  | V4L2_CAP_STREAMING;
    		else
    			cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
    					  | V4L2_CAP_STREAMING;
    
    1. VIDIOC_ENUM_FMT // format数组应是在设备被枚举时设置的
     format = &video->streaming->format[fmt->index];
    
    1. VIDIOC_G_FMT //这个ioctr命令就是为了获取UVC设备支持的format和frame并返回
      uvc_v4l2_get_format // USB摄像头支持多种格式fromat, 每种格式下有多种frame(比如分辨率)
                	struct uvc_format *format = video->streaming->cur_format;
                	struct uvc_frame *frame = video->streaming->cur_frame;
    
    1. VIDIOC_TRY_FMT //对比用户设置的默写参数UVC设备是否支持
      uvc_v4l2_try_format

           /* Check if the hardware supports the requested format. */
           
       	/* Find the closest image size. The distance between image sizes is
       	 * the size in pixels of the non-overlapping regions between the
       	 * requested size and the frame-specified size.
       	 */
      
    2. VIDIOC_S_FMT // 只是把参数保存起来,还没有发给USB摄像头

            uvc_v4l2_set_format
                uvc_v4l2_try_format
            	video->streaming->cur_format = format;
            	video->streaming->cur_frame = frame;
    
    1. VIDIOC_REQBUFS //申请缓冲区
            uvc_alloc_buffers
               	for (; nbuffers > 0; --nbuffers) {
            		mem = vmalloc_32(nbuffers * bufsize);
            		if (mem != NULL)
            			break;
            	}
    
    1. VIDIOC_QUERYBUF //查询已经分配完成的buf
    uvc_query_buffer
            __uvc_query_buffer
                memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);  // 复制参数
    
    1. mmap
    	uvc_v4l2_mmap
    

    //之前的 操作,在内核空间中申请了buffer空间,但是在进一步的操作过程中,对内核空间内存的读写需要涉及到相对复杂的操作,因此需要将分配的buffer空间映射到用户空间当中。

    1. VIDIOC_QBUF //将buffer放在队列当中,有数据再将buffer取出来
            uvc_queue_buffer
            	list_add_tail(&buf->stream, &queue->mainqueue);
            	list_add_tail(&buf->queue, &queue->irqqueue);
    
    1. VIDIOC_STREAMON
            uvc_video_enable(video, 1)  // 把所设置的参数发给硬件,然后启动摄像头
                /* Commit the streaming parameters. */
                uvc_commit_video
                    uvc_set_video_ctrl  /* 设置格式fromat, frame */
                        	ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,
                        		video->streaming->intfnum  /* 哪一个接口: VS */,
                        		probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                        		uvc_timeout_param);
                        
                /* 启动:Initialize isochronous/bulk URBs and allocate transfer buffers. */
                uvc_init_video(video, GFP_KERNEL);
                        uvc_init_video_isoc / uvc_init_video_bulk
                            urb->complete = uvc_video_complete; (收到数据后此函数被调用,它又调用video->decode(urb, video, buf); ==> uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);)
                            
                        usb_submit_urb                    	
    
    1. poll //等待数据发生,等待过程中处于休眠状态
           uvc_v4l2_poll            
                uvc_queue_poll
                    poll_wait(file, &buf->wait, wait);  // 休眠等待有数据
    
    1. VIDIOC_DQBUF //将buffer重新放回队列当中等待数据接收
      uvc_dequeue_buffer
      list_del(&buf->stream);

    2. VIDIOC_STREAMOFF
      uvc_video_enable(video, 0);
      usb_kill_urb(urb);
      usb_free_urb(urb);

    分析设置亮度过程:

    ioctl: VIDIOC_S_CTRL
                uvc_ctrl_set
                uvc_ctrl_commit
                    __uvc_ctrl_commit(video, 0);
                        uvc_ctrl_commit_entity(video->dev, entity, rollback);
                    			ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个unit/terminal */,
                    				dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
                    				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                    				ctrl->info->size);
    
    总结:
    1. UVC设备有2个interface: VideoControl Interface, VideoStreaming Interface

    2. VideoControl Interface用于控制,比如设置亮度。它内部有多个Unit/Terminal(在程序里Unit/Terminal都称为entity)
      可以通过类似的函数来访问:
      ret = uvc_query_ctrl(dev /* 哪一个USB设备 /, SET_CUR, ctrl->entity->id / 哪一个unit/terminal /,
      dev->intfnum /
      哪一个接口: VC interface */, ctrl->info->selector,
      uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
      ctrl->info->size);

    3. VideoStreaming Interface用于获得视频数据,也可以用来选择fromat/frame(VS可能有多种format, 一个format支持多种frame, frame用来表示分辨率等信息)
      可以通过类似的函数来访问:
      ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 /, SET_CUR, 0,
      video->streaming->intfnum /
      哪一个接口: VS */,
      probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
      uvc_timeout_param);

    4. 我们在设置FORMAT时只是简单的使用video->streaming->format[fmt->index]等数据,
      这些数据哪来的?
      应是设备被枚举时设置的,也就是分析它的描述符时设置的。

    5. UVC驱动的重点在于:
      描述符的分析
      属性的控制: 通过VideoControl Interface来设置
      格式的选择:通过VideoStreaming Interface来设置
      数据的获得:通过VideoStreaming Interface的URB来获得

    展开全文
  • 这篇论文是用台湾凌越tp6800视频主控芯片来讲述linux下USB摄像头驱动基本架构……
  • 作者: wincemobile ... 下面是驱动以及测试应用程序下载连接(适用于ARM+Windows CE.net 4.2):   http://www.cnblogs.com/Files/lonemaverick/USB%20Camera.rar  本文转自  ...

    作者:wincemobile

    转自:http://blog.csdn.net/wincemobile/article/details/2037675


    导读: 
      主要是国庆假期之间做的一点工作,大部分资料来源于微软公司的代码。原来的解码是用的IImage接口,这里做了修改,自己添加图象解码算法,移植到Windows CE.net 4.2+ARM上可以运行。要在其他平台运行,修改工程设置,直接编译出来就可以使用。 
      适应的摄像头型号: 
      Logitech QuickCam Pro5000 
      Logitech QuickCam Fusion 
      Logitech QuickCam Orbit 
      Logitech QuickCam Notebook Pro 
      据说也适用于Creative,Samsung系列的使用Philips芯片的摄像头,不过没有测试过,就没在驱动里面添加它们的PID/VID。用Logitech的实际测试效果是不错的! 
      下面是驱动以及测试应用程序下载连接(适用于ARM+Windows CE.net 4.2): 
      http://www.cnblogs.com/Files/lonemaverick/USB%20Camera.rar 

    本文转自 
    http://www.cnblogs.com/lonemaverick/archive/2007/11/25/527308.html#971628

    展开全文
  • 我最近在安卓平板上调用USB摄像头时,用的是网上通用的https://bitbucket.org/neuralassembly/simplewebcam/src这个里边的jni,现在我发现拍出来的照片有的亮有的暗,我想问一下,该如何写函数去控制摄像头的参数,...
  • 1 写一个 USB 摄像头驱动程序 1.1 虚拟摄像头的驱动步骤 1.2 USB 的驱动步骤 1.3 UVC 驱动步骤 2 uvc_driver.c 源码分析 1. 构造设置一个 usb_driver 3 USB 摄像头的硬件内部框架 1)硬件框图 4 总结 1 ...
  • 万能摄像头驱动程序自动检测摄像头芯片,集成了301、303、168、268、325、211、308、380等市面基本所有方案。不用再担心不知道自己摄像头是什么芯片,不用再担心丢失了驱动程序就无法再使用了!
  • 在上一节视频中我们写了一个虚拟摄像头驱动程序,里面的数据是我们虚构出来的,本节我们要写USB摄像头程序了,它里面...1.写一个USB摄像头驱动程序 当我们把usb设备接到电脑或板子上去时,如果这个设备能够被usb...
  • usb摄像头驱动程序,里面涉及硬件的操作。比如说,想设置亮度的时候,需要把亮度的参数发给硬件。去得到真正视频数据的时候,需要访问硬件得到数据。usb摄像头驱动程序框架与虚拟摄像头驱动程序的框架是一样的。 1...
  • USB摄像头驱动分析

    千次阅读 2015-09-29 17:43:26
    写一个USB摄像头驱动程序: 怎么写摄像头驱动程序: 1.分配video_device结构体:video_device_alloc函数 2.设置 .fops .ioctl_ops(里面需要设置11项) 如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_...
  • 一、写一个USB摄像头驱动程序 uvc_driver.c分析 uvc_probe

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 558
精华内容 223
关键字:

usb摄像头驱动程序