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 
online_show(struct device * dev,struct device_attribute * attr,char * buf)26 static 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 }
33 static DEVICE_ATTR_RO(online);
34 
enabled_show(struct device * dev,struct device_attribute * attr,char * buf)35 static 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 
enabled_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t n)45 static 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 }
62 static DEVICE_ATTR_RW(enabled);
63 
card_name_show(struct device * dev,struct device_attribute * attr,char * buf)64 static 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 }
72 static DEVICE_ATTR_RO(card_name);
73 
read_area_size_show(struct device * dev,struct device_attribute * attr,char * buf)74 static 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 }
81 static DEVICE_ATTR_RO(read_area_size);
82 
write_area_size_show(struct device * dev,struct device_attribute * attr,char * buf)83 static 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 }
90 static DEVICE_ATTR_RO(write_area_size);
91 
fieldbus_id_show(struct device * dev,struct device_attribute * attr,char * buf)92 static 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 }
99 static DEVICE_ATTR_RO(fieldbus_id);
100 
fieldbus_type_show(struct device * dev,struct device_attribute * attr,char * buf)101 static 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 }
118 static DEVICE_ATTR_RO(fieldbus_type);
119 
120 static 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 
fieldbus_is_visible(struct kobject * kobj,struct attribute * attr,int n)131 static 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 
149 static const struct attribute_group fieldbus_group = {
150 	.attrs = fieldbus_attrs,
151 	.is_visible = fieldbus_is_visible,
152 };
153 __ATTRIBUTE_GROUPS(fieldbus);
154 
155 static struct class fieldbus_class = {
156 	.name =		"fieldbus_dev",
157 	.dev_groups =	fieldbus_groups,
158 };
159 
160 struct fb_open_file {
161 	struct fieldbus_dev *fbdev;
162 	int dc_event;
163 };
164 
fieldbus_open(struct inode * inode,struct file * filp)165 static 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 
fieldbus_release(struct inode * node,struct file * filp)180 static 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 
fieldbus_read(struct file * filp,char __user * buf,size_t size,loff_t * offset)188 static 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 
fieldbus_write(struct file * filp,const char __user * buf,size_t size,loff_t * offset)198 static 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 
fieldbus_poll(struct file * filp,poll_table * wait)207 static __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 
220 static 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 
fieldbus_dev_area_updated(struct fieldbus_dev * fb)230 void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
231 {
232 	fb->dc_event++;
233 	wake_up_all(&fb->dc_wq);
234 }
235 EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
236 
fieldbus_dev_online_changed(struct fieldbus_dev * fb,bool online)237 void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
238 {
239 	fb->online = online;
240 	kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
241 }
242 EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
243 
__fieldbus_dev_unregister(struct fieldbus_dev * fb)244 static 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_simple_remove(&fieldbus_ida, fb->id);
251 }
252 
fieldbus_dev_unregister(struct fieldbus_dev * fb)253 void fieldbus_dev_unregister(struct fieldbus_dev *fb)
254 {
255 	mutex_lock(&fieldbus_mtx);
256 	__fieldbus_dev_unregister(fb);
257 	mutex_unlock(&fieldbus_mtx);
258 }
259 EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
260 
__fieldbus_dev_register(struct fieldbus_dev * fb)261 static 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_simple_get(&fieldbus_ida, 0, MAX_FIELDBUSES, 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 
290 err_dev_create:
291 	cdev_del(&fb->cdev);
292 err_cdev:
293 	ida_simple_remove(&fieldbus_ida, fb->id);
294 	return err;
295 }
296 
fieldbus_dev_register(struct fieldbus_dev * fb)297 int 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 }
307 EXPORT_SYMBOL_GPL(fieldbus_dev_register);
308 
fieldbus_init(void)309 static 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 
326 err_alloc:
327 	class_unregister(&fieldbus_class);
328 	return err;
329 }
330 
fieldbus_exit(void)331 static 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 
338 subsys_initcall(fieldbus_init);
339 module_exit(fieldbus_exit);
340 
341 MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
342 MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
343 MODULE_DESCRIPTION("Fieldbus Device Driver Core");
344 MODULE_LICENSE("GPL v2");
345