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, fmt: "%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, fmt: "%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(s: buf, res: &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, fmt: "%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, fmt: "%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, fmt: "%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, fmt: "%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(size: 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(objp: 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, wait_address: &fbdev->dc_wq, p: 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(kobj: &fb->dev->kobj, action: 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(cls: &fieldbus_class, devt: fb->cdev.dev);
249 cdev_del(&fb->cdev);
250 ida_free(&fieldbus_ida, id: 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(lock: &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(ida: &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(cls: &fieldbus_class, parent: fb->parent, devt: devno, drvdata: fb,
283 fmt: "fieldbus_dev%d", fb->id);
284 if (IS_ERR(ptr: fb->dev)) {
285 err = PTR_ERR(ptr: 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, id: 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(lock: &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(class: &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(class: &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(class: &fieldbus_class);
335 ida_destroy(ida: &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

source code of linux/drivers/staging/fieldbus/dev_core.c