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