mc-dev-allocator.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. * This file adds a global refcounted Media Controller Device Instance API.
  11. * A system wide global media device list is managed and each media device
  12. * includes a kref count. The last put on the media device releases the media
  13. * device instance.
  14. *
  15. */
  16. #include <linux/kref.h>
  17. #include <linux/module.h>
  18. #include <linux/slab.h>
  19. #include <linux/usb.h>
  20. #include <media/media-device.h>
  21. #include <media/media-dev-allocator.h>
  22. static LIST_HEAD(media_device_list);
  23. static DEFINE_MUTEX(media_device_lock);
  24. struct media_device_instance {
  25. struct media_device mdev;
  26. struct module *owner;
  27. struct list_head list;
  28. struct kref refcount;
  29. };
  30. static inline struct media_device_instance *
  31. to_media_device_instance(struct media_device *mdev)
  32. {
  33. return container_of(mdev, struct media_device_instance, mdev);
  34. }
  35. static void media_device_instance_release(struct kref *kref)
  36. {
  37. struct media_device_instance *mdi =
  38. container_of(kref, struct media_device_instance, refcount);
  39. dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__);
  40. mutex_lock(&media_device_lock);
  41. media_device_unregister(&mdi->mdev);
  42. media_device_cleanup(&mdi->mdev);
  43. list_del(&mdi->list);
  44. mutex_unlock(&media_device_lock);
  45. kfree(mdi);
  46. }
  47. /* Callers should hold media_device_lock when calling this function */
  48. static struct media_device *__media_device_get(struct device *dev,
  49. const char *module_name,
  50. struct module *owner)
  51. {
  52. struct media_device_instance *mdi;
  53. list_for_each_entry(mdi, &media_device_list, list) {
  54. if (mdi->mdev.dev != dev)
  55. continue;
  56. kref_get(&mdi->refcount);
  57. /* get module reference for the media_device owner */
  58. if (owner != mdi->owner && !try_module_get(mdi->owner))
  59. dev_err(dev,
  60. "%s: module %s get owner reference error\n",
  61. __func__, module_name);
  62. else
  63. dev_dbg(dev, "%s: module %s got owner reference\n",
  64. __func__, module_name);
  65. return &mdi->mdev;
  66. }
  67. mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
  68. if (!mdi)
  69. return NULL;
  70. mdi->owner = owner;
  71. kref_init(&mdi->refcount);
  72. list_add_tail(&mdi->list, &media_device_list);
  73. dev_dbg(dev, "%s: Allocated media device for owner %s\n",
  74. __func__, module_name);
  75. return &mdi->mdev;
  76. }
  77. struct media_device *media_device_usb_allocate(struct usb_device *udev,
  78. const char *module_name,
  79. struct module *owner)
  80. {
  81. struct media_device *mdev;
  82. mutex_lock(&media_device_lock);
  83. mdev = __media_device_get(&udev->dev, module_name, owner);
  84. if (!mdev) {
  85. mutex_unlock(&media_device_lock);
  86. return ERR_PTR(-ENOMEM);
  87. }
  88. /* check if media device is already initialized */
  89. if (!mdev->dev)
  90. __media_device_usb_init(mdev, udev, udev->product,
  91. module_name);
  92. mutex_unlock(&media_device_lock);
  93. return mdev;
  94. }
  95. EXPORT_SYMBOL_GPL(media_device_usb_allocate);
  96. void media_device_delete(struct media_device *mdev, const char *module_name,
  97. struct module *owner)
  98. {
  99. struct media_device_instance *mdi = to_media_device_instance(mdev);
  100. mutex_lock(&media_device_lock);
  101. /* put module reference for the media_device owner */
  102. if (mdi->owner != owner) {
  103. module_put(mdi->owner);
  104. dev_dbg(mdi->mdev.dev,
  105. "%s: module %s put owner module reference\n",
  106. __func__, module_name);
  107. }
  108. mutex_unlock(&media_device_lock);
  109. kref_put(&mdi->refcount, media_device_instance_release);
  110. }
  111. EXPORT_SYMBOL_GPL(media_device_delete);