linux/drivers/media/mc/mc-dev-allocator.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * media-dev-allocator.c - Media Controller Device Allocator API
   4 *
   5 * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
   6 *
   7 * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com>
   8 */
   9
  10/*
  11 * This file adds a global refcounted Media Controller Device Instance API.
  12 * A system wide global media device list is managed and each media device
  13 * includes a kref count. The last put on the media device releases the media
  14 * device instance.
  15 *
  16 */
  17
  18#include <linux/kref.h>
  19#include <linux/module.h>
  20#include <linux/slab.h>
  21#include <linux/usb.h>
  22
  23#include <media/media-device.h>
  24#include <media/media-dev-allocator.h>
  25
  26static LIST_HEAD(media_device_list);
  27static DEFINE_MUTEX(media_device_lock);
  28
  29struct media_device_instance {
  30        struct media_device mdev;
  31        struct module *owner;
  32        struct list_head list;
  33        struct kref refcount;
  34};
  35
  36static inline struct media_device_instance *
  37to_media_device_instance(struct media_device *mdev)
  38{
  39        return container_of(mdev, struct media_device_instance, mdev);
  40}
  41
  42static void media_device_instance_release(struct kref *kref)
  43{
  44        struct media_device_instance *mdi =
  45                container_of(kref, struct media_device_instance, refcount);
  46
  47        dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__);
  48
  49        mutex_lock(&media_device_lock);
  50
  51        media_device_unregister(&mdi->mdev);
  52        media_device_cleanup(&mdi->mdev);
  53
  54        list_del(&mdi->list);
  55        mutex_unlock(&media_device_lock);
  56
  57        kfree(mdi);
  58}
  59
  60/* Callers should hold media_device_lock when calling this function */
  61static struct media_device *__media_device_get(struct device *dev,
  62                                                const char *module_name,
  63                                                struct module *owner)
  64{
  65        struct media_device_instance *mdi;
  66
  67        list_for_each_entry(mdi, &media_device_list, list) {
  68                if (mdi->mdev.dev != dev)
  69                        continue;
  70
  71                kref_get(&mdi->refcount);
  72
  73                /* get module reference for the media_device owner */
  74                if (owner != mdi->owner && !try_module_get(mdi->owner))
  75                        dev_err(dev,
  76                                "%s: module %s get owner reference error\n",
  77                                        __func__, module_name);
  78                else
  79                        dev_dbg(dev, "%s: module %s got owner reference\n",
  80                                        __func__, module_name);
  81                return &mdi->mdev;
  82        }
  83
  84        mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
  85        if (!mdi)
  86                return NULL;
  87
  88        mdi->owner = owner;
  89        kref_init(&mdi->refcount);
  90        list_add_tail(&mdi->list, &media_device_list);
  91
  92        dev_dbg(dev, "%s: Allocated media device for owner %s\n",
  93                        __func__, module_name);
  94        return &mdi->mdev;
  95}
  96
  97struct media_device *media_device_usb_allocate(struct usb_device *udev,
  98                                               const char *module_name,
  99                                               struct module *owner)
 100{
 101        struct media_device *mdev;
 102
 103        mutex_lock(&media_device_lock);
 104        mdev = __media_device_get(&udev->dev, module_name, owner);
 105        if (!mdev) {
 106                mutex_unlock(&media_device_lock);
 107                return ERR_PTR(-ENOMEM);
 108        }
 109
 110        /* check if media device is already initialized */
 111        if (!mdev->dev)
 112                __media_device_usb_init(mdev, udev, udev->product,
 113                                        module_name);
 114        mutex_unlock(&media_device_lock);
 115        return mdev;
 116}
 117EXPORT_SYMBOL_GPL(media_device_usb_allocate);
 118
 119void media_device_delete(struct media_device *mdev, const char *module_name,
 120                         struct module *owner)
 121{
 122        struct media_device_instance *mdi = to_media_device_instance(mdev);
 123
 124        mutex_lock(&media_device_lock);
 125        /* put module reference for the media_device owner */
 126        if (mdi->owner != owner) {
 127                module_put(mdi->owner);
 128                dev_dbg(mdi->mdev.dev,
 129                        "%s: module %s put owner module reference\n",
 130                        __func__, module_name);
 131        }
 132        mutex_unlock(&media_device_lock);
 133        kref_put(&mdi->refcount, media_device_instance_release);
 134}
 135EXPORT_SYMBOL_GPL(media_device_delete);
 136