1
2
3
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
16#include "fieldbus_dev.h"
17
18
19#define MAX_FIELDBUSES 32
20
21
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 sprintf(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 sprintf(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
70
71
72
73 return snprintf(buf, PAGE_SIZE, "%s\n", fb->card_name);
74}
75static DEVICE_ATTR_RO(card_name);
76
77static 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}
84static DEVICE_ATTR_RO(read_area_size);
85
86static 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}
93static DEVICE_ATTR_RO(write_area_size);
94
95static 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}
102static DEVICE_ATTR_RO(fieldbus_id);
103
104static 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}
121static DEVICE_ATTR_RO(fieldbus_type);
122
123static 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
134static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
135 int n)
136{
137 struct device *dev = kobj_to_dev(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
152static const struct attribute_group fieldbus_group = {
153 .attrs = fieldbus_attrs,
154 .is_visible = fieldbus_is_visible,
155};
156__ATTRIBUTE_GROUPS(fieldbus);
157
158static struct class fieldbus_class = {
159 .name = "fieldbus_dev",
160 .owner = THIS_MODULE,
161 .dev_groups = fieldbus_groups,
162};
163
164struct fb_open_file {
165 struct fieldbus_dev *fbdev;
166 int dc_event;
167};
168
169static 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
184static 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
192static 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
202static 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
211static __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
219 if (fbdev->dc_event != of->dc_event)
220 mask |= EPOLLPRI | EPOLLERR;
221 return mask;
222}
223
224static 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
234void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
235{
236 fb->dc_event++;
237 wake_up_all(&fb->dc_wq);
238}
239EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
240
241void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
242{
243 fb->online = online;
244 kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
245}
246EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
247
248static 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
257void fieldbus_dev_unregister(struct fieldbus_dev *fb)
258{
259 mutex_lock(&fieldbus_mtx);
260 __fieldbus_dev_unregister(fb);
261 mutex_unlock(&fieldbus_mtx);
262}
263EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
264
265static 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
294err_dev_create:
295 cdev_del(&fb->cdev);
296err_cdev:
297 ida_simple_remove(&fieldbus_ida, fb->id);
298 return err;
299}
300
301int 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}
311EXPORT_SYMBOL_GPL(fieldbus_dev_register);
312
313static 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
330err_alloc:
331 class_unregister(&fieldbus_class);
332 return err;
333}
334
335static 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
342subsys_initcall(fieldbus_init);
343module_exit(fieldbus_exit);
344
345MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
346MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
347MODULE_DESCRIPTION("Fieldbus Device Driver Core");
348MODULE_LICENSE("GPL v2");
349