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", ¤t->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 ¤t->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", ¤t->index); 388 fclose(sysfsfp); 389 free(filename); 390 /* Find the scale */ 391 ret = iioutils_get_param_float(¤t->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(¤t->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(¤t->is_signed, 406 ¤t->bytes, 407 ¤t->bits_used, 408 ¤t->shift, 409 ¤t->mask, 410 ¤t->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