Chinaunix首页 | 论坛 | 博客
  • 博客访问: 225380
  • 博文数量: 149
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1127
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-26 15:53
个人简介

喷泉之所以漂亮,是因为她有压力;瀑布之所以壮观,是因为她没退路。

文章分类

全部博文(149)

文章存档

2016年(25)

2015年(124)

我的朋友

分类: Android平台

2015-11-17 12:21:13

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_g_needs_reset_num,
                    (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
    /*    {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
        {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_try_fmt_cap_num,
                    (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
        {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
    /*    {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_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_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
        {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;

                /* Slave is connected? */
                if (s->u.slave->master)//slave中master已经被赋值说明已经连接起来
                    continue;

                /* Slave wants to attach to master? */
                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中有这样的定义:
    /* IOCTL command numbers. */
    enum v4l2_int_ioctl_num {
        /*
         *
         * "Proper" V4L ioctls, as in struct video_device.
         *
         */
        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");

        /* Register the device driver structure. */
        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)
    {
        /* Create g_cam and initialize it. */
        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;

        /* Set up the v4l2 device and register it*/
        mxc_v4l2_int_device.priv = g_cam;//注意这里的g_cam赋值,后面mattch函数中会用到
        /* This function contains a bug that won't let this be rmmod'd. */
        v4l2_int_device_register(&mxc_v4l2_int_device);//向v4l2注册int_device

        /* register v4l video 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");

        /* Default everything to 0 */
        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);

        /* setup cropping */
        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;  /* Need to determine how to set this correctly with
                * multiple video input devices. */

        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标准

        /* Used to detect TV in (type 1) vs. camera (type 0)*/
        cam->device_type = cam_fmt.fmt.pix.priv;

        /* Set the input size to the ipu for this device */
        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;

        /* This also is the max crop size for this device. */
        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;

        /* At this point, this is also the current image size. */
        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);

        /* A minor value of -1 marks this video device as never
           having been registered */
        vdev->minor = -1;

        /* the release callback MUST be present */
        WARN_ON(!vdev->release);
        if (!vdev->release)
            return -EINVAL;

        /* Part 1: check device type,检查设备类型,我们这里是VFL_TYPE_GRABBER */
        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;

        /* Part 2: find a free minor, kernel number and device index. 找一个空的minor*/
    #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
        /* Keep the ranges for the first four types 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_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

        /* Pick a minor number */
        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
        /* 1-on-1 mapping of kernel number to minor number */
        i = nr;
    #else
        /* The kernel number and minor numbers are independent */
        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]);
        /* Should not happen since we thought this minor was free */
        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;
        }

        /* Part 3: Initialize the character device ,初始化字符设备*/
        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;
        }

        /* Part 4: register the device with sysfs 注册的到sysfs*/
        memset(&vdev->dev, 0, sizeof(vdev->dev));
        /* The memset above cleared the device's drvdata, so
           put back the copy we made earlier. */
        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;
        }
        /* Register the release callback that will be called when the last
           reference to the device goes away. */
        vdev->dev.release = v4l2_device_release;

        /* Part 5: Activate this minor. The char device can now be used. 激活设备,设备可以使用*/
        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);
        /* Mark this video device as never having been registered. */
        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;

        /* Check if the video device is available */
        mutex_lock(&videodev_lock);
        vdev = video_devdata(filp);
        /* return ENODEV if the video device has been removed
           already or if it is not registered anymore. */
        if (vdev == NULL || video_is_unregistered(vdev)) {
            mutex_unlock(&videodev_lock);
            return -ENODEV;
        }
        /* and increase the device refcount */
        video_get(vdev);
        mutex_unlock(&videodev_lock);
        if (vdev->fops->open)//video_device的ops操作,要回想到上面的mxc_v4l_template
            ret = vdev->fops->open(filp);

        /* decrease the refcount in case of an error */
        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);

        /* make this _really_ smp-safe */
        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;    /* using shared anonymous pages */

          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;
        /* Allow ioctl to continue even if the device was unregistered.
           Things like dequeueing buffers might still be useful. */
        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);
        /* make this _really_ smp-safe */
        if (down_interruptible(&cam->busy_lock))
            return -EBUSY;

        switch (ioctlnr) {
        /*!
         * V4l2 VIDIOC_QUERYCAP ioctl
         */
        case VIDIOC_QUERYCAP: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_G_FMT ioctl
         */
        case VIDIOC_G_FMT: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_S_FMT ioctl
         */
        case VIDIOC_S_FMT: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_REQBUFS ioctl
         */
        case VIDIOC_REQBUFS: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_QUERYBUF ioctl
         */
        case VIDIOC_QUERYBUF: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_QBUF ioctl
         */
        case VIDIOC_QBUF: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_DQBUF ioctl
         */
        case VIDIOC_DQBUF: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_STREAMON ioctl
         */
        case VIDIOC_STREAMON: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_STREAMOFF ioctl
         */
        case VIDIOC_STREAMOFF: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_G_CTRL ioctl
         */
        case VIDIOC_G_CTRL: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_S_CTRL ioctl
         */
        case VIDIOC_S_CTRL: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_CROPCAP ioctl
         */
        case VIDIOC_CROPCAP: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_G_CROP ioctl
         */
        case VIDIOC_G_CROP: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_S_CROP ioctl
         */
        case VIDIOC_S_CROP: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_OVERLAY ioctl
         */
        case VIDIOC_OVERLAY: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_G_FBUF ioctl
         */
        case VIDIOC_G_FBUF: {
            .....
            break;
        }

        /*!
         * V4l2 VIDIOC_S_FBUF ioctl
         */
        case VIDIOC_S_FBUF: {
            .....
            break;
        }

        case VIDIOC_G_PARM: {
            .....
            break;
        }

        case VIDIOC_S_PARM:  {
            .....
            break;
        }

        /* linux v4l2 bug, kernel c0485619 user c0405619 */
        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命令进行应用编程。
  • 阅读(872) | 评论(0) | 转发(0) |
    0

    上一篇:Android getprop

    下一篇:V4L2(Video 4 Linux 2)

    给主人留下些什么吧!~~