summaryrefslogtreecommitdiff
path: root/src/xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xml.c')
-rw-r--r--src/xml.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/src/xml.c b/src/xml.c
new file mode 100644
index 0000000..fae5e75
--- /dev/null
+++ b/src/xml.c
@@ -0,0 +1,458 @@
+/*
+ * 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;
+}