V4L2相關代碼位於linux-3.4/drivers/media/video目錄中。
v4l2-dev.c文件提供設備節點實現與用戶層數據交流,設備節點在/dev/目錄下以video0、video1等名字出現。註冊字符設備的語句如下:
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
只要調用函數video_register_device即可把一個video_device註冊到V4L2架構中;但這只是一個總綱領,Camera驅動主要是實現V4L2的一個子系統的功能,子系統用一個v4l2_subdev結構體來描述,只有實現好了系統要求的相關函數操作,最後一步纔是註冊到V4L2中。
子系統實現的方式每個平臺都有差別,這裏分析的是全志A23平臺的代碼。
在sunxi-vfe/vfe.c文件中,以platform形式註冊了前後攝像頭的平臺資源。匹配過程忽略,最終vfe_probe函數會被調用。在probe中,看到有函數v4l2_i2c_new_subdev_board:
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs,
NULL);
else
client = i2c_new_device(adapter, info);
...............................
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
這裏的client獲得在之前寫的I2C驅動的文章已經分析過,可見驅動的知識都是環環相扣的;根據I2C驅動文章的分析可知獲取client過程中,device_register(&client->dev)會被調用,而根據device和device_driver的模型關係可知,device所在的bus總線會進行匹配,device處於i2c總線下,根據I2C match函數的匹配,名字相同的device_driver將被匹配中;我們假設i2c_client的name爲"ov5460"吧,這樣ov5460.c中看看:
static struct i2c_driver sensor_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
毫無疑問,sensor_probe函數會被調用了:
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct v4l2_subdev *sd;
struct sensor_info *info;
// int ret;
info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
sd = &info->sd;
glb_sd = sd;
v4l2_i2c_subdev_init(sd, client, &sensor_ops);
info->fmt = &sensor_formats[0];
info->af_first_flag = 1;
info->init_first_flag = 1;
info->auto_focus = 0;
return 0;
}
這裏就看到了前面提到的v4l2_subdev結構體,v4l2_i2c_subdev_init函數會進入v4l2-subdev.c進行一系列初始化操作,並且用i2c_set_clientdata(client, sd);保存子系統指針,以便後續取出。這裏sensor_ops結構體即爲子系統支持的類型:
static const struct v4l2_subdev_ops sensor_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
};
Camera當然是是video的了,core是核心,應該是不可少的操作吧。其實v4l2_subdev所支持的類型很多,其全部類型定義如下:
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core;
const struct v4l2_subdev_tuner_ops *tuner;
const struct v4l2_subdev_audio_ops *audio;
const struct v4l2_subdev_video_ops *video;
const struct v4l2_subdev_vbi_ops *vbi;
const struct v4l2_subdev_ir_ops *ir;
const struct v4l2_subdev_sensor_ops *sensor;
const struct v4l2_subdev_pad_ops *pad;
};
tuner應該屬於電視一類的吧,還有ir,紅外;audio,音頻等。看看video要實現的操作:
struct v4l2_subdev_video_ops {
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
int (*s_stream)(struct v4l2_subdev *sd, int enable);
int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);
int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*g_frame_interval)(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval);
int (*s_frame_interval)(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval);
int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);
int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
int (*enum_dv_presets) (struct v4l2_subdev *sd,
struct v4l2_dv_enum_preset *preset);
int (*s_dv_preset)(struct v4l2_subdev *sd,
struct v4l2_dv_preset *preset);
int (*g_dv_preset)(struct v4l2_subdev *sd,
struct v4l2_dv_preset *preset);
int (*query_dv_preset)(struct v4l2_subdev *sd,
struct v4l2_dv_preset *preset);
int (*s_dv_timings)(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings);
int (*g_dv_timings)(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings);
int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code);
int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
struct v4l2_frmsizeenum *fsize);
int (*g_mbus_fmt)(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt);
int (*try_mbus_fmt)(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt);
int (*s_mbus_fmt)(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt);
int (*g_mbus_config)(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg);
int (*s_mbus_config)(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg);
};
太多了,這就是一個具體攝像頭主要實現的操作,而ov5640只是支持其中一部分操作而已:
static const struct v4l2_subdev_core_ops sensor_core_ops = {
.g_chip_ident = sensor_g_chip_ident,
.g_ctrl = sensor_g_ctrl,
.s_ctrl = sensor_s_ctrl,
.queryctrl = sensor_queryctrl,
.reset = sensor_reset,
.init = sensor_init,
.s_power = sensor_power,
.ioctl = sensor_ioctl,
};
static const struct v4l2_subdev_video_ops sensor_video_ops = {
.enum_mbus_fmt = sensor_enum_fmt,
.enum_framesizes = sensor_enum_size,
.try_mbus_fmt = sensor_try_fmt,
.s_mbus_fmt = sensor_s_fmt,
.s_parm = sensor_s_parm,
.g_parm = sensor_g_parm,
.g_mbus_config = sensor_g_mbus_config,
};
這樣回到v4l2_i2c_new_subdev_board函數中,調用v4l2_device_register_subdev把sd註冊到了子系統中去。
回到vfe.c的probe函數中。常用到的操作是v4l2_subdev_call,其實這是一個宏:
#define v4l2_subdev_call(sd, o, f, args...) \
(!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \
(sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
好處在於檢查子系統是否支持某個操作,比如上述ov5640的,只支持sensor_core_ops和sensor_video_ops所定義的操作。其他的返回error。
最後:
vfd = video_device_alloc();
*vfd = vfe_template;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, dev->id);
分配video_device空間並賦值,賦值內容如下:
static struct video_device vfe_template = {
.name = "vfe",
.fops = &vfe_fops,
.ioctl_ops = &vfe_ioctl_ops,
.release = video_device_release,
};
最後註冊到了V4L2系統中。
如果從上層ioctl調用開始分析,vdev->fops->ioctl(filp, cmd, arg);調用的就是這裏的vfe_fops:
static const struct v4l2_file_operations vfe_fops = {
.owner = THIS_MODULE,
.open = vfe_open,
.release = vfe_close,
.read = vfe_read,
.poll = vfe_poll,
.ioctl = video_ioctl2,
//.unlocked_ioctl =
.mmap = vfe_mmap,
};
video_ioctl2是要調用v4l2-ioctl.c通用的ioctl來處理,它會檢查用戶空間參數時候合法等操作。在該文件中調用__video_do_ioctl函數,包含了所有V4L2支持的ioctl操作命令,函數很長,代碼就不貼出來了;在該ioctl中最終才調用vfe_ioctl_ops的操作:
static const struct v4l2_ioctl_ops vfe_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
};
而在這些通用的函數操作用,最終用v4l2_subdev_call來調用具體的Camera操作如ov5640