459 lines
11 KiB
C
459 lines
11 KiB
C
/*
|
|
* libiio - Library for interfacing industrial I/O (IIO) devices
|
|
*
|
|
* Copyright (C) 2014 Analog Devices, Inc.
|
|
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* */
|
|
|
|
#include "debug.h"
|
|
#include "iio-private.h"
|
|
|
|
#include <errno.h>
|
|
#include <libxml/tree.h>
|
|
#include <string.h>
|
|
|
|
static int add_attr_to_channel(struct iio_channel *chn, xmlNode *n)
|
|
{
|
|
xmlAttr *attr;
|
|
char *name = NULL, *filename = NULL;
|
|
struct iio_channel_attr *attrs;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
if (!strcmp((char *) attr->name, "name")) {
|
|
name = iio_strdup((char *) attr->children->content);
|
|
} else if (!strcmp((char *) attr->name, "filename")) {
|
|
filename = iio_strdup((char *) attr->children->content);
|
|
} else {
|
|
WARNING("Unknown field \'%s\' in channel %s\n",
|
|
attr->name, chn->id);
|
|
}
|
|
}
|
|
|
|
if (!name) {
|
|
ERROR("Incomplete attribute in channel %s\n", chn->id);
|
|
goto err_free;
|
|
}
|
|
|
|
if (!filename) {
|
|
filename = iio_strdup(name);
|
|
if (!filename)
|
|
goto err_free;
|
|
}
|
|
|
|
attrs = realloc(chn->attrs, (1 + chn->nb_attrs) *
|
|
sizeof(struct iio_channel_attr));
|
|
if (!attrs)
|
|
goto err_free;
|
|
|
|
attrs[chn->nb_attrs].filename = filename;
|
|
attrs[chn->nb_attrs++].name = name;
|
|
chn->attrs = attrs;
|
|
return 0;
|
|
|
|
err_free:
|
|
if (name)
|
|
free(name);
|
|
if (filename)
|
|
free(filename);
|
|
return -1;
|
|
}
|
|
|
|
static int add_attr_to_device(struct iio_device *dev, xmlNode *n, enum iio_attr_type type)
|
|
{
|
|
xmlAttr *attr;
|
|
char **attrs, *name = NULL;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
if (!strcmp((char *) attr->name, "name")) {
|
|
name = iio_strdup((char *) attr->children->content);
|
|
} else {
|
|
WARNING("Unknown field \'%s\' in device %s\n",
|
|
attr->name, dev->id);
|
|
}
|
|
}
|
|
|
|
if (!name) {
|
|
ERROR("Incomplete attribute in device %s\n", dev->id);
|
|
goto err_free;
|
|
}
|
|
|
|
switch(type) {
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
attrs = realloc(dev->debug_attrs,
|
|
(1 + dev->nb_debug_attrs) * sizeof(char *));
|
|
break;
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
attrs = realloc(dev->attrs,
|
|
(1 + dev->nb_attrs) * sizeof(char *));
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
attrs = realloc(dev->buffer_attrs,
|
|
(1 + dev->nb_buffer_attrs) * sizeof(char *));
|
|
break;
|
|
default:
|
|
attrs = NULL;
|
|
break;
|
|
}
|
|
if (!attrs)
|
|
goto err_free;
|
|
|
|
switch(type) {
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
attrs[dev->nb_debug_attrs++] = name;
|
|
dev->debug_attrs = attrs;
|
|
break;
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
attrs[dev->nb_attrs++] = name;
|
|
dev->attrs = attrs;
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
attrs[dev->nb_buffer_attrs++] = name;
|
|
dev->buffer_attrs = attrs;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_free:
|
|
if (name)
|
|
free(name);
|
|
return -1;
|
|
}
|
|
|
|
static void setup_scan_element(struct iio_channel *chn, xmlNode *n)
|
|
{
|
|
xmlAttr *attr;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
const char *name = (const char *) attr->name,
|
|
*content = (const char *) attr->children->content;
|
|
if (!strcmp(name, "index")) {
|
|
chn->index = atol(content);
|
|
} else if (!strcmp(name, "format")) {
|
|
char e, s;
|
|
if (strchr(content, 'X')) {
|
|
sscanf(content, "%ce:%c%u/%uX%u>>%u", &e, &s,
|
|
&chn->format.bits,
|
|
&chn->format.length,
|
|
&chn->format.repeat,
|
|
&chn->format.shift);
|
|
} else {
|
|
chn->format.repeat = 1;
|
|
sscanf(content, "%ce:%c%u/%u>>%u", &e, &s,
|
|
&chn->format.bits,
|
|
&chn->format.length,
|
|
&chn->format.shift);
|
|
}
|
|
chn->format.is_be = e == 'b';
|
|
chn->format.is_signed = (s == 's' || s == 'S');
|
|
chn->format.is_fully_defined = (s == 'S' || s == 'U' ||
|
|
chn->format.bits == chn->format.length);
|
|
} else if (!strcmp(name, "scale")) {
|
|
chn->format.with_scale = true;
|
|
chn->format.scale = atof(content);
|
|
} else {
|
|
WARNING("Unknown attribute \'%s\' in <scan-element>\n",
|
|
name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct iio_channel * create_channel(struct iio_device *dev, xmlNode *n)
|
|
{
|
|
xmlAttr *attr;
|
|
struct iio_channel *chn = zalloc(sizeof(*chn));
|
|
if (!chn)
|
|
return NULL;
|
|
|
|
chn->dev = dev;
|
|
|
|
/* Set the default index value < 0 (== no index) */
|
|
chn->index = -ENOENT;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
const char *name = (const char *) attr->name,
|
|
*content = (const char *) attr->children->content;
|
|
if (!strcmp(name, "name")) {
|
|
chn->name = iio_strdup(content);
|
|
} else if (!strcmp(name, "id")) {
|
|
chn->id = iio_strdup(content);
|
|
} else if (!strcmp(name, "type")) {
|
|
if (!strcmp(content, "output"))
|
|
chn->is_output = true;
|
|
else if (strcmp(content, "input"))
|
|
WARNING("Unknown channel type %s\n", content);
|
|
} else {
|
|
WARNING("Unknown attribute \'%s\' in <channel>\n",
|
|
name);
|
|
}
|
|
}
|
|
|
|
if (!chn->id) {
|
|
ERROR("Incomplete <attribute>\n");
|
|
goto err_free_channel;
|
|
}
|
|
|
|
for (n = n->children; n; n = n->next) {
|
|
if (!strcmp((char *) n->name, "attribute")) {
|
|
if (add_attr_to_channel(chn, n) < 0)
|
|
goto err_free_channel;
|
|
} else if (!strcmp((char *) n->name, "scan-element")) {
|
|
chn->is_scan_element = true;
|
|
setup_scan_element(chn, n);
|
|
} else if (strcmp((char *) n->name, "text")) {
|
|
WARNING("Unknown children \'%s\' in <channel>\n",
|
|
n->name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
iio_channel_init_finalize(chn);
|
|
|
|
return chn;
|
|
|
|
err_free_channel:
|
|
free_channel(chn);
|
|
return NULL;
|
|
}
|
|
|
|
static struct iio_device * create_device(struct iio_context *ctx, xmlNode *n)
|
|
{
|
|
xmlAttr *attr;
|
|
struct iio_device *dev = zalloc(sizeof(*dev));
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
dev->ctx = ctx;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
if (!strcmp((char *) attr->name, "name")) {
|
|
dev->name = iio_strdup(
|
|
(char *) attr->children->content);
|
|
} else if (!strcmp((char *) attr->name, "id")) {
|
|
dev->id = iio_strdup((char *) attr->children->content);
|
|
} else {
|
|
WARNING("Unknown attribute \'%s\' in <device>\n",
|
|
attr->name);
|
|
}
|
|
}
|
|
|
|
if (!dev->id) {
|
|
ERROR("Unable to read device ID\n");
|
|
goto err_free_device;
|
|
}
|
|
|
|
for (n = n->children; n; n = n->next) {
|
|
if (!strcmp((char *) n->name, "channel")) {
|
|
struct iio_channel **chns,
|
|
*chn = create_channel(dev, n);
|
|
if (!chn) {
|
|
ERROR("Unable to create channel\n");
|
|
goto err_free_device;
|
|
}
|
|
|
|
chns = realloc(dev->channels, (1 + dev->nb_channels) *
|
|
sizeof(struct iio_channel *));
|
|
if (!chns) {
|
|
ERROR("Unable to allocate memory\n");
|
|
free(chn);
|
|
goto err_free_device;
|
|
}
|
|
|
|
chns[dev->nb_channels++] = chn;
|
|
dev->channels = chns;
|
|
} else if (!strcmp((char *) n->name, "attribute")) {
|
|
if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_DEVICE) < 0)
|
|
goto err_free_device;
|
|
} else if (!strcmp((char *) n->name, "debug-attribute")) {
|
|
if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_DEBUG) < 0)
|
|
goto err_free_device;
|
|
} else if (!strcmp((char *) n->name, "buffer-attribute")) {
|
|
if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_BUFFER) < 0)
|
|
goto err_free_device;
|
|
} else if (strcmp((char *) n->name, "text")) {
|
|
WARNING("Unknown children \'%s\' in <device>\n",
|
|
n->name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
dev->words = (dev->nb_channels + 31) / 32;
|
|
if (dev->words) {
|
|
dev->mask = calloc(dev->words, sizeof(*dev->mask));
|
|
if (!dev->mask) {
|
|
errno = ENOMEM;
|
|
goto err_free_device;
|
|
}
|
|
}
|
|
|
|
return dev;
|
|
|
|
err_free_device:
|
|
free_device(dev);
|
|
return NULL;
|
|
}
|
|
|
|
static struct iio_context * xml_clone(const struct iio_context *ctx)
|
|
{
|
|
return xml_create_context_mem(ctx->xml, strlen(ctx->xml));
|
|
}
|
|
|
|
static const struct iio_backend_ops xml_ops = {
|
|
.clone = xml_clone,
|
|
};
|
|
|
|
static int parse_context_attr(struct iio_context *ctx, xmlNode *n)
|
|
{
|
|
xmlAttr *attr;
|
|
const char *name = NULL, *value = NULL;
|
|
|
|
for (attr = n->properties; attr; attr = attr->next) {
|
|
if (!strcmp((const char *) attr->name, "name")) {
|
|
name = (const char *) attr->children->content;
|
|
} else if (!strcmp((const char *) attr->name, "value")) {
|
|
value = (const char *) attr->children->content;
|
|
}
|
|
}
|
|
|
|
if (!name || !value)
|
|
return -EINVAL;
|
|
else
|
|
return iio_context_add_attr(ctx, name, value);
|
|
}
|
|
|
|
static struct iio_context * iio_create_xml_context_helper(xmlDoc *doc)
|
|
{
|
|
unsigned int i;
|
|
xmlNode *root, *n;
|
|
xmlAttr *attr;
|
|
int err = -ENOMEM;
|
|
struct iio_context *ctx = zalloc(sizeof(*ctx));
|
|
if (!ctx)
|
|
goto err_set_errno;
|
|
|
|
ctx->name = "xml";
|
|
ctx->ops = &xml_ops;
|
|
|
|
root = xmlDocGetRootElement(doc);
|
|
if (strcmp((char *) root->name, "context")) {
|
|
ERROR("Unrecognized XML file\n");
|
|
err = -EINVAL;
|
|
goto err_free_ctx;
|
|
}
|
|
|
|
for (attr = root->properties; attr; attr = attr->next) {
|
|
if (!strcmp((char *) attr->name, "description"))
|
|
ctx->description = iio_strdup(
|
|
(char *) attr->children->content);
|
|
else if (strcmp((char *) attr->name, "name"))
|
|
WARNING("Unknown parameter \'%s\' in <context>\n",
|
|
(char *) attr->children->content);
|
|
}
|
|
|
|
for (n = root->children; n; n = n->next) {
|
|
struct iio_device **devs, *dev;
|
|
|
|
if (!strcmp((char *) n->name, "context-attribute")) {
|
|
err = parse_context_attr(ctx, n);
|
|
if (err)
|
|
goto err_free_devices;
|
|
else
|
|
continue;
|
|
} else if (strcmp((char *) n->name, "device")) {
|
|
if (strcmp((char *) n->name, "text"))
|
|
WARNING("Unknown children \'%s\' in "
|
|
"<context>\n", n->name);
|
|
continue;
|
|
}
|
|
|
|
dev = create_device(ctx, n);
|
|
if (!dev) {
|
|
ERROR("Unable to create device\n");
|
|
goto err_free_devices;
|
|
}
|
|
|
|
devs = realloc(ctx->devices, (1 + ctx->nb_devices) *
|
|
sizeof(struct iio_device *));
|
|
if (!devs) {
|
|
ERROR("Unable to allocate memory\n");
|
|
free(dev);
|
|
goto err_free_devices;
|
|
}
|
|
|
|
devs[ctx->nb_devices++] = dev;
|
|
ctx->devices = devs;
|
|
}
|
|
|
|
err = iio_context_init(ctx);
|
|
if (err)
|
|
goto err_free_devices;
|
|
|
|
return ctx;
|
|
|
|
err_free_devices:
|
|
for (i = 0; i < ctx->nb_devices; i++)
|
|
free_device(ctx->devices[i]);
|
|
if (ctx->nb_devices)
|
|
free(ctx->devices);
|
|
for (i = 0; i < ctx->nb_attrs; i++) {
|
|
free(ctx->attrs[i]);
|
|
free(ctx->values[i]);
|
|
}
|
|
free(ctx->attrs);
|
|
free(ctx->values);
|
|
err_free_ctx:
|
|
free(ctx);
|
|
err_set_errno:
|
|
errno = -err;
|
|
return NULL;
|
|
}
|
|
|
|
struct iio_context * xml_create_context(const char *xml_file)
|
|
{
|
|
struct iio_context *ctx;
|
|
xmlDoc *doc;
|
|
|
|
LIBXML_TEST_VERSION;
|
|
|
|
doc = xmlReadFile(xml_file, NULL, XML_PARSE_DTDVALID);
|
|
if (!doc) {
|
|
ERROR("Unable to parse XML file\n");
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
ctx = iio_create_xml_context_helper(doc);
|
|
xmlFreeDoc(doc);
|
|
return ctx;
|
|
}
|
|
|
|
struct iio_context * xml_create_context_mem(const char *xml, size_t len)
|
|
{
|
|
struct iio_context *ctx;
|
|
xmlDoc *doc;
|
|
|
|
LIBXML_TEST_VERSION;
|
|
|
|
doc = xmlReadMemory(xml, (int) len, NULL, NULL, XML_PARSE_DTDVALID);
|
|
if (!doc) {
|
|
ERROR("Unable to parse XML file\n");
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
ctx = iio_create_xml_context_helper(doc);
|
|
xmlFreeDoc(doc);
|
|
return ctx;
|
|
}
|