我们先看具体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