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 const struct device_type counter_device_type = { 53 .name = "counter_device", 54 .release = counter_device_release, 55 }; 56 57 static const 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 put_device(dev); 128 return NULL; 129 err_chrdev_add: 130 131 ida_free(&counter_ida, dev->id); 132 err_ida_alloc: 133 134 kfree(ch); 135 136 return NULL; 137 } 138 EXPORT_SYMBOL_NS_GPL(counter_alloc, "COUNTER"); 139 140 void counter_put(struct counter_device *counter) 141 { 142 put_device(&counter->dev); 143 } 144 EXPORT_SYMBOL_NS_GPL(counter_put, "COUNTER"); 145 146 /** 147 * counter_add - complete registration of a counter 148 * @counter: the counter to add 149 * 150 * This is part two of counter registration. 151 * 152 * If this succeeds, call counter_unregister() to get rid of the counter_device again. 153 */ 154 int counter_add(struct counter_device *counter) 155 { 156 int err; 157 struct device *dev = &counter->dev; 158 159 if (counter->parent) { 160 dev->parent = counter->parent; 161 dev->of_node = counter->parent->of_node; 162 } 163 164 err = counter_sysfs_add(counter); 165 if (err < 0) 166 return err; 167 168 /* implies device_add(dev) */ 169 return cdev_device_add(&counter->chrdev, dev); 170 } 171 EXPORT_SYMBOL_NS_GPL(counter_add, "COUNTER"); 172 173 /** 174 * counter_unregister - unregister Counter from the system 175 * @counter: pointer to Counter to unregister 176 * 177 * The Counter is unregistered from the system. 178 */ 179 void counter_unregister(struct counter_device *const counter) 180 { 181 if (!counter) 182 return; 183 184 cdev_device_del(&counter->chrdev, &counter->dev); 185 186 mutex_lock(&counter->ops_exist_lock); 187 188 counter->ops = NULL; 189 wake_up(&counter->events_wait); 190 191 mutex_unlock(&counter->ops_exist_lock); 192 } 193 EXPORT_SYMBOL_NS_GPL(counter_unregister, "COUNTER"); 194 195 static void devm_counter_release(void *counter) 196 { 197 counter_unregister(counter); 198 } 199 200 static void devm_counter_put(void *counter) 201 { 202 counter_put(counter); 203 } 204 205 /** 206 * devm_counter_alloc - allocate a counter_device 207 * @dev: the device to register the release callback for 208 * @sizeof_priv: size of the driver private data 209 * 210 * This is the device managed version of counter_add(). It registers a cleanup 211 * callback to care for calling counter_put(). 212 */ 213 struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv) 214 { 215 struct counter_device *counter; 216 int err; 217 218 counter = counter_alloc(sizeof_priv); 219 if (!counter) 220 return NULL; 221 222 err = devm_add_action_or_reset(dev, devm_counter_put, counter); 223 if (err < 0) 224 return NULL; 225 226 return counter; 227 } 228 EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, "COUNTER"); 229 230 /** 231 * devm_counter_add - complete registration of a counter 232 * @dev: the device to register the release callback for 233 * @counter: the counter to add 234 * 235 * This is the device managed version of counter_add(). It registers a cleanup 236 * callback to care for calling counter_unregister(). 237 */ 238 int devm_counter_add(struct device *dev, 239 struct counter_device *const counter) 240 { 241 int err; 242 243 err = counter_add(counter); 244 if (err < 0) 245 return err; 246 247 return devm_add_action_or_reset(dev, devm_counter_release, counter); 248 } 249 EXPORT_SYMBOL_NS_GPL(devm_counter_add, "COUNTER"); 250 251 #define COUNTER_DEV_MAX 256 252 253 static int __init counter_init(void) 254 { 255 int err; 256 257 err = bus_register(&counter_bus_type); 258 if (err < 0) 259 return err; 260 261 err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, 262 COUNTER_NAME); 263 if (err < 0) 264 goto err_unregister_bus; 265 266 return 0; 267 268 err_unregister_bus: 269 bus_unregister(&counter_bus_type); 270 return err; 271 } 272 273 static void __exit counter_exit(void) 274 { 275 unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX); 276 bus_unregister(&counter_bus_type); 277 } 278 279 subsys_initcall(counter_init); 280 module_exit(counter_exit); 281 282 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 283 MODULE_DESCRIPTION("Generic Counter interface"); 284 MODULE_LICENSE("GPL v2"); 285