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