Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Generic Counter interface
0004  * Copyright (C) 2020 William Breathitt Gray
0005  */
0006 #include <linux/cdev.h>
0007 #include <linux/counter.h>
0008 #include <linux/device.h>
0009 #include <linux/device/bus.h>
0010 #include <linux/export.h>
0011 #include <linux/fs.h>
0012 #include <linux/gfp.h>
0013 #include <linux/idr.h>
0014 #include <linux/init.h>
0015 #include <linux/kdev_t.h>
0016 #include <linux/module.h>
0017 #include <linux/mutex.h>
0018 #include <linux/slab.h>
0019 #include <linux/types.h>
0020 #include <linux/wait.h>
0021 
0022 #include "counter-chrdev.h"
0023 #include "counter-sysfs.h"
0024 
0025 #define COUNTER_NAME    "counter"
0026 
0027 /* Provides a unique ID for each counter device */
0028 static DEFINE_IDA(counter_ida);
0029 
0030 struct counter_device_allochelper {
0031     struct counter_device counter;
0032 
0033     /*
0034      * This is cache line aligned to ensure private data behaves like if it
0035      * were kmalloced separately.
0036      */
0037     unsigned long privdata[] ____cacheline_aligned;
0038 };
0039 
0040 static void counter_device_release(struct device *dev)
0041 {
0042     struct counter_device *const counter =
0043         container_of(dev, struct counter_device, dev);
0044 
0045     counter_chrdev_remove(counter);
0046     ida_free(&counter_ida, dev->id);
0047 
0048     kfree(container_of(counter, struct counter_device_allochelper, counter));
0049 }
0050 
0051 static struct device_type counter_device_type = {
0052     .name = "counter_device",
0053     .release = counter_device_release,
0054 };
0055 
0056 static struct bus_type counter_bus_type = {
0057     .name = "counter",
0058     .dev_name = "counter",
0059 };
0060 
0061 static dev_t counter_devt;
0062 
0063 /**
0064  * counter_priv - access counter device private data
0065  * @counter: counter device
0066  *
0067  * Get the counter device private data
0068  */
0069 void *counter_priv(const struct counter_device *const counter)
0070 {
0071     struct counter_device_allochelper *ch =
0072         container_of(counter, struct counter_device_allochelper, counter);
0073 
0074     return &ch->privdata;
0075 }
0076 EXPORT_SYMBOL_GPL(counter_priv);
0077 
0078 /**
0079  * counter_alloc - allocate a counter_device
0080  * @sizeof_priv: size of the driver private data
0081  *
0082  * This is part one of counter registration. The structure is allocated
0083  * dynamically to ensure the right lifetime for the embedded struct device.
0084  *
0085  * If this succeeds, call counter_put() to get rid of the counter_device again.
0086  */
0087 struct counter_device *counter_alloc(size_t sizeof_priv)
0088 {
0089     struct counter_device_allochelper *ch;
0090     struct counter_device *counter;
0091     struct device *dev;
0092     int err;
0093 
0094     ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL);
0095     if (!ch)
0096         return NULL;
0097 
0098     counter = &ch->counter;
0099     dev = &counter->dev;
0100 
0101     /* Acquire unique ID */
0102     err = ida_alloc(&counter_ida, GFP_KERNEL);
0103     if (err < 0)
0104         goto err_ida_alloc;
0105     dev->id = err;
0106 
0107     mutex_init(&counter->ops_exist_lock);
0108     dev->type = &counter_device_type;
0109     dev->bus = &counter_bus_type;
0110     dev->devt = MKDEV(MAJOR(counter_devt), dev->id);
0111 
0112     err = counter_chrdev_add(counter);
0113     if (err < 0)
0114         goto err_chrdev_add;
0115 
0116     device_initialize(dev);
0117 
0118     err = dev_set_name(dev, COUNTER_NAME "%d", dev->id);
0119     if (err)
0120         goto err_dev_set_name;
0121 
0122     return counter;
0123 
0124 err_dev_set_name:
0125 
0126     counter_chrdev_remove(counter);
0127 err_chrdev_add:
0128 
0129     ida_free(&counter_ida, dev->id);
0130 err_ida_alloc:
0131 
0132     kfree(ch);
0133 
0134     return NULL;
0135 }
0136 EXPORT_SYMBOL_GPL(counter_alloc);
0137 
0138 void counter_put(struct counter_device *counter)
0139 {
0140     put_device(&counter->dev);
0141 }
0142 EXPORT_SYMBOL_GPL(counter_put);
0143 
0144 /**
0145  * counter_add - complete registration of a counter
0146  * @counter: the counter to add
0147  *
0148  * This is part two of counter registration.
0149  *
0150  * If this succeeds, call counter_unregister() to get rid of the counter_device again.
0151  */
0152 int counter_add(struct counter_device *counter)
0153 {
0154     int err;
0155     struct device *dev = &counter->dev;
0156 
0157     if (counter->parent) {
0158         dev->parent = counter->parent;
0159         dev->of_node = counter->parent->of_node;
0160     }
0161 
0162     err = counter_sysfs_add(counter);
0163     if (err < 0)
0164         return err;
0165 
0166     /* implies device_add(dev) */
0167     return cdev_device_add(&counter->chrdev, dev);
0168 }
0169 EXPORT_SYMBOL_GPL(counter_add);
0170 
0171 /**
0172  * counter_unregister - unregister Counter from the system
0173  * @counter:    pointer to Counter to unregister
0174  *
0175  * The Counter is unregistered from the system.
0176  */
0177 void counter_unregister(struct counter_device *const counter)
0178 {
0179     if (!counter)
0180         return;
0181 
0182     cdev_device_del(&counter->chrdev, &counter->dev);
0183 
0184     mutex_lock(&counter->ops_exist_lock);
0185 
0186     counter->ops = NULL;
0187     wake_up(&counter->events_wait);
0188 
0189     mutex_unlock(&counter->ops_exist_lock);
0190 }
0191 EXPORT_SYMBOL_GPL(counter_unregister);
0192 
0193 static void devm_counter_release(void *counter)
0194 {
0195     counter_unregister(counter);
0196 }
0197 
0198 static void devm_counter_put(void *counter)
0199 {
0200     counter_put(counter);
0201 }
0202 
0203 /**
0204  * devm_counter_alloc - allocate a counter_device
0205  * @dev: the device to register the release callback for
0206  * @sizeof_priv: size of the driver private data
0207  *
0208  * This is the device managed version of counter_add(). It registers a cleanup
0209  * callback to care for calling counter_put().
0210  */
0211 struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv)
0212 {
0213     struct counter_device *counter;
0214     int err;
0215 
0216     counter = counter_alloc(sizeof_priv);
0217     if (!counter)
0218         return NULL;
0219 
0220     err = devm_add_action_or_reset(dev, devm_counter_put, counter);
0221     if (err < 0)
0222         return NULL;
0223 
0224     return counter;
0225 }
0226 EXPORT_SYMBOL_GPL(devm_counter_alloc);
0227 
0228 /**
0229  * devm_counter_add - complete registration of a counter
0230  * @dev: the device to register the release callback for
0231  * @counter: the counter to add
0232  *
0233  * This is the device managed version of counter_add(). It registers a cleanup
0234  * callback to care for calling counter_unregister().
0235  */
0236 int devm_counter_add(struct device *dev,
0237              struct counter_device *const counter)
0238 {
0239     int err;
0240 
0241     err = counter_add(counter);
0242     if (err < 0)
0243         return err;
0244 
0245     return devm_add_action_or_reset(dev, devm_counter_release, counter);
0246 }
0247 EXPORT_SYMBOL_GPL(devm_counter_add);
0248 
0249 #define COUNTER_DEV_MAX 256
0250 
0251 static int __init counter_init(void)
0252 {
0253     int err;
0254 
0255     err = bus_register(&counter_bus_type);
0256     if (err < 0)
0257         return err;
0258 
0259     err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX,
0260                   COUNTER_NAME);
0261     if (err < 0)
0262         goto err_unregister_bus;
0263 
0264     return 0;
0265 
0266 err_unregister_bus:
0267     bus_unregister(&counter_bus_type);
0268     return err;
0269 }
0270 
0271 static void __exit counter_exit(void)
0272 {
0273     unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
0274     bus_unregister(&counter_bus_type);
0275 }
0276 
0277 subsys_initcall(counter_init);
0278 module_exit(counter_exit);
0279 
0280 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
0281 MODULE_DESCRIPTION("Generic Counter interface");
0282 MODULE_LICENSE("GPL v2");