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