Branch data Line data Source code
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 : : 26 : : static LIST_HEAD(media_device_list); 27 : : static DEFINE_MUTEX(media_device_lock); 28 : : 29 : : struct media_device_instance { 30 : : struct media_device mdev; 31 : : struct module *owner; 32 : : struct list_head list; 33 : : struct kref refcount; 34 : : }; 35 : : 36 : : static inline struct media_device_instance * 37 : : to_media_device_instance(struct media_device *mdev) 38 : : { 39 : : return container_of(mdev, struct media_device_instance, mdev); 40 : : } 41 : : 42 : 0 : static void media_device_instance_release(struct kref *kref) 43 : : { 44 : : struct media_device_instance *mdi = 45 : 0 : container_of(kref, struct media_device_instance, refcount); 46 : : 47 : : dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__); 48 : : 49 : 0 : mutex_lock(&media_device_lock); 50 : : 51 : 0 : media_device_unregister(&mdi->mdev); 52 : 0 : media_device_cleanup(&mdi->mdev); 53 : : 54 : : list_del(&mdi->list); 55 : 0 : mutex_unlock(&media_device_lock); 56 : : 57 : 0 : kfree(mdi); 58 : 0 : } 59 : : 60 : : /* Callers should hold media_device_lock when calling this function */ 61 : 0 : static 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 [ # # ]: 0 : list_for_each_entry(mdi, &media_device_list, list) { 68 [ # # ]: 0 : if (mdi->mdev.dev != dev) 69 : 0 : continue; 70 : : 71 : : kref_get(&mdi->refcount); 72 : : 73 : : /* get module reference for the media_device owner */ 74 [ # # # # ]: 0 : if (owner != mdi->owner && !try_module_get(mdi->owner)) 75 : 0 : 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 : 0 : return &mdi->mdev; 82 : : } 83 : : 84 : 0 : mdi = kzalloc(sizeof(*mdi), GFP_KERNEL); 85 [ # # ]: 0 : if (!mdi) 86 : : return NULL; 87 : : 88 : 0 : mdi->owner = owner; 89 : : kref_init(&mdi->refcount); 90 : 0 : 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 : 0 : return &mdi->mdev; 95 : : } 96 : : 97 : 0 : struct 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 : 0 : mutex_lock(&media_device_lock); 104 : 0 : mdev = __media_device_get(&udev->dev, module_name, owner); 105 [ # # ]: 0 : if (!mdev) { 106 : 0 : mutex_unlock(&media_device_lock); 107 : 0 : return ERR_PTR(-ENOMEM); 108 : : } 109 : : 110 : : /* check if media device is already initialized */ 111 [ # # ]: 0 : if (!mdev->dev) 112 : 0 : __media_device_usb_init(mdev, udev, udev->product, 113 : : module_name); 114 : 0 : mutex_unlock(&media_device_lock); 115 : 0 : return mdev; 116 : : } 117 : : EXPORT_SYMBOL_GPL(media_device_usb_allocate); 118 : : 119 : 0 : void 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 : 0 : mutex_lock(&media_device_lock); 125 : : /* put module reference for the media_device owner */ 126 [ # # ]: 0 : if (mdi->owner != owner) { 127 : 0 : 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 : 0 : mutex_unlock(&media_device_lock); 133 : 0 : kref_put(&mdi->refcount, media_device_instance_release); 134 : 0 : } 135 : : EXPORT_SYMBOL_GPL(media_device_delete);