static int __init vivi_init(void) struct vb2_queue *q; /* 找到支持的字体格式 */ const struct font_desc *font = find_font("VGA8x16"); /* 创建一个实例 */ ret = vivi_create_instance //用于管理v4l2_ctrl,亮度,音量等属性信息就是用v4l2_ctrl来表示的 struct v4l2_ctrl_handler *hdl; /* 封装的结构体 */ struct vivi_dev *dev; /* 摄像头的结构体 */ struct video_device *vfd; dev = kzalloc(sizeof(*dev), GFP_KERNEL); //对video_device进行赋值,里面有要回调的方法集 *vfd = vivi_template; //初始化v4l2_ctrl_handler v4l2_ctrl_handler_init(hdl, 11); //根据ID(V4L2_CID_AUDIO_VOLUME)添加一个新的标准属性 dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); v4l2_ctrl_new(hdl, ops, id, name, type,min, max, step, def, flags, NULL, NULL); struct v4l2_ctrl *ctrl; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); /* 初始化和设置v4l2_ctrl */ INIT_LIST_HEAD(&ctrl->node); INIT_LIST_HEAD(&ctrl->ev_subs); ctrl->handler = hdl; ctrl->ops = ops; ctrl->id = id; //添加到v4l2_ctrl_handler尾部 list_add_tail(&ctrl->node, &hdl->ctrls); //添加自定义的v4l2_ctrl dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); /* 那么什么时候用v4l2_ctrl呢? 是在应用层调用ioctl时根据不同的命令码在__video_do_ioctl中调用不同的v4l2_ctrl */ /* 对dev->v4l2_dev简单的初始化 */ ret = v4l2_device_register(NULL, &dev->v4l2_dev); /* 对vb2_queue初始化 */ q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; /* vivi_dev中包含v4l2_dev和video_device,video_device包含v4l2_dev,video_device指向v4l2_dev */ vfd = &dev->vdev; /* 提供操作方法集 */ *vfd = vivi_template; vfd->debug = debug; vfd->v4l2_dev = &dev->v4l2_dev; vfd->queue = q; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); /* 将vivi_dev放入video_device中的dev中的私有数据中 */ video_set_drvdata(vfd, dev); ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); __video_register_device(vdev, type, nr, 1, vdev->fops->owner); /* 设置名字 */ case VFL_TYPE_GRABBER: name_base = "video"; /* video的次设备号从0到64 */ case VFL_TYPE_GRABBER: minor_offset = 0; minor_cnt = 64; /* 将注册的video_device放入全局video_device中 */ video_device[vdev->minor] = vdev; /* 初始化字符设备那一套 */ vdev->cdev = cdev_alloc(); /* 初始化操作方法集,这个操作操作方法集是上层调用的 */ vdev->cdev->ops = &v4l2_fops; cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); /* 创建类,属性文件等 */ ....... APP:open static const struct file_operations v4l2_fops = { .open = v4l2_open, /* 根据此设备号找到video_device */ vdev = video_devdata(filp); video_device[iminor(file_inode(file))]; /* 调用video_device中操作方法集中的open */ if (vdev->fops->open) { if (video_is_registered(vdev)) ret = vdev->fops->open(filp); /* 回调video_device中操作方法集中的open */ v4l2_fh_open struct video_device *vdev = video_devdata(filp); video_device[iminor(file_inode(file))]; struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); filp->private_data = fh; if (fh == NULL) return -ENOMEM; v4l2_fh_init(fh, vdev); v4l2_fh_add(fh); APP:read 同open 重点是ioctl static const struct file_operations v4l2_fops = { .unlocked_ioctl = v4l2_ioctl, struct video_device *vdev = video_devdata(filp); ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); /* 回调video_device中操作方法集中的ioctl */ static const struct v4l2_file_operations vivi_fops = { return video_usercopy(file, cmd, arg, __video_do_ioctl); err = func(file, cmd, parg); /* 回调__video_do_ioctl函数 */ __video_do_ioctl struct video_device *vfd = video_devdata(file); /* 获得video_device中的ioctl_ops */ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; /* 根据cmd从v4l2_ioctl_info数组中取出相应的v4l2_ioctl_info */ if (v4l2_is_known_ioctl(cmd)) { info = &v4l2_ioctls[_IOC_NR(cmd)]; /* 调用cmd对应的函数 */ ret = info->u.func(ops, file, fh, arg); case VIDIOC_S_CTRL: //根据不同的命令码应用层传进来的arg类型也是不同的,arg只是一个指针而已,具体类型需要根据命令码来转换 struct v4l2_control *p = arg; if (vfd->ctrl_handler) { ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p); //根据命令码找到v4l2_ctrl,因为v4l2_ctrl就是之前根据命令码创建出来的 struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id); 以ioctl的参数VIDIOC_STREAMON为例,分析调用过程 IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), 当cmd命令为VIDIOC_STREAMON时:调用v4l_streamon函数 v4l_streamon const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; /* 第一个参数时video_device里的ioctl_ops */ static int v4l_streamon(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) ops->vidioc_streamon(file, fh, *(unsigned int *)arg); 回调video_device里的ioctl_ops的vidioc_streamon static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_streamon = vb2_ioctl_streamon, vb2_streamon(vdev->queue, i); vb2_internal_streamon(q, type); list_for_each_entry(vb, &q->queued_list, queued_entry) __enqueue_in_driver(vb); /* 每个vb2_buffer都指向所属的vb2_queue,所有的vb2_buffer通过list_head连接到 vb2_queue缓存队列上 */ struct vb2_queue *q = vb->vb2_queue; vb->state = VB2_BUF_STATE_ACTIVE; /* 调用缓存队列上的ops操作方法集中的buf_queue */ q->ops->buf_queue(vb); 回调static const struct vb2_ops vivi_video_qops = { .buf_queue = buffer_queue, static void buffer_queue(struct vb2_buffer *vb) list_add_tail(&buf->list, &vidq->active); //回到__enqueue_in_driver(vb);下面 ret = vb2_start_streaming(q); ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count)); /* 宏解析,就是调用 vb2_queue中的ops中的start_streaming函数*/ #define call_qop(q, op, args...) \ (((q)->ops->op) ? ((q)->ops->op(args)) : 0) //回调vb2_queue中的ops中的start_streaming函数 static const struct vb2_ops vivi_video_qops = { .start_streaming = start_streaming, static int start_streaming(struct vb2_queue *vq, unsigned int count) vivi_start_generating(dev); /* 创建内核线程 */ dma_q->kthread = kthread_run(vivi_thread, dev, "%s",dev->v4l2_dev.name); 跳到内核线程 static int vivi_thread(void *data) vivi_sleep(dev); //计算超时时间 timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); vivi_thread_tick(dev); /* 从vivi_dev中的vivi_dmaqueue中取出一个激活的vivi_buffer */ buf = list_entry(dma_q->active.next, struct vivi_buffer, list); /* 然后从vivi_dmaqueue队列中删除刚才的那个vivi_buffer */ list_del(&buf->list); /* Fill buffer */ vivi_fillbuff(dev, buf); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); /* 将vb2_buffer加入到vb2_queue的完成链表中 */ list_add_tail(&vb->done_entry, &q->done_list); /* 唤醒因为读完成队列中的数据为空时而休眠的阻塞 */ wake_up(&q->done_wq); static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; const char * const name; union { u32 offset; int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *p); } u; void (*debug)(const void *arg, bool write_only); }; static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)), IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0), IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)), IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)), IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)), IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL), IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)), IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0), IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0), IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0), IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)), IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)), IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)), IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0), IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0), IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), }; static const struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, .release = video_device_release_empty, }; static const struct file_operations v4l2_fops = { .owner = THIS_MODULE, .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l2_compat_ioctl32, #endif .release = v4l2_release, .poll = v4l2_poll, .llseek = no_llseek, }; /* 视频缓存buf是由缓存队列来管理的 */ struct vb2_queue { enum v4l2_buf_type type; unsigned int io_modes; unsigned int io_flags; struct mutex *lock; struct v4l2_fh *owner; /* 操作buf的方法集 */ const struct vb2_ops *ops; /* 分配缓存的操作方法集 */ const struct vb2_mem_ops *mem_ops; void *drv_priv; unsigned int buf_struct_size; u32 timestamp_type; gfp_t gfp_flags; /* private: internal use only */ enum v4l2_memory memory; struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; unsigned int num_buffers; struct list_head queued_list; atomic_t queued_count; struct list_head done_list; spinlock_t done_lock; wait_queue_head_t done_wq; void *alloc_ctx[VIDEO_MAX_PLANES]; unsigned int plane_sizes[VIDEO_MAX_PLANES]; unsigned int streaming:1; unsigned int retry_start_streaming:1; unsigned int waiting_for_buffers:1; struct vb2_fileio_data *fileio; };