zoukankan      html  css  js  c++  java
  • 从零写一个摄像头驱动(四)

    1、装载驱动时发现,模块缺少依赖

    解决方法:
      1、先安装Ubantu里面自带的vivi程序,它会把它所依赖的驱动程序安装进来/

      2、sudo rmmod vivi.ko

      3、ls /dev/video*
      4、xawtv -c /dev/video* 
      我写的myvivi.ko编译出来之后,对应的是video1

    /*APP在调用ioctl VIDIOC_QBUF时导致此函数被调用
     *它会填充Video_buffer结构体(头部),并调用videobuf_iolock来分配内存
     *实际上是用mmap函数分配内存的
     */
    static int
    myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                            enum v4l2_field field)
    {
        /* 1.做些准备工作 */
    #if 0
        /* 2.调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
        if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
            rc = videobuf_iolock(vq, &buf->vb, NULL);
            if (rc < 0)
                goto fail;
        }
    #endif

    队列的内存实际上是调用 fops中的mmap函数分配的,

    加入mmap,并实现myvivi_mmap

    编译加载后,运行,原来的错误信息消失了

    ,现在请求buf,分配buf, 队列操作等函数已经实现,现在缺少的是:怎么构造生产数据?

    再次根据系统提示的信息修改代码

    加入摄像头启动/关闭函数

    加入v4l2_poll函数(select)来查询数据

    运行后发现,无法退出界面,是因为 如果poll没有查询到数据则在队列buf->done上休眠,

    下一步构造数据唤醒进程。

    在vivi.c中,实际上是创建了一个内核线程,线程平时是休眠的。

    调用关系如下:

    vivi_open
      vivi_start_thread
        vivi_thread /*创建内核线程*/   
         for (;;) {
           vivi_sleep(fh); /* 平时是休眠的 */       
            /* Calculate time to wake up */
             timeout = msecs_to_jiffies(frames_to_ms(1));
             vivi_thread_tick(fh);
                vivi_fillbuff(fh, buf); /*填充buf数据*/
                wake_up(&buf->vb.done); /*填充完后唤醒vb.done*/
             /*会休眠指定时间,时间到了会重新运行,调用vivi_thread_tick*/
             schedule_timeout_interruptible(timeout);

    为简单操作,使用定时器唤醒进程

    用定时器产生数据并唤醒进程

    在入口函数里面初始化timer

      1 /*仿照vivi.c*/
      2 #include <linux/module.h>
      3 #include <linux/delay.h>
      4 #include <linux/errno.h>
      5 #include <linux/fs.h>
      6 #include <linux/kernel.h>
      7 #include <linux/slab.h>
      8 #include <linux/mm.h>
      9 #include <linux/ioport.h>
     10 #include <linux/init.h>
     11 #include <linux/sched.h>
     12 #include <linux/pci.h>
     13 #include <linux/random.h>
     14 #include <linux/version.h>
     15 #include <linux/mutex.h>
     16 #include <linux/videodev2.h>
     17 #include <linux/dma-mapping.h>
     18 #include <linux/interrupt.h>
     19 #include <linux/kthread.h>
     20 #include <linux/highmem.h>
     21 #include <linux/freezer.h>
     22 #include <media/videobuf-vmalloc.h>
     23 #include <media/v4l2-device.h>
     24 #include <media/v4l2-ioctl.h>
     25 
     26 
     27 static struct v4l2_format myvivi_format; /* 自己定义v4l2_format结构体 */
     28 
     29 /* 队列操作1:  定义*/
     30 static struct videobuf_queue myvivi_vb_vidqueue;
     31 static spinlock_t     myvivi_queue_slock; //定义队列所需的自旋锁,并在入口处初始化
     32 static struct list_head       myvivi_vb_local_queue;  
     33 static struct timer_list myvivi_timer; /*定义一个timer,然后初始化*/
     34 
     35 #include "fillbuf.c"
     36 
     37 /* ------------------------------------------------------------------
     38     Videobuf operations
     39    ------------------------------------------------------------------*/
     40 /*APP在调用ioctl vidioc_reqbufs时会导致此函数被调用
     41  *它重新调整count和size,避免浪费空间
     42  */
     43 static int
     44 myvivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
     45 {
     46 
     47     *size = myvivi_format.fmt.pix.sizeimage;
     48 
     49     if (0 == *count)
     50         *count = 32;
     51 
     52     return 0;
     53 }
     54 
     55 /*APP在调用ioctl VIDIOC_QBUF时导致此函数被调用
     56  *它会填充Video_buffer结构体(头部),并调用videobuf_iolock来分配内存
     57  *实际上是用mmap函数分配内存的
     58  */
     59 static int
     60 myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
     61                         enum v4l2_field field)
     62 {
     63     /* 0.设置videobuf */
     64     vb->size = myvivi_format.fmt.pix.pixelformat;
     65     vb->bytesperline = myvivi_format.fmt.pix.pixelformat;
     66     vb->width  = fh->width;
     67     vb->height = fh->height;
     68     vb->field  = field;
     69 
     70     /* 1.做些准备工作 */
     71     myvivi_precalculate_bars(0); //第一个通道的条纹
     72 #if 0
     73     /* 2.调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
     74     if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
     75         rc = videobuf_iolock(vq, &buf->vb, NULL);
     76         if (rc < 0)
     77             goto fail;
     78     }
     79 #endif
     80     /* 3.设置状态,填充vb结构体,说明状态已经准备好 */
     81     vb->state = VIDEOBUF_PREPARED;
     82 
     83     return 0;
     84 }
     85 
     86 /*APP在调用ioctl VIDIOC_QBUF时
     87  *1.先调用buffer_prepare进行准备工作
     88  *2.把buf放入队列
     89  *3.调用buf_queue(起通知、记录作用)
     90  */
     91 static void
     92 myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
     93 {
     94     vb->state = VIDEOBUF_QUEUED;
     95     
     96     /*把videobuf放入本地一个队列尾部
     97      *定时时间到后,定时器处理函数就可以从本地队列取出videobuf
     98      */
     99     list_add_tail(&vb->queue, &myvivi_vb_local_queue);
    100 }
    101 
    102 /*APP不再使用队列时,使用此函数释放内存
    103  */
    104 static void myvivi_buffer_release(struct videobuf_queue *vq,
    105                struct videobuf_buffer *vb)
    106 {
    107     videobuf_vmalloc_free(vb);
    108     vb->state = VIDEOBUF_NEEDS_INIT; //状态改为初始状态
    109 }
    110 
    111 
    112 /* 在内核文档的v4l2-framework中查找
    113  * buf_setup /buf_prepare/buf_queue/buf_release
    114  *以上四个函数设置好后,videobuf_queue_ops结构体就设置好了
    115  */
    116 static struct videobuf_queue_ops myvivi_video_qops = {
    117     .buf_setup      = myvivi_buffer_setup,
    118     .buf_prepare    = myvivi_buffer_prepare,
    119     .buf_queue      = myvivi_buffer_queue,
    120     .buf_release    = myvivi_buffer_release,
    121 };
    122 
    123 
    124 /* ------------------------------------------------------------------
    125     File operations for the device
    126    ------------------------------------------------------------------*/
    127 
    128 static int myvivi_open(struct file *file)
    129 {
    130     /* 队列操作2: 
    131      *初始化
    132      *  1).    定义队列:myvivi_vb_vidqueue            
    133      *  2). 设置队列操作函数结构体,并实现里面的操作函数:myvivi_video_qops
    134      *  3). 定义队列所需的自旋锁,并在入口处初始化
    135      *  4). 设置buffer头部大小
    136      * ---------设置好队列相关初始化后,继续完成有关于队列与缓冲区的ioctl操作----------
    137      */
    138     videobuf_queue_vmalloc_init(&myvivi_vb_vidqueue, &myvivi_video_qops,
    139             NULL, &myvivi_queue_slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
    140             sizeof(struct videobuf_buffer), NULL);  /* 倒数第二个参数是buffer头部大小 */
    141 
    142     myvivi_timer.expires = jiffies + 1; /*expires为定时器到时时间,*/
    143     add_timer(&myvivi_timer); /*在用到vivitimer时才去分配,故放在open,非入口函数*/
    144 
    145     return 0;
    146 }
    147 
    148 
    149 static int myvivi_close(struct file *file)
    150 {
    151     videobuf_stop(&myvivi_vb_vidqueue);
    152     videobuf_mmap_free(&myvivi_vb_vidqueue);
    153     del_timer(&myvivi_timer);
    154     return 0;
    155 }
    156 
    157 static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
    158 {
    159     return  videobuf_mmap_mapper(&myvivi_vb_vidqueue, vma);
    160     
    161 }
    162 
    163 static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
    164 {
    165     return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);
    166 }
    167 
    168 
    169 /*------------------------------------------------------------------
    170  */
    171 
    172 static int myvivi_vidioc_querycap(struct file *file, void  *priv,
    173                     struct v4l2_capability *cap)
    174 {
    175 
    176     strcpy(cap->driver, "myvivi");
    177     strcpy(cap->card, "myvivi");
    178     cap->version = 0x0001;
    179     cap->capabilities =    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
    180     return 0;
    181 }
    182 
    183 
    184 /* ------------------------------------------------------------------
    185     用于列举、获得、测试、设置摄像头的数据的格式
    186    ------------------------------------------------------------------*/
    187 
    188 /* 列举支持哪种格式,此处只让其支持一种格式 */
    189 static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
    190                     struct v4l2_fmtdesc *f)
    191 {
    192 
    193     if (f->index >= 1) /* index为格式的种数,在此判断条件设置为1 */
    194         return -EINVAL;
    195 
    196     strcpy(f->description, "4:2:2, packed, YUYV");
    197     f->pixelformat = V4L2_PIX_FMT_YUYV; //s设置支持一种格式
    198     return 0;
    199 }
    200 
    201 /* 返回当前所使用的格式 */
    202 static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
    203                     struct v4l2_format *f)
    204 {
    205     memcpy(f, &myvivi_format, sizeof(myvivi_format));
    206     return (0);
    207 }
    208 
    209 /* 测试驱动程序是否支持某种格式,此处即上面固定的格式 */
    210 static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
    211             struct v4l2_format *f)
    212 {
    213     unsigned int maxw, maxh;
    214     enum v4l2_field field;
    215     
    216     /* 判断是否是支持的格式,否则返回错误 */
    217     if(f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
    218     return -EINVAL;
    219     
    220     field = f->fmt.pix.field;
    221 
    222     if (field == V4L2_FIELD_ANY) {
    223         field = V4L2_FIELD_INTERLACED;
    224     } else if (V4L2_FIELD_INTERLACED != field) {
    225         return -EINVAL;
    226     }
    227 
    228     maxw  = 1024;
    229     maxh  = 768;
    230 
    231     /* 调整format的width、height,
    232      * 计算每一行的字节数bytesperline、sizeimage
    233      */
    234     v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
    235                   &f->fmt.pix.height, 32, maxh, 0, 0);
    236     f->fmt.pix.bytesperline =
    237         (f->fmt.pix.width * 16) >> 3;
    238     f->fmt.pix.sizeimage =
    239         f->fmt.pix.height * f->fmt.pix.bytesperline;
    240 
    241     return 0;
    242 }
    243 
    244 
    245 static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
    246                     struct v4l2_format *f)
    247 {
    248     /* 判断 */
    249     int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
    250     if (ret < 0)
    251         return ret;
    252     /* 把传进来的参数复制到myvivi_format */
    253     memcpy(&myvivi_format, f, sizeof(myvivi_format));
    254 
    255     return ret;
    256 }
    257 
    258 
    259 /* ------------------------------------------------------------------
    260      缓冲区操作: 申请/查询/放入队列/取出队列
    261    ------------------------------------------------------------------*/
    262 
    263 static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
    264               struct v4l2_requestbuffers *p)
    265 {
    266     return (videobuf_reqbufs(&myvivi_vb_vidqueue, p));
    267 }
    268 
    269 static int myvivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
    270 {
    271     return (videobuf_querybuf(&myvivi_vb_vidqueue, p));
    272 }
    273 
    274 static int myvivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    275 {
    276     return (videobuf_qbuf(&myvivi_vb_vidqueue, p));
    277 }
    278 
    279 static int myvivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    280 {
    281     return (videobuf_dqbuf(&myvivi_vb_vidqueue, p,
    282                 file->f_flags & O_NONBLOCK));
    283 }
    284 
    285 
    286 /* ------------------------------------------------------------------
    287                              启动/停止摄像头
    288    ------------------------------------------------------------------*/
    289 
    290 static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
    291 {
    292     return videobuf_streamon(&myvivi_vb_vidqueue);
    293 }
    294 
    295 static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
    296 {
    297     return videobuf_streamoff(&myvivi_vb_vidqueue);
    298 }
    299 
    300 
    301 static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
    302 
    303     //摄像头驱动程序必需的11个ioctl:
    304     // 表示它是一个摄像头设备
    305     .vidioc_querycap      = myvivi_vidioc_querycap,
    306 
    307     /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    308     .vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
    309     .vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
    310     .vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
    311     .vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,
    312 
    313 
    314     /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
    315     .vidioc_reqbufs       = myvivi_vidioc_reqbufs,
    316     .vidioc_querybuf      = myvivi_vidioc_querybuf,
    317     .vidioc_qbuf          = myvivi_vidioc_qbuf,
    318     .vidioc_dqbuf         = myvivi_vidioc_dqbuf,
    319 
    320     /* 启动/停止 */
    321     .vidioc_streamon      = myvivi_vidioc_streamon,
    322     .vidioc_streamoff     = myvivi_vidioc_streamoff,    
    323 
    324 };
    325 
    326 static const struct v4l2_file_operations myvivi_fops = {
    327     .owner     = THIS_MODULE,
    328     .open      = myvivi_open,
    329     .release   = myvivi_close,
    330     .mmap      = myvivi_mmap,
    331     .ioctl     = video_ioctl2, /* V4L2 ioctl handler 最终会调用到ioctl_ops里面的ioctl*/   
    332     .poll      = myvivi_poll,
    333     
    334 };
    335 
    336 
    337 /*  video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间 */
    338 static struct video_device *myvivi_device;
    339 
    340 static void myvivi_release(struct video_device *vdev)
    341 {
    342 
    343 }
    344 
    345 static void myvivi_timer_function(unsigned long data)
    346 {
    347     struct videobuf_buffer *vb;
    348     void *vbuf;
    349     struct timeval ts;
    350     
    351     /* 1. 构造数据:从队列头部取出第一个videobuf,填充数据*/
    352     
    353         /* 如果链表是空的,表明还没有应用程序关心里面的数据->跳出 */
    354      if (list_empty(&myvivi_vb_local_queue)) {
    355         goto out; //
    356     }
    357         /* 1.1 从队列myvivi_vb_local_queue的头部取出第一个videobuf*/
    358     vb = list_entry(myvivi_vb_local_queue.next,
    359              struct videobuf_buffer, queue);
    360 
    361         /* 本地队列中没有队列等待唤醒,跳出 */
    362     if (!waitqueue_active(&vb->done))
    363         goto out;
    364 
    365         /* 1.2 填充数据 */
    366         vbuf = videobuf_to_vmalloc(vb);
    367         //memset(vbuf, 0, vb->size); //为新分配的vbuf内存填充数据(为申请的内存做初始化工作)
    368         myvivi_fillbuff(vb);
    369         
    370         /* 填充后要将状态标志为DONE,状态会在poll函数中进行判断 */
    371         vb->field_count++;
    372         do_gettimeofday(&ts);
    373         vb->ts = ts;
    374         vb->state = VIDEOBUF_DONE;
    375         
    376         /* 1.3 把videobuf从本地队列中删除 */
    377         list_del(&vb->queue);
    378         
    379     /* 2. 唤醒进程: 唤醒videobuf->done上的进程 */
    380     wake_up(&vb->done);
    381     
    382 out:
    383     /* 3. 修改timer的超时时间:30fps, 1秒内有30帧
    384      *   每1/30秒时间产生一帧数据
    385      */
    386      mod_timer(&myvivi_timer, jiffies + HZ/30);
    387 }
    388 
    389 /*对于驱动程序,先写入口、出口函数*/
    390 static int myvivi_init(void)
    391 {
    392     int error;
    393     
    394     /* 1.分配一个video_device结构体*/
    395     myvivi_device = video_device_alloc();  //
    396 
    397     /* 2.设置结构体 */
    398 
    399     /* 2.1 */
    400     myvivi_device->release = myvivi_release; //结构体中要有release函数,否则返回错误
    401 
    402     /* 2.2 */
    403     myvivi_device->fops    = &myvivi_fops;
    404 
    405     /* 2.3 */
    406     myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    407 
    408     /* 2.4 队列操作
    409      *  a. 定义/初始化一个队列(会用到一个spinlock)
    410      */
    411     spin_lock_init(&myvivi_queue_slock); //初始化锁
    412     /* 3.注册 */
    413     /* nr: device node number */
    414     error = video_register_device(myvivi_device,VFL_TYPE_GRABBER, -1);
    415 
    416     /*用定时器产生数据并唤醒进程*/
    417     init_timer(&myvivi_timer); //初始化timer
    418     myvivi_timer.function = myvivi_timer_function; /* 超时处理函数 */
    419 
    420     INIT_LIST_HEAD(&myvivi_vb_local_queue); //初始化本地队列
    421     
    422     return error;
    423 }
    424 
    425 static void myvivi_exit(void)
    426 {
    427     video_unregister_device(myvivi_device);
    428     video_device_release(myvivi_device);
    429 }
    430 
    431 /*修饰入口出口函数,使用insmod,rmmod时就会调用对应函数*/
    432 module_init(myvivi_init);
    433 module_exit(myvivi_exit);
    434 
    435 /*加入GPL协议,通用性公开许可证(General Public License)*/
    436 MODULE_LICENSE("GPL");
    myvivi.c
  • 相关阅读:
    设计模式(八): 策略模式
    设计模式(七): 迭代器模式
    设计模式(六): 建造者模式
    设计模式(五): 装饰者模式
    设计模式(四): 适配器模式
    设计模式(三): 抽象工厂模式
    设计模式(二): 工厂模式
    设计模式(一): 单例模式
    Hibernate三种状态,缓存,以及update更新问题
    Servlet 生命周期、工作原理
  • 原文地址:https://www.cnblogs.com/y4247464/p/10626389.html
Copyright © 2011-2022 走看看