1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* IIO - useful set of util functionality |
3 | * |
4 | * Copyright (c) 2008 Jonathan Cameron |
5 | */ |
6 | #include <string.h> |
7 | #include <stdlib.h> |
8 | #include <stdio.h> |
9 | #include <stdint.h> |
10 | #include <dirent.h> |
11 | #include <errno.h> |
12 | #include <ctype.h> |
13 | #include "iio_utils.h" |
14 | |
15 | const char *iio_dir = "/sys/bus/iio/devices/" ; |
16 | |
17 | static char * const iio_direction[] = { |
18 | "in" , |
19 | "out" , |
20 | }; |
21 | |
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 |
26 | * |
27 | * Returns 0 on success, or a negative error code if string extraction failed. |
28 | **/ |
29 | int iioutils_break_up_name(const char *full_name, char **generic_name) |
30 | { |
31 | char *current; |
32 | char *w, *r; |
33 | char *working, *prefix = "" ; |
34 | int i, ret; |
35 | |
36 | for (i = 0; i < ARRAY_SIZE(iio_direction); i++) |
37 | if (!strncmp(full_name, iio_direction[i], |
38 | strlen(iio_direction[i]))) { |
39 | prefix = iio_direction[i]; |
40 | break; |
41 | } |
42 | |
43 | current = strdup(full_name + strlen(prefix) + 1); |
44 | if (!current) |
45 | return -ENOMEM; |
46 | |
47 | working = strtok(current, "_\0" ); |
48 | if (!working) { |
49 | free(current); |
50 | return -EINVAL; |
51 | } |
52 | |
53 | w = working; |
54 | r = working; |
55 | |
56 | while (*r != '\0') { |
57 | if (!isdigit(*r)) { |
58 | *w = *r; |
59 | w++; |
60 | } |
61 | |
62 | r++; |
63 | } |
64 | *w = '\0'; |
65 | ret = asprintf(generic_name, "%s_%s" , prefix, working); |
66 | free(current); |
67 | |
68 | return (ret == -1) ? -ENOMEM : 0; |
69 | } |
70 | |
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 |
75 | * @bits_used: output number of valid bits of data |
76 | * @shift: output amount of bits to shift right data before applying bit mask |
77 | * @mask: output a bit mask for the raw data |
78 | * @be: output if data in big endian |
79 | * @device_dir: the IIO device directory |
80 | * @buffer_idx: the IIO buffer index |
81 | * @name: the channel name |
82 | * @generic_name: the channel type name |
83 | * |
84 | * Returns a value >= 0 on success, otherwise a negative error code. |
85 | **/ |
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, |
89 | const char *device_dir, int buffer_idx, |
90 | const char *name, const char *generic_name) |
91 | { |
92 | FILE *sysfsfp; |
93 | int ret; |
94 | DIR *dp; |
95 | char *scan_el_dir, *builtname, *builtname_generic, *filename = 0; |
96 | char signchar, endianchar; |
97 | unsigned padint; |
98 | const struct dirent *ent; |
99 | |
100 | ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx); |
101 | if (ret < 0) |
102 | return -ENOMEM; |
103 | |
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); |
116 | if (!dp) { |
117 | ret = -errno; |
118 | goto error_free_builtname_generic; |
119 | } |
120 | |
121 | ret = -ENOENT; |
122 | while (ent = readdir(dp), ent) |
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 | } |
131 | |
132 | sysfsfp = fopen(filename, "r" ); |
133 | if (!sysfsfp) { |
134 | ret = -errno; |
135 | fprintf(stderr, "failed to open %s\n" , |
136 | filename); |
137 | goto error_free_filename; |
138 | } |
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) { |
147 | ret = -errno; |
148 | fprintf(stderr, |
149 | "failed to pass scan type description\n" ); |
150 | goto error_close_sysfsfp; |
151 | } else if (ret != 5) { |
152 | ret = -EIO; |
153 | fprintf(stderr, |
154 | "scan type description didn't match\n" ); |
155 | goto error_close_sysfsfp; |
156 | } |
157 | |
158 | *be = (endianchar == 'b'); |
159 | *bytes = padint / 8; |
160 | if (*bits_used == 64) |
161 | *mask = ~(0ULL); |
162 | else |
163 | *mask = (1ULL << *bits_used) - 1ULL; |
164 | |
165 | *is_signed = (signchar == 's'); |
166 | if (fclose(sysfsfp)) { |
167 | ret = -errno; |
168 | fprintf(stderr, "Failed to close %s\n" , |
169 | filename); |
170 | goto error_free_filename; |
171 | } |
172 | |
173 | sysfsfp = 0; |
174 | free(filename); |
175 | filename = 0; |
176 | |
177 | /* |
178 | * Avoid having a more generic entry overwriting |
179 | * the settings. |
180 | */ |
181 | if (strcmp(builtname, ent->d_name) == 0) |
182 | break; |
183 | } |
184 | |
185 | error_close_sysfsfp: |
186 | if (sysfsfp) |
187 | if (fclose(sysfsfp)) |
188 | perror("iioutils_get_type(): Failed to close file" ); |
189 | |
190 | error_free_filename: |
191 | if (filename) |
192 | free(filename); |
193 | |
194 | error_closedir: |
195 | if (closedir(dp) == -1) |
196 | perror("iioutils_get_type(): Failed to close directory" ); |
197 | |
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); |
204 | |
205 | return ret; |
206 | } |
207 | |
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 | **/ |
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) |
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); |
230 | if (ret < 0) |
231 | return -ENOMEM; |
232 | |
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 | } |
239 | |
240 | dp = opendir(device_dir); |
241 | if (!dp) { |
242 | ret = -errno; |
243 | goto error_free_builtname_generic; |
244 | } |
245 | |
246 | ret = -ENOENT; |
247 | while (ent = readdir(dp), ent) |
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 | } |
256 | |
257 | sysfsfp = fopen(filename, "r" ); |
258 | if (!sysfsfp) { |
259 | ret = -errno; |
260 | goto error_free_filename; |
261 | } |
262 | |
263 | errno = 0; |
264 | if (fscanf(sysfsfp, "%f" , output) != 1) |
265 | ret = errno ? -errno : -ENODATA; |
266 | |
267 | fclose(sysfsfp); |
268 | break; |
269 | } |
270 | error_free_filename: |
271 | if (filename) |
272 | free(filename); |
273 | |
274 | error_closedir: |
275 | if (closedir(dp) == -1) |
276 | perror("iioutils_get_param_float(): Failed to close directory" ); |
277 | |
278 | error_free_builtname_generic: |
279 | free(builtname_generic); |
280 | error_free_builtname: |
281 | free(builtname); |
282 | |
283 | return ret; |
284 | } |
285 | |
286 | /** |
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 |
290 | **/ |
291 | |
292 | void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt) |
293 | { |
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++) |
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; |
303 | } |
304 | } |
305 | |
306 | /** |
307 | * build_channel_array() - function to figure out what channels are present |
308 | * @device_dir: the IIO device directory in sysfs |
309 | * @buffer_idx: the IIO buffer for this channel array |
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. |
314 | **/ |
315 | int build_channel_array(const char *device_dir, int buffer_idx, |
316 | struct iio_channel_info **ci_array, int *counter) |
317 | { |
318 | DIR *dp; |
319 | FILE *sysfsfp; |
320 | int count = 0, i; |
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; |
328 | ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx); |
329 | if (ret < 0) |
330 | return -ENOMEM; |
331 | |
332 | dp = opendir(scan_el_dir); |
333 | if (!dp) { |
334 | ret = -errno; |
335 | goto error_free_name; |
336 | } |
337 | |
338 | while (ent = readdir(dp), ent) |
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 | } |
347 | |
348 | sysfsfp = fopen(filename, "r" ); |
349 | free(filename); |
350 | if (!sysfsfp) { |
351 | ret = -errno; |
352 | goto error_close_dir; |
353 | } |
354 | |
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 | |
361 | goto error_close_dir; |
362 | } |
363 | if (ret == 1) |
364 | (*counter)++; |
365 | |
366 | if (fclose(sysfsfp)) { |
367 | ret = -errno; |
368 | goto error_close_dir; |
369 | } |
370 | |
371 | } |
372 | |
373 | *ci_array = malloc(sizeof(**ci_array) * (*counter)); |
374 | if (!*ci_array) { |
375 | ret = -ENOMEM; |
376 | goto error_close_dir; |
377 | } |
378 | |
379 | rewinddir(dp); |
380 | while (ent = readdir(dp), ent) { |
381 | if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en" ), |
382 | "_en" ) == 0) { |
383 | int current_enabled = 0; |
384 | |
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 | } |
394 | |
395 | sysfsfp = fopen(filename, "r" ); |
396 | free(filename); |
397 | if (!sysfsfp) { |
398 | ret = -errno; |
399 | count--; |
400 | goto error_cleanup_array; |
401 | } |
402 | |
403 | errno = 0; |
404 | if (fscanf(sysfsfp, "%i" , ¤t_enabled) != 1) { |
405 | ret = errno ? -errno : -ENODATA; |
406 | count--; |
407 | goto error_cleanup_array; |
408 | } |
409 | |
410 | if (fclose(sysfsfp)) { |
411 | ret = -errno; |
412 | count--; |
413 | goto error_cleanup_array; |
414 | } |
415 | |
416 | if (!current_enabled) { |
417 | count--; |
418 | continue; |
419 | } |
420 | |
421 | current->scale = 1.0; |
422 | current->offset = 0; |
423 | current->name = strndup(ent->d_name, |
424 | strlen(ent->d_name) - |
425 | strlen("_en" )); |
426 | if (!current->name) { |
427 | ret = -ENOMEM; |
428 | count--; |
429 | goto error_cleanup_array; |
430 | } |
431 | |
432 | /* Get the generic and specific name elements */ |
433 | ret = iioutils_break_up_name(full_name: current->name, |
434 | generic_name: ¤t->generic_name); |
435 | if (ret) { |
436 | free(current->name); |
437 | count--; |
438 | goto error_cleanup_array; |
439 | } |
440 | |
441 | ret = asprintf(&filename, |
442 | "%s/%s_index" , |
443 | scan_el_dir, |
444 | current->name); |
445 | if (ret < 0) { |
446 | ret = -ENOMEM; |
447 | goto error_cleanup_array; |
448 | } |
449 | |
450 | sysfsfp = fopen(filename, "r" ); |
451 | free(filename); |
452 | if (!sysfsfp) { |
453 | ret = -errno; |
454 | fprintf(stderr, "failed to open %s/%s_index\n" , |
455 | scan_el_dir, current->name); |
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 | |
465 | goto error_cleanup_array; |
466 | } |
467 | |
468 | if (fclose(sysfsfp)) { |
469 | ret = -errno; |
470 | goto error_cleanup_array; |
471 | } |
472 | |
473 | /* Find the scale */ |
474 | ret = iioutils_get_param_float(output: ¤t->scale, |
475 | param_name: "scale" , |
476 | device_dir, |
477 | name: current->name, |
478 | generic_name: current->generic_name); |
479 | if ((ret < 0) && (ret != -ENOENT)) |
480 | goto error_cleanup_array; |
481 | |
482 | ret = iioutils_get_param_float(output: ¤t->offset, |
483 | param_name: "offset" , |
484 | device_dir, |
485 | name: current->name, |
486 | generic_name: current->generic_name); |
487 | if ((ret < 0) && (ret != -ENOENT)) |
488 | goto error_cleanup_array; |
489 | |
490 | ret = iioutils_get_type(¤t->is_signed, |
491 | ¤t->bytes, |
492 | ¤t->bits_used, |
493 | ¤t->shift, |
494 | ¤t->mask, |
495 | ¤t->be, |
496 | device_dir, |
497 | buffer_idx, |
498 | current->name, |
499 | current->generic_name); |
500 | if (ret < 0) |
501 | goto error_cleanup_array; |
502 | } |
503 | } |
504 | |
505 | if (closedir(dp) == -1) { |
506 | ret = -errno; |
507 | goto error_cleanup_array; |
508 | } |
509 | |
510 | free(scan_el_dir); |
511 | /* reorder so that the array is in index order */ |
512 | bsort_channel_array_by_index(ci_array: *ci_array, cnt: *counter); |
513 | |
514 | return 0; |
515 | |
516 | error_cleanup_array: |
517 | for (i = count - 1; i >= 0; i--) { |
518 | free((*ci_array)[i].name); |
519 | free((*ci_array)[i].generic_name); |
520 | } |
521 | free(*ci_array); |
522 | *ci_array = NULL; |
523 | *counter = 0; |
524 | error_close_dir: |
525 | if (dp) |
526 | if (closedir(dp) == -1) |
527 | perror("build_channel_array(): Failed to close dir" ); |
528 | |
529 | error_free_name: |
530 | free(scan_el_dir); |
531 | |
532 | return ret; |
533 | } |
534 | |
535 | static int calc_digits(int num) |
536 | { |
537 | int count = 0; |
538 | |
539 | /* It takes a digit to represent zero */ |
540 | if (!num) |
541 | return 1; |
542 | |
543 | while (num != 0) { |
544 | num /= 10; |
545 | count++; |
546 | } |
547 | |
548 | return count; |
549 | } |
550 | |
551 | /** |
552 | * find_type_by_name() - function to match top level types by name |
553 | * @name: top level type instance name |
554 | * @type: the type of top level instance being searched |
555 | * |
556 | * Returns the device number of a matched IIO device on success, otherwise a |
557 | * negative error code. |
558 | * Typical types this is used for are device and trigger. |
559 | **/ |
560 | int find_type_by_name(const char *name, const char *type) |
561 | { |
562 | const struct dirent *ent; |
563 | int number, numstrlen, ret; |
564 | |
565 | FILE *namefp; |
566 | DIR *dp; |
567 | char thisname[IIO_MAX_NAME_LENGTH]; |
568 | char *filename; |
569 | |
570 | dp = opendir(iio_dir); |
571 | if (!dp) { |
572 | fprintf(stderr, "No industrialio devices available\n" ); |
573 | return -ENODEV; |
574 | } |
575 | |
576 | while (ent = readdir(dp), ent) { |
577 | if (strcmp(ent->d_name, "." ) != 0 && |
578 | strcmp(ent->d_name, ".." ) != 0 && |
579 | strlen(ent->d_name) > strlen(type) && |
580 | strncmp(ent->d_name, type, strlen(type)) == 0) { |
581 | errno = 0; |
582 | ret = sscanf(ent->d_name + strlen(type), "%d" , &number); |
583 | if (ret < 0) { |
584 | ret = -errno; |
585 | fprintf(stderr, |
586 | "failed to read element number\n" ); |
587 | goto error_close_dir; |
588 | } else if (ret != 1) { |
589 | ret = -EIO; |
590 | fprintf(stderr, |
591 | "failed to match element number\n" ); |
592 | goto error_close_dir; |
593 | } |
594 | |
595 | numstrlen = calc_digits(num: number); |
596 | /* verify the next character is not a colon */ |
597 | if (strncmp(ent->d_name + strlen(type) + numstrlen, |
598 | ":" , 1) != 0) { |
599 | filename = malloc(strlen(iio_dir) + strlen(type) |
600 | + numstrlen + 6); |
601 | if (!filename) { |
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; |
611 | } |
612 | |
613 | namefp = fopen(filename, "r" ); |
614 | if (!namefp) { |
615 | free(filename); |
616 | continue; |
617 | } |
618 | |
619 | free(filename); |
620 | errno = 0; |
621 | if (fscanf(namefp, "%s" , thisname) != 1) { |
622 | ret = errno ? -errno : -ENODATA; |
623 | goto error_close_dir; |
624 | } |
625 | |
626 | if (fclose(namefp)) { |
627 | ret = -errno; |
628 | goto error_close_dir; |
629 | } |
630 | |
631 | if (strcmp(name, thisname) == 0) { |
632 | if (closedir(dp) == -1) |
633 | return -errno; |
634 | |
635 | return number; |
636 | } |
637 | } |
638 | } |
639 | } |
640 | if (closedir(dp) == -1) |
641 | return -errno; |
642 | |
643 | return -ENODEV; |
644 | |
645 | error_close_dir: |
646 | if (closedir(dp) == -1) |
647 | perror("find_type_by_name(): Failed to close directory" ); |
648 | |
649 | return ret; |
650 | } |
651 | |
652 | static int _write_sysfs_int(const char *filename, const char *basedir, int val, |
653 | int verify) |
654 | { |
655 | int ret = 0; |
656 | FILE *sysfsfp; |
657 | int test; |
658 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
659 | |
660 | if (!temp) |
661 | return -ENOMEM; |
662 | |
663 | ret = sprintf(temp, "%s/%s" , basedir, filename); |
664 | if (ret < 0) |
665 | goto error_free; |
666 | |
667 | sysfsfp = fopen(temp, "w" ); |
668 | if (!sysfsfp) { |
669 | ret = -errno; |
670 | fprintf(stderr, "failed to open %s\n" , temp); |
671 | goto error_free; |
672 | } |
673 | |
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 | |
687 | if (verify) { |
688 | sysfsfp = fopen(temp, "r" ); |
689 | if (!sysfsfp) { |
690 | ret = -errno; |
691 | fprintf(stderr, "failed to open %s\n" , temp); |
692 | goto error_free; |
693 | } |
694 | |
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 | |
708 | if (test != val) { |
709 | fprintf(stderr, |
710 | "Possible failure in int write %d to %s/%s\n" , |
711 | val, basedir, filename); |
712 | ret = -1; |
713 | } |
714 | } |
715 | |
716 | error_free: |
717 | free(temp); |
718 | return ret; |
719 | } |
720 | |
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 | **/ |
729 | int write_sysfs_int(const char *filename, const char *basedir, int val) |
730 | { |
731 | return _write_sysfs_int(filename, basedir, val, verify: 0); |
732 | } |
733 | |
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 | **/ |
743 | int write_sysfs_int_and_verify(const char *filename, const char *basedir, |
744 | int val) |
745 | { |
746 | return _write_sysfs_int(filename, basedir, val, verify: 1); |
747 | } |
748 | |
749 | static int _write_sysfs_string(const char *filename, const char *basedir, |
750 | const char *val, int verify) |
751 | { |
752 | int ret = 0; |
753 | FILE *sysfsfp; |
754 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
755 | |
756 | if (!temp) { |
757 | fprintf(stderr, "Memory allocation failed\n" ); |
758 | return -ENOMEM; |
759 | } |
760 | |
761 | ret = sprintf(temp, "%s/%s" , basedir, filename); |
762 | if (ret < 0) |
763 | goto error_free; |
764 | |
765 | sysfsfp = fopen(temp, "w" ); |
766 | if (!sysfsfp) { |
767 | ret = -errno; |
768 | fprintf(stderr, "Could not open %s\n" , temp); |
769 | goto error_free; |
770 | } |
771 | |
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 | |
785 | if (verify) { |
786 | sysfsfp = fopen(temp, "r" ); |
787 | if (!sysfsfp) { |
788 | ret = -errno; |
789 | fprintf(stderr, "Could not open file to verify\n" ); |
790 | goto error_free; |
791 | } |
792 | |
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 | |
806 | if (strcmp(temp, val) != 0) { |
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); |
811 | ret = -1; |
812 | } |
813 | } |
814 | |
815 | error_free: |
816 | free(temp); |
817 | |
818 | return ret; |
819 | } |
820 | |
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 |
826 | * |
827 | * Returns a value >= 0 on success, otherwise a negative error code. |
828 | **/ |
829 | int write_sysfs_string_and_verify(const char *filename, const char *basedir, |
830 | const char *val) |
831 | { |
832 | return _write_sysfs_string(filename, basedir, val, verify: 1); |
833 | } |
834 | |
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 | **/ |
843 | int write_sysfs_string(const char *filename, const char *basedir, |
844 | const char *val) |
845 | { |
846 | return _write_sysfs_string(filename, basedir, val, verify: 0); |
847 | } |
848 | |
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 | **/ |
857 | int read_sysfs_posint(const char *filename, const char *basedir) |
858 | { |
859 | int ret; |
860 | FILE *sysfsfp; |
861 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
862 | |
863 | if (!temp) { |
864 | fprintf(stderr, "Memory allocation failed" ); |
865 | return -ENOMEM; |
866 | } |
867 | |
868 | ret = sprintf(temp, "%s/%s" , basedir, filename); |
869 | if (ret < 0) |
870 | goto error_free; |
871 | |
872 | sysfsfp = fopen(temp, "r" ); |
873 | if (!sysfsfp) { |
874 | ret = -errno; |
875 | goto error_free; |
876 | } |
877 | |
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 | |
890 | error_free: |
891 | free(temp); |
892 | |
893 | return ret; |
894 | } |
895 | |
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 | **/ |
904 | int read_sysfs_float(const char *filename, const char *basedir, float *val) |
905 | { |
906 | int ret = 0; |
907 | FILE *sysfsfp; |
908 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
909 | |
910 | if (!temp) { |
911 | fprintf(stderr, "Memory allocation failed" ); |
912 | return -ENOMEM; |
913 | } |
914 | |
915 | ret = sprintf(temp, "%s/%s" , basedir, filename); |
916 | if (ret < 0) |
917 | goto error_free; |
918 | |
919 | sysfsfp = fopen(temp, "r" ); |
920 | if (!sysfsfp) { |
921 | ret = -errno; |
922 | goto error_free; |
923 | } |
924 | |
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 | |
937 | error_free: |
938 | free(temp); |
939 | |
940 | return ret; |
941 | } |
942 | |
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 | **/ |
951 | int read_sysfs_string(const char *filename, const char *basedir, char *str) |
952 | { |
953 | int ret = 0; |
954 | FILE *sysfsfp; |
955 | char *temp = malloc(strlen(basedir) + strlen(filename) + 2); |
956 | |
957 | if (!temp) { |
958 | fprintf(stderr, "Memory allocation failed" ); |
959 | return -ENOMEM; |
960 | } |
961 | |
962 | ret = sprintf(temp, "%s/%s" , basedir, filename); |
963 | if (ret < 0) |
964 | goto error_free; |
965 | |
966 | sysfsfp = fopen(temp, "r" ); |
967 | if (!sysfsfp) { |
968 | ret = -errno; |
969 | goto error_free; |
970 | } |
971 | |
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 | |
984 | error_free: |
985 | free(temp); |
986 | |
987 | return ret; |
988 | } |
989 | |