linux/drivers/staging/fieldbus/dev_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Fieldbus Device Driver Core
   4 *
   5 */
   6
   7#include <linux/mutex.h>
   8#include <linux/module.h>
   9#include <linux/device.h>
  10#include <linux/idr.h>
  11#include <linux/fs.h>
  12#include <linux/slab.h>
  13#include <linux/poll.h>
  14
  15/* move to <linux/fieldbus_dev.h> when taking this out of staging */
  16#include "fieldbus_dev.h"
  17
  18/* Maximum number of fieldbus devices */
  19#define MAX_FIELDBUSES          32
  20
  21/* the dev_t structure to store the dynamically allocated fieldbus devices */
  22static dev_t fieldbus_devt;
  23static DEFINE_IDA(fieldbus_ida);
  24static DEFINE_MUTEX(fieldbus_mtx);
  25
  26static ssize_t online_show(struct device *dev, struct device_attribute *attr,
  27                           char *buf)
  28{
  29        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  30
  31        return sprintf(buf, "%d\n", !!fb->online);
  32}
  33static DEVICE_ATTR_RO(online);
  34
  35static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
  36                            char *buf)
  37{
  38        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  39
  40        if (!fb->enable_get)
  41                return -EINVAL;
  42        return sprintf(buf, "%d\n", !!fb->enable_get(fb));
  43}
  44
  45static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
  46                             const char *buf, size_t n)
  47{
  48        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  49        bool value;
  50        int ret;
  51
  52        if (!fb->simple_enable_set)
  53                return -ENOTSUPP;
  54        ret = kstrtobool(buf, &value);
  55        if (ret)
  56                return ret;
  57        ret = fb->simple_enable_set(fb, value);
  58        if (ret < 0)
  59                return ret;
  60        return n;
  61}
  62static DEVICE_ATTR_RW(enabled);
  63
  64static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
  65                              char *buf)
  66{
  67        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  68
  69        /*
  70         * card_name was provided by child driver, could potentially be long.
  71         * protect against buffer overrun.
  72         */
  73        return snprintf(buf, PAGE_SIZE, "%s\n", fb->card_name);
  74}
  75static DEVICE_ATTR_RO(card_name);
  76
  77static ssize_t read_area_size_show(struct device *dev,
  78                                   struct device_attribute *attr, char *buf)
  79{
  80        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  81
  82        return sprintf(buf, "%zu\n", fb->read_area_sz);
  83}
  84static DEVICE_ATTR_RO(read_area_size);
  85
  86static ssize_t write_area_size_show(struct device *dev,
  87                                    struct device_attribute *attr, char *buf)
  88{
  89        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  90
  91        return sprintf(buf, "%zu\n", fb->write_area_sz);
  92}
  93static DEVICE_ATTR_RO(write_area_size);
  94
  95static ssize_t fieldbus_id_show(struct device *dev,
  96                                struct device_attribute *attr, char *buf)
  97{
  98        struct fieldbus_dev *fb = dev_get_drvdata(dev);
  99
 100        return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
 101}
 102static DEVICE_ATTR_RO(fieldbus_id);
 103
 104static ssize_t fieldbus_type_show(struct device *dev,
 105                                  struct device_attribute *attr, char *buf)
 106{
 107        struct fieldbus_dev *fb = dev_get_drvdata(dev);
 108        const char *t;
 109
 110        switch (fb->fieldbus_type) {
 111        case FIELDBUS_DEV_TYPE_PROFINET:
 112                t = "profinet";
 113                break;
 114        default:
 115                t = "unknown";
 116                break;
 117        }
 118
 119        return sprintf(buf, "%s\n", t);
 120}
 121static DEVICE_ATTR_RO(fieldbus_type);
 122
 123static struct attribute *fieldbus_attrs[] = {
 124        &dev_attr_enabled.attr,
 125        &dev_attr_card_name.attr,
 126        &dev_attr_fieldbus_id.attr,
 127        &dev_attr_read_area_size.attr,
 128        &dev_attr_write_area_size.attr,
 129        &dev_attr_online.attr,
 130        &dev_attr_fieldbus_type.attr,
 131        NULL,
 132};
 133
 134static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
 135                                   int n)
 136{
 137        struct device *dev = container_of(kobj, struct device, kobj);
 138        struct fieldbus_dev *fb = dev_get_drvdata(dev);
 139        umode_t mode = attr->mode;
 140
 141        if (attr == &dev_attr_enabled.attr) {
 142                mode = 0;
 143                if (fb->enable_get)
 144                        mode |= 0444;
 145                if (fb->simple_enable_set)
 146                        mode |= 0200;
 147        }
 148
 149        return mode;
 150}
 151
 152static const struct attribute_group fieldbus_group = {
 153        .attrs = fieldbus_attrs,
 154        .is_visible = fieldbus_is_visible,
 155};
 156__ATTRIBUTE_GROUPS(fieldbus);
 157
 158static struct class fieldbus_class = {
 159        .name =         "fieldbus_dev",
 160        .owner =        THIS_MODULE,
 161        .dev_groups =   fieldbus_groups,
 162};
 163
 164struct fb_open_file {
 165        struct fieldbus_dev *fbdev;
 166        int dc_event;
 167};
 168
 169static int fieldbus_open(struct inode *inode, struct file *filp)
 170{
 171        struct fb_open_file *of;
 172        struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
 173                                                struct fieldbus_dev,
 174                                                cdev);
 175
 176        of = kzalloc(sizeof(*of), GFP_KERNEL);
 177        if (!of)
 178                return -ENOMEM;
 179        of->fbdev = fbdev;
 180        filp->private_data = of;
 181        return 0;
 182}
 183
 184static int fieldbus_release(struct inode *node, struct file *filp)
 185{
 186        struct fb_open_file *of = filp->private_data;
 187
 188        kfree(of);
 189        return 0;
 190}
 191
 192static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
 193                             loff_t *offset)
 194{
 195        struct fb_open_file *of = filp->private_data;
 196        struct fieldbus_dev *fbdev = of->fbdev;
 197
 198        of->dc_event = fbdev->dc_event;
 199        return fbdev->read_area(fbdev, buf, size, offset);
 200}
 201
 202static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
 203                              size_t size, loff_t *offset)
 204{
 205        struct fb_open_file *of = filp->private_data;
 206        struct fieldbus_dev *fbdev = of->fbdev;
 207
 208        return fbdev->write_area(fbdev, buf, size, offset);
 209}
 210
 211static __poll_t fieldbus_poll(struct file *filp, poll_table *wait)
 212{
 213        struct fb_open_file *of = filp->private_data;
 214        struct fieldbus_dev *fbdev = of->fbdev;
 215        __poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
 216
 217        poll_wait(filp, &fbdev->dc_wq, wait);
 218        /* data changed ? */
 219        if (fbdev->dc_event != of->dc_event)
 220                mask |= EPOLLPRI | EPOLLERR;
 221        return mask;
 222}
 223
 224static const struct file_operations fieldbus_fops = {
 225        .open           = fieldbus_open,
 226        .release        = fieldbus_release,
 227        .read           = fieldbus_read,
 228        .write          = fieldbus_write,
 229        .poll           = fieldbus_poll,
 230        .llseek         = generic_file_llseek,
 231        .owner          = THIS_MODULE,
 232};
 233
 234void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
 235{
 236        fb->dc_event++;
 237        wake_up_all(&fb->dc_wq);
 238}
 239EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
 240
 241void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
 242{
 243        fb->online = online;
 244        kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
 245}
 246EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
 247
 248static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
 249{
 250        if (!fb)
 251                return;
 252        device_destroy(&fieldbus_class, fb->cdev.dev);
 253        cdev_del(&fb->cdev);
 254        ida_simple_remove(&fieldbus_ida, fb->id);
 255}
 256
 257void fieldbus_dev_unregister(struct fieldbus_dev *fb)
 258{
 259        mutex_lock(&fieldbus_mtx);
 260        __fieldbus_dev_unregister(fb);
 261        mutex_unlock(&fieldbus_mtx);
 262}
 263EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
 264
 265static int __fieldbus_dev_register(struct fieldbus_dev *fb)
 266{
 267        dev_t devno;
 268        int err;
 269
 270        if (!fb)
 271                return -EINVAL;
 272        if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
 273                return -EINVAL;
 274        fb->id = ida_simple_get(&fieldbus_ida, 0, MAX_FIELDBUSES, GFP_KERNEL);
 275        if (fb->id < 0)
 276                return fb->id;
 277        devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
 278        init_waitqueue_head(&fb->dc_wq);
 279        cdev_init(&fb->cdev, &fieldbus_fops);
 280        err = cdev_add(&fb->cdev, devno, 1);
 281        if (err) {
 282                pr_err("fieldbus_dev%d unable to add device %d:%d\n",
 283                       fb->id, MAJOR(fieldbus_devt), fb->id);
 284                goto err_cdev;
 285        }
 286        fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
 287                                "fieldbus_dev%d", fb->id);
 288        if (IS_ERR(fb->dev)) {
 289                err = PTR_ERR(fb->dev);
 290                goto err_dev_create;
 291        }
 292        return 0;
 293
 294err_dev_create:
 295        cdev_del(&fb->cdev);
 296err_cdev:
 297        ida_simple_remove(&fieldbus_ida, fb->id);
 298        return err;
 299}
 300
 301int fieldbus_dev_register(struct fieldbus_dev *fb)
 302{
 303        int err;
 304
 305        mutex_lock(&fieldbus_mtx);
 306        err = __fieldbus_dev_register(fb);
 307        mutex_unlock(&fieldbus_mtx);
 308
 309        return err;
 310}
 311EXPORT_SYMBOL_GPL(fieldbus_dev_register);
 312
 313static int __init fieldbus_init(void)
 314{
 315        int err;
 316
 317        err = class_register(&fieldbus_class);
 318        if (err < 0) {
 319                pr_err("fieldbus_dev: could not register class\n");
 320                return err;
 321        }
 322        err = alloc_chrdev_region(&fieldbus_devt, 0,
 323                                  MAX_FIELDBUSES, "fieldbus_dev");
 324        if (err < 0) {
 325                pr_err("fieldbus_dev: unable to allocate char dev region\n");
 326                goto err_alloc;
 327        }
 328        return 0;
 329
 330err_alloc:
 331        class_unregister(&fieldbus_class);
 332        return err;
 333}
 334
 335static void __exit fieldbus_exit(void)
 336{
 337        unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
 338        class_unregister(&fieldbus_class);
 339        ida_destroy(&fieldbus_ida);
 340}
 341
 342subsys_initcall(fieldbus_init);
 343module_exit(fieldbus_exit);
 344
 345MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
 346MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
 347MODULE_DESCRIPTION("Fieldbus Device Driver Core");
 348MODULE_LICENSE("GPL v2");
 349