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