/***************************************************************************//** * @file iio_app.c * @brief C file of iio_app * @author Mihail Chindris (mihail.chindris@analog.com) ******************************************************************************** * Copyright 2020(c) Analog Devices, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of Analog Devices, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #ifdef IIO_SUPPORT #include #include #include "iio_app.h" #include "parameters.h" #include "no_os_alloc.h" #if defined(ADUCM_PLATFORM) #include "aducm3029_uart.h" #include "aducm3029_irq.h" #endif #if defined(XILINX_PLATFORM) #include "xilinx_uart.h" #include "xilinx_irq.h" #endif #if defined(STM32_PLATFORM) #include #include "stm32_uart.h" #include "stm32_irq.h" #endif #if defined(MAXIM_PLATFORM) #include "maxim_irq.h" #include "maxim_uart.h" #endif #ifdef NO_OS_NETWORKING /* Fix: Use static buffers instead of calloc for new connections */ #warning "iio may not work with WIFI on aducm3029." #ifdef ADUCM_PLATFORM #include "wifi.h" #endif #include "tcp_socket.h" #endif #ifdef LINUX_PLATFORM #include "linux_socket.h" #include "tcp_socket.h" #endif #ifdef NO_OS_LWIP_NETWORKING #include "lwip_socket.h" #endif // The default baudrate iio_app will use to print messages to console. #define UART_BAUDRATE_DEFAULT 115200 #define UART_STOPBITS_DEFAULT NO_OS_UART_STOP_1_BIT static inline uint32_t _calc_uart_xfer_time(uint32_t len, uint32_t baudrate) { uint32_t ms = 1000ul * len * 8 / UART_BAUDRATE_DEFAULT; ms += ms / 10; // overhead return ms; } #if !defined(LINUX_PLATFORM) && !defined(NO_OS_NETWORKING) && !defined(NO_OS_USB_UART) static int32_t iio_print_uart_info_message(struct no_os_uart_desc **uart_desc, struct no_os_uart_init_param *user_uart_params, char *message, int32_t msglen) { int32_t status; uint32_t delay_ms; status = no_os_uart_write(*uart_desc, (uint8_t *)message, msglen); if (status < 0) return status; delay_ms = _calc_uart_xfer_time(msglen, UART_BAUDRATE_DEFAULT); no_os_mdelay(delay_ms); /** Reinitialize UART with parameters required by the IIO application */ no_os_uart_remove(*uart_desc); return no_os_uart_init(uart_desc, user_uart_params); } #endif static int32_t print_uart_hello_message(struct no_os_uart_desc **uart_desc, struct no_os_uart_init_param *user_uart_params) { #if defined(LINUX_PLATFORM) || defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING) || defined(NO_OS_USB_UART) return 0; #else const char *uart_data_size[] = { "5", "6", "7", "8", "9" }; const char *uart_parity[] = { "none", "mark", "space", "odd", "even" }; const char *uart_stop[] = { "1", "2" }; char message[512]; uint32_t msglen = sprintf(message, "Running IIOD server...\n" "If successful, you may connect an IIO client application by:\n" "1. Disconnecting the serial terminal you use to view this message.\n" "2. Connecting the IIO client application using the serial backend configured as shown:\n" "\tBaudrate: %lu\n" "\tData size: %s bits\n" "\tParity: %s\n" "\tStop bits: %s\n" "\tFlow control: none\n", user_uart_params->baud_rate, uart_data_size[user_uart_params->size], uart_parity[user_uart_params->parity], uart_stop[user_uart_params->stop]); return iio_print_uart_info_message(uart_desc, user_uart_params, message, msglen); #endif } static int32_t print_uart_error_message(struct no_os_uart_desc **uart_desc, struct no_os_uart_init_param *user_uart_params, int32_t status) { char message[512]; uint32_t msglen = sprintf(message, "IIOD server failed with code %d.\n", (int)status); #if defined(LINUX_PLATFORM) || defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING) || defined(NO_OS_USB_UART) (void)msglen; printf("%s", message); return 0; #else return iio_print_uart_info_message(uart_desc, user_uart_params, message, msglen); #endif } #if defined(NO_OS_LWIP_NETWORKING) static int32_t lwip_network_setup(struct iio_app_desc *app, struct iio_app_init_param param, struct iio_init_param *iio_init_param) { static struct tcp_socket_init_param socket_param; static struct lwip_network_desc lwip_desc; static bool is_initialized = false; int ret; if (NO_OS_LWIP_INIT_ONETIME && is_initialized) { socket_param.net = &lwip_desc.no_os_net; } else { ret = no_os_lwip_init(&app->lwip_desc, ¶m.lwip_param); if (ret) return ret; socket_param.net = &app->lwip_desc->no_os_net; memcpy(&lwip_desc, app->lwip_desc, sizeof(lwip_desc)); } is_initialized = true; socket_param.max_buff_size = 0; iio_init_param->phy_type = USE_NETWORK; iio_init_param->tcp_socket_init_param = &socket_param; return 0; } #endif #if defined(NO_OS_NETWORKING) || defined(LINUX_PLATFORM) static int32_t network_setup(struct iio_init_param *iio_init_param, struct no_os_uart_desc *uart_desc, void *irq_desc) { static struct tcp_socket_init_param socket_param; #ifdef LINUX_PLATFORM socket_param.net = &linux_net; #endif #ifdef ADUCM_PLATFORM int32_t status; static struct wifi_desc *wifi; struct wifi_init_param wifi_param = { .irq_desc = irq_desc, .uart_desc = uart_desc, .uart_irq_conf = uart_desc, .uart_irq_id = UART_IRQ_ID, .sw_reset_en = true }; status = wifi_init(&wifi, &wifi_param); if (status < 0) return status; status = wifi_connect(wifi, WIFI_SSID, WIFI_PWD); if (status < 0) return status; char buff[100]; wifi_get_ip(wifi, buff, 100); printf("iiod ip is: %s\n", buff); wifi_get_network_interface(wifi, &socket_param.net); #endif socket_param.max_buff_size = 0; iio_init_param->phy_type = USE_NETWORK; iio_init_param->tcp_socket_init_param = &socket_param; return 0; } #endif static int32_t uart_setup(struct no_os_uart_desc **uart_desc, struct no_os_uart_init_param *uart_init_par) { #if defined(LINUX_PLATFORM) || defined(NO_OS_LWIP_NETWORKING) *uart_desc = NULL; return 0; #endif struct no_os_uart_init_param luart_par = { .device_id = uart_init_par->device_id, /* TODO: remove this ifdef when asynchrounous rx is implemented on every platform. */ #if defined(STM32_PLATFORM) || defined(MAXIM_PLATFORM) || defined(ADUCM_PLATFORM) || defined(PICO_PLATFORM) .irq_id = uart_init_par->irq_id, .asynchronous_rx = true, #endif .baud_rate = UART_BAUDRATE_DEFAULT, .size = NO_OS_UART_CS_8, .parity = NO_OS_UART_PAR_NO, .stop = NO_OS_UART_STOP_1_BIT, .platform_ops = uart_init_par->platform_ops, #ifndef ADUCM_PLATFORM .extra = uart_init_par->extra #endif }; return no_os_uart_init(uart_desc, &luart_par); } #if defined(ADUCM_PLATFORM) || (defined(STM32_PLATFORM)) || defined(MAXIM_PLATFORM) static int32_t irq_setup(struct no_os_irq_ctrl_desc **irq_desc) { int32_t status; #if defined(ADUCM_PLATFORM) void *platform_irq_init_par = NULL; const struct no_os_irq_platform_ops *platform_irq_ops = &aducm_irq_ops; #elif defined(STM32_PLATFORM) void *platform_irq_init_par = NULL; const struct no_os_irq_platform_ops *platform_irq_ops = &stm32_irq_ops; #elif defined(MAXIM_PLATFORM) void *platform_irq_init_par = NULL; const struct no_os_irq_platform_ops *platform_irq_ops = &max_irq_ops; #endif struct no_os_irq_init_param irq_init_param = { .irq_ctrl_id = INTC_DEVICE_ID, .platform_ops = platform_irq_ops, .extra = platform_irq_init_par }; status = no_os_irq_ctrl_init(irq_desc, &irq_init_param); if (status < 0) return status; return no_os_irq_global_enable(*irq_desc); } #endif /** * @brief Register devices for an iio application * * Configuration for communication is done in parameters.h * @param app - the iio application descriptor * @param app_init_param - the iio application initialization parameters * @return 0 on success, negative value otherwise */ int iio_app_init(struct iio_app_desc **app, struct iio_app_init_param app_init_param) { struct iio_device_init *iio_init_devs = NULL; struct iio_init_param iio_init_param; struct no_os_uart_desc *uart_desc; struct iio_app_desc *application; struct iio_data_buffer *buff; unsigned int i; int status; void *irq_desc = app_init_param.irq_desc; application = (struct iio_app_desc *)no_os_calloc(1, sizeof(*application)); if (!application) return -ENOMEM; application->post_step_callback = app_init_param.post_step_callback; application->arg = app_init_param.arg; #if defined(ADUCM_PLATFORM) || defined(STM32_PLATFORM) /* Only one irq controller can exist and be initialized in * any of the iio_devices. */ for (i = 0; i < app_init_param.nb_devices; i++) { if (app_init_param.devices[i].dev_descriptor->irq_desc) { irq_desc = (struct no_os_irq_ctrl_desc *) app_init_param.devices[i].dev_descriptor->irq_desc; break; } } if (!irq_desc) { status = irq_setup((struct no_os_irq_ctrl_desc **)&irq_desc); if (status < 0) return status; } #endif status = uart_setup(&uart_desc, &app_init_param.uart_init_params); if (status < 0) goto error_uart; status = print_uart_hello_message(&uart_desc, &app_init_param.uart_init_params); if (status < 0) goto error; application->uart_desc = uart_desc; #if defined(NO_OS_LWIP_NETWORKING) status = lwip_network_setup(application, app_init_param, &iio_init_param); if (status) goto error; #elif defined(NO_OS_NETWORKING) || defined(LINUX_PLATFORM) status = network_setup(&iio_init_param, uart_desc, application->irq_desc); if (status < 0) goto error; #else iio_init_param.phy_type = USE_UART; iio_init_param.uart_desc = uart_desc; #endif iio_init_devs = no_os_calloc(app_init_param.nb_devices, sizeof(*iio_init_devs)); if (!iio_init_devs) { status = -ENOMEM; goto error; } for (i = 0; i < app_init_param.nb_devices; ++i) { /* * iio_app_device is from iio_app.h and we don't want to include * this in iio.h. * At the moment iio_device_init has the same parameters but * it will change. * When changes are done in iio. iio_app_device may be removed *iio_init_param and use iio_device_init instead. * This way faster changes can be done without changing all * project for each change. */ iio_init_devs[i].name = app_init_param.devices[i].name; iio_init_devs[i].dev = app_init_param.devices[i].dev; iio_init_devs[i].dev_descriptor = app_init_param.devices[i].dev_descriptor; iio_init_devs[i].trigger_id = app_init_param.devices[i].default_trigger_id; buff = app_init_param.devices[i].read_buff ? app_init_param.devices[i].read_buff : app_init_param.devices[i].write_buff; if (buff) { iio_init_devs[i].raw_buf = buff->buff; iio_init_devs[i].raw_buf_len = buff->size; } else { iio_init_devs[i].raw_buf = NULL; iio_init_devs[i].raw_buf_len = 0; } } iio_init_param.devs = iio_init_devs; iio_init_param.nb_devs = app_init_param.nb_devices; iio_init_param.trigs = app_init_param.trigs; iio_init_param.nb_trigs = app_init_param.nb_trigs; iio_init_param.ctx_attrs = app_init_param.ctx_attrs; iio_init_param.nb_ctx_attr = app_init_param.nb_ctx_attr; status = iio_init(&application->iio_desc, &iio_init_param); if (status < 0) goto error; no_os_free(iio_init_devs); *app = application; return 0; error_uart: /** We might have to reinit UART, settings might have changed for IIO */ uart_setup(&uart_desc, &app_init_param.uart_init_params); error: no_os_free(iio_init_devs); no_os_free(application); print_uart_error_message(&uart_desc, &app_init_param.uart_init_params, status); no_os_uart_remove(uart_desc); return status; } /** * @brief Start an IIO application * * Configuration for communication is done through iio_app_init_param * @param app - the iio application parameters * @return 0 on success, negative value otherwise */ int iio_app_run(struct iio_app_desc *app) { int status; do { status = iio_step(app->iio_desc); if (status && status != -EAGAIN && status != -ENOTCONN && status != -NO_OS_EOVERRUN) return status; if (app->post_step_callback) { status = app->post_step_callback(app->arg); if (status) return status; } } while (true); } /** * @brief Remove resources allocated by the IIO application * * Configuration for communication is done through iio_app_init_param * @param app - the iio application parameters * @return 0 on success, negative value otherwise */ int iio_app_remove(struct iio_app_desc *app) { int ret; #if defined(ADUCM_PLATFORM) || (defined(XILINX_PLATFORM) && !defined(PLATFORM_MB)) || defined(STM32_PLATFORM) ret = no_os_irq_ctrl_remove(app->irq_desc); if (ret) return ret; #endif if (app->uart_desc) { ret = no_os_uart_remove(app->uart_desc); if (ret) return ret; } ret = iio_remove(app->iio_desc); if (ret) return ret; no_os_free(app); return 0; } #endif