3.2 vidioc_int_* 类函数的调用过程

在《2.masterslave的匹配过程》中我们分析了master设备和slave设备的匹配过程,但是在操作过程中,如果想要获取slave设备的一些信息,该如何获得呢?你也许会说,只需要定义一个函数,去slave设备文件中获取即可,但是,如果一个驱动会支持好几个slave设备,那么对于不同的slave设备,驱动中都需要为它们定义一个功能相似的函数。如果是多master对应多slave设备的情况下,那么这些函数就需要在多个文件中定义多次,这种现象在内核中是肯定不会允许的。对于相似功能的函数,在驱动中只存在一个,而这个函数会根据不同的slave设备在不同的slave设备文件中转换成各自的子函数。所以,这里使用了Linux设备驱动框架设计中的分割的思想,提炼出一种通用的函数接口,在不同的slave设备中会转换成不同的底层调用函数,这些就是vidioc_int_*类函数调用。


首先以ov5640.c为例,回忆slave设备的注册过程:

[cpp]  view plain  copy
  1. module_i2c_driver(ov5640_i2c_driver);   
  2. static struct i2c_driver ov5640_i2c_driver = {   
  3.     .driver = {   
  4.           .owner = THIS_MODULE,   
  5.           .name  = "ov564x",   
  6.           },   
  7.     .probe  = ov5640_probe,   
  8.     .remove = ov5640_remove,   
  9.     .id_table = ov5640_id,   
  10. };   

调用ov5640_probe函数,在ov5640_probe函数中,首先会为structsensor_data ov5640_data设置初始值,然后通ov5640_int_device.priv= &ov5640_data;

retval= v4l2_int_device_register(&ov5640_int_device);

v4l2_int_device_register函数中,会在里面通过list_add将这个设备添加到int_list链表中,由于无论master还是slave设备都会调用到这个函数,所以这个int_list链表中会存在masterslave设备,然后根据不同的type类型来区分它们。之后在v4l2_int_device_register函数继续调用v4l2_int_device_try_attach_all()函数,会从int_list链表找到master设备和第一个没有设置masterslave设备,然后将这个slave设备的master设置成找到的master,并且调用masterattach函数完成匹配过程。

v4l2_int_device_register函数注册的是ov5640_int_device结构体,这个结构体如下所示:

[cpp]  view plain  copy
  1. static struct v4l2_int_device ov5640_int_device = {   
  2.     .module = THIS_MODULE,   
  3.     .name = "ov564x",   
  4.     .type = v4l2_int_type_slave,   
  5.     .u = {   
  6.         .slave = &ov5640_slave,   
  7.     },   
  8. };  

这个结构体里面u.slave变量是v4l2_int_slave类型的,如下所示:

[cpp]  view plain  copy
  1. static struct v4l2_int_slave ov5640_slave = {   
  2.     .ioctls = ov5640_ioctl_desc,   
  3.     .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc),   
  4. };   

同样继续追踪ov5640_ioctl_desc结构体

[html]  view plain  copy
  1. static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = {   
  2.     { vidioc_int_dev_init_num,   
  3.       (v4l2_int_ioctl_func *)ioctl_dev_init },   
  4.     { vidioc_int_dev_exit_num,   
  5.       ioctl_dev_exit},   
  6.     { vidioc_int_s_power_num,   
  7.       (v4l2_int_ioctl_func *)ioctl_s_power },   
  8.     { vidioc_int_g_ifparm_num,   
  9.       (v4l2_int_ioctl_func *)ioctl_g_ifparm },   
  10.     { vidioc_int_init_num,   
  11.       (v4l2_int_ioctl_func *)ioctl_init },   
  12.     { vidioc_int_enum_fmt_cap_num,   
  13.       (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },   
  14.     { vidioc_int_g_fmt_cap_num,   
  15.       (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },   
  16.     { vidioc_int_g_parm_num,   
  17.       (v4l2_int_ioctl_func *)ioctl_g_parm },   
  18.     { vidioc_int_s_parm_num,   
  19.       (v4l2_int_ioctl_func *)ioctl_s_parm },   
  20.     { vidioc_int_g_ctrl_num,   
  21.       (v4l2_int_ioctl_func *)ioctl_g_ctrl },   
  22.     { vidioc_int_s_ctrl_num,   
  23.       (v4l2_int_ioctl_func *)ioctl_s_ctrl },   
  24.     { vidioc_int_enum_framesizes_num,   
  25.       (v4l2_int_ioctl_func *)ioctl_enum_framesizes },   
  26.     { vidioc_int_enum_frameintervals_num,   
  27.       (v4l2_int_ioctl_func *)ioctl_enum_frameintervals },   
  28.     { vidioc_int_g_chip_ident_num,   
  29.       (v4l2_int_ioctl_func *)ioctl_g_chip_ident },   
  30. };   


这些ioctl函数是如何调用的?下面来分析这一个过程v4l2-int-device.h中有这样的定义:

[cpp]  view plain  copy
  1. enum v4l2_int_ioctl_num {   
  2.     /*  
  3.      *  
  4.      * "Proper" V4L ioctls, as in struct video_device.  
  5.      *  
  6.      */   
  7.     vidioc_int_enum_fmt_cap_num = 1,   
  8.     vidioc_int_g_fmt_cap_num,   
  9.     vidioc_int_s_fmt_cap_num,   
  10.     vidioc_int_try_fmt_cap_num,   
  11.     vidioc_int_queryctrl_num,   
  12.     vidioc_int_g_ctrl_num,   
  13.     vidioc_int_s_ctrl_num,   
  14.     vidioc_int_cropcap_num,   
  15.     vidioc_int_g_crop_num,   
  16.     vidioc_int_s_crop_num,   
  17.     vidioc_int_g_parm_num,   
  18.     vidioc_int_s_parm_num,   
  19.     vidioc_int_querystd_num,   
  20.     vidioc_int_s_std_num,   
  21.     vidioc_int_s_video_routing_num,   
  22.   
  23.     ..............  
  24.     /*  
  25.     * Get slave private data, e.g. platform-specific slave  
  26.     * configuration used by the master.  
  27.     */   
  28.     vidioc_int_g_priv_num,   
  29.     /* Get slave interface parameters. */   
  30.     vidioc_int_g_ifparm_num,   
  31.     /* Does the slave need to be reset after VIDIOC_DQBUF? */   
  32.     vidioc_int_g_needs_reset_num,   
  33.     vidioc_int_enum_framesizes_num,   
  34.     vidioc_int_enum_frameintervals_num,   
  35.      .................  
  36.     vidioc_int_priv_start_num = 2000,   
  37. };  
[cpp]  view plain  copy
  1. #define V4L2_INT_WRAPPER_1(name, arg_type, asterisk)            \   
  2.     static inline int vidioc_int_##name(struct v4l2_int_device *d,  \   
  3.                         arg_type asterisk arg)  \   
  4.     {                               \   
  5.         return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \   
  6.                     (void *)(unsigned long)arg);    \   
  7.     }                               \   
  8.                                     \   
  9.     static inline struct v4l2_int_ioctl_desc            \   
  10.     vidioc_int_##name##_cb(int (*func)              \   
  11.                    (struct v4l2_int_device *,       \   
  12.                 arg_type asterisk))         \   
  13.     {                               \   
  14.         struct v4l2_int_ioctl_desc desc;            \   
  15.                                     \   
  16.         desc.num = vidioc_int_##name##_num;         \   
  17.         desc.func = (v4l2_int_ioctl_func *)func;        \   
  18.                                     \   
  19.         return desc;                        \   
  20.     }   

[cpp]  view plain  copy
  1. V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *);   
  2. V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *);   
  3. V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *);   
  4. V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *);   
  5. V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *);   
  6. V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *);   
  7. V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *);   
  8. V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *);   
  9. V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);   
  10. V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);   
  11. V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);   
  12. V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);   
  13. V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);   
  14. V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);   
  15. V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);   
  16.   
  17. V4L2_INT_WRAPPER_0(dev_init);   
  18. V4L2_INT_WRAPPER_0(dev_exit);   
  19. V4L2_INT_WRAPPER_1(s_power, enum v4l2_power, /*dummy arg*/);   
  20. V4L2_INT_WRAPPER_1(g_priv, void, *);   
  21. V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *);   
  22. V4L2_INT_WRAPPER_1(g_needs_reset, void, *);   
  23. V4L2_INT_WRAPPER_1(enum_framesizes, struct v4l2_frmsizeenum, *);   
  24. V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *);   
  25.   
  26. V4L2_INT_WRAPPER_0(reset);   
  27. V4L2_INT_WRAPPER_0(init);   
  28. V4L2_INT_WRAPPER_1(g_chip_ident, int, *);  

上面这个宏定义中的##是连字符,相当于直接将##后面的字符连到##号之前的字符后面。这样做的目的是什么?

这种用法一般用在宏定义中,比如定义一个宏:

#defineAAAAA(name, type, num) xxxxx_##name(type, num)

如果在代码中使用到这个宏,编译器就会根据宏中不同的name字段来自动生成几个不同的函数。

#defineAAAAA(aaa, int, 1)

#defineAAAAA(bbb, int, 2)

#defineAAAAA(ccc, int, 3)

在编译的时候,就会生成:

xxxxx_aaa(int,1)

xxxxx_bbb(int,2)

xxxxx_ccc(int,3)


在后面用到的mxc_v4l2_capture.c中的open函数中,调用了vidioc_int_g_ifparm这样一个函数,我在内核源码中搜索都没有找到这个函数的定义,但是与vidioc_int...相关的头文件只有这个v4l2-int-device.h,所以仔细看这个头文件中,它采用一种gcc宏扩展的方式定义了一个宏V4L2_INT_WRAPPER_1,如上所示,就以这个vidioc_int_g_ifparm为例来说明:

[cpp]  view plain  copy
  1. V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *);  

通过上面这个宏就相当于声明创建了两个内联函数:

[cpp]  view plain  copy
  1. static inline int vidioc_int_g_ifparm(struct v4l2_int_device *d,    \   
  2.                         arg_type asterisk arg)  \   
  3.     {                               \   
  4.         return v4l2_int_ioctl_1(d, vidioc_int_g_ifparm_num, \   
  5.                     (void *)(unsigned long)arg);    \   
  6.     }                               \   
  7.                                     \   
  8.     static inline struct v4l2_int_ioctl_desc            \   
  9.     vidioc_int_g_ifparm_cb(int (*func)              \   
  10.                    (struct v4l2_int_device *,       \   
  11.                 arg_type asterisk))         \   
  12.     {                               \   
  13.         struct v4l2_int_ioctl_desc desc;            \   
  14.                                     \   
  15.         desc.num = vidioc_int_g_ifparm_num;         \   
  16.         desc.func = (v4l2_int_ioctl_func *)func;        \   
  17.                                     \   
  18.         return desc;                        \   
  19.     }  

这样的话,当mxc_v4l2_capture.c中的open函数中调用vidioc_int_g_ifparm的话,就会调用到v4l2_int_ioctl_1函数,这个函数如下所示:

[cpp]  view plain  copy
  1. int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)   
  2. {   
  3.     return ((v4l2_int_ioctl_func_1 *)   
  4.         find_ioctl(d->u.slave, cmd,   
  5.                (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);   
  6. }   

然后就转到find_ioctl函数里面,

[cpp]  view plain  copy
  1. static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,   
  2.                        v4l2_int_ioctl_func *no_such_ioctl)   
  3. {   
  4.     const struct v4l2_int_ioctl_desc *first = slave->ioctls;   
  5.     const struct v4l2_int_ioctl_desc *last =   
  6.         first + slave->num_ioctls - 1;   
  7.   
  8.     while (first <= last) {   
  9.         const struct v4l2_int_ioctl_desc *mid;   
  10.   
  11.         mid = (last - first) / 2 + first;   
  12.   
  13.         if (mid->num < cmd)   
  14.             first = mid + 1;   
  15.         else if (mid->num > cmd)   
  16.             last = mid - 1;   
  17.         else   
  18.             return mid->func;   
  19. /* 找到就返回具体的函数,具体的说这里的函数就是ov5640 slave定义的 ov5640_ioctl_desc 中的ioctl_g_ifparm 函数! */  
  20.     }   
  21.   
  22.     return no_such_ioctl;   
  23. }  

这个find_ioctl函数通过一个二分查找,根据vidioc_int_g_ifparm_num来找到具体的函数即ioctl_g_ifparm函数。

也就是说如果其他函数中有调用vidioc_int_g_ifparm的话,最终就会调用到ov5640.c中的

ioctl_g_ifparm函数。


同理,对于其他vidioc_int*类函数调用,最终都会根据不同的slave设备来对应找到vidioc_int_*_num函数,然后根据v4l2_int_ioctl_desc中的定义来找到对应的函数。


其他相似的函数比如:

vidioc_int_enum_fmt

vidioc_int_g_fmt

vidioc_int_g_ctrl

可以自己分析分析这几个函数的调用过程,就会对这种方式比较理解。

猜你喜欢

转载自blog.csdn.net/dragon101788/article/details/80660833
3.2