diff options
Diffstat (limited to 'src/xml.c')
-rw-r--r-- | src/xml.c | 458 |
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; +} |