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