Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
[linux-2.6-microblaze.git] / drivers / staging / fieldbus / dev_core.c
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 = 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
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");