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