Merge tag 'usb-4.6-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[deliverable/linux.git] / drivers / media / media-device.c
index e9219f528d7e8ada4876649c28b9d0c54ce68117..3cfd7af8c5cab06607a94c716951daa6166b60e2 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/media.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/usb.h>
 
 #include <media/media-device.h>
 #include <media/media-devnode.h>
  * Userspace API
  */
 
+static inline void __user *media_get_uptr(__u64 arg)
+{
+       return (void __user *)(uintptr_t)arg;
+}
+
 static int media_device_open(struct file *filp)
 {
        return 0;
@@ -58,7 +65,11 @@ static int media_device_get_info(struct media_device *dev,
 
        memset(&info, 0, sizeof(info));
 
-       strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
+       if (dev->driver_name[0])
+               strlcpy(info.driver, dev->driver_name, sizeof(info.driver));
+       else
+               strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
+
        strlcpy(info.model, dev->model, sizeof(info.model));
        strlcpy(info.serial, dev->serial, sizeof(info.serial));
        strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));
@@ -257,7 +268,6 @@ static long media_device_setup_link(struct media_device *mdev,
        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)
 {
@@ -265,10 +275,10 @@ static long __media_device_get_topology(struct media_device *mdev,
        struct media_interface *intf;
        struct media_pad *pad;
        struct media_link *link;
-       struct media_v2_entity kentity, *uentity;
-       struct media_v2_interface kintf, *uintf;
-       struct media_v2_pad kpad, *upad;
-       struct media_v2_link klink, *ulink;
+       struct media_v2_entity kentity, __user *uentity;
+       struct media_v2_interface kintf, __user *uintf;
+       struct media_v2_pad kpad, __user *upad;
+       struct media_v2_link klink, __user *ulink;
        unsigned int i;
        int ret = 0;
 
@@ -413,7 +423,6 @@ static long media_device_get_topology(struct media_device *mdev,
 
        return 0;
 }
-#endif
 
 static long media_device_ioctl(struct file *filp, unsigned int cmd,
                               unsigned long arg)
@@ -447,14 +456,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
                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;
        }
@@ -503,9 +511,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
        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:
@@ -564,6 +570,7 @@ static void media_device_release(struct media_devnode *mdev)
 int __must_check media_device_register_entity(struct media_device *mdev,
                                              struct media_entity *entity)
 {
+       struct media_entity_notify *notify, *next;
        unsigned int i;
        int ret;
 
@@ -603,8 +610,33 @@ int __must_check media_device_register_entity(struct media_device *mdev,
                media_gobj_create(mdev, MEDIA_GRAPH_PAD,
                               &entity->pads[i].graph_obj);
 
+       /* invoke entity_notify callbacks */
+       list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) {
+               (notify)->notify(entity, notify->notify_data);
+       }
+
        spin_unlock(&mdev->lock);
 
+       mutex_lock(&mdev->graph_mutex);
+       if (mdev->entity_internal_idx_max
+           >= mdev->pm_count_walk.ent_enum.idx_max) {
+               struct media_entity_graph new = { .top = 0 };
+
+               /*
+                * Initialise the new graph walk before cleaning up
+                * the old one in order not to spoil the graph walk
+                * object of the media device if graph walk init fails.
+                */
+               ret = media_entity_graph_walk_init(&new, mdev);
+               if (ret) {
+                       mutex_unlock(&mdev->graph_mutex);
+                       return ret;
+               }
+               media_entity_graph_walk_cleanup(&mdev->pm_count_walk);
+               mdev->pm_count_walk = new;
+       }
+       mutex_unlock(&mdev->graph_mutex);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(media_device_register_entity);
@@ -636,6 +668,8 @@ static void __media_device_unregister_entity(struct media_entity *entity)
        /* Remove the entity */
        media_gobj_destroy(&entity->graph_obj);
 
+       /* invoke entity_notify callbacks to handle entity removal?? */
+
        entity->graph_obj.mdev = NULL;
 }
 
@@ -668,6 +702,7 @@ void media_device_init(struct media_device *mdev)
        INIT_LIST_HEAD(&mdev->interfaces);
        INIT_LIST_HEAD(&mdev->pads);
        INIT_LIST_HEAD(&mdev->links);
+       INIT_LIST_HEAD(&mdev->entity_notify);
        spin_lock_init(&mdev->lock);
        mutex_init(&mdev->graph_mutex);
        ida_init(&mdev->entity_internal_idx);
@@ -680,6 +715,7 @@ void media_device_cleanup(struct media_device *mdev)
 {
        ida_destroy(&mdev->entity_internal_idx);
        mdev->entity_internal_idx_max = 0;
+       media_entity_graph_walk_cleanup(&mdev->pm_count_walk);
        mutex_destroy(&mdev->graph_mutex);
 }
 EXPORT_SYMBOL_GPL(media_device_cleanup);
@@ -713,11 +749,40 @@ int __must_check __media_device_register(struct media_device *mdev,
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
 
+int __must_check media_device_register_entity_notify(struct media_device *mdev,
+                                       struct media_entity_notify *nptr)
+{
+       spin_lock(&mdev->lock);
+       list_add_tail(&nptr->list, &mdev->entity_notify);
+       spin_unlock(&mdev->lock);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
+
+/*
+ * Note: Should be called with mdev->lock held.
+ */
+static void __media_device_unregister_entity_notify(struct media_device *mdev,
+                                       struct media_entity_notify *nptr)
+{
+       list_del(&nptr->list);
+}
+
+void media_device_unregister_entity_notify(struct media_device *mdev,
+                                       struct media_entity_notify *nptr)
+{
+       spin_lock(&mdev->lock);
+       __media_device_unregister_entity_notify(mdev, nptr);
+       spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
+
 void media_device_unregister(struct media_device *mdev)
 {
        struct media_entity *entity;
        struct media_entity *next;
        struct media_interface *intf, *tmp_intf;
+       struct media_entity_notify *notify, *nextp;
 
        if (mdev == NULL)
                return;
@@ -734,6 +799,10 @@ void media_device_unregister(struct media_device *mdev)
        list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
                __media_device_unregister_entity(entity);
 
+       /* Remove all entity_notify callbacks from the media device */
+       list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
+               __media_device_unregister_entity_notify(mdev, notify);
+
        /* Remove all interfaces from the media device */
        list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
                                 graph_obj.list) {
@@ -777,4 +846,58 @@ struct media_device *media_device_find_devres(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(media_device_find_devres);
 
+#if IS_ENABLED(CONFIG_PCI)
+void media_device_pci_init(struct media_device *mdev,
+                          struct pci_dev *pci_dev,
+                          const char *name)
+{
+       mdev->dev = &pci_dev->dev;
+
+       if (name)
+               strlcpy(mdev->model, name, sizeof(mdev->model));
+       else
+               strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model));
+
+       sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev));
+
+       mdev->hw_revision = (pci_dev->subsystem_vendor << 16)
+                           | pci_dev->subsystem_device;
+
+       mdev->driver_version = LINUX_VERSION_CODE;
+
+       media_device_init(mdev);
+}
+EXPORT_SYMBOL_GPL(media_device_pci_init);
+#endif
+
+#if IS_ENABLED(CONFIG_USB)
+void __media_device_usb_init(struct media_device *mdev,
+                            struct usb_device *udev,
+                            const char *board_name,
+                            const char *driver_name)
+{
+       mdev->dev = &udev->dev;
+
+       if (driver_name)
+               strlcpy(mdev->driver_name, driver_name,
+                       sizeof(mdev->driver_name));
+
+       if (board_name)
+               strlcpy(mdev->model, board_name, sizeof(mdev->model));
+       else if (udev->product)
+               strlcpy(mdev->model, udev->product, sizeof(mdev->model));
+       else
+               strlcpy(mdev->model, "unknown model", sizeof(mdev->model));
+       if (udev->serial)
+               strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+       usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info));
+       mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+       mdev->driver_version = LINUX_VERSION_CODE;
+
+       media_device_init(mdev);
+}
+EXPORT_SYMBOL_GPL(__media_device_usb_init);
+#endif
+
+
 #endif /* CONFIG_MEDIA_CONTROLLER */
This page took 0.026964 seconds and 5 git commands to generate.