zoukankan      html  css  js  c++  java
  • 2.4 自己编写一个vivi驱动程序

    学习目标:从零编写一个vivi驱动程序,并测试;

    一. vivi驱动应用程序调用过程

    上节对xawtv对vivi程序调用欧城进行了详细分析,可总结为以下流程:

    二、仿照vivi.c编写myvivi.c驱动程序

      1 #include <linux/module.h>
      2 #include <linux/delay.h>
      3 #include <linux/errno.h>
      4 #include <linux/fs.h>
      5 #include <linux/kernel.h>
      6 #include <linux/slab.h>
      7 #include <linux/mm.h>
      8 #include <linux/ioport.h>
      9 #include <linux/init.h>
     10 #include <linux/sched.h>
     11 #include <linux/pci.h>
     12 #include <linux/random.h>
     13 #include <linux/version.h>
     14 #include <linux/mutex.h>
     15 #include <linux/videodev2.h>
     16 #include <linux/dma-mapping.h>
     17 #include <linux/interrupt.h>
     18 #include <linux/kthread.h>
     19 #include <linux/highmem.h>
     20 #include <linux/freezer.h>
     21 #include <media/videobuf-vmalloc.h>
     22 #include <media/v4l2-device.h>
     23 #include <media/v4l2-ioctl.h>
     24 
     25 
     26 static struct v4l2_format myvivi_format;
     27 
     28 /* 队列操作1: 定义 */
     29 static struct videobuf_queue myvivi_vb_vidqueue;
     30 static spinlock_t myvivi_queue_slock;
     31 
     32 static struct list_head myvivi_vb_local_queue; //定义本地队列,用于把videobuf放入该队列的尾部
     33 
     34 static struct timer_list myvivi_timer; //定义定时器
     35 
     36 /* ------------------------------------------------------------------
     37     Videobuf operations
     38    ------------------------------------------------------------------*/
     39 /* APP调用ioctl VIDIOC_REQBUFS时会导致此函数被调用,
     40  * 它重新调整count和size
     41  */
     42 static int myvivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
     43 {
     44 
     45     *size = myvivi_format.fmt.pix.sizeimage;
     46 
     47     if (0 == *count)
     48         *count = 32;
     49 
     50     return 0;
     51 }
     52 
     53 /* APP调用ioctlVIDIOC_QBUF时导致此函数被调用,
     54  * 它会填充video_buffer结构体并调用videobuf_iolock来分配内存
     55  * 
     56  */
     57 static int myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
     58                         enum v4l2_field field)
     59 {
     60     /* 0. 设置videobuf */
     61     vb->size = myvivi_format.fmt.pix.sizeimage;
     62     vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
     63     vb->width  = myvivi_format.fmt.pix.width;
     64     vb->height = myvivi_format.fmt.pix.height;
     65     vb->field  = field;
     66     
     67     /* 1. 做些准备工作 */
     68 
     69 #if 0
     70     /* 2. 调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
     71     if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
     72         rc = videobuf_iolock(vq, &buf->vb, NULL);
     73         if (rc < 0)
     74             goto fail;
     75     }
     76 #endif
     77     /* 3. 设置状态 */
     78     vb->state = VIDEOBUF_PREPARED;
     79 
     80     return 0;
     81 }
     82 
     83 
     84 /* APP调用ioctl VIDIOC_QBUF时:
     85  * 1. 先调用buf_prepare进行一些准备工作
     86  * 2. 把buf放入stream队列
     87  * 3. 调用buf_queue(起通知、记录作用)
     88  */
     89 static void myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
     90 {
     91     vb->state = VIDEOBUF_QUEUED;
     92 
     93     /* 把videobuf放入本地一个队列尾部
     94      * 定时器处理函数就可以从本地队列取出videobuf
     95      */
     96     list_add_tail(&vb->queue, &myvivi_vb_local_queue);
     97 }
     98 
     99 /* APP不再使用队列时, 用它来释放内存 */
    100 static void myvivi_buffer_release(struct videobuf_queue *vq,
    101                struct videobuf_buffer *vb)
    102 {
    103     videobuf_vmalloc_free(vb);
    104     vb->state = VIDEOBUF_NEEDS_INIT;
    105 }
    106 
    107 static struct videobuf_queue_ops myvivi_video_qops = {
    108     .buf_setup      = myvivi_buffer_setup, /* 计算大小以免浪费 */
    109     .buf_prepare    = myvivi_buffer_prepare,
    110     .buf_queue      = myvivi_buffer_queue,
    111     .buf_release    = myvivi_buffer_release,
    112 };
    113 
    114 /* ------------------------------------------------------------------
    115     File operations for the device
    116    ------------------------------------------------------------------*/
    117 
    118 static int myvivi_open(struct file *file)
    119 {
    120     /* 队列操作2: 初始化 */
    121     videobuf_queue_vmalloc_init(&myvivi_vb_vidqueue, &myvivi_video_qops,
    122             NULL, &myvivi_queue_slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
    123             sizeof(struct videobuf_buffer), NULL); /* 倒数第2个参数是buffer的头部大小 */
    124 
    125     myvivi_timer.expires = jiffies + 1;
    126     add_timer(&myvivi_timer);
    127 
    128     return 0;
    129 }
    130 
    131 
    132 static int myvivi_close(struct file *file)
    133 {
    134     del_timer(&myvivi_timer);
    135     videobuf_stop(&myvivi_vb_vidqueue);
    136     videobuf_mmap_free(&myvivi_vb_vidqueue);
    137     
    138     return 0;
    139 }
    140 
    141 static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
    142 {
    143     return videobuf_mmap_mapper(&myvivi_vb_vidqueue, vma);
    144 }
    145 
    146 static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
    147 {
    148     return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);
    149 }
    150 
    151 static int myvivi_vidioc_querycap(struct file *file, void  *priv,
    152                     struct v4l2_capability *cap)
    153 {
    154     strcpy(cap->driver, "myvivi");
    155     strcpy(cap->card, "myvivi");
    156     cap->version = 0x0001;
    157     cap->capabilities =    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
    158     return 0;
    159 }
    160 
    161 /* 列举支持哪种格式 */
    162 static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
    163                     struct v4l2_fmtdesc *f)
    164 {
    165     if (f->index >= 1)
    166         return -EINVAL;
    167 
    168     strcpy(f->description, "4:2:2, packed, YUYV");
    169     f->pixelformat = V4L2_PIX_FMT_YUYV;
    170     return 0;
    171 }
    172 
    173 /* 返回当前所使用的格式 */
    174 static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
    175                     struct v4l2_format *f)
    176 {
    177     memcpy(f, &myvivi_format, sizeof(myvivi_format));
    178     return (0);
    179 }
    180 
    181 /* 测试驱动程序是否支持某种格式 */
    182 static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
    183             struct v4l2_format *f)
    184 {
    185     unsigned int maxw, maxh;
    186     enum v4l2_field field;
    187 
    188     if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
    189         return -EINVAL;
    190 
    191     field = f->fmt.pix.field;
    192 
    193     if (field == V4L2_FIELD_ANY) {
    194         field = V4L2_FIELD_INTERLACED;
    195     } else if (V4L2_FIELD_INTERLACED != field) {
    196         return -EINVAL;
    197     }
    198 
    199     maxw  = 1024;
    200     maxh  = 768;
    201 
    202     /* 调整format的width, height, 
    203      * 计算bytesperline, sizeimage
    204      */
    205     v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
    206                   &f->fmt.pix.height, 32, maxh, 0, 0);
    207     f->fmt.pix.bytesperline =
    208         (f->fmt.pix.width * 16) >> 3;
    209     f->fmt.pix.sizeimage =
    210         f->fmt.pix.height * f->fmt.pix.bytesperline;
    211 
    212     return 0;
    213 }
    214 
    215 static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
    216                     struct v4l2_format *f)
    217 {
    218     int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
    219     if (ret < 0)
    220         return ret;
    221 
    222     memcpy(&myvivi_format, f, sizeof(myvivi_format));
    223     
    224     return ret;
    225 }
    226 
    227 static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
    228               struct v4l2_requestbuffers *p)
    229 {
    230     return (videobuf_reqbufs(&myvivi_vb_vidqueue, p));
    231 }
    232 
    233 static int myvivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
    234 {
    235     return (videobuf_querybuf(&myvivi_vb_vidqueue, p));
    236 }
    237 
    238 static int myvivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    239 {
    240     return (videobuf_qbuf(&myvivi_vb_vidqueue, p));
    241 }
    242 
    243 static int myvivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    244 {
    245     return (videobuf_dqbuf(&myvivi_vb_vidqueue, p,
    246                 file->f_flags & O_NONBLOCK));
    247 }
    248 
    249 static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
    250 {
    251     return videobuf_streamon(&myvivi_vb_vidqueue);
    252 }
    253 
    254 static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
    255 {
    256     videobuf_streamoff(&myvivi_vb_vidqueue);
    257     return 0;
    258 }
    259 
    260 
    261 static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
    262         // 表示它是一个摄像头设备
    263         .vidioc_querycap      = myvivi_vidioc_querycap,
    264 
    265         /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    266         .vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
    267         .vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
    268         .vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
    269         .vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,
    270         
    271         /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
    272         .vidioc_reqbufs       = myvivi_vidioc_reqbufs,
    273         .vidioc_querybuf      = myvivi_vidioc_querybuf,
    274         .vidioc_qbuf          = myvivi_vidioc_qbuf,
    275         .vidioc_dqbuf         = myvivi_vidioc_dqbuf,
    276         
    277         // 启动/停止
    278         .vidioc_streamon      = myvivi_vidioc_streamon,
    279         .vidioc_streamoff     = myvivi_vidioc_streamoff,   
    280 };
    281 
    282 static const struct v4l2_file_operations myvivi_fops = {
    283     .owner        = THIS_MODULE,
    284     .open       = myvivi_open,
    285     .release    = myvivi_close,
    286     .mmap       = myvivi_mmap,
    287     .ioctl      = video_ioctl2, /* V4L2 ioctl handler */
    288     .poll       = myvivi_poll,
    289 };
    290 
    291 static struct video_device *myvivi_device;
    292 
    293 static void myvivi_release(struct video_device *vdev)
    294 {
    295 }
    296 
    297 static void myvivi_timer_function(unsigned long data)
    298 {
    299     struct videobuf_buffer *vb;
    300     void *vbuf;
    301     struct timeval ts;
    302     
    303     /* 1. 构造数据: 从队列头部取出第1个videobuf, 填充数据
    304      */
    305 
    306     /* 1.1 从本地队列取出第1个videobuf */
    307     if (list_empty(&myvivi_vb_local_queue)) { //已在myvivi_buffer_queue函数中放入的
    308         goto out;
    309     }
    310     
    311     vb = list_entry(myvivi_vb_local_queue.next,
    312              struct videobuf_buffer, queue);
    313     
    314     /* Nobody is waiting on this buffer, return */
    315     if (!waitqueue_active(&vb->done))
    316         goto out;
    317     
    318 
    319     /* 1.2 填充数据 */
    320     vbuf = videobuf_to_vmalloc(vb);
    321     memset(vbuf, 0, vb->size); // 写入0;
    322     vb->field_count++;
    323     do_gettimeofday(&ts);
    324     vb->ts = ts;
    325     vb->state = VIDEOBUF_DONE;
    326 
    327     /* 1.3 把videobuf从本地队列中删除 *****注意:这不是myvivi_vb_local_queue队列*/
    328     list_del(&vb->queue);
    329 
    330     /* 2. 唤醒进程: 唤醒videobuf->done上的进程 */
    331     wake_up(&vb->done);
    332     
    333 out:
    334     /* 3. 修改timer的超时时间 : 30fps, 1秒里有30帧数据
    335      *    每1/30 秒产生一帧数据
    336      */
    337     mod_timer(&myvivi_timer, jiffies + HZ/30);
    338 }
    339 
    340 static int myvivi_init(void)
    341 {
    342     int error;
    343     
    344     /* 1. 分配一个video_device结构体 */
    345     myvivi_device = video_device_alloc();
    346 
    347     /* 2. 设置 */
    348 
    349     /* 2.1 */
    350     myvivi_device->release = myvivi_release;
    351 
    352     /* 2.2 */
    353     myvivi_device->fops    = &myvivi_fops;
    354 
    355     /* 2.3 */
    356     myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    357 
    358     /* 2.4 队列操作
    359      *  a. 定义/初始化一个队列(会用到一个spinlock)
    360      */
    361     spin_lock_init(&myvivi_queue_slock);
    362 
    363     /* 3. 注册 */
    364     error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);
    365 
    366     /* 用定时器产生数据并唤醒进程 */
    367     init_timer(&myvivi_timer);
    368     myvivi_timer.function  = myvivi_timer_function;
    369 
    370     INIT_LIST_HEAD(&myvivi_vb_local_queue);
    371     
    372     return error;
    373 }
    374 
    375 static void myvivi_exit(void)
    376 {
    377     video_unregister_device(myvivi_device);
    378     video_device_release(myvivi_device);
    379 }
    380 
    381 module_init(myvivi_init);
    382 module_exit(myvivi_exit);
    383 MODULE_LICENSE("GPL");

    (参考:https://home.cnblogs.com/u/linhaostudy和百问网)

  • 相关阅读:
    百练 2712 细菌繁殖 解题报告
    Elasticsearch常用最全最常用工具清单
    并发工具类使用详解及区别(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
    Elasticsearch 启动过程详解
    Elasticsearch 编译调试总结
    gradle镜像源配置
    Activiti6详细教程
    CSDN-markdown编辑器语法
    Spring Boot Activiti 整合工作流引擎开发
    less使用语法详解
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10307095.html
Copyright © 2011-2022 走看看