Linux V4L2 子系统源码分析
来源:互联网 发布:淘宝全球买手香港 编辑:程序博客网 时间:2024/06/15 09:48
V4L2 设备分析
目录
- V4L2 设备分析
- 目录
- v4l2_device 结构体
- V4L2设备注册
- v4l2_device_release
- v4l2_device_disconnect
- V4L2设备的注销
- v4l2_device_unregister_subdev v4l2_device_register_subdev_nodes
v4l2_device 结构体
struct v4l2_device
结构体是v4l2 设备驱动的主要结构体。v4l2_device结构体如下:
//linux/include/media/v4l2-device.hstruct v4l2_device { struct device *dev; //pointer to struct device.#if defined(CONFIG_MEDIA_CONTROLLER) struct media_device *mdev;#endif struct list_head subdevs; //used to keep track of the registered subdevs spinlock_t lock; char name[V4L2_DEVICE_NAME_SIZE]; //unique device name, by default the driver name + bus ID void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg); //notify callback called by some sub-devices struct v4l2_ctrl_handler *ctrl_handler; //The control handler. May be %NULL. struct v4l2_prio_state prio; struct kref ref; //Keep track of the references to this struct. void (*release)(struct v4l2_device *v4l2_dev);//Release function that is called when the ref count goes to 0.};
每一个v4l2 设备都需要对用有一个struct v4l2_device
。 这里需要注意的是成员subdevs
,它用于管理已经注册的各个subdevice。在驱动程序中还经常需要用的是,dev->driver_data 指向了这个结构体。方便通过device 结构体找到此结构体。
enum v4l2_priority { V4L2_PRIORITY_UNSET = 0, /* not initialized */ V4L2_PRIORITY_BACKGROUND = 1, V4L2_PRIORITY_INTERACTIVE = 2, V4L2_PRIORITY_RECORD = 3, V4L2_PRIORITY_DEFAULT = V4L2_PRIORITY_INTERACTIVE,};struct v4l2_prio_state { atomic_t prios[4];};
struct v4l2_ctrl_handler { struct mutex _lock; struct mutex *lock; struct list_head ctrls; struct list_head ctrl_refs; struct v4l2_ctrl_ref *cached; struct v4l2_ctrl_ref **buckets; v4l2_ctrl_notify_fnc notify; void *notify_priv; u16 nr_of_buckets; int error;};
V4L2设备注册
v4l2_device_register
函数完成对v4l2设备的注册:
//linux/include/media/v4l2-core/v4l2-device.cint 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_prio_init(&v4l2_dev->prio); kref_init(&v4l2_dev->ref); get_device(dev); v4l2_dev->dev = dev; if (dev == NULL) { /* If dev == NULL, then name must be filled in by the caller */ if (WARN_ON(!v4l2_dev->name[0])) return -EINVAL; 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)) dev_set_drvdata(dev, v4l2_dev); return 0;}EXPORT_SYMBOL_GPL(v4l2_device_register);
v4l2_device_register
函数为v4l2设备注册一个v4l2_device
结构体。分析代码可以的出:首先初始化subdevs链表、自旋锁lock、v4l2_prio_state 结构体、以及ref。之后get_device(dev)对dev进行引用计数。最后通过dev_set_drvdata
函数将dev->driver_data 指向了v4l2_device结构体。
v4l2_device_release
//linux/include/media/v4l2-core/v4l2-device.cstatic 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_device_release
函数在v4l2_device -> ref 为0时调用(也就意味着已经没有人在使用v4l2设备了),也即调用v4l2_dev->release函数释放v4l2设备。
v4l2_device_disconnect
void v4l2_device_disconnect(struct v4l2_device *v4l2_dev){ if (v4l2_dev->dev == NULL) return; if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) dev_set_drvdata(v4l2_dev->dev, NULL); put_device(v4l2_dev->dev); v4l2_dev->dev = NULL;}
v4l2_device_disconnect
函数用于将v4l2设备的state改变为disconnect。
V4L2设备的注销
void v4l2_device_unregister(struct v4l2_device *v4l2_dev){ struct v4l2_subdev *sd, *next; if (v4l2_dev == NULL || !v4l2_dev->name[0]) return; v4l2_device_disconnect(v4l2_dev); /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { v4l2_device_unregister_subdev(sd);#if IS_ENABLED(CONFIG_I2C) if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { struct i2c_client *client = v4l2_get_subdevdata(sd); if (client && !client->dev.of_node && !client->dev.fwnode) i2c_unregister_device(client); continue; }#endif#if defined(CONFIG_SPI) if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { struct spi_device *spi = v4l2_get_subdevdata(sd); if (spi && !spi->dev.of_node && !spi->dev.fwnode) spi_unregister_device(spi); continue; }#endif } /* Mark as unregistered, thus preventing duplicate unregistrations */ v4l2_dev->name[0] = '\0';}
v4l2_device_unregister
函数将会注销掉所有的sub_device和其他的一些资源,如IIC或则SPI。在函数中首先便利链表找到每一个已经注册sub_device,并调用 v4l2_device_unregister_subdev
函数注销掉相应的sub_device。在这里可以适当关注一下函数v4l2_get_subdevdata
:
static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd){ return sd->dev_priv;}
此函数使用了v4l2_subdev结构体中的dev_priv。
v4l2_device_unregister_subdev &v4l2_device_register_subdev_nodes
下面分析一下函数v4l2_device_unregister_subdev
:
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd){ struct v4l2_device *v4l2_dev; /* return if it isn't registered */ if (sd == NULL || sd->v4l2_dev == NULL) return; v4l2_dev = sd->v4l2_dev; spin_lock(&v4l2_dev->lock); list_del(&sd->list); spin_unlock(&v4l2_dev->lock); if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); sd->v4l2_dev = NULL;#if defined(CONFIG_MEDIA_CONTROLLER) if (v4l2_dev->mdev) { /* * No need to explicitly remove links, as both pads and * links are removed by the function below, in the right order */ media_device_unregister_entity(&sd->entity); }#endif video_unregister_device(sd->devnode); if (!sd->owner_v4l2_dev) module_put(sd->owner);}
函数首先删除sd->list,然后通过调用sd->internal_ops->unregistered(sd)注销掉相应的sub_device。sd->internal_ops相关的内容会在后续对sub_device结构体的分析中介绍说明。然后设置sd->v4l2_dev = NULL。并注销掉相应media_device和video_device节点。(media_device和video_device)会在后面的分析中介绍说明。与之想对应的函数为:v4l2_device_register_subdev_nodes
完成对sub_device设备的注册:
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; }#if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.info.dev.major = VIDEO_MAJOR; sd->entity.info.dev.minor = vdev->minor; if (vdev->v4l2_dev->mdev) { struct media_link *link; link = media_create_intf_link(&sd->entity, &vdev->intf_devnode->intf, MEDIA_LNK_FL_ENABLED); if (!link) { err = -ENOMEM; goto clean_up; } }#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;}
函数首先遍历v4l2_dev->subdevs
链表找到每个已注册的sub_device,并且判断是否已经生成设备节点(通过V4L2_SUBDEV_FL_HAS_DEVNODEflag判断),如果没有生成则会在注册video_device设备并生成相应的设备节点:首先申请video_device结构体,通过video_set_drvdata
函数将video_device -> dev ->driver_data 指向了sub_device结构体,并通过v4l2_device设置video_device结构体。最后通过__video_register_device
函数注册video_device设备并创建设备节点。最后设置sd->devnode = vdev,完成为v4l2设备注册一个sub_device。
注:linux内核博大精深,菜鸟学习难免会有一些错误,各位大神发现还请批评指正。
- Linux V4L2 子系统源码分析
- [完结]Linux内核中的V4L2核心框架分析(V4L2 framework,video for linux 2,linux视频子系统)
- Linux内核中的V4L2核心框架分析(V4L2 framework,video for linux 2,linux视频子系统)
- Linux内核中的V4L2核心框架分析(V4L2 framework,video for linux 2,linux视频子系统) .
- USB子系统源码分析
- Linux输入子系统分析
- LINUX 输入子系统分析
- linux input 子系统分析
- Linux input子系统分析
- LINUX 输入子系统分析
- Linux I2C 子系统分析
- linux输入子系统分析
- linux输入子系统分析
- linux输入子系统分析
- Linux I2C子系统分析
- linux网络子系统分析
- linux input 子系统分析
- linux输入子系统分析
- 用函数编程计算并输出杨辉三角形
- ARX给CAD发送命令的五种方法
- 【笔记-C++】 继承
- 搜索引擎_lucene
- LeetCode Add Two Numbers
- Linux V4L2 子系统源码分析
- 安卓中的布局
- 银联与微支不得不说的秘密
- 机器学习算法中如何选取超参数:学习速率、正则项系数、minibatch size
- [LUOGU1071] 潜伏者
- Ubuntu 14.04下载Android代码
- 炼数成金实战Java高并发程序设计(附讲义与作业) Java 学习推荐
- 首次usaco感言
- Nepire的校OJ入门题解—17蓝桥选拔篇(二)