]>
Commit | Line | Data |
---|---|---|
847ec80b JC |
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> | |
847ec80b | 19 | #include <linux/poll.h> |
ffc18afa | 20 | #include <linux/sched.h> |
4439c935 | 21 | #include <linux/wait.h> |
847ec80b | 22 | #include <linux/cdev.h> |
5a0e3ad6 | 23 | #include <linux/slab.h> |
847ec80b JC |
24 | #include "iio.h" |
25 | #include "trigger_consumer.h" | |
df9c1c42 | 26 | #include "iio_core.h" |
847ec80b JC |
27 | |
28 | #define IIO_ID_PREFIX "device" | |
29 | #define IIO_ID_FORMAT IIO_ID_PREFIX "%d" | |
30 | ||
31 | /* IDR to assign each registered device a unique id*/ | |
b156cf70 | 32 | static DEFINE_IDA(iio_ida); |
847ec80b | 33 | /* IDR to allocate character device minor numbers */ |
b156cf70 | 34 | static DEFINE_IDA(iio_chrdev_ida); |
847ec80b | 35 | /* Lock used to protect both of the above */ |
b156cf70 | 36 | static DEFINE_SPINLOCK(iio_ida_lock); |
847ec80b JC |
37 | |
38 | dev_t iio_devt; | |
847ec80b JC |
39 | |
40 | #define IIO_DEV_MAX 256 | |
5aaaeba8 | 41 | struct bus_type iio_bus_type = { |
847ec80b | 42 | .name = "iio", |
847ec80b | 43 | }; |
5aaaeba8 | 44 | EXPORT_SYMBOL(iio_bus_type); |
847ec80b | 45 | |
1d892719 | 46 | static const char * const iio_chan_type_name_spec_shared[] = { |
1d892719 | 47 | [IIO_IN] = "in", |
ae19178e | 48 | [IIO_OUT] = "out", |
faf290e8 MH |
49 | [IIO_CURRENT] = "current", |
50 | [IIO_POWER] = "power", | |
9bff02f8 | 51 | [IIO_ACCEL] = "accel", |
1d892719 JC |
52 | [IIO_IN_DIFF] = "in-in", |
53 | [IIO_GYRO] = "gyro", | |
1d892719 | 54 | [IIO_MAGN] = "magn", |
9bff02f8 BF |
55 | [IIO_LIGHT] = "illuminance", |
56 | [IIO_INTENSITY] = "intensity", | |
f09f2c81 | 57 | [IIO_PROXIMITY] = "proximity", |
9bff02f8 | 58 | [IIO_TEMP] = "temp", |
1d892719 JC |
59 | [IIO_INCLI] = "incli", |
60 | [IIO_ROT] = "rot", | |
1d892719 | 61 | [IIO_ANGL] = "angl", |
9bff02f8 | 62 | [IIO_TIMESTAMP] = "timestamp", |
1d892719 JC |
63 | }; |
64 | ||
65 | static const char * const iio_chan_type_name_spec_complex[] = { | |
66 | [IIO_IN_DIFF] = "in%d-in%d", | |
67 | }; | |
68 | ||
69 | static const char * const iio_modifier_names_light[] = { | |
70 | [IIO_MOD_LIGHT_BOTH] = "both", | |
71 | [IIO_MOD_LIGHT_IR] = "ir", | |
72 | }; | |
73 | ||
74 | static const char * const iio_modifier_names_axial[] = { | |
75 | [IIO_MOD_X] = "x", | |
76 | [IIO_MOD_Y] = "y", | |
77 | [IIO_MOD_Z] = "z", | |
78 | }; | |
79 | ||
80 | /* relies on pairs of these shared then separate */ | |
81 | static const char * const iio_chan_info_postfix[] = { | |
82 | [IIO_CHAN_INFO_SCALE_SHARED/2] = "scale", | |
83 | [IIO_CHAN_INFO_OFFSET_SHARED/2] = "offset", | |
84 | [IIO_CHAN_INFO_CALIBSCALE_SHARED/2] = "calibscale", | |
85 | [IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias", | |
eb7fea53 JC |
86 | [IIO_CHAN_INFO_PEAK_SHARED/2] = "peak_raw", |
87 | [IIO_CHAN_INFO_PEAK_SCALE_SHARED/2] = "peak_scale", | |
7d438178 JC |
88 | [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED/2] |
89 | = "quadrature_correction_raw", | |
1d892719 JC |
90 | }; |
91 | ||
9aa1a167 | 92 | /* Return a negative errno on failure */ |
df9c1c42 | 93 | static int iio_get_new_ida_val(struct ida *this_ida) |
9aa1a167 JC |
94 | { |
95 | int ret; | |
96 | int val; | |
97 | ||
98 | ida_again: | |
99 | if (unlikely(ida_pre_get(this_ida, GFP_KERNEL) == 0)) | |
100 | return -ENOMEM; | |
101 | ||
102 | spin_lock(&iio_ida_lock); | |
103 | ret = ida_get_new(this_ida, &val); | |
104 | spin_unlock(&iio_ida_lock); | |
105 | if (unlikely(ret == -EAGAIN)) | |
106 | goto ida_again; | |
107 | else if (unlikely(ret)) | |
108 | return ret; | |
109 | ||
110 | return val; | |
111 | } | |
9aa1a167 | 112 | |
df9c1c42 | 113 | static void iio_free_ida_val(struct ida *this_ida, int id) |
9aa1a167 JC |
114 | { |
115 | spin_lock(&iio_ida_lock); | |
116 | ida_remove(this_ida, id); | |
117 | spin_unlock(&iio_ida_lock); | |
118 | } | |
9aa1a167 | 119 | |
aaf370db JC |
120 | int iio_push_event(struct iio_dev *dev_info, |
121 | int ev_line, | |
122 | int ev_code, | |
123 | s64 timestamp) | |
847ec80b | 124 | { |
aaf370db JC |
125 | struct iio_event_interface *ev_int |
126 | = &dev_info->event_interfaces[ev_line]; | |
847ec80b JC |
127 | struct iio_detected_event_list *ev; |
128 | int ret = 0; | |
129 | ||
130 | /* Does anyone care? */ | |
131 | mutex_lock(&ev_int->event_list_lock); | |
132 | if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) { | |
75c80753 JC |
133 | if (ev_int->current_events == ev_int->max_events) { |
134 | mutex_unlock(&ev_int->event_list_lock); | |
847ec80b | 135 | return 0; |
75c80753 | 136 | } |
847ec80b JC |
137 | ev = kmalloc(sizeof(*ev), GFP_KERNEL); |
138 | if (ev == NULL) { | |
139 | ret = -ENOMEM; | |
75c80753 | 140 | mutex_unlock(&ev_int->event_list_lock); |
847ec80b JC |
141 | goto error_ret; |
142 | } | |
143 | ev->ev.id = ev_code; | |
144 | ev->ev.timestamp = timestamp; | |
847ec80b | 145 | |
3b8ebfb4 | 146 | list_add_tail(&ev->list, &ev_int->det_events); |
847ec80b JC |
147 | ev_int->current_events++; |
148 | mutex_unlock(&ev_int->event_list_lock); | |
149 | wake_up_interruptible(&ev_int->wait); | |
150 | } else | |
151 | mutex_unlock(&ev_int->event_list_lock); | |
152 | ||
153 | error_ret: | |
154 | return ret; | |
155 | } | |
847ec80b JC |
156 | EXPORT_SYMBOL(iio_push_event); |
157 | ||
847ec80b JC |
158 | |
159 | /* This turns up an awful lot */ | |
160 | ssize_t iio_read_const_attr(struct device *dev, | |
161 | struct device_attribute *attr, | |
162 | char *buf) | |
163 | { | |
164 | return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string); | |
165 | } | |
166 | EXPORT_SYMBOL(iio_read_const_attr); | |
167 | ||
847ec80b | 168 | |
77712e5f MB |
169 | static ssize_t iio_event_chrdev_read(struct file *filep, |
170 | char __user *buf, | |
171 | size_t count, | |
172 | loff_t *f_ps) | |
847ec80b JC |
173 | { |
174 | struct iio_event_interface *ev_int = filep->private_data; | |
175 | struct iio_detected_event_list *el; | |
176 | int ret; | |
177 | size_t len; | |
178 | ||
179 | mutex_lock(&ev_int->event_list_lock); | |
3b8ebfb4 | 180 | if (list_empty(&ev_int->det_events)) { |
847ec80b JC |
181 | if (filep->f_flags & O_NONBLOCK) { |
182 | ret = -EAGAIN; | |
183 | goto error_mutex_unlock; | |
184 | } | |
185 | mutex_unlock(&ev_int->event_list_lock); | |
186 | /* Blocking on device; waiting for something to be there */ | |
187 | ret = wait_event_interruptible(ev_int->wait, | |
188 | !list_empty(&ev_int | |
3b8ebfb4 | 189 | ->det_events)); |
847ec80b JC |
190 | if (ret) |
191 | goto error_ret; | |
25985edc | 192 | /* Single access device so no one else can get the data */ |
847ec80b JC |
193 | mutex_lock(&ev_int->event_list_lock); |
194 | } | |
195 | ||
3b8ebfb4 | 196 | el = list_first_entry(&ev_int->det_events, |
847ec80b JC |
197 | struct iio_detected_event_list, |
198 | list); | |
199 | len = sizeof el->ev; | |
200 | if (copy_to_user(buf, &(el->ev), len)) { | |
201 | ret = -EFAULT; | |
202 | goto error_mutex_unlock; | |
203 | } | |
204 | list_del(&el->list); | |
205 | ev_int->current_events--; | |
206 | mutex_unlock(&ev_int->event_list_lock); | |
847ec80b JC |
207 | kfree(el); |
208 | ||
209 | return len; | |
210 | ||
211 | error_mutex_unlock: | |
212 | mutex_unlock(&ev_int->event_list_lock); | |
213 | error_ret: | |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
77712e5f | 218 | static int iio_event_chrdev_release(struct inode *inode, struct file *filep) |
847ec80b JC |
219 | { |
220 | struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); | |
221 | struct iio_event_interface *ev_int = hand->private; | |
222 | struct iio_detected_event_list *el, *t; | |
223 | ||
224 | mutex_lock(&ev_int->event_list_lock); | |
225 | clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags); | |
226 | /* | |
227 | * In order to maintain a clean state for reopening, | |
228 | * clear out any awaiting events. The mask will prevent | |
229 | * any new __iio_push_event calls running. | |
230 | */ | |
3b8ebfb4 | 231 | list_for_each_entry_safe(el, t, &ev_int->det_events, list) { |
847ec80b JC |
232 | list_del(&el->list); |
233 | kfree(el); | |
234 | } | |
235 | mutex_unlock(&ev_int->event_list_lock); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
77712e5f | 240 | static int iio_event_chrdev_open(struct inode *inode, struct file *filep) |
847ec80b JC |
241 | { |
242 | struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev); | |
243 | struct iio_event_interface *ev_int = hand->private; | |
244 | ||
245 | mutex_lock(&ev_int->event_list_lock); | |
246 | if (test_and_set_bit(IIO_BUSY_BIT_POS, &hand->flags)) { | |
247 | fops_put(filep->f_op); | |
248 | mutex_unlock(&ev_int->event_list_lock); | |
249 | return -EBUSY; | |
250 | } | |
251 | filep->private_data = hand->private; | |
252 | mutex_unlock(&ev_int->event_list_lock); | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | static const struct file_operations iio_event_chrdev_fileops = { | |
258 | .read = iio_event_chrdev_read, | |
259 | .release = iio_event_chrdev_release, | |
260 | .open = iio_event_chrdev_open, | |
261 | .owner = THIS_MODULE, | |
6038f373 | 262 | .llseek = noop_llseek, |
847ec80b JC |
263 | }; |
264 | ||
265 | static void iio_event_dev_release(struct device *dev) | |
266 | { | |
267 | struct iio_event_interface *ev_int | |
268 | = container_of(dev, struct iio_event_interface, dev); | |
269 | cdev_del(&ev_int->handler.chrdev); | |
270 | iio_device_free_chrdev_minor(MINOR(dev->devt)); | |
271 | }; | |
272 | ||
273 | static struct device_type iio_event_type = { | |
274 | .release = iio_event_dev_release, | |
275 | }; | |
276 | ||
277 | int iio_device_get_chrdev_minor(void) | |
278 | { | |
9aa1a167 | 279 | int ret; |
847ec80b | 280 | |
9aa1a167 JC |
281 | ret = iio_get_new_ida_val(&iio_chrdev_ida); |
282 | if (ret < IIO_DEV_MAX) /* both errors and valid */ | |
847ec80b | 283 | return ret; |
9aa1a167 | 284 | else |
847ec80b | 285 | return -ENOMEM; |
847ec80b JC |
286 | } |
287 | ||
288 | void iio_device_free_chrdev_minor(int val) | |
289 | { | |
9aa1a167 | 290 | iio_free_ida_val(&iio_chrdev_ida, val); |
847ec80b JC |
291 | } |
292 | ||
b9d40a9d | 293 | static int iio_setup_ev_int(struct iio_event_interface *ev_int, |
c74b0de1 JC |
294 | const char *dev_name, |
295 | int index, | |
296 | struct module *owner, | |
297 | struct device *dev) | |
847ec80b JC |
298 | { |
299 | int ret, minor; | |
300 | ||
5aaaeba8 | 301 | ev_int->dev.bus = &iio_bus_type; |
847ec80b JC |
302 | ev_int->dev.parent = dev; |
303 | ev_int->dev.type = &iio_event_type; | |
304 | device_initialize(&ev_int->dev); | |
305 | ||
306 | minor = iio_device_get_chrdev_minor(); | |
307 | if (minor < 0) { | |
308 | ret = minor; | |
309 | goto error_device_put; | |
310 | } | |
311 | ev_int->dev.devt = MKDEV(MAJOR(iio_devt), minor); | |
c74b0de1 | 312 | dev_set_name(&ev_int->dev, "%s:event%d", dev_name, index); |
847ec80b JC |
313 | |
314 | ret = device_add(&ev_int->dev); | |
315 | if (ret) | |
316 | goto error_free_minor; | |
317 | ||
318 | cdev_init(&ev_int->handler.chrdev, &iio_event_chrdev_fileops); | |
319 | ev_int->handler.chrdev.owner = owner; | |
320 | ||
321 | mutex_init(&ev_int->event_list_lock); | |
322 | /* discussion point - make this variable? */ | |
323 | ev_int->max_events = 10; | |
324 | ev_int->current_events = 0; | |
3b8ebfb4 | 325 | INIT_LIST_HEAD(&ev_int->det_events); |
847ec80b JC |
326 | init_waitqueue_head(&ev_int->wait); |
327 | ev_int->handler.private = ev_int; | |
328 | ev_int->handler.flags = 0; | |
329 | ||
330 | ret = cdev_add(&ev_int->handler.chrdev, ev_int->dev.devt, 1); | |
331 | if (ret) | |
332 | goto error_unreg_device; | |
333 | ||
334 | return 0; | |
335 | ||
336 | error_unreg_device: | |
337 | device_unregister(&ev_int->dev); | |
338 | error_free_minor: | |
339 | iio_device_free_chrdev_minor(minor); | |
340 | error_device_put: | |
341 | put_device(&ev_int->dev); | |
342 | ||
343 | return ret; | |
344 | } | |
345 | ||
b9d40a9d | 346 | static void iio_free_ev_int(struct iio_event_interface *ev_int) |
847ec80b JC |
347 | { |
348 | device_unregister(&ev_int->dev); | |
349 | put_device(&ev_int->dev); | |
350 | } | |
351 | ||
847ec80b JC |
352 | static int __init iio_init(void) |
353 | { | |
354 | int ret; | |
355 | ||
5aaaeba8 JC |
356 | /* Register sysfs bus */ |
357 | ret = bus_register(&iio_bus_type); | |
847ec80b JC |
358 | if (ret < 0) { |
359 | printk(KERN_ERR | |
5aaaeba8 | 360 | "%s could not register bus type\n", |
847ec80b JC |
361 | __FILE__); |
362 | goto error_nothing; | |
363 | } | |
364 | ||
9aa1a167 JC |
365 | ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio"); |
366 | if (ret < 0) { | |
367 | printk(KERN_ERR "%s: failed to allocate char dev region\n", | |
368 | __FILE__); | |
5aaaeba8 | 369 | goto error_unregister_bus_type; |
9aa1a167 | 370 | } |
847ec80b JC |
371 | |
372 | return 0; | |
373 | ||
5aaaeba8 JC |
374 | error_unregister_bus_type: |
375 | bus_unregister(&iio_bus_type); | |
847ec80b JC |
376 | error_nothing: |
377 | return ret; | |
378 | } | |
379 | ||
380 | static void __exit iio_exit(void) | |
381 | { | |
9aa1a167 JC |
382 | if (iio_devt) |
383 | unregister_chrdev_region(iio_devt, IIO_DEV_MAX); | |
5aaaeba8 | 384 | bus_unregister(&iio_bus_type); |
847ec80b JC |
385 | } |
386 | ||
1d892719 JC |
387 | static ssize_t iio_read_channel_info(struct device *dev, |
388 | struct device_attribute *attr, | |
389 | char *buf) | |
847ec80b | 390 | { |
1d892719 JC |
391 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
392 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
393 | int val, val2; | |
6fe8135f JC |
394 | int ret = indio_dev->info->read_raw(indio_dev, this_attr->c, |
395 | &val, &val2, this_attr->address); | |
1d892719 JC |
396 | |
397 | if (ret < 0) | |
398 | return ret; | |
847ec80b | 399 | |
1d892719 JC |
400 | if (ret == IIO_VAL_INT) |
401 | return sprintf(buf, "%d\n", val); | |
402 | else if (ret == IIO_VAL_INT_PLUS_MICRO) { | |
403 | if (val2 < 0) | |
404 | return sprintf(buf, "-%d.%06u\n", val, -val2); | |
405 | else | |
406 | return sprintf(buf, "%d.%06u\n", val, val2); | |
71646e2c MH |
407 | } else if (ret == IIO_VAL_INT_PLUS_NANO) { |
408 | if (val2 < 0) | |
409 | return sprintf(buf, "-%d.%09u\n", val, -val2); | |
410 | else | |
411 | return sprintf(buf, "%d.%09u\n", val, val2); | |
1d892719 JC |
412 | } else |
413 | return 0; | |
414 | } | |
415 | ||
416 | static ssize_t iio_write_channel_info(struct device *dev, | |
417 | struct device_attribute *attr, | |
418 | const char *buf, | |
419 | size_t len) | |
420 | { | |
421 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
422 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
5c04af04 | 423 | int ret, integer = 0, fract = 0, fract_mult = 100000; |
1d892719 JC |
424 | bool integer_part = true, negative = false; |
425 | ||
426 | /* Assumes decimal - precision based on number of digits */ | |
6fe8135f | 427 | if (!indio_dev->info->write_raw) |
1d892719 | 428 | return -EINVAL; |
5c04af04 MH |
429 | |
430 | if (indio_dev->info->write_raw_get_fmt) | |
431 | switch (indio_dev->info->write_raw_get_fmt(indio_dev, | |
432 | this_attr->c, this_attr->address)) { | |
433 | case IIO_VAL_INT_PLUS_MICRO: | |
434 | fract_mult = 100000; | |
435 | break; | |
436 | case IIO_VAL_INT_PLUS_NANO: | |
437 | fract_mult = 100000000; | |
438 | break; | |
439 | default: | |
440 | return -EINVAL; | |
441 | } | |
442 | ||
1d892719 JC |
443 | if (buf[0] == '-') { |
444 | negative = true; | |
445 | buf++; | |
446 | } | |
5c04af04 | 447 | |
1d892719 JC |
448 | while (*buf) { |
449 | if ('0' <= *buf && *buf <= '9') { | |
450 | if (integer_part) | |
451 | integer = integer*10 + *buf - '0'; | |
452 | else { | |
5c04af04 MH |
453 | fract += fract_mult*(*buf - '0'); |
454 | if (fract_mult == 1) | |
1d892719 | 455 | break; |
5c04af04 | 456 | fract_mult /= 10; |
1d892719 JC |
457 | } |
458 | } else if (*buf == '\n') { | |
459 | if (*(buf + 1) == '\0') | |
460 | break; | |
461 | else | |
462 | return -EINVAL; | |
463 | } else if (*buf == '.') { | |
464 | integer_part = false; | |
465 | } else { | |
466 | return -EINVAL; | |
467 | } | |
468 | buf++; | |
469 | } | |
470 | if (negative) { | |
471 | if (integer) | |
472 | integer = -integer; | |
473 | else | |
5c04af04 | 474 | fract = -fract; |
1d892719 JC |
475 | } |
476 | ||
6fe8135f | 477 | ret = indio_dev->info->write_raw(indio_dev, this_attr->c, |
5c04af04 | 478 | integer, fract, this_attr->address); |
1d892719 JC |
479 | if (ret) |
480 | return ret; | |
481 | ||
482 | return len; | |
483 | } | |
484 | ||
485 | static int __iio_build_postfix(struct iio_chan_spec const *chan, | |
486 | bool generic, | |
487 | const char *postfix, | |
488 | char **result) | |
489 | { | |
490 | char *all_post; | |
491 | /* 3 options - generic, extend_name, modified - if generic, extend_name | |
492 | * and modified cannot apply.*/ | |
493 | ||
494 | if (generic || (!chan->modified && !chan->extend_name)) { | |
495 | all_post = kasprintf(GFP_KERNEL, "%s", postfix); | |
496 | } else if (chan->modified) { | |
497 | const char *intermediate; | |
498 | switch (chan->type) { | |
499 | case IIO_INTENSITY: | |
500 | intermediate | |
501 | = iio_modifier_names_light[chan->channel2]; | |
502 | break; | |
503 | case IIO_ACCEL: | |
504 | case IIO_GYRO: | |
505 | case IIO_MAGN: | |
506 | case IIO_INCLI: | |
507 | case IIO_ROT: | |
508 | case IIO_ANGL: | |
509 | intermediate | |
510 | = iio_modifier_names_axial[chan->channel2]; | |
511 | break; | |
512 | default: | |
513 | return -EINVAL; | |
514 | } | |
515 | if (chan->extend_name) | |
516 | all_post = kasprintf(GFP_KERNEL, "%s_%s_%s", | |
517 | intermediate, | |
518 | chan->extend_name, | |
519 | postfix); | |
520 | else | |
521 | all_post = kasprintf(GFP_KERNEL, "%s_%s", | |
522 | intermediate, | |
523 | postfix); | |
524 | } else | |
525 | all_post = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name, | |
526 | postfix); | |
527 | if (all_post == NULL) | |
528 | return -ENOMEM; | |
529 | *result = all_post; | |
530 | return 0; | |
531 | } | |
532 | ||
df9c1c42 | 533 | static |
1d892719 JC |
534 | int __iio_device_attr_init(struct device_attribute *dev_attr, |
535 | const char *postfix, | |
536 | struct iio_chan_spec const *chan, | |
537 | ssize_t (*readfunc)(struct device *dev, | |
538 | struct device_attribute *attr, | |
539 | char *buf), | |
540 | ssize_t (*writefunc)(struct device *dev, | |
541 | struct device_attribute *attr, | |
542 | const char *buf, | |
543 | size_t len), | |
544 | bool generic) | |
545 | { | |
546 | int ret; | |
547 | char *name_format, *full_postfix; | |
548 | sysfs_attr_init(&dev_attr->attr); | |
549 | ret = __iio_build_postfix(chan, generic, postfix, &full_postfix); | |
550 | if (ret) | |
847ec80b | 551 | goto error_ret; |
1d892719 JC |
552 | |
553 | /* Special case for types that uses both channel numbers in naming */ | |
554 | if (chan->type == IIO_IN_DIFF && !generic) | |
555 | name_format | |
556 | = kasprintf(GFP_KERNEL, "%s_%s", | |
557 | iio_chan_type_name_spec_complex[chan->type], | |
558 | full_postfix); | |
559 | else if (generic || !chan->indexed) | |
560 | name_format | |
561 | = kasprintf(GFP_KERNEL, "%s_%s", | |
562 | iio_chan_type_name_spec_shared[chan->type], | |
563 | full_postfix); | |
564 | else | |
565 | name_format | |
566 | = kasprintf(GFP_KERNEL, "%s%d_%s", | |
567 | iio_chan_type_name_spec_shared[chan->type], | |
568 | chan->channel, | |
569 | full_postfix); | |
570 | ||
571 | if (name_format == NULL) { | |
572 | ret = -ENOMEM; | |
573 | goto error_free_full_postfix; | |
574 | } | |
575 | dev_attr->attr.name = kasprintf(GFP_KERNEL, | |
576 | name_format, | |
577 | chan->channel, | |
578 | chan->channel2); | |
579 | if (dev_attr->attr.name == NULL) { | |
580 | ret = -ENOMEM; | |
581 | goto error_free_name_format; | |
582 | } | |
583 | ||
584 | if (readfunc) { | |
585 | dev_attr->attr.mode |= S_IRUGO; | |
586 | dev_attr->show = readfunc; | |
587 | } | |
588 | ||
589 | if (writefunc) { | |
590 | dev_attr->attr.mode |= S_IWUSR; | |
591 | dev_attr->store = writefunc; | |
592 | } | |
593 | kfree(name_format); | |
594 | kfree(full_postfix); | |
595 | ||
596 | return 0; | |
597 | ||
598 | error_free_name_format: | |
599 | kfree(name_format); | |
600 | error_free_full_postfix: | |
601 | kfree(full_postfix); | |
602 | error_ret: | |
603 | return ret; | |
604 | } | |
605 | ||
df9c1c42 | 606 | static void __iio_device_attr_deinit(struct device_attribute *dev_attr) |
1d892719 JC |
607 | { |
608 | kfree(dev_attr->attr.name); | |
609 | } | |
610 | ||
611 | int __iio_add_chan_devattr(const char *postfix, | |
612 | const char *group, | |
613 | struct iio_chan_spec const *chan, | |
614 | ssize_t (*readfunc)(struct device *dev, | |
615 | struct device_attribute *attr, | |
616 | char *buf), | |
617 | ssize_t (*writefunc)(struct device *dev, | |
618 | struct device_attribute *attr, | |
619 | const char *buf, | |
620 | size_t len), | |
621 | int mask, | |
622 | bool generic, | |
623 | struct device *dev, | |
624 | struct list_head *attr_list) | |
625 | { | |
626 | int ret; | |
627 | struct iio_dev_attr *iio_attr, *t; | |
628 | ||
629 | iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL); | |
630 | if (iio_attr == NULL) { | |
631 | ret = -ENOMEM; | |
632 | goto error_ret; | |
633 | } | |
634 | ret = __iio_device_attr_init(&iio_attr->dev_attr, | |
635 | postfix, chan, | |
636 | readfunc, writefunc, generic); | |
637 | if (ret) | |
638 | goto error_iio_dev_attr_free; | |
639 | iio_attr->c = chan; | |
640 | iio_attr->address = mask; | |
641 | list_for_each_entry(t, attr_list, l) | |
642 | if (strcmp(t->dev_attr.attr.name, | |
643 | iio_attr->dev_attr.attr.name) == 0) { | |
644 | if (!generic) | |
645 | dev_err(dev, "tried to double register : %s\n", | |
646 | t->dev_attr.attr.name); | |
647 | ret = -EBUSY; | |
648 | goto error_device_attr_deinit; | |
649 | } | |
650 | ||
651 | ret = sysfs_add_file_to_group(&dev->kobj, | |
652 | &iio_attr->dev_attr.attr, group); | |
653 | if (ret < 0) | |
654 | goto error_device_attr_deinit; | |
655 | ||
656 | list_add(&iio_attr->l, attr_list); | |
657 | ||
658 | return 0; | |
659 | ||
660 | error_device_attr_deinit: | |
661 | __iio_device_attr_deinit(&iio_attr->dev_attr); | |
662 | error_iio_dev_attr_free: | |
663 | kfree(iio_attr); | |
664 | error_ret: | |
665 | return ret; | |
666 | } | |
667 | ||
668 | static int iio_device_add_channel_sysfs(struct iio_dev *dev_info, | |
669 | struct iio_chan_spec const *chan) | |
670 | { | |
671 | int ret, i; | |
672 | ||
673 | ||
674 | if (chan->channel < 0) | |
675 | return 0; | |
676 | if (chan->processed_val) | |
677 | ret = __iio_add_chan_devattr("input", NULL, chan, | |
678 | &iio_read_channel_info, | |
679 | NULL, | |
680 | 0, | |
681 | 0, | |
682 | &dev_info->dev, | |
683 | &dev_info->channel_attr_list); | |
684 | else | |
685 | ret = __iio_add_chan_devattr("raw", NULL, chan, | |
686 | &iio_read_channel_info, | |
ae19178e MH |
687 | (chan->type == IIO_OUT ? |
688 | &iio_write_channel_info : NULL), | |
1d892719 JC |
689 | 0, |
690 | 0, | |
691 | &dev_info->dev, | |
692 | &dev_info->channel_attr_list); | |
693 | if (ret) | |
694 | goto error_ret; | |
695 | ||
696 | for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) { | |
697 | ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2], | |
698 | NULL, chan, | |
699 | &iio_read_channel_info, | |
700 | &iio_write_channel_info, | |
701 | (1 << i), | |
702 | !(i%2), | |
703 | &dev_info->dev, | |
704 | &dev_info->channel_attr_list); | |
705 | if (ret == -EBUSY && (i%2 == 0)) { | |
706 | ret = 0; | |
707 | continue; | |
708 | } | |
709 | if (ret < 0) | |
710 | goto error_ret; | |
711 | } | |
712 | error_ret: | |
713 | return ret; | |
714 | } | |
715 | ||
716 | static void iio_device_remove_and_free_read_attr(struct iio_dev *dev_info, | |
717 | struct iio_dev_attr *p) | |
718 | { | |
719 | sysfs_remove_file_from_group(&dev_info->dev.kobj, | |
720 | &p->dev_attr.attr, NULL); | |
721 | kfree(p->dev_attr.attr.name); | |
722 | kfree(p); | |
723 | } | |
724 | ||
1b732888 JC |
725 | static ssize_t iio_show_dev_name(struct device *dev, |
726 | struct device_attribute *attr, | |
727 | char *buf) | |
728 | { | |
729 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
730 | return sprintf(buf, "%s\n", indio_dev->name); | |
731 | } | |
732 | ||
733 | static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); | |
734 | ||
f7256877 JC |
735 | static struct attribute *iio_base_dummy_attrs[] = { |
736 | NULL | |
737 | }; | |
738 | static struct attribute_group iio_base_dummy_group = { | |
739 | .attrs = iio_base_dummy_attrs, | |
740 | }; | |
741 | ||
1d892719 JC |
742 | static int iio_device_register_sysfs(struct iio_dev *dev_info) |
743 | { | |
744 | int i, ret = 0; | |
745 | struct iio_dev_attr *p, *n; | |
746 | ||
f7256877 | 747 | if (dev_info->info->attrs) |
6fe8135f JC |
748 | ret = sysfs_create_group(&dev_info->dev.kobj, |
749 | dev_info->info->attrs); | |
f7256877 JC |
750 | else |
751 | ret = sysfs_create_group(&dev_info->dev.kobj, | |
752 | &iio_base_dummy_group); | |
753 | ||
754 | if (ret) { | |
755 | dev_err(dev_info->dev.parent, | |
756 | "Failed to register sysfs hooks\n"); | |
757 | goto error_ret; | |
847ec80b JC |
758 | } |
759 | ||
1d892719 JC |
760 | /* |
761 | * New channel registration method - relies on the fact a group does | |
762 | * not need to be initialized if it is name is NULL. | |
763 | */ | |
764 | INIT_LIST_HEAD(&dev_info->channel_attr_list); | |
765 | if (dev_info->channels) | |
766 | for (i = 0; i < dev_info->num_channels; i++) { | |
767 | ret = iio_device_add_channel_sysfs(dev_info, | |
768 | &dev_info | |
769 | ->channels[i]); | |
770 | if (ret < 0) | |
771 | goto error_clear_attrs; | |
772 | } | |
f7256877 | 773 | if (dev_info->name) { |
1b732888 JC |
774 | ret = sysfs_add_file_to_group(&dev_info->dev.kobj, |
775 | &dev_attr_name.attr, | |
776 | NULL); | |
777 | if (ret) | |
778 | goto error_clear_attrs; | |
779 | } | |
1d892719 | 780 | return 0; |
1b732888 | 781 | |
1d892719 JC |
782 | error_clear_attrs: |
783 | list_for_each_entry_safe(p, n, | |
784 | &dev_info->channel_attr_list, l) { | |
785 | list_del(&p->l); | |
786 | iio_device_remove_and_free_read_attr(dev_info, p); | |
787 | } | |
6fe8135f JC |
788 | if (dev_info->info->attrs) |
789 | sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs); | |
f7256877 JC |
790 | else |
791 | sysfs_remove_group(&dev_info->dev.kobj, &iio_base_dummy_group); | |
847ec80b JC |
792 | error_ret: |
793 | return ret; | |
1d892719 | 794 | |
847ec80b JC |
795 | } |
796 | ||
797 | static void iio_device_unregister_sysfs(struct iio_dev *dev_info) | |
798 | { | |
1d892719 JC |
799 | |
800 | struct iio_dev_attr *p, *n; | |
1b732888 JC |
801 | if (dev_info->name) |
802 | sysfs_remove_file_from_group(&dev_info->dev.kobj, | |
803 | &dev_attr_name.attr, | |
804 | NULL); | |
1d892719 JC |
805 | list_for_each_entry_safe(p, n, &dev_info->channel_attr_list, l) { |
806 | list_del(&p->l); | |
807 | iio_device_remove_and_free_read_attr(dev_info, p); | |
808 | } | |
809 | ||
6fe8135f JC |
810 | if (dev_info->info->attrs) |
811 | sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs); | |
f7256877 JC |
812 | else |
813 | sysfs_remove_group(&dev_info->dev.kobj, &iio_base_dummy_group); | |
847ec80b JC |
814 | } |
815 | ||
1d892719 JC |
816 | static const char * const iio_ev_type_text[] = { |
817 | [IIO_EV_TYPE_THRESH] = "thresh", | |
818 | [IIO_EV_TYPE_MAG] = "mag", | |
819 | [IIO_EV_TYPE_ROC] = "roc" | |
820 | }; | |
821 | ||
822 | static const char * const iio_ev_dir_text[] = { | |
823 | [IIO_EV_DIR_EITHER] = "either", | |
824 | [IIO_EV_DIR_RISING] = "rising", | |
825 | [IIO_EV_DIR_FALLING] = "falling" | |
826 | }; | |
827 | ||
828 | static ssize_t iio_ev_state_store(struct device *dev, | |
829 | struct device_attribute *attr, | |
830 | const char *buf, | |
831 | size_t len) | |
832 | { | |
833 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
aaf370db | 834 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
1d892719 | 835 | int ret; |
c74b0de1 JC |
836 | bool val; |
837 | ||
838 | ret = strtobool(buf, &val); | |
839 | if (ret < 0) | |
840 | return ret; | |
1d892719 | 841 | |
6fe8135f JC |
842 | ret = indio_dev->info->write_event_config(indio_dev, |
843 | this_attr->address, | |
844 | val); | |
1d892719 JC |
845 | return (ret < 0) ? ret : len; |
846 | } | |
847 | ||
848 | static ssize_t iio_ev_state_show(struct device *dev, | |
849 | struct device_attribute *attr, | |
850 | char *buf) | |
851 | { | |
852 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
aaf370db | 853 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
6fe8135f JC |
854 | int val = indio_dev->info->read_event_config(indio_dev, |
855 | this_attr->address); | |
1d892719 JC |
856 | |
857 | if (val < 0) | |
858 | return val; | |
859 | else | |
860 | return sprintf(buf, "%d\n", val); | |
861 | } | |
862 | ||
863 | static ssize_t iio_ev_value_show(struct device *dev, | |
864 | struct device_attribute *attr, | |
865 | char *buf) | |
866 | { | |
867 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
868 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
869 | int val, ret; | |
870 | ||
6fe8135f JC |
871 | ret = indio_dev->info->read_event_value(indio_dev, |
872 | this_attr->address, &val); | |
1d892719 JC |
873 | if (ret < 0) |
874 | return ret; | |
875 | ||
876 | return sprintf(buf, "%d\n", val); | |
877 | } | |
878 | ||
879 | static ssize_t iio_ev_value_store(struct device *dev, | |
880 | struct device_attribute *attr, | |
881 | const char *buf, | |
882 | size_t len) | |
883 | { | |
884 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
885 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
886 | unsigned long val; | |
887 | int ret; | |
888 | ||
889 | ret = strict_strtoul(buf, 10, &val); | |
890 | if (ret) | |
891 | return ret; | |
892 | ||
6fe8135f JC |
893 | ret = indio_dev->info->write_event_value(indio_dev, this_attr->address, |
894 | val); | |
1d892719 JC |
895 | if (ret < 0) |
896 | return ret; | |
897 | ||
898 | return len; | |
899 | } | |
900 | ||
1d892719 JC |
901 | static int iio_device_add_event_sysfs(struct iio_dev *dev_info, |
902 | struct iio_chan_spec const *chan) | |
903 | { | |
904 | ||
df9c1c42 | 905 | int ret = 0, i, mask = 0; |
1d892719 JC |
906 | char *postfix; |
907 | if (!chan->event_mask) | |
908 | return 0; | |
909 | ||
910 | for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) { | |
911 | postfix = kasprintf(GFP_KERNEL, "%s_%s_en", | |
912 | iio_ev_type_text[i/IIO_EV_TYPE_MAX], | |
913 | iio_ev_dir_text[i%IIO_EV_TYPE_MAX]); | |
914 | if (postfix == NULL) { | |
915 | ret = -ENOMEM; | |
916 | goto error_ret; | |
917 | } | |
918 | switch (chan->type) { | |
919 | /* Switch this to a table at some point */ | |
920 | case IIO_IN: | |
921 | mask = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, | |
922 | i/IIO_EV_TYPE_MAX, | |
923 | i%IIO_EV_TYPE_MAX); | |
924 | break; | |
925 | case IIO_ACCEL: | |
926 | mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel, | |
927 | i/IIO_EV_TYPE_MAX, | |
928 | i%IIO_EV_TYPE_MAX); | |
929 | break; | |
930 | case IIO_IN_DIFF: | |
931 | mask = IIO_MOD_EVENT_CODE(chan->type, chan->channel, | |
932 | chan->channel2, | |
933 | i/IIO_EV_TYPE_MAX, | |
934 | i%IIO_EV_TYPE_MAX); | |
935 | break; | |
936 | default: | |
937 | printk(KERN_INFO "currently unhandled type of event\n"); | |
9076faa9 | 938 | continue; |
1d892719 | 939 | } |
aaf370db JC |
940 | ret = __iio_add_chan_devattr(postfix, |
941 | NULL, | |
942 | chan, | |
943 | &iio_ev_state_show, | |
944 | iio_ev_state_store, | |
945 | mask, | |
946 | /*HACK. - limits us to one | |
947 | event interface - fix by | |
948 | extending the bitmask - but | |
949 | how far*/ | |
950 | 0, | |
6fe8135f | 951 | &dev_info->event_interfaces[0].dev, |
aaf370db JC |
952 | &dev_info->event_interfaces[0]. |
953 | dev_attr_list); | |
1d892719 JC |
954 | kfree(postfix); |
955 | if (ret) | |
956 | goto error_ret; | |
957 | ||
958 | postfix = kasprintf(GFP_KERNEL, "%s_%s_value", | |
959 | iio_ev_type_text[i/IIO_EV_TYPE_MAX], | |
960 | iio_ev_dir_text[i%IIO_EV_TYPE_MAX]); | |
961 | if (postfix == NULL) { | |
962 | ret = -ENOMEM; | |
963 | goto error_ret; | |
964 | } | |
965 | ret = __iio_add_chan_devattr(postfix, NULL, chan, | |
966 | iio_ev_value_show, | |
967 | iio_ev_value_store, | |
968 | mask, | |
969 | 0, | |
970 | &dev_info->event_interfaces[0] | |
971 | .dev, | |
972 | &dev_info->event_interfaces[0] | |
973 | .dev_attr_list); | |
974 | kfree(postfix); | |
975 | if (ret) | |
976 | goto error_ret; | |
977 | ||
978 | } | |
979 | ||
980 | error_ret: | |
981 | return ret; | |
982 | } | |
983 | ||
232b9cba JC |
984 | static inline void __iio_remove_event_config_attrs(struct iio_dev *dev_info, |
985 | int i) | |
1d892719 JC |
986 | { |
987 | struct iio_dev_attr *p, *n; | |
1d892719 | 988 | list_for_each_entry_safe(p, n, |
232b9cba | 989 | &dev_info->event_interfaces[i]. |
1d892719 JC |
990 | dev_attr_list, l) { |
991 | sysfs_remove_file_from_group(&dev_info | |
232b9cba | 992 | ->event_interfaces[i].dev.kobj, |
1d892719 | 993 | &p->dev_attr.attr, |
232b9cba | 994 | NULL); |
1d892719 JC |
995 | kfree(p->dev_attr.attr.name); |
996 | kfree(p); | |
997 | } | |
1d892719 JC |
998 | } |
999 | ||
847ec80b JC |
1000 | static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i) |
1001 | { | |
1d892719 | 1002 | int j; |
847ec80b | 1003 | int ret; |
232b9cba | 1004 | INIT_LIST_HEAD(&dev_info->event_interfaces[i].dev_attr_list); |
1d892719 JC |
1005 | /* Dynically created from the channels array */ |
1006 | if (dev_info->channels) { | |
1007 | for (j = 0; j < dev_info->num_channels; j++) { | |
1008 | ret = iio_device_add_event_sysfs(dev_info, | |
1009 | &dev_info | |
1010 | ->channels[j]); | |
1011 | if (ret) | |
1012 | goto error_clear_attrs; | |
1013 | } | |
1014 | } | |
847ec80b JC |
1015 | return 0; |
1016 | ||
1d892719 | 1017 | error_clear_attrs: |
232b9cba | 1018 | __iio_remove_event_config_attrs(dev_info, i); |
847ec80b JC |
1019 | |
1020 | return ret; | |
1021 | } | |
1022 | ||
847ec80b JC |
1023 | static int iio_device_register_eventset(struct iio_dev *dev_info) |
1024 | { | |
1025 | int ret = 0, i, j; | |
1026 | ||
6fe8135f | 1027 | if (dev_info->info->num_interrupt_lines == 0) |
847ec80b JC |
1028 | return 0; |
1029 | ||
1030 | dev_info->event_interfaces = | |
1031 | kzalloc(sizeof(struct iio_event_interface) | |
6fe8135f | 1032 | *dev_info->info->num_interrupt_lines, |
847ec80b JC |
1033 | GFP_KERNEL); |
1034 | if (dev_info->event_interfaces == NULL) { | |
1035 | ret = -ENOMEM; | |
1036 | goto error_ret; | |
1037 | } | |
1038 | ||
6fe8135f | 1039 | for (i = 0; i < dev_info->info->num_interrupt_lines; i++) { |
847ec80b | 1040 | ret = iio_setup_ev_int(&dev_info->event_interfaces[i], |
c74b0de1 JC |
1041 | dev_name(&dev_info->dev), |
1042 | i, | |
6fe8135f | 1043 | dev_info->info->driver_module, |
847ec80b JC |
1044 | &dev_info->dev); |
1045 | if (ret) { | |
1046 | dev_err(&dev_info->dev, | |
1047 | "Could not get chrdev interface\n"); | |
cc2439fd | 1048 | goto error_free_setup_event_lines; |
847ec80b | 1049 | } |
847ec80b | 1050 | |
5cba220b JC |
1051 | dev_set_drvdata(&dev_info->event_interfaces[i].dev, |
1052 | (void *)dev_info); | |
1d892719 | 1053 | |
6fe8135f | 1054 | if (dev_info->info->event_attrs != NULL) |
1d892719 JC |
1055 | ret = sysfs_create_group(&dev_info |
1056 | ->event_interfaces[i] | |
1057 | .dev.kobj, | |
6fe8135f JC |
1058 | &dev_info->info |
1059 | ->event_attrs[i]); | |
5cba220b | 1060 | |
847ec80b JC |
1061 | if (ret) { |
1062 | dev_err(&dev_info->dev, | |
1063 | "Failed to register sysfs for event attrs"); | |
cc2439fd JC |
1064 | iio_free_ev_int(&dev_info->event_interfaces[i]); |
1065 | goto error_free_setup_event_lines; | |
847ec80b | 1066 | } |
847ec80b | 1067 | ret = __iio_add_event_config_attrs(dev_info, i); |
cc2439fd JC |
1068 | if (ret) { |
1069 | if (dev_info->info->event_attrs != NULL) | |
1070 | sysfs_remove_group(&dev_info | |
1071 | ->event_interfaces[i] | |
1072 | .dev.kobj, | |
1073 | &dev_info->info | |
1074 | ->event_attrs[i]); | |
1075 | iio_free_ev_int(&dev_info->event_interfaces[i]); | |
1076 | goto error_free_setup_event_lines; | |
1077 | } | |
847ec80b JC |
1078 | } |
1079 | ||
1080 | return 0; | |
1081 | ||
cc2439fd JC |
1082 | error_free_setup_event_lines: |
1083 | for (j = 0; j < i; j++) { | |
1084 | __iio_remove_event_config_attrs(dev_info, j); | |
6fe8135f | 1085 | if (dev_info->info->event_attrs != NULL) |
1d892719 | 1086 | sysfs_remove_group(&dev_info |
cc2439fd JC |
1087 | ->event_interfaces[j].dev.kobj, |
1088 | &dev_info->info->event_attrs[j]); | |
847ec80b | 1089 | iio_free_ev_int(&dev_info->event_interfaces[j]); |
cc2439fd | 1090 | } |
847ec80b JC |
1091 | kfree(dev_info->event_interfaces); |
1092 | error_ret: | |
1093 | ||
1094 | return ret; | |
1095 | } | |
1096 | ||
1097 | static void iio_device_unregister_eventset(struct iio_dev *dev_info) | |
1098 | { | |
1099 | int i; | |
1100 | ||
6fe8135f | 1101 | if (dev_info->info->num_interrupt_lines == 0) |
847ec80b | 1102 | return; |
6fe8135f | 1103 | for (i = 0; i < dev_info->info->num_interrupt_lines; i++) { |
1d892719 | 1104 | __iio_remove_event_config_attrs(dev_info, i); |
6fe8135f | 1105 | if (dev_info->info->event_attrs != NULL) |
1d892719 JC |
1106 | sysfs_remove_group(&dev_info |
1107 | ->event_interfaces[i].dev.kobj, | |
6fe8135f | 1108 | &dev_info->info->event_attrs[i]); |
847ec80b | 1109 | iio_free_ev_int(&dev_info->event_interfaces[i]); |
cc2439fd | 1110 | } |
847ec80b JC |
1111 | kfree(dev_info->event_interfaces); |
1112 | } | |
1113 | ||
1114 | static void iio_dev_release(struct device *device) | |
1115 | { | |
df9c1c42 | 1116 | struct iio_dev *dev_info = container_of(device, struct iio_dev, dev); |
847ec80b | 1117 | iio_put(); |
df9c1c42 | 1118 | kfree(dev_info); |
847ec80b JC |
1119 | } |
1120 | ||
1121 | static struct device_type iio_dev_type = { | |
1122 | .name = "iio_device", | |
1123 | .release = iio_dev_release, | |
1124 | }; | |
1125 | ||
6f7c8ee5 | 1126 | struct iio_dev *iio_allocate_device(int sizeof_priv) |
847ec80b | 1127 | { |
6f7c8ee5 JC |
1128 | struct iio_dev *dev; |
1129 | size_t alloc_size; | |
1130 | ||
1131 | alloc_size = sizeof(struct iio_dev); | |
1132 | if (sizeof_priv) { | |
1133 | alloc_size = ALIGN(alloc_size, IIO_ALIGN); | |
1134 | alloc_size += sizeof_priv; | |
1135 | } | |
1136 | /* ensure 32-byte alignment of whole construct ? */ | |
1137 | alloc_size += IIO_ALIGN - 1; | |
1138 | ||
1139 | dev = kzalloc(alloc_size, GFP_KERNEL); | |
847ec80b JC |
1140 | |
1141 | if (dev) { | |
1142 | dev->dev.type = &iio_dev_type; | |
5aaaeba8 | 1143 | dev->dev.bus = &iio_bus_type; |
847ec80b JC |
1144 | device_initialize(&dev->dev); |
1145 | dev_set_drvdata(&dev->dev, (void *)dev); | |
1146 | mutex_init(&dev->mlock); | |
1147 | iio_get(); | |
1148 | } | |
1149 | ||
1150 | return dev; | |
1151 | } | |
1152 | EXPORT_SYMBOL(iio_allocate_device); | |
1153 | ||
1154 | void iio_free_device(struct iio_dev *dev) | |
1155 | { | |
1156 | if (dev) | |
1157 | iio_put_device(dev); | |
1158 | } | |
1159 | EXPORT_SYMBOL(iio_free_device); | |
1160 | ||
1161 | int iio_device_register(struct iio_dev *dev_info) | |
1162 | { | |
1163 | int ret; | |
1164 | ||
c74b0de1 JC |
1165 | dev_info->id = iio_get_new_ida_val(&iio_ida); |
1166 | if (dev_info->id < 0) { | |
1167 | ret = dev_info->id; | |
847ec80b JC |
1168 | dev_err(&dev_info->dev, "Failed to get id\n"); |
1169 | goto error_ret; | |
1170 | } | |
1171 | dev_set_name(&dev_info->dev, "device%d", dev_info->id); | |
1172 | ||
1173 | ret = device_add(&dev_info->dev); | |
1174 | if (ret) | |
b156cf70 | 1175 | goto error_free_ida; |
847ec80b JC |
1176 | ret = iio_device_register_sysfs(dev_info); |
1177 | if (ret) { | |
1178 | dev_err(dev_info->dev.parent, | |
1179 | "Failed to register sysfs interfaces\n"); | |
1180 | goto error_del_device; | |
1181 | } | |
1182 | ret = iio_device_register_eventset(dev_info); | |
1183 | if (ret) { | |
1184 | dev_err(dev_info->dev.parent, | |
c849d253 | 1185 | "Failed to register event set\n"); |
847ec80b JC |
1186 | goto error_free_sysfs; |
1187 | } | |
1188 | if (dev_info->modes & INDIO_RING_TRIGGERED) | |
1189 | iio_device_register_trigger_consumer(dev_info); | |
1190 | ||
1191 | return 0; | |
1192 | ||
1193 | error_free_sysfs: | |
1194 | iio_device_unregister_sysfs(dev_info); | |
1195 | error_del_device: | |
1196 | device_del(&dev_info->dev); | |
b156cf70 | 1197 | error_free_ida: |
c74b0de1 | 1198 | iio_free_ida_val(&iio_ida, dev_info->id); |
847ec80b JC |
1199 | error_ret: |
1200 | return ret; | |
1201 | } | |
1202 | EXPORT_SYMBOL(iio_device_register); | |
1203 | ||
1204 | void iio_device_unregister(struct iio_dev *dev_info) | |
1205 | { | |
1206 | if (dev_info->modes & INDIO_RING_TRIGGERED) | |
1207 | iio_device_unregister_trigger_consumer(dev_info); | |
1208 | iio_device_unregister_eventset(dev_info); | |
1209 | iio_device_unregister_sysfs(dev_info); | |
c74b0de1 | 1210 | iio_free_ida_val(&iio_ida, dev_info->id); |
847ec80b JC |
1211 | device_unregister(&dev_info->dev); |
1212 | } | |
1213 | EXPORT_SYMBOL(iio_device_unregister); | |
1214 | ||
1215 | void iio_put(void) | |
1216 | { | |
1217 | module_put(THIS_MODULE); | |
1218 | } | |
1219 | ||
1220 | void iio_get(void) | |
1221 | { | |
1222 | __module_get(THIS_MODULE); | |
1223 | } | |
1224 | ||
1225 | subsys_initcall(iio_init); | |
1226 | module_exit(iio_exit); | |
1227 | ||
1228 | MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); | |
1229 | MODULE_DESCRIPTION("Industrial I/O core"); | |
1230 | MODULE_LICENSE("GPL"); |