Skip to content

Commit

Permalink
iiod: Support running on UART
Browse files Browse the repository at this point in the history
Add a -s option to serve IIO over UART. This option requires the UART
dev node and optionally the baud/parity/bits settings to be specified
as the parameter.

For instance, this will run IIOD on the /dev/ttyS2 UART, at 57600 bps,
no parity, 8 bits per datum:

iiod -s /dev/ttyS2,57600n8

Fixes #560.

Signed-off-by: Paul Cercueil <[email protected]>
  • Loading branch information
pcercuei committed Apr 15, 2021
1 parent c188d75 commit 1f8d4bd
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 1 deletion.
1 change: 1 addition & 0 deletions iio-config.h.cmakein
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#cmakedefine WITH_NETWORK_GET_BUFFER
#cmakedefine01 WITH_NETWORK_EVENTFD
#cmakedefine01 WITH_IIOD_USBD
#cmakedefine01 WITH_IIOD_SERIAL
#cmakedefine01 WITH_LOCAL_CONFIG
#cmakedefine01 HAVE_DNS_SD
#cmakedefine01 HAVE_AVAHI
Expand Down
5 changes: 5 additions & 0 deletions iiod/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ if (HAVE_FUNCTIONFS_V2)
endif (WITH_IIOD_USBD)
endif (HAVE_FUNCTIONFS_V2)

option(WITH_IIOD_SERIAL "Add serial (UART) support" ON)
if (WITH_IIOD_SERIAL)
set(IIOD_CFILES ${IIOD_CFILES} serial.c)
endif()

add_executable(iiod ${IIOD_CFILES})
set_target_properties(iiod PROPERTIES
C_STANDARD 99
Expand Down
66 changes: 65 additions & 1 deletion iiod/iiod.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <sys/eventfd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>
#if WITH_ZSTD
#include <zstd.h>
Expand Down Expand Up @@ -79,6 +80,7 @@ static const struct option options[] = {
{"aio", no_argument, 0, 'a'},
{"ffs", required_argument, 0, 'F'},
{"nb-pipes", required_argument, 0, 'n'},
{"serial", required_argument, 0, 's'},
{0, 0, 0, 0},
};

Expand All @@ -91,6 +93,7 @@ static const char *options_descriptions[] = {
"Use asynchronous I/O.",
"Use the given FunctionFS mountpoint to serve over USB",
"Specify the number of USB pipes (ep couples) to use",
"Run " MY_NAME " on the specified UART.",
};


Expand Down Expand Up @@ -134,6 +137,7 @@ static void sig_handler(int sig)
static int main_interactive(struct iio_context *ctx, bool verbose, bool use_aio,
const void *xml_zstd, size_t xml_zstd_len)
{
struct termios stdin_attrs;
int flags;

if (!use_aio) {
Expand All @@ -158,6 +162,13 @@ static int main_interactive(struct iio_context *ctx, bool verbose, bool use_aio,
}
}

if (!verbose) {
tcgetattr(STDIN_FILENO, &stdin_attrs);
stdin_attrs.c_lflag &= ~ECHO;
stdin_attrs.c_oflag &= ~OPOST;
tcsetattr(STDIN_FILENO, TCSANOW, &stdin_attrs);
}

interpreter(ctx, STDIN_FILENO, STDOUT_FILENO, verbose,
false, use_aio, main_thread_pool, xml_zstd, xml_zstd_len);
return EXIT_SUCCESS;
Expand Down Expand Up @@ -348,6 +359,27 @@ static void *get_xml_zstd_data(const struct iio_context *ctx, size_t *out_len)
#endif
}

static char * get_uart_params(const char *str,
unsigned int *bps, unsigned int *bits)
{
const char *ptr;
char *dev_name;

/* Default values when unspecified */
*bps = 57600;
*bits = 8;

ptr = strchr(str, ',');
if (!ptr) {
dev_name = strdup(str);
} else {
dev_name = strndup(str, ptr - str);
sscanf(ptr, ",%un%u", bps, bits);
}

return dev_name;
}

int main(int argc, char **argv)
{
bool debug = false, interactive = false, use_aio = false;
Expand All @@ -356,12 +388,14 @@ int main(int argc, char **argv)
struct iio_context *ctx;
int c, option_index = 0;
char *ffs_mountpoint = NULL;
char *uart_params = NULL, *uart_dev = NULL;
unsigned int uart_bps, uart_bits;
char err_str[1024];
void *xml_zstd;
size_t xml_zstd_len = 0;
int ret;

while ((c = getopt_long(argc, argv, "+hVdDiaF:n:",
while ((c = getopt_long(argc, argv, "+hVdDiaF:n:s:",
options, &option_index)) != -1) {
switch (c) {
case 'd':
Expand Down Expand Up @@ -402,6 +436,15 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
break;
case 's':
if (!WITH_IIOD_SERIAL) {
IIO_ERROR("IIOD was not compiled with serial support.\n");
return EXIT_FAILURE;

}

uart_params = optarg;
break;
case 'h':
usage();
return EXIT_SUCCESS;
Expand Down Expand Up @@ -450,6 +493,27 @@ int main(int argc, char **argv)
}
}

if (WITH_IIOD_SERIAL && uart_params) {
uart_dev = get_uart_params(uart_params, &uart_bps, &uart_bits);
if (!uart_dev) {
IIO_ERROR("Unable to parse serial parameters\n");
ret = EXIT_FAILURE;
goto out_destroy_thread_pool;
}

ret = start_serial_daemon(ctx, uart_dev, uart_bps, uart_bits,
debug, main_thread_pool,
xml_zstd, xml_zstd_len);
if (ret) {
iio_strerror(-ret, err_str, sizeof(err_str));
IIO_ERROR("Unable to start serial daemon: %s\n", err_str);
ret = EXIT_FAILURE;
goto out_destroy_thread_pool;
}

free(uart_dev);
}

if (interactive)
ret = main_interactive(ctx, debug, use_aio, xml_zstd, xml_zstd_len);
else
Expand Down
4 changes: 4 additions & 0 deletions iiod/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ int start_usb_daemon(struct iio_context *ctx, const char *ffs,
bool debug, bool use_aio, unsigned int nb_pipes,
struct thread_pool *pool,
const void *xml_zstd, size_t xml_zstd_len);
int start_serial_daemon(struct iio_context *ctx, const char *dev,
unsigned int uart_bps, unsigned int uart_bits,
bool debug, struct thread_pool *pool,
const void *xml_zstd, size_t xml_zstd_len);

int open_dev(struct parser_pdata *pdata, struct iio_device *dev,
size_t samples_count, const char *mask, bool cyclic);
Expand Down
161 changes: 161 additions & 0 deletions iiod/serial.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* libiio - Library for interfacing industrial I/O (IIO) devices
*
* Copyright (C) 2021 Analog Devices, Inc.
* Author: Paul Cercueil <[email protected]>
*/

#include "../debug.h"
#include "ops.h"
#include "thread-pool.h"

#include <errno.h>
#include <fcntl.h>
#include <linux/serial.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <termios.h>

struct serial_pdata {
struct iio_context *ctx;
bool debug;
int fd;
const void *xml_zstd;
size_t xml_zstd_len;
};

static void serial_main(struct thread_pool *pool, void *d)
{
struct serial_pdata *pdata = d;

interpreter(pdata->ctx, pdata->fd, pdata->fd, pdata->debug,
false, false, pool,
pdata->xml_zstd, pdata->xml_zstd_len);

close(pdata->fd);
free(pdata);
}

static int serial_configure(int fd, unsigned int uart_bps, unsigned int uart_bits)
{
struct termios tty_attrs;
int err;

err = tcgetattr(fd, &tty_attrs);
if (err == -1) {
IIO_ERROR("tcgetattr failed\n");
return -errno;
}

tty_attrs.c_lflag &= ~ECHO;
tty_attrs.c_oflag &= ~OPOST;

tty_attrs.c_cflag |= CLOCAL | CREAD;
tty_attrs.c_cflag &= ~(CSIZE | CBAUD);

#define CASE_BPS(bps, attr) case bps: (attr)->c_cflag |= B##bps; break
switch (uart_bps) {
CASE_BPS(50, &tty_attrs);
CASE_BPS(75, &tty_attrs);
CASE_BPS(110, &tty_attrs);
CASE_BPS(134, &tty_attrs);
CASE_BPS(150, &tty_attrs);
CASE_BPS(200, &tty_attrs);
CASE_BPS(300, &tty_attrs);
CASE_BPS(600, &tty_attrs);
CASE_BPS(1200, &tty_attrs);
CASE_BPS(1800, &tty_attrs);
CASE_BPS(2400, &tty_attrs);
CASE_BPS(4800, &tty_attrs);
CASE_BPS(9600, &tty_attrs);
CASE_BPS(19200, &tty_attrs);
CASE_BPS(38400, &tty_attrs);
CASE_BPS(57600, &tty_attrs);
CASE_BPS(115200, &tty_attrs);
CASE_BPS(230400, &tty_attrs);
CASE_BPS(460800, &tty_attrs);
CASE_BPS(500000, &tty_attrs);
CASE_BPS(576000, &tty_attrs);
CASE_BPS(921600, &tty_attrs);
CASE_BPS(1000000, &tty_attrs);
CASE_BPS(1152000, &tty_attrs);
CASE_BPS(1500000, &tty_attrs);
CASE_BPS(2000000, &tty_attrs);
CASE_BPS(2500000, &tty_attrs);
CASE_BPS(3000000, &tty_attrs);
CASE_BPS(3500000, &tty_attrs);
CASE_BPS(4000000, &tty_attrs);
default:
IIO_ERROR("Invalid baud rate\n");
return -EINVAL;
}

switch (uart_bits) {
case 5:
tty_attrs.c_cflag |= CS5;
break;
case 6:
tty_attrs.c_cflag |= CS6;
break;
case 7:
tty_attrs.c_cflag |= CS7;
break;
case 8:
tty_attrs.c_cflag |= CS8;
break;
default:
IIO_ERROR("Invalid serial configuration\n");
return -EINVAL;
}

err = tcsetattr(fd, TCSANOW, &tty_attrs);
if (err == -1) {
IIO_ERROR("Unable to apply serial settings\n");
return -errno;
}

return 0;
}

int start_serial_daemon(struct iio_context *ctx, const char *dev,
unsigned int uart_bps, unsigned int uart_bits,
bool debug, struct thread_pool *pool,
const void *xml_zstd, size_t xml_zstd_len)
{
struct serial_pdata *pdata;
int fd, err;

pdata = zalloc(sizeof(*pdata));
if (!pdata)
return -ENOMEM;

fd = open(dev, O_RDWR | O_CLOEXEC);
if (fd == -1) {
err = -errno;
goto err_free_pdata;
}

err = serial_configure(fd, uart_bps, uart_bits);
if (err)
goto err_close_fd;

pdata->ctx = ctx;
pdata->debug = debug;
pdata->fd = fd;
pdata->xml_zstd = xml_zstd;
pdata->xml_zstd_len = xml_zstd_len;

IIO_DEBUG("Serving over UART on %s at %u bps, %u bits\n",
dev, uart_bps, uart_bits);

return thread_pool_add_thread(pool, serial_main, pdata, "iiod_serial_thd");

err_close_fd:
close(fd);
err_free_pdata:
free(pdata);
return err;
}

0 comments on commit 1f8d4bd

Please sign in to comment.