From 75548561df98314405206392b5c4c781b6856ff5 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 25 Jan 2024 11:18:20 +0100 Subject: [PATCH 01/20] utilities: Avoid inclusion of network headers No-OS applications most likely don't have . This header was included from dns_sd.h, which was included in utilities.c. utilities.c only needed dns_sd.h for the FQDN_LEN macro; so move this macro to network.h instead, which does not include any non-standard headers. Signed-off-by: Paul Cercueil --- dns_sd.h | 1 - dns_sd_bonjour.c | 1 + network.h | 2 ++ utilities.c | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dns_sd.h b/dns_sd.h index c7a5f1314..039e7a199 100644 --- a/dns_sd.h +++ b/dns_sd.h @@ -24,7 +24,6 @@ /* IPv6 Max = 4*8 + 7 + 1 for '%' + interface length */ #define DNS_SD_ADDRESS_STR_MAX (40 + IF_NAMESIZE) -#define FQDN_LEN (255) /* RFC 1035 */ /* MacOS doesn't include ENOMEDIUM (No medium found) like Linux does */ #ifndef ENOMEDIUM diff --git a/dns_sd_bonjour.c b/dns_sd_bonjour.c index dacad17e6..ff3614220 100644 --- a/dns_sd_bonjour.c +++ b/dns_sd_bonjour.c @@ -8,6 +8,7 @@ */ #include "dns_sd.h" +#include "network.h" // for FQDN_LEN #include #include diff --git a/network.h b/network.h index f1d11250a..db73d50a5 100644 --- a/network.h +++ b/network.h @@ -12,6 +12,8 @@ #include +#define FQDN_LEN (255) /* RFC 1035 */ + struct iio_context_params; struct iio_context_pdata; struct addrinfo; diff --git a/utilities.c b/utilities.c index 3ae7b76a0..040798d1c 100644 --- a/utilities.c +++ b/utilities.c @@ -9,9 +9,9 @@ /* Force the XSI version of strerror_r */ #undef _GNU_SOURCE -#include "dns_sd.h" #include "iio-config.h" #include "iio-private.h" +#include "network.h" // for FQDN_LEN #include #include From 4a9fed46932373149f9170d00dca461a5f0b6f21 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 23 Jan 2024 16:28:54 +0100 Subject: [PATCH 02/20] iiod: Start enqueue/dequeue tasks when creating buffer The problem with starting the enqueue/dequeue tasks when enabling the IIO buffer is that the blocks might not have been enqueued yet by the time the buffer is enabled. By starting the enqueue/dequeue tasks when creating the IIO buffer, the blocks are much more likely to have been enqueued by the time the buffer is enabled (but still not guaranteed, unfortunately). Signed-off-by: Paul Cercueil --- iiod/responder.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/iiod/responder.c b/iiod/responder.c index 621d08ce1..a7880c794 100644 --- a/iiod/responder.c +++ b/iiod/responder.c @@ -48,6 +48,9 @@ static void free_buffer_entry(struct buffer_entry *entry) { struct block_entry *block_entry, *block_next; + iio_task_stop(entry->dequeue_task); + iio_task_stop(entry->enqueue_task); + iio_task_destroy(entry->enqueue_task); iio_task_destroy(entry->dequeue_task); @@ -436,6 +439,9 @@ static void handle_create_buffer(struct parser_pdata *pdata, IIO_DEBUG("Buffer %u created.\n", entry->idx); + iio_task_start(entry->enqueue_task); + iio_task_start(entry->dequeue_task); + /* Send the success code + updated mask back */ iiod_io_send_response(io, data.size, &data, 1); return; @@ -567,13 +573,9 @@ static void handle_set_enabled_buffer(struct parser_pdata *pdata, goto out_send_response; if (enabled) { - iio_task_start(entry->enqueue_task); - iio_task_start(entry->dequeue_task); ret = iio_buffer_enable(buf); } else { ret = iio_buffer_disable(buf); - iio_task_stop(entry->dequeue_task); - iio_task_stop(entry->enqueue_task); } out_send_response: From cc53e33277295e932cb470d4a9ffdd3f0e2869ac Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 23 Jan 2024 12:47:52 +0100 Subject: [PATCH 03/20] CMake: Allow to compile IIOD without local backend IIOD uses the high-level API of Libiio, it can run on top of any backend (even though it would typically use the local backend). Signed-off-by: Paul Cercueil --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29f6ccc8c..80fecf1fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,10 +328,6 @@ endif() IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") option(WITH_IIOD "Build the IIO Daemon" ON) option(WITH_LOCAL_BACKEND "Enable the local backend" ON) - - if (WITH_IIOD AND NOT WITH_LOCAL_BACKEND) - message(SEND_ERROR "IIOD can only be enabled if the local backend is enabled") - endif() endif() set(DOXYGEN_INPUT "${CMAKE_SOURCE_DIR}") From 0ee73a4ba7b6d8d3cb194fe0618e0441b3ca9c0f Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 13:38:35 +0100 Subject: [PATCH 04/20] CMake: Add support for external backend Add support for having an external backend, which is provided by the application linking to Libiio. This is especially useful on embedded systems, which cannot provide a dynamically-loaded module. Signed-off-by: Paul Cercueil --- CMakeLists.txt | 3 +++ README_BUILD.md | 1 + context.c | 1 + iio-config.h.cmakein | 1 + include/iio/iio-backend.h | 7 +++++++ 5 files changed, 13 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80fecf1fb..0e8ec0dd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,8 @@ if (ENABLE_IPV6) endif() endif() +option(WITH_EXTERNAL_BACKEND "Support external backend provided by the application" OFF) + option(WITH_USB_BACKEND "Enable the libusb backend" ON) if (WITH_USB_BACKEND) find_package(PkgConfig) @@ -674,6 +676,7 @@ toggle_iio_feature("${LIBIIO_COMPAT}" compat) toggle_iio_feature("${WITH_XML_BACKEND}" xml) toggle_iio_feature("${WITH_ZSTD}" zstd) toggle_iio_feature("${WITH_NETWORK_BACKEND}" network) +toggle_iio_feature("${WITH_EXTERNAL_BACKEND}" external) toggle_iio_feature("${HAVE_DNS_SD}" dns-sd) toggle_iio_feature("${HAVE_AVAHI}" avahi) toggle_iio_feature("${HAVE_BONJOUR}" bonjour) diff --git a/README_BUILD.md b/README_BUILD.md index 7894a4a09..15784bd30 100644 --- a/README_BUILD.md +++ b/README_BUILD.md @@ -72,6 +72,7 @@ Cmake Options | Default | Depends on | Description `WITH_SERIAL_BACKEND_DYNAMIC` | ON | Modules + serial backend | Compile the serial backend as a module | `WITH_NETWORK_BACKEND` | ON | | Supports TCP/IP | `WITH_NETWORK_BACKEND_DYNAMIC` | ON | Modules + network backend | Compile the network backend as a module | +`WITH_EXTERNAL_BACKEND` | OFF | | Support external backend provided by the application | `HAVE_DNS_SD` | ON | Networking | Enable DNS-SD (ZeroConf) support | `ENABLE_IPV6` | ON | Networking | Define if you want to enable IPv6 support | `WITH_LOCAL_BACKEND` | ON | Linux | Enables local support with iiod | diff --git a/context.c b/context.c index 636e56b94..601ef2a93 100644 --- a/context.c +++ b/context.c @@ -396,6 +396,7 @@ const struct iio_backend * const iio_backends[] = { IF_ENABLED(WITH_USB_BACKEND && !WITH_USB_BACKEND_DYNAMIC, &iio_usb_backend), IF_ENABLED(WITH_XML_BACKEND, &iio_xml_backend), + IF_ENABLED(WITH_EXTERNAL_BACKEND, &iio_external_backend), }; const unsigned int iio_backends_size = ARRAY_SIZE(iio_backends); diff --git a/iio-config.h.cmakein b/iio-config.h.cmakein index 2af5377ec..0359a826b 100644 --- a/iio-config.h.cmakein +++ b/iio-config.h.cmakein @@ -21,6 +21,7 @@ #cmakedefine01 WITH_NETWORK_BACKEND #cmakedefine01 WITH_USB_BACKEND #cmakedefine01 WITH_SERIAL_BACKEND +#cmakedefine01 WITH_EXTERNAL_BACKEND #cmakedefine01 WITH_MODULES #cmakedefine01 WITH_NETWORK_BACKEND_DYNAMIC diff --git a/include/iio/iio-backend.h b/include/iio/iio-backend.h index ae907ba63..0fadcaecb 100644 --- a/include/iio/iio-backend.h +++ b/include/iio/iio-backend.h @@ -148,6 +148,13 @@ struct iio_backend { unsigned int default_timeout_ms; }; +/* + * If Libiio was compiled with external backend support (WITH_EXTERNAL_BACKEND), + * applications should implement their own backend and provide the + * iio_external_backend symbol. + */ +extern const struct iio_backend iio_external_backend; + struct iio_context * iio_context_create_from_backend( const struct iio_backend *backend, const char *description); From c5708f41db5241113628d7553a4a26b547b86044 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 12:05:08 +0100 Subject: [PATCH 05/20] CMake: Add support for building a thread-less library Add support for building a thread-less Libiio. This disables all current backends, but applications are still free to provide their own external backend. This is especially useful on embedded, where threads generally aren't a thing. Signed-off-by: Paul Cercueil --- CMakeLists.txt | 18 +++++++++- iio-config.h.cmakein | 2 +- lock-dummy.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 lock-dummy.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e8ec0dd6..1b82abec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,6 +372,10 @@ set(LIBIIO_VERSION ${VERSION}.g${LIBIIO_VERSION_GIT}) file(WRITE ${CMAKE_BINARY_DIR}/.version ${LIBIIO_VERSION}) if(WITH_LOCAL_BACKEND) + if (NO_THREADS) + message(SEND_ERROR "Local backend require threads.") + endif() + target_sources(iio PRIVATE local.c) # Link with librt if present @@ -576,7 +580,10 @@ elseif(NEED_LIBXML2) "If you want to enable the XML backend, set WITH_XML_BACKEND=ON.") endif() -if (WIN32) +option(NO_THREADS "Build a thread-less Libiio library" OFF) +if (NO_THREADS) + target_sources(iio PRIVATE lock-dummy.c) +elseif (WIN32) target_sources(iio PRIVATE lock-windows.c) else () if (NOT ANDROID) @@ -606,6 +613,10 @@ if (IIOD_CLIENT OR WITH_IIOD) endif() if (IIOD_CLIENT) + if (NO_THREADS) + message(SEND_ERROR "Backends using iiod-client require threads.") + endif() + target_sources(iio PRIVATE iiod-client.c) target_link_libraries(iio PRIVATE iiod-responder) endif() @@ -625,6 +636,10 @@ if(WITH_EXAMPLES) add_subdirectory(examples) endif() if (WITH_IIOD) + if (NO_THREADS) + message(SEND_ERROR "IIOD require threads.") + endif() + if (NOT PTHREAD_LIBRARIES) message(SEND_ERROR "IIOD requires pthread support.\n" "If you want to disable IIOD, set WITH_IIOD=OFF.") @@ -672,6 +687,7 @@ string(REPLACE ";" "," LIBIIO_SCAN_BACKENDS "${LIBIIO_SCAN_BACKENDS}") configure_file(iio-config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/iio-config.h @ONLY) +toggle_iio_feature("${NO_THREADS}" threadless) toggle_iio_feature("${LIBIIO_COMPAT}" compat) toggle_iio_feature("${WITH_XML_BACKEND}" xml) toggle_iio_feature("${WITH_ZSTD}" zstd) diff --git a/iio-config.h.cmakein b/iio-config.h.cmakein index 0359a826b..3f647eb09 100644 --- a/iio-config.h.cmakein +++ b/iio-config.h.cmakein @@ -41,6 +41,7 @@ #cmakedefine01 HAVE_DNS_SD #cmakedefine01 HAVE_AVAHI #cmakedefine01 WITH_ZSTD +#cmakedefine01 NO_THREADS #cmakedefine HAS_PIPE2 #cmakedefine HAS_STRDUP @@ -50,7 +51,6 @@ #cmakedefine HAS_NEWLOCALE #cmakedefine HAS_PTHREAD_SETNAME_NP #cmakedefine HAVE_IPV6 -#cmakedefine NO_THREADS #define IF_ENABLED(cfg, ptr) ((cfg) ? (ptr) : NULL) diff --git a/lock-dummy.c b/lock-dummy.c new file mode 100644 index 000000000..b944f36d1 --- /dev/null +++ b/lock-dummy.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Paul Cercueil + */ + +#include +#include + +#include +#include + +struct iio_mutex { + int dummy; /* Flawfinder: ignore */ +}; + +struct iio_cond { + int dummy; /* Flawfinder: ignore */ +}; + +struct iio_mutex * iio_mutex_create(void) +{ + struct iio_mutex *lock = malloc(sizeof(*lock)); + + if (!lock) + return iio_ptr(-ENOMEM); + + return lock; +} + +void iio_mutex_destroy(struct iio_mutex *lock) +{ + free(lock); +} + +void iio_mutex_lock(struct iio_mutex *lock) +{ +} + +void iio_mutex_unlock(struct iio_mutex *lock) +{ +} + +struct iio_cond * iio_cond_create(void) +{ + struct iio_cond *cond = malloc(sizeof(*cond)); + + if (!cond) + return iio_ptr(-ENOMEM); + + return cond; +} + +void iio_cond_destroy(struct iio_cond *cond) +{ + free(cond); +} + +int iio_cond_wait(struct iio_cond *cond, struct iio_mutex *lock, + unsigned int timeout_ms) +{ + return -ETIMEDOUT; +} + +void iio_cond_signal(struct iio_cond *cond) +{ +} + +struct iio_thrd * iio_thrd_create(int (*thrd)(void *), + void *d, const char *name) +{ + return iio_ptr(-ENOSYS); +} + +int iio_thrd_join_and_destroy(struct iio_thrd *thrd) +{ + return 0; +} From 79a4cf401f4d582ef2975cfe1138b3443e1b6fc4 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 12:25:37 +0100 Subject: [PATCH 06/20] task: Support running thread-less In thread-less mode, instead of having a thread process the task entries, they will be processed in-line in iio_task_enqueue() if the task is running, and in iio_task_start() otherwise. Signed-off-by: Paul Cercueil --- task.c | 87 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/task.c b/task.c index a5953246d..b08a8eafb 100644 --- a/task.c +++ b/task.c @@ -2,10 +2,12 @@ /* * libiio - Library for interfacing industrial I/O (IIO) devices * - * Copyright (C) 2022 Analog Devices, Inc. + * Copyright (C) 2022-2024 Analog Devices, Inc. * Author: Paul Cercueil */ +#include "iio-config.h" + #include #include @@ -41,47 +43,52 @@ static void iio_task_token_destroy(struct iio_task_token *token) free(token); } -static int iio_task_run(void *d) +static void iio_task_process(struct iio_task *task) { - struct iio_task *task = d; struct iio_task_token *entry; bool autoclear; - iio_mutex_lock(task->lock); + /* Signal that we're idle */ + iio_cond_signal(task->cond); - for (;;) { - /* Signal that we're idle */ - iio_cond_signal(task->cond); + while (!task->stop && !(task->list && task->running)) { + iio_cond_wait(task->cond, task->lock, 0); - while (!task->stop && !(task->list && task->running)) { - iio_cond_wait(task->cond, task->lock, 0); + /* If iio_task_stop() was called while we were waiting + * for clients, notify that we're idle. */ + if (!task->running) + iio_cond_signal(task->cond); + } - /* If iio_task_stop() was called while we were waiting - * for clients, notify that we're idle. */ - if (!task->running) - iio_cond_signal(task->cond); - } + if (task->stop) + return; - if (task->stop) - break; + entry = task->list; + task->list = entry->next; + iio_mutex_unlock(task->lock); - entry = task->list; - task->list = entry->next; - iio_mutex_unlock(task->lock); + entry->ret = task->fn(task->firstarg, entry->elm); - entry->ret = task->fn(task->firstarg, entry->elm); + iio_mutex_lock(entry->done_lock); + entry->done = true; + autoclear = entry->autoclear; + iio_cond_signal(entry->done_cond); + iio_mutex_unlock(entry->done_lock); - iio_mutex_lock(entry->done_lock); - entry->done = true; - autoclear = entry->autoclear; - iio_cond_signal(entry->done_cond); - iio_mutex_unlock(entry->done_lock); + if (autoclear) + iio_task_token_destroy(entry); - if (autoclear) - iio_task_token_destroy(entry); + iio_mutex_lock(task->lock); +} - iio_mutex_lock(task->lock); - } +static int iio_task_run(void *d) +{ + struct iio_task *task = d; + + iio_mutex_lock(task->lock); + + while (!task->stop) + iio_task_process(task); iio_mutex_unlock(task->lock); @@ -111,10 +118,12 @@ struct iio_task * iio_task_create(int (*fn)(void *, void *), task->fn = fn; task->firstarg = firstarg; - task->thrd = iio_thrd_create(iio_task_run, task, name); - err = iio_err(task->thrd); - if (err) - goto err_free_cond; + if (!NO_THREADS) { + task->thrd = iio_thrd_create(iio_task_run, task, name); + err = iio_err(task->thrd); + if (err) + goto err_free_cond; + } return task; @@ -172,6 +181,9 @@ iio_task_do_enqueue(struct iio_task *task, void *elm, bool autoclear) iio_cond_signal(task->cond); iio_mutex_unlock(task->lock); + if (NO_THREADS && !task->stop && task->running) + iio_task_process(task); + return entry; err_free_lock: @@ -241,14 +253,15 @@ void iio_task_flush(struct iio_task *task) int iio_task_destroy(struct iio_task *task) { - int ret; + int ret = 0; iio_mutex_lock(task->lock); task->stop = true; iio_cond_signal(task->cond); iio_mutex_unlock(task->lock); - ret = iio_thrd_join_and_destroy(task->thrd); + if (!NO_THREADS) + ret = iio_thrd_join_and_destroy(task->thrd); iio_task_flush(task); @@ -305,6 +318,10 @@ void iio_task_start(struct iio_task *task) task->running = true; iio_cond_signal(task->cond); iio_mutex_unlock(task->lock); + + if (NO_THREADS && !task->stop) + while (task->list) + iio_task_process(task); } void iio_task_stop(struct iio_task *task) From 24b489da7478d680a2eb3655be2c6308da1371aa Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 12:41:25 +0100 Subject: [PATCH 07/20] iiod-responder: Support running without threads In thread-less mode, the main loop will be executed inside iiod_responder_wait_done(). Signed-off-by: Paul Cercueil --- CMakeLists.txt | 2 +- iiod-responder.c | 35 +++++++++++++++++++++++++---------- iiod-responder.h | 3 +++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b82abec7..01769d35a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -601,7 +601,7 @@ endif() if (IIOD_CLIENT OR WITH_IIOD) add_library(iiod-responder STATIC iiod-responder.c) - target_include_directories(iiod-responder PRIVATE include) + target_include_directories(iiod-responder PRIVATE include ${CMAKE_BINARY_DIR}) set_target_properties(iiod-responder PROPERTIES POSITION_INDEPENDENT_CODE ON) # Link against exported symbols of Libiio and not the diff --git a/iiod-responder.c b/iiod-responder.c index a29c9b23d..5e7170594 100644 --- a/iiod-responder.c +++ b/iiod-responder.c @@ -7,6 +7,7 @@ */ #include "iiod-responder.h" +#include "iio-config.h" #include #include @@ -237,9 +238,8 @@ static void iiod_responder_cancel_responses(struct iiod_responder *priv) } } -static int iiod_responder_reader_thrd(void *d) +static int iiod_responder_reader_worker(struct iiod_responder *priv) { - struct iiod_responder *priv = d; struct iiod_command cmd; struct iiod_buf cmd_buf, ok_buf; struct iiod_io *io; @@ -342,6 +342,11 @@ static int iiod_responder_reader_thrd(void *d) return (int) ret; } +static int iiod_responder_reader_thrd(void *d) +{ + return iiod_responder_reader_worker(d); +} + static int iiod_responder_write(void *p, void *elm) { struct iiod_responder *priv = p; @@ -668,11 +673,13 @@ iiod_responder_create(const struct iiod_responder_ops *ops, void *d) if (err) goto err_free_io; - priv->read_thrd = iio_thrd_create(iiod_responder_reader_thrd, priv, - "iiod-responder-reader-thd"); - err = iio_err(priv->read_thrd); - if (err) - goto err_free_write_task; + if (!NO_THREADS) { + priv->read_thrd = iio_thrd_create(iiod_responder_reader_thrd, priv, + "iiod-responder-reader-thd"); + err = iio_err(priv->read_thrd); + if (err) + goto err_free_write_task; + } iio_task_start(priv->write_task); @@ -689,9 +696,14 @@ iiod_responder_create(const struct iiod_responder_ops *ops, void *d) return iio_ptr(err); } -void iiod_responder_destroy(struct iiod_responder *priv) +void iiod_responder_stop(struct iiod_responder *priv) { priv->thrd_stop = true; +} + +void iiod_responder_destroy(struct iiod_responder *priv) +{ + iiod_responder_stop(priv); iiod_responder_wait_done(priv); iio_task_destroy(priv->write_task); @@ -703,9 +715,12 @@ void iiod_responder_destroy(struct iiod_responder *priv) void iiod_responder_wait_done(struct iiod_responder *priv) { - if (priv->read_thrd) { - iio_thrd_join_and_destroy(priv->read_thrd); + if (!NO_THREADS) { + if (priv->read_thrd) + iio_thrd_join_and_destroy(priv->read_thrd); priv->read_thrd = NULL; + } else if (!priv->thrd_stop) { + iiod_responder_reader_worker(priv); } } diff --git a/iiod-responder.h b/iiod-responder.h index 3b71e11db..ac0d6c877 100644 --- a/iiod-responder.h +++ b/iiod-responder.h @@ -91,6 +91,9 @@ void iiod_io_set_timeout(struct iiod_io *io, unsigned int timeout_ms); /* Read the current value of the micro-second counter */ uint64_t iiod_responder_read_counter_us(void); +/* Stop the iiod_responder. */ +void iiod_responder_stop(struct iiod_responder *responder); + /* Wait until the iiod_responder stops. */ void iiod_responder_wait_done(struct iiod_responder *responder); From 45b01742901a006bdcefee5118ed03462b8a2587 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 12:26:57 +0100 Subject: [PATCH 08/20] iiod-responder: Remove unused field This field was not used anywhere. Signed-off-by: Paul Cercueil --- iiod-responder.c | 1 - 1 file changed, 1 deletion(-) diff --git a/iiod-responder.c b/iiod-responder.c index 5e7170594..d0fa5b6ce 100644 --- a/iiod-responder.c +++ b/iiod-responder.c @@ -75,7 +75,6 @@ struct iiod_responder { struct iio_mutex *lock; struct iio_thrd *read_thrd; struct iio_task *write_task; - struct iio_thrd *write_thrd; bool thrd_stop; int thrd_err_code; From 2dc6e4b3b1e5b7344d57c04c088286e692207838 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 16 Jan 2024 11:54:34 +0100 Subject: [PATCH 09/20] iiod-client: Drop unused function iiod_client_set_kernel_buffers_count() This function was not used anywhere. Signed-off-by: Paul Cercueil --- iiod-client.c | 19 ------------------- include/iio/iiod-client.h | 4 ---- 2 files changed, 23 deletions(-) diff --git a/iiod-client.c b/iiod-client.c index 136bfe42f..15b7c40fe 100644 --- a/iiod-client.c +++ b/iiod-client.c @@ -440,25 +440,6 @@ int iiod_client_set_trigger(struct iiod_client *client, return ret; } -int iiod_client_set_kernel_buffers_count(struct iiod_client *client, - const struct iio_device *dev, - unsigned int nb_blocks) -{ - int ret; - char buf[1024]; - - if (iiod_client_uses_binary_interface(client)) - return -ENOSYS; - - iio_snprintf(buf, sizeof(buf), "SET %s BUFFERS_COUNT %u\r\n", - iio_device_get_id(dev), nb_blocks); - - iio_mutex_lock(client->lock); - ret = iiod_client_exec_command(client, buf); - iio_mutex_unlock(client->lock); - return ret; -} - static unsigned int calculate_remote_timeout(unsigned int timeout_ms) { /* XXX(pcercuei): We currently hardcode timeout / 2 for the backend used diff --git a/include/iio/iiod-client.h b/include/iio/iiod-client.h index 1ec23f141..b02f8eb48 100644 --- a/include/iio/iiod-client.h +++ b/include/iio/iiod-client.h @@ -48,10 +48,6 @@ __api int iiod_client_set_trigger(struct iiod_client *client, const struct iio_device *dev, const struct iio_device *trigger); -__api int iiod_client_set_kernel_buffers_count(struct iiod_client *client, - const struct iio_device *dev, - unsigned int nb_blocks); - __api int iiod_client_set_timeout(struct iiod_client *client, unsigned int timeout); From 3f2a6969928feb5f4e33f83512ccc9d45db7e0be Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 24 Jan 2024 17:50:39 +0100 Subject: [PATCH 10/20] iiod-client: Support new protocol without ZSTD Support the PRINT command sending an uncompressed XML string, instead of requiring ZSTD compression. This makes it easier for embedded tinyiiod applications, which then don't need to duplicate the XML string (once in plain text to create the Libiio context using the XML backend, and once ZSTD-compressed for tinyiiod). Signed-off-by: Paul Cercueil --- iiod-client.c | 64 +++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/iiod-client.c b/iiod-client.c index 15b7c40fe..e5afacdc8 100644 --- a/iiod-client.c +++ b/iiod-client.c @@ -944,13 +944,16 @@ iiod_client_create_context_private_new(struct iiod_client *client, struct iio_context *ctx = NULL; unsigned long long len; char *xml_zstd, *xml; + bool is_zstd; int ret; - xml = malloc(xml_len); + xml = malloc(xml_len + uri_len); if (!xml) return iio_ptr(-ENOMEM); - ret = iiod_client_send_print(client, xml, xml_len); + memcpy(xml, "xml:", uri_len); + + ret = iiod_client_send_print(client, &xml[uri_len], xml_len); if (ret < 0) { prm_perror(client->params, -ret, "Unable to send PRINT command"); @@ -959,37 +962,42 @@ iiod_client_create_context_private_new(struct iiod_client *client, xml_len = ret; + is_zstd = strncmp(xml, "xml:params, "Received ZSTD-compressed XML string.\n"); + else + prm_dbg(client->params, "Received uncompressed XML string.\n"); - prm_dbg(client->params, "Received ZSTD-compressed XML string.\n"); - - len = ZSTD_getFrameContentSize(xml, xml_len); - if (len == ZSTD_CONTENTSIZE_UNKNOWN || - len == ZSTD_CONTENTSIZE_ERROR) { - ret = -EIO; - goto out_free_xml; - } + if (is_zstd) { + len = ZSTD_getFrameContentSize(&xml[uri_len], xml_len); + if (len == ZSTD_CONTENTSIZE_UNKNOWN || + len == ZSTD_CONTENTSIZE_ERROR) { + ret = -EIO; + goto out_free_xml; + } - xml_zstd = malloc(uri_len + len + 1); - if (!xml_zstd) { - ret = -ENOMEM; - goto out_free_xml; - } + xml_zstd = malloc(uri_len + len + 1); + if (!xml_zstd) { + ret = -ENOMEM; + goto out_free_xml; + } - xml_len = ZSTD_decompress(xml_zstd + uri_len, len, xml, xml_len); - if (ZSTD_isError(xml_len)) { - prm_err(client->params, "Unable to decompress ZSTD data: %s\n", - ZSTD_getErrorName(xml_len)); - ret = -EIO; - free(xml_zstd); - goto out_free_xml; - } + xml_len = ZSTD_decompress(&xml_zstd[uri_len], len, &xml[uri_len], xml_len); + if (ZSTD_isError(xml_len)) { + prm_err(client->params, "Unable to decompress ZSTD data: %s\n", + ZSTD_getErrorName(xml_len)); + ret = -EIO; + free(xml_zstd); + goto out_free_xml; + } - memcpy(xml_zstd, "xml:", uri_len); - xml_zstd[uri_len + xml_len] = '\0'; + memcpy(xml_zstd, "xml:", uri_len); + xml_zstd[uri_len + xml_len] = '\0'; - /* Free compressed data, make "xml" point to uncompressed data */ - free(xml); - xml = xml_zstd; + /* Free compressed data, make "xml" point to uncompressed data */ + free(xml); + xml = xml_zstd; + } prm_dbg(client->params, "Creating context\n"); From 1ffd8fd4f5816e4bd6149ad8155216dad0daa86a Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 24 Jan 2024 17:48:37 +0100 Subject: [PATCH 11/20] iiod: Support new protocol without ZSTD If ZSTD was not enabled during compilation, the PRINT command will simply send the uncompressed XML string. The client can then detect and use the uncompressed string directly. Signed-off-by: Paul Cercueil --- iiod/iiod.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/iiod/iiod.c b/iiod/iiod.c index 175686b2a..0971ed36e 100644 --- a/iiod/iiod.c +++ b/iiod/iiod.c @@ -98,10 +98,11 @@ static void sig_handler_usr1(int sig) static void *get_xml_zstd_data(const struct iio_context *ctx, size_t *out_len) { -#if WITH_ZSTD const char *xml = iio_context_get_xml(ctx); - size_t len, xml_len = strlen(xml); + size_t xml_len = strlen(xml); void *buf; +#if WITH_ZSTD + size_t len; size_t ret; len = ZSTD_compressBound(xml_len); @@ -118,11 +119,15 @@ static void *get_xml_zstd_data(const struct iio_context *ctx, size_t *out_len) } *out_len = ret; - - return buf; #else - return NULL; + buf = iio_strdup(xml); + if (!buf) + return NULL; + + *out_len = xml_len; #endif + + return buf; } static void free_device_pdata(struct iio_context *ctx) From 8745a222bde4a497783428a19f4e508a684a3a19 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 23 Jan 2024 16:35:46 +0100 Subject: [PATCH 12/20] iiod: Start thread-less enqueue/dequeue tasks before enabling buffer When running threadless, the enqueue/dequeue operations will be executed in-line; therefore, the buffer must be enabled before the tasks are executed, otherwise these tasks will block on iio_block_enqueue() or iio_block_dequeue(). Signed-off-by: Paul Cercueil --- iiod/responder.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/iiod/responder.c b/iiod/responder.c index a7880c794..176597d64 100644 --- a/iiod/responder.c +++ b/iiod/responder.c @@ -48,8 +48,10 @@ static void free_buffer_entry(struct buffer_entry *entry) { struct block_entry *block_entry, *block_next; - iio_task_stop(entry->dequeue_task); - iio_task_stop(entry->enqueue_task); + if (!NO_THREADS) { + iio_task_stop(entry->dequeue_task); + iio_task_stop(entry->enqueue_task); + } iio_task_destroy(entry->enqueue_task); iio_task_destroy(entry->dequeue_task); @@ -439,8 +441,10 @@ static void handle_create_buffer(struct parser_pdata *pdata, IIO_DEBUG("Buffer %u created.\n", entry->idx); - iio_task_start(entry->enqueue_task); - iio_task_start(entry->dequeue_task); + if (!NO_THREADS) { + iio_task_start(entry->enqueue_task); + iio_task_start(entry->dequeue_task); + } /* Send the success code + updated mask back */ iiod_io_send_response(io, data.size, &data, 1); @@ -574,7 +578,17 @@ static void handle_set_enabled_buffer(struct parser_pdata *pdata, if (enabled) { ret = iio_buffer_enable(buf); + + if (NO_THREADS) { + iio_task_start(entry->enqueue_task); + iio_task_start(entry->dequeue_task); + } } else { + if (NO_THREADS) { + iio_task_stop(entry->enqueue_task); + iio_task_stop(entry->dequeue_task); + } + ret = iio_buffer_disable(buf); } From 5118b8e073a6b4ec64ea4fba2f227d8d2925227e Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 24 Jan 2024 12:41:32 +0100 Subject: [PATCH 13/20] iiod: Use iio_mutex locks instead of using pthread directly This permits to compile a thread-less IIOD by using the dummy iio-lock implementation. Signed-off-by: Paul Cercueil --- iiod/iiod.c | 20 +++++++++++++++- iiod/ops.h | 6 ++++- iiod/responder.c | 61 ++++++++++++++++++++++++++---------------------- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/iiod/iiod.c b/iiod/iiod.c index 0971ed36e..312c3ce78 100644 --- a/iiod/iiod.c +++ b/iiod/iiod.c @@ -11,6 +11,8 @@ #include "ops.h" #include "thread-pool.h" +#include + #include #include #include @@ -316,6 +318,18 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, xml_zstd = get_xml_zstd_data(ctx, &xml_zstd_len); + buflist_lock = iio_mutex_create(); + if (iio_err(buflist_lock)) { + ret = EXIT_FAILURE; + goto out_free_xml_data; + } + + evlist_lock = iio_mutex_create(); + if (iio_err(evlist_lock)) { + ret = EXIT_FAILURE; + goto out_free_buflist_lock; + } + if (WITH_IIOD_USBD && ffs_mountpoint) { ret = start_usb_daemon(ctx, ffs_mountpoint, (unsigned int) nb_pipes, ep0_fd, @@ -323,7 +337,7 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, if (ret) { IIO_PERROR(ret, "Unable to start USB daemon"); ret = EXIT_FAILURE; - goto out_free_xml_data; + goto out_free_evlist_lock; } } @@ -356,6 +370,10 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, * the worker threads are signaled to shutdown. */ thread_pool_stop_and_wait(main_thread_pool); +out_free_evlist_lock: + iio_mutex_destroy(evlist_lock); +out_free_buflist_lock: + iio_mutex_destroy(buflist_lock); out_free_xml_data: free(xml_zstd); out_destroy_context: diff --git a/iiod/ops.h b/iiod/ops.h index 11011e6e2..f116f0ff1 100644 --- a/iiod/ops.h +++ b/iiod/ops.h @@ -45,12 +45,16 @@ #define TEST_BIT(addr, bit) (!!(*(((uint32_t *) addr) + BIT_WORD(bit)) \ & BIT_MASK(bit))) +struct iio_mutex; struct iio_task; struct iiod_io; struct thread_pool; extern struct thread_pool *main_thread_pool; struct DevEntry; +extern struct iio_mutex *buflist_lock; +extern struct iio_mutex *evlist_lock; + enum iio_attr_type { IIO_ATTR_TYPE_DEVICE, IIO_ATTR_TYPE_DEBUG, @@ -76,7 +80,7 @@ struct buffer_entry { uint16_t idx; SLIST_HEAD(BlockList, block_entry) blocklist; - pthread_mutex_t lock; + struct iio_mutex *lock; }; struct evstream_entry { diff --git a/iiod/responder.c b/iiod/responder.c index 176597d64..4fa83e1c7 100644 --- a/iiod/responder.c +++ b/iiod/responder.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -29,12 +28,12 @@ static struct iio_buffer * get_iio_buffer(struct parser_pdata *pdata, static SLIST_HEAD(BufferList, buffer_entry) bufferlist; /* Protect bufferlist from parallel access */ -static pthread_mutex_t buflist_lock = PTHREAD_MUTEX_INITIALIZER; +struct iio_mutex *buflist_lock; static SLIST_HEAD(EventStreamList, evstream_entry) evlist; /* Protect evlist from parallel access */ -static pthread_mutex_t evlist_lock = PTHREAD_MUTEX_INITIALIZER; +struct iio_mutex *evlist_lock; static void free_block_entry(struct block_entry *entry) { @@ -56,7 +55,7 @@ static void free_buffer_entry(struct buffer_entry *entry) iio_task_destroy(entry->enqueue_task); iio_task_destroy(entry->dequeue_task); - pthread_mutex_lock(&entry->lock); + iio_mutex_lock(entry->lock); for (block_entry = SLIST_FIRST(&entry->blocklist); block_entry; block_entry = block_next) { @@ -64,10 +63,10 @@ static void free_buffer_entry(struct buffer_entry *entry) free_block_entry(block_entry); } - pthread_mutex_unlock(&entry->lock); + iio_mutex_unlock(entry->lock); iio_buffer_destroy(entry->buf); - pthread_mutex_destroy(&entry->lock); + iio_mutex_destroy(entry->lock); free(entry->words); free(entry); } @@ -412,10 +411,15 @@ static void handle_create_buffer(struct parser_pdata *pdata, entry->dev = dev; entry->idx = (uint16_t) cmd->code; + entry->lock = iio_mutex_create(); + ret = iio_err(entry->lock); + if (ret) + goto err_destroy_dequeue_task; + buf = iio_device_create_buffer(dev, entry->idx, mask); ret = iio_err(buf); if (ret) - goto err_destroy_dequeue_task; + goto err_destroy_lock; /* Rewrite the "words" bitmask according to the mask object, * which may have been modified when creating the buffer. */ @@ -433,11 +437,10 @@ static void handle_create_buffer(struct parser_pdata *pdata, entry->buf = buf; entry->pdata = pdata; - pthread_mutex_init(&entry->lock, NULL); - pthread_mutex_lock(&buflist_lock); + iio_mutex_lock(buflist_lock); SLIST_INSERT_HEAD(&bufferlist, entry, entry); - pthread_mutex_unlock(&buflist_lock); + iio_mutex_unlock(buflist_lock); IIO_DEBUG("Buffer %u created.\n", entry->idx); @@ -450,6 +453,8 @@ static void handle_create_buffer(struct parser_pdata *pdata, iiod_io_send_response(io, data.size, &data, 1); return; +err_destroy_lock: + iio_mutex_destroy(entry->lock); err_destroy_dequeue_task: iio_task_destroy(entry->dequeue_task); err_destroy_enqueue_task: @@ -476,7 +481,7 @@ static struct iio_buffer * get_iio_buffer(struct parser_pdata *pdata, if (!dev) return iio_ptr(-EINVAL); - pthread_mutex_lock(&buflist_lock); + iio_mutex_lock(buflist_lock); SLIST_FOREACH(entry, &bufferlist, entry) { if (entry->dev == dev && entry->idx == (cmd->code & 0xffff)) { @@ -485,7 +490,7 @@ static struct iio_buffer * get_iio_buffer(struct parser_pdata *pdata, } } - pthread_mutex_unlock(&buflist_lock); + iio_mutex_unlock(buflist_lock); if (buf && entry_ptr) *entry_ptr = entry; @@ -502,7 +507,7 @@ static struct iio_block * get_iio_block(struct parser_pdata *pdata, struct iio_block *block = NULL; int err; - pthread_mutex_lock(&entry_buf->lock); + iio_mutex_lock(entry_buf->lock); SLIST_FOREACH(entry, &entry_buf->blocklist, entry) { if (entry->client_id == cmd->client_id) { @@ -511,7 +516,7 @@ static struct iio_block * get_iio_block(struct parser_pdata *pdata, } } - pthread_mutex_unlock(&entry_buf->lock); + iio_mutex_unlock(entry_buf->lock); if (block && entry_ptr) *entry_ptr = entry; @@ -541,7 +546,7 @@ static void handle_free_buffer(struct parser_pdata *pdata, ret = -EBADF; - pthread_mutex_lock(&buflist_lock); + iio_mutex_lock(buflist_lock); SLIST_FOREACH(entry, &bufferlist, entry) { if (entry != buf_entry) @@ -553,7 +558,7 @@ static void handle_free_buffer(struct parser_pdata *pdata, break; } - pthread_mutex_unlock(&buflist_lock); + iio_mutex_unlock(buflist_lock); IIO_DEBUG("Buffer %u freed.\n", cmd->code); @@ -670,9 +675,9 @@ static void handle_create_block(struct parser_pdata *pdata, /* Keep a reference to the iiod_io until the block is freed. */ iiod_io_ref(io); - pthread_mutex_lock(&buf_entry->lock); + iio_mutex_lock(buf_entry->lock); SLIST_INSERT_HEAD(&buf_entry->blocklist, entry, entry); - pthread_mutex_unlock(&buf_entry->lock); + iio_mutex_unlock(buf_entry->lock); out_send_response: iiod_io_send_response_code(io, ret); @@ -702,7 +707,7 @@ static void handle_free_block(struct parser_pdata *pdata, ret = -EBADF; - pthread_mutex_lock(&buf_entry->lock); + iio_mutex_lock(buf_entry->lock); SLIST_FOREACH(entry, &buf_entry->blocklist, entry) { if (entry->block != block) @@ -715,7 +720,7 @@ static void handle_free_block(struct parser_pdata *pdata, break; } - pthread_mutex_unlock(&buf_entry->lock); + iio_mutex_unlock(buf_entry->lock); IIO_DEBUG("Block %u freed.\n", cmd->code); @@ -900,9 +905,9 @@ static void handle_create_evstream(struct parser_pdata *pdata, /* Keep a reference to the iiod_io until the evstream is freed. */ iiod_io_ref(io); - pthread_mutex_lock(&evlist_lock); + iio_mutex_lock(evlist_lock); SLIST_INSERT_HEAD(&evlist, entry, entry); - pthread_mutex_unlock(&evlist_lock); + iio_mutex_unlock(evlist_lock); out_send_response: iiod_io_send_response_code(io, ret); @@ -921,7 +926,7 @@ static struct evstream_entry * get_evstream(struct parser_pdata *pdata, if (!dev) return NULL; - pthread_mutex_lock(&evlist_lock); + iio_mutex_lock(evlist_lock); SLIST_FOREACH(entry, &evlist, entry) { if (entry->client_id == idx && entry->dev == dev @@ -933,7 +938,7 @@ static struct evstream_entry * get_evstream(struct parser_pdata *pdata, if (entry && remove) SLIST_REMOVE(&evlist, entry, evstream_entry, entry); - pthread_mutex_unlock(&evlist_lock); + iio_mutex_unlock(evlist_lock); return entry; } @@ -1076,7 +1081,7 @@ static void iiod_responder_free_resources(struct parser_pdata *pdata) struct buffer_entry *buf_entry, *buf_next; struct evstream_entry *ev_entry, *ev_next; - pthread_mutex_lock(&buflist_lock); + iio_mutex_lock(buflist_lock); for (buf_entry = SLIST_FIRST(&bufferlist); buf_entry; buf_entry = buf_next) { @@ -1089,9 +1094,9 @@ static void iiod_responder_free_resources(struct parser_pdata *pdata) } } - pthread_mutex_unlock(&buflist_lock); + iio_mutex_unlock(buflist_lock); - pthread_mutex_lock(&evlist_lock); + iio_mutex_lock(evlist_lock); for (ev_entry = SLIST_FIRST(&evlist); ev_entry; ev_entry = ev_next) { ev_next = SLIST_NEXT(ev_entry, entry); @@ -1103,7 +1108,7 @@ static void iiod_responder_free_resources(struct parser_pdata *pdata) } } - pthread_mutex_unlock(&evlist_lock); + iio_mutex_unlock(evlist_lock); } int binary_parse(struct parser_pdata *pdata) From 872acee7de183804210b6731464c58372dae8db6 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 25 Jan 2024 12:18:51 +0100 Subject: [PATCH 14/20] iiod: Move read_all/write_all to new file These functions are used by both IIOD and tinyiiod, and there's no convenient place to place them; so move them to a new rw.c file. Signed-off-by: Paul Cercueil --- iiod/CMakeLists.txt | 2 +- iiod/interpreter.c | 34 ---------------------------------- iiod/rw.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 35 deletions(-) create mode 100644 iiod/rw.c diff --git a/iiod/CMakeLists.txt b/iiod/CMakeLists.txt index 7eb148573..b419bbbb7 100644 --- a/iiod/CMakeLists.txt +++ b/iiod/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_DEFINITIONS) add_executable(iiod - iiod.c interpreter.c responder.c thread-pool.c + iiod.c interpreter.c responder.c rw.c thread-pool.c ) set_target_properties(iiod PROPERTIES C_STANDARD 99 diff --git a/iiod/interpreter.c b/iiod/interpreter.c index f601f000a..f1528c28d 100644 --- a/iiod/interpreter.c +++ b/iiod/interpreter.c @@ -195,40 +195,6 @@ static ssize_t writefd_io(struct parser_pdata *pdata, const void *src, size_t le return ret; } -ssize_t write_all(struct parser_pdata *pdata, const void *src, size_t len) -{ - uintptr_t ptr = (uintptr_t) src; - - while (len) { - ssize_t ret = pdata->writefd(pdata, (void *) ptr, len); - if (ret < 0) - return ret; - if (!ret) - return -EPIPE; - ptr += ret; - len -= ret; - } - - return ptr - (uintptr_t) src; -} - -ssize_t read_all(struct parser_pdata *pdata, void *dst, size_t len) -{ - uintptr_t ptr = (uintptr_t) dst; - - while (len) { - ssize_t ret = pdata->readfd(pdata, (void *) ptr, len); - if (ret < 0) - return ret; - if (!ret) - return -EPIPE; - ptr += ret; - len -= ret; - } - - return ptr - (uintptr_t) dst; -} - void interpreter(struct iio_context *ctx, int fd_in, int fd_out, bool is_socket, bool is_usb, struct thread_pool *pool, const void *xml_zstd, diff --git a/iiod/rw.c b/iiod/rw.c new file mode 100644 index 000000000..3796a4e89 --- /dev/null +++ b/iiod/rw.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Paul Cercueil + */ + +#include "ops.h" + +ssize_t write_all(struct parser_pdata *pdata, const void *src, size_t len) +{ + uintptr_t ptr = (uintptr_t) src; + + while (len) { + ssize_t ret = pdata->writefd(pdata, (void *) ptr, len); + if (ret < 0) + return ret; + if (!ret) + return -EPIPE; + ptr += ret; + len -= ret; + } + + return ptr - (uintptr_t) src; +} + +ssize_t read_all(struct parser_pdata *pdata, void *dst, size_t len) +{ + uintptr_t ptr = (uintptr_t) dst; + + while (len) { + ssize_t ret = pdata->readfd(pdata, (void *) ptr, len); + if (ret < 0) + return ret; + if (!ret) + return -EPIPE; + ptr += ret; + len -= ret; + } + + return ptr - (uintptr_t) dst; +} From f3c57301b8c2764340a2926c8b76de319bf2f6b4 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 24 Jan 2024 17:47:48 +0100 Subject: [PATCH 15/20] iiod: Use instead of duplicating code It provides zalloc() and the enum iio_attr_type. Signed-off-by: Paul Cercueil --- iiod/ops.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/iiod/ops.h b/iiod/ops.h index f116f0ff1..f7b47681a 100644 --- a/iiod/ops.h +++ b/iiod/ops.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -55,12 +56,6 @@ struct DevEntry; extern struct iio_mutex *buflist_lock; extern struct iio_mutex *evlist_lock; -enum iio_attr_type { - IIO_ATTR_TYPE_DEVICE, - IIO_ATTR_TYPE_DEBUG, - IIO_ATTR_TYPE_BUFFER, -}; - struct block_entry { SLIST_ENTRY(block_entry) entry; struct iio_block *block; @@ -129,11 +124,6 @@ struct iio_device_pdata { extern bool server_demux; /* Defined in iiod.c */ -static inline void *zalloc(size_t size) -{ - return calloc(1, size); -} - void interpreter(struct iio_context *ctx, int fd_in, int fd_out, bool is_socket, bool is_usb, struct thread_pool *pool, const void *xml_zstd, size_t xml_zstd_len); From f3ddc3e45cba188c9deb67e4a0d621138b7ff4fa Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 25 Jan 2024 12:03:46 +0100 Subject: [PATCH 16/20] iiod: Move poll_nointr() to iiod.c No-OS programs likely don't have , and don't need poll_nointr() anyway; so move that function out of the way to iiod.c. Signed-off-by: Paul Cercueil --- iiod/iiod.c | 12 ++++++++++++ iiod/ops.h | 13 ++----------- iiod/usbd.c | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/iiod/iiod.c b/iiod/iiod.c index 312c3ce78..dba7f17b7 100644 --- a/iiod/iiod.c +++ b/iiod/iiod.c @@ -15,6 +15,7 @@ #include #include +#include #include #if WITH_ZSTD #include @@ -381,3 +382,14 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, return ret; } + +int poll_nointr(struct pollfd *pfd, unsigned int num_pfd) +{ + int ret; + + do { + ret = poll(pfd, num_pfd, -1); + } while (ret == -1 && errno == EINTR); + + return ret; +} diff --git a/iiod/ops.h b/iiod/ops.h index f7b47681a..c376fc324 100644 --- a/iiod/ops.h +++ b/iiod/ops.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -49,6 +48,7 @@ struct iio_mutex; struct iio_task; struct iiod_io; +struct pollfd; struct thread_pool; extern struct thread_pool *main_thread_pool; struct DevEntry; @@ -180,15 +180,6 @@ static __inline__ void output(struct parser_pdata *pdata, const char *text) pdata->stop = true; } -static __inline__ int poll_nointr(struct pollfd *pfd, unsigned int num_pfd) -{ - int ret; - - do { - ret = poll(pfd, num_pfd, -1); - } while (ret == -1 && errno == EINTR); - - return ret; -} +int poll_nointr(struct pollfd *pfd, unsigned int num_pfd); #endif /* __OPS_H__ */ diff --git a/iiod/usbd.c b/iiod/usbd.c index ca341b76e..7414a4332 100644 --- a/iiod/usbd.c +++ b/iiod/usbd.c @@ -12,6 +12,7 @@ #include #include +#include #include #include From d61fbe77a43b3c1414fb6d6ec7bedc0c5f2d1127 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 25 Jan 2024 12:04:58 +0100 Subject: [PATCH 17/20] iiod: Remove non-standard includes from ops.h Move the to where it's actually required, in interpreter.c. Remove the include which doesn't seem to be needed at all. Signed-off-by: Paul Cercueil --- iiod/interpreter.c | 1 + iiod/ops.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/iiod/interpreter.c b/iiod/interpreter.c index f1528c28d..0c0b00e68 100644 --- a/iiod/interpreter.c +++ b/iiod/interpreter.c @@ -16,6 +16,7 @@ #include #include #endif +#include #if WITH_AIO static ssize_t async_io(struct parser_pdata *pdata, void *buf, size_t len, diff --git a/iiod/ops.h b/iiod/ops.h index c376fc324..079416eb4 100644 --- a/iiod/ops.h +++ b/iiod/ops.h @@ -12,14 +12,12 @@ #include "../iio-config.h" #include "queue.h" -#include #include #include #include #include #include #include -#include #include #if WITH_AIO From 31188d2fb585bf190456c60fc6c40a3186985b5f Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 23 Jan 2024 17:22:25 +0100 Subject: [PATCH 18/20] iiod: Add support for building libtinyiiod Add a WITH_LIBTINYIIOD CMake option, which defaults to OFF. When enabled, a libtinyiiod.a static library will be built. The library's API is described in tinyiiod/tinyiiod.h. Signed-off-by: Paul Cercueil --- CMakeLists.txt | 9 ++++- README_BUILD.md | 1 + tinyiiod/CMakeLists.txt | 20 +++++++++++ tinyiiod/tinyiiod.c | 80 +++++++++++++++++++++++++++++++++++++++++ tinyiiod/tinyiiod.h | 40 +++++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tinyiiod/CMakeLists.txt create mode 100644 tinyiiod/tinyiiod.c create mode 100644 tinyiiod/tinyiiod.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 01769d35a..5eee82f7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,6 +327,8 @@ else() message(STATUS "GCC ${CMAKE_COMPILER_IS_GNUCC}") endif() +option(WITH_LIBTINYIIOD "Build libtinyiiod" OFF) + IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") option(WITH_IIOD "Build the IIO Daemon" ON) option(WITH_LOCAL_BACKEND "Enable the local backend" ON) @@ -599,7 +601,7 @@ else () target_sources(iio PRIVATE lock.c) endif() -if (IIOD_CLIENT OR WITH_IIOD) +if (IIOD_CLIENT OR WITH_IIOD OR WITH_LIBTINYIIOD) add_library(iiod-responder STATIC iiod-responder.c) target_include_directories(iiod-responder PRIVATE include ${CMAKE_BINARY_DIR}) set_target_properties(iiod-responder PROPERTIES POSITION_INDEPENDENT_CODE ON) @@ -648,6 +650,10 @@ if (WITH_IIOD) add_subdirectory(iiod) endif() +if (WITH_LIBTINYIIOD) + add_subdirectory(tinyiiod) +endif() + if ((WITH_IIOD OR IIOD_CLIENT) AND NOT WITH_ZSTD) message(WARNING "IIOD async. API requires ZSTD support, consider enabling it.\n" "If you want to enable libzstd support, set WITH_ZSTD=ON.") @@ -705,6 +711,7 @@ toggle_iio_feature("${WITH_HWMON}" hwmon) toggle_iio_feature("${WITH_USB_BACKEND}" usb) toggle_iio_feature("${WITH_UTILS}" utils) toggle_iio_feature("${WITH_EXAMPLES}" examples) +toggle_iio_feature("${WITH_LIBTINYIIOD}" libtinyiiod) toggle_iio_feature("${WITH_IIOD}" iiod) toggle_iio_feature("${WITH_MODULES}" modules) toggle_iio_feature("${WITH_USB_BACKEND_DYNAMIC}" usb-dynamic) diff --git a/README_BUILD.md b/README_BUILD.md index 15784bd30..29e92e103 100644 --- a/README_BUILD.md +++ b/README_BUILD.md @@ -110,6 +110,7 @@ Cmake Options | Default | Description | `WITH_IIOD_SERIAL` | ON | Add serial (UART) support | `WITH_IIOD_USBD` | ON | Add support for USB through FunctionFS within IIOD | `WITH_IIOD_V0_COMPAT` | ON | Add support for Libiio v0.x protocol and clients | +`WITH_LIBTINYIIOD` | OFF | Build libtinyiiod | `WITH_AIO` | ON | Build IIOD with async. I/O support | `WITH_SYSTEMD` | OFF | Enable installation of systemd service file for iiod | `SYSTEMD_UNIT_INSTALL_DIR` | /lib/systemd/system | default install path for systemd unit files | diff --git a/tinyiiod/CMakeLists.txt b/tinyiiod/CMakeLists.txt new file mode 100644 index 000000000..20765a43d --- /dev/null +++ b/tinyiiod/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) +project(tinyiiod C) + +add_library(tinyiiod STATIC + tinyiiod.c + ${CMAKE_SOURCE_DIR}/iiod/responder.c + ${CMAKE_SOURCE_DIR}/iiod/rw.c +) +target_include_directories(tinyiiod PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} +) +set_target_properties(tinyiiod PROPERTIES + VERSION ${LIBIIO_VERSION_MAJOR}.${LIBIIO_VERSION_MINOR} + SOVERSION ${LIBIIO_VERSION_MAJOR} + PUBLIC_HEADER tinyiiod.h + C_STANDARD 99 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) +target_link_libraries(tinyiiod LINK_PRIVATE iio iiod-responder) diff --git a/tinyiiod/tinyiiod.c b/tinyiiod/tinyiiod.c new file mode 100644 index 000000000..9e74bab58 --- /dev/null +++ b/tinyiiod/tinyiiod.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Paul Cercueil + */ + +#include "../iiod/ops.h" +#include "tinyiiod.h" + +#include + +#define container_of(ptr, type, member) \ + ((type *)(void *)((uintptr_t)(ptr) - offsetof(type, member))) + +struct iiod_pdata; + +struct iio_context_params iiod_params = { + .log_level = LEVEL_INFO, +}; + +struct iiod_ctx { + struct parser_pdata parser_pdata; + struct iiod_pdata *pdata; + ssize_t (*read_cb)(struct iiod_pdata *, void *, size_t); + ssize_t (*write_cb)(struct iiod_pdata *, const void *, size_t); +}; + +static ssize_t iiod_readfd(struct parser_pdata *pdata, void *buf, size_t len) +{ + struct iiod_ctx *ctx = container_of(pdata, struct iiod_ctx, parser_pdata); + + ctx->read_cb(ctx->pdata, buf, len); +} + +static ssize_t iiod_writefd(struct parser_pdata *pdata, const void *buf, size_t len) +{ + struct iiod_ctx *ctx = container_of(pdata, struct iiod_ctx, parser_pdata); + + ctx->write_cb(ctx->pdata, buf, len); +} + +int iiod_interpreter(struct iio_context *ctx, + struct iiod_pdata *pdata, + ssize_t (*read_cb)(struct iiod_pdata *, void *, size_t), + ssize_t (*write_cb)(struct iiod_pdata *, const void *, size_t), + const void *xml_zstd, size_t xml_zstd_len) +{ + struct iiod_ctx iiod_ctx = { + .parser_pdata = { + .ctx = ctx, + .xml_zstd = xml_zstd, + .xml_zstd_len = xml_zstd_len, + .readfd = iiod_readfd, + .writefd = iiod_writefd, + }, + .read_cb = read_cb, + .write_cb = write_cb, + .pdata = pdata, + }; + int ret; + + buflist_lock = iio_mutex_create(); + ret = iio_err(buflist_lock); + if (ret) + return ret; + + evlist_lock = iio_mutex_create(); + ret = iio_err(evlist_lock); + if (ret) + goto out_destroy_buflist_lock; + + binary_parse(&iiod_ctx.parser_pdata); + + iio_mutex_destroy(evlist_lock); +out_destroy_buflist_lock: + iio_mutex_destroy(buflist_lock); + return ret; +} diff --git a/tinyiiod/tinyiiod.h b/tinyiiod/tinyiiod.h new file mode 100644 index 000000000..06152bd5e --- /dev/null +++ b/tinyiiod/tinyiiod.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Paul Cercueil + */ + +#ifndef __LIBTINYIIOD_H__ +#define __LIBTINYIIOD_H__ + +#include + +struct iiod_pdata; + +/* Default Libiio context parameters for IIOD. + * You can modify it if needed before calling iiod_interpreter(). */ +extern struct iio_context_params iiod_params; + +/* Execute the IIOD interpreter using the specified read_cb/write_cb callbacks. + * IIOD will run until one of the callbacks returns a negative error code. + * + * ctx: Libiio context to use + * pdata: User-provided data structure (or NULL) that will be passed along to + * the read_cb/write_cb callbacks + * read_cb: Callback to read from the bus. This can block, and the application + * is free to do any unrelated processing in this callback. + * write_cb: Callback to write data to the bus. + * xml: XML representation of the Libiio context. It is recommended to pass a + * ZSTD-compressed buffer here, as it would be much faster to transfer on the + * bus; but a plain XML string works as well. + * xml_len: Size of the ZSTD-compressed data, or length of the XML string. + */ +int iiod_interpreter(struct iio_context *ctx, + struct iiod_pdata *pdata, + ssize_t (*read_cb)(struct iiod_pdata *, void *, size_t), + ssize_t (*write_cb)(struct iiod_pdata *, const void *, size_t), + const void *xml, size_t xml_len); + +#endif /* __LIBTINYIIOD_H__ */ From d476d1bcdb3f0b2e8ad6630829f97580ab3cd33c Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 24 Jan 2024 12:17:28 +0100 Subject: [PATCH 19/20] iiod: Handle event reads in tinyiiod Due to the fact that tinyiiod runs thread-less, it is not possible to have a blocking read to iio_event_stream_read(). To handle event reads properly, tinyiiod based applications should return -EAGAIN in the iio_backend_ops.read_ev() callback if there is no event, and have the responsability to call iiod_set_event() when an event (or error) do occur. Signed-off-by: Paul Cercueil --- iiod/responder.c | 33 +++++++++++++++++++++++++++++++++ tinyiiod/tinyiiod.h | 8 ++++++++ 2 files changed, 41 insertions(+) diff --git a/iiod/responder.c b/iiod/responder.c index 4fa83e1c7..91d5fbf7f 100644 --- a/iiod/responder.c +++ b/iiod/responder.c @@ -835,6 +835,33 @@ static void handle_retry_dequeue_block(struct parser_pdata *pdata, iiod_io_send_response_code(block_entry->io, ret); } +void iiod_set_event(struct iio_event_stream *stream, + const struct iio_event *event, + int err_code_or_zero) +{ + struct evstream_entry *elm, *entry = NULL; + struct iiod_buf buf = { + .ptr = (void *)event, + .size = sizeof(*event), + }; + + iio_mutex_lock(evlist_lock); + + SLIST_FOREACH(elm, &evlist, entry) { + if (elm->stream == stream) { + entry = elm; + break; + } + } + + iio_mutex_unlock(evlist_lock); + + if (err_code_or_zero) + iiod_io_send_response_code(entry->io, err_code_or_zero); + else + iiod_io_send_response(entry->io, sizeof(*event), &buf, 1); +} + static int evstream_read(void *priv, void *d) { struct evstream_entry *entry = priv; @@ -846,6 +873,12 @@ static int evstream_read(void *priv, void *d) int ret; ret = iio_event_stream_read(entry->stream, &event, false); + if (ret == -EAGAIN) { + /* If iio_event_stream_read() returned -EAGAIN for a blocking + * read, trust that the application will call iiod_set_event(). */ + return 0; + } + if (ret < 0) return iiod_io_send_response_code(entry->io, ret); diff --git a/tinyiiod/tinyiiod.h b/tinyiiod/tinyiiod.h index 06152bd5e..a4a8d37a8 100644 --- a/tinyiiod/tinyiiod.h +++ b/tinyiiod/tinyiiod.h @@ -37,4 +37,12 @@ int iiod_interpreter(struct iio_context *ctx, ssize_t (*write_cb)(struct iiod_pdata *, const void *, size_t), const void *xml, size_t xml_len); +/* When a blocking iio_backend_ops.read_ev() is called, and there is no event, + * the callback is expected to return -EAGAIN; only then, when/if an event + * eventually occurs, the application should call iiod_set_event() once to + * answer. */ +void iiod_set_event(struct iio_event_stream *stream, + const struct iio_event *event, + int err_code_or_zero); + #endif /* __LIBTINYIIOD_H__ */ From 50e1ae6ffe7ceef6d0657e922acbf9b3cd43cc08 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 30 Jan 2024 11:00:11 +0100 Subject: [PATCH 20/20] xml: Shrink header size used to detect XML string embedded in URI The previous string used to detect the XML header contained the encoding hardcoded to "utf-8", which means that a XML string using the "UTF-8" (or any other encoding really) wouldn't be recognized as a valid XML string. Signed-off-by: Paul Cercueil --- xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml.c b/xml.c index f2e62577d..c778f5171 100644 --- a/xml.c +++ b/xml.c @@ -15,7 +15,7 @@ #include #include -#define XML_HEADER "" +#define XML_HEADER "