mc-dev-allocator.c (3569B)
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);