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