Add files via upload
This commit is contained in:
@@ -0,0 +1,927 @@
|
||||
/***************************************************************************//**
|
||||
* @file iiod.c
|
||||
* @brief Nonblocking implementation of iiod.
|
||||
* @author Mihail Chindris (mihail.chindris@analog.com)
|
||||
********************************************************************************
|
||||
* Copyright 2022(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.
|
||||
*******************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "iiod.h"
|
||||
#include "iiod_private.h"
|
||||
|
||||
#include "no_os_error.h"
|
||||
#include "no_os_util.h"
|
||||
|
||||
#define SET_DUMMY_IF_NULL(func, dummy) ((func) ? (func) : (dummy))
|
||||
|
||||
static char delim[] = " \r\n";
|
||||
|
||||
|
||||
static const char *attr_types_strs[] = {
|
||||
[IIO_ATTR_TYPE_DEBUG] = "DEBUG",
|
||||
[IIO_ATTR_TYPE_BUFFER] = "BUFFER",
|
||||
[IIO_ATTR_TYPE_CH_OUT] = "OUTPUT",
|
||||
[IIO_ATTR_TYPE_CH_IN] = "INPUT"
|
||||
};
|
||||
|
||||
static struct iiod_str cmds[] = {
|
||||
[IIOD_CMD_HELP] = IIOD_STR("HELP"),
|
||||
[IIOD_CMD_EXIT] = IIOD_STR("EXIT"),
|
||||
[IIOD_CMD_PRINT] = IIOD_STR("PRINT"),
|
||||
[IIOD_CMD_VERSION] = IIOD_STR("VERSION"),
|
||||
[IIOD_CMD_TIMEOUT] = IIOD_STR("TIMEOUT"),
|
||||
[IIOD_CMD_OPEN] = IIOD_STR("OPEN"),
|
||||
[IIOD_CMD_CLOSE] = IIOD_STR("CLOSE"),
|
||||
[IIOD_CMD_READ] = IIOD_STR("READ"),
|
||||
[IIOD_CMD_WRITE] = IIOD_STR("WRITE"),
|
||||
[IIOD_CMD_READBUF] = IIOD_STR("READBUF"),
|
||||
[IIOD_CMD_WRITEBUF] = IIOD_STR("WRITEBUF"),
|
||||
[IIOD_CMD_GETTRIG] = IIOD_STR("GETTRIG"),
|
||||
[IIOD_CMD_SETTRIG] = IIOD_STR("SETTRIG"),
|
||||
[IIOD_CMD_SET] = IIOD_STR("SET")
|
||||
};
|
||||
static const uint32_t priority_array[] = {
|
||||
/* Order not tested, just personal expectation. Function can
|
||||
* be improved, this improvement is chosen for simplicity */
|
||||
IIOD_CMD_READBUF,
|
||||
IIOD_CMD_WRITEBUF,
|
||||
IIOD_CMD_READ,
|
||||
IIOD_CMD_WRITE,
|
||||
IIOD_CMD_OPEN,
|
||||
IIOD_CMD_CLOSE,
|
||||
IIOD_CMD_PRINT,
|
||||
IIOD_CMD_EXIT,
|
||||
IIOD_CMD_TIMEOUT,
|
||||
IIOD_CMD_VERSION,
|
||||
IIOD_CMD_GETTRIG,
|
||||
IIOD_CMD_SETTRIG,
|
||||
IIOD_CMD_HELP,
|
||||
IIOD_CMD_SET
|
||||
};
|
||||
|
||||
static_assert(NO_OS_ARRAY_SIZE(cmds) == NO_OS_ARRAY_SIZE(priority_array),
|
||||
"Arrays must have the same size");
|
||||
|
||||
/* Set res->cmd to corresponding cmd and return the processed length of buf */
|
||||
static int32_t parse_cmd(const char *token, struct comand_desc *res)
|
||||
{
|
||||
uint32_t i;
|
||||
struct iiod_str *cmd;
|
||||
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < NO_OS_ARRAY_SIZE(cmds); ++i) {
|
||||
cmd = &cmds[priority_array[i]];
|
||||
if (strcmp(token, cmd->str) == 0) {
|
||||
res->cmd = priority_array[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int32_t parse_num(const char *token, uint32_t *res, uint32_t base)
|
||||
{
|
||||
char *end_ptr;
|
||||
|
||||
*res = strtoul(token, &end_ptr, base);
|
||||
if (*res == 0 && *end_ptr != '\0')
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t iiod_parse_open(const char *token, struct comand_desc *res,
|
||||
char **ctx)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
ret = parse_num(token, &res->sample_count, 10);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
ret = parse_num(token, &res->mask, 16);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
res->cyclic = 0;
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
if (token) {
|
||||
if (strcmp(token, "CYCLIC") == 0)
|
||||
res->cyclic = 1;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t iiod_parse_set(const char *token, struct comand_desc *res,
|
||||
char **ctx)
|
||||
{
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(token, "BUFFERS_COUNT"))
|
||||
return -EINVAL;
|
||||
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
return parse_num(token, &res->count, 10);
|
||||
}
|
||||
|
||||
static int32_t iiod_parse_rw_attr(const char *token, struct comand_desc *res,
|
||||
char **ctx)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
res->type = IIO_ATTR_TYPE_DEVICE;
|
||||
if (token) {
|
||||
for (i = 0; i < IIO_ATTR_TYPE_DEVICE; ++i) {
|
||||
if (strcmp(token, attr_types_strs[i]) == 0) {
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
res->type = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res->type == IIO_ATTR_TYPE_CH_IN ||
|
||||
res->type == IIO_ATTR_TYPE_CH_OUT) {
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
strncpy(res->channel, token, sizeof(res->channel));
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
}
|
||||
|
||||
if (res->cmd == IIOD_CMD_WRITE) {
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (*token >= '0' && *token <= '9') {
|
||||
memset(res->attr, 0, sizeof(res->attr));
|
||||
|
||||
return parse_num(token, &res->bytes_count, 10);
|
||||
}
|
||||
|
||||
strncpy(res->attr, token, sizeof(res->attr));
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
return parse_num(token, &res->bytes_count, 10);
|
||||
}
|
||||
|
||||
if (token)
|
||||
strncpy(res->attr, token, sizeof(res->attr));
|
||||
else
|
||||
memset(res->attr, 0, sizeof(res->attr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t iiod_parse_line(char *buf, struct comand_desc *res, char **ctx)
|
||||
{
|
||||
int32_t ret;
|
||||
char *token;
|
||||
|
||||
token = strtok_r(buf, delim, ctx);
|
||||
ret = parse_cmd(token, res);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
/* Commands without device */
|
||||
switch (res->cmd) {
|
||||
case IIOD_CMD_HELP:
|
||||
case IIOD_CMD_EXIT:
|
||||
case IIOD_CMD_PRINT:
|
||||
case IIOD_CMD_VERSION:
|
||||
return 0;
|
||||
case IIOD_CMD_TIMEOUT:
|
||||
return parse_num(token, &res->timeout, 10);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(res->device, token, sizeof(res->device));
|
||||
token = strtok_r(NULL, delim, ctx);
|
||||
switch (res->cmd) {
|
||||
case IIOD_CMD_CLOSE:
|
||||
case IIOD_CMD_GETTRIG:
|
||||
return 0;
|
||||
case IIOD_CMD_OPEN:
|
||||
return iiod_parse_open(token, res, ctx);
|
||||
case IIOD_CMD_READ:
|
||||
case IIOD_CMD_WRITE:
|
||||
return iiod_parse_rw_attr(token, res, ctx);
|
||||
case IIOD_CMD_READBUF:
|
||||
case IIOD_CMD_WRITEBUF:
|
||||
return parse_num(token, &res->bytes_count, 10);
|
||||
case IIOD_CMD_SETTRIG:
|
||||
if (token)
|
||||
strncpy(res->trigger, token, sizeof(res->trigger));
|
||||
else
|
||||
memset(res->trigger, 0, sizeof(res->trigger));
|
||||
|
||||
return 0;
|
||||
case IIOD_CMD_SET:
|
||||
return iiod_parse_set(token, res, ctx);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_open(struct iiod_ctx *ctx, const char *device,
|
||||
uint32_t samples, uint32_t mask, bool cyclic)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_close(struct iiod_ctx *ctx, const char *device)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_rw_attr(struct iiod_ctx *ctx, const char *device,
|
||||
struct iiod_attr *attr, char *buf, uint32_t len)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_rd_data(struct iiod_ctx *ctx, const char *device, char *buf,
|
||||
uint32_t bytes)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_wr_data(struct iiod_ctx *ctx, const char *device,
|
||||
const char *trig, uint32_t bytes)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_set_timeout(struct iiod_ctx *ctx, uint32_t timeout)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_set_buffers_count(struct iiod_ctx *ctx, const char *device,
|
||||
uint32_t buffers_count)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int32_t iiod_copy_ops(struct iiod_ops *ops, struct iiod_ops *new_ops)
|
||||
{
|
||||
if (!new_ops->recv || !new_ops->send)
|
||||
return -EINVAL;
|
||||
|
||||
ops->recv = new_ops->recv;
|
||||
ops->send = new_ops->send;
|
||||
|
||||
ops->open = SET_DUMMY_IF_NULL(new_ops->open, dummy_open);
|
||||
ops->close = SET_DUMMY_IF_NULL(new_ops->close, dummy_close);
|
||||
ops->read_buffer = SET_DUMMY_IF_NULL(new_ops->read_buffer, dummy_rd_data);
|
||||
ops->write_buffer = SET_DUMMY_IF_NULL(new_ops->write_buffer, dummy_wr_data);
|
||||
ops->read_attr = SET_DUMMY_IF_NULL(new_ops->read_attr, dummy_rw_attr);
|
||||
ops->write_attr = SET_DUMMY_IF_NULL(new_ops->write_attr, dummy_rw_attr);
|
||||
ops->get_trigger = SET_DUMMY_IF_NULL(new_ops->get_trigger, dummy_rd_data);
|
||||
ops->set_trigger = SET_DUMMY_IF_NULL(new_ops->set_trigger, dummy_wr_data);
|
||||
ops->set_timeout = SET_DUMMY_IF_NULL(new_ops->set_timeout, dummy_set_timeout);
|
||||
ops->set_buffers_count = SET_DUMMY_IF_NULL(new_ops->set_buffers_count,
|
||||
dummy_set_buffers_count);
|
||||
ops->refill_buffer = SET_DUMMY_IF_NULL(new_ops->refill_buffer,
|
||||
dummy_close);
|
||||
ops->push_buffer = SET_DUMMY_IF_NULL(new_ops->push_buffer,
|
||||
dummy_close);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t iiod_init(struct iiod_desc **desc, struct iiod_init_param *param)
|
||||
{
|
||||
struct iiod_desc *ldesc;
|
||||
int32_t ret;
|
||||
|
||||
if (!desc || !param || !param->ops)
|
||||
return -EINVAL;
|
||||
|
||||
ldesc = (struct iiod_desc *)calloc(1, sizeof(*ldesc));
|
||||
if (!ldesc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = iiod_copy_ops(&ldesc->ops, param->ops);
|
||||
if (NO_OS_IS_ERR_VALUE(ret)) {
|
||||
free(ldesc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ldesc->xml = param->xml;
|
||||
ldesc->xml_len = param->xml_len;
|
||||
ldesc->app_instance = param->instance;
|
||||
ldesc->phy_type = param->phy_type;
|
||||
|
||||
*desc = ldesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iiod_remove(struct iiod_desc *desc)
|
||||
{
|
||||
free(desc);
|
||||
}
|
||||
|
||||
static void conn_clean_state(struct iiod_conn_priv *conn)
|
||||
{
|
||||
memset(&conn->cmd_data, 0, sizeof(conn->cmd_data));
|
||||
memset(&conn->res, 0, sizeof(conn->res));
|
||||
memset(&conn->nb_buf, 0, sizeof(conn->nb_buf));
|
||||
|
||||
conn->res.buf.buf = NULL;
|
||||
conn->res.buf.idx = 0;
|
||||
conn->parser_idx = 0;
|
||||
conn->state = IIOD_READING_LINE;
|
||||
}
|
||||
|
||||
int32_t iiod_conn_add(struct iiod_desc *desc, struct iiod_conn_data *data,
|
||||
uint32_t *new_conn_id)
|
||||
{
|
||||
uint32_t i;
|
||||
struct iiod_conn_priv *conn;
|
||||
|
||||
if (!desc || !new_conn_id)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < IIOD_MAX_CONNECTIONS; ++i)
|
||||
if (!desc->conns[i].used) {
|
||||
conn = &desc->conns[i];
|
||||
memset(conn, 0, sizeof(*conn));
|
||||
conn->used = 1;
|
||||
conn->conn = data->conn;
|
||||
/*
|
||||
* TODO in future:
|
||||
* think of using other buffer (e.g. ciruclar_buffer)
|
||||
* to somehow implement zero copy
|
||||
*/
|
||||
conn->payload_buf = data->buf;
|
||||
conn->payload_buf_len = data->len;
|
||||
*new_conn_id = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int32_t iiod_conn_remove(struct iiod_desc *desc, uint32_t conn_id,
|
||||
struct iiod_conn_data *data)
|
||||
{
|
||||
if (!desc || conn_id > IIOD_MAX_CONNECTIONS ||
|
||||
!desc->conns[conn_id].used)
|
||||
return -EINVAL;
|
||||
struct iiod_conn_priv *conn;
|
||||
conn = &desc->conns[conn_id];
|
||||
data->conn = conn->conn;
|
||||
data->len = conn->payload_buf_len;
|
||||
data->buf = conn->payload_buf;
|
||||
conn->used = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t call_op(struct iiod_ops *ops, struct comand_desc *data,
|
||||
struct iiod_ctx *ctx)
|
||||
{
|
||||
switch (data->cmd) {
|
||||
case IIOD_CMD_HELP:
|
||||
return -EINVAL;
|
||||
case IIOD_CMD_TIMEOUT:
|
||||
return ops->set_timeout(ctx, data->timeout);
|
||||
case IIOD_CMD_OPEN:
|
||||
return ops->open(ctx, data->device, data->sample_count,
|
||||
data->mask, data->cyclic);
|
||||
case IIOD_CMD_CLOSE:
|
||||
return ops->close(ctx, data->device);
|
||||
case IIOD_CMD_SETTRIG:
|
||||
return ops->set_trigger(ctx, data->device, data->trigger,
|
||||
strlen(data->trigger));
|
||||
case IIOD_CMD_SET:
|
||||
return ops->set_buffers_count(ctx, data->device, data->count);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unload data from buf without blocking.
|
||||
* When done will return 0, if there is still data to be sent it will return
|
||||
* -EAGIAN. On error, an negative error code is returned
|
||||
*/
|
||||
static int32_t rw_iiod_buff(struct iiod_desc *desc, struct iiod_conn_priv *conn,
|
||||
struct iiod_buff *buf, uint8_t flags)
|
||||
{
|
||||
struct iiod_ctx ctx = IIOD_CTX(desc, conn);
|
||||
uint8_t *tmp_buf;
|
||||
int32_t ret;
|
||||
int32_t len;
|
||||
|
||||
len = buf->len - buf->idx;
|
||||
if (len) {
|
||||
tmp_buf = (uint8_t *)buf->buf + buf->idx;
|
||||
if (flags & IIOD_WR)
|
||||
ret = desc->ops.send(&ctx, tmp_buf, len);
|
||||
else
|
||||
ret = desc->ops.recv(&ctx, tmp_buf, len);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
buf->idx += ret;
|
||||
if (ret < len)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (flags & IIOD_ENDL) {
|
||||
ret = desc->ops.send(&ctx, (uint8_t *)"\n", 1);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
if (ret != 1)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t do_read_buff_delayed(struct iiod_desc *desc,
|
||||
struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx = IIOD_CTX(desc, conn);
|
||||
uint32_t max_to_read;
|
||||
int32_t ret, len;
|
||||
|
||||
conn->nb_buf.buf = conn->payload_buf;
|
||||
len = no_os_min(conn->payload_buf_len, conn->cmd_data.bytes_count);
|
||||
max_to_read = len - conn->nb_buf.len;
|
||||
ret = desc->ops.read_buffer(&ctx, conn->cmd_data.device,
|
||||
conn->nb_buf.buf + conn->nb_buf.len, max_to_read);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
conn->nb_buf.len += ret;
|
||||
|
||||
if (conn->nb_buf.len < conn->cmd_data.bytes_count)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = rw_iiod_buff(desc, conn, &conn->nb_buf, IIOD_WR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
conn->nb_buf.len = 0;
|
||||
conn->nb_buf.idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t do_read_buff(struct iiod_desc *desc, struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx;
|
||||
int32_t ret, len;
|
||||
|
||||
/*
|
||||
* When using the network backend wait for a whole buffer to be filled
|
||||
* before sending in order to reduce the ammount of network traffic.
|
||||
*/
|
||||
if (desc->phy_type == USE_NETWORK)
|
||||
return do_read_buff_delayed(desc, conn);
|
||||
|
||||
ctx = (struct iiod_ctx)IIOD_CTX(desc, conn);
|
||||
if (conn->nb_buf.len == 0) {
|
||||
conn->nb_buf.buf = conn->payload_buf;
|
||||
len = no_os_min(conn->payload_buf_len,
|
||||
conn->cmd_data.bytes_count);
|
||||
/* Read from dev */
|
||||
ret = desc->ops.read_buffer(&ctx, conn->cmd_data.device,
|
||||
conn->nb_buf.buf, len);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
len = ret;
|
||||
conn->nb_buf.len = len;
|
||||
conn->nb_buf.idx = 0;
|
||||
}
|
||||
if (conn->nb_buf.idx < conn->nb_buf.len) {
|
||||
/* Write on conn */
|
||||
ret = rw_iiod_buff(desc, conn, &conn->nb_buf, IIOD_WR);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
conn->cmd_data.bytes_count -= conn->nb_buf.len;
|
||||
conn->nb_buf.len = 0;
|
||||
if (conn->cmd_data.bytes_count)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t do_write_buff(struct iiod_desc *desc,
|
||||
struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx = IIOD_CTX(desc, conn);
|
||||
int32_t ret, len;
|
||||
|
||||
if (conn->nb_buf.len == 0) {
|
||||
conn->nb_buf.buf = conn->payload_buf;
|
||||
len = no_os_min(conn->payload_buf_len,
|
||||
conn->cmd_data.bytes_count);
|
||||
conn->nb_buf.len = len;
|
||||
conn->nb_buf.idx = 0;
|
||||
}
|
||||
if (conn->nb_buf.idx < conn->nb_buf.len) {
|
||||
/* Read from conn */
|
||||
ret = rw_iiod_buff(desc, conn, &conn->nb_buf, IIOD_RD);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
}
|
||||
/* Write to dev */
|
||||
ret = desc->ops.write_buffer(&ctx, conn->cmd_data.device,
|
||||
conn->nb_buf.buf, conn->nb_buf.len);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
conn->cmd_data.bytes_count -= conn->nb_buf.len;
|
||||
conn->nb_buf.len = 0;
|
||||
if (conn->cmd_data.bytes_count)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t iiod_run_cmd(struct iiod_desc *desc,
|
||||
struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx = IIOD_CTX(desc, conn);
|
||||
struct comand_desc *data = &conn->cmd_data;
|
||||
struct iiod_attr attr = {
|
||||
.type = data->type,
|
||||
.name = data->attr,
|
||||
.channel = data->channel
|
||||
};
|
||||
int32_t ret;
|
||||
|
||||
switch (data->cmd) {
|
||||
case IIOD_CMD_HELP:
|
||||
case IIOD_CMD_TIMEOUT:
|
||||
case IIOD_CMD_OPEN:
|
||||
case IIOD_CMD_CLOSE:
|
||||
case IIOD_CMD_SETTRIG:
|
||||
case IIOD_CMD_SET:
|
||||
if (data->cmd == IIOD_CMD_OPEN) {
|
||||
conn->mask = data->mask;
|
||||
if (data->cyclic)
|
||||
conn->is_cyclic_buffer = true;
|
||||
}
|
||||
if (data->cmd == IIOD_CMD_CLOSE)
|
||||
/* Set is_cyclic_buffer to false every time the device is closed */
|
||||
conn->is_cyclic_buffer = false;
|
||||
conn->res.val = call_op(&desc->ops, data, &ctx);
|
||||
conn->res.write_val = 1;
|
||||
break;
|
||||
case IIOD_CMD_EXIT:
|
||||
conn->res.val = 0;
|
||||
conn->res.write_val = 1;
|
||||
|
||||
return -ENOTCONN;
|
||||
case IIOD_CMD_PRINT:
|
||||
conn->res.val = desc->xml_len;
|
||||
conn->res.write_val = 1;
|
||||
conn->res.buf.buf = desc->xml;
|
||||
conn->res.buf.len = desc->xml_len;
|
||||
break;
|
||||
case IIOD_CMD_VERSION:
|
||||
conn->res.buf.buf = IIOD_VERSION;
|
||||
conn->res.buf.len = IIOD_VERSION_LEN;
|
||||
break;
|
||||
case IIOD_CMD_READ:
|
||||
case IIOD_CMD_GETTRIG:
|
||||
if (data->cmd == IIOD_CMD_READ)
|
||||
ret = desc->ops.read_attr(&ctx, data->device, &attr,
|
||||
conn->payload_buf,
|
||||
conn->payload_buf_len);
|
||||
else
|
||||
ret = desc->ops.get_trigger(&ctx, data->device,
|
||||
conn->payload_buf,
|
||||
conn->payload_buf_len);
|
||||
conn->res.val = ret;
|
||||
conn->res.write_val = 1;
|
||||
if (!NO_OS_IS_ERR_VALUE(ret)) {
|
||||
conn->res.buf.buf = conn->payload_buf;
|
||||
conn->res.buf.len = ret;
|
||||
}
|
||||
break;
|
||||
case IIOD_CMD_WRITE:
|
||||
conn->payload_buf[data->bytes_count] = '\0';
|
||||
ret = desc->ops.write_attr(&ctx, data->device, &attr,
|
||||
conn->payload_buf,
|
||||
data->bytes_count);
|
||||
conn->nb_buf.len = 0;
|
||||
conn->res.val = ret;
|
||||
conn->res.write_val = 1;
|
||||
break;
|
||||
case IIOD_CMD_READBUF:
|
||||
conn->res.write_val = 1;
|
||||
ret = desc->ops.refill_buffer(&ctx, data->device);
|
||||
if (NO_OS_IS_ERR_VALUE(ret)) {
|
||||
conn->res.val = ret;
|
||||
break;
|
||||
}
|
||||
conn->res.val = data->bytes_count;
|
||||
ret = snprintf(conn->buf_mask, 10, "%08"PRIx32, conn->mask);
|
||||
conn->res.buf.buf = conn->buf_mask;
|
||||
conn->res.buf.len = ret;
|
||||
break;
|
||||
case IIOD_CMD_WRITEBUF:
|
||||
conn->res.val = data->bytes_count;
|
||||
conn->res.write_val = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t iiod_read_line(struct iiod_desc *desc,
|
||||
struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx = {
|
||||
.instance = desc->app_instance,
|
||||
.conn = conn->conn
|
||||
};
|
||||
int32_t ret;
|
||||
char *ch;
|
||||
|
||||
while (conn->parser_idx < IIOD_PARSER_MAX_BUF_SIZE - 1) {
|
||||
ch = conn->parser_buf + conn->parser_idx;
|
||||
ret = desc->ops.recv(&ctx, (uint8_t *)ch, 1);
|
||||
if (ret == -EAGAIN || ret == 0)
|
||||
return -EAGAIN;
|
||||
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
goto end;
|
||||
|
||||
if (conn->parser_idx == 0 && (*ch == '\n' || *ch == '\r'))
|
||||
continue ;
|
||||
|
||||
++conn->parser_idx;
|
||||
if (*ch == '\n') {
|
||||
conn->parser_buf[conn->parser_idx] = '\0';
|
||||
ret = 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
end:
|
||||
conn->parser_idx = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function will return SUCCESS when a state was processed.
|
||||
* If a state is still in processing state, it will return -EAGAIN.
|
||||
* If other error occur. E.g. Connection errors, they are returned and
|
||||
* the connection must be cleaned up.
|
||||
*/
|
||||
static int32_t iiod_run_state(struct iiod_desc *desc,
|
||||
struct iiod_conn_priv *conn)
|
||||
{
|
||||
struct iiod_ctx ctx = {
|
||||
.instance = desc->app_instance,
|
||||
.conn = conn->conn
|
||||
};
|
||||
int32_t ret;
|
||||
|
||||
switch (conn->state) {
|
||||
case IIOD_READING_LINE:
|
||||
/* Read input data until \n. I/O Calls */
|
||||
ret = iiod_read_line(desc, conn);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
/* Fill struct comand_desc with data from line. No I/O */
|
||||
ret = iiod_parse_line(conn->parser_buf, &conn->cmd_data,
|
||||
&conn->strtok_ctx);
|
||||
if (NO_OS_IS_ERR_VALUE(ret)) {
|
||||
/* Parsing line failed */
|
||||
conn->res.write_val = 1;
|
||||
conn->res.val = ret;
|
||||
conn->state = IIOD_WRITING_CMD_RESULT;
|
||||
} else if (conn->cmd_data.cmd == IIOD_CMD_WRITE) {
|
||||
/* Special case. Attribute needs to be read */
|
||||
conn->nb_buf.buf = conn->payload_buf;
|
||||
conn->nb_buf.len = conn->cmd_data.bytes_count;
|
||||
conn->nb_buf.idx = 0;
|
||||
conn->state = IIOD_READING_WRITE_DATA;
|
||||
} else {
|
||||
conn->state = IIOD_RUNNING_CMD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case IIOD_RUNNING_CMD:
|
||||
/* Execute or call necessary ops depending on cmd. No I/O */
|
||||
ret = iiod_run_cmd(desc, conn);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
conn->state = IIOD_WRITING_CMD_RESULT;
|
||||
|
||||
return 0;
|
||||
case IIOD_WRITING_CMD_RESULT:
|
||||
/* Write result or the length of data to be sent*/
|
||||
if (conn->res.write_val) {
|
||||
if (conn->nb_buf.len == 0) {
|
||||
conn->nb_buf.buf = conn->parser_buf;
|
||||
ret = sprintf(conn->nb_buf.buf, "%"PRIi32,
|
||||
conn->res.val);
|
||||
conn->nb_buf.len = ret;
|
||||
conn->nb_buf.idx = 0;
|
||||
}
|
||||
/* Non-blocking. Will enter here until val is sent */
|
||||
if (conn->nb_buf.idx < conn->nb_buf.len) {
|
||||
ret = rw_iiod_buff(desc, conn, &conn->nb_buf,
|
||||
IIOD_WR | IIOD_ENDL);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Send buf from result. Non blocking */
|
||||
if (conn->res.buf.buf &&
|
||||
conn->res.buf.idx < conn->res.buf.len) {
|
||||
ret = rw_iiod_buff(desc, conn, &conn->res.buf,
|
||||
IIOD_WR | IIOD_ENDL);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (conn->cmd_data.cmd != IIOD_CMD_READBUF &&
|
||||
conn->cmd_data.cmd != IIOD_CMD_WRITEBUF) {
|
||||
if (conn->is_cyclic_buffer && conn->cmd_data.cmd != IIOD_CMD_OPEN)
|
||||
conn->state = IIOD_PUSH_CYCLIC_BUFFER;
|
||||
else
|
||||
conn->state = IIOD_LINE_DONE;
|
||||
} else {
|
||||
/* Preapre for IIOD_RW_BUF state */
|
||||
memset(&conn->nb_buf, 0, sizeof(conn->nb_buf));
|
||||
conn->state = IIOD_RW_BUF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case IIOD_RW_BUF:
|
||||
/* IIOD_CMD_READBUF and IIOD_CMD_WRITEBUF special case */
|
||||
/* Non blocking read/write until all data is processed */
|
||||
if (conn->cmd_data.cmd == IIOD_CMD_READBUF)
|
||||
ret = do_read_buff(desc, conn);
|
||||
else {
|
||||
ret = do_write_buff(desc, conn);
|
||||
if (ret == 0) {
|
||||
conn->res.write_val = 1;
|
||||
ret = desc->ops.push_buffer(&ctx,
|
||||
conn->cmd_data.device);
|
||||
if (NO_OS_IS_ERR_VALUE(ret)) {
|
||||
conn->res.val = ret;
|
||||
conn->state = IIOD_LINE_DONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
memset(&conn->res.buf, 0, sizeof(conn->res.buf));
|
||||
conn->res.val = conn->cmd_data.bytes_count;
|
||||
conn->cmd_data.cmd = IIOD_CMD_PRINT;
|
||||
conn->state = IIOD_WRITING_CMD_RESULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
conn->state = IIOD_LINE_DONE;
|
||||
|
||||
return 0;
|
||||
case IIOD_READING_WRITE_DATA:
|
||||
/* Read attribute */
|
||||
ret = rw_iiod_buff(desc, conn, &conn->nb_buf, IIOD_RD);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
conn->state = IIOD_RUNNING_CMD;
|
||||
|
||||
return 0;
|
||||
case IIOD_PUSH_CYCLIC_BUFFER:
|
||||
/* Push puffer to IIO application */
|
||||
ret = desc->ops.push_buffer(&ctx,
|
||||
conn->cmd_data.device);
|
||||
/* If an error was encountered, close connection */
|
||||
if (NO_OS_IS_ERR_VALUE(ret)) {
|
||||
conn->res.val = ret;
|
||||
desc->ops.close(&ctx, conn->cmd_data.device);
|
||||
conn->state = IIOD_LINE_DONE;
|
||||
conn->is_cyclic_buffer = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read data from the client to verify whether a close command has been sent */
|
||||
ret = iiod_read_line(desc, conn);
|
||||
if (NO_OS_IS_ERR_VALUE(ret))
|
||||
return 0;
|
||||
|
||||
/* Fill struct comand_desc with data from line */
|
||||
ret = iiod_parse_line(conn->parser_buf, &conn->cmd_data,
|
||||
&conn->strtok_ctx);
|
||||
if (!NO_OS_IS_ERR_VALUE(ret) && conn->cmd_data.cmd == IIOD_CMD_CLOSE) {
|
||||
/* Exit this state only if a close command is received
|
||||
All other commands will be ignored.
|
||||
*/
|
||||
conn->nb_buf.len = 0;
|
||||
conn->state = IIOD_RUNNING_CMD;
|
||||
conn->is_cyclic_buffer = false;
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/* Should never get here */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t iiod_conn_step(struct iiod_desc *desc, uint32_t conn_id)
|
||||
{
|
||||
struct iiod_conn_priv *conn;
|
||||
int32_t ret;
|
||||
|
||||
if (!desc || conn_id > IIOD_MAX_CONNECTIONS ||
|
||||
!desc->conns[conn_id].used)
|
||||
return -EINVAL;
|
||||
|
||||
conn = &desc->conns[conn_id];
|
||||
do {
|
||||
ret = iiod_run_state(desc, conn);
|
||||
if (ret == -EAGAIN)
|
||||
return ret;
|
||||
if (NO_OS_IS_ERR_VALUE(ret) || conn->state == IIOD_LINE_DONE)
|
||||
break;
|
||||
//The loop will continue because the state was changed.
|
||||
} while (true);
|
||||
|
||||
conn_clean_state(conn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user