——————/
——————/
——————/分析读写过程
——————/
怎么写v4l2驱动?
① 分配/设置/注册v4l2_device结构:v4l2_device_register
② 分配video_device:video_device_alloc
设置(包含v4l2_device指针,使其指向①所分配设置的)
注册
——————/
APP通过ioctl设置亮度等信息,驱动中谁来接收/存储/提供/设置到硬件?驱动抽象出struct v4l2_ctrl结构,一个v4l2_ctrl对应一项,谁来管理众多v4l2_ctrl?v4l2_ctrl_handler
uvc_probe //uvc_driver.c硬件相关层,定会分配设置向核心层注册一结构体
v4l2_device_register
uvc_register_chains
uvc_register_terms
uvc_register_video
struct video_device *vdev=video_device_alloc();
vdev->fops = &uvc_fops;
video_register_device(vdev,…… //核心v4l2_dev.c
static const struct file_operations v4l2_fops = { //标准统一接口
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.unlocked_ioctl = v4l2_ioctl,
……
}
APP调用open,导致调用v4l2_fops中v4l2_open,最终用到video_device提供的各种函数及属性
——————/
vivi_init
vivi_create_instance
v4l2_device_register //初始化
//struct vivi_dev *dev; struct v4l2_ctrl*volume;
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, //添加新的标准ctrl
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); //id,最小最大默认
…………
dev->button =v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
…………
struct video_device *vfd=video_device_alloc;//分配
*vfd = vivi_template; //设置
vfd->v4l2_dev = &dev->v4l2_dev;
video_register_device(struct video_device *vdev,int type, int nr) //注册
__video_register_device
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
…… //根据类型得到不同名字
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
…… //根据类型得到不同次设备号
//以上用于创建设备节点
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add;
video_device[vdev->minor] = vdev;
//以次设备号为下标,在数组中放入…
——————/
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
……
};
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
};
file_operations中函数会调用到v4l2_file_operations中的函数
——————/分析读写过程
APP : open(“/dev/video0”,……)
v4l2_open
struct video_device *vdev = video_devdata(filp);
//据次…从数组中得到video_device结构体
return video_device[iminor(file->f_path.dentry->d_inode)];
if (vdev->fops->open)
……//若存在则调用,实际调用v4l2_file_operations的v4l2_fh_open
read、write过程同上
——————/
APP : ioctl
v4l2_ioctl
struct video_device *vdev = video_devdata(filp);
if (vdev->fops->unlocked_ioctl)
vdev->fops->unlocked_ioctl(filp, cmd, arg);
video_ioctl2
return video_usercopy(file, cmd, arg, __video_do_ioctl);
__video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
struct video_device *vfd = video_devdata(file);
switch (cmd)
case VIDIOC_QUERYCAP: //v4l2-ioctl.c
……
//根据APP传入cmd,设置、获得相关属性
——————/
怎么写v4l2驱动?
① 分配/设置/注册v4l2_device结构:v4l2_device_register
② 分配video_device:video_device_alloc
设置(包含v4l2_device指针,使其指向①所分配设置的)
注册
——————/
APP通过ioctl设置亮度等信息,驱动中谁来接收/存储/提供/设置到硬件?驱动抽象出struct v4l2_ctrl结构,一个v4l2_ctrl对应一项,谁来管理众多v4l2_ctrl?v4l2_ctrl_handler
vivi_create_instance
struct video_device *vfd;
struct vivi_dev *dev;
struct v4l2_ctrl_handler *hdl;
hdl = &dev->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 11); //初始化ctrl_handler
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
…………
//handler含链表头,用v4l2_ctrl_new_std、v4l2_ctrl_new_custom向其添加v4l2_ctrl(属性)
dev->v4l2_dev.ctrl_handler = hdl; //与v4l2_device关联
vfd->v4l2_dev = &dev->v4l2_dev;
__video_register_device开头
struct video_device *vdev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
…………
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
v4l2_ctrl_handler使用过程:
__video_do_ioctl
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
switch (cmd)
case VIDIOC_QUERYCAP:
ops->vidioc_querycap(file, fh, cap); //部分情况直接调用
……
case VIDIOC_QUERYCTRL:
if (vfd->ctrl_handler)
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
struct v4l2_ctrl *ctrl;
struct v4l2_ctrl_ref *ref;
ref = find_ref(hdl, id); //据id从v4l2_ctrl_handler找到v4l2_ctrl
ctrl = ref->ctrl;
qc->minimum = ctrl->minimum; //返回其最小最大值默认值
qc->maximum = ctrl->maximum;