1 /*
2 * libiio - Library for interfacing industrial I/O (IIO) devices
3 *
4 * Copyright (C) 2014-2016 Analog Devices, Inc.
5 * Author: Paul Cercueil <paul.cercueil@analog.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 */
18
19 #include "debug.h"
20 #include "iiod-client.h"
21 #include "iio-lock.h"
22 #include "iio-private.h"
23
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 struct iiod_client {
30 struct iio_context_pdata *pdata;
31 const struct iiod_client_ops *ops;
32 struct iio_mutex *lock;
33 };
34
iiod_client_read_integer(struct iiod_client * client,void * desc,int * val)35 static ssize_t iiod_client_read_integer(struct iiod_client *client,
36 void *desc, int *val)
37 {
38 unsigned int i;
39 char buf[1024], *ptr = NULL, *end;
40 ssize_t ret;
41 int value;
42
43 do {
44 ret = client->ops->read_line(client->pdata,
45 desc, buf, sizeof(buf));
46 if (ret < 0)
47 return ret;
48
49 for (i = 0; i < (unsigned int) ret; i++) {
50 if (buf[i] != '\n') {
51 if (!ptr)
52 ptr = &buf[i];
53 } else if (!!ptr) {
54 break;
55 }
56 }
57 } while (!ptr);
58
59 buf[i] = '\0';
60
61 value = (int) strtol(ptr, &end, 10);
62 if (ptr == end)
63 return -EINVAL;
64
65 *val = value;
66 return 0;
67 }
68
iiod_client_exec_command(struct iiod_client * client,void * desc,const char * cmd)69 static int iiod_client_exec_command(struct iiod_client *client,
70 void *desc, const char *cmd)
71 {
72 int resp;
73 ssize_t ret;
74
75 ret = client->ops->write(client->pdata, desc, cmd, strlen(cmd));
76 if (ret < 0)
77 return (int) ret;
78
79 ret = iiod_client_read_integer(client, desc, &resp);
80 return ret < 0 ? (int) ret : resp;
81 }
82
iiod_client_write_all(struct iiod_client * client,void * desc,const void * src,size_t len)83 static ssize_t iiod_client_write_all(struct iiod_client *client,
84 void *desc, const void *src, size_t len)
85 {
86 struct iio_context_pdata *pdata = client->pdata;
87 const struct iiod_client_ops *ops = client->ops;
88 uintptr_t ptr = (uintptr_t) src;
89
90 while (len) {
91 ssize_t ret = ops->write(pdata, desc, (const void *) ptr, len);
92
93 if (ret < 0) {
94 if (ret == -EINTR)
95 continue;
96 else
97 return ret;
98 }
99
100 if (ret == 0)
101 return -EPIPE;
102
103 ptr += ret;
104 len -= ret;
105 }
106
107 return (ssize_t) (ptr - (uintptr_t) src);
108 }
109
iiod_client_read_all(struct iiod_client * client,void * desc,void * dst,size_t len)110 static ssize_t iiod_client_read_all(struct iiod_client *client,
111 void *desc, void *dst, size_t len)
112 {
113 struct iio_context_pdata *pdata = client->pdata;
114 const struct iiod_client_ops *ops = client->ops;
115 uintptr_t ptr = (uintptr_t) dst;
116
117 while (len) {
118 ssize_t ret = ops->read(pdata, desc, (void *) ptr, len);
119
120 if (ret < 0) {
121 if (ret == -EINTR)
122 continue;
123 else
124 return ret;
125 }
126
127 if (ret == 0)
128 return -EPIPE;
129
130 ptr += ret;
131 len -= ret;
132 }
133
134 return (ssize_t) (ptr - (uintptr_t) dst);
135 }
136
iiod_client_new(struct iio_context_pdata * pdata,struct iio_mutex * lock,const struct iiod_client_ops * ops)137 struct iiod_client * iiod_client_new(struct iio_context_pdata *pdata,
138 struct iio_mutex *lock, const struct iiod_client_ops *ops)
139 {
140 struct iiod_client *client;
141
142 client = malloc(sizeof(*client));
143 if (!client) {
144 errno = ENOMEM;
145 return NULL;
146 }
147
148 client->lock = lock;
149 client->pdata = pdata;
150 client->ops = ops;
151 return client;
152 }
153
iiod_client_destroy(struct iiod_client * client)154 void iiod_client_destroy(struct iiod_client *client)
155 {
156 free(client);
157 }
158
iiod_client_get_version(struct iiod_client * client,void * desc,unsigned int * major,unsigned int * minor,char * git_tag)159 int iiod_client_get_version(struct iiod_client *client, void *desc,
160 unsigned int *major, unsigned int *minor, char *git_tag)
161 {
162 struct iio_context_pdata *pdata = client->pdata;
163 const struct iiod_client_ops *ops = client->ops;
164 char buf[256], *ptr = buf, *end;
165 long maj, min;
166 int ret;
167
168 iio_mutex_lock(client->lock);
169
170 ret = ops->write(pdata, desc, "VERSION\r\n", sizeof("VERSION\r\n") - 1);
171 if (ret < 0) {
172 iio_mutex_unlock(client->lock);
173 return ret;
174 }
175
176 ret = ops->read_line(pdata, desc, buf, sizeof(buf));
177 iio_mutex_unlock(client->lock);
178
179 if (ret < 0)
180 return ret;
181
182 maj = strtol(ptr, &end, 10);
183 if (ptr == end)
184 return -EIO;
185
186 ptr = end + 1;
187 min = strtol(ptr, &end, 10);
188 if (ptr == end)
189 return -EIO;
190
191 ptr = end + 1;
192 if (buf + ret < ptr + 8)
193 return -EIO;
194
195 /* Strip the \n */
196 ptr[buf + ret - ptr - 1] = '\0';
197
198 if (major)
199 *major = (unsigned int) maj;
200 if (minor)
201 *minor = (unsigned int) min;
202 if (git_tag)
203 strncpy(git_tag, ptr, 8);
204 return 0;
205 }
206
iiod_client_get_trigger(struct iiod_client * client,void * desc,const struct iio_device * dev,const struct iio_device ** trigger)207 int iiod_client_get_trigger(struct iiod_client *client, void *desc,
208 const struct iio_device *dev, const struct iio_device **trigger)
209 {
210 const struct iio_context *ctx = iio_device_get_context(dev);
211 unsigned int i, nb_devices = iio_context_get_devices_count(ctx);
212 char buf[1024];
213 unsigned int name_len;
214 int ret;
215
216 iio_snprintf(buf, sizeof(buf), "GETTRIG %s\r\n",
217 iio_device_get_id(dev));
218
219 iio_mutex_lock(client->lock);
220 ret = iiod_client_exec_command(client, desc, buf);
221
222 if (ret == 0)
223 *trigger = NULL;
224 if (ret <= 0)
225 goto out_unlock;
226
227 if ((unsigned int) ret > sizeof(buf) - 1) {
228 ret = -EIO;
229 goto out_unlock;
230 }
231
232 name_len = ret;
233
234 ret = (int) iiod_client_read_all(client, desc, buf, name_len + 1);
235 if (ret < 0)
236 goto out_unlock;
237
238 ret = -ENXIO;
239
240 for (i = 0; i < nb_devices; i++) {
241 struct iio_device *cur = iio_context_get_device(ctx, i);
242
243 if (iio_device_is_trigger(cur)) {
244 const char *name = iio_device_get_name(cur);
245
246 if (!name)
247 continue;
248
249 if (!strncmp(name, buf, name_len)) {
250 *trigger = cur;
251 ret = 0;
252 goto out_unlock;
253 }
254 }
255 }
256
257 out_unlock:
258 iio_mutex_unlock(client->lock);
259 return ret;
260 }
261
iiod_client_set_trigger(struct iiod_client * client,void * desc,const struct iio_device * dev,const struct iio_device * trigger)262 int iiod_client_set_trigger(struct iiod_client *client, void *desc,
263 const struct iio_device *dev, const struct iio_device *trigger)
264 {
265 char buf[1024];
266 int ret;
267
268 if (trigger) {
269 iio_snprintf(buf, sizeof(buf), "SETTRIG %s %s\r\n",
270 iio_device_get_id(dev),
271 iio_device_get_id(trigger));
272 } else {
273 iio_snprintf(buf, sizeof(buf), "SETTRIG %s\r\n",
274 iio_device_get_id(dev));
275 }
276
277 iio_mutex_lock(client->lock);
278 ret = iiod_client_exec_command(client, desc, buf);
279 iio_mutex_unlock(client->lock);
280 return ret;
281 }
282
iiod_client_set_kernel_buffers_count(struct iiod_client * client,void * desc,const struct iio_device * dev,unsigned int nb_blocks)283 int iiod_client_set_kernel_buffers_count(struct iiod_client *client, void *desc,
284 const struct iio_device *dev, unsigned int nb_blocks)
285 {
286 int ret;
287 char buf[1024];
288
289 iio_snprintf(buf, sizeof(buf), "SET %s BUFFERS_COUNT %u\r\n",
290 iio_device_get_id(dev), nb_blocks);
291
292 iio_mutex_lock(client->lock);
293 ret = iiod_client_exec_command(client, desc, buf);
294 iio_mutex_unlock(client->lock);
295 return ret;
296 }
297
iiod_client_set_timeout(struct iiod_client * client,void * desc,unsigned int timeout)298 int iiod_client_set_timeout(struct iiod_client *client,
299 void *desc, unsigned int timeout)
300 {
301 int ret;
302 char buf[1024];
303
304 iio_snprintf(buf, sizeof(buf), "TIMEOUT %u\r\n", timeout);
305
306 iio_mutex_lock(client->lock);
307 ret = iiod_client_exec_command(client, desc, buf);
308 iio_mutex_unlock(client->lock);
309 return ret;
310 }
311
iiod_client_discard(struct iiod_client * client,void * desc,char * buf,size_t buf_len,size_t to_discard)312 static int iiod_client_discard(struct iiod_client *client, void *desc,
313 char *buf, size_t buf_len, size_t to_discard)
314 {
315 do {
316 size_t read_len;
317 ssize_t ret;
318
319 if (to_discard > buf_len)
320 read_len = buf_len;
321 else
322 read_len = to_discard;
323
324 ret = iiod_client_read_all(client, desc, buf, read_len);
325 if (ret < 0)
326 return ret;
327
328 to_discard -= (size_t) ret;
329 } while (to_discard);
330
331 return 0;
332 }
333
iiod_client_read_attr(struct iiod_client * client,void * desc,const struct iio_device * dev,const struct iio_channel * chn,const char * attr,char * dest,size_t len,enum iio_attr_type type)334 ssize_t iiod_client_read_attr(struct iiod_client *client, void *desc,
335 const struct iio_device *dev, const struct iio_channel *chn,
336 const char *attr, char *dest, size_t len, enum iio_attr_type type)
337 {
338 const char *id = iio_device_get_id(dev);
339 char buf[1024];
340 ssize_t ret;
341
342 if (attr) {
343 if (chn) {
344 if (!iio_channel_find_attr(chn, attr))
345 return -ENOENT;
346 } else {
347 switch (type) {
348 case IIO_ATTR_TYPE_DEVICE:
349 if (!iio_device_find_attr(dev, attr))
350 return -ENOENT;
351 break;
352 case IIO_ATTR_TYPE_DEBUG:
353 if (!iio_device_find_debug_attr(dev, attr))
354 return -ENOENT;
355 break;
356 case IIO_ATTR_TYPE_BUFFER:
357 if (!iio_device_find_buffer_attr(dev, attr))
358 return -ENOENT;
359 break;
360 default:
361 return -EINVAL;
362 }
363 }
364 }
365
366 if (chn) {
367 iio_snprintf(buf, sizeof(buf), "READ %s %s %s %s\r\n", id,
368 iio_channel_is_output(chn) ? "OUTPUT" : "INPUT",
369 iio_channel_get_id(chn), attr ? attr : "");
370 } else {
371 switch (type) {
372 case IIO_ATTR_TYPE_DEVICE:
373 iio_snprintf(buf, sizeof(buf), "READ %s %s\r\n",
374 id, attr ? attr : "");
375 break;
376 case IIO_ATTR_TYPE_DEBUG:
377 iio_snprintf(buf, sizeof(buf), "READ %s DEBUG %s\r\n",
378 id, attr ? attr : "");
379 break;
380 case IIO_ATTR_TYPE_BUFFER:
381 iio_snprintf(buf, sizeof(buf), "READ %s BUFFER %s\r\n",
382 id, attr ? attr : "");
383 break;
384 }
385 }
386
387 iio_mutex_lock(client->lock);
388
389 ret = (ssize_t) iiod_client_exec_command(client, desc, buf);
390 if (ret < 0)
391 goto out_unlock;
392
393 if ((size_t) ret + 1 > len) {
394 iiod_client_discard(client, desc, dest, len, ret + 1);
395 ret = -EIO;
396 goto out_unlock;
397 }
398
399 /* +1: Also read the trailing \n */
400 ret = iiod_client_read_all(client, desc, dest, ret + 1);
401
402 if (ret > 0) {
403 /* Discard the trailing \n */
404 ret--;
405
406 /* Replace it with a \0 just in case */
407 dest[ret] = '\0';
408 }
409
410 out_unlock:
411 iio_mutex_unlock(client->lock);
412 return ret;
413 }
414
iiod_client_write_attr(struct iiod_client * client,void * desc,const struct iio_device * dev,const struct iio_channel * chn,const char * attr,const char * src,size_t len,enum iio_attr_type type)415 ssize_t iiod_client_write_attr(struct iiod_client *client, void *desc,
416 const struct iio_device *dev, const struct iio_channel *chn,
417 const char *attr, const char *src, size_t len, enum iio_attr_type type)
418 {
419 struct iio_context_pdata *pdata = client->pdata;
420 const struct iiod_client_ops *ops = client->ops;
421 const char *id = iio_device_get_id(dev);
422 char buf[1024];
423 ssize_t ret;
424 int resp;
425
426 if (attr) {
427 if (chn) {
428 if (!iio_channel_find_attr(chn, attr))
429 return -ENOENT;
430 } else {
431 switch (type) {
432 case IIO_ATTR_TYPE_DEVICE:
433 if (!iio_device_find_attr(dev, attr))
434 return -ENOENT;
435 break;
436 case IIO_ATTR_TYPE_DEBUG:
437 if (!iio_device_find_debug_attr(dev, attr))
438 return -ENOENT;
439 break;
440 case IIO_ATTR_TYPE_BUFFER:
441 if (!iio_device_find_buffer_attr(dev, attr))
442 return -ENOENT;
443 break;
444 default:
445 return -EINVAL;
446 }
447 }
448 }
449
450 if (chn) {
451 iio_snprintf(buf, sizeof(buf), "WRITE %s %s %s %s %lu\r\n", id,
452 iio_channel_is_output(chn) ? "OUTPUT" : "INPUT",
453 iio_channel_get_id(chn), attr ? attr : "",
454 (unsigned long) len);
455 } else {
456 switch (type) {
457 case IIO_ATTR_TYPE_DEVICE:
458 iio_snprintf(buf, sizeof(buf), "WRITE %s %s %lu\r\n",
459 id, attr ? attr : "", (unsigned long) len);
460 break;
461 case IIO_ATTR_TYPE_DEBUG:
462 iio_snprintf(buf, sizeof(buf), "WRITE %s DEBUG %s %lu\r\n",
463 id, attr ? attr : "", (unsigned long) len);
464 break;
465 case IIO_ATTR_TYPE_BUFFER:
466 iio_snprintf(buf, sizeof(buf), "WRITE %s BUFFER %s %lu\r\n",
467 id, attr ? attr : "", (unsigned long) len);
468 break;
469 }
470 }
471
472 iio_mutex_lock(client->lock);
473 ret = ops->write(pdata, desc, buf, strlen(buf));
474 if (ret < 0)
475 goto out_unlock;
476
477 ret = iiod_client_write_all(client, desc, src, len);
478 if (ret < 0)
479 goto out_unlock;
480
481 ret = iiod_client_read_integer(client, desc, &resp);
482 if (ret < 0)
483 goto out_unlock;
484
485 ret = (ssize_t) resp;
486
487 out_unlock:
488 iio_mutex_unlock(client->lock);
489 return ret;
490 }
491
iiod_client_create_context(struct iiod_client * client,void * desc)492 struct iio_context * iiod_client_create_context(
493 struct iiod_client *client, void *desc)
494 {
495 struct iio_context *ctx = NULL;
496 size_t xml_len;
497 char *xml;
498 int ret;
499
500 iio_mutex_lock(client->lock);
501 ret = iiod_client_exec_command(client, desc, "PRINT\r\n");
502 if (ret < 0)
503 goto out_unlock;
504
505 xml_len = (size_t) ret;
506 xml = malloc(xml_len + 1);
507 if (!xml) {
508 ret = -ENOMEM;
509 goto out_unlock;
510 }
511
512 /* +1: Also read the trailing \n */
513 ret = (int) iiod_client_read_all(client, desc, xml, xml_len + 1);
514 if (ret < 0)
515 goto out_free_xml;
516
517 ctx = iio_create_xml_context_mem(xml, xml_len);
518 if (!ctx)
519 ret = -errno;
520
521 out_free_xml:
522 free(xml);
523 out_unlock:
524 iio_mutex_unlock(client->lock);
525 if (!ctx)
526 errno = -ret;
527 return ctx;
528 }
529
iiod_client_open_unlocked(struct iiod_client * client,void * desc,const struct iio_device * dev,size_t samples_count,bool cyclic)530 int iiod_client_open_unlocked(struct iiod_client *client, void *desc,
531 const struct iio_device *dev, size_t samples_count, bool cyclic)
532 {
533 char buf[1024], *ptr;
534 size_t i;
535
536 iio_snprintf(buf, sizeof(buf), "OPEN %s %lu ",
537 iio_device_get_id(dev), (unsigned long) samples_count);
538 ptr = buf + strlen(buf);
539
540 for (i = dev->words; i > 0; i--, ptr += 8) {
541 iio_snprintf(ptr, (ptr - buf) + i * 8, "%08" PRIx32,
542 dev->mask[i - 1]);
543 }
544
545 strcpy(ptr, cyclic ? " CYCLIC\r\n" : "\r\n");
546
547 return iiod_client_exec_command(client, desc, buf);
548 }
549
iiod_client_close_unlocked(struct iiod_client * client,void * desc,const struct iio_device * dev)550 int iiod_client_close_unlocked(struct iiod_client *client, void *desc,
551 const struct iio_device *dev)
552 {
553 char buf[1024];
554
555 iio_snprintf(buf, sizeof(buf), "CLOSE %s\r\n", iio_device_get_id(dev));
556 return iiod_client_exec_command(client, desc, buf);
557 }
558
iiod_client_read_mask(struct iiod_client * client,void * desc,uint32_t * mask,size_t words)559 static int iiod_client_read_mask(struct iiod_client *client,
560 void *desc, uint32_t *mask, size_t words)
561 {
562 size_t i;
563 ssize_t ret;
564 char *buf, *ptr;
565
566 buf = malloc(words * 8 + 1);
567 if (!buf)
568 return -ENOMEM;
569
570 ret = iiod_client_read_all(client, desc, buf, words * 8 + 1);
571 if (ret < 0)
572 goto out_buf_free;
573 else
574 ret = 0;
575
576 buf[words*8] = '\0';
577
578 DEBUG("Reading mask\n");
579
580 for (i = words, ptr = buf; i > 0; i--) {
581 sscanf(ptr, "%08" PRIx32, &mask[i - 1]);
582 DEBUG("mask[%lu] = 0x%08" PRIx32 "\n",
583 (unsigned long)(i - 1), mask[i - 1]);
584
585 ptr = (char *) ((uintptr_t) ptr + 8);
586 }
587
588 out_buf_free:
589 free(buf);
590 return (int) ret;
591 }
592
iiod_client_read_unlocked(struct iiod_client * client,void * desc,const struct iio_device * dev,void * dst,size_t len,uint32_t * mask,size_t words)593 ssize_t iiod_client_read_unlocked(struct iiod_client *client, void *desc,
594 const struct iio_device *dev, void *dst, size_t len,
595 uint32_t *mask, size_t words)
596 {
597 unsigned int nb_channels = iio_device_get_channels_count(dev);
598 uintptr_t ptr = (uintptr_t) dst;
599 char buf[1024];
600 ssize_t ret, read = 0;
601
602 if (!len || words != (nb_channels + 31) / 32)
603 return -EINVAL;
604
605 iio_snprintf(buf, sizeof(buf), "READBUF %s %lu\r\n",
606 iio_device_get_id(dev), (unsigned long) len);
607
608 ret = iiod_client_write_all(client, desc, buf, strlen(buf));
609 if (ret < 0)
610 return ret;
611
612 do {
613 int to_read;
614
615 ret = iiod_client_read_integer(client, desc, &to_read);
616 if (ret < 0)
617 return ret;
618 if (to_read < 0)
619 return (ssize_t) to_read;
620 if (!to_read)
621 break;
622
623 if (mask) {
624 ret = iiod_client_read_mask(client, desc, mask, words);
625 if (ret < 0)
626 return ret;
627
628 mask = NULL; /* We read the mask only once */
629 }
630
631 ret = iiod_client_read_all(client, desc, (char *) ptr, to_read);
632 if (ret < 0)
633 return ret;
634
635 ptr += ret;
636 read += ret;
637 len -= ret;
638 } while (len);
639
640 return read;
641 }
642
iiod_client_write_unlocked(struct iiod_client * client,void * desc,const struct iio_device * dev,const void * src,size_t len)643 ssize_t iiod_client_write_unlocked(struct iiod_client *client, void *desc,
644 const struct iio_device *dev, const void *src, size_t len)
645 {
646 ssize_t ret;
647 char buf[1024];
648 int val;
649
650 iio_snprintf(buf, sizeof(buf), "WRITEBUF %s %lu\r\n",
651 dev->id, (unsigned long) len);
652
653 ret = iiod_client_write_all(client, desc, buf, strlen(buf));
654 if (ret < 0)
655 return ret;
656
657 ret = iiod_client_read_integer(client, desc, &val);
658 if (ret < 0)
659 return ret;
660 if (val < 0)
661 return (ssize_t) val;
662
663 ret = iiod_client_write_all(client, desc, src, len);
664 if (ret < 0)
665 return ret;
666
667 ret = iiod_client_read_integer(client, desc, &val);
668 if (ret < 0)
669 return ret;
670 if (val < 0)
671 return (ssize_t) val;
672
673 return (ssize_t) len;
674 }
675