天天看点

基于imx6.linux理解之v4l2

amingriyue.blog.chinaunix.net

imx6 代码框架文件

..................................................................................

   App    open   (close  write  read )                     user 

...................................................................................

          |

.....................................................................................

 v4l2-dev.c   (v4l2_open  v4l2_read  v4l2_write)   kernel   v4l2 

.....................................................................................

         |

.......................................................................................

  mxc_v4l2_capture.c  ( mxc_v4l_open mxc_v4l_read mxc_v4l_close)  kernel mxc_v4l2_driver)

...................................................................................... 

         |

........................................................................................

ad7180.c 

........................................................................................

.....................................................................................................

  • #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) /*查询能力*/
  • #define VIDIO_G_FMT _IOWR('V', 4, struct v4l2_format) /*获得格式*/
  • #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) /*设置格式*/
  • #define VIDIOC_REQBUFS _IOWR('V', 8, strut v4l2_requestbuffers) /*申请内存*/
  • #define VIDIOC_G_FBUF _IOW('V', 10, struct v4l2_framebuffer) /*获得Framebuffer*/
  • #define VIDIOC_S_BUF _IOW('V', 11, struct v4l2_framebuffer) /*设置Framebuffer*/
  • #define VIDIOC_OVERLAY _IOW('V', 14, int) /*设置Overlay*/
  • #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) /*将内存加入队列*/
  • #define VIDIOC_DQBUF _IOWR('V', 17, strut v4l2_buffer) /*从队列取出内存*/
  • #define VIDIOC_STREAMON _IOW('V', 18, int) /*开始流*/
  • #define VIDIOC_STREAMOFF _IOW('V', 19, int) /*停止流*/
  • #define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) /*得到控制*/
  • #define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) /*设置控制*/
  • 我们先看具体sensor slave怎么注册到v4l2的:

    static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = {//ioctl与对应的序号联系在一起,在v4l2层将被转换成固定的名字

        {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_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_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},

        {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},

    };

    static struct v4l2_int_slave ov5642_slave = {//slave

        .ioctls = ov5642_ioctl_desc,

        .num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc),

    };

    static struct v4l2_int_device ov5642_int_device = {

        .module = THIS_MODULE,

        .name = "ov5642",

        .type = v4l2_int_type_slave,

        .u = {

            .slave = &ov5642_slave,

        },

    };

    v4l2_int_device_register(&ov5642_int_device);//注册v4l2_int_device:

    int v4l2_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);//无论是slave还是master都会添加到int_list中

        v4l2_int_device_try_attach_all();//都会做匹配动作

        mutex_unlock(&mutex);

        return 0;

    }

    我们看下v4l2匹配函数v4l2_int_device_try_attach_all():

    void v4l2_int_device_try_attach_all(void)

    {

        struct v4l2_int_device *m, *s;

        list_for_each_entry(m, &int_list, head) {//对int_list中每个master

            if (m->type != v4l2_int_type_master)

                continue;

            list_for_each_entry(s, &int_list, head) {//对int_list中的每个salve

                if (s->type != v4l2_int_type_slave)

                    continue;

                if (s->u.slave->master)//slave中master已经被赋值说明已经连接起来

                    continue;

                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)) {//执行master的匹配函数

                    s->u.slave->master = NULL;

                    module_put(m->module);

                    continue;

                }

            }

        }

    }

    上面即是slave注册到v4l2的过程,也许你会比较的奇怪,开始那些ioctl哪去呢?系统怎么访问呢?下面我们就分析这个过程。

    我们在v4l2-int-device.h中有这样的定义:

    enum v4l2_int_ioctl_num {

        vidioc_int_enum_fmt_cap_num = 1,

        vidioc_int_g_fmt_cap_num,

        vidioc_int_s_fmt_cap_num,

        vidioc_int_try_fmt_cap_num,

        vidioc_int_queryctrl_num,

        vidioc_int_g_ctrl_num,

        vidioc_int_s_ctrl_num,

        vidioc_int_cropcap_num,

        vidioc_int_g_crop_num,

        vidioc_int_s_crop_num,

        vidioc_int_g_parm_num,

        vidioc_int_s_parm_num,

        vidioc_int_querystd_num,

        vidioc_int_s_std_num,

        vidioc_int_s_video_routing_num,

    ......

    };

    V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *);

    V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *);

    V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *);

    V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *);

    V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *);

    V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *);

    V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *);

    V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *);

    V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);

    V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);

    V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);

    V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);

    V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);

    V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);

    V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);

    我们看下V4L2_INT_WRAPPER_1这个宏定义:

    #define V4L2_INT_WRAPPER_1(name, arg_type, asterisk)            \

        static inline int vidioc_int_##name(struct v4l2_int_device *d,    \

                            arg_type asterisk arg)    \

        {                                \

            return v4l2_int_ioctl_1(d, vidioc_int_##name##_num,    \

                        (void *)(unsigned long)arg);    \

        }                                \

                                        \

        static inline struct v4l2_int_ioctl_desc            \

        vidioc_int_##name##_cb(int (*func)                \

                       (struct v4l2_int_device *,        \

                    arg_type asterisk))            \

        {                                \

            struct v4l2_int_ioctl_desc desc;            \

                                        \

            desc.num = vidioc_int_##name##_num;            \

            desc.func = (v4l2_int_ioctl_func *)func;        \

                                        \

            return desc;                        \

        }

    我们举例来说,V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *),那也就是有了这样的定义:

        static inline int vidioc_int_s_ctrl(struct v4l2_int_device *d,    

                            arg_type asterisk arg)    

        {                                

            return v4l2_int_ioctl_1(d, vidioc_int_s_ctrl_num,    

                        (void *)(unsigned long)arg);    

        }                                

        static inline struct v4l2_int_ioctl_desc            

        vidioc_int_s_ctrl_cb(int (*func)                

                       (struct v4l2_int_device *,        

                    arg_type asterisk))            

        {                                

            struct v4l2_int_ioctl_desc desc;            

            desc.num = vidioc_int_s_ctrl_num;            

            desc.func = (v4l2_int_ioctl_func *)func;        

            return desc;                        

        }

    也就是定义了这两个内联函数。我们再看下v4l2_int_ioctl_1(d, vidioc_int_s_ctrl_num, (void *)(unsigned long)arg):

    int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)

    {

        return ((v4l2_int_ioctl_func_1 *)

            find_ioctl(d->u.slave, cmd,

                   (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);

    }

    转到find_ioctl:

    static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,//这里的slave就上面定义的ov5642_slave

                           v4l2_int_ioctl_func *no_such_ioctl)

    {

        const struct v4l2_int_ioctl_desc *first = slave->ioctls;//这里其实就上面的ov5642_ioctl_desc

        const struct v4l2_int_ioctl_desc *last =

            first + slave->num_ioctls - 1;//slave->num_ioctls=ARRAY_SIZE(ov5642_ioctl_desc)

        while (first <= last) {

            const struct v4l2_int_ioctl_desc *mid;

            mid = (last - first) / 2 + first;//二分法

            if (mid->num < cmd)

                first = mid + 1;

            else if (mid->num > cmd)

                last = mid - 1;

            else

                return mid->func;//找到就返回具体的函数,具体的说这里的函数就是ov5642_slave定义的ov5642_ioctl_desc中的具体函数!

        }

        return no_such_ioctl;

    }

    总结这里的函数对应关系,以ov5642_ioctl_desc中的{vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl}为例,如果系统有这样的函数vidioc_int_s_ctrl(...)使用,那也就是对应引用了

    ov5642_slave的定义的ioctl_s_ctrl(...)。

    下面看下master注册过程:

    static __init int camera_init(void)

    {

        u8 err = 0;

        pr_debug("In MVC:camera_init\n");

        err = platform_driver_register(&mxc_v4l2_driver);//平台注册v4l2驱动

        if (err != 0) {

            pr_err("ERROR: v4l2 capture:camera_init: "

                "platform_driver_register failed.\n");

            return err;

        }

        return err;

    }

    mxc_v4l2_driver定义:

    static struct platform_driver mxc_v4l2_driver = {

        .driver = {

               .name = "mxc_v4l2_capture",

               },

        .probe = mxc_v4l2_probe,

        .remove = mxc_v4l2_remove,

        .suspend = mxc_v4l2_suspend,

        .resume = mxc_v4l2_resume,

        .shutdown = NULL,

    };

    注册成功将会执行mxc_v4l2_probe:

    static int mxc_v4l2_probe(struct platform_device *pdev)

    {

        g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);

        if (g_cam == NULL) {

            pr_err("ERROR: v4l2 capture: failed to register camera\n");

            return -1;

        }

        init_camera_struct(g_cam, pdev);//初始化cam_data结构

        pdev->dev.release = camera_platform_release;

        mxc_v4l2_int_device.priv = g_cam;//注意这里的g_cam赋值,后面mattch函数中会用到

        v4l2_int_device_register(&mxc_v4l2_int_device);//向v4l2注册int_device

        if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)//注册video设备

            == -1) {

            kfree(g_cam);

            g_cam = NULL;

            pr_err("ERROR: v4l2 capture: video_register_device failed\n");

            return -1;

        }

        pr_debug("   Video device registered: %s #%d\n",

             g_cam->video_dev->name, g_cam->video_dev->minor);

        if (device_create_file(&g_cam->video_dev->dev,

                               &dev_attr_fsl_v4l2_capture_property))//创建fsl_v4l2_capture_property属性文件

            dev_err(&pdev->dev, "Error on creating sysfs file"

                " for capture\n");

        if (device_create_file(&g_cam->video_dev->dev,

                &dev_attr_fsl_v4l2_overlay_property))//创建fsl_v4l2_overlay_property属性文件

            dev_err(&pdev->dev, "Error on creating sysfs file"

                " for overlay\n");

        return 0;

    }

    我们首先看init_camera_struct(g_cam, pdev):

    static void init_camera_struct(cam_data *cam, struct platform_device *pdev)

    {

        pr_debug("In MVC: init_camera_struct\n");

        memset(cam, 0, sizeof(cam_data));

        init_MUTEX(&cam->param_lock);

        init_MUTEX(&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);//设置私有数据到video_dev的dev

        dev_set_drvdata(&pdev->dev, (void *)cam);

        cam->video_dev->minor = -1;

        init_waitqueue_head(&cam->enc_queue);

        init_waitqueue_head(&cam->still_queue);

        cam->crop_bounds.left = 0;

        cam->crop_bounds.width = 640;

        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->crop_current.width,

                    cam->crop_current.height, cam->csi);

        ipu_csi_set_window_pos(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 = 352 * 288 * 3 / 2;

        cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;

        cam->v2f.fmt.pix.width = 288;

        cam->v2f.fmt.pix.height = 352;

        cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;

        cam->win.w.width = 160;

        cam->win.w.height = 160;

        cam->win.w.left = 0;

        cam->win.w.top = 0;

        cam->csi = 0; 

        cam->enc_callback = camera_callback;//设置callback

        init_waitqueue_head(&cam->power_queue);

        spin_lock_init(&cam->queue_int_lock);

        spin_lock_init(&cam->dqueue_int_lock);

    }

    这个函数主要是对cam_data结构的初始化。这里需要注意的是*(cam->video_dev) = mxc_v4l_template,我们看下mxc_v4l_template定义:

    static struct 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,

    };

    static struct video_device mxc_v4l_template = {

        .name = "Mxc Camera",

        .fops = &mxc_v4l_fops,

        .release = video_device_release,

    };

    这个定义很重要,v4l2子系统很多调用将通过这个mxc_v4l_template的ops实现。

    下面看下probe函数中的v4l2_int_device_register(&mxc_v4l2_int_device),这个之前在slave注册已经分析过了。从前面的分析知道,无论master还是slave注册时都会互相去匹配,

    找到后会调用master的匹配函数m->u.master->attach(s)。我们看下mxc_v4l2_int_device的定义:

    static struct v4l2_int_master mxc_v4l2_master = {

        .attach = mxc_v4l2_master_attach,

        .detach = mxc_v4l2_master_detach,

    };

    static struct v4l2_int_device mxc_v4l2_int_device = {

        .module = THIS_MODULE,

        .name = "mxc_v4l2_cap",

        .type = v4l2_int_type_master,

        .u = {

            .master = &mxc_v4l2_master,

            },

    };

    从定义中可以看出,attach对应mxc_v4l2_master_attach:

    static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)

    {

        cam_data *cam = slave->u.slave->master->priv;//g_cam,即probe中的mxc_v4l2_int_device.priv = g_cam;

        struct v4l2_format cam_fmt;

        pr_debug("In MVC: mxc_v4l2_master_attach\n");

        pr_debug("   slave.name = %s\n", slave->name);

        pr_debug("   master.name = %s\n", slave->u.slave->master->name);

        cam->sensor = slave;//找到的slave(ov5642)

        if (slave == NULL) {

            pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");

            return -1;

        }

        ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);

        vidioc_int_s_power(cam->sensor, 1);//从上面的slave的ov5642_ioctl_desc定义分析,它将会调用vidioc_int_s_power_num对应的函数

        vidioc_int_dev_init(slave);//调用vidioc_int_dev_init_num对应的函数,初始化

        ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);

        cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);//调用vidioc_int_g_fmt_cap_num对应的函数,得到fmt,并以此为fmt标准

        cam->device_type = cam_fmt.fmt.pix.priv;

        cam->crop_bounds.top = cam->crop_bounds.left = 0;

        cam->crop_bounds.width = cam_fmt.fmt.pix.width;

        cam->crop_bounds.height = cam_fmt.fmt.pix.height;

        cam->crop_defrect.top = cam->crop_defrect.left = 0;

        cam->crop_defrect.width = cam_fmt.fmt.pix.width;

        cam->crop_defrect.height = cam_fmt.fmt.pix.height;

        cam->crop_current.top = cam->crop_current.left = 0;

        cam->crop_current.width = cam_fmt.fmt.pix.width;

        cam->crop_current.height = cam_fmt.fmt.pix.height;

        pr_debug("End of %s: v2f pix widthxheight %d x %d\n",

             __func__,

             cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);

        pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",

             __func__,

             cam->crop_bounds.width, cam->crop_bounds.height);

        pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",

             __func__,

             cam->crop_defrect.width, cam->crop_defrect.height);

        pr_debug("End of %s: crop_current widthxheight %d x %d\n",

             __func__,

             cam->crop_current.width, cam->crop_current.height);

        return 0;

    }

    我们再看下probe中的video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr):

    int video_register_device(struct video_device *vdev, int type, int nr)

    {

        return video_register_device_index(vdev, type, nr, -1);

    }

    转到video_register_device_index(vdev, type, nr, -1):

    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;

        const char *name_base;

        void *priv = video_get_drvdata(vdev);

        vdev->minor = -1;

        WARN_ON(!vdev->release);

        if (!vdev->release)

            return -EINVAL;

        switch (type) {

        case VFL_TYPE_GRABBER:

            name_base = "video";

            break;

        case VFL_TYPE_VTX:

            name_base = "vtx";

            break;

        case VFL_TYPE_VBI:

            name_base = "vbi";

            break;

        case VFL_TYPE_RADIO:

            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;

    #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES

        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

        mutex_lock(&videodev_lock);

        nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);

        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

        i = nr;

    #else

        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 not get a free minor\n");

            return -ENFILE;

        }

    #endif

        vdev->minor = i + minor_offset;

        vdev->num = nr;

        set_bit(nr, video_nums[type]);

        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;

        }

        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;//注意这里的ops,用户打开设备后的file ops

        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;

        }

        memset(&vdev->dev, 0, sizeof(vdev->dev));

        video_set_drvdata(vdev, priv);

        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;

        }

        vdev->dev.release = v4l2_device_release;

        mutex_lock(&videodev_lock);

        video_device[vdev->minor] = vdev;

        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);

        vdev->minor = -1;

        return ret;

    }

    如此,v4l2的slave,master注册算是基本结束了。下面我们开始分析对video_dev的读写ioctl等操作。

    我们从video_register_device_index中知道,注册字符设备时,注册了该字符设备的ops:

    vdev->cdev->ops = &v4l2_fops,我们下面看下v4l2_fops定义:

    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,

        .mmap = v4l2_mmap,

        .ioctl = v4l2_ioctl,

    #ifdef CONFIG_COMPAT

        .compat_ioctl = v4l2_compat_ioctl32,

    #endif

        .release = v4l2_release,

        .poll = v4l2_poll,

        .llseek = no_llseek,

    };

    我们这里只分析一下read,open,ioctl,mmap。先从open开始:

    static int v4l2_open(struct inode *inode, struct file *filp)

    {

        struct video_device *vdev;

        int ret = 0;

        mutex_lock(&videodev_lock);

        vdev = video_devdata(filp);

        if (vdev == NULL || video_is_unregistered(vdev)) {

            mutex_unlock(&videodev_lock);

            return -ENODEV;

        }

        video_get(vdev);

        mutex_unlock(&videodev_lock);

        if (vdev->fops->open)//video_device的ops操作,要回想到上面的mxc_v4l_template

            ret = vdev->fops->open(filp);

        if (ret)

            video_put(vdev);

        return ret;

    }

    我们这边再贴一下mxc_v4l_template:

    static struct v4l2_file_operations mxc_v4l_fops = {

        .owner = THISMODULE,

        .open = mxc_v4l_open,

        .release = mxc_v4l_close,

        .read = mxc_v4l_read,

        .ioctl = mxc_v4l_ioctl,

        .mmap = mxc_mmap,

        .poll = mxc_poll,

    };

    static struct video_device mxc_v4l_template = {

        .name = "Mxc Camera",

        .fops = &mxc_v4l_fops,

        .release = video_device_release,

    };

    看到这个定义,一切真相大白!vdev->fops->open(filp)也就是执行mxc_v4l_open(filp):

    static int mxc_v4l_open(struct file *file)

    {

    ......

        //对csi接口的初始化等一些列动作

    ......

    }

    我们再看v4l2_read:

    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);

    }

    vdev->fops->read(filp, buf, sz, off)转到执行mxc_v4l_read(filp, buf, sz, off):

    static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count,

                    loff_t *ppos)

    {

    ......

        v_address[0] = dma_alloc_coherent(0,//申请dma

                           PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),

                           &cam->still_buf[0],

                           GFP_DMA | GFP_KERNEL);

        v_address[1] = dma_alloc_coherent(0,

                           PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),

                           &cam->still_buf[1],

                           GFP_DMA | GFP_KERNEL);

    ......

        cam->still_counter = 0;

        err = cam->csi_start(cam);//开始camera

        if (err != 0) {

            err = -EIO;

            goto exit1;

        }

    ......

        err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage);//拷贝到用户空间

    ......

    }

    再看v4l2_mmap:

    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);

    }

    vdev->fops->mmap(filp, vm)转到执行mxc_mmap(filp, vm):

    static int mxc_mmap(struct file *file, struct vm_area_struct *vma)

    {

        struct video_device *dev = video_devdata(file);

        unsigned long size;

        int res = 0;

        cam_data *cam = video_get_drvdata(dev);

        pr_debug("In MVC:mxc_mmap\n");

        pr_debug("   pgoff=0x%lx, start=0x%lx, end=0x%lx\n",

             vma->vm_pgoff, vma->vm_start, vma->vm_end);

        if (down_interruptible(&cam->busy_lock))

            return -EINTR;

        size = vma->vm_end - vma->vm_start;

        vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);//定义内存标记属性

        if (remap_pfn_range(vma, vma->vm_start,//将内核空间映射到用户空间

                    vma->vm_pgoff, size, vma->vm_page_prot)) {

            pr_err("ERROR: v4l2 capture: mxc_mmap: "

                "remap_pfn_range failed\n");

            res = -ENOBUFS;

            goto mxc_mmap_exit;

        }

        vma->vm_flags &= ~VM_IO;   

          mxc_mmap_exit:

        up(&cam->busy_lock);

        return res;

    }

    再看v4l2_ioctl:

    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;

        return vdev->fops->ioctl(filp, cmd, arg);

    }

    vdev->fops->ioctl(filp, cmd, arg)转到执行mxc_v4l_ioctl(filp, cmd, arg):

    static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,

                 unsigned long arg)

    {

        pr_debug("In MVC:mxc_v4l_ioctl\n");

        return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);

    }

    video_usercopy这个函数将会根据cmd做一下判断,主要工作由mxc_v4l_do_ioctl完成。这个函数是最重要最核心的一个函数。它要实现v4l2所有常用命令的功能。我们下面不具体分析mxc_v4l_do_ioctl,只列出它的大体结构:

    static long mxc_v4l_do_ioctl(struct file *file,

                    unsigned int ioctlnr, void *arg)

    {

        struct video_device *dev = video_devdata(file);

        cam_data *cam = video_get_drvdata(dev);

        int retval = 0;

        unsigned long lock_flags;

        pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr);

        wait_event_interruptible(cam->power_queue, cam->low_power == false);

        if (down_interruptible(&cam->busy_lock))

            return -EBUSY;

        switch (ioctlnr) {

        case VIDIOC_QUERYCAP: {

            .....

            break;

        }

        case VIDIOC_G_FMT: {

            .....

            break;

        }

        case VIDIOC_S_FMT: {

            .....

            break;

        }

        case VIDIOC_REQBUFS: {

            .....

            break;

        }

        case VIDIOC_QUERYBUF: {

            .....

            break;

        }

        case VIDIOC_QBUF: {

            .....

            break;

        }

        case VIDIOC_DQBUF: {

            .....

            break;

        }

        case VIDIOC_STREAMON: {

            .....

            break;

        }

        case VIDIOC_STREAMOFF: {

            .....

            break;

        }

        case VIDIOC_G_CTRL: {

            .....

            break;

        }

        case VIDIOC_S_CTRL: {

            .....

            break;

        }

        case VIDIOC_CROPCAP: {

            .....

            break;

        }

        case VIDIOC_G_CROP: {

            .....

            break;

        }

        case VIDIOC_S_CROP: {

            .....

            break;

        }

        case VIDIOC_OVERLAY: {

            .....

            break;

        }

        case VIDIOC_G_FBUF: {

            .....

            break;

        }

        case VIDIOC_S_FBUF: {

            .....

            break;

        }

        case VIDIOC_G_PARM: {

            .....

            break;

        }

        case VIDIOC_S_PARM:  {

            .....

            break;

        }

        case VIDIOC_ENUMSTD: {

            .....

            break;

        }

        case VIDIOC_G_STD: {

            .....

            break;

        }

        case VIDIOC_S_STD: {

            .....

            break;

        }

        case VIDIOC_ENUMOUTPUT: {

            .....

            break;

        }

        case VIDIOC_G_OUTPUT: {

            .....

            break;

        }

        case VIDIOC_S_OUTPUT: {

            .....

            break;

        }

        case VIDIOC_ENUMINPUT: {

            .....

            break;

        }

        case VIDIOC_G_INPUT: {

            .....ut;

            break;

        }

        case VIDIOC_S_INPUT: {

            .....

            break;

        }

        case VIDIOC_ENUM_FMT: {

            .....

            break;

        }

        case VIDIOC_ENUM_FRAMESIZES: {

            .....

            break;

        }

        case VIDIOC_DBG_G_CHIP_IDENT: {

            .....

            break;

        }

        case VIDIOC_TRY_FMT:

        case VIDIOC_QUERYCTRL:

        case VIDIOC_G_TUNER:

        case VIDIOC_S_TUNER:

        case VIDIOC_G_FREQUENCY:

        case VIDIOC_S_FREQUENCY:

        default:

            pr_debug("   case default or not supported\n");

            retval = -EINVAL;

            break;

        }

        up(&cam->busy_lock);

        return retval;

    }

    上面这些命令非常重要,是v4l2的标准命令实现,这样基于v4l2实现的camera可以用统一的命令进行用户空间操作,对程序的可重用性可移植带来极大的方便。

    这样整个v4l2子系统分析就差不多了,整体来说v4l2基本架构还是比较清晰的:用户空间-->v4l2-->master。要想对v4l2有更深入的理解掌握,需要实际利用v4l2的标准ioctl命令进行应用编程。