小弟我对linux理解之v4l2------camera---ov5642---v4l2分析

我对linux理解之v4l2
我们先看具体sensor slave怎么注册到v4l2的:
static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = {//ioctl与对应的序号联系在一起,在v4l2层将被转换成固定的名字
    {vidioc_int_dev_ 我对linux理解之v4l2
我们先看具体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 = 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,
};
看到这个定义,一切真相大白!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命令进行应用编程。

http://www.qrsdev.com/forum.php?mod=viewthread&tid=433&extra=page%3D1 我对linux理解之v4l2
我们先看具体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 = 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,
};
看到这个定义,一切真相大白!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命令进行应用编程。

http://www.qrsdev.com/forum.php?mod=viewthread&tid=433&extra=page%3D1 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 = 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,
};
看到这个定义,一切真相大白!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命令进行应用编程。

http://www.qrsdev.com/forum.php?mod=viewthread&tid=433&extra=page%3D1我对linux理解之v4l2
我们先看具体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 = 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,
};
看到这个定义,一切真相大白!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命令进行应用编程。

http://www.qrsdev.com/forum.php?mod=viewthread&tid=433&extra=page%3D1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值