From caa95f8b1057cf908c805270e198098e23cede33 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Thu, 18 Nov 2021 16:57:01 -0500 Subject: [PATCH 1/9] Altough strndup is POSIX.1-2008; Windows does not provide it Add a backup function for Windows machines. Signed-off-by: Robin Getz --- iio-private.h | 1 + utilities.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/iio-private.h b/iio-private.h index 41872d154..6938a9e9c 100644 --- a/iio-private.h +++ b/iio-private.h @@ -260,6 +260,7 @@ void iio_channel_init_finalize(struct iio_channel *chn); unsigned int find_channel_modifier(const char *s, size_t *len_p); char *iio_strdup(const char *str); +char *iio_strndup(const char *str, size_t n); size_t iio_strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); char * iio_getenv (char * envvar); diff --git a/utilities.c b/utilities.c index 579555d50..132c75845 100644 --- a/utilities.c +++ b/utilities.c @@ -229,6 +229,24 @@ char *iio_strdup(const char *str) #endif } +/* strndup conforms to POSIX.1-2008; but Windows does not provided it + */ +char *iio_strndup(const char *str, size_t n) +{ +#ifdef HAS_STRNDUP + return strndup(str, n); +#else + size_t len = strnlen(str, n + 1); + char *buf = malloc(len + 1); + if (buf) { + /* len = size of buf, so memcpy is OK */ + memcpy(buf, str, len); /* Flawfinder: ignore */ + buf[len] = 0; + } + return buf; +#endif +} + /* strlcpy is designed to be safer, more consistent, and less error prone * replacements for strncpy, since it guarantees to NUL-terminate the result. * From 468f663a8af0fc40a60d0736fe6c4290b84766f8 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 22 Nov 2021 18:25:49 -0500 Subject: [PATCH 2/9] Windows doesn't provide strtok_r, it uses strtok_s so wrap it to make sure we can find it. Signed-off-by: Robin Getz --- CMakeLists.txt | 1 + iio-config.h.cmakein | 1 + iio-private.h | 1 + utilities.c | 11 +++++++++++ 4 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30576c045..698676f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ include(CheckSymbolExists) check_symbol_exists(strdup "string.h" HAS_STRDUP) check_symbol_exists(strndup "string.h" HAS_STRNDUP) check_symbol_exists(strerror_r "string.h" HAS_STRERROR_R) +check_symbol_exists(strtok_r "string.h" HAS_STRTOK_R) check_symbol_exists(newlocale "locale.h" HAS_NEWLOCALE) option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON) diff --git a/iio-config.h.cmakein b/iio-config.h.cmakein index ab2687dc2..6f8932536 100644 --- a/iio-config.h.cmakein +++ b/iio-config.h.cmakein @@ -28,6 +28,7 @@ #cmakedefine HAS_PIPE2 #cmakedefine HAS_STRDUP #cmakedefine HAS_STRNDUP +#cmakedefine HAS_STRTOK_R #cmakedefine HAS_STRERROR_R #cmakedefine HAS_NEWLOCALE #cmakedefine HAS_PTHREAD_SETNAME_NP diff --git a/iio-private.h b/iio-private.h index 6938a9e9c..71a46afef 100644 --- a/iio-private.h +++ b/iio-private.h @@ -261,6 +261,7 @@ unsigned int find_channel_modifier(const char *s, size_t *len_p); char *iio_strdup(const char *str); char *iio_strndup(const char *str, size_t n); +char *iio_strtok_r(char *str, const char *delim, char **saveptr); size_t iio_strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); char * iio_getenv (char * envvar); diff --git a/utilities.c b/utilities.c index 132c75845..87b7f516e 100644 --- a/utilities.c +++ b/utilities.c @@ -213,6 +213,17 @@ void iio_strerror(int err, char *buf, size_t len) } } +char *iio_strtok_r(char *str, const char *delim, char **saveptr) +{ +#if defined(_WIN32) + return strtok_s(str, delim, saveptr); +#elif defined(HAS_STRTOK_R) + return strtok_r(str, delim, saveptr); +#else +#error Need a implentation of strtok_r for this platform +#endif +} + char *iio_strdup(const char *str) { #if defined(_WIN32) From eb861be6dbaaac5fcde83094cb88b37c43f652c6 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 17 Feb 2022 09:46:29 +0000 Subject: [PATCH 3/9] scan: Rework scanning Instead of going through the list of enabled backends and checking for each one whether or not we were asked to scan from it, we now go through the user-supplied backends list, find the corresponding backend, and call its scan function. This change will make it possible to support scanning from the same backend multiple times, for instance with different USB devices. Signed-off-by: Paul Cercueil --- scan.c | 66 ++++++++++++++++++++++++++-------------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/scan.c b/scan.c index 4e3515801..bee16f591 100644 --- a/scan.c +++ b/scan.c @@ -14,9 +14,7 @@ #include struct iio_scan_context { - bool scan_usb; - bool scan_network; - bool scan_local; + char *backendopts; }; const char * iio_context_info_get_description( @@ -35,37 +33,34 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, struct iio_context_info ***info) { struct iio_scan_result scan_result = { 0, NULL }; - - if (WITH_LOCAL_BACKEND && ctx->scan_local) { - int ret = local_context_scan(&scan_result); - if (ret < 0) { - if (scan_result.info) - iio_context_info_list_free(scan_result.info); - return ret; - } - } - - if (WITH_USB_BACKEND && ctx->scan_usb) { - int ret = usb_context_scan(&scan_result); - if (ret < 0) { - if (scan_result.info) - iio_context_info_list_free(scan_result.info); - return ret; - } - } - - if (HAVE_DNS_SD && ctx->scan_network) { - int ret = dnssd_context_scan(&scan_result); - if (ret < 0) { - if (scan_result.info) - iio_context_info_list_free(scan_result.info); - return ret; + char *token, *rest=NULL; + ssize_t ret; + + for (token = iio_strtok_r(ctx->backendopts, ":", &rest); + token; token = iio_strtok_r(NULL, ":", &rest)) { + + /* Since tokens are all null terminated, it's safe to use strcmp on them */ + if (WITH_LOCAL_BACKEND && !strcmp(token, "local")) { + ret = local_context_scan(&scan_result); + } else if (WITH_USB_BACKEND && !strcmp(token, "usb")) { + ret = usb_context_scan(&scan_result); + } else if (HAVE_DNS_SD && !strcmp(token, "ip")) { + ret = dnssd_context_scan(&scan_result); + } else { + ret = -ENODEV; } + if (ret < 0) + goto err_free_scan_result_info; } *info = scan_result.info; return (ssize_t) scan_result.size; + +err_free_scan_result_info: + if (scan_result.info) + iio_context_info_list_free(scan_result.info); + return ret; } void iio_context_info_list_free(struct iio_context_info **list) @@ -126,20 +121,19 @@ struct iio_scan_context * iio_create_scan_context( return NULL; } - if (!backend || strstr(backend, "local")) - ctx->scan_local = true; - - if (!backend || strstr(backend, "usb")) - ctx->scan_usb = true; - - if (!backend || strstr(backend, "ip")) - ctx->scan_network = true; + ctx->backendopts = iio_strndup(backend ? backend : "local:usb:ip", PATH_MAX); + if (!ctx->backendopts) { + free(ctx); + errno = ENOMEM; + return NULL; + } return ctx; } void iio_scan_context_destroy(struct iio_scan_context *ctx) { + free(ctx->backendopts); free(ctx); } From 4aaafc07c8a58052369dd912492df29698705112 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 17 Feb 2022 09:47:25 +0000 Subject: [PATCH 4/9] scan: Prefer comma instead of colon for backends separator Libiio 1.x (in the dev branch) uses a comma instead of a colon as the backends separator in the scan list string. This makes it possible to use the colon as the VID/PID separator in the USB backend parameters. Update the code and documentation to specify that commas should be used, while still allowing colons as separators to be backwards-compatible. Signed-off-by: Paul Cercueil --- iio.h | 15 ++++++++++----- scan.c | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/iio.h b/iio.h index 2f6a44321..07a1a8004 100644 --- a/iio.h +++ b/iio.h @@ -236,13 +236,18 @@ enum iio_event_direction { /** @brief Create a scan context - * @param backend A NULL-terminated string containing the backend(s) to use for - * scanning (example: pre version 0.20 : "local", "ip", or "usb"; post version - * 0.20 can handle multiple, including "local:usb:", "ip:usb:", "local:usb:ip:"). - * If NULL, all the available backends are used. + * @param backend A NULL-terminated string containing a comma-separated + * list of the backend(s) to use for scanning. * @param flags Unused for now. Set to 0. * @return on success, a pointer to a iio_scan_context structure - * @return On failure, NULL is returned and errno is set appropriately */ + * @return On failure, NULL is returned and errno is set appropriately + * + * NOTE: Libiio version 0.20 and above can handle multiple + * strings, for instance "local:usb:", "ip:usb:", "local:usb:ip:", and + * require a colon as the delimiter. If NULL, the local, USB and IP + * backends will be scanned. + * Libiio version 0.24 and above prefer a comma instead of colon as the + * delimiter. */ __api __check_ret struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags); diff --git a/scan.c b/scan.c index bee16f591..4e9e72ebc 100644 --- a/scan.c +++ b/scan.c @@ -36,8 +36,8 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, char *token, *rest=NULL; ssize_t ret; - for (token = iio_strtok_r(ctx->backendopts, ":", &rest); - token; token = iio_strtok_r(NULL, ":", &rest)) { + for (token = iio_strtok_r(ctx->backendopts, ",", &rest); + token; token = iio_strtok_r(NULL, ",", &rest)) { /* Since tokens are all null terminated, it's safe to use strcmp on them */ if (WITH_LOCAL_BACKEND && !strcmp(token, "local")) { @@ -108,6 +108,7 @@ struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags) { struct iio_scan_context *ctx; + unsigned int i, len; /* "flags" must be zero for now */ if (flags != 0) { @@ -121,13 +122,21 @@ struct iio_scan_context * iio_create_scan_context( return NULL; } - ctx->backendopts = iio_strndup(backend ? backend : "local:usb:ip", PATH_MAX); + ctx->backendopts = iio_strndup(backend ? backend : "local,usb,ip", PATH_MAX); if (!ctx->backendopts) { free(ctx); errno = ENOMEM; return NULL; } + if (backend) { + /* Replace the colon separator with a comma. */ + len = strlen(ctx->backendopts); + for (i = 0; i < len; i++) + if (ctx->backendopts[i] == ':') + ctx->backendopts[i] = ','; + } + return ctx; } From fe4cf272aefa6b062f3e126932f6a7dbd18ba2a3 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 15 Nov 2021 10:02:20 -0500 Subject: [PATCH 5/9] usb: when scanning provide option to qualify USB vendor ID or product ID When we do a USB scan, we open each device looking for a string "IIO", and if it is not, then we close the device. The archirecture of libusb is that when we are doing this, other applications are locked out from using that device. This is known to cause some user problems on other devices (not IIO) - most notibly HackRF and USRP. This change adds the ability to restricting scans to certain vendors, or specific product IDs - which is what most purpose build software is looking for. (GNU Radio doesn't want to open every device, only the ones it knows about). This should allow us to play nicer with others. Examples: usb,local local,usb=0456:b673,usb=0456:b672 usb=0456:* VENDOR ID and PRODUCT ID are hexadecimal numbers (no prefix needed), "*" (match any). By default (no vid:pid provided) all devices are opened and checked (which is the previous behavour). Signed-off-by: Robin Getz Signed-off-by: Paul Cercueil --- iio-private.h | 2 +- iio.h | 10 ++++++--- scan.c | 19 ++++++++++++++-- usb.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/iio-private.h b/iio-private.h index 71a46afef..9cc50a5c5 100644 --- a/iio-private.h +++ b/iio-private.h @@ -249,7 +249,7 @@ struct iio_context * serial_create_context_from_uri(const char *uri); int local_context_scan(struct iio_scan_result *scan_result); -int usb_context_scan(struct iio_scan_result *scan_result); +int usb_context_scan(struct iio_scan_result *scan_result, const char *args); int dnssd_context_scan(struct iio_scan_result *scan_result); diff --git a/iio.h b/iio.h index 07a1a8004..7442a9f3b 100644 --- a/iio.h +++ b/iio.h @@ -244,10 +244,14 @@ enum iio_event_direction { * * NOTE: Libiio version 0.20 and above can handle multiple * strings, for instance "local:usb:", "ip:usb:", "local:usb:ip:", and - * require a colon as the delimiter. If NULL, the local, USB and IP - * backends will be scanned. + * require a colon as the delimiter. * Libiio version 0.24 and above prefer a comma instead of colon as the - * delimiter. */ + * delimiter, and handle specifying backend-specific information. For + * instance, "local,usb=0456:*" will scan the local backend and limit + * scans on USB to vendor ID 0x0456, and accept all product IDs. The + * "usb=0456:b673" string would limit the scan to the device with this + * particular VID/PID. Both IDs are expected in hexadecimal, no 0x + * prefix needed. */ __api __check_ret struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags); diff --git a/scan.c b/scan.c index 4e9e72ebc..33f2ce2cf 100644 --- a/scan.c +++ b/scan.c @@ -42,8 +42,10 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, /* Since tokens are all null terminated, it's safe to use strcmp on them */ if (WITH_LOCAL_BACKEND && !strcmp(token, "local")) { ret = local_context_scan(&scan_result); - } else if (WITH_USB_BACKEND && !strcmp(token, "usb")) { - ret = usb_context_scan(&scan_result); + } else if (WITH_USB_BACKEND && (!strcmp(token, "usb") || + !strncmp(token, "usb=", sizeof("usb=") - 1))) { + token = token[3] == '=' ? token + 4 : NULL; + ret = usb_context_scan(&scan_result, token); } else if (HAVE_DNS_SD && !strcmp(token, "ip")) { ret = dnssd_context_scan(&scan_result); } else { @@ -108,6 +110,7 @@ struct iio_scan_context * iio_create_scan_context( const char *backend, unsigned int flags) { struct iio_scan_context *ctx; + char *ptr, *ptr2; unsigned int i, len; /* "flags" must be zero for now */ @@ -135,6 +138,18 @@ struct iio_scan_context * iio_create_scan_context( for (i = 0; i < len; i++) if (ctx->backendopts[i] == ':') ctx->backendopts[i] = ','; + + /* The only place where a colon is accepted is in the usb arguments: + * usb=vid:pid */ + for (ptr = strstr(ctx->backendopts, "usb="); ptr; + ptr = strstr(ptr, "usb=")) { + ptr += sizeof("usb="); + strtoul(ptr, &ptr2, 16); + + /* The USB backend will take care of errors */ + if (ptr2 != ptr && *ptr2 == ',') + *ptr2 = ':'; + } } return ctx; diff --git a/usb.c b/usb.c index 97a4496b9..e12fb332f 100644 --- a/usb.c +++ b/usb.c @@ -1200,14 +1200,58 @@ static int usb_fill_context_info(struct iio_context_info *info, return 0; } -int usb_context_scan(struct iio_scan_result *scan_result) +static int parse_vid_pid(const char *vid_pid, uint16_t *vid, uint16_t *pid) +{ + unsigned long val; + char *ptr; + + /* + * vid_pid string must be either: + * - NULL: scan everything, + * - "vid:*": scan all devices with the given VID, + * - "vid:pid": scan the device with the given VID/PID. + * IDs are given in hexadecimal, and the 0x prefix is not required. + */ + + *vid = 0; + *pid = 0; + + if (!vid_pid) + return 0; + + val = strtoul(vid_pid, &ptr, 16); + if (ptr == vid_pid || val > 0xFFFF || *ptr != ':') + return -EINVAL; + + *vid = (uint16_t) val; + + vid_pid = ptr + 1; + + if (*vid_pid == '*') + return vid_pid[1] == '\0' ? 0 : -EINVAL; + + val = strtoul(vid_pid, &ptr, 16); + if (ptr == vid_pid || val > 0xFFFF || *ptr != '\0') + return -EINVAL; + + *pid = (uint16_t) val; + + return 0; +} + +int usb_context_scan(struct iio_scan_result *scan_result, const char *args) { struct iio_context_info *info; libusb_device **device_list; libusb_context *ctx; + uint16_t vid, pid; unsigned int i; int ret; + ret = parse_vid_pid(args, &vid, &pid); + if (ret) + return ret; + ret = libusb_init(&ctx); if (ret < 0) return -(int) libusb_to_errno(ret); @@ -1222,6 +1266,21 @@ int usb_context_scan(struct iio_scan_result *scan_result) struct libusb_device_handle *hdl; struct libusb_device *dev = device_list[i]; unsigned int intrfc = 0; + struct libusb_device_descriptor device_descriptor; + + /* If we are given a pid or vid, use that to qualify for things, + * this avoids open/closing random devices & potentially locking + * (blocking them) from other applications + */ + if(vid || pid) { + ret = libusb_get_device_descriptor(dev, &device_descriptor); + if (ret) + continue; + if (vid && vid != device_descriptor.idVendor) + continue; + if (pid && pid != device_descriptor.idProduct) + continue; + } ret = libusb_open(dev, &hdl); if (ret) From 977fbd508aaf729af4b391394d8b48d97d6441e9 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 22 Nov 2021 23:55:59 -0500 Subject: [PATCH 6/9] scan: with the new scan options that we support, handle duplicates With the updated scan options, it's possible to scan usb, or ip multiple times, and get duplicates in the list. This sorts the list, and removes duplicates if found, so users get a minimal list. Signed-off-by: Robin Getz Signed-off-by: Paul Cercueil --- scan.c | 50 ++++++++++++++++++++++++++++++++++++++++---------- sort.c | 17 +++++++++++++++++ sort.h | 1 + 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/scan.c b/scan.c index 33f2ce2cf..503864e5e 100644 --- a/scan.c +++ b/scan.c @@ -8,6 +8,7 @@ #include "iio-config.h" #include "iio-private.h" +#include "sort.h" #include #include @@ -33,7 +34,9 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, struct iio_context_info ***info) { struct iio_scan_result scan_result = { 0, NULL }; + struct iio_context_info *out; char *token, *rest=NULL; + size_t i, j = 0; ssize_t ret; for (token = iio_strtok_r(ctx->backendopts, ",", &rest); @@ -57,6 +60,38 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, *info = scan_result.info; + if (scan_result.size > 1) { + qsort(scan_result.info, scan_result.size, + sizeof(struct iio_context_info *), + iio_context_info_compare); + + /* there might be duplicates */ + for (i = 1; i < scan_result.size; i++) { + /* ipv6 and ipv4 can have the same uri, but have different descriptions, + * so check both if necessary + */ + if ((!strcmp(scan_result.info[i - 1]->uri, + scan_result.info[i]->uri)) && + (!strcmp(scan_result.info[i - 1]->description, + scan_result.info[i]->description))) { + out = scan_result.info[i - 1]; + j++; + free(out->description); + free(out->uri); + out->description = NULL; + out->uri = NULL; + } + } + if (j) { + /* Force all the nulls to the end */ + qsort(scan_result.info, scan_result.size, + sizeof(struct iio_context_info *), + iio_context_info_compare); + return (ssize_t) scan_result.size - j; + } + } + + return (ssize_t) scan_result.size; err_free_scan_result_info: @@ -67,17 +102,12 @@ ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, void iio_context_info_list_free(struct iio_context_info **list) { - struct iio_context_info **it; - - if (!list) - return; - - for (it = list; *it; it++) { - struct iio_context_info *info = *it; + unsigned int i; - free(info->description); - free(info->uri); - free(info); + for (i = 0; list && list[i]; i++) { + free(list[i]->description); + free(list[i]->uri); + free(list[i]); } free(list); diff --git a/sort.c b/sort.c index 04187e102..822cde0f7 100644 --- a/sort.c +++ b/sort.c @@ -81,3 +81,20 @@ int iio_buffer_attr_compare(const void *p1, const void *p2) return strcmp(tmp1, tmp2); } +int iio_context_info_compare(const void *p1, const void *p2) +{ + int ret; + const struct iio_context_info *tmp1 = *(struct iio_context_info **)p1; + const struct iio_context_info *tmp2 = *(struct iio_context_info **)p2; + + if(!tmp1->uri) + return 1; + if (!tmp2->uri) + return 0; + + ret = strcmp(tmp1->uri, tmp2->uri); + if (ret) + return ret; + + return strcmp(tmp1->description, tmp2->description); +} diff --git a/sort.h b/sort.h index 66953518d..670f1dc6a 100644 --- a/sort.h +++ b/sort.h @@ -14,5 +14,6 @@ int iio_channel_attr_compare(const void *p1, const void *p2); int iio_device_compare(const void *p1, const void *p2); int iio_device_attr_compare(const void *p1, const void *p2); int iio_buffer_attr_compare(const void *p1, const void *p2); +int iio_context_info_compare(const void *p1, const void *p2); #endif /* __IIO_QSORT_H__ */ From f5a83d4d118ac9f6c551d4718d638744d9931aff Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 17 Feb 2022 10:12:03 +0000 Subject: [PATCH 7/9] udev: Only scan the requested USB device Instead of scanning all USB devices everytime a new device is plugged, which might disrupt the functionality of the unrelated devices being scanned, only scan for the USB device we are interested in, since we know its VID/PID. Signed-off-by: Paul Cercueil --- libiio.rules.cmakein | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libiio.rules.cmakein b/libiio.rules.cmakein index 55b7a9d92..298d0a2fd 100644 --- a/libiio.rules.cmakein +++ b/libiio.rules.cmakein @@ -1 +1 @@ -SUBSYSTEM=="usb", PROGRAM=="/bin/sh -c '@CMAKE_INSTALL_FULL_BINDIR@/iio_info -S usb | grep %s{idVendor}:%s{idProduct}'", RESULT!="", MODE="666" +SUBSYSTEM=="usb", PROGRAM=="/bin/sh -c '@CMAKE_INSTALL_FULL_BINDIR@/iio_info -S usb=%s{idVendor}:%s{idProduct} | grep %s{idVendor}:%s{idProduct}'", RESULT!="", MODE="666" From 7e904c5c5eb9c0a70683b2b9307839287bc368dc Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 15 Nov 2021 18:19:48 -0500 Subject: [PATCH 8/9] utils: unify scan across all utilities and update man pages this ensures you can do a "-S" on any utility, and it scans the same way on all different utilities. Signed-off-by: Robin Getz Signed-off-by: Paul Cercueil --- man/iio_attr.1.in | 7 +++++-- man/iio_info.1.in | 5 ++++- man/iio_readdev.1.in | 7 ++++++- man/iio_writedev.1.in | 6 ++++++ tests/iio_adi_xflow_check.c | 6 ++++++ tests/iio_reg.c | 6 ++++++ 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/man/iio_attr.1.in b/man/iio_attr.1.in index a76feae06..aa4975139 100644 --- a/man/iio_attr.1.in +++ b/man/iio_attr.1.in @@ -83,8 +83,11 @@ Read and Write IIO Context attributes .B \-D \-\-debug-attr Read and Write IIO Debug attributes .TP -.B \-S, \-\-Scan -Scan for available IIO contexts, optional arg of specific backend(s) 'ip', 'usb' or 'ip:usb'. If no argument is given, it checks all that are availble. +.B \-S, \-\-scan +Scan for available IIO contexts, optional arg of specific backend(s) 'ip', 'usb' or 'ip,usb'. +Specific options for USB include Vendor ID, Product ID to limit scanning to specific devices 'usb=0456:b673'. +vid,pid are hexadecimal numbers (no prefix needed), "*" (match any for pid only) +If no argument is given, it checks all that are availble. .TP .B \-h, \-\-help Tells diff --git a/man/iio_info.1.in b/man/iio_info.1.in index 1d5a88aed..9fb15aedc 100644 --- a/man/iio_info.1.in +++ b/man/iio_info.1.in @@ -79,7 +79,10 @@ with no address part .RE .TP .B \-S, \-\-scan -Scan for available backends +Scan for available IIO contexts, optional arg of specific backend(s) 'ip', 'usb' or 'ip,usb'. +Specific options for USB include Vendor ID, Product ID to limit scanning to specific devices 'usb=0456:b673'. +vid,pid are hexadecimal numbers (no prefix needed), "*" (match any for pid only) +If no argument is given, it checks all that are availble. .TP .B \-a, \-\-auto Scan for available contexts and if only one is available use it. diff --git a/man/iio_readdev.1.in b/man/iio_readdev.1.in index 862d7f531..6d0f0c267 100644 --- a/man/iio_readdev.1.in +++ b/man/iio_readdev.1.in @@ -76,7 +76,12 @@ Buffer timeout in milliseconds. 0 = no timeout. Default is 0. .TP .B \-a, \-\-auto Scan for available contexts and if only one is available use it. - +.TP +.B \-S, \-\-scan +Scan for available IIO contexts, optional arg of specific backend(s) 'ip', 'usb' or 'ip,usb'. +Specific options for USB include Vendor ID, Product ID to limit scanning to specific devices 'usb=0456:b673'. +vid,pid are hexadecimal numbers (no prefix needed), "*" (match any for pid only) +If no argument is given, it checks all that are availble. .SH RETURN VALUE If the specified device is not found, a non-zero exit code is returned. diff --git a/man/iio_writedev.1.in b/man/iio_writedev.1.in index 83d8e85b0..723d5b6e0 100644 --- a/man/iio_writedev.1.in +++ b/man/iio_writedev.1.in @@ -60,6 +60,12 @@ normally returned from .IP serial:[port] .IP local with no address part +.TP +.B \-S, \-\-scan +Scan for available IIO contexts, optional arg of specific backend(s) 'ip', 'usb' or 'ip,usb'. +Specific options for USB include Vendor ID, Product ID to limit scanning to specific devices 'usb=0456:b673'. +vid,pid are hexadecimal numbers (no prefix needed), "*" (match any for pid only) +If no argument is given, it checks all that are availble. .RE .TP .B \-t \-\-trigger diff --git a/tests/iio_adi_xflow_check.c b/tests/iio_adi_xflow_check.c index 0b05cb9b1..193f77dc8 100644 --- a/tests/iio_adi_xflow_check.c +++ b/tests/iio_adi_xflow_check.c @@ -168,6 +168,7 @@ int main(int argc, char **argv) char unit; int ret; struct option *opts; + bool do_scan = false; argw = dup_argv(MY_NAME, argc, argv); @@ -188,6 +189,8 @@ int main(int argc, char **argv) case 'T': break; case 'S': + do_scan = true; + /* FALLTHROUGH */ case 'a': if (!optarg && argc > optind && argv[optind] != NULL && argv[optind][0] != '-') @@ -215,6 +218,9 @@ int main(int argc, char **argv) } free(opts); + if (do_scan) + return EXIT_SUCCESS; + if (optind + 1 != argc) { fprintf(stderr, "Incorrect number of arguments.\n\n"); usage(MY_NAME, options, options_descriptions); diff --git a/tests/iio_reg.c b/tests/iio_reg.c index 04bb27e4a..c7e1e3802 100644 --- a/tests/iio_reg.c +++ b/tests/iio_reg.c @@ -82,6 +82,7 @@ int main(int argc, char **argv) int c; char * name; struct option *opts; + bool do_scan = false; argw = dup_argv(MY_NAME, argc, argv); @@ -102,6 +103,8 @@ int main(int argc, char **argv) case 'T': break; case 'S': + do_scan = true; + /* FALLTHRU */ case 'a': if (!optarg && argc > optind && argv[optind] != NULL && argv[optind][0] != '-') @@ -114,6 +117,9 @@ int main(int argc, char **argv) } free(opts); + if (do_scan) + return EXIT_SUCCESS; + if ((argc - optind) < 2 || (argc - optind) > 3) { usage(MY_NAME, options, options_descriptions); return EXIT_SUCCESS; From 888dd29120dfe16c3dcafa436535f2ff9cb7a577 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 15 Nov 2021 18:24:25 -0500 Subject: [PATCH 9/9] iio_readdev/iio_writedev: print out examples based on context If you are given a context, but no device/channel, walk through things and print out examples which you might be able to use, as a hint for people. (so I can stop telling them to do it with iio_attr manually). Signed-off-by: Robin Getz --- tests/iio_readdev.c | 47 +++++++++++++++++++++++++++++++++++++++++--- tests/iio_writedev.c | 47 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/tests/iio_readdev.c b/tests/iio_readdev.c index b200fd761..1d250b16f 100644 --- a/tests/iio_readdev.c +++ b/tests/iio_readdev.c @@ -195,7 +195,7 @@ static ssize_t print_sample(const struct iio_channel *chn, int main(int argc, char **argv) { char **argw; - unsigned int i, nb_channels; + unsigned int i, j, nb_channels; unsigned int nb_active_channels = 0; unsigned int buffer_size = SAMPLES_PER_READ; unsigned int refill_per_benchmark = REFILL_PER_BENCHMARK; @@ -263,7 +263,7 @@ int main(int argc, char **argv) } free(opts); - if (argc == optind) { + if (argc < optind || argc > optind + 2) { fprintf(stderr, "Incorrect number of arguments.\n\n"); usage(MY_NAME, options, options_descriptions); return EXIT_FAILURE; @@ -272,6 +272,48 @@ int main(int argc, char **argv) if (!ctx) return EXIT_FAILURE; + if (!argw[optind]) { + unsigned int nb_devices = iio_context_get_devices_count(ctx); + + for (i = 0; i < nb_devices; i++) { + const char *dev_id, *label, *name; + bool hit; + + dev = iio_context_get_device(ctx, i); + nb_channels = iio_device_get_channels_count(dev); + + if (!nb_channels) + continue; + + hit = false; + for (j = 0; j < nb_channels; j++) { + struct iio_channel *ch = iio_device_get_channel(dev, j); + + if (!iio_channel_is_scan_element(ch) || + iio_channel_is_output(ch)) + continue; + + hit = true; + + dev_id = iio_device_get_id(dev); + label = iio_device_get_label(dev); + name = iio_device_get_name(dev); + + printf("Example : " MY_NAME " -u %s -b 256 -s 1024 %s %s\n", + iio_context_get_attr_value(ctx, "uri"), + label ? label : name ? name : dev_id, + iio_channel_get_id(ch)); + } + if (hit) + printf("Example : " MY_NAME " -u %s -b 256 -s 1024 %s\n", + iio_context_get_attr_value(ctx, "uri"), + label ? label : name ? name : dev_id); + } + iio_context_destroy(ctx); + usage(MY_NAME, options, options_descriptions); + return EXIT_FAILURE; + } + setup_sig_handler(); dev = iio_context_find_device(ctx, argw[optind]); @@ -332,7 +374,6 @@ int main(int argc, char **argv) } } else { for (i = 0; i < nb_channels; i++) { - unsigned int j; struct iio_channel *ch = iio_device_get_channel(dev, i); for (j = optind + 1; j < (unsigned int) argc; j++) { const char *n = iio_channel_get_name(ch); diff --git a/tests/iio_writedev.c b/tests/iio_writedev.c index ea74c2ec7..2491e45b1 100644 --- a/tests/iio_writedev.c +++ b/tests/iio_writedev.c @@ -206,7 +206,7 @@ static ssize_t read_sample(const struct iio_channel *chn, int main(int argc, char **argv) { char **argw; - unsigned int i, nb_channels; + unsigned int i, j, nb_channels; unsigned int nb_active_channels = 0; unsigned int buffer_size = SAMPLES_PER_READ; unsigned int refill_per_benchmark = REFILL_PER_BENCHMARK; @@ -276,7 +276,7 @@ int main(int argc, char **argv) } free(opts); - if (argc == optind) { + if (argc < optind || argc > optind + 2) { fprintf(stderr, "Incorrect number of arguments.\n\n"); usage(MY_NAME, options, options_descriptions); return EXIT_FAILURE; @@ -285,6 +285,48 @@ int main(int argc, char **argv) if (!ctx) return EXIT_FAILURE; + if (!argw[optind]) { + unsigned int nb_devices = iio_context_get_devices_count(ctx); + + for (i = 0; i < nb_devices; i++) { + const char *dev_id, *label, *name; + bool hit; + + dev = iio_context_get_device(ctx, i); + nb_channels = iio_device_get_channels_count(dev); + + if (!nb_channels) + continue; + + hit = false; + for (j = 0; j < nb_channels; j++) { + struct iio_channel *ch = iio_device_get_channel(dev, j); + + if (!iio_channel_is_scan_element(ch) || + !iio_channel_is_output(ch)) + continue; + + hit = true; + + dev_id = iio_device_get_id(dev); + label = iio_device_get_label(dev); + name = iio_device_get_name(dev); + + printf("Example : " MY_NAME " -u %s -b 256 -s 1024 %s %s\n", + iio_context_get_attr_value(ctx, "uri"), + label ? label : name ? name : dev_id, + iio_channel_get_id(ch)); + } + if (hit) + printf("Example : " MY_NAME " -u %s -b 256 -s 1024 %s\n", + iio_context_get_attr_value(ctx, "uri"), + label ? label : name ? name : dev_id); + } + iio_context_destroy(ctx); + usage(MY_NAME, options, options_descriptions); + return EXIT_FAILURE; + } + if (benchmark && cyclic_buffer) { fprintf(stderr, "Cannot benchmark in cyclic mode.\n"); iio_context_destroy(ctx); @@ -351,7 +393,6 @@ int main(int argc, char **argv) } } else { for (i = 0; i < nb_channels; i++) { - unsigned int j; struct iio_channel *ch = iio_device_get_channel(dev, i); for (j = optind + 1; j < (unsigned int) argc; j++) { const char *n = iio_channel_get_name(ch);