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