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