iio_utils.c 14.9 KB
Newer Older
1 2 3 4 5 6 7 8
/* IIO - useful set of util functionality
 *
 * Copyright (c) 2008 Jonathan Cameron
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */
9 10
#ifndef _IIO_UTILS_H
#define _IIO_UTILS_H
11

12 13
#include <string.h>
#include <stdlib.h>
14 15
#include <stdio.h>
#include <stdint.h>
16
#include <dirent.h>
17
#include <errno.h>
18 19
#include <ctype.h>
#include "iio_utils.h"
20

21 22
const char *iio_dir = "/sys/bus/iio/devices/";

23 24 25 26 27
static char * const iio_direction[] = {
	"in",
	"out",
};

28 29 30 31 32
/**
 * iioutils_break_up_name() - extract generic name from full channel name
 * @full_name: the full channel name
 * @generic_name: the output generic channel name
 **/
33
int iioutils_break_up_name(const char *full_name,
34 35 36 37
				  char **generic_name)
{
	char *current;
	char *w, *r;
38 39 40 41 42 43 44 45 46
	char *working, *prefix = "";
	int i;

	for (i = 0; i < sizeof(iio_direction) / sizeof(iio_direction[0]); i++)
		if (!strncmp(full_name, iio_direction[i],
			     strlen(iio_direction[i]))) {
			prefix = iio_direction[i];
			break;
		}
47

48
	current = strdup(full_name + strlen(prefix) + 1);
49
	working = strtok(current, "_\0");
50

51 52 53
	w = working;
	r = working;

54
	while (*r != '\0') {
55 56 57 58 59 60 61
		if (!isdigit(*r)) {
			*w = *r;
			w++;
		}
		r++;
	}
	*w = '\0';
62
	asprintf(generic_name, "%s_%s", prefix, working);
63 64 65 66 67 68 69 70 71 72
	free(current);

	return 0;
}

/**
 * iioutils_get_type() - find and process _type attribute data
 * @is_signed: output whether channel is signed
 * @bytes: output how many bytes the channel storage occupies
 * @mask: output a bit mask for the raw data
73
 * @be: big endian
74 75 76 77
 * @device_dir: the iio device directory
 * @name: the channel name
 * @generic_name: the channel type name
 **/
78
int iioutils_get_type(unsigned *is_signed,
79 80
			     unsigned *bytes,
			     unsigned *bits_used,
81
			     unsigned *shift,
82
			     uint64_t *mask,
83
			     unsigned *be,
84 85 86 87 88 89 90 91
			     const char *device_dir,
			     const char *name,
			     const char *generic_name)
{
	FILE *sysfsfp;
	int ret;
	DIR *dp;
	char *scan_el_dir, *builtname, *builtname_generic, *filename = 0;
92
	char signchar, endianchar;
93
	unsigned padint;
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	const struct dirent *ent;

	ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_ret;
	}
	ret = asprintf(&builtname, FORMAT_TYPE_FILE, name);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_free_scan_el_dir;
	}
	ret = asprintf(&builtname_generic, FORMAT_TYPE_FILE, generic_name);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_free_builtname;
	}

	dp = opendir(scan_el_dir);
	if (dp == NULL) {
		ret = -errno;
		goto error_free_builtname_generic;
	}
	while (ent = readdir(dp), ent != NULL)
		/*
		 * Do we allow devices to override a generic name with
		 * a specific one?
		 */
		if ((strcmp(builtname, ent->d_name) == 0) ||
		    (strcmp(builtname_generic, ent->d_name) == 0)) {
			ret = asprintf(&filename,
				       "%s/%s", scan_el_dir, ent->d_name);
			if (ret < 0) {
				ret = -ENOMEM;
				goto error_closedir;
			}
			sysfsfp = fopen(filename, "r");
			if (sysfsfp == NULL) {
				ret = -errno;
H
Hartmut Knaack 已提交
133
				printf("failed to open %s\n", filename);
134 135
				goto error_free_filename;
			}
136 137 138 139 140 141 142 143

			ret = fscanf(sysfsfp,
				     "%ce:%c%u/%u>>%u",
				     &endianchar,
				     &signchar,
				     bits_used,
				     &padint, shift);
			if (ret < 0) {
144
				ret = -errno;
H
Hartmut Knaack 已提交
145
				printf("failed to pass scan type description\n");
146
				goto error_close_sysfsfp;
147 148 149 150
			} else if (ret != 5) {
				ret = -EIO;
				printf("scan type description didn't match\n");
				goto error_close_sysfsfp;
151
			}
152
			*be = (endianchar == 'b');
153
			*bytes = padint / 8;
154
			if (*bits_used == 64)
155 156 157 158 159 160 161
				*mask = ~0;
			else
				*mask = (1 << *bits_used) - 1;
			if (signchar == 's')
				*is_signed = 1;
			else
				*is_signed = 0;
162 163 164 165
			fclose(sysfsfp);
			free(filename);

			filename = 0;
166
			sysfsfp = 0;
167
		}
168 169 170
error_close_sysfsfp:
	if (sysfsfp)
		fclose(sysfsfp);
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
error_free_filename:
	if (filename)
		free(filename);
error_closedir:
	closedir(dp);
error_free_builtname_generic:
	free(builtname_generic);
error_free_builtname:
	free(builtname);
error_free_scan_el_dir:
	free(scan_el_dir);
error_ret:
	return ret;
}

186
int iioutils_get_param_float(float *output,
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
				    const char *param_name,
				    const char *device_dir,
				    const char *name,
				    const char *generic_name)
{
	FILE *sysfsfp;
	int ret;
	DIR *dp;
	char *builtname, *builtname_generic;
	char *filename = NULL;
	const struct dirent *ent;

	ret = asprintf(&builtname, "%s_%s", name, param_name);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_ret;
	}
	ret = asprintf(&builtname_generic,
		       "%s_%s", generic_name, param_name);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_free_builtname;
	}
	dp = opendir(device_dir);
	if (dp == NULL) {
		ret = -errno;
		goto error_free_builtname_generic;
	}
	while (ent = readdir(dp), ent != NULL)
		if ((strcmp(builtname, ent->d_name) == 0) ||
		    (strcmp(builtname_generic, ent->d_name) == 0)) {
			ret = asprintf(&filename,
				       "%s/%s", device_dir, ent->d_name);
			if (ret < 0) {
				ret = -ENOMEM;
				goto error_closedir;
			}
			sysfsfp = fopen(filename, "r");
			if (!sysfsfp) {
				ret = -errno;
				goto error_free_filename;
			}
			fscanf(sysfsfp, "%f", output);
			break;
		}
error_free_filename:
	if (filename)
		free(filename);
error_closedir:
	closedir(dp);
error_free_builtname_generic:
	free(builtname_generic);
error_free_builtname:
	free(builtname);
error_ret:
	return ret;
}

245 246 247 248 249
/**
 * bsort_channel_array_by_index() - reorder so that the array is in index order
 *
 **/

250
void bsort_channel_array_by_index(struct iio_channel_info **ci_array,
251 252 253 254 255 256 257 258 259 260 261 262 263 264
					 int cnt)
{

	struct iio_channel_info temp;
	int x, y;

	for (x = 0; x < cnt; x++)
		for (y = 0; y < (cnt - 1); y++)
			if ((*ci_array)[y].index > (*ci_array)[y+1].index) {
				temp = (*ci_array)[y + 1];
				(*ci_array)[y + 1] = (*ci_array)[y];
				(*ci_array)[y] = temp;
			}
}
265 266 267 268 269 270

/**
 * build_channel_array() - function to figure out what channels are present
 * @device_dir: the IIO device directory in sysfs
 * @
 **/
271
int build_channel_array(const char *device_dir,
272 273 274 275 276
			      struct iio_channel_info **ci_array,
			      int *counter)
{
	DIR *dp;
	FILE *sysfsfp;
277
	int count, i;
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	struct iio_channel_info *current;
	int ret;
	const struct dirent *ent;
	char *scan_el_dir;
	char *filename;

	*counter = 0;
	ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
	if (ret < 0) {
		ret = -ENOMEM;
		goto error_ret;
	}
	dp = opendir(scan_el_dir);
	if (dp == NULL) {
		ret = -errno;
		goto error_free_name;
	}
	while (ent = readdir(dp), ent != NULL)
		if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
			   "_en") == 0) {
			ret = asprintf(&filename,
				       "%s/%s", scan_el_dir, ent->d_name);
			if (ret < 0) {
				ret = -ENOMEM;
				goto error_close_dir;
			}
			sysfsfp = fopen(filename, "r");
			if (sysfsfp == NULL) {
				ret = -errno;
				free(filename);
				goto error_close_dir;
			}
310
			fscanf(sysfsfp, "%i", &ret);
311 312 313 314 315
			if (ret == 1)
				(*counter)++;
			fclose(sysfsfp);
			free(filename);
		}
316
	*ci_array = malloc(sizeof(**ci_array) * (*counter));
317 318 319 320 321
	if (*ci_array == NULL) {
		ret = -ENOMEM;
		goto error_close_dir;
	}
	seekdir(dp, 0);
322
	count = 0;
323 324 325
	while (ent = readdir(dp), ent != NULL) {
		if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"),
			   "_en") == 0) {
326
			int current_enabled = 0;
327

328 329 330 331 332 333 334 335 336 337 338 339
			current = &(*ci_array)[count++];
			ret = asprintf(&filename,
				       "%s/%s", scan_el_dir, ent->d_name);
			if (ret < 0) {
				ret = -ENOMEM;
				/* decrement count to avoid freeing name */
				count--;
				goto error_cleanup_array;
			}
			sysfsfp = fopen(filename, "r");
			if (sysfsfp == NULL) {
				ret = -errno;
H
Hartmut Knaack 已提交
340
				free(filename);
341
				count--;
342 343
				goto error_cleanup_array;
			}
344
			fscanf(sysfsfp, "%i", &current_enabled);
345
			fclose(sysfsfp);
346

347
			if (!current_enabled) {
348 349 350 351 352
				free(filename);
				count--;
				continue;
			}

353 354 355 356 357 358 359 360
			current->scale = 1.0;
			current->offset = 0;
			current->name = strndup(ent->d_name,
						strlen(ent->d_name) -
						strlen("_en"));
			if (current->name == NULL) {
				free(filename);
				ret = -ENOMEM;
361
				count--;
362 363 364 365 366 367 368
				goto error_cleanup_array;
			}
			/* Get the generic and specific name elements */
			ret = iioutils_break_up_name(current->name,
						     &current->generic_name);
			if (ret) {
				free(filename);
369 370
				free(current->name);
				count--;
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
				goto error_cleanup_array;
			}
			ret = asprintf(&filename,
				       "%s/%s_index",
				       scan_el_dir,
				       current->name);
			if (ret < 0) {
				free(filename);
				ret = -ENOMEM;
				goto error_cleanup_array;
			}
			sysfsfp = fopen(filename, "r");
			fscanf(sysfsfp, "%u", &current->index);
			fclose(sysfsfp);
			free(filename);
			/* Find the scale */
			ret = iioutils_get_param_float(&current->scale,
						       "scale",
						       device_dir,
						       current->name,
						       current->generic_name);
			if (ret < 0)
				goto error_cleanup_array;
			ret = iioutils_get_param_float(&current->offset,
						       "offset",
						       device_dir,
						       current->name,
						       current->generic_name);
			if (ret < 0)
				goto error_cleanup_array;
			ret = iioutils_get_type(&current->is_signed,
						&current->bytes,
						&current->bits_used,
404
						&current->shift,
405
						&current->mask,
406
						&current->be,
407 408 409 410 411
						device_dir,
						current->name,
						current->generic_name);
		}
	}
412

413
	closedir(dp);
414
	free(scan_el_dir);
415 416
	/* reorder so that the array is in index order */
	bsort_channel_array_by_index(ci_array, *counter);
417 418 419 420

	return 0;

error_cleanup_array:
421
	for (i = count - 1;  i >= 0; i--) {
422
		free((*ci_array)[i].name);
423 424
		free((*ci_array)[i].generic_name);
	}
425 426 427 428 429 430 431 432 433
	free(*ci_array);
error_close_dir:
	closedir(dp);
error_free_name:
	free(scan_el_dir);
error_ret:
	return ret;
}

434 435 436 437 438 439 440
/**
 * find_type_by_name() - function to match top level types by name
 * @name: top level type instance name
 * @type: the type of top level instance being sort
 *
 * Typical types this is used for are device and trigger.
 **/
441
int find_type_by_name(const char *name, const char *type)
442 443
{
	const struct dirent *ent;
444
	int number, numstrlen;
445 446 447

	FILE *nameFile;
	DIR *dp;
448 449 450
	char thisname[IIO_MAX_NAME_LENGTH];
	char *filename;

451 452
	dp = opendir(iio_dir);
	if (dp == NULL) {
453
		printf("No industrialio devices available\n");
454
		return -ENODEV;
455
	}
456

457 458
	while (ent = readdir(dp), ent != NULL) {
		if (strcmp(ent->d_name, ".") != 0 &&
459 460 461 462 463 464 465 466 467 468 469 470 471
			strcmp(ent->d_name, "..") != 0 &&
			strlen(ent->d_name) > strlen(type) &&
			strncmp(ent->d_name, type, strlen(type)) == 0) {
			numstrlen = sscanf(ent->d_name + strlen(type),
					   "%d",
					   &number);
			/* verify the next character is not a colon */
			if (strncmp(ent->d_name + strlen(type) + numstrlen,
					":",
					1) != 0) {
				filename = malloc(strlen(iio_dir)
						+ strlen(type)
						+ numstrlen
472
						+ 6);
473 474
				if (filename == NULL) {
					closedir(dp);
475
					return -ENOMEM;
476
				}
477 478 479 480 481
				sprintf(filename, "%s%s%d/name",
					iio_dir,
					type,
					number);
				nameFile = fopen(filename, "r");
482 483
				if (!nameFile) {
					free(filename);
484
					continue;
485
				}
486
				free(filename);
487 488
				fscanf(nameFile, "%s", thisname);
				fclose(nameFile);
489 490 491 492
				if (strcmp(name, thisname) == 0) {
					closedir(dp);
					return number;
				}
493 494 495
			}
		}
	}
496
	closedir(dp);
497
	return -ENODEV;
498 499
}

500
int _write_sysfs_int(char *filename, char *basedir, int val, int verify)
501
{
502
	int ret = 0;
503 504 505
	FILE *sysfsfp;
	int test;
	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
506

507 508 509
	if (temp == NULL)
		return -ENOMEM;
	sprintf(temp, "%s/%s", basedir, filename);
510
	sysfsfp = fopen(temp, "w");
511 512
	if (sysfsfp == NULL) {
		ret = -errno;
H
Hartmut Knaack 已提交
513
		printf("failed to open %s\n", temp);
514 515
		goto error_free;
	}
516 517
	fprintf(sysfsfp, "%d", val);
	fclose(sysfsfp);
518 519 520 521
	if (verify) {
		sysfsfp = fopen(temp, "r");
		if (sysfsfp == NULL) {
			ret = -errno;
H
Hartmut Knaack 已提交
522
			printf("failed to open %s\n", temp);
523 524 525
			goto error_free;
		}
		fscanf(sysfsfp, "%d", &test);
526
		fclose(sysfsfp);
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
		if (test != val) {
			printf("Possible failure in int write %d to %s%s\n",
				val,
				basedir,
				filename);
			ret = -1;
		}
	}
error_free:
	free(temp);
	return ret;
}

int write_sysfs_int(char *filename, char *basedir, int val)
{
	return _write_sysfs_int(filename, basedir, val, 0);
543 544
}

545
int write_sysfs_int_and_verify(char *filename, char *basedir, int val)
546 547 548 549 550
{
	return _write_sysfs_int(filename, basedir, val, 1);
}

int _write_sysfs_string(char *filename, char *basedir, char *val, int verify)
551
{
552
	int ret = 0;
553
	FILE  *sysfsfp;
554
	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
555

556 557 558 559 560
	if (temp == NULL) {
		printf("Memory allocation failed\n");
		return -ENOMEM;
	}
	sprintf(temp, "%s/%s", basedir, filename);
561
	sysfsfp = fopen(temp, "w");
562 563
	if (sysfsfp == NULL) {
		ret = -errno;
H
Hartmut Knaack 已提交
564
		printf("Could not open %s\n", temp);
565 566 567
		goto error_free;
	}
	fprintf(sysfsfp, "%s", val);
568
	fclose(sysfsfp);
569 570 571 572
	if (verify) {
		sysfsfp = fopen(temp, "r");
		if (sysfsfp == NULL) {
			ret = -errno;
H
Hartmut Knaack 已提交
573
			printf("could not open file to verify\n");
574 575 576
			goto error_free;
		}
		fscanf(sysfsfp, "%s", temp);
577
		fclose(sysfsfp);
578 579 580
		if (strcmp(temp, val) != 0) {
			printf("Possible failure in string write of %s "
				"Should be %s "
L
Lucas De Marchi 已提交
581
				"written to %s\%s\n",
582 583 584 585 586 587
				temp,
				val,
				basedir,
				filename);
			ret = -1;
		}
588
	}
589 590
error_free:
	free(temp);
591

592
	return ret;
593
}
594

595 596 597 598 599 600 601 602
/**
 * write_sysfs_string_and_verify() - string write, readback and verify
 * @filename: name of file to write to
 * @basedir: the sysfs directory in which the file is to be found
 * @val: the string to write
 **/
int write_sysfs_string_and_verify(char *filename, char *basedir, char *val)
{
603 604
	return _write_sysfs_string(filename, basedir, val, 1);
}
605

606 607 608
int write_sysfs_string(char *filename, char *basedir, char *val)
{
	return _write_sysfs_string(filename, basedir, val, 0);
609 610 611 612 613 614
}

int read_sysfs_posint(char *filename, char *basedir)
{
	int ret;
	FILE  *sysfsfp;
615
	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
616

617 618 619 620 621
	if (temp == NULL) {
		printf("Memory allocation failed");
		return -ENOMEM;
	}
	sprintf(temp, "%s/%s", basedir, filename);
622
	sysfsfp = fopen(temp, "r");
623 624 625 626
	if (sysfsfp == NULL) {
		ret = -errno;
		goto error_free;
	}
627 628
	fscanf(sysfsfp, "%d\n", &ret);
	fclose(sysfsfp);
629 630 631 632 633 634 635
error_free:
	free(temp);
	return ret;
}

int read_sysfs_float(char *filename, char *basedir, float *val)
{
636
	int ret = 0;
637 638
	FILE  *sysfsfp;
	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
639

640 641 642 643 644 645 646 647 648 649 650 651 652 653
	if (temp == NULL) {
		printf("Memory allocation failed");
		return -ENOMEM;
	}
	sprintf(temp, "%s/%s", basedir, filename);
	sysfsfp = fopen(temp, "r");
	if (sysfsfp == NULL) {
		ret = -errno;
		goto error_free;
	}
	fscanf(sysfsfp, "%f\n", val);
	fclose(sysfsfp);
error_free:
	free(temp);
654 655
	return ret;
}
656

657
int read_sysfs_string(const char *filename, const char *basedir, char *str)
658
{
659
	int ret = 0;
660 661
	FILE  *sysfsfp;
	char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
662

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
	if (temp == NULL) {
		printf("Memory allocation failed");
		return -ENOMEM;
	}
	sprintf(temp, "%s/%s", basedir, filename);
	sysfsfp = fopen(temp, "r");
	if (sysfsfp == NULL) {
		ret = -errno;
		goto error_free;
	}
	fscanf(sysfsfp, "%s\n", str);
	fclose(sysfsfp);
error_free:
	free(temp);
	return ret;
}
679 680

#endif /* _IIO_UTILS_H */