blob: b8b4dafb77d54e3c6fb239832aa456ac97785c02 [file] [log] [blame]
Paul Cercueil5d9db302014-02-18 18:16:51 +01001#include "debug.h"
2#include "iio-private.h"
3
4#include <errno.h>
5#include <libxml/tree.h>
6#include <string.h>
7
8struct value_map {
9 const struct iio_device *dev;
10 const struct iio_channel *chn;
11 const char *attr;
12 char *value;
13};
14
15struct xml_pdata {
16 struct value_map *values;
17 unsigned int nb_values;
18};
19
20static int add_attr_to_channel(struct iio_channel *chn, xmlNode *n)
21{
22 xmlAttr *attr;
23 char *name = NULL, *value = NULL;
24 const char **attrs;
25 struct xml_pdata *pdata = chn->dev->ctx->backend_data;
26 struct value_map *values;
27
28 for (attr = n->properties; attr; attr = attr->next) {
29 if (!strcmp((char *) attr->name, "name")) {
30 name = strdup((char *) attr->children->content);
31 } else if (!strcmp((char *) attr->name, "value")) {
32 value = strdup((char *) attr->children->content);
33 } else {
34 WARNING("Unknown field \'%s\' in channel %s\n",
35 attr->name, chn->id);
36 }
37 }
38
39 if (!name || !value) {
40 ERROR("Incomplete attribute in channel %s\n", chn->id);
41 goto err_free;
42 }
43
44 attrs = realloc(chn->attrs, (1 + chn->nb_attrs) * sizeof(char *));
45 if (!attrs)
46 goto err_free;
47
48 values = realloc(pdata->values,
49 (1 + pdata->nb_values) * sizeof(struct value_map));
50 if (!values)
51 goto err_update_attrs;
52
53 values[pdata->nb_values].dev = NULL;
54 values[pdata->nb_values].chn = chn;
55 values[pdata->nb_values].attr = name;
56 values[pdata->nb_values++].value = value;
57 pdata->values = values;
58
59 attrs[chn->nb_attrs++] = name;
60 chn->attrs = attrs;
61 return 0;
62
63err_update_attrs:
64 /* the first realloc succeeded so we must update chn->attrs
65 * even if an error occured later */
66 chn->attrs = attrs;
67err_free:
68 if (name)
69 free(name);
70 if (value)
71 free(value);
72 return -1;
73}
74
75static int add_attr_to_device(struct iio_device *dev, xmlNode *n)
76{
77 xmlAttr *attr;
78 char *name = NULL, *value = NULL;
79 const char **attrs;
80 struct xml_pdata *pdata = dev->ctx->backend_data;
81 struct value_map *values;
82
83 for (attr = n->properties; attr; attr = attr->next) {
84 if (!strcmp((char *) attr->name, "name")) {
85 name = strdup((char *) attr->children->content);
86 } else if (!strcmp((char *) attr->name, "value")) {
87 value = strdup((char *) attr->children->content);
88 } else {
89 WARNING("Unknown field \'%s\' in device %s\n",
90 attr->name, dev->id);
91 }
92 }
93
94 if (!name || !value) {
95 ERROR("Incomplete attribute in device %s\n", dev->id);
96 goto err_free;
97 }
98
99 attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
100 if (!attrs)
101 goto err_free;
102
103 values = realloc(pdata->values,
104 (1 + pdata->nb_values) * sizeof(struct value_map));
105 if (!values)
106 goto err_update_attrs;
107
108 values[pdata->nb_values].dev = dev;
109 values[pdata->nb_values].chn = NULL;
110 values[pdata->nb_values].attr = name;
111 values[pdata->nb_values++].value = value;
112 pdata->values = values;
113
114 attrs[dev->nb_attrs++] = name;
115 dev->attrs = attrs;
116 return 0;
117
118err_update_attrs:
119 /* the first realloc succeeded so we must update dev->attrs
120 * even if an error occured later */
121 dev->attrs = attrs;
122err_free:
123 if (name)
124 free(name);
125 if (value)
126 free(value);
127 return -1;
128}
129
130static struct iio_channel * create_channel(struct iio_device *dev, xmlNode *n)
131{
132 xmlAttr *attr;
133 struct iio_channel *chn = calloc(1, sizeof(*chn));
134 if (!chn)
135 return NULL;
136
137 chn->dev = dev;
Paul Cercueil5d9db302014-02-18 18:16:51 +0100138
139 for (attr = n->properties; attr; attr = attr->next) {
Paul Cercueil35a01312014-02-20 10:56:57 +0100140 const char *name = (const char *) attr->name,
141 *content = (const char *) attr->children->content;
142 if (!strcmp(name, "name")) {
143 chn->name = strdup(content);
144 } else if (!strcmp(name, "id")) {
145 chn->id = strdup(content);
146 } else if (!strcmp(name, "type")) {
147 if (!strcmp(content, "output"))
148 chn->is_output = true;
149 else if (strcmp(content, "input"))
150 WARNING("Unknown channel type %s\n", content);
Paul Cercueil5d9db302014-02-18 18:16:51 +0100151 } else {
152 WARNING("Unknown attribute \'%s\' in <channel>\n",
Paul Cercueil35a01312014-02-20 10:56:57 +0100153 name);
Paul Cercueil5d9db302014-02-18 18:16:51 +0100154 }
155 }
156
157 if (!chn->id) {
158 ERROR("Incomplete <attribute>\n");
159 goto err_free_channel;
160 }
161
162 for (n = n->children; n; n = n->next) {
163 if (!strcmp((char *) n->name, "attribute")) {
164 if (add_attr_to_channel(chn, n) < 0)
165 goto err_free_channel;
166 } else if (strcmp((char *) n->name, "text")) {
167 WARNING("Unknown children \'%s\' in <device>\n",
168 n->name);
169 continue;
170 }
171 }
172
173 return chn;
174
175err_free_channel:
176 free_channel(chn);
177 return NULL;
178}
179
180static struct iio_device * create_device(struct iio_context *ctx, xmlNode *n)
181{
182 xmlAttr *attr;
183 struct iio_device *dev = calloc(1, sizeof(*dev));
184 if (!dev)
185 return NULL;
186
187 dev->ctx = ctx;
188
189 for (attr = n->properties; attr; attr = attr->next) {
190 if (!strcmp((char *) attr->name, "name")) {
191 dev->name = strdup((char *) attr->children->content);
192 } else if (!strcmp((char *) attr->name, "id")) {
193 dev->id = strdup((char *) attr->children->content);
194 } else {
195 WARNING("Unknown attribute \'%s\' in <context>\n",
196 attr->name);
197 }
198 }
199
200 if (!dev->id) {
201 ERROR("Unable to read device ID\n");
202 goto err_free_device;
203 }
204
205 for (n = n->children; n; n = n->next) {
206 if (!strcmp((char *) n->name, "channel")) {
207 struct iio_channel **chns,
208 *chn = create_channel(dev, n);
209 if (!chn) {
210 ERROR("Unable to create channel\n");
211 goto err_free_device;
212 }
213
214 chns = realloc(dev->channels, (1 + dev->nb_channels) *
215 sizeof(struct iio_channel *));
216 if (!chns) {
217 ERROR("Unable to allocate memory\n");
218 free(chn);
219 goto err_free_device;
220 }
221
222 chns[dev->nb_channels++] = chn;
223 dev->channels = chns;
224 } else if (!strcmp((char *) n->name, "attribute")) {
225 if (add_attr_to_device(dev, n) < 0)
226 goto err_free_device;
227 } else if (strcmp((char *) n->name, "text")) {
228 WARNING("Unknown children \'%s\' in <device>\n",
229 n->name);
230 continue;
231 }
232 }
233
234 return dev;
235
236err_free_device:
237 free_device(dev);
238 return NULL;
239}
240
241static ssize_t xml_read_attr_helper(struct xml_pdata *pdata,
242 const struct iio_device *dev,
243 const struct iio_channel *chn,
244 const char *path, char *dst, size_t len)
245{
246 unsigned int i;
247 for (i = 0; i < pdata->nb_values; i++) {
248 struct value_map *map = &pdata->values[i];
249
250 if (dev == map->dev && chn == map->chn
251 && !strcmp(path, map->attr)) {
252 size_t value_len = strlen(map->value);
253 strncpy(dst, map->value, len);
254 return value_len + 1;
255 }
256 }
257
258 return -ENOENT;
259}
260
261static ssize_t xml_read_dev_attr(const struct iio_device *dev,
262 const char *path, char *dst, size_t len)
263{
264 struct xml_pdata *pdata = dev->ctx->backend_data;
265 return xml_read_attr_helper(pdata, dev, NULL, path, dst, len);
266}
267
268static ssize_t xml_read_chn_attr(const struct iio_channel *chn,
269 const char *path, char *dst, size_t len)
270{
271 struct xml_pdata *pdata = chn->dev->ctx->backend_data;
272 return xml_read_attr_helper(pdata, NULL, chn, path, dst, len);
273}
274
275static void xml_shutdown(struct iio_context *ctx)
276{
277 struct xml_pdata *pdata = ctx->backend_data;
278 unsigned int i;
279 for (i = 0; i < pdata->nb_values; i++) {
280 struct value_map *map = &pdata->values[i];
281
282 /* note: map->attr and map->dev are freed elsewhere */
283 free(map->value);
284 }
285
286 if (pdata->nb_values)
287 free(pdata->values);
288 free(pdata);
289}
290
291static struct iio_backend_ops xml_ops = {
292 .read_device_attr = xml_read_dev_attr,
293 .read_channel_attr = xml_read_chn_attr,
294 .shutdown = xml_shutdown,
295};
296
297struct iio_context * iio_create_xml_context(const char *xml_file)
298{
299 unsigned int i;
300 xmlDoc *doc;
301 xmlNode *root, *n;
302 struct iio_context *ctx = calloc(1, sizeof(*ctx));
303 if (!ctx)
304 return NULL;
305
306 ctx->backend_data = calloc(1, sizeof(struct xml_pdata));
307 if (!ctx->backend_data) {
308 ERROR("Unable to allocate memory\n");
Paul Cercueil92de9f22014-02-19 14:07:24 +0100309 goto err_free_ctx;
Paul Cercueil5d9db302014-02-18 18:16:51 +0100310 }
311
312 ctx->name = "xml";
313 ctx->ops = &xml_ops;
314
315 LIBXML_TEST_VERSION;
316
Paul Cercueil92de9f22014-02-19 14:07:24 +0100317 doc = xmlReadFile(xml_file, NULL, XML_PARSE_DTDVALID);
Paul Cercueil5d9db302014-02-18 18:16:51 +0100318 if (!doc) {
319 ERROR("Unable to parse XML file\n");
Paul Cercueil92de9f22014-02-19 14:07:24 +0100320 goto err_free_bdata;
Paul Cercueil5d9db302014-02-18 18:16:51 +0100321 }
322
323 root = xmlDocGetRootElement(doc);
324 if (strcmp((char *) root->name, "context")) {
325 ERROR("Unrecognized XML file\n");
326 goto err_free_doc;
327 }
328
329 for (n = root->children; n; n = n->next) {
330 struct iio_device **devs, *dev;
331
332 if (strcmp((char *) n->name, "device")) {
333 if (strcmp((char *) n->name, "text"))
334 WARNING("Unknown children \'%s\' in "
335 "<context>\n", n->name);
336 continue;
337 }
338
339 dev = create_device(ctx, n);
340 if (!dev) {
341 ERROR("Unable to create device\n");
342 goto err_free_devices;
343 }
344
345 devs = realloc(ctx->devices, (1 + ctx->nb_devices) *
346 sizeof(struct iio_device *));
347 if (!devs) {
348 ERROR("Unable to allocate memory\n");
349 free(dev);
350 goto err_free_devices;
351 }
352
353 devs[ctx->nb_devices++] = dev;
354 ctx->devices = devs;
355 }
356
Paul Cercueil92de9f22014-02-19 14:07:24 +0100357 xmlFreeDoc(doc);
358 xmlCleanupParser();
Paul Cercueil5d9db302014-02-18 18:16:51 +0100359 return ctx;
360
361err_free_devices:
362 for (i = 0; i < ctx->nb_devices; i++)
363 free_device(ctx->devices[i]);
364 if (ctx->nb_devices)
365 free(ctx->devices);
366err_free_doc:
367 xmlFreeDoc(doc);
368 xmlCleanupParser();
Paul Cercueil92de9f22014-02-19 14:07:24 +0100369err_free_bdata:
370 free(ctx->backend_data);
Paul Cercueil5d9db302014-02-18 18:16:51 +0100371err_free_ctx:
372 free(ctx);
373 return NULL;
374}