Commit | Line | Data |
---|---|---|
c57f1ba7 JC |
1 | /* IIO - useful set of util functionality |
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 | */ | |
9d8ae6c8 JC |
9 | #include <string.h> |
10 | #include <stdlib.h> | |
e58537cc JC |
11 | #include <stdio.h> |
12 | #include <stdint.h> | |
bc9f35db | 13 | #include <dirent.h> |
bb23378c | 14 | #include <errno.h> |
bdcb31d0 RD |
15 | #include <ctype.h> |
16 | #include "iio_utils.h" | |
c57f1ba7 | 17 | |
9d8ae6c8 JC |
18 | const char *iio_dir = "/sys/bus/iio/devices/"; |
19 | ||
d9d7b990 IT |
20 | static char * const iio_direction[] = { |
21 | "in", | |
22 | "out", | |
23 | }; | |
24 | ||
e58537cc JC |
25 | /** |
26 | * iioutils_break_up_name() - extract generic name from full channel name | |
27 | * @full_name: the full channel name | |
28 | * @generic_name: the output generic channel name | |
5dc65d79 HK |
29 | * |
30 | * Returns 0 on success, or a negative error code if string extraction failed. | |
e58537cc | 31 | **/ |
7663a4aa | 32 | int iioutils_break_up_name(const char *full_name, char **generic_name) |
e58537cc JC |
33 | { |
34 | char *current; | |
35 | char *w, *r; | |
d9d7b990 | 36 | char *working, *prefix = ""; |
e9e45b43 | 37 | int i, ret; |
d9d7b990 | 38 | |
34cbea19 | 39 | for (i = 0; i < ARRAY_SIZE(iio_direction); i++) |
d9d7b990 IT |
40 | if (!strncmp(full_name, iio_direction[i], |
41 | strlen(iio_direction[i]))) { | |
42 | prefix = iio_direction[i]; | |
43 | break; | |
44 | } | |
79bdd48a | 45 | |
d9d7b990 | 46 | current = strdup(full_name + strlen(prefix) + 1); |
e9e45b43 HK |
47 | if (!current) |
48 | return -ENOMEM; | |
49 | ||
e58537cc | 50 | working = strtok(current, "_\0"); |
53118557 HK |
51 | if (!working) { |
52 | free(current); | |
53 | return -EINVAL; | |
54 | } | |
d9d7b990 | 55 | |
e58537cc JC |
56 | w = working; |
57 | r = working; | |
58 | ||
8b68bb20 | 59 | while (*r != '\0') { |
e58537cc JC |
60 | if (!isdigit(*r)) { |
61 | *w = *r; | |
62 | w++; | |
63 | } | |
7663a4aa | 64 | |
e58537cc JC |
65 | r++; |
66 | } | |
67 | *w = '\0'; | |
e9e45b43 | 68 | ret = asprintf(generic_name, "%s_%s", prefix, working); |
e58537cc JC |
69 | free(current); |
70 | ||
e9e45b43 | 71 | return (ret == -1) ? -ENOMEM : 0; |
e58537cc JC |
72 | } |
73 | ||
e58537cc JC |
74 | /** |
75 | * iioutils_get_type() - find and process _type attribute data | |
76 | * @is_signed: output whether channel is signed | |
77 | * @bytes: output how many bytes the channel storage occupies | |
5dc65d79 HK |
78 | * @bits_used: output number of valid bits of data |
79 | * @shift: output amount of bits to shift right data before applying bit mask | |
e58537cc | 80 | * @mask: output a bit mask for the raw data |
5dc65d79 HK |
81 | * @be: output if data in big endian |
82 | * @device_dir: the IIO device directory | |
e58537cc JC |
83 | * @name: the channel name |
84 | * @generic_name: the channel type name | |
5dc65d79 HK |
85 | * |
86 | * Returns a value >= 0 on success, otherwise a negative error code. | |
e58537cc | 87 | **/ |
7663a4aa HK |
88 | int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used, |
89 | unsigned *shift, uint64_t *mask, unsigned *be, | |
90 | const char *device_dir, const char *name, | |
91 | const char *generic_name) | |
e58537cc JC |
92 | { |
93 | FILE *sysfsfp; | |
94 | int ret; | |
95 | DIR *dp; | |
96 | char *scan_el_dir, *builtname, *builtname_generic, *filename = 0; | |
117cf8b7 | 97 | char signchar, endianchar; |
fc7f95a9 | 98 | unsigned padint; |
e58537cc JC |
99 | const struct dirent *ent; |
100 | ||
101 | ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); | |
0e799878 HK |
102 | if (ret < 0) |
103 | return -ENOMEM; | |
104 | ||
e58537cc JC |
105 | ret = asprintf(&builtname, FORMAT_TYPE_FILE, name); |
106 | if (ret < 0) { | |
107 | ret = -ENOMEM; | |
108 | goto error_free_scan_el_dir; | |
109 | } | |
110 | ret = asprintf(&builtname_generic, FORMAT_TYPE_FILE, generic_name); | |
111 | if (ret < 0) { | |
112 | ret = -ENOMEM; | |
113 | goto error_free_builtname; | |
114 | } | |
115 | ||
116 | dp = opendir(scan_el_dir); | |
ff1ac639 | 117 | if (!dp) { |
e58537cc JC |
118 | ret = -errno; |
119 | goto error_free_builtname_generic; | |
120 | } | |
7663a4aa | 121 | |
53118557 | 122 | ret = -ENOENT; |
ff1ac639 | 123 | while (ent = readdir(dp), ent) |
e58537cc JC |
124 | /* |
125 | * Do we allow devices to override a generic name with | |
126 | * a specific one? | |
127 | */ | |
128 | if ((strcmp(builtname, ent->d_name) == 0) || | |
129 | (strcmp(builtname_generic, ent->d_name) == 0)) { | |
130 | ret = asprintf(&filename, | |
131 | "%s/%s", scan_el_dir, ent->d_name); | |
132 | if (ret < 0) { | |
133 | ret = -ENOMEM; | |
134 | goto error_closedir; | |
135 | } | |
7663a4aa | 136 | |
e58537cc | 137 | sysfsfp = fopen(filename, "r"); |
ff1ac639 | 138 | if (!sysfsfp) { |
e58537cc | 139 | ret = -errno; |
d9abc615 CO |
140 | fprintf(stderr, "failed to open %s\n", |
141 | filename); | |
e58537cc JC |
142 | goto error_free_filename; |
143 | } | |
a7f7c364 JC |
144 | |
145 | ret = fscanf(sysfsfp, | |
146 | "%ce:%c%u/%u>>%u", | |
147 | &endianchar, | |
148 | &signchar, | |
149 | bits_used, | |
150 | &padint, shift); | |
151 | if (ret < 0) { | |
578f737d | 152 | ret = -errno; |
d9abc615 CO |
153 | fprintf(stderr, |
154 | "failed to pass scan type description\n"); | |
578f737d | 155 | goto error_close_sysfsfp; |
dc8b5d6e HK |
156 | } else if (ret != 5) { |
157 | ret = -EIO; | |
d9abc615 CO |
158 | fprintf(stderr, |
159 | "scan type description didn't match\n"); | |
dc8b5d6e | 160 | goto error_close_sysfsfp; |
a7f7c364 | 161 | } |
7663a4aa | 162 | |
117cf8b7 | 163 | *be = (endianchar == 'b'); |
e58537cc | 164 | *bytes = padint / 8; |
fc7f95a9 | 165 | if (*bits_used == 64) |
e58537cc JC |
166 | *mask = ~0; |
167 | else | |
ae067cb6 | 168 | *mask = (1ULL << *bits_used) - 1; |
7663a4aa | 169 | |
33ebcb21 | 170 | *is_signed = (signchar == 's'); |
53118557 HK |
171 | if (fclose(sysfsfp)) { |
172 | ret = -errno; | |
d9abc615 CO |
173 | fprintf(stderr, "Failed to close %s\n", |
174 | filename); | |
53118557 HK |
175 | goto error_free_filename; |
176 | } | |
177 | ||
ace76e42 | 178 | sysfsfp = 0; |
a7f7c364 | 179 | free(filename); |
a7f7c364 | 180 | filename = 0; |
e58537cc | 181 | } |
7663a4aa | 182 | |
578f737d PM |
183 | error_close_sysfsfp: |
184 | if (sysfsfp) | |
53118557 HK |
185 | if (fclose(sysfsfp)) |
186 | perror("iioutils_get_type(): Failed to close file"); | |
187 | ||
e58537cc JC |
188 | error_free_filename: |
189 | if (filename) | |
190 | free(filename); | |
7663a4aa | 191 | |
e58537cc | 192 | error_closedir: |
53118557 HK |
193 | if (closedir(dp) == -1) |
194 | perror("iioutils_get_type(): Failed to close directory"); | |
195 | ||
e58537cc JC |
196 | error_free_builtname_generic: |
197 | free(builtname_generic); | |
198 | error_free_builtname: | |
199 | free(builtname); | |
200 | error_free_scan_el_dir: | |
201 | free(scan_el_dir); | |
0e799878 | 202 | |
e58537cc JC |
203 | return ret; |
204 | } | |
205 | ||
5dc65d79 HK |
206 | /** |
207 | * iioutils_get_param_float() - read a float value from a channel parameter | |
208 | * @output: output the float value | |
209 | * @param_name: the parameter name to read | |
210 | * @device_dir: the IIO device directory in sysfs | |
211 | * @name: the channel name | |
212 | * @generic_name: the channel type name | |
213 | * | |
214 | * Returns a value >= 0 on success, otherwise a negative error code. | |
215 | **/ | |
7663a4aa HK |
216 | int iioutils_get_param_float(float *output, const char *param_name, |
217 | const char *device_dir, const char *name, | |
218 | const char *generic_name) | |
e58537cc JC |
219 | { |
220 | FILE *sysfsfp; | |
221 | int ret; | |
222 | DIR *dp; | |
223 | char *builtname, *builtname_generic; | |
224 | char *filename = NULL; | |
225 | const struct dirent *ent; | |
226 | ||
227 | ret = asprintf(&builtname, "%s_%s", name, param_name); | |
0e799878 HK |
228 | if (ret < 0) |
229 | return -ENOMEM; | |
230 | ||
e58537cc JC |
231 | ret = asprintf(&builtname_generic, |
232 | "%s_%s", generic_name, param_name); | |
233 | if (ret < 0) { | |
234 | ret = -ENOMEM; | |
235 | goto error_free_builtname; | |
236 | } | |
7663a4aa | 237 | |
e58537cc | 238 | dp = opendir(device_dir); |
ff1ac639 | 239 | if (!dp) { |
e58537cc JC |
240 | ret = -errno; |
241 | goto error_free_builtname_generic; | |
242 | } | |
7663a4aa | 243 | |
53118557 | 244 | ret = -ENOENT; |
ff1ac639 | 245 | while (ent = readdir(dp), ent) |
e58537cc JC |
246 | if ((strcmp(builtname, ent->d_name) == 0) || |
247 | (strcmp(builtname_generic, ent->d_name) == 0)) { | |
248 | ret = asprintf(&filename, | |
249 | "%s/%s", device_dir, ent->d_name); | |
250 | if (ret < 0) { | |
251 | ret = -ENOMEM; | |
252 | goto error_closedir; | |
253 | } | |
7663a4aa | 254 | |
e58537cc JC |
255 | sysfsfp = fopen(filename, "r"); |
256 | if (!sysfsfp) { | |
257 | ret = -errno; | |
258 | goto error_free_filename; | |
259 | } | |
7663a4aa | 260 | |
53118557 HK |
261 | errno = 0; |
262 | if (fscanf(sysfsfp, "%f", output) != 1) | |
263 | ret = errno ? -errno : -ENODATA; | |
264 | ||
e58537cc JC |
265 | break; |
266 | } | |
267 | error_free_filename: | |
268 | if (filename) | |
269 | free(filename); | |
7663a4aa | 270 | |
e58537cc | 271 | error_closedir: |
53118557 HK |
272 | if (closedir(dp) == -1) |
273 | perror("iioutils_get_param_float(): Failed to close directory"); | |
274 | ||
e58537cc JC |
275 | error_free_builtname_generic: |
276 | free(builtname_generic); | |
277 | error_free_builtname: | |
278 | free(builtname); | |
0e799878 | 279 | |
e58537cc JC |
280 | return ret; |
281 | } | |
282 | ||
8b68bb20 | 283 | /** |
5dc65d79 HK |
284 | * bsort_channel_array_by_index() - sort the array in index order |
285 | * @ci_array: the iio_channel_info array to be sorted | |
286 | * @cnt: the amount of array elements | |
8b68bb20 MH |
287 | **/ |
288 | ||
95ddd3f4 | 289 | void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt) |
8b68bb20 | 290 | { |
8b68bb20 MH |
291 | struct iio_channel_info temp; |
292 | int x, y; | |
293 | ||
294 | for (x = 0; x < cnt; x++) | |
295 | for (y = 0; y < (cnt - 1); y++) | |
95ddd3f4 JAS |
296 | if (ci_array[y].index > ci_array[y + 1].index) { |
297 | temp = ci_array[y + 1]; | |
298 | ci_array[y + 1] = ci_array[y]; | |
299 | ci_array[y] = temp; | |
8b68bb20 MH |
300 | } |
301 | } | |
e58537cc JC |
302 | |
303 | /** | |
304 | * build_channel_array() - function to figure out what channels are present | |
305 | * @device_dir: the IIO device directory in sysfs | |
5dc65d79 HK |
306 | * @ci_array: output the resulting array of iio_channel_info |
307 | * @counter: output the amount of array elements | |
308 | * | |
309 | * Returns 0 on success, otherwise a negative error code. | |
e58537cc | 310 | **/ |
bdcb31d0 | 311 | int build_channel_array(const char *device_dir, |
7663a4aa | 312 | struct iio_channel_info **ci_array, int *counter) |
e58537cc JC |
313 | { |
314 | DIR *dp; | |
315 | FILE *sysfsfp; | |
1e7c3478 | 316 | int count = 0, i; |
e58537cc JC |
317 | struct iio_channel_info *current; |
318 | int ret; | |
319 | const struct dirent *ent; | |
320 | char *scan_el_dir; | |
321 | char *filename; | |
322 | ||
323 | *counter = 0; | |
324 | ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); | |
0e799878 HK |
325 | if (ret < 0) |
326 | return -ENOMEM; | |
327 | ||
e58537cc | 328 | dp = opendir(scan_el_dir); |
ff1ac639 | 329 | if (!dp) { |
e58537cc JC |
330 | ret = -errno; |
331 | goto error_free_name; | |
332 | } | |
7663a4aa | 333 | |
ff1ac639 | 334 | while (ent = readdir(dp), ent) |
e58537cc JC |
335 | if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"), |
336 | "_en") == 0) { | |
337 | ret = asprintf(&filename, | |
338 | "%s/%s", scan_el_dir, ent->d_name); | |
339 | if (ret < 0) { | |
340 | ret = -ENOMEM; | |
341 | goto error_close_dir; | |
342 | } | |
7663a4aa | 343 | |
e58537cc | 344 | sysfsfp = fopen(filename, "r"); |
ff1ac639 | 345 | if (!sysfsfp) { |
e58537cc JC |
346 | ret = -errno; |
347 | free(filename); | |
348 | goto error_close_dir; | |
349 | } | |
7663a4aa | 350 | |
53118557 HK |
351 | errno = 0; |
352 | if (fscanf(sysfsfp, "%i", &ret) != 1) { | |
353 | ret = errno ? -errno : -ENODATA; | |
354 | if (fclose(sysfsfp)) | |
355 | perror("build_channel_array(): Failed to close file"); | |
356 | ||
357 | free(filename); | |
358 | goto error_close_dir; | |
359 | } | |
e58537cc JC |
360 | if (ret == 1) |
361 | (*counter)++; | |
7663a4aa | 362 | |
53118557 HK |
363 | if (fclose(sysfsfp)) { |
364 | ret = -errno; | |
365 | free(filename); | |
366 | goto error_close_dir; | |
367 | } | |
368 | ||
e58537cc JC |
369 | free(filename); |
370 | } | |
7663a4aa | 371 | |
8b68bb20 | 372 | *ci_array = malloc(sizeof(**ci_array) * (*counter)); |
ff1ac639 | 373 | if (!*ci_array) { |
e58537cc JC |
374 | ret = -ENOMEM; |
375 | goto error_close_dir; | |
376 | } | |
7663a4aa | 377 | |
e58537cc | 378 | seekdir(dp, 0); |
ff1ac639 | 379 | while (ent = readdir(dp), ent) { |
e58537cc JC |
380 | if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"), |
381 | "_en") == 0) { | |
66c65d90 | 382 | int current_enabled = 0; |
79bdd48a | 383 | |
e58537cc JC |
384 | current = &(*ci_array)[count++]; |
385 | ret = asprintf(&filename, | |
386 | "%s/%s", scan_el_dir, ent->d_name); | |
387 | if (ret < 0) { | |
388 | ret = -ENOMEM; | |
389 | /* decrement count to avoid freeing name */ | |
390 | count--; | |
391 | goto error_cleanup_array; | |
392 | } | |
7663a4aa | 393 | |
e58537cc | 394 | sysfsfp = fopen(filename, "r"); |
ff1ac639 | 395 | if (!sysfsfp) { |
e58537cc | 396 | ret = -errno; |
2b6a6e67 | 397 | free(filename); |
121b5e50 | 398 | count--; |
e58537cc JC |
399 | goto error_cleanup_array; |
400 | } | |
7663a4aa | 401 | |
53118557 HK |
402 | errno = 0; |
403 | if (fscanf(sysfsfp, "%i", ¤t_enabled) != 1) { | |
404 | ret = errno ? -errno : -ENODATA; | |
405 | free(filename); | |
406 | count--; | |
407 | goto error_cleanup_array; | |
408 | } | |
409 | ||
410 | if (fclose(sysfsfp)) { | |
411 | ret = -errno; | |
412 | free(filename); | |
413 | count--; | |
414 | goto error_cleanup_array; | |
415 | } | |
8b68bb20 | 416 | |
66c65d90 | 417 | if (!current_enabled) { |
8b68bb20 MH |
418 | free(filename); |
419 | count--; | |
420 | continue; | |
421 | } | |
422 | ||
e58537cc JC |
423 | current->scale = 1.0; |
424 | current->offset = 0; | |
425 | current->name = strndup(ent->d_name, | |
426 | strlen(ent->d_name) - | |
427 | strlen("_en")); | |
ff1ac639 | 428 | if (!current->name) { |
e58537cc JC |
429 | free(filename); |
430 | ret = -ENOMEM; | |
121b5e50 | 431 | count--; |
e58537cc JC |
432 | goto error_cleanup_array; |
433 | } | |
7663a4aa | 434 | |
e58537cc JC |
435 | /* Get the generic and specific name elements */ |
436 | ret = iioutils_break_up_name(current->name, | |
437 | ¤t->generic_name); | |
438 | if (ret) { | |
439 | free(filename); | |
121b5e50 HK |
440 | free(current->name); |
441 | count--; | |
e58537cc JC |
442 | goto error_cleanup_array; |
443 | } | |
7663a4aa | 444 | |
e58537cc JC |
445 | ret = asprintf(&filename, |
446 | "%s/%s_index", | |
447 | scan_el_dir, | |
448 | current->name); | |
449 | if (ret < 0) { | |
450 | free(filename); | |
451 | ret = -ENOMEM; | |
452 | goto error_cleanup_array; | |
453 | } | |
7663a4aa | 454 | |
e58537cc | 455 | sysfsfp = fopen(filename, "r"); |
ff1ac639 | 456 | if (!sysfsfp) { |
53118557 | 457 | ret = -errno; |
d9abc615 CO |
458 | fprintf(stderr, "failed to open %s\n", |
459 | filename); | |
53118557 HK |
460 | free(filename); |
461 | goto error_cleanup_array; | |
462 | } | |
463 | ||
464 | errno = 0; | |
465 | if (fscanf(sysfsfp, "%u", ¤t->index) != 1) { | |
466 | ret = errno ? -errno : -ENODATA; | |
467 | if (fclose(sysfsfp)) | |
468 | perror("build_channel_array(): Failed to close file"); | |
469 | ||
470 | free(filename); | |
471 | goto error_cleanup_array; | |
472 | } | |
473 | ||
474 | if (fclose(sysfsfp)) { | |
475 | ret = -errno; | |
476 | free(filename); | |
477 | goto error_cleanup_array; | |
478 | } | |
479 | ||
e58537cc JC |
480 | free(filename); |
481 | /* Find the scale */ | |
482 | ret = iioutils_get_param_float(¤t->scale, | |
483 | "scale", | |
484 | device_dir, | |
485 | current->name, | |
486 | current->generic_name); | |
7868dfd2 | 487 | if ((ret < 0) && (ret != -ENOENT)) |
e58537cc | 488 | goto error_cleanup_array; |
7663a4aa | 489 | |
e58537cc JC |
490 | ret = iioutils_get_param_float(¤t->offset, |
491 | "offset", | |
492 | device_dir, | |
493 | current->name, | |
494 | current->generic_name); | |
7868dfd2 | 495 | if ((ret < 0) && (ret != -ENOENT)) |
e58537cc | 496 | goto error_cleanup_array; |
7663a4aa | 497 | |
e58537cc JC |
498 | ret = iioutils_get_type(¤t->is_signed, |
499 | ¤t->bytes, | |
500 | ¤t->bits_used, | |
52615d47 | 501 | ¤t->shift, |
e58537cc | 502 | ¤t->mask, |
117cf8b7 | 503 | ¤t->be, |
e58537cc JC |
504 | device_dir, |
505 | current->name, | |
506 | current->generic_name); | |
53118557 HK |
507 | if (ret < 0) |
508 | goto error_cleanup_array; | |
e58537cc JC |
509 | } |
510 | } | |
8b68bb20 | 511 | |
53118557 HK |
512 | if (closedir(dp) == -1) { |
513 | ret = -errno; | |
514 | goto error_cleanup_array; | |
515 | } | |
516 | ||
66dd08fd | 517 | free(scan_el_dir); |
8b68bb20 | 518 | /* reorder so that the array is in index order */ |
95ddd3f4 | 519 | bsort_channel_array_by_index(*ci_array, *counter); |
e58537cc JC |
520 | |
521 | return 0; | |
522 | ||
523 | error_cleanup_array: | |
63f05c85 | 524 | for (i = count - 1; i >= 0; i--) { |
e58537cc | 525 | free((*ci_array)[i].name); |
63f05c85 HK |
526 | free((*ci_array)[i].generic_name); |
527 | } | |
e58537cc | 528 | free(*ci_array); |
6b20f406 JAS |
529 | *ci_array = NULL; |
530 | *counter = 0; | |
e58537cc | 531 | error_close_dir: |
53118557 HK |
532 | if (dp) |
533 | if (closedir(dp) == -1) | |
534 | perror("build_channel_array(): Failed to close dir"); | |
535 | ||
e58537cc JC |
536 | error_free_name: |
537 | free(scan_el_dir); | |
0e799878 | 538 | |
e58537cc JC |
539 | return ret; |
540 | } | |
541 | ||
5e37c523 | 542 | static int calc_digits(int num) |
096f9b86 HK |
543 | { |
544 | int count = 0; | |
545 | ||
546 | while (num != 0) { | |
547 | num /= 10; | |
548 | count++; | |
549 | } | |
550 | ||
551 | return count; | |
552 | } | |
553 | ||
9d8ae6c8 JC |
554 | /** |
555 | * find_type_by_name() - function to match top level types by name | |
556 | * @name: top level type instance name | |
5dc65d79 | 557 | * @type: the type of top level instance being searched |
9d8ae6c8 | 558 | * |
5dc65d79 HK |
559 | * Returns the device number of a matched IIO device on success, otherwise a |
560 | * negative error code. | |
9d8ae6c8 JC |
561 | * Typical types this is used for are device and trigger. |
562 | **/ | |
bdcb31d0 | 563 | int find_type_by_name(const char *name, const char *type) |
c57f1ba7 | 564 | { |
c57f1ba7 | 565 | const struct dirent *ent; |
096f9b86 | 566 | int number, numstrlen, ret; |
c57f1ba7 | 567 | |
a9d7acc8 | 568 | FILE *namefp; |
c57f1ba7 | 569 | DIR *dp; |
9d8ae6c8 JC |
570 | char thisname[IIO_MAX_NAME_LENGTH]; |
571 | char *filename; | |
9d8ae6c8 | 572 | |
c57f1ba7 | 573 | dp = opendir(iio_dir); |
ff1ac639 | 574 | if (!dp) { |
d9abc615 | 575 | fprintf(stderr, "No industrialio devices available\n"); |
9d8ae6c8 | 576 | return -ENODEV; |
c57f1ba7 | 577 | } |
9d8ae6c8 | 578 | |
ff1ac639 | 579 | while (ent = readdir(dp), ent) { |
c57f1ba7 | 580 | if (strcmp(ent->d_name, ".") != 0 && |
7663a4aa HK |
581 | strcmp(ent->d_name, "..") != 0 && |
582 | strlen(ent->d_name) > strlen(type) && | |
583 | strncmp(ent->d_name, type, strlen(type)) == 0) { | |
096f9b86 HK |
584 | errno = 0; |
585 | ret = sscanf(ent->d_name + strlen(type), "%d", &number); | |
586 | if (ret < 0) { | |
587 | ret = -errno; | |
d9abc615 CO |
588 | fprintf(stderr, |
589 | "failed to read element number\n"); | |
096f9b86 HK |
590 | goto error_close_dir; |
591 | } else if (ret != 1) { | |
592 | ret = -EIO; | |
d9abc615 CO |
593 | fprintf(stderr, |
594 | "failed to match element number\n"); | |
096f9b86 HK |
595 | goto error_close_dir; |
596 | } | |
597 | ||
598 | numstrlen = calc_digits(number); | |
9d8ae6c8 JC |
599 | /* verify the next character is not a colon */ |
600 | if (strncmp(ent->d_name + strlen(type) + numstrlen, | |
7663a4aa HK |
601 | ":", 1) != 0) { |
602 | filename = malloc(strlen(iio_dir) + strlen(type) | |
603 | + numstrlen + 6); | |
ff1ac639 | 604 | if (!filename) { |
53118557 HK |
605 | ret = -ENOMEM; |
606 | goto error_close_dir; | |
607 | } | |
608 | ||
609 | ret = sprintf(filename, "%s%s%d/name", iio_dir, | |
610 | type, number); | |
611 | if (ret < 0) { | |
612 | free(filename); | |
613 | goto error_close_dir; | |
a4d429e3 | 614 | } |
53118557 | 615 | |
a9d7acc8 HK |
616 | namefp = fopen(filename, "r"); |
617 | if (!namefp) { | |
a4d429e3 | 618 | free(filename); |
9d8ae6c8 | 619 | continue; |
a4d429e3 | 620 | } |
7663a4aa | 621 | |
9d8ae6c8 | 622 | free(filename); |
53118557 | 623 | errno = 0; |
a9d7acc8 | 624 | if (fscanf(namefp, "%s", thisname) != 1) { |
53118557 HK |
625 | ret = errno ? -errno : -ENODATA; |
626 | goto error_close_dir; | |
627 | } | |
628 | ||
a9d7acc8 | 629 | if (fclose(namefp)) { |
53118557 HK |
630 | ret = -errno; |
631 | goto error_close_dir; | |
632 | } | |
633 | ||
a4d429e3 | 634 | if (strcmp(name, thisname) == 0) { |
53118557 HK |
635 | if (closedir(dp) == -1) |
636 | return -errno; | |
7663a4aa | 637 | |
a4d429e3 PM |
638 | return number; |
639 | } | |
c57f1ba7 JC |
640 | } |
641 | } | |
642 | } | |
53118557 HK |
643 | if (closedir(dp) == -1) |
644 | return -errno; | |
645 | ||
9d8ae6c8 | 646 | return -ENODEV; |
096f9b86 HK |
647 | |
648 | error_close_dir: | |
649 | if (closedir(dp) == -1) | |
650 | perror("find_type_by_name(): Failed to close directory"); | |
7663a4aa | 651 | |
096f9b86 | 652 | return ret; |
c57f1ba7 JC |
653 | } |
654 | ||
9d475254 HK |
655 | static int _write_sysfs_int(const char *filename, const char *basedir, int val, |
656 | int verify) | |
c57f1ba7 | 657 | { |
11cb454f | 658 | int ret = 0; |
9d8ae6c8 JC |
659 | FILE *sysfsfp; |
660 | int test; | |
661 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); | |
79bdd48a | 662 | |
ff1ac639 | 663 | if (!temp) |
9d8ae6c8 | 664 | return -ENOMEM; |
7663a4aa | 665 | |
53118557 HK |
666 | ret = sprintf(temp, "%s/%s", basedir, filename); |
667 | if (ret < 0) | |
668 | goto error_free; | |
669 | ||
c57f1ba7 | 670 | sysfsfp = fopen(temp, "w"); |
ff1ac639 | 671 | if (!sysfsfp) { |
9d8ae6c8 | 672 | ret = -errno; |
d9abc615 | 673 | fprintf(stderr, "failed to open %s\n", temp); |
9d8ae6c8 JC |
674 | goto error_free; |
675 | } | |
7663a4aa | 676 | |
53118557 HK |
677 | ret = fprintf(sysfsfp, "%d", val); |
678 | if (ret < 0) { | |
679 | if (fclose(sysfsfp)) | |
680 | perror("_write_sysfs_int(): Failed to close dir"); | |
681 | ||
682 | goto error_free; | |
683 | } | |
684 | ||
685 | if (fclose(sysfsfp)) { | |
686 | ret = -errno; | |
687 | goto error_free; | |
688 | } | |
689 | ||
9d8ae6c8 JC |
690 | if (verify) { |
691 | sysfsfp = fopen(temp, "r"); | |
ff1ac639 | 692 | if (!sysfsfp) { |
9d8ae6c8 | 693 | ret = -errno; |
d9abc615 | 694 | fprintf(stderr, "failed to open %s\n", temp); |
9d8ae6c8 JC |
695 | goto error_free; |
696 | } | |
7663a4aa | 697 | |
53118557 HK |
698 | if (fscanf(sysfsfp, "%d", &test) != 1) { |
699 | ret = errno ? -errno : -ENODATA; | |
700 | if (fclose(sysfsfp)) | |
701 | perror("_write_sysfs_int(): Failed to close dir"); | |
702 | ||
703 | goto error_free; | |
704 | } | |
705 | ||
706 | if (fclose(sysfsfp)) { | |
707 | ret = -errno; | |
708 | goto error_free; | |
709 | } | |
710 | ||
9d8ae6c8 | 711 | if (test != val) { |
d9abc615 CO |
712 | fprintf(stderr, |
713 | "Possible failure in int write %d to %s/%s\n", | |
714 | val, basedir, filename); | |
9d8ae6c8 JC |
715 | ret = -1; |
716 | } | |
717 | } | |
7663a4aa | 718 | |
9d8ae6c8 JC |
719 | error_free: |
720 | free(temp); | |
721 | return ret; | |
722 | } | |
723 | ||
5dc65d79 HK |
724 | /** |
725 | * write_sysfs_int() - write an integer value to a sysfs file | |
726 | * @filename: name of the file to write to | |
727 | * @basedir: the sysfs directory in which the file is to be found | |
728 | * @val: integer value to write to file | |
729 | * | |
730 | * Returns a value >= 0 on success, otherwise a negative error code. | |
731 | **/ | |
9d475254 | 732 | int write_sysfs_int(const char *filename, const char *basedir, int val) |
9d8ae6c8 JC |
733 | { |
734 | return _write_sysfs_int(filename, basedir, val, 0); | |
c57f1ba7 JC |
735 | } |
736 | ||
5dc65d79 HK |
737 | /** |
738 | * write_sysfs_int_and_verify() - write an integer value to a sysfs file | |
739 | * and verify | |
740 | * @filename: name of the file to write to | |
741 | * @basedir: the sysfs directory in which the file is to be found | |
742 | * @val: integer value to write to file | |
743 | * | |
744 | * Returns a value >= 0 on success, otherwise a negative error code. | |
745 | **/ | |
9d475254 HK |
746 | int write_sysfs_int_and_verify(const char *filename, const char *basedir, |
747 | int val) | |
9d8ae6c8 JC |
748 | { |
749 | return _write_sysfs_int(filename, basedir, val, 1); | |
750 | } | |
751 | ||
9d475254 HK |
752 | static int _write_sysfs_string(const char *filename, const char *basedir, |
753 | const char *val, int verify) | |
eaf86ff9 | 754 | { |
e58537cc | 755 | int ret = 0; |
eaf86ff9 | 756 | FILE *sysfsfp; |
9d8ae6c8 | 757 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
79bdd48a | 758 | |
ff1ac639 | 759 | if (!temp) { |
d9abc615 | 760 | fprintf(stderr, "Memory allocation failed\n"); |
9d8ae6c8 JC |
761 | return -ENOMEM; |
762 | } | |
7663a4aa | 763 | |
53118557 HK |
764 | ret = sprintf(temp, "%s/%s", basedir, filename); |
765 | if (ret < 0) | |
766 | goto error_free; | |
767 | ||
eaf86ff9 | 768 | sysfsfp = fopen(temp, "w"); |
ff1ac639 | 769 | if (!sysfsfp) { |
9d8ae6c8 | 770 | ret = -errno; |
d9abc615 | 771 | fprintf(stderr, "Could not open %s\n", temp); |
9d8ae6c8 JC |
772 | goto error_free; |
773 | } | |
7663a4aa | 774 | |
53118557 HK |
775 | ret = fprintf(sysfsfp, "%s", val); |
776 | if (ret < 0) { | |
777 | if (fclose(sysfsfp)) | |
778 | perror("_write_sysfs_string(): Failed to close dir"); | |
779 | ||
780 | goto error_free; | |
781 | } | |
782 | ||
783 | if (fclose(sysfsfp)) { | |
784 | ret = -errno; | |
785 | goto error_free; | |
786 | } | |
787 | ||
9d8ae6c8 JC |
788 | if (verify) { |
789 | sysfsfp = fopen(temp, "r"); | |
ff1ac639 | 790 | if (!sysfsfp) { |
9d8ae6c8 | 791 | ret = -errno; |
d9abc615 | 792 | fprintf(stderr, "Could not open file to verify\n"); |
9d8ae6c8 JC |
793 | goto error_free; |
794 | } | |
7663a4aa | 795 | |
53118557 HK |
796 | if (fscanf(sysfsfp, "%s", temp) != 1) { |
797 | ret = errno ? -errno : -ENODATA; | |
798 | if (fclose(sysfsfp)) | |
799 | perror("_write_sysfs_string(): Failed to close dir"); | |
800 | ||
801 | goto error_free; | |
802 | } | |
803 | ||
804 | if (fclose(sysfsfp)) { | |
805 | ret = -errno; | |
806 | goto error_free; | |
807 | } | |
808 | ||
9d8ae6c8 | 809 | if (strcmp(temp, val) != 0) { |
d9abc615 CO |
810 | fprintf(stderr, |
811 | "Possible failure in string write of %s " | |
812 | "Should be %s written to %s/%s\n", temp, val, | |
813 | basedir, filename); | |
9d8ae6c8 JC |
814 | ret = -1; |
815 | } | |
eaf86ff9 | 816 | } |
7663a4aa | 817 | |
9d8ae6c8 JC |
818 | error_free: |
819 | free(temp); | |
eaf86ff9 | 820 | |
9d8ae6c8 | 821 | return ret; |
eaf86ff9 | 822 | } |
e58537cc | 823 | |
c57f1ba7 JC |
824 | /** |
825 | * write_sysfs_string_and_verify() - string write, readback and verify | |
826 | * @filename: name of file to write to | |
827 | * @basedir: the sysfs directory in which the file is to be found | |
828 | * @val: the string to write | |
5dc65d79 HK |
829 | * |
830 | * Returns a value >= 0 on success, otherwise a negative error code. | |
c57f1ba7 | 831 | **/ |
9d475254 HK |
832 | int write_sysfs_string_and_verify(const char *filename, const char *basedir, |
833 | const char *val) | |
c57f1ba7 | 834 | { |
9d8ae6c8 JC |
835 | return _write_sysfs_string(filename, basedir, val, 1); |
836 | } | |
c57f1ba7 | 837 | |
5dc65d79 HK |
838 | /** |
839 | * write_sysfs_string() - write string to a sysfs file | |
840 | * @filename: name of file to write to | |
841 | * @basedir: the sysfs directory in which the file is to be found | |
842 | * @val: the string to write | |
843 | * | |
844 | * Returns a value >= 0 on success, otherwise a negative error code. | |
845 | **/ | |
9d475254 HK |
846 | int write_sysfs_string(const char *filename, const char *basedir, |
847 | const char *val) | |
9d8ae6c8 JC |
848 | { |
849 | return _write_sysfs_string(filename, basedir, val, 0); | |
c57f1ba7 JC |
850 | } |
851 | ||
5dc65d79 HK |
852 | /** |
853 | * read_sysfs_posint() - read an integer value from file | |
854 | * @filename: name of file to read from | |
855 | * @basedir: the sysfs directory in which the file is to be found | |
856 | * | |
857 | * Returns the read integer value >= 0 on success, otherwise a negative error | |
858 | * code. | |
859 | **/ | |
9d475254 | 860 | int read_sysfs_posint(const char *filename, const char *basedir) |
c57f1ba7 JC |
861 | { |
862 | int ret; | |
863 | FILE *sysfsfp; | |
9d8ae6c8 | 864 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
79bdd48a | 865 | |
ff1ac639 | 866 | if (!temp) { |
d9abc615 | 867 | fprintf(stderr, "Memory allocation failed"); |
9d8ae6c8 JC |
868 | return -ENOMEM; |
869 | } | |
7663a4aa | 870 | |
53118557 HK |
871 | ret = sprintf(temp, "%s/%s", basedir, filename); |
872 | if (ret < 0) | |
873 | goto error_free; | |
874 | ||
c57f1ba7 | 875 | sysfsfp = fopen(temp, "r"); |
ff1ac639 | 876 | if (!sysfsfp) { |
9d8ae6c8 JC |
877 | ret = -errno; |
878 | goto error_free; | |
879 | } | |
7663a4aa | 880 | |
53118557 HK |
881 | errno = 0; |
882 | if (fscanf(sysfsfp, "%d\n", &ret) != 1) { | |
883 | ret = errno ? -errno : -ENODATA; | |
884 | if (fclose(sysfsfp)) | |
885 | perror("read_sysfs_posint(): Failed to close dir"); | |
886 | ||
887 | goto error_free; | |
888 | } | |
889 | ||
890 | if (fclose(sysfsfp)) | |
891 | ret = -errno; | |
892 | ||
9d8ae6c8 JC |
893 | error_free: |
894 | free(temp); | |
7663a4aa | 895 | |
9d8ae6c8 JC |
896 | return ret; |
897 | } | |
898 | ||
5dc65d79 HK |
899 | /** |
900 | * read_sysfs_float() - read a float value from file | |
901 | * @filename: name of file to read from | |
902 | * @basedir: the sysfs directory in which the file is to be found | |
903 | * @val: output the read float value | |
904 | * | |
905 | * Returns a value >= 0 on success, otherwise a negative error code. | |
906 | **/ | |
9d475254 | 907 | int read_sysfs_float(const char *filename, const char *basedir, float *val) |
9d8ae6c8 | 908 | { |
f5709d5f | 909 | int ret = 0; |
9d8ae6c8 JC |
910 | FILE *sysfsfp; |
911 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); | |
79bdd48a | 912 | |
ff1ac639 | 913 | if (!temp) { |
d9abc615 | 914 | fprintf(stderr, "Memory allocation failed"); |
9d8ae6c8 JC |
915 | return -ENOMEM; |
916 | } | |
7663a4aa | 917 | |
53118557 HK |
918 | ret = sprintf(temp, "%s/%s", basedir, filename); |
919 | if (ret < 0) | |
920 | goto error_free; | |
921 | ||
9d8ae6c8 | 922 | sysfsfp = fopen(temp, "r"); |
ff1ac639 | 923 | if (!sysfsfp) { |
9d8ae6c8 JC |
924 | ret = -errno; |
925 | goto error_free; | |
926 | } | |
7663a4aa | 927 | |
53118557 HK |
928 | errno = 0; |
929 | if (fscanf(sysfsfp, "%f\n", val) != 1) { | |
930 | ret = errno ? -errno : -ENODATA; | |
931 | if (fclose(sysfsfp)) | |
932 | perror("read_sysfs_float(): Failed to close dir"); | |
933 | ||
934 | goto error_free; | |
935 | } | |
936 | ||
937 | if (fclose(sysfsfp)) | |
938 | ret = -errno; | |
939 | ||
9d8ae6c8 JC |
940 | error_free: |
941 | free(temp); | |
7663a4aa | 942 | |
c57f1ba7 JC |
943 | return ret; |
944 | } | |
49d916ec | 945 | |
5dc65d79 HK |
946 | /** |
947 | * read_sysfs_string() - read a string from file | |
948 | * @filename: name of file to read from | |
949 | * @basedir: the sysfs directory in which the file is to be found | |
950 | * @str: output the read string | |
951 | * | |
952 | * Returns a value >= 0 on success, otherwise a negative error code. | |
953 | **/ | |
f5709d5f | 954 | int read_sysfs_string(const char *filename, const char *basedir, char *str) |
49d916ec | 955 | { |
f5709d5f | 956 | int ret = 0; |
49d916ec MS |
957 | FILE *sysfsfp; |
958 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); | |
79bdd48a | 959 | |
ff1ac639 | 960 | if (!temp) { |
d9abc615 | 961 | fprintf(stderr, "Memory allocation failed"); |
49d916ec MS |
962 | return -ENOMEM; |
963 | } | |
7663a4aa | 964 | |
53118557 HK |
965 | ret = sprintf(temp, "%s/%s", basedir, filename); |
966 | if (ret < 0) | |
967 | goto error_free; | |
968 | ||
49d916ec | 969 | sysfsfp = fopen(temp, "r"); |
ff1ac639 | 970 | if (!sysfsfp) { |
49d916ec MS |
971 | ret = -errno; |
972 | goto error_free; | |
973 | } | |
7663a4aa | 974 | |
53118557 HK |
975 | errno = 0; |
976 | if (fscanf(sysfsfp, "%s\n", str) != 1) { | |
977 | ret = errno ? -errno : -ENODATA; | |
978 | if (fclose(sysfsfp)) | |
979 | perror("read_sysfs_string(): Failed to close dir"); | |
980 | ||
981 | goto error_free; | |
982 | } | |
983 | ||
984 | if (fclose(sysfsfp)) | |
985 | ret = -errno; | |
986 | ||
49d916ec MS |
987 | error_free: |
988 | free(temp); | |
7663a4aa | 989 | |
49d916ec MS |
990 | return ret; |
991 | } |