Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3226510
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: LINUX

2014-08-05 14:10:23

/*************************************************************************************************************************************/
/* v4l2-dev.h */

/*
 *
 * V 4 L 2   D R I V E R   H E L P E R   A P I
 *
 * Moved from videodev2.h
 *
 * Some commonly needed functions for drivers (v4l2-common.o module)
 */
#ifndef _V4L2_DEV_H
#define _V4L2_DEV_H

#include
#include
#include
#include
#include
#include

#define VIDEO_MAJOR 81  /* 主设备号 */

/* struct video_device *vdev->vfl_type 的取值*/
#define VFL_TYPE_GRABBER 0   /* 表示一个图像采集设备--包括摄像头、调谐器等*/
#define VFL_TYPE_VBI  1   /* 从视频消隐时间段获取信息的设备 */
#define VFL_TYPE_RADIO  2   /* 无线电设备 */
#define VFL_TYPE_VTX  3   /* 视传设备 */
#define VFL_TYPE_MAX  4   /* 视频类型最大值 */

struct v4l2_ioctl_callbacks;
struct video_device;
struct v4l2_device;

/* Flag to mark the video_device struct as unregistered.
   Drivers can set this flag if they want to block all future
   device access. It is set by video_unregister_device. */
#define V4L2_FL_UNREGISTERED (0)

struct v4l2_file_operations {  /* 视频设备操作函数集 */
 struct module *owner;  /* 初始化为THIS_MODULE */
 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  /* 读设备 */
 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); /*写设备 */
 unsigned int (*poll) (struct file *, struct poll_table_struct *); /* 支持poll机制 */
 long (*ioctl) (struct file *, unsigned int, unsigned long);  /* ioctl */
 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); /* 无锁版本的ioctl */
 unsigned long (*get_unmapped_area) (struct file *, unsigned long,
    unsigned long, unsigned long, unsigned long);/* 取消映射 */
 int (*mmap) (struct file *, struct vm_area_struct *);  /* 映射 */ 
 int (*open) (struct file *); /* 打开 */
 int (*release) (struct file *); /* 关闭 */
};

/*
 * Newer version of video_device, handled by videodev2.c
 *  This version moves redundant code from video device code to
 * the common handler
 */

struct video_device  /* 视频设备描述结构体 */
{
 /* device ops */
 const struct v4l2_file_operations *fops;  /* 视频设备操作函数 */

 /* sysfs */
 struct device dev;  /* v4l device */  /* 核心层的设备 */
 struct cdev *cdev;  /* character device */ /* 字符设备 */

 /* Set either parent or v4l2_dev if your driver uses v4l2_device */
 struct device *parent;  /* device parent */ /* 父设备 */
 struct v4l2_device *v4l2_dev; /* v4l2_device parent */

 /* device info */
 char name[32];  /* 名字 */
 int vfl_type;/* 类型,取值为宏VFL_TYPE_* */
 /* 'minor' is set to -1 if the registration failed */
 int minor; /* 次设备号,通常初始化为-1 ,这样会让v4l2子系统在注册时自动分配一个次设备号*/
 u16 num;  /*第几个设备 */
 /* use bitops to set/clear/test flags */
 unsigned long flags; /*标志位 */
 /* attribute to differentiate multiple indices on one physical device */
 int index;  /* 引索 号*/

 int debug;   /* Activates debug level*/

 /* Video standard vars */
 v4l2_std_id tvnorms;  /* Supported tv norms */  /*支持视频标准 */
 v4l2_std_id current_norm; /* Current tvnorm */ /* 当前视频标准 */

 /* callbacks */
 void (*release)(struct video_device *vdev); /* 释放函数(回调) ,必须初始化,这个函数通常包含一个简单的kfree()调用*/

 /* ioctl callbacks */
 const struct v4l2_ioctl_ops *ioctl_ops; /* ioctl回调,主要用于配置视频设备 */
};

/* dev to video-device */
#define to_video_device(cd) container_of(cd, struct video_device, dev)

/* Register video devices. Note that if video_register_device fails,
   the release() callback of the video_device structure is *not* called, so
   the caller is responsible for freeing any data. Usually that means that
   you call video_device_release() on failure.

   Also note that vdev->minor is set to -1 if the registration failed. */
int __must_check video_register_device(struct video_device *vdev, int type, int nr);
int __must_check video_register_device_index(struct video_device *vdev,
      int type, int nr, int index);

/* Unregister video devices. Will do nothing if vdev == NULL or
   vdev->minor < 0. */
void video_unregister_device(struct video_device *vdev);

/* helper functions to alloc/release struct video_device, the
   latter can also be used for video_device->release(). */
struct video_device * __must_check video_device_alloc(void);

/* this release function frees the vdev pointer */
void video_device_release(struct video_device *vdev);

/* this release function does nothing, use when the video_device is a
   static global struct. Note that having a static video_device is
   a dubious construction at best. */
void video_device_release_empty(struct video_device *vdev);

/* helper functions to access driver private data. */
static inline void *video_get_drvdata(struct video_device *vdev)
{
 return dev_get_drvdata(&vdev->dev);  /* return vdev->dev->dev->driver_data */
}

static inline void video_set_drvdata(struct video_device *vdev, void *data)
{
 dev_set_drvdata(&vdev->dev, data);
}

struct video_device *video_devdata(struct file *file);

/* Combine video_get_drvdata and video_devdata as this is
   used very often. */
   /* 获取私有数据 */
static inline void *video_drvdata(struct file *file)
{
 return video_get_drvdata(video_devdata(file));
}

/* 测试视频设备是否已经注销 */
static inline int video_is_unregistered(struct video_device *vdev)
{
 return test_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
}

#endif /* _V4L2_DEV_H *//*************************************************************************************************************************************/
/* v4l2-device.h */

/*
    V4L2 device support header.

    Copyright (C) 2008  Hans Verkuil <>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _V4L2_DEVICE_H
#define _V4L2_DEVICE_H

#include

/* Each instance of a V4L2 device should create the v4l2_device struct,
   either stand-alone or embedded in a larger struct.

   It allows easy access to sub-devices (see v4l2-subdev.h) and provides
   basic V4L2 device-level support.
 */

#define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16)

struct v4l2_device {  /* v4l2设备描述结构体 */
 /* dev->driver_data points to this struct.
    Note: dev might be NULL if there is no parent device
    as is the case with e.g. ISA devices. */
 struct device *dev;  /* 设备模型核心的设备 */
 /* used to keep track of the registered subdevs */
 struct list_head subdevs;  /* 子设备链表 */
 /* lock this struct; can be used by the driver as well if this
    struct is embedded into a larger struct. */
 spinlock_t lock;
 /* unique device name, by default the driver name + bus ID */
 char name[V4L2_DEVICE_NAME_SIZE];  /* 设备的名字 */
 /* notify callback called by some sub-devices. */
 void (*notify)(struct v4l2_subdev *sd,
   unsigned int notification, void *arg);  /* 通知回调函数 */
};

/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
   dev may be NULL in rare cases (ISA devices). In that case you
   must fill in the v4l2_dev->name field before calling this function. */
int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
/* Set v4l2_dev->dev to NULL. Call when the USB parent disconnects.
   Since the parent disappears this ensures that v4l2_dev doesn't have an
   invalid parent pointer. */
void v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
/* Unregister all sub-devices and any other resources related to v4l2_dev. */
void v4l2_device_unregister(struct v4l2_device *v4l2_dev);

/* Register a subdev with a v4l2 device. While registered the subdev module
   is marked as in-use. An error is returned if the module is no longer
   loaded when you attempt to register it. */
int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
      struct v4l2_subdev *sd);
/* Unregister a subdev with a v4l2 device. Can also be called if the subdev
   wasn't registered. In that case it will do nothing. */
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);

/* Iterate over all subdevs. */
#define v4l2_device_for_each_subdev(sd, v4l2_dev)   \
 list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)

/* Call the specified callback for all subdevs matching the condition.
   Ignore any errors. Note that you cannot add or delete a subdev
   while walking the subdevs list. */
#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...)  \
 do {         \
  struct v4l2_subdev *sd;     \
         \
  list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)    \
   if ((cond) && sd->ops->o && sd->ops->o->f)  \
    sd->ops->o->f(sd , ##args);   \
 } while (0)

/* Call the specified callback for all subdevs matching the condition.
   If the callback returns an error other than 0 or -ENOIOCTLCMD, then
   return with that error code. Note that you cannot add or delete a
   subdev while walking the subdevs list. */
#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \
({          \
 struct v4l2_subdev *sd;      \
 long err = 0;        \
         \
 list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) {   \
  if ((cond) && sd->ops->o && sd->ops->o->f)   \
   err = sd->ops->o->f(sd , ##args);   \
  if (err && err != -ENOIOCTLCMD)    \
   break;       \
 }         \
 (err == -ENOIOCTLCMD) ? 0 : err;     \
})

/* Call the specified callback for all subdevs matching grp_id (if 0, then
   match them all). Ignore any errors. Note that you cannot add or delete
   a subdev while walking the subdevs list. */
#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...)   \
 __v4l2_device_call_subdevs(v4l2_dev,     \
   !(grpid) || sd->grp_id == (grpid), o, f , ##args)

/* Call the specified callback for all subdevs matching grp_id (if 0, then
   match them all). If the callback returns an error other than 0 or
   -ENOIOCTLCMD, then return with that error code. Note that you cannot
   add or delete a subdev while walking the subdevs list. */
#define v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args...)  \
 __v4l2_device_call_subdevs_until_err(v4l2_dev,   \
         !(grpid) || sd->grp_id == (grpid), o, f , ##args)

#endif

/*************************************************************************************************************************************/
/* v4l2-ioctl.h */

/*
 *
 * V 4 L 2   D R I V E R   H E L P E R   A P I
 *
 * Moved from videodev2.h
 *
 * Some commonly needed functions for drivers (v4l2-common.o module)
 */
#ifndef _V4L2_IOCTL_H
#define _V4L2_IOCTL_H

#include
#include
#include
#include
#include /* need __user */
#ifdef CONFIG_VIDEO_V4L1_COMPAT
#define __MIN_V4L1
#include
#else
#include
#endif

struct v4l2_ioctl_ops {  /* ioctl回调函数集,用于配置视频设备 */
 /* ioctl callbacks */

 /* VIDIOC_QUERYCAP handler */
 /* 查询驱动功能,看看设备具有什么功能 ,对应命令VIDIOC_QUERYCAP*/
 int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);

 /* Priority handling */
 int (*vidioc_g_priority)   (struct file *file, void *fh,
        enum v4l2_priority *p);
 int (*vidioc_s_priority)   (struct file *file, void *fh,
        enum v4l2_priority p);

 /* VIDIOC_ENUM_FMT handlers */
  /* 获取当前视频支持的视频格式,对应命令VIDIOC_ENUM_FMT */
 int (*vidioc_enum_fmt_vid_cap)     (struct file *file, void *fh,
         struct v4l2_fmtdesc *f); /* 捕获视频 */
 int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh,
         struct v4l2_fmtdesc *f);
 int (*vidioc_enum_fmt_vid_out)     (struct file *file, void *fh,
         struct v4l2_fmtdesc *f); /* 输出视频 */
 int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
         struct v4l2_fmtdesc *f);

 /* VIDIOC_G_FMT handlers */
  /* 读取当前驱动的视频捕获格式 ,对应命令VIDIOC_G_FMT */
 int (*vidioc_g_fmt_vid_cap)    (struct file *file, void *fh,
     struct v4l2_format *f); /* 捕获视频 */
 int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_vid_out)    (struct file *file, void *fh,
     struct v4l2_format *f); /* 输出视频 */
 int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_vbi_cap)    (struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_vbi_out)    (struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
     struct v4l2_format *f);

 /* VIDIOC_S_FMT handlers */
 /* 配置当前驱动的视频捕获格式,对应命令 VIDIOC_S_FMT*/
 int (*vidioc_s_fmt_vid_cap)    (struct file *file, void *fh,
     struct v4l2_format *f); /* 捕获视频 */
 int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_vid_out)    (struct file *file, void *fh,
     struct v4l2_format *f);/* 输出视频 */
 int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_vbi_cap)    (struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_vbi_out)    (struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
     struct v4l2_format *f);
 int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
     struct v4l2_format *f);

 /* VIDIOC_TRY_FMT handlers */
 /* 验证当前驱动的显示格式 ,对应命令VIDIOC_TRY_FMT*/
 int (*vidioc_try_fmt_vid_cap)    (struct file *file, void *fh,
       struct v4l2_format *f);/* 捕获视频 */
 int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_vid_out)    (struct file *file, void *fh,
       struct v4l2_format *f);/* 输出视频 */
 int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_vbi_cap)    (struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_vbi_out)    (struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
       struct v4l2_format *f);
 int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
       struct v4l2_format *f);

 /* Buffer handlers */
 /* 向驱动申请缓冲区,一般不超过5个 ,对应命令VIDIOC_REQBUFS */
 int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b);

 /* 查询已经分配的v4l2的视频缓冲区的相关信息,包括视频缓冲
            区的使用状态、在内核空间的偏移地址、缓冲区长度等(应用
            程序通过该调用来获取内核的视频缓冲区信息,然后调用mmap
            把内核空间地址映射到用户空间),对应命令VIDIOC_QUERYBUF*/
 int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b);

 /*投放申请到的视频缓冲区到视频输入队列中,对应命令VIDIOC_QBUF*/
 int (*vidioc_qbuf)    (struct file *file, void *fh, struct v4l2_buffer *b);

 /*从视频缓冲输出队列获取一个保存有视频数据的视频缓冲区 ,对应命令VIDIOC_DQBUF*/
 int (*vidioc_dqbuf)   (struct file *file, void *fh, struct v4l2_buffer *b);


 int (*vidioc_overlay) (struct file *file, void *fh, unsigned int i);
#ifdef CONFIG_VIDEO_V4L1_COMPAT
   /* buffer type is struct vidio_mbuf * */
 int (*vidiocgmbuf)  (struct file *file, void *fh, struct video_mbuf *p);
#endif
 int (*vidioc_g_fbuf)   (struct file *file, void *fh,
    struct v4l2_framebuffer *a);
 int (*vidioc_s_fbuf)   (struct file *file, void *fh,
    struct v4l2_framebuffer *a);

  /* Stream on/off */
  /* 启动视频采集,应用程序调用VIDIOC_STREAMON启动视频采集命令后,
             视频设备驱动程序开始采集视频数据,并把采集到的视频数据保存到
             视频驱动的视频缓冲区中 ,对应命令VIDIOC_STREAMON*/ 
 int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i);  /* 开始传输 */

  /* 停止视频的采集 ,对应命令VIDIOC_STREAMOFF*/ 
 int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i);   /* 停止传输 */

  /* Standard handling
   ENUMSTD is handled by videodev.c
   */
 /* 用来获取现在视频设备哪种视频标准是激活的(也就是video_device的current_norm字段的值 ),对应命令VIDIOC_G_STD*/
 int (*vidioc_g_std) (struct file *file, void *fh, v4l2_std_id *norm);   /* 用于获取视频设备正在使用的视频标准 */

  /* 用来设置视频标准,对应命令VIDIOC_S_STD */
 int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm);  /* 用于设置视频标准 */

 /* 检查当前视频设备支持的标准例如PAL或NTSC (在亚洲,一般使用
            PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480)),对应命令VIDIOC_QUERYSTD*/
 int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a);

  /* Input handling */
  /* 用于查询设备支持的所有可用的输入,对应命令VIDIOC_ENUMINPUT */
 int (*vidioc_enum_input)(struct file *file, void *fh,
     struct v4l2_input *inp);

 /* 获取设备的输入通道 */
 int (*vidioc_g_input)   (struct file *file, void *fh, unsigned int *i);  /* */

 /* 配置输入通道,采用struct struct v4l2_input结构,针对多通道设备,对应命令VIDIOC_S_INPUT */
 int (*vidioc_s_input)   (struct file *file, void *fh, unsigned int i); /* */

  /* Output handling */
 int (*vidioc_enum_output) (struct file *file, void *fh,
      struct v4l2_output *a);
 int (*vidioc_g_output)   (struct file *file, void *fh, unsigned int *i);
 int (*vidioc_s_output)   (struct file *file, void *fh, unsigned int i);

  /* Control handling */
  /* 查询控制 ,对应命令VIDIOC_QUERYCTRL*/
 int (*vidioc_queryctrl)        (struct file *file, void *fh,
     struct v4l2_queryctrl *a);
   /* 获取控制 */
 int (*vidioc_g_ctrl)           (struct file *file, void *fh,
     struct v4l2_control *a);
  /* 设置控制  */
 int (*vidioc_s_ctrl)           (struct file *file, void *fh,
     struct v4l2_control *a);
 int (*vidioc_g_ext_ctrls)      (struct file *file, void *fh,
     struct v4l2_ext_controls *a);
 int (*vidioc_s_ext_ctrls)      (struct file *file, void *fh,
     struct v4l2_ext_controls *a);
 int (*vidioc_try_ext_ctrls)    (struct file *file, void *fh,
     struct v4l2_ext_controls *a);
 int (*vidioc_querymenu)        (struct file *file, void *fh,
     struct v4l2_querymenu *a);

 /* Audio ioctls */
 int (*vidioc_enumaudio)        (struct file *file, void *fh,
     struct v4l2_audio *a);
 int (*vidioc_g_audio)          (struct file *file, void *fh,
     struct v4l2_audio *a);
 int (*vidioc_s_audio)          (struct file *file, void *fh,
     struct v4l2_audio *a);

 /* Audio out ioctls */
 int (*vidioc_enumaudout)       (struct file *file, void *fh,
     struct v4l2_audioout *a);
 int (*vidioc_g_audout)         (struct file *file, void *fh,
     struct v4l2_audioout *a);
 int (*vidioc_s_audout)         (struct file *file, void *fh,
     struct v4l2_audioout *a);
 int (*vidioc_g_modulator)      (struct file *file, void *fh,
     struct v4l2_modulator *a);
 int (*vidioc_s_modulator)      (struct file *file, void *fh,
     struct v4l2_modulator *a);
 /* Crop ioctls */
  /* 查询驱动的修剪能力 */
 int (*vidioc_cropcap)          (struct file *file, void *fh,
     struct v4l2_cropcap *a);
 /* 读取视频信号的边框(窗口参数) */
 int (*vidioc_g_crop)           (struct file *file, void *fh,
     struct v4l2_crop *a);
 /* 配置视频信号的边框 (窗口参数) */
 int (*vidioc_s_crop)           (struct file *file, void *fh,
     struct v4l2_crop *a);
 /* Compression ioctls */
 int (*vidioc_g_jpegcomp)       (struct file *file, void *fh,
     struct v4l2_jpegcompression *a);
 int (*vidioc_s_jpegcomp)       (struct file *file, void *fh,
     struct v4l2_jpegcompression *a);
 int (*vidioc_g_enc_index)      (struct file *file, void *fh,
     struct v4l2_enc_idx *a);
 int (*vidioc_encoder_cmd)      (struct file *file, void *fh,
     struct v4l2_encoder_cmd *a);
 int (*vidioc_try_encoder_cmd)  (struct file *file, void *fh,
     struct v4l2_encoder_cmd *a);

 /* Stream type-dependent parameter ioctls */
 int (*vidioc_g_parm)           (struct file *file, void *fh,
     struct v4l2_streamparm *a);
 int (*vidioc_s_parm)           (struct file *file, void *fh,
     struct v4l2_streamparm *a);

 /* Tuner ioctls */
 int (*vidioc_g_tuner)          (struct file *file, void *fh,
     struct v4l2_tuner *a);
 int (*vidioc_s_tuner)          (struct file *file, void *fh,
     struct v4l2_tuner *a);
 int (*vidioc_g_frequency)      (struct file *file, void *fh,
     struct v4l2_frequency *a);
 int (*vidioc_s_frequency)      (struct file *file, void *fh,
     struct v4l2_frequency *a);

 /* Sliced VBI cap */
 int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh,
     struct v4l2_sliced_vbi_cap *a);

 /* Log status ioctl */
 int (*vidioc_log_status)       (struct file *file, void *fh);

 int (*vidioc_s_hw_freq_seek)   (struct file *file, void *fh,
     struct v4l2_hw_freq_seek *a);

 /* Debugging ioctls */
#ifdef CONFIG_VIDEO_ADV_DEBUG
 int (*vidioc_g_register)       (struct file *file, void *fh,
     struct v4l2_dbg_register *reg);
 int (*vidioc_s_register)       (struct file *file, void *fh,
     struct v4l2_dbg_register *reg);
#endif
 int (*vidioc_g_chip_ident)     (struct file *file, void *fh,
     struct v4l2_dbg_chip_ident *chip);

 int (*vidioc_enum_framesizes)   (struct file *file, void *fh,
      struct v4l2_frmsizeenum *fsize);

 int (*vidioc_enum_frameintervals) (struct file *file, void *fh,
        struct v4l2_frmivalenum *fival);

 /* For other private ioctls */
 long (*vidioc_default)        (struct file *file, void *fh,
     int cmd, void *arg);
};


/* v4l debugging and diagnostics */

/* Debug bitmask flags to be used on V4L2 */
#define V4L2_DEBUG_IOCTL     0x01
#define V4L2_DEBUG_IOCTL_ARG 0x02

/* Use this macro for non-I2C drivers. Pass the driver name as the first arg. */
#define v4l_print_ioctl(name, cmd)     \
 do {        \
  printk(KERN_DEBUG "%s: ", name); \
  v4l_printk_ioctl(cmd);   \
 } while (0)

/* Use this macro in I2C drivers where 'client' is the struct i2c_client
   pointer */
#define v4l_i2c_print_ioctl(client, cmd)      \
 do {              \
  v4l_client_printk(KERN_DEBUG, client, ""); \
  v4l_printk_ioctl(cmd);      \
 } while (0)

/*  Video standard functions  */
extern const char *v4l2_norm_to_name(v4l2_std_id id);
extern void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod);
extern int v4l2_video_std_construct(struct v4l2_standard *vs,
        int id, const char *name);
/* Prints the ioctl in a human-readable format */
extern void v4l_printk_ioctl(unsigned int cmd);

/* names for fancy debug output */
extern const char *v4l2_field_names[];
extern const char *v4l2_type_names[];

/*  Compatibility layer interface  --  v4l1-compat module */
typedef long (*v4l2_kioctl)(struct file *file,
      unsigned int cmd, void *arg);
#ifdef CONFIG_VIDEO_V4L1_COMPAT
long v4l_compat_translate_ioctl(struct file *file,
          int cmd, void *arg, v4l2_kioctl driver_ioctl);
#else
#define v4l_compat_translate_ioctl(file, cmd, arg, ioctl) (-EINVAL)
#endif

#ifdef CONFIG_COMPAT
/* 32 Bits compatibility layer for 64 bits processors */
extern long v4l2_compat_ioctl32(struct file *file, unsigned int cmd,
    unsigned long arg);
#endif

/* Include support for obsoleted stuff */
extern long video_usercopy(struct file *file, unsigned int cmd,
    unsigned long arg, v4l2_kioctl func);

/* Standard handlers for V4L ioctl's */
extern long video_ioctl2(struct file *file,
   unsigned int cmd, unsigned long arg);

#endif /* _V4L2_IOCTL_H */
/*************************************************************************************************************************************/
/* v4l2-subdev.h */

/*
    V4L2 sub-device support header.

    Copyright (C) 2008  Hans Verkuil <>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _V4L2_SUBDEV_H
#define _V4L2_SUBDEV_H

#include

struct v4l2_device;
struct v4l2_subdev;
struct tuner_setup;

/* decode_vbi_line */
struct v4l2_decode_vbi_line {
 u32 is_second_field; /* Set to 0 for the first (odd) field,
       set to 1 for the second (even) field. */
 u8 *p;    /* Pointer to the sliced VBI data from the decoder.
       On exit points to the start of the payload. */
 u32 line;  /* Line number of the sliced VBI data (1-23) */
 u32 type;  /* VBI service type (V4L2_SLICED_*). 0 if no service found */
};

/* Sub-devices are devices that are connected somehow to the main bridge
   device. These devices are usually audio/video muxers/encoders/decoders or
   sensors and webcam controllers.

   Usually these devices are controlled through an i2c bus, but other busses
   may also be used.

   The v4l2_subdev struct provides a way of accessing these devices in a
   generic manner. Most operations that these sub-devices support fall in
   a few categories: core ops, audio ops, video ops and tuner ops.

   More categories can be added if needed, although this should remain a
   limited set (no more than approx. 8 categories).

   Each category has its own set of ops that subdev drivers can implement.

   A subdev driver can leave the pointer to the category ops NULL if
   it does not implement them (e.g. an audio subdev will generally not
   implement the video category ops). The exception is the core category:
   this must always be present.

   These ops are all used internally so it is no problem to change, remove
   or add ops or move ops from one to another category. Currently these
   ops are based on the original ioctls, but since ops are not limited to
   one argument there is room for improvement here once all i2c subdev
   drivers are converted to use these ops.
 */

/* Core ops: it is highly recommended to implement at least these ops:

   g_chip_ident
   log_status
   g_register
   s_register

   This provides basic debugging support.

   The ioctl ops is meant for generic ioctl-like commands. Depending on
   the use-case it might be better to use subdev-specific ops (currently
   not yet implemented) since ops provide proper type-checking.
 */

/* init: initialize the sensor registors to some sort of reasonable default
 values. Do not use for new drivers and should be removed in existing
 drivers.

   load_fw: load firmware.

   reset: generic reset command. The argument selects which subsystems to
 reset. Passing 0 will always reset the whole chip. Do not use for new
 drivers without discussing this first on the linux-media mailinglist.
 There should be no reason normally to reset a device.

   s_gpio: set GPIO pins. Very simple right now, might need to be extended with
 a direction argument if needed.
 */
struct v4l2_subdev_core_ops { /* 子设备核心操作函数集 */
 int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
 int (*log_status)(struct v4l2_subdev *sd);
 int (*init)(struct v4l2_subdev *sd, u32 val);
 int (*load_fw)(struct v4l2_subdev *sd);
 int (*reset)(struct v4l2_subdev *sd, u32 val);
 int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
 int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
 int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
 int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
 int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
 int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
 int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
 int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
 int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
 long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
#ifdef CONFIG_VIDEO_ADV_DEBUG
 int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
 int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
#endif
};

/* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio.

   s_radio: v4l device was opened in Radio mode, to be replaced by s_mode.

   s_type_addr: sets tuner type and its I2C addr.

   s_config: sets tda9887 specific stuff, like port1, port2 and qss

   s_standby: puts tuner on powersaving state, disabling it, except for i2c.
 */
struct v4l2_subdev_tuner_ops {
 int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type);
 int (*s_radio)(struct v4l2_subdev *sd);
 int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
 int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
 int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
 int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
 int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
 int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
 int (*s_standby)(struct v4l2_subdev *sd);
};

/* s_clock_freq: set the frequency (in Hz) of the audio clock output.
 Used to slave an audio processor to the video decoder, ensuring that
 audio and video remain synchronized. Usual values for the frequency
 are 48000, 44100 or 32000 Hz. If the frequency is not supported, then
 -EINVAL is returned.

   s_i2s_clock_freq: sets I2S speed in bps. This is used to provide a standard
 way to select I2S clock used by driving digital audio streams at some
 board designs. Usual values for the frequency are 1024000 and 2048000.
 If the frequency is not supported, then -EINVAL is returned.

   s_routing: used to define the input and/or output pins of an audio chip,
 and any additional configuration data.
 Never attempt to use user-level input IDs (e.g. Composite, S-Video,
 Tuner) at this level. An i2c device shouldn't know about whether an
 input pin is connected to a Composite connector, become on another
 board or platform it might be connected to something else entirely.
 The calling driver is responsible for mapping a user-level input to
 the right pins on the i2c device.
 */
struct v4l2_subdev_audio_ops {  /* v4l2子设备音频操作函数集 */
 int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
 int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
 int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
};

/*
   decode_vbi_line: video decoders that support sliced VBI need to implement
 this ioctl. Field p of the v4l2_sliced_vbi_line struct is set to the
 start of the VBI data that was generated by the decoder. The driver
 then parses the sliced VBI data and sets the other fields in the
 struct accordingly. The pointer p is updated to point to the start of
 the payload which can be copied verbatim into the data field of the
 v4l2_sliced_vbi_data struct. If no valid VBI data was found, then the
 type field is set to 0 on return.

   s_vbi_data: used to generate VBI signals on a video signal.
 v4l2_sliced_vbi_data is filled with the data packets that should be
 output. Note that if you set the line field to 0, then that VBI signal
 is disabled. If no valid VBI data was found, then the type field is
 set to 0 on return.

   g_vbi_data: used to obtain the sliced VBI packet from a readback register.
 Not all video decoders support this. If no data is available because
 the readback register contains invalid or erroneous data -EIO is
 returned. Note that you must fill in the 'id' member and the 'field'
 member (to determine whether CC data from the first or second field
 should be obtained).

   s_std_output: set v4l2_std_id for video OUTPUT devices. This is ignored by
 video input devices.

  s_crystal_freq: sets the frequency of the crystal used to generate the
 clocks in Hz. An extra flags field allows device specific configuration
 regarding clock frequency dividers, etc. If not used, then set flags
 to 0. If the frequency is not supported, then -EINVAL is returned.

   g_input_status: get input status. Same as the status field in the v4l2_input
 struct.

   s_routing: see s_routing in audio_ops, except this version is for video
 devices.
 */
struct v4l2_subdev_video_ops {  /* 子设备视频操作函数集 */
 int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
 int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
 int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);
 int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);
 int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
 int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
 int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
 int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
 int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
 int (*s_stream)(struct v4l2_subdev *sd, int enable);
 int (*enum_fmt)(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmtdesc);
 int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 int (*try_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
 int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
 int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);
 int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
};

struct v4l2_subdev_ops {  /* 子设备操作函数 */
 const struct v4l2_subdev_core_ops  *core;
 const struct v4l2_subdev_tuner_ops *tuner;
 const struct v4l2_subdev_audio_ops *audio;
 const struct v4l2_subdev_video_ops *video;
};

#define V4L2_SUBDEV_NAME_SIZE 32

/* Each instance of a subdev driver should create this struct, either
   stand-alone or embedded in a larger struct.
 */
struct v4l2_subdev { /* v4l2设备的子设备 */
 struct list_head list; /*用于形成链表 */
 struct module *owner;
 struct v4l2_device *v4l2_dev; /* 所属的视频设备 */
 const struct v4l2_subdev_ops *ops; /* 子设备操作函数集 */
 /* name must be unique */
 char name[V4L2_SUBDEV_NAME_SIZE];  /* 名字 */
 /* can be used to group similar subdevs, value is driver-specific */
 u32 grp_id;
 /* pointer to private data */
 void *priv; /* 私有数据 */
};

static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
{
 sd->priv = p;
}

static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd)
{
 return sd->priv;
}

/*初始化v4l2子设备 */
static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
     const struct v4l2_subdev_ops *ops)
{
 INIT_LIST_HEAD(&sd->list);
 /* ops->core MUST be set */
 BUG_ON(!ops || !ops->core);
 sd->ops = ops;
 sd->v4l2_dev = NULL;
 sd->name[0] = '\0';
 sd->grp_id = 0;
 sd->priv = NULL;
}

/* Call an ops of a v4l2_subdev, doing the right checks against
   NULL pointers.

   Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
 */
#define v4l2_subdev_call(sd, o, f, args...)    \
 (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \
  (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))

/* Send a notification to v4l2_device. */
#define v4l2_subdev_notify(sd, notification, arg)      \
 ((!(sd) || !(sd)->v4l2_dev || !(sd)->v4l2_dev->notify) ? -ENODEV : \
  (sd)->v4l2_dev->notify((sd), (notification), (arg)))

#endif
/*************************************************************************************************************************************/
/* videobuf-core.h */

/*
 * generic helper functions for handling video4linux capture buffers
 *
 * (c) 2007 Mauro Carvalho Chehab, <>
 *
 * Highly based on video-buf written originally by:
 * (c) 2001,02 Gerd Knorr <>
 * (c) 2006 Mauro Carvalho Chehab, <>
 * (c) 2006 Ted Walther and John Sokol
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2
 */

#ifndef _VIDEOBUF_CORE_H
#define _VIDEOBUF_CORE_H

#include
#ifdef CONFIG_VIDEO_V4L1_COMPAT
#define __MIN_V4L1
#include
#endif
#include

#define UNSET (-1U)


struct videobuf_buffer;
struct videobuf_queue;

/* --------------------------------------------------------------------- */

/*
 * A small set of helper functions to manage video4linux buffers.
 *
 * struct videobuf_buffer holds the data structures used by the helper
 * functions, additionally some commonly used fields for v4l buffers
 * (width, height, lists, waitqueue) are in there.  That struct should
 * be used as first element in the drivers buffer struct.
 *
 * about the mmap helpers (videobuf_mmap_*):
 *
 * The mmaper function allows to map any subset of contingous buffers.
 * This includes one mmap() call for all buffers (which the original
 * video4linux API uses) as well as one mmap() for every single buffer
 * (which v4l2 uses).
 *
 * If there is a valid mapping for a buffer, buffer->baddr/bsize holds
 * userspace address + size which can be feeded into the
 * videobuf_dma_init_user function listed above.
 *
 */

struct videobuf_mapping { /* 视频缓冲区映射 */
 unsigned int count;   /* 映射计数 */
 unsigned long start;  /* 起始地址,=struct vm_area_struct *vma->vm_start */
 unsigned long end;   /* 结束地址 ,=struct vm_area_struct *vma->vm_end*/
 struct videobuf_queue *q;  /* 指向视频缓冲区队列 */
};

enum videobuf_state {  /* 视频缓冲区状态 */
 VIDEOBUF_NEEDS_INIT = 0,  /* 需要初始化 */
 VIDEOBUF_PREPARED   = 1,   /* 准备好的 */
 VIDEOBUF_QUEUED     = 2,    /* 在队列中 */
 VIDEOBUF_ACTIVE     = 3,     /* 正在使用 */
 VIDEOBUF_DONE       = 4,      /* 使用完成 */
 VIDEOBUF_ERROR      = 5,     /* 出现错误 */
 VIDEOBUF_IDLE       = 6,       /* 空闲 */
};

struct videobuf_buffer {  /* 视频缓冲区 */
 unsigned int            i;    /* 缓冲区引索----第几个缓冲区 */
 u32                     magic;

 /* info about the buffer */
 unsigned int            width;    /*图像的宽度 */
 unsigned int            height;  /* 图像的高度  */
 unsigned int            bytesperline; /* use only if != 0 */
 unsigned long           size; /* 缓冲区大小 */
 unsigned int            input;
 enum v4l2_field         field; /* 域 */
 enum videobuf_state     state; /* 缓冲区状态 */
 struct list_head        stream;  /* QBUF/DQBUF list */ /*用于形成链表*/

 /* touched by irq handler */
 struct list_head        queue; /* 用于添加到队列*/
 wait_queue_head_t       done;  /* 等待队列 */
 unsigned int            field_count;  /* 域计数 */
 struct timeval          ts;  /* 时间 */

 /* Memory type */
 enum v4l2_memory        memory; /*内存类型 */

 /* buffer size */
 size_t                  bsize; /* buff大小 */

 /* buffer offset (mmap + overlay) */
 size_t                  boff; /* buffer偏移 */

 /* buffer addr (userland ptr!) */
 unsigned long           baddr; /* buffer地址 */

 /* for mmap'ed buffers */
 struct videobuf_mapping *map; /*  buffer映射*/

 /* Private pointer to allow specific methods to store their data */
  int   privsize; /* 私有数据大小 */
 void                    *priv;  /* 驱动私有数据 */
};

struct videobuf_queue_ops {  /* 视频缓冲区队列操作函数 */
 int (*buf_setup)(struct videobuf_queue *q,
    unsigned int *count, unsigned int *size);  /* 设置缓冲区 */
 int (*buf_prepare)(struct videobuf_queue *q,
      struct videobuf_buffer *vb,
      enum v4l2_field field);  /* 准备缓冲区 */
 void (*buf_queue)(struct videobuf_queue *q,
     struct videobuf_buffer *vb);  /* 添加到队列 */
 void (*buf_release)(struct videobuf_queue *q,
       struct videobuf_buffer *vb);  /* 释放缓冲区 */
};

#define MAGIC_QTYPE_OPS 0x12261003

/* Helper operations - device type dependent */
struct videobuf_qtype_ops { /* 缓冲区帮助函数(依赖于设备类型) */
 u32                     magic; /* 幻数 */

 void *(*alloc)  (size_t size);  /* 分配缓冲区 */
 void *(*vmalloc) (struct videobuf_buffer *buf);  /* 分配缓冲区(vmalloc) */
 int (*iolock)  (struct videobuf_queue* q,
     struct videobuf_buffer *vb,
     struct v4l2_framebuffer *fbuf);  /* IO锁定 */
 int (*mmap)  (struct videobuf_queue *q,
     unsigned int *count,
     unsigned int *size,
     enum v4l2_memory memory);  /* 映射 */
 int (*sync)  (struct videobuf_queue* q,
     struct videobuf_buffer *buf);  /* 同步 */
 int (*video_copy_to_user)(struct videobuf_queue *q,
     char __user *data,
     size_t count,
     int nonblocking);  /* 拷贝到用于空间 */
 int (*copy_stream) (struct videobuf_queue *q,
     char __user *data,
     size_t count,
     size_t pos,
     int vbihack,
     int nonblocking);/* 拷贝流 */
 int (*mmap_free) (struct videobuf_queue *q);   /*释放映射 */
 int (*mmap_mapper) (struct videobuf_queue *q,
    struct vm_area_struct *vma); /* mmap映射 */
};

struct videobuf_queue {  /* 视频缓冲区队列 */
 struct mutex               vb_lock;
 spinlock_t                 *irqlock;
 struct device     *dev;

 wait_queue_head_t    wait; /* wait if queue is empty */ /* 等待队列(用于等待队列为空) */

 enum v4l2_buf_type         type;  /* 缓冲区类型 */
 unsigned int               inputs; /* for V4L2_BUF_FLAG_INPUT */ /* 输入,=V4L2_BUF_FLAG_INPUT  */
 unsigned int               msize; /* 大小 */
 enum v4l2_field            field; /* 域 */
 enum v4l2_field            last;   /* for field=V4L2_FIELD_ALTERNATE *//* =V4L2_FIELD_ALTERNATE */
 struct videobuf_buffer     *bufs[VIDEO_MAX_FRAME];/* 指针数组,分别指向每个缓冲区 */
 struct videobuf_queue_ops  *ops;/* 队列操作函数集 */
 struct videobuf_qtype_ops  *int_ops;/* 缓冲区初始化函数集 */

 unsigned int               streaming:1;/* 是否有视频流 */
 unsigned int               reading:1;/* 是否正在读 */
 unsigned int     is_mmapped:1;/* 是否已经映射 */

 /* capture via mmap() + ioctl(QBUF/DQBUF) */
 struct list_head           stream;/* 用于指向流形成的链表的头 */

 /* capture via read() */
 unsigned int               read_off;/* 读偏移 */
 struct videobuf_buffer     *read_buf;/* 读缓冲区 */

 /* driver private data */
 void                       *priv_data;/*驱动私有数据  */
};

int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);
int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,
  struct v4l2_framebuffer *fbuf);

void *videobuf_alloc(struct videobuf_queue* q);

/* Used on videobuf-dvb */
void *videobuf_queue_to_vmalloc (struct videobuf_queue* q,
     struct videobuf_buffer *buf);

void videobuf_queue_core_init(struct videobuf_queue *q,
    struct videobuf_queue_ops *ops,
    struct device *dev,
    spinlock_t *irqlock,
    enum v4l2_buf_type type,
    enum v4l2_field field,
    unsigned int msize,
    void *priv,
    struct videobuf_qtype_ops *int_ops);
int  videobuf_queue_is_busy(struct videobuf_queue *q);
void videobuf_queue_cancel(struct videobuf_queue *q);

enum v4l2_field videobuf_next_field(struct videobuf_queue *q);
int videobuf_reqbufs(struct videobuf_queue *q,
       struct v4l2_requestbuffers *req);
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b);
int videobuf_qbuf(struct videobuf_queue *q,
    struct v4l2_buffer *b);
int videobuf_dqbuf(struct videobuf_queue *q,
     struct v4l2_buffer *b, int nonblocking);
#ifdef CONFIG_VIDEO_V4L1_COMPAT
int videobuf_cgmbuf(struct videobuf_queue *q,
      struct video_mbuf *mbuf, int count);
#endif
int videobuf_streamon(struct videobuf_queue *q);
int videobuf_streamoff(struct videobuf_queue *q);

void videobuf_stop(struct videobuf_queue *q);

int videobuf_read_start(struct videobuf_queue *q);
void videobuf_read_stop(struct videobuf_queue *q);
ssize_t videobuf_read_stream(struct videobuf_queue *q,
        char __user *data, size_t count, loff_t *ppos,
        int vbihack, int nonblocking);
ssize_t videobuf_read_one(struct videobuf_queue *q,
     char __user *data, size_t count, loff_t *ppos,
     int nonblocking);
unsigned int videobuf_poll_stream(struct file *file,
      struct videobuf_queue *q,
      poll_table *wait);

int videobuf_mmap_setup(struct videobuf_queue *q,
   unsigned int bcount, unsigned int bsize,
   enum v4l2_memory memory);
int __videobuf_mmap_setup(struct videobuf_queue *q,
   unsigned int bcount, unsigned int bsize,
   enum v4l2_memory memory);
int videobuf_mmap_free(struct videobuf_queue *q);
int videobuf_mmap_mapper(struct videobuf_queue *q,
    struct vm_area_struct *vma);

#endif
/*************************************************************************************************************************************/
/* videobuf-vmalloc.h */

/*
 * helper functions for vmalloc capture buffers
 *
 * The functions expect the hardware being able to scatter gather
 * (i.e. the buffers are not linear in physical memory, but fragmented
 * into PAGE_SIZE chunks).  They also assume the driver does not need
 * to touch the video data.
 *
 * (c) 2007 Mauro Carvalho Chehab, <>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2
 */
#ifndef _VIDEOBUF_VMALLOC_H
#define _VIDEOBUF_VMALLOC_H

#include

/* --------------------------------------------------------------------- */

struct videobuf_vmalloc_memory  /* vmalloc内存 */
{
 u32                 magic;  /* 幻数 */

 void                *vmalloc;  /* 虚拟地址 */

 /* remap_vmalloc_range seems to need to run after mmap() on some cases */
 struct vm_area_struct *vma; /* 指向用户空间内存 */
};

void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
    struct videobuf_queue_ops *ops,
    void *dev,
    spinlock_t *irqlock,
    enum v4l2_buf_type type,
    enum v4l2_field field,
    unsigned int msize,
    void *priv);

void *videobuf_to_vmalloc (struct videobuf_buffer *buf);

void videobuf_vmalloc_free (struct videobuf_buffer *buf);

#endif
/*************************************************************************************************************************************/
/* videodev2.h */

/*
 *  Video for Linux Two header file
 *
 *  Copyright (C) 1999-2007 the contributors
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Alternatively you can redistribute this file under the terms of the
 *  BSD license as stated below:
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. The names of its contributors may not be used to endorse or promote
 *     products derived from this software without specific prior written
 *     permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Header file for v4l or V4L2 drivers and applications
 * with public API.
 * All kernel-specific stuff were moved to media/v4l2-dev.h, so
 * no #if __KERNEL tests are allowed here
 *
 * See for more info
 *
 * Author: Bill Dirks <>
 *  Justin Schoeman
 *              Hans Verkuil <>
 *  et al.
 */
#ifndef __LINUX_VIDEODEV2_H
#define __LINUX_VIDEODEV2_H

#ifdef __KERNEL__
#include      /* need struct timeval */
#else
#include
#endif
#include
#include
#include

/*
 * Common stuff for both V4L1 and V4L2
 * Moved from videodev.h
 */
#define VIDEO_MAX_FRAME               32  /* 最大帧数 */

#ifndef __KERNEL__

/* These defines are V4L1 specific and should not be used with the V4L2 API!
   They will be removed from this header in the future. */
/* v4L1使用 */
#define VID_TYPE_CAPTURE 1 /* Can capture */
#define VID_TYPE_TUNER  2 /* Can tune */
#define VID_TYPE_TELETEXT 4 /* Does teletext */
#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
#define VID_TYPE_CLIPPING 32 /* Can clip */
#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
#define VID_TYPE_SCALES  128 /* Scalable */
#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */
#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */
#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */
#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */
#endif

/*
 * M I S C E L L A N E O U S
 */

/*  Four-character-code (FOURCC) */
#define v4l2_fourcc(a, b, c, d)\
 ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))

/*
 * E N U M S
 */
enum v4l2_field {  /* 很多图片的源会使数据交错---先传输奇数行,然后使偶数行,真正的摄像头是不会做数据的
                                     交错的,v4l2 API允许应用使用很多种方式交错字段*/
 V4L2_FIELD_ANY           = 0, /* driver can choose from none,
      top, bottom, interlaced
      depending on whatever it thinks
      is approximate ... */  /* 无所谓 */
 V4L2_FIELD_NONE          = 1, /* this device has no fields ... */  /* 字段不交错 */
 V4L2_FIELD_TOP           = 2, /* top field only */  /* 只交错顶部字面 */
 V4L2_FIELD_BOTTOM        = 3, /* bottom field only */  /* 只交错底部字面 */
 V4L2_FIELD_INTERLACED    = 4, /* both fields interlaced */ /* 全部交错 */
 V4L2_FIELD_SEQ_TB        = 5, /* both fields sequential into one
      buffer, top-bottom order */
 V4L2_FIELD_SEQ_BT        = 6, /* same as above + bottom-top order */
 V4L2_FIELD_ALTERNATE     = 7, /* both fields alternating into
      separate buffers */
 V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field
      first and the top field is
      transmitted first */
 V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field
      first and the bottom field is
      transmitted first */
};
#define V4L2_FIELD_HAS_TOP(field) \
 ((field) == V4L2_FIELD_TOP  ||\
  (field) == V4L2_FIELD_INTERLACED ||\
  (field) == V4L2_FIELD_INTERLACED_TB ||\
  (field) == V4L2_FIELD_INTERLACED_BT ||\
  (field) == V4L2_FIELD_SEQ_TB ||\
  (field) == V4L2_FIELD_SEQ_BT)
#define V4L2_FIELD_HAS_BOTTOM(field) \
 ((field) == V4L2_FIELD_BOTTOM  ||\
  (field) == V4L2_FIELD_INTERLACED ||\
  (field) == V4L2_FIELD_INTERLACED_TB ||\
  (field) == V4L2_FIELD_INTERLACED_BT ||\
  (field) == V4L2_FIELD_SEQ_TB ||\
  (field) == V4L2_FIELD_SEQ_BT)
#define V4L2_FIELD_HAS_BOTH(field) \
 ((field) == V4L2_FIELD_INTERLACED ||\
  (field) == V4L2_FIELD_INTERLACED_TB ||\
  (field) == V4L2_FIELD_INTERLACED_BT ||\
  (field) == V4L2_FIELD_SEQ_TB ||\
  (field) == V4L2_FIELD_SEQ_BT)

enum v4l2_buf_type { /* v4L2缓冲区类型 */
 V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,  /* 捕获视频 */
 V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,   /* 视频输出 */
 V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,   /*  */
 V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
 V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
 V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
 V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
#if 1
 /* Experimental */
 V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
#endif
 V4L2_BUF_TYPE_PRIVATE              = 0x80,
};

enum v4l2_ctrl_type {/* v4L2控制类型 */
 V4L2_CTRL_TYPE_INTEGER      = 1,
 V4L2_CTRL_TYPE_BOOLEAN      = 2,
 V4L2_CTRL_TYPE_MENU      = 3,
 V4L2_CTRL_TYPE_BUTTON      = 4,
 V4L2_CTRL_TYPE_INTEGER64     = 5,
 V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
};

enum v4l2_tuner_type {/* v4L2协调器类型 */
 V4L2_TUNER_RADIO      = 1,
 V4L2_TUNER_ANALOG_TV      = 2,
 V4L2_TUNER_DIGITAL_TV      = 3,
};

enum v4l2_memory {  /* v4L2内存类型 */
 V4L2_MEMORY_MMAP             = 1,    /* 通过mmap系统调用映射内核空间的缓冲区到用户空间 */
 V4L2_MEMORY_USERPTR          = 2,  /* 缓冲区在用户空间 */
 V4L2_MEMORY_OVERLAY          = 3,
};

/* see also */
enum v4l2_colorspace {  /* v4L2色域描述结构体*/
 /* ITU-R 601 -- broadcast NTSC/PAL */
 V4L2_COLORSPACE_SMPTE170M     = 1,  /* NTSC或PAL等电视信号的模拟彩色表示方法,电视调谐器通常产生的色域都属于
                                                                      这个色域*/

 /* 1125-Line (US) HDTV */
 V4L2_COLORSPACE_SMPTE240M     = 2,

 /* HD and modern captures. */
 V4L2_COLORSPACE_REC709        = 3,

 /* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
 V4L2_COLORSPACE_BT878         = 4,

 /* These should be useful.  Assume 601 extents. */
 V4L2_COLORSPACE_470_SYSTEM_M  = 5,
 V4L2_COLORSPACE_470_SYSTEM_BG = 6,

 /* I know there will be cameras that send this.  So, this is
  * unspecified chromaticities and full 0-255 on each of the
  * Y'CbCr components
  */
 V4L2_COLORSPACE_JPEG          = 7,  /* JPEG格式 */

 /* For RGB colourspaces, this is probably a good start. */
 V4L2_COLORSPACE_SRGB          = 8,  /* RGB色域 */
};

enum v4l2_priority {  /* 优先级 */
 V4L2_PRIORITY_UNSET       = 0,  /* not initialized */
 V4L2_PRIORITY_BACKGROUND  = 1,
 V4L2_PRIORITY_INTERACTIVE = 2,
 V4L2_PRIORITY_RECORD      = 3,
 V4L2_PRIORITY_DEFAULT     = V4L2_PRIORITY_INTERACTIVE,
};

struct v4l2_rect { /* 视频窗口大小 */
 __s32   left;           /* 表示视频采集区域的起始横坐标 */
 __s32   top;           /* 表示视频采集区域的起始纵坐标 */
 __s32   width;       /* 表示采集图像的宽度*/
 __s32   height;     /* 表示采集图像的高度 */
};

struct v4l2_fract {  /* 设置帧率,通过分母分子实现  */
 __u32   numerator; /* 分子 */
 __u32   denominator; /* 分母 */
};

/*
 * D R I V E R   C A P A B I L I T I E S
 */
struct v4l2_capability { /* 视频设备能力描述结构体 */
 __u8 driver[16]; /* i.e. "bttv" */ /* 驱动名 */
 __u8 card[32]; /* i.e. "Hauppauge WinTV" */  /* card名 */
 __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */  /* 总线信息 */
 __u32   version;        /* should use KERNEL_VERSION() */  /* 版本,可以使用KERNEL_VERSION()函数获取 */
 __u32 capabilities; /* Device capabilities */ /* 视频设备能力,查看宏 V4L2_CAP_*  */
 __u32 reserved[4];
};

/* Values for 'capabilities' field */
/*  struct v4l2_capability结构体的capabilities的取值*/
#define V4L2_CAP_VIDEO_CAPTURE  0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT  0x00000002  /* Is a video output device */
#define V4L2_CAP_VIDEO_OVERLAY  0x00000004  /* Can do video overlay */
#define V4L2_CAP_VBI_CAPTURE  0x00000010  /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT  0x00000020  /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040  /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080  /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE  0x00000100  /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200  /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK  0x00000400  /* Can do hardware frequency seek  */

#define V4L2_CAP_TUNER   0x00010000  /* has a tuner */
#define V4L2_CAP_AUDIO   0x00020000  /* has audio support */
#define V4L2_CAP_RADIO   0x00040000  /* is a radio device */

#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */  /* 视频流操作 */

/*
 * V I D E O   I M A G E   F O R M A T
 */
struct v4l2_pix_format {  /* 像素格式 */
 __u32           width;  /*  图片的宽度,以像素为单位,必须是16的倍数*/
 __u32   height; /*  图片的高度,以像素为单位,必须是16的倍数 */
 __u32   pixelformat;/* 像素格式 */
 enum v4l2_field   field; /* 域类型 */
 __u32             bytesperline; /* for padding, zero if unused *//* 相邻扫描行之间的字节数,这包括各种设备可能会加入的填充字节,
                                                                                                      对于屏幕格式,这个值描述的是最大的平面,如果没有使用则为0 */
 __u32            sizeimage;/* 存储图片所需缓冲区的大小即图像大小 */
 enum v4l2_colorspace colorspace;/* 色域 */
 __u32   priv;  /* private data, depends on pixelformat *//* 私有数据 */
};

/*      Pixel format         FOURCC                        depth  Description  */
/* struct v4l2_pix_format的pixelformat成员的取值 */

/*  RGB颜色组成 */
#define V4L2_PIX_FMT_RGB332  v4l2_fourcc('R', 'G', 'B', '1') /*  8  RGB-3-3-2     */
#define V4L2_PIX_FMT_RGB444  v4l2_fourcc('R', '4', '4', '4') /* 16  xxxxrrrr ggggbbbb */
#define V4L2_PIX_FMT_RGB555  v4l2_fourcc('R', 'G', 'B', 'O') /* 16  RGB-5-5-5     */
#define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
#define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
#define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
#define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
#define V4L2_PIX_FMT_RGB32   v4l2_fourcc('R', 'G', 'B', '4') /* 32  RGB-8-8-8-8   */


/* Y:亮度信号U,V:两个色差信号R-Y(U,偏向红色),B-Y(V,偏向红色)

    YUV与RGB相互转换的公式如下(RGB取值范围均为0-255):
 RGB=>YUV:
    Y = 0.299R + 0.587G + 0.114B
    U = -0.147R - 0.289G + 0.436B
    V = 0.615R - 0.515G - 0.100B
YUV=>RGB:
    R = Y + 1.14V
    G = Y - 0.39U - 0.58V
    B = Y + 2.03U

RGB=>YCbCr
    Y = 0.257*R + 0.504*G + 0.098*B + 16
    Cb = -0.148*R - 0.291*G + 0.439*B + 128
    Cr = 0.439*R - 0.368*G - 0.071*B + 128
    R = 1.164*(Y-16) + 1.596*(Cr-128)


    YUV主要采样格式:
    主要采样格式有YCbCr4:2:0 ,YCbCr4:2:2 ,YCbCr4:1:1 ,YCbCr4:4:4,其中YCbCr4:1:1比较常用,其含义为每点保存一个
    8bit的亮度值(Y),每2   ×2个点保存一个Cr和Cb值,图像在肉眼中的感觉不会起太大的变化。所以原
    来用RGB模型,一个点需要16/24位,若按4:4:4采样后,YUV仍各占8位,按r4:1:1 采样后,而现在一个平
    均需要8+(8/4)+(8/4)=12bits,这样就把图像的数据压缩了一半。

    YUV格式分为打包格式和平面格式,在打包格式中,Y,U和V组件存储在一个数组中。像素被组织到了
    一些巨像素组中。巨像素组中的布局取决于格式。在平面格式中Y,U和V组件作为三个单独的平面
    进行存储。

   */
#define V4L2_PIX_FMT_GREY    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
#define V4L2_PIX_FMT_Y16     v4l2_fourcc('Y', '1', '6', ' ') /* 16  Greyscale     */
#define V4L2_PIX_FMT_PAL8    v4l2_fourcc('P', 'A', 'L', '8') /*  8  8-bit palette */
#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y', 'V', 'U', '9') /*  9  YVU 4:1:0     */
#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
#define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
#define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16  YUV 4:2:2     */
#define V4L2_PIX_FMT_VYUY    v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16  YUV 4:2:2     */
#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16  YVU422 planar */
#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16  YVU411 planar */
#define V4L2_PIX_FMT_Y41P    v4l2_fourcc('Y', '4', '1', 'P') /* 12  YUV 4:1:1     */
#define V4L2_PIX_FMT_YUV444  v4l2_fourcc('Y', '4', '4', '4') /* 16  xxxxyyyy uuuuvvvv */
#define V4L2_PIX_FMT_YUV555  v4l2_fourcc('Y', 'U', 'V', 'O') /* 16  YUV-5-5-5     */
#define V4L2_PIX_FMT_YUV565  v4l2_fourcc('Y', 'U', 'V', 'P') /* 16  YUV-5-6-5     */
#define V4L2_PIX_FMT_YUV32   v4l2_fourcc('Y', 'U', 'V', '4') /* 32  YUV-8-8-8-8   */

/* two planes -- one Y, one Cr + Cb interleaved  */
#define V4L2_PIX_FMT_NV12    v4l2_fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
#define V4L2_PIX_FMT_NV21    v4l2_fourcc('N', 'V', '2', '1') /* 12  Y/CrCb 4:2:0  */
#define V4L2_PIX_FMT_NV16    v4l2_fourcc('N', 'V', '1', '6') /* 16  Y/CbCr 4:2:2  */
#define V4L2_PIX_FMT_NV61    v4l2_fourcc('N', 'V', '6', '1') /* 16  Y/CrCb 4:2:2  */

/*  The following formats are not defined in the V4L2 specification */
#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
#define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16  YUV 4:2:2     */
#define V4L2_PIX_FMT_HI240   v4l2_fourcc('H', 'I', '2', '4') /*  8  8-bit color   */
#define V4L2_PIX_FMT_HM12    v4l2_fourcc('H', 'M', '1', '2') /*  8  YUV 4:2:0 16x16 macroblocks */

/* see */
#define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG8  v4l2_fourcc('G', 'B', 'R', 'G') /*  8  GBGB.. RGRG.. */
/*
 * 10bit raw bayer, expanded to 16 bits
 * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
 */
#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0')
/* 10bit raw bayer DPCM compressed to 8 bits */
#define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0')
#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16  BGBG.. GRGR.. */

/* compressed formats */
/* 压缩格式 */
#define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */
#define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG     */
#define V4L2_PIX_FMT_DV       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */

/*  Vendor-specific formats   */
#define V4L2_PIX_FMT_WNVA     v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
#define V4L2_PIX_FMT_SN9C10X  v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
#define V4L2_PIX_FMT_PWC1     v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */
#define V4L2_PIX_FMT_PWC2     v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */
#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */
#define V4L2_PIX_FMT_SPCA501  v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */
#define V4L2_PIX_FMT_SPCA505  v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */
#define V4L2_PIX_FMT_SPCA508  v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
#define V4L2_PIX_FMT_SPCA561  v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
#define V4L2_PIX_FMT_PAC207   v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
#define V4L2_PIX_FMT_SQ905C   v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
#define V4L2_PIX_FMT_PJPG     v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
#define V4L2_PIX_FMT_YVYU    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16  YVU 4:2:2     */

/*
 * F O R M A T   E N U M E R A T I O N
 */
struct v4l2_fmtdesc {  /* 帧格式 描述结构体*/
 __u32      index;             /* Format number      */  /* 要查询的格式序号,应用程序设置 */
 enum v4l2_buf_type  type;              /* buffer type        *//* 帧类型,应用程序设置 */
 __u32               flags;                                                      /* 是否压缩格式 ,V4L2_FMT_FLAG_COMPRESSED*/
 __u8      description[32];   /* Description string */ /* 格式名称 */
 __u32      pixelformat;       /* Format fourcc      */  /* 视频表现方式的fourcc编码 */
 __u32      reserved[4];
};

#define V4L2_FMT_FLAG_COMPRESSED 0x0001  /* 压缩视频 */

#if 1
 /* Experimental Frame Size and frame rate enumeration */
/*
 * F R A M E   S I Z E   E N U M E R A T I O N
 */
enum v4l2_frmsizetypes {
 V4L2_FRMSIZE_TYPE_DISCRETE = 1,
 V4L2_FRMSIZE_TYPE_CONTINUOUS = 2,
 V4L2_FRMSIZE_TYPE_STEPWISE = 3,
};

struct v4l2_frmsize_discrete {
 __u32   width;  /* Frame width [pixel] */
 __u32   height;  /* Frame height [pixel] */
};

struct v4l2_frmsize_stepwise {
 __u32   min_width; /* Minimum frame width [pixel] */
 __u32   max_width; /* Maximum frame width [pixel] */
 __u32   step_width; /* Frame width step size [pixel] */
 __u32   min_height; /* Minimum frame height [pixel] */
 __u32   max_height; /* Maximum frame height [pixel] */
 __u32   step_height; /* Frame height step size [pixel] */
};

struct v4l2_frmsizeenum {
 __u32   index;  /* Frame size number */
 __u32   pixel_format; /* Pixel format */
 __u32   type;  /* Frame size type the device supports. */

 union {     /* Frame size */
  struct v4l2_frmsize_discrete discrete;
  struct v4l2_frmsize_stepwise stepwise;
 };

 __u32   reserved[2];   /* Reserved space for future use */
};

/*
 * F R A M E   R A T E   E N U M E R A T I O N
 */
enum v4l2_frmivaltypes {
 V4L2_FRMIVAL_TYPE_DISCRETE = 1,
 V4L2_FRMIVAL_TYPE_CONTINUOUS = 2,
 V4L2_FRMIVAL_TYPE_STEPWISE = 3,
};

struct v4l2_frmival_stepwise {
 struct v4l2_fract min;  /* Minimum frame interval [s] */
 struct v4l2_fract max;  /* Maximum frame interval [s] */
 struct v4l2_fract step;  /* Frame interval step size [s] */
};

struct v4l2_frmivalenum {
 __u32   index;  /* Frame format index */
 __u32   pixel_format; /* Pixel format */
 __u32   width;  /* Frame width */
 __u32   height;  /* Frame height */
 __u32   type;  /* Frame interval type the device supports. */

 union {     /* Frame interval */
  struct v4l2_fract  discrete;
  struct v4l2_frmival_stepwise stepwise;
 };

 __u32 reserved[2];   /* Reserved space for future use */
};
#endif

/*
 * T I M E C O D E
 */
struct v4l2_timecode {
 __u32 type;
 __u32 flags;
 __u8 frames;
 __u8 seconds;
 __u8 minutes;
 __u8 hours;
 __u8 userbits[4];
};

/*  Type  */
#define V4L2_TC_TYPE_24FPS  1
#define V4L2_TC_TYPE_25FPS  2
#define V4L2_TC_TYPE_30FPS  3
#define V4L2_TC_TYPE_50FPS  4
#define V4L2_TC_TYPE_60FPS  5

/*  Flags  */
#define V4L2_TC_FLAG_DROPFRAME  0x0001 /* "drop-frame" mode */
#define V4L2_TC_FLAG_COLORFRAME  0x0002
#define V4L2_TC_USERBITS_field  0x000C
#define V4L2_TC_USERBITS_USERDEFINED 0x0000
#define V4L2_TC_USERBITS_8BITCHARS 0x0008
/* The above is based on SMPTE timecodes */

struct v4l2_jpegcompression {  /* JPEG格式压缩 */
 int quality;

 int  APPn;              /* Number of APP segment to be written,
     * must be 0..15 */
 int  APP_len;           /* Length of data in JPEG APPn segment */
 char APP_data[60];      /* Data in the JPEG APPn segment. */

 int  COM_len;           /* Length of data in JPEG COM segment */
 char COM_data[60];      /* Data in JPEG COM segment */

 __u32 jpeg_markers;     /* Which markers should go into the JPEG
     * output. Unless you exactly know what
     * you do, leave them untouched.
     * Inluding less markers will make the
     * resulting code smaller, but there will
     * be fewer aplications which can read it.
     * The presence of the APP and COM marker
     * is influenced by APP_len and COM_len
     * ONLY, not by this property! */

#define V4L2_JPEG_MARKER_DHT (1<<3)    /* Define Huffman Tables */
#define V4L2_JPEG_MARKER_DQT (1<<4)    /* Define Quantization Tables */
#define V4L2_JPEG_MARKER_DRI (1<<5)    /* Define Restart Interval */
#define V4L2_JPEG_MARKER_COM (1<<6)    /* Comment segment */
#define V4L2_JPEG_MARKER_APP (1<<7)    /* App segment, driver will
     * allways use APP0 */
};

/*
 * M E M O R Y - M A P P I N G   B U F F E R S
 */
struct v4l2_requestbuffers { /* 请求缓冲区 */
 __u32   count;   /* 想要使用的缓冲区的数目(应用程序可以设置该字段为0来释放掉所有已经存在的缓冲区) */
 enum v4l2_buf_type      type;  /* 数据流类型 */
 enum v4l2_memory        memory;  /* 内存类型 */
 __u32   reserved[2];
};

struct v4l2_buffer {  /* v4L2缓冲区(当输入输出有效的,帧是以改结构体的形式在应用和驱动程序之间传输的) */
 __u32   index; /* buffer序号,它只在内存映射缓冲区使用 */
 enum v4l2_buf_type      type; /* buffer类型 */
 __u32   bytesused;  /* buffer中已经使用的字节数 */
 __u32   flags;   /* 区分MMAP还是USERPTR ,查看宏V4L2_BUF_FLAG_*   */
 enum v4l2_field  field; /* 图像存在缓冲区的哪一个区域 */
 struct timeval  timestamp; /* 获取第一个字节的系统时间 */
 struct v4l2_timecode timecode; /* 用来存放时间编码,对于视频编辑类的应用是非常有用的 */
 __u32   sequence; /* 队列中的序号 */

 /* memory location */
 enum v4l2_memory        memory; /* IO方式,被应用程序设置 */
 union {
  __u32           offset; /* 缓冲帧地址,只对MMAP有效 */
  unsigned long   userptr; /* 缓冲区的用户空间地址 */
 } m;
 __u32   length; /* 缓冲区长度 */
 __u32   input; /* 该字段可以用来快速切换捕获设备的输入 */
 __u32   reserved; /*  */
};

/*  Flags for 'flags' field */
/*  struct v4l2_buffer的成员flags的取值 */
#define V4L2_BUF_FLAG_MAPPED 0x0001  /* Buffer is mapped (flag) *//* 缓冲区已映射到用户空间 */
#define V4L2_BUF_FLAG_QUEUED 0x0002 /* Buffer is queued for processing */ /* 缓冲区排队等待处理*/
#define V4L2_BUF_FLAG_DONE 0x0004 /* Buffer is ready */  /* 缓冲区在驱动的传出队列 */
#define V4L2_BUF_FLAG_KEYFRAME 0x0008 /* Image is a keyframe (I-frame) */ /* 缓冲区包含一个关键帧,它在压缩流中是非常有用的 */

/* 应用于压缩流中,它们代表的是预测的或者说是差分的帧 */
#define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */ 
#define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */   /*  */

#define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */  /* timecode字段有效 */
#define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */   /* input字段有效 */

/*
 * O V E R L A Y   P R E V I E W
 */
struct v4l2_framebuffer {
 __u32   capability;
 __u32   flags;
/* FIXME: in theory we should pass something like PCI device + memory
 * region + offset instead of some physical address */
 void                    *base;
 struct v4l2_pix_format fmt;
};
/*  Flags for the 'capability' field. Read only */
#define V4L2_FBUF_CAP_EXTERNOVERLAY 0x0001
#define V4L2_FBUF_CAP_CHROMAKEY  0x0002
#define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
#define V4L2_FBUF_CAP_BITMAP_CLIPPING 0x0008
#define V4L2_FBUF_CAP_LOCAL_ALPHA 0x0010
#define V4L2_FBUF_CAP_GLOBAL_ALPHA 0x0020
#define V4L2_FBUF_CAP_LOCAL_INV_ALPHA 0x0040
/*  Flags for the 'flags' field. */
#define V4L2_FBUF_FLAG_PRIMARY  0x0001
#define V4L2_FBUF_FLAG_OVERLAY  0x0002
#define V4L2_FBUF_FLAG_CHROMAKEY 0x0004
#define V4L2_FBUF_FLAG_LOCAL_ALPHA 0x0008
#define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010
#define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020

struct v4l2_clip {
 struct v4l2_rect        c;
 struct v4l2_clip __user *next;
};

struct v4l2_window {
 struct v4l2_rect        w;
 enum v4l2_field   field;
 __u32   chromakey;
 struct v4l2_clip __user *clips;
 __u32   clipcount;
 void   __user *bitmap;
 __u8                    global_alpha;
};

/*
 * C A P T U R E   P A R A M E T E R S
 */
struct v4l2_captureparm {   /* 捕获视频参数 */
 __u32     capability;   /*  Supported modes */ /* 支持的模式----V4L2_CAP_TIMEPERFRAME  */
 __u32     capturemode;   /*  Current mode */ /* 当前模式----V4L2_MODE_HIGHQUALITY */
 struct v4l2_fract  timeperframe;  /*  Time per frame in .1us units */  /* 用于指定想要使用的帧频率 */
 __u32     extendedmode;  /*  Driver-specific extensions */ /* 驱动不使用的话则设置它为0 */
 __u32              readbuffers;   /*  # of buffers for read */ /* read()操作被调用时内核应为输入的帧准备的缓冲区数量 */
 __u32     reserved[4];
};

/*  Flags for 'capability' and 'capturemode' fields */
#define V4L2_MODE_HIGHQUALITY 0x0001 /*  High quality imaging mode */ /* 高质量图像模式 */
#define V4L2_CAP_TIMEPERFRAME 0x1000 /*  timeperframe field is supported */ /* 可以改变帧频率 */

struct v4l2_outputparm { /* 输出视频参数 */
 __u32     capability;  /*  Supported modes */ /* 支持的模式 */
 __u32     outputmode;  /*  Current mode */ /* 输出模式 */
 struct v4l2_fract  timeperframe; /*  Time per frame in seconds */ /* 用于指定想要使用的帧频率 */
 __u32     extendedmode; /*  Driver-specific extensions */ /* 驱动不使用的话则设置它为0 */
 __u32              writebuffers; /*  # of buffers for write */ /* write()操作被调用时内核应为输出的帧准备的缓冲区数量 */
 __u32     reserved[4];
};

/*
 * I N P U T   I M A G E   C R O P P I N G
 */
struct v4l2_cropcap {  /* 图像的放缩 */
 enum v4l2_buf_type      type;   /* 缓冲区类型,应用程序设置 */
 struct v4l2_rect        bounds;   /* 最大边界 */
 struct v4l2_rect        defrect;   /* 默认值 */
 struct v4l2_fract       pixelaspect;  /*  */
};

struct v4l2_crop {  /* 视频的采集窗口*/
 enum v4l2_buf_type      type;  /* 缓冲区类型,应用程序设置 */
 struct v4l2_rect        c;  /* 视频窗口大小 */
};

/*
 *      A N A L O G   V I D E O   S T A N D A R D
 */

typedef __u64 v4l2_std_id;  /* 描述的是视频标准 */

/* one bit for each */
/* PAL标准(主要是欧洲、非洲和亚洲使用(PAL(720*576))) */
#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)
#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)
#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)
#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)
#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)
#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)
#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)
#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)

#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)
#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)
#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)
#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)

#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)  /* 标准NTSC (NTSC主要是北美使用)*/
#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000) /* 日本使用的NTSC变种标准 */
#define V4L2_STD_NTSC_443       ((v4l2_std_id)0x00004000)
#define V4L2_STD_NTSC_M_KR      ((v4l2_std_id)0x00008000)

/* SECAM标准(主要是法国、俄罗斯和非洲部分国家使用) */
#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)
#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)
#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)
#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)
#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)
#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)
#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)
#define V4L2_STD_SECAM_LC       ((v4l2_std_id)0x00800000)

/* ATSC/HDTV */
#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)
#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)

/* FIXME:
   Although std_id is 64 bits, there is an issue on PPC32 architecture that
   makes switch(__u64) to break. So, there's a hack on v4l2-common.c rounding
   this value to 32 bits.
   As, currently, the max value is for V4L2_STD_ATSC_16_VSB (30 bits wide),
   it should work fine. However, if needed to add more than two standards,
   v4l2-common.c should be fixed.
 */

/* some merged standards */
#define V4L2_STD_MN (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)
#define V4L2_STD_B (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)
#define V4L2_STD_GH (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)
#define V4L2_STD_DK (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)

/* some common needed stuff */
#define V4L2_STD_PAL_BG  (V4L2_STD_PAL_B  |\
     V4L2_STD_PAL_B1 |\
     V4L2_STD_PAL_G)
#define V4L2_STD_PAL_DK  (V4L2_STD_PAL_D  |\
     V4L2_STD_PAL_D1 |\
     V4L2_STD_PAL_K)
#define V4L2_STD_PAL  (V4L2_STD_PAL_BG |\
     V4L2_STD_PAL_DK |\
     V4L2_STD_PAL_H  |\
     V4L2_STD_PAL_I)
#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M |\
     V4L2_STD_NTSC_M_JP     |\
     V4L2_STD_NTSC_M_KR)     /* 如果一个设备可以处理所有NTSC标准,则它就可以使用该宏 */
#define V4L2_STD_SECAM_DK       (V4L2_STD_SECAM_D |\
     V4L2_STD_SECAM_K |\
     V4L2_STD_SECAM_K1)
#define V4L2_STD_SECAM  (V4L2_STD_SECAM_B |\
     V4L2_STD_SECAM_G |\
     V4L2_STD_SECAM_H |\
     V4L2_STD_SECAM_DK |\
     V4L2_STD_SECAM_L       |\
     V4L2_STD_SECAM_LC)

#define V4L2_STD_525_60  (V4L2_STD_PAL_M  |\
     V4L2_STD_PAL_60 |\
     V4L2_STD_NTSC  |\
     V4L2_STD_NTSC_443)
#define V4L2_STD_625_50  (V4L2_STD_PAL  |\
     V4L2_STD_PAL_N  |\
     V4L2_STD_PAL_Nc |\
     V4L2_STD_SECAM)
#define V4L2_STD_ATSC           (V4L2_STD_ATSC_8_VSB    |\
     V4L2_STD_ATSC_16_VSB)

#define V4L2_STD_UNKNOWN        0
#define V4L2_STD_ALL            (V4L2_STD_525_60 |\
     V4L2_STD_625_50)

struct v4l2_standard { /* 视频标准 */
 __u32       index;
 v4l2_std_id          id;
 __u8       name[24];
 struct v4l2_fract    frameperiod; /* Frames, not fields */
 __u32       framelines;
 __u32       reserved[4];
};

/*
 * V I D E O   I N P U T S
 */
struct v4l2_input {  /* 视频设备支持的所有可用的输入描述结构体 */
 __u32      index;  /*  Which input */  /* 应用程序关注的输入引索号,这是唯一一个用户空间设定的字段,驱动程序
                                                                            要分配引索号给输入*/
 __u8      name[32];  /*  Label */    /* 输入的名字,由驱动设定 */
 __u32      type;  /*  Type of input */      /* 输入类型,有两个取值: V4L2_INPUT_TYPE_TUNER和V4L2_INPUT_TYPE_CAMERA */
 __u32      audioset;  /*  Associated audios (bitfield) */  /* 描述哪个音频输入可以与视频输入相关联,如果没有音频输入可以
                                                                                                  与视频相关联或者只有一个可选,则可以简单地把这个字段置0*/
 __u32        tuner;             /*  Associated tuner */ /* 如果输入是调谐器,那么这个字段就是会包含一个相应的调谐设备的引索号 */
 v4l2_std_id  std;               /* 描述设备支持哪个或哪些视频标准 */
 __u32      status;         /* 输入的状态 */
 __u32      reserved[4];/*  */
};

/*  Values for the 'type' field */
/* struct v4l2_input 的type成员的取值  */
#define V4L2_INPUT_TYPE_TUNER  1   /* 调谐器 */
#define V4L2_INPUT_TYPE_CAMERA  2  /* 摄像头 */

 

/*struct v4l2_input 的status成员的取值  */
/* field 'status' - general */
/* 通用的 */
#define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
#define V4L2_IN_ST_NO_SIGNAL   0x00000002
#define V4L2_IN_ST_NO_COLOR    0x00000004

/* field 'status' - sensor orientation */
/* If sensor is mounted upside down set both bits */
/* 传感器 */
#define V4L2_IN_ST_HFLIP       0x00000010 /* Frames are flipped horizontally */
#define V4L2_IN_ST_VFLIP       0x00000020 /* Frames are flipped vertically */

/* field 'status' - analog */
/* 模拟 */
#define V4L2_IN_ST_NO_H_LOCK   0x00000100  /* No horizontal sync lock */
#define V4L2_IN_ST_COLOR_KILL  0x00000200  /* Color killer is active */

/* field 'status' - digital */
/* 数字 */
#define V4L2_IN_ST_NO_SYNC     0x00010000  /* No synchronization lock */
#define V4L2_IN_ST_NO_EQU      0x00020000  /* No equalizer lock */
#define V4L2_IN_ST_NO_CARRIER  0x00040000  /* Carrier recovery failed */

/* field 'status' - VCR and set-top box */
/* 摄像机和机顶盒 */
#define V4L2_IN_ST_MACROVISION 0x01000000  /* Macrovision detected */
#define V4L2_IN_ST_NO_ACCESS   0x02000000  /* Conditional access denied */
#define V4L2_IN_ST_VTR         0x04000000  /* VTR time constant */

/*
 * V I D E O   O U T P U T S
 */
struct v4l2_output {  /* 视频设备支持的所有可用的输出描述结构体 */
 __u32      index;  /*  Which output */ /* 输出引索号 */
 __u8      name[32];  /*  Label */ /* 输出的名字 */
 __u32      type;  /*  Type of output */  /* 输出的类型,查看宏 V4L2_OUTPUT_TYPE_*   */
 __u32      audioset;  /*  Associated audios (bitfield) */ /* 能与这个视频协同工作的音频集 */
 __u32      modulator;         /*  Associated modulator */  /* 与此设备相关的调制器 */
 v4l2_std_id  std;               /* 输出所支持的视频标准 */
 __u32      reserved[4];
};
/*  Values for the 'type' field */
/* struct v4l2_output 的type成员的取值 */
#define V4L2_OUTPUT_TYPE_MODULATOR  1          /* 用于模拟电视调制器 */
#define V4L2_OUTPUT_TYPE_ANALOG   2          /* 用于基本的模拟视频输出 */
#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY 3  /* 用于模拟VGA覆盖设备 */

/*
 * C O N T R O L S
 */
struct v4l2_control { /* 控制 */
 __u32       id;
 __s32       value;
};

struct v4l2_ext_control { /* 扩展控制描述结构体 */
 __u32 id;
 __u32 reserved2[2];
 union {
  __s32 value;
  __s64 value64;
  void *reserved;
 };
} __attribute__ ((packed));

struct v4l2_ext_controls {
 __u32 ctrl_class;
 __u32 count;
 __u32 error_idx;
 __u32 reserved[2];
 struct v4l2_ext_control *controls;
};

/*  Values for ctrl_class field */
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */
#define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */

#define V4L2_CTRL_ID_MASK         (0x0fffffff)
#define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000)

/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
struct v4l2_queryctrl {  /* 查询控制 */
 __u32       id;   /* ID */
 enum v4l2_ctrl_type  type;  /* 控制类型 */
 __u8       name[32]; /* Whatever */ /* 名字 */
 __s32       minimum; /* Note signedness */
 __s32       maximum;
 __s32       step;
 __s32       default_value;
 __u32                flags;
 __u32       reserved[2];
};

/*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
struct v4l2_querymenu { /* 查询菜单项 */
 __u32  id;
 __u32  index;
 __u8  name[32]; /* Whatever */
 __u32  reserved;
};

/*  Control flags  */
/* struct v4l2_queryctrl的flags取值 */
#define V4L2_CTRL_FLAG_DISABLED  0x0001
#define V4L2_CTRL_FLAG_GRABBED  0x0002
#define V4L2_CTRL_FLAG_READ_ONLY  0x0004
#define V4L2_CTRL_FLAG_UPDATE   0x0008
#define V4L2_CTRL_FLAG_INACTIVE  0x0010
#define V4L2_CTRL_FLAG_SLIDER   0x0020
#define V4L2_CTRL_FLAG_WRITE_ONLY  0x0040

/*  Query flag, to be ORed with the control ID */
#define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000

/*  User-class control IDs defined by V4L2 */
#define V4L2_CID_BASE   (V4L2_CTRL_CLASS_USER | 0x900)
#define V4L2_CID_USER_BASE   V4L2_CID_BASE
/*  IDs reserved for driver specific controls */
#define V4L2_CID_PRIVATE_BASE  0x08000000


/* struct v4l2_queryctrl的成员id的取值 */
#define V4L2_CID_USER_CLASS   (V4L2_CTRL_CLASS_USER | 1) /* 用户类 */
#define V4L2_CID_BRIGHTNESS  (V4L2_CID_BASE+0)    /* 亮度 */
#define V4L2_CID_CONTRAST  (V4L2_CID_BASE+1)           /* 对比度 */
#define V4L2_CID_SATURATION  (V4L2_CID_BASE+2)    /* 色饱和度 */
#define V4L2_CID_HUE   (V4L2_CID_BASE+3)           /* 色彩 */
#define V4L2_CID_AUDIO_VOLUME  (V4L2_CID_BASE+5)      /* 音频音量 */
#define V4L2_CID_AUDIO_BALANCE  (V4L2_CID_BASE+6)  /* 音频平衡 */
#define V4L2_CID_AUDIO_BASS  (V4L2_CID_BASE+7)         /* 低音 */
#define V4L2_CID_AUDIO_TREBLE  (V4L2_CID_BASE+8)         /* 高音 */
#define V4L2_CID_AUDIO_MUTE  (V4L2_CID_BASE+9)         /* 静音 */
#define V4L2_CID_AUDIO_LOUDNESS  (V4L2_CID_BASE+10) /* 响度 */
#define V4L2_CID_BLACK_LEVEL  (V4L2_CID_BASE+11) /* Deprecated */
#define V4L2_CID_AUTO_WHITE_BALANCE (V4L2_CID_BASE+12)  /* 白色平衡 */
#define V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13)
#define V4L2_CID_RED_BALANCE  (V4L2_CID_BASE+14)   /* 红色平衡 */
#define V4L2_CID_BLUE_BALANCE  (V4L2_CID_BASE+15)   /* 蓝色平衡 */
#define V4L2_CID_GAMMA   (V4L2_CID_BASE+16)
#define V4L2_CID_WHITENESS  (V4L2_CID_GAMMA) /* Deprecated */
#define V4L2_CID_EXPOSURE  (V4L2_CID_BASE+17)   /* 曝光  */
#define V4L2_CID_AUTOGAIN  (V4L2_CID_BASE+18)
#define V4L2_CID_GAIN   (V4L2_CID_BASE+19)
#define V4L2_CID_HFLIP   (V4L2_CID_BASE+20)
#define V4L2_CID_VFLIP   (V4L2_CID_BASE+21)

/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */
#define V4L2_CID_HCENTER  (V4L2_CID_BASE+22)
#define V4L2_CID_VCENTER  (V4L2_CID_BASE+23)

#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24)
enum v4l2_power_line_frequency {
 V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0,
 V4L2_CID_POWER_LINE_FREQUENCY_50HZ = 1,
 V4L2_CID_POWER_LINE_FREQUENCY_60HZ = 2,
};
#define V4L2_CID_HUE_AUTO   (V4L2_CID_BASE+25)
#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE+26)
#define V4L2_CID_SHARPNESS   (V4L2_CID_BASE+27)
#define V4L2_CID_BACKLIGHT_COMPENSATION  (V4L2_CID_BASE+28)
#define V4L2_CID_CHROMA_AGC                     (V4L2_CID_BASE+29)
#define V4L2_CID_COLOR_KILLER                   (V4L2_CID_BASE+30)
#define V4L2_CID_COLORFX   (V4L2_CID_BASE+31)
enum v4l2_colorfx {
 V4L2_COLORFX_NONE = 0,
 V4L2_COLORFX_BW  = 1,
 V4L2_COLORFX_SEPIA = 2,
};

/* last CID + 1 */
#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+32)

/*  MPEG-class control IDs defined by V4L2 */
#define V4L2_CID_MPEG_BASE    (V4L2_CTRL_CLASS_MPEG | 0x900)
#define V4L2_CID_MPEG_CLASS    (V4L2_CTRL_CLASS_MPEG | 1)

/*  MPEG streams */
#define V4L2_CID_MPEG_STREAM_TYPE   (V4L2_CID_MPEG_BASE+0)
enum v4l2_mpeg_stream_type {
 V4L2_MPEG_STREAM_TYPE_MPEG2_PS   = 0, /* MPEG-2 program stream */
 V4L2_MPEG_STREAM_TYPE_MPEG2_TS   = 1, /* MPEG-2 transport stream */
 V4L2_MPEG_STREAM_TYPE_MPEG1_SS   = 2, /* MPEG-1 system stream */
 V4L2_MPEG_STREAM_TYPE_MPEG2_DVD  = 3, /* MPEG-2 DVD-compatible stream */
 V4L2_MPEG_STREAM_TYPE_MPEG1_VCD  = 4, /* MPEG-1 VCD-compatible stream */
 V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */
};
#define V4L2_CID_MPEG_STREAM_PID_PMT   (V4L2_CID_MPEG_BASE+1)
#define V4L2_CID_MPEG_STREAM_PID_AUDIO   (V4L2_CID_MPEG_BASE+2)
#define V4L2_CID_MPEG_STREAM_PID_VIDEO   (V4L2_CID_MPEG_BASE+3)
#define V4L2_CID_MPEG_STREAM_PID_PCR   (V4L2_CID_MPEG_BASE+4)
#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO  (V4L2_CID_MPEG_BASE+5)
#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO  (V4L2_CID_MPEG_BASE+6)
#define V4L2_CID_MPEG_STREAM_VBI_FMT   (V4L2_CID_MPEG_BASE+7)
enum v4l2_mpeg_stream_vbi_fmt {
 V4L2_MPEG_STREAM_VBI_FMT_NONE = 0,  /* No VBI in the MPEG stream */
 V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1,  /* VBI in private packets, IVTV format */
};

/*  MPEG audio */
#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ  (V4L2_CID_MPEG_BASE+100)
enum v4l2_mpeg_audio_sampling_freq {
 V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
 V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1,
 V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2,
};
#define V4L2_CID_MPEG_AUDIO_ENCODING   (V4L2_CID_MPEG_BASE+101)
enum v4l2_mpeg_audio_encoding {
 V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
 V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
 V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
 V4L2_MPEG_AUDIO_ENCODING_AAC     = 3,
 V4L2_MPEG_AUDIO_ENCODING_AC3     = 4,
};
#define V4L2_CID_MPEG_AUDIO_L1_BITRATE   (V4L2_CID_MPEG_BASE+102)
enum v4l2_mpeg_audio_l1_bitrate {
 V4L2_MPEG_AUDIO_L1_BITRATE_32K  = 0,
 V4L2_MPEG_AUDIO_L1_BITRATE_64K  = 1,
 V4L2_MPEG_AUDIO_L1_BITRATE_96K  = 2,
 V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3,
 V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4,
 V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5,
 V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6,
 V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7,
 V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8,
 V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9,
 V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10,
 V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11,
 V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12,
 V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13,
};
#define V4L2_CID_MPEG_AUDIO_L2_BITRATE   (V4L2_CID_MPEG_BASE+103)
enum v4l2_mpeg_audio_l2_bitrate {
 V4L2_MPEG_AUDIO_L2_BITRATE_32K  = 0,
 V4L2_MPEG_AUDIO_L2_BITRATE_48K  = 1,
 V4L2_MPEG_AUDIO_L2_BITRATE_56K  = 2,
 V4L2_MPEG_AUDIO_L2_BITRATE_64K  = 3,
 V4L2_MPEG_AUDIO_L2_BITRATE_80K  = 4,
 V4L2_MPEG_AUDIO_L2_BITRATE_96K  = 5,
 V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6,
 V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7,
 V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8,
 V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9,
 V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10,
 V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11,
 V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12,
 V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13,
};
#define V4L2_CID_MPEG_AUDIO_L3_BITRATE   (V4L2_CID_MPEG_BASE+104)
enum v4l2_mpeg_audio_l3_bitrate {
 V4L2_MPEG_AUDIO_L3_BITRATE_32K  = 0,
 V4L2_MPEG_AUDIO_L3_BITRATE_40K  = 1,
 V4L2_MPEG_AUDIO_L3_BITRATE_48K  = 2,
 V4L2_MPEG_AUDIO_L3_BITRATE_56K  = 3,
 V4L2_MPEG_AUDIO_L3_BITRATE_64K  = 4,
 V4L2_MPEG_AUDIO_L3_BITRATE_80K  = 5,
 V4L2_MPEG_AUDIO_L3_BITRATE_96K  = 6,
 V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7,
 V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8,
 V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9,
 V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10,
 V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11,
 V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12,
 V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13,
};
#define V4L2_CID_MPEG_AUDIO_MODE   (V4L2_CID_MPEG_BASE+105)
enum v4l2_mpeg_audio_mode {
 V4L2_MPEG_AUDIO_MODE_STEREO       = 0,
 V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1,
 V4L2_MPEG_AUDIO_MODE_DUAL         = 2,
 V4L2_MPEG_AUDIO_MODE_MONO         = 3,
};
#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION  (V4L2_CID_MPEG_BASE+106)
enum v4l2_mpeg_audio_mode_extension {
 V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4  = 0,
 V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8  = 1,
 V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2,
 V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3,
};
#define V4L2_CID_MPEG_AUDIO_EMPHASIS   (V4L2_CID_MPEG_BASE+107)
enum v4l2_mpeg_audio_emphasis {
 V4L2_MPEG_AUDIO_EMPHASIS_NONE         = 0,
 V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1,
 V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17    = 2,
};
#define V4L2_CID_MPEG_AUDIO_CRC   (V4L2_CID_MPEG_BASE+108)
enum v4l2_mpeg_audio_crc {
 V4L2_MPEG_AUDIO_CRC_NONE  = 0,
 V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
};
#define V4L2_CID_MPEG_AUDIO_MUTE   (V4L2_CID_MPEG_BASE+109)
#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE  (V4L2_CID_MPEG_BASE+110)
#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE  (V4L2_CID_MPEG_BASE+111)
enum v4l2_mpeg_audio_ac3_bitrate {
 V4L2_MPEG_AUDIO_AC3_BITRATE_32K  = 0,
 V4L2_MPEG_AUDIO_AC3_BITRATE_40K  = 1,
 V4L2_MPEG_AUDIO_AC3_BITRATE_48K  = 2,
 V4L2_MPEG_AUDIO_AC3_BITRATE_56K  = 3,
 V4L2_MPEG_AUDIO_AC3_BITRATE_64K  = 4,
 V4L2_MPEG_AUDIO_AC3_BITRATE_80K  = 5,
 V4L2_MPEG_AUDIO_AC3_BITRATE_96K  = 6,
 V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
 V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
 V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
 V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
 V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
 V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
 V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
 V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
 V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
 V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
 V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
 V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
};

/*  MPEG video */
#define V4L2_CID_MPEG_VIDEO_ENCODING   (V4L2_CID_MPEG_BASE+200)
enum v4l2_mpeg_video_encoding {
 V4L2_MPEG_VIDEO_ENCODING_MPEG_1     = 0,
 V4L2_MPEG_VIDEO_ENCODING_MPEG_2     = 1,
 V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
};
#define V4L2_CID_MPEG_VIDEO_ASPECT   (V4L2_CID_MPEG_BASE+201)
enum v4l2_mpeg_video_aspect {
 V4L2_MPEG_VIDEO_ASPECT_1x1     = 0,
 V4L2_MPEG_VIDEO_ASPECT_4x3     = 1,
 V4L2_MPEG_VIDEO_ASPECT_16x9    = 2,
 V4L2_MPEG_VIDEO_ASPECT_221x100 = 3,
};
#define V4L2_CID_MPEG_VIDEO_B_FRAMES   (V4L2_CID_MPEG_BASE+202)
#define V4L2_CID_MPEG_VIDEO_GOP_SIZE   (V4L2_CID_MPEG_BASE+203)
#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE  (V4L2_CID_MPEG_BASE+204)
#define V4L2_CID_MPEG_VIDEO_PULLDOWN   (V4L2_CID_MPEG_BASE+205)
#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE  (V4L2_CID_MPEG_BASE+206)
enum v4l2_mpeg_video_bitrate_mode {
 V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0,
 V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1,
};
#define V4L2_CID_MPEG_VIDEO_BITRATE   (V4L2_CID_MPEG_BASE+207)
#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK  (V4L2_CID_MPEG_BASE+208)
#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
#define V4L2_CID_MPEG_VIDEO_MUTE   (V4L2_CID_MPEG_BASE+210)
#define V4L2_CID_MPEG_VIDEO_MUTE_YUV   (V4L2_CID_MPEG_BASE+211)

/*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
#define V4L2_CID_MPEG_CX2341X_BASE     (V4L2_CTRL_CLASS_MPEG | 0x1000)
#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE  (V4L2_CID_MPEG_CX2341X_BASE+0)
enum v4l2_mpeg_cx2341x_video_spatial_filter_mode {
 V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0,
 V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO   = 1,
};
#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER   (V4L2_CID_MPEG_CX2341X_BASE+1)
#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE  (V4L2_CID_MPEG_CX2341X_BASE+2)
enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type {
 V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF                  = 0,
 V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR               = 1,
 V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT              = 2,
 V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE      = 3,
 V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4,
};
#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE  (V4L2_CID_MPEG_CX2341X_BASE+3)
enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type {
 V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF    = 0,
 V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
};
#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE  (V4L2_CID_MPEG_CX2341X_BASE+4)
enum v4l2_mpeg_cx2341x_video_temporal_filter_mode {
 V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0,
 V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO   = 1,
};
#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER   (V4L2_CID_MPEG_CX2341X_BASE+5)
#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE   (V4L2_CID_MPEG_CX2341X_BASE+6)
enum v4l2_mpeg_cx2341x_video_median_filter_type {
 V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF      = 0,
 V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR      = 1,
 V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT     = 2,
 V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3,
 V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG     = 4,
};
#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM  (V4L2_CID_MPEG_CX2341X_BASE+7)
#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP  (V4L2_CID_MPEG_CX2341X_BASE+8)
#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9)
#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP  (V4L2_CID_MPEG_CX2341X_BASE+10)
#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS  (V4L2_CID_MPEG_CX2341X_BASE+11)

/*  Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE  (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS   (V4L2_CTRL_CLASS_CAMERA | 1)

#define V4L2_CID_EXPOSURE_AUTO   (V4L2_CID_CAMERA_CLASS_BASE+1)
enum  v4l2_exposure_auto_type {
 V4L2_EXPOSURE_AUTO = 0,
 V4L2_EXPOSURE_MANUAL = 1,
 V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
 V4L2_EXPOSURE_APERTURE_PRIORITY = 3
};
#define V4L2_CID_EXPOSURE_ABSOLUTE  (V4L2_CID_CAMERA_CLASS_BASE+2)
#define V4L2_CID_EXPOSURE_AUTO_PRIORITY  (V4L2_CID_CAMERA_CLASS_BASE+3)

#define V4L2_CID_PAN_RELATIVE   (V4L2_CID_CAMERA_CLASS_BASE+4)
#define V4L2_CID_TILT_RELATIVE   (V4L2_CID_CAMERA_CLASS_BASE+5)
#define V4L2_CID_PAN_RESET   (V4L2_CID_CAMERA_CLASS_BASE+6)
#define V4L2_CID_TILT_RESET   (V4L2_CID_CAMERA_CLASS_BASE+7)

#define V4L2_CID_PAN_ABSOLUTE   (V4L2_CID_CAMERA_CLASS_BASE+8)
#define V4L2_CID_TILT_ABSOLUTE   (V4L2_CID_CAMERA_CLASS_BASE+9)

#define V4L2_CID_FOCUS_ABSOLUTE   (V4L2_CID_CAMERA_CLASS_BASE+10)
#define V4L2_CID_FOCUS_RELATIVE   (V4L2_CID_CAMERA_CLASS_BASE+11)
#define V4L2_CID_FOCUS_AUTO   (V4L2_CID_CAMERA_CLASS_BASE+12)

#define V4L2_CID_ZOOM_ABSOLUTE   (V4L2_CID_CAMERA_CLASS_BASE+13)
#define V4L2_CID_ZOOM_RELATIVE   (V4L2_CID_CAMERA_CLASS_BASE+14)
#define V4L2_CID_ZOOM_CONTINUOUS  (V4L2_CID_CAMERA_CLASS_BASE+15)

#define V4L2_CID_PRIVACY   (V4L2_CID_CAMERA_CLASS_BASE+16)

/*
 * T U N I N G
 */
struct v4l2_tuner {
 __u32                   index;
 __u8   name[32];
 enum v4l2_tuner_type    type;
 __u32   capability;
 __u32   rangelow;
 __u32   rangehigh;
 __u32   rxsubchans;
 __u32   audmode;
 __s32   signal;
 __s32   afc;
 __u32   reserved[4];
};

struct v4l2_modulator {
 __u32   index;
 __u8   name[32];
 __u32   capability;
 __u32   rangelow;
 __u32   rangehigh;
 __u32   txsubchans;
 __u32   reserved[4];
};

/*  Flags for the 'capability' field */
#define V4L2_TUNER_CAP_LOW  0x0001
#define V4L2_TUNER_CAP_NORM  0x0002
#define V4L2_TUNER_CAP_STEREO  0x0010
#define V4L2_TUNER_CAP_LANG2  0x0020
#define V4L2_TUNER_CAP_SAP  0x0020
#define V4L2_TUNER_CAP_LANG1  0x0040

/*  Flags for the 'rxsubchans' field */
#define V4L2_TUNER_SUB_MONO  0x0001
#define V4L2_TUNER_SUB_STEREO  0x0002
#define V4L2_TUNER_SUB_LANG2  0x0004
#define V4L2_TUNER_SUB_SAP  0x0004
#define V4L2_TUNER_SUB_LANG1  0x0008

/*  Values for the 'audmode' field */
#define V4L2_TUNER_MODE_MONO  0x0000
#define V4L2_TUNER_MODE_STEREO  0x0001
#define V4L2_TUNER_MODE_LANG2  0x0002
#define V4L2_TUNER_MODE_SAP  0x0002
#define V4L2_TUNER_MODE_LANG1  0x0003
#define V4L2_TUNER_MODE_LANG1_LANG2 0x0004

struct v4l2_frequency {
 __u32        tuner;
 enum v4l2_tuner_type  type;
 __u32        frequency;
 __u32        reserved[8];
};

struct v4l2_hw_freq_seek {
 __u32        tuner;
 enum v4l2_tuner_type  type;
 __u32        seek_upward;
 __u32        wrap_around;
 __u32        reserved[8];
};

/*
 * A U D I O
 */
struct v4l2_audio {
 __u32 index;
 __u8 name[32];
 __u32 capability;
 __u32 mode;
 __u32 reserved[2];
};

/*  Flags for the 'capability' field */
#define V4L2_AUDCAP_STEREO  0x00001
#define V4L2_AUDCAP_AVL   0x00002

/*  Flags for the 'mode' field */
#define V4L2_AUDMODE_AVL  0x00001

struct v4l2_audioout {
 __u32 index;
 __u8 name[32];
 __u32 capability;
 __u32 mode;
 __u32 reserved[2];
};

/*
 * M P E G   S E R V I C E S
 *
 * NOTE: EXPERIMENTAL API
 */
#if 1
#define V4L2_ENC_IDX_FRAME_I    (0)
#define V4L2_ENC_IDX_FRAME_P    (1)
#define V4L2_ENC_IDX_FRAME_B    (2)
#define V4L2_ENC_IDX_FRAME_MASK (0xf)

struct v4l2_enc_idx_entry {
 __u64 offset;
 __u64 pts;
 __u32 length;
 __u32 flags;
 __u32 reserved[2];
};

#define V4L2_ENC_IDX_ENTRIES (64)
struct v4l2_enc_idx {
 __u32 entries;
 __u32 entries_cap;
 __u32 reserved[4];
 struct v4l2_enc_idx_entry entry[V4L2_ENC_IDX_ENTRIES];
};


#define V4L2_ENC_CMD_START      (0)
#define V4L2_ENC_CMD_STOP       (1)
#define V4L2_ENC_CMD_PAUSE      (2)
#define V4L2_ENC_CMD_RESUME     (3)

/* Flags for V4L2_ENC_CMD_STOP */
#define V4L2_ENC_CMD_STOP_AT_GOP_END    (1 << 0)

struct v4l2_encoder_cmd {
 __u32 cmd;
 __u32 flags;
 union {
  struct {
   __u32 data[8];
  } raw;
 };
};

#endif


/*
 * D A T A   S E R V I C E S   ( V B I )
 *
 * Data services API by Michael Schimek
 */

/* Raw VBI */
struct v4l2_vbi_format {  /* 数据服务格式 */
 __u32 sampling_rate;  /* in 1 Hz */
 __u32 offset;
 __u32 samples_per_line;
 __u32 sample_format;  /* V4L2_PIX_FMT_* */
 __s32 start[2];
 __u32 count[2];
 __u32 flags;   /* V4L2_VBI_* */
 __u32 reserved[2];  /* must be zero */
};

/*  VBI flags  */
#define V4L2_VBI_UNSYNC  (1 << 0)
#define V4L2_VBI_INTERLACED (1 << 1)

/* Sliced VBI
 *
 *    This implements is a proposal V4L2 API to allow SLICED VBI
 * required for some hardware encoders. It should change without
 * notice in the definitive implementation.
 */

struct v4l2_sliced_vbi_format {
 __u16   service_set;
 /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
    service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
     (equals frame lines 313-336 for 625 line video
      standards, 263-286 for 525 line standards) */
 __u16   service_lines[2][24];
 __u32   io_size;
 __u32   reserved[2];            /* must be zero */
};

/* Teletext World System Teletext
   (WST), defined on ITU-R BT.653-2 */
#define V4L2_SLICED_TELETEXT_B          (0x0001)
/* Video Program System, defined on ETS 300 231*/
#define V4L2_SLICED_VPS                 (0x0400)
/* Closed Caption, defined on EIA-608 */
#define V4L2_SLICED_CAPTION_525         (0x1000)
/* Wide Screen System, defined on ITU-R BT1119.1 */
#define V4L2_SLICED_WSS_625             (0x4000)

#define V4L2_SLICED_VBI_525             (V4L2_SLICED_CAPTION_525)
#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)

struct v4l2_sliced_vbi_cap {
 __u16   service_set;
 /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
    service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
     (equals frame lines 313-336 for 625 line video
      standards, 263-286 for 525 line standards) */
 __u16   service_lines[2][24];
 enum v4l2_buf_type type;
 __u32   reserved[3];    /* must be 0 */
};

struct v4l2_sliced_vbi_data {
 __u32   id;
 __u32   field;          /* 0: first field, 1: second field */
 __u32   line;           /* 1-23 */
 __u32   reserved;       /* must be 0 */
 __u8    data[48];
};

/*
 * Sliced VBI data inserted into MPEG Streams
 */

/*
 * V4L2_MPEG_STREAM_VBI_FMT_IVTV:
 *
 * Structure of payload contained in an MPEG 2 Private Stream 1 PES Packet in an
 * MPEG-2 Program Pack that contains V4L2_MPEG_STREAM_VBI_FMT_IVTV Sliced VBI
 * data
 *
 * Note, the MPEG-2 Program Pack and Private Stream 1 PES packet header
 * definitions are not included here.  See the MPEG-2 specifications for details
 * on these headers.
 */

/* Line type IDs */
#define V4L2_MPEG_VBI_IVTV_TELETEXT_B     (1)
#define V4L2_MPEG_VBI_IVTV_CAPTION_525    (4)
#define V4L2_MPEG_VBI_IVTV_WSS_625        (5)
#define V4L2_MPEG_VBI_IVTV_VPS            (7)

struct v4l2_mpeg_vbi_itv0_line {
 __u8 id; /* One of V4L2_MPEG_VBI_IVTV_* above */
 __u8 data[42]; /* Sliced VBI data for the line */
} __attribute__ ((packed));

struct v4l2_mpeg_vbi_itv0 {
 __le32 linemask[2]; /* Bitmasks of VBI service lines present */
 struct v4l2_mpeg_vbi_itv0_line line[35];
} __attribute__ ((packed));

struct v4l2_mpeg_vbi_ITV0 {
 struct v4l2_mpeg_vbi_itv0_line line[36];
} __attribute__ ((packed));

#define V4L2_MPEG_VBI_IVTV_MAGIC0 "itv0"
#define V4L2_MPEG_VBI_IVTV_MAGIC1 "ITV0"

struct v4l2_mpeg_vbi_fmt_ivtv {
 __u8 magic[4];
 union {
  struct v4l2_mpeg_vbi_itv0 itv0;
  struct v4l2_mpeg_vbi_ITV0 ITV0;
 };
} __attribute__ ((packed));

/*
 * A G G R E G A T E   S T R U C T U R E S
 */

/* Stream data format
 */
struct v4l2_format {  /* 视频数据格式 */
 enum v4l2_buf_type type;  /* 总线类型 */
 union {
  struct v4l2_pix_format  pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */  /* 像素格式: V4L2_BUF_TYPE_VIDEO_CAPTURE*/
  struct v4l2_window  win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */  /* 窗口:V4L2_BUF_TYPE_VIDEO_OVERLAY */
  struct v4l2_vbi_format  vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */       /* 数据服务格式:V4L2_BUF_TYPE_VBI_CAPTURE */
  struct v4l2_sliced_vbi_format sliced;  /* *//* 分割数据的数据服务格式:V4L2_BUF_TYPE_SLICED_VBI_CAPTURE  */
  __u8 raw_data[200];                   /* user-defined */  /* 用户数据 */
 } fmt;
};


/* Stream type-dependent parameters
 */
struct v4l2_streamparm {  /* 视频流参数 */
 enum v4l2_buf_type type;  /* 数据流类型 */
 union {
  struct v4l2_captureparm capture;  /* 捕获参数 */
  struct v4l2_outputparm output;  /* 视频输出参数 */
  __u8 raw_data[200];  /* user-defined */
 } parm;
};

/*
 * A D V A N C E D   D E B U G G I N G
 *
 * NOTE: EXPERIMENTAL API, NEVER RELY ON THIS IN APPLICATIONS!
 * FOR DEBUGGING, TESTING AND INTERNAL USE ONLY!
 */

/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */

#define V4L2_CHIP_MATCH_HOST       0  /* Match against chip ID on host (0 for the host) */
#define V4L2_CHIP_MATCH_I2C_DRIVER 1  /* Match against I2C driver name */
#define V4L2_CHIP_MATCH_I2C_ADDR   2  /* Match against I2C 7-bit address */
#define V4L2_CHIP_MATCH_AC97       3  /* Match against anciliary AC97 chip */

struct v4l2_dbg_match { /* 调试匹配 */
 __u32 type; /* Match type */ /* 匹配类型 */
 union {     /* Match this chip, meaning determined by type */
  __u32 addr;  /* 地址 */
  char name[32];  /* 名字 */
 };
} __attribute__ ((packed));

struct v4l2_dbg_register {  /* 调试寄存器 */
 struct v4l2_dbg_match match;
 __u32 size; /* register size in bytes */ /* 寄存器大小 */
 __u64 reg;  /* 寄存器 */
 __u64 val;  /* 值 */
} __attribute__ ((packed));

/* VIDIOC_DBG_G_CHIP_IDENT */
struct v4l2_dbg_chip_ident {  /* 调试芯片识别 */
 struct v4l2_dbg_match match;
 __u32 ident;       /* chip identifier as specified in */
 __u32 revision;    /* chip revision, chip specific */
} __attribute__ ((packed));

/*
 * I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
 *
 */
 /* v4l2摄像头驱动程序在struct video_device->fops的ioctl桉树中通常需要实现如下的ioctl名 */
#define VIDIOC_QUERYCAP   _IOR('V',  0, struct v4l2_capability)  /* 查询驱动功能,看看设备具有什么功能 */
#define VIDIOC_RESERVED    _IO('V',  1)
#define VIDIOC_ENUM_FMT         _IOWR('V',  2, struct v4l2_fmtdesc)    /* 获取当前视频支持的视频格式 */
#define VIDIOC_G_FMT  _IOWR('V',  4, struct v4l2_format)         /* 读取当前驱动的视频捕获格式 */
#define VIDIOC_S_FMT  _IOWR('V',  5, struct v4l2_format)          /* 配置当前驱动的视频捕获格式 */
#define VIDIOC_REQBUFS  _IOWR('V',  8, struct v4l2_requestbuffers)  /* 向驱动申请缓冲区,一般不超过5个 */
#define VIDIOC_QUERYBUF  _IOWR('V',  9, struct v4l2_buffer)   /* 查询已经分配的v4l2的视频缓冲区的相关信息,包括视频缓冲
                                                                                                                  区的使用状态、在内核空间的偏移地址、缓冲区长度等(应用
                                                                                                                  程序通过该调用来获取内核的视频缓冲区信息,然后调用mmap
                                                                                                                  把内核空间地址映射到用户空间)*/
#define VIDIOC_G_FBUF   _IOR('V', 10, struct v4l2_framebuffer)
#define VIDIOC_S_FBUF   _IOW('V', 11, struct v4l2_framebuffer)
#define VIDIOC_OVERLAY   _IOW('V', 14, int)
#define VIDIOC_QBUF  _IOWR('V', 15, struct v4l2_buffer)     /*投放申请到的视频缓冲区到视频输入队列中*/
#define VIDIOC_DQBUF  _IOWR('V', 17, struct v4l2_buffer)  /*从视频缓冲输出队列获取一个保存有视频数据的视频缓冲区 */
                                                                                                         
#define VIDIOC_STREAMON   _IOW('V', 18, int)     /* 启动视频采集,应用程序调用VIDIOC_STREAMON启动视频采集命令后,
                                                                                            视频设备驱动程序开始采集视频数据,并把采集到的视频数据保存到
                                                                                            视频驱动的视频缓冲区中 */
                                                                                           
#define VIDIOC_STREAMOFF  _IOW('V', 19, int)            /* 停止视频的采集 */
#define VIDIOC_G_PARM  _IOWR('V', 21, struct v4l2_streamparm)
#define VIDIOC_S_PARM  _IOWR('V', 22, struct v4l2_streamparm)
#define VIDIOC_G_STD   _IOR('V', 23, v4l2_std_id)      /* 用来获取现在视频设备哪种视频标准是激活的(也就是video_device的current_norm字段的值 )*/
#define VIDIOC_S_STD   _IOW('V', 24, v4l2_std_id)     /* 用来设置视频标准 */
#define VIDIOC_ENUMSTD  _IOWR('V', 25, struct v4l2_standard)  /* 用于查询设备实现了哪些标准 (也就是video_device的tvnorm字段的值)*/
#define VIDIOC_ENUMINPUT _IOWR('V', 26, struct v4l2_input)       /* 用于查询设备支持的所有可用的输入 */
#define VIDIOC_G_CTRL  _IOWR('V', 27, struct v4l2_control)    /* 获取控制 */
#define VIDIOC_S_CTRL  _IOWR('V', 28, struct v4l2_control)    /* 设置控制  */
#define VIDIOC_G_TUNER  _IOWR('V', 29, struct v4l2_tuner)
#define VIDIOC_S_TUNER   _IOW('V', 30, struct v4l2_tuner)
#define VIDIOC_G_AUDIO   _IOR('V', 33, struct v4l2_audio)
#define VIDIOC_S_AUDIO   _IOW('V', 34, struct v4l2_audio)
#define VIDIOC_QUERYCTRL _IOWR('V', 36, struct v4l2_queryctrl)  /* 查询控制 */
#define VIDIOC_QUERYMENU _IOWR('V', 37, struct v4l2_querymenu)
#define VIDIOC_G_INPUT   _IOR('V', 38, int)
#define VIDIOC_S_INPUT  _IOWR('V', 39, int)     /* 配置输入通道,采用struct struct v4l2_input结构,针对多通道设备 */
#define VIDIOC_G_OUTPUT   _IOR('V', 46, int)
#define VIDIOC_S_OUTPUT  _IOWR('V', 47, int)
#define VIDIOC_ENUMOUTPUT _IOWR('V', 48, struct v4l2_output)
#define VIDIOC_G_AUDOUT   _IOR('V', 49, struct v4l2_audioout)
#define VIDIOC_S_AUDOUT   _IOW('V', 50, struct v4l2_audioout)
#define VIDIOC_G_MODULATOR _IOWR('V', 54, struct v4l2_modulator)
#define VIDIOC_S_MODULATOR  _IOW('V', 55, struct v4l2_modulator)
#define VIDIOC_G_FREQUENCY _IOWR('V', 56, struct v4l2_frequency)
#define VIDIOC_S_FREQUENCY  _IOW('V', 57, struct v4l2_frequency)
#define VIDIOC_CROPCAP  _IOWR('V', 58, struct v4l2_cropcap)  /* 查询驱动的修剪能力 */
#define VIDIOC_G_CROP  _IOWR('V', 59, struct v4l2_crop)       /* 读取视频信号的边框(窗口参数) */
#define VIDIOC_S_CROP   _IOW('V', 60, struct v4l2_crop)        /* 配置视频信号的边框 (窗口参数) */
#define VIDIOC_G_JPEGCOMP  _IOR('V', 61, struct v4l2_jpegcompression)
#define VIDIOC_S_JPEGCOMP  _IOW('V', 62, struct v4l2_jpegcompression)
#define VIDIOC_QUERYSTD        _IOR('V', 63, v4l2_std_id)                /* 检查当前视频设备支持的标准例如PAL或NTSC (在亚洲,一般使用
                                                                                                            PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480))*/
#define VIDIOC_TRY_FMT       _IOWR('V', 64, struct v4l2_format)  /* 验证当前驱动的显示格式 */
#define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio)
#define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout)
#define VIDIOC_G_PRIORITY        _IOR('V', 67, enum v4l2_priority)
#define VIDIOC_S_PRIORITY        _IOW('V', 68, enum v4l2_priority)
#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap)
#define VIDIOC_LOG_STATUS         _IO('V', 70)
#define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls)
#define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls)
#define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls)
#if 1
#define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum)
#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum)
#define VIDIOC_G_ENC_INDEX       _IOR('V', 76, struct v4l2_enc_idx)
#define VIDIOC_ENCODER_CMD      _IOWR('V', 77, struct v4l2_encoder_cmd)
#define VIDIOC_TRY_ENCODER_CMD  _IOWR('V', 78, struct v4l2_encoder_cmd)
#endif

#if 1
/* Experimental, meant for debugging, testing and internal use.
   Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined.
   You must be root to use these ioctls. Never use these in applications! */
#define VIDIOC_DBG_S_REGISTER   _IOW('V', 79, struct v4l2_dbg_register)
#define VIDIOC_DBG_G_REGISTER  _IOWR('V', 80, struct v4l2_dbg_register)

/* Experimental, meant for debugging, testing and internal use.
   Never use this ioctl in applications! */
#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)
#endif

#define VIDIOC_S_HW_FREQ_SEEK  _IOW('V', 82, struct v4l2_hw_freq_seek)
/* Reminder: when adding new ioctls please add support for them to
   drivers/media/video/v4l2-compat-ioctl32.c as well! */

#ifdef __OLD_VIDIOC_
/* for compatibility, will go away some day */
#define VIDIOC_OVERLAY_OLD      _IOWR('V', 14, int)
#define VIDIOC_S_PARM_OLD        _IOW('V', 22, struct v4l2_streamparm)
#define VIDIOC_S_CTRL_OLD        _IOW('V', 28, struct v4l2_control)
#define VIDIOC_G_AUDIO_OLD      _IOWR('V', 33, struct v4l2_audio)
#define VIDIOC_G_AUDOUT_OLD     _IOWR('V', 49, struct v4l2_audioout)
#define VIDIOC_CROPCAP_OLD       _IOR('V', 58, struct v4l2_cropcap)
#endif

#define BASE_VIDIOC_PRIVATE 192  /* 192-255 are private */

#endif /* __LINUX_VIDEODEV2_H */
/*************************************************************************************************************************************/
/* v4l2-dev.c */

/*
 * Video capture interface for Linux version 2
 *
 * A generic video device interface for the LINUX operating system
 * using a set of device structures/vectors for low level operations.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * Authors: Alan Cox, <> (version 1)
 *              Mauro Carvalho Chehab <> (version 2)
 *
 * Fixes: 20000516  Claudio Matsuoka <>
 *  - Added procfs support
 */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include

#define VIDEO_NUM_DEVICES 256   /*视频设备的数量 */
#define VIDEO_NAME              "video4linux"

/*
 * sysfs stuff
 */

static ssize_t show_index(struct device *cd,
    struct device_attribute *attr, char *buf)
{
 struct video_device *vdev = to_video_device(cd);

 return sprintf(buf, "%i\n", vdev->index);
}

static ssize_t show_name(struct device *cd,
    struct device_attribute *attr, char *buf)
{
 struct video_device *vdev = to_video_device(cd);

 return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}

static struct device_attribute video_device_attrs[] = {  /* 设备属性 */
 __ATTR(name, S_IRUGO, show_name, NULL),
 __ATTR(index, S_IRUGO, show_index, NULL),
 __ATTR_NULL
};

/*
 * Active devices
 */
static struct video_device *video_device[VIDEO_NUM_DEVICES]; /* 用于存放注册进来的不同的视频设备 */
static DEFINE_MUTEX(videodev_lock);

/*
   #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

   #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, 8 * sizeof(long))   // 将一个bit转换为long

   #define DECLARE_BITMAP(name,bits) \
 unsigned long name[BITS_TO_LONGS(bits)]   //定义位映射
          ↓
   #define DECLARE_BITMAP(video_nums[4],256)  unsigned long video_nums[4][BITS_TO_LONGS(256)]   
                ↓
   unsigned long video_nums[4][DIV_ROUND_UP(256, 8 * 4)]   
                ↓
   unsigned long video_nums[4][(((256) + (32) - 1) / (32)) ]  
                ↓
    unsigned long video_nums[4][8 ]               
  */

static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);  /* 定义一个位图 ,相当于定义了static  unsigned long video_nums[4][8 ]*/

struct video_device *video_device_alloc(void)  /*  分配一个struct video_device 结构体*/
{
 return kzalloc(sizeof(struct video_device), GFP_KERNEL);
}
EXPORT_SYMBOL(video_device_alloc);

void video_device_release(struct video_device *vdev)
{
 kfree(vdev);  /* 释放设备 */
}
EXPORT_SYMBOL(video_device_release);

void video_device_release_empty(struct video_device *vdev)
{
 /* Do nothing */
 /* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL(video_device_release_empty);

/* 增加设备的引用计数 */
static inline void video_get(struct video_device *vdev)
{
 get_device(&vdev->dev);
}
/* 减小设备的引用计数 */
static inline void video_put(struct video_device *vdev)
{
 put_device(&vdev->dev);
}

/* Called when the last user of the video device exits. */
static void v4l2_device_release(struct device *cd)  /* 释放函数,由设备驱动模型核心调用(当设备引用计数等于0时) */
{
 struct video_device *vdev = to_video_device(cd);

 mutex_lock(&videodev_lock);
 if (video_device[vdev->minor] != vdev) {  /* 判断数组项中的对应的项是否和设备匹配 */
  mutex_unlock(&videodev_lock);
  /* should not happen */
  WARN_ON(1);
  return;
 }

 /* Free up this device for reuse */
 video_device[vdev->minor] = NULL; /* 将数组项对应项设置为0 */

 /* Delete the cdev on this minor as well */
 cdev_del(vdev->cdev); /* 注销该字符设备 */
 /* Just in case some driver tries to access this from
    the release() callback. */
 vdev->cdev = NULL;

 /* Mark minor as free */
 clear_bit(vdev->num, video_nums[vdev->vfl_type]);  /* 清除位图中对应的位 */

 mutex_unlock(&videodev_lock);

 /* Release video_device and perform other
    cleanups as needed. */
 vdev->release(vdev); /* 调用设备的release函数来释放设备 */
}

static struct class video_class = {  /* 定义一个类 */
 .name = VIDEO_NAME,
 .dev_attrs = video_device_attrs,
};

struct video_device *video_devdata(struct file *file)
{
 return video_device[iminor(file->f_path.dentry->d_inode)];  /* 获取视频设备在全局数组 video_device[]中相应的项*/
}
EXPORT_SYMBOL(video_devdata);

static ssize_t v4l2_read(struct file *filp, char __user *buf,
  size_t sz, loff_t *off)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->read)
  return -EINVAL;
 if (video_is_unregistered(vdev))
  return -EIO;
 return vdev->fops->read(filp, buf, sz, off);
}

static ssize_t v4l2_write(struct file *filp, const char __user *buf,
  size_t sz, loff_t *off)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->write)
  return -EINVAL;
 if (video_is_unregistered(vdev))
  return -EIO;
 return vdev->fops->write(filp, buf, sz, off);
}

static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->poll || video_is_unregistered(vdev))
  return DEFAULT_POLLMASK;
 return vdev->fops->poll(filp, poll);
}

static int v4l2_ioctl(struct inode *inode, struct file *filp,
  unsigned int cmd, unsigned long arg)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->ioctl)
  return -ENOTTY;
 /* Allow ioctl to continue even if the device was unregistered.
    Things like dequeueing buffers might still be useful. */
 return vdev->fops->ioctl(filp, cmd, arg);
}

static long v4l2_unlocked_ioctl(struct file *filp,
  unsigned int cmd, unsigned long arg)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->unlocked_ioctl)
  return -ENOTTY;
 /* Allow ioctl to continue even if the device was unregistered.
    Things like dequeueing buffers might still be useful. */
 return vdev->fops->unlocked_ioctl(filp, cmd, arg);
}

#ifdef CONFIG_MMU
#define v4l2_get_unmapped_area NULL
#else
static unsigned long v4l2_get_unmapped_area(struct file *filp,
  unsigned long addr, unsigned long len, unsigned long pgoff,
  unsigned long flags)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->get_unmapped_area)
  return -ENOSYS;
 if (video_is_unregistered(vdev))
  return -ENODEV;
 return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
}
#endif

static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
 struct video_device *vdev = video_devdata(filp);

 if (!vdev->fops->mmap ||
     video_is_unregistered(vdev))
  return -ENODEV;
 return vdev->fops->mmap(filp, vm);
}

/* Override for the open function */
static int v4l2_open(struct inode *inode, struct file *filp)
{
 struct video_device *vdev;
 int ret = 0;

 /* Check if the video device is available */
 mutex_lock(&videodev_lock);
 vdev = video_devdata(filp);   /* 获取视频设备在全局数组 video_device[]中相应的项*/
 /* return ENODEV if the video device has been removed
    already or if it is not registered anymore. */
 if (vdev == NULL || video_is_unregistered(vdev)) {  /* 如果获取的数组项为NULL或者设备已经注销了则返回错误值 */
  mutex_unlock(&videodev_lock);
  return -ENODEV;
 }
 /* and increase the device refcount */
 video_get(vdev);  /* 增加设备的引用计数 */
 mutex_unlock(&videodev_lock);
 if (vdev->fops->open)  /**/
  ret = vdev->fops->open(filp);  /* 调用视频设备的打开函数 */

 /* decrease the refcount in case of an error */
 if (ret)
  video_put(vdev);
 return ret;
}

/* Override for the release function */
static int v4l2_release(struct inode *inode, struct file *filp)
{
 struct video_device *vdev = video_devdata(filp);
 int ret = 0;

 if (vdev->fops->release)
  vdev->fops->release(filp);  /* 调用视频设备的操作函数即 vdev->fops->release*/

 /* decrease the refcount unconditionally since the release()
    return value is ignored. */
 video_put(vdev);  /* 减小设备的引用计数 */
 return ret;
}

static const struct file_operations v4l2_unlocked_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_unlocked_ioctl,
#ifdef CONFIG_COMPAT
 .compat_ioctl = v4l2_compat_ioctl32,
#endif
 .release = v4l2_release,
 .poll = v4l2_poll,
 .llseek = no_llseek,
};

static const struct file_operations v4l2_fops = {  /* 视频设备的操作函数(视频设备作为字符设备存在的) -----这些函数实际是调用
                                                                        对应的我们注册的视频的操作函数即struct video_device *vdev->fops字段相应的函数来
                                                                        完成具体的操作的*/
 .owner = THIS_MODULE,
 .read = v4l2_read,
 .write = v4l2_write,
 .open = v4l2_open,
 .get_unmapped_area = v4l2_get_unmapped_area,
 .mmap = v4l2_mmap,
 .ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
 .compat_ioctl = v4l2_compat_ioctl32,
#endif
 .release = v4l2_release,
 .poll = v4l2_poll,
 .llseek = no_llseek,
};

/**
 * get_index - assign stream number based on parent device
 * @vdev: video_device to assign index number to, vdev->parent should be assigned
 * @num:  -1 if auto assign, requested number otherwise
 *
 * Note that when this is called the new device has not yet been registered
 * in the video_device array.
 *
 * Returns -ENFILE if num is already in use, a free index number if
 * successful.
 */
 /* 分配引索号基于父设备 */
static int get_index(struct video_device *vdev, int num)
{
 /* This can be static since this function is called with the global
    videodev_lock held. */
 static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
 int i;

 if (num >= VIDEO_NUM_DEVICES) {
  printk(KERN_ERR "videodev: %s num is too large\n", __func__);
  return -EINVAL;
 }

 /* Some drivers do not set the parent. In that case always return
    num or 0. */
 if (vdev->parent == NULL)
  return num >= 0 ? num : 0;

 bitmap_zero(used, VIDEO_NUM_DEVICES);

 for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
  if (video_device[i] != NULL &&
      video_device[i]->parent == vdev->parent) {
   set_bit(video_device[i]->index, used);
  }
 }

 if (num >= 0) {
  if (test_bit(num, used))
   return -ENFILE;
  return num;
 }

 i = find_first_zero_bit(used, VIDEO_NUM_DEVICES);
 return i == VIDEO_NUM_DEVICES ? -ENFILE : i;
}
/* 注册一个v4L2驱动程序 */
int video_register_device(struct video_device *vdev, int type, int nr)
{
 return video_register_device_index(vdev, type, nr, -1);
}
EXPORT_SYMBOL(video_register_device);

/**
 * video_register_device_index - register video4linux devices
 * @vdev: video device structure we want to register
 * @type: type of device to register
 * @nr:   which device number (0 == /dev/video0, 1 == /dev/video1, ...
 *             -1 == first free)
 * @index: stream number based on parent device;
 *  -1 if auto assign, requested number otherwise
 *
 * The registration code assigns minor numbers based on the type
 * requested. -ENFILE is returned in all the device slots for this
 * category are full. If not then the minor field is set and the
 * driver initialize function is called (if non %NULL).
 *
 * Zero is returned on success.
 *
 * Valid types are
 *
 * %VFL_TYPE_GRABBER - A frame grabber
 *
 * %VFL_TYPE_VTX - A teletext device
 *
 * %VFL_TYPE_VBI - Vertical blank data (undecoded)
 *
 * %VFL_TYPE_RADIO - A radio card
 */
 /* 注册视频设备
     该函数的主要内容如下:
     1:判断struct video_device *vdev->release函数指针有无初始化,如果没有初始化则做出错处理
     2:根据参数type(设备类型)设置设备的名称(name_base),并将参数type赋值给vdev->vfl_type字段
     3:如果vdev->v4l2_dev->dev有定义则设置其为本设备的父设备
     4:根据参数type为设备寻址一个可用的子设备号
     5: 在寻找位图 video_nums[type]中的下一个0bit位并将其在位图中对应的偏移赋值给参数nr,并判断nr与设置的
         子设备号是否有冲突,如果有则出错处理
     6: 在全局数组video_device[]寻找一个空项并用i记录其在数组中对应的下标
     7:设置 设备的次设备号vdev->minor =该设备在v数组ideo_device[]中对应的项的下标+次设备号偏移
     8:设置vdev->num = nr
     10:获取设备的引索号并复制给vdev->index
     11:分配一个字符设备,并赋值给vdev->cdev
     12:设置视频设备的操作函数:vdev->cdev->ops = &v4l2_fops;
     13:注册该字符设备:
     14:将设备的私有数据保存到vdev->dev->dev->driver_data
     15:设置设备的名字为name_base
     16: 向设备驱动模型核心注册设备:device_register(&vdev->dev
     17: 设置释放设备函数(这是必须的) :vdev->dev.release = v4l2_device_release
     18:将注册的设备放入全局设备数组video_device[] 中,以次设备号为下标:video_device[vdev->minor] = vdev
     */
int video_register_device_index(struct video_device *vdev, int type, int nr,
     int index)
{
 int i = 0;
 int ret;
 int minor_offset = 0;  /* 次设备号偏移 */
 int minor_cnt = VIDEO_NUM_DEVICES;  /* =256 */
 const char *name_base;
 void *priv = video_get_drvdata(vdev);  /* 获取私有数据 */

 /* A minor value of -1 marks this video device as never
    having been registered */
 vdev->minor = -1;  /* 标识该视频设备没有被注册 */

 /* the release callback MUST be present */
 WARN_ON(!vdev->release);
 if (!vdev->release) /* struct video_device *vdev->release一定要定义 */
  return -EINVAL;

 /* Part 1: check device type */
 switch (type) {
 case VFL_TYPE_GRABBER:  /* 视频设备----视频捕获接口 (视频捕获接口还有一个变体,存在于video overlay interface(视频覆盖接口),
                                               它的工作是方便视频显示设备直接从捕获设备上获取数据,视频数据直接从捕获设备传
                                               到显示设备,无需经过CPU)*/
  name_base = "video";
  break;
 case VFL_TYPE_VTX:  /* 视频传输设备----视频输出接口 */
  name_base = "vtx";
  break;
 case VFL_TYPE_VBI:  /* 从视频消隐时间段获取信息的设备----垂直消隐接口,其提供垂直消隐期的数据接入,这个接口
                                      包括raw和sliced两种接口,其分别用于在硬件中处理的VBI数据量*/
  name_base = "vbi";
  break;
 case VFL_TYPE_RADIO:  /* 广播设备----广播接口,用于从AM或FM调谐器中获取音频数据 */
  name_base = "radio";
  break;
 default:
  printk(KERN_ERR "%s called with unknown type: %d\n",
         __func__, type);
  return -EINVAL;
 }

 vdev->vfl_type = type;  /* 初始化 */
 vdev->cdev = NULL;
 if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
  vdev->parent = vdev->v4l2_dev->dev;  /* 如果vdev->v4l2_dev->dev有定义则设置其为本设备的父设备 */

 /* Part 2: find a free minor, kernel number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
 /* Keep the ranges for the first four types for historical
  * reasons.
  * Newer devices (not yet in place) should use the range
  * of 128-191 and just pick the first free minor there
  * (new style). */
  /* 为设备寻址一个可用的子设备号 */
 switch (type) {
 case VFL_TYPE_GRABBER:
  minor_offset = 0;
  minor_cnt = 64;
  break;
 case VFL_TYPE_RADIO:
  minor_offset = 64;
  minor_cnt = 64;
  break;
 case VFL_TYPE_VTX:
  minor_offset = 192;
  minor_cnt = 32;
  break;
 case VFL_TYPE_VBI:
  minor_offset = 224;
  minor_cnt = 32;
  break;
 default:
  minor_offset = 128;
  minor_cnt = 64;
  break;
 }
#endif

 /* Pick a minor number */
 mutex_lock(&videodev_lock);
 nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);  /* 寻找位图 video_nums[type]中的下一个0bit位*/
 if (nr == minor_cnt)
  nr = find_first_zero_bit(video_nums[type], minor_cnt);
 if (nr == minor_cnt) {
  printk(KERN_ERR "could not get a free kernel number\n");
  mutex_unlock(&videodev_lock);
  return -ENFILE;
 }
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
 /* 1-on-1 mapping of kernel number to minor number */
 i = nr;
#else
 /* The kernel number and minor numbers are independent */
 for (i = 0; i < VIDEO_NUM_DEVICES; i++)
  if (video_device[i] == NULL)
   break;                           /* 在全局数组video_device[]寻找一个空项 */
 if (i == VIDEO_NUM_DEVICES) {   /* 如果i=256则说明已经达到设备数的最大容量则做出错处理 */
  mutex_unlock(&videodev_lock);
  printk(KERN_ERR "could not get a free minor\n");
  return -ENFILE;
 }
#endif
 vdev->minor = i + minor_offset;  /* 次设备号=该设备在v数组ideo_device[]中对应的项的下标+次设备号偏移 */
 vdev->num = nr; 
 set_bit(nr, video_nums[type]);  /* 将位图中相应的位设置为1 */
 /* Should not happen since we thought this minor was free */
 WARN_ON(video_device[vdev->minor] != NULL);
 ret = vdev->index = get_index(vdev, index);  /* 获取设备引索号 */
 mutex_unlock(&videodev_lock);

 if (ret < 0) {
  printk(KERN_ERR "%s: get_index failed\n", __func__);
  goto cleanup;
 }

 /* Part 3: Initialize the character device */
 vdev->cdev = cdev_alloc();  /* 分配一个字符设备 */
 if (vdev->cdev == NULL) {
  ret = -ENOMEM;
  goto cleanup;
 }
 if (vdev->fops->unlocked_ioctl)
  vdev->cdev->ops = &v4l2_unlocked_fops;
 else
  vdev->cdev->ops = &v4l2_fops;  /* 设置操作函数 */
 vdev->cdev->owner = vdev->fops->owner;
 ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);  /* 注册(添加)字符设备 */
 if (ret < 0) {
  printk(KERN_ERR "%s: cdev_add failed\n", __func__);
  kfree(vdev->cdev);
  vdev->cdev = NULL;
  goto cleanup;
 }

 /* Part 4: register the device with sysfs */
 memset(&vdev->dev, 0, sizeof(vdev->dev));
 /* The memset above cleared the device's drvdata, so
    put back the copy we made earlier. */
 video_set_drvdata(vdev, priv);  /* 将私有数据保存到vdev->dev->dev->driver_data */
 vdev->dev.class = &video_class;   /* 设置类 */
 vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);   /* 初始化设备模型核心的次设备号 */
 if (vdev->parent)
  vdev->dev.parent = vdev->parent; /* 设置父设备 */
 dev_set_name(&vdev->dev, "%s%d", name_base, nr); /* 设置设备的名字 */
 ret = device_register(&vdev->dev);  /* 向设备驱动模型核心注册设备 */
 if (ret < 0) {
  printk(KERN_ERR "%s: device_register failed\n", __func__);
  goto cleanup;
 }
 /* Register the release callback that will be called when the last
    reference to the device goes away. */
 vdev->dev.release = v4l2_device_release;  /* 设置释放设备函数(这是必须的) */

 /* Part 5: Activate this minor. The char device can now be used. */
 mutex_lock(&videodev_lock);
 video_device[vdev->minor] = vdev;  /* 将注册的设备放入全局设备数组video_device[] 中,以次设备号为下标*/
 mutex_unlock(&videodev_lock);
 return 0;

cleanup:
 mutex_lock(&videodev_lock);
 if (vdev->cdev)
  cdev_del(vdev->cdev);
 clear_bit(vdev->num, video_nums[type]);
 mutex_unlock(&videodev_lock);
 /* Mark this video device as never having been registered. */
 vdev->minor = -1;
 return ret;
}
EXPORT_SYMBOL(video_register_device_index);

/**
 * video_unregister_device - unregister a video4linux device
 * @vdev: the device to unregister
 *
 * This unregisters the passed device. Future open calls will
 * be met with errors.
 */
 /* 注销视频设备 */
void video_unregister_device(struct video_device *vdev)
{
 /* Check if vdev was ever registered at all */
 if (!vdev || vdev->minor < 0)
  return;

 mutex_lock(&videodev_lock);
 set_bit(V4L2_FL_UNREGISTERED, &vdev->flags); 
 mutex_unlock(&videodev_lock);
 device_unregister(&vdev->dev);/* 向驱动模型核心注销设备 */
}
EXPORT_SYMBOL(video_unregister_device);

/*
 * Initialise video for linux
 */
static int __init videodev_init(void)
{
 dev_t dev = MKDEV(VIDEO_MAJOR, 0);  /* 装配设备编号(主设备号为81,次设备号为0) */
 int ret;

 printk(KERN_INFO "Linux video capture interface: v2.00\n");
 ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);  /* 分配256个设备编号 */
 if (ret < 0) {
  printk(KERN_WARNING "videodev: unable to get major %d\n",
    VIDEO_MAJOR);
  return ret;
 }

 ret = class_register(&video_class);  /* 注册一个类 */
 if (ret < 0) {
  unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
  printk(KERN_WARNING "video_dev: class_register failed\n");
  return -EIO;
 }

 return 0;
}

static void __exit videodev_exit(void)
{
 dev_t dev = MKDEV(VIDEO_MAJOR, 0);

 class_unregister(&video_class);
 unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
}

module_init(videodev_init)
module_exit(videodev_exit)

MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <>");
MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);


/*
 * Local variables:
 * c-basic-offset: 8
 * End:
 */
/*************************************************************************************************************************************/
/* v4l2-device.c */

/*
    V4L2 device support.

    Copyright (C) 2008  Hans Verkuil <>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include
#include
#include
#include
#include

/* 注册v4l2设备
    参数dev不能为NULL*/
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
 if (v4l2_dev == NULL)
  return -EINVAL;

 INIT_LIST_HEAD(&v4l2_dev->subdevs);  /* 初始化链表头v4l2_dev->subdevs */
 spin_lock_init(&v4l2_dev->lock);
 v4l2_dev->dev = dev;
 if (dev == NULL) {
  /* If dev == NULL, then name must be filled in by the caller */
  WARN_ON(!v4l2_dev->name[0]);
  return 0;
 }

 /* Set name to driver name + device name if it is empty. */
 if (!v4l2_dev->name[0])
  snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
   dev->driver->name, dev_name(dev));
 if (dev_get_drvdata(dev))
  v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n");
 dev_set_drvdata(dev, v4l2_dev);  /* 将v4l2_dev保存到dev->driver_data字段 */
 return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register);

/* 取消v4l2设备连接 */
void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
{
 if (v4l2_dev->dev) {
  dev_set_drvdata(v4l2_dev->dev, NULL); /* v4l2_dev->dev->driver_data=NULL */
  v4l2_dev->dev = NULL;
 }
}
EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
/* 注销v4l2设备 */
void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
{
 struct v4l2_subdev *sd, *next;

 if (v4l2_dev == NULL)
  return;
 v4l2_device_disconnect(v4l2_dev);

 /* Unregister subdevs */
 list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list)
  v4l2_device_unregister_subdev(sd);  /* 遍历链表v4l2_dev->subdevs,注销链表上的每个子设备 */
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);

/* 注册v4l2子设备 */
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
      struct v4l2_subdev *sd)
{
 /* Check for valid input */
 if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
  return -EINVAL;
 /* Warn if we apparently re-register a subdev */
 WARN_ON(sd->v4l2_dev != NULL);
 if (!try_module_get(sd->owner))
  return -ENODEV;
 sd->v4l2_dev = v4l2_dev;
 spin_lock(&v4l2_dev->lock);
 list_add_tail(&sd->list, &v4l2_dev->subdevs);  /* 添加4l2_dev->subdevs链表的尾部 */
 spin_unlock(&v4l2_dev->lock);
 return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);

/*  注销v4l2子设备 */
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
{
 /* return if it isn't registered */
 if (sd == NULL || sd->v4l2_dev == NULL)
  return;
 spin_lock(&sd->v4l2_dev->lock);
 list_del(&sd->list); /* 从链表中删除 */
 spin_unlock(&sd->v4l2_dev->lock);
 sd->v4l2_dev = NULL;
 module_put(sd->owner);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
/*************************************************************************************************************************************/
/* v4l2-common.c */

/* v4l2 i2c子设备初始化 */
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
  const struct v4l2_subdev_ops *ops)
{
 v4l2_subdev_init(sd, ops); /*初始化i2c子设备 */
 /* the owner is the same as the i2c_client's driver owner */
 sd->owner = client->driver->driver.owner;
 /* i2c_client and v4l2_subdev point to one another */
 v4l2_set_subdevdata(sd, client);  /* 将client保存到sd->priv */
 i2c_set_clientdata(client, sd);  /* 把sd存放在struct i2c_client *dev->dev->driver_data */
 /* initialize name */
 snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
  client->driver->driver.name, i2c_adapter_id(client->adapter),
  client->addr);
}
EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);

 

/* Load an i2c sub-device. */
/* 加载一个i2c子设备 */
struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
  struct i2c_adapter *adapter,
  const char *module_name, const char *client_type, u8 addr)
{
 struct v4l2_subdev *sd = NULL;
 struct i2c_client *client;
 struct i2c_board_info info;

 BUG_ON(!v4l2_dev);

 if (module_name)
  request_module(module_name);

 /* Setup the i2c board info with the device type and
    the device address. */
 memset(&info, 0, sizeof(info));
 strlcpy(info.type, client_type, sizeof(info.type));
 info.addr = addr;

 /* Create the i2c client */
 client = i2c_new_device(adapter, &info);  /* 创建一个i2c_client并将设备添加到设备链表 */
 /* Note: it is possible in the future that
    c->driver is NULL if the driver is still being loaded.
    We need better support from the kernel so that we
    can easily wait for the load to finish. */
 if (client == NULL || client->driver == NULL)
  goto error;

 /* Lock the module so we can safely get the v4l2_subdev pointer */
 if (!try_module_get(client->driver->driver.owner))
  goto error;
 sd = i2c_get_clientdata(client);

 /* Register with the v4l2_device which increases the module's
    use count as well. */
 if (v4l2_device_register_subdev(v4l2_dev, sd))  /* 注册v4l2子设备 */
  sd = NULL;
 /* Decrease the module use count to match the first try_module_get. */
 module_put(client->driver->driver.owner);

error:
 /* If we have a client but no subdev, then something went wrong and
    we must unregister the client. */
 if (client && sd == NULL)
  i2c_unregister_device(client);
 return sd;
}
EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);

/* Probe and load an i2c sub-device. */
/* 探测并且加载一个i2c子设备 */
struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev,
 struct i2c_adapter *adapter,
 const char *module_name, const char *client_type,
 const unsigned short *addrs)
{
 struct v4l2_subdev *sd = NULL;
 struct i2c_client *client = NULL;
 struct i2c_board_info info;

 BUG_ON(!v4l2_dev);

 if (module_name)
  request_module(module_name);

 /* Setup the i2c board info with the device type and
    the device address. */
 memset(&info, 0, sizeof(info));
 strlcpy(info.type, client_type, sizeof(info.type));

 /* Probe and create the i2c client */
 client = i2c_new_probed_device(adapter, &info, addrs); /* 为适配器探测新的从设备 */
 /* Note: it is possible in the future that
    c->driver is NULL if the driver is still being loaded.
    We need better support from the kernel so that we
    can easily wait for the load to finish. */
 if (client == NULL || client->driver == NULL)
  goto error;

 /* Lock the module so we can safely get the v4l2_subdev pointer */
 if (!try_module_get(client->driver->driver.owner))
  goto error;
 sd = i2c_get_clientdata(client);

 /* Register with the v4l2_device which increases the module's
    use count as well. */
 if (v4l2_device_register_subdev(v4l2_dev, sd))  /* 注册v4l2子设备 */
  sd = NULL;
 /* Decrease the module use count to match the first try_module_get. */
 module_put(client->driver->driver.owner);

error:
 /* If we have a client but no subdev, then something went wrong and
    we must unregister the client. */
 if (client && sd == NULL)
  i2c_unregister_device(client);
 return sd;
}
EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev);

/* 探测v4l2子设备 */
struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev,
  struct i2c_adapter *adapter,
  const char *module_name, const char *client_type, u8 addr)
{
 unsigned short addrs[2] = { addr, I2C_CLIENT_END };

 return v4l2_i2c_new_probed_subdev(v4l2_dev, adapter,
   module_name, client_type, addrs);
}
EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr);

/* Return i2c client address of v4l2_subdev. */
/* 获取i2c设备地址*/
unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
{
 struct i2c_client *client = v4l2_get_subdevdata(sd);

 return client ? client->addr : I2C_CLIENT_END;
}
EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);

/* Return a list of I2C tuner addresses to probe. Use only if the tuner
   addresses are unknown. */
const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
{
 static const unsigned short radio_addrs[] = {
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
  0x10,
#endif
  0x60,
  I2C_CLIENT_END
 };
 static const unsigned short demod_addrs[] = {
  0x42, 0x43, 0x4a, 0x4b,
  I2C_CLIENT_END
 };
 static const unsigned short tv_addrs[] = {
  0x42, 0x43, 0x4a, 0x4b,  /* tda8290 */
  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
  I2C_CLIENT_END
 };

 switch (type) {
 case ADDRS_RADIO:
  return radio_addrs;
 case ADDRS_DEMOD:
  return demod_addrs;
 case ADDRS_TV:
  return tv_addrs;
 case ADDRS_TV_WITH_DEMOD:
  return tv_addrs + 4;
 }
 return NULL;
}
EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
/*************************************************************************************************************************************/
/* videobuf-core.c */

/*
 * generic helper functions for handling video4linux capture buffers
 *
 * (c) 2007 Mauro Carvalho Chehab, <>
 *
 * Highly based on video-buf written originally by:
 * (c) 2001,02 Gerd Knorr <>
 * (c) 2006 Mauro Carvalho Chehab, <>
 * (c) 2006 Ted Walther and John Sokol
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2
 */

#include
#include
#include
#include
#include
#include

#include

#define MAGIC_BUFFER 0x20070728
#define MAGIC_CHECK(is, should) do {        \
 if (unlikely((is) != (should))) {       \
 printk(KERN_ERR "magic mismatch: %x (expected %x)\n", is, should); \
 BUG(); } } while (0)

static int debug;
module_param(debug, int, 0644);

MODULE_DESCRIPTION("helper module to manage video4linux buffers");
MODULE_AUTHOR("Mauro Carvalho Chehab <>");
MODULE_LICENSE("GPL");

#define dprintk(level, fmt, arg...) do {   \
 if (debug >= level)      \
 printk(KERN_DEBUG "vbuf: " fmt , ## arg); } while (0)

/* --------------------------------------------------------------------- */

#define CALL(q, f, arg...)      \
 ((q->int_ops->f) ? q->int_ops->f(arg) : 0)  /* 调用struct videobuf_queue *q ->int_ops相应的函数*/

/* 分配缓冲区 */
void *videobuf_alloc(struct videobuf_queue *q)
{
 struct videobuf_buffer *vb;

 BUG_ON(q->msize < sizeof(*vb));

 if (!q->int_ops || !q->int_ops->alloc) {
  printk(KERN_ERR "No specific ops defined!\n");
  BUG();
 }

 vb = q->int_ops->alloc(q->msize);  /* 调用struct videobuf_queue *q->int_ops->alloc函数来分配缓冲区 */

 if (NULL != vb) {
  init_waitqueue_head(&vb->done);  /* 如果分配不成功的话则初始化等待队列头vb->done */
  vb->magic     = MAGIC_BUFFER;
 }

 return vb;
}

#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\
    vb->state != VIDEOBUF_QUEUED)   /* 服务条件 */
/* 等待服务 */
int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
{
 MAGIC_CHECK(vb->magic, MAGIC_BUFFER);

 if (non_blocking) {
  if (WAITON_CONDITION)  /* 服务条件不满足则返回 */
   return 0;
  else
   return -EAGAIN;
 }

 if (intr)
  return wait_event_interruptible(vb->done, WAITON_CONDITION);  /* 休眠在 vb->done的进程直到WAITON_CONDITION为真*/
 else
  wait_event(vb->done, WAITON_CONDITION); 

 return 0;
}

int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
      struct v4l2_framebuffer *fbuf)
{
 MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 return CALL(q, iolock, q, vb, fbuf);  /* 调用 struct videobuf_queue *q->iolock函数*/
}

/* 使用vmalloc分配缓冲区 */
void *videobuf_queue_to_vmalloc (struct videobuf_queue *q,
      struct videobuf_buffer *buf)
{
 if (q->int_ops->vmalloc)
  return q->int_ops->vmalloc(buf);  /* 调用struct videobuf_queue q->int_ops->vmalloc函数来分配 */
 else
  return NULL;
}
EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc);

/* --------------------------------------------------------------------- */

/* 视频缓冲区队列初始化 */
void videobuf_queue_core_init(struct videobuf_queue *q,
    struct videobuf_queue_ops *ops,
    struct device *dev,
    spinlock_t *irqlock,
    enum v4l2_buf_type type,
    enum v4l2_field field,
    unsigned int msize,
    void *priv,
    struct videobuf_qtype_ops *int_ops)
{
 memset(q, 0, sizeof(*q));
 q->irqlock   = irqlock;
 q->dev       = dev;
 q->type      = type;
 q->field     = field;
 q->msize     = msize;
 q->ops       = ops;
 q->priv_data = priv;
 q->int_ops   = int_ops;

 /* All buffer operations are mandatory */
 BUG_ON(!q->ops->buf_setup);
 BUG_ON(!q->ops->buf_prepare);
 BUG_ON(!q->ops->buf_queue);
 BUG_ON(!q->ops->buf_release);

 /* Lock is mandatory for queue_cancel to work */
 BUG_ON(!irqlock);

 /* Having implementations for abstract methods are mandatory */
 BUG_ON(!q->int_ops);

 mutex_init(&q->vb_lock);
 init_waitqueue_head(&q->wait);
 INIT_LIST_HEAD(&q->stream);
}

/* Locking: Only usage in bttv unsafe find way to remove */
/* 判断视频缓冲区队列是否忙 */
int videobuf_queue_is_busy(struct videobuf_queue *q)
{
 int i;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 if (q->streaming) { /* 有视频流 */
  dprintk(1, "busy: streaming active\n");
  return 1;
 }
 if (q->reading) {    /* 正在读 */
  dprintk(1, "busy: pending read #1\n");
  return 1;
 }
 if (q->read_buf) {  /* 存在读缓冲区 */
  dprintk(1, "busy: pending read #2\n");
  return 1;
 }
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (NULL == q->bufs[i])
   continue;
  if (q->bufs[i]->map) {  /* buffer被映射 */
   dprintk(1, "busy: buffer #%d mapped\n", i);
   return 1;
  }
  if (q->bufs[i]->state == VIDEOBUF_QUEUED) {  /* 缓冲区正在队列中 */
   dprintk(1, "busy: buffer #%d queued\n", i);
   return 1;
  }
  if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {  /* 缓冲区正在使用 */
   dprintk(1, "busy: buffer #%d avtive\n", i);
   return 1;
  }
 }
 return 0;
}

/* Locking: Caller holds q->vb_lock */
/* 将缓冲区从队列中删除并且将缓冲区释放(需要持有q->vb_lock锁) */
void videobuf_queue_cancel(struct videobuf_queue *q)
{
 unsigned long flags = 0;
 int i;

 q->streaming = 0;
 q->reading  = 0;
 wake_up_interruptible_sync(&q->wait); /* 唤醒等待队列q->wait上的进程*/

 /* remove queued buffers from list */
 spin_lock_irqsave(q->irqlock, flags);
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (NULL == q->bufs[i])
   continue;
  if (q->bufs[i]->state == VIDEOBUF_QUEUED) {  /* 如果缓冲区在队列中 */
   list_del(&q->bufs[i]->queue);  /*从队列中删除该缓冲区 */
   q->bufs[i]->state = VIDEOBUF_ERROR;  /* 设置缓冲区状态为出现错误 */
   wake_up_all(&q->bufs[i]->done);  /*唤醒等待队列q->bufs[i]->done上的所有进程 */
  }
 }
 spin_unlock_irqrestore(q->irqlock, flags);

 /* free all buffers + clear queue */
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (NULL == q->bufs[i])
   continue;
  q->ops->buf_release(q, q->bufs[i]);  /* 释放缓冲区 */
 }
 INIT_LIST_HEAD(&q->stream);  /* 初始化链表头 q->stream*/
}

/* --------------------------------------------------------------------- */

/* Locking: Caller holds q->vb_lock */
/* 寻找下一个预 */
enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
{
 enum v4l2_field field = q->field;

 BUG_ON(V4L2_FIELD_ANY == field);

 if (V4L2_FIELD_ALTERNATE == field) {
  if (V4L2_FIELD_TOP == q->last) {
   field   = V4L2_FIELD_BOTTOM;
   q->last = V4L2_FIELD_BOTTOM;
  } else {
   field   = V4L2_FIELD_TOP;
   q->last = V4L2_FIELD_TOP;
  }
 }
 return field;
}

/* Locking: Caller holds q->vb_lock */
/* 设置视频缓冲区状态 */
static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
       struct videobuf_buffer *vb, enum v4l2_buf_type type)
{
 MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 b->index    = vb->i;
 b->type     = type;

 b->memory   = vb->memory;
 switch (b->memory) {
 case V4L2_MEMORY_MMAP:
  b->m.offset  = vb->boff;
  b->length    = vb->bsize;
  break;
 case V4L2_MEMORY_USERPTR:
  b->m.userptr = vb->baddr;
  b->length    = vb->bsize;
  break;
 case V4L2_MEMORY_OVERLAY:
  b->m.offset  = vb->boff;
  break;
 }

 b->flags    = 0;
 if (vb->map)
  b->flags |= V4L2_BUF_FLAG_MAPPED;

 switch (vb->state) {
 case VIDEOBUF_PREPARED:
 case VIDEOBUF_QUEUED:
 case VIDEOBUF_ACTIVE:
  b->flags |= V4L2_BUF_FLAG_QUEUED;
  break;
 case VIDEOBUF_DONE:
 case VIDEOBUF_ERROR:
  b->flags |= V4L2_BUF_FLAG_DONE;
  break;
 case VIDEOBUF_NEEDS_INIT:
 case VIDEOBUF_IDLE:
  /* nothing */
  break;
 }

 if (vb->input != UNSET) {
  b->flags |= V4L2_BUF_FLAG_INPUT;
  b->input  = vb->input;
 }

 b->field     = vb->field;
 b->timestamp = vb->ts;
 b->bytesused = vb->size;
 b->sequence  = vb->field_count >> 1;
}

/* Locking: Caller holds q->vb_lock */
/* 释放映射 */
static int __videobuf_mmap_free(struct videobuf_queue *q)
{
 int i;
 int rc;

 if (!q)
  return 0;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);


 rc  = CALL(q, mmap_free, q); /* 调用 struct videobuf_queue *q->int_ops->mmap_free函数来释放映射*/

 q->is_mmapped = 0; /* 表示已经释放映射 */

 if (rc < 0)
  return rc;

 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (NULL == q->bufs[i])
   continue;
  q->ops->buf_release(q, q->bufs[i]);  /* 是否缓冲区 */
  kfree(q->bufs[i]);
  q->bufs[i] = NULL;
 }

 return rc;
}
/* 释放映射 */
int videobuf_mmap_free(struct videobuf_queue *q)
{
 int ret;
 mutex_lock(&q->vb_lock);
 ret = __videobuf_mmap_free(q);
 mutex_unlock(&q->vb_lock);
 return ret;
}

/* Locking: Caller holds q->vb_lock */
/* mmap映射设置 */
int __videobuf_mmap_setup(struct videobuf_queue *q,
   unsigned int bcount, unsigned int bsize,
   enum v4l2_memory memory)
{
 unsigned int i;
 int err;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 err = __videobuf_mmap_free(q);   /* 释放映射 */
 if (0 != err)
  return err;

 /* Allocate and initialize buffers */
 for (i = 0; i < bcount; i++) {
  q->bufs[i] = videobuf_alloc(q);  /* 分配bcount个缓冲区 */

  if (q->bufs[i] == NULL)
   break;

  q->bufs[i]->i      = i;
  q->bufs[i]->input  = UNSET;
  q->bufs[i]->memory = memory;
  q->bufs[i]->bsize  = bsize;
  switch (memory) {
  case V4L2_MEMORY_MMAP:
   q->bufs[i]->boff  = bsize * i;
   break;
  case V4L2_MEMORY_USERPTR:
  case V4L2_MEMORY_OVERLAY:
   /* nothing */
   break;
  }
 }

 if (!i)
  return -ENOMEM;

 dprintk(1, "mmap setup: %d buffers, %d bytes each\n",
  i, bsize);

 return i;
}

/* mmap映射设置 */
int videobuf_mmap_setup(struct videobuf_queue *q,
   unsigned int bcount, unsigned int bsize,
   enum v4l2_memory memory)
{
 int ret;
 mutex_lock(&q->vb_lock);
 ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
 mutex_unlock(&q->vb_lock);
 return ret;
}

 /* 向驱动申请缓冲区,一般不超过5个 */
int videobuf_reqbufs(struct videobuf_queue *q,
   struct v4l2_requestbuffers *req)
{
 unsigned int size, count;
 int retval;

 if (req->count < 1) {
  dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
  return -EINVAL;
 }

 if (req->memory != V4L2_MEMORY_MMAP     &&
     req->memory != V4L2_MEMORY_USERPTR  &&
     req->memory != V4L2_MEMORY_OVERLAY) {
  dprintk(1, "reqbufs: memory type invalid\n");
  return -EINVAL;
 }

 mutex_lock(&q->vb_lock);
 if (req->type != q->type) {
  dprintk(1, "reqbufs: queue type invalid\n");
  retval = -EINVAL;
  goto done;
 }

 if (q->streaming) {
  dprintk(1, "reqbufs: streaming already exists\n");
  retval = -EBUSY;
  goto done;
 }
 if (!list_empty(&q->stream)) {
  dprintk(1, "reqbufs: stream running\n");
  retval = -EBUSY;
  goto done;
 }

 count = req->count;
 if (count > VIDEO_MAX_FRAME)
  count = VIDEO_MAX_FRAME;
 size = 0;
 q->ops->buf_setup(q, &count, &size);  /*调用 struct videobuf_queue *q->ops->buf_setup函数来设置缓冲区*/
 size = PAGE_ALIGN(size); /*设置对齐 */
 dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
  count, size, (count*size)>>PAGE_SHIFT);

 retval = __videobuf_mmap_setup(q, count, size, req->memory); /* mmap映射设置 */
 if (retval < 0) {
  dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
  goto done;
 }

 req->count = retval;

 done:
 mutex_unlock(&q->vb_lock);
 return retval;
}

 /* 查询已经分配的v4l2的视频缓冲区的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、
     缓冲区长度等(应用程序通过该调用来获取内核的视频缓冲区信息,然后调用mmap把内核空间地址映射
     到用户空间)*/
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
 int ret = -EINVAL;

 mutex_lock(&q->vb_lock);
 if (unlikely(b->type != q->type)) {
  dprintk(1, "querybuf: Wrong type.\n");
  goto done;
 }
 if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) {
  dprintk(1, "querybuf: index out of range.\n");
  goto done;
 }
 if (unlikely(NULL == q->bufs[b->index])) {
  dprintk(1, "querybuf: buffer is null.\n");
  goto done;
 }

 videobuf_status(q, b, q->bufs[b->index], q->type);

 ret = 0;
done:
 mutex_unlock(&q->vb_lock);
 return ret;
}

/*投放申请到的视频缓冲区到视频输入队列中*/
int videobuf_qbuf(struct videobuf_queue *q,
       struct v4l2_buffer *b)
{
 struct videobuf_buffer *buf;
 enum v4l2_field field;
 unsigned long flags = 0;
 int retval;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 if (b->memory == V4L2_MEMORY_MMAP)
  down_read(¤t->mm->mmap_sem);

 mutex_lock(&q->vb_lock);
 retval = -EBUSY;
 if (q->reading) {
  dprintk(1, "qbuf: Reading running...\n");
  goto done;
 }
 retval = -EINVAL;
 if (b->type != q->type) {
  dprintk(1, "qbuf: Wrong type.\n");
  goto done;
 }
 if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {
  dprintk(1, "qbuf: index out of range.\n");
  goto done;
 }
 buf = q->bufs[b->index];
 if (NULL == buf) {
  dprintk(1, "qbuf: buffer is null.\n");
  goto done;
 }
 MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
 if (buf->memory != b->memory) {
  dprintk(1, "qbuf: memory type is wrong.\n");
  goto done;
 }
 if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
  dprintk(1, "qbuf: buffer is already queued or active.\n");
  goto done;
 }

 if (b->flags & V4L2_BUF_FLAG_INPUT) {
  if (b->input >= q->inputs) {
   dprintk(1, "qbuf: wrong input.\n");
   goto done;
  }
  buf->input = b->input;
 } else {
  buf->input = UNSET;
 }

 switch (b->memory) {
 case V4L2_MEMORY_MMAP:
  if (0 == buf->baddr) {
   dprintk(1, "qbuf: mmap requested "
       "but buffer addr is zero!\n");
   goto done;
  }
  break;
 case V4L2_MEMORY_USERPTR:
  if (b->length < buf->bsize) {
   dprintk(1, "qbuf: buffer length is not enough\n");
   goto done;
  }
  if (VIDEOBUF_NEEDS_INIT != buf->state &&
      buf->baddr != b->m.userptr)
   q->ops->buf_release(q, buf);
  buf->baddr = b->m.userptr;
  break;
 case V4L2_MEMORY_OVERLAY:
  buf->boff = b->m.offset;
  break;
 default:
  dprintk(1, "qbuf: wrong memory type\n");
  goto done;
 }

 dprintk(1, "qbuf: requesting next field\n");
 field = videobuf_next_field(q);
 retval = q->ops->buf_prepare(q, buf, field);  /* 准备缓冲区 */
 if (0 != retval) {
  dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
  goto done;
 }

 list_add_tail(&buf->stream, &q->stream); /* 添加到队列 */
 if (q->streaming) {
  spin_lock_irqsave(q->irqlock, flags);
  q->ops->buf_queue(q, buf);
  spin_unlock_irqrestore(q->irqlock, flags);
 }
 dprintk(1, "qbuf: succeded\n");
 retval = 0;
 wake_up_interruptible_sync(&q->wait);

 done:
 mutex_unlock(&q->vb_lock);

 if (b->memory == V4L2_MEMORY_MMAP)
  up_read(¤t->mm->mmap_sem);

 return retval;
}


/* Locking: Caller holds q->vb_lock */
static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
{
 int retval;

checks:
 if (!q->streaming) {
  dprintk(1, "next_buffer: Not streaming\n");
  retval = -EINVAL;
  goto done;
 }

 if (list_empty(&q->stream)) {
  if (noblock) {
   retval = -EAGAIN;
   dprintk(2, "next_buffer: no buffers to dequeue\n");
   goto done;
  } else {
   dprintk(2, "next_buffer: waiting on buffer\n");

   /* Drop lock to avoid deadlock with qbuf */
   mutex_unlock(&q->vb_lock);

   /* Checking list_empty and streaming is safe without
    * locks because we goto checks to validate while
    * holding locks before proceeding */
   retval = wait_event_interruptible(q->wait,
    !list_empty(&q->stream) || !q->streaming);
   mutex_lock(&q->vb_lock);

   if (retval)
    goto done;

   goto checks;
  }
 }

 retval = 0;

done:
 return retval;
}


/* Locking: Caller holds q->vb_lock */
/* 获取下一个buffer */
static int stream_next_buffer(struct videobuf_queue *q,
   struct videobuf_buffer **vb, int nonblocking)
{
 int retval;
 struct videobuf_buffer *buf = NULL;

 retval = stream_next_buffer_check_queue(q, nonblocking);
 if (retval)
  goto done;

 buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
 retval = videobuf_waiton(buf, nonblocking, 1);
 if (retval < 0)
  goto done;

 *vb = buf;
done:
 return retval;
}

 /*从视频缓冲输出队列获取一个保存有视频数据的视频缓冲区 */
int videobuf_dqbuf(struct videobuf_queue *q,
        struct v4l2_buffer *b, int nonblocking)
{
 struct videobuf_buffer *buf = NULL;
 int retval;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 mutex_lock(&q->vb_lock);

 retval = stream_next_buffer(q, &buf, nonblocking);/* 获取下一个buffer */
 if (retval < 0) {
  dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
  goto done;
 }

 switch (buf->state) {
 case VIDEOBUF_ERROR:
  dprintk(1, "dqbuf: state is error\n");
  retval = -EIO;
  CALL(q, sync, q, buf);
  buf->state = VIDEOBUF_IDLE;
  break;
 case VIDEOBUF_DONE:
  dprintk(1, "dqbuf: state is done\n");
  CALL(q, sync, q, buf);
  buf->state = VIDEOBUF_IDLE;
  break;
 default:
  dprintk(1, "dqbuf: state invalid\n");
  retval = -EINVAL;
  goto done;
 }
 list_del(&buf->stream);  /* 从链表中删除 */
 memset(b, 0, sizeof(*b));
 videobuf_status(q, b, buf, q->type);

 done:
 mutex_unlock(&q->vb_lock);
 return retval;
}

/* 启动视频采集,应用程序调用VIDIOC_STREAMON启动视频采集命令后,视频设备驱动程序开始采集视频数据,
     并把采集到的视频数据保存到视频驱动的视频缓冲区中 */
int videobuf_streamon(struct videobuf_queue *q)
{
 struct videobuf_buffer *buf;
 unsigned long flags = 0;
 int retval;

 mutex_lock(&q->vb_lock);
 retval = -EBUSY;
 if (q->reading)
  goto done;
 retval = 0;
 if (q->streaming)
  goto done;
 q->streaming = 1;
 spin_lock_irqsave(q->irqlock, flags);
 list_for_each_entry(buf, &q->stream, stream)
  if (buf->state == VIDEOBUF_PREPARED)
   q->ops->buf_queue(q, buf);
 spin_unlock_irqrestore(q->irqlock, flags);

 wake_up_interruptible_sync(&q->wait);
 done:
 mutex_unlock(&q->vb_lock);
 return retval;
}

/* Locking: Caller holds q->vb_lock */
  /* 停止视频的采集 */
static int __videobuf_streamoff(struct videobuf_queue *q)
{
 if (!q->streaming)
  return -EINVAL;

 videobuf_queue_cancel(q);

 return 0;
}
/* 停止视频的采集 */
int videobuf_streamoff(struct videobuf_queue *q)
{
 int retval;

 mutex_lock(&q->vb_lock);
 retval = __videobuf_streamoff(q);/* 停止视频的采集 */
 mutex_unlock(&q->vb_lock);

 return retval;
}

/* Locking: Caller holds q->vb_lock */
/* 用零拷贝的方式读视频缓冲区 */
static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
          char __user *data,
          size_t count, loff_t *ppos)
{
 enum v4l2_field field;
 unsigned long flags = 0;
 int retval;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 /* setup stuff */
 q->read_buf = videobuf_alloc(q);
 if (NULL == q->read_buf)
  return -ENOMEM;

 q->read_buf->memory = V4L2_MEMORY_USERPTR;
 q->read_buf->baddr  = (unsigned long)data;
 q->read_buf->bsize  = count;

 field = videobuf_next_field(q);
 retval = q->ops->buf_prepare(q, q->read_buf, field);
 if (0 != retval)
  goto done;

 /* start capture & wait */
 spin_lock_irqsave(q->irqlock, flags);
 q->ops->buf_queue(q, q->read_buf);
 spin_unlock_irqrestore(q->irqlock, flags);
 retval = videobuf_waiton(q->read_buf, 0, 0);
 if (0 == retval) {
  CALL(q, sync, q, q->read_buf);
  if (VIDEOBUF_ERROR == q->read_buf->state)
   retval = -EIO;
  else
   retval = q->read_buf->size;
 }

 done:
 /* cleanup */
 q->ops->buf_release(q, q->read_buf);
 kfree(q->read_buf);
 q->read_buf = NULL;
 return retval;
}

/* 读一个 */
ssize_t videobuf_read_one(struct videobuf_queue *q,
     char __user *data, size_t count, loff_t *ppos,
     int nonblocking)
{
 enum v4l2_field field;
 unsigned long flags = 0;
 unsigned size = 0, nbufs = 1;
 int retval;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 mutex_lock(&q->vb_lock);

 q->ops->buf_setup(q, &nbufs, &size);

 if (NULL == q->read_buf  &&
     count >= size        &&
     !nonblocking) {
  retval = videobuf_read_zerocopy(q, data, count, ppos);
  if (retval >= 0  ||  retval == -EIO)
   /* ok, all done */
   goto done;
  /* fallback to kernel bounce buffer on failures */
 }

 if (NULL == q->read_buf) {
  /* need to capture a new frame */
  retval = -ENOMEM;
  q->read_buf = videobuf_alloc(q);

  dprintk(1, "video alloc=0x%p\n", q->read_buf);
  if (NULL == q->read_buf)
   goto done;
  q->read_buf->memory = V4L2_MEMORY_USERPTR;
  q->read_buf->bsize = count; /* preferred size */
  field = videobuf_next_field(q);
  retval = q->ops->buf_prepare(q, q->read_buf, field);

  if (0 != retval) {
   kfree(q->read_buf);
   q->read_buf = NULL;
   goto done;
  }

  spin_lock_irqsave(q->irqlock, flags);
  q->ops->buf_queue(q, q->read_buf);
  spin_unlock_irqrestore(q->irqlock, flags);

  q->read_off = 0;
 }

 /* wait until capture is done */
 retval = videobuf_waiton(q->read_buf, nonblocking, 1);
 if (0 != retval)
  goto done;

 CALL(q, sync, q, q->read_buf);

 if (VIDEOBUF_ERROR == q->read_buf->state) {
  /* catch I/O errors */
  q->ops->buf_release(q, q->read_buf);
  kfree(q->read_buf);
  q->read_buf = NULL;
  retval = -EIO;
  goto done;
 }

 /* Copy to userspace */
 retval = CALL(q, video_copy_to_user, q, data, count, nonblocking);
 if (retval < 0)
  goto done;

 q->read_off += retval;
 if (q->read_off == q->read_buf->size) {
  /* all data copied, cleanup */
  q->ops->buf_release(q, q->read_buf);
  kfree(q->read_buf);
  q->read_buf = NULL;
 }

 done:
 mutex_unlock(&q->vb_lock);
 return retval;
}

/* Locking: Caller holds q->vb_lock */
/* 开始读 */
static int __videobuf_read_start(struct videobuf_queue *q)
{
 enum v4l2_field field;
 unsigned long flags = 0;
 unsigned int count = 0, size = 0;
 int err, i;

 q->ops->buf_setup(q, &count, &size);  /* 设置缓冲区 */
 if (count < 2)
  count = 2;
 if (count > VIDEO_MAX_FRAME)
  count = VIDEO_MAX_FRAME;
 size = PAGE_ALIGN(size);  /* 设置对齐 */

 err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR);
 if (err < 0)
  return err;

 count = err;

 for (i = 0; i < count; i++) {
  field = videobuf_next_field(q);
  err = q->ops->buf_prepare(q, q->bufs[i], field);
  if (err)
   return err;
  list_add_tail(&q->bufs[i]->stream, &q->stream);
 }
 spin_lock_irqsave(q->irqlock, flags);
 for (i = 0; i < count; i++)
  q->ops->buf_queue(q, q->bufs[i]);
 spin_unlock_irqrestore(q->irqlock, flags);
 q->reading = 1;
 return 0;
}

/* 停止读 */
static void __videobuf_read_stop(struct videobuf_queue *q)
{
 int i;

 videobuf_queue_cancel(q);
 __videobuf_mmap_free(q);
 INIT_LIST_HEAD(&q->stream);
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (NULL == q->bufs[i])
   continue;
  kfree(q->bufs[i]);
  q->bufs[i] = NULL;
 }
 q->read_buf = NULL;

}
/* 开始读 */
int videobuf_read_start(struct videobuf_queue *q)
{
 int rc;

 mutex_lock(&q->vb_lock);
 rc = __videobuf_read_start(q);
 mutex_unlock(&q->vb_lock);

 return rc;
}
/* 停止读 */
void videobuf_read_stop(struct videobuf_queue *q)
{
 mutex_lock(&q->vb_lock);
 __videobuf_read_stop(q);
 mutex_unlock(&q->vb_lock);
}

/* 停止buffer */
void videobuf_stop(struct videobuf_queue *q)
{
 mutex_lock(&q->vb_lock);

 if (q->streaming)
  __videobuf_streamoff(q);

 if (q->reading)
  __videobuf_read_stop(q);

 mutex_unlock(&q->vb_lock);
}

/* 读流 */
ssize_t videobuf_read_stream(struct videobuf_queue *q,
        char __user *data, size_t count, loff_t *ppos,
        int vbihack, int nonblocking)
{
 int rc, retval;
 unsigned long flags = 0;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 dprintk(2, "%s\n", __func__);
 mutex_lock(&q->vb_lock);
 retval = -EBUSY;
 if (q->streaming)
  goto done;
 if (!q->reading) {
  retval = __videobuf_read_start(q);
  if (retval < 0)
   goto done;
 }

 retval = 0;
 while (count > 0) {
  /* get / wait for data */
  if (NULL == q->read_buf) {
   q->read_buf = list_entry(q->stream.next,
       struct videobuf_buffer,
       stream);
   list_del(&q->read_buf->stream);
   q->read_off = 0;
  }
  rc = videobuf_waiton(q->read_buf, nonblocking, 1);
  if (rc < 0) {
   if (0 == retval)
    retval = rc;
   break;
  }

  if (q->read_buf->state == VIDEOBUF_DONE) {
   rc = CALL(q, copy_stream, q, data + retval, count,
     retval, vbihack, nonblocking);
   if (rc < 0) {
    retval = rc;
    break;
   }
   retval      += rc;
   count       -= rc;
   q->read_off += rc;
  } else {
   /* some error */
   q->read_off = q->read_buf->size;
   if (0 == retval)
    retval = -EIO;
  }

  /* requeue buffer when done with copying */
  if (q->read_off == q->read_buf->size) {
   list_add_tail(&q->read_buf->stream,
          &q->stream);
   spin_lock_irqsave(q->irqlock, flags);
   q->ops->buf_queue(q, q->read_buf);
   spin_unlock_irqrestore(q->irqlock, flags);
   q->read_buf = NULL;
  }
  if (retval < 0)
   break;
 }

 done:
 mutex_unlock(&q->vb_lock);
 return retval;
}

/*  poll机制,面向的是视频流 */
unsigned int videobuf_poll_stream(struct file *file,
      struct videobuf_queue *q,
      poll_table *wait)
{
 struct videobuf_buffer *buf = NULL;
 unsigned int rc = 0;

 mutex_lock(&q->vb_lock);
 if (q->streaming) {  /* 有视频流 */
  if (!list_empty(&q->stream))
   buf = list_entry(q->stream.next,
      struct videobuf_buffer, stream);  /* 获取流对应的视频缓冲区 */
 } else {
  if (!q->reading)
   __videobuf_read_start(q);
  if (!q->reading) {
   rc = POLLERR;
  } else if (NULL == q->read_buf) {
   q->read_buf = list_entry(q->stream.next,
       struct videobuf_buffer,
       stream);
   list_del(&q->read_buf->stream);
   q->read_off = 0;
  }
  buf = q->read_buf;
 }
 if (!buf)
  rc = POLLERR;

 if (0 == rc) {
  poll_wait(file, &buf->done, wait);
  if (buf->state == VIDEOBUF_DONE ||
      buf->state == VIDEOBUF_ERROR)
   rc = POLLIN|POLLRDNORM;
 }
 mutex_unlock(&q->vb_lock);
 return rc;
}

/* mmap映射 */
int videobuf_mmap_mapper(struct videobuf_queue *q,
    struct vm_area_struct *vma)
{
 int retval;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 mutex_lock(&q->vb_lock);
 retval = CALL(q, mmap_mapper, q, vma);  /* 调用struct videobuf_queue 的成员 int_ops函数集的mmap_mapper函数指针*/
 q->is_mmapped = 1;  /* 设置已经映射了 */
 mutex_unlock(&q->vb_lock);

 return retval;
}

#ifdef CONFIG_VIDEO_V4L1_COMPAT
int videobuf_cgmbuf(struct videobuf_queue *q,
      struct video_mbuf *mbuf, int count)
{
 struct v4l2_requestbuffers req;
 int rc, i;

 MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);

 memset(&req, 0, sizeof(req));
 req.type   = q->type;
 req.count  = count;
 req.memory = V4L2_MEMORY_MMAP;
 rc = videobuf_reqbufs(q, &req);
 if (rc < 0)
  return rc;

 mbuf->frames = req.count;
 mbuf->size   = 0;
 for (i = 0; i < mbuf->frames; i++) {
  mbuf->offsets[i]  = q->bufs[i]->boff;
  mbuf->size       += q->bufs[i]->bsize;
 }

 return 0;
}
EXPORT_SYMBOL_GPL(videobuf_cgmbuf);
#endif

/* --------------------------------------------------------------------- */

EXPORT_SYMBOL_GPL(videobuf_waiton);
EXPORT_SYMBOL_GPL(videobuf_iolock);

EXPORT_SYMBOL_GPL(videobuf_alloc);

EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);

EXPORT_SYMBOL_GPL(videobuf_next_field);
EXPORT_SYMBOL_GPL(videobuf_reqbufs);
EXPORT_SYMBOL_GPL(videobuf_querybuf);
EXPORT_SYMBOL_GPL(videobuf_qbuf);
EXPORT_SYMBOL_GPL(videobuf_dqbuf);
EXPORT_SYMBOL_GPL(videobuf_streamon);
EXPORT_SYMBOL_GPL(videobuf_streamoff);

EXPORT_SYMBOL_GPL(videobuf_read_start);
EXPORT_SYMBOL_GPL(videobuf_read_stop);
EXPORT_SYMBOL_GPL(videobuf_stop);
EXPORT_SYMBOL_GPL(videobuf_read_stream);
EXPORT_SYMBOL_GPL(videobuf_read_one);
EXPORT_SYMBOL_GPL(videobuf_poll_stream);

EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_free);
EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
/*************************************************************************************************************************************/
/* videobuf-dma-contig.c */

/*
 * helper functions for physically contiguous capture buffers
 *
 * The functions support hardware lacking scatter gather support
 * (i.e. the buffers must be linear in physical memory)
 *
 * Copyright (c) 2008 Magnus Damm
 *
 * Based on videobuf-vmalloc.c,
 * (c) 2007 Mauro Carvalho Chehab, <>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2
 */

#include
#include
#include
#include
#include
/* 该文件定义的函数用于赋值给 struct videobuf_queue  *int_ops字段(适用于物理内存连续的捕获缓冲区) */


struct videobuf_dma_contig_memory {  /* DMA连续内存 */
 u32 magic;
 void *vaddr;  /* 虚拟地址 */
 dma_addr_t dma_handle;  /* 物理地址 */
 unsigned long size; /* 大小 */
};

#define MAGIC_DC_MEM 0x0733ac61
#define MAGIC_CHECK(is, should)          \
 if (unlikely((is) != (should))) {        \
  pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
  BUG();           \
 }

static void
videobuf_vm_open(struct vm_area_struct *vma)
{
 struct videobuf_mapping *map = vma->vm_private_data;

 dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
  map, map->count, vma->vm_start, vma->vm_end);

 map->count++;
}

static void videobuf_vm_close(struct vm_area_struct *vma)
{
 struct videobuf_mapping *map = vma->vm_private_data;
 struct videobuf_queue *q = map->q;
 int i;

 dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
  map, map->count, vma->vm_start, vma->vm_end);

 map->count--;
 if (0 == map->count) {
  struct videobuf_dma_contig_memory *mem;

  dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q);
  mutex_lock(&q->vb_lock);

  /* We need first to cancel streams, before unmapping */
  if (q->streaming)
   videobuf_queue_cancel(q);

  for (i = 0; i < VIDEO_MAX_FRAME; i++) {
   if (NULL == q->bufs[i])
    continue;

   if (q->bufs[i]->map != map)
    continue;

   mem = q->bufs[i]->priv;
   if (mem) {
    /* This callback is called only if kernel has
       allocated memory and this memory is mmapped.
       In this case, memory should be freed,
       in order to do memory unmap.
     */

    MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

    /* vfree is not atomic - can't be
       called with IRQ's disabled
     */
    dev_dbg(map->q->dev, "buf[%d] freeing %p\n",
     i, mem->vaddr);

    dma_free_coherent(q->dev, mem->size,
        mem->vaddr, mem->dma_handle);
    mem->vaddr = NULL;
   }

   q->bufs[i]->map   = NULL;
   q->bufs[i]->baddr = 0;
  }

  kfree(map);

  mutex_unlock(&q->vb_lock);
 }
}

static struct vm_operations_struct videobuf_vm_ops = {
 .open     = videobuf_vm_open,
 .close    = videobuf_vm_close,
};

static void *__videobuf_alloc(size_t size)
{
 struct videobuf_dma_contig_memory *mem;
 struct videobuf_buffer *vb;

 vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
 if (vb) {
  mem = vb->priv = ((char *)vb) + size;
  mem->magic = MAGIC_DC_MEM;
 }

 return vb;
}

static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf)
{
 struct videobuf_dma_contig_memory *mem = buf->priv;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 return mem->vaddr;
}

static int __videobuf_iolock(struct videobuf_queue *q,
        struct videobuf_buffer *vb,
        struct v4l2_framebuffer *fbuf)
{
 struct videobuf_dma_contig_memory *mem = vb->priv;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 switch (vb->memory) {
 case V4L2_MEMORY_MMAP:
  dev_dbg(q->dev, "%s memory method MMAP\n", __func__);

  /* All handling should be done by __videobuf_mmap_mapper() */
  if (!mem->vaddr) {
   dev_err(q->dev, "memory is not alloced/mmapped.\n");
   return -EINVAL;
  }
  break;
 case V4L2_MEMORY_USERPTR:
  dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);

  /* The only USERPTR currently supported is the one needed for
     read() method.
   */
  if (vb->baddr)
   return -EINVAL;

  mem->size = PAGE_ALIGN(vb->size);
  mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
      &mem->dma_handle, GFP_KERNEL);
  if (!mem->vaddr) {
   dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
      mem->size);
   return -ENOMEM;
  }

  dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
   mem->vaddr, mem->size);
  break;
 case V4L2_MEMORY_OVERLAY:
 default:
  dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
   __func__);
  return -EINVAL;
 }

 return 0;
}

static int __videobuf_sync(struct videobuf_queue *q,
      struct videobuf_buffer *buf)
{
 struct videobuf_dma_contig_memory *mem = buf->priv;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
    DMA_FROM_DEVICE);
 return 0;
}

static int __videobuf_mmap_free(struct videobuf_queue *q)
{
 unsigned int i;

 dev_dbg(q->dev, "%s\n", __func__);
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (q->bufs[i] && q->bufs[i]->map)
   return -EBUSY;
 }

 return 0;
}

static int __videobuf_mmap_mapper(struct videobuf_queue *q,
      struct vm_area_struct *vma)
{
 struct videobuf_dma_contig_memory *mem;
 struct videobuf_mapping *map;
 unsigned int first;
 int retval;
 unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT;

 dev_dbg(q->dev, "%s\n", __func__);
 if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
  return -EINVAL;

 /* look for first buffer to map */
 for (first = 0; first < VIDEO_MAX_FRAME; first++) {
  if (!q->bufs[first])
   continue;

  if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
   continue;
  if (q->bufs[first]->boff == offset)
   break;
 }
 if (VIDEO_MAX_FRAME == first) {
  dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n",
   offset);
  return -EINVAL;
 }

 /* create mapping + update buffer list */
 map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
 if (!map)
  return -ENOMEM;

 q->bufs[first]->map = map;
 map->start = vma->vm_start;
 map->end = vma->vm_end;
 map->q = q;

 q->bufs[first]->baddr = vma->vm_start;

 mem = q->bufs[first]->priv;
 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
 mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
     &mem->dma_handle, GFP_KERNEL); /* 申请DMA缓冲区,建立一致性映射 */
 if (!mem->vaddr) {
  dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
   mem->size);
  goto error;
 }
 dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
  mem->vaddr, mem->size);

 /* Try to remap memory */

 size = vma->vm_end - vma->vm_start;
 size = (size < mem->size) ? size : mem->size;

 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 retval = remap_pfn_range(vma, vma->vm_start,
     mem->dma_handle >> PAGE_SHIFT,
     size, vma->vm_page_prot);
 if (retval) {
  dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
  dma_free_coherent(q->dev, mem->size,
      mem->vaddr, mem->dma_handle);
  goto error;
 }

 vma->vm_ops          = &videobuf_vm_ops;
 vma->vm_flags       |= VM_DONTEXPAND;
 vma->vm_private_data = map;

 dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
  map, q, vma->vm_start, vma->vm_end,
  (long int) q->bufs[first]->bsize,
  vma->vm_pgoff, first);

 videobuf_vm_open(vma);

 return 0;

error:
 kfree(map);
 return -ENOMEM;
}

static int __videobuf_copy_to_user(struct videobuf_queue *q,
       char __user *data, size_t count,
       int nonblocking)
{
 struct videobuf_dma_contig_memory *mem = q->read_buf->priv;
 void *vaddr;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
 BUG_ON(!mem->vaddr);

 /* copy to userspace */
 if (count > q->read_buf->size - q->read_off)
  count = q->read_buf->size - q->read_off;

 vaddr = mem->vaddr;

 if (copy_to_user(data, vaddr + q->read_off, count))
  return -EFAULT;

 return count;
}

static int __videobuf_copy_stream(struct videobuf_queue *q,
      char __user *data, size_t count, size_t pos,
      int vbihack, int nonblocking)
{
 unsigned int  *fc;
 struct videobuf_dma_contig_memory *mem = q->read_buf->priv;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 if (vbihack) {
  /* dirty, undocumented hack -- pass the frame counter
   * within the last four bytes of each vbi data block.
   * We need that one to maintain backward compatibility
   * to all vbi decoding software out there ... */
  fc = (unsigned int *)mem->vaddr;
  fc += (q->read_buf->size >> 2) - 1;
  *fc = q->read_buf->field_count >> 1;
  dev_dbg(q->dev, "vbihack: %d\n", *fc);
 }

 /* copy stuff using the common method */
 count = __videobuf_copy_to_user(q, data, count, nonblocking);

 if ((count == -EFAULT) && (pos == 0))
  return -EFAULT;

 return count;
}

static struct videobuf_qtype_ops qops = {
 .magic        = MAGIC_QTYPE_OPS,

 .alloc        = __videobuf_alloc,
 .iolock       = __videobuf_iolock,
 .sync         = __videobuf_sync,
 .mmap_free    = __videobuf_mmap_free,
 .mmap_mapper  = __videobuf_mmap_mapper,
 .video_copy_to_user = __videobuf_copy_to_user,
 .copy_stream  = __videobuf_copy_stream,
 .vmalloc      = __videobuf_to_vmalloc,
};

void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
        struct videobuf_queue_ops *ops,
        struct device *dev,
        spinlock_t *irqlock,
        enum v4l2_buf_type type,
        enum v4l2_field field,
        unsigned int msize,
        void *priv)
{
 videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
     priv, &qops);
}
EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);

dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
{
 struct videobuf_dma_contig_memory *mem = buf->priv;

 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 return mem->dma_handle;
}
EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);

void videobuf_dma_contig_free(struct videobuf_queue *q,
         struct videobuf_buffer *buf)
{
 struct videobuf_dma_contig_memory *mem = buf->priv;

 /* mmapped memory can't be freed here, otherwise mmapped region
    would be released, while still needed. In this case, the memory
    release should happen inside videobuf_vm_close().
    So, it should free memory only if the memory were allocated for
    read() operation.
  */
 if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
  return;

 if (!mem)
  return;

 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

 dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
 mem->vaddr = NULL;
}
EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);

MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL");
/*************************************************************************************************************************************/
/* videobuf-vmalloc.c */

/*
 * helper functions for vmalloc video4linux capture buffers
 *
 * The functions expect the hardware being able to scatter gather
 * (i.e. the buffers are not linear in physical memory, but fragmented
 * into PAGE_SIZE chunks).  They also assume the driver does not need
 * to touch the video data.
 *
 * (c) 2007 Mauro Carvalho Chehab, <>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2
 */

#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#include
/* 该文件定义的函数用于赋值给 struct videobuf_queue  *int_ops字段(这些函数需要硬件支持分散聚合IO,这是在假设
     驱动不需要解除视频数据的情况下)*/


/* magic的取值 */
#define MAGIC_DMABUF   0x17760309    /* DMA缓冲区 */
#define MAGIC_VMAL_MEM 0x18221223

#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \
 { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); }

static int debug;
module_param(debug, int, 0644);

MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
MODULE_AUTHOR("Mauro Carvalho Chehab <>");
MODULE_LICENSE("GPL");

#define dprintk(level, fmt, arg...) if (debug >= level) \
 printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)


/***************************************************************************/

static void
videobuf_vm_open(struct vm_area_struct *vma)
{
 struct videobuf_mapping *map = vma->vm_private_data;

 dprintk(2,"vm_open %p [count=%u,vma=%08lx-%08lx]\n",map,
  map->count,vma->vm_start,vma->vm_end);

 map->count++; /* 增加计数 */
}

static void videobuf_vm_close(struct vm_area_struct *vma)
{
 struct videobuf_mapping *map = vma->vm_private_data;
 struct videobuf_queue *q = map->q;
 int i;

 dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
  map->count, vma->vm_start, vma->vm_end);

 map->count--;  /* 减小计数 */
 if (0 == map->count) {  /*如果计数为0 */
  struct videobuf_vmalloc_memory *mem;

  dprintk(1, "munmap %p q=%p\n", map, q);
  mutex_lock(&q->vb_lock);

  /* We need first to cancel streams, before unmapping */
  if (q->streaming)  /* 如果还有视频流 */
   videobuf_queue_cancel(q);  /* 将缓冲区从队列中删除并且将缓冲区释放(需要持有q->vb_lock锁) */

  for (i = 0; i < VIDEO_MAX_FRAME; i++) {
   if (NULL == q->bufs[i])
    continue;

   if (q->bufs[i]->map != map)
    continue;

   mem = q->bufs[i]->priv;
   if (mem) {
    /* This callback is called only if kernel has
       allocated memory and this memory is mmapped.
       In this case, memory should be freed,
       in order to do memory unmap.
     */

    MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);

    /* vfree is not atomic - can't be
       called with IRQ's disabled
     */
    dprintk(1, "%s: buf[%d] freeing (%p)\n",
     __func__, i, mem->vmalloc);

    vfree(mem->vmalloc);  /* 释放内存 */
    mem->vmalloc = NULL;
   }

   q->bufs[i]->map   = NULL;
   q->bufs[i]->baddr = 0;
  }

  kfree(map);  /* 释放私有数据*/

  mutex_unlock(&q->vb_lock);
 }

 return;
}

static struct vm_operations_struct videobuf_vm_ops =  /* 虚拟内存区域操作函数 */
{
 .open     = videobuf_vm_open,
 .close    = videobuf_vm_close,
};

/* ---------------------------------------------------------------------
 * vmalloc handlers for the generic methods
 */

/* Allocated area consists on 3 parts:
 struct video_buffer
 struct _buffer (cx88_buffer, saa7134_buf, ...)
 struct videobuf_dma_sg_memory
 */
/*分配内存 */
static void *__videobuf_alloc(size_t size)
{
 struct videobuf_vmalloc_memory *mem;
 struct videobuf_buffer *vb;

 vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);  /* 分配size大小+一个struct videobuf_vmalloc_memory结构体大小的内存 */

 mem = vb->priv = ((char *)vb)+size;  /* 将分配的struct videobuf_vmalloc_memory结构体的指针保存到 struct videobuf_buffer的私有数据指针priv */
 mem->magic=MAGIC_VMAL_MEM;  /* 设置幻数 */

 dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
  __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),
  mem,(long)sizeof(*mem));

 return vb;  /* 返回分配的内存的地址 */
}

static int __videobuf_iolock (struct videobuf_queue* q,
         struct videobuf_buffer *vb,
         struct v4l2_framebuffer *fbuf)
{
 struct videobuf_vmalloc_memory *mem = vb->priv;
 int pages;

 BUG_ON(!mem);

 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); /* 检查幻数 */

 switch (vb->memory) {  /*  */
 case V4L2_MEMORY_MMAP:  /* mmap映射的内存 */
  dprintk(1, "%s memory method MMAP\n", __func__);

  /* All handling should be done by __videobuf_mmap_mapper() */
  if (!mem->vmalloc) {
   printk(KERN_ERR "memory is not alloced/mmapped.\n");
   return -EINVAL;
  }
  break;
 case V4L2_MEMORY_USERPTR:  /* 缓冲区在用户空间 */
  pages = PAGE_ALIGN(vb->size); /* 设置页对齐 */

  dprintk(1, "%s memory method USERPTR\n", __func__);

#if 1
  if (vb->baddr) {
   printk(KERN_ERR "USERPTR is currently not supported\n");
   return -EINVAL;
  }
#endif

  /* The only USERPTR currently supported is the one needed for
     read() method.
   */

  mem->vmalloc = vmalloc_user(pages);  /* 分配内存 */
  if (!mem->vmalloc) {
   printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
   return -ENOMEM;
  }
  dprintk(1, "vmalloc is at addr %p (%d pages)\n",
   mem->vmalloc, pages);

#if 0
  int rc;
  /* Kernel userptr is used also by read() method. In this case,
     there's no need to remap, since data will be copied to user
   */
  if (!vb->baddr)
   return 0;

  /* FIXME: to properly support USERPTR, remap should occur.
     The code below won't work, since mem->vma = NULL
   */
  /* Try to remap memory */
  rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
  if (rc < 0) {
   printk(KERN_ERR "mmap: remap failed with error %d. ", rc);
   return -ENOMEM;
  }
#endif

  break;
 case V4L2_MEMORY_OVERLAY:  /*不支持 */
 default:
  dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);

  /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
  printk(KERN_ERR "Memory method currently unsupported.\n");
  return -EINVAL;
 }

 return 0;
}

static int __videobuf_sync(struct videobuf_queue *q,
      struct videobuf_buffer *buf)
{
 return 0;
}

static int __videobuf_mmap_free(struct videobuf_queue *q)
{
 unsigned int i;

 dprintk(1, "%s\n", __func__);
 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
  if (q->bufs[i]) {
   if (q->bufs[i]->map)
    return -EBUSY;
  }
 }

 return 0;
}
/* mmap映射 */
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
    struct vm_area_struct *vma)
{
 struct videobuf_vmalloc_memory *mem;
 struct videobuf_mapping *map;
 unsigned int first;
 int retval, pages;
 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;  /* 转换为以字节为单位 */

 dprintk(1, "%s\n", __func__);
 if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
  return -EINVAL;

 /* look for first buffer to map */
 for (first = 0; first < VIDEO_MAX_FRAME; first++) {
  if (NULL == q->bufs[first])
   continue;

  if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
   continue;
  if (q->bufs[first]->boff == offset)
   break;
 }
 if (VIDEO_MAX_FRAME == first) {
  dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n",
   (vma->vm_pgoff << PAGE_SHIFT));
  return -EINVAL;
 }

 /* create mapping + update buffer list */
 map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);  /* 分配一个struct videobuf_mapping结构体 */
 if (NULL == map)
  return -ENOMEM;

 q->bufs[first]->map = map;
 map->start = vma->vm_start;
 map->end   = vma->vm_end;
 map->q     = q;

 q->bufs[first]->baddr = vma->vm_start;

 mem = q->bufs[first]->priv;
 BUG_ON(!mem);
 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);

 pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
 mem->vmalloc = vmalloc_user(pages);
 if (!mem->vmalloc) {
  printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
  goto error;
 }
 dprintk(1, "vmalloc is at addr %p (%d pages)\n",
  mem->vmalloc, pages);

 /* Try to remap memory */
 retval = remap_vmalloc_range(vma, mem->vmalloc, 0); /*为vmalloc分配的内存建立页表 */
 if (retval < 0) {
  printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
  vfree(mem->vmalloc);
  goto error;
 }

 vma->vm_ops          = &videobuf_vm_ops;
 vma->vm_flags       |= VM_DONTEXPAND | VM_RESERVED;
 vma->vm_private_data = map;

 dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
  map, q, vma->vm_start, vma->vm_end,
  (long int) q->bufs[first]->bsize,
  vma->vm_pgoff, first);

 videobuf_vm_open(vma); /*打开 */

 return 0;

error:
 mem = NULL;
 kfree(map);
 return -ENOMEM;
}

/* 拷贝到用户空间 */
static int __videobuf_copy_to_user ( struct videobuf_queue *q,
    char __user *data, size_t count,
    int nonblocking )
{
 struct videobuf_vmalloc_memory *mem=q->read_buf->priv;
 BUG_ON (!mem);
 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);

 BUG_ON (!mem->vmalloc);

 /* copy to userspace */
 if (count > q->read_buf->size - q->read_off)
  count = q->read_buf->size - q->read_off;

 if (copy_to_user(data, mem->vmalloc+q->read_off, count))
  return -EFAULT;

 return count;
}
/* 拷贝流 */
static int __videobuf_copy_stream ( struct videobuf_queue *q,
    char __user *data, size_t count, size_t pos,
    int vbihack, int nonblocking )
{
 unsigned int  *fc;
 struct videobuf_vmalloc_memory *mem=q->read_buf->priv;
 BUG_ON (!mem);
 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);

 if (vbihack) {
  /* dirty, undocumented hack -- pass the frame counter
   * within the last four bytes of each vbi data block.
   * We need that one to maintain backward compatibility
   * to all vbi decoding software out there ... */
  fc  = (unsigned int*)mem->vmalloc;
  fc += (q->read_buf->size>>2) -1;
  *fc = q->read_buf->field_count >> 1;
  dprintk(1,"vbihack: %d\n",*fc);
 }

 /* copy stuff using the common method */
 count = __videobuf_copy_to_user (q,data,count,nonblocking);

 if ( (count==-EFAULT) && (0 == pos) )
  return -EFAULT;

 return count;
}

static struct videobuf_qtype_ops qops = {
 .magic        = MAGIC_QTYPE_OPS,

 .alloc        = __videobuf_alloc,
 .iolock       = __videobuf_iolock,
 .sync         = __videobuf_sync,
 .mmap_free    = __videobuf_mmap_free,
 .mmap_mapper  = __videobuf_mmap_mapper,
 .video_copy_to_user = __videobuf_copy_to_user,
 .copy_stream  = __videobuf_copy_stream,
 .vmalloc      = videobuf_to_vmalloc,
};

/* 视频缓冲区使用vmalloc时的初始化 */
void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
    struct videobuf_queue_ops *ops,
    void *dev,
    spinlock_t *irqlock,
    enum v4l2_buf_type type,
    enum v4l2_field field,
    unsigned int msize,
    void *priv)
{
 videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
     priv, &qops);
}

EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);

/* 获取虚拟地址 */
void *videobuf_to_vmalloc (struct videobuf_buffer *buf)
{
 struct videobuf_vmalloc_memory *mem=buf->priv;
 BUG_ON (!mem);
 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);

 return mem->vmalloc;
}
EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);

void videobuf_vmalloc_free (struct videobuf_buffer *buf)
{
 struct videobuf_vmalloc_memory *mem = buf->priv;

 /* mmapped memory can't be freed here, otherwise mmapped region
    would be released, while still needed. In this case, the memory
    release should happen inside videobuf_vm_close().
    So, it should free memory only if the memory were allocated for
    read() operation.
  */
 if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
  return;

 if (!mem)
  return;

 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);

 vfree(mem->vmalloc);  /* 释放分配的内存 */
 mem->vmalloc = NULL;

 return;
}
EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);

/*
 * Local variables:
 * c-basic-offset: 8
 * End:
 */

阅读(1723) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~