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