counter-core.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Generic Counter interface
  4. * Copyright (C) 2020 William Breathitt Gray
  5. */
  6. #include <linux/cdev.h>
  7. #include <linux/counter.h>
  8. #include <linux/device.h>
  9. #include <linux/device/bus.h>
  10. #include <linux/export.h>
  11. #include <linux/fs.h>
  12. #include <linux/gfp.h>
  13. #include <linux/idr.h>
  14. #include <linux/init.h>
  15. #include <linux/kdev_t.h>
  16. #include <linux/module.h>
  17. #include <linux/mutex.h>
  18. #include <linux/slab.h>
  19. #include <linux/types.h>
  20. #include <linux/wait.h>
  21. #include "counter-chrdev.h"
  22. #include "counter-sysfs.h"
  23. #define COUNTER_NAME "counter"
  24. /* Provides a unique ID for each counter device */
  25. static DEFINE_IDA(counter_ida);
  26. struct counter_device_allochelper {
  27. struct counter_device counter;
  28. /*
  29. * This ensures private data behaves like if it were kmalloced
  30. * separately. Also ensures the minimum alignment for safe DMA
  31. * operations (which may or may not mean cache alignment).
  32. */
  33. unsigned long privdata[] __aligned(ARCH_DMA_MINALIGN);
  34. };
  35. static void counter_device_release(struct device *dev)
  36. {
  37. struct counter_device *const counter =
  38. container_of(dev, struct counter_device, dev);
  39. counter_chrdev_remove(counter);
  40. ida_free(&counter_ida, dev->id);
  41. kfree(container_of(counter, struct counter_device_allochelper, counter));
  42. }
  43. static const struct device_type counter_device_type = {
  44. .name = "counter_device",
  45. .release = counter_device_release,
  46. };
  47. static const struct bus_type counter_bus_type = {
  48. .name = "counter",
  49. .dev_name = "counter",
  50. };
  51. static dev_t counter_devt;
  52. /**
  53. * counter_priv - access counter device private data
  54. * @counter: counter device
  55. *
  56. * Get the counter device private data
  57. */
  58. void *counter_priv(const struct counter_device *const counter)
  59. {
  60. struct counter_device_allochelper *ch =
  61. container_of(counter, struct counter_device_allochelper, counter);
  62. return &ch->privdata;
  63. }
  64. EXPORT_SYMBOL_NS_GPL(counter_priv, COUNTER);
  65. /**
  66. * counter_alloc - allocate a counter_device
  67. * @sizeof_priv: size of the driver private data
  68. *
  69. * This is part one of counter registration. The structure is allocated
  70. * dynamically to ensure the right lifetime for the embedded struct device.
  71. *
  72. * If this succeeds, call counter_put() to get rid of the counter_device again.
  73. */
  74. struct counter_device *counter_alloc(size_t sizeof_priv)
  75. {
  76. struct counter_device_allochelper *ch;
  77. struct counter_device *counter;
  78. struct device *dev;
  79. int err;
  80. ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL);
  81. if (!ch)
  82. return NULL;
  83. counter = &ch->counter;
  84. dev = &counter->dev;
  85. /* Acquire unique ID */
  86. err = ida_alloc(&counter_ida, GFP_KERNEL);
  87. if (err < 0)
  88. goto err_ida_alloc;
  89. dev->id = err;
  90. mutex_init(&counter->ops_exist_lock);
  91. dev->type = &counter_device_type;
  92. dev->bus = &counter_bus_type;
  93. dev->devt = MKDEV(MAJOR(counter_devt), dev->id);
  94. err = counter_chrdev_add(counter);
  95. if (err < 0)
  96. goto err_chrdev_add;
  97. device_initialize(dev);
  98. err = dev_set_name(dev, COUNTER_NAME "%d", dev->id);
  99. if (err)
  100. goto err_dev_set_name;
  101. return counter;
  102. err_dev_set_name:
  103. counter_chrdev_remove(counter);
  104. err_chrdev_add:
  105. ida_free(&counter_ida, dev->id);
  106. err_ida_alloc:
  107. kfree(ch);
  108. return NULL;
  109. }
  110. EXPORT_SYMBOL_NS_GPL(counter_alloc, COUNTER);
  111. void counter_put(struct counter_device *counter)
  112. {
  113. put_device(&counter->dev);
  114. }
  115. EXPORT_SYMBOL_NS_GPL(counter_put, COUNTER);
  116. /**
  117. * counter_add - complete registration of a counter
  118. * @counter: the counter to add
  119. *
  120. * This is part two of counter registration.
  121. *
  122. * If this succeeds, call counter_unregister() to get rid of the counter_device again.
  123. */
  124. int counter_add(struct counter_device *counter)
  125. {
  126. int err;
  127. struct device *dev = &counter->dev;
  128. if (counter->parent) {
  129. dev->parent = counter->parent;
  130. dev->of_node = counter->parent->of_node;
  131. }
  132. err = counter_sysfs_add(counter);
  133. if (err < 0)
  134. return err;
  135. /* implies device_add(dev) */
  136. return cdev_device_add(&counter->chrdev, dev);
  137. }
  138. EXPORT_SYMBOL_NS_GPL(counter_add, COUNTER);
  139. /**
  140. * counter_unregister - unregister Counter from the system
  141. * @counter: pointer to Counter to unregister
  142. *
  143. * The Counter is unregistered from the system.
  144. */
  145. void counter_unregister(struct counter_device *const counter)
  146. {
  147. if (!counter)
  148. return;
  149. cdev_device_del(&counter->chrdev, &counter->dev);
  150. mutex_lock(&counter->ops_exist_lock);
  151. counter->ops = NULL;
  152. wake_up(&counter->events_wait);
  153. mutex_unlock(&counter->ops_exist_lock);
  154. }
  155. EXPORT_SYMBOL_NS_GPL(counter_unregister, COUNTER);
  156. static void devm_counter_release(void *counter)
  157. {
  158. counter_unregister(counter);
  159. }
  160. static void devm_counter_put(void *counter)
  161. {
  162. counter_put(counter);
  163. }
  164. /**
  165. * devm_counter_alloc - allocate a counter_device
  166. * @dev: the device to register the release callback for
  167. * @sizeof_priv: size of the driver private data
  168. *
  169. * This is the device managed version of counter_add(). It registers a cleanup
  170. * callback to care for calling counter_put().
  171. */
  172. struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv)
  173. {
  174. struct counter_device *counter;
  175. int err;
  176. counter = counter_alloc(sizeof_priv);
  177. if (!counter)
  178. return NULL;
  179. err = devm_add_action_or_reset(dev, devm_counter_put, counter);
  180. if (err < 0)
  181. return NULL;
  182. return counter;
  183. }
  184. EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, COUNTER);
  185. /**
  186. * devm_counter_add - complete registration of a counter
  187. * @dev: the device to register the release callback for
  188. * @counter: the counter to add
  189. *
  190. * This is the device managed version of counter_add(). It registers a cleanup
  191. * callback to care for calling counter_unregister().
  192. */
  193. int devm_counter_add(struct device *dev,
  194. struct counter_device *const counter)
  195. {
  196. int err;
  197. err = counter_add(counter);
  198. if (err < 0)
  199. return err;
  200. return devm_add_action_or_reset(dev, devm_counter_release, counter);
  201. }
  202. EXPORT_SYMBOL_NS_GPL(devm_counter_add, COUNTER);
  203. #define COUNTER_DEV_MAX 256
  204. static int __init counter_init(void)
  205. {
  206. int err;
  207. err = bus_register(&counter_bus_type);
  208. if (err < 0)
  209. return err;
  210. err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX,
  211. COUNTER_NAME);
  212. if (err < 0)
  213. goto err_unregister_bus;
  214. return 0;
  215. err_unregister_bus:
  216. bus_unregister(&counter_bus_type);
  217. return err;
  218. }
  219. static void __exit counter_exit(void)
  220. {
  221. unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
  222. bus_unregister(&counter_bus_type);
  223. }
  224. subsys_initcall(counter_init);
  225. module_exit(counter_exit);
  226. MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
  227. MODULE_DESCRIPTION("Generic Counter interface");
  228. MODULE_LICENSE("GPL v2");