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