counter-core.c 6.3 KB

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