come from :https://blog.csdn.net/lxllinux/article/details/81700765
V4l2可以支持多种设备,它可以有以下几种接口:
1. 视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头.V4L2的最初设计就是应用于这种功能的.
2. 视频输出接口(video output interface):可以驱动计算机的外围视频图像设备--像可以输出电视信号格式的设备.
3. 直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU.
4. 视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号.
5. 收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流.
V4L2 驱动核心
V4L2 的驱动源码在drivers/media/video目录下,主要核心代码有:
v4l2-dev.c //linux版本2视频捕捉接口,主要结构体 video_device 的注册
v4l2-common.c //在Linux操作系统体系采用低级别的操作一套设备structures/vectors的通用视频设备接口。
//此文件将替换videodev.c的文件配备常规的内核分配。
v4l2-device.c //V4L2的设备支持。注册v4l2_device
v4l22-ioctl.c //处理V4L2的ioctl命令的一个通用的框架。
v4l2-subdev.c //v4l2子设备
v4l2-mem2mem.c //内存到内存为Linux和videobuf视频设备的框架。设备的辅助函数,使用其源和目的地videobuf缓冲区。
头文件linux/videodev2.h、media/v4l2-common.h、media/v4l2-device.h、media/v4l2-ioctl.h、media/v4l2-dev.h、media/v4l2-ioctl.h等。
1.V4l2_device
struct V4l2_device{
/* DEV-> driver_data指向这个结构。 注:DEV可能是空的,如果没有父设备是如同ISA设备。 */
struct device *dev;
/* 用于跟踪注册的subdevs */
struct list_head subdevs;
/*锁定此结构体;可以使用的驱动程序以及如果这个结构嵌入到一个更大的结构。 */
spinlock_t lock;
/* 独特的设备名称,默认情况下,驱动程序姓名+总线ID */
char name[V4L2_DEVICE_NAME_SIZE];
/*报告由一些子设备调用的回调函数。 */
void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg);
};
v4l2_device注册和注销
v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
v4l2_device_unregister(struct v4l2_device *v4l2_dev);
2.video_device (进行视频编程时v4l的最重要也是最常用功能)
在/dev目录下的设备节点使用的 struct video_device(v4l2_dev.h)创建。
struct video_device
{
/*设备操作函数 */
const struct v4l2_file_operations *fops;
/* 虚拟文件系统 */
struct device dev; /* v4l 设备 */
struct cdev *cdev; /* 字符设备 */
struct device *parent; /*父设备 */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* 设备信息 */
char name[32];
int vfl_type;
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
/* use bitops to set/clear/test flags */
unsigned long flags;
/*属性来区分一个物理设备上的多个索引 */
int index;
/* V4L2 文件句柄 */
spinlock_t fh_lock; /*锁定所有的 v4l2_fhs */
struct list_head fh_list; /* List of struct v4l2_fh */
int debug; /* Activates debug level*/
/* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */
/* 释放的回调函数 */
void (*release)(struct video_device *vdev);
/* 控制的回调函数 */
const struct v4l2_ioctl_ops *ioctl_ops;
}
动态分配:
struct video_device *vdev = video_device_alloc();
video_register_device(struct video_device *vdev, int type, int nr);
__video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use)
video_unregister_device(struct video_device *vdev);
3.v4l2_subdev 子设备结构体
truct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
#endif
struct list_head list;
struct module *owner;
u32 flags;
struct v4l2_device *v4l2_dev;
const struct v4l2_subdev_ops *ops;
/* 从驱动程序中不要调用这些内部操作函数! */
const struct v4l2_subdev_internal_ops *internal_ops;
/*这个subdev控制处理程序。可能是NULL。 */
struct v4l2_ctrl_handler *ctrl_handler;
/* 名字必须是唯一 */
char name[V4L2_SUBDEV_NAME_SIZE];
/* 可用于到类似subdevs组,值是驱动程序特定的 */
u32 grp_id;
/* 私有数据的指针 */
void *dev_priv;
void *host_priv;
/* subdev 设备节点*/
struct video_device devnode;
/* 事件的数量在打开的时候被分配 */
unsigned int nevents;
};
4.v4l2_buffer 缓冲区结构体
struct v4l2_buffer {
__u32 index;
enum v4l2_buf_type type;
__u32 byteSUSEd;
__u32 flags;
enum v4l2_field field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
enum v4l2_memory memory;
union {
__u32 offset;
unsigned long userptr;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
V4L2核心API提供了一套标准方法的用于处理视频缓冲器(称为“videobuf”)。这些方法允许驱动程序以一致的方式来实现 read(),mmap()和overlay()。目前使用的设备上的视频缓冲器,支持scatter/gather方法(videobuf-dma- SG),线性存取的DMA的(videobuf-DMA-contig),vmalloc分配的缓冲区,主要用于在USB驱动程序(DMA缓冲区的方法 videobuf-vmalloc)。
videobuf层的功能为一种V4L2驱动和用户空间之间的粘合层。它可以处理存储视频帧缓冲区的分配和管理。有一组可用于执行许多标准的POSIX I / O系统调用的功能,包括read(),poll()的,happily,mmap()。另一套功能可以用来实现大部分的V4L2的ioctl()调用相关 的流式I/ O的,包括缓冲区分配,排队和dequeueing,流控制。驱动作者使用videobuf规定了一些设计决定,但回收期在驱动器和一个V4L2的用户空 间API的贯彻实施在减少代码的形式。
关于videobuf的层的更多信息,请参阅Documentation/video4linux/videobuf
入口:+int __init vivi_init(void)
+ vivi_create_instance(i) /*创建设备*//**/。
+ 分配一个vivi_dev的结构体 /*它嵌套这结构体v4l2_device 和video_device*/
+ v4l2_device_register(NULL, &dev->v4l2_dev);/*注册vivi_dev中的V4l2_device*/
+ 初始化视频的DMA队列
+ 初始化锁
+ video_device_alloc(); 动态分配video_device结构体
+ 构建一个video_device结构体 vivi_template 并赋给上面分配的video_device
static struct video_device vivi_template = {
. name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
+ video_set_drvdata(vfd, dev);设置驱动程序专有数据
+ 所有控件设置为其默认值
+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);添加到设备列表
+ 构建 v4l2_file_operations 结构体vivi_fops 并实现.open .release .read .poll .mmap函数
----- .ioctl 用标准的v4l2控制处理程序
+ 构建 v4l2_ioctl_ops结构体 vivi_ioctl_ops
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
};
+ int vivi_open(struct file *file)
+ vivi_dev *dev = video_drvdata(file); 访问驱动程序专用数据
+ 分配+初始化句柄(vivi_fh)数据
+ 重置帧计数器
+ videobuf_queue_vmalloc_init(); 初始化视频缓冲队列
+ 开启一个新线程用于开始和暂停
+ 实现自定义的v4l2_ioctl_ops 函数