imx6平台video简介(二)
在上节中我们介绍V4L2的概念、v4l2的设备以及上层点击APP时的调用过程;这节我们详细分底层驱动的注册以及API的调用过程。
参考链接:https://blog.****.net/jack_a8/article/details/43113103向这位老师傅致敬,写的非常好!
言归正传,在这节中slave设备是ti940_mipi.c,master设备就不说了。先来看看流程图:
这张图基本上能说明了各个调用过程,接下里我们分析代码:
Dvr设备是通过I2C进行通信的,既然是I2C设备,那么肯定也有主从分别,现在看下slave设备注册,以ti940为例:
staticstruct v4l2_int_ioctl_desc ti_ub940_ioctl_desc[] = {
{ vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init },
{vidioc_int_dev_exit_num, ioctl_dev_exit },
{ vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power },
{ vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm },
{ vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init },
{ vidioc_int_enum_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_enum_fmt_cap },
{vidioc_int_enum_frameintervals_num,(v4l2_int_ioctl_func*) ioctl_enum_frameintervals},
{ vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap },
{ vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm },
{ vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm },
{ vidioc_int_enum_framesizes_num,(v4l2_int_ioctl_func*)ioctl_enum_framesizes },
{ vidioc_int_g_chip_ident_num, (v4l2_int_ioctl_func*)ioctl_g_chip_ident },
{ vidioc_int_reset_num, (v4l2_int_ioctl_func*)ioctl_reset },
};
staticstruct v4l2_int_slave ti_ub940_slave = {
.ioctls =ti_ub940_ioctl_desc,
.num_ioctls = ARRAY_SIZE(ti_ub940_ioctl_desc),
};
staticstruct v4l2_int_device ti_ub940_int_device = {
.module =THIS_MODULE,
.name ="ti_ub940",
.type =v4l2_int_type_slave,
.u =
{
.slave = &ti_ub940_slave,
},
};
在probe函数中用 v4l2_int_device_register(&ti_ub940_int_device);注册v4l2_int_device
intv4l2_int_device_register(struct v4l2_int_device *d)
{
if (d->type == v4l2_int_type_slave)
sort(d->u.slave->ioctls,d->u.slave->num_ioctls, //依次排序存储
sizeof(struct v4l2_int_ioctl_desc),
&ioctl_sort_cmp, NULL);
mutex_lock(&mutex);
list_add(&d->head, &int_list); //主从设备都会加到int_list链表中
v4l2_int_device_try_attach_all(); //调用此函数进行适配
mutex_unlock(&mutex);
return 0;
}
voidv4l2_int_device_try_attach_all(void)
{
struct v4l2_int_device *m, *s;
list_for_each_entry(m, &int_list,head) { //对链表中的每一个master进行适配
if (m->type !=v4l2_int_type_master)
continue;
list_for_each_entry(s,&int_list, head) { //对链表中每一个slave进行适配
if (s->type !=v4l2_int_type_slave)
continue;
/* Slave is connected? */
if(s->u.slave->master) //slave有master了就继续
continue;
/* Slave wants to attach tomaster? */
if(s->u.slave->attach_to[0] != 0
&& strncmp(m->name,s->u.slave->attach_to,
V4L2NAMESIZE))
continue;
if(!try_module_get(m->module))
continue;
s->u.slave->master =m; //slave找到匹配的master
if(m->u.master->attach(s)) {
s->u.slave->master= NULL;
module_put(m->module);
continue;
}
}
}
}
Master的注册过程,我们直接看prober函数:
staticint mxc_v4l2_probe(struct platform_device *pdev)
{
/* Create cam and initialize it. */
cam_data *cam = kmalloc(sizeof(cam_data),GFP_KERNEL);
if (cam == NULL) {
pr_err("ERROR: v4l2 capture:failed to register camera\n");
return -1;
}
init_camera_struct(cam, pdev); //初始化camera_data结构体
cam->video_dev->ioctl_lock =(struct mutex *)kzalloc(sizeof(struct mutex), GFP_KERNEL);
if(cam->video_dev->ioctl_lock)
mutex_init(cam->video_dev->ioctl_lock);
pdev->dev.release =camera_platform_release;
/* Set up the v4l2 device and registerit*/
cam->self->priv = cam; //私有数据保存
v4l2_int_device_register(cam->self); //与slave类似,注册到链表中
/* register v4l video device */
if(video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)
== -1) { //注册video设备
kfree(cam);
cam = NULL;
pr_err("ERROR: v4l2 capture: video_register_devicefailed\n");
return -1;
}
return 0;
}
先来看看init_camera_struct(cam, pdev)函数,这个函数是对camera图像、帧率等要素的设置:
staticvoid init_camera_struct(cam_data *cam, struct platform_device *pdev)
{
struct fsl_mxc_capture_platform_data*pdata = pdev->dev.platform_data;
pr_debug("In MVC:init_camera_struct\n");
/* Default everything to 0 */
memset(cam, 0, sizeof(cam_data));
cam->ipu = ipu_get_soc(pdata->ipu);
init_MUTEX(&cam->param_lock); //初始化cam->param_lock互斥量
init_MUTEX(&cam->busy_lock); //初始化cam->busy_lock互斥量
cam->video_dev = video_device_alloc(); //分配内存
if (cam->video_dev == NULL)
return;
*(cam->video_dev) =mxc_v4l_template; //提供ops接口
video_set_drvdata(cam->video_dev,cam);
dev_set_drvdata(&pdev->dev, (void*)cam);
cam->video_dev->minor = -1;
init_waitqueue_head(&cam->enc_queue); //初始化等待队列头
init_waitqueue_head(&cam->still_queue);
/* setup cropping */ //图像显示设置
cam->crop_bounds.left = 0;
cam->crop_bounds.width = 720;
cam->crop_bounds.top = 0;
cam->crop_bounds.height = 480;
cam->crop_current =cam->crop_defrect = cam->crop_bounds;
ipu_csi_set_window_size(cam->ipu,cam->crop_current.width,
cam->crop_current.height,cam->csi);
ipu_csi_set_window_pos(cam->ipu,cam->crop_current.left,
cam->crop_current.top,cam->csi);
cam->streamparm.parm.capture.capturemode= 0;
cam->standard.index = 0;
cam->standard.id = V4L2_STD_UNKNOWN;
cam->standard.frameperiod.denominator= 30;
cam->standard.frameperiod.numerator =1;
cam->standard.framelines = 480;
cam->standard_autodetect = true;
cam->streamparm.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->streamparm.parm.capture.timeperframe= cam->standard.frameperiod;
cam->streamparm.parm.capture.capability= V4L2_CAP_TIMEPERFRAME;
cam->overlay_on = false;
cam->capture_on = false;
cam->v4l2_fb.flags =V4L2_FBUF_FLAG_OVERLAY;
cam->v2f.fmt.pix.sizeimage = 720 * 480* 3 / 2;
cam->v2f.fmt.pix.bytesperline = 720 *3 / 2;
cam->v2f.fmt.pix.width = 720;
cam->v2f.fmt.pix.height = 480;
cam->v2f.fmt.pix.pixelformat =V4L2_PIX_FMT_YUV420;
cam->win.w.width = 1024;
cam->win.w.height = 600;
cam->win.w.left = 0;
cam->win.w.top = 0;
cam->ipu_id = pdata->ipu;
cam->csi = pdata->csi;
cam->mclk_source =pdata->mclk_source;
cam->mclk_on[cam->mclk_source] =false;
cam->enc_callback = camera_callback; //设置回调函数,设置ioctl中队里的唤醒条件。
init_waitqueue_head(&cam->power_queue);
spin_lock_init(&cam->queue_int_lock); //
spin_lock_init(&cam->dqueue_int_lock);
INIT_WORK(&cam->reset_work, mxc_reset_stream_async);
setup_timer(&cam->watchdog,mxc_mipi_timeout, (unsigned long)cam);
cam->dummy_frame.vaddress =dma_alloc_coherent(0,
SZ_8M,&cam->dummy_frame.paddress,
GFP_DMA | GFP_KERNEL);
if (cam->dummy_frame.vaddress == 0)
pr_err("ERROR: v4l2 capture:Allocate dummy frame "
"failed.\n");
cam->dummy_frame.buffer.length =SZ_8M;
cam->self = kmalloc(sizeof(structv4l2_int_device), GFP_KERNEL);
cam->self->module = THIS_MODULE;
sprintf(cam->self->name, "mxc_v4l2_cap%d",pdev->id);
cam->self->type =v4l2_int_type_master;
cam->self->u.master =&mxc_v4l2_master; //主从设备找到后进行匹配函数
} /* ADD-END */
}
*(cam->video_dev)= mxc_v4l_template ,这个函数为上层用户提供接口,但是这个接口对APP用户来说还有一层封装;后面我们在来分析。
我们继续prober函数,从上面slave函数中的分析中我们知道,无论是slave设备还是master设备都会在链表中调用m->u.master->attach(s) 去相互去匹配。调用的函数就是init_camera_struct中的cam->self->u.master = &mxc_v4l2_master中的.attach = mxc_v4l2_master_attach。
继续probe函数分析,video_register_device(cam->video_dev,VFL_TYPE_GRABBER, video_nr)这个函数注册V4L设备并对底层操作接口进行封装。
staticinline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev,type, nr, 1, vdev->fops->owner);
}跳转
int__video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, structmodule *owner)
{
/* Part 1: check device type */ //检查设备类型,我们应该是VFL_TYPE_GRABBER
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base ="v4l-subdev";
break;
default:
printk(KERN_ERR "%s calledwith unknown type: %d\n",
__func__, type);
return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent =vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler =vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer isNULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio =&vdev->v4l2_dev->prio;
}
/* Part 2: find a free minor, device nodenumber and device index. */ //找空闲节点挂载
#ifdefCONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first fourtypes 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_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 :nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0,minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could notget a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdefCONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node numberto minor number */
i = nr;
#else
/* The device node number and minornumbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES;i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could notget a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
/* Should not happen since we thoughtthis minor was free */
WARN_ON(video_device[vdev->minor] !=NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
/* Part 3: Initialize the characterdevice */ //初始化字符设备
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops; //上层调用接口
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev,MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_addfailed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs*/ //注册到sysfs
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, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_registerfailed\n", __func__);
goto cleanup;
}
/* Register the release callback thatwill be called when the last
reference to the device goes away. */
vdev->dev.release =v4l2_device_release;
if (nr != -1 && nr !=vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s:requested %s%d, got %s\n", __func__,
name_base, nr,video_device_node_name(vdev));
/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#ifdefined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev &&vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type =MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name =vdev->name;
vdev->entity.v4l.major =VIDEO_MAJOR;
vdev->entity.v4l.minor =vdev->minor;
ret =media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entityfailed\n",
__func__);
}
#endif
/* Part 6: Activate this minor. The chardevice can now be used. */ //**字符设备
set_bit(V4L2_FL_REGISTERED,&vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
}到目前为止,master和slave的注册基本上结束,接下来我们来分析上层的调用过程:
在上面分析中介绍了ops的操作,vdev->cdev->ops =&v4l2_fops; 代码在v4l2_dev.c中展开分析:
staticconst struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area =v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdefCONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
我们主要分析下open、和ioctl函数,其他的函数与这两个函数类似。
staticint v4l2_open(struct inode *inode, struct file *filp)
{
………………
/* and increase the device refcount */
if (vdev->fops->open) { // video_device中的ops操作
if (vdev->lock &&mutex_lock_interruptible(vdev->lock)) {
ret = -ERESTARTSYS;
goto err;
}
……………….
}
}
还记得*(cam->video_dev) = mxc_v4l_template;函数吗?我们贴出来看看:
staticstruct v4l2_file_operations mxc_v4l_fops = {
.owner = THIS_MODULE,
.open = mxc_v4l_open,
.release = mxc_v4l_close,
.read = mxc_v4l_read,
.ioctl = mxc_v4l_ioctl,
.mmap = mxc_mmap,
.poll = mxc_poll,
};
staticstruct video_device mxc_v4l_template = {
.name = "Mxc Camera",
.fops = &mxc_v4l_fops,
.release = video_device_release,
};/*!
看这个定义vdev->fops->open中的vdev是video_device结构体,他最终调用的是mxc_v4l_fops中的open函数。类似的我们在看ioctl函数,
staticlong v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
………………….
if(vdev->fops->ioctl) {
staticDEFINE_MUTEX(v4l2_ioctl_mutex);
struct mutex *m =vdev->ioctl_lock ?
vdev->ioctl_lock:(vdev->v4l2_dev?&vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex);
…………………………..
}
vdev->fops->ioctl最终会调动到mxc_v4l_template 中的mxc_v4l_ioctl函数,这个函数中video_usercopy会根据用户的cmd值来判断用哪个ioctl。