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);