V4l2的子设备一般是摄像头和摄像头控制器,它们和主机的控制操作是通过i2c总线完成的。V4l2驱动框架中跟i2c相关的代码在deriver/media/video/v4l2_common.c中,下边就相关函数作一简单分析:
下边宏的作用是如果v4l2的子设备通过i2c总线和主机通信时,才能用到下边的函数,因为有可能通过其他总线通信,比如SPI等。
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
这个函数的功能是判断i2c client和v4l2是否匹配,主要通过匹配类型来分类判断,如果过匹配类型是V4L2_CHIP_MATCH_I2C_DRIVER,那么就比较i2c client deriver->name和match-〉name是否相同;如果匹配类型是V4L2_CHIP_MATCH_I2C_ADDR,那么判断它们的地址是否相同。相同则返回1,否则返回0。
1、v4l2_chip_match_i2c_client
- int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
- {
- int len;
- if (c == NULL || match == NULL)
- return 0;
- //通过switch case来判断匹配类型
- switch (match->type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- if (c->driver == NULL || c->driver->driver.name == NULL)
- return 0;
- len = strlen(c->driver->driver.name);
- /* legacy drivers have a ' suffix, don't try to match that */
- if (len && c->driver->driver.name[len - 1] == '\'')
- len--;
- //比较name
- return len && !strncmp(c->driver->driver.name, match->name, len);
- case V4L2_CHIP_MATCH_I2C_ADDR:
- //比较addr
- return c->addr == match->addr;
- default:
- return 0;
- }
- }
2、v4l2_chip_ident_i2c_client
//设置v4l2 chip的标示符
- int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
- u32 ident, u32 revision)
- {
- //如果v4l2和i2c不匹配,则退出
- if (!v4l2_chip_match_i2c_client(c, &chip->match))
- return 0;
- if (chip->ident == V4L2_IDENT_NONE) {
- chip->ident = ident;
- chip->revision = revision;
- }
- else {
- chip->ident = V4L2_IDENT_AMBIGUOUS;
- chip->revision = 0;
- }
- return 0;
- }
- EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
以上两个函数时debug模式时用的,下边是i2c的辅助函数:
/* ----------------------------------------------------------------- */
3、v4l2_i2c_subdev_init
/* I2C Helper functions */
- 该函数初始化v4l2_subdev数据结构,部分数据是从i2c_client结构中得到。建立i2c_client和v4l2_subdev之间的指向关系。
- void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
- const struct v4l2_subdev_ops *ops)
- {
- v4l2_subdev_init(sd, ops);//简单的初始化v4l2_subdev结构体(v4l2_subdev.c)
- sd->flags |= V4L2_SUBDEV_FL_IS_I2C;//设置v4l2 subdev的控制状态
- /* the owner is the same as the i2c_client's driver owner */
- sd->owner = client->driver->driver.owner;
- /* i2c_client and v4l2_subdev point to one another */
- v4l2_set_subdevdata(sd, client);
- i2c_set_clientdata(client, sd);
- /* initialize name */
- snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
- client->driver->driver.name, i2c_adapter_id(client->adapter),
- client->addr);
- }
4、v4l2_i2c_new_subdev_board
- /* Load an i2c sub-device. */
- V4l2中增加i2c设备,返回创建新的v4l2 子设备
- struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter, const char *module_name,
- struct i2c_board_info *info, const unsigned short *probe_addrs)
- {
- struct v4l2_subdev *sd = NULL;
- struct i2c_client *client;
- BUG_ON(!v4l2_dev);
- if (module_name)
- request_module(module_name);//加载i2c驱动模块
- /* Create the i2c client */
- 创建i2c设备时从设备地址是明确的还是要通过探测。
- 在这两个调用中会向系统中增加sysfs,会把驱动和设备关联起来,并会调用i2c驱动中的probe接口。
- if (info->addr == 0 && probe_addrs)
- client = i2c_new_probed_device(adapter, info, probe_addrs);
- else
- client = i2c_new_device(adapter, info);
- /* Note: by loading the module first we are certain that c->driver will be set if the driver was found. If the module was not loaded first, then the i2c core tries to delay-load the module for us, and then c->driver is still NULL until the module is finally loaded. This delay-load mechanism doesn't work if other drivers want to use the i2c device, so explicitly loading the module is the best alternative. */
- if (client == NULL || client->driver == NULL)
- goto error;
- /* Lock the module so we can safely get the v4l2_subdev pointer */
- if (!try_module_get(client->driver->driver.owner))
- goto error;
- sd = i2c_get_clientdata(client);//获取v4l2子设备
- /* Register with the v4l2_device which increases the module's use count as well. */
- //注册v4l2子设备(driver/media/video/v4l2_device.c)
- if (v4l2_device_register_subdev(v4l2_dev, sd))
- sd = NULL;
- /* Decrease the module use count to match the first try_module_get. */
- module_put(client->driver->driver.owner);
- 3.3.1版本中没有下边的if语句
- if (sd) {
- /* We return errors from v4l2_subdev_call only if we have the
- callback as the .s_config is not mandatory */
- int err = v4l2_subdev_call(sd, core, s_config, info->irq, info->platform_data);
- if (err && err != -ENOIOCTLCMD) {
- v4l2_device_unregister_subdev(sd);
- sd = NULL;
- }
- }
- error:
- /* If we have a client but no subdev, then something went wrong and
- we must unregister the client. */
- if (client && sd == NULL)
- i2c_unregister_device(client);
- return sd;
- }
- EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
加载i2c 模块到系统,并返回初始化好的v4l2_subdev结构体,在加载模块时,当module->name不为空时,才掉用request_module() 创建i2c 模块。Client_type参数是适配器的名称。
5、v4l2_i2c_new_subdev_cfg
- struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter,
- const char *module_name, const char *client_type,
- int irq, void *platform_data,
- u8 addr, const unsigned short *probe_addrs)
- {
- struct i2c_board_info info;
- /* Setup the i2c board info with the device type and the device address. */
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, client_type, sizeof(info.type));
- info.addr = addr;
- info.irq = irq;
- info.platform_data = platform_data;
- //调用创建和注册v4l2_subdev、i2c_client的函数
- return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, module_name,
- &info, probe_addrs);
- }
在3.3.1版本中,上边的函数用下边的函数替代:
- struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter, const char *client_type,
- u8 addr, const unsigned short *probe_addrs)
- {
- struct i2c_board_info info;
- /* Setup the i2c board info with the device type and the device address. */
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, client_type, sizeof(info.type));
- info.addr = addr;
- return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
- }
基本功能一样就是接口不一样,少了几个参数
6、v4l2_i2c_subdev_addr
- /* Return i2c client address of v4l2_subdev. */
- unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
- {
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- return client ? client->addr : I2C_CLIENT_END;
- }
- EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);