学习目标:从零编写一个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和百问网)