amingriyue.blog.chinaunix.net
imx6 代碼架構檔案
..................................................................................
App open (close write read ) user
...................................................................................
|
.....................................................................................
v4l2-dev.c (v4l2_open v4l2_read v4l2_write) kernel v4l2
.....................................................................................
|
.......................................................................................
mxc_v4l2_capture.c ( mxc_v4l_open mxc_v4l_read mxc_v4l_close) kernel mxc_v4l2_driver)
......................................................................................
|
........................................................................................
ad7180.c
........................................................................................
.....................................................................................................
- #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) /*查詢能力*/
- #define VIDIO_G_FMT _IOWR('V', 4, struct v4l2_format) /*獲得格式*/
- #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) /*設定格式*/
- #define VIDIOC_REQBUFS _IOWR('V', 8, strut v4l2_requestbuffers) /*申請記憶體*/
- #define VIDIOC_G_FBUF _IOW('V', 10, struct v4l2_framebuffer) /*獲得Framebuffer*/
- #define VIDIOC_S_BUF _IOW('V', 11, struct v4l2_framebuffer) /*設定Framebuffer*/
- #define VIDIOC_OVERLAY _IOW('V', 14, int) /*設定Overlay*/
- #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) /*将記憶體加入隊列*/
- #define VIDIOC_DQBUF _IOWR('V', 17, strut v4l2_buffer) /*從隊列取出記憶體*/
- #define VIDIOC_STREAMON _IOW('V', 18, int) /*開始流*/
- #define VIDIOC_STREAMOFF _IOW('V', 19, int) /*停止流*/
- #define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) /*得到控制*/
- #define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) /*設定控制*/
-
我們先看具體sensor slave怎麼注冊到v4l2的:
static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = {//ioctl與對應的序号聯系在一起,在v4l2層将被轉換成固定的名字
{vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
{vidioc_int_dev_exit_num, ioctl_dev_exit},
{vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
{vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
{vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
{vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
{vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
{vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
{vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
{vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
};
static struct v4l2_int_slave ov5642_slave = {//slave
.ioctls = ov5642_ioctl_desc,
.num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc),
};
static struct v4l2_int_device ov5642_int_device = {
.module = THIS_MODULE,
.name = "ov5642",
.type = v4l2_int_type_slave,
.u = {
.slave = &ov5642_slave,
},
};
v4l2_int_device_register(&ov5642_int_device);//注冊v4l2_int_device:
int v4l2_int_device_register(struct v4l2_int_device *d)
{
if (d->type == v4l2_int_type_slave)
sort(d->u.slave->ioctls, d->u.slave->num_ioctls,//按照序号存儲,加快通路速度
sizeof(struct v4l2_int_ioctl_desc),
&ioctl_sort_cmp, NULL);
mutex_lock(&mutex);
list_add(&d->head, &int_list);//無論是slave還是master都會添加到int_list中
v4l2_int_device_try_attach_all();//都會做比對動作
mutex_unlock(&mutex);
return 0;
}
我們看下v4l2比對函數v4l2_int_device_try_attach_all():
void v4l2_int_device_try_attach_all(void)
{
struct v4l2_int_device *m, *s;
list_for_each_entry(m, &int_list, head) {//對int_list中每個master
if (m->type != v4l2_int_type_master)
continue;
list_for_each_entry(s, &int_list, head) {//對int_list中的每個salve
if (s->type != v4l2_int_type_slave)
continue;
if (s->u.slave->master)//slave中master已經被指派說明已經連接配接起來
continue;
if (s->u.slave->attach_to[0] != 0
&& strncmp(m->name, s->u.slave->attach_to,
V4L2NAMESIZE))
continue;
if (!try_module_get(m->module))
continue;
s->u.slave->master = m;//說明slave找到了master
if (m->u.master->attach(s)) {//執行master的比對函數
s->u.slave->master = NULL;
module_put(m->module);
continue;
}
}
}
}
上面即是slave注冊到v4l2的過程,也許你會比較的奇怪,開始那些ioctl哪去呢?系統怎麼通路呢?下面我們就分析這個過程。
我們在v4l2-int-device.h中有這樣的定義:
enum v4l2_int_ioctl_num {
vidioc_int_enum_fmt_cap_num = 1,
vidioc_int_g_fmt_cap_num,
vidioc_int_s_fmt_cap_num,
vidioc_int_try_fmt_cap_num,
vidioc_int_queryctrl_num,
vidioc_int_g_ctrl_num,
vidioc_int_s_ctrl_num,
vidioc_int_cropcap_num,
vidioc_int_g_crop_num,
vidioc_int_s_crop_num,
vidioc_int_g_parm_num,
vidioc_int_s_parm_num,
vidioc_int_querystd_num,
vidioc_int_s_std_num,
vidioc_int_s_video_routing_num,
......
};
V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *);
V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *);
V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *);
V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *);
V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *);
V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *);
V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);
V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);
V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);
我們看下V4L2_INT_WRAPPER_1這個宏定義:
#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \
static inline int vidioc_int_##name(struct v4l2_int_device *d, \
arg_type asterisk arg) \
{ \
return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \
(void *)(unsigned long)arg); \
} \
\
static inline struct v4l2_int_ioctl_desc \
vidioc_int_##name##_cb(int (*func) \
(struct v4l2_int_device *, \
arg_type asterisk)) \
{ \
struct v4l2_int_ioctl_desc desc; \
\
desc.num = vidioc_int_##name##_num; \
desc.func = (v4l2_int_ioctl_func *)func; \
\
return desc; \
}
我們舉例來說,V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *),那也就是有了這樣的定義:
static inline int vidioc_int_s_ctrl(struct v4l2_int_device *d,
arg_type asterisk arg)
{
return v4l2_int_ioctl_1(d, vidioc_int_s_ctrl_num,
(void *)(unsigned long)arg);
}
static inline struct v4l2_int_ioctl_desc
vidioc_int_s_ctrl_cb(int (*func)
(struct v4l2_int_device *,
arg_type asterisk))
{
struct v4l2_int_ioctl_desc desc;
desc.num = vidioc_int_s_ctrl_num;
desc.func = (v4l2_int_ioctl_func *)func;
return desc;
}
也就是定義了這兩個内聯函數。我們再看下v4l2_int_ioctl_1(d, vidioc_int_s_ctrl_num, (void *)(unsigned long)arg):
int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
{
return ((v4l2_int_ioctl_func_1 *)
find_ioctl(d->u.slave, cmd,
(v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
}
轉到find_ioctl:
static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,//這裡的slave就上面定義的ov5642_slave
v4l2_int_ioctl_func *no_such_ioctl)
{
const struct v4l2_int_ioctl_desc *first = slave->ioctls;//這裡其實就上面的ov5642_ioctl_desc
const struct v4l2_int_ioctl_desc *last =
first + slave->num_ioctls - 1;//slave->num_ioctls=ARRAY_SIZE(ov5642_ioctl_desc)
while (first <= last) {
const struct v4l2_int_ioctl_desc *mid;
mid = (last - first) / 2 + first;//二分法
if (mid->num < cmd)
first = mid + 1;
else if (mid->num > cmd)
last = mid - 1;
else
return mid->func;//找到就傳回具體的函數,具體的說這裡的函數就是ov5642_slave定義的ov5642_ioctl_desc中的具體函數!
}
return no_such_ioctl;
}
總結這裡的函數對應關系,以ov5642_ioctl_desc中的{vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl}為例,如果系統有這樣的函數vidioc_int_s_ctrl(...)使用,那也就是對應引用了
ov5642_slave的定義的ioctl_s_ctrl(...)。
下面看下master注冊過程:
static __init int camera_init(void)
{
u8 err = 0;
pr_debug("In MVC:camera_init\n");
err = platform_driver_register(&mxc_v4l2_driver);//平台注冊v4l2驅動
if (err != 0) {
pr_err("ERROR: v4l2 capture:camera_init: "
"platform_driver_register failed.\n");
return err;
}
return err;
}
mxc_v4l2_driver定義:
static struct platform_driver mxc_v4l2_driver = {
.driver = {
.name = "mxc_v4l2_capture",
},
.probe = mxc_v4l2_probe,
.remove = mxc_v4l2_remove,
.suspend = mxc_v4l2_suspend,
.resume = mxc_v4l2_resume,
.shutdown = NULL,
};
注冊成功将會執行mxc_v4l2_probe:
static int mxc_v4l2_probe(struct platform_device *pdev)
{
g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
if (g_cam == NULL) {
pr_err("ERROR: v4l2 capture: failed to register camera\n");
return -1;
}
init_camera_struct(g_cam, pdev);//初始化cam_data結構
pdev->dev.release = camera_platform_release;
mxc_v4l2_int_device.priv = g_cam;//注意這裡的g_cam指派,後面mattch函數中會用到
v4l2_int_device_register(&mxc_v4l2_int_device);//向v4l2注冊int_device
if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)//注冊video裝置
== -1) {
kfree(g_cam);
g_cam = NULL;
pr_err("ERROR: v4l2 capture: video_register_device failed\n");
return -1;
}
pr_debug(" Video device registered: %s #%d\n",
g_cam->video_dev->name, g_cam->video_dev->minor);
if (device_create_file(&g_cam->video_dev->dev,
&dev_attr_fsl_v4l2_capture_property))//建立fsl_v4l2_capture_property屬性檔案
dev_err(&pdev->dev, "Error on creating sysfs file"
" for capture\n");
if (device_create_file(&g_cam->video_dev->dev,
&dev_attr_fsl_v4l2_overlay_property))//建立fsl_v4l2_overlay_property屬性檔案
dev_err(&pdev->dev, "Error on creating sysfs file"
" for overlay\n");
return 0;
}
我們首先看init_camera_struct(g_cam, pdev):
static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
{
pr_debug("In MVC: init_camera_struct\n");
memset(cam, 0, sizeof(cam_data));
init_MUTEX(&cam->param_lock);
init_MUTEX(&cam->busy_lock);
cam->video_dev = video_device_alloc();
if (cam->video_dev == NULL)
return;
*(cam->video_dev) = mxc_v4l_template;//注意這裡的指派,包含了ops操作
video_set_drvdata(cam->video_dev, cam);//設定私有資料到video_dev的dev
dev_set_drvdata(&pdev->dev, (void *)cam);
cam->video_dev->minor = -1;
init_waitqueue_head(&cam->enc_queue);
init_waitqueue_head(&cam->still_queue);
cam->crop_bounds.left = 0;
cam->crop_bounds.width = 640;
cam->crop_bounds.top = 0;
cam->crop_bounds.height = 480;
cam->crop_current = cam->crop_defrect = cam->crop_bounds;
ipu_csi_set_window_size(cam->crop_current.width,
cam->crop_current.height, cam->csi);
ipu_csi_set_window_pos(cam->crop_current.left,
cam->crop_current.top, cam->csi);
cam->streamparm.parm.capture.capturemode = 0;
cam->standard.index = 0;
cam->standard.id = V4L2_STD_UNKNOWN;
cam->standard.frameperiod.denominator = 30;
cam->standard.frameperiod.numerator = 1;
cam->standard.framelines = 480;
cam->standard_autodetect = true;
cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
cam->overlay_on = false;
cam->capture_on = false;
cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
cam->v2f.fmt.pix.width = 288;
cam->v2f.fmt.pix.height = 352;
cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
cam->win.w.width = 160;
cam->win.w.height = 160;
cam->win.w.left = 0;
cam->win.w.top = 0;
cam->csi = 0;
cam->enc_callback = camera_callback;//設定callback
init_waitqueue_head(&cam->power_queue);
spin_lock_init(&cam->queue_int_lock);
spin_lock_init(&cam->dqueue_int_lock);
}
這個函數主要是對cam_data結構的初始化。這裡需要注意的是*(cam->video_dev) = mxc_v4l_template,我們看下mxc_v4l_template定義:
static struct v4l2_file_operations mxc_v4l_fops = {
.owner = THIS_MODULE,
.open = mxc_v4l_open,
.release = mxc_v4l_close,
.read = mxc_v4l_read,
.ioctl = mxc_v4l_ioctl,
.mmap = mxc_mmap,
.poll = mxc_poll,
};
static struct video_device mxc_v4l_template = {
.name = "Mxc Camera",
.fops = &mxc_v4l_fops,
.release = video_device_release,
};
這個定義很重要,v4l2子系統很多調用将通過這個mxc_v4l_template的ops實作。
下面看下probe函數中的v4l2_int_device_register(&mxc_v4l2_int_device),這個之前在slave注冊已經分析過了。從前面的分析知道,無論master還是slave注冊時都會互相去比對,
找到後會調用master的比對函數m->u.master->attach(s)。我們看下mxc_v4l2_int_device的定義:
static struct v4l2_int_master mxc_v4l2_master = {
.attach = mxc_v4l2_master_attach,
.detach = mxc_v4l2_master_detach,
};
static struct v4l2_int_device mxc_v4l2_int_device = {
.module = THIS_MODULE,
.name = "mxc_v4l2_cap",
.type = v4l2_int_type_master,
.u = {
.master = &mxc_v4l2_master,
},
};
從定義中可以看出,attach對應mxc_v4l2_master_attach:
static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
{
cam_data *cam = slave->u.slave->master->priv;//g_cam,即probe中的mxc_v4l2_int_device.priv = g_cam;
struct v4l2_format cam_fmt;
pr_debug("In MVC: mxc_v4l2_master_attach\n");
pr_debug(" slave.name = %s\n", slave->name);
pr_debug(" master.name = %s\n", slave->u.slave->master->name);
cam->sensor = slave;//找到的slave(ov5642)
if (slave == NULL) {
pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
return -1;
}
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
vidioc_int_s_power(cam->sensor, 1);//從上面的slave的ov5642_ioctl_desc定義分析,它将會調用vidioc_int_s_power_num對應的函數
vidioc_int_dev_init(slave);//調用vidioc_int_dev_init_num對應的函數,初始化
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);//調用vidioc_int_g_fmt_cap_num對應的函數,得到fmt,并以此為fmt标準
cam->device_type = cam_fmt.fmt.pix.priv;
cam->crop_bounds.top = cam->crop_bounds.left = 0;
cam->crop_bounds.width = cam_fmt.fmt.pix.width;
cam->crop_bounds.height = cam_fmt.fmt.pix.height;
cam->crop_defrect.top = cam->crop_defrect.left = 0;
cam->crop_defrect.width = cam_fmt.fmt.pix.width;
cam->crop_defrect.height = cam_fmt.fmt.pix.height;
cam->crop_current.top = cam->crop_current.left = 0;
cam->crop_current.width = cam_fmt.fmt.pix.width;
cam->crop_current.height = cam_fmt.fmt.pix.height;
pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
__func__,
cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
__func__,
cam->crop_bounds.width, cam->crop_bounds.height);
pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
__func__,
cam->crop_defrect.width, cam->crop_defrect.height);
pr_debug("End of %s: crop_current widthxheight %d x %d\n",
__func__,
cam->crop_current.width, cam->crop_current.height);
return 0;
}
我們再看下probe中的video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr):
int video_register_device(struct video_device *vdev, int type, int nr)
{
return video_register_device_index(vdev, type, nr, -1);
}
轉到video_register_device_index(vdev, type, nr, -1):
int video_register_device_index(struct video_device *vdev, int type, int nr,
int index)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
void *priv = video_get_drvdata(vdev);
vdev->minor = -1;
WARN_ON(!vdev->release);
if (!vdev->release)
return -EINVAL;
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VTX:
name_base = "vtx";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VTX:
minor_offset = 192;
minor_cnt = 32;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
mutex_lock(&videodev_lock);
nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
if (nr == minor_cnt)
nr = find_first_zero_bit(video_nums[type], minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free kernel number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
i = nr;
#else
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
set_bit(nr, video_nums[type]);
WARN_ON(video_device[vdev->minor] != NULL);
ret = vdev->index = get_index(vdev, index);
mutex_unlock(&videodev_lock);
if (ret < 0) {
printk(KERN_ERR "%s: get_index failed\n", __func__);
goto cleanup;
}
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
if (vdev->fops->unlocked_ioctl)
vdev->cdev->ops = &v4l2_unlocked_fops;
else
vdev->cdev->ops = &v4l2_fops;//注意這裡的ops,使用者打開裝置後的file ops
vdev->cdev->owner = vdev->fops->owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
memset(&vdev->dev, 0, sizeof(vdev->dev));
video_set_drvdata(vdev, priv);
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, nr);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
vdev->dev.release = v4l2_device_release;
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
clear_bit(vdev->num, video_nums[type]);
mutex_unlock(&videodev_lock);
vdev->minor = -1;
return ret;
}
如此,v4l2的slave,master注冊算是基本結束了。下面我們開始分析對video_dev的讀寫ioctl等操作。
我們從video_register_device_index中知道,注冊字元裝置時,注冊了該字元裝置的ops:
vdev->cdev->ops = &v4l2_fops,我們下面看下v4l2_fops定義:
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
我們這裡隻分析一下read,open,ioctl,mmap。先從open開始:
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
int ret = 0;
mutex_lock(&videodev_lock);
vdev = video_devdata(filp);
if (vdev == NULL || video_is_unregistered(vdev)) {
mutex_unlock(&videodev_lock);
return -ENODEV;
}
video_get(vdev);
mutex_unlock(&videodev_lock);
if (vdev->fops->open)//video_device的ops操作,要回想到上面的mxc_v4l_template
ret = vdev->fops->open(filp);
if (ret)
video_put(vdev);
return ret;
}
我們這邊再貼一下mxc_v4l_template:
static struct v4l2_file_operations mxc_v4l_fops = {
.owner = THISMODULE,
.open = mxc_v4l_open,
.release = mxc_v4l_close,
.read = mxc_v4l_read,
.ioctl = mxc_v4l_ioctl,
.mmap = mxc_mmap,
.poll = mxc_poll,
};
static struct video_device mxc_v4l_template = {
.name = "Mxc Camera",
.fops = &mxc_v4l_fops,
.release = video_device_release,
};
看到這個定義,一切真相大白!vdev->fops->open(filp)也就是執行mxc_v4l_open(filp):
static int mxc_v4l_open(struct file *file)
{
......
//對csi接口的初始化等一些列動作
......
}
我們再看v4l2_read:
static ssize_t v4l2_read(struct file *filp, char __user *buf,
size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->read)
return -EINVAL;
if (video_is_unregistered(vdev))
return -EIO;
return vdev->fops->read(filp, buf, sz, off);
}
vdev->fops->read(filp, buf, sz, off)轉到執行mxc_v4l_read(filp, buf, sz, off):
static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count,
loff_t *ppos)
{
......
v_address[0] = dma_alloc_coherent(0,//申請dma
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
&cam->still_buf[0],
GFP_DMA | GFP_KERNEL);
v_address[1] = dma_alloc_coherent(0,
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
&cam->still_buf[1],
GFP_DMA | GFP_KERNEL);
......
cam->still_counter = 0;
err = cam->csi_start(cam);//開始camera
if (err != 0) {
err = -EIO;
goto exit1;
}
......
err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage);//拷貝到使用者空間
......
}
再看v4l2_mmap:
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->mmap ||
video_is_unregistered(vdev))
return -ENODEV;
return vdev->fops->mmap(filp, vm);
}
vdev->fops->mmap(filp, vm)轉到執行mxc_mmap(filp, vm):
static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *dev = video_devdata(file);
unsigned long size;
int res = 0;
cam_data *cam = video_get_drvdata(dev);
pr_debug("In MVC:mxc_mmap\n");
pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
vma->vm_pgoff, vma->vm_start, vma->vm_end);
if (down_interruptible(&cam->busy_lock))
return -EINTR;
size = vma->vm_end - vma->vm_start;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);//定義記憶體标記屬性
if (remap_pfn_range(vma, vma->vm_start,//将核心空間映射到使用者空間
vma->vm_pgoff, size, vma->vm_page_prot)) {
pr_err("ERROR: v4l2 capture: mxc_mmap: "
"remap_pfn_range failed\n");
res = -ENOBUFS;
goto mxc_mmap_exit;
}
vma->vm_flags &= ~VM_IO;
mxc_mmap_exit:
up(&cam->busy_lock);
return res;
}
再看v4l2_ioctl:
static int v4l2_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
if (!vdev->fops->ioctl)
return -ENOTTY;
return vdev->fops->ioctl(filp, cmd, arg);
}
vdev->fops->ioctl(filp, cmd, arg)轉到執行mxc_v4l_ioctl(filp, cmd, arg):
static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
pr_debug("In MVC:mxc_v4l_ioctl\n");
return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
}
video_usercopy這個函數将會根據cmd做一下判斷,主要工作由mxc_v4l_do_ioctl完成。這個函數是最重要最核心的一個函數。它要實作v4l2所有常用指令的功能。我們下面不具體分析mxc_v4l_do_ioctl,隻列出它的大體結構:
static long mxc_v4l_do_ioctl(struct file *file,
unsigned int ioctlnr, void *arg)
{
struct video_device *dev = video_devdata(file);
cam_data *cam = video_get_drvdata(dev);
int retval = 0;
unsigned long lock_flags;
pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr);
wait_event_interruptible(cam->power_queue, cam->low_power == false);
if (down_interruptible(&cam->busy_lock))
return -EBUSY;
switch (ioctlnr) {
case VIDIOC_QUERYCAP: {
.....
break;
}
case VIDIOC_G_FMT: {
.....
break;
}
case VIDIOC_S_FMT: {
.....
break;
}
case VIDIOC_REQBUFS: {
.....
break;
}
case VIDIOC_QUERYBUF: {
.....
break;
}
case VIDIOC_QBUF: {
.....
break;
}
case VIDIOC_DQBUF: {
.....
break;
}
case VIDIOC_STREAMON: {
.....
break;
}
case VIDIOC_STREAMOFF: {
.....
break;
}
case VIDIOC_G_CTRL: {
.....
break;
}
case VIDIOC_S_CTRL: {
.....
break;
}
case VIDIOC_CROPCAP: {
.....
break;
}
case VIDIOC_G_CROP: {
.....
break;
}
case VIDIOC_S_CROP: {
.....
break;
}
case VIDIOC_OVERLAY: {
.....
break;
}
case VIDIOC_G_FBUF: {
.....
break;
}
case VIDIOC_S_FBUF: {
.....
break;
}
case VIDIOC_G_PARM: {
.....
break;
}
case VIDIOC_S_PARM: {
.....
break;
}
case VIDIOC_ENUMSTD: {
.....
break;
}
case VIDIOC_G_STD: {
.....
break;
}
case VIDIOC_S_STD: {
.....
break;
}
case VIDIOC_ENUMOUTPUT: {
.....
break;
}
case VIDIOC_G_OUTPUT: {
.....
break;
}
case VIDIOC_S_OUTPUT: {
.....
break;
}
case VIDIOC_ENUMINPUT: {
.....
break;
}
case VIDIOC_G_INPUT: {
.....ut;
break;
}
case VIDIOC_S_INPUT: {
.....
break;
}
case VIDIOC_ENUM_FMT: {
.....
break;
}
case VIDIOC_ENUM_FRAMESIZES: {
.....
break;
}
case VIDIOC_DBG_G_CHIP_IDENT: {
.....
break;
}
case VIDIOC_TRY_FMT:
case VIDIOC_QUERYCTRL:
case VIDIOC_G_TUNER:
case VIDIOC_S_TUNER:
case VIDIOC_G_FREQUENCY:
case VIDIOC_S_FREQUENCY:
default:
pr_debug(" case default or not supported\n");
retval = -EINVAL;
break;
}
up(&cam->busy_lock);
return retval;
}
上面這些指令非常重要,是v4l2的标準指令實作,這樣基于v4l2實作的camera可以用統一的指令進行使用者空間操作,對程式的可重用性可移植帶來極大的友善。
這樣整個v4l2子系統分析就差不多了,整體來說v4l2基本架構還是比較清晰的:使用者空間-->v4l2-->master。要想對v4l2有更深入的了解掌握,需要實際利用v4l2的标準ioctl指令進行應用程式設計。