v4l2_common.c浅析

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



 

你可能感兴趣的:(v4l2)