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 */
9#ifndef _IIO_UTILS_H
10#define _IIO_UTILS_H
11
12#include <string.h>
13#include <stdlib.h>
14#include <stdio.h>
15#include <stdint.h>
16#include <dirent.h>
17#include <errno.h>
18#include <ctype.h>
19#include "iio_utils.h"
20
21const char *iio_dir = "/sys/bus/iio/devices/";
22
23static char * const iio_direction[] = {
24	"in",
25	"out",
26};
27
28/**
29 * iioutils_break_up_name() - extract generic name from full channel name
30 * @full_name: the full channel name
31 * @generic_name: the output generic channel name
32 **/
33int iioutils_break_up_name(const char *full_name,
34				  char **generic_name)
35{
36	char *current;
37	char *w, *r;
38	char *working, *prefix = "";
39	int i;
40
41	for (i = 0; i < sizeof(iio_direction) / sizeof(iio_direction[0]); i++)
42		if (!strncmp(full_name, iio_direction[i],
43			     strlen(iio_direction[i]))) {
44			prefix = iio_direction[i];
45			break;
46		}
47
48	current = strdup(full_name + strlen(prefix) + 1);
49	working = strtok(current, "_\0");
50
51	w = working;
52	r = working;
53
54	while (*r != '\0') {
55		if (!isdigit(*r)) {
56			*w = *r;
57			w++;
58		}
59		r++;
60	}
61	*w = '\0';
62	asprintf(generic_name, "%s_%s", prefix, working);
63	free(current);
64
65	return 0;
66}
67
68/**
69 * iioutils_get_type() - find and process _type attribute data
70 * @is_signed: output whether channel is signed
71 * @bytes: output how many bytes the channel storage occupies
72 * @mask: output a bit mask for the raw data
73 * @be: big endian
74 * @device_dir: the iio device directory
75 * @name: the channel name
76 * @generic_name: the channel type name
77 **/
78int iioutils_get_type(unsigned *is_signed,
79			     unsigned *bytes,
80			     unsigned *bits_used,
81			     unsigned *shift,
82			     uint64_t *mask,
83			     unsigned *be,
84			     const char *device_dir,
85			     const char *name,
86			     const char *generic_name)
87{
88	FILE *sysfsfp;
89	int ret;
90	DIR *dp;
91	char *scan_el_dir, *builtname, *builtname_generic, *filename = 0;
92	char signchar, endianchar;
93	unsigned padint;
94	const struct dirent *ent;
95
96	ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
97	if (ret < 0) {
98		ret = -ENOMEM;
99		goto error_ret;
100	}
101	ret = asprintf(&builtname, FORMAT_TYPE_FILE, name);
102	if (ret < 0) {
103		ret = -ENOMEM;
104		goto error_free_scan_el_dir;
105	}
106	ret = asprintf(&builtname_generic, FORMAT_TYPE_FILE, generic_name);
107	if (ret < 0) {
108		ret = -ENOMEM;
109		goto error_free_builtname;
110	}
111
112	dp = opendir(scan_el_dir);
113	if (dp == NULL) {
114		ret = -errno;
115		goto error_free_builtname_generic;
116	}
117	while (ent = readdir(dp), ent != NULL)
118		/*
119		 * Do we allow devices to override a generic name with
120		 * a specific one?
121		 */
122		if ((strcmp(builtname, ent->d_name) == 0) ||
123		    (strcmp(builtname_generic, ent->d_name) == 0)) {
124			ret = asprintf(&filename,
125				       "%s/%s", scan_el_dir, ent->d_name);
126			if (ret < 0) {
127				ret = -ENOMEM;
128				goto error_closedir;
129			}
130			sysfsfp = fopen(filename, "r");
131			if (sysfsfp == NULL) {
132				printf("failed to open %s\n", filename);
133				ret = -errno;
134				goto error_free_filename;
135			}
136
137			ret = fscanf(sysfsfp,
138				     "%ce:%c%u/%u>>%u",
139				     &endianchar,
140				     &signchar,
141				     bits_used,
142				     &padint, shift);
143			if (ret < 0) {
144				printf("failed to pass scan type description\n");
145				ret = -errno;
146				goto error_close_sysfsfp;
147			}
148			*be = (endianchar == 'b');
149			*bytes = padint / 8;
150			if (*bits_used == 64)
151				*mask = ~0;
152			else
153				*mask = (1 << *bits_used) - 1;
154			if (signchar == 's')
155				*is_signed = 1;
156			else
157				*is_signed = 0;
158			fclose(sysfsfp);
159			free(filename);
160
161			filename = 0;
162			sysfsfp = 0;
163		}
164error_close_sysfsfp:
165	if (sysfsfp)
166		fclose(sysfsfp);
167error_free_filename:
168	if (filename)
169		free(filename);
170error_closedir:
171	closedir(dp);
172error_free_builtname_generic:
173	free(builtname_generic);
174error_free_builtname:
175	free(builtname);
176error_free_scan_el_dir:
177	free(scan_el_dir);
178error_ret:
179	return ret;
180}
181
182int iioutils_get_param_float(float *output,
183				    const char *param_name,
184				    const char *device_dir,
185				    const char *name,
186				    const char *generic_name)
187{
188	FILE *sysfsfp;
189	int ret;
190	DIR *dp;
191	char *builtname, *builtname_generic;
192	char *filename = NULL;
193	const struct dirent *ent;
194
195	ret = asprintf(&builtname, "%s_%s", name, param_name);
196	if (ret < 0) {
197		ret = -ENOMEM;
198		goto error_ret;
199	}
200	ret = asprintf(&builtname_generic,
201		       "%s_%s", generic_name, param_name);
202	if (ret < 0) {
203		ret = -ENOMEM;
204		goto error_free_builtname;
205	}
206	dp = opendir(device_dir);
207	if (dp == NULL) {
208		ret = -errno;
209		goto error_free_builtname_generic;
210	}
211	while (ent = readdir(dp), ent != NULL)
212		if ((strcmp(builtname, ent->d_name) == 0) ||
213		    (strcmp(builtname_generic, ent->d_name) == 0)) {
214			ret = asprintf(&filename,
215				       "%s/%s", device_dir, ent->d_name);
216			if (ret < 0) {
217				ret = -ENOMEM;
218				goto error_closedir;
219			}
220			sysfsfp = fopen(filename, "r");
221			if (!sysfsfp) {
222				ret = -errno;
223				goto error_free_filename;
224			}
225			fscanf(sysfsfp, "%f", output);
226			break;
227		}
228error_free_filename:
229	if (filename)
230		free(filename);
231error_closedir:
232	closedir(dp);
233error_free_builtname_generic:
234	free(builtname_generic);
235error_free_builtname:
236	free(builtname);
237error_ret:
238	return ret;
239}
240
241/**
242 * bsort_channel_array_by_index() - reorder so that the array is in index order
243 *
244 **/
245
246void bsort_channel_array_by_index(struct iio_channel_info **ci_array,
247					 int cnt)
248{
249
250	struct iio_channel_info temp;
251	int x, y;
252
253	for (x = 0; x < cnt; x++)
254		for (y = 0; y < (cnt - 1); y++)
255			if ((*ci_array)[y].index > (*ci_array)[y+1].index) {
256				temp = (*ci_array)[y + 1];
257				(*ci_array)[y + 1] = (*ci_array)[y];
258				(*ci_array)[y] = temp;
259			}
260}
261
262/**
263 * build_channel_array() - function to figure out what channels are present
264 * @device_dir: the IIO device directory in sysfs
265 * @
266 **/
267int build_channel_array(const char *device_dir,
268			      struct iio_channel_info **ci_array,
269			      int *counter)
270{
271	DIR *dp;
272	FILE *sysfsfp;
273	int count, i;
274	struct iio_channel_info *current;
275	int ret;
276	const struct dirent *ent;
277	char *scan_el_dir;
278	char *filename;
279
280	*counter = 0;
281	ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
282	if (ret < 0) {
283		ret = -ENOMEM;
284		goto error_ret;
285	}
286	dp = opendir(scan_el_dir);
287	if (dp == NULL) {
288		ret = -errno;
289		goto error_free_name;
290	}
291	while (ent = readdir(dp), ent != NULL)
292		if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
293			   "_en") == 0) {
294			ret = asprintf(&filename,
295				       "%s/%s", scan_el_dir, ent->d_name);
296			if (ret < 0) {
297				ret = -ENOMEM;
298				goto error_close_dir;
299			}
300			sysfsfp = fopen(filename, "r");
301			if (sysfsfp == NULL) {
302				ret = -errno;
303				free(filename);
304				goto error_close_dir;
305			}
306			fscanf(sysfsfp, "%i", &ret);
307			if (ret == 1)
308				(*counter)++;
309			fclose(sysfsfp);
310			free(filename);
311		}
312	*ci_array = malloc(sizeof(**ci_array) * (*counter));
313	if (*ci_array == NULL) {
314		ret = -ENOMEM;
315		goto error_close_dir;
316	}
317	seekdir(dp, 0);
318	count = 0;
319	while (ent = readdir(dp), ent != NULL) {
320		if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
321			   "_en") == 0) {
322			int current_enabled = 0;
323
324			current = &(*ci_array)[count++];
325			ret = asprintf(&filename,
326				       "%s/%s", scan_el_dir, ent->d_name);
327			if (ret < 0) {
328				ret = -ENOMEM;
329				/* decrement count to avoid freeing name */
330				count--;
331				goto error_cleanup_array;
332			}
333			sysfsfp = fopen(filename, "r");
334			if (sysfsfp == NULL) {
335				free(filename);
336				ret = -errno;
337				goto error_cleanup_array;
338			}
339			fscanf(sysfsfp, "%i", &current_enabled);
340			fclose(sysfsfp);
341
342			if (!current_enabled) {
343				free(filename);
344				count--;
345				continue;
346			}
347
348			current->scale = 1.0;
349			current->offset = 0;
350			current->name = strndup(ent->d_name,
351						strlen(ent->d_name) -
352						strlen("_en"));
353			if (current->name == NULL) {
354				free(filename);
355				ret = -ENOMEM;
356				goto error_cleanup_array;
357			}
358			/* Get the generic and specific name elements */
359			ret = iioutils_break_up_name(current->name,
360						     &current->generic_name);
361			if (ret) {
362				free(filename);
363				goto error_cleanup_array;
364			}
365			ret = asprintf(&filename,
366				       "%s/%s_index",
367				       scan_el_dir,
368				       current->name);
369			if (ret < 0) {
370				free(filename);
371				ret = -ENOMEM;
372				goto error_cleanup_array;
373			}
374			sysfsfp = fopen(filename, "r");
375			fscanf(sysfsfp, "%u", &current->index);
376			fclose(sysfsfp);
377			free(filename);
378			/* Find the scale */
379			ret = iioutils_get_param_float(&current->scale,
380						       "scale",
381						       device_dir,
382						       current->name,
383						       current->generic_name);
384			if (ret < 0)
385				goto error_cleanup_array;
386			ret = iioutils_get_param_float(&current->offset,
387						       "offset",
388						       device_dir,
389						       current->name,
390						       current->generic_name);
391			if (ret < 0)
392				goto error_cleanup_array;
393			ret = iioutils_get_type(&current->is_signed,
394						&current->bytes,
395						&current->bits_used,
396						&current->shift,
397						&current->mask,
398						&current->be,
399						device_dir,
400						current->name,
401						current->generic_name);
402		}
403	}
404
405	closedir(dp);
406	/* reorder so that the array is in index order */
407	bsort_channel_array_by_index(ci_array, *counter);
408
409	return 0;
410
411error_cleanup_array:
412	for (i = count - 1;  i >= 0; i--)
413		free((*ci_array)[i].name);
414	free(*ci_array);
415error_close_dir:
416	closedir(dp);
417error_free_name:
418	free(scan_el_dir);
419error_ret:
420	return ret;
421}
422
423/**
424 * find_type_by_name() - function to match top level types by name
425 * @name: top level type instance name
426 * @type: the type of top level instance being sort
427 *
428 * Typical types this is used for are device and trigger.
429 **/
430int find_type_by_name(const char *name, const char *type)
431{
432	const struct dirent *ent;
433	int number, numstrlen;
434
435	FILE *nameFile;
436	DIR *dp;
437	char thisname[IIO_MAX_NAME_LENGTH];
438	char *filename;
439
440	dp = opendir(iio_dir);
441	if (dp == NULL) {
442		printf("No industrialio devices available\n");
443		return -ENODEV;
444	}
445
446	while (ent = readdir(dp), ent != NULL) {
447		if (strcmp(ent->d_name, ".") != 0 &&
448			strcmp(ent->d_name, "..") != 0 &&
449			strlen(ent->d_name) > strlen(type) &&
450			strncmp(ent->d_name, type, strlen(type)) == 0) {
451			numstrlen = sscanf(ent->d_name + strlen(type),
452					   "%d",
453					   &number);
454			/* verify the next character is not a colon */
455			if (strncmp(ent->d_name + strlen(type) + numstrlen,
456					":",
457					1) != 0) {
458				filename = malloc(strlen(iio_dir)
459						+ strlen(type)
460						+ numstrlen
461						+ 6);
462				if (filename == NULL) {
463					closedir(dp);
464					return -ENOMEM;
465				}
466				sprintf(filename, "%s%s%d/name",
467					iio_dir,
468					type,
469					number);
470				nameFile = fopen(filename, "r");
471				if (!nameFile) {
472					free(filename);
473					continue;
474				}
475				free(filename);
476				fscanf(nameFile, "%s", thisname);
477				fclose(nameFile);
478				if (strcmp(name, thisname) == 0) {
479					closedir(dp);
480					return number;
481				}
482			}
483		}
484	}
485	closedir(dp);
486	return -ENODEV;
487}
488
489int _write_sysfs_int(char *filename, char *basedir, int val, int verify)
490{
491	int ret = 0;
492	FILE *sysfsfp;
493	int test;
494	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
495
496	if (temp == NULL)
497		return -ENOMEM;
498	sprintf(temp, "%s/%s", basedir, filename);
499	sysfsfp = fopen(temp, "w");
500	if (sysfsfp == NULL) {
501		printf("failed to open %s\n", temp);
502		ret = -errno;
503		goto error_free;
504	}
505	fprintf(sysfsfp, "%d", val);
506	fclose(sysfsfp);
507	if (verify) {
508		sysfsfp = fopen(temp, "r");
509		if (sysfsfp == NULL) {
510			printf("failed to open %s\n", temp);
511			ret = -errno;
512			goto error_free;
513		}
514		fscanf(sysfsfp, "%d", &test);
515		fclose(sysfsfp);
516		if (test != val) {
517			printf("Possible failure in int write %d to %s%s\n",
518				val,
519				basedir,
520				filename);
521			ret = -1;
522		}
523	}
524error_free:
525	free(temp);
526	return ret;
527}
528
529int write_sysfs_int(char *filename, char *basedir, int val)
530{
531	return _write_sysfs_int(filename, basedir, val, 0);
532}
533
534int write_sysfs_int_and_verify(char *filename, char *basedir, int val)
535{
536	return _write_sysfs_int(filename, basedir, val, 1);
537}
538
539int _write_sysfs_string(char *filename, char *basedir, char *val, int verify)
540{
541	int ret = 0;
542	FILE  *sysfsfp;
543	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
544
545	if (temp == NULL) {
546		printf("Memory allocation failed\n");
547		return -ENOMEM;
548	}
549	sprintf(temp, "%s/%s", basedir, filename);
550	sysfsfp = fopen(temp, "w");
551	if (sysfsfp == NULL) {
552		printf("Could not open %s\n", temp);
553		ret = -errno;
554		goto error_free;
555	}
556	fprintf(sysfsfp, "%s", val);
557	fclose(sysfsfp);
558	if (verify) {
559		sysfsfp = fopen(temp, "r");
560		if (sysfsfp == NULL) {
561			printf("could not open file to verify\n");
562			ret = -errno;
563			goto error_free;
564		}
565		fscanf(sysfsfp, "%s", temp);
566		fclose(sysfsfp);
567		if (strcmp(temp, val) != 0) {
568			printf("Possible failure in string write of %s "
569				"Should be %s "
570				"written to %s\%s\n",
571				temp,
572				val,
573				basedir,
574				filename);
575			ret = -1;
576		}
577	}
578error_free:
579	free(temp);
580
581	return ret;
582}
583
584/**
585 * write_sysfs_string_and_verify() - string write, readback and verify
586 * @filename: name of file to write to
587 * @basedir: the sysfs directory in which the file is to be found
588 * @val: the string to write
589 **/
590int write_sysfs_string_and_verify(char *filename, char *basedir, char *val)
591{
592	return _write_sysfs_string(filename, basedir, val, 1);
593}
594
595int write_sysfs_string(char *filename, char *basedir, char *val)
596{
597	return _write_sysfs_string(filename, basedir, val, 0);
598}
599
600int read_sysfs_posint(char *filename, char *basedir)
601{
602	int ret;
603	FILE  *sysfsfp;
604	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
605
606	if (temp == NULL) {
607		printf("Memory allocation failed");
608		return -ENOMEM;
609	}
610	sprintf(temp, "%s/%s", basedir, filename);
611	sysfsfp = fopen(temp, "r");
612	if (sysfsfp == NULL) {
613		ret = -errno;
614		goto error_free;
615	}
616	fscanf(sysfsfp, "%d\n", &ret);
617	fclose(sysfsfp);
618error_free:
619	free(temp);
620	return ret;
621}
622
623int read_sysfs_float(char *filename, char *basedir, float *val)
624{
625	int ret = 0;
626	FILE  *sysfsfp;
627	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
628
629	if (temp == NULL) {
630		printf("Memory allocation failed");
631		return -ENOMEM;
632	}
633	sprintf(temp, "%s/%s", basedir, filename);
634	sysfsfp = fopen(temp, "r");
635	if (sysfsfp == NULL) {
636		ret = -errno;
637		goto error_free;
638	}
639	fscanf(sysfsfp, "%f\n", val);
640	fclose(sysfsfp);
641error_free:
642	free(temp);
643	return ret;
644}
645
646int read_sysfs_string(const char *filename, const char *basedir, char *str)
647{
648	int ret = 0;
649	FILE  *sysfsfp;
650	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
651
652	if (temp == NULL) {
653		printf("Memory allocation failed");
654		return -ENOMEM;
655	}
656	sprintf(temp, "%s/%s", basedir, filename);
657	sysfsfp = fopen(temp, "r");
658	if (sysfsfp == NULL) {
659		ret = -errno;
660		goto error_free;
661	}
662	fscanf(sysfsfp, "%s\n", str);
663	fclose(sysfsfp);
664error_free:
665	free(temp);
666	return ret;
667}
668
669#endif /* _IIO_UTILS_H */
670