media framework 是嵌入到lv4l2 框架中,主要分成entity pad link三大部分,entity设备的实例
pad 用来作为entity的连接点 link 用来连接entity 下图可以直观的看到
首先看media-devnode.c文件,在/sys/bus/下注册了media总线,后续相关的media设备都会注册到下面
Z:\RV1109\kernel\drivers\media\media-devnode.c
subsys_initcall(media_devnode_init);
static int __init media_devnode_init(void)
{
int ret;
pr_info("Linux media interface: v0.10\n");
ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
MEDIA_NAME);
if (ret < 0) {
pr_warn("unable to allocate major\n");
return ret;
}
ret = bus_register(&media_bus_type);
if (ret < 0) {
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
pr_warn("bus_register failed\n");
return -EIO;
}
return 0;
}
注册完总线后需要把媒体设备实例化,调用注册媒体设备接口在media总线下注册
Z:\RV1109\kernel\drivers\media\media-device.c
//初始化media device下的 entity pad link 链表
void media_device_init(struct media_device *mdev)
{
INIT_LIST_HEAD(&mdev->entities);
INIT_LIST_HEAD(&mdev->interfaces);
INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links);
INIT_LIST_HEAD(&mdev->entity_notify);
mutex_init(&mdev->graph_mutex);
ida_init(&mdev->entity_internal_idx);
dev_dbg(mdev->dev, "Media device initialized\n");
}
//将媒体实例化注册到media总线下 实际调用media_devnode_register 注册media节点
int __must_check __media_device_register(struct media_device *mdev,
struct module *owner)
{
struct media_devnode *devnode;
int ret;
devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
if (!devnode)
return -ENOMEM;
/* Register the device node. */
mdev->devnode = devnode;
devnode->fops = &media_device_fops;
devnode->parent = mdev->dev;
devnode->release = media_device_release;
/* Set version 0 to indicate user-space that the graph is static */
mdev->topology_version = 0;
ret = media_devnode_register(mdev, devnode, owner);
if (ret < 0) {
/* devnode free is handled in media_devnode_*() */
mdev->devnode = NULL;
return ret;
}
ret = device_create_file(&devnode->dev, &dev_attr_model);
if (ret < 0) {
/* devnode free is handled in media_devnode_*() */
mdev->devnode = NULL;
media_devnode_unregister_prepare(devnode);
media_devnode_unregister(devnode);
return ret;
}
dev_dbg(mdev->dev, "Media device registered\n");
return 0;
}
//注册media node节点
int __must_check media_devnode_register(struct media_device *mdev,
struct media_devnode *devnode,
struct module *owner)
{
int minor;
int ret;
/* Part 1: Find a free minor number */
mutex_lock(&media_devnode_lock);
minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0);
if (minor == MEDIA_NUM_DEVICES) {
mutex_unlock(&media_devnode_lock);
pr_err("could not get a free minor\n");
kfree(devnode);
return -ENFILE;
}
set_bit(minor, media_devnode_nums);
mutex_unlock(&media_devnode_lock);
devnode->minor = minor;
devnode->media_dev = mdev;
/* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */
devnode->dev.bus = &media_bus_type;
devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
devnode->dev.release = media_devnode_release;
if (devnode->parent)
devnode->dev.parent = devnode->parent;
dev_set_name(&devnode->dev, "media%d", devnode->minor);
device_initialize(&devnode->dev);
/* Part 2: Initialize the character device */
cdev_init(&devnode->cdev, &media_devnode_fops);
devnode->cdev.owner = owner;
/* Part 3: Add the media and char device */
ret = cdev_device_add(&devnode->cdev, &devnode->dev);
if (ret < 0) {
pr_err("%s: cdev_device_add failed\n", __func__);
goto cdev_add_error;
}
/* Part 4: Activate this minor. The char device can now be used. */
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
return 0;
cdev_add_error:
mutex_lock(&media_devnode_lock);
clear_bit(devnode->minor, media_devnode_nums);
devnode->media_dev = NULL;
mutex_unlock(&media_devnode_lock);
put_device(&devnode->dev);
return ret;
}
//pads entity links 头文件
media/media-entity.h
//entity init
int media_entity_init(struct media_entity *entity, u16 num_pads,
struct media_pad *pads, u16 extra_links)
//entity pad init
int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
struct media_pad *pads)
{
struct media_device *mdev = entity->graph_obj.mdev;
unsigned int i;
if (num_pads >= MEDIA_ENTITY_MAX_PADS)
return -E2BIG;
entity->num_pads = num_pads;
entity->pads = pads;
if (mdev)
mutex_lock(&mdev->graph_mutex);
for (i = 0; i < num_pads; i++) {
pads[i].entity = entity;
pads[i].index = i;
if (mdev)
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
}
if (mdev)
mutex_unlock(&mdev->graph_mutex);
return 0;
}
//控制pipeline开启
__must_check int media_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe)
{
struct media_device *mdev = entity->graph_obj.mdev;
int ret;
mutex_lock(&mdev->graph_mutex);
ret = __media_pipeline_start(entity, pipe);
mutex_unlock(&mdev->graph_mutex);
return ret;
}
//控制pipeline 关闭
void media_pipeline_stop(struct media_entity *entity)
{
struct media_device *mdev = entity->graph_obj.mdev;
mutex_lock(&mdev->graph_mutex);
__media_pipeline_stop(entity);
mutex_unlock(&mdev->graph_mutex);
}
//entity的相关连接是通过pad之间的连接
//创建pad -> pad 的连接
media_create_pad_link()
//remove link
media_entity_remove_links()
//interface to entity的连接
media_create_intf_link()
//remove
media_remove_intf_links()
每个设备实体都代表着一个struct v4l2_device,在linux中我们往往把这个结构嵌入到大框架中,旗下包含着设备实体
//注册v4l2 device
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_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;
}
//初始化v4l2 device 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;
}
相对于一个监控系统来说,v4l2 subdevice 通常是作为一个sensor来做相应的操作
//v4l2 subdevice init
void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
{
INIT_LIST_HEAD(&sd->list);
BUG_ON(!ops);
sd->ops = ops;
sd->v4l2_dev = NULL;
sd->flags = 0;
sd->name[0] = '\0';
sd->grp_id = 0;
sd->dev_priv = NULL;
sd->host_priv = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
#endif
}
//v4l2 subdevice通常引入media framework
//注册subdevice到v4l2 框架中
v4l2_device_register_subdev();
//初始化device
v4l2_i2c_subdev_init()
//v4l2_subdev struct to the i2c_client struct
struct i2c_client *client = v4l2_get_subdevdata(sd);
//i2c_client to a v4l2_subdev struct
struct v4l2_subdev *sd = i2c_get_clientdata(client);
work
//注册subdevice到v4l2 框架中
v4l2_device_register_subdev();
#### 2.I2c subdevice 相关操作
```c
//初始化device
v4l2_i2c_subdev_init()
//v4l2_subdev struct to the i2c_client struct
struct i2c_client *client = v4l2_get_subdevdata(sd);
//i2c_client to a v4l2_subdev struct
struct v4l2_subdev *sd = i2c_get_clientdata(client);