// SPDX-License-Identifier: GPL-2.0-only
/* IIO - useful set of util functionality
*
* Copyright (c) 2008 Jonathan Cameron
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
#include "iio_utils.h"
const char *iio_dir = "/sys/bus/iio/devices/";
static char * const iio_direction[] = {
"in",
"out",
};
/**
* iioutils_break_up_name() - extract generic name from full channel name
* @full_name: the full channel name
* @generic_name: the output generic channel name
*
* Returns 0 on success, or a negative error code if string extraction failed.
**/
int iioutils_break_up_name(const char *full_name, char **generic_name)
{
char *current;
char *w, *r;
char *working, *prefix = "";
int i, ret;
for (i = 0; i < ARRAY_SIZE(iio_direction); i++)
if (!strncmp(full_name, iio_direction[i],
strlen(iio_direction[i]))) {
prefix = iio_direction[i];
break;
}
current = strdup(full_name + strlen(prefix) + 1);
if (!current)
return -ENOMEM;
working = strtok(current, "_\0");
if (!working) {
free(current);
return -EINVAL;
}
w = working;
r = working;
while (*r != '\0') {
if (!isdigit(*r)) {
*w = *r;
w++;
}
r++;
}
*w = '\0';
ret = asprintf(generic_name, "%s_%s", prefix, working);
free(current);
return (ret == -1) ? -ENOMEM : 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
* @bits_used: output number of valid bits of data
* @shift: output amount of bits to shift right data before applying bit mask
* @mask: output a bit mask for the raw data
* @be: output if data in big endian
* @device_dir: the IIO device directory
* @buffer_idx: the IIO buffer index
* @name: the channel name
* @generic_name: the channel type name
*
* Returns a value >= 0 on success, otherwise a negative error code.
**/
static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
unsigned int *bits_used, unsigned int *shift,
uint64_t *mask, unsigned int *be,
const char *device_dir, int buffer_idx,
const char *name, const char *generic_name)
{
FILE *sysfsfp;
int ret;
DIR *dp;
char *scan_el_dir, *builtname, *builtname_generic, *filename = 0;
char signchar, endianchar;
unsigned padint;
const struct dirent *ent;
ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx);
if (ret < 0)
return -ENOMEM;
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) {
ret = -errno;
goto error_free_builtname_generic;
}
ret = -ENOENT;
while (ent = readdir(dp), ent)
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) {
ret = -errno;
fprintf(stderr, "failed to open %s\n",
filename);
goto error_free_filename;
}
ret = fscanf(sysfsfp,
"%ce:%c%u/%u>>%u",
&endianchar,
&signchar,
bits_used,
&padint, shift);
if (ret < 0) {
ret = -errno;
fprintf(stderr,
"failed to pass scan type description\n");
goto error_close_sysfsfp;
} else if (ret != 5) {
ret = -EIO;
fprintf(stderr,
"scan type description didn't match\n");
goto error_close_sysfsfp;
}
*be = (endianchar == 'b');
*bytes = padint / 8;
if (*bits_used == 64)
*mask = ~(0ULL);
else
*mask = (1ULL << *bits_used) - 1ULL;
*is_signed = (signchar == 's');
if (fclose(sysfsfp)) {
ret = -errno;
fprintf(stderr, "Failed to close %s\n",
filename);
goto error_free_filename;
}
sysfsfp = 0;
free(filename);
filename = 0;
/*
* Avoid having a more generic entry overwriting
* the settings.
*/
if (strcmp(builtname, ent->d_name) == 0)
break;
}
error_close_sysfsfp:
if (sysfsfp)
if (fclose(sysfsfp))
perror("iioutils_get_type(): Failed to close file");
error_free_filename:
if (filename)
free(filename);
error_closedir