forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource. 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 <[email protected]> Signed-off-by: Hans Verkuil <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
- Loading branch information
Showing
4 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* media-dev-allocator.c - Media Controller Device Allocator API | ||
* | ||
* Copyright (c) 2019 Shuah Khan <[email protected]> | ||
* | ||
* Credits: Suggested by Laurent Pinchart <[email protected]> | ||
*/ | ||
|
||
/* | ||
* 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> | ||
#include <media/media-dev-allocator.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: releasing Media Device\n", __func__); | ||
|
||
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, | ||
const char *module_name, | ||
struct module *owner) | ||
{ | ||
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 (owner != mdi->owner && !try_module_get(mdi->owner)) | ||
dev_err(dev, | ||
"%s: module %s get owner reference error\n", | ||
__func__, module_name); | ||
else | ||
dev_dbg(dev, "%s: module %s got owner reference\n", | ||
__func__, module_name); | ||
return &mdi->mdev; | ||
} | ||
|
||
mdi = kzalloc(sizeof(*mdi), GFP_KERNEL); | ||
if (!mdi) | ||
return NULL; | ||
|
||
mdi->owner = owner; | ||
kref_init(&mdi->refcount); | ||
list_add_tail(&mdi->list, &media_device_list); | ||
|
||
dev_dbg(dev, "%s: Allocated media device for owner %s\n", | ||
__func__, module_name); | ||
return &mdi->mdev; | ||
} | ||
|
||
struct media_device *media_device_usb_allocate(struct usb_device *udev, | ||
const char *module_name, | ||
struct module *owner) | ||
{ | ||
struct media_device *mdev; | ||
|
||
mutex_lock(&media_device_lock); | ||
mdev = __media_device_get(&udev->dev, module_name, owner); | ||
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); | ||
|
||
void media_device_delete(struct media_device *mdev, const char *module_name, | ||
struct module *owner) | ||
{ | ||
struct media_device_instance *mdi = to_media_device_instance(mdev); | ||
|
||
mutex_lock(&media_device_lock); | ||
/* put module reference for the media_device owner */ | ||
if (mdi->owner != owner) { | ||
module_put(mdi->owner); | ||
dev_dbg(mdi->mdev.dev, | ||
"%s: module %s put owner module reference\n", | ||
__func__, module_name); | ||
} | ||
mutex_unlock(&media_device_lock); | ||
kref_put(&mdi->refcount, media_device_instance_release); | ||
} | ||
EXPORT_SYMBOL_GPL(media_device_delete); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* SPDX-License-Identifier: GPL-2.0+ */ | ||
/* | ||
* media-dev-allocator.h - Media Controller Device Allocator API | ||
* | ||
* Copyright (c) 2019 Shuah Khan <[email protected]> | ||
* | ||
* Credits: Suggested by Laurent Pinchart <[email protected]> | ||
*/ | ||
|
||
/* | ||
* 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_ALLOCATOR_H | ||
#define _MEDIA_DEV_ALLOCATOR_H | ||
|
||
struct usb_device; | ||
|
||
#if defined(CONFIG_MEDIA_CONTROLLER) && defined(CONFIG_USB) | ||
/** | ||
* media_device_usb_allocate() - Allocate and return struct &media device | ||
* | ||
* @udev: struct &usb_device pointer | ||
* @module_name: should be filled with %KBUILD_MODNAME | ||
* @owner: struct module pointer %THIS_MODULE for the driver. | ||
* %THIS_MODULE is null for a built-in driver. | ||
* It is safe even when %THIS_MODULE is null. | ||
* | ||
* 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, | ||
const char *module_name, | ||
struct module *owner); | ||
/** | ||
* media_device_delete() - Release media device. Calls kref_put(). | ||
* | ||
* @mdev: struct &media_device pointer | ||
* @module_name: should be filled with %KBUILD_MODNAME | ||
* @owner: struct module pointer %THIS_MODULE for the driver. | ||
* %THIS_MODULE is null for a built-in driver. | ||
* It is safe even when %THIS_MODULE is null. | ||
* | ||
* This interface should be called to put Media Device Instance kref. | ||
*/ | ||
void media_device_delete(struct media_device *mdev, const char *module_name, | ||
struct module *owner); | ||
#else | ||
static inline struct media_device *media_device_usb_allocate( | ||
struct usb_device *udev, const char *module_name, | ||
struct module *owner) | ||
{ return NULL; } | ||
static inline void media_device_delete( | ||
struct media_device *mdev, const char *module_name, | ||
struct module *owner) { } | ||
#endif /* CONFIG_MEDIA_CONTROLLER && CONFIG_USB */ | ||
#endif /* _MEDIA_DEV_ALLOCATOR_H */ |