diff options
author | Jonathan Cameron <jic23@cam.ac.uk> | 2009-08-18 13:06:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-15 15:02:24 -0400 |
commit | 847ec80bbaa76aae41062d6802cea9c1b2289f14 (patch) | |
tree | fd68009e13b5abd5e02704c29b6639be9df3d630 /drivers/staging/iio/industrialio-core.c | |
parent | a5ca2dfc4ebd33e18f981f562833c39efdc2585c (diff) |
Staging: IIO: core support for device registration and management
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/iio/industrialio-core.c')
-rw-r--r-- | drivers/staging/iio/industrialio-core.c | 851 |
1 files changed, 851 insertions, 0 deletions
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c new file mode 100644 index 00000000000..660a9c1a1f3 --- /dev/null +++ b/drivers/staging/iio/industrialio-core.c | |||
@@ -0,0 +1,851 @@ | |||
1 | /* The industrial I/O core | ||
2 | * | ||
3 | * Copyright (c) 2008 Jonathan Cameron | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * Based on elements of hwmon and input subsystems. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/idr.h> | ||
15 | #include <linux/kdev_t.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/poll.h> | ||
21 | #include <linux/cdev.h> | ||
22 | #include "iio.h" | ||
23 | #include "trigger_consumer.h" | ||
24 | |||
25 | #define IIO_ID_PREFIX "device" | ||
26 | #define IIO_ID_FORMAT IIO_ID_PREFIX "%d" | ||
27 | |||
28 | /* IDR to assign each registered device a unique id*/ | ||
29 | static DEFINE_IDR(iio_idr); | ||
30 | |||
31 | /* IDR for general event identifiers */ | ||
32 | static DEFINE_IDR(iio_event_idr); | ||
33 | /* IDR to allocate character device minor numbers */ | ||
34 | static DEFINE_IDR(iio_chrdev_idr); | ||
35 | /* Lock used to protect both of the above */ | ||
36 | static DEFINE_SPINLOCK(iio_idr_lock); | ||
37 | |||
38 | dev_t iio_devt; | ||
39 | EXPORT_SYMBOL(iio_devt); | ||
40 | |||
41 | #define IIO_DEV_MAX 256 | ||
42 | static char *iio_nodename(struct device *dev) | ||
43 | { | ||
44 | return kasprintf(GFP_KERNEL, "iio/%s", dev_name(dev)); | ||
45 | } | ||
46 | |||
47 | struct class iio_class = { | ||
48 | .name = "iio", | ||
49 | .nodename = iio_nodename, | ||
50 | }; | ||
51 | EXPORT_SYMBOL(iio_class); | ||
52 | |||
53 | void __iio_change_event(struct iio_detected_event_list *ev, | ||
54 | int ev_code, | ||
55 | s64 timestamp) | ||
56 | { | ||
57 | ev->ev.id = ev_code; | ||
58 | ev->ev.timestamp = timestamp; | ||
59 | } | ||
60 | EXPORT_SYMBOL(__iio_change_event); | ||
61 | |||
62 | /* Used both in the interrupt line put events and the ring buffer ones */ | ||
63 | |||
64 | /* Note that in it's current form someone has to be listening before events | ||
65 | * are queued. Hence a client MUST open the chrdev before the ring buffer is | ||
66 | * switched on. | ||
67 | */ | ||
68 | int __iio_push_event(struct iio_event_interface *ev_int, | ||
69 | int ev_code, | ||
70 | s64 timestamp, | ||
71 | struct iio_shared_ev_pointer * | ||
72 | shared_pointer_p) | ||
73 | { | ||
74 | struct iio_detected_event_list *ev; | ||
75 | int ret = 0; | ||
76 | |||
77 | /* Does anyone care? */ | ||
78 | mutex_lock(&ev_int->event_list_lock); | ||
79 | if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) { | ||
80 | if (ev_int->current_events == ev_int->max_events) | ||
81 | return 0; | ||
82 | ev = kmalloc(sizeof(*ev), GFP_KERNEL); | ||
83 | if (ev == NULL) { | ||
84 | ret = -ENOMEM; | ||
85 | goto error_ret; | ||
86 | } | ||
87 | ev->ev.id = ev_code; | ||
88 | ev->ev.timestamp = timestamp; | ||
89 | ev->shared_pointer = shared_pointer_p; | ||
90 | if (ev->shared_pointer) | ||
91 | shared_pointer_p->ev_p = ev; | ||
92 | |||
93 | list_add_tail(&ev->list, &ev_int->det_events.list); | ||
94 | ev_int->current_events++; | ||
95 | mutex_unlock(&ev_int->event_list_lock); | ||
96 | wake_up_interruptible(&ev_int->wait); | ||
97 | } else | ||
98 | mutex_unlock(&ev_int->event_list_lock); | ||
99 | |||
100 | error_ret: | ||
101 | return ret; | ||
102 | } | ||
103 | EXPORT_SYMBOL(__iio_push_event); | ||
104 | |||
105 | int iio_push_event(struct iio_dev *dev_info, | ||
106 | int ev_line, | ||
107 | int ev_code, | ||
108 | s64 timestamp) | ||
109 | { | ||
110 | return __iio_push_event(&dev_info->event_interfaces[ev_line], | ||
111 | ev_code, timestamp, NULL); | ||
112 | } | ||
113 | EXPORT_SYMBOL(iio_push_event); | ||
114 | |||
115 | /* Generic interrupt line interrupt handler */ | ||
116 | irqreturn_t iio_interrupt_handler(int irq, void *_int_info) | ||
117 | { | ||
118 | struct iio_interrupt *int_info = _int_info; | ||
119 | struct iio_dev *dev_info = int_info->dev_info; | ||
120 | struct iio_event_handler_list *p; | ||
121 | s64 time_ns; | ||
122 | unsigned long flags; | ||
123 | |||
124 | spin_lock_irqsave(&int_info->ev_list_lock, flags); | ||
125 | if (list_empty(&int_info->ev_list)) { | ||
126 | spin_unlock_irqrestore(&int_info->ev_list_lock, flags); | ||
127 | return IRQ_NONE; | ||
128 | } | ||
129 | |||
130 | time_ns = iio_get_time_ns(); | ||
131 | /* detect single element list*/ | ||
132 | if (list_is_singular(&int_info->ev_list)) { | ||
133 | disable_irq_nosync(irq); | ||
134 | p = list_first_entry(&int_info->ev_list, | ||
135 | struct iio_event_handler_list, | ||
136 | list); | ||
137 | /* single event handler - maybe shared */ | ||
138 | p->handler(dev_info, 1, time_ns, !(p->refcount > 1)); | ||
139 | } else | ||
140 | list_for_each_entry(p, &int_info->ev_list, list) { | ||
141 | disable_irq_nosync(irq); | ||
142 | p->handler(dev_info, 1, time_ns, 0); | ||
143 | } | ||
144 | spin_unlock_irqrestore(&int_info->ev_list_lock, flags); | ||
145 | |||
146 | return IRQ_HANDLED; | ||
147 | } | ||
148 | |||
149 | static struct iio_interrupt *iio_allocate_interrupt(void) | ||
150 | { | ||
151 | struct iio_interrupt *i = kmalloc(sizeof *i, GFP_KERNEL); | ||
152 | if (i) { | ||
153 | spin_lock_init(&i->ev_list_lock); | ||
154 | INIT_LIST_HEAD(&i->ev_list); | ||
155 | } | ||
156 | return i; | ||
157 | } | ||
158 | |||
159 | /* Confirming the validity of supplied irq is left to drivers.*/ | ||
160 | int iio_register_interrupt_line(unsigned int irq, | ||
161 | struct iio_dev *dev_info, | ||
162 | int line_number, | ||
163 | unsigned long type, | ||
164 | const char *name) | ||
165 | { | ||
166 | int ret; | ||
167 | |||
168 | dev_info->interrupts[line_number] = iio_allocate_interrupt(); | ||
169 | if (dev_info->interrupts[line_number] == NULL) { | ||
170 | ret = -ENOMEM; | ||
171 | goto error_ret; | ||
172 | } | ||
173 | dev_info->interrupts[line_number]->line_number = line_number; | ||
174 | dev_info->interrupts[line_number]->irq = irq; | ||
175 | dev_info->interrupts[line_number]->dev_info = dev_info; | ||
176 | |||
177 | /* Possibly only request on demand? | ||
178 | * Can see this may complicate the handling of interrupts. | ||
179 | * However, with this approach we might end up handling lots of | ||
180 | * events no-one cares about.*/ | ||
181 | ret = request_irq(irq, | ||
182 | &iio_interrupt_handler, | ||
183 | type, | ||
184 | name, | ||
185 | dev_info->interrupts[line_number]); | ||
186 | |||
187 | error_ret: | ||
188 | return ret; | ||
189 | } | ||
190 | EXPORT_SYMBOL(iio_register_interrupt_line); | ||
191 | |||
192 | /* This turns up an awful lot */ | ||
193 | ssize_t iio_read_const_attr(struct device *dev, | ||
194 | struct device_attribute *attr, | ||
195 | char *buf) | ||
196 | { | ||
197 | return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string); | ||
198 | } | ||
199 | EXPORT_SYMBOL(iio_read_const_attr); | ||
200 | |||
201 | /* Before this runs the interrupt generator must have been disabled */ | ||
202 | void iio_unregister_interrupt_line(struct iio_dev *dev_info, int line_number) | ||
203 | { | ||
204 | /* make sure the interrupt handlers are all done */ | ||
205 | flush_scheduled_work(); | ||
206 | free_irq(dev_info->interrupts[line_number]->irq, | ||
207 | dev_info->interrupts[line_number]); | ||
208 | kfree(dev_info->interrupts[line_number]); | ||
209 | } | ||
210 | EXPORT_SYMBOL(iio_unregister_interrupt_line); | ||
211 | |||
212 | /* Reference counted add and remove */ | ||
213 | void iio_add_event_to_list(struct iio_event_handler_list *el, | ||
214 | struct list_head *head) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | struct iio_interrupt *inter = to_iio_interrupt(head); | ||
218 | |||
219 | /* take mutex to protect this element */ | ||
220 | mutex_lock(&el->exist_lock); | ||
221 | if (el->refcount == 0) { | ||
222 | /* Take the event list spin lock */ | ||
223 | spin_lock_irqsave(&inter->ev_list_lock, flags); | ||
224 | list_add(&el->list, head); | ||
225 | spin_unlock_irqrestore(&inter->ev_list_lock, flags); | ||
226 | } | ||
227 | el->refcount++; | ||
228 | mutex_unlock(&el->exist_lock); | ||
229 | } | ||
230 | EXPORT_SYMBOL(iio_add_event_to_list); | ||
231 | |||
232 | void iio_remove_event_from_list(struct iio_event_handler_list *el, | ||
233 | struct list_head *head) | ||
234 | { | ||
235 | unsigned long flags; | ||
236 | struct iio_interrupt *inter = to_iio_interrupt(head); | ||
237 | |||
238 | mutex_lock(&el->exist_lock); | ||
239 | el->refcount--; | ||
240 | if (el->refcount == 0) { | ||
241 | /* Take the event list spin lock */ | ||
242 | spin_lock_irqsave(&inter->ev_list_lock, flags); | ||
243 | list_del_init(&el->list); | ||
244 | spin_unlock_irqrestore(&inter->ev_list_lock, flags); | ||
245 | } | ||
246 | mutex_unlock(&el->exist_lock); | ||
247 | } | ||
248 | EXPORT_SYMBOL(iio_remove_event_from_list); | ||
249 | |||
250 | ssize_t iio_event_chrdev_read(struct file *filep, | ||
251 | char *buf, | ||
252 | size_t count, | ||
253 | loff_t *f_ps) | ||
254 | { | ||
255 | struct iio_event_interface *ev_int = filep->private_data; | ||
256 | struct iio_detected_event_list *el; | ||
257 | int ret; | ||
258 | size_t len; | ||
259 | |||
260 | mutex_lock(&ev_int->event_list_lock); | ||
261 | if (list_empty(&ev_int->det_events.list)) { | ||
262 | if (filep->f_flags & O_NONBLOCK) { | ||
263 | ret = -EAGAIN; | ||
264 | goto error_mutex_unlock; | ||
265 | } | ||
266 | mutex_unlock(&ev_int->event_list_lock); | ||
267 | /* Blocking on device; waiting for something to be there */ | ||
268 | ret = wait_event_interruptible(ev_int->wait, | ||
269 | !list_empty(&ev_int | ||
270 | ->det_events.list)); | ||
271 | if (ret) | ||
272 | goto error_ret; | ||
273 | /* Single access device so noone else can get the data */ | ||
274 | mutex_lock(&ev_int->event_list_lock); | ||
275 | } | ||
276 | |||
277 | el = list_first_entry(&ev_int->det_events.list, | ||
278 | struct iio_detected_event_list, | ||
279 | list); | ||
280 | len = sizeof el->ev; | ||
281 | if (copy_to_user(buf, &(el->ev), len)) { | ||
282 | ret = -EFAULT; | ||
283 | goto error_mutex_unlock; | ||
284 | } | ||
285 | list_del(&el->list); | ||
286 | ev_int->current_events--; | ||
287 | mutex_unlock(&ev_int->event_list_lock); | ||
288 | /* | ||
289 | * Possible concurency issue if an update of this event is on its way | ||
290 | * through. May lead to new even being removed whilst the reported event | ||
291 | * was the unescalated event. In typical use case this is not a problem | ||
292 | * as userspace will say read half the buffer due to a 50% full event | ||
293 | * which would make the correct 100% full incorrect anyway. | ||
294 | */ | ||
295 | spin_lock(&el->shared_pointer->lock); | ||
296 | if (el->shared_pointer) | ||
297 | (el->shared_pointer->ev_p) = NULL; | ||
298 | spin_unlock(&el->shared_pointer->lock); | ||
299 | |||
300 | kfree(el); | ||
301 | |||
302 | return len; | ||
303 | |||
304 | error_mutex_unlock: | ||
305 | mutex_unlock(&ev_int->event_list_lock); | ||
306 | error_ret: | ||
307 | |||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | int iio_event_chrdev_release(struct inode *inode, struct file *filep) | ||
312 | { | ||
313 | struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); | ||
314 | struct iio_event_interface *ev_int = hand->private; | ||
315 | struct iio_detected_event_list *el, *t; | ||
316 | |||
317 | mutex_lock(&ev_int->event_list_lock); | ||
318 | clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags); | ||
319 | /* | ||
320 | * In order to maintain a clean state for reopening, | ||
321 | * clear out any awaiting events. The mask will prevent | ||
322 | * any new __iio_push_event calls running. | ||
323 | */ | ||
324 | list_for_each_entry_safe(el, t, &ev_int->det_events.list, list) { | ||
325 | list_del(&el->list); | ||
326 | kfree(el); | ||
327 | } | ||
328 | mutex_unlock(&ev_int->event_list_lock); | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | int iio_event_chrdev_open(struct inode *inode, struct file *filep) | ||
334 | { | ||
335 | struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); | ||
336 | struct iio_event_interface *ev_int = hand->private; | ||
337 | |||
338 | mutex_lock(&ev_int->event_list_lock); | ||
339 | if (test_and_set_bit(IIO_BUSY_BIT_POS, &hand->flags)) { | ||
340 | fops_put(filep->f_op); | ||
341 | mutex_unlock(&ev_int->event_list_lock); | ||
342 | return -EBUSY; | ||
343 | } | ||
344 | filep->private_data = hand->private; | ||
345 | mutex_unlock(&ev_int->event_list_lock); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static const struct file_operations iio_event_chrdev_fileops = { | ||
351 | .read = iio_event_chrdev_read, | ||
352 | .release = iio_event_chrdev_release, | ||
353 | .open = iio_event_chrdev_open, | ||
354 | .owner = THIS_MODULE, | ||
355 | }; | ||
356 | |||
357 | static void iio_event_dev_release(struct device *dev) | ||
358 | { | ||
359 | struct iio_event_interface *ev_int | ||
360 | = container_of(dev, struct iio_event_interface, dev); | ||
361 | cdev_del(&ev_int->handler.chrdev); | ||
362 | iio_device_free_chrdev_minor(MINOR(dev->devt)); | ||
363 | }; | ||
364 | |||
365 | static struct device_type iio_event_type = { | ||
366 | .release = iio_event_dev_release, | ||
367 | }; | ||
368 | |||
369 | int iio_device_get_chrdev_minor(void) | ||
370 | { | ||
371 | int ret, val; | ||
372 | |||
373 | idr_again: | ||
374 | if (unlikely(idr_pre_get(&iio_chrdev_idr, GFP_KERNEL) == 0)) | ||
375 | return -ENOMEM; | ||
376 | spin_lock(&iio_idr_lock); | ||
377 | ret = idr_get_new(&iio_chrdev_idr, NULL, &val); | ||
378 | spin_unlock(&iio_idr_lock); | ||
379 | if (unlikely(ret == -EAGAIN)) | ||
380 | goto idr_again; | ||
381 | else if (unlikely(ret)) | ||
382 | return ret; | ||
383 | if (val > IIO_DEV_MAX) | ||
384 | return -ENOMEM; | ||
385 | return val; | ||
386 | } | ||
387 | |||
388 | void iio_device_free_chrdev_minor(int val) | ||
389 | { | ||
390 | spin_lock(&iio_idr_lock); | ||
391 | idr_remove(&iio_chrdev_idr, val); | ||
392 | spin_unlock(&iio_idr_lock); | ||
393 | } | ||
394 | |||
395 | int iio_setup_ev_int(struct iio_event_interface *ev_int, | ||
396 | const char *name, | ||
397 | struct module *owner, | ||
398 | struct device *dev) | ||
399 | { | ||
400 | int ret, minor; | ||
401 | |||
402 | ev_int->dev.class = &iio_class; | ||
403 | ev_int->dev.parent = dev; | ||
404 | ev_int->dev.type = &iio_event_type; | ||
405 | device_initialize(&ev_int->dev); | ||
406 | |||
407 | minor = iio_device_get_chrdev_minor(); | ||
408 | if (minor < 0) { | ||
409 | ret = minor; | ||
410 | goto error_device_put; | ||
411 | } | ||
412 | ev_int->dev.devt = MKDEV(MAJOR(iio_devt), minor); | ||
413 | dev_set_name(&ev_int->dev, "%s", name); | ||
414 | |||
415 | ret = device_add(&ev_int->dev); | ||
416 | if (ret) | ||
417 | goto error_free_minor; | ||
418 | |||
419 | cdev_init(&ev_int->handler.chrdev, &iio_event_chrdev_fileops); | ||
420 | ev_int->handler.chrdev.owner = owner; | ||
421 | |||
422 | mutex_init(&ev_int->event_list_lock); | ||
423 | /* discussion point - make this variable? */ | ||
424 | ev_int->max_events = 10; | ||
425 | ev_int->current_events = 0; | ||
426 | INIT_LIST_HEAD(&ev_int->det_events.list); | ||
427 | init_waitqueue_head(&ev_int->wait); | ||
428 | ev_int->handler.private = ev_int; | ||
429 | ev_int->handler.flags = 0; | ||
430 | |||
431 | ret = cdev_add(&ev_int->handler.chrdev, ev_int->dev.devt, 1); | ||
432 | if (ret) | ||
433 | goto error_unreg_device; | ||
434 | |||
435 | return 0; | ||
436 | |||
437 | error_unreg_device: | ||
438 | device_unregister(&ev_int->dev); | ||
439 | error_free_minor: | ||
440 | iio_device_free_chrdev_minor(minor); | ||
441 | error_device_put: | ||
442 | put_device(&ev_int->dev); | ||
443 | |||
444 | return ret; | ||
445 | } | ||
446 | |||
447 | void iio_free_ev_int(struct iio_event_interface *ev_int) | ||
448 | { | ||
449 | device_unregister(&ev_int->dev); | ||
450 | put_device(&ev_int->dev); | ||
451 | } | ||
452 | |||
453 | static int __init iio_dev_init(void) | ||
454 | { | ||
455 | int err; | ||
456 | |||
457 | err = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio"); | ||
458 | if (err < 0) | ||
459 | printk(KERN_ERR "%s: failed to allocate char dev region\n", | ||
460 | __FILE__); | ||
461 | |||
462 | return err; | ||
463 | } | ||
464 | |||
465 | static void __exit iio_dev_exit(void) | ||
466 | { | ||
467 | if (iio_devt) | ||
468 | unregister_chrdev_region(iio_devt, IIO_DEV_MAX); | ||
469 | } | ||
470 | |||
471 | static int __init iio_init(void) | ||
472 | { | ||
473 | int ret; | ||
474 | |||
475 | /* Create sysfs class */ | ||
476 | ret = class_register(&iio_class); | ||
477 | if (ret < 0) { | ||
478 | printk(KERN_ERR | ||
479 | "%s could not create sysfs class\n", | ||
480 | __FILE__); | ||
481 | goto error_nothing; | ||
482 | } | ||
483 | |||
484 | ret = iio_dev_init(); | ||
485 | if (ret < 0) | ||
486 | goto error_unregister_class; | ||
487 | |||
488 | return 0; | ||
489 | |||
490 | error_unregister_class: | ||
491 | class_unregister(&iio_class); | ||
492 | error_nothing: | ||
493 | return ret; | ||
494 | } | ||
495 | |||
496 | static void __exit iio_exit(void) | ||
497 | { | ||
498 | iio_dev_exit(); | ||
499 | class_unregister(&iio_class); | ||
500 | } | ||
501 | |||
502 | static int iio_device_register_sysfs(struct iio_dev *dev_info) | ||
503 | { | ||
504 | int ret = 0; | ||
505 | |||
506 | ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs); | ||
507 | if (ret) { | ||
508 | dev_err(dev_info->dev.parent, | ||
509 | "Failed to register sysfs hooks\n"); | ||
510 | goto error_ret; | ||
511 | } | ||
512 | |||
513 | if (dev_info->scan_el_attrs) { | ||
514 | ret = sysfs_create_group(&dev_info->dev.kobj, | ||
515 | dev_info->scan_el_attrs); | ||
516 | if (ret) | ||
517 | dev_err(&dev_info->dev, | ||
518 | "Failed to add sysfs scan els\n"); | ||
519 | } | ||
520 | |||
521 | error_ret: | ||
522 | return ret; | ||
523 | } | ||
524 | |||
525 | static void iio_device_unregister_sysfs(struct iio_dev *dev_info) | ||
526 | { | ||
527 | if (dev_info->scan_el_attrs) | ||
528 | sysfs_remove_group(&dev_info->dev.kobj, | ||
529 | dev_info->scan_el_attrs); | ||
530 | |||
531 | sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); | ||
532 | } | ||
533 | |||
534 | int iio_get_new_idr_val(struct idr *this_idr) | ||
535 | { | ||
536 | int ret; | ||
537 | int val; | ||
538 | |||
539 | idr_again: | ||
540 | if (unlikely(idr_pre_get(this_idr, GFP_KERNEL) == 0)) | ||
541 | return -ENOMEM; | ||
542 | |||
543 | spin_lock(&iio_idr_lock); | ||
544 | ret = idr_get_new(this_idr, NULL, &val); | ||
545 | spin_unlock(&iio_idr_lock); | ||
546 | if (unlikely(ret == -EAGAIN)) | ||
547 | goto idr_again; | ||
548 | else if (unlikely(ret)) | ||
549 | return ret; | ||
550 | |||
551 | return val; | ||
552 | } | ||
553 | EXPORT_SYMBOL(iio_get_new_idr_val); | ||
554 | |||
555 | void iio_free_idr_val(struct idr *this_idr, int id) | ||
556 | { | ||
557 | spin_lock(&iio_idr_lock); | ||
558 | idr_remove(this_idr, id); | ||
559 | spin_unlock(&iio_idr_lock); | ||
560 | } | ||
561 | EXPORT_SYMBOL(iio_free_idr_val); | ||
562 | |||
563 | static int iio_device_register_id(struct iio_dev *dev_info, | ||
564 | struct idr *this_idr) | ||
565 | { | ||
566 | |||
567 | dev_info->id = iio_get_new_idr_val(&iio_idr); | ||
568 | if (dev_info->id < 0) | ||
569 | return dev_info->id; | ||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static void iio_device_unregister_id(struct iio_dev *dev_info) | ||
574 | { | ||
575 | iio_free_idr_val(&iio_idr, dev_info->id); | ||
576 | } | ||
577 | |||
578 | static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i) | ||
579 | { | ||
580 | int ret; | ||
581 | /*p for adding, q for removing */ | ||
582 | struct attribute **attrp, **attrq; | ||
583 | |||
584 | if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) { | ||
585 | attrp = dev_info->event_conf_attrs[i].attrs; | ||
586 | while (*attrp) { | ||
587 | ret = sysfs_add_file_to_group(&dev_info->dev.kobj, | ||
588 | *attrp, | ||
589 | dev_info | ||
590 | ->event_attrs[i].name); | ||
591 | if (ret) | ||
592 | goto error_ret; | ||
593 | attrp++; | ||
594 | } | ||
595 | } | ||
596 | return 0; | ||
597 | |||
598 | error_ret: | ||
599 | attrq = dev_info->event_conf_attrs[i].attrs; | ||
600 | while (attrq != attrp) { | ||
601 | sysfs_remove_file_from_group(&dev_info->dev.kobj, | ||
602 | *attrq, | ||
603 | dev_info->event_attrs[i].name); | ||
604 | attrq++; | ||
605 | } | ||
606 | |||
607 | return ret; | ||
608 | } | ||
609 | |||
610 | static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info, | ||
611 | int i) | ||
612 | { | ||
613 | struct attribute **attrq; | ||
614 | |||
615 | if (dev_info->event_conf_attrs | ||
616 | && dev_info->event_conf_attrs[i].attrs) { | ||
617 | attrq = dev_info->event_conf_attrs[i].attrs; | ||
618 | while (*attrq) { | ||
619 | sysfs_remove_file_from_group(&dev_info->dev.kobj, | ||
620 | *attrq, | ||
621 | dev_info | ||
622 | ->event_attrs[i].name); | ||
623 | attrq++; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int iio_device_register_eventset(struct iio_dev *dev_info) | ||
631 | { | ||
632 | int ret = 0, i, j; | ||
633 | |||
634 | if (dev_info->num_interrupt_lines == 0) | ||
635 | return 0; | ||
636 | |||
637 | dev_info->event_interfaces = | ||
638 | kzalloc(sizeof(struct iio_event_interface) | ||
639 | *dev_info->num_interrupt_lines, | ||
640 | GFP_KERNEL); | ||
641 | if (dev_info->event_interfaces == NULL) { | ||
642 | ret = -ENOMEM; | ||
643 | goto error_ret; | ||
644 | } | ||
645 | |||
646 | dev_info->interrupts = kzalloc(sizeof(struct iio_interrupt *) | ||
647 | *dev_info->num_interrupt_lines, | ||
648 | GFP_KERNEL); | ||
649 | if (dev_info->interrupts == NULL) { | ||
650 | ret = -ENOMEM; | ||
651 | goto error_free_event_interfaces; | ||
652 | } | ||
653 | |||
654 | for (i = 0; i < dev_info->num_interrupt_lines; i++) { | ||
655 | dev_info->event_interfaces[i].owner = dev_info->driver_module; | ||
656 | ret = iio_get_new_idr_val(&iio_event_idr); | ||
657 | if (ret) | ||
658 | goto error_free_setup_ev_ints; | ||
659 | else | ||
660 | dev_info->event_interfaces[i].id = ret; | ||
661 | |||
662 | snprintf(dev_info->event_interfaces[i]._name, 20, | ||
663 | "event_line%d", | ||
664 | dev_info->event_interfaces[i].id); | ||
665 | |||
666 | ret = iio_setup_ev_int(&dev_info->event_interfaces[i], | ||
667 | (const char *)(dev_info | ||
668 | ->event_interfaces[i] | ||
669 | ._name), | ||
670 | dev_info->driver_module, | ||
671 | &dev_info->dev); | ||
672 | if (ret) { | ||
673 | dev_err(&dev_info->dev, | ||
674 | "Could not get chrdev interface\n"); | ||
675 | iio_free_idr_val(&iio_event_idr, | ||
676 | dev_info->event_interfaces[i].id); | ||
677 | goto error_free_setup_ev_ints; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | for (i = 0; i < dev_info->num_interrupt_lines; i++) { | ||
682 | snprintf(dev_info->event_interfaces[i]._attrname, 20, | ||
683 | "event_line%d_sources", i); | ||
684 | dev_info->event_attrs[i].name | ||
685 | = (const char *) | ||
686 | (dev_info->event_interfaces[i]._attrname); | ||
687 | ret = sysfs_create_group(&dev_info->dev.kobj, | ||
688 | &dev_info->event_attrs[i]); | ||
689 | if (ret) { | ||
690 | dev_err(&dev_info->dev, | ||
691 | "Failed to register sysfs for event attrs"); | ||
692 | goto error_remove_sysfs_interfaces; | ||
693 | } | ||
694 | } | ||
695 | |||
696 | for (i = 0; i < dev_info->num_interrupt_lines; i++) { | ||
697 | ret = __iio_add_event_config_attrs(dev_info, i); | ||
698 | if (ret) | ||
699 | goto error_unregister_config_attrs; | ||
700 | } | ||
701 | |||
702 | return 0; | ||
703 | |||
704 | error_unregister_config_attrs: | ||
705 | for (j = 0; j < i; j++) | ||
706 | __iio_remove_event_config_attrs(dev_info, i); | ||
707 | i = dev_info->num_interrupt_lines - 1; | ||
708 | error_remove_sysfs_interfaces: | ||
709 | for (j = 0; j < i; j++) | ||
710 | sysfs_remove_group(&dev_info->dev.kobj, | ||
711 | &dev_info->event_attrs[j]); | ||
712 | i = dev_info->num_interrupt_lines - 1; | ||
713 | error_free_setup_ev_ints: | ||
714 | for (j = 0; j < i; j++) { | ||
715 | iio_free_idr_val(&iio_event_idr, | ||
716 | dev_info->event_interfaces[i].id); | ||
717 | iio_free_ev_int(&dev_info->event_interfaces[j]); | ||
718 | } | ||
719 | kfree(dev_info->interrupts); | ||
720 | error_free_event_interfaces: | ||
721 | kfree(dev_info->event_interfaces); | ||
722 | error_ret: | ||
723 | |||
724 | return ret; | ||
725 | } | ||
726 | |||
727 | static void iio_device_unregister_eventset(struct iio_dev *dev_info) | ||
728 | { | ||
729 | int i; | ||
730 | |||
731 | if (dev_info->num_interrupt_lines == 0) | ||
732 | return; | ||
733 | for (i = 0; i < dev_info->num_interrupt_lines; i++) | ||
734 | sysfs_remove_group(&dev_info->dev.kobj, | ||
735 | &dev_info->event_attrs[i]); | ||
736 | |||
737 | for (i = 0; i < dev_info->num_interrupt_lines; i++) { | ||
738 | iio_free_idr_val(&iio_event_idr, | ||
739 | dev_info->event_interfaces[i].id); | ||
740 | iio_free_ev_int(&dev_info->event_interfaces[i]); | ||
741 | } | ||
742 | kfree(dev_info->interrupts); | ||
743 | kfree(dev_info->event_interfaces); | ||
744 | } | ||
745 | |||
746 | static void iio_dev_release(struct device *device) | ||
747 | { | ||
748 | struct iio_dev *dev = to_iio_dev(device); | ||
749 | |||
750 | iio_put(); | ||
751 | kfree(dev); | ||
752 | } | ||
753 | |||
754 | static struct device_type iio_dev_type = { | ||
755 | .name = "iio_device", | ||
756 | .release = iio_dev_release, | ||
757 | }; | ||
758 | |||
759 | struct iio_dev *iio_allocate_device(void) | ||
760 | { | ||
761 | struct iio_dev *dev = kzalloc(sizeof *dev, GFP_KERNEL); | ||
762 | |||
763 | if (dev) { | ||
764 | dev->dev.type = &iio_dev_type; | ||
765 | dev->dev.class = &iio_class; | ||
766 | device_initialize(&dev->dev); | ||
767 | dev_set_drvdata(&dev->dev, (void *)dev); | ||
768 | mutex_init(&dev->mlock); | ||
769 | iio_get(); | ||
770 | } | ||
771 | |||
772 | return dev; | ||
773 | } | ||
774 | EXPORT_SYMBOL(iio_allocate_device); | ||
775 | |||
776 | void iio_free_device(struct iio_dev *dev) | ||
777 | { | ||
778 | if (dev) | ||
779 | iio_put_device(dev); | ||
780 | } | ||
781 | EXPORT_SYMBOL(iio_free_device); | ||
782 | |||
783 | int iio_device_register(struct iio_dev *dev_info) | ||
784 | { | ||
785 | int ret; | ||
786 | |||
787 | ret = iio_device_register_id(dev_info, &iio_idr); | ||
788 | if (ret) { | ||
789 | dev_err(&dev_info->dev, "Failed to get id\n"); | ||
790 | goto error_ret; | ||
791 | } | ||
792 | dev_set_name(&dev_info->dev, "device%d", dev_info->id); | ||
793 | |||
794 | ret = device_add(&dev_info->dev); | ||
795 | if (ret) | ||
796 | goto error_free_idr; | ||
797 | ret = iio_device_register_sysfs(dev_info); | ||
798 | if (ret) { | ||
799 | dev_err(dev_info->dev.parent, | ||
800 | "Failed to register sysfs interfaces\n"); | ||
801 | goto error_del_device; | ||
802 | } | ||
803 | ret = iio_device_register_eventset(dev_info); | ||
804 | if (ret) { | ||
805 | dev_err(dev_info->dev.parent, | ||
806 | "Failed to register event set \n"); | ||
807 | goto error_free_sysfs; | ||
808 | } | ||
809 | if (dev_info->modes & INDIO_RING_TRIGGERED) | ||
810 | iio_device_register_trigger_consumer(dev_info); | ||
811 | |||
812 | return 0; | ||
813 | |||
814 | error_free_sysfs: | ||
815 | iio_device_unregister_sysfs(dev_info); | ||
816 | error_del_device: | ||
817 | device_del(&dev_info->dev); | ||
818 | error_free_idr: | ||
819 | iio_device_unregister_id(dev_info); | ||
820 | error_ret: | ||
821 | return ret; | ||
822 | } | ||
823 | EXPORT_SYMBOL(iio_device_register); | ||
824 | |||
825 | void iio_device_unregister(struct iio_dev *dev_info) | ||
826 | { | ||
827 | if (dev_info->modes & INDIO_RING_TRIGGERED) | ||
828 | iio_device_unregister_trigger_consumer(dev_info); | ||
829 | iio_device_unregister_eventset(dev_info); | ||
830 | iio_device_unregister_sysfs(dev_info); | ||
831 | iio_device_unregister_id(dev_info); | ||
832 | device_unregister(&dev_info->dev); | ||
833 | } | ||
834 | EXPORT_SYMBOL(iio_device_unregister); | ||
835 | |||
836 | void iio_put(void) | ||
837 | { | ||
838 | module_put(THIS_MODULE); | ||
839 | } | ||
840 | |||
841 | void iio_get(void) | ||
842 | { | ||
843 | __module_get(THIS_MODULE); | ||
844 | } | ||
845 | |||
846 | subsys_initcall(iio_init); | ||
847 | module_exit(iio_exit); | ||
848 | |||
849 | MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); | ||
850 | MODULE_DESCRIPTION("Industrial I/O core"); | ||
851 | MODULE_LICENSE("GPL"); | ||