Message ID | e474dd16f1d6443c12b1361376193c9d0efcced6.1541109584.git.shuah@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Media Device Allocator API | expand |
On Thu 2018-11-01 18:31:30, shuah@kernel.org wrote: > From: Shuah Khan <shuah@kernel.org> > > Media Device Allocator API to allows multiple drivers share a media device. > Using this API, drivers can allocate a media device with the shared struct > device as the key. Once the media device is allocated by a driver, other > drivers can get a reference to it. The media device is released when all > the references are released. Sounds like a ... bad idea? That's what new "media control" framework is for, no? Why do you need this? Pavel
On 11/02/2018 01:31 AM, shuah@kernel.org wrote: > From: Shuah Khan <shuah@kernel.org> > > Media Device Allocator API to allows multiple drivers share a media device. > Using this API, drivers can allocate a media device with the shared struct > device as the key. Once the media device is allocated by a driver, other > drivers can get a reference to it. The media device is released when all > the references are released. > > Signed-off-by: Shuah Khan <shuah@kernel.org> > --- > Documentation/media/kapi/mc-core.rst | 37 ++++++++ > drivers/media/Makefile | 3 +- > drivers/media/media-dev-allocator.c | 132 +++++++++++++++++++++++++++ > include/media/media-dev-allocator.h | 53 +++++++++++ > 4 files changed, 224 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/media-dev-allocator.c > create mode 100644 include/media/media-dev-allocator.h > > diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst > index 0c05503eaf1f..d6f409598065 100644 > --- a/Documentation/media/kapi/mc-core.rst > +++ b/Documentation/media/kapi/mc-core.rst > @@ -257,8 +257,45 @@ Subsystems should facilitate link validation by providing subsystem specific > helper functions to provide easy access for commonly needed information, and > in the end provide a way to use driver-specific callbacks. > > +Media Controller Device Allocator API > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > + > +When media device belongs to more than one driver, the shared media device When -> When the > +is allocated with the shared struct device as the key for look ups. > + > +Shared media device should stay in registered state until the last driver Shared -> The shared > +unregisters it. In addition, media device should be released when all the media -> the media > +references are released. Each driver gets a reference to the media device > +during probe, when it allocates the media device. If media device is already > +allocated, allocate API bumps up the refcount and return the existing media allocate -> the allocate return -> returns > +device. Driver puts the reference back from its disconnect routine when it Driver -> The driver from -> in > +calls :c:func:`media_device_delete()`. > + > +Media device is unregistered and cleaned up from the kref put handler to Media -> The media from -> in > +ensure that the media device stays in registered state until the last driver > +unregisters the media device. What happens if an application still has the media device open and you forcibly remove the last driver? I think it should be OK since the media_devnode struct isn't freed until the last open filehandle closes. But it is good to test this. > + > +**Driver Usage** > + > +Drivers should use the media-core routines to get register reference and 'get register reference'? Not sure what you meant to say. > +call :c:func:`media_device_delete()` routine to make sure the shared media > +device delete is handled correctly. > + > +**driver probe:** > +Call :c:func:`media_device_usb_allocate()` to allocate or get a reference > +Call :c:func:`media_device_register()`, if media devnode isn't registered > + > +**driver disconnect:** > +Call :c:func:`media_device_delete()` to free the media_device. Free'ing is Free'ing -> Freeing > +handled by the kref put handler. > + > +API Definitions > +^^^^^^^^^^^^^^^ > + > .. kernel-doc:: include/media/media-device.h > > .. kernel-doc:: include/media/media-devnode.h > > .. kernel-doc:: include/media/media-entity.h > + > +.. kernel-doc:: include/media/media-dev-allocator.h > diff --git a/drivers/media/Makefile b/drivers/media/Makefile > index 594b462ddf0e..8608f0a41dca 100644 > --- a/drivers/media/Makefile > +++ b/drivers/media/Makefile > @@ -3,7 +3,8 @@ > # Makefile for the kernel multimedia device drivers. > # > > -media-objs := media-device.o media-devnode.o media-entity.o > +media-objs := media-device.o media-devnode.o media-entity.o \ > + media-dev-allocator.o Perhaps only add media-dev-allocator if CONFIG_USB is enabled? > > # > # I2C drivers should come before other drivers, otherwise they'll fail > diff --git a/drivers/media/media-dev-allocator.c b/drivers/media/media-dev-allocator.c > new file mode 100644 > index 000000000000..262d1293dc13 > --- /dev/null > +++ b/drivers/media/media-dev-allocator.c > @@ -0,0 +1,132 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * media-dev-allocator.c - Media Controller Device Allocator API > + * > + * Copyright (c) 2018 Shuah Khan <shuah@kernel.org> > + * > + * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> > + */ > + > +/* > + * 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> > + > +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: mdev=%p\n", __func__, &mdi->mdev); > + > + mutex_lock(&media_device_lock); Can't the lock be moved down to just before list_del? Or am I missing something? > + > + 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, > + char *module_name) const char * > +{ > + 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 (find_module(module_name) != mdi->owner && > + !try_module_get(mdi->owner)) > + dev_err(dev, "%s: try_module_get() error\n", __func__); > + dev_dbg(dev, "%s: get mdev=%p module_name %s\n", > + __func__, &mdi->mdev, module_name); > + return &mdi->mdev; > + } > + > + mdi = kzalloc(sizeof(*mdi), GFP_KERNEL); > + if (!mdi) > + return NULL; > + > + mdi->owner = find_module(module_name); > + kref_init(&mdi->refcount); > + list_add_tail(&mdi->list, &media_device_list); > + > + dev_dbg(dev, "%s: alloc mdev=%p module_name %s\n", __func__, > + &mdi->mdev, module_name); > + return &mdi->mdev; > +} > + > +#if IS_ENABLED(CONFIG_USB) > +struct media_device *media_device_usb_allocate(struct usb_device *udev, > + char *module_name) const char * > +{ > + struct media_device *mdev; > + > + mutex_lock(&media_device_lock); > + mdev = __media_device_get(&udev->dev, module_name); > + 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); > +#endif > + > +void media_device_delete(struct media_device *mdev, char *module_name) > +{ > + struct media_device_instance *mdi = to_media_device_instance(mdev); > + > + dev_dbg(mdi->mdev.dev, "%s: mdev=%p module_name %s\n", > + __func__, &mdi->mdev, module_name); > + > + mutex_lock(&media_device_lock); > + /* put module reference if media_device owner is not THIS_MODULE */ > + if (mdi->owner != find_module(module_name)) { > + module_put(mdi->owner); > + dev_dbg(mdi->mdev.dev, > + "%s decremented owner module reference\n", __func__); > + } > + mutex_unlock(&media_device_lock); > + kref_put(&mdi->refcount, media_device_instance_release); > +} > +EXPORT_SYMBOL_GPL(media_device_delete); > diff --git a/include/media/media-dev-allocator.h b/include/media/media-dev-allocator.h > new file mode 100644 > index 000000000000..8fbadff2cef8 > --- /dev/null > +++ b/include/media/media-dev-allocator.h > @@ -0,0 +1,53 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * media-dev-allocator.h - Media Controller Device Allocator API > + * > + * Copyright (c) 2018 Shuah Khan <shuah@kernel.org> > + * > + * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> > + */ > + > +/* > + * This file adds a global ref-counted 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. > + */ > + > +#ifndef _MEDIA_DEV_ALLOCTOR_H > +#define _MEDIA_DEV_ALLOCTOR_H > + > +struct usb_device; > + > +#ifdef CONFIG_MEDIA_CONTROLLER Perhaps change this to: #if defined(CONFIG_MEDIA_CONTROLLER) && defined(CONFIG_USB) Since for now this only makes sense for USB devices. > +/** > + * media_device_usb_allocate() - Allocate and return struct &media device > + * > + * @udev: struct &usb_device pointer > + * @module_name: should be filled with %KBUILD_MODNAME > + * > + * This interface should be called to allocate a Media Device when multiple > + * drivers share usb_device and the media device. This interface allocates > + * &media_device structure and calls media_device_usb_init() to initialize > + * it. > + * > + */ > +struct media_device *media_device_usb_allocate(struct usb_device *udev, > + char *module_name); > +/** > + * media_device_delete() - Release media device. Calls kref_put(). > + * > + * @mdev: struct &media_device pointer > + * @module_name: should be filled with %KBUILD_MODNAME > + * > + * This interface should be called to put Media Device Instance kref. > + */ > +void media_device_delete(struct media_device *mdev, char *module_name); > +#else > +static inline struct media_device *media_device_usb_allocate( > + struct usb_device *udev, char *module_name) > + { return NULL; } > +static inline void media_device_delete( > + struct media_device *mdev, char *module_name) { } > +#endif /* CONFIG_MEDIA_CONTROLLER */ > +#endif > Regards, Hans
Hi Hans, On 11/20/18 4:20 AM, Hans Verkuil wrote: > On 11/02/2018 01:31 AM, shuah@kernel.org wrote: >> From: Shuah Khan <shuah@kernel.org> >> >> Media Device Allocator API to allows multiple drivers share a media device. >> Using this API, drivers can allocate a media device with the shared struct >> device as the key. Once the media device is allocated by a driver, other >> drivers can get a reference to it. The media device is released when all >> the references are released. >> >> Signed-off-by: Shuah Khan <shuah@kernel.org> Thanks for catching the documentation corrections. Fixed them all and working on the next revision of the patch. >> --- >> Documentation/media/kapi/mc-core.rst | 37 ++++++++ >> drivers/media/Makefile | 3 +- >> drivers/media/media-dev-allocator.c | 132 +++++++++++++++++++++++++++ >> include/media/media-dev-allocator.h | 53 +++++++++++ >> 4 files changed, 224 insertions(+), 1 deletion(-) >> create mode 100644 drivers/media/media-dev-allocator.c >> create mode 100644 include/media/media-dev-allocator.h >> >> diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst >> index 0c05503eaf1f..d6f409598065 100644 >> --- a/Documentation/media/kapi/mc-core.rst >> +++ b/Documentation/media/kapi/mc-core.rst >> @@ -257,8 +257,45 @@ Subsystems should facilitate link validation by providing subsystem specific >> helper functions to provide easy access for commonly needed information, and >> in the end provide a way to use driver-specific callbacks. >> >> +Media Controller Device Allocator API >> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> + >> +When media device belongs to more than one driver, the shared media device > > When -> When the > >> +is allocated with the shared struct device as the key for look ups. >> + >> +Shared media device should stay in registered state until the last driver > > Shared -> The shared > >> +unregisters it. In addition, media device should be released when all the > > media -> the media > >> +references are released. Each driver gets a reference to the media device >> +during probe, when it allocates the media device. If media device is already >> +allocated, allocate API bumps up the refcount and return the existing media > > allocate -> the allocate > return -> returns > >> +device. Driver puts the reference back from its disconnect routine when it > > Driver -> The driver > from -> in > >> +calls :c:func:`media_device_delete()`. >> + >> +Media device is unregistered and cleaned up from the kref put handler to > > Media -> The media > from -> in > >> +ensure that the media device stays in registered state until the last driver >> +unregisters the media device. > > What happens if an application still has the media device open and you forcibly > remove the last driver? I think it should be OK since the media_devnode struct > isn't freed until the last open filehandle closes. But it is good to test this. > >> + >> +**Driver Usage** >> + >> +Drivers should use the media-core routines to get register reference and > > 'get register reference'? Not sure what you meant to say. > >> +call :c:func:`media_device_delete()` routine to make sure the shared media >> +device delete is handled correctly. >> + >> +**driver probe:** >> +Call :c:func:`media_device_usb_allocate()` to allocate or get a reference >> +Call :c:func:`media_device_register()`, if media devnode isn't registered >> + >> +**driver disconnect:** >> +Call :c:func:`media_device_delete()` to free the media_device. Free'ing is > > Free'ing -> Freeing > >> +handled by the kref put handler. >> + >> +API Definitions >> +^^^^^^^^^^^^^^^ >> + >> .. kernel-doc:: include/media/media-device.h >> >> .. kernel-doc:: include/media/media-devnode.h >> >> .. kernel-doc:: include/media/media-entity.h >> + >> +.. kernel-doc:: include/media/media-dev-allocator.h >> diff --git a/drivers/media/Makefile b/drivers/media/Makefile >> index 594b462ddf0e..8608f0a41dca 100644 >> --- a/drivers/media/Makefile >> +++ b/drivers/media/Makefile >> @@ -3,7 +3,8 @@ >> # Makefile for the kernel multimedia device drivers. >> # >> >> -media-objs := media-device.o media-devnode.o media-entity.o >> +media-objs := media-device.o media-devnode.o media-entity.o \ >> + media-dev-allocator.o > > Perhaps only add media-dev-allocator if CONFIG_USB is enabled? > >> >> # >> # I2C drivers should come before other drivers, otherwise they'll fail >> diff --git a/drivers/media/media-dev-allocator.c b/drivers/media/media-dev-allocator.c >> new file mode 100644 >> index 000000000000..262d1293dc13 >> --- /dev/null >> +++ b/drivers/media/media-dev-allocator.c >> @@ -0,0 +1,132 @@ >> +// SPDX-License-Identifier: GPL-2.0-or-later >> +/* >> + * media-dev-allocator.c - Media Controller Device Allocator API >> + * >> + * Copyright (c) 2018 Shuah Khan <shuah@kernel.org> >> + * >> + * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> >> + */ >> + >> +/* >> + * 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> >> + >> +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: mdev=%p\n", __func__, &mdi->mdev); >> + >> + mutex_lock(&media_device_lock); > > Can't the lock be moved down to just before list_del? Or am I missing something? > The lock needs to be held while media_device_unregister() and media_device_cleanup() to avoid races in the unregister path if two drivers call media_device_delete(). >> + >> + 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, >> + char *module_name) > > const char * Thanks fixed all of these. I am running final tests and will send the next version this week. thanks, -- Shuah
On 11/19/18 1:59 AM, Pavel Machek wrote: > On Thu 2018-11-01 18:31:30, shuah@kernel.org wrote: >> From: Shuah Khan <shuah@kernel.org> >> >> Media Device Allocator API to allows multiple drivers share a media device. >> Using this API, drivers can allocate a media device with the shared struct >> device as the key. Once the media device is allocated by a driver, other >> drivers can get a reference to it. The media device is released when all >> the references are released. > > Sounds like a ... bad idea? > > That's what new "media control" framework is for, no? > > Why do you need this? > Pavel > Media control framework doesn't address this problem of ownership of the media device when non-media drivers have to own the pipeline. In this case, snd-usb owns the audio pipeline when an audio application is using the device. Without this work, media drivers won't be able to tell if snd-usb is using the tuner and owns the media pipeline. I am going to clarify this in the commit log. thanks, -- Shuah
On Thu 2018-12-06 08:33:14, shuah wrote: > On 11/19/18 1:59 AM, Pavel Machek wrote: > >On Thu 2018-11-01 18:31:30, shuah@kernel.org wrote: > >>From: Shuah Khan <shuah@kernel.org> > >> > >>Media Device Allocator API to allows multiple drivers share a media device. > >>Using this API, drivers can allocate a media device with the shared struct > >>device as the key. Once the media device is allocated by a driver, other > >>drivers can get a reference to it. The media device is released when all > >>the references are released. > > > >Sounds like a ... bad idea? > > > >That's what new "media control" framework is for, no? > > > >Why do you need this? > > Media control framework doesn't address this problem of ownership of the > media device when non-media drivers have to own the pipeline. In this case, > snd-usb owns the audio pipeline when an audio application is using the > device. Without this work, media drivers won't be able to tell if snd-usb is > using the tuner and owns the media pipeline. > > I am going to clarify this in the commit log. I guess I'll need the explanation, yes. How can usb soundcard use the tuner? I thought we'd always have userspace component active and moving data between tuner and usb sound card? Pavel
Em Sun, 9 Dec 2018 09:09:44 +0100 Pavel Machek <pavel@ucw.cz> escreveu: > On Thu 2018-12-06 08:33:14, shuah wrote: > > On 11/19/18 1:59 AM, Pavel Machek wrote: > > >On Thu 2018-11-01 18:31:30, shuah@kernel.org wrote: > > >>From: Shuah Khan <shuah@kernel.org> > > >> > > >>Media Device Allocator API to allows multiple drivers share a media device. > > >>Using this API, drivers can allocate a media device with the shared struct > > >>device as the key. Once the media device is allocated by a driver, other > > >>drivers can get a reference to it. The media device is released when all > > >>the references are released. > > > > > >Sounds like a ... bad idea? > > > > > >That's what new "media control" framework is for, no? > > > > > >Why do you need this? > > > > Media control framework doesn't address this problem of ownership of the > > media device when non-media drivers have to own the pipeline. In this case, > > snd-usb owns the audio pipeline when an audio application is using the > > device. Without this work, media drivers won't be able to tell if snd-usb is > > using the tuner and owns the media pipeline. > > > > I am going to clarify this in the commit log. > > I guess I'll need the explanation, yes. > > How can usb soundcard use the tuner? I thought we'd always have > userspace component active and moving data between tuner and usb sound > card? It sounds that the description of the patch is not 100%, as it seems that you're not seeing the hole picture. This is designed to solve a very common usecase for media devices where one physical device (an USB stick) provides both audio and video. That's, for example, the case of cameras with microphones and TV USB devices. Those usually expose the audio via standard USB Audio Class, and video either via USB Video Class or via some proprietary vendor class. Due to the way USB Audio Class is handled, it means that two independent drivers will provide the pipelines for a single physical USB bridge. The same problem also applies to more sophisticated embedded devices, like on SOCs designed to be used on TVs and Set Top Boxes, where the hardware pipeline has both audio and video components on it, logically mapped into different drivers (using Linux DTV API, V4L2 API and ALSA). On such kind of devices, it is important to have a way to see and control the entire audio and video pipeline present on them through a single media controller device, specially if one wants to provide a hardware pipeline within the SoC that won't be copying data between Kernel-userspace. Now, if the audio is implemented on a separate device (like an Intel HDA compatible chipset at the motherboard), it should be exposed as a separate media controller. So, for example, a system that has both an USB audio/video stick and an Intel HDA-compatible chipset, both exposed via the media controller, will have two media controller devices, one for each physically independent device. On the other hand, an SoC designed for TV products will likely expose a single media controller, even if each part of the pipeline is exposed via independent Linux device drivers. Thanks, Mauro
Hi! > > On Thu 2018-12-06 08:33:14, shuah wrote: > > > On 11/19/18 1:59 AM, Pavel Machek wrote: > > > >On Thu 2018-11-01 18:31:30, shuah@kernel.org wrote: > > > >>From: Shuah Khan <shuah@kernel.org> > > > >> > > > >>Media Device Allocator API to allows multiple drivers share a media device. > > > >>Using this API, drivers can allocate a media device with the shared struct > > > >>device as the key. Once the media device is allocated by a driver, other > > > >>drivers can get a reference to it. The media device is released when all > > > >>the references are released. > > > > > > > >Sounds like a ... bad idea? > > > > > > > >That's what new "media control" framework is for, no? > > > > > > > >Why do you need this? > > > > > > Media control framework doesn't address this problem of ownership of the > > > media device when non-media drivers have to own the pipeline. In this case, > > > snd-usb owns the audio pipeline when an audio application is using the > > > device. Without this work, media drivers won't be able to tell if snd-usb is > > > using the tuner and owns the media pipeline. > > > > > > I am going to clarify this in the commit log. > > > > I guess I'll need the explanation, yes. > > > > How can usb soundcard use the tuner? I thought we'd always have > > userspace component active and moving data between tuner and usb sound > > card? > > It sounds that the description of the patch is not 100%, as it seems > that you're not seeing the hole picture. > > This is designed to solve a very common usecase for media devices > where one physical device (an USB stick) provides both audio > and video. Aha, ok, it makes sense now. Thanks! Pavel
diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst index 0c05503eaf1f..d6f409598065 100644 --- a/Documentation/media/kapi/mc-core.rst +++ b/Documentation/media/kapi/mc-core.rst @@ -257,8 +257,45 @@ Subsystems should facilitate link validation by providing subsystem specific helper functions to provide easy access for commonly needed information, and in the end provide a way to use driver-specific callbacks. +Media Controller Device Allocator API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When media device belongs to more than one driver, the shared media device +is allocated with the shared struct device as the key for look ups. + +Shared media device should stay in registered state until the last driver +unregisters it. In addition, media device should be released when all the +references are released. Each driver gets a reference to the media device +during probe, when it allocates the media device. If media device is already +allocated, allocate API bumps up the refcount and return the existing media +device. Driver puts the reference back from its disconnect routine when it +calls :c:func:`media_device_delete()`. + +Media device is unregistered and cleaned up from the kref put handler to +ensure that the media device stays in registered state until the last driver +unregisters the media device. + +**Driver Usage** + +Drivers should use the media-core routines to get register reference and +call :c:func:`media_device_delete()` routine to make sure the shared media +device delete is handled correctly. + +**driver probe:** +Call :c:func:`media_device_usb_allocate()` to allocate or get a reference +Call :c:func:`media_device_register()`, if media devnode isn't registered + +**driver disconnect:** +Call :c:func:`media_device_delete()` to free the media_device. Free'ing is +handled by the kref put handler. + +API Definitions +^^^^^^^^^^^^^^^ + .. kernel-doc:: include/media/media-device.h .. kernel-doc:: include/media/media-devnode.h .. kernel-doc:: include/media/media-entity.h + +.. kernel-doc:: include/media/media-dev-allocator.h diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 594b462ddf0e..8608f0a41dca 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -3,7 +3,8 @@ # Makefile for the kernel multimedia device drivers. # -media-objs := media-device.o media-devnode.o media-entity.o +media-objs := media-device.o media-devnode.o media-entity.o \ + media-dev-allocator.o # # I2C drivers should come before other drivers, otherwise they'll fail diff --git a/drivers/media/media-dev-allocator.c b/drivers/media/media-dev-allocator.c new file mode 100644 index 000000000000..262d1293dc13 --- /dev/null +++ b/drivers/media/media-dev-allocator.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * media-dev-allocator.c - Media Controller Device Allocator API + * + * Copyright (c) 2018 Shuah Khan <shuah@kernel.org> + * + * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +/* + * 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> + +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: mdev=%p\n", __func__, &mdi->mdev); + + 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, + char *module_name) +{ + 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 (find_module(module_name) != mdi->owner && + !try_module_get(mdi->owner)) + dev_err(dev, "%s: try_module_get() error\n", __func__); + dev_dbg(dev, "%s: get mdev=%p module_name %s\n", + __func__, &mdi->mdev, module_name); + return &mdi->mdev; + } + + mdi = kzalloc(sizeof(*mdi), GFP_KERNEL); + if (!mdi) + return NULL; + + mdi->owner = find_module(module_name); + kref_init(&mdi->refcount); + list_add_tail(&mdi->list, &media_device_list); + + dev_dbg(dev, "%s: alloc mdev=%p module_name %s\n", __func__, + &mdi->mdev, module_name); + return &mdi->mdev; +} + +#if IS_ENABLED(CONFIG_USB) +struct media_device *media_device_usb_allocate(struct usb_device *udev, + char *module_name) +{ + struct media_device *mdev; + + mutex_lock(&media_device_lock); + mdev = __media_device_get(&udev->dev, module_name); + 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); +#endif + +void media_device_delete(struct media_device *mdev, char *module_name) +{ + struct media_device_instance *mdi = to_media_device_instance(mdev); + + dev_dbg(mdi->mdev.dev, "%s: mdev=%p module_name %s\n", + __func__, &mdi->mdev, module_name); + + mutex_lock(&media_device_lock); + /* put module reference if media_device owner is not THIS_MODULE */ + if (mdi->owner != find_module(module_name)) { + module_put(mdi->owner); + dev_dbg(mdi->mdev.dev, + "%s decremented owner module reference\n", __func__); + } + mutex_unlock(&media_device_lock); + kref_put(&mdi->refcount, media_device_instance_release); +} +EXPORT_SYMBOL_GPL(media_device_delete); diff --git a/include/media/media-dev-allocator.h b/include/media/media-dev-allocator.h new file mode 100644 index 000000000000..8fbadff2cef8 --- /dev/null +++ b/include/media/media-dev-allocator.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * media-dev-allocator.h - Media Controller Device Allocator API + * + * Copyright (c) 2018 Shuah Khan <shuah@kernel.org> + * + * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +/* + * This file adds a global ref-counted 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. + */ + +#ifndef _MEDIA_DEV_ALLOCTOR_H +#define _MEDIA_DEV_ALLOCTOR_H + +struct usb_device; + +#ifdef CONFIG_MEDIA_CONTROLLER +/** + * media_device_usb_allocate() - Allocate and return struct &media device + * + * @udev: struct &usb_device pointer + * @module_name: should be filled with %KBUILD_MODNAME + * + * This interface should be called to allocate a Media Device when multiple + * drivers share usb_device and the media device. This interface allocates + * &media_device structure and calls media_device_usb_init() to initialize + * it. + * + */ +struct media_device *media_device_usb_allocate(struct usb_device *udev, + char *module_name); +/** + * media_device_delete() - Release media device. Calls kref_put(). + * + * @mdev: struct &media_device pointer + * @module_name: should be filled with %KBUILD_MODNAME + * + * This interface should be called to put Media Device Instance kref. + */ +void media_device_delete(struct media_device *mdev, char *module_name); +#else +static inline struct media_device *media_device_usb_allocate( + struct usb_device *udev, char *module_name) + { return NULL; } +static inline void media_device_delete( + struct media_device *mdev, char *module_name) { } +#endif /* CONFIG_MEDIA_CONTROLLER */ +#endif