imx6平台video简介(二)

在上节中我们介绍V4L2的概念、v4l2的设备以及上层点击APP时的调用过程;这节我们详细分底层驱动的注册以及API的调用过程。

参考链接:https://blog.csdn.net/jack_a8/article/details/43113103向这位老师傅致敬,写的非常好!

言归正传,在这节中slave设备是ti940_mipi.c,master设备就不说了。先来看看流程图:

imx6平台video简介(二)

这张图基本上能说明了各个调用过程,接下里我们分析代码:

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。

以上是 imx6平台video简介(二) 的全部内容, 来源链接: utcz.com/a/69587.html

回到顶部