v4l2_device.c浅析


关于v4l2及v4l2_subdev设备实例的注册注消等操作在deriver/media/video/v4l2_device.c文件中,下边将各个函数分析一下(分析的时候以2.6.32.40内核为主,并列出与3.3.1的区别):

注册一个v4l2设备实例,主要建立dev和v4l2_dev的关系(下边黄低红字)。初始化v4l2_dev,使dev_driver_data指向v4l2_dev,dev在少数情况下(ISA devices)可能为NULL。在这种情况下必须在调用该函数之前给v4l2_dev->name赋值。
1. v4l2_device_register

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
 if (v4l2_dev == NULL)
  return -EINVAL;

 INIT_LIST_HEAD(&v4l2_dev->subdevs);//初始化子设备链表
 spin_lock_init(&v4l2_dev->lock);
 v4l2_dev->dev = dev;//将基本设备dev包含到v4l2_dev中
 if (dev == NULL) {
  /* If dev == NULL, then name must be filled in by the caller */
  WARN_ON(!v4l2_dev->name[0]);
  return 0;
 }

 /* Set name to driver name + device name if it is empty. */
 if (!v4l2_dev->name[0])
  snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
   dev->driver->name, dev_name(dev));
 if (dev_get_drvdata(dev))
  v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n");
 dev_set_drvdata(dev, v4l2_dev);//dev->p->deriver_data = v4l2_dev
 return 0;
}


设置v4l2设备的名称,用驱动的名称和驱动全局原子实例(deriver-global atomic)初始化结构体v4l2_device中的名称,这个函数将增加实例的引用计数和实例的名称。
2.v4l2_device_set_name

int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
atomic_t *instance)
{
 int num = atomic_inc_return(instance) - 1;//实例的序号
 int len = strlen(basename);//设备实例原始名称
//如果原始名称后边就有数字,那么在实例原始名称以实例序号中加入横线
 if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
  snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
    "%s-%d", basename, num);
//否则直接将它俩组合到一起
 else
  snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s%d", basename, num);
 return num;
}
EXPORT_SYMBOL_GPL(v4l2_device_set_name);


设置v4l2_dev->dev->p->driver_data和v4l2_dev->dev,在USB设备断开的时候调用,因此父设备消失时这个就能确保v4l2_dev就不会有一个无效的父设备指针。
3.v4l2_device_disconnect

void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
{
 if (v4l2_dev->dev) {
  dev_set_drvdata(v4l2_dev->dev, NULL);
  v4l2_dev->dev = NULL;
 }
}
EXPORT_SYMBOL_GPL(v4l2_device_disconnect);


注销掉v4l2_dev的所有子设备和v4l2_dev其他相关的资源,比如i2c。
4 . v4l2_device_unregister

void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
{
 struct v4l2_subdev *sd, *next;

 if (v4l2_dev == NULL)
  return;
 v4l2_device_disconnect(v4l2_dev);//断开dev->p->drvdata和v4l2_dev

 /* Unregister subdevs  遍历注消子设备*/
 list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
  v4l2_device_unregister_subdev(sd);
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
  if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) {//如果用到了i2c接口,则也注销掉它
   struct i2c_client *client = v4l2_get_subdevdata(sd);
/* We need to unregister the i2c client explicitly.We cannot rely on i2c_del_adapter to always unregister clients for us, since if the i2c bus is a platform bus, then it is never deleted. */
   if (client)
    i2c_unregister_device(client);
  }
#endif
 }
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);


5、v4l2_device_register_subdev
为v4l2 device注册一个子设备,主要是建立起来v4l2_device和v4l2_subdev两个设备之前的关系,以便以后处理调用。

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd)
{
 /* Check for valid input */
 if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
  return -EINVAL;
 /* Warn if we apparently re-register a subdev */
 WARN_ON(sd->v4l2_dev != NULL);
 if (!try_module_get(sd->owner))
  return -ENODEV;
 sd->v4l2_dev = v4l2_dev;//让v4l2_device指向v4l2_subdev的相应成员
 spin_lock(&v4l2_dev->lock);
 list_add_tail(&sd->list, &v4l2_dev->subdevs);//添加子设备到v4l2_device子设备链表
 spin_unlock(&v4l2_dev->lock);
3.3.1种还执行了一下操作:
  /* This just returns 0 if either of the two args is NULL */
//添加控制处理:
  err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
  if (err) {
   if (sd->internal_ops && sd->internal_ops->unregistered)
    sd->internal_ops->unregistered(sd);
   module_put(sd->owner);
   return err;
  }

#if defined(CONFIG_MEDIA_CONTROLLER)
  /* Register the entity. */
//注册多媒体
 if (v4l2_dev->mdev) {
   err = media_device_register_entity(v4l2_dev->mdev, entity);
   if (err < 0) {
    if (sd->internal_ops && sd->internal_ops->unregistered)
     sd->internal_ops->unregistered(sd);
    module_put(sd->owner);
    return err;
   }
  }
#endif
 return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);


6、v4l2_device_unregister_subdev

void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
{
 /* return if it isn't registered */
 if (sd == NULL || sd->v4l2_dev == NULL)
  return;
 spin_lock(&sd->v4l2_dev->lock);
 list_del(&sd->list);//从链表中删除要注消的子设备
 spin_unlock(&sd->v4l2_dev->lock);
 sd->v4l2_dev = NULL;//
 module_put(sd->owner);
3.3.1中还有一下操作:
 v4l2_dev = sd->v4l2_dev;
//注消控制处理
 if (sd->internal_ops && sd->internal_ops->unregistered)
  sd->internal_ops->unregistered(sd);
 sd->v4l2_dev = NULL;
//注消media实体
#if defined(CONFIG_MEDIA_CONTROLLER)
 if (v4l2_dev->mdev)
  media_device_unregister_entity(&sd->entity);
#endif
//注消video设备节点
 video_unregister_device(sd->devnode);
 module_put(sd->owner);
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);


3.3.1种还有下边一些函数:
7、v4l2_device_put

int v4l2_device_put(struct v4l2_device *v4l2_dev)
{
 return kref_put(&v4l2_dev->ref, v4l2_device_release);
}
static void v4l2_device_release(struct kref *ref)
{
 struct v4l2_device *v4l2_dev =
  container_of(ref, struct v4l2_device, ref);

 if (v4l2_dev->release)
  v4l2_dev->release(v4l2_dev);
}


注册v4l2设备中所有子设备的设备节点,它们被标记为V4L2_SUBDEV_FL_HAS_DEVNODE
8、v4l2_device_register_subdev_nodes

int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
 struct video_device *vdev;
 struct v4l2_subdev *sd;
 int err;

 //Register a device node for every subdev marked with the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
初始化并注册子设备节点
 list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
  if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
   continue;

  vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
  if (!vdev) {
   err = -ENOMEM;
   goto clean_up;
  }



  video_set_drvdata(vdev, sd);
  strlcpy(vdev->name, sd->name, sizeof(vdev->name));
  vdev->v4l2_dev = v4l2_dev;
  vdev->fops = &v4l2_subdev_fops;
  vdev->release = v4l2_device_release_subdev_node;
  vdev->ctrl_handler = sd->ctrl_handler;
  err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
           sd->owner);
  if (err < 0) {
   kfree(vdev);
   goto clean_up;
  }
//media 实体设备号处理
#if defined(CONFIG_MEDIA_CONTROLLER)
  sd->entity.info.v4l.major = VIDEO_MAJOR;
  sd->entity.info.v4l.minor = vdev->minor;
#endif
  sd->devnode = vdev;
 }
 return 0;
//发生错误时注消掉所有节点
clean_up:
 list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
  if (!sd->devnode)
   break;
  video_unregister_device(sd->devnode);
 }

 return err;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);

//清除子设备节点操作函数
static void v4l2_device_release_subdev_node(struct video_device *vdev)
{
 struct v4l2_subdev *sd = video_get_drvdata(vdev);
 sd->devnode = NULL;
 kfree(vdev);
}


还有一个内联函数(和v4l2_device_put对应):
9、v4l2_device_get

static inline void v4l2_device_get(struct v4l2_device *v4l2_dev)
{
 kref_get(&v4l2_dev->ref);
}


 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值