123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * media-dev-allocator.c - Media Controller Device Allocator API
- *
- * Copyright (c) 2019 Shuah Khan <[email protected]>
- *
- * Credits: Suggested by Laurent Pinchart <[email protected]>
- */
- /*
- * This file adds a global refcounted Media Controller Device Instance API.
- * A system wide global media device list is managed and each media device
- * includes a kref count. The last put on the media device releases the media
- * device instance.
- *
- */
- #include <linux/kref.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/usb.h>
- #include <media/media-device.h>
- #include <media/media-dev-allocator.h>
- static LIST_HEAD(media_device_list);
- static DEFINE_MUTEX(media_device_lock);
- struct media_device_instance {
- struct media_device mdev;
- struct module *owner;
- struct list_head list;
- struct kref refcount;
- };
- static inline struct media_device_instance *
- to_media_device_instance(struct media_device *mdev)
- {
- return container_of(mdev, struct media_device_instance, mdev);
- }
- static void media_device_instance_release(struct kref *kref)
- {
- struct media_device_instance *mdi =
- container_of(kref, struct media_device_instance, refcount);
- dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__);
- mutex_lock(&media_device_lock);
- media_device_unregister(&mdi->mdev);
- media_device_cleanup(&mdi->mdev);
- list_del(&mdi->list);
- mutex_unlock(&media_device_lock);
- kfree(mdi);
- }
- /* Callers should hold media_device_lock when calling this function */
- static struct media_device *__media_device_get(struct device *dev,
- const char *module_name,
- struct module *owner)
- {
- struct media_device_instance *mdi;
- list_for_each_entry(mdi, &media_device_list, list) {
- if (mdi->mdev.dev != dev)
- continue;
- kref_get(&mdi->refcount);
- /* get module reference for the media_device owner */
- if (owner != mdi->owner && !try_module_get(mdi->owner))
- dev_err(dev,
- "%s: module %s get owner reference error\n",
- __func__, module_name);
- else
- dev_dbg(dev, "%s: module %s got owner reference\n",
- __func__, module_name);
- return &mdi->mdev;
- }
- mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
- if (!mdi)
- return NULL;
- mdi->owner = owner;
- kref_init(&mdi->refcount);
- list_add_tail(&mdi->list, &media_device_list);
- dev_dbg(dev, "%s: Allocated media device for owner %s\n",
- __func__, module_name);
- return &mdi->mdev;
- }
- struct media_device *media_device_usb_allocate(struct usb_device *udev,
- const char *module_name,
- struct module *owner)
- {
- struct media_device *mdev;
- mutex_lock(&media_device_lock);
- mdev = __media_device_get(&udev->dev, module_name, owner);
- if (!mdev) {
- mutex_unlock(&media_device_lock);
- return ERR_PTR(-ENOMEM);
- }
- /* check if media device is already initialized */
- if (!mdev->dev)
- __media_device_usb_init(mdev, udev, udev->product,
- module_name);
- mutex_unlock(&media_device_lock);
- return mdev;
- }
- EXPORT_SYMBOL_GPL(media_device_usb_allocate);
- void media_device_delete(struct media_device *mdev, const char *module_name,
- struct module *owner)
- {
- struct media_device_instance *mdi = to_media_device_instance(mdev);
- mutex_lock(&media_device_lock);
- /* put module reference for the media_device owner */
- if (mdi->owner != owner) {
- module_put(mdi->owner);
- dev_dbg(mdi->mdev.dev,
- "%s: module %s put owner module reference\n",
- __func__, module_name);
- }
- mutex_unlock(&media_device_lock);
- kref_put(&mdi->refcount, media_device_instance_release);
- }
- EXPORT_SYMBOL_GPL(media_device_delete);
|