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 sysfs_emit(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 sysfs_emit(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 /* card_name was provided by child driver. */ 70 return sysfs_emit(buf, "%s\n", fb->card_name); 71} 72static DEVICE_ATTR_RO(card_name); 73 74static ssize_t read_area_size_show(struct device *dev, 75 struct device_attribute *attr, char *buf) 76{ 77 struct fieldbus_dev *fb = dev_get_drvdata(dev); 78 79 return sysfs_emit(buf, "%zu\n", fb->read_area_sz); 80} 81static DEVICE_ATTR_RO(read_area_size); 82 83static ssize_t write_area_size_show(struct device *dev, 84 struct device_attribute *attr, char *buf) 85{ 86 struct fieldbus_dev *fb = dev_get_drvdata(dev); 87 88 return sysfs_emit(buf, "%zu\n", fb->write_area_sz); 89} 90static DEVICE_ATTR_RO(write_area_size); 91 92static ssize_t fieldbus_id_show(struct device *dev, 93 struct device_attribute *attr, char *buf) 94{ 95 struct fieldbus_dev *fb = dev_get_drvdata(dev); 96 97 return fb->fieldbus_id_get(fb, buf, PAGE_SIZE); 98} 99static DEVICE_ATTR_RO(fieldbus_id); 100 101static ssize_t fieldbus_type_show(struct device *dev, 102 struct device_attribute *attr, char *buf) 103{ 104 struct fieldbus_dev *fb = dev_get_drvdata(dev); 105 const char *t; 106 107 switch (fb->fieldbus_type) { 108 case FIELDBUS_DEV_TYPE_PROFINET: 109 t = "profinet"; 110 break; 111 default: 112 t = "unknown"; 113 break; 114 } 115 116 return sysfs_emit(buf, "%s\n", t); 117} 118static DEVICE_ATTR_RO(fieldbus_type); 119 120static struct attribute *fieldbus_attrs[] = { 121 &dev_attr_enabled.attr, 122 &dev_attr_card_name.attr, 123 &dev_attr_fieldbus_id.attr, 124 &dev_attr_read_area_size.attr, 125 &dev_attr_write_area_size.attr, 126 &dev_attr_online.attr, 127 &dev_attr_fieldbus_type.attr, 128 NULL, 129}; 130 131static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr, 132 int n) 133{ 134 struct device *dev = kobj_to_dev(kobj); 135 struct fieldbus_dev *fb = dev_get_drvdata(dev); 136 umode_t mode = attr->mode; 137 138 if (attr == &dev_attr_enabled.attr) { 139 mode = 0; 140 if (fb->enable_get) 141 mode |= 0444; 142 if (fb->simple_enable_set) 143 mode |= 0200; 144 } 145 146 return mode; 147} 148 149static const struct attribute_group fieldbus_group = { 150 .attrs = fieldbus_attrs, 151 .is_visible = fieldbus_is_visible, 152}; 153__ATTRIBUTE_GROUPS(fieldbus); 154 155static const struct class fieldbus_class = { 156 .name = "fieldbus_dev", 157 .dev_groups = fieldbus_groups, 158}; 159 160struct fb_open_file { 161 struct fieldbus_dev *fbdev; 162 int dc_event; 163}; 164 165static int fieldbus_open(struct inode *inode, struct file *filp) 166{ 167 struct fb_open_file *of; 168 struct fieldbus_dev *fbdev = container_of(inode->i_cdev, 169 struct fieldbus_dev, 170 cdev); 171 172 of = kzalloc(sizeof(*of), GFP_KERNEL); 173 if (!of) 174 return -ENOMEM; 175 of->fbdev = fbdev; 176 filp->private_data = of; 177 return 0; 178} 179 180static int fieldbus_release(struct inode *node, struct file *filp) 181{ 182 struct fb_open_file *of = filp->private_data; 183 184 kfree(of); 185 return 0; 186} 187 188static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size, 189 loff_t *offset) 190{ 191 struct fb_open_file *of = filp->private_data; 192 struct fieldbus_dev *fbdev = of->fbdev; 193 194 of->dc_event = fbdev->dc_event; 195 return fbdev->read_area(fbdev, buf, size, offset); 196} 197 198static ssize_t fieldbus_write(struct file *filp, const char __user *buf, 199 size_t size, loff_t *offset) 200{ 201 struct fb_open_file *of = filp->private_data; 202 struct fieldbus_dev *fbdev = of->fbdev; 203 204 return fbdev->write_area(fbdev, buf, size, offset); 205} 206 207static __poll_t fieldbus_poll(struct file *filp, poll_table *wait) 208{ 209 struct fb_open_file *of = filp->private_data; 210 struct fieldbus_dev *fbdev = of->fbdev; 211 __poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; 212 213 poll_wait(filp, &fbdev->dc_wq, wait); 214 /* data changed ? */ 215 if (fbdev->dc_event != of->dc_event) 216 mask |= EPOLLPRI | EPOLLERR; 217 return mask; 218} 219 220static const struct file_operations fieldbus_fops = { 221 .open = fieldbus_open, 222 .release = fieldbus_release, 223 .read = fieldbus_read, 224 .write = fieldbus_write, 225 .poll = fieldbus_poll, 226 .llseek = generic_file_llseek, 227 .owner = THIS_MODULE, 228}; 229 230void fieldbus_dev_area_updated(struct fieldbus_dev *fb) 231{ 232 fb->dc_event++; 233 wake_up_all(&fb->dc_wq); 234} 235EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated); 236 237void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online) 238{ 239 fb->online = online; 240 kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE); 241} 242EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed); 243 244static void __fieldbus_dev_unregister(struct fieldbus_dev *fb) 245{ 246 if (!fb) 247 return; 248 device_destroy(&fieldbus_class, fb->cdev.dev); 249 cdev_del(&fb->cdev); 250 ida_free(&fieldbus_ida, fb->id); 251} 252 253void fieldbus_dev_unregister(struct fieldbus_dev *fb) 254{ 255 mutex_lock(&fieldbus_mtx); 256 __fieldbus_dev_unregister(fb); 257 mutex_unlock(&fieldbus_mtx); 258} 259EXPORT_SYMBOL_GPL(fieldbus_dev_unregister); 260 261static int __fieldbus_dev_register(struct fieldbus_dev *fb) 262{ 263 dev_t devno; 264 int err; 265 266 if (!fb) 267 return -EINVAL; 268 if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get) 269 return -EINVAL; 270 fb->id = ida_alloc_max(&fieldbus_ida, MAX_FIELDBUSES - 1, GFP_KERNEL); 271 if (fb->id < 0) 272 return fb->id; 273 devno = MKDEV(MAJOR(fieldbus_devt), fb->id); 274 init_waitqueue_head(&fb->dc_wq); 275 cdev_init(&fb->cdev, &fieldbus_fops); 276 err = cdev_add(&fb->cdev, devno, 1); 277 if (err) { 278 pr_err("fieldbus_dev%d unable to add device %d:%d\n", 279 fb->id, MAJOR(fieldbus_devt), fb->id); 280 goto err_cdev; 281 } 282 fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb, 283 "fieldbus_dev%d", fb->id); 284 if (IS_ERR(fb->dev)) { 285 err = PTR_ERR(fb->dev); 286 goto err_dev_create; 287 } 288 return 0; 289 290err_dev_create: 291 cdev_del(&fb->cdev); 292err_cdev: 293 ida_free(&fieldbus_ida, fb->id); 294 return err; 295} 296 297int fieldbus_dev_register(struct fieldbus_dev *fb) 298{ 299 int err; 300 301 mutex_lock(&fieldbus_mtx); 302 err = __fieldbus_dev_register(fb); 303 mutex_unlock(&fieldbus_mtx); 304 305 return err; 306} 307EXPORT_SYMBOL_GPL(fieldbus_dev_register); 308 309static int __init fieldbus_init(void) 310{ 311 int err; 312 313 err = class_register(&fieldbus_class); 314 if (err < 0) { 315 pr_err("fieldbus_dev: could not register class\n"); 316 return err; 317 } 318 err = alloc_chrdev_region(&fieldbus_devt, 0, 319 MAX_FIELDBUSES, "fieldbus_dev"); 320 if (err < 0) { 321 pr_err("fieldbus_dev: unable to allocate char dev region\n"); 322 goto err_alloc; 323 } 324 return 0; 325 326err_alloc: 327 class_unregister(&fieldbus_class); 328 return err; 329} 330 331static void __exit fieldbus_exit(void) 332{ 333 unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES); 334 class_unregister(&fieldbus_class); 335 ida_destroy(&fieldbus_ida); 336} 337 338subsys_initcall(fieldbus_init); 339module_exit(fieldbus_exit); 340 341MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>"); 342MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>"); 343MODULE_DESCRIPTION("Fieldbus Device Driver Core"); 344MODULE_LICENSE("GPL v2"); 345