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/types.h> 19 #include <linux/wait.h> 20 21 #include "counter-chrdev.h" 22 #include "counter-sysfs.h" 23 24 /* Provides a unique ID for each counter device */ 25 static DEFINE_IDA(counter_ida); 26 27 static void counter_device_release(struct device *dev) 28 { 29 struct counter_device *const counter = dev_get_drvdata(dev); 30 31 counter_chrdev_remove(counter); 32 ida_free(&counter_ida, dev->id); 33 } 34 35 static struct device_type counter_device_type = { 36 .name = "counter_device", 37 .release = counter_device_release, 38 }; 39 40 static struct bus_type counter_bus_type = { 41 .name = "counter", 42 .dev_name = "counter", 43 }; 44 45 static dev_t counter_devt; 46 47 /** 48 * counter_register - register Counter to the system 49 * @counter: pointer to Counter to register 50 * 51 * This function registers a Counter to the system. A sysfs "counter" directory 52 * will be created and populated with sysfs attributes correlating with the 53 * Counter Signals, Synapses, and Counts respectively. 54 * 55 * RETURNS: 56 * 0 on success, negative error number on failure. 57 */ 58 int counter_register(struct counter_device *const counter) 59 { 60 struct device *const dev = &counter->dev; 61 int id; 62 int err; 63 64 /* Acquire unique ID */ 65 id = ida_alloc(&counter_ida, GFP_KERNEL); 66 if (id < 0) 67 return id; 68 69 mutex_init(&counter->ops_exist_lock); 70 71 /* Configure device structure for Counter */ 72 dev->id = id; 73 dev->type = &counter_device_type; 74 dev->bus = &counter_bus_type; 75 dev->devt = MKDEV(MAJOR(counter_devt), id); 76 if (counter->parent) { 77 dev->parent = counter->parent; 78 dev->of_node = counter->parent->of_node; 79 } 80 device_initialize(dev); 81 dev_set_drvdata(dev, counter); 82 83 err = counter_sysfs_add(counter); 84 if (err < 0) 85 goto err_free_id; 86 87 err = counter_chrdev_add(counter); 88 if (err < 0) 89 goto err_free_id; 90 91 err = cdev_device_add(&counter->chrdev, dev); 92 if (err < 0) 93 goto err_remove_chrdev; 94 95 return 0; 96 97 err_remove_chrdev: 98 counter_chrdev_remove(counter); 99 err_free_id: 100 put_device(dev); 101 return err; 102 } 103 EXPORT_SYMBOL_GPL(counter_register); 104 105 /** 106 * counter_unregister - unregister Counter from the system 107 * @counter: pointer to Counter to unregister 108 * 109 * The Counter is unregistered from the system. 110 */ 111 void counter_unregister(struct counter_device *const counter) 112 { 113 if (!counter) 114 return; 115 116 cdev_device_del(&counter->chrdev, &counter->dev); 117 118 mutex_lock(&counter->ops_exist_lock); 119 120 counter->ops = NULL; 121 wake_up(&counter->events_wait); 122 123 mutex_unlock(&counter->ops_exist_lock); 124 125 put_device(&counter->dev); 126 } 127 EXPORT_SYMBOL_GPL(counter_unregister); 128 129 static void devm_counter_release(void *counter) 130 { 131 counter_unregister(counter); 132 } 133 134 /** 135 * devm_counter_register - Resource-managed counter_register 136 * @dev: device to allocate counter_device for 137 * @counter: pointer to Counter to register 138 * 139 * Managed counter_register. The Counter registered with this function is 140 * automatically unregistered on driver detach. This function calls 141 * counter_register internally. Refer to that function for more information. 142 * 143 * RETURNS: 144 * 0 on success, negative error number on failure. 145 */ 146 int devm_counter_register(struct device *dev, 147 struct counter_device *const counter) 148 { 149 int err; 150 151 err = counter_register(counter); 152 if (err < 0) 153 return err; 154 155 return devm_add_action_or_reset(dev, devm_counter_release, counter); 156 } 157 EXPORT_SYMBOL_GPL(devm_counter_register); 158 159 #define COUNTER_DEV_MAX 256 160 161 static int __init counter_init(void) 162 { 163 int err; 164 165 err = bus_register(&counter_bus_type); 166 if (err < 0) 167 return err; 168 169 err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter"); 170 if (err < 0) 171 goto err_unregister_bus; 172 173 return 0; 174 175 err_unregister_bus: 176 bus_unregister(&counter_bus_type); 177 return err; 178 } 179 180 static void __exit counter_exit(void) 181 { 182 unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX); 183 bus_unregister(&counter_bus_type); 184 } 185 186 subsys_initcall(counter_init); 187 module_exit(counter_exit); 188 189 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 190 MODULE_DESCRIPTION("Generic Counter interface"); 191 MODULE_LICENSE("GPL v2"); 192