#include <linux/compat.h>
#include <linux/export.h>
+#include <linux/idr.h>
#include <linux/ioctl.h>
#include <linux/media.h>
-#include <linux/types.h>
#include <linux/slab.h>
+#include <linux/types.h>
#include <media/media-device.h>
#include <media/media-devnode.h>
u_ent.id = media_entity_id(ent);
if (ent->name)
strlcpy(u_ent.name, ent->name, sizeof(u_ent.name));
- u_ent.type = ent->type;
- u_ent.revision = ent->revision;
+ u_ent.type = ent->function;
+ u_ent.revision = 0; /* Unused */
u_ent.flags = ent->flags;
- u_ent.group_id = ent->group_id;
+ u_ent.group_id = 0; /* Unused */
u_ent.pads = ent->num_pads;
u_ent.links = ent->num_links - ent->num_backlinks;
memcpy(&u_ent.raw, &ent->info, sizeof(ent->info));
}
if (links->links) {
- struct media_link *ent_link;
- struct media_link_desc __user *ulink = links->links;
+ struct media_link *link;
+ struct media_link_desc __user *ulink_desc = links->links;
- list_for_each_entry(ent_link, &entity->links, list) {
- struct media_link_desc link;
+ list_for_each_entry(link, &entity->links, list) {
+ struct media_link_desc klink_desc;
/* Ignore backlinks. */
- if (ent_link->source->entity != entity)
+ if (link->source->entity != entity)
continue;
- memset(&link, 0, sizeof(link));
- media_device_kpad_to_upad(ent_link->source,
- &link.source);
- media_device_kpad_to_upad(ent_link->sink,
- &link.sink);
- link.flags = ent_link->flags;
- if (copy_to_user(ulink, &link, sizeof(*ulink)))
+ memset(&klink_desc, 0, sizeof(klink_desc));
+ media_device_kpad_to_upad(link->source,
+ &klink_desc.source);
+ media_device_kpad_to_upad(link->sink,
+ &klink_desc.sink);
+ klink_desc.flags = link->flags;
+ if (copy_to_user(ulink_desc, &klink_desc,
+ sizeof(*ulink_desc)))
return -EFAULT;
- ulink++;
+ ulink_desc++;
}
}
return ret;
}
+#if 0 /* Let's postpone it to Kernel 4.6 */
static long __media_device_get_topology(struct media_device *mdev,
struct media_v2_topology *topo)
{
struct media_interface *intf;
struct media_pad *pad;
struct media_link *link;
- struct media_v2_entity uentity;
- struct media_v2_interface uintf;
- struct media_v2_pad upad;
- struct media_v2_link ulink;
- int ret = 0, i;
+ struct media_v2_entity kentity, *uentity;
+ struct media_v2_interface kintf, *uintf;
+ struct media_v2_pad kpad, *upad;
+ struct media_v2_link klink, *ulink;
+ unsigned int i;
+ int ret = 0;
topo->topology_version = mdev->topology_version;
/* Get entities and number of entities */
i = 0;
+ uentity = media_get_uptr(topo->ptr_entities);
media_device_for_each_entity(entity, mdev) {
i++;
-
- if (ret || !topo->entities)
+ if (ret || !uentity)
continue;
if (i > topo->num_entities) {
}
/* Copy fields to userspace struct if not error */
- memset(&uentity, 0, sizeof(uentity));
- uentity.id = entity->graph_obj.id;
- strncpy(uentity.name, entity->name,
- sizeof(uentity.name));
+ memset(&kentity, 0, sizeof(kentity));
+ kentity.id = entity->graph_obj.id;
+ kentity.function = entity->function;
+ strncpy(kentity.name, entity->name,
+ sizeof(kentity.name));
- if (copy_to_user(&topo->entities[i - 1], &uentity, sizeof(uentity)))
+ if (copy_to_user(uentity, &kentity, sizeof(kentity)))
ret = -EFAULT;
+ uentity++;
}
topo->num_entities = i;
/* Get interfaces and number of interfaces */
i = 0;
+ uintf = media_get_uptr(topo->ptr_interfaces);
media_device_for_each_intf(intf, mdev) {
i++;
-
- if (ret || !topo->interfaces)
+ if (ret || !uintf)
continue;
if (i > topo->num_interfaces) {
continue;
}
- memset(&uintf, 0, sizeof(uintf));
+ memset(&kintf, 0, sizeof(kintf));
/* Copy intf fields to userspace struct */
- uintf.id = intf->graph_obj.id;
- uintf.intf_type = intf->type;
- uintf.flags = intf->flags;
+ kintf.id = intf->graph_obj.id;
+ kintf.intf_type = intf->type;
+ kintf.flags = intf->flags;
if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) {
struct media_intf_devnode *devnode;
devnode = intf_to_devnode(intf);
- uintf.devnode.major = devnode->major;
- uintf.devnode.minor = devnode->minor;
+ kintf.devnode.major = devnode->major;
+ kintf.devnode.minor = devnode->minor;
}
- if (copy_to_user(&topo->interfaces[i - 1], &uintf, sizeof(uintf)))
+ if (copy_to_user(uintf, &kintf, sizeof(kintf)))
ret = -EFAULT;
+ uintf++;
}
topo->num_interfaces = i;
/* Get pads and number of pads */
i = 0;
+ upad = media_get_uptr(topo->ptr_pads);
media_device_for_each_pad(pad, mdev) {
i++;
-
- if (ret || !topo->pads)
+ if (ret || !upad)
continue;
if (i > topo->num_pads) {
continue;
}
- memset(&upad, 0, sizeof(upad));
+ memset(&kpad, 0, sizeof(kpad));
/* Copy pad fields to userspace struct */
- upad.id = pad->graph_obj.id;
- upad.entity_id = pad->entity->graph_obj.id;
- upad.flags = pad->flags;
+ kpad.id = pad->graph_obj.id;
+ kpad.entity_id = pad->entity->graph_obj.id;
+ kpad.flags = pad->flags;
- if (copy_to_user(&topo->pads[i - 1], &upad, sizeof(upad)))
+ if (copy_to_user(upad, &kpad, sizeof(kpad)))
ret = -EFAULT;
+ upad++;
}
topo->num_pads = i;
/* Get links and number of links */
i = 0;
+ ulink = media_get_uptr(topo->ptr_links);
media_device_for_each_link(link, mdev) {
+ if (link->is_backlink)
+ continue;
+
i++;
- if (ret || !topo->links)
+ if (ret || !ulink)
continue;
if (i > topo->num_links) {
continue;
}
- memset(&ulink, 0, sizeof(ulink));
+ memset(&klink, 0, sizeof(klink));
/* Copy link fields to userspace struct */
- ulink.id = link->graph_obj.id;
- ulink.source_id = link->gobj0->id;
- ulink.sink_id = link->gobj1->id;
- ulink.flags = link->flags;
-
- if (media_type(link->gobj0) != MEDIA_GRAPH_PAD)
- ulink.flags |= MEDIA_LNK_FL_INTERFACE_LINK;
+ klink.id = link->graph_obj.id;
+ klink.source_id = link->gobj0->id;
+ klink.sink_id = link->gobj1->id;
+ klink.flags = link->flags;
- if (copy_to_user(&topo->links[i - 1], &ulink, sizeof(ulink)))
+ if (copy_to_user(ulink, &klink, sizeof(klink)))
ret = -EFAULT;
+ ulink++;
}
topo->num_links = i;
struct media_v2_topology ktopo;
int ret;
- ret = copy_from_user(&ktopo, utopo, sizeof(ktopo));
-
- if (ret < 0)
- return ret;
+ if (copy_from_user(&ktopo, utopo, sizeof(ktopo)))
+ return -EFAULT;
ret = __media_device_get_topology(mdev, &ktopo);
if (ret < 0)
return ret;
- ret = copy_to_user(utopo, &ktopo, sizeof(*utopo));
+ if (copy_to_user(utopo, &ktopo, sizeof(*utopo)))
+ return -EFAULT;
- return ret;
+ return 0;
}
+#endif
static long media_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
mutex_unlock(&dev->graph_mutex);
break;
+#if 0 /* Let's postpone it to Kernel 4.6 */
case MEDIA_IOC_G_TOPOLOGY:
mutex_lock(&dev->graph_mutex);
ret = media_device_get_topology(dev,
(struct media_v2_topology __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
-
+#endif
default:
ret = -ENOIOCTLCMD;
}
case MEDIA_IOC_DEVICE_INFO:
case MEDIA_IOC_ENUM_ENTITIES:
case MEDIA_IOC_SETUP_LINK:
+#if 0 /* Let's postpone it to Kernel 4.6 */
case MEDIA_IOC_G_TOPOLOGY:
+#endif
return media_device_ioctl(filp, cmd, arg);
case MEDIA_IOC_ENUM_LINKS32:
}
/**
- * media_device_register - register a media device
+ * media_device_register_entity - Register an entity with a media device
+ * @mdev: The media device
+ * @entity: The entity
+ */
+int __must_check media_device_register_entity(struct media_device *mdev,
+ struct media_entity *entity)
+{
+ unsigned int i;
+ int ret;
+
+ if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
+ entity->function == MEDIA_ENT_F_UNKNOWN)
+ dev_warn(mdev->dev,
+ "Entity type for entity %s was not initialized!\n",
+ entity->name);
+
+ /* Warn if we apparently re-register an entity */
+ WARN_ON(entity->graph_obj.mdev != NULL);
+ entity->graph_obj.mdev = mdev;
+ INIT_LIST_HEAD(&entity->links);
+ entity->num_links = 0;
+ entity->num_backlinks = 0;
+
+ if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
+ return -ENOMEM;
+
+ spin_lock(&mdev->lock);
+
+ ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
+ &entity->internal_idx);
+ if (ret < 0) {
+ spin_unlock(&mdev->lock);
+ return ret;
+ }
+
+ mdev->entity_internal_idx_max =
+ max(mdev->entity_internal_idx_max, entity->internal_idx);
+
+ /* Initialize media_gobj embedded at the entity */
+ media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
+
+ /* Initialize objects at the pads */
+ for (i = 0; i < entity->num_pads; i++)
+ media_gobj_create(mdev, MEDIA_GRAPH_PAD,
+ &entity->pads[i].graph_obj);
+
+ spin_unlock(&mdev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(media_device_register_entity);
+
+static void __media_device_unregister_entity(struct media_entity *entity)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_link *link, *tmp;
+ struct media_interface *intf;
+ unsigned int i;
+
+ ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx);
+
+ /* Remove all interface links pointing to this entity */
+ list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
+ list_for_each_entry_safe(link, tmp, &intf->links, list) {
+ if (link->entity == entity)
+ __media_remove_intf_link(link);
+ }
+ }
+
+ /* Remove all data links that belong to this entity */
+ __media_entity_remove_links(entity);
+
+ /* Remove all pads that belong to this entity */
+ for (i = 0; i < entity->num_pads; i++)
+ media_gobj_destroy(&entity->pads[i].graph_obj);
+
+ /* Remove the entity */
+ media_gobj_destroy(&entity->graph_obj);
+
+ entity->graph_obj.mdev = NULL;
+}
+
+void media_device_unregister_entity(struct media_entity *entity)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+
+ if (mdev == NULL)
+ return;
+
+ spin_lock(&mdev->lock);
+ __media_device_unregister_entity(entity);
+ spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_device_unregister_entity);
+
+/**
+ * media_device_init() - initialize a media device
* @mdev: The media device
*
* The caller is responsible for initializing the media device before
* - dev must point to the parent device
* - model must be filled with the device model name
*/
-int __must_check __media_device_register(struct media_device *mdev,
- struct module *owner)
+void media_device_init(struct media_device *mdev)
{
- int ret;
-
- if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
- return -EINVAL;
-
INIT_LIST_HEAD(&mdev->entities);
INIT_LIST_HEAD(&mdev->interfaces);
INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex);
+ ida_init(&mdev->entity_internal_idx);
+
+ dev_dbg(mdev->dev, "Media device initialized\n");
+}
+EXPORT_SYMBOL_GPL(media_device_init);
+
+void media_device_cleanup(struct media_device *mdev)
+{
+ ida_destroy(&mdev->entity_internal_idx);
+ mdev->entity_internal_idx_max = 0;
+ mutex_destroy(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_device_cleanup);
+
+int __must_check __media_device_register(struct media_device *mdev,
+ struct module *owner)
+{
+ int ret;
/* Register the device node. */
mdev->devnode.fops = &media_device_fops;
mdev->devnode.parent = mdev->dev;
mdev->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)
return ret;
}
EXPORT_SYMBOL_GPL(__media_device_register);
-/**
- * media_device_unregister - unregister a media device
- * @mdev: The media device
- *
- */
void media_device_unregister(struct media_device *mdev)
{
struct media_entity *entity;
struct media_entity *next;
+ struct media_interface *intf, *tmp_intf;
- list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
- media_device_unregister_entity(entity);
-
- device_remove_file(&mdev->devnode.dev, &dev_attr_model);
- media_devnode_unregister(&mdev->devnode);
-
- dev_dbg(mdev->dev, "Media device unregistered\n");
-}
-EXPORT_SYMBOL_GPL(media_device_unregister);
-
-/**
- * media_device_register_entity - Register an entity with a media device
- * @mdev: The media device
- * @entity: The entity
- */
-int __must_check media_device_register_entity(struct media_device *mdev,
- struct media_entity *entity)
-{
- int i;
-
- if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_UNKNOWN ||
- entity->type == MEDIA_ENT_T_UNKNOWN)
- dev_warn(mdev->dev,
- "Entity type for entity %s was not initialized!\n",
- entity->name);
-
- /* Warn if we apparently re-register an entity */
- WARN_ON(entity->graph_obj.mdev != NULL);
- entity->graph_obj.mdev = mdev;
- INIT_LIST_HEAD(&entity->links);
+ if (mdev == NULL)
+ return;
spin_lock(&mdev->lock);
- /* Initialize media_gobj embedded at the entity */
- media_gobj_init(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
-
- /* Initialize objects at the pads */
- for (i = 0; i < entity->num_pads; i++)
- media_gobj_init(mdev, MEDIA_GRAPH_PAD,
- &entity->pads[i].graph_obj);
-
- spin_unlock(&mdev->lock);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(media_device_register_entity);
-
-/**
- * media_device_unregister_entity - Unregister an entity
- * @entity: The entity
- *
- * If the entity has never been registered this function will return
- * immediately.
- */
-void media_device_unregister_entity(struct media_entity *entity)
-{
- int i;
- struct media_device *mdev = entity->graph_obj.mdev;
- struct media_link *link, *tmp;
- if (mdev == NULL)
+ /* Check if mdev was ever registered at all */
+ if (!media_devnode_is_registered(&mdev->devnode)) {
+ spin_unlock(&mdev->lock);
return;
+ }
- spin_lock(&mdev->lock);
- list_for_each_entry_safe(link, tmp, &entity->links, list) {
- media_gobj_remove(&link->graph_obj);
- list_del(&link->list);
- kfree(link);
+ /* Remove all entities from the media device */
+ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
+ __media_device_unregister_entity(entity);
+
+ /* Remove all interfaces from the media device */
+ list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
+ graph_obj.list) {
+ __media_remove_intf_links(intf);
+ media_gobj_destroy(&intf->graph_obj);
+ kfree(intf);
}
- for (i = 0; i < entity->num_pads; i++)
- media_gobj_remove(&entity->pads[i].graph_obj);
- media_gobj_remove(&entity->graph_obj);
+
spin_unlock(&mdev->lock);
- entity->graph_obj.mdev = NULL;
+
+ device_remove_file(&mdev->devnode.dev, &dev_attr_model);
+ media_devnode_unregister(&mdev->devnode);
+
+ dev_dbg(mdev->dev, "Media device unregistered\n");
}
-EXPORT_SYMBOL_GPL(media_device_unregister_entity);
+EXPORT_SYMBOL_GPL(media_device_unregister);
static void media_device_release_devres(struct device *dev, void *res)
{
}
-/*
- * media_device_get_devres() - get media device as device resource
- * creates if one doesn't exist
-*/
struct media_device *media_device_get_devres(struct device *dev)
{
struct media_device *mdev;
}
EXPORT_SYMBOL_GPL(media_device_get_devres);
-/*
- * media_device_find_devres() - find media device as device resource
-*/
struct media_device *media_device_find_devres(struct device *dev)
{
return devres_find(dev, media_device_release_devres, NULL, NULL);