2040 lines
53 KiB
C
2040 lines
53 KiB
C
/***************************************************************************//**
|
|
* @file iio.c
|
|
* @brief Implementation of iio.
|
|
* @author Cristian Pop (cristian.pop@analog.com)
|
|
* @author Mihail Chindris (mihail.chindris@analog.com)
|
|
********************************************************************************
|
|
* Copyright 2019(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 "iio.h"
|
|
#include "iio_types.h"
|
|
#include "iiod.h"
|
|
#include "ctype.h"
|
|
#include "no_os_util.h"
|
|
#include "no_os_list.h"
|
|
#include "no_os_error.h"
|
|
#include "no_os_uart.h"
|
|
#include "no_os_error.h"
|
|
#include "no_os_alloc.h"
|
|
#include "no_os_circular_buffer.h"
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef NO_OS_NETWORKING
|
|
#include "no_os_delay.h"
|
|
#include "tcp_socket.h"
|
|
#endif
|
|
|
|
#ifdef NO_OS_LWIP_NETWORKING
|
|
#include "no_os_delay.h"
|
|
#include "tcp_socket.h"
|
|
#include "lwip_socket.h"
|
|
#endif
|
|
|
|
#define IIOD_PORT 30431
|
|
#define MAX_SOCKET_TO_HANDLE 10
|
|
#define REG_ACCESS_ATTRIBUTE "direct_reg_access"
|
|
#define IIOD_CONN_BUFFER_SIZE 0x1000
|
|
#define NO_TRIGGER (uint32_t)-1
|
|
|
|
#define NO_OS_STRINGIFY(x) #x
|
|
#define NO_OS_TOSTRING(x) NO_OS_STRINGIFY(x)
|
|
|
|
static char uart_buff[IIOD_CONN_BUFFER_SIZE];
|
|
|
|
static char header[] =
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
"<!DOCTYPE context ["
|
|
"<!ELEMENT context (device | context-attribute)*>"
|
|
"<!ELEMENT context-attribute EMPTY>"
|
|
"<!ELEMENT device (channel | attribute | debug-attribute | buffer-attribute)*>"
|
|
"<!ELEMENT channel (scan-element?, attribute*)>"
|
|
"<!ELEMENT attribute EMPTY>"
|
|
"<!ELEMENT scan-element EMPTY>"
|
|
"<!ELEMENT debug-attribute EMPTY>"
|
|
"<!ELEMENT buffer-attribute EMPTY>"
|
|
"<!ATTLIST context name CDATA #REQUIRED description CDATA #IMPLIED>"
|
|
"<!ATTLIST context-attribute name CDATA #REQUIRED value CDATA #REQUIRED>"
|
|
"<!ATTLIST device id CDATA #REQUIRED name CDATA #IMPLIED>"
|
|
"<!ATTLIST channel id CDATA #REQUIRED type (input|output) #REQUIRED name CDATA #IMPLIED>"
|
|
"<!ATTLIST scan-element index CDATA #REQUIRED format CDATA #REQUIRED scale CDATA #IMPLIED>"
|
|
"<!ATTLIST attribute name CDATA #REQUIRED filename CDATA #IMPLIED>"
|
|
"<!ATTLIST debug-attribute name CDATA #REQUIRED>"
|
|
"<!ATTLIST buffer-attribute name CDATA #REQUIRED>"
|
|
"]>"
|
|
"<context name=\"xml\" description=\"no-OS/projects/"
|
|
NO_OS_TOSTRING(NO_OS_PROJECT)" "
|
|
NO_OS_TOSTRING(NO_OS_VERSION)"\" >";
|
|
static char header_end[] = "</context>";
|
|
|
|
static const char * const iio_chan_type_string[] = {
|
|
[IIO_VOLTAGE] = "voltage",
|
|
[IIO_CURRENT] = "current",
|
|
[IIO_ALTVOLTAGE] = "altvoltage",
|
|
[IIO_ANGL_VEL] = "anglvel",
|
|
[IIO_TEMP] = "temp",
|
|
[IIO_CAPACITANCE] = "capacitance",
|
|
[IIO_ACCEL] = "accel",
|
|
[IIO_RESISTANCE] = "resistance",
|
|
[IIO_MAGN] = "magn",
|
|
[IIO_INCLI] = "incli",
|
|
[IIO_VELOCITY] = "velocity",
|
|
[IIO_ANGL] = "angl",
|
|
[IIO_ROT] = "rot",
|
|
[IIO_COUNT] = "count",
|
|
[IIO_DELTA_ANGL] = "deltaangl",
|
|
[IIO_DELTA_VELOCITY] = "deltavelocity",
|
|
[IIO_WEIGHT] = "weight",
|
|
};
|
|
|
|
static const char * const iio_modifier_names[] = {
|
|
[IIO_MOD_X] = "x",
|
|
[IIO_MOD_Y] = "y",
|
|
[IIO_MOD_Z] = "z",
|
|
[IIO_MOD_TEMP_AMBIENT] = "ambient",
|
|
[IIO_MOD_PITCH] = "pitch",
|
|
[IIO_MOD_YAW] = "yaw",
|
|
[IIO_MOD_ROLL] = "roll",
|
|
};
|
|
|
|
/* Parameters used in show and store functions */
|
|
struct attr_fun_params {
|
|
void *dev_instance;
|
|
char *buf;
|
|
uint32_t len;
|
|
struct iio_ch_info *ch_info;
|
|
};
|
|
|
|
struct iio_buffer_priv {
|
|
/* Field visible by user */
|
|
struct iio_buffer public;
|
|
/** Buffer to read or write data. A reference will be found in buffer */
|
|
struct no_os_circular_buffer cb;
|
|
/* Buffer provide by user. */
|
|
int8_t *raw_buf;
|
|
/* Length of raw_buf */
|
|
uint32_t raw_buf_len;
|
|
/* Set when this devices has buffer */
|
|
bool initalized;
|
|
/* Set when no_os_calloc was used to initalize cb.buf */
|
|
bool allocated;
|
|
};
|
|
|
|
/**
|
|
* @struct iio_dev_priv
|
|
* @brief Links a physical device instance "void *dev_instance"
|
|
* with a "iio_device *iio" that describes capabilities of the device.
|
|
*/
|
|
struct iio_dev_priv {
|
|
/** Will be: iio:device[0...n] n beeing the count of registerd devices*/
|
|
char dev_id[MAX_DEV_ID];
|
|
/** Device name */
|
|
const char *name;
|
|
/** Physical instance of a device */
|
|
void *dev_instance;
|
|
/** Structure to be passed to callbacks */
|
|
struct iio_device_data dev_data;
|
|
/** Used to read debug attributes */
|
|
uint32_t active_reg_addr;
|
|
/** Device descriptor(describes channels and attributes) */
|
|
struct iio_device *dev_descriptor;
|
|
/* Structure storing buffer related fields */
|
|
struct iio_buffer_priv buffer;
|
|
/* Set to -1 when no trigger is set*/
|
|
uint32_t trig_idx;
|
|
};
|
|
|
|
/**
|
|
* @struct iio_trig_priv
|
|
* @brief Links a physical trigger instance "void *instance"
|
|
* with a "iio_trigger *descriptor" that describes capabilities of the trigger.
|
|
*/
|
|
struct iio_trig_priv {
|
|
/** Will be: iio:trigger[0...n] */
|
|
char id[MAX_TRIG_ID];
|
|
/** Trigger name */
|
|
char *name;
|
|
/** Physical instance of a trigger */
|
|
void *instance;
|
|
/** Trigger descriptor(describes type of trigger and its attributes) */
|
|
struct iio_trigger *descriptor;
|
|
/** Set to true when the triggering condition is met */
|
|
bool triggered;
|
|
};
|
|
|
|
struct iio_desc {
|
|
struct iiod_desc *iiod;
|
|
struct iiod_ops iiod_ops;
|
|
void *phy_desc;
|
|
char *xml_desc;
|
|
uint32_t xml_size;
|
|
struct iio_ctx_attr *ctx_attrs;
|
|
uint32_t nb_ctx_attr;
|
|
struct iio_dev_priv *devs;
|
|
uint32_t nb_devs;
|
|
struct iio_trig_priv *trigs;
|
|
uint32_t nb_trigs;
|
|
struct no_os_uart_desc *uart_desc;
|
|
int (*recv)(void *conn, uint8_t *buf, uint32_t len);
|
|
int (*send)(void *conn, uint8_t *buf, uint32_t len);
|
|
/* FIFO for socket descriptors */
|
|
struct no_os_circular_buffer *conns;
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
struct tcp_socket_desc *current_sock;
|
|
/* Instance of server socket */
|
|
struct tcp_socket_desc *server;
|
|
#endif
|
|
};
|
|
|
|
static inline int32_t _pop_conn(struct iio_desc *desc, uint32_t *conn_id)
|
|
{
|
|
uint32_t size;
|
|
|
|
no_os_cb_size(desc->conns, &size);
|
|
if (size < sizeof(uint32_t))
|
|
return -EAGAIN;
|
|
|
|
return no_os_cb_read(desc->conns, conn_id, sizeof(*conn_id));
|
|
}
|
|
|
|
static inline int32_t _push_conn(struct iio_desc *desc, uint32_t conn_id)
|
|
{
|
|
return no_os_cb_write(desc->conns, &conn_id, sizeof(conn_id));
|
|
}
|
|
|
|
static inline int32_t _nb_active_conns(struct iio_desc *desc)
|
|
{
|
|
uint32_t size;
|
|
|
|
no_os_cb_size(desc->conns, &size);
|
|
|
|
return size / sizeof(uint32_t);
|
|
}
|
|
|
|
|
|
static int iio_recv(struct iiod_ctx *ctx, uint8_t *buf, uint32_t len)
|
|
{
|
|
struct iio_desc *desc = ctx->instance;
|
|
|
|
return desc->recv(ctx->conn, buf, len);
|
|
}
|
|
|
|
static int iio_send(struct iiod_ctx *ctx, uint8_t *buf, uint32_t len)
|
|
{
|
|
struct iio_desc *desc = ctx->instance;
|
|
|
|
return desc->send(ctx->conn, buf, len);
|
|
}
|
|
|
|
static inline void _print_ch_id(char *buff, struct iio_channel *ch)
|
|
{
|
|
if (ch->modified) {
|
|
sprintf(buff, "%s_%s", iio_chan_type_string[ch->ch_type],
|
|
iio_modifier_names[ch->channel2]);
|
|
} else {
|
|
if (ch->indexed) {
|
|
if (ch->diferential)
|
|
sprintf(buff, "%s%d-%s%d", iio_chan_type_string[ch->ch_type],
|
|
(int)ch->channel, iio_chan_type_string[ch->ch_type],
|
|
(int)ch->channel2);
|
|
else
|
|
sprintf(buff, "%s%d", iio_chan_type_string[ch->ch_type],
|
|
(int)ch->channel);
|
|
} else {
|
|
sprintf(buff, "%s", iio_chan_type_string[ch->ch_type]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get channel ID from a list of channels.
|
|
* @param channel - Channel name.
|
|
* @param desc - Device descriptor
|
|
* @param ch_out - If "true" is output channel, if "false" is input channel.
|
|
* @return Channel ID, or negative value if attribute is not found.
|
|
*/
|
|
static inline struct iio_channel *iio_get_channel(const char *channel,
|
|
struct iio_device *desc, bool ch_out)
|
|
{
|
|
int16_t i = 0;
|
|
char ch_id[MAX_CHN_ID];
|
|
|
|
while (i < desc->num_ch) {
|
|
_print_ch_id(ch_id, &desc->channels[i]);
|
|
if (!strcmp(channel, ch_id) &&
|
|
(desc->channels[i].ch_out == ch_out))
|
|
return &desc->channels[i];
|
|
i++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Find interface with "device_name".
|
|
* @param device_name - Device name.
|
|
* @param iio_dev_privs - List of interfaces.
|
|
* @return Interface pointer if interface is found, NULL otherwise.
|
|
*/
|
|
static struct iio_dev_priv *get_iio_device(struct iio_desc *desc,
|
|
const char *device_name)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < desc->nb_devs; i++) {
|
|
if (strcmp(desc->devs[i].dev_id, device_name) == 0)
|
|
return &desc->devs[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Find interface with "trigger_id".
|
|
* @param trigger_id - Trigger id (trigger0, trigger1, etc.).
|
|
* @param iio_trig_privs - List of interfaces.
|
|
* @return Interface pointer if interface is found, NULL otherwise.
|
|
*/
|
|
static struct iio_trig_priv *get_iio_trig_device(struct iio_desc *desc,
|
|
const char *trigger_id)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < desc->nb_trigs; i++) {
|
|
if (strcmp(desc->trigs[i].id, trigger_id) == 0)
|
|
return &desc->trigs[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets buffers count.
|
|
* @param ctx - IIO instance and conn instance.
|
|
* @param device - String containing device name.
|
|
* @param buffers_count - Value to be set.
|
|
* @return Positive if index was set, negative if not.
|
|
*/
|
|
static int iio_set_buffers_count(struct iiod_ctx *ctx, const char *device,
|
|
uint32_t buffers_count)
|
|
{
|
|
struct iio_desc *desc = ctx->instance;
|
|
|
|
if (!get_iio_device(desc, device))
|
|
return -ENODEV;
|
|
|
|
/* Our implementation uses a circular buffer to send/receive data so
|
|
* only 1 is a valid value.
|
|
*/
|
|
if (buffers_count != 1)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Read all attributes from an attribute list.
|
|
* @param device - Physical instance of a device.
|
|
* @param buf - Buffer where values are read.
|
|
* @param len - Maximum length of value to be stored in buf.
|
|
* @param channel - Channel properties.
|
|
* @param attributes - List of attributes to be read.
|
|
* @return Number of bytes read or negative value in case of error.
|
|
*/
|
|
static int iio_read_all_attr(struct attr_fun_params *params,
|
|
struct iio_attribute *attributes)
|
|
{
|
|
/* TODO Not sure if working corectly */
|
|
return -EINVAL;
|
|
|
|
#if 0
|
|
int16_t i = 0, j = 0;
|
|
char local_buf[256];
|
|
int attr_length;
|
|
uint32_t *pattr_length;
|
|
|
|
while (attributes[i].name) {
|
|
attr_length = attributes[i].show(params->dev_instance,
|
|
local_buf, params->len,
|
|
params->ch_info,
|
|
attributes[i].priv);
|
|
if (NO_OS_IS_ERR_VALUE(attr_length))
|
|
attr_length = snprintf(local_buf, params->len, "%d",
|
|
attr_length);
|
|
|
|
attr_length += 1;//Add '\0' to the count
|
|
pattr_length = (uint32_t *)(params->buf + j);
|
|
if (j + 4 > params->len)
|
|
return -EINVAL;
|
|
*pattr_length = no_os_bswap_constant_32(attr_length);
|
|
j += 4;
|
|
if (attr_length >= 0) {
|
|
if (attr_length + j > params->len)
|
|
return -EINVAL;
|
|
sprintf(params->buf + j, "%s", local_buf);
|
|
if (attr_length & 0x3) /* multiple of 4 */
|
|
attr_length = ((attr_length >> 2) + 1) << 2;
|
|
j += attr_length;
|
|
}
|
|
i++;
|
|
}
|
|
if (j == 0)
|
|
return -ENOENT;
|
|
|
|
return j;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Write all attributes from an attribute list.
|
|
* @param device - Physical instance of a device.
|
|
* @param buf - Values to be written.
|
|
* @param len - Length of buf.
|
|
* @param channel - Channel properties.
|
|
* @param attributes - List of attributes to be written.
|
|
* @return Number of written bytes or negative value in case of error.
|
|
*/
|
|
static int iio_write_all_attr(struct attr_fun_params *params,
|
|
struct iio_attribute *attributes)
|
|
{
|
|
/* TODO Not sure if working corectly */
|
|
return -EINVAL;
|
|
|
|
#if 0
|
|
int16_t i = 0, j = 0;
|
|
int16_t attr_length;
|
|
|
|
while (attributes[i].name) {
|
|
attr_length = no_os_bswap_constant_32((uint32_t)(params->buf + j));
|
|
j += 4;
|
|
attributes[i].store(params->dev_instance, (params->buf + j),
|
|
attr_length, params->ch_info,
|
|
attributes[i].priv);
|
|
j += attr_length;
|
|
if (j & 0x3)
|
|
j = ((j >> 2) + 1) << 2;
|
|
i++;
|
|
}
|
|
|
|
if (params->len == 0)
|
|
return -ENOENT;
|
|
|
|
return params->len;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Read/write attribute.
|
|
* @param params - Structure describing parameters for store and show functions
|
|
* @param attributes - Array of attributes.
|
|
* @param attr_name - Attribute name to be modified
|
|
* @param is_write -If it has value "1", writes attribute, otherwise reads
|
|
* attribute.
|
|
* @return Length of chars written/read or negative value in case of error.
|
|
*/
|
|
static int iio_rd_wr_attribute(struct attr_fun_params *params,
|
|
struct iio_attribute *attributes,
|
|
const char *attr_name,
|
|
bool is_write)
|
|
{
|
|
int16_t i = 0;
|
|
|
|
/* Search attribute */
|
|
while (attributes[i].name) {
|
|
if (!strcmp(attr_name, attributes[i].name))
|
|
break;
|
|
i++;
|
|
}
|
|
|
|
if (!attributes[i].name)
|
|
return -ENOENT;
|
|
|
|
if (is_write) {
|
|
if (!attributes[i].store)
|
|
return -ENOENT;
|
|
|
|
return attributes[i].store(params->dev_instance, params->buf,
|
|
params->len, params->ch_info,
|
|
attributes[i].priv);
|
|
} else {
|
|
if (!attributes[i].show)
|
|
return -ENOENT;
|
|
return attributes[i].show(params->dev_instance, params->buf,
|
|
params->len, params->ch_info,
|
|
attributes[i].priv);
|
|
}
|
|
}
|
|
|
|
/* Read a device register. The register address to read is set on
|
|
* in desc->active_reg_addr in the function set_demo_reg_attr
|
|
*/
|
|
static int32_t debug_reg_read(struct iio_dev_priv *dev, char *buf, uint32_t len)
|
|
{
|
|
uint32_t value;
|
|
int32_t ret;
|
|
|
|
value = 0;
|
|
ret = dev->dev_descriptor->debug_reg_read(dev->dev_instance,
|
|
dev->active_reg_addr,
|
|
&value);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
return snprintf(buf, len, "%"PRIu32"", value);
|
|
}
|
|
|
|
/* Flow of reading and writing registers. This is how iio works for
|
|
* direct_reg_access attribute:
|
|
* Read register:
|
|
* //Reg_addr in decimal
|
|
* reg_addr = "10";
|
|
* 1. debug_reg_write(dev, reg_addr, len);
|
|
* 2. debug_reg_read(dev, out_buf, out_len);
|
|
* Write register:
|
|
* sprintf(write_buf, "0x%x 0x%x", reg_addr, value);
|
|
* 1. debug_reg_write(dev, write_buf,len);
|
|
*/
|
|
static int32_t debug_reg_write(struct iio_dev_priv *dev, const char *buf,
|
|
uint32_t len)
|
|
{
|
|
uint32_t nb_filled;
|
|
uint32_t addr;
|
|
uint32_t value;
|
|
int32_t ret;
|
|
|
|
nb_filled = sscanf(buf, "0x%"PRIx32" 0x%"PRIx32"", &addr, &value);
|
|
if (nb_filled == 2) {
|
|
/* Write register */
|
|
ret = dev->dev_descriptor->debug_reg_write(dev->dev_instance,
|
|
addr, value);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
} else {
|
|
nb_filled = sscanf(buf, "%"PRIu32, &addr);
|
|
if (nb_filled == 1) {
|
|
dev->active_reg_addr = addr;
|
|
return len;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static int32_t __iio_str_parse(char *buf, int32_t *integer, int32_t *_fract,
|
|
int32_t *_fract_scale, bool scale_db)
|
|
{
|
|
char *p;
|
|
|
|
p = strtok(buf, ".");
|
|
if (p == NULL)
|
|
return -EINVAL;
|
|
|
|
*integer = strtol(p, NULL, 0);
|
|
|
|
if (scale_db) {
|
|
p = strtok(NULL, "db");
|
|
if (p == NULL)
|
|
p = strtok(NULL, " db");
|
|
} else
|
|
p = strtok(NULL, "\n");
|
|
|
|
if (p == NULL)
|
|
return -EINVAL;
|
|
|
|
*_fract = strtol(p, NULL, 10);
|
|
|
|
/* Handle leading zeroes */
|
|
while (*p++ == '0' && *_fract > 0)
|
|
*_fract_scale *= 10;
|
|
|
|
/* Handle values between -1 and 0 */
|
|
if (*integer == 0 && buf[0] == '-')
|
|
*_fract *= -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t _iio_fract_interpret(int32_t fract, int32_t subunits)
|
|
{
|
|
int32_t temp;
|
|
int32_t mult = 1;
|
|
|
|
if (fract < 0) {
|
|
mult = -1;
|
|
fract = -fract;
|
|
}
|
|
|
|
/* Divide to nearest subunit-scale if fract part is more than subunit */
|
|
while (fract >= subunits)
|
|
fract = NO_OS_DIV_ROUND_CLOSEST(fract, 10);
|
|
|
|
temp = fract;
|
|
|
|
while ((subunits != 0) || (temp != 0)) {
|
|
temp /= 10;
|
|
subunits /= 10;
|
|
if (!temp)
|
|
break;
|
|
if (subunits <= 1)
|
|
fract /= 10;
|
|
}
|
|
|
|
return fract * subunits * mult;
|
|
}
|
|
|
|
int32_t iio_parse_value(char *buf, enum iio_val fmt, int32_t *val,
|
|
int32_t *val2)
|
|
{
|
|
int32_t ret = 0;
|
|
int32_t integer, _fract = 0, _fract_scale = 1;
|
|
char ch;
|
|
|
|
switch (fmt) {
|
|
case IIO_VAL_INT:
|
|
integer = strtol(buf, NULL, 0);
|
|
break;
|
|
case IIO_VAL_INT_PLUS_MICRO_DB:
|
|
ret = __iio_str_parse(buf, &integer, &_fract,
|
|
&_fract_scale, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
_fract = _iio_fract_interpret(_fract, 1000000 / _fract_scale);
|
|
break;
|
|
case IIO_VAL_INT_PLUS_MICRO:
|
|
ret = __iio_str_parse(buf, &integer, &_fract,
|
|
&_fract_scale, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
_fract = _iio_fract_interpret(_fract, 1000000 / _fract_scale);
|
|
break;
|
|
case IIO_VAL_INT_PLUS_NANO:
|
|
ret = __iio_str_parse(buf, &integer, &_fract,
|
|
&_fract_scale, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
_fract = _iio_fract_interpret(_fract,
|
|
1000000000 / _fract_scale);
|
|
break;
|
|
case IIO_VAL_FRACTIONAL:
|
|
ret = __iio_str_parse(buf, &integer, &_fract,
|
|
&_fract_scale, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
case IIO_VAL_CHAR:
|
|
if (sscanf(buf, "%c", &ch) != 1)
|
|
return -EINVAL;
|
|
integer = ch;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (val)
|
|
*val = integer;
|
|
if (val2)
|
|
*val2 = _fract;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int iio_format_value(char *buf, uint32_t len, enum iio_val fmt,
|
|
int32_t size, int32_t *vals)
|
|
{
|
|
int64_t tmp;
|
|
int32_t integer, fractional;
|
|
bool dB = false;
|
|
int32_t i = 0;
|
|
uint32_t l = 0;
|
|
|
|
switch (fmt) {
|
|
case IIO_VAL_INT:
|
|
return snprintf(buf, len, "%"PRIi32"", vals[0]);
|
|
case IIO_VAL_INT_PLUS_MICRO_DB:
|
|
dB = true;
|
|
/* intentional fall through */
|
|
case IIO_VAL_INT_PLUS_MICRO:
|
|
return snprintf(buf, len, "%s%"PRIi32".%06"PRIu32"%s",
|
|
vals[1] < 0 ? "-" : "", vals[0],
|
|
(uint32_t)vals[1], dB ? " dB" : "");
|
|
case IIO_VAL_INT_PLUS_NANO:
|
|
return snprintf(buf, len, "%s%"PRIi32".%09"PRIu32"",
|
|
vals[1] < 0 ? "-" : "", vals[0],
|
|
(uint32_t)vals[1]);
|
|
case IIO_VAL_FRACTIONAL:
|
|
tmp = no_os_div_s64((int64_t)vals[0] * 1000000000LL, vals[1]);
|
|
fractional = vals[1];
|
|
integer = (int32_t)no_os_div_s64_rem(tmp, 1000000000, &fractional);
|
|
|
|
if (integer == 0 && fractional < 0)
|
|
return snprintf(buf, len, "-0.%09u", abs(fractional));
|
|
|
|
return snprintf(buf, len, "%"PRIi32".%09u", integer,
|
|
abs(fractional));
|
|
case IIO_VAL_FRACTIONAL_LOG2:
|
|
tmp = no_os_shift_right((int64_t)vals[0] * 1000000000LL, vals[1]);
|
|
integer = (int32_t)no_os_div_s64_rem(tmp, 1000000000LL, &fractional);
|
|
|
|
if (integer == 0 && fractional < 0)
|
|
return snprintf(buf, len, "-0.%09u", abs(fractional));
|
|
|
|
return snprintf(buf, len, "%"PRIi32".%09u", integer,
|
|
abs(fractional));
|
|
case IIO_VAL_INT_MULTIPLE: {
|
|
while (i < size) {
|
|
l += snprintf(&buf[l], len - l, "%"PRIi32" ", vals[i]);
|
|
if (l >= len)
|
|
break;
|
|
i++;
|
|
}
|
|
return l;
|
|
}
|
|
case IIO_VAL_CHAR:
|
|
return snprintf(buf, len, "%c", (char)vals[0]);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static struct iio_attribute *get_attributes(enum iio_attr_type type,
|
|
struct iio_dev_priv *dev,
|
|
struct iio_channel *ch)
|
|
{
|
|
switch (type) {
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
return dev->dev_descriptor->debug_attributes;
|
|
break;
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
return dev->dev_descriptor->attributes;
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
return dev->dev_descriptor->buffer_attributes;
|
|
break;
|
|
case IIO_ATTR_TYPE_CH_IN:
|
|
case IIO_ATTR_TYPE_CH_OUT:
|
|
return ch->attributes;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns trigger attributes.
|
|
* @param type - Attribute type.
|
|
* @param trig - Trigger instance.
|
|
* @return Attributes pointer if attributes exist, NULL otherwise.
|
|
*/
|
|
static struct iio_attribute *get_trig_attributes(enum iio_attr_type type,
|
|
struct iio_trig_priv *trig)
|
|
{
|
|
switch (type) {
|
|
/* Only device type attributes allowed for triggers */
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
return trig->descriptor->attributes;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Read global attribute of a device.
|
|
* @param ctx - IIO instance and conn instance
|
|
* @param device - String containing device name.
|
|
* @param attr - String containing attribute name.
|
|
* @param buf - Buffer where value is read.
|
|
* @param len - Maximum length of value to be stored in buf.
|
|
* @return Number of bytes read.
|
|
*/
|
|
static int iio_read_attr(struct iiod_ctx *ctx, const char *device,
|
|
struct iiod_attr *attr, char *buf, uint32_t len)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig_dev;
|
|
struct iio_ch_info ch_info;
|
|
struct iio_channel *ch = NULL;
|
|
struct attr_fun_params params;
|
|
struct iio_attribute *attributes;
|
|
int8_t ch_out;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
|
|
/* If IIO device with given name is found, handle reading of attributes */
|
|
if (dev) {
|
|
if (attr->type == IIO_ATTR_TYPE_DEBUG &&
|
|
strcmp(attr->name, REG_ACCESS_ATTRIBUTE) == 0) {
|
|
if (dev->dev_descriptor->debug_reg_read)
|
|
return debug_reg_read(dev, buf, len);
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (attr->channel[0] != '\0') {
|
|
ch_out = attr->type == IIO_ATTR_TYPE_CH_OUT ? 1 : 0;
|
|
ch = iio_get_channel(attr->channel, dev->dev_descriptor,
|
|
ch_out);
|
|
if (!ch)
|
|
return -ENOENT;
|
|
ch_info.ch_out = ch_out;
|
|
ch_info.ch_num = ch->channel;
|
|
ch_info.type = ch->ch_type;
|
|
ch_info.differential = ch->diferential;
|
|
ch_info.address = ch->address;
|
|
params.ch_info = &ch_info;
|
|
} else {
|
|
params.ch_info = NULL;
|
|
}
|
|
|
|
params.buf = buf;
|
|
params.len = len;
|
|
params.dev_instance = dev->dev_instance;
|
|
attributes = get_attributes(attr->type, dev, ch);
|
|
if (!strcmp(attr->name, ""))
|
|
return iio_read_all_attr(¶ms, attributes);
|
|
return iio_rd_wr_attribute(¶ms, attributes, attr->name, 0);
|
|
}
|
|
|
|
/* IIO device with given name is not found, verify if it corresponds to a trigger */
|
|
trig_dev = get_iio_trig_device(ctx->instance, device);
|
|
|
|
/* If IIO trigger with given name is found, handle reading of attributes */
|
|
if (trig_dev) {
|
|
params.ch_info = NULL; /* Triggers cannot have channels */
|
|
params.buf = buf;
|
|
params.len = len;
|
|
params.dev_instance = trig_dev->instance;
|
|
attributes = get_trig_attributes(attr->type, trig_dev);
|
|
if (!strcmp(attr->name, ""))
|
|
return iio_read_all_attr(¶ms, attributes);
|
|
return iio_rd_wr_attribute(¶ms, attributes, attr->name, 0);
|
|
}
|
|
|
|
/* No device and no trigger with given name were found */
|
|
return -ENODEV;
|
|
}
|
|
|
|
/**
|
|
* @brief Write global attribute of a device.
|
|
* @param device - String containing device name.
|
|
* @param ctx - IIO instance and conn instance
|
|
* @param attr - String containing attribute name.
|
|
* @param buf - Value to be written.
|
|
* @param len - Length of data.
|
|
* @return Number of written bytes.
|
|
*/
|
|
static int iio_write_attr(struct iiod_ctx *ctx, const char *device,
|
|
struct iiod_attr *attr, char *buf, uint32_t len)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig_dev;
|
|
struct attr_fun_params params;
|
|
struct iio_attribute *attributes;
|
|
struct iio_ch_info ch_info;
|
|
struct iio_channel *ch = NULL;
|
|
int8_t ch_out;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
|
|
/* If IIO device with given name is found, handle writing of attributes */
|
|
if (dev) {
|
|
|
|
if (attr->type == IIO_ATTR_TYPE_DEBUG &&
|
|
strcmp(attr->name, REG_ACCESS_ATTRIBUTE) == 0) {
|
|
if (dev->dev_descriptor->debug_reg_write)
|
|
return debug_reg_write(dev, buf, len);
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (attr->channel[0] != '\0') {
|
|
ch_out = attr->type == IIO_ATTR_TYPE_CH_OUT ? 1 : 0;
|
|
ch = iio_get_channel(attr->channel, dev->dev_descriptor,
|
|
ch_out);
|
|
if (!ch)
|
|
return -ENOENT;
|
|
|
|
ch_info.ch_out = ch_out;
|
|
ch_info.ch_num = ch->channel;
|
|
ch_info.type = ch->ch_type;
|
|
ch_info.differential = ch->diferential;
|
|
ch_info.address = ch->address;
|
|
params.ch_info = &ch_info;
|
|
} else {
|
|
params.ch_info = NULL;
|
|
}
|
|
|
|
params.buf = (char *)buf;
|
|
params.len = len;
|
|
params.dev_instance = dev->dev_instance;
|
|
attributes = get_attributes(attr->type, dev, ch);
|
|
if (!strcmp(attr->name, ""))
|
|
return iio_write_all_attr(¶ms, attributes);
|
|
return iio_rd_wr_attribute(¶ms, attributes, attr->name, 1);
|
|
}
|
|
|
|
/* IIO device with given name is not found, verify if it corresponds to a trigger */
|
|
trig_dev = get_iio_trig_device(ctx->instance, device);
|
|
|
|
/* If IIO trigger with given name is found, handle writing of attributes */
|
|
if (trig_dev) {
|
|
params.ch_info = NULL; /* Triggers cannot have channels */
|
|
params.buf = (char *)buf;
|
|
params.len = len;
|
|
params.dev_instance = trig_dev->instance;
|
|
attributes = get_trig_attributes(attr->type, trig_dev);
|
|
if (!strcmp(attr->name, ""))
|
|
return iio_read_all_attr(¶ms, attributes);
|
|
return iio_rd_wr_attribute(¶ms, attributes, attr->name, 1);
|
|
}
|
|
|
|
/* No device and no trigger with given name were found */
|
|
return -ENODEV;
|
|
}
|
|
|
|
/**
|
|
* @brief Searches for trigger id and returns trigger index.
|
|
* @param desc - IIO descriptor.
|
|
* @param id - Trigger id (trigger0, trigger1, etc.).
|
|
* @return Trigger index. NO_TRIGGER in case trigger is not found.
|
|
*/
|
|
static uint32_t iio_get_trig_idx_by_id(struct iio_desc *desc, const char *id)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!id)
|
|
return NO_TRIGGER;
|
|
|
|
for (i = 0; i < desc->nb_trigs; i++)
|
|
if (strcmp(desc->trigs[i].id, id) == 0)
|
|
return i;
|
|
|
|
return NO_TRIGGER;
|
|
}
|
|
|
|
/**
|
|
* @brief Searches for trigger name and returns trigger index.
|
|
* @param desc - IIO descriptor.
|
|
* @param name - Trigger name.
|
|
* @return Trigger index. NO_TRIGGER in case trigger is not found.
|
|
*/
|
|
static uint32_t iio_get_trig_idx_by_name(struct iio_desc *desc,
|
|
const char *name)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!name)
|
|
return NO_TRIGGER;
|
|
|
|
for (i = 0; i < desc->nb_trigs; i++)
|
|
if (strcmp(desc->trigs[i].name, name) == 0)
|
|
return i;
|
|
|
|
return NO_TRIGGER;
|
|
}
|
|
|
|
/**
|
|
* @brief Searches for active trigger of the given device and returns trigger name.
|
|
* @param ctx - IIO instance and conn instance.
|
|
* @param device - String containing device name.
|
|
* @param trigger - Trigger name to be returned.
|
|
* @param len - Maximum length of value to be stored in name.
|
|
* @return Number of bytes written in name.
|
|
*/
|
|
static int iio_get_trigger(struct iiod_ctx *ctx, const char *device,
|
|
char *trigger, uint32_t len)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig;
|
|
struct iio_desc *desc = ctx->instance;
|
|
|
|
if (!desc->nb_trigs)
|
|
return -ENOENT;
|
|
|
|
trig = get_iio_trig_device(desc, device);
|
|
/* Device is a trigger and triggers cannot have other triggers */
|
|
if (trig)
|
|
return -ENOENT;
|
|
|
|
dev = get_iio_device(desc, device);
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
if (dev->trig_idx == NO_TRIGGER) {
|
|
trigger[0] = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
return snprintf(trigger, len, "%s", desc->trigs[dev->trig_idx].name);
|
|
}
|
|
|
|
/**
|
|
* @brief Searches for given trigger id for the given device and if found, it
|
|
* sets the trigger.
|
|
* @param ctx - IIO instance and conn instance.
|
|
* @param device - String containing device name.
|
|
* @param trigger - Trigger id to be set.
|
|
* @param len - Maximum length of value to be returned.
|
|
* @return Positive if index was set, negative if not.
|
|
*/
|
|
static int iio_set_trigger(struct iiod_ctx *ctx, const char *device,
|
|
const char *trigger, uint32_t len)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig;
|
|
uint32_t i;
|
|
struct iio_desc *desc = ctx->instance;
|
|
|
|
if (!desc->nb_trigs)
|
|
return -ENOENT;
|
|
|
|
trig = get_iio_trig_device(desc, device);
|
|
/* Device is a trigger and triggers cannot have other triggers */
|
|
if (trig)
|
|
return -ENOENT;
|
|
|
|
dev = get_iio_device(desc, device);
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
if (trigger[0] == '\0') {
|
|
dev->trig_idx = NO_TRIGGER;
|
|
return 0;
|
|
}
|
|
|
|
i = iio_get_trig_idx_by_id(desc, trigger);
|
|
if (i == NO_TRIGGER)
|
|
return -EINVAL;
|
|
|
|
dev->trig_idx = i;
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* @brief Asynchronous trigger processing routine.
|
|
* @param desc - IIO descriptor.
|
|
*/
|
|
static void iio_process_async_triggers(struct iio_desc *desc)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < desc->nb_devs; i++) {
|
|
dev = desc->devs + i;
|
|
if (dev->trig_idx == NO_TRIGGER)
|
|
continue;
|
|
|
|
if (!desc->trigs[dev->trig_idx].triggered)
|
|
continue;
|
|
|
|
if (dev->dev_descriptor->trigger_handler) {
|
|
dev->dev_descriptor->trigger_handler(&dev->dev_data);
|
|
desc->trigs[i].triggered = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Searches for trigger name and processes the trigger based on its
|
|
* type (sync or async with the interrupt).
|
|
* @param desc - IIO descriptor.
|
|
* @param trigger_name - Trigger name.
|
|
*
|
|
* @return ret - Result of the processing procedure.
|
|
*/
|
|
int iio_process_trigger_type(struct iio_desc *desc, char *trigger_name)
|
|
{
|
|
uint32_t i;
|
|
uint32_t trig_id;
|
|
struct iio_trig_priv *trig;
|
|
|
|
trig_id = iio_get_trig_idx_by_name(desc, trigger_name);
|
|
|
|
if (trig_id == NO_TRIGGER)
|
|
return -EINVAL;
|
|
|
|
struct iio_dev_priv *dev;
|
|
|
|
for (i = 0; i < desc->nb_devs; i++) {
|
|
dev = desc->devs + i;
|
|
if (dev->trig_idx == trig_id) {
|
|
trig = &desc->trigs[trig_id];
|
|
if (trig->descriptor->is_synchronous) {
|
|
if (dev->dev_descriptor->trigger_handler)
|
|
dev->dev_descriptor->trigger_handler(&dev->dev_data);
|
|
} else {
|
|
trig->triggered = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t bytes_per_scan(struct iio_channel *channels, uint32_t mask)
|
|
{
|
|
uint32_t cnt, i, length, largest = 1;
|
|
|
|
cnt = 0;
|
|
i = 0;
|
|
while (mask) {
|
|
if ((mask & 1)) {
|
|
length = channels[i].scan_type->storagebits / 8;
|
|
|
|
if (length > largest)
|
|
largest = length;
|
|
|
|
if (cnt % length)
|
|
cnt += 2 * length - (cnt % length);
|
|
else
|
|
cnt += length;
|
|
}
|
|
|
|
mask >>= 1;
|
|
++i;
|
|
}
|
|
|
|
if (cnt % largest)
|
|
cnt += largest - (cnt % largest);
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/**
|
|
* @brief Open device.
|
|
* @param ctx - IIO instance and conn instance
|
|
* @param device - String containing device name.
|
|
* @param sample_size - Sample size.
|
|
* @param mask - Channels to be opened.
|
|
* @return 0, negative value in case of failure.
|
|
*/
|
|
static int iio_open_dev(struct iiod_ctx *ctx, const char *device,
|
|
uint32_t samples, uint32_t mask, bool cyclic)
|
|
{
|
|
struct iio_desc *desc;
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig;
|
|
uint32_t ch_mask;
|
|
int32_t ret;
|
|
int8_t *buf;
|
|
uint32_t buf_size;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
if (!dev->buffer.initalized)
|
|
return -EINVAL;
|
|
|
|
ch_mask = 0xFFFFFFFF >> (32 - dev->dev_descriptor->num_ch);
|
|
mask &= ch_mask;
|
|
if (!mask)
|
|
return -ENOENT;
|
|
|
|
dev->buffer.public.cyclic_info.is_cyclic = cyclic;
|
|
dev->buffer.public.cyclic_info.buff_index = 0;
|
|
|
|
dev->buffer.public.active_mask = mask;
|
|
dev->buffer.public.bytes_per_scan =
|
|
bytes_per_scan(dev->dev_descriptor->channels, mask);
|
|
dev->buffer.public.size = dev->buffer.public.bytes_per_scan * samples;
|
|
dev->buffer.public.samples = samples;
|
|
if (dev->buffer.raw_buf && dev->buffer.raw_buf_len) {
|
|
if (dev->buffer.raw_buf_len < dev->buffer.public.size)
|
|
/* Need a bigger buffer or to allocate */
|
|
return -ENOMEM;
|
|
buf_size = dev->buffer.raw_buf_len - (dev->buffer.raw_buf_len %
|
|
dev->buffer.public.size);
|
|
buf = dev->buffer.raw_buf;
|
|
} else {
|
|
if (dev->buffer.allocated) {
|
|
/* Free in case iio_close_dev wasn't called to free it*/
|
|
no_os_free(dev->buffer.cb.buff);
|
|
dev->buffer.allocated = 0;
|
|
}
|
|
buf_size = dev->buffer.public.size;
|
|
buf = (int8_t *)no_os_calloc(dev->buffer.public.size, sizeof(*buf));
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
dev->buffer.allocated = 1;
|
|
}
|
|
|
|
ret = no_os_cb_cfg(&dev->buffer.cb, buf, buf_size);
|
|
if (NO_OS_IS_ERR_VALUE(ret)) {
|
|
if (dev->buffer.allocated) {
|
|
no_os_free(dev->buffer.cb.buff);
|
|
dev->buffer.allocated = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (dev->dev_descriptor->pre_enable) {
|
|
ret = dev->dev_descriptor->pre_enable(dev->dev_instance, mask);
|
|
if (NO_OS_IS_ERR_VALUE(ret)) {
|
|
if (dev->buffer.allocated) {
|
|
no_os_free(dev->buffer.cb.buff);
|
|
dev->buffer.allocated = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
desc = ctx->instance;
|
|
if (dev->trig_idx != NO_TRIGGER) {
|
|
trig = &desc->trigs[dev->trig_idx];
|
|
if (trig->descriptor->enable)
|
|
ret = trig->descriptor->enable(trig->instance);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Close device.
|
|
* @param ctx - IIO instance and conn instance
|
|
* @param device - String containing device name.
|
|
* @return 0, negative value in case of failure.
|
|
*/
|
|
static int iio_close_dev(struct iiod_ctx *ctx, const char *device)
|
|
{
|
|
struct iio_desc *desc;
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig;
|
|
int ret = 0;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (!dev->buffer.initalized)
|
|
return -EINVAL;
|
|
|
|
if (dev->buffer.allocated) {
|
|
/* Should something else be used to free internal strucutre */
|
|
no_os_free(dev->buffer.cb.buff);
|
|
dev->buffer.allocated = 0;
|
|
}
|
|
|
|
desc = ctx->instance;
|
|
if (dev->trig_idx != NO_TRIGGER) {
|
|
trig = &desc->trigs[dev->trig_idx];
|
|
if (trig->descriptor->disable) {
|
|
ret = trig->descriptor->disable(trig->instance);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
dev->buffer.public.active_mask = 0;
|
|
if (dev->dev_descriptor->post_disable)
|
|
ret = dev->dev_descriptor->post_disable(dev->dev_instance);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int iio_call_submit(struct iiod_ctx *ctx, const char *device,
|
|
enum iio_buffer_direction dir)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
if (!dev || !dev->buffer.initalized)
|
|
return -EINVAL;
|
|
|
|
dev->buffer.public.dir = dir;
|
|
if (dev->dev_descriptor->submit && dev->trig_idx == NO_TRIGGER)
|
|
return dev->dev_descriptor->submit(&dev->dev_data);
|
|
else if ((dir == IIO_DIRECTION_INPUT && dev->dev_descriptor->read_dev
|
|
&& dev->trig_idx == NO_TRIGGER)
|
|
|| (dir == IIO_DIRECTION_OUTPUT &&
|
|
dev->dev_descriptor->write_dev && dev->trig_idx == NO_TRIGGER)) {
|
|
/* Code used to don't break devices using read_dev */
|
|
int32_t ret;
|
|
void *buff;
|
|
struct iio_buffer *buffer = &dev->buffer.public;
|
|
|
|
ret = iio_buffer_get_block(buffer, &buff);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
if (dir == IIO_DIRECTION_INPUT)
|
|
ret = dev->dev_descriptor->read_dev(dev->dev_instance,
|
|
buff, buffer->samples);
|
|
else
|
|
ret = dev->dev_descriptor->write_dev(dev->dev_instance,
|
|
buff, buffer->samples);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
return iio_buffer_block_done(buffer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iio_push_buffer(struct iiod_ctx *ctx, const char *device)
|
|
{
|
|
return iio_call_submit(ctx, device, IIO_DIRECTION_OUTPUT);
|
|
}
|
|
|
|
static int iio_refill_buffer(struct iiod_ctx *ctx, const char *device)
|
|
{
|
|
return iio_call_submit(ctx, device, IIO_DIRECTION_INPUT);
|
|
}
|
|
|
|
/**
|
|
* @brief Read chunk of data from RAM to pbuf. Call
|
|
* "iio_transfer_dev_to_mem()" first.
|
|
* @param device - String containing device name.
|
|
* @param pbuf - Buffer where value is stored.
|
|
* @param offset - Offset to the remaining data after reading n chunks.
|
|
* @param bytes_count - Number of bytes to read.
|
|
* @return: Bytes_count or negative value in case of error.
|
|
*/
|
|
static int iio_read_buffer(struct iiod_ctx *ctx, const char *device, char *buf,
|
|
uint32_t bytes)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
int32_t ret;
|
|
uint32_t size;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
if (!dev || !dev->buffer.initalized)
|
|
return -EINVAL;
|
|
|
|
ret = no_os_cb_size(&dev->buffer.cb, &size);
|
|
#ifdef IIO_IGNORE_BUFF_OVERRUN_ERR
|
|
#warning Buffer overrun error checking is disabled.
|
|
if (ret != -NO_OS_EOVERRUN)
|
|
#endif
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
bytes = no_os_min(size, bytes);
|
|
if (!bytes)
|
|
return -EAGAIN;
|
|
|
|
|
|
ret = no_os_cb_read(&dev->buffer.cb, buf, bytes);
|
|
#ifdef IIO_IGNORE_BUFF_OVERRUN_ERR
|
|
if (ret != -NO_OS_EOVERRUN)
|
|
#endif
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
return bytes;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Write chunk of data into RAM.
|
|
* @param device - String containing device name.
|
|
* @param buf - Values to write.
|
|
* @param offset - Offset in memory after the nth chunk of data.
|
|
* @param bytes_count - Number of bytes to write.
|
|
* @return Bytes_count or negative value in case of error.
|
|
*/
|
|
static int iio_write_buffer(struct iiod_ctx *ctx, const char *device,
|
|
const char *buf, uint32_t bytes)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
int32_t ret;
|
|
uint32_t available;
|
|
uint32_t size;
|
|
|
|
dev = get_iio_device(ctx->instance, device);
|
|
if (!dev || !dev->buffer.initalized)
|
|
return -EINVAL;
|
|
|
|
ret = no_os_cb_size(&dev->buffer.cb, &size);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
available = dev->buffer.public.size - size;
|
|
bytes = no_os_min(available, bytes);
|
|
ret = no_os_cb_write(&dev->buffer.cb, buf, bytes);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
return bytes;
|
|
}
|
|
|
|
int iio_buffer_get_block(struct iio_buffer *buffer, void **addr)
|
|
{
|
|
uint32_t size;
|
|
|
|
if (!buffer)
|
|
return -EINVAL;
|
|
|
|
if (buffer->dir == IIO_DIRECTION_INPUT)
|
|
return no_os_cb_prepare_async_write(buffer->buf, buffer->size, addr, &size);
|
|
|
|
return no_os_cb_prepare_async_read(buffer->buf, buffer->size, addr, &size);
|
|
}
|
|
|
|
int iio_buffer_block_done(struct iio_buffer *buffer)
|
|
{
|
|
if (!buffer)
|
|
return -EINVAL;
|
|
|
|
if (buffer->dir == IIO_DIRECTION_INPUT)
|
|
return no_os_cb_end_async_write(buffer->buf);
|
|
|
|
return no_os_cb_end_async_read(buffer->buf);
|
|
}
|
|
|
|
/* Write to buffer iio_buffer.bytes_per_scan bytes from data */
|
|
int iio_buffer_push_scan(struct iio_buffer *buffer, void *data)
|
|
{
|
|
if (!buffer)
|
|
return -EINVAL;
|
|
|
|
return no_os_cb_write(buffer->buf, data, buffer->bytes_per_scan);
|
|
}
|
|
|
|
/* Read from buffer iio_buffer.bytes_per_scan bytes into data */
|
|
int iio_buffer_pop_scan(struct iio_buffer *buffer, void *data)
|
|
{
|
|
if (!buffer)
|
|
return -EINVAL;
|
|
|
|
int ret;
|
|
|
|
ret = no_os_cb_read(buffer->buf, data, buffer->bytes_per_scan);
|
|
|
|
if (buffer->cyclic_info.is_cyclic) {
|
|
if (buffer->buf->read.idx == buffer->buf->write.idx)
|
|
buffer->buf->read.idx = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
|
|
static int32_t accept_network_clients(struct iio_desc *desc)
|
|
{
|
|
struct tcp_socket_desc *sock;
|
|
struct iiod_conn_data data;
|
|
int32_t ret;
|
|
uint32_t id;
|
|
|
|
do {
|
|
ret = socket_accept(desc->server, &sock);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
data.conn = sock;
|
|
data.buf = no_os_calloc(1, IIOD_CONN_BUFFER_SIZE);
|
|
data.len = IIOD_CONN_BUFFER_SIZE;
|
|
|
|
if (!data.buf) {
|
|
ret = -ENOMEM;
|
|
goto close_socket;
|
|
}
|
|
|
|
ret = iiod_conn_add(desc->iiod, &data, &id);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_buf;
|
|
|
|
ret = _push_conn(desc, id);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto remove_conn;
|
|
} while (true);
|
|
|
|
return 0;
|
|
|
|
remove_conn:
|
|
iiod_conn_remove(desc->iiod, id, &data);
|
|
free_buf:
|
|
no_os_free(data.buf);
|
|
close_socket:
|
|
socket_remove(sock);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Execute an iio step
|
|
* @param desc - IIo descriptor
|
|
* @return 0 in case of success or negative value otherwise.
|
|
*/
|
|
int iio_step(struct iio_desc *desc)
|
|
{
|
|
struct iiod_conn_data data;
|
|
uint32_t conn_id;
|
|
int32_t ret;
|
|
|
|
iio_process_async_triggers(desc);
|
|
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
if (desc->server) {
|
|
ret = accept_network_clients(desc);
|
|
if (NO_OS_IS_ERR_VALUE(ret) && ret != -EAGAIN)
|
|
return ret;
|
|
#if defined(NO_OS_LWIP_NETWORKING)
|
|
no_os_lwip_step(desc->server->net->net, desc->server->net->net);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
ret = _pop_conn(desc, &conn_id);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
return ret;
|
|
|
|
ret = iiod_conn_step(desc->iiod, conn_id);
|
|
if (ret == -ENOTCONN) {
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
iiod_conn_remove(desc->iiod, conn_id, &data);
|
|
socket_remove(data.conn);
|
|
no_os_free(data.buf);
|
|
#endif
|
|
} else {
|
|
_push_conn(desc, conn_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Add context attributes into xml string buffer.
|
|
* @param desc - IIo descriptor.
|
|
* @param buff - xml buffer.
|
|
* @param buff_size - size of buffer
|
|
* @return 0 in case of success or negative value otherwise.
|
|
*/
|
|
static uint32_t iio_add_ctx_attr_in_xml(struct iio_desc *desc, char *buff,
|
|
uint32_t buff_size)
|
|
{
|
|
struct iio_ctx_attr *attr;
|
|
char dummy_buff[50];
|
|
int32_t i;
|
|
int32_t j;
|
|
int32_t n;
|
|
|
|
if ((int32_t)buff_size == -1)
|
|
n = 0;
|
|
else
|
|
n = buff_size;
|
|
|
|
if (buff == NULL)
|
|
/* Set dummy value for buff. It is used only for counting */
|
|
buff = dummy_buff;
|
|
|
|
i = 0;
|
|
|
|
attr = desc->ctx_attrs;
|
|
if (attr)
|
|
for (j = 0; j < (int32_t)desc->nb_ctx_attr; j++) {
|
|
i += snprintf(buff + i,
|
|
no_os_max(n - i, 0),
|
|
"<context-attribute name=\"%s\" ",
|
|
attr[j].name);
|
|
|
|
i += snprintf(buff + i,
|
|
no_os_max(n - i, 0),
|
|
"value=\"%s\" />",
|
|
attr[j].value);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Generate an xml describing a device and write it to buff.
|
|
* Will return the size of the xml.
|
|
* If buff_size is 0, no data will be written to buff, but size will be returned
|
|
*/
|
|
static uint32_t iio_generate_device_xml(struct iio_device *device, char *name,
|
|
char *id, char *buff,
|
|
uint32_t buff_size)
|
|
{
|
|
struct iio_channel *ch;
|
|
struct iio_attribute *attr;
|
|
char ch_id[50];
|
|
int32_t i;
|
|
int32_t j;
|
|
int32_t k;
|
|
int32_t n;
|
|
|
|
if ((int32_t)buff_size == -1)
|
|
n = 0;
|
|
else
|
|
n = buff_size;
|
|
|
|
if (buff == NULL)
|
|
/* Set dummy value for buff. It is used only for counting */
|
|
buff = ch_id;
|
|
|
|
i = 0;
|
|
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<device id=\"%s\" name=\"%s\">", id, name);
|
|
|
|
/* Write channels */
|
|
if (device->channels)
|
|
for (j = 0; j < device->num_ch; j++) {
|
|
ch = &device->channels[j];
|
|
_print_ch_id(ch_id, ch);
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<channel id=\"%s\"",
|
|
ch_id);
|
|
if (ch->name)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
" name=\"%s\"",
|
|
ch->name);
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
" type=\"%s\" >",
|
|
ch->ch_out ? "output" : "input");
|
|
|
|
if (ch->scan_type)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<scan-element index=\"%d\""
|
|
" format=\"%s:%c%d/%d>>%d\" />",
|
|
ch->scan_index,
|
|
ch->scan_type->is_big_endian ? "be" : "le",
|
|
ch->scan_type->sign,
|
|
ch->scan_type->realbits,
|
|
ch->scan_type->storagebits,
|
|
ch->scan_type->shift);
|
|
|
|
/* Write channel attributes */
|
|
if (ch->attributes)
|
|
for (k = 0; ch->attributes[k].name; k++) {
|
|
attr = &ch->attributes[k];
|
|
i += snprintf(buff + i, no_os_max(n - i, 0), "<attribute name=\"%s\" ",
|
|
attr->name);
|
|
if (ch->diferential) {
|
|
switch (attr->shared) {
|
|
case IIO_SHARED_BY_ALL:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s\"",
|
|
attr->name);
|
|
break;
|
|
case IIO_SHARED_BY_DIR:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
attr->name);
|
|
break;
|
|
case IIO_SHARED_BY_TYPE:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s-%s_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
iio_chan_type_string[ch->ch_type],
|
|
iio_chan_type_string[ch->ch_type],
|
|
attr->name);
|
|
break;
|
|
case IIO_SEPARATE:
|
|
if (!ch->indexed) {
|
|
// Differential channels must be indexed!
|
|
return -EINVAL;
|
|
}
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s%d-%s%d_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
iio_chan_type_string[ch->ch_type],
|
|
ch->channel,
|
|
iio_chan_type_string[ch->ch_type],
|
|
ch->channel2,
|
|
attr->name);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (attr->shared) {
|
|
case IIO_SHARED_BY_ALL:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s\"",
|
|
attr->name);
|
|
break;
|
|
case IIO_SHARED_BY_DIR:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
attr->name);
|
|
break;
|
|
case IIO_SHARED_BY_TYPE:
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
iio_chan_type_string[ch->ch_type],
|
|
attr->name);
|
|
break;
|
|
case IIO_SEPARATE:
|
|
if (ch->indexed)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s%d_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
iio_chan_type_string[ch->ch_type],
|
|
ch->channel,
|
|
attr->name);
|
|
else
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"filename=\"%s_%s_%s\"",
|
|
ch->ch_out ? "out" : "in",
|
|
iio_chan_type_string[ch->ch_type],
|
|
attr->name);
|
|
break;
|
|
}
|
|
}
|
|
i += snprintf(buff + i, no_os_max(n - i, 0), " />");
|
|
}
|
|
|
|
i += snprintf(buff + i, no_os_max(n - i, 0), "</channel>");
|
|
}
|
|
|
|
/* Write device attributes */
|
|
if (device->attributes)
|
|
for (j = 0; device->attributes[j].name; j++)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<attribute name=\"%s\" />",
|
|
device->attributes[j].name);
|
|
|
|
/* Write debug attributes */
|
|
if (device->debug_attributes)
|
|
for (j = 0; device->debug_attributes[j].name; j++)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<debug-attribute name=\"%s\" />",
|
|
device->debug_attributes[j].name);
|
|
if (device->debug_reg_read || device->debug_reg_write)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<debug-attribute name=\""REG_ACCESS_ATTRIBUTE"\" />");
|
|
|
|
/* Write buffer attributes */
|
|
if (device->buffer_attributes)
|
|
for (j = 0; device->buffer_attributes[j].name; j++)
|
|
i += snprintf(buff + i, no_os_max(n - i, 0),
|
|
"<buffer-attribute name=\"%s\" />",
|
|
device->buffer_attributes[j].name);
|
|
|
|
i += snprintf(buff + i, no_os_max(n - i, 0), "</device>");
|
|
|
|
return i;
|
|
}
|
|
|
|
static int32_t iio_init_xml(struct iio_desc *desc)
|
|
{
|
|
struct iio_dev_priv *dev;
|
|
struct iio_trig_priv *trig;
|
|
struct iio_device dummy = { 0 };
|
|
uint32_t i, size, of;
|
|
|
|
/* -2 because of the 0 character */
|
|
size = sizeof(header) + sizeof(header_end) - 2;
|
|
size += iio_add_ctx_attr_in_xml(desc, NULL, -1);
|
|
for (i = 0; i < desc->nb_devs; i++) {
|
|
dev = desc->devs + i;
|
|
size += iio_generate_device_xml(dev->dev_descriptor,
|
|
(char *)dev->name,
|
|
dev->dev_id, NULL, -1);
|
|
}
|
|
for (i = 0; i < desc->nb_trigs; i++) {
|
|
trig = desc->trigs + i;
|
|
dummy.attributes = trig->descriptor->attributes;
|
|
size += iio_generate_device_xml(&dummy, trig->name, trig->id,
|
|
NULL, -1);
|
|
}
|
|
|
|
desc->xml_desc = (char *)no_os_calloc(size + 1, sizeof(*desc->xml_desc));
|
|
if (!desc->xml_desc)
|
|
return -ENOMEM;
|
|
|
|
desc->xml_size = size;
|
|
|
|
strcpy(desc->xml_desc, header);
|
|
of = sizeof(header) - 1;
|
|
of += iio_add_ctx_attr_in_xml(desc, desc->xml_desc + of, size - of);
|
|
for (i = 0; i < desc->nb_devs; i++) {
|
|
dev = desc->devs + i;
|
|
of += iio_generate_device_xml(dev->dev_descriptor,
|
|
(char *)dev->name, dev->dev_id,
|
|
desc->xml_desc + of, size - of);
|
|
}
|
|
for (i = 0; i < desc->nb_trigs; i++) {
|
|
trig = desc->trigs + i;
|
|
dummy.attributes = trig->descriptor->attributes;
|
|
of += iio_generate_device_xml(&dummy, trig->name, trig->id,
|
|
desc->xml_desc + of, size - of);
|
|
}
|
|
|
|
strcpy(desc->xml_desc + of, header_end);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t iio_init_devs(struct iio_desc *desc,
|
|
struct iio_device_init *devs, uint32_t n)
|
|
{
|
|
uint32_t i;
|
|
struct iio_dev_priv *ldev;
|
|
struct iio_device_init *ndev;
|
|
|
|
desc->nb_devs = n;
|
|
desc->devs = (struct iio_dev_priv *)no_os_calloc(desc->nb_devs,
|
|
sizeof(*desc->devs));
|
|
if (!desc->devs)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
ndev = devs + i;
|
|
ldev = desc->devs + i;
|
|
ldev->dev_descriptor = ndev->dev_descriptor;
|
|
sprintf(ldev->dev_id, "iio:device%"PRIu32"", i);
|
|
ldev->trig_idx = iio_get_trig_idx_by_id(desc, ndev->trigger_id);
|
|
ldev->dev_instance = ndev->dev;
|
|
ldev->dev_data.dev = ndev->dev;
|
|
ldev->dev_data.buffer = &ldev->buffer.public;
|
|
ldev->name = ndev->name;
|
|
if (ndev->dev_descriptor->read_dev ||
|
|
ndev->dev_descriptor->write_dev ||
|
|
ndev->dev_descriptor->submit ||
|
|
ndev->dev_descriptor->trigger_handler) {
|
|
ldev->buffer.raw_buf = ndev->raw_buf;
|
|
ldev->buffer.raw_buf_len = ndev->raw_buf_len;
|
|
ldev->buffer.public.buf = &ldev->buffer.cb;
|
|
ldev->buffer.initalized = 1;
|
|
} else {
|
|
ldev->buffer.initalized = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Initializes IIO triggers.
|
|
* @param desc - IIO descriptor.
|
|
* @param trigs - Triggers array.
|
|
* @param n - Number of triggers to be initialized.
|
|
* @return 0 in case of success or negative value otherwise.
|
|
*/
|
|
static int32_t iio_init_trigs(struct iio_desc *desc,
|
|
struct iio_trigger_init *trigs, uint32_t n)
|
|
{
|
|
uint32_t i;
|
|
struct iio_trig_priv *trig_priv_iter;
|
|
struct iio_trigger_init *trig_init_iter;
|
|
|
|
desc->nb_trigs = n;
|
|
desc->trigs = (struct iio_trig_priv *)no_os_calloc(desc->nb_trigs,
|
|
sizeof(*desc->trigs));
|
|
if (!desc->trigs)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
trig_init_iter = trigs + i;
|
|
trig_priv_iter = desc->trigs + i;
|
|
trig_priv_iter->instance = trig_init_iter->trig;
|
|
trig_priv_iter->name = trig_init_iter->name;
|
|
trig_priv_iter->descriptor = trig_init_iter->descriptor;
|
|
sprintf(trig_priv_iter->id, "trigger%"PRIu32"", i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set communication ops and read/write ops
|
|
* @param desc - iio descriptor.
|
|
* @param init_param - appropriate init param.
|
|
* @return 0 in case of success or negative value otherwise.
|
|
*/
|
|
int iio_init(struct iio_desc **desc, struct iio_init_param *init_param)
|
|
{
|
|
int32_t ret;
|
|
struct iio_desc *ldesc;
|
|
struct iiod_ops *ops;
|
|
struct iiod_init_param iiod_param;
|
|
uint32_t conn_id;
|
|
|
|
if (!desc || !init_param)
|
|
return -EINVAL;
|
|
|
|
ldesc = (struct iio_desc *)no_os_calloc(1, sizeof(*ldesc));
|
|
if (!ldesc)
|
|
return -ENOMEM;
|
|
|
|
ldesc->ctx_attrs = init_param->ctx_attrs;
|
|
ldesc->nb_ctx_attr = init_param->nb_ctx_attr;
|
|
|
|
ret = iio_init_trigs(ldesc, init_param->trigs, init_param->nb_trigs);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_devs;
|
|
|
|
ret = iio_init_devs(ldesc, init_param->devs, init_param->nb_devs);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_desc;
|
|
|
|
ret = iio_init_xml(ldesc);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_trigs;
|
|
|
|
/* device operations */
|
|
ops = &ldesc->iiod_ops;
|
|
ops->read_attr = iio_read_attr;
|
|
ops->write_attr = iio_write_attr;
|
|
ops->get_trigger = iio_get_trigger;
|
|
ops->set_trigger = iio_set_trigger;
|
|
ops->read_buffer = iio_read_buffer;
|
|
ops->write_buffer = iio_write_buffer;
|
|
ops->refill_buffer = iio_refill_buffer;
|
|
ops->push_buffer = iio_push_buffer;
|
|
ops->open = iio_open_dev;
|
|
ops->close = iio_close_dev;
|
|
ops->send = iio_send;
|
|
ops->recv = iio_recv;
|
|
ops->set_buffers_count = iio_set_buffers_count;
|
|
|
|
iiod_param.instance = ldesc;
|
|
iiod_param.ops = ops;
|
|
iiod_param.xml = ldesc->xml_desc;
|
|
iiod_param.xml_len = ldesc->xml_size;
|
|
iiod_param.phy_type = init_param->phy_type;
|
|
|
|
ret = iiod_init(&ldesc->iiod, &iiod_param);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_xml;
|
|
|
|
ret = no_os_cb_init(&ldesc->conns,
|
|
sizeof(uint32_t) * (IIOD_MAX_CONNECTIONS + 1));
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_iiod;
|
|
|
|
if (init_param->phy_type == USE_UART) {
|
|
ldesc->send = (int (*)())no_os_uart_write;
|
|
ldesc->recv = (int (*)())no_os_uart_read;
|
|
ldesc->uart_desc = init_param->uart_desc;
|
|
|
|
struct iiod_conn_data data = {
|
|
.conn = ldesc->uart_desc,
|
|
.buf = uart_buff,
|
|
.len = sizeof(uart_buff)
|
|
};
|
|
ret = iiod_conn_add(ldesc->iiod, &data, &conn_id);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_conns;
|
|
_push_conn(ldesc, conn_id);
|
|
}
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
else if (init_param->phy_type == USE_NETWORK) {
|
|
ldesc->send = (int (*)())socket_send;
|
|
ldesc->recv = (int (*)())socket_recv;
|
|
ret = socket_init(&ldesc->server,
|
|
init_param->tcp_socket_init_param);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_conns;
|
|
ret = socket_bind(ldesc->server, IIOD_PORT);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_pylink;
|
|
ret = socket_listen(ldesc->server, MAX_BACKLOG);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_pylink;
|
|
}
|
|
#endif
|
|
else if (init_param->phy_type == USE_LOCAL_BACKEND) {
|
|
ldesc->recv = init_param->local_backend->local_backend_event_read;
|
|
ldesc->send = init_param->local_backend->local_backend_event_write;
|
|
|
|
struct iiod_conn_data data = {
|
|
.conn = NULL,
|
|
.buf = init_param->local_backend->local_backend_buff,
|
|
.len = init_param->local_backend->local_backend_buff_len
|
|
};
|
|
ret = iiod_conn_add(ldesc->iiod, &data, &conn_id);
|
|
if (NO_OS_IS_ERR_VALUE(ret))
|
|
goto free_conns;
|
|
_push_conn(ldesc, conn_id);
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto free_conns;
|
|
}
|
|
|
|
*desc = ldesc;
|
|
|
|
return 0;
|
|
|
|
free_pylink:
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
socket_remove(ldesc->server);
|
|
#endif
|
|
free_conns:
|
|
no_os_cb_remove(ldesc->conns);
|
|
free_iiod:
|
|
iiod_remove(ldesc->iiod);
|
|
free_xml:
|
|
no_os_free(ldesc->xml_desc);
|
|
free_trigs:
|
|
no_os_free(ldesc->trigs);
|
|
free_devs:
|
|
no_os_free(ldesc->devs);
|
|
free_desc:
|
|
no_os_free(ldesc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Free the resources allocated by "iio_init()".
|
|
* @param desc: iio descriptor.
|
|
* @return 0 in case of success or negative value otherwise.
|
|
*/
|
|
int iio_remove(struct iio_desc *desc)
|
|
{
|
|
struct iiod_conn_data data;
|
|
int ret;
|
|
|
|
if (!desc)
|
|
return -EINVAL;
|
|
|
|
#if defined(NO_OS_NETWORKING) || defined(NO_OS_LWIP_NETWORKING)
|
|
for (int i = 0; i < IIOD_MAX_CONNECTIONS; i++) {
|
|
ret = iiod_conn_remove(desc->iiod, i, &data);
|
|
if (!ret) {
|
|
no_os_free(data.buf);
|
|
socket_remove(data.conn);
|
|
}
|
|
}
|
|
socket_remove(desc->server);
|
|
#endif
|
|
no_os_cb_remove(desc->conns);
|
|
iiod_remove(desc->iiod);
|
|
no_os_free(desc->devs);
|
|
no_os_free(desc->trigs);
|
|
no_os_free(desc->xml_desc);
|
|
no_os_free(desc);
|
|
|
|
return 0;
|
|
}
|