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