也就说必须有个地方专门去实现s_config函数,这就是具体摄像头的驱动需要作的工作。这个驱动需要我们自己去实现。我把它放在了driver/media/video/ov9650/这个目录下。里面有两个.c源码,ov9650.c和sccb.c。sccb.c是对sccb总线协议的软件模拟,其实就是用gpio来模拟i2c总线,因为sccb相当于一种弱化的i2c总线。Ov9650.c则是真正的摄像头驱动。这个驱动的主要任务就是初始化摄像头里的寄存器(通过i2c总线),当然它还可以实现其它附加功能,比如改变摄像头的分辨率,调节亮度、对比度等等,但是这里都没有实现。里面我们自己定义了一个结构体:struct ov9650_state
65 struct ov9650_state {
66 struct ov9650_platform_data *pdata;
67 struct v4l2_subdev sd;
68 struct v4l2_pix_format pix;
69 struct v4l2_fract timeperframe;
70 struct ov9650_userset userset;
71 int freq; /* MCLK in KHz */
72 int is_mipi;
73 int isize;
74 int ver;
75 int fps;
76 };
这里面唯一需要关心的成员就是第二项struct v4l2_subdev sd,内核中用struct v4l2_subdev来抽象一个v4l2子设备,这个成员一定是用来注册v4l2-subdev的。那么什么时候注册成了我们需要关心的问题。注意到这里还有一个重要的结构体:
621 static struct v4l2_i2c_driver_data v4l2_i2c_data = {
622 .name = OV9650_DRIVER_NAME,
623 .probe = ov9650_probe,
624 .remove = ov9650_remove,
625 .id_table = ov9650_id,
626 };
这里面有probe函数,照理说应该注册一个driver才对,但是整个驱动都没有类似register_xxx_driver之类的函数,但是如果我们看看这个结构体是如何定义的,就知道问题的答案了。struct v4l2_i2c_driver_data定义在include/media/ v4l2-i2c-drv.h:
38 #ifndef __V4L2_I2C_DRV_H__
39 #define __V4L2_I2C_DRV_H__
40
41 #include <media/v4l2-common.h>
42
43 struct v4l2_i2c_driver_data {
44 const char * const name;
45 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
46 int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
47 int (*remove)(struct i2c_client *client);
48 int (*suspend)(struct i2c_client *client, pm_message_t state);
49 int (*resume)(struct i2c_client *client);
50 const struct i2c_device_id *id_table;
51 };
52
53 static struct v4l2_i2c_driver_data v4l2_i2c_data;
54 static struct i2c_driver v4l2_i2c_driver;
55
56
57 /* Bus-based I2C implementation for kernels >= 2.6.26 */
58
59 static int __init v4l2_i2c_drv_init(void)
60 {
61 v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;
62 v4l2_i2c_driver.command = v4l2_i2c_data.command;
63 v4l2_i2c_driver.probe = v4l2_i2c_data.probe;
64 v4l2_i2c_driver.remove = v4l2_i2c_data.remove;
65 v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;
66 v4l2_i2c_driver.resume = v4l2_i2c_data.resume;
67 v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;
68 return i2c_add_driver(&v4l2_i2c_driver);
69 }
72 static void __exit v4l2_i2c_drv_cleanup(void)
73 {
74 i2c_del_driver(&v4l2_i2c_driver);
75 }
76
77 module_init(v4l2_i2c_drv_init);
78 module_exit(v4l2_i2c_drv_cleanup);
79
80 #endif /* __V4L2_I2C_DRV_H__ */
其实,只要包含了这个头文件,就会向内核注册一个i2c-driver。而我们在摄像头驱动里实现的probe函数就会被赋给这个v4l2_i2c_drvier的probe成员。这个i2c-driver是和v4l2-subdev紧密关联的。针对v4l2-subdev,驱动里面注册了两套操作集合,分别是v4l2_subdev_video_ops和v4l2_subdev_core_ops。这两个结构体里面包含了很多回调函数,但是大多数回调函数我们在驱动都是以空函数的形式呈现的。只是实现了最关键的两个函数,分别是:
555 .init = ov9650_init, /* initializing API */
556 .s_config = ov9650_s_config, /* Fetch platform data */
从前面的分析可以知道s_config函数是在v4l2_i2c_new_subdev_board()被调用的时候注册的。v4l2_i2c_new_subdev_board是在drivers/media/video/v4l2-common.c里实现的,关键代码如下:
897 /* Load an i2c sub-device. */
898 struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
899 struct i2c_adapter *adapter, const char *module_name,
900 struct i2c_board_info *info, const unsigned short *probe_addrs)
901 {
902 struct v4l2_subdev *sd = NULL;
903 struct i2c_client *client;
904
905 BUG_ON(!v4l2_dev);
906
907 if (module_name)
908 request_module(module_name);
909
910 /* Create the i2c client */
911 if (info->addr == 0 && probe_addrs){
912 client = i2c_new_probed_device(adapter, info, probe_addrs);
913 printk("r1.====================client_addr : %d=======================================\n",client->addr);
914 }else{
915 client = i2c_new_device(adapter, info);
916 // printk("r2.====================client_addr : 0x%x, clent->driver->id :
0x%x========================================\n",client->addr,client->driver->id);
917 }
918
919 /* Note: by loading the module first we are certain that c->driver
920 will be set if the driver was found. If the module was not loaded
921 first, then the i2c core tries to delay-load the module for us,
922 and then c->driver is still NULL until the module is finally
923 loaded. This delay-load mechanism doesn't work if other drivers
924 want to use the i2c device, so explicitly loading the module
925 is the best alternative. */
926 if (client == NULL || client->driver == NULL)
927 goto error;
928
929
printk("1.============================================================\n");
930 /* Lock the module so we can safely get the v4l2_subdev pointer */
931 if (!try_module_get(client->driver->driver.owner))
932 goto error;
933 sd = i2c_get_clientdata(client);
934
printk("2.============================================================\n");
935
936 /* Register with the v4l2_device which increases the module's
937 use count as well. */
938 if (v4l2_device_register_subdev(v4l2_dev, sd))
939 sd = NULL;
940 printk("3.============================================================\n");
941 /* Decrease the module use count to match the first try_module_get. */
942 module_put(client->driver->driver.owner);
943
944 if (sd) {
945 /* We return errors from v4l2_subdev_call only if we have the
946 callback as the .s_config is not mandatory */
947 int err = v4l2_subdev_call(sd, core, s_config,
948 info->irq, info->platform_data);
949
950 if (err && err != -ENOIOCTLCMD) {
951 v4l2_device_unregister_subdev(sd);
952 d = NULL;
953 }
954 }
955
956 error:
957 /* If we have a client but no subdev, then something went wrong and
958 we must unregister the client. */
959 if (client && sd == NULL)
960 i2c_unregister_device(client);
961 return sd;
962 }
我们通过函数参数已经把i2c_adapter和i2c_board_info等信息传递过来,驱动里面正是利用这些信息向内核添加了一个i2c_device,即915行的client = i2c_new_device(adapter, info); 当i2c总线上新注册了一个i2c_device,总线就会为我们匹配相应的i2c_driver,如果匹配成功就会调用驱动的probe函数。这时刚才分析ov9650.c里面的ov9650_probe函数就会被调用,在这个probe里面其实就是初始化了v4l2-subdev:
v4l2_i2c_subdev_init(sd, client, &ov9650_ops);
这个函数把v4l2-subdev和i2c_client互相关联起来,就是把指针赋给彼此的void *private。因此在注册完i2c_device之后,sd = i2c_get_clientdata(client);这条语句就可以把保存在i2c_cilent里的指向v4l2-subdev的指针取出来。得到sd之后,就调用v4l2_device_register_subdev(v4l2_dev, sd)注册一个v4l2-subdev。如果注册成功,就会调用v4l2_subdev_call(sd, core, s_config, info->irq, info->platform_data);这个函数会调用sd对应的v4l2_subdev_core_ops操作集合里的s_config函数,因此这时ov9650.c里的ov9650_s_config函数就会被调用。这个s_config函数的第三个参数是platform_data,因此驱动里面可以把平台数据保存起来以便使用,当然我们这里只是保存起来并没有使用,可以留作以后的功能扩展。