/* * libiio - Library for interfacing industrial I/O (IIO) devices * * Copyright (C) 2014, 2017 Analog Devices, Inc. * Author: Paul Cercueil * Robin Getz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * */ #include #include #include #include #include #include #include #include #define MY_NAME "iio_attr" #ifdef _WIN32 #define snprintf sprintf_s #else #define _strdup strdup #endif enum backend { LOCAL, XML, AUTO }; static bool str_match(const char * haystack, char * needle, bool ignore) { bool ret = false; int i; char *ncpy, *hcpy, *idx, first, last; if (!haystack || !needle) return false; if (!strlen(haystack) || !strlen(needle)) return false; /* '.' means match any */ if (!strcmp(".", needle) || !strcmp("*", needle)) return true; ncpy = _strdup(needle); hcpy = _strdup(haystack); if (!ncpy || !hcpy) goto eek; if (ignore) { for (i = 0; hcpy[i]; i++) hcpy[i] = tolower(hcpy[i]); for (i = 0; ncpy[i]; i++) ncpy[i] = tolower(ncpy[i]); } first = ncpy[0]; last = ncpy[strlen(ncpy) - 1]; if (first != '*' && last == '*') { /* 'key*' */ ret = !strncmp(hcpy, ncpy, strlen(ncpy) - 1); } else if ((first == '*') && (last == '*')) { /* '*key*' */ ncpy[strlen(ncpy) - 1] = 0; ret = strstr(hcpy, &ncpy[1]); } else if ((first == '*') && (last != '*')) { /* '*key' */ idx = strstr(hcpy, &ncpy[1]); if ((idx + strlen(&ncpy[1])) == (hcpy + strlen(hcpy))) ret = true; } else { /* 'key' */ ret = !strcmp(hcpy, ncpy); } eek: free(ncpy); free(hcpy); return ret; } static struct iio_context * autodetect_context(void) { struct iio_scan_context *scan_ctx; struct iio_context_info **info; struct iio_context *ctx = NULL; unsigned int i; ssize_t ret; scan_ctx = iio_create_scan_context(NULL, 0); if (!scan_ctx) { fprintf(stderr, "Unable to create scan context\n"); return NULL; } ret = iio_scan_context_get_info_list(scan_ctx, &info); if (ret < 0) { char err_str[1024]; iio_strerror(-ret, err_str, sizeof(err_str)); fprintf(stderr, "Scanning for IIO contexts failed: %s\n", err_str); goto err_free_ctx; } if (ret == 0) { printf("No IIO context found.\n"); goto err_free_info_list; } if (ret == 1) { printf("Using auto-detected IIO context at URI \"%s\"\n", iio_context_info_get_uri(info[0])); ctx = iio_create_context_from_uri(iio_context_info_get_uri(info[0])); } else { fprintf(stderr, "Multiple contexts found. Please select one using --uri:\n"); for (i = 0; i < (size_t) ret; i++) { fprintf(stderr, "\t%d: %s [%s]\n", i, iio_context_info_get_description(info[i]), iio_context_info_get_uri(info[i])); } } err_free_info_list: iio_context_info_list_free(info); err_free_ctx: iio_scan_context_destroy(scan_ctx); return ctx; } static void dump_device_attributes(const struct iio_device *dev, const char *attr, const char *wbuf, bool quiet) { ssize_t ret; char buf[1024]; if (!wbuf || !quiet) { if (!quiet) printf("dev '%s', attr '%s', value :", iio_device_get_name(dev), attr); ret = iio_device_attr_read(dev, attr, buf, sizeof(buf)); if (ret > 0) { if (quiet) printf("%s\n", buf); else printf("'%s'\n", buf); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li)\n", buf, (long)ret); } } if (wbuf) { ret = iio_device_attr_write(dev, attr, wbuf); if (ret > 0) { if (!quiet) printf("wrote %li bytes to %s\n", (long)ret, attr); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li) while writing '%s' with '%s'\n", buf, (long)ret, attr, wbuf); } dump_device_attributes(dev, attr, NULL, quiet); } } static void dump_buffer_attributes(const struct iio_device *dev, const char *attr, const char *wbuf, bool quiet) { ssize_t ret; char buf[1024]; if (!wbuf || !quiet) { ret = iio_device_buffer_attr_read(dev, attr, buf, sizeof(buf)); if (!quiet) printf("dev '%s', buffer attr '%s', value :", iio_device_get_name(dev), attr); if (ret > 0) { if (quiet) printf("%s\n", buf); else printf("'%s'\n", buf); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li)\n", buf, (long)ret); } } if (wbuf) { ret = iio_device_buffer_attr_write(dev, attr, wbuf); if (ret > 0) { if (!quiet) printf("wrote %li bytes to %s\n", (long)ret, attr); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li) while writing '%s' with '%s'\n", buf, (long)ret, attr, wbuf); } dump_buffer_attributes(dev, attr, NULL, quiet); } } static void dump_debug_attributes(const struct iio_device *dev, const char *attr, const char *wbuf, bool quiet) { ssize_t ret; char buf[1024]; if (!wbuf || !quiet) { ret = iio_device_debug_attr_read(dev, attr, buf, sizeof(buf)); if (!quiet) printf("dev '%s', debug attr '%s', value :", iio_device_get_name(dev), attr); if (ret > 0) { if (quiet) printf("%s\n", buf); else printf("'%s'\n", buf); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li)\n", buf, (long)ret); } } if (wbuf) { ret = iio_device_debug_attr_write(dev, attr, wbuf); if (ret > 0) { if (!quiet) printf("wrote %li bytes to %s\n", (long)ret, attr); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li) while writing '%s' with '%s'\n", buf, (long)ret, attr, wbuf); } dump_debug_attributes(dev, attr, NULL, quiet); } } static void dump_channel_attributes(const struct iio_device *dev, struct iio_channel *ch, const char *attr, const char *wbuf, bool quiet) { ssize_t ret; char buf[1024]; const char *type_name; if (!wbuf || !quiet) { if (iio_channel_is_output(ch)) type_name = "output"; else type_name = "input"; ret = iio_channel_attr_read(ch, attr, buf, sizeof(buf)); if (!quiet) printf("dev '%s', channel '%s' (%s), ", iio_device_get_name(dev), iio_channel_get_id(ch), type_name); if (iio_channel_get_name(ch) && !quiet) printf("id '%s', ", iio_channel_get_name(ch)); if (!quiet) printf("attr '%s', ", attr); if (ret > 0) { if (quiet) printf("%s\n", buf); else printf("value '%s'\n", buf); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("ERROR: %s (%li)\n", buf, (long)ret); } } if (wbuf) { ret = iio_channel_attr_write(ch, attr, wbuf); if (ret > 0) { if (!quiet) printf("wrote %li bytes to %s\n", (long)ret, attr); } else { iio_strerror(-ret, buf, sizeof(buf)); printf("error %s (%li) while writing '%s' with '%s'\n", buf, (long)ret, attr, wbuf); } dump_channel_attributes(dev, ch, attr, NULL, quiet); } } static const struct option options[] = { /* help */ {"help", no_argument, 0, 'h'}, {"ignore-case", no_argument, 0, 'I'}, {"quiet", no_argument, 0, 'q'}, /* context connection */ {"auto", no_argument, 0, 'a'}, {"uri", required_argument, 0, 'u'}, /* Channel qualifiers */ {"input-channel", no_argument, 0, 'i'}, {"output-channel", no_argument, 0, 'o'}, {"scan-channel", no_argument, 0, 's'}, /* Attribute type */ {"device-attr", no_argument, 0, 'd'}, {"channel-attr", no_argument, 0, 'c'}, {"context-attr", no_argument, 0, 'C'}, {"buffer-attr", no_argument, 0, 'B'}, {"debug-attr", no_argument, 0, 'D'}, {0, 0, 0, 0}, }; static const char *options_descriptions[] = { /* help */ "Show this help and quit.", "Ignore case distinctions.", "Return result only.", /* context connection */ "Use the first context found.", "Use the context at the provided URI.", /* Channel qualifiers */ "Filter Input Channels only.", "Filter Output Channels only.", "Filter Scan Channels only.", /* attribute type */ "Read/Write device attributes", "Read/Write channel attributes.", "Read IIO context attributes.", "Read/Write buffer attributes.", "Read/Write debug attributes.", }; static void usage(void) { unsigned int i, j = 0, k; printf("Usage:\n\t" MY_NAME " [OPTION]...\t-d [device] [attr] [value]\n" "\t\t\t\t-c [device] [channel] [attr] [value]\n" "\t\t\t\t-B [device] [attr] [value]\n" "\t\t\t\t-D [device] [attr] [value]\n" "\t\t\t\t-C [attr]\nOptions:\n"); for (i = 0; options[i].name; i++) { k = strlen(options[i].name); if (k > j) j = k; } j++; for (i = 0; options[i].name; i++) { printf("\t-%c, --%s%*c: %s\n", options[i].val, options[i].name, j - (int)strlen(options[i].name), ' ', options_descriptions[i]); if (i == 3) printf("Optional qualifiers:\n"); if (i == 6) printf("Attribute types:\n"); } } int main(int argc, char **argv) { struct iio_context *ctx; int c, option_index = 0; int device_index = 0, channel_index = 0, attr_index = 0; const char *arg_uri = NULL; enum backend backend = LOCAL; bool detect_context = false, search_device = false, ignore_case = false, search_channel = false, search_buffer = false, search_debug = false, search_context = false, input_only = false, output_only = false, scan_only = false, quiet = false; unsigned int i; char *wbuf = NULL; while ((c = getopt_long(argc, argv, "+hau:CdcBDiosIq", options, &option_index)) != -1) { switch (c) { /* help */ case 'h': usage(); return EXIT_SUCCESS; /* context connection */ case 'a': detect_context = true; break; case 'u': backend = AUTO; arg_uri = optarg; break; /* Attribute type * 'd'evice, 'c'hannel, 'C'ontext, 'B'uffer or 'D'ebug */ case 'd': search_device = true; break; case 'c': search_channel = true; break; case 'B': search_buffer = true; break; case 'D': search_debug = true; break; case 'C': search_context = true; break; /* Channel qualifiers */ case 'i': input_only = true; break; case 'o': output_only = true; break; case 's': scan_only = true; break; /* options */ case 'I': ignore_case = true; break; case 'q': quiet = true; break; case '?': printf("Unknown argument '%c'\n", c); return EXIT_FAILURE; } } if ((search_device + search_channel + search_context + search_debug + search_buffer) >= 2 ) { fprintf(stderr, "The options -d, -c, -C, -B, and -D are exclusive" " (can use only one).\n"); return EXIT_FAILURE; } if (!(search_device + search_channel + search_context + search_debug + search_buffer)) { if (argc == 1) { usage(); return EXIT_SUCCESS; } fprintf(stderr, "must specify one of -d, -c, -C, -B or -D.\n"); return EXIT_FAILURE; } if (search_context) { /* -C [IIO_attribute] */ if (argc >= optind + 1) attr_index = optind; if (argc >= optind + 2) { fprintf(stderr, "Too many options for searching for context attributes\n"); return EXIT_FAILURE; } } else if (search_device) { /* -d [device] [attr] [value] */ if (argc >= optind + 1) device_index = optind; if (argc >= optind + 2) attr_index = optind + 1; if (argc >= optind + 3) wbuf = argv[optind + 2]; if (argc >= optind + 4) { fprintf(stderr, "Too many options for searching for device attributes\n"); return EXIT_FAILURE; } } else if (search_channel) { /* -c [device] [channel] [attr] [value] */ if (argc >= optind + 1) device_index = optind; if (argc >= optind + 2) channel_index = optind + 1; if (argc >= optind + 3) attr_index = optind + 2; if (argc >= optind + 4) wbuf = argv[optind + 3]; if (argc >= optind + 5) { fprintf(stderr, "Too many options for searching for channel attributes\n"); return EXIT_FAILURE; } } else if (search_buffer) { /* -B [device] [attribute] [value] */ if (argc >= optind + 1) device_index = optind; if (argc >= optind + 2) attr_index = optind + 1; if (argc >= optind + 3) wbuf = argv[optind + 2]; if (argc >= optind + 4) { fprintf(stderr, "Too many options for searching for buffer attributes\n"); return EXIT_FAILURE; } } else if (search_debug) { /* -D [device] [attribute] [value] */ if (argc >= optind + 1) device_index = optind; if (argc >= optind + 2) attr_index = optind + 1; if (argc >= optind + 3) wbuf = argv[optind + 2]; if (argc >= optind + 4) { fprintf(stderr, "Too many options for searching for device attributes\n"); return EXIT_FAILURE; } } else { fprintf(stderr, "error in application\n"); return EXIT_FAILURE; } if (device_index && !argv[device_index]) return EXIT_FAILURE; if (channel_index && !argv[channel_index]) return EXIT_FAILURE; if (attr_index && !argv[attr_index]) return EXIT_FAILURE; if (wbuf && !wbuf) return EXIT_FAILURE; if (wbuf && ((device_index && (!strcmp(".", argv[device_index]) || strchr(argv[device_index], '*'))) || (channel_index && (!strcmp(".", argv[channel_index]) || strchr(argv[channel_index], '*'))) || (attr_index && (!strcmp(".", argv[attr_index]) || strchr(argv[attr_index], '*'))))) { printf("can't write value with wildcard match\n"); return EXIT_FAILURE; } if (detect_context) ctx = autodetect_context(); else if (backend == AUTO) ctx = iio_create_context_from_uri(arg_uri); else ctx = iio_create_default_context(); if (!ctx) { if (!detect_context) { char buf[1024]; iio_strerror(errno, buf, sizeof(buf)); fprintf(stderr, "Unable to create IIO context: %s\n", buf); } return EXIT_FAILURE; } if (search_context) { unsigned int nb_ctx_attrs = iio_context_get_attrs_count(ctx); if (!attr_index && nb_ctx_attrs > 0) printf("IIO context with %u attributes:\n", nb_ctx_attrs); for (i = 0; i < nb_ctx_attrs; i++) { const char *key, *value; iio_context_get_attr(ctx, i, &key, &value); if (!attr_index || str_match(key, argv[attr_index], ignore_case)) { printf("%s: %s\n", key, value); } } } if (search_device || search_channel || search_buffer || search_debug) { unsigned int nb_devices = iio_context_get_devices_count(ctx); if (!device_index) printf("IIO context has %u devices:\n", nb_devices); for (i = 0; i < nb_devices; i++) { const struct iio_device *dev = iio_context_get_device(ctx, i); const char *name = iio_device_get_name(dev); unsigned int nb_attrs, nb_channels, j; if (device_index && !str_match(name, argv[device_index], ignore_case)) continue; if ((search_device && !device_index) || (search_channel && !device_index) || (search_buffer && !device_index) || (search_debug && !device_index)) { printf("\t%s:", iio_device_get_id(dev)); if (name) printf(" %s", name); printf(", "); } if (search_channel && !device_index) printf("found %u channels\n", iio_device_get_channels_count(dev)); nb_channels = iio_device_get_channels_count(dev); for (j = 0; j < nb_channels; j++) { struct iio_channel *ch; const char *type_name; unsigned int k, nb_attrs; if (!search_channel || !device_index) continue; ch = iio_device_get_channel(dev, j); if (input_only && iio_channel_is_output(ch)) continue; if (output_only && !iio_channel_is_output(ch)) continue; if (scan_only && !iio_channel_is_scan_element(ch)) continue; if (iio_channel_is_output(ch)) type_name = "output"; else type_name = "input"; name = iio_channel_get_name(ch); if (channel_index && !str_match(iio_channel_get_id(ch), argv[channel_index], ignore_case) && (!name || (name && !str_match( name,argv[channel_index], ignore_case)))) continue; if ((!scan_only && !channel_index) || ( scan_only && iio_channel_is_scan_element(ch))) { printf("dev '%s', channel '%s'", iio_device_get_name(dev), iio_channel_get_id(ch)); if (name) printf(", id '%s'", name); printf(" (%s", type_name); if (iio_channel_is_scan_element(ch)) { const struct iio_data_format *format = iio_channel_get_data_format(ch); char sign = format->is_signed ? 's' : 'u'; char repeat[12] = ""; if (format->is_fully_defined) sign += 'A' - 'a'; if (format->repeat > 1) snprintf(repeat, sizeof(repeat), "X%u", format->repeat); printf(", index: %lu, format: %ce:%c%u/%u%s>>%u)", iio_channel_get_index(ch), format->is_be ? 'b' : 'l', sign, format->bits, format->length, repeat, format->shift); if (scan_only) printf("\n"); else printf(", "); } else { printf("), "); } } nb_attrs = iio_channel_get_attrs_count(ch); if (!channel_index) printf("found %u channel-specific attributes\n", nb_attrs); if (!nb_attrs || !channel_index) continue; for (k = 0; k < nb_attrs; k++) { const char *attr = iio_channel_get_attr(ch, k); if (attr_index && !str_match(attr, argv[attr_index], ignore_case)) continue; dump_channel_attributes(dev, ch, attr, wbuf, attr_index ? quiet : false); } } nb_attrs = iio_device_get_attrs_count(dev); if (search_device && !device_index) printf("found %u device attributes\n", nb_attrs); if (search_device && device_index && nb_attrs) { unsigned int j; for (j = 0; j < nb_attrs; j++) { const char *attr = iio_device_get_attr(dev, j); if (attr_index && !str_match(attr, argv[attr_index], ignore_case)) continue; dump_device_attributes(dev, attr, wbuf, attr_index ? quiet : false); } } nb_attrs = iio_device_get_buffer_attrs_count(dev); if (search_buffer && !device_index) printf("found %u buffer attributes\n", nb_attrs); if (search_buffer && device_index && nb_attrs) { unsigned int j; for (j = 0; j < nb_attrs; j++) { const char *attr = iio_device_get_buffer_attr(dev, j); if ((attr_index && str_match(attr, argv[attr_index], ignore_case)) || !attr_index) dump_buffer_attributes(dev, attr, wbuf, attr_index ? quiet : false); } } nb_attrs = iio_device_get_debug_attrs_count(dev); if (search_debug && !device_index) printf("found %u debug attributes\n", nb_attrs); if (search_debug && device_index && nb_attrs) { unsigned int j; for (j = 0; j < nb_attrs; j++) { const char *attr = iio_device_get_debug_attr(dev, j); if ((attr_index && str_match(attr, argv[attr_index], ignore_case)) || !attr_index) dump_debug_attributes(dev, attr, wbuf, attr_index ? quiet : false); } } } } iio_context_destroy(ctx); return EXIT_SUCCESS; }