2.4 自己编写一个vivi驱动程序

时间:2022-03-17 23:35:38

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

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

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

2.4 自己编写一个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和百问网)