brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · ae17887 Raw
136 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * media-dev-allocator.c - Media Controller Device Allocator API4 *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 device13 * includes a kref count. The last put on the media device releases the media14 * 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		else79			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