S5PC100平台上Linux Camera驱动开发详解(6)

也就说必须有个地方专门去实现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,因此驱动里面可以把平台数据保存起来以便使用,当然我们这里只是保存起来并没有使用,可以留作以后的功能扩展。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/7ef92923623d14e8d9f2bdca4c92469b.html